refactor(iframe): [PKG-IFRAME-04] preserve types across module consumers

This commit is contained in:
Harvey Zhao committed 2026-09-13 01:43:09 +08:00
1 parent 1342de94ef
commit 047e2df945
24 files changed
+1496 -69

No files matched your search

@@ -0,0 +1,723 @@
{
"task": "PKG-IFRAME-04",
"status": "done",
"checkpoint": "verified-public-types-editor-and-installed-consumers",
"beforeCommit": "1342de94ef2e9dc3e06ddac12fcf1fc5229b648d",
"sourceSha256LF": {
"package.json": "1ca2df37409d1634abe58ce40f50bbee162033af4c57a9908697477e564a7909",
"packages/artplayer-tool-iframe/package.json": "91985c93522c98be5d2a6d02dc0a39df5eeededdd94077758511873fe2290cf4",
"packages/artplayer-tool-iframe/.npmignore": "145c5fa0341195e21cf13df3423a057282f8ea374e9a1b3065e2280e1ce1c5c1",
"packages/artplayer-tool-iframe/README.md": "6e0b3afbad28bf0160d744b89d1a4dad997fff77fb23c61d707389f08f49e219",
"packages/artplayer-tool-iframe/ARCHITECTURE.md": "b5754cfecc61728db0742739f488973f6d9330ebd2f2ec9b60505953fc746546",
"packages/artplayer-tool-iframe/types/artplayer-tool-iframe.d.ts": "e1efb4c7f22de24721923f02cf0488728a6ef381505c49187eba0bc2b90b1207",
"packages/artplayer-tool-iframe/types/artplayer-tool-iframe.d.cts": "66850135f45c81d4bd7a0de33b6a2f63a655de9000f2698f43660039727919f3",
"packages/artplayer-tool-iframe/types/artplayer-tool-iframe.d.mts": "cd394fd68e61fab3c4474daab29852a3f87dd0086b007e7937106871272676fb",
"docs/assets/ts/artplayer-tool-iframe.d.ts": "c06c6819146ba64bd6ee498871d9e19ab5a9e88c9cc7bed2c0c4968fa39dfed7",
"scripts/build-ts.js": "ce6e8da55d934c31f2291326b33a577c9b7dd15b84167f3c63142085d3dcb56d",
"scripts/plugin-editor-types.mjs": "c485550ed0f44d38c2c78faced58c2bec08d496f4bd5a66f4c3dd2ed4bc8b57e",
"scripts/typecheck.mjs": "02aa38836fc33b01f1e2de3def1443ed4d35f038a3595e11106a4dfbff12e86d",
"test/types/iframe.ts": "7ecdc1f7cafda5edb3dbe0908c1029ea7cf3308eb909db661f3cfec27f434b90",
"test/types/iframe-commonjs.cts": "16ac9bea7bb5fd1e5b79301d3f4e3d2761357fbe96a05211a941e26853866729",
"test/types/iframe-source.ts": "29d816c19301d16c81521e809245ac33783362a9023d8cd9ac26185fb3e85ad6",
"refactor/fixtures/consumers/iframe-workspace.ts": "c81fc54c919aabab57ead9efcffc9fe3e469be7fb3b57fbcafa5fd2653d83afb",
"refactor/scripts/iframe-types.test.mjs": "69931a4b0779f996c0af46929c92ce1c9ccc437067a42fa1e9fac9d39ab856a0",
"refactor/scripts/iframe-package-types.mjs": "8dd0006f605752ffdbf51c58bf5ee4cca503dc6c3c1793a8b03dc14fc62d27ea"
},
"artifacts": {
"packages/artplayer-tool-iframe/dist/artplayer-tool-iframe.js": "7408fceccb000e1b304abe30ac9bc364da7860c3896ad5aaa6af427804c237e6",
"packages/artplayer-tool-iframe/dist/artplayer-tool-iframe.legacy.js": "4e8bd39cdc37e98bff9cd19b5e0c9453ebe5231264242fc1c573aa96edb5ea8a",
"packages/artplayer-tool-iframe/dist/artplayer-tool-iframe.mjs": "ac4c5efa670c0429fc95bdab7902df60f5b5540f0dad9c7e65c2bd660796dc83"
},
"browserEvidenceReuse": {
"file": "refactor/baselines/iframe-navigation-validation.json",
"reason": "All six production source modules and three runtime build bytes are unchanged; this task changes declarations/editor generation and manifest type routing. Native frame matrices remain 351/351/207 from task 03; installed export routing is separately tested here."
},
"installed": {
"report": "refactor/.cache/iframe-package-types-AHSyi2/report.json",
"sha256": "1dadc173afa37655cd6c148cb34483f93e82fe82ae7897f6f4dfe89b8eb8b18b",
"task": "PKG-IFRAME-04",
"scope": "Actual npm archive, frozen Git workspace pack and candidate Yarn tarball installed outside workspace; byte verification, offline/frozen lock and strict positive/negative consumers. Old npm CJS namespace and distinct helper are preserved observations, not claims the candidate provides old package-name/helper aliases. Native player and distribution migration remain 05/06.",
"packages": [
{
"label": "published",
"name": "artplayer-plugin-iframe",
"archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\releases\\artplayer-plugin-iframe-1.0.0.tgz",
"sha256": "16dc92aa84d93bd99bc0a34815dc49f1609fafc1ff255eb353ea893e70b5f91f",
"files": {
"package/README.md": "52c0a1199b6529d60ce8f4414984f9bd1bc3a8daa5bb6cfec8f0aae0c8338e46",
"package/dist/artplayer-helper-iframe.js": "47730ed608f0637594549b9762c01191686a4a55b3bd467132304a016d227f4a",
"package/dist/artplayer-helper-iframe.legacy.js": "f4c3b11806cad6d984f03a2d9de63deba78cbe538ffe4044a106b0dddc5d4197",
"package/dist/artplayer-plugin-iframe.js": "df469c3e4582b127f35798494d0845f1fc4ca2d19bbaf01b918492e035479745",
"package/dist/artplayer-plugin-iframe.legacy.js": "15299cb5c848cc61614f7573a802443d69b7c6d0a938a1867835c7cbb4884edd",
"package/package.json": "563a6c8407753b92b86319a5e6d9d567525146c32cb010b45b0d88ff481859f6",
"package/src/index.js": "2e0b9efeedef0d6c4f2d2b526db35f634cd02af044e4e28dd1a0b26a5b9a02bf",
"package/types/artplayer-plugin-iframe.d.ts": "d55d99a2488d60892a0ab82882dc96a0f373dab0c3d2a3cee978bc35aa7c2d0c"
}
},
{
"label": "workspace",
"name": "artplayer-tool-iframe",
"archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\iframe-package-types-AHSyi2\\workspace.tgz",
"sha256": "6fc603122610b785aa3ecb43f2692fb92c39e96386ca1bcab5ee23253af92ae6",
"files": {
"package/README.md": "6910a44b767ff80c1d1edc3c90ca7ca77a9b45307fb61d4abd9d52526c7614b3",
"package/dist/artplayer-tool-iframe.js": "2ec257c8055a9448d12af3fecf1fae8ba58c06f47e85072d02c386cd258b0c86",
"package/dist/artplayer-tool-iframe.legacy.js": "c0755d79f316cda71f045da86749648f95faf47c09180d525fb04bb753576d5f",
"package/dist/artplayer-tool-iframe.mjs": "cbcdb0516dd5f17ca2572a6b3e363ea63b0cfd9733f3e47689c02088f23f29b9",
"package/package.json": "b28f5156f02a250539f0acaf302ec87fdd506f63b0e73dd308326ec78c691d03",
"package/types/artplayer-tool-iframe.d.ts": "4f02aa22a6b373393ead533ae3421ad5acf0de7116057f351021c72d3448bd1e"
}
},
{
"label": "candidate",
"name": "artplayer-tool-iframe",
"archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\iframe-package-types-AHSyi2\\candidate.tgz",
"sha256": "25e88268584f67cb690048f8cb53312b6fcfe64d622a70bf954604a402be9471",
"files": {
"package/ARCHITECTURE.md": "b5754cfecc61728db0742739f488973f6d9330ebd2f2ec9b60505953fc746546",
"package/README.md": "ef5af60bcb831667a78cc3421f6e3c7b5d9a50b3d2e571c5cd02d46fc533e9ce",
"package/dist/artplayer-tool-iframe.js": "7408fceccb000e1b304abe30ac9bc364da7860c3896ad5aaa6af427804c237e6",
"package/dist/artplayer-tool-iframe.legacy.js": "4e8bd39cdc37e98bff9cd19b5e0c9453ebe5231264242fc1c573aa96edb5ea8a",
"package/dist/artplayer-tool-iframe.mjs": "ac4c5efa670c0429fc95bdab7902df60f5b5540f0dad9c7e65c2bd660796dc83",
"package/package.json": "0de9c86eeb3f9a2fb46f9297105b5beddb8d6164163ffaa9874cf5cd68ba7a58",
"package/types/artplayer-tool-iframe.d.cts": "66850135f45c81d4bd7a0de33b6a2f63a655de9000f2698f43660039727919f3",
"package/types/artplayer-tool-iframe.d.mts": "cd394fd68e61fab3c4474daab29852a3f87dd0086b007e7937106871272676fb",
"package/types/artplayer-tool-iframe.d.ts": "e760de22559caf7ffff48d7c5713485bf9f0ddbe876a93080295fb6106535bfa"
}
}
],
"matrix": [
{
"package": "published",
"compiler": "5.9.3",
"mode": "node10-commonjs",
"diagnostics": [],
"negative": []
},
{
"package": "published",
"compiler": "5.9.3",
"mode": "nodenext-cjs",
"diagnostics": [],
"negative": []
},
{
"package": "published",
"compiler": "5.9.3",
"mode": "nodenext-esm",
"diagnostics": [],
"negative": []
},
{
"package": "published",
"compiler": "5.9.3",
"mode": "bundler-esm",
"diagnostics": [],
"negative": []
},
{
"package": "published",
"compiler": "4.3.5",
"mode": "node10-commonjs",
"diagnostics": [],
"negative": []
},
{
"package": "workspace",
"compiler": "5.9.3",
"mode": "node10-commonjs",
"diagnostics": [],
"negative": []
},
{
"package": "workspace",
"compiler": "5.9.3",
"mode": "nodenext-cjs",
"diagnostics": [],
"negative": []
},
{
"package": "workspace",
"compiler": "5.9.3",
"mode": "nodenext-esm",
"diagnostics": [
{
"code": 2344,
"message": "Type 'false' does not satisfy the constraint 'true'."
},
{
"code": 2344,
"message": "Type 'typeof import(\"<consumer>/node_modules/artplayer-tool-iframe/types/artplayer-tool-iframe\")' does not satisfy the constraint 'abstract new (...args: any) => any'.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-tool-iframe/types/artplayer-tool-iframe\")' provides no match for the signature 'new (...args: any): any'."
},
{
"code": 2344,
"message": "Type 'false' does not satisfy the constraint 'true'."
},
{
"code": 2709,
"message": "Cannot use namespace 'Iframe' as a type."
},
{
"code": 2339,
"message": "Property 'onMessage' does not exist on type 'typeof import(\"<consumer>/node_modules/artplayer-tool-iframe/types/artplayer-tool-iframe\")'."
},
{
"code": 2344,
"message": "Type 'false' does not satisfy the constraint 'true'."
},
{
"code": 2709,
"message": "Cannot use namespace 'Iframe' as a type."
},
{
"code": 2344,
"message": "Type 'false' does not satisfy the constraint 'true'."
},
{
"code": 2709,
"message": "Cannot use namespace 'Iframe' as a type."
},
{
"code": 2709,
"message": "Cannot use namespace 'Iframe' as a type."
},
{
"code": 2351,
"message": "This expression is not constructable.\n Type 'typeof import(\"<consumer>/node_modules/artplayer-tool-iframe/types/artplayer-tool-iframe\")' has no construct signatures."
},
{
"code": 2339,
"message": "Property 'postMessage' does not exist on type 'typeof import(\"<consumer>/node_modules/artplayer-tool-iframe/types/artplayer-tool-iframe\")'."
},
{
"code": 2339,
"message": "Property 'inject' does not exist on type 'typeof import(\"<consumer>/node_modules/artplayer-tool-iframe/types/artplayer-tool-iframe\")'."
},
{
"code": 2339,
"message": "Property 'onMessage' does not exist on type 'typeof import(\"<consumer>/node_modules/artplayer-tool-iframe/types/artplayer-tool-iframe\")'."
},
{
"code": 7006,
"message": "Parameter 'value' implicitly has an 'any' type."
},
{
"code": 7006,
"message": "Parameter 'additional' implicitly has an 'any' type."
},
{
"code": 2339,
"message": "Property 'onMessage' does not exist on type 'typeof import(\"<consumer>/node_modules/artplayer-tool-iframe/types/artplayer-tool-iframe\")'."
},
{
"code": 2339,
"message": "Property 'iframe' does not exist on type 'typeof import(\"<consumer>/node_modules/artplayer-tool-iframe/types/artplayer-tool-iframe\")'."
}
],
"negative": []
},
{
"package": "workspace",
"compiler": "5.9.3",
"mode": "bundler-esm",
"diagnostics": [],
"negative": []
},
{
"package": "workspace",
"compiler": "4.3.5",
"mode": "node10-commonjs",
"diagnostics": [],
"negative": []
},
{
"package": "candidate",
"compiler": "5.9.3",
"mode": "node10-commonjs",
"diagnostics": [],
"negative": [
{
"code": 2554,
"message": "Expected 1 arguments, but got 0."
},
{
"code": 2322,
"message": "Type 'string' is not assignable to type 'HTMLIFrameElement'."
},
{
"code": 2322,
"message": "Type 'number' is not assignable to type 'string'."
},
{
"code": 2345,
"message": "Argument of type '{ type: string; }' is not assignable to parameter of type 'Message<any>'.\n Property 'data' is missing in type '{ type: string; }' but required in type 'Message<any>'."
},
{
"code": 2322,
"message": "Type 'number' is not assignable to type 'string'."
},
{
"code": 2322,
"message": "Type 'string' is not assignable to type 'number'."
},
{
"code": 2540,
"message": "Cannot assign to 'injected' because it is a read-only property."
},
{
"code": 2322,
"message": "Type 'void' is not assignable to type 'Promise<void>'."
},
{
"code": 2339,
"message": "Property 'default' does not exist on type 'typeof ArtplayerToolIframe'."
},
{
"code": 2540,
"message": "Cannot assign to 'iframe' because it is a read-only property."
},
{
"code": 2353,
"message": "Object literal may only specify known properties, and 'id' does not exist in type 'Notification<unknown>'."
},
{
"code": 2322,
"message": "Type 'string' is not assignable to type 'number'."
},
{
"code": 2345,
"message": "Argument of type '{ type: \"response\"; data: string; id: number; }' is not assignable to parameter of type 'ProtocolMessage<number>'.\n Types of property 'data' are incompatible.\n Type 'string' is not assignable to type 'number'."
},
{
"code": 2345,
"message": "Argument of type 'string' is not assignable to parameter of type 'number | PromiseLike<number>'."
},
{
"code": 2551,
"message": "Property 'resolve' does not exist on type 'Callbacks'. Did you mean 'resove'?"
},
{
"code": 18046,
"message": "'message.data' is of type 'unknown'."
}
]
},
{
"package": "candidate",
"compiler": "5.9.3",
"mode": "nodenext-cjs",
"diagnostics": [],
"negative": [
{
"code": 2554,
"message": "Expected 1 arguments, but got 0."
},
{
"code": 2322,
"message": "Type 'string' is not assignable to type 'HTMLIFrameElement'."
},
{
"code": 2322,
"message": "Type 'number' is not assignable to type 'string'."
},
{
"code": 2345,
"message": "Argument of type '{ type: string; }' is not assignable to parameter of type 'Message<any>'.\n Property 'data' is missing in type '{ type: string; }' but required in type 'Message<any>'."
},
{
"code": 2322,
"message": "Type 'number' is not assignable to type 'string'."
},
{
"code": 2322,
"message": "Type 'string' is not assignable to type 'number'."
},
{
"code": 2540,
"message": "Cannot assign to 'injected' because it is a read-only property."
},
{
"code": 2322,
"message": "Type 'void' is not assignable to type 'Promise<void>'."
},
{
"code": 2339,
"message": "Property 'default' does not exist on type 'typeof ArtplayerToolIframe'."
},
{
"code": 2540,
"message": "Cannot assign to 'iframe' because it is a read-only property."
},
{
"code": 2353,
"message": "Object literal may only specify known properties, and 'id' does not exist in type 'Notification<unknown>'."
},
{
"code": 2322,
"message": "Type 'string' is not assignable to type 'number'."
},
{
"code": 2345,
"message": "Argument of type '{ type: \"response\"; data: string; id: number; }' is not assignable to parameter of type 'ProtocolMessage<number>'.\n Types of property 'data' are incompatible.\n Type 'string' is not assignable to type 'number'."
},
{
"code": 2345,
"message": "Argument of type 'string' is not assignable to parameter of type 'number | PromiseLike<number>'."
},
{
"code": 2551,
"message": "Property 'resolve' does not exist on type 'Callbacks'. Did you mean 'resove'?"
},
{
"code": 18046,
"message": "'message.data' is of type 'unknown'."
}
]
},
{
"package": "candidate",
"compiler": "5.9.3",
"mode": "nodenext-esm",
"diagnostics": [],
"negative": [
{
"code": 2554,
"message": "Expected 1 arguments, but got 0."
},
{
"code": 2322,
"message": "Type 'string' is not assignable to type 'HTMLIFrameElement'."
},
{
"code": 2322,
"message": "Type 'number' is not assignable to type 'string'."
},
{
"code": 2345,
"message": "Argument of type '{ type: string; }' is not assignable to parameter of type 'Message<any>'.\n Property 'data' is missing in type '{ type: string; }' but required in type 'Message<any>'."
},
{
"code": 2322,
"message": "Type 'number' is not assignable to type 'string'."
},
{
"code": 2322,
"message": "Type 'string' is not assignable to type 'number'."
},
{
"code": 2540,
"message": "Cannot assign to 'injected' because it is a read-only property."
},
{
"code": 2322,
"message": "Type 'void' is not assignable to type 'Promise<void>'."
},
{
"code": 2339,
"message": "Property 'default' does not exist on type 'typeof ArtplayerToolIframe'."
},
{
"code": 2540,
"message": "Cannot assign to 'iframe' because it is a read-only property."
},
{
"code": 2353,
"message": "Object literal may only specify known properties, and 'id' does not exist in type 'Notification<unknown>'."
},
{
"code": 2322,
"message": "Type 'string' is not assignable to type 'number'."
},
{
"code": 2345,
"message": "Argument of type '{ type: \"response\"; data: string; id: number; }' is not assignable to parameter of type 'ProtocolMessage<number>'.\n Types of property 'data' are incompatible.\n Type 'string' is not assignable to type 'number'."
},
{
"code": 2345,
"message": "Argument of type 'string' is not assignable to parameter of type 'number | PromiseLike<number>'."
},
{
"code": 2551,
"message": "Property 'resolve' does not exist on type 'Callbacks'. Did you mean 'resove'?"
},
{
"code": 18046,
"message": "'message.data' is of type 'unknown'."
}
]
},
{
"package": "candidate",
"compiler": "5.9.3",
"mode": "bundler-esm",
"diagnostics": [],
"negative": [
{
"code": 2554,
"message": "Expected 1 arguments, but got 0."
},
{
"code": 2322,
"message": "Type 'string' is not assignable to type 'HTMLIFrameElement'."
},
{
"code": 2322,
"message": "Type 'number' is not assignable to type 'string'."
},
{
"code": 2345,
"message": "Argument of type '{ type: string; }' is not assignable to parameter of type 'Message<any>'.\n Property 'data' is missing in type '{ type: string; }' but required in type 'Message<any>'."
},
{
"code": 2322,
"message": "Type 'number' is not assignable to type 'string'."
},
{
"code": 2322,
"message": "Type 'string' is not assignable to type 'number'."
},
{
"code": 2540,
"message": "Cannot assign to 'injected' because it is a read-only property."
},
{
"code": 2322,
"message": "Type 'void' is not assignable to type 'Promise<void>'."
},
{
"code": 2339,
"message": "Property 'default' does not exist on type 'typeof ArtplayerToolIframe'."
},
{
"code": 2540,
"message": "Cannot assign to 'iframe' because it is a read-only property."
},
{
"code": 2353,
"message": "Object literal may only specify known properties, and 'id' does not exist in type 'Notification<unknown>'."
},
{
"code": 2322,
"message": "Type 'string' is not assignable to type 'number'."
},
{
"code": 2345,
"message": "Argument of type '{ type: \"response\"; data: string; id: number; }' is not assignable to parameter of type 'ProtocolMessage<number>'.\n Types of property 'data' are incompatible.\n Type 'string' is not assignable to type 'number'."
},
{
"code": 2345,
"message": "Argument of type 'string' is not assignable to parameter of type 'number | PromiseLike<number>'."
},
{
"code": 2551,
"message": "Property 'resolve' does not exist on type 'Callbacks'. Did you mean 'resove'?"
},
{
"code": 18046,
"message": "'message.data' is of type 'unknown'."
}
]
},
{
"package": "candidate",
"compiler": "4.3.5",
"mode": "node10-commonjs",
"diagnostics": [],
"negative": [
{
"code": 2554,
"message": "Expected 1 arguments, but got 0."
},
{
"code": 2322,
"message": "Type 'string' is not assignable to type 'HTMLIFrameElement'."
},
{
"code": 2322,
"message": "Type 'number' is not assignable to type 'string'."
},
{
"code": 2345,
"message": "Argument of type '{ type: string; }' is not assignable to parameter of type 'Message<any>'.\n Property 'data' is missing in type '{ type: string; }' but required in type 'Message<any>'."
},
{
"code": 2322,
"message": "Type 'number' is not assignable to type 'string'."
},
{
"code": 2322,
"message": "Type 'string' is not assignable to type 'number | undefined'."
},
{
"code": 2540,
"message": "Cannot assign to 'injected' because it is a read-only property."
},
{
"code": 2322,
"message": "Type 'void' is not assignable to type 'Promise<void>'."
},
{
"code": 2339,
"message": "Property 'default' does not exist on type 'typeof ArtplayerToolIframe'."
},
{
"code": 2540,
"message": "Cannot assign to 'iframe' because it is a read-only property."
},
{
"code": 2322,
"message": "Type '{ type: string; data: number; id: number; }' is not assignable to type 'Notification<unknown>'.\n Object literal may only specify known properties, and 'id' does not exist in type 'Notification<unknown>'."
},
{
"code": 2322,
"message": "Type 'string' is not assignable to type 'number | undefined'."
},
{
"code": 2345,
"message": "Argument of type '{ type: \"response\"; data: string; id: number; }' is not assignable to parameter of type 'ProtocolMessage<number>'.\n Type '{ type: \"response\"; data: string; id: number; }' is not assignable to type '{ type: \"response\"; data: number; id: number; }'.\n Types of property 'data' are incompatible.\n Type 'string' is not assignable to type 'number'."
},
{
"code": 2345,
"message": "Argument of type 'string' is not assignable to parameter of type 'number | PromiseLike<number>'."
},
{
"code": 2551,
"message": "Property 'resolve' does not exist on type 'Callbacks'. Did you mean 'resove'?"
},
{
"code": 2571,
"message": "Object is of type 'unknown'."
}
]
},
{
"package": "candidate",
"compiler": "5.9.3",
"mode": "nodenext-cjs-no-interop",
"diagnostics": [],
"negative": [
{
"code": 2339,
"message": "Property 'default' does not exist on type 'typeof ArtplayerToolIframe'."
}
]
}
],
"runtime": [
{
"package": "published",
"commonjs": "object",
"commonjsDefault": "function",
"esmDefault": "object",
"helperIsDistinct": true,
"ssrImport": true
},
{
"package": "workspace",
"commonjs": "function",
"commonjsDefault": "undefined",
"esmDefault": "function",
"helperIsDistinct": false,
"ssrImport": true
},
{
"package": "candidate",
"commonjs": "function",
"commonjsDefault": "undefined",
"esmDefault": "function",
"helperIsDistinct": false,
"ssrImport": true
}
]
},
"diagnostics": {
"editorBefore": {
"file": "refactor/.cache/iframe04-editor-before.json",
"codes": [
2309,
2686,
2303
],
"meaning": "Frozen default/export= combination and incorrect lowercase global; both compiler versions reproduce the same three codes, in compiler-specific ordering."
},
"workspaceNodeNextEsm": {
"report": "refactor/.cache/iframe-package-types-6qThuH/workspace-5.9.3-nodenext-esm.json",
"count": 18,
"meaning": "Frozen workspace declaration represents a namespace rather than a constructable native ESM default. Candidate paired d.mts/d.cts routes pass the unchanged workspace fixture."
},
"trials": [
"refactor/.cache/iframe04-types-first.log",
"refactor/.cache/iframe04-types-second.log",
"refactor/.cache/iframe04-editor-build.log",
"refactor/.cache/iframe04-lint-fix.log"
],
"meaning": "Initial tests exposed historical diagnostic ordering/context differences and the known nullable-source callback mismatch; exact expectations are explicit. The editor build found method-signature lint errors, repaired in authored types before regeneration.",
"intermediateCi": {
"log": "refactor/.cache/iframe04-ci.log",
"tests": 1542,
"status": "pass-before-error-payload-review",
"scope": "Not final acceptance after ProtocolMessage error payload was corrected to unknown."
},
"nonStringErrors": {
"file": "refactor/.cache/iframe04-error-payload-probe.json",
"sha256": "e5d81d661be5fe555def11f4366443ce463e7b0b53fbd5cca4d11b0ac64a779e",
"observations": 10,
"scope": "Two non-Error throws across four immutable historical artifacts and candidate source produce numeric/undefined data. Permanent baseline regression repeats these checks."
}
},
"preservedDifferences": [
"Default Message.data remains required and static onMessage remains void; RuntimeConstructor exposes the actual optional-data/Promise<void> surface.",
"Default callback remains non-null while actual source allows null. The source satisfies RuntimeConstructor; exact historical mismatch 2419 is asserted separately, not hidden by skipLibCheck.",
"Default commit keeps Promise<ReturnType<T>>, including old nested-Promise inference. ResolverInstance provides an explicit result for the literal resolve(...) body protocol; neither view adds async-function-body or closure support.",
"Published Function callbacks versus callable workspace callbacks are an existing cross-package-name declaration difference, with both compiler diagnostics pinned. Old-name/helper distribution is still IFRAME-06.",
"Historical npm native ESM default is an object despite accepted old declaration usage; the candidate tool native ESM default is a class. No old package/helper alias has been delivered here.",
"No self-default is added. Old-style TypeScript CommonJS default imports require interop; modern direct import= is checked without interop.",
"Known error packets carry unknown data because thrown values need not be Error objects. New typed consumers narrow before treating the payload as text."
],
"checks": [
{
"command": "yarn ci:check",
"result": "pass",
"exitCode": 0,
"tests": 1543,
"unit": 1365,
"engineering": 14,
"baseline": 164,
"repeatedContracts": 44,
"productionTypeScriptFiles": 330,
"log": "refactor/.cache/iframe04-ci-final.log",
"sha256": "7a0b080a0e6f708c0f8bdbd831d8e93b0970b5779f4c7ded42ea96be4fc0da64"
},
{
"command": "yarn test:imports",
"result": "pass",
"tests": 3,
"log": "refactor/.cache/iframe04-imports.log"
},
{
"command": "yarn build:ts artplayer-tool-iframe",
"result": "pass",
"log": "refactor/.cache/iframe04-editor-build-verified.log"
},
{
"command": "yarn build artplayer-tool-iframe",
"result": "pass",
"log": "refactor/.cache/iframe04-build.log",
"scope": "All three runtime outputs remain byte-identical to task 03."
},
{
"command": "yarn test:iframe-types-package",
"result": "pass",
"log": "refactor/.cache/iframe04-package-verified.log",
"compilerCases": 16,
"candidateCases": 6,
"candidateNegativeDiagnostics": 81
}
],
"remaining": [
"PKG-IFRAME-05 full core/demo/BFCache/device/interrupted-navigation integration",
"PKG-IFRAME-06 old package name/helper paths, emitted consumer runtime and final distribution",
"PKG-MB-10 standalone editor treatment of split media declarations surfaced during an initial all-package generation; unrelated generated changes were restored and no completion is claimed"
],
"verifiedAt": "2026-09-12T17:41:36.279Z"
}
@@ -0,0 +1,88 @@
# PKG-IFRAME-04 public types and installed consumers
Started from 1342de94ef2e9dc3e06ddac12fcf1fc5229b648d with a clean worktree.
The previous task completed six strict source modules and native document
ownership. This task owns public declarations and their consumer/editor paths.
## Verified starting points
- Frozen npm plugin-iframe@1.0.0 has export= / global ArtplayerPluginIframe but
actually returns a CommonJS default namespace. Its extra helper is a distinct
protocol and has no dedicated declaration. No tool@1.1.0 npm archive is invented.
- Frozen/current workspace default class has required Message.data, readonly
instance fields, void static onMessage and Promise<ReturnType<T>> commit.
Runtime static onMessage is async, data may be omitted and callback can be null.
Preserve legacy extraction/replacement types; expose precise opt-in views.
- Current main/legacy return the class directly, with no self-default property.
Do not borrow factory.default declarations from other packages.
- The generated editor declaration uses lowercase artplayerToolIframe although
runtime/class is ArtplayerToolIframe, and combines default export with export=.
Reproduce and repair generation, not the generated file by hand.
## Planned acceptance
Named option/message/protocol/callback types, unchanged default class signatures,
precise optional runtime views, correct CJS/ESM declaration routing, strict
current/old compiler consumers and standalone editor generation. Isolated Yarn
pack/install must distinguish the real old npm archive, frozen workspace and
candidate, preserve exact historical errors and verify no workspace type leakage.
Legacy package-name/helper distribution remains independently tracked by 06;
full player/demo/device integration remains 05. Do not claim either passed here.
## Implementation and decisions
The default .d.ts keeps all old field/method types and adds named option, envelope,
notification and callback types. The CJS .d.cts exports the actual class with an
instance alias and type namespace; .d.mts handles native ESM. The legacy route and
typesVersions preserve old compiler resolution. No runtime default property or
JavaScript method is added. Source files and all three runtime artifacts remain
byte-identical to task 03; package/editor declaration routes are tested separately.
RuntimeConstructor/RuntimeInstance opt into async static receipt, optional outgoing
data and a nullable typed callback. Generic response T is an application assertion,
not payload validation. ResolverInstance gives an explicit result for the existing
literal resolve(...) function-body protocol. The default nested-Promise/void commit
inference remains unchanged; no async-function-body or closure execution is added.
Independent review proved error payloads may be numbers or undefined when executed
code throws non-Error values, in four historical main/legacy builds and candidate
source. The new ProtocolMessage error data therefore uses unknown with narrowing;
the first green CI predated this correction and is retained as intermediate only.
The frozen editor yields 2309/2686/2303; semantic class generation removes the
default/export= conflict and uses ArtplayerToolIframe with named-type aliases.
build:ts accepts selected package names to regenerate the current package without
rewriting unrelated packages mid-migration. Its old all-package mode remains;
the initial all-package run exposed split MediaBunny media declaration treatment,
now explicitly assigned to MB-10. No hand-editing of generated declarations.
Isolated Yarn installs cover the real npm archive, a pack made exclusively from
frozen Git inputs, and candidate pack. Every installed file matches its archive;
frozen reinstall preserves the lock and compiler files cannot resolve outside the
consumer except standard libraries. Old workspace NodeNext ESM has 18 exact
diagnostics; candidate accepts the unchanged fixture. Published Function callback
fields and its CJS namespace are existing differences from workspace callable
fields/direct class, not silently rewritten. Old package/helper facade and emitted
consumer compatibility stay under IFRAME-06, with IFRAME-DIST-01 still open.
The default non-null callback also differs from source's nullable field. Tests pin
exact diagnostic 2419 for the old view and prove the source satisfies the opt-in
RuntimeConstructor. This documents a preserved type boundary, not skipLibCheck.
No new dependencies, version changes, push or publish. Final checks and hashes
are in baselines/iframe-types-validation.json. Roll back this task's declarations,
manifest and generator changes together, then regenerate the iframe editor types.
Completion subject: `refactor(iframe): [PKG-IFRAME-04] preserve types across module consumers`.
## Final verification
Full CI passes 1543 (1365 unit + 14 engineering + 164 baseline), 44 repeated
contracts and 330 production TS files. The four new baseline tests include
positive/negative views, exact historical differences, error-payload runtime
evidence and both-compiler standalone editor checks. Installed consumers verify
16 compiler cases across three archives, including six candidate cases with
81 rejected invalid uses; old workspace ESM errors stay explicit negative controls.
Three import/SSR files pass. Source/declaration/editor hashes, final tarball bytes,
all runtime source modules and byte-identical runtime/docs outputs are verified.
Package-name/helper and full integration gates remain open under 05/06. This is
a declaration/consumer completion, not an npm release or final browser/demo review.
@@ -0,0 +1,37 @@
import Iframe from 'artplayer-tool-iframe'
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 OldMessage { type: string, data: any, id?: number }
type ExactConstructor = Assert<Equal<ConstructorParameters<typeof Iframe>, [option: { iframe: HTMLIFrameElement, url: string }]>>
type ExactMessage = Assert<Equal<Parameters<Iframe['postMessage']>, [message: OldMessage]>>
type ExactStaticReceiver = Assert<Equal<ReturnType<typeof Iframe.onMessage>, void>>
type ExactCallbacks = Assert<Equal<Iframe['promises'], Record<number, { resove: (...args: any[]) => any, reject: (...args: any[]) => any }>>>
type ExactCallback = Assert<Equal<Iframe['messageCallback'], (...args: any[]) => any>>
declare const frame: HTMLIFrameElement
const tool: Iframe = new Iframe({ iframe: frame, url: '/iframe.html' })
const message: OldMessage = { type: 'custom', data: { enabled: true }, id: 1 }
Iframe.postMessage(message)
Iframe.inject()
const staticResult: void = Iframe.onMessage(new MessageEvent('message', { data: message }))
tool.onMessage(new MessageEvent('message', { data: message }))
const custom: Promise<any> = tool.postMessage(message)
const sync: Promise<number> = tool.commit(() => {
return 1
})
const nested: Promise<Promise<number>> = tool.commit(() => {
return Promise.resolve(1)
})
const resolver: Promise<void> = tool.commit((resolve: (value: number) => void) => {
resolve(1)
})
tool.message((value, additional) => [value, additional])
tool.promises[1] = { resove: (...args: any[]) => args, reject: (...args: any[]) => args }
tool.promises[1].resove(1, 2)
tool.promises[1].reject(new Error('old'))
const replacement: typeof Iframe.onMessage = () => {}
const enabled: boolean = Iframe.iframe
tool.destroy()
void [staticResult, custom, sync, nested, resolver, replacement, enabled]
export type { ExactCallback, ExactCallbacks, ExactConstructor, ExactMessage, ExactStaticReceiver }
+5 -4
View File
@@ -4,7 +4,7 @@
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 222 项,范围 22 个包及工作区/示例。
状态:todo 105 / doing 9 / blocked 0 / done 108 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
状态:todo 104 / doing 9 / blocked 0 / done 109 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
@@ -338,7 +338,7 @@
| PKG-MB-07 | artplayer-proxy-mediabunny<br>整理 HLS 配对轨道与 selector | PKG-MB-05, PKG-MB-06, CORE-14 | m3u8 配对、质量/音频选择及拓扑清理 | 实际选择高亮、切到无轨道来源清理、无重复 readiness | H | done |
| PKG-MB-08 | artplayer-proxy-mediabunny<br>完成 TS 与媒体能力声明 | PKG-MB-07, ENG-04, ENG-06 | 8 个自有 JS 模块迁移与 Result/shim 类型 | 解码器/Stream/DOM 类型清楚,旧 Option 和 art.mediabunny 使用保持 | H | done |
| PKG-MB-09 | artplayer-proxy-mediabunny<br>完成新旧核心和真实媒体组合 | PKG-MB-08, CORE-22 | 跨浏览器能力、长播放、DPiP、HLS 音轨/质量报告 | 资源释放、事件顺序、AV sync 与支持范围满足基线 | H | doing |
| PKG-MB-10 | artplayer-proxy-mediabunny<br>完成分发和文档 | PKG-MB-09, ENG-07 | mediabunny.js、README、依赖版本、三产物和许可 | tarball 可消费、无意外依赖升级、旧调用与回退可用 | H | todo |
| PKG-MB-10 | artplayer-proxy-mediabunny<br>完成分发和文档 | PKG-MB-09, ENG-07 | mediabunny.js、README、依赖版本、三产物和许可; 拆分media声明后的独立编辑器生成/辅助文件过滤与语义检查(IFRAME-04全量生成时暴露) | tarball 可消费、无意外依赖升级、旧调用与回退可用 | H | todo |
## 5 包迁移:artplayer-tool-iframe
@@ -347,9 +347,9 @@
| PKG-IFRAME-01 | artplayer-tool-iframe<br>核对包契约与历史用法 | BASE-05 | constructor/commit/message/inject、postMessage 协议与历史公开拼写 | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结 | H | done |
| PKG-IFRAME-02 | artplayer-tool-iframe<br>建立特有行为与错误测试 | PKG-IFRAME-01, ENG-03, ENG-05 | 跨窗口消息、ID 匹配、请求失败、重复 inject、销毁中请求 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | done |
| PKG-IFRAME-03 | artplayer-tool-iframe<br>整理内部职责与资源 | PKG-IFRAME-02, CORE-02, BASE-07 | 请求注册/响应匹配/监听清理分离;origin/source 安全边界独立决策 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | done |
| PKG-IFRAME-04 | artplayer-tool-iframe<br>迁移自有源码和公开类型 | PKG-IFRAME-03, ENG-04, ENG-06, CORE-07 | 消息联合类型、回调/Promise 推导、旧公开字段兼容;旧npm export=与实际namespace、额外helper协议分别核验 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | todo |
| PKG-IFRAME-04 | artplayer-tool-iframe<br>迁移自有源码和公开类型 | PKG-IFRAME-03, ENG-04, ENG-06, CORE-07 | 消息联合类型、回调/Promise 推导、旧公开字段兼容;旧npm export=与实际namespace、额外helper协议分别核验 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | done |
| PKG-IFRAME-05 | artplayer-tool-iframe<br>验证新旧核心和组合 | PKG-IFRAME-04, CORE-22 | 真实同源/跨源 iframe、既有 commit 协议;安全变化有独立结论;新旧核心/demo、实际 BFCache/设备和外部中断导航验收,旧端无文档标记的限制明确 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo |
| PKG-IFRAME-06 | artplayer-tool-iframe<br>验证分发并同步文档 | PKG-IFRAME-05, ENG-07 | iframe.js、示例集成和原 script/class 导出验证;旧包名/额外helper深入口与新工具名的迁移结论 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo |
| PKG-IFRAME-06 | artplayer-tool-iframe<br>验证分发并同步文档 | PKG-IFRAME-05, ENG-07 | iframe.js、示例集成和原 script/class 导出验证;旧包名/额外helper深入口与新工具名的迁移结论; 保留旧npm Function回调及namespace/helper与工具类的区别,核验实际编译后消费而非仅声明通过 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo |
## 5 包迁移:artplayer-tool-thumbnail
@@ -533,4 +533,5 @@
- PKG-IFRAME-01: [记录](changes/2026-09-12-PKG-IFRAME-01-contract.md) [记录](baselines/iframe-release.json) [记录](baselines/iframe-contract.md) [记录](baselines/iframe-contract-validation.json)
- PKG-IFRAME-02: [记录](changes/2026-09-12-PKG-IFRAME-02-behavior.md) [记录](baselines/iframe-behavior-validation.json)
- PKG-IFRAME-03: [记录](changes/2026-09-12-PKG-IFRAME-03-requests-checkpoint.md) [记录](baselines/iframe-requests-checkpoint.json) [记录](changes/2026-09-12-PKG-IFRAME-03-boundaries-checkpoint.md) [记录](baselines/iframe-boundaries-checkpoint.json) [记录](iframe-message-boundary.md) [记录](changes/2026-09-13-PKG-IFRAME-03-navigation.md) [记录](baselines/iframe-navigation-validation.json) [记录](iframe-document-protocol.md)
- PKG-IFRAME-04: [记录](changes/2026-09-13-PKG-IFRAME-04-types.md) [记录](baselines/iframe-types-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)
+14
View File
@@ -1,5 +1,19 @@
# 进度与证据
## PKG-IFRAME-04 完成:公开类型、模块入口和编辑器
保留默认类的required data、void静态接收、readonly、resove及旧commit推导,增加
可选Runtime/Resolver视图、消息联合与命名类型。CJS/ESM声明分别路由,旧TS通过
types/legacy入口;无运行时self-default。编辑器使用实际大写类名并语义生成声明。
旧工作区NodeNext ESM的18条错误已复现并修复;旧编辑器三条错误在两编译器复现。
独立Yarn安装旧npm、冻结工作区和候选共16编译组合,候选6组/81非法使用被拒绝;
历史Function字段、namespace/helper及nullable回调区别明确保留,05/06仍待验收。
复查发现非Error抛出可产生非字符串error载荷,已用四历史产物与源码复现并修正类型。
最终CI1543与44重复契约、3导入/SSR通过,330生产TS。三运行产物及源码字节未变,
沿用03的真实浏览器证据,不冒称新跑浏览器。分包编辑器脚本避免改写迁移中的其他包。
见[变更](changes/2026-09-13-PKG-IFRAME-04-types.md)和[验证](baselines/iframe-types-validation.json)。
222项:109 done、9 doing、104 todo;本项独立本地提交,三个Iframe风险仍open。
## PKG-IFRAME-03 完成:按文档管理请求与六模块拆分
入口、连接、请求、消息边界、父页导航和子页生命周期拆为六个严格TS模块。
+4 -2
View File
@@ -3827,12 +3827,14 @@
],
"status": "open",
"confirmation": "reproduced",
"compatibleResolution": "Keep actual old plugin/helper exports distinct from workspace tool and document package-name/type/export migration; do not fabricate a tool release or use a renamed implementation as an old archive.",
"compatibleResolution": "IFRAME-04 adds paired CJS/ESM declarations and verifies three actual isolated installs, including old namespace/helper and existing Function callback differences. Keep old package-name/helper facade, emitted-consumer runtime and final distribution gates with IFRAME-06; the time-scoped tool registry observation is not a release claim.",
"closureCriteria": "Actual installed entry/declaration tests and documented old-name/helper/new-tool distribution behavior; registry observation remains time scoped.",
"evidence": [
"refactor/baselines/iframe-contract.md",
"refactor/baselines/iframe-release.json",
"refactor/changes/2026-09-12-PKG-IFRAME-01-contract.md"
"refactor/changes/2026-09-12-PKG-IFRAME-01-contract.md",
"refactor/changes/2026-09-13-PKG-IFRAME-04-types.md",
"refactor/baselines/iframe-types-validation.json"
]
}
]
+112
View File
@@ -0,0 +1,112 @@
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 { verifyIframeContract } from './iframe-contract.mjs'
import { hash, readMember } from './releases.mjs'
async function main() {
assert.equal(process.env.npm_config_user_agent?.split(' ')[0], 'yarn/1.22.22', 'Use yarn test:iframe-types-package')
const yarn = process.env.npm_execpath
assert(yarn && fs.existsSync(yarn))
const output = fs.mkdtempSync(path.join(workspace, 'refactor/.cache/iframe-package-types-'))
console.log(`Iframe installed package evidence: ${output}`)
const contract = await verifyIframeContract()
const frozen = path.join(output, 'frozen-workspace')
fs.mkdirSync(frozen)
const prefix = 'packages/artplayer-tool-iframe/'
for (const [file, bytes] of contract.sources) {
if (!file.startsWith(prefix))
continue
const target = path.join(frozen, file.slice(prefix.length))
fs.mkdirSync(path.dirname(target), { recursive: true })
fs.writeFileSync(target, bytes)
}
const packages = [{ label: 'published', name: contract.baseline.release.name, archive: contract.archive, sha256: contract.baseline.release.sha256, files: contract.baseline.release.files }]
for (const [label, folder] of [['workspace', frozen], ['candidate', path.join(workspace, 'packages/artplayer-tool-iframe')]]) {
const archive = path.join(output, `${label}.tgz`)
fs.writeFileSync(path.join(output, `${label}-pack.log`), run([yarn, 'pack', '--filename', archive], folder))
const manifest = JSON.parse(readMember(archive, 'package/package.json'))
const files = packedFiles(archive)
checkFiles(manifest, files)
packages.push({ label, name: manifest.name, archive, sha256: hash(fs.readFileSync(archive)), files: Object.fromEntries(files.map(member => [member, hash(readMember(archive, member))])) })
}
const matrix = []
const runtime = []
for (const pkg of packages) {
const consumer = consumerDirectory()
try {
writeJson(path.join(consumer, 'package.json'), { name: 'iframe-isolated-consumer', private: true, dependencies: { [pkg.name]: `file:${pkg.archive.replaceAll('\\', '/')}` } })
fs.writeFileSync(path.join(output, `${pkg.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, `${pkg.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, `${pkg.label}-yarn.lock`), lock)
const installed = path.join(consumer, 'node_modules', pkg.name)
assert.equal(fs.realpathSync(installed), installed, 'Consumer must not use workspace links')
for (const [member, expected] of Object.entries(pkg.files))
assert.equal(hash(fs.readFileSync(path.join(installed, member.slice(8)))), expected, member)
const runtimeFile = path.join(consumer, 'runtime.cjs')
fs.writeFileSync(runtimeFile, `const assert = require('node:assert/strict');
const value = require('${pkg.name}');
assert.equal(typeof value, '${pkg.label === 'published' ? 'object' : 'function'}');
assert.equal(typeof value.default, '${pkg.label === 'published' ? 'function' : 'undefined'}');
${pkg.label === 'published' ? `for (const suffix of ['js', 'legacy.js']) { const helper = require('${pkg.name}/dist/artplayer-helper-iframe.' + suffix); assert.equal(typeof helper.default, 'function'); assert.equal(typeof helper.default.destroy, 'function'); assert.equal(typeof value.default.destroy, 'undefined'); }` : `const legacy = require('${pkg.name}/legacy'); assert.equal(typeof legacy, 'function'); assert.equal(legacy.default, undefined);`}
import('${pkg.name}').then(module => {
assert.equal(typeof module.default, '${pkg.label === 'published' ? 'object' : 'function'}');
${pkg.label === 'published' ? '' : 'assert.deepEqual(Object.keys(module), [\'default\']);'}
console.log(JSON.stringify({ commonjs: typeof value, commonjsDefault: typeof value.default, esmDefault: typeof module.default, helperIsDistinct: ${pkg.label === 'published'}, ssrImport: true }));
}).catch(error => { console.error(error); process.exitCode = 1; });`)
const observation = run([runtimeFile], consumer)
fs.writeFileSync(path.join(output, `${pkg.label}-runtime.log`), observation)
runtime.push({ package: pkg.label, ...JSON.parse(observation.trim()) })
const modes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']]
if (pkg.label === 'candidate')
modes.push([ts, 'nodenext-cjs-no-interop'])
for (const [compiler, mode] of modes) {
const next = mode.startsWith('nodenext')
const noInterop = mode.endsWith('no-interop')
const filename = path.join(consumer, `consumer.${next ? mode.includes('-cjs') ? 'cts' : 'mts' : 'ts'}`)
const options = { strict: true, noEmit: true, skipLibCheck: false, types: [], esModuleInterop: !noInterop, 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 }
let source = fs.readFileSync(path.join(workspace, pkg.label === 'candidate' ? noInterop ? 'test/types/iframe-commonjs.cts' : 'test/types/iframe.ts' : 'refactor/fixtures/consumers/iframe-workspace.ts'), 'utf8')
const files = [filename]
if (pkg.label === 'published')
source = source.replaceAll('artplayer-tool-iframe', pkg.name).replace('Record<number, { resove: (...args: any[]) => any, reject: (...args: any[]) => any }>', 'Record<number, { resove: Function, reject: Function }>')
if (pkg.label === 'candidate' && !noInterop) {
const oldFile = path.join(consumer, path.basename(filename).replace('consumer', 'historical'))
fs.copyFileSync(path.join(workspace, 'refactor/fixtures/consumers/iframe-workspace.ts'), oldFile)
files.push(oldFile)
}
function compile(code) {
fs.writeFileSync(filename, code)
const program = compiler.createProgram(files, options)
for (const file of program.getSourceFiles()) {
const real = fs.realpathSync(file.fileName)
assert(real.startsWith(fs.realpathSync(consumer) + path.sep) || (program.isSourceFileDefaultLibrary(file) && path.dirname(real) === fs.realpathSync(path.dirname(compiler.sys.getExecutingFilePath()))), `Type escaped installed consumer: ${real}`)
}
return compiler.getPreEmitDiagnostics(program).map(item => ({ code: item.code, message: compiler.flattenDiagnosticMessageText(item.messageText, '\n').replaceAll(consumer.replaceAll('\\', '/'), '<consumer>') }))
}
const diagnostics = compile(source)
const expected = pkg.label === 'workspace' && mode === 'nodenext-esm' ? [2344, 2344, 2344, 2709, 2339, 2344, 2709, 2344, 2709, 2709, 2351, 2339, 2339, 2339, 7006, 7006, 2339, 2339] : []
writeJson(path.join(output, `${pkg.label}-${compiler.version}-${mode}.json`), diagnostics)
assert.deepEqual(diagnostics.map(item => item.code), expected, `${pkg.label} ${compiler.version} ${mode}`)
const negative = pkg.label === 'candidate' ? compile(source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')) : []
if (pkg.label === 'candidate')
assert.equal(negative.length, noInterop ? 1 : 16)
matrix.push({ package: pkg.label, compiler: compiler.version, mode, diagnostics, negative })
}
}
finally { removeConsumer(consumer) }
}
writeJson(path.join(output, 'report.json'), { task: 'PKG-IFRAME-04', scope: 'Actual npm archive, frozen Git workspace pack and candidate Yarn tarball installed outside workspace; byte verification, offline/frozen lock and strict positive/negative consumers. Old npm CJS namespace and distinct helper are preserved observations, not claims the candidate provides old package-name/helper aliases. Native player and distribution migration remain 05/06.', packages, matrix, runtime })
console.log(`Iframe installed types verified: ${matrix.length} compiler cases; ${output}`)
}
main().catch((error) => {
console.error(error)
process.exitCode = 1
})
+105
View File
@@ -0,0 +1,105 @@
import assert from 'node:assert/strict'
import fs from 'node:fs'
import path from 'node:path'
// eslint-disable-next-line test/no-import-node-test -- Compiler and historical declaration contract runner.
import test from 'node:test'
import { ESLint } from 'eslint'
import ts from 'typescript'
import compat from 'typescript-compat'
import { checkPluginEditorDeclaration, generatePluginEditorDeclaration } from '../../scripts/plugin-editor-types.mjs'
import { checkConsumer } from '../../scripts/typecheck.mjs'
import { iframeCandidate, iframeEnvironment, iframeHistorical } from '../../test/helpers/iframe.js'
import { verifyIframeContract } from './iframe-contract.mjs'
import { readMember } from './releases.mjs'
const modes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']]
const invalid = code => code.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')
test('Iframe error envelopes preserve non-string thrown messages rather than claiming string-only payloads', async () => {
const implementations = [...(await iframeHistorical()).filter(item => !item.name.includes('helper')), await iframeCandidate()]
for (const implementation of implementations) {
for (const [body, expected] of [['throw { message: 7 }', 7], ['throw 3', undefined]]) {
const env = iframeEnvironment(implementation, true)
await assert.rejects(env.Factory.onMessage({ data: { type: 'commit', data: body, id: 1 }, source: env.box.parent }), () => true)
assert.equal(env.sent.length, 1)
assert.equal(env.sent[0].packet.type, 'error')
assert.equal(env.sent[0].packet.data, expected)
assert.equal(env.sent[0].packet.id, 1)
}
}
})
test('Iframe public types preserve legacy extraction while precise views reject invalid protocol calls', () => {
const source = fs.readFileSync('test/types/iframe.ts', 'utf8')
for (const [compiler, mode] of modes) {
assert.deepEqual(checkConsumer(compiler, mode, source), [], `${compiler.version} ${mode}`)
assert.equal(checkConsumer(compiler, mode, invalid(source)).length, 16)
assert.deepEqual(checkConsumer(compiler, mode, fs.readFileSync('refactor/fixtures/consumers/iframe-workspace.ts', 'utf8')), [])
}
const commonjs = fs.readFileSync('test/types/iframe-commonjs.cts', 'utf8')
assert.deepEqual(checkConsumer(ts, 'nodenext-cjs', commonjs), [])
assert.equal(checkConsumer(ts, 'nodenext-cjs', invalid(commonjs)).length, 1)
const implementationSource = fs.readFileSync('test/types/iframe-source.ts', 'utf8')
assert.deepEqual(checkConsumer(ts, 'node10-commonjs', implementationSource), [])
const implementation = checkConsumer(ts, 'node10-commonjs', invalid(implementationSource))
assert.deepEqual(implementation.map(item => item.code), [2419], 'Only the preserved non-null legacy callback differs from the actual constructor')
assert(implementation[0].message.includes('messageCallback') && implementation[0].message.includes('null'))
})
test('Iframe frozen workspace keeps exact default signatures and published Function callbacks remain a separate legacy boundary', async () => {
const contract = await verifyIframeContract()
const source = fs.readFileSync('refactor/fixtures/consumers/iframe-workspace.ts', 'utf8')
const filename = path.resolve('test/types/iframe-history.ts')
const declaration = path.resolve('test/types/iframe-history.d.ts')
for (const compiler of [ts, compat]) {
function historical(code, definitions) {
const options = { strict: true, noEmit: true, skipLibCheck: false, types: [], esModuleInterop: true, module: compiler.ModuleKind.CommonJS, moduleResolution: compiler.ModuleResolutionKind.NodeJs, target: compiler.ScriptTarget.ES2020, lib: ['lib.es2020.d.ts', 'lib.dom.d.ts'] }
const host = compiler.createCompilerHost(options)
const original = host.getSourceFile.bind(host)
host.getSourceFile = (file, language, ...rest) => path.resolve(file) === filename
? compiler.createSourceFile(file, code.replaceAll('artplayer-tool-iframe', './iframe-history'), language, true)
: path.resolve(file) === declaration ? compiler.createSourceFile(file, definitions, language, true) : original(file, language, ...rest)
const exists = host.fileExists.bind(host)
host.fileExists = file => path.resolve(file) === declaration || exists(file)
const program = compiler.createProgram([filename, declaration], options, host)
assert(program.getSourceFile(declaration))
return compiler.getPreEmitDiagnostics(program).map(item => item.code)
}
const frozen = contract.sources.get('packages/artplayer-tool-iframe/types/artplayer-tool-iframe.d.ts')
const published = readMember(contract.archive, 'package/types/artplayer-plugin-iframe.d.ts').toString()
assert.deepEqual(historical(source, frozen), [])
assert.deepEqual(historical(source, published), [2344], 'Published Function versus workspace callable field is a pre-refactor difference')
const broad = `import Iframe from 'artplayer-tool-iframe'; declare const tool: Iframe; declare const callback: Function; tool.promises[1] = { resove: callback, reject: callback };`
assert.deepEqual(historical(broad, published), [])
assert.deepEqual(historical(broad, frozen), [2322, 2322])
assert.deepEqual(checkConsumer(compiler, 'node10-commonjs', broad).map(item => item.code), [2322, 2322])
assert.deepEqual(historical(`${source}\nIframe.postMessage({ type: 'custom' });`, frozen), [2345])
assert.deepEqual(checkConsumer(compiler, 'node10-commonjs', `${source}\nIframe.postMessage({ type: 'custom' });`).map(item => item.code), [2345])
}
})
test('Iframe standalone editor generation fixes the frozen export errors and preserves class plus named types', async () => {
const contract = await verifyIframeContract()
const historical = contract.sources.get('docs/assets/ts/artplayer-tool-iframe.d.ts')
const source = fs.readFileSync('packages/artplayer-tool-iframe/types/artplayer-tool-iframe.d.ts', 'utf8')
const generated = generatePluginEditorDeclaration(source, 'ArtplayerToolIframe')
const file = 'docs/assets/ts/artplayer-tool-iframe.d.ts'
const [formatted] = await new ESLint({ fix: true, fixTypes: ['layout'] }).lintText(generated, { filePath: file })
assert.equal(formatted.errorCount, 0)
assert.equal(fs.readFileSync(file, 'utf8').replaceAll('\r\n', '\n'), formatted.output || generated)
const consumer = `declare const frame: HTMLIFrameElement;
const option: ArtplayerToolIframe.Option = { iframe: frame, url: '/frame' };
const tool: ArtplayerToolIframe = new ArtplayerToolIframe(option);
const runtime = ArtplayerToolIframe as ArtplayerToolIframe.RuntimeConstructor;
runtime.postMessage({ type: 'custom' });
const promise: Promise<void> = runtime.onMessage(new MessageEvent('message', { data: { type: 'custom' } }));
// @ts-expect-error No lowercase runtime global exists.
new artplayerToolIframe(option);
// @ts-expect-error URL remains text.
new ArtplayerToolIframe({ iframe: frame, url: 1 });`
for (const compiler of [ts, compat]) {
assert.deepEqual(checkPluginEditorDeclaration(historical, '', '', compiler).map(item => item.code).sort(), [2303, 2309, 2686])
assert.deepEqual(checkPluginEditorDeclaration(generated, '', consumer, compiler), [])
assert.equal(checkPluginEditorDeclaration(generated, '', invalid(consumer), compiler).length, 2)
}
})
+7 -4
View File
@@ -3598,7 +3598,7 @@
],
"status": "todo",
"risk": "H",
"deliverable": "mediabunny.js、README、依赖版本、三产物和许可",
"deliverable": "mediabunny.js、README、依赖版本、三产物和许可; 拆分media声明后的独立编辑器生成/辅助文件过滤与语义检查(IFRAME-04全量生成时暴露)",
"acceptance": "tarball 可消费、无意外依赖升级、旧调用与回退可用",
"evidence": []
},
@@ -3684,11 +3684,14 @@
"ENG-06",
"CORE-07"
],
"status": "todo",
"status": "done",
"risk": "H",
"deliverable": "消息联合类型、回调/Promise 推导、旧公开字段兼容;旧npm export=与实际namespace、额外helper协议分别核验",
"acceptance": "严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容",
"evidence": []
"evidence": [
"changes/2026-09-13-PKG-IFRAME-04-types.md",
"baselines/iframe-types-validation.json"
]
},
{
"id": "PKG-IFRAME-05",
@@ -3720,7 +3723,7 @@
],
"status": "todo",
"risk": "H",
"deliverable": "iframe.js、示例集成和原 script/class 导出验证;旧包名/额外helper深入口与新工具名的迁移结论",
"deliverable": "iframe.js、示例集成和原 script/class 导出验证;旧包名/额外helper深入口与新工具名的迁移结论; 保留旧npm Function回调及namespace/helper与工具类的区别,核验实际编译后消费而非仅声明通过",
"acceptance": "tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录",
"evidence": []
},