fix(types): [PKG-FACTORY-01] preserve published factory compatibility

This commit is contained in:
Harvey Zhao committed 2026-09-13 16:53:27 +08:00
1 parent 1117107032
commit c4c7491e16
35 files changed
+670 -229

No files matched your search

@@ -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
+38 -5
View File
@@ -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'
@@ -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'
+10
View File
@@ -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