mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 10:56:15 -08:00
fix(types): [PKG-FACTORY-01] preserve published factory compatibility
This commit is contained in:
1 parent
1117107032
commit
c4c7491e16
35 files changed
+670
-229
No files matched your search
+18
-11
@@ -1,7 +1,7 @@
|
||||
// Generated from the package public declaration by yarn build:ts. Do not edit.
|
||||
/* eslint-disable ts/no-redeclare -- Callable and public type namespace intentionally merge. */
|
||||
declare namespace artplayerPluginAmbilight {
|
||||
interface Option {
|
||||
declare namespace artplayerPluginAmbilightDefinitions {
|
||||
export interface Option {
|
||||
/** CSS blur radius. @default '50px' */
|
||||
blur?: string
|
||||
/** Grid cell opacity. @default 0.5 */
|
||||
@@ -13,23 +13,30 @@ declare namespace artplayerPluginAmbilight {
|
||||
/** Background color transition duration in seconds. @default 0.3 */
|
||||
duration?: number
|
||||
}
|
||||
interface Result {
|
||||
export interface Result {
|
||||
name: 'artplayerPluginAmbilight'
|
||||
/** Start sampling; does nothing after the player is destroyed. */
|
||||
start: () => void
|
||||
/** Stop sampling while retaining the last colors. */
|
||||
stop: () => void
|
||||
}
|
||||
interface Callable {
|
||||
/** Published 1.1.0 factory shape; the options argument remains required. */
|
||||
export type Callable = (option: Option) => (art: Artplayer) => Result
|
||||
export type Factory = Callable
|
||||
/** Accurate optional invocation and CommonJS self alias, exposed by /runtime. */
|
||||
export interface RuntimeFactory {
|
||||
(option?: Option): (art: Artplayer) => Result
|
||||
/** Keep Parameters extraction identical to the published 1.1.0 signature. */
|
||||
(option: Option): (art: Artplayer) => Result
|
||||
}
|
||||
interface Factory extends Callable {
|
||||
/** Same function, including historical require(package).default calls. */
|
||||
readonly default: Factory
|
||||
readonly default: RuntimeFactory
|
||||
}
|
||||
export const artplayerPluginAmbilight: (option: Option) => (art: Artplayer) => Result
|
||||
}
|
||||
declare const artplayerPluginAmbilight: typeof artplayerPluginAmbilightDefinitions.artplayerPluginAmbilight
|
||||
declare namespace artplayerPluginAmbilight {
|
||||
export type Option = artplayerPluginAmbilightDefinitions.Option
|
||||
export type Result = artplayerPluginAmbilightDefinitions.Result
|
||||
export type Callable = artplayerPluginAmbilightDefinitions.Callable
|
||||
export type Factory = artplayerPluginAmbilightDefinitions.Factory
|
||||
export type RuntimeFactory = artplayerPluginAmbilightDefinitions.RuntimeFactory
|
||||
}
|
||||
declare const artplayerPluginAmbilight: artplayerPluginAmbilight.Factory
|
||||
export = artplayerPluginAmbilight
|
||||
export as namespace artplayerPluginAmbilight;
|
||||
+21
-14
@@ -1,21 +1,28 @@
|
||||
// Generated from the package public declaration by yarn build:ts. Do not edit.
|
||||
/* eslint-disable ts/no-redeclare -- Callable and public type namespace intentionally merge. */
|
||||
declare namespace artplayerProxyCanvas {
|
||||
declare namespace artplayerProxyCanvasDefinitions {
|
||||
/** Runs after drawing and bitmap release, before the draw event. */
|
||||
type Option = (ctx: CanvasRenderingContext2D, video: HTMLVideoElement) => void
|
||||
/** Preserve the exact published return type and its assignability. */
|
||||
type Result = HTMLCanvasElement
|
||||
/** Opt-in view of supported enumerable media members; native Canvas members win. */
|
||||
type MediaCanvas = HTMLCanvasElement & Pick<HTMLVideoElement, Exclude<keyof HTMLVideoElement, keyof HTMLCanvasElement>>
|
||||
interface Callable {
|
||||
/** Keep Parameters extraction optional, as published in 1.1.0. */
|
||||
(option?: Option): (art: Artplayer) => Result
|
||||
}
|
||||
interface Factory extends Callable {
|
||||
/** Same factory, for historical require(package).default calls. */
|
||||
readonly default: Factory
|
||||
export type Option = (ctx: CanvasRenderingContext2D, video: HTMLVideoElement) => void
|
||||
/** Preserve the exact published 1.1.0 return type and its assignability. */
|
||||
export type Result = HTMLCanvasElement
|
||||
export type Factory = (option?: Option) => (art: Artplayer) => Result
|
||||
export type Callable = Factory
|
||||
/** Explicit view of forwarded media members; native Canvas members win. */
|
||||
export type MediaCanvas = HTMLCanvasElement & Pick<HTMLVideoElement, Exclude<keyof HTMLVideoElement, keyof HTMLCanvasElement>>
|
||||
/** Opt-in runtime identity; the historical root factory has no required properties. */
|
||||
export interface RuntimeFactory extends Factory {
|
||||
readonly default: RuntimeFactory
|
||||
}
|
||||
export const artplayerProxyCanvas: (option?: Option) => (art: Artplayer) => Result
|
||||
}
|
||||
declare const artplayerProxyCanvas: typeof artplayerProxyCanvasDefinitions.artplayerProxyCanvas
|
||||
declare namespace artplayerProxyCanvas {
|
||||
export type Option = artplayerProxyCanvasDefinitions.Option
|
||||
export type Result = artplayerProxyCanvasDefinitions.Result
|
||||
export type Factory = artplayerProxyCanvasDefinitions.Factory
|
||||
export type Callable = artplayerProxyCanvasDefinitions.Callable
|
||||
export type MediaCanvas = artplayerProxyCanvasDefinitions.MediaCanvas
|
||||
export type RuntimeFactory = artplayerProxyCanvasDefinitions.RuntimeFactory
|
||||
}
|
||||
declare const artplayerProxyCanvas: artplayerProxyCanvas.Factory
|
||||
export = artplayerProxyCanvas
|
||||
export as namespace artplayerProxyCanvas;
|
||||
@@ -53,22 +53,36 @@ video/canvas with a lexical native RAF observer across published cores 5.1.7/5.4
|
||||
and candidate core in Chromium/Firefox/WebKit. Proxy/device and complete packed
|
||||
browser/demo acceptance remain tasks 05-06; see `refactor/ambilight-validation.md`.
|
||||
|
||||
Public types merge a callable value with a type-only namespace. An optional first
|
||||
overload permits omitted/undefined options; the required final overload preserves
|
||||
the published 1.1.0 `Parameters` extraction. Do not reorder these signatures. The
|
||||
runtime factory has a self-referencing `default` property to support both historical
|
||||
CommonJS forms. `.d.mts`/`.d.cts` forward to the same declaration for the actual ESM/
|
||||
CommonJS entry kinds; `typesVersions` supplies `/legacy` to older resolvers. The
|
||||
editor global is generated by `yarn build:ts` through the semantic plugin generator.
|
||||
Root and `/legacy` public types preserve the exact published 1.1.0 required-option
|
||||
factory and result. Do not add an optional overload or a required `default`
|
||||
property: preserving `Parameters` alone does not preserve reverse assignment of
|
||||
ordinary replacement factories. The JavaScript factory still has its historical
|
||||
self-referencing `default` property and accepts omitted options.
|
||||
|
||||
`RuntimeFactory` and `/runtime` describe those optional calls and the self alias.
|
||||
The subpath maps to the same production files as root, with `runtime.d.mts` for
|
||||
native ESM and `runtime.d.cts` for CommonJS. `runtime.d.ts` provides Node10's
|
||||
export-assignment shape through `typesVersions`. Root and `/legacy` use a
|
||||
types-first mapping to the original default-export declaration, retaining 1.1.0's
|
||||
NodeNext ESM namespace shape. The editor global is generated from the root
|
||||
declaration by `yarn build:ts`; it likewise requires an options argument.
|
||||
|
||||
This follows the approved `refactor/type-compatibility-policy.md` and ADR-025.
|
||||
The earlier 1.0.0 export-assignment and required-field conflicts have migration
|
||||
examples in README. No source, emitted JavaScript, defaults, or old JS entrypoint
|
||||
changes are part of this type repair.
|
||||
|
||||
Run `node --test refactor/scripts/ambilight-types.test.mjs` for public/editor/format
|
||||
contracts and `yarn test:ambilight-types-package` for offline tarball installations
|
||||
outside the workspace. The latter verifies two actual published plugin archives and
|
||||
candidate against the same packed candidate core, five compiler modes plus two
|
||||
candidate modes with interop disabled, and ten negative uses. Published 1.1.0 fails
|
||||
NodeNext ESM with its old declarations; candidate must not inherit that failure.
|
||||
Published 1.0.0 required all option fields; 1.1.0 already made them optional. Current
|
||||
inference follows 1.1.0 while retaining valid calls from both generations.
|
||||
candidate modes with interop disabled, and fifteen negative uses at their exact
|
||||
statement lines. Published 1.1.0 has four NodeNext ESM diagnostics for the frozen
|
||||
direct-call fixture; candidate preserves those codes and its valid namespace
|
||||
consumer. Earlier 1.0.0 raw `import = require` root calls are separately checked:
|
||||
they compile against 1.0.0 and retain the 1.1.0/candidate non-callable diagnostic.
|
||||
Candidate `/runtime` raw require and native ESM calls compile successfully.
|
||||
Published 1.0.0 required all option fields; 1.1.0 already made them optional.
|
||||
|
||||
`test/ambilight-proxy.test.js` and `test/browser/ambilight-proxy.spec.js` protect
|
||||
resized output geometry. Browser tests play real video with the actual workspace
|
||||
|
||||
@@ -20,11 +20,44 @@ const option: Option = { blur: '50px', opacity: 0.5, frequency: 10, duration: 0.
|
||||
const plugins = [artplayerPluginAmbilight(option)]
|
||||
```
|
||||
|
||||
Options can be omitted. `require('artplayer-plugin-ambilight')` and the historical
|
||||
`require('artplayer-plugin-ambilight').default` refer to the same factory. The
|
||||
`/legacy` entry remains available. The historical `zIndex` input is accepted but
|
||||
ignored; the grid uses 9. Option fields stay optional as in version 1.1.0; code
|
||||
extracting their types should account for `undefined`.
|
||||
Root and `/legacy` types preserve the latest published 1.1.0 contract: the options
|
||||
argument is required, its fields are optional, and an ordinary replacement factory
|
||||
does not need a `default` property. Pass `{}` to use defaults with these types.
|
||||
The historical `zIndex` input is accepted but ignored; the grid uses 9.
|
||||
|
||||
For accurate optional invocation and module interop, use `/runtime`:
|
||||
|
||||
```ts
|
||||
import ambilight from 'artplayer-plugin-ambilight/runtime'
|
||||
|
||||
const plugins = [ambilight(), ambilight(undefined), ambilight.default({})]
|
||||
```
|
||||
|
||||
It reuses the root implementation. Existing JavaScript calls with omitted options,
|
||||
`require('artplayer-plugin-ambilight')` and
|
||||
`require('artplayer-plugin-ambilight').default` still work. Start/stop remain
|
||||
synchronous and `/legacy` keeps the historical build.
|
||||
|
||||
### Earlier TypeScript declarations
|
||||
|
||||
Version 1.0.0 used `export =` and required every option field; 1.1.0 changed to a
|
||||
default export and optional fields. The root follows 1.1.0 rather than combining
|
||||
incompatible factory signatures. Earlier TypeScript `import = require` callers
|
||||
can switch only their module path:
|
||||
|
||||
```ts
|
||||
import ambilight = require('artplayer-plugin-ambilight/runtime')
|
||||
|
||||
ambilight({ blur: '50px', opacity: 0.5, frequency: 10, zIndex: 9, duration: 0.3 })
|
||||
```
|
||||
|
||||
Code reading an option field as a required value should supply a fallback, for
|
||||
example `option.blur ?? '50px'`. This optional-field change already existed in
|
||||
1.1.0. NodeNext ESM sees the historical root declaration as a CommonJS namespace;
|
||||
that type shape is preserved, including its previous direct-call diagnostics.
|
||||
Use `/runtime` for an accurate callable default import in native ESM. Node10
|
||||
TypeScript default imports of `/runtime` require `esModuleInterop`; `import =
|
||||
require` also works with interop disabled. The runtime does not add any dependency.
|
||||
|
||||
## Demo
|
||||
|
||||
|
||||
@@ -19,24 +19,24 @@
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./types/artplayer-plugin-ambilight.d.ts",
|
||||
"import": "./dist/artplayer-plugin-ambilight.mjs",
|
||||
"require": "./dist/artplayer-plugin-ambilight.js"
|
||||
},
|
||||
"./legacy": {
|
||||
"types": "./types/artplayer-plugin-ambilight.d.ts",
|
||||
"import": "./dist/artplayer-plugin-ambilight.legacy.js",
|
||||
"require": "./dist/artplayer-plugin-ambilight.legacy.js"
|
||||
},
|
||||
"./runtime": {
|
||||
"import": {
|
||||
"types": "./types/artplayer-plugin-ambilight.d.mts",
|
||||
"types": "./types/runtime.d.mts",
|
||||
"default": "./dist/artplayer-plugin-ambilight.mjs"
|
||||
},
|
||||
"require": {
|
||||
"types": "./types/artplayer-plugin-ambilight.d.cts",
|
||||
"types": "./types/runtime.d.cts",
|
||||
"default": "./dist/artplayer-plugin-ambilight.js"
|
||||
}
|
||||
},
|
||||
"./legacy": {
|
||||
"import": {
|
||||
"types": "./types/artplayer-plugin-ambilight.d.cts",
|
||||
"default": "./dist/artplayer-plugin-ambilight.legacy.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./types/artplayer-plugin-ambilight.d.cts",
|
||||
"default": "./dist/artplayer-plugin-ambilight.legacy.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main": "./dist/artplayer-plugin-ambilight.js",
|
||||
@@ -48,6 +48,9 @@
|
||||
"*": {
|
||||
"legacy": [
|
||||
"types/artplayer-plugin-ambilight.d.ts"
|
||||
],
|
||||
"runtime": [
|
||||
"types/runtime.d.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
// eslint-disable-next-line ts/no-require-imports -- Preserve the CommonJS export-assignment namespace.
|
||||
import artplayerPluginAmbilight = require('./artplayer-plugin-ambilight.js')
|
||||
|
||||
export = artplayerPluginAmbilight
|
||||
@@ -1,2 +0,0 @@
|
||||
export { default } from './artplayer-plugin-ambilight.js'
|
||||
export type { Callable, Factory, Option, Result } from './artplayer-plugin-ambilight.js'
|
||||
+31
-36
@@ -1,40 +1,35 @@
|
||||
/* eslint-disable ts/no-redeclare -- Callable and public type namespace intentionally merge. */
|
||||
import type Artplayer from 'artplayer'
|
||||
|
||||
declare namespace artplayerPluginAmbilight {
|
||||
interface Option {
|
||||
/** CSS blur radius. @default '50px' */
|
||||
blur?: string
|
||||
/** Grid cell opacity. @default 0.5 */
|
||||
opacity?: number
|
||||
/** Maximum sampling frequency in frames per second. @default 10 */
|
||||
frequency?: number
|
||||
/** Historical input retained for compatibility; runtime uses a fixed z-index of 9. */
|
||||
zIndex?: number
|
||||
/** Background color transition duration in seconds. @default 0.3 */
|
||||
duration?: number
|
||||
}
|
||||
|
||||
interface Result {
|
||||
name: 'artplayerPluginAmbilight'
|
||||
/** Start sampling; does nothing after the player is destroyed. */
|
||||
start: () => void
|
||||
/** Stop sampling while retaining the last colors. */
|
||||
stop: () => void
|
||||
}
|
||||
|
||||
interface Callable {
|
||||
(option?: Option): (art: Artplayer) => Result
|
||||
/** Keep Parameters extraction identical to the published 1.1.0 signature. */
|
||||
(option: Option): (art: Artplayer) => Result
|
||||
}
|
||||
|
||||
interface Factory extends Callable {
|
||||
/** Same function, including historical require(package).default calls. */
|
||||
readonly default: Factory
|
||||
}
|
||||
export interface Option {
|
||||
/** CSS blur radius. @default '50px' */
|
||||
blur?: string
|
||||
/** Grid cell opacity. @default 0.5 */
|
||||
opacity?: number
|
||||
/** Maximum sampling frequency in frames per second. @default 10 */
|
||||
frequency?: number
|
||||
/** Historical input retained for compatibility; runtime uses a fixed z-index of 9. */
|
||||
zIndex?: number
|
||||
/** Background color transition duration in seconds. @default 0.3 */
|
||||
duration?: number
|
||||
}
|
||||
|
||||
declare const artplayerPluginAmbilight: artplayerPluginAmbilight.Factory
|
||||
export = artplayerPluginAmbilight
|
||||
export as namespace artplayerPluginAmbilight
|
||||
export interface Result {
|
||||
name: 'artplayerPluginAmbilight'
|
||||
/** Start sampling; does nothing after the player is destroyed. */
|
||||
start: () => void
|
||||
/** Stop sampling while retaining the last colors. */
|
||||
stop: () => void
|
||||
}
|
||||
|
||||
/** Published 1.1.0 factory shape; the options argument remains required. */
|
||||
export type Callable = (option: Option) => (art: Artplayer) => Result
|
||||
export type Factory = Callable
|
||||
|
||||
/** Accurate optional invocation and CommonJS self alias, exposed by /runtime. */
|
||||
export interface RuntimeFactory {
|
||||
(option?: Option): (art: Artplayer) => Result
|
||||
readonly default: RuntimeFactory
|
||||
}
|
||||
|
||||
declare const artplayerPluginAmbilight: (option: Option) => (art: Artplayer) => Result
|
||||
export default artplayerPluginAmbilight
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { RuntimeFactory } from './artplayer-plugin-ambilight.js'
|
||||
|
||||
declare const artplayerPluginAmbilight: RuntimeFactory
|
||||
// eslint-disable-next-line ts/no-redeclare -- Ambient merge exposes types on the CommonJS factory.
|
||||
declare namespace artplayerPluginAmbilight {
|
||||
type Option = import('./artplayer-plugin-ambilight.js').Option
|
||||
type Result = import('./artplayer-plugin-ambilight.js').Result
|
||||
type RuntimeFactory = import('./artplayer-plugin-ambilight.js').RuntimeFactory
|
||||
}
|
||||
export = artplayerPluginAmbilight
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { RuntimeFactory } from './artplayer-plugin-ambilight.js'
|
||||
|
||||
declare const artplayerPluginAmbilight: RuntimeFactory
|
||||
export default artplayerPluginAmbilight
|
||||
export type { Option, Result, RuntimeFactory } from './artplayer-plugin-ambilight.js'
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { RuntimeFactory } from './artplayer-plugin-ambilight.js'
|
||||
|
||||
declare const artplayerPluginAmbilight: RuntimeFactory
|
||||
// eslint-disable-next-line ts/no-redeclare -- Ambient merge exposes types on the CommonJS factory.
|
||||
declare namespace artplayerPluginAmbilight {
|
||||
type Option = import('./artplayer-plugin-ambilight.js').Option
|
||||
type Result = import('./artplayer-plugin-ambilight.js').Result
|
||||
type RuntimeFactory = import('./artplayer-plugin-ambilight.js').RuntimeFactory
|
||||
}
|
||||
export = artplayerPluginAmbilight
|
||||
@@ -3,10 +3,18 @@
|
||||
Public entry: `src/index.ts`; public declarations remain in
|
||||
`types/artplayer-proxy-canvas.d.ts`. The factory accepts the optional drawing callback,
|
||||
then synchronously returns an actual HTMLCanvasElement when registered with ArtPlayer.
|
||||
The public namespace preserves the optional callback and exact Canvas return extraction.
|
||||
The root preserves the latest npm 1.1.0 pure factory, optional callback and exact Canvas
|
||||
return extraction. Do not attach a required default member to this type: that breaks
|
||||
assigning an ordinary historical replacement function back to typeof factory.
|
||||
MediaCanvas is an explicit type view, not a narrowed factory result. CommonJS and browser
|
||||
factory.default are self aliases; .d.mts/.d.cts wrappers distinguish ESM and CommonJS
|
||||
resolution, and typesVersions keeps the legacy path available to older TypeScript.
|
||||
factory.default remain runtime self aliases. Accurate RuntimeFactory lives behind the
|
||||
additive /runtime entry, whose runtime.d.mts/runtime.d.cts bridges distinguish ESM and
|
||||
CommonJS and whose runtime.d.ts supports old Node10 export= resolution. Both entrypoints
|
||||
load the same existing dist files. The old root declaration bridges remain packaged,
|
||||
but root/legacy conditional types deliberately select the original .d.ts module shape.
|
||||
Root NodeNext ESM therefore keeps the published namespace shape; it is not silently made
|
||||
callable. Use /runtime for precise ESM default calls. The package README documents the
|
||||
approved 1.0 export= migration; no JavaScript calls were removed by this type decision.
|
||||
Historical JavaScript falsy callback arguments remain ignored at runtime; the typed public
|
||||
callback remains optional and function-valued.
|
||||
|
||||
@@ -72,8 +80,13 @@ geometry and is separate from native media pixel acceptance. npm core 5.1.7 has
|
||||
option and is only a native-player capability control.
|
||||
|
||||
`yarn test:canvas-types-package` installs real archived and candidate packages outside the
|
||||
workspace, verifies installed bytes and checks old/current compiler modes. The public
|
||||
namespace also generates the local editor declaration through the semantic generator.
|
||||
workspace, verifies installed bytes and checks old/current compiler modes. It preserves
|
||||
the actual old NodeNext direct-import errors and 1.0 export= differences, while testing
|
||||
latest namespace replacements, plain factory bidirectional assignments, runtime self
|
||||
identity and negative statements at their exact lines. No skipLibCheck or strictness
|
||||
relaxation is used. The public declarations also generate the local editor declaration
|
||||
through the semantic generator; the editor root stays plain, with an explicit
|
||||
artplayerProxyCanvas.RuntimeFactory assertion for code needing the runtime self alias.
|
||||
Broader installed artifact contents/deep paths and 8082 demos remain 06.
|
||||
Real Safari/mobile/device scope remains 05; Windows Playwright WebKit is not a device claim.
|
||||
See `refactor/canvas-validation.md` and the task evidence for actual outcomes and limitations.
|
||||
@@ -29,14 +29,19 @@ historical `require('artplayer-proxy-canvas').default` refer to the same factory
|
||||
|
||||
## TypeScript
|
||||
|
||||
The optional callback and exact `HTMLCanvasElement` return type retain published 1.1.0
|
||||
inference. `Option`, `Result`, `Factory` and `MediaCanvas` are exported types. The last is
|
||||
an explicit view for code using supported forwarded media members:
|
||||
The root and `/legacy` declarations preserve the latest published 1.1.0 factory:
|
||||
an optional callback, an exact `HTMLCanvasElement` result, and no required factory
|
||||
properties. Ordinary replacement functions remain assignable in both directions.
|
||||
`Factory` describes that historical shape. The additive `/runtime` entry uses the same
|
||||
JavaScript implementation and describes its readonly `default` self alias with
|
||||
`RuntimeFactory`. It also exports an explicit `MediaCanvas` view:
|
||||
|
||||
```ts
|
||||
import type { MediaCanvas, Option } from 'artplayer-proxy-canvas'
|
||||
import type { MediaCanvas, Option } from 'artplayer-proxy-canvas/runtime'
|
||||
import canvas from 'artplayer-proxy-canvas/runtime'
|
||||
|
||||
const callback: Option = (context, video) => context.drawImage(video, 0, 0)
|
||||
canvas.default(callback)
|
||||
const media = art.template.$video as MediaCanvas
|
||||
await media.play()
|
||||
```
|
||||
@@ -48,6 +53,27 @@ Runtime falsy callback arguments retain historical behavior; declarations accept
|
||||
optional function, matching the published types. Implementation and test guidance is in
|
||||
[ARCHITECTURE.md](ARCHITECTURE.md).
|
||||
|
||||
### Older TypeScript consumers
|
||||
|
||||
The 1.0.0 declaration used `export =` and required a callback; 1.1.0 changed to
|
||||
`export default` and made the callback optional. These conflicting type shapes cannot
|
||||
both remain the root factory. Following the approved compatibility policy, the root
|
||||
keeps 1.1.0. A 1.0.0 TypeScript `import canvas = require('artplayer-proxy-canvas')`
|
||||
consumer that directly calls `canvas(...)` can select the accurate entry instead:
|
||||
|
||||
```ts
|
||||
import canvas = require('artplayer-proxy-canvas/runtime')
|
||||
canvas((context, video) => context.drawImage(video, 0, 0))
|
||||
canvas.default()
|
||||
```
|
||||
|
||||
This works with Node10 resolution even without `esModuleInterop`. Default imports from
|
||||
`/runtime` work with interoperability enabled and with native NodeNext ESM. Under
|
||||
NodeNext ESM, the root intentionally preserves 1.1.0's historical declaration namespace
|
||||
shape (`root.default(...)` after a default import); that declaration shape does not
|
||||
describe the actual ESM factory. Use a default import from `/runtime` for accurate ESM
|
||||
typing. Existing JavaScript root/legacy calls and distribution paths keep working.
|
||||
|
||||
## Demo
|
||||
|
||||
[https://artplayer.org](https://artplayer.org/?libs=./uncompiled/artplayer-proxy-canvas/index.js&example=canvas)
|
||||
|
||||
@@ -19,24 +19,24 @@
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./types/artplayer-proxy-canvas.d.ts",
|
||||
"import": "./dist/artplayer-proxy-canvas.mjs",
|
||||
"require": "./dist/artplayer-proxy-canvas.js"
|
||||
},
|
||||
"./legacy": {
|
||||
"types": "./types/artplayer-proxy-canvas.d.ts",
|
||||
"import": "./dist/artplayer-proxy-canvas.legacy.js",
|
||||
"require": "./dist/artplayer-proxy-canvas.legacy.js"
|
||||
},
|
||||
"./runtime": {
|
||||
"import": {
|
||||
"types": "./types/artplayer-proxy-canvas.d.mts",
|
||||
"types": "./types/runtime.d.mts",
|
||||
"default": "./dist/artplayer-proxy-canvas.mjs"
|
||||
},
|
||||
"require": {
|
||||
"types": "./types/artplayer-proxy-canvas.d.cts",
|
||||
"types": "./types/runtime.d.cts",
|
||||
"default": "./dist/artplayer-proxy-canvas.js"
|
||||
}
|
||||
},
|
||||
"./legacy": {
|
||||
"import": {
|
||||
"types": "./types/artplayer-proxy-canvas.d.cts",
|
||||
"default": "./dist/artplayer-proxy-canvas.legacy.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./types/artplayer-proxy-canvas.d.cts",
|
||||
"default": "./dist/artplayer-proxy-canvas.legacy.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main": "./dist/artplayer-proxy-canvas.js",
|
||||
@@ -48,6 +48,9 @@
|
||||
"*": {
|
||||
"legacy": [
|
||||
"types/artplayer-proxy-canvas.d.ts"
|
||||
],
|
||||
"runtime": [
|
||||
"types/runtime.d.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
// eslint-disable-next-line ts/no-require-imports -- Preserve the CommonJS export-assignment namespace.
|
||||
import artplayerProxyCanvas = require('./artplayer-proxy-canvas.js')
|
||||
|
||||
export = artplayerProxyCanvas
|
||||
export { default } from './artplayer-proxy-canvas.js'
|
||||
export type { Callable, Factory, MediaCanvas, Option, Result, RuntimeFactory } from './artplayer-proxy-canvas.js'
|
||||
@@ -1,2 +1,5 @@
|
||||
export { default } from './artplayer-proxy-canvas.js'
|
||||
export type { Callable, Factory, MediaCanvas, Option, Result } from './artplayer-proxy-canvas.js'
|
||||
import type { Factory } from './artplayer-proxy-canvas.js'
|
||||
|
||||
declare const artplayerProxyCanvas: Factory
|
||||
export default artplayerProxyCanvas
|
||||
export type { Callable, Factory, MediaCanvas, Option, Result, RuntimeFactory } from './artplayer-proxy-canvas.js'
|
||||
@@ -1,27 +1,22 @@
|
||||
/* eslint-disable ts/no-redeclare -- Callable and public type namespace intentionally merge. */
|
||||
import type Artplayer from 'artplayer'
|
||||
|
||||
declare namespace artplayerProxyCanvas {
|
||||
/** Runs after drawing and bitmap release, before the draw event. */
|
||||
type Option = (ctx: CanvasRenderingContext2D, video: HTMLVideoElement) => void
|
||||
/** Runs after drawing and bitmap release, before the draw event. */
|
||||
export type Option = (ctx: CanvasRenderingContext2D, video: HTMLVideoElement) => void
|
||||
|
||||
/** Preserve the exact published return type and its assignability. */
|
||||
type Result = HTMLCanvasElement
|
||||
/** Preserve the exact published 1.1.0 return type and its assignability. */
|
||||
export type Result = HTMLCanvasElement
|
||||
|
||||
/** Opt-in view of supported enumerable media members; native Canvas members win. */
|
||||
type MediaCanvas = HTMLCanvasElement & Pick<HTMLVideoElement, Exclude<keyof HTMLVideoElement, keyof HTMLCanvasElement>>
|
||||
export type Factory = (option?: Option) => (art: Artplayer) => Result
|
||||
export type Callable = Factory
|
||||
|
||||
interface Callable {
|
||||
/** Keep Parameters extraction optional, as published in 1.1.0. */
|
||||
(option?: Option): (art: Artplayer) => Result
|
||||
}
|
||||
/** Explicit view of forwarded media members; native Canvas members win. */
|
||||
export type MediaCanvas = HTMLCanvasElement & Pick<HTMLVideoElement, Exclude<keyof HTMLVideoElement, keyof HTMLCanvasElement>>
|
||||
|
||||
interface Factory extends Callable {
|
||||
/** Same factory, for historical require(package).default calls. */
|
||||
readonly default: Factory
|
||||
}
|
||||
/** Opt-in runtime identity; the historical root factory has no required properties. */
|
||||
export interface RuntimeFactory extends Factory {
|
||||
readonly default: RuntimeFactory
|
||||
}
|
||||
|
||||
declare const artplayerProxyCanvas: artplayerProxyCanvas.Factory
|
||||
export = artplayerProxyCanvas
|
||||
export as namespace artplayerProxyCanvas
|
||||
declare const artplayerProxyCanvas: (option?: Option) => (art: Artplayer) => Result
|
||||
|
||||
export default artplayerProxyCanvas
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { RuntimeFactory } from './artplayer-proxy-canvas.js'
|
||||
|
||||
declare const artplayerProxyCanvas: RuntimeFactory
|
||||
// eslint-disable-next-line ts/no-redeclare -- Ambient merge exposes types on the CommonJS factory.
|
||||
declare namespace artplayerProxyCanvas {
|
||||
type Option = import('./artplayer-proxy-canvas.js').Option
|
||||
type Result = import('./artplayer-proxy-canvas.js').Result
|
||||
type Factory = import('./artplayer-proxy-canvas.js').Factory
|
||||
type MediaCanvas = import('./artplayer-proxy-canvas.js').MediaCanvas
|
||||
type RuntimeFactory = import('./artplayer-proxy-canvas.js').RuntimeFactory
|
||||
}
|
||||
export = artplayerProxyCanvas
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { RuntimeFactory } from './artplayer-proxy-canvas.js'
|
||||
|
||||
declare const artplayerProxyCanvas: RuntimeFactory
|
||||
export default artplayerProxyCanvas
|
||||
export type { Factory, MediaCanvas, Option, Result, RuntimeFactory } from './artplayer-proxy-canvas.js'
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { RuntimeFactory } from './artplayer-proxy-canvas.js'
|
||||
|
||||
declare const artplayerProxyCanvas: RuntimeFactory
|
||||
// eslint-disable-next-line ts/no-redeclare -- Ambient merge exposes types on the CommonJS factory.
|
||||
declare namespace artplayerProxyCanvas {
|
||||
type Option = import('./artplayer-proxy-canvas.js').Option
|
||||
type Result = import('./artplayer-proxy-canvas.js').Result
|
||||
type Factory = import('./artplayer-proxy-canvas.js').Factory
|
||||
type MediaCanvas = import('./artplayer-proxy-canvas.js').MediaCanvas
|
||||
type RuntimeFactory = import('./artplayer-proxy-canvas.js').RuntimeFactory
|
||||
}
|
||||
export = artplayerProxyCanvas
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"task": "PKG-FACTORY-01",
|
||||
"capturedAt": "2026-09-13T08:50:20.867Z",
|
||||
"status": "done",
|
||||
"policy": "refactor/type-compatibility-policy.md",
|
||||
"baseline": "Latest actual npm 1.1.0 for both packages; earlier 1.0.0 conflicts explicitly migrated, not silently claimed compatible.",
|
||||
"reports": [
|
||||
{
|
||||
"name": "canvas",
|
||||
"report": "refactor/.cache/canvas-package-types-2pltVA/report.json",
|
||||
"sha256": "a1c381b23928f4bcc3fe4a8e54d90277a1ff6f4a4ec8bc0423ba1a6117cc18d1",
|
||||
"archiveSha256": "da8cb6d8eb0f01cd97a9e4bccdebdab028fb4f985e54ff45743a5a6661980e62",
|
||||
"modes": 17,
|
||||
"candidateModes": 7,
|
||||
"negativeStatementsPerCandidateMode": 14,
|
||||
"historicalDiagnosticsRetained": true,
|
||||
"sourceAndDistUnchanged": true
|
||||
},
|
||||
{
|
||||
"name": "ambilight",
|
||||
"report": "refactor/.cache/ambilight-package-types-sHTRf5/report.json",
|
||||
"sha256": "4efcad331c5879d62b50d7333538bf908959b3ccda70933a37c233e804d7ff62",
|
||||
"archiveSha256": "1a07bde5804eb63bd0436d0f63af6a9d0d35873e30e120b9ff1553806729649e",
|
||||
"modes": 17,
|
||||
"candidateModes": 7,
|
||||
"negativeStatementsPerCandidateMode": 15,
|
||||
"historicalDiagnosticsRetained": true,
|
||||
"sourceAndDistUnchanged": true
|
||||
}
|
||||
],
|
||||
"checks": [
|
||||
{
|
||||
"file": "refactor/.cache/factory01-types-final.log",
|
||||
"sha256": "e73fd496e06fd0eaba1646183d4f55e0e57350d24f3cdbbefebee5db40301476"
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/factory01-typecheck.log",
|
||||
"sha256": "876eed09bef39ddecdbdf7202ba3b5d0ae062502da8f6b8d87d73efe90cab8d9"
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/factory01-editor.log",
|
||||
"sha256": "c61d3d927caaf293c1442a2bbcc88ac3c79d25f6f7b16efcdb05feb7e4b02c79"
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/factory01-parent-lint.log",
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
}
|
||||
],
|
||||
"verified": [
|
||||
"Plain root factories assign both ways, exact Parameters/ReturnType.",
|
||||
"Canvas optional callback and exact HTMLCanvasElement result; Ambilight required option and exact synchronous Result.",
|
||||
"Root/legacy preserve 1.1.0 NodeNext namespace; runtime provides callable native ESM and CommonJS self alias.",
|
||||
"Each actual tarball installed outside workspace, offline frozen reinstall and exact installed bytes.",
|
||||
"Generated editor declarations and strict old/current compilers, actual main/legacy/global/ESM factory identities."
|
||||
],
|
||||
"limitations": [
|
||||
"No source or emitted JavaScript changed; no new browser/device evidence claimed.",
|
||||
"Package 05/06 and release reviews remain separate tasks."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
# PKG-FACTORY-01 历史工厂类型修复完成
|
||||
|
||||
根据用户2026-09-13确认及ADR-025,Canvas和Ambilight根/legacy声明保留真实npm
|
||||
1.1.0的完整纯函数形状。Canvas回调可选、结果精确为HTMLCanvasElement;Ambilight
|
||||
参数必填、字段可选、结果仍为同步start/stop。普通替代函数可双向赋值,不再要求
|
||||
必填default成员,也不靠重载只保住Parameters。
|
||||
|
||||
新增/runtime映射到现有JS文件,独立描述可选调用及readonly self.default。
|
||||
ESM/CommonJS/Node10解析各有准确声明。根入口保留1.1.0 NodeNext ESM历史namespace
|
||||
及原直接调用诊断;不能以修复该旧诊断为由破坏原合法namespace赋值。较早1.0.0的
|
||||
export=及参数/字段差异有真实归档对照,并在包README给出/runtime迁移示例。
|
||||
|
||||
两包最终tarball分别验证17配置,合计34配置。候选各7配置均无正例诊断,Canvas
|
||||
每配置拒绝14个指定反例,Ambilight为15个;反例按语句行核对。安装目录位于工作区
|
||||
之外,执行离线安装、frozen重装、归档及安装字节比对;不借用workspace类型软链。
|
||||
旧1.0及1.1诊断保留,不将其混作候选失败或静默删除。
|
||||
|
||||
8项专项测试核对真实历史声明、完整工厂赋值、编辑器生成和main/legacy/global/ESM
|
||||
身份。共享typecheck在修改当时验证361个生产TS文件及所有已有消费模式,通过;
|
||||
后续Chromecast新增源码不计入该快照。定向lint通过。可追溯报告和哈希见
|
||||
[验证记录](../baselines/factory-compatibility-validation.json)。
|
||||
|
||||
生产源码和JS产物未改,已有运行时行为保留;本次没有新增浏览器或设备验证声明。
|
||||
FACTORY-TYPE-01已关闭,包05/06和npm发布复盘仍须继续。一个任务一个独立完成提交,
|
||||
不推送、不发布。
|
||||
@@ -1,15 +1,17 @@
|
||||
# Canvas / Ambilight 工厂类型兼容取舍(已确认,待实施)
|
||||
# Canvas / Ambilight 工厂类型兼容取舍(已确认并实施)
|
||||
|
||||
PKG-FACTORY-01 已复现当前声明的问题,但尚未修改生产声明,不能标记完成。
|
||||
2026-09-13用户已接受统一规则,见[确认记录](type-compatibility-policy.md)。本页推荐
|
||||
方向已授权;此前Ads单项授权之外的待决状态由此次确认替代,实施和验证仍须完成。
|
||||
2026-09-13用户已接受统一规则,见[确认记录](type-compatibility-policy.md)。
|
||||
PKG-FACTORY-01随后完成两包声明、/runtime、迁移文档及真实安装验证,见
|
||||
[完成记录](changes/2026-09-13-PKG-FACTORY-01-compatible-types.md)和
|
||||
[验证证据](baselines/factory-compatibility-validation.json)。以下矩阵保留决策时的
|
||||
历史方案对照,不能将其中故意展示的失败方案视为当前生产实现。
|
||||
|
||||
## 问题与真实发布对照
|
||||
|
||||
两包 npm 1.0.0 声明均为 `export =`,npm 1.1.0 均改为 `export default`。
|
||||
归档由各包 release 基线核验;测试读取真实 tarball 中的声明,不用工作区文件替代。
|
||||
|
||||
当前为了兼容运行时 `require(package)` 和 `require(package).default`,公开 Factory
|
||||
修复前为了兼容运行时 `require(package)` 和 `require(package).default`,公开 Factory
|
||||
增加了必填 `.default`。这使 1.1.0 原本合法的 `const replacement: typeof factory = ...`
|
||||
报 TS2741。Ambilight 另外增加的可选参数重载也使只接受必填参数的旧替代工厂报 TS2322;
|
||||
保留最后一个必填重载只能保住 Parameters,无法保住整个函数的赋值关系。
|
||||
@@ -63,17 +65,17 @@ import module = require('artplayer-plugin-ambilight')
|
||||
module.default({})
|
||||
```
|
||||
|
||||
完整候选声明由 `scripts/factory-assignability.mjs` 的 declaration(pkg, 'latest-default')
|
||||
生成。当前它只是可编译的核心签名提案,尚未加入全部命名类型、格式包装和安装验收,
|
||||
属于已批准方向,尚不属于已经实施并验证的生产修复。
|
||||
提案核心签名仍由 `scripts/factory-assignability.mjs` 的 declaration(pkg, 'latest-default')
|
||||
生成用于历史对照。实际生产声明已加入命名类型与/runtime格式桥,并完成34个真实安装
|
||||
配置验证。根NodeNext ESM保留1.1.0 namespace形状;准确默认导入应使用/runtime。
|
||||
|
||||
## 决策与后续
|
||||
|
||||
- 状态:用户已明确接受上述较早版本TypeScript导入迁移;不因批准而跳过验证。
|
||||
- 下一步:修改两包声明和包装,补充 RuntimeFactory、全工厂赋值测试、编辑器和包内文档,
|
||||
重建并执行真实安装消费矩阵;保留逐版本负例,验证 JS 产物与浏览器证据,单独完成任务提交。
|
||||
- 已完成:两包声明、RuntimeFactory、全工厂赋值测试、编辑器与文档、真实安装消费矩阵。
|
||||
JS产物没有修改,现有实际分发身份测试通过,不声称新增浏览器设备证据。
|
||||
- 仍不得把optional .default、any、skipLibCheck或删除历史消费者当作修复。
|
||||
- PKG-CANVAS-05、PKG-AMBILIGHT-05、REL-01 仍依赖本任务完成。
|
||||
- PKG-CANVAS-05、PKG-AMBILIGHT-05、REL-01的此项依赖已满足,各自剩余验证继续实施。
|
||||
|
||||
证据:[编译矩阵](baselines/factory-compatibility-proposals.json)、
|
||||
[原始回归](baselines/factory-assignment-gaps.json)。
|
||||
+5
-5
@@ -4,7 +4,7 @@
|
||||
|
||||
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 228 项,范围 22 个包及工作区/示例。
|
||||
|
||||
状态:todo 79 / doing 17 / blocked 0 / done 132 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
状态:todo 78 / doing 17 / blocked 0 / done 133 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
|
||||
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
|
||||
|
||||
@@ -289,7 +289,7 @@
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| PKG-CAST-01 | artplayer-plugin-chromecast<br>核对包契约与历史用法 | BASE-05 | url/sdk/icon/mimeType、loader、会话、全局 SDK 与结果 | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结 | H | done |
|
||||
| PKG-CAST-02 | artplayer-plugin-chromecast<br>建立特有行为与错误测试 | PKG-CAST-01, ENG-03, ENG-05 | SDK 脚本加载失败/重入、无设备/拒绝、重复实例与销毁 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | done |
|
||||
| PKG-CAST-03 | artplayer-plugin-chromecast<br>整理内部职责与资源 | PKG-CAST-02, CORE-11, CORE-13 | SDK singleton/实例订阅/会话与控件职责分离 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | todo |
|
||||
| PKG-CAST-03 | artplayer-plugin-chromecast<br>整理内部职责与资源 | PKG-CAST-02, CORE-11, CORE-13 | SDK singleton/实例订阅/会话与控件职责分离 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | doing |
|
||||
| PKG-CAST-04 | artplayer-plugin-chromecast<br>迁移自有源码和公开类型 | PKG-CAST-03, ENG-04, ENG-06, CORE-07 | Cast 能力 adapter、全局对象和元数据类型 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | todo |
|
||||
| PKG-CAST-05 | artplayer-plugin-chromecast<br>验证新旧核心和组合 | PKG-CAST-04, CORE-22 | stub 测试之外记录实际 Cast 设备会话、源更新和断开 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo |
|
||||
| PKG-CAST-06 | artplayer-plugin-chromecast<br>验证分发并同步文档 | PKG-CAST-05, ENG-07 | chromecast.js、SDK URL/权限能力及待支持环境说明 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo |
|
||||
@@ -417,7 +417,7 @@
|
||||
|
||||
| ID | 范围 / 步骤 | 前置依赖 | 交付物 | 验收条件 | 风险 | 状态 |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| PKG-FACTORY-01 | artplayer-proxy-canvas, artplayer-plugin-ambilight<br>修复已迁移工厂的反向赋值兼容 | PKG-CANVAS-04, PKG-AMBILIGHT-04, PKG-DPIP-04 | 复现两包旧typeof工厂替换与新.default/可选重载冲突,修复公开类型并补安装消费者、产物和文档 | 默认工厂的历史合法反向赋值继续通过;既有类型/运行时导出逐版本核对,不能只验证Parameters或直接调用 | H | doing |
|
||||
| PKG-FACTORY-01 | artplayer-proxy-canvas, artplayer-plugin-ambilight<br>修复已迁移工厂的反向赋值兼容 | PKG-CANVAS-04, PKG-AMBILIGHT-04, PKG-DPIP-04 | 复现两包旧typeof工厂替换与新.default/可选重载冲突,修复公开类型并补安装消费者、产物和文档 | 默认工厂的历史合法反向赋值继续通过;既有类型/运行时导出逐版本核对,不能只验证Parameters或直接调用 | H | done |
|
||||
|
||||
## 2 测试与工程基础
|
||||
|
||||
@@ -533,7 +533,7 @@
|
||||
- PKG-ASR-02: [记录](changes/2026-09-13-PKG-ASR-02-audio-baseline.md) [记录](baselines/asr-audio-validation.json)
|
||||
- PKG-ASR-03: [记录](changes/2026-09-13-PKG-ASR-03-audio-ownership.md) [记录](baselines/asr-ownership-validation.json)
|
||||
- PKG-ASR-04: [记录](changes/2026-09-13-PKG-ASR-04-public-types.md) [记录](baselines/asr-types-validation.json) [记录](baselines/asr-type-diagnostics.json)
|
||||
- PKG-ASR-05: [记录](baselines/asr-volume-validation.json) [记录](baselines/asr-fallback-validation.json) [记录](baselines/asr-core.json) [记录](baselines/asr-local-core-validation.json) [记录](changes/2026-09-13-PKG-ASR-05-local-core-checkpoint.md) [记录](baselines/asr-combinations-validation.json) [记录](changes/2026-09-13-PKG-ASR-05-cors-combinations.md) [记录](baselines/asr-explicit-capture-validation.json)
|
||||
- PKG-ASR-05: [记录](baselines/asr-volume-validation.json) [记录](baselines/asr-fallback-validation.json) [记录](baselines/asr-core.json) [记录](baselines/asr-local-core-validation.json) [记录](changes/2026-09-13-PKG-ASR-05-local-core-checkpoint.md) [记录](baselines/asr-combinations-validation.json) [记录](changes/2026-09-13-PKG-ASR-05-cors-combinations.md) [记录](baselines/asr-explicit-capture-validation.json) [记录](changes/2026-09-13-PKG-ASR-05-editor-generation.md)
|
||||
- PKG-ADS-01: [记录](baselines/ads-release.json) [记录](baselines/ads-contract.md) [记录](baselines/ads-contract-validation.json) [记录](changes/2026-09-12-PKG-ADS-01-contract.md) [记录](scripts/ads-contract.test.mjs)
|
||||
- PKG-ADS-02: [记录](changes/2026-09-12-PKG-ADS-02-tests.md) [记录](ads-validation.md) [记录](baselines/ads-validation.json)
|
||||
- PKG-ADS-03: [记录](changes/2026-09-12-PKG-ADS-03-lifecycle.md) [记录](ads-validation.md) [记录](baselines/ads-lifecycle-validation.json)
|
||||
@@ -571,7 +571,7 @@
|
||||
- PKG-TOOL-THUMB-02: [记录](baselines/thumbnail-behavior-validation.json) [记录](changes/2026-09-13-PKG-TOOL-THUMB-02-behavior.md)
|
||||
- PKG-TOOL-THUMB-03: [记录](changes/2026-09-13-PKG-TOOL-THUMB-03-input-checkpoint.md) [记录](baselines/thumbnail-input-checkpoint.json) [记录](changes/2026-09-13-PKG-TOOL-THUMB-03-lifecycle.md) [记录](baselines/thumbnail-lifecycle-validation.json)
|
||||
- PKG-TOOL-THUMB-04: [记录](changes/2026-09-13-PKG-TOOL-THUMB-04-runtime-types.md) [记录](baselines/thumbnail-runtime-types-validation.json) [记录](changes/2026-09-13-PKG-TOOL-THUMB-04-public-types.md) [记录](baselines/thumbnail-public-types-validation.json) [记录](changes/2026-09-13-PKG-TOOL-THUMB-04-emitter.md) [记录](baselines/thumbnail-emitter-validation.json)
|
||||
- PKG-FACTORY-01: [记录](baselines/factory-assignment-gaps.json) [记录](baselines/factory-compatibility-proposals.json) [记录](factory-compatibility-decision.md) [记录](changes/2026-09-12-PKG-FACTORY-01-decision.md)
|
||||
- PKG-FACTORY-01: [记录](baselines/factory-assignment-gaps.json) [记录](baselines/factory-compatibility-proposals.json) [记录](factory-compatibility-decision.md) [记录](changes/2026-09-12-PKG-FACTORY-01-decision.md) [记录](type-compatibility-policy.md) [记录](baselines/factory-compatibility-validation.json) [记录](changes/2026-09-13-PKG-FACTORY-01-compatible-types.md)
|
||||
- CORE-25: [记录](changes/2026-09-13-CORE-25-defaults-ssr.md) [记录](baselines/defaults-ssr-validation.json)
|
||||
- ENG-12: [记录](changes/2026-09-13-ENG-12-library-public.md) [记录](baselines/library-public-validation.json)
|
||||
- PKG-ASR-07: [记录](changes/2026-09-13-PKG-ASR-07-playback-volume.md) [记录](baselines/asr-volume-validation.json)
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# 进度与证据
|
||||
|
||||
## PKG-FACTORY-01 历史工厂类型修复完成
|
||||
|
||||
Canvas/Ambilight落实已批准规则:保留真实npm1.1.0根工厂与NodeNext namespace,
|
||||
/runtime提供准确可选调用与self.default;1.0冲突迁移已入两包README。共34个真实
|
||||
安装配置、14个候选配置全部正例通过,每配置分别拒绝14/15个指定反例。8项专项
|
||||
测试、共享strict消费检查和编辑器生成通过。见[变更](changes/2026-09-13-PKG-FACTORY-01-compatible-types.md)
|
||||
及[证据](baselines/factory-compatibility-validation.json)。FACTORY-TYPE-01关闭;
|
||||
包05/06及发布验收仍独立。ASR编辑器导出冲突另已修复并提交111710703。
|
||||
228项:133 done、17 doing、78 todo。CAST03源码迁移开始;VTT/MultiSub类型规则
|
||||
由独立子代理继续落实。未推送或发布。
|
||||
|
||||
|
||||
## PKG-CAST-02 Chromecast 错误基线完成
|
||||
|
||||
93项受控历史测试通过,其中65项新增错误/生命周期观察。首次会话结果误用、已就绪
|
||||
|
||||
@@ -167,7 +167,7 @@
|
||||
| DPIP-STYLE-01 | resolved / 已复现 | Document PiP 1.1.0 and frozen source duplicate style IDs when evaluated before DOMContentLoaded | PKG-DPIP-03 |
|
||||
| DPIP-MEDIA-01 | open / 已复现 | WebKit reports styled width640 for width320 media in the controlled Document PiP iframe matrix | PKG-DPIP-05 |
|
||||
| DPIP-RESOLVE-01 | resolved / 已复现 | Historical Document PiP exports hide declarations or infer an uncallable CommonJS namespace in modern resolution | PKG-DPIP-04, PKG-DPIP-06 |
|
||||
| FACTORY-TYPE-01 | open / 已复现 | Canvas and Ambilight default self property narrows assignability of historical factory replacements | PKG-FACTORY-01 |
|
||||
| FACTORY-TYPE-01 | resolved / 已复现 | Canvas and Ambilight default self property narrows assignability of historical factory replacements | PKG-FACTORY-01 |
|
||||
| MB-LICENSE-01 | open / 已复现 | Historical MediaBunny proxy bundles embed SDK code without MPL notice markers or separate license/source provenance | PKG-MB-10 |
|
||||
| MB-LIFE-01 | open / 已复现 | MediaBunny load, track replacement, timeout, RAF and audio callbacks lack one shared terminal owner | PKG-MB-02, PKG-MB-03, PKG-MB-04, PKG-MB-05, PKG-MB-06, PKG-MB-07, PKG-MB-09 |
|
||||
| MB-TYPE-01 | resolved / 已复现 | MediaBunny proxy declares only Canvas while exposing a shim and changes CommonJS export generation between releases | PKG-MB-08, PKG-MB-10 |
|
||||
|
||||
+11
-3
@@ -3632,18 +3632,26 @@
|
||||
{
|
||||
"id": "FACTORY-TYPE-01",
|
||||
"title": "Canvas and Ambilight default self property narrows assignability of historical factory replacements",
|
||||
"status": "open",
|
||||
"status": "resolved",
|
||||
"confirmation": "reproduced",
|
||||
"owners": [
|
||||
"PKG-FACTORY-01"
|
||||
],
|
||||
"compatibleResolution": "Preserve historical complete factory assignments while modeling actual export variants explicitly; do not equate unchanged Parameters with complete callable compatibility. The 72-case proposal matrix demonstrates that required or optional self.default and pure export-default/export-equals shapes each preserve different historical consumers; the proposed latest-release type baseline is awaiting explicit user acceptance.",
|
||||
"compatibleResolution": "User accepted latest published 1.1.0 root type shapes on 2026-09-13 (ADR-025). Pure factories preserve reverse assignments and NodeNext namespace; additive runtime declarations model optional calls and self aliases. Earlier 1.0 conflicts have explicit migrations.",
|
||||
"closureCriteria": "Both historical and candidate replacement fixtures, all supported export/declaration paths and installed consumers verified; unresolved variant conflicts require an explicit decision rather than silent narrowing.",
|
||||
"evidence": [
|
||||
"refactor/baselines/factory-assignment-gaps.json",
|
||||
"refactor/changes/2026-09-12-PKG-DPIP-04-types.md",
|
||||
"refactor/baselines/factory-compatibility-proposals.json",
|
||||
"refactor/factory-compatibility-decision.md"
|
||||
"refactor/factory-compatibility-decision.md",
|
||||
"refactor/type-compatibility-policy.md",
|
||||
"refactor/baselines/factory-compatibility-validation.json",
|
||||
"refactor/changes/2026-09-13-PKG-FACTORY-01-compatible-types.md"
|
||||
],
|
||||
"resolutionRationale": "34 actual isolated installation cases, 14 zero-diagnostic candidate modes with 14/15 invalid statements each, eight focused tests and shared strict consumer checks; old diagnostics and legal namespace replacements retained. JavaScript files unchanged.",
|
||||
"resolutionEvidence": [
|
||||
"refactor/baselines/factory-compatibility-validation.json",
|
||||
"refactor/changes/2026-09-13-PKG-FACTORY-01-compatible-types.md"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -50,8 +50,9 @@ async function main() {
|
||||
const runtimeFile = path.join(consumer, 'exports.cjs')
|
||||
fs.writeFileSync(runtimeFile, plugin.label === 'candidate'
|
||||
? `const assert = require('node:assert/strict');
|
||||
for (const name of ['artplayer-plugin-ambilight', 'artplayer-plugin-ambilight/legacy']) { const factory = require(name); assert.equal(typeof factory, 'function'); assert.equal(factory.default, factory); assert.equal(typeof factory(), 'function'); }
|
||||
import('artplayer-plugin-ambilight').then(module => { assert.deepEqual(Object.keys(module), ['default']); assert.equal(module.default.default, module.default); }).catch(error => { console.error(error); process.exitCode = 1; });`
|
||||
for (const name of ['artplayer-plugin-ambilight', 'artplayer-plugin-ambilight/legacy', 'artplayer-plugin-ambilight/runtime']) { const factory = require(name); assert.equal(typeof factory, 'function'); assert.equal(factory.default, factory); assert.equal(typeof factory(), 'function'); }
|
||||
assert.equal(require('artplayer-plugin-ambilight/runtime'), require('artplayer-plugin-ambilight'));
|
||||
Promise.all(['artplayer-plugin-ambilight', 'artplayer-plugin-ambilight/runtime', 'artplayer-plugin-ambilight/legacy'].map(name => import(name))).then(([root, runtime, legacy]) => { assert.deepEqual(Object.keys(root), ['default']); assert.deepEqual(Object.keys(runtime), ['default']); assert.equal(runtime.default, root.default); assert.equal(root.default.default, root.default); assert.equal(legacy.default, require('artplayer-plugin-ambilight/legacy')); }).catch(error => { console.error(error); process.exitCode = 1; });`
|
||||
: `const assert = require('node:assert/strict'); const value = require('artplayer-plugin-ambilight'); assert.equal(typeof value, '${plugin.version === '1.0.0' ? 'object' : 'function'}'); assert.equal(typeof (value.default || value)({}), 'function');`)
|
||||
fs.writeFileSync(path.join(output, `${plugin.label}-runtime.log`), run([runtimeFile], consumer))
|
||||
const modes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']]
|
||||
@@ -60,14 +61,24 @@ import('artplayer-plugin-ambilight').then(module => { assert.deepEqual(Object.ke
|
||||
for (const [compiler, mode] of modes) {
|
||||
const next = mode.startsWith('nodenext')
|
||||
const extension = next ? mode.endsWith('-cjs') ? 'cts' : 'mts' : 'ts'
|
||||
const source = fs.readFileSync(path.join(workspace, plugin.label === 'candidate' ? 'test/types/ambilight.ts' : 'refactor/fixtures/consumers/ambilight-published.ts'), 'utf8')
|
||||
let source = fs.readFileSync(path.join(workspace, plugin.label === 'candidate' ? 'test/types/ambilight.ts' : 'refactor/fixtures/consumers/ambilight-published.ts'), 'utf8')
|
||||
if (plugin.label === 'candidate' && mode === 'nodenext-esm') {
|
||||
source = source
|
||||
.replace('import ambilight from \'artplayer-plugin-ambilight\'', 'import ambilightModule from \'artplayer-plugin-ambilight\'\nconst ambilight = ambilightModule.default')
|
||||
.replace('import legacy from \'artplayer-plugin-ambilight/legacy\'', 'import legacyModule from \'artplayer-plugin-ambilight/legacy\'\nconst legacy = legacyModule.default')
|
||||
}
|
||||
if (mode.endsWith('-no-interop'))
|
||||
source = source.replace('import runtime from \'artplayer-plugin-ambilight/runtime\'', 'import runtime = require(\'artplayer-plugin-ambilight/runtime\')')
|
||||
const filename = path.join(consumer, `consumer.${extension}`)
|
||||
const historical = path.join(consumer, `historical.${extension}`)
|
||||
fs.copyFileSync(path.join(workspace, 'refactor/fixtures/consumers/ambilight-published.ts'), historical)
|
||||
const files = plugin.label === 'candidate' ? [filename, historical] : [filename]
|
||||
if (mode === 'nodenext-cjs' && plugin.label !== 'published-1.1.0') {
|
||||
const commonjs = path.join(consumer, 'commonjs.cts')
|
||||
fs.writeFileSync(commonjs, `import ambilight = require('artplayer-plugin-ambilight'); ambilight({ blur: '50px', opacity: 0.5, frequency: 10, zIndex: 9, duration: 0.3 }); ${plugin.label === 'candidate' ? 'ambilight.default();' : ''}`)
|
||||
const files = [filename]
|
||||
if (plugin.label === 'candidate' && mode !== 'bundler-esm' && mode !== 'nodenext-esm') {
|
||||
const commonjs = path.join(consumer, `commonjs.${extension}`)
|
||||
fs.writeFileSync(commonjs, `import ambilight = require('artplayer-plugin-ambilight'); import legacy = require('artplayer-plugin-ambilight/legacy'); import runtime = require('artplayer-plugin-ambilight/runtime');
|
||||
import type Artplayer from 'artplayer';
|
||||
const replacement: typeof ambilight.default = (_option) => (_art: Artplayer) => ({name: 'artplayerPluginAmbilight', start() {}, stop() {}});
|
||||
const legacyReplacement: typeof legacy.default = replacement;
|
||||
ambilight.default({}); legacy.default({}); runtime(); runtime.default();
|
||||
const option: runtime.Option = {}; runtime(option); void legacyReplacement;`)
|
||||
files.push(commonjs)
|
||||
}
|
||||
const options = { strict: true, noEmit: true, skipLibCheck: false, types: [], esModuleInterop: !mode.endsWith('-no-interop'), target: compiler.ScriptTarget.ES2020, lib: ['lib.es2020.d.ts', 'lib.dom.d.ts'], module: next ? compiler.ModuleKind.NodeNext : mode === 'bundler-esm' ? compiler.ModuleKind.ESNext : compiler.ModuleKind.CommonJS, moduleResolution: next ? compiler.ModuleResolutionKind.NodeNext : mode === 'bundler-esm' ? compiler.ModuleResolutionKind.Bundler : compiler.ModuleResolutionKind.NodeJs }
|
||||
@@ -78,22 +89,43 @@ import('artplayer-plugin-ambilight').then(module => { assert.deepEqual(Object.ke
|
||||
const actual = fs.realpathSync(file.fileName)
|
||||
assert(actual.startsWith(fs.realpathSync(consumer) + path.sep) || (program.isSourceFileDefaultLibrary(file) && path.dirname(actual) === fs.realpathSync(path.dirname(compiler.sys.getExecutingFilePath()))), `Types escaped installed consumer: ${actual}`)
|
||||
}
|
||||
return compiler.getPreEmitDiagnostics(program).map(item => ({ code: item.code, message: compiler.flattenDiagnosticMessageText(item.messageText, '\n') }))
|
||||
return compiler.getPreEmitDiagnostics(program).map(item => ({ code: item.code, line: item.file && item.start !== undefined ? item.file.getLineAndCharacterOfPosition(item.start).line + 1 : null, message: compiler.flattenDiagnosticMessageText(item.messageText, '\n').replaceAll(consumer.replaceAll('\\', '/'), '<consumer>') }))
|
||||
}
|
||||
const diagnostics = compile(source)
|
||||
const historicalFailure = plugin.label === 'published-1.1.0' && mode === 'nodenext-esm'
|
||||
assert.deepEqual(diagnostics.map(item => item.code), historicalFailure ? [2349, 2349, 2322, 2344] : [], `${plugin.label} ${compiler.version} ${mode}`)
|
||||
const invalid = plugin.label === 'candidate' ? compile(source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')) : []
|
||||
if (plugin.label === 'candidate')
|
||||
assert.equal(invalid.length, 10, 'Installed declarations must reject all invalid uses')
|
||||
matrix.push({ plugin: plugin.label, compiler: compiler.version, mode, historicalFailure, diagnostics, invalid })
|
||||
if (plugin.label === 'candidate') {
|
||||
assert.equal(invalid.length, 15, 'Installed declarations must reject all invalid uses')
|
||||
const expectedLines = []
|
||||
let line = 1
|
||||
for (const text of source.split('\n')) {
|
||||
if (text.startsWith('// @ts-expect-error'))
|
||||
expectedLines.push(line)
|
||||
else line++
|
||||
}
|
||||
assert.deepEqual(invalid.map(item => item.line).sort((a, b) => a - b), expectedLines, 'Each invalid statement must fail at its own line')
|
||||
}
|
||||
const published = plugin.label === 'candidate' ? compile(fs.readFileSync(path.join(workspace, 'refactor/fixtures/consumers/ambilight-published.ts'), 'utf8')) : diagnostics
|
||||
assert.deepEqual(published.map(item => item.code), (plugin.label === 'candidate' || plugin.label === 'published-1.1.0') && mode === 'nodenext-esm' ? [2349, 2349, 2322, 2344] : [], 'Preserve exact historical direct-consumer diagnostic codes')
|
||||
const namespace = plugin.label !== 'published-1.0.0' && mode === 'nodenext-esm'
|
||||
? compile(`import ambilight from 'artplayer-plugin-ambilight'; import type Artplayer from 'artplayer';
|
||||
const replacement: typeof ambilight.default = (_option) => (_art: Artplayer) => ({name: 'artplayerPluginAmbilight', start() {}, stop() {}});
|
||||
ambilight.default({}); void replacement;`)
|
||||
: []
|
||||
assert.deepEqual(namespace, [], 'Latest published NodeNext namespace remains valid')
|
||||
const olderRequire = mode !== 'bundler-esm' && mode !== 'nodenext-esm'
|
||||
? compile(`import ambilight = require('artplayer-plugin-ambilight'); ambilight({blur: '50px', opacity: 0.5, frequency: 10, zIndex: 9, duration: 0.3});`)
|
||||
: []
|
||||
assert.deepEqual(olderRequire.map(item => item.code), mode !== 'bundler-esm' && mode !== 'nodenext-esm' && plugin.label !== 'published-1.0.0' ? [2349] : [], 'Earlier export-assignment typing conflicts are retained and documented')
|
||||
matrix.push({ plugin: plugin.label, compiler: compiler.version, mode, historicalFailure, diagnostics, invalid, published, namespace, olderRequire })
|
||||
}
|
||||
}
|
||||
finally {
|
||||
removeConsumer(consumer)
|
||||
}
|
||||
}
|
||||
writeJson(path.join(output, 'report.json'), { suite: 'ambilight-isolated-package-types', introducedBy: 'PKG-AMBILIGHT-04', scope: 'Two actual published plugins and packed candidate installed outside workspace with packed candidate core; offline and frozen reinstall, exact bytes, compiler/default/import= contracts. Published 1.0.0 required option fields became optional in 1.1.0; candidate retains 1.1.0 extraction. This is not proxy, device or complete distribution acceptance.', packages, published: candidates.slice(0, 2).map(pkg => ({ label: pkg.label, archive: pkg.archive, sha256: pkg.sha256 })), matrix })
|
||||
writeJson(path.join(output, 'report.json'), { suite: 'ambilight-isolated-package-types', introducedBy: 'PKG-FACTORY-01', scope: 'Two actual published plugins and packed candidate installed outside workspace with packed candidate core; offline and frozen reinstall, exact bytes, full factory replacements and required root option; runtime optional calls and self alias. Published 1.1.0 NodeNext ESM namespace and four old direct-call diagnostics are retained. Published 1.0.0 export-assignment and required fields have documented migration. This is not proxy, device or complete distribution acceptance.', packages, published: candidates.slice(0, 2).map(pkg => ({ label: pkg.label, archive: pkg.archive, sha256: pkg.sha256 })), matrix })
|
||||
console.log(`Ambilight installed types passed: ${matrix.length} cases; ${output}`)
|
||||
}
|
||||
|
||||
|
||||
@@ -14,14 +14,23 @@ import { readMember } from './releases.mjs'
|
||||
|
||||
const modes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']]
|
||||
|
||||
test('Ambilight current types retain inference, optional runtime arguments and ten invalid-use rejections', () => {
|
||||
function publicSource(source, mode) {
|
||||
return mode === 'nodenext-esm'
|
||||
? source.replace('import ambilight from \'artplayer-plugin-ambilight\'', 'import ambilightModule from \'artplayer-plugin-ambilight\'\nconst ambilight = ambilightModule.default')
|
||||
.replace('import legacy from \'artplayer-plugin-ambilight/legacy\'', 'import legacyModule from \'artplayer-plugin-ambilight/legacy\'\nconst legacy = legacyModule.default')
|
||||
: source
|
||||
}
|
||||
|
||||
test('Ambilight retains published factory replacements and separates accurate optional runtime calls', () => {
|
||||
const source = fs.readFileSync('test/types/ambilight.ts', 'utf8')
|
||||
for (const [compiler, mode] of modes) {
|
||||
assert.deepEqual(checkConsumer(compiler, mode, source), [], `${compiler.version} ${mode}`)
|
||||
assert.equal(checkConsumer(compiler, mode, source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')).length, 10)
|
||||
const adapted = publicSource(source, mode)
|
||||
assert.deepEqual(checkConsumer(compiler, mode, adapted), [], `${compiler.version} ${mode}`)
|
||||
assert.equal(checkConsumer(compiler, mode, adapted.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')).length, 15)
|
||||
}
|
||||
assert.deepEqual(checkConsumer(ts, 'nodenext-cjs', `import ambilight = require('artplayer-plugin-ambilight'); import legacy = require('artplayer-plugin-ambilight/legacy');
|
||||
const option: ambilight.Option = {}; ambilight(option); ambilight.default(option); legacy(); legacy.default(option);`), [])
|
||||
import runtime = require('artplayer-plugin-ambilight/runtime');
|
||||
const option: ambilight.Option = {}; ambilight.default(option); legacy.default(option); runtime(); runtime.default(option);`), [])
|
||||
})
|
||||
|
||||
test('Ambilight published calls compile against both exact historical declarations and candidate without new inference widening', async () => {
|
||||
@@ -53,7 +62,7 @@ test('Ambilight published calls compile against both exact historical declaratio
|
||||
}
|
||||
})
|
||||
|
||||
test('Ambilight editor globals are generated from the public namespace and retain negative checks', async () => {
|
||||
test('Ambilight editor globals retain the required root factory and negative checks', async () => {
|
||||
const source = fs.readFileSync('packages/artplayer-plugin-ambilight/types/artplayer-plugin-ambilight.d.ts', 'utf8')
|
||||
const generated = generatePluginEditorDeclaration(source, 'artplayerPluginAmbilight')
|
||||
const file = 'docs/assets/ts/artplayer-plugin-ambilight.d.ts'
|
||||
@@ -61,12 +70,17 @@ test('Ambilight editor globals are generated from the public namespace and retai
|
||||
assert.equal(formatted.errorCount, 0)
|
||||
assert.equal(fs.readFileSync(file, 'utf8').replaceAll('\r\n', '\n'), formatted.output || generated)
|
||||
const core = fs.readFileSync('docs/assets/ts/artplayer.d.ts', 'utf8')
|
||||
const consumer = `const option: artplayerPluginAmbilight.Option = {}; artplayerPluginAmbilight(); artplayerPluginAmbilight.default(option);
|
||||
const consumer = `const option: artplayerPluginAmbilight.Option = {}; artplayerPluginAmbilight(option);
|
||||
const replacement: typeof artplayerPluginAmbilight = (_option) => (_art) => ({name: 'artplayerPluginAmbilight', start() {}, stop() {}});
|
||||
// @ts-expect-error Required root argument.
|
||||
artplayerPluginAmbilight();
|
||||
// @ts-expect-error Optional invocation belongs to the runtime module.
|
||||
artplayerPluginAmbilight.default(option);
|
||||
// @ts-expect-error Invalid blur.
|
||||
artplayerPluginAmbilight({ blur: 2 });`
|
||||
for (const compiler of [ts, compat]) {
|
||||
assert.deepEqual(checkPluginEditorDeclaration(generated, core, consumer, compiler), [])
|
||||
assert.equal(checkPluginEditorDeclaration(generated, core, consumer.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, ''), compiler).length, 1)
|
||||
assert.equal(checkPluginEditorDeclaration(generated, core, consumer.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, ''), compiler).length, 3)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import ts from 'typescript'
|
||||
import compat from 'typescript-compat'
|
||||
import { checkFiles, packedFiles } from '../../scripts/package-check.mjs'
|
||||
@@ -9,7 +10,46 @@ import { consumerDirectory, removeConsumer, run, workspace, writeJson } from '..
|
||||
import { verifyCanvasContract } from './canvas-contract.mjs'
|
||||
import { hash, readMember } from './releases.mjs'
|
||||
|
||||
export function canvasConsumerSource(source, mode) {
|
||||
if (mode === 'nodenext-esm') {
|
||||
source = source.replace('import canvas from \'artplayer-proxy-canvas\'', 'import canvasModule from \'artplayer-proxy-canvas\'\nconst canvas = canvasModule.default')
|
||||
.replace('import legacy from \'artplayer-proxy-canvas/legacy\'', 'import legacyModule from \'artplayer-proxy-canvas/legacy\'\nconst legacy = legacyModule.default')
|
||||
.replace('namespace.default', 'namespace.default.default')
|
||||
}
|
||||
if (mode.endsWith('-no-interop'))
|
||||
source = source.replace('import runtime from \'artplayer-proxy-canvas/runtime\'', 'import runtime = require(\'artplayer-proxy-canvas/runtime\')')
|
||||
return source
|
||||
}
|
||||
|
||||
export const canvasNamespaceConsumer = `import type Artplayer from 'artplayer';
|
||||
import canvasModule from 'artplayer-proxy-canvas';
|
||||
import * as canvasNamespace from 'artplayer-proxy-canvas';
|
||||
import legacyModule from 'artplayer-proxy-canvas/legacy';
|
||||
type Callback = (ctx: CanvasRenderingContext2D, video: HTMLVideoElement) => void;
|
||||
type Factory = (option?: Callback) => (art: Artplayer) => HTMLCanvasElement;
|
||||
const replacement: Factory = (_option?: Callback) => (_art: Artplayer) => document.createElement('canvas');
|
||||
canvasModule.default(); canvasNamespace.default.default(); legacyModule.default();
|
||||
const fromOld: typeof canvasModule = { default: replacement };
|
||||
const toOld: { default: Factory } = canvasModule;
|
||||
const namespaceReplacement: typeof canvasNamespace.default = { default: replacement };
|
||||
const oldNamespace: { default: Factory } = canvasNamespace.default;
|
||||
const optional: Parameters<typeof canvasModule.default>[0] = undefined;
|
||||
const result: ReturnType<ReturnType<typeof canvasModule.default>> = document.createElement('canvas');
|
||||
void [fromOld, toOld, namespaceReplacement, oldNamespace, optional, result];`
|
||||
|
||||
export function checkInvalidCanvasStatements(source, diagnostics) {
|
||||
const expected = []
|
||||
let line = 1
|
||||
for (const text of source.split('\n')) {
|
||||
if (text.startsWith('// @ts-expect-error'))
|
||||
expected.push(line)
|
||||
else line++
|
||||
}
|
||||
assert.deepEqual(diagnostics.map(item => item.line).sort((a, b) => a - b), expected, 'Each invalid Canvas statement must fail at its own line')
|
||||
}
|
||||
|
||||
async function main() {
|
||||
assert.equal(process.version.slice(1), fs.readFileSync(path.join(workspace, '.node-version'), 'utf8').trim(), 'Use the repository Node version')
|
||||
assert.equal(process.env.npm_config_user_agent?.split(' ')[0], 'yarn/1.22.22', 'Use yarn test:canvas-types-package')
|
||||
const yarn = process.env.npm_execpath
|
||||
assert(yarn && fs.existsSync(yarn))
|
||||
@@ -50,8 +90,9 @@ async function main() {
|
||||
const runtimeFile = path.join(consumer, 'exports.cjs')
|
||||
fs.writeFileSync(runtimeFile, plugin.label === 'candidate'
|
||||
? `const assert = require('node:assert/strict');
|
||||
for (const name of ['artplayer-proxy-canvas', 'artplayer-proxy-canvas/legacy']) { const factory = require(name); assert.equal(typeof factory, 'function'); assert.equal(factory.default, factory); assert.equal(typeof factory(), 'function'); }
|
||||
import('artplayer-proxy-canvas').then(module => { assert.deepEqual(Object.keys(module), ['default']); assert.equal(module.default.default, module.default); }).catch(error => { console.error(error); process.exitCode = 1; });`
|
||||
for (const name of ['artplayer-proxy-canvas', 'artplayer-proxy-canvas/legacy', 'artplayer-proxy-canvas/runtime']) { const factory = require(name); assert.equal(typeof factory, 'function'); assert.equal(factory.default, factory); assert.equal(typeof factory(), 'function'); }
|
||||
assert.equal(require('artplayer-proxy-canvas/runtime'), require('artplayer-proxy-canvas'));
|
||||
Promise.all([import('artplayer-proxy-canvas'), import('artplayer-proxy-canvas/runtime')]).then(([module, runtime]) => { assert.deepEqual(Object.keys(module), ['default']); assert.equal(module.default.default, module.default); assert.equal(runtime.default, module.default); }).catch(error => { console.error(error); process.exitCode = 1; });`
|
||||
: `const assert = require('node:assert/strict'); const value = require('artplayer-proxy-canvas'); assert.equal(typeof value, '${plugin.version === '1.0.0' ? 'object' : 'function'}'); assert.equal(typeof (value.default || value)(() => {}), 'function');`)
|
||||
fs.writeFileSync(path.join(output, `${plugin.label}-runtime.log`), run([runtimeFile], consumer))
|
||||
const modes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']]
|
||||
@@ -60,14 +101,19 @@ import('artplayer-proxy-canvas').then(module => { assert.deepEqual(Object.keys(m
|
||||
for (const [compiler, mode] of modes) {
|
||||
const next = mode.startsWith('nodenext')
|
||||
const extension = next ? mode.endsWith('-cjs') ? 'cts' : 'mts' : 'ts'
|
||||
const source = fs.readFileSync(path.join(workspace, plugin.label === 'candidate' ? 'test/types/canvas.ts' : 'refactor/fixtures/consumers/canvas-published.ts'), 'utf8')
|
||||
const fixture = fs.readFileSync(path.join(workspace, plugin.label === 'candidate' ? 'test/types/canvas.ts' : 'refactor/fixtures/consumers/canvas-published.ts'), 'utf8')
|
||||
const source = plugin.label === 'candidate' ? canvasConsumerSource(fixture, mode) : fixture
|
||||
const filename = path.join(consumer, `consumer.${extension}`)
|
||||
const historical = path.join(consumer, `historical.${extension}`)
|
||||
fs.copyFileSync(path.join(workspace, 'refactor/fixtures/consumers/canvas-published.ts'), historical)
|
||||
fs.writeFileSync(historical, canvasConsumerSource(fs.readFileSync(path.join(workspace, 'refactor/fixtures/consumers/canvas-published.ts'), 'utf8'), mode))
|
||||
const files = plugin.label === 'candidate' ? [filename, historical] : [filename]
|
||||
if (mode === 'nodenext-cjs' && plugin.label !== 'published-1.1.0') {
|
||||
const commonjs = path.join(consumer, 'commonjs.cts')
|
||||
fs.writeFileSync(commonjs, `import canvas = require('artplayer-proxy-canvas'); canvas((ctx, video) => ctx.drawImage(video, 0, 0)); ${plugin.label === 'candidate' ? 'canvas.default();' : ''}`)
|
||||
if ((mode === 'nodenext-cjs' && plugin.label === 'published-1.0.0') || (plugin.label === 'candidate' && mode !== 'nodenext-esm' && mode !== 'bundler-esm')) {
|
||||
const commonjs = path.join(consumer, `commonjs.${extension}`)
|
||||
fs.writeFileSync(commonjs, plugin.label === 'candidate'
|
||||
? `import canvas = require('artplayer-proxy-canvas'); import legacy = require('artplayer-proxy-canvas/legacy'); import runtime = require('artplayer-proxy-canvas/runtime');
|
||||
canvas.default(); legacy.default(); runtime(); runtime.default();
|
||||
const callback: runtime.Option = (ctx, video) => ctx.drawImage(video, 0, 0); runtime(callback);`
|
||||
: `import canvas = require('artplayer-proxy-canvas'); canvas((ctx, video) => ctx.drawImage(video, 0, 0));`)
|
||||
files.push(commonjs)
|
||||
}
|
||||
const options = { strict: true, noEmit: true, skipLibCheck: false, types: [], esModuleInterop: !mode.endsWith('-no-interop'), target: compiler.ScriptTarget.ES2020, lib: ['lib.es2020.d.ts', 'lib.dom.d.ts'], module: next ? compiler.ModuleKind.NodeNext : mode === 'bundler-esm' ? compiler.ModuleKind.ESNext : compiler.ModuleKind.CommonJS, moduleResolution: next ? compiler.ModuleResolutionKind.NodeNext : mode === 'bundler-esm' ? compiler.ModuleResolutionKind.Bundler : compiler.ModuleResolutionKind.NodeJs }
|
||||
@@ -78,7 +124,7 @@ import('artplayer-proxy-canvas').then(module => { assert.deepEqual(Object.keys(m
|
||||
const actual = fs.realpathSync(file.fileName)
|
||||
assert(actual.startsWith(fs.realpathSync(consumer) + path.sep) || (program.isSourceFileDefaultLibrary(file) && path.dirname(actual) === fs.realpathSync(path.dirname(compiler.sys.getExecutingFilePath()))), `Types escaped installed consumer: ${actual}`)
|
||||
}
|
||||
return compiler.getPreEmitDiagnostics(program).map(item => ({ code: item.code, message: compiler.flattenDiagnosticMessageText(item.messageText, '\n') }))
|
||||
return compiler.getPreEmitDiagnostics(program).map(item => ({ file: item.file && path.relative(consumer, item.file.fileName), line: item.file && item.start !== undefined ? item.file.getLineAndCharacterOfPosition(item.start).line + 1 : null, code: item.code, message: compiler.flattenDiagnosticMessageText(item.messageText, '\n') }))
|
||||
}
|
||||
const diagnostics = compile(source)
|
||||
const historicalFailure = plugin.label === 'published-1.1.0' && mode === 'nodenext-esm'
|
||||
@@ -86,19 +132,28 @@ import('artplayer-proxy-canvas').then(module => { assert.deepEqual(Object.keys(m
|
||||
assert.deepEqual(diagnostics.map(item => item.code), historicalFailure ? [2349, 2349, 2322, 2344, 2344] : [], `${plugin.label} ${compiler.version} ${mode}`)
|
||||
const invalid = plugin.label === 'candidate' ? compile(source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')) : []
|
||||
if (plugin.label === 'candidate')
|
||||
assert.equal(invalid.length, 11, 'Installed declarations must reject all invalid uses')
|
||||
matrix.push({ plugin: plugin.label, compiler: compiler.version, mode, historicalFailure, diagnostics, invalid })
|
||||
checkInvalidCanvasStatements(source, invalid)
|
||||
const namespace = mode === 'nodenext-esm' && plugin.label !== 'published-1.0.0' ? compile(canvasNamespaceConsumer) : []
|
||||
assert.deepEqual(namespace, [], 'Latest published and candidate NodeNext ESM namespace shapes must accept plain module replacements')
|
||||
const exportAssignment = mode.includes('commonjs') || mode === 'nodenext-cjs'
|
||||
? compile(`import canvas = require('artplayer-proxy-canvas'); canvas((context: CanvasRenderingContext2D, video: HTMLVideoElement) => context.drawImage(video, 0, 0));`)
|
||||
: null
|
||||
if (exportAssignment)
|
||||
assert.deepEqual(exportAssignment.map(item => item.code), plugin.label === 'published-1.0.0' ? [] : [2349], 'Preserve the approved 1.0 export= versus latest default-export distinction')
|
||||
matrix.push({ plugin: plugin.label, compiler: compiler.version, mode, historicalFailure, diagnostics, invalid, namespace, exportAssignment })
|
||||
}
|
||||
}
|
||||
finally {
|
||||
removeConsumer(consumer)
|
||||
}
|
||||
}
|
||||
writeJson(path.join(output, 'report.json'), { suite: 'canvas-isolated-package-types', introducedBy: 'PKG-CANVAS-04', scope: 'Two actual published proxies and packed candidate installed outside workspace with packed candidate core; offline and frozen reinstall, exact bytes, compiler/default/import= contracts. Published 1.0.0 required callback became optional in 1.1.0; candidate retains optional Parameters and exact HTMLCanvasElement return inference. This is not native proxy media, device or complete distribution acceptance.', packages, published: candidates.slice(0, 2).map(pkg => ({ label: pkg.label, archive: pkg.archive, sha256: pkg.sha256 })), matrix })
|
||||
writeJson(path.join(output, 'report.json'), { suite: 'canvas-isolated-package-types', introducedBy: 'PKG-CANVAS-04', updatedBy: 'PKG-FACTORY-01', node: process.version, scope: 'Two actual published proxies and packed candidate installed outside workspace with packed candidate core; offline and frozen reinstall, exact bytes and root/legacy/runtime contracts. Root preserves published 1.1.0 pure factory, optional Parameters, exact HTMLCanvasElement result and NodeNext ESM namespace shape. Runtime adds accurate self identity with CommonJS/ESM and old Node10 resolution. Published 1.0.0 required callback/export= differences and 1.1.0 direct NodeNext ESM errors remain recorded. No native media, device or complete distribution acceptance.', packages, published: candidates.slice(0, 2).map(pkg => ({ label: pkg.label, archive: pkg.archive, sha256: pkg.sha256 })), matrix })
|
||||
console.log(`Canvas installed types passed: ${matrix.length} cases; ${output}`)
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error)
|
||||
process.exitCode = 1
|
||||
})
|
||||
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
||||
main().catch((error) => {
|
||||
console.error(error)
|
||||
process.exitCode = 1
|
||||
})
|
||||
}
|
||||
@@ -10,18 +10,21 @@ import compat from 'typescript-compat'
|
||||
import { checkPluginEditorDeclaration, generatePluginEditorDeclaration } from '../../scripts/plugin-editor-types.mjs'
|
||||
import { checkConsumer } from '../../scripts/typecheck.mjs'
|
||||
import { verifyCanvasContract } from './canvas-contract.mjs'
|
||||
import { canvasConsumerSource, canvasNamespaceConsumer, checkInvalidCanvasStatements } from './canvas-package-types.mjs'
|
||||
import { readMember } from './releases.mjs'
|
||||
|
||||
const modes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']]
|
||||
|
||||
test('Canvas current types preserve optional Parameters and exact Canvas result, while rejecting eleven invalid uses', () => {
|
||||
test('Canvas types preserve the latest plain factory and expose accurate runtime identity separately', () => {
|
||||
const source = fs.readFileSync('test/types/canvas.ts', 'utf8')
|
||||
for (const [compiler, mode] of modes) {
|
||||
assert.deepEqual(checkConsumer(compiler, mode, source), [], `${compiler.version} ${mode}`)
|
||||
assert.equal(checkConsumer(compiler, mode, source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')).length, 11)
|
||||
const consumer = canvasConsumerSource(source, mode)
|
||||
assert.deepEqual(checkConsumer(compiler, mode, consumer), [], `${compiler.version} ${mode}`)
|
||||
checkInvalidCanvasStatements(consumer, checkConsumer(compiler, mode, consumer.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')))
|
||||
}
|
||||
assert.deepEqual(checkConsumer(ts, 'nodenext-cjs', `import canvas = require('artplayer-proxy-canvas'); import legacy = require('artplayer-proxy-canvas/legacy');
|
||||
const callback: canvas.Option = (ctx, video) => ctx.drawImage(video, 0, 0); canvas(callback); canvas.default(); legacy(); legacy.default(callback);`), [])
|
||||
assert.deepEqual(checkConsumer(ts, 'nodenext-cjs', `import canvas = require('artplayer-proxy-canvas'); import legacy = require('artplayer-proxy-canvas/legacy'); import runtime = require('artplayer-proxy-canvas/runtime');
|
||||
const callback: canvas.Option = (ctx, video) => ctx.drawImage(video, 0, 0); canvas.default(callback); legacy.default(); runtime(); runtime.default(callback);`), [])
|
||||
assert.deepEqual(checkConsumer(ts, 'nodenext-esm', canvasNamespaceConsumer), [])
|
||||
})
|
||||
|
||||
test('Canvas published declarations distinguish required 1.0.0 and optional 1.1.0 callbacks without changing Canvas return assignability', async () => {
|
||||
@@ -48,11 +51,17 @@ test('Canvas published declarations distinguish required 1.0.0 and optional 1.1.
|
||||
assert.deepEqual(historical(source), [])
|
||||
assert.deepEqual(historical(`${source}\n${optional}`), version === '1.0.0' ? [2322, 2554] : [])
|
||||
assert.deepEqual(checkConsumer(compiler, 'node10-commonjs', `${source}\n${optional}`), [])
|
||||
if (version === '1.1.0') {
|
||||
assert.deepEqual(historical(`${source}
|
||||
type Factory = (option?: (ctx: CanvasRenderingContext2D, video: HTMLVideoElement) => void) => (art: Artplayer) => HTMLCanvasElement;
|
||||
const replacement: Factory = () => () => document.createElement('canvas');
|
||||
const fromOld: typeof canvas = replacement; const toOld: Factory = canvas; void [fromOld, toOld];`), [])
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
test('Canvas editor globals are generated from public types with optional callback, media view and negative checking', async () => {
|
||||
test('Canvas editor globals preserve the plain root factory with an explicit accurate runtime type view', async () => {
|
||||
const source = fs.readFileSync('packages/artplayer-proxy-canvas/types/artplayer-proxy-canvas.d.ts', 'utf8')
|
||||
const generated = generatePluginEditorDeclaration(source, 'artplayerProxyCanvas')
|
||||
const file = 'docs/assets/ts/artplayer-proxy-canvas.d.ts'
|
||||
@@ -60,14 +69,19 @@ test('Canvas editor globals are generated from public types with optional callba
|
||||
assert.equal(formatted.errorCount, 0)
|
||||
assert.equal(fs.readFileSync(file, 'utf8').replaceAll('\r\n', '\n'), formatted.output || generated)
|
||||
const core = fs.readFileSync('docs/assets/ts/artplayer.d.ts', 'utf8')
|
||||
const consumer = `artplayerProxyCanvas(); artplayerProxyCanvas.default((ctx, video) => ctx.drawImage(video, 0, 0));
|
||||
const consumer = `artplayerProxyCanvas(); artplayerProxyCanvas((ctx, video) => ctx.drawImage(video, 0, 0));
|
||||
const replacement: typeof artplayerProxyCanvas = (_option?: artplayerProxyCanvas.Option) => (_art: Artplayer) => document.createElement('canvas');
|
||||
const precise = artplayerProxyCanvas as artplayerProxyCanvas.RuntimeFactory; precise.default();
|
||||
const result: artplayerProxyCanvas.Result = document.createElement('canvas');
|
||||
const media = result as artplayerProxyCanvas.MediaCanvas; media.play();
|
||||
void replacement;
|
||||
// @ts-expect-error Root factory does not require or expose a self alias.
|
||||
artplayerProxyCanvas.default();
|
||||
// @ts-expect-error Callback must be a function in public types.
|
||||
artplayerProxyCanvas(42);`
|
||||
for (const compiler of [ts, compat]) {
|
||||
assert.deepEqual(checkPluginEditorDeclaration(generated, core, consumer, compiler), [])
|
||||
assert.equal(checkPluginEditorDeclaration(generated, core, consumer.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, ''), compiler).length, 1)
|
||||
assert.equal(checkPluginEditorDeclaration(generated, core, consumer.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, ''), compiler).length, 2)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
+8
-4
@@ -2718,7 +2718,8 @@
|
||||
"changes/2026-09-13-PKG-ASR-05-local-core-checkpoint.md",
|
||||
"baselines/asr-combinations-validation.json",
|
||||
"changes/2026-09-13-PKG-ASR-05-cors-combinations.md",
|
||||
"baselines/asr-explicit-capture-validation.json"
|
||||
"baselines/asr-explicit-capture-validation.json",
|
||||
"changes/2026-09-13-PKG-ASR-05-editor-generation.md"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -3036,7 +3037,7 @@
|
||||
"CORE-11",
|
||||
"CORE-13"
|
||||
],
|
||||
"status": "todo",
|
||||
"status": "doing",
|
||||
"risk": "H",
|
||||
"deliverable": "SDK singleton/实例订阅/会话与控件职责分离",
|
||||
"acceptance": "结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过",
|
||||
@@ -4505,7 +4506,7 @@
|
||||
"PKG-AMBILIGHT-04",
|
||||
"PKG-DPIP-04"
|
||||
],
|
||||
"status": "doing",
|
||||
"status": "done",
|
||||
"risk": "H",
|
||||
"deliverable": "复现两包旧typeof工厂替换与新.default/可选重载冲突,修复公开类型并补安装消费者、产物和文档",
|
||||
"acceptance": "默认工厂的历史合法反向赋值继续通过;既有类型/运行时导出逐版本核对,不能只验证Parameters或直接调用",
|
||||
@@ -4513,7 +4514,10 @@
|
||||
"baselines/factory-assignment-gaps.json",
|
||||
"baselines/factory-compatibility-proposals.json",
|
||||
"factory-compatibility-decision.md",
|
||||
"changes/2026-09-12-PKG-FACTORY-01-decision.md"
|
||||
"changes/2026-09-12-PKG-FACTORY-01-decision.md",
|
||||
"type-compatibility-policy.md",
|
||||
"baselines/factory-compatibility-validation.json",
|
||||
"changes/2026-09-13-PKG-FACTORY-01-compatible-types.md"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
+13
-2
@@ -85,8 +85,19 @@ export function runTypechecks() {
|
||||
for (const fixture of ['test/types/public.ts', 'test/types/declaration-inputs.ts', 'test/types/declaration-legacy.ts', 'test/types/plugins-public.ts', 'test/types/playback-public.ts', 'test/types/chapter-options.ts', 'test/types/chapter-exports.ts', 'test/types/hls-control.ts', 'test/types/dash-control.ts', 'test/types/ads.ts', 'refactor/fixtures/consumers/ads-published.ts', 'refactor/fixtures/consumers/ads-workspace.ts', 'refactor/fixtures/consumers/dash-control-legacy.ts', 'test/types/audio-track.ts', 'test/types/language-value.ts', 'refactor/fixtures/consumers/language.ts', 'refactor/fixtures/consumers/legacy-plugin.ts']) {
|
||||
assert.deepEqual(checkConsumer(compiler, mode, fs.readFileSync(path.join(root, fixture), 'utf8')), [], `Consumer failed: TS ${compiler.version} ${mode} ${fixture}`)
|
||||
}
|
||||
for (const fixture of ['test/types/ambilight.ts', 'refactor/fixtures/consumers/ambilight-published.ts', 'test/types/canvas.ts', 'refactor/fixtures/consumers/canvas-published.ts', 'test/types/mediabunny.ts', 'refactor/fixtures/consumers/mediabunny-1.0.ts', 'refactor/fixtures/consumers/mediabunny-published.ts', 'test/types/dpip.ts', 'refactor/fixtures/consumers/dpip-published.ts'])
|
||||
assert.deepEqual(checkConsumer(compiler, mode, fs.readFileSync(path.join(root, fixture), 'utf8')), [], `Consumer failed: TS ${compiler.version} ${mode} ${fixture}`)
|
||||
for (const fixture of ['test/types/ambilight.ts', 'refactor/fixtures/consumers/ambilight-published.ts', 'test/types/canvas.ts', 'refactor/fixtures/consumers/canvas-published.ts', 'test/types/mediabunny.ts', 'refactor/fixtures/consumers/mediabunny-1.0.ts', 'refactor/fixtures/consumers/mediabunny-published.ts', 'test/types/dpip.ts', 'refactor/fixtures/consumers/dpip-published.ts']) {
|
||||
let source = fs.readFileSync(path.join(root, fixture), 'utf8')
|
||||
if (mode === 'nodenext-esm') {
|
||||
for (const [binding, name] of [['canvas', 'artplayer-proxy-canvas'], ['ambilight', 'artplayer-plugin-ambilight']]) {
|
||||
if (fixture.includes(binding)) {
|
||||
source = source.replace(`import ${binding} from '${name}'`, `import ${binding}Module from '${name}'\nconst ${binding} = ${binding}Module.default`)
|
||||
.replace(`import legacy from '${name}/legacy'`, `import legacyModule from '${name}/legacy'\nconst legacy = legacyModule.default`)
|
||||
.replace('namespace.default', 'namespace.default.default')
|
||||
}
|
||||
}
|
||||
}
|
||||
assert.deepEqual(checkConsumer(compiler, mode, source), [], `Consumer failed: TS ${compiler.version} ${mode} ${fixture}`)
|
||||
}
|
||||
if (mode === 'nodenext-cjs')
|
||||
assert.deepEqual(checkConsumer(compiler, mode, fs.readFileSync(path.join(root, 'test/types/commonjs.cts'), 'utf8')), [], 'CommonJS export assignment consumer failed')
|
||||
for (const fixture of ['test/types/iframe.ts', 'refactor/fixtures/consumers/iframe-workspace.ts'])
|
||||
|
||||
+48
-11
@@ -1,23 +1,59 @@
|
||||
import type { Factory, Option, Result } from 'artplayer-plugin-ambilight'
|
||||
import type { Factory, Option, Result, RuntimeFactory } from 'artplayer-plugin-ambilight'
|
||||
import type { RuntimeFactory as EntryFactory, Option as EntryOption, Result as EntryResult } from 'artplayer-plugin-ambilight/runtime'
|
||||
import Artplayer from 'artplayer'
|
||||
import ambilight from 'artplayer-plugin-ambilight'
|
||||
import legacy from 'artplayer-plugin-ambilight/legacy'
|
||||
import runtime from 'artplayer-plugin-ambilight/runtime'
|
||||
|
||||
type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? true : false
|
||||
type Assert<T extends true> = T
|
||||
interface PublishedOption {
|
||||
blur?: string
|
||||
opacity?: number
|
||||
frequency?: number
|
||||
zIndex?: number
|
||||
duration?: number
|
||||
}
|
||||
interface PublishedResult {
|
||||
name: 'artplayerPluginAmbilight'
|
||||
start: () => void
|
||||
stop: () => void
|
||||
}
|
||||
type PublishedFactory = (option: PublishedOption) => (art: Artplayer) => PublishedResult
|
||||
type RootFactory = Assert<Equal<typeof ambilight, PublishedFactory>>
|
||||
type RootArguments = Assert<Equal<Parameters<typeof ambilight>, [option: PublishedOption]>>
|
||||
type RootResult = Assert<Equal<ReturnType<ReturnType<typeof ambilight>>, PublishedResult>>
|
||||
type RuntimeArguments = Assert<Equal<Parameters<typeof runtime>, [option?: Option]>>
|
||||
type RuntimeTypes = Assert<Equal<[EntryOption, EntryResult, EntryFactory], [Option, Result, RuntimeFactory]>>
|
||||
|
||||
const replacement: PublishedFactory = (_option: PublishedOption) => (_art: Artplayer) => ({ name: 'artplayerPluginAmbilight', start() {}, stop() {} })
|
||||
const assignToOld: PublishedFactory = ambilight
|
||||
const assignFromOld: typeof ambilight = replacement
|
||||
const option: Option = { blur: '40px', opacity: 0.6, frequency: 10, duration: 0.2, zIndex: 3 }
|
||||
const art = new Artplayer({ container: '#player', url: 'video.mp4', plugins: [ambilight(), ambilight(option), legacy({})] })
|
||||
ambilight(undefined)
|
||||
ambilight.default(option)
|
||||
legacy.default()
|
||||
const art = new Artplayer({ container: '#player', url: 'video.mp4', plugins: [ambilight(option), legacy({}), runtime()] })
|
||||
runtime(undefined)
|
||||
runtime.default(option)
|
||||
runtime.default.default()
|
||||
declare const optional: Option | undefined
|
||||
ambilight(optional)
|
||||
runtime(optional)
|
||||
const inferred: Parameters<typeof ambilight>[0] = option
|
||||
const blur: string | undefined = inferred.blur
|
||||
const factory: Factory = ambilight
|
||||
const factory: Factory = replacement
|
||||
const result: Result = factory(option)(art)
|
||||
const name: 'artplayerPluginAmbilight' = result.name
|
||||
const calls: void[] = [result.start(), result.stop()]
|
||||
const registration: (art: Artplayer) => Result = ambilight()
|
||||
// @ts-expect-error Keep required last overload for existing Parameters extraction.
|
||||
const registration: (art: Artplayer) => Result = runtime()
|
||||
// @ts-expect-error Published 1.1.0 requires the options argument.
|
||||
ambilight()
|
||||
// @ts-expect-error Published 1.1.0 does not permit undefined as its argument.
|
||||
ambilight(undefined)
|
||||
// @ts-expect-error Optional input belongs to the accurate runtime entry.
|
||||
ambilight(optional)
|
||||
// @ts-expect-error Historical replacement factories must not require a self alias.
|
||||
ambilight.default(option)
|
||||
// @ts-expect-error Legacy retains the root required-argument type.
|
||||
legacy()
|
||||
// @ts-expect-error Root Parameters retains the required options argument.
|
||||
const missing: Parameters<typeof ambilight>[0] = undefined
|
||||
// @ts-expect-error Blur is a CSS string, not a number.
|
||||
ambilight({ blur: 50 })
|
||||
@@ -32,9 +68,10 @@ ambilight({ zIndex: '9' })
|
||||
// @ts-expect-error Null was never accepted by the public types.
|
||||
ambilight(null)
|
||||
// @ts-expect-error Unknown options do not silently enter the contract.
|
||||
ambilight({ speed: 10 })
|
||||
runtime({ speed: 10 })
|
||||
// @ts-expect-error Methods are synchronous.
|
||||
const promise: Promise<void> = result.start()
|
||||
// @ts-expect-error No public plugin destroy method is introduced.
|
||||
result.destroy()
|
||||
void [blur, name, calls, registration, missing, promise]
|
||||
void [assignToOld, assignFromOld, blur, name, calls, registration, missing, promise]
|
||||
export type { RootArguments, RootFactory, RootResult, RuntimeArguments, RuntimeTypes }
|
||||
+28
-7
@@ -1,12 +1,26 @@
|
||||
import type { Factory, MediaCanvas, Option, Result } from 'artplayer-proxy-canvas'
|
||||
import type { Factory, Option, Result, RuntimeFactory } from 'artplayer-proxy-canvas'
|
||||
import type { MediaCanvas, RuntimeFactory as RuntimeEntry, Factory as RuntimeEntryFactory, Option as RuntimeOption, Result as RuntimeResult } from 'artplayer-proxy-canvas/runtime'
|
||||
import Artplayer from 'artplayer'
|
||||
import canvas from 'artplayer-proxy-canvas'
|
||||
import * as namespace from 'artplayer-proxy-canvas'
|
||||
import legacy from 'artplayer-proxy-canvas/legacy'
|
||||
import runtime from 'artplayer-proxy-canvas/runtime'
|
||||
|
||||
type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? true : false
|
||||
type Assert<T extends true> = T
|
||||
type OptionalInput = Assert<Equal<Parameters<typeof canvas>, [option?: Option]>>
|
||||
type ExactResult = Assert<Equal<ReturnType<ReturnType<typeof canvas>>, HTMLCanvasElement>>
|
||||
type HistoricalFactory = (option?: (ctx: CanvasRenderingContext2D, video: HTMLVideoElement) => void) => (art: Artplayer) => HTMLCanvasElement
|
||||
type ExactFactory = Assert<Equal<typeof canvas, HistoricalFactory>>
|
||||
type RuntimeEntries = Assert<Equal<[RuntimeEntryFactory, RuntimeOption, RuntimeResult, RuntimeEntry], [Factory, Option, Result, RuntimeFactory]>>
|
||||
type ExactRuntime = Assert<Equal<typeof runtime, RuntimeFactory>>
|
||||
|
||||
const replacement: HistoricalFactory = (_option?: Option) => (_art: Artplayer) => document.createElement('canvas')
|
||||
const toOld: HistoricalFactory = canvas
|
||||
const fromOld: typeof canvas = replacement
|
||||
const namedFactory: Factory = replacement
|
||||
const namespaceFactory: HistoricalFactory = namespace.default
|
||||
const legacyFactory: typeof legacy = replacement
|
||||
|
||||
const option: Option = (context, video) => {
|
||||
context.drawImage(video, 0, 0)
|
||||
@@ -17,8 +31,9 @@ const option: Option = (context, video) => {
|
||||
const art = new Artplayer({ container: '#player', url: 'video.mp4', proxy: canvas(option) })
|
||||
canvas()
|
||||
canvas(undefined)
|
||||
canvas.default(option)
|
||||
legacy.default()
|
||||
runtime.default(option)
|
||||
runtime.default.default()
|
||||
legacy()
|
||||
declare const optional: Option | undefined
|
||||
canvas(optional)
|
||||
const missing: Parameters<typeof canvas>[0] = undefined
|
||||
@@ -51,9 +66,15 @@ const promise: Promise<HTMLCanvasElement> = canvas()(art)
|
||||
media.getContext('2d')?.play()
|
||||
// @ts-expect-error Native Canvas dimensions remain numeric.
|
||||
media.width = '320'
|
||||
// @ts-expect-error Self default identity is readonly in declarations.
|
||||
canvas.default = legacy
|
||||
// @ts-expect-error Historical root factory has no required or exposed self default member.
|
||||
canvas.default()
|
||||
// @ts-expect-error Legacy factory retains the same plain factory shape.
|
||||
legacy.default()
|
||||
// @ts-expect-error A precise runtime replacement must provide its self identity.
|
||||
const missingAlias: RuntimeFactory = replacement
|
||||
// @ts-expect-error Self default identity is readonly in precise declarations.
|
||||
runtime.default = runtime
|
||||
// @ts-expect-error Registration requires a host.
|
||||
canvas()()
|
||||
void [missing, originalResult, play, context, listen, width, video, promise]
|
||||
export type { ExactResult, OptionalInput }
|
||||
void [toOld, fromOld, namedFactory, namespaceFactory, legacyFactory, missing, originalResult, play, context, listen, width, video, promise, missingAlias]
|
||||
export type { ExactFactory, ExactResult, ExactRuntime, OptionalInput, RuntimeEntries }
|
||||
Reference in new issue
Block a user