refactor(asr): [PKG-ASR-04] preserve old declarations and add precise runtime types

This commit is contained in:
Harvey Zhao committed 2026-09-13 15:07:04 +08:00
1 parent 0cf2af4840
commit 93208e59e1
24 files changed
+1224 -19

No files matched your search

+2 -1
View File
@@ -94,7 +94,8 @@
"check:ci": "node refactor/scripts/ci-workflow.mjs",
"test:ci": "node --test test/ci-summary.test.js refactor/scripts/ci-workflow.test.mjs refactor/scripts/impact.test.mjs test/package-runtime.test.js",
"test:package:runtime": "node scripts/package-runtime.mjs --canonical",
"test:asr": "node --test test/asr.test.js test/asr-audio.test.js test/asr-encoding.test.js test/asr-lifecycle.test.js test/asr-distribution.test.js"
"test:asr": "node --test test/asr.test.js test/asr-audio.test.js test/asr-encoding.test.js test/asr-lifecycle.test.js test/asr-distribution.test.js",
"test:asr-types-package": "node refactor/scripts/asr-package-types.mjs"
},
"browserslist": "last 1 Chrome version",
"devDependencies": {
+2 -1
View File
@@ -2,4 +2,5 @@ node_modules
.DS_Store
yarn.lock
package-lock.json
src
src
tsconfig.json
+19 -4
View File
@@ -63,10 +63,20 @@ HTML, punctuation splitting, replacement, ignored non-string values and the
historical `length=0` behavior. The CJS function has a non-enumerable self `default`
alias for consumers of the 2.0 namespace, while preserving the callable 2.1 form.
Root public declarations have not yet migrated: they describe stop as void and
the callback as void/Promise<void>. PKG-ASR-04 owns exact asynchronous public types,
old assignment consumers, module forms and installed tarball acceptance. Internal
strict TypeScript does not prove that public type migration complete.
Root and `/legacy` declarations intentionally retain the historical factory,
void stop and void/Promise<void> callback. Their NodeNext ESM default import remains
the old namespace type: changing it into a callable default would break legal
namespace substitution. Historical direct-call errors are frozen separately from
legal namespace consumers. Do not silently rewrite these declarations to match
runtime behavior or require a self-default property on old replacement factories.
The additive `/runtime` entry references the same main/ESM implementation, with
`RuntimeOption`, `RuntimeResult` and `RuntimeFactory`: callback text/null/no result
and asynchronous stop are accurate. Its `.d.mts` default export and `.d.cts`/
`.d.ts` export assignment support ESM, raw CommonJS and old Node10 resolution.
Pure type namespaces make all four named runtime types available in every mode.
The root also exports those types for convenient annotations. No runtime code or
version changes are needed for this entry.
From the repository root:
@@ -76,6 +86,11 @@ From the repository root:
published/current core, pause/resume, source switch, stop/restart and destroy.
Set `ARTPLAYER_ASR_ARTIFACT` to an actual main/legacy file to test that artifact.
- `yarn typecheck`: includes the package strict/noUncheckedIndexedAccess project.
- `yarn test:asr-types-package`: packs and installs core/ASR outside the workspace,
checks frozen 2.0/2.1 consumers and candidate declarations in strict compiler
modes, verifies offline frozen reinstall and file hashes, exercises actual
import/require exports, and rejects invalid uses. Historical declaration errors
are explicit expectations and never counted as candidate success.
- `yarn dev artplayer-plugin-asr` and `yarn build artplayer-plugin-asr`: normal
repository development and three-format production builds.
+29
View File
@@ -8,6 +8,35 @@ restart capture; the player's direct Web Audio connection remains alive until
the player is destroyed so that stopping recognition does not silence playback.
Call `art.destroy()` when disposing the player.
## TypeScript
Existing imports keep their historical types, including `stop(): void` and a
`void | Promise<void>` recognition callback. For accurate asynchronous types, use
the additive `/runtime` entry. It loads the same implementation as the root entry:
```ts
import Artplayer from 'artplayer'
import asr from 'artplayer-plugin-asr/runtime'
import type { RuntimeOption } from 'artplayer-plugin-asr/runtime'
const option: RuntimeOption = {
onAudioChunk: async ({ pcm, wav }) => {
// Send one of these buffers to your own recognition service.
void [pcm, wav]
return 'Recognized text.'
},
}
const art = new Artplayer({ container: '#player', url: '/video.mp4' })
const recognition = asr(option)(art)
await recognition.stop()
```
The callback may return text, `null`, or no value, synchronously or asynchronously.
CommonJS TypeScript can use `import asr = require('artplayer-plugin-asr/runtime')`.
The root declaration retains its historical NodeNext ESM namespace shape; use
`/runtime` for a directly callable default import in that mode. This is an opt-in
type entry, and does not require changes to existing JavaScript consumers.
## Maintenance
See [ARCHITECTURE.md](ARCHITECTURE.md) for the current audio flow, public behavior,
+21 -1
View File
@@ -27,11 +27,31 @@
"types": "./types/artplayer-plugin-asr.d.ts",
"import": "./dist/artplayer-plugin-asr.legacy.js",
"require": "./dist/artplayer-plugin-asr.legacy.js"
},
"./runtime": {
"import": {
"types": "./types/runtime.d.mts",
"default": "./dist/artplayer-plugin-asr.mjs"
},
"require": {
"types": "./types/runtime.d.cts",
"default": "./dist/artplayer-plugin-asr.js"
}
}
},
"main": "./dist/artplayer-plugin-asr.js",
"module": "./dist/artplayer-plugin-asr.mjs",
"types": "./types/artplayer-plugin-asr.d.ts",
"legacy": "./dist/artplayer-plugin-asr.legacy.js",
"browserslist": "last 1 Chrome version"
"browserslist": "last 1 Chrome version",
"typesVersions": {
"*": {
"legacy": [
"types/artplayer-plugin-asr.d.ts"
],
"runtime": [
"types/runtime.d.ts"
]
}
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": { "allowJs": false },
"include": ["src/**/*.ts", "types/**/*.d.ts", "../../types/assets.d.ts"]
"include": ["src/**/*.ts", "types/**/*.d.ts", "types/**/*.d.mts", "types/**/*.d.cts", "../../types/assets.d.ts"]
}
@@ -1,11 +1,11 @@
import type Artplayer from 'artplayer'
interface AudioChunk {
export interface AudioChunk {
pcm: ArrayBuffer
wav: ArrayBuffer
}
interface AsrPluginOption {
export interface AsrPluginOption {
length?: number
interval?: number
sampleRate?: number
@@ -13,13 +13,30 @@ interface AsrPluginOption {
onAudioChunk?: (chunk: AudioChunk) => void | Promise<void>
}
interface AsrPluginInstance {
export interface AsrPluginInstance {
name: 'artplayerPluginAsr'
stop: () => void
hide: () => void
append: (subtitle: string) => void
}
/** Historical factory shape, including void stop and callback results. */
export type Factory = (option?: AsrPluginOption) => (art: Artplayer) => AsrPluginInstance
/** Accurate asynchronous view available through the /runtime entry. */
export interface RuntimeOption extends Omit<AsrPluginOption, 'onAudioChunk'> {
onAudioChunk?: (chunk: AudioChunk) => string | void | null | Promise<string | void | null>
}
export interface RuntimeResult extends Omit<AsrPluginInstance, 'stop'> {
stop: () => Promise<void>
}
export interface RuntimeFactory {
(option?: RuntimeOption): (art: Artplayer) => RuntimeResult
readonly default: RuntimeFactory
}
declare function artplayerPluginAsr(option?: AsrPluginOption): (art: Artplayer) => AsrPluginInstance
export default artplayerPluginAsr
@@ -0,0 +1,11 @@
import type { RuntimeFactory } from './artplayer-plugin-asr.js'
declare const artplayerPluginAsr: RuntimeFactory
// eslint-disable-next-line ts/no-redeclare -- Ambient merge exposes types on the CommonJS factory.
declare namespace artplayerPluginAsr {
type AudioChunk = import('./artplayer-plugin-asr.js').AudioChunk
type RuntimeFactory = import('./artplayer-plugin-asr.js').RuntimeFactory
type RuntimeOption = import('./artplayer-plugin-asr.js').RuntimeOption
type RuntimeResult = import('./artplayer-plugin-asr.js').RuntimeResult
}
export = artplayerPluginAsr
@@ -0,0 +1,5 @@
import type { RuntimeFactory } from './artplayer-plugin-asr.js'
declare const artplayerPluginAsr: RuntimeFactory
export default artplayerPluginAsr
export type { AudioChunk, RuntimeFactory, RuntimeOption, RuntimeResult } from './artplayer-plugin-asr.js'
+11
View File
@@ -0,0 +1,11 @@
import type { RuntimeFactory } from './artplayer-plugin-asr.js'
declare const artplayerPluginAsr: RuntimeFactory
// eslint-disable-next-line ts/no-redeclare -- Ambient merge exposes types on the CommonJS factory.
declare namespace artplayerPluginAsr {
type AudioChunk = import('./artplayer-plugin-asr.js').AudioChunk
type RuntimeFactory = import('./artplayer-plugin-asr.js').RuntimeFactory
type RuntimeOption = import('./artplayer-plugin-asr.js').RuntimeOption
type RuntimeResult = import('./artplayer-plugin-asr.js').RuntimeResult
}
export = artplayerPluginAsr
@@ -0,0 +1,254 @@
{
"schemaVersion": 1,
"task": "PKG-ASR-04",
"sourceReport": "refactor/.cache/asr-package-types-3KzGBO/report.json",
"scope": "Observed strict installed consumers from frozen 2.0.0 and 2.1.0 archives with packed candidate core. Both old NodeNext ESM default imports are namespaces; direct-call diagnostics are retained, not counted as passing compatibility. Legal namespace consumers are verified separately.",
"cases": [
{
"plugin": "published-2.0.0",
"compiler": "5.9.3",
"mode": "node10-commonjs",
"diagnostics": []
},
{
"plugin": "published-2.0.0",
"compiler": "5.9.3",
"mode": "nodenext-cjs",
"diagnostics": []
},
{
"plugin": "published-2.0.0",
"compiler": "5.9.3",
"mode": "nodenext-esm",
"diagnostics": [
{
"file": "consumer.mts",
"line": 26,
"code": 2322,
"message": "Type '{ length: number; interval: number; sampleRate: number; autoHideTimeout: number; onAudioChunk(chunk: any): void; }' is not assignable to type 'never'."
},
{
"file": "consumer.mts",
"line": 26,
"code": 2344,
"message": "Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' does not satisfy the constraint '(...args: any) => any'.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' provides no match for the signature '(...args: any): any'."
},
{
"file": "consumer.mts",
"line": 31,
"code": 7006,
"message": "Parameter 'chunk' implicitly has an 'any' type."
},
{
"file": "consumer.mts",
"line": 37,
"code": 2322,
"message": "Type 'undefined' is not assignable to type 'never'."
},
{
"file": "consumer.mts",
"line": 37,
"code": 2344,
"message": "Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' does not satisfy the constraint '(...args: any) => any'.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' provides no match for the signature '(...args: any): any'."
},
{
"file": "consumer.mts",
"line": 39,
"code": 2344,
"message": "Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' does not satisfy the constraint '(...args: any) => any'.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' provides no match for the signature '(...args: any): any'."
},
{
"file": "consumer.mts",
"line": 46,
"code": 2322,
"message": "Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' is not assignable to type 'PublishedFactory'.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' provides no match for the signature '(option?: PublishedOption | undefined): (art: Artplayer) => PublishedResult'."
},
{
"file": "consumer.mts",
"line": 47,
"code": 2322,
"message": "Type 'PublishedFactory' is not assignable to type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")'."
},
{
"file": "consumer.mts",
"line": 48,
"code": 2322,
"message": "Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' is not assignable to type 'PublishedFactory'.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' provides no match for the signature '(option?: PublishedOption | undefined): (art: Artplayer) => PublishedResult'."
},
{
"file": "consumer.mts",
"line": 49,
"code": 2349,
"message": "This expression is not callable.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' has no call signatures."
},
{
"file": "consumer.mts",
"line": 49,
"code": 2349,
"message": "This expression is not callable.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' has no call signatures."
},
{
"file": "consumer.mts",
"line": 49,
"code": 2349,
"message": "This expression is not callable.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' has no call signatures."
},
{
"file": "consumer.mts",
"line": 49,
"code": 2349,
"message": "This expression is not callable.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' has no call signatures."
},
{
"file": "consumer.mts",
"line": 50,
"code": 2349,
"message": "This expression is not callable.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' has no call signatures."
},
{
"file": "consumer.mts",
"line": 54,
"code": 2339,
"message": "Property 'onAudioChunk' does not exist on type 'never'."
}
]
},
{
"plugin": "published-2.0.0",
"compiler": "5.9.3",
"mode": "bundler-esm",
"diagnostics": []
},
{
"plugin": "published-2.0.0",
"compiler": "4.3.5",
"mode": "node10-commonjs",
"diagnostics": []
},
{
"plugin": "published-2.1.0",
"compiler": "5.9.3",
"mode": "node10-commonjs",
"diagnostics": []
},
{
"plugin": "published-2.1.0",
"compiler": "5.9.3",
"mode": "nodenext-cjs",
"diagnostics": []
},
{
"plugin": "published-2.1.0",
"compiler": "5.9.3",
"mode": "nodenext-esm",
"diagnostics": [
{
"file": "consumer.mts",
"line": 26,
"code": 2322,
"message": "Type '{ length: number; interval: number; sampleRate: number; autoHideTimeout: number; onAudioChunk(chunk: any): void; }' is not assignable to type 'never'."
},
{
"file": "consumer.mts",
"line": 26,
"code": 2344,
"message": "Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' does not satisfy the constraint '(...args: any) => any'.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' provides no match for the signature '(...args: any): any'."
},
{
"file": "consumer.mts",
"line": 31,
"code": 7006,
"message": "Parameter 'chunk' implicitly has an 'any' type."
},
{
"file": "consumer.mts",
"line": 37,
"code": 2322,
"message": "Type 'undefined' is not assignable to type 'never'."
},
{
"file": "consumer.mts",
"line": 37,
"code": 2344,
"message": "Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' does not satisfy the constraint '(...args: any) => any'.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' provides no match for the signature '(...args: any): any'."
},
{
"file": "consumer.mts",
"line": 39,
"code": 2344,
"message": "Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' does not satisfy the constraint '(...args: any) => any'.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' provides no match for the signature '(...args: any): any'."
},
{
"file": "consumer.mts",
"line": 46,
"code": 2322,
"message": "Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' is not assignable to type 'PublishedFactory'.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' provides no match for the signature '(option?: PublishedOption | undefined): (art: Artplayer) => PublishedResult'."
},
{
"file": "consumer.mts",
"line": 47,
"code": 2322,
"message": "Type 'PublishedFactory' is not assignable to type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")'."
},
{
"file": "consumer.mts",
"line": 48,
"code": 2322,
"message": "Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' is not assignable to type 'PublishedFactory'.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' provides no match for the signature '(option?: PublishedOption | undefined): (art: Artplayer) => PublishedResult'."
},
{
"file": "consumer.mts",
"line": 49,
"code": 2349,
"message": "This expression is not callable.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' has no call signatures."
},
{
"file": "consumer.mts",
"line": 49,
"code": 2349,
"message": "This expression is not callable.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' has no call signatures."
},
{
"file": "consumer.mts",
"line": 49,
"code": 2349,
"message": "This expression is not callable.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' has no call signatures."
},
{
"file": "consumer.mts",
"line": 49,
"code": 2349,
"message": "This expression is not callable.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' has no call signatures."
},
{
"file": "consumer.mts",
"line": 50,
"code": 2349,
"message": "This expression is not callable.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-plugin-asr/types/artplayer-plugin-asr\")' has no call signatures."
},
{
"file": "consumer.mts",
"line": 54,
"code": 2339,
"message": "Property 'onAudioChunk' does not exist on type 'never'."
}
]
},
{
"plugin": "published-2.1.0",
"compiler": "5.9.3",
"mode": "bundler-esm",
"diagnostics": []
},
{
"plugin": "published-2.1.0",
"compiler": "4.3.5",
"mode": "node10-commonjs",
"diagnostics": []
}
],
"fixtures": {
"refactor/fixtures/consumers/asr-published.ts": "7611ab9ef9765fe6a6ca5112589228d49012400bf0b9d9cede96ead8e63cffc2",
"refactor/fixtures/consumers/asr-esm-namespace.ts": "1e3b7475452f43f71e4e038c17368860fe2bc00a61451397fe609e9a3e078d98"
}
}
@@ -0,0 +1,363 @@
{
"schemaVersion": 1,
"task": "PKG-ASR-04",
"capturedAt": "2026-09-13T07:06:14.836Z",
"baseCommit": "0cf2af48400908fbe4217259e7d9fd37c1e15d1f",
"runtimeChanged": false,
"dependenciesAdded": [],
"versionChanged": false,
"installation": {
"node": "v24.21.0",
"yarn": "1.22.22",
"outsideWorkspace": true,
"offline": true,
"frozenReinstall": true,
"installedHashesVerified": true,
"workspaceLinks": false,
"runtimeExportsExecuted": true,
"packages": [
{
"name": "artplayer",
"archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\asr-package-types-nlZPaL\\artplayer.tgz",
"sha256": "0c0ea52d41575261b836aefac68280733b119c75834ecad6d4bc78edb073f4c1",
"files": {
"package/ARCHITECTURE.md": "bee98fc9d93a3911d58e98ef15f2dbe07e12f2274c444d989094248a22079e46",
"package/README.md": "1e185e65f67fc2081b4182bbc6c4558ea05b6e3fa3acc5dd0c3ac7df1d542b09",
"package/THIRD_PARTY_NOTICES": "b77b5b46b79fa0cda850b0781f78c3b1112c4ed9672fa291956fe8eff53098e7",
"package/dist/artplayer.js": "6dc8ca6d6eb34aa8a056cbc31328efd634530b3c5c60f0014cb7ce719fabc335",
"package/dist/artplayer.legacy.js": "80f74a8a39df4baf64cc00c65d21cd57c011bfcdead0da38eea077fbc67c5c04",
"package/dist/artplayer.mjs": "432e2b0ad92009a09743e020bf7c928c8b8f18a52496622b16259ec9ab20059f",
"package/dist/i18n/ar.js": "bd0bef65f4f7dc3f8ed562904cc6fa9071ff4537fb64c1e0fc3d554141bcd3cb",
"package/dist/i18n/ar.mjs": "59310b7bbeea83bbb7d73aadc5ca82a6ec269c3530d19aa741c94d5c64fc5da8",
"package/dist/i18n/cs.js": "6e2c93daba24c2e48dfa206bcf4f2f2a8a02713b856e404d2e4e4ecd8309ca2f",
"package/dist/i18n/cs.mjs": "c73fba18797d62d7b2581e67fe7340c35fe84510b2250ff1ecaad253d9cabbac",
"package/dist/i18n/es.js": "f4133ac6ea06b69a474b33c4a1809a4066e2376bb9464a3f3aa8330f01f1944a",
"package/dist/i18n/es.mjs": "1390bca3def2be115e811de337df0a0c3f63998e51359f283023246245c552a7",
"package/dist/i18n/fa.js": "8d3ef2e0f0cc0ea480c2167b6fcb87cdf444e90759c3519e8daa139fec576da4",
"package/dist/i18n/fa.mjs": "ab441e8a2d79043b29ceca87c783bf06874bb9d3a149c1604bc167eb481be2a9",
"package/dist/i18n/fr.js": "89dc4194a6c0a962b9ce756b52c90401c1acf3b159f265f3bc0d7d6d7d987f03",
"package/dist/i18n/fr.mjs": "99491878430fb7b6f2b7bf93da0588a831704b412e01116f23ba1d9d45863816",
"package/dist/i18n/id.js": "4c6dc86db741bcc2857df55a9d037e82e91d375fa6fd22e0562809ff621b92db",
"package/dist/i18n/id.mjs": "383e5803a6b6686c6de4e867770241de75b6f517dcca2bdedc6f2004bf6e1fb3",
"package/dist/i18n/pl.js": "3a38828b1c3d719c0aca9906cf6a46c23555a8411f8d57645f0e0ce8147dc664",
"package/dist/i18n/pl.mjs": "079ed96732979035297af387c64a7b99519b5a43107fb86735a654ea4fe8e9b5",
"package/dist/i18n/ru.js": "62fac9dddb302eb16382e6e9301fc4b2c66f5f69d4a358a26b67026a5fa4e787",
"package/dist/i18n/ru.mjs": "a893aeaee1d79409db72e324fbff200e3864f33ed168f04d91d25f7cf2efa765",
"package/dist/i18n/tr.js": "1492c36b069a7d73db4f3913bd18085faff7afd8b3c158138fe264e31600e38c",
"package/dist/i18n/tr.mjs": "a90478cac078836b76c2a98261907610fee07f3e4b28671b8e7dbf533778f918",
"package/dist/i18n/vi.js": "c7216ef7e608a79b7e2c479a07bf310d4ab79577947ac4a62d676faaced0e9cf",
"package/dist/i18n/vi.mjs": "66270fdfa0b5da396322d1769169b7df37a030ba15ac18eb30bd87b3af8074ec",
"package/dist/i18n/zh-tw.js": "11c49857d6d40a5ffd224ffa59c9026e71621b345f63c5a086326db80a22371a",
"package/dist/i18n/zh-tw.mjs": "a38e630cb96de96c15d824686aec750e25cf6c8ada01285a292442a81566314f",
"package/package.json": "82e92699e7c29847f32ac6767ece0401290e7d7a12854722bdbcfd09b90b7954",
"package/types/COMPATIBILITY.md": "a7da9e102a116104b513df4f7451247b7759bc502d3375f30bd5b14a541826e0",
"package/types/README.md": "1bd9868e773610e6312fd48a1c5aa20772d29df0af40c3db66e75b7ab4a1aee0",
"package/types/artplayer.d.cts": "3685065e4263abcfd9d953176e461d5b3eb990b0363b54689a7f55deba770fef",
"package/types/artplayer.d.mts": "b664fec51ee5c5381f2ceab25d99d52aee56feab04a2ee7b4ea5b2145ea5fa0e",
"package/types/artplayer.d.ts": "5d0162adf7ac6d0f2bb9b5cfe361f28657c4d9c540cadbf36eba3cc954ce50f8",
"package/types/component.d.ts": "ae5bc1ddeaa1ee1ebbc62573e32423993727079205c64751a5a314170e588a32",
"package/types/config.d.ts": "36f9a84cd436f17701a0d11d3417fc1fe0414e0c52c80083b94a85dfc329e37e",
"package/types/cssVar.d.ts": "93217860fff2ebf750f606d95ddd22220a58837119e63bdf2f5f78fd7488458b",
"package/types/emitter.d.ts": "7e1f9a2a8ce65feb95734f1ed52d1334e57aa388bbef2a43d8bc53a9b6f6623c",
"package/types/events.d.ts": "f83624c9268a284477e8bd793fe3562a2363b72f2a44d5c64fe8c6a6cede8830",
"package/types/i18n.d.ts": "f4f2f05bdfda73c74fef34767cb18ee8e11b743120b1c4598e9714d7846018e5",
"package/types/icons.d.ts": "326a67ba7bdcf52862c5b2a269e540140aff98d05c7b96196485a77f84fe5960",
"package/types/language.d.cts": "f7928e108cf7b7814c2d75cb0a525333e95873fca6cd6d89a740450656b9c1cd",
"package/types/language.d.mts": "6619a26d604b7cccbe2f596243d87ae37cfaae33b8b52874ec071d9787446f9e",
"package/types/legacy/language.d.ts": "c7abda4d506fd933c350e9030290bf9e64347a921d683a66115da1995ebe0a81",
"package/types/option.d.ts": "ccf2c8ddc537dc65c9682d39f044726e23bff18f476b622512d7e6a4b949b5f6",
"package/types/playback.d.ts": "58705c273df6e76a77a6575e769a9ec6c013785c25080f2551aa22415788d870",
"package/types/player.d.ts": "92b00211b70ac71336e495899df972a0519dd138111846044fe4e0f967966177",
"package/types/plugin.d.ts": "fc3594a41c51d8248713e33c486fb1f6a518ac5c7ce31be6988a6cd8d052c5f3",
"package/types/quality.d.ts": "1ac1a48b4f74ff9ba1ce60443e1463d50d1c6a2e5d87d63dc7d2a8eb7f4c2c34",
"package/types/runtime.d.cts": "7ee988dfea9a0ac71a34a656f96f7b74e78aa92547cfdfab7ff7e1139a77d4a3",
"package/types/runtime.d.mts": "dcb85f59a8b7714918ced0c1d18c2bb24e8b80b56d8c594e019dcbdb5ad88b08",
"package/types/runtime.d.ts": "9c6ccd39a1be293f17ae46dae8ea077053e12635ee79d73a5e3b3a559230fcff",
"package/types/runtime/component.d.ts": "1b4c0cee44568649e80140fe778febfcae88af0ac85515f1cbeff78ab0c6d14f",
"package/types/runtime/construction.d.ts": "7a87bedf6f7ffff0c46078aa5f1551e0077d35574e286516121f5f935e2d9ce4",
"package/types/runtime/events.d.ts": "a1e2f9d387475453eaf95c2a5ee79e228636c0bb859bda3fcc46da46574a0c31",
"package/types/runtime/media.d.ts": "8fc89401be7fe25542c169d07dd5675f10916ed20be8493e19695966cf3864a8",
"package/types/runtime/option.d.ts": "6e334e1a76fdcc40b4665c02523bd31c3994d96078fadeb40e23422aa54c1a1d",
"package/types/runtime/player.d.ts": "8ac6383502e4872e83659838844856903031a81a0adc8e8535feb78453aa042c",
"package/types/runtime/plugin.d.ts": "8528f2b17843149f42f9cc16d6fd70baa238c9dc2c329b67d2c38096799c69bd",
"package/types/runtime/services.d.ts": "604c9f8e4b6c0da818f96576bf9f4b0e96085a9003927b2f7ba30027b1ba237e",
"package/types/runtime/setting.d.ts": "349c1fcb205ad312916bd3ee55e6bf241ea8a569fb2ab6c80822e69fc518f7d7",
"package/types/runtime/subtitle.d.ts": "269ff84678d327b3a9dc2f5bc6cc0d731d1e081b85422e474621d5d792efb153",
"package/types/runtime/template.d.ts": "7a62f968a38b4bf9109a4439557c02074eb1d62ba1f6e21a8eb194e91f0badfe",
"package/types/runtime/utils.d.ts": "6bf731705d86853e066dbaf3b8d4003b6548423fe615b7412b722bd0d3a6e48b",
"package/types/setting.d.ts": "08305a75dff96f06490430e099338b463df471b550c1d0dac3abc8995b39eb09",
"package/types/subtitle.d.ts": "b18314e2d64efdb6045830b6e12f615ae2ed585416130e562329ad3ea337f6ce",
"package/types/template.d.ts": "4792bc0c2cbcb7966e4375118bd34b326166e0831862e94ace4cf6b1bbe6a8c8",
"package/types/utils.d.ts": "fca0d3d814df94a25f9eb067c86fa0cf272e2791bac793b91229d57680797712"
}
},
{
"name": "artplayer-plugin-asr",
"archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\asr-package-types-nlZPaL\\artplayer-plugin-asr.tgz",
"sha256": "d784829c8fbc06f78d8a7647ef3530cec1cf3f5157a053bfbc58111d772eb543",
"files": {
"package/ARCHITECTURE.md": "a46a1abf28a4c783a4cec2983c4ba276b7e9e33e8446d811cc9c49c1f5231ef8",
"package/README.md": "d8eec6fafd308a3f52c16548a27ea07c2cb247d950b3e155d94f98e010e6b994",
"package/dist/artplayer-plugin-asr.js": "533b60d6348764fa460c2436cc1197b95544051fb4d1ec4d950fec9f88e1c5cb",
"package/dist/artplayer-plugin-asr.legacy.js": "c36ebbea3a4e5f30b2736ffb6a964d45e330b0911f51e32d9cab44397ec16912",
"package/dist/artplayer-plugin-asr.mjs": "751201241a848cdc26235334ce7603b174a90bf4ac793ff3f85182b1783f6996",
"package/package.json": "781b1eebefdff5fdc234b793ff9d6d8263275115ccff4c4d90c55eca942f2444",
"package/types/artplayer-plugin-asr.d.ts": "8fce55a45c368dc95fc6169750d2157ad57f683f62b586b8162ac720e1efdf42",
"package/types/runtime.d.cts": "2965dccc49a3a75c3b16c0c13501077b8ac199e78bac2d19d57be3d811583606",
"package/types/runtime.d.mts": "ff0769d3e408d66c7d38574171ff2621456afccc50ee8191ca4ee394dc57666e",
"package/types/runtime.d.ts": "2965dccc49a3a75c3b16c0c13501077b8ac199e78bac2d19d57be3d811583606"
}
}
],
"published": [
{
"label": "published-2.0.0",
"archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\releases\\artplayer-plugin-asr-2.0.0.tgz",
"sha256": "faa726b9536306664d935c95367230e0c622bd046faf636fc2dcef4bbce4cd47"
},
{
"label": "published-2.1.0",
"archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\releases\\artplayer-plugin-asr-2.1.0.tgz",
"sha256": "8bc3cd93f78ce99e5c09cc546c770a63e8aea5729d3d584134ce04d826b7708b"
}
]
},
"strict": {
"skipLibCheck": false,
"ambientTypes": [],
"sourceProjects": [
"tsconfig.json",
"packages/artplayer-plugin-asr/tsconfig.json"
],
"installedResolution": "Only isolated consumer and executing compiler standard libraries",
"compilerVersions": [
"5.9.3",
"4.3.5"
]
},
"matrix": [
{
"plugin": "published-2.0.0",
"compiler": "5.9.3",
"mode": "node10-commonjs",
"validConsumerDiagnostics": 0,
"invalidStatementsRejected": 0,
"originalDirectDiagnostics": 0,
"namespaceChecked": false,
"namespaceDiagnostics": 0
},
{
"plugin": "published-2.0.0",
"compiler": "5.9.3",
"mode": "nodenext-cjs",
"validConsumerDiagnostics": 0,
"invalidStatementsRejected": 0,
"originalDirectDiagnostics": 0,
"namespaceChecked": false,
"namespaceDiagnostics": 0
},
{
"plugin": "published-2.0.0",
"compiler": "5.9.3",
"mode": "nodenext-esm",
"validConsumerDiagnostics": 15,
"invalidStatementsRejected": 0,
"originalDirectDiagnostics": 15,
"namespaceChecked": true,
"namespaceDiagnostics": 0
},
{
"plugin": "published-2.0.0",
"compiler": "5.9.3",
"mode": "bundler-esm",
"validConsumerDiagnostics": 0,
"invalidStatementsRejected": 0,
"originalDirectDiagnostics": 0,
"namespaceChecked": false,
"namespaceDiagnostics": 0
},
{
"plugin": "published-2.0.0",
"compiler": "4.3.5",
"mode": "node10-commonjs",
"validConsumerDiagnostics": 0,
"invalidStatementsRejected": 0,
"originalDirectDiagnostics": 0,
"namespaceChecked": false,
"namespaceDiagnostics": 0
},
{
"plugin": "published-2.1.0",
"compiler": "5.9.3",
"mode": "node10-commonjs",
"validConsumerDiagnostics": 0,
"invalidStatementsRejected": 0,
"originalDirectDiagnostics": 0,
"namespaceChecked": false,
"namespaceDiagnostics": 0
},
{
"plugin": "published-2.1.0",
"compiler": "5.9.3",
"mode": "nodenext-cjs",
"validConsumerDiagnostics": 0,
"invalidStatementsRejected": 0,
"originalDirectDiagnostics": 0,
"namespaceChecked": false,
"namespaceDiagnostics": 0
},
{
"plugin": "published-2.1.0",
"compiler": "5.9.3",
"mode": "nodenext-esm",
"validConsumerDiagnostics": 15,
"invalidStatementsRejected": 0,
"originalDirectDiagnostics": 15,
"namespaceChecked": true,
"namespaceDiagnostics": 0
},
{
"plugin": "published-2.1.0",
"compiler": "5.9.3",
"mode": "bundler-esm",
"validConsumerDiagnostics": 0,
"invalidStatementsRejected": 0,
"originalDirectDiagnostics": 0,
"namespaceChecked": false,
"namespaceDiagnostics": 0
},
{
"plugin": "published-2.1.0",
"compiler": "4.3.5",
"mode": "node10-commonjs",
"validConsumerDiagnostics": 0,
"invalidStatementsRejected": 0,
"originalDirectDiagnostics": 0,
"namespaceChecked": false,
"namespaceDiagnostics": 0
},
{
"plugin": "candidate",
"compiler": "5.9.3",
"mode": "node10-commonjs",
"validConsumerDiagnostics": 0,
"invalidStatementsRejected": 12,
"originalDirectDiagnostics": 0,
"namespaceChecked": false,
"namespaceDiagnostics": 0
},
{
"plugin": "candidate",
"compiler": "5.9.3",
"mode": "nodenext-cjs",
"validConsumerDiagnostics": 0,
"invalidStatementsRejected": 12,
"originalDirectDiagnostics": 0,
"namespaceChecked": false,
"namespaceDiagnostics": 0
},
{
"plugin": "candidate",
"compiler": "5.9.3",
"mode": "nodenext-esm",
"validConsumerDiagnostics": 0,
"invalidStatementsRejected": 12,
"originalDirectDiagnostics": 15,
"namespaceChecked": true,
"namespaceDiagnostics": 0
},
{
"plugin": "candidate",
"compiler": "5.9.3",
"mode": "bundler-esm",
"validConsumerDiagnostics": 0,
"invalidStatementsRejected": 12,
"originalDirectDiagnostics": 0,
"namespaceChecked": false,
"namespaceDiagnostics": 0
},
{
"plugin": "candidate",
"compiler": "4.3.5",
"mode": "node10-commonjs",
"validConsumerDiagnostics": 0,
"invalidStatementsRejected": 12,
"originalDirectDiagnostics": 0,
"namespaceChecked": false,
"namespaceDiagnostics": 0
},
{
"plugin": "candidate",
"compiler": "5.9.3",
"mode": "node10-commonjs-no-interop",
"validConsumerDiagnostics": 0,
"invalidStatementsRejected": 12,
"originalDirectDiagnostics": 0,
"namespaceChecked": false,
"namespaceDiagnostics": 0
},
{
"plugin": "candidate",
"compiler": "4.3.5",
"mode": "node10-commonjs-no-interop",
"validConsumerDiagnostics": 0,
"invalidStatementsRejected": 12,
"originalDirectDiagnostics": 0,
"namespaceChecked": false,
"namespaceDiagnostics": 0
}
],
"historicalLimits": "Both old releases have 15 NodeNext ESM direct-call diagnostics. These are frozen in asr-type-diagnostics.json and remain on candidate root; legal namespace consumers pass all three packages. Runtime ESM default import is accurate through the additive /runtime entry.",
"correctedDuringReview": [
"Added missing runtime named type exports to CommonJS/Node10 declarations",
"Retained original root namespace declarations after detecting replacement-object incompatibility",
"Excluded implementation tsconfig from actual package",
"Used a compiler-portable invalid buffer example; old TS typed-array structural acceptance is not claimed as rejection"
],
"inputs": {
"packages/artplayer-plugin-asr/.npmignore": "145c5fa0341195e21cf13df3423a057282f8ea374e9a1b3065e2280e1ce1c5c1",
"packages/artplayer-plugin-asr/package.json": "781b1eebefdff5fdc234b793ff9d6d8263275115ccff4c4d90c55eca942f2444",
"packages/artplayer-plugin-asr/tsconfig.json": "583e4ac1afa99e3a29c7f899df9fdeefcbd14d3847d69143e3fa6a8facac6d99",
"packages/artplayer-plugin-asr/types/artplayer-plugin-asr.d.ts": "dca6dfb5a6b6d58a891412c29d857214c95834e60bdabf0aa9d2fec48f31ec12",
"packages/artplayer-plugin-asr/types/runtime.d.ts": "2965dccc49a3a75c3b16c0c13501077b8ac199e78bac2d19d57be3d811583606",
"packages/artplayer-plugin-asr/types/runtime.d.cts": "2965dccc49a3a75c3b16c0c13501077b8ac199e78bac2d19d57be3d811583606",
"packages/artplayer-plugin-asr/types/runtime.d.mts": "ff0769d3e408d66c7d38574171ff2621456afccc50ee8191ca4ee394dc57666e",
"refactor/scripts/asr-package-types.mjs": "00de7c03cba39bed912933d58d686fb0b4aea374944c460ee70a7e404119d259",
"refactor/fixtures/consumers/asr-published.ts": "7611ab9ef9765fe6a6ca5112589228d49012400bf0b9d9cede96ead8e63cffc2",
"refactor/fixtures/consumers/asr-esm-namespace.ts": "1e3b7475452f43f71e4e038c17368860fe2bc00a61451397fe609e9a3e078d98",
"test/types/asr-public.ts": "19eac65cf88ccf908f175dc03243f6933b62dfa3d254525501c2396a571c8843",
"test/types/asr-source.ts": "f2541df4f4e7976a97fa9f31c730298e98fea4749eef2a2e098da539fbc240ca",
"scripts/typecheck.mjs": "96b166178bada2ab2130af1ea422e01965cc6467746ecf605e87ec3b29068309",
"package.json": "a95566e7e6825cda949428a71d11cbcc39c1ba3651fc631ed8511ca5e078d340"
},
"evidence": [
{
"file": "refactor/.cache/asr-package-types-nlZPaL/report.json",
"sha256": "5b32bda6481f74a9db7fe68f54f36a9752deb2cbb8e56b2a42cdc061297b8df7"
},
{
"file": "refactor/.cache/asr04-installed-final.log",
"sha256": "cebbdebc4b5419f16e419a33e0ba5ae33102091c3dcfd2fb95d541f0e2640634"
},
{
"file": "refactor/.cache/asr04-strict-final.log",
"sha256": "0d6e90ea1b4dadf17e3dd5dee44c3894e17455e124d2a7ab31d5850249ec72b7"
},
{
"file": "refactor/.cache/asr04-lint-final.log",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
{
"file": "refactor/.cache/asr04-artifact-identity.log",
"sha256": "0ad9dc4536c26e81edf671c68cc218aef044e3655bc1ba91afbcf42187783215"
}
],
"unchangedRuntimeEvidence": "refactor/baselines/asr-ownership-validation.json",
"limitation": [
"No full CI or browser rerun for unchanged JS",
"Node 24 actual installs only; full Node/OS distribution matrix remains later acceptance",
"ASR-05 native fallback/volume/CORS/device evidence remains incomplete",
"ASR-06 release documentation and distribution remains incomplete"
],
"releaseReady": false
}
@@ -0,0 +1,53 @@
# PKG-ASR-04:ASR 公共类型与隔离安装
本任务延续 ASR-03 的 8 个严格 TS 模块,处理公共类型与安装边界。保留根入口及
`/legacy` 的历史工厂形状,新增 `/runtime` 精确描述回调返回字幕、异步 stop。
不改运行时 JS、不新增依赖、不修改版本;大版本与发布仍按后续任务统一执行。
## 兼容决策
- 根入口仍是原来的可选参数、同步注册、void stop、void/Promise<void> callback;
旧工厂和插件结果可双向赋值,Parameters/ReturnType 提取也保留。
- `/runtime` 加载同一 main/ESM 产物,只提供准确的类型入口:回调可同步或异步返回
string/null/void,stop 返回 Promise<void>,工厂具有只读 self.default。
不将 self.default 加到旧工厂类型,避免要求用户的替换函数提供额外属性。
- root 与 runtime 都导出 AudioChunk/RuntimeOption/RuntimeResult/RuntimeFactory。
runtime 的 ESM 使用 `.d.mts`,CommonJS/Node10 使用 export assignment。
子代理发现后者漏了命名类型,已补纯类型 namespace 并加入四类型等价回归。
- 旧根声明在 NodeNext ESM 下把 default import 描述成命名空间,合法的
`asr.default()`、`namespace.default.default()` 和 `{default: factory}` 替换必须保留。
原拟直接工厂 `.d.mts` 会破坏这些旧类型用法,已撤回;准确 ESM 默认导入用 `/runtime`。
这是声明兼容测试,不能推断旧 JS 支持所有双层 default 调用。
包内 README 给出新入口用法,ARCHITECTURE 记录维护限制。实际打包发现 tsconfig
泄漏,已补 `.npmignore`,保留所有历史 dist/types 文件。源码 TS 与测试不进入包。
## 验证与证据
`yarn test:asr-types-package` 实际打包核心与 ASR,在工作区外逐个安装 frozen
2.0.0、2.1.0 及候选包。离线安装后用 frozen lockfile 再安装;检查锁文件不变、
无工作区符号链接、全部包文件与 archive 哈希相符。类型程序只能读取该临时消费者
及当前编译器标准库;strict、skipLibCheck:false、types:[],不靠 ambient 类型兜底。
- 2 个旧发布包各 5 种配置;每包 4 种直接用法通过,NodeNext ESM 原有 15 条诊断
按完整消息冻结。合法 ESM 命名空间 fixture 在两个旧包及候选包均通过。
- 候选 7 种配置:TS 5.9.3 Node10/NodeNext CJS/NodeNext ESM/Bundler,TS 4.3.5
Node10,以及两编译器 Node10 关闭 interop。每种公共用法零诊断,12 项负例在
各自语句行被拒绝;原 direct fixture 的 NodeNext ESM 诊断也与旧包一致。
- 实际 Node 24.21.0 import/require 验证三候选入口、root/runtime 实现身份、
self.default 不可枚举、legacy 默认导入身份;旧包验证各自 CJS 和 ESM 导出。
- 根项目及 ASR 分包严格检查通过,source/public 类型边界加入持续检查。
定向 ESLint 和台账测试通过,ASR 构建 JS 与 ASR-03 HEAD 逐字节相同。
类型反例中的 typed-array 与 ArrayBuffer 在旧 TS 标准库下结构可赋值,不能声称
TS 4.3.5 会拒绝。负例使用所有目标编译器都应拒绝的字符串 buffer,保持公开
ArrayBuffer 字段不变;没有通过缩窄字段或升级最低编译器绕过兼容性。
具体 archive、输入、日志哈希及矩阵摘要见
[验证记录](../baselines/asr-types-validation.json),旧 direct 诊断见
[冻结声明诊断](../baselines/asr-type-diagnostics.json)。此前 ASR-03 的音频/浏览器
证据继续对应未变的 JS;本任务没有重复整仓 CI 或浏览器回归,也不声称完成真实
fallback、音量/静音、CORS、物理设备或全 Node 版本验收。这些仍归 ASR-05/06。
子代理独立提供公共/历史消费者与声明审查,主代理修复声明、运行实际隔离安装、
整合证据和提交。回退本任务移除新增类型入口与测试,不改变 ASR-03 运行时修复。
@@ -0,0 +1,46 @@
import type Artplayer from 'artplayer'
import asr from 'artplayer-plugin-asr'
import * as namespace from 'artplayer-plugin-asr'
// Compile this fixture as .mts with NodeNext: published declarations expose
// a CommonJS declaration namespace even though the runtime import is ESM.
interface HistoricalOption {
length?: number
interval?: number
sampleRate?: number
autoHideTimeout?: number
onAudioChunk?: (chunk: { pcm: ArrayBuffer, wav: ArrayBuffer }) => void | Promise<void>
}
interface HistoricalResult {
name: 'artplayerPluginAsr'
stop: () => void
hide: () => void
append: (subtitle: string) => void
}
type HistoricalFactory = (option?: HistoricalOption) => (art: Artplayer) => HistoricalResult
interface HistoricalModule { default: HistoricalFactory }
const result: ReturnType<ReturnType<typeof asr.default>> = {
name: 'artplayerPluginAsr',
stop() {},
hide() {},
append(_subtitle: string) {},
}
const replacementFactory: HistoricalFactory = (_option?: HistoricalOption) => (_art: Artplayer) => result
const replacementModule: typeof asr = { default: replacementFactory }
const assignToOld: HistoricalModule = asr
const assignFromOld: typeof asr = assignToOld
const namespaceModule: typeof namespace.default = replacementModule
const factoryToOld: HistoricalFactory = asr.default
const factoryFromOld: typeof asr.default = replacementFactory
const option: Parameters<typeof asr.default>[0] = {
onAudioChunk: async (_chunk): Promise<void> => {},
}
const absent: Parameters<typeof namespace.default.default>[0] = undefined
declare const art: Artplayer
const installed: HistoricalResult = asr.default(option)(art)
const namespaceResult: HistoricalResult = namespace.default.default(absent)(art)
const stop: void = installed.stop()
const append: void = namespaceResult.append('Old namespace import.')
void [assignFromOld, namespaceModule, factoryToOld, factoryFromOld, stop, append]
@@ -0,0 +1,55 @@
import Artplayer from 'artplayer'
import asr from 'artplayer-plugin-asr'
import * as namespace from 'artplayer-plugin-asr'
interface PublishedChunk {
pcm: ArrayBuffer
wav: ArrayBuffer
}
interface PublishedOption {
length?: number
interval?: number
sampleRate?: number
autoHideTimeout?: number
onAudioChunk?: (chunk: PublishedChunk) => void | Promise<void>
}
interface PublishedResult {
name: 'artplayerPluginAsr'
stop: () => void
hide: () => void
append: (subtitle: string) => void
}
type PublishedFactory = (option?: PublishedOption) => (art: Artplayer) => PublishedResult
const option: Parameters<typeof asr>[0] = {
length: 3,
interval: 100,
sampleRate: 16000,
autoHideTimeout: 10000,
onAudioChunk(chunk) {
const pcm: ArrayBuffer = chunk.pcm
const wav: ArrayBuffer = chunk.wav
void [pcm, wav]
},
}
const empty: Parameters<typeof asr>[0] = undefined
const asyncOption: PublishedOption = { onAudioChunk: async (_chunk: PublishedChunk): Promise<void> => {} }
const result: ReturnType<ReturnType<typeof asr>> = {
name: 'artplayerPluginAsr',
stop() {},
hide() {},
append(_subtitle: string) {},
}
const replacement: PublishedFactory = (_option?: PublishedOption) => (_art: Artplayer) => result
const assignToOld: PublishedFactory = asr
const assignFromOld: typeof asr = replacement
const namespaceFactory: PublishedFactory = namespace.default
const art = new Artplayer({ container: '#player', url: '/video.mp4', plugins: [asr(option), asr(asyncOption), asr(), namespace.default(empty)] })
const installed: PublishedResult = asr(option)(art)
const stop: void = installed.stop()
const hide: void = installed.hide()
const append: void = installed.append('Historical subtitle.')
const callback: ((chunk: PublishedChunk) => void | Promise<void>) | undefined = option.onAudioChunk
void [assignToOld, assignFromOld, namespaceFactory, stop, hide, append, callback]
+3 -2
View File
@@ -4,7 +4,7 @@
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 225 项,范围 22 个包及工作区/示例。
状态:todo 83 / doing 16 / blocked 0 / done 126 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
状态:todo 82 / doing 16 / blocked 0 / done 127 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
@@ -254,7 +254,7 @@
| PKG-ASR-01 | artplayer-plugin-asr<br>核对包契约与历史用法 | BASE-05 | length/interval/sampleRate/onAudioChunk、append/hide/stop 的真实能力 | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结 | H | done |
| PKG-ASR-02 | artplayer-plugin-asr<br>建立特有行为与错误测试 | PKG-ASR-01, ENG-03, ENG-05 | PCM/WAV、chunk 时序、回调慢/拒绝、重复初始化与停止 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | done |
| PKG-ASR-03 | artplayer-plugin-asr<br>整理内部职责与资源 | PKG-ASR-02, CORE-10, CORE-18 | AudioContext/Worklet/Stream 生命周期和背压分离 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | done |
| PKG-ASR-04 | artplayer-plugin-asr<br>迁移自有源码和公开类型 | PKG-ASR-03, ENG-04, ENG-06, CORE-07 | 主线程/Worklet/音频 buffer 与异步回调类型 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | todo |
| PKG-ASR-04 | artplayer-plugin-asr<br>迁移自有源码和公开类型 | PKG-ASR-03, ENG-04, ENG-06, CORE-07 | 主线程/Worklet/音频 buffer 与异步回调类型 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | done |
| PKG-ASR-05 | artplayer-plugin-asr<br>验证新旧核心和组合 | PKG-ASR-04, CORE-22 | 真实 WebAudio 分块、播放暂停/切源/销毁,不引入网络 ASR | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo |
| PKG-ASR-06 | artplayer-plugin-asr<br>验证分发并同步文档 | PKG-ASR-05, ENG-07 | asr.js、Worklet 资源、输出格式与声明证据 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo |
@@ -529,6 +529,7 @@
- PKG-ASR-01: [记录](changes/2026-09-13-PKG-ASR-01-contracts.md) [记录](baselines/asr-contract.md) [记录](baselines/asr-release.json)
- 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-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)
+14
View File
@@ -1,5 +1,19 @@
# 进度与证据
## PKG-ASR-04 公共类型与安装兼容完成
保留根入口及 legacy 的旧工厂、void stop 和 NodeNext ESM 命名空间形状;新增
runtime 精确类型入口,描述字幕回调与 Promise stop。子代理补消费者并发现
CommonJS 命名类型遗漏、旧 ESM 替换对象兼容风险,均修复后由主代理隔离安装验收。
实际 2.0/2.1/候选共 17 配置:候选 7 配置通过,每种 12 负例按语句拒绝;两旧包
各有 1 组原生 NodeNext direct 声明错误单独冻结,三个包的合法 namespace 用法通过。
根/分包 strict、定向 lint、包字节与 frozen reinstall 检查通过,修复 tsconfig 打包泄漏。
见[变更](changes/2026-09-13-PKG-ASR-04-public-types.md)与[证据](baselines/asr-types-validation.json)。
225 项:127 done、16 doing、82 todo;205 风险,ASR-TYPE-01 闭环。
下一步 ASR-05 原生 fallback/音量/跨源/设备组合与 ASR-06 发行;运行时 JS 未改,
沿用 ASR-03 浏览器证据,本项不等于 ASR 整包发布验收,没有推送或发布。
## PKG-ASR-03 TS 拆分与音频生命周期完成
生产源码拆为 8 个严格 TS 模块;队列保留半块/尾部,识别回调背压、拒绝恢复、
+1 -1
View File
@@ -206,6 +206,6 @@
| JASSUB-EXPORT-01 | open / 已复现 | JASSUB historical CommonJS object.default changes to a direct factory in 1.1.0 | PKG-JASSUB-04, PKG-JASSUB-06 |
| CORE-DEFAULTS-SSR-01 | resolved / 已复现 | Reading static defaults throws when navigator is absent | CORE-25 |
| ENG-DIST-PUBLIC-01 | resolved / 已复现 | Vite copied authored declarations into library distribution | ENG-12 |
| ASR-TYPE-01 | open / 源码/产物事实 | ASR declarations omit asynchronous stop and callback text results | PKG-ASR-04 |
| ASR-TYPE-01 | resolved / 已复现 | ASR declarations omit asynchronous stop and callback text results | PKG-ASR-04 |
| ASR-CHUNK-01 | resolved / 已复现 | ASR tick discards underfilled chunks and overrun samples in source | PKG-ASR-02, PKG-ASR-03 |
| ASR-LIFE-01 | resolved / 已复现 | ASR partial initialization, pause resume and stale callback cleanup need controlled evidence | PKG-ASR-02, PKG-ASR-03, PKG-ASR-05 |
+11 -3
View File
@@ -4545,8 +4545,8 @@
{
"id": "ASR-TYPE-01",
"title": "ASR declarations omit asynchronous stop and callback text results",
"confirmation": "source-observed",
"status": "open",
"confirmation": "reproduced",
"status": "resolved",
"owners": [
"PKG-ASR-04"
],
@@ -4555,7 +4555,15 @@
"refactor/baselines/asr-release.json"
],
"compatibleResolution": "Keep historical assignment compatibility while supplying accurate asynchronous consumption.",
"closureCriteria": "Actual old/new installed TS consumers cover void callback, Promise<void>, returned text and factory substitution."
"closureCriteria": "Actual old/new installed TS consumers cover void callback, Promise<void>, returned text and factory substitution.",
"resolutionEvidence": [
"refactor/changes/2026-09-13-PKG-ASR-04-public-types.md",
"refactor/baselines/asr-types-validation.json",
"refactor/scripts/asr-package-types.mjs",
"test/types/asr-public.ts",
"refactor/fixtures/consumers/asr-esm-namespace.ts"
],
"resolutionRationale": "Root/legacy preserve historical void callback and stop, factory substitution and legal NodeNext namespace shape. Additive runtime supplies text/null/void callbacks and Promise stop with consistent named types. Actual isolated 2.0/2.1/candidate installs, seven candidate compiler configurations and 12 per-mode invalid statements verify declarations. Existing old NodeNext direct-call diagnostics remain explicit, not candidate success."
},
{
"id": "ASR-CHUNK-01",
+166
View File
@@ -0,0 +1,166 @@
import assert from 'node:assert/strict'
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import ts from 'typescript'
import compat from 'typescript-compat'
import { checkFiles, packedFiles } from '../../scripts/package-check.mjs'
import { consumerDirectory, removeConsumer, run, workspace, writeJson } from '../../scripts/package-consumer.mjs'
import { verifyAsrContract } from './asr-contract.mjs'
import { hash, readMember } from './releases.mjs'
const publicFixture = fs.readFileSync(path.join(workspace, 'test/types/asr-public.ts'), 'utf8')
const publishedFixture = fs.readFileSync(path.join(workspace, 'refactor/fixtures/consumers/asr-published.ts'), 'utf8')
const standardModes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']]
function typecheck(consumer, compiler, mode, candidate) {
const next = mode.startsWith('nodenext')
const extension = next ? mode.endsWith('-cjs') ? 'cts' : 'mts' : 'ts'
const filename = path.join(consumer, `consumer.${extension}`)
const files = [filename]
if (candidate && mode !== 'bundler-esm' && mode !== 'nodenext-esm') {
const commonjs = path.join(consumer, `commonjs.${extension}`)
fs.writeFileSync(commonjs, `import asr = require('artplayer-plugin-asr');
import legacy = require('artplayer-plugin-asr/legacy');
import runtime = require('artplayer-plugin-asr/runtime');
import type Artplayer from 'artplayer';
const replacement: typeof asr.default = () => (_art: Artplayer) => ({name: 'artplayerPluginAsr', stop() {}, hide() {}, append(_text: string) {}});
const legacyReplacement: typeof legacy.default = replacement;
asr.default(); legacy.default(); runtime({onAudioChunk: async () => 'Text.'}); runtime.default();
declare const art: Artplayer;
const stopped: Promise<void> = runtime()(art).stop();
void [legacyReplacement, stopped];`)
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,
}
function compile(code) {
fs.writeFileSync(filename, code)
const program = compiler.createProgram(files, options)
for (const file of program.getSourceFiles()) {
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 => ({
file: item.file ? path.relative(consumer, item.file.fileName).replaceAll('\\', '/') : null,
line: item.file && item.start !== undefined ? item.file.getLineAndCharacterOfPosition(item.start).line + 1 : null,
code: item.code,
message: compiler.flattenDiagnosticMessageText(item.messageText, '\n').replaceAll(consumer.replaceAll('\\', '/'), '<consumer>'),
}))
}
let source = candidate
? mode.endsWith('-no-interop') ? publicFixture.replace('import runtime from \'artplayer-plugin-asr/runtime\'', 'import runtime = require(\'artplayer-plugin-asr/runtime\')') : publicFixture
: publishedFixture
if (candidate && mode === 'nodenext-esm') {
source = source
.replace('import legacy from \'artplayer-plugin-asr\'', 'import legacyModule from \'artplayer-plugin-asr\'\nconst legacy = legacyModule.default')
.replace('import legacyEntry from \'artplayer-plugin-asr/legacy\'', 'import legacyEntryModule from \'artplayer-plugin-asr/legacy\'\nconst legacyEntry = legacyEntryModule.default')
.replace('namespace.default', 'namespace.default.default')
}
const diagnostics = compile(source)
const invalid = candidate ? compile(source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')) : []
if (candidate) {
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 = candidate ? compile(publishedFixture) : diagnostics
const namespace = mode === 'nodenext-esm' ? compile(fs.readFileSync(path.join(workspace, 'refactor/fixtures/consumers/asr-esm-namespace.ts'), 'utf8')) : []
return { compiler: compiler.version, mode, diagnostics, invalid, published, namespace }
}
async function main() {
assert.equal(process.env.npm_config_user_agent?.split(' ')[0], 'yarn/1.22.22', 'Use yarn test:asr-types-package')
const yarn = process.env.npm_execpath
assert(yarn && fs.existsSync(yarn))
const output = fs.mkdtempSync(path.join(workspace, 'refactor/.cache/asr-package-types-'))
console.log(`ASR isolated package evidence: ${output}`)
const contract = await verifyAsrContract()
const expected = JSON.parse(fs.readFileSync(path.join(workspace, 'refactor/baselines/asr-type-diagnostics.json'), 'utf8'))
for (const [file, digest] of Object.entries(expected.fixtures))
assert.equal(hash(fs.readFileSync(path.join(workspace, file), 'utf8').replaceAll('\r\n', '\n')), digest, `Frozen ASR fixture changed: ${file}`)
const packages = []
for (const name of ['artplayer', 'artplayer-plugin-asr']) {
const archive = path.join(output, `${name}.tgz`)
fs.writeFileSync(path.join(output, `${name}-pack.log`), run([yarn, 'pack', '--filename', archive], path.join(workspace, 'packages', name)))
const manifest = JSON.parse(readMember(archive, 'package/package.json'))
const files = packedFiles(archive)
checkFiles(manifest, files, name === contract.baseline.release.name ? Object.keys(contract.baseline.release.files) : [])
packages.push({ name, archive, sha256: hash(fs.readFileSync(archive)), files: Object.fromEntries(files.map(member => [member, hash(readMember(archive, member))])) })
}
const matrix = []
const core = packages[0]
const candidates = [
...[...contract.baseline.previous].reverse().concat(contract.baseline.release).map(release => ({ ...release, archive: contract.archives.get(release.version), label: `published-${release.version}` })),
{ ...packages[1], label: 'candidate' },
]
for (const plugin of candidates) {
const candidate = plugin.label === 'candidate'
const consumer = consumerDirectory()
try {
writeJson(path.join(consumer, 'package.json'), { name: 'asr-isolated-consumer', private: true, dependencies: Object.fromEntries([core, plugin].map(pkg => [pkg.name, `file:${pkg.archive.replaceAll('\\', '/')}`])) })
fs.copyFileSync(path.join(workspace, 'yarn.lock'), path.join(consumer, 'yarn.lock'))
fs.writeFileSync(path.join(output, `${plugin.label}-install.log`), run([yarn, 'install', '--offline', '--ignore-scripts', '--non-interactive'], consumer))
const lock = fs.readFileSync(path.join(consumer, 'yarn.lock'))
fs.writeFileSync(path.join(output, `${plugin.label}-frozen-install.log`), run([yarn, 'install', '--offline', '--frozen-lockfile', '--ignore-scripts', '--non-interactive'], consumer))
assert.deepEqual(fs.readFileSync(path.join(consumer, 'yarn.lock')), lock)
fs.writeFileSync(path.join(output, `${plugin.label}-yarn.lock`), lock)
for (const pkg of [core, plugin]) {
const root = path.join(consumer, 'node_modules', pkg.name)
assert.equal(fs.realpathSync(root), root, 'Installed package must not be a workspace link')
for (const [member, expected] of Object.entries(pkg.files))
assert.equal(hash(fs.readFileSync(path.join(root, member.slice(8)))), expected, `Installed bytes differ: ${member}`)
}
const runtimeFile = path.join(consumer, 'exports.cjs')
fs.writeFileSync(runtimeFile, candidate
? `const assert = require('node:assert/strict');
for (const name of ['artplayer-plugin-asr', 'artplayer-plugin-asr/legacy', 'artplayer-plugin-asr/runtime']) { const factory = require(name); assert.equal(typeof factory, 'function'); assert.equal(factory.default, factory); assert.equal(Object.getOwnPropertyDescriptor(factory, 'default').enumerable, false); assert.equal(typeof factory({}), 'function'); }
assert.equal(require('artplayer-plugin-asr/runtime'), require('artplayer-plugin-asr'));
Promise.all(['artplayer-plugin-asr', 'artplayer-plugin-asr/runtime', 'artplayer-plugin-asr/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-asr/legacy')); }).catch(error => { console.error(error); process.exitCode = 1; });`
: `const assert = require('node:assert/strict'); const value = require('artplayer-plugin-asr'); assert.equal(typeof value, '${plugin.version === '2.1.0' ? 'function' : 'object'}'); assert.equal(typeof (value.default || value)({}), 'function'); assert.equal(typeof value.default, '${plugin.version === '2.1.0' ? 'undefined' : 'function'}'); import('artplayer-plugin-asr').then(module => { assert.equal(typeof module.default, 'function'); assert.equal(typeof module.default({}), 'function'); }).catch(error => { console.error(error); process.exitCode = 1; });`)
fs.writeFileSync(path.join(output, `${plugin.label}-runtime.log`), run([runtimeFile], consumer))
const modes = candidate ? [...standardModes, [ts, 'node10-commonjs-no-interop'], [compat, 'node10-commonjs-no-interop']] : standardModes
for (const [compiler, mode] of modes) {
const result = { plugin: plugin.label, ...typecheck(consumer, compiler, mode, candidate) }
matrix.push(result)
writeJson(path.join(output, `${plugin.label}-${compiler.version}-${mode}.json`), result)
console.log(`${plugin.label} TS ${compiler.version} ${mode}: ${result.diagnostics.length} diagnostics; ${result.invalid.length} negative diagnostics`)
}
}
finally {
removeConsumer(consumer)
}
}
writeJson(path.join(output, 'report.json'), { suite: 'asr-isolated-package-types', introducedBy: 'PKG-ASR-04', packages, published: candidates.slice(0, -1).map(pkg => ({ label: pkg.label, archive: pkg.archive, sha256: hash(fs.readFileSync(pkg.archive)) })), matrix })
for (const result of matrix) {
const baseline = result.plugin === 'candidate' ? [] : expected.cases.find(item => item.plugin === result.plugin && item.compiler === result.compiler && item.mode === result.mode)?.diagnostics
assert.deepEqual(result.diagnostics, baseline, `${result.plugin} TS ${result.compiler} ${result.mode}`)
assert.deepEqual(result.namespace, [], `Historical ESM namespace: ${result.plugin}`)
if (result.plugin === 'candidate') {
const publishedBaseline = result.mode === 'nodenext-esm' ? expected.cases.find(item => item.plugin === 'published-2.1.0' && item.compiler === result.compiler && item.mode === result.mode).diagnostics : []
assert.deepEqual(result.published, publishedBaseline, `Historical direct consumer: ${result.compiler} ${result.mode}`)
assert.equal(result.invalid.length, 12, 'Installed declarations must reject all 12 invalid uses')
}
}
console.log(`ASR installed matrix verified: ${matrix.length} cases; ${output}`)
}
main().catch((error) => {
console.error(error)
process.exitCode = 1
})
+6 -2
View File
@@ -2682,11 +2682,15 @@
"ENG-06",
"CORE-07"
],
"status": "todo",
"status": "done",
"risk": "H",
"deliverable": "主线程/Worklet/音频 buffer 与异步回调类型",
"acceptance": "严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容",
"evidence": []
"evidence": [
"changes/2026-09-13-PKG-ASR-04-public-types.md",
"baselines/asr-types-validation.json",
"baselines/asr-type-diagnostics.json"
]
},
{
"id": "PKG-ASR-05",
+9
View File
@@ -73,6 +73,15 @@ export function runTypechecks() {
console.log(`Strict project passed: ${relative(config)} (${result.files.length} root files)`)
}
for (const [compiler, mode] of [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'bundler-esm'], [ts, 'nodenext-esm'], [compat, 'node10-commonjs']]) {
for (const fixture of ['test/types/asr-public.ts', mode === 'nodenext-esm' ? 'refactor/fixtures/consumers/asr-esm-namespace.ts' : 'refactor/fixtures/consumers/asr-published.ts']) {
let source = fs.readFileSync(path.join(root, fixture), 'utf8')
if (fixture === 'test/types/asr-public.ts' && mode === 'nodenext-esm') {
source = source.replace('import legacy from \'artplayer-plugin-asr\'', 'import legacyModule from \'artplayer-plugin-asr\'\nconst legacy = legacyModule.default')
.replace('import legacyEntry from \'artplayer-plugin-asr/legacy\'', 'import legacyEntryModule from \'artplayer-plugin-asr/legacy\'\nconst legacyEntry = legacyEntryModule.default')
.replace('namespace.default', 'namespace.default.default')
}
assert.deepEqual(checkConsumer(compiler, mode, source), [], `ASR consumer failed: TS ${compiler.version} ${mode}`)
}
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}`)
}
+101
View File
@@ -0,0 +1,101 @@
import type { AsrPluginInstance, AsrPluginOption, AudioChunk, RuntimeFactory, RuntimeOption, RuntimeResult } from 'artplayer-plugin-asr'
import type { AudioChunk as EntryChunk, RuntimeFactory as EntryFactory, RuntimeOption as EntryOption, RuntimeResult as EntryResult } from 'artplayer-plugin-asr/runtime'
import Artplayer from 'artplayer'
import legacy from 'artplayer-plugin-asr'
import * as namespace from 'artplayer-plugin-asr'
import legacyEntry from 'artplayer-plugin-asr/legacy'
import runtime from 'artplayer-plugin-asr/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 RuntimeEntryTypes = Assert<Equal<[EntryChunk, EntryFactory, EntryOption, EntryResult], [AudioChunk, RuntimeFactory, RuntimeOption, RuntimeResult]>>
interface HistoricalOption {
length?: number
interval?: number
sampleRate?: number
autoHideTimeout?: number
onAudioChunk?: (chunk: { pcm: ArrayBuffer, wav: ArrayBuffer }) => void | Promise<void>
}
interface HistoricalResult {
name: 'artplayerPluginAsr'
stop: () => void
hide: () => void
append: (subtitle: string) => void
}
type HistoricalFactory = (option?: HistoricalOption) => (art: Artplayer) => HistoricalResult
type RootArguments = Assert<Equal<Parameters<typeof legacy>, [option?: AsrPluginOption]>>
type RootOption = Assert<Equal<AsrPluginOption, HistoricalOption>>
type RootResult = Assert<Equal<ReturnType<ReturnType<typeof legacy>>, AsrPluginInstance>>
type RootShape = Assert<Equal<AsrPluginInstance, HistoricalResult>>
type RootFactory = Assert<Equal<typeof legacy, HistoricalFactory>>
type RootStop = Assert<Equal<ReturnType<AsrPluginInstance['stop']>, void>>
type ChunkShape = Assert<Equal<AudioChunk, { pcm: ArrayBuffer, wav: ArrayBuffer }>>
type PreciseArguments = Assert<Equal<Parameters<typeof runtime>, [option?: RuntimeOption]>>
type PreciseResult = Assert<Equal<ReturnType<ReturnType<typeof runtime>>, RuntimeResult>>
type PreciseStop = Assert<Equal<ReturnType<RuntimeResult['stop']>, Promise<void>>>
type PreciseFactory = Assert<Equal<typeof runtime, RuntimeFactory>>
type PreciseCallback = Assert<Equal<NonNullable<RuntimeOption['onAudioChunk']>, (chunk: AudioChunk) => string | void | null | Promise<string | void | null>>>
const replacement: HistoricalFactory = (_option?: HistoricalOption) => (_art: Artplayer) => ({
name: 'artplayerPluginAsr',
stop() {},
hide() {},
append(_subtitle: string) {},
})
const assignToOld: HistoricalFactory = legacy
const assignFromOld: typeof legacy = replacement
const namespaceFactory: HistoricalFactory = namespace.default
const explicitLegacy: HistoricalFactory = legacyEntry
const option: AsrPluginOption = { onAudioChunk: async (_chunk: AudioChunk): Promise<void> => {} }
const absent: Parameters<typeof legacy>[0] = undefined
const art = new Artplayer({ container: '#player', url: '/video.mp4', plugins: [legacy(option), runtime(), legacyEntry(absent)] })
const old = legacy(option)(art)
const oldReturns: void[] = [old.stop(), old.hide(), old.append('Subtitle.')]
const bareRoot: typeof legacy = replacement
const runtimeOption: RuntimeOption = { length: 2, interval: 250, sampleRate: 48000, autoHideTimeout: 3000, onAudioChunk: async (chunk) => {
const pcm: ArrayBuffer = chunk.pcm
const wav: ArrayBuffer = chunk.wav
void [pcm, wav]
return 'Recognized subtitle.'
} }
const correct: RuntimeResult = runtime(runtimeOption)(art)
const stopped: Promise<void> = correct.stop()
const alias: RuntimeFactory = runtime.default.default
const aliasResult: RuntimeResult = alias(undefined)(art)
runtime({ onAudioChunk: () => 'Synchronous subtitle.' })
runtime({ onAudioChunk: () => null })
runtime({ onAudioChunk: () => undefined })
runtime({ onAudioChunk: async () => {} })
runtime({ onAudioChunk: async () => null })
runtime(option)
// @ts-expect-error Root options preserve the historical void/Promise<void> callback shape.
legacy({ onAudioChunk: () => 'Only the precise entry describes returned subtitles.' })
// @ts-expect-error Root stop extraction remains void for historical replacement factories.
const rootPromise: Promise<void> = old.stop()
// @ts-expect-error The legacy factory type must not demand or expose a self default alias.
bareRoot.default()
// @ts-expect-error Accurate stop is asynchronous.
const synchronous: void = correct.stop()
// @ts-expect-error Accurate result replacements cannot return void from stop.
const wrongStop: RuntimeResult = { name: 'artplayerPluginAsr', stop() {}, hide() {}, append(_text: string) {} }
// @ts-expect-error Callback results do not include booleans.
runtime({ onAudioChunk: () => true })
// @ts-expect-error Async callback results do not include booleans.
runtime({ onAudioChunk: async () => true })
// @ts-expect-error Options use numeric sample rates.
runtime({ sampleRate: '16000' })
// @ts-expect-error Unknown options remain rejected.
runtime({ unknown: true })
// @ts-expect-error Registration requires an Artplayer instance.
runtime()()
// @ts-expect-error Append accepts a string.
correct.append(123)
// @ts-expect-error PCM is an ArrayBuffer rather than encoded text.
const invalidChunk: AudioChunk = { pcm: 'raw audio', wav: new ArrayBuffer(4) }
void [assignToOld, assignFromOld, namespaceFactory, explicitLegacy, oldReturns, stopped, aliasResult, rootPromise, synchronous, wrongStop, invalidChunk]
export type { ChunkShape, PreciseArguments, PreciseCallback, PreciseFactory, PreciseResult, PreciseStop, RootArguments, RootFactory, RootOption, RootResult, RootShape, RootStop, RuntimeEntryTypes }
+21
View File
@@ -0,0 +1,21 @@
import type { AudioChunk, Factory, RuntimeFactory, RuntimeOption, RuntimeResult } from 'artplayer-plugin-asr'
import type source from '../../packages/artplayer-plugin-asr/src/index'
import type { AsrOptions, AsrResult, AudioChunk as SourceChunk } from '../../packages/artplayer-plugin-asr/src/types'
declare const implementation: typeof source
declare const runtimeFactory: RuntimeFactory
declare const runtimeOption: RuntimeOption
declare const sourceResult: AsrResult
declare const publicResult: RuntimeResult
declare const sourceChunk: SourceChunk
declare const publicChunk: AudioChunk
const historical: Factory = implementation
const accurate: Factory = runtimeFactory
const runtimeCall: (option?: RuntimeOption) => ReturnType<RuntimeFactory> = implementation
const sourceOption: AsrOptions = runtimeOption
const resultToPublic: RuntimeResult = sourceResult
const resultToSource: AsrResult = publicResult
const chunkToPublic: AudioChunk = sourceChunk
const chunkToSource: SourceChunk = publicChunk
void [historical, accurate, runtimeCall, sourceOption, resultToPublic, resultToSource, chunkToPublic, chunkToSource]