From 4f39d6f2b964d47437e164c15127b610bf693cfb Mon Sep 17 00:00:00 2001 From: Harvey Zhao Date: Sat, 12 Sep 2026 09:06:10 +0800 Subject: [PATCH] test(vast): [PKG-VAST-01] freeze published and workspace contracts --- .../baselines/vast-contract-validation.json | 144 +++++++++ refactor/baselines/vast-contract.md | 47 +++ refactor/baselines/vast-release.json | 284 ++++++++++++++++++ .../2026-09-12-PKG-VAST-01-contract.md | 26 ++ refactor/plan.md | 15 +- refactor/progress.md | 10 + refactor/risk-table.md | 4 + refactor/risks.json | 83 ++++- refactor/scripts/vast-contract.mjs | 43 +++ refactor/scripts/vast-contract.test.mjs | 137 +++++++++ refactor/tasks.json | 21 +- 11 files changed, 797 insertions(+), 17 deletions(-) create mode 100644 refactor/baselines/vast-contract-validation.json create mode 100644 refactor/baselines/vast-contract.md create mode 100644 refactor/baselines/vast-release.json create mode 100644 refactor/changes/2026-09-12-PKG-VAST-01-contract.md create mode 100644 refactor/scripts/vast-contract.mjs create mode 100644 refactor/scripts/vast-contract.test.mjs diff --git a/refactor/baselines/vast-contract-validation.json b/refactor/baselines/vast-contract-validation.json new file mode 100644 index 000000000..021f416fa --- /dev/null +++ b/refactor/baselines/vast-contract-validation.json @@ -0,0 +1,144 @@ +{ + "schemaVersion": 1, + "task": "PKG-VAST-01", + "sourceCommit": "29692d28158ad50e1d404293f592679c10dbf3f5", + "scope": "Immutable published archives and frozen-source wrapper contracts only; no production changes", + "inputs": [ + { + "file": "refactor/baselines/vast-release.json", + "sha256LF": "493e15fa8d3cd1a9215a409dd78db61ecaef5f82f9629dfdc3f6090e7fad8453" + }, + { + "file": "refactor/scripts/vast-contract.mjs", + "sha256LF": "718ec6c0765caa7c9604e55d10a3b6c6dc25bdd42fc30d6b6097200888a7dd9e" + }, + { + "file": "refactor/scripts/vast-contract.test.mjs", + "sha256LF": "b9cfa09402b2f7e91dfead607c303015967c2cc0d96353029bbda798c3b56bc9" + } + ], + "archives": [ + { + "name": "artplayer-plugin-vast", + "version": "1.0.0", + "sha256": "35681d60fa3fa09b92f0f30887e39c73cf55e7b14d51a1c38fccbc6c1f253bab", + "members": 6 + }, + { + "name": "@glomex/vast-ima-player", + "version": "1.21.0", + "sha256": "924405b92ac5ccb4cc775d13b5caba6dc6fb537d55b0cb3ca128cb9be4b2bc48", + "members": 27 + }, + { + "name": "@glomex/vast-ima-player", + "version": "1.21.2", + "sha256": "5b5575c3a6c68a2a550c986348093de7471476318c9ec443285063dfbef3f9b5", + "members": 27 + } + ], + "historicalCore": { + "commit": "252c226473792fd1537c1ff4a323344bf1060efc", + "file": "packages/artplayer/package.json", + "version": "5.1.7", + "sha256LF": "8bd532103099c367fb2c289e0d410f144fdeb00b1b62cf850762a24b61e63c8a", + "note": "Git association only; actual core tarball and browser combination remain task02/05.", + "limitation": "Git association only; actual core tarball and browser execution pending PKG-VAST-02/05" + }, + "currentWorkspaceTypes": { + "source": "import vast from \"artplayer-plugin-vast\"; vast(({imaPlayer})=>{void imaPlayer});", + "result": [ + { + "compiler": "5.9.3", + "diagnostics": [ + { + "file": "node_modules/@glomex/vast-ima-player/dist/index.d.ts", + "code": 2307, + "line": 1, + "message": "Cannot find module '@alugha/ima' or its corresponding type declarations." + }, + { + "file": "node_modules/@glomex/vast-ima-player/dist/vast-ima-player.d.ts", + "code": 2307, + "line": 6, + "message": "Cannot find module '@alugha/ima' or its corresponding type declarations." + } + ] + }, + { + "compiler": "4.3.5", + "diagnostics": [ + { + "file": "node_modules/@glomex/vast-ima-player/dist/index.d.ts", + "code": 2307, + "line": 1, + "message": "Cannot find module '@alugha/ima' or its corresponding type declarations." + }, + { + "file": "node_modules/@glomex/vast-ima-player/dist/vast-ima-player.d.ts", + "code": 2307, + "line": 6, + "message": "Cannot find module '@alugha/ima' or its corresponding type declarations." + } + ] + } + ], + "rawEvidence": { + "file": "refactor/.cache/vast01-current-types.json", + "sha256": "c9aa1d29b2ed4204e3e62f38b10e5dc4a81486a1b241e40d7b0424689080b32c" + } + }, + "checks": [ + { + "command": "eslint refactor/scripts/vast-contract.mjs refactor/scripts/vast-contract.test.mjs", + "result": "pass" + }, + { + "command": "yarn test:baseline", + "result": "pass", + "tests": 53, + "passed": 53, + "failed": 0, + "skipped": 0, + "log": { + "file": "refactor/.cache/vast01-baseline.log", + "sha256": "2bc059268118cd284ffdadfaf9d05481b368e4abceabb73cf7270660824ea2c8" + } + }, + { + "command": "yarn lint", + "result": "pass", + "log": { + "file": "refactor/.cache/vast01-lint.log", + "sha256": "219a458ee587661bd80c1b6c34d49079560b732dcabe35afb30a804408d89277" + } + }, + { + "command": "node refactor/scripts/plan.mjs --check", + "result": "pass", + "tasks": 217, + "packages": 22, + "states": { + "done": 79, + "doing": 5, + "todo": 133 + } + }, + { + "command": "node refactor/scripts/risk-register.mjs --check", + "result": "pass", + "risks": 147, + "vendoredGroups": 8, + "integrations": 12 + } + ], + "fixtureCorrection": "First export VM omitted CustomEvent and triggered document fallback; supplying native platform CustomEvent fixed the fixture. This does not prove generic production SSR support.", + "limitations": [ + "Injected SDK Player records wrapper behavior; no Google IMA media execution", + "Type diagnostic is current workspace evidence, not yet isolated installation proof", + "VAST VPN exception recorded but not exercised", + "VAST-CONTEXT-01, VAST-TYPE-01, VAST-LIFE-01, VAST-DIST-01 and SDK-07 remain open" + ], + "status": "done", + "validationScope": "Targeted contract tests, all 53 baseline tests, repository lint, plan and risk checks. No production changes; no new full CI/build/media run for this baseline-only task." +} diff --git a/refactor/baselines/vast-contract.md b/refactor/baselines/vast-contract.md new file mode 100644 index 000000000..25d24bc5d --- /dev/null +++ b/refactor/baselines/vast-contract.md @@ -0,0 +1,47 @@ +# VAST 实际发布契约 + +PKG-VAST-01;固定内容见 [发布及SDK归档](vast-release.json)。2026-09-12查阅npm注册表, +VAST仅发布1.0.0(2024-10-12),工作区1.2.0未发布;目标依既定政策为2.0.0,正式 +发布前重新核对版本占用。此处不是未来latest承诺。 + +## 发布版与工作区差异 + +| 契约 | 实际npm 1.0.0 | 冻结工作区1.2.0 | 后续处理 | +| --- | --- | --- | --- | +| 工厂 | `factory(callback)(art)`,第二层async;先await SDK,再await callback | 同样async,可省略callback | 保持Promise与回调完成顺序,补错误/销毁竞态测试 | +| 公开声明 | export=、全局namespace;第二层错误声明为同步Result;callback参数必需 | default export;仍错误声明同步Result;新增Instance/Option类型 | 协调实际Promise与旧类型消费者,不把Ads专属批准自动扩大到VAST | +| callback字段 | art/id/ima/imaPlayer/$container/playUrl/playRes | art/playUrl/playRes/init/ima/adsRenderingSettings/playerOptions;imaPlayer/container为getter | 恢复真实发布别名并评估两套消费形状,不能漏掉旧id/$container | +| 初始化 | 回调前创建Player和container,imaPlayer非空 | init/playUrl/playRes触发懒初始化,回调初始imaPlayer/container为null | 立即初始化与惰性行为存在真实冲突,明确处置而非假称两者一致 | +| 请求 | playUrl写adTagUrl;playRes写adsResponse;都返回void | 增加config枚举覆盖,可覆盖主字段;广告播放中请求直接返回 | 保留合法请求及config用法,测试继承字段/重复/失败路径 | +| 返回值 | await后仅{name} | await后{name,destroy},destroy同步 | 核心destroy与显式SDK destroy区分,核实重建语义 | +| 容器 | `art-${Date.now()}`,display:none;交由SDK显示 | `art-vast-${Date.now()}`,黑底/pointerEvents;监听四种广告事件显隐 | DOM/ID/事件时序及同毫秒多实例待测 | +| 默认SDK设置 | new AdsRenderingSettings/PlayerOptions默认值 | 额外打开restoreCustomPlaybackStateOnAdBreakComplete和enablePreloading | 先固定真实SDK行为,禁止把默认值变化当作无影响整理 | +| CommonJS/script | Parcel namespace `{default:factory}`,window全局;main/legacy | callable、ESM/legacy和exports | 兼容旧`.default`、原dist路径和类型入口 | +| 生命周期 | 无核心destroy订阅,无自动清理 | 仍无核心destroy订阅,只有回调完成后的公开destroy | 资源所有权、SDK加载中销毁、callback拒绝、晚到事件需复现及修复 | + +## SDK与历史核心 + +发布版依赖范围@glomex/vast-ima-player ^1.21.0,工作区^1.21.2、锁定安装1.21.2。 +冻结SDK1.21.0/1.21.2的全部27+27成员及插件6成员,SHA512和SHA256校验,不把 +工作区node_modules当作实际发布来源。SDK打包代码包含loadImaSdk,远程地址为 +`https://imasdk.googleapis.com/js/sdkloader/ima3.js`;该远程服务不是已冻结的本地SDK +tarball,真实网络/广告行为仍需05取证。 + +两版SDK的公开dist/index.d.ts和vast-ima-player.d.ts都引用@alugha/ima,但仅把它 +列入devDependencies。当前工作区未安装该包,TS5.9.3和4.3.5的最小VAST消费者各有 +两个TS2307;记录在本步验证证据中。04应使实际安装后的声明自洽,不能依靠skipLibCheck。 + +发布gitHead对应核心5.1.7,源manifest哈希已固定;这是历史关联,不是该核心tarball +或媒体组合已通过。02/05应核验真实5.1.7以及5.4.1/候选核心。 + +## 验证边界 + +`vast-contract.mjs`校验三份归档、60成员、冻结Git源码与核心关联。 +`vast-contract.test.mjs`执行实际发布main/legacy的导出形状,并在准确SDK注入边界 +运行哈希校验过的两套源码,观察callback字段、提前/惰性初始化、Promise等待、 +请求配置和Result。这里的Player是受控记录器,不是Google IMA或真实广告播放。 +VM导出检查提供当前平台CustomEvent;初次未提供时触发SDK事件polyfill的document +访问,属于测试夹具缺项,不能泛称当前Node的SSR必然失败。 + +用户明确更正:只有VAST脚本确因VPN规则始终无法加载时,可以记录并跳过,留给用户 +后续处理。仍记录实际失败与未验证范围;本轮注册表/归档下载正常,未使用此例外。 diff --git a/refactor/baselines/vast-release.json b/refactor/baselines/vast-release.json new file mode 100644 index 000000000..eb3f37260 --- /dev/null +++ b/refactor/baselines/vast-release.json @@ -0,0 +1,284 @@ +{ + "schemaVersion": 1, + "task": "PKG-VAST-01", + "capturedAt": "2026-09-12T00:54:44.270Z", + "sourceCommit": "29692d28158ad50e1d404293f592679c10dbf3f5", + "registryObservation": { + "registryUrl": "https://registry.npmjs.org/artplayer-plugin-vast", + "distTags": { + "latest": "1.0.0" + }, + "versions": [ + "1.0.0" + ], + "publishedAt": "2024-10-12T08:56:54.614Z", + "workspaceVersion": "1.2.0", + "workspaceVersionPublished": false + }, + "release": { + "name": "artplayer-plugin-vast", + "version": "1.0.0", + "metadataUrl": "https://registry.npmjs.org/artplayer-plugin-vast/1.0.0", + "tarball": "https://registry.npmjs.org/artplayer-plugin-vast/-/artplayer-plugin-vast-1.0.0.tgz", + "integrity": "sha512-6ia0BYlZMBUAbvfqPq6RD3kvCP/Vpnqjs4zgzcio0CUh3a/iJkp+caKFOFjROfilbL+B0lXUiA7U1kS9KqEwTg==", + "sha256": "35681d60fa3fa09b92f0f30887e39c73cf55e7b14d51a1c38fccbc6c1f253bab", + "archiveBytes": 18457, + "registryGitHead": "252c226473792fd1537c1ff4a323344bf1060efc", + "files": { + "package/README.md": "2476f0b0ec9c6957f8cf0902384faf638f34e35309b00a24dbb74ee4ce3d3019", + "package/dist/artplayer-plugin-vast.js": "bf3429ca7650fdedc03a7e3e92abc192c4eac87a97c0895e3a0f82921aa361d0", + "package/dist/artplayer-plugin-vast.legacy.js": "3f86ff7558662687f794790a33a76305e2947304a0800e0e4d6788fbec54bbcf", + "package/package.json": "8e2eadca73c8ecbdad130115ea340efe5ae6d52668771f27e1b3c6009d706f46", + "package/src/index.js": "6811308508bfcb3f8b8c94510c90bde0308996ecdaf1008b110d041dc962d4a4", + "package/types/artplayer-plugin-vast.d.ts": "51257361f482f026a9b5ba84ca85d320d9e1457a34d0d259ac3c7b46c4fbd29c" + }, + "manifest": { + "license": "MIT", + "version": "1.0.0", + "name": "artplayer-plugin-vast", + "types": "types/artplayer-plugin-vast.d.ts", + "description": "vast plugin for ArtPlayer", + "main": "dist/artplayer-plugin-vast.js", + "legacy": "dist/artplayer-plugin-vast.legacy.js", + "author": "Harvey Zack ", + "browserslist": "last 1 Chrome version", + "homepage": "https://artplayer.org", + "repository": { + "type": "git", + "url": "git+https://github.com/zhw2590582/ArtPlayer.git" + }, + "bugs": { + "url": "https://github.com/zhw2590582/ArtPlayer/issues" + }, + "keywords": [ + "html5", + "video", + "player" + ], + "dependencies": { + "@glomex/vast-ima-player": "^1.21.0" + } + } + }, + "sdk": [ + { + "name": "@glomex/vast-ima-player", + "version": "1.21.0", + "metadataUrl": "https://registry.npmjs.org/%40glomex%2Fvast-ima-player/1.21.0", + "tarball": "https://registry.npmjs.org/@glomex/vast-ima-player/-/vast-ima-player-1.21.0.tgz", + "integrity": "sha512-TfMiihsXP1Wwhe+H9EzRoiq8FUVssiKWiCAKgAw2EN32tdF28mmVc83LCdh2j65tKXZ8yT5RSUdI5Hlz4t79aQ==", + "sha256": "924405b92ac5ccb4cc775d13b5caba6dc6fb537d55b0cb3ca128cb9be4b2bc48", + "archiveBytes": 148164, + "registryGitHead": "789a7ab51d0a176ec747c9a99e6e482184d51d80", + "files": { + "package/LICENSE": "68b35bb64e6269277318ff4e4ee66a738bdeb83bbcdc7e3c3af85a0bdded8d0b", + "package/README.md": "26867a31c5546f9b40b41b50c168c0b073bdf9d8ff331521540adfc2187946ed", + "package/dist/custom-playhead.d.ts": "24de4901d6ff04c5a8f55fd2a5001c8c47c72cbd3f072d673a1deafa200656a9", + "package/dist/delegated-event-target.d.ts": "d1eb0fdc1e9c497e6c04d5c83dc22483d5644d1bd9b430bd51c63d13d32432c7", + "package/dist/index.d.ts": "ce1c59659a68c54fe5af34b94550f3de9d4400b8315c188f679ecab19f87b80f", + "package/dist/vast-ima-player.d.ts": "a6d80180646fcebc1255a75dcd780b618f226a111dfa33cb5fa64681071b7c16", + "package/dist/vast-ima-player.js": "5f565b78d336eecb32982bc0038528547e1c6d17b6478ee60c3ecbe3b720b58d", + "package/dist/vast-ima-player.js.map": "a55d8ef507597c87337d63de2158b62316193700264ef2ecadef0d0b909b6f00", + "package/dist/vast-ima-player.modern.js": "23c119fb54e190e38398a899fb2fb1809dbe5eaa9fccfda2f27d11920c28f3fe", + "package/dist/vast-ima-player.modern.js.map": "24e45e89da1ebcc9c276e6681c1bfb824f884a1129f15daea0e6da903e33e8b1", + "package/dist/vast-ima-player.module.js": "52f4bf3f39771e35c40b464d23de3ba0e770ca4684d908590f926055dba5b4f9", + "package/dist/vast-ima-player.module.js.map": "a1e91c835162f8ea2b13bcb63390f62cfe7a38fb018119662f9d9602e443d910", + "package/dist/vast-ima-player.umd.js": "38014c18a51138e5093ae4cf743e84cdae3e431816581f08f16cf0c10e932eae", + "package/dist/vast-ima-player.umd.js.map": "e119d5f96b8113710a72cdabe18e45531c455176a5fd74d3466e6446e31e3e45", + "package/package.json": "340bbbb564be16642e5326d180d6a4a5a8e0bb82e688d2d995d3ff5454f79667", + "package/src/custom-playhead.ts": "326ddefbb15b05b5f3c9b25de88b84bce97ace248e747efcfebd476951f7734e", + "package/src/delegated-event-target.ts": "0937aeaea09a115b10f742ec95dd19bfeee5aece4a95e83ef95d9ea7a09d1064", + "package/src/index.ts": "db58ca160cf01af6e8ef50872a077a3fc4b1c6551cecde1646c06fb5d944771e", + "package/src/vast-ima-player.ts": "25a630d51f770a298cba35791581d30ea3079c406830988c5efb28e0ad9531e4", + "package/test-page/bulma-slider.min.css": "9bd5f05ad71360d0e87f21dd3d07d94c080486342f335ff041f5394c6aa6dac9", + "package/test-page/bulma-slider.min.js": "e520e9ef6e3223ecfbb50b4649d238af5a85af9161800ad1d003dac80d748f24", + "package/test-page/bulma.min.css": "58b28659220961ead137cb5b346b5759562750ce703094d70fc786e0db467033", + "package/test-page/index.html": "41eb8677b1cbd260407d0d829ab8cb4838dbe834c537f5e7c372d7d25b024591", + "package/test-page/rollup.config.js": "8949cb766b7e4cab9506607e28d79887e3c60ae3b152c9445aef86d8e4d0f22d", + "package/test-page/test-page.bundle.js": "a7b4b66fc708b6cfc7cf07a11d00e825f658952ae9e8935388288cb05221c6d8", + "package/test-page/test-page.bundle.js.map": "ae996c9a5298853aebbd0374fef99f57016053746bc8e7083f978cc482f0f92f", + "package/test-page/test-page.js": "a4d7d98bd4fb57425df80e8448c1631a4aeeadfd88cf4a788d1d787c59756a53" + }, + "manifest": { + "name": "@glomex/vast-ima-player", + "version": "1.21.0", + "description": "Convenience wrapper for advertising video/audio content with Google IMA", + "source": "src/index.ts", + "main": "dist/vast-ima-player.js", + "module": "dist/vast-ima-player.module.js", + "unpkg": "dist/vast-ima-player.umd.js", + "types": "dist/index.d.ts", + "mangle": { + "regex": "^_" + }, + "scripts": { + "prepare": "npm run -s build", + "build": "npm-run-all --parallel build:*", + "build:bundle": "rollup -c test-page/rollup.config.js", + "build:test-page": "microbundle", + "start": "npm-run-all --parallel start:*", + "start:serve": "serve", + "start:bundle": "microbundle watch", + "start:test-page": "rollup -c test-page/rollup.config.js --watch", + "lint": "tslint -c tslint.json 'src/**/*.ts'", + "test": "karma start karma.conf.js --single-run" + }, + "files": [ + "dist", + "src", + "test-page" + ], + "keywords": [ + "ima", + "ima-sdk", + "ad", + "googleads", + "vast", + "vpaid", + "vmap", + "player", + "video-player" + ], + "repository": "github:glomex/vast-ima-player", + "bugs": { + "url": "https://github.com/glomex/vast-ima-player/issues" + }, + "author": "glomex GmbH", + "license": "Apache-2.0", + "devDependencies": { + "@alugha/ima": "^2.1.0", + "@ungap/custom-event": "^0.3.1", + "form-to-object": "^6.0.0", + "jasmine": "^4.1.0", + "karma": "^6.3.18", + "karma-chrome-launcher": "^3.1.1", + "karma-firefox-launcher": "^2.1.2", + "karma-jasmine": "^5.0.0", + "microbundle": "^0.13.3", + "npm-run-all": "^4.1.5", + "serve": "^13.0.2", + "tslint": "^6.1.3", + "typescript": "^4.6.3" + } + } + }, + { + "name": "@glomex/vast-ima-player", + "version": "1.21.2", + "metadataUrl": "https://registry.npmjs.org/%40glomex%2Fvast-ima-player/1.21.2", + "tarball": "https://registry.npmjs.org/@glomex/vast-ima-player/-/vast-ima-player-1.21.2.tgz", + "integrity": "sha512-dmWB5enpmCvDEIg0AwDWfGehnshg/nvUpuDzgyKLGmqQx8K0vl/1GXEvgrAhN4fVb0lCIgwB/rXFdBZ9yPLwhw==", + "sha256": "5b5575c3a6c68a2a550c986348093de7471476318c9ec443285063dfbef3f9b5", + "archiveBytes": 148148, + "registryGitHead": "2a72ce27d81ff709cf8488324d80107e0fc484f9", + "files": { + "package/LICENSE": "68b35bb64e6269277318ff4e4ee66a738bdeb83bbcdc7e3c3af85a0bdded8d0b", + "package/README.md": "26867a31c5546f9b40b41b50c168c0b073bdf9d8ff331521540adfc2187946ed", + "package/dist/custom-playhead.d.ts": "24de4901d6ff04c5a8f55fd2a5001c8c47c72cbd3f072d673a1deafa200656a9", + "package/dist/delegated-event-target.d.ts": "d1eb0fdc1e9c497e6c04d5c83dc22483d5644d1bd9b430bd51c63d13d32432c7", + "package/dist/index.d.ts": "ce1c59659a68c54fe5af34b94550f3de9d4400b8315c188f679ecab19f87b80f", + "package/dist/vast-ima-player.d.ts": "a6d80180646fcebc1255a75dcd780b618f226a111dfa33cb5fa64681071b7c16", + "package/dist/vast-ima-player.js": "7e5481a5ea01df8178202fc36f96830e0ec1c920c27023e8de8479f90168f5eb", + "package/dist/vast-ima-player.js.map": "af1e5d4d981f914d5ed05b2f04469d0f469dd73233a23816467c2daf7cd821bf", + "package/dist/vast-ima-player.modern.js": "b2e40300da6392465a662ff05f617998435bbc9d8e36d76c91e0440ce0277c97", + "package/dist/vast-ima-player.modern.js.map": "86ed6a4ab1c5344480b2293141ba6f6784656393f75afb268596f3b623b769b2", + "package/dist/vast-ima-player.module.js": "861ace7a869c1eda98ccae1c42e617afa74e8a6d79e26e6ce2a052812e7314a7", + "package/dist/vast-ima-player.module.js.map": "8e4c6c85aae149581bcbb52d8d6192dcd22a354de47571db7e73d47ad275af40", + "package/dist/vast-ima-player.umd.js": "bfe5f9ba07cf3c6964edb91cba222af0214365a8fbbd25783a7a39a1393a49c7", + "package/dist/vast-ima-player.umd.js.map": "55d37417c392bbc758ecce58e96d7edc6c84e1309663eca5477a53ebe1e9e289", + "package/package.json": "c7361321ce792f6830af9eb40e70b7508a75f68290a3c0077e26a6f36daa7097", + "package/src/custom-playhead.ts": "326ddefbb15b05b5f3c9b25de88b84bce97ace248e747efcfebd476951f7734e", + "package/src/delegated-event-target.ts": "0937aeaea09a115b10f742ec95dd19bfeee5aece4a95e83ef95d9ea7a09d1064", + "package/src/index.ts": "db58ca160cf01af6e8ef50872a077a3fc4b1c6551cecde1646c06fb5d944771e", + "package/src/vast-ima-player.ts": "09c59b3051aa38b66657bc84f8d0e55f48b46577bff8090930b4729e9a3022d1", + "package/test-page/bulma-slider.min.css": "9bd5f05ad71360d0e87f21dd3d07d94c080486342f335ff041f5394c6aa6dac9", + "package/test-page/bulma-slider.min.js": "e520e9ef6e3223ecfbb50b4649d238af5a85af9161800ad1d003dac80d748f24", + "package/test-page/bulma.min.css": "58b28659220961ead137cb5b346b5759562750ce703094d70fc786e0db467033", + "package/test-page/index.html": "41eb8677b1cbd260407d0d829ab8cb4838dbe834c537f5e7c372d7d25b024591", + "package/test-page/rollup.config.js": "8949cb766b7e4cab9506607e28d79887e3c60ae3b152c9445aef86d8e4d0f22d", + "package/test-page/test-page.bundle.js": "a7b4b66fc708b6cfc7cf07a11d00e825f658952ae9e8935388288cb05221c6d8", + "package/test-page/test-page.bundle.js.map": "ae996c9a5298853aebbd0374fef99f57016053746bc8e7083f978cc482f0f92f", + "package/test-page/test-page.js": "a4d7d98bd4fb57425df80e8448c1631a4aeeadfd88cf4a788d1d787c59756a53" + }, + "manifest": { + "name": "@glomex/vast-ima-player", + "version": "1.21.2", + "description": "Convenience wrapper for advertising video/audio content with Google IMA", + "source": "src/index.ts", + "main": "dist/vast-ima-player.js", + "module": "dist/vast-ima-player.module.js", + "unpkg": "dist/vast-ima-player.umd.js", + "types": "dist/index.d.ts", + "mangle": { + "regex": "^_" + }, + "scripts": { + "prepare": "npm run -s build", + "build": "npm-run-all --parallel build:*", + "build:bundle": "rollup -c test-page/rollup.config.js", + "build:test-page": "microbundle", + "start": "npm-run-all --parallel start:*", + "start:serve": "serve", + "start:bundle": "microbundle watch", + "start:test-page": "rollup -c test-page/rollup.config.js --watch", + "lint": "tslint -c tslint.json 'src/**/*.ts'", + "test": "karma start karma.conf.js --single-run" + }, + "files": [ + "dist", + "src", + "test-page" + ], + "keywords": [ + "ima", + "ima-sdk", + "ad", + "googleads", + "vast", + "vpaid", + "vmap", + "player", + "video-player" + ], + "repository": "github:glomex/vast-ima-player", + "bugs": { + "url": "https://github.com/glomex/vast-ima-player/issues" + }, + "author": "glomex GmbH", + "license": "Apache-2.0", + "devDependencies": { + "@alugha/ima": "^2.1.0", + "@ungap/custom-event": "^0.3.1", + "form-to-object": "^6.0.0", + "jasmine": "^4.1.0", + "karma": "^6.3.18", + "karma-chrome-launcher": "^3.1.1", + "karma-firefox-launcher": "^2.1.2", + "karma-jasmine": "^5.0.0", + "microbundle": "^0.13.3", + "npm-run-all": "^4.1.5", + "serve": "^13.0.2", + "tslint": "^6.1.3", + "typescript": "^4.6.3" + } + } + } + ], + "source": { + "packages/artplayer-plugin-vast/src/index.js": "cba7d7a5822d4088655c301264280dc19d67b1f1716e4e50c8a6dcb8b5580763", + "packages/artplayer-plugin-vast/types/artplayer-plugin-vast.d.ts": "38ba4b3e03b261c53a062d94bc73e1a9c4f453681d33ce6f7bf8d9b06ec2f052", + "packages/artplayer-plugin-vast/package.json": "615a17004252aa9301167a28d4b067f9d2866aa2392febad71c85cea02dbbd8e", + "packages/artplayer-plugin-vast/README.md": "91e1cdfd47fb6eabfc4cd44f15d44dc33201479e09552ac237505bb0658a8fbe", + "docs/assets/example/vast.js": "ce93b48fabc2f1fd39fdaa8af3ae9f86a420c9aa65551e2e196d754070f36064", + "packages/artplayer-plugin-vast/dist/artplayer-plugin-vast.js": "72842ae0b4a44e44a080f88783c0c9bcd0b572fa0e946d817b2c36d960a08bfe", + "packages/artplayer-plugin-vast/dist/artplayer-plugin-vast.legacy.js": "4b88cec8d12844d639cc7703f88703136a4fe586781d9939bf2e3a0c93b42b60", + "packages/artplayer-plugin-vast/dist/artplayer-plugin-vast.mjs": "91fbe929a9d8d780f1656b53697fb0fe400b9375db79370866ec2f4c5313e727" + }, + "historicalCore": { + "commit": "252c226473792fd1537c1ff4a323344bf1060efc", + "file": "packages/artplayer/package.json", + "version": "5.1.7", + "sha256LF": "8bd532103099c367fb2c289e0d410f144fdeb00b1b62cf850762a24b61e63c8a", + "note": "Git association only; actual core tarball and browser combination remain task02/05." + } +} diff --git a/refactor/changes/2026-09-12-PKG-VAST-01-contract.md b/refactor/changes/2026-09-12-PKG-VAST-01-contract.md new file mode 100644 index 000000000..a5087953f --- /dev/null +++ b/refactor/changes/2026-09-12-PKG-VAST-01-contract.md @@ -0,0 +1,26 @@ +# PKG-VAST-01 真实契约冻结 + +起点29692d28。核对实际发布VAST1.0.0、未发布工作区1.2.0、SDK1.21.0/1.21.2及 +发布关联核心5.1.7。详情见 [契约表](../baselines/vast-contract.md) 和 +[归档](../baselines/vast-release.json)。本任务不修改生产源码、类型、依赖或产物。 + +新增可持续运行的归档验证和四项契约测试;实际发布JS/legacy导出直接执行,两套 +源码在仅替换SDK的受控边界运行。证实发布callback拥有id/$container及已创建Player, +工作区移除别名并改为懒创建;两者实际均异步而声明写成同步。另确认SDK公开类型 +引用dev-only依赖,当前最小消费者在两种TS版本各复现两个TS2307。 + +API-06/07/09/11差异、默认设置及初始化时机、资源清理和外部SDK验证分别登记为 +VAST-CONTEXT-01/TYPE-01/LIFE-01/DIST-01和既有SDK-07,指派02~06处理,不以 +本次基线通过宣称这些问题已修复。Ads类型批准不扩大到VAST异步声明差异。 + +用户已更正VPN跳过授权对象为VAST;若脚本持续被规则阻止,可记录并跳过其实际 +网络验证,由用户后续修复。正常的本地契约/生命周期/类型改造继续执行。 + +所有检查和输入指纹见 [验证记录](../baselines/vast-contract-validation.json)。 +本步53项基线测试全通过(其中新增VAST四项),脚本及仓库lint、计划与147项风险 +台账检查通过。没有生产变更,本步未重复完整CI、构建或真实广告媒体验收。 +首次VM测试遗漏CustomEvent,SDK fallback访问document导致失败;提供平台原生 +CustomEvent后实际导出检查通过,不修改发布bundle。生产SSR仍由分发任务单独测试。 + +回退本任务仅撤销本步文档/台账/测试,不改第三方归档或生产文件。完成后独立本地 +提交PKG-VAST-01,无推送、tag、发布。下一步02建立SDK失败、回调、事件和销毁测试。 diff --git a/refactor/plan.md b/refactor/plan.md index c48929999..7be3e3ca5 100644 --- a/refactor/plan.md +++ b/refactor/plan.md @@ -4,7 +4,7 @@ 基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 217 项,范围 22 个包及工作区/示例。 -状态:todo 134 / doing 5 / blocked 0 / done 78 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 +状态:todo 133 / doing 5 / blocked 0 / done 79 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。 @@ -267,12 +267,12 @@ | ID | 范围 / 步骤 | 前置依赖 | 交付物 | 验收条件 | 风险 | 状态 | | --- | --- | --- | --- | --- | --- | --- | -| PKG-VAST-01 | artplayer-plugin-vast
核对包契约与历史用法 | BASE-05 | 异步工厂、callback context、playUrl/playRes/init 与 SDK 版本 | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结 | H | todo | -| PKG-VAST-02 | artplayer-plugin-vast
建立特有行为与错误测试 | PKG-VAST-01, ENG-03, ENG-05 | SDK 失败、重复初始化、广告事件、内容恢复和销毁竞态 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | todo | -| PKG-VAST-03 | artplayer-plugin-vast
整理内部职责与资源 | PKG-VAST-02, CORE-10, CORE-13 | SDK loader、IMA adapter、广告状态和 DOM 清理分离 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | todo | -| PKG-VAST-04 | artplayer-plugin-vast
迁移自有源码和公开类型 | PKG-VAST-03, ENG-04, ENG-06, CORE-07 | IMA/context/Promise 真实类型,外部 SDK 动态对象限定边界 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | todo | -| PKG-VAST-05 | artplayer-plugin-vast
验证新旧核心和组合 | PKG-VAST-04, CORE-22 | 官方测试广告或受控 SDK 环境,错误后主视频状态恢复 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo | -| PKG-VAST-06 | artplayer-plugin-vast
验证分发并同步文档 | PKG-VAST-05, ENG-07 | vast.js、SDK 资源、声明消费和独立版本记录 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo | +| PKG-VAST-01 | artplayer-plugin-vast
核对包契约与历史用法 | BASE-05 | 异步工厂、callback context、playUrl/playRes/init 与 SDK 版本;实际 npm 1.0.0 与未发布 1.2.0、SDK 1.21.0/1.21.2 的60成员归档和发布关联核心5.1.7 | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结 | H | done | +| PKG-VAST-02 | artplayer-plugin-vast
建立特有行为与错误测试 | PKG-VAST-01, ENG-03, ENG-05 | SDK 失败、重复初始化、广告事件、内容恢复和销毁竞态;发布 id/$container/eager 与工作区 lazy/config 覆盖、callback拒绝、加载中销毁、同毫秒多实例与晚到事件 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | todo | +| PKG-VAST-03 | artplayer-plugin-vast
整理内部职责与资源 | PKG-VAST-02, CORE-10, CORE-13 | SDK loader、IMA adapter、广告状态和 DOM 清理分离;保持发布 callback 别名,明确初始化冲突;核心终止与显式 destroy 后重建区分 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | todo | +| PKG-VAST-04 | artplayer-plugin-vast
迁移自有源码和公开类型 | PKG-VAST-03, ENG-04, ENG-06, CORE-07 | IMA/context/Promise 真实类型,外部 SDK 动态对象限定边界;修复 @alugha/ima 声明依赖链,保留 export= 和 require.default 消费;同步错误声明的兼容决策单列 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | todo | +| PKG-VAST-05 | artplayer-plugin-vast
验证新旧核心和组合 | PKG-VAST-04, CORE-22 | 官方测试广告或受控 SDK 环境,错误后主视频状态恢复;核验真实5.1.7/5.4.1/候选核心,VAST VPN外部脚本例外逐项记录,不将受控SDK当作实际IMA验收 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo | +| PKG-VAST-06 | artplayer-plugin-vast
验证分发并同步文档 | PKG-VAST-05, ENG-07 | vast.js、SDK 资源、声明消费和独立版本记录;核验实际发布 namespace default、历史深路径、ESM和准确声明,版本按政策2.0.0由REL-09统一处理 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo | ## 5 包迁移:artplayer-plugin-chromecast @@ -489,3 +489,4 @@ - PKG-ADS-03: [记录](changes/2026-09-12-PKG-ADS-03-lifecycle.md) [记录](ads-validation.md) [记录](baselines/ads-lifecycle-validation.json) - PKG-ADS-04: [记录](changes/2026-09-12-PKG-ADS-04-types.md) [记录](baselines/ads-types-validation.json) - PKG-ADS-05: [记录](changes/2026-09-12-PKG-ADS-05-browser.md) [记录](baselines/ads-ui-visibility-validation.json) +- PKG-VAST-01: [记录](baselines/vast-contract.md) [记录](baselines/vast-release.json) [记录](changes/2026-09-12-PKG-VAST-01-contract.md) [记录](baselines/vast-contract-validation.json) diff --git a/refactor/progress.md b/refactor/progress.md index 9932f1f43..af351bd6e 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -1,5 +1,15 @@ # 进度与证据 +## 最新完成:VAST-01 实际发布契约 + +固定实际发布1.0.0、未发布工作区1.2.0及SDK1.21.0/1.21.2的60个归档成员。 +四项可重跑测试确认公开callback字段、初始化、异步时序、请求配置和实际导出; +最小TS消费者复现缺失的SDK类型依赖。差异分配给02~06,尚未修改VAST生产源码。 +VAST脚本的VPN跳过例外已按用户更正记录,本次未使用。下一步02补齐失败和销毁回归。 +当前217项:79 done、5 doing、133 todo。详见 +[本步记录](changes/2026-09-12-PKG-VAST-01-contract.md) 和 +[验证证据](baselines/vast-contract-validation.json)。 + ## 当前实施:Ads-05 页面与媒体组合 补上27项原生全屏/多实例真实视频/详情导航/窄屏布局验证,源码、正式main/legacy均通过。 diff --git a/refactor/risk-table.md b/refactor/risk-table.md index 0e89ca121..8fccb3e26 100644 --- a/refactor/risk-table.md +++ b/refactor/risk-table.md @@ -147,3 +147,7 @@ | ADS-LIFE-01 | resolved / 已复现 | Ads 计时链、重复/过早调用、工厂复用与销毁缺乏资源边界 | PKG-ADS-02, PKG-ADS-03, PKG-ADS-05 | | ADS-MEDIA-01 | open / 已复现 | Ads metadata 与 play 拒绝、隐藏页媒体状态和内容恢复仍缺真实验证 | PKG-ADS-02, PKG-ADS-03, PKG-ADS-05 | | ADS-UI-01 | resolved / 已复现 | Ads 零等待跳过与初始状态不符,全屏图标/外部状态需核验 | PKG-ADS-02, PKG-ADS-03, PKG-ADS-05 | +| VAST-CONTEXT-01 | open / 已复现 | Published VAST callback aliases and eager Player differ from the unpublished lazy workspace | PKG-VAST-02, PKG-VAST-03, PKG-VAST-04 | +| VAST-TYPE-01 | open / 已复现 | VAST declares an async factory as synchronous and imports an unavailable SDK type dependency | PKG-VAST-04, PKG-VAST-06 | +| VAST-LIFE-01 | open / 源码/产物事实 | VAST SDK and container lack core destruction ownership and pending work cancellation | PKG-VAST-02, PKG-VAST-03, PKG-VAST-05 | +| VAST-DIST-01 | open / 已复现 | Published VAST CommonJS default namespace differs from the current callable distribution | PKG-VAST-04, PKG-VAST-06 | diff --git a/refactor/risks.json b/refactor/risks.json index bff932cfe..25fcc672e 100644 --- a/refactor/risks.json +++ b/refactor/risks.json @@ -702,10 +702,15 @@ "PKG-VAST-05" ], "evidence": [ - "packages/artplayer-plugin-vast/src/index.js" + "packages/artplayer-plugin-vast/src/index.js", + "refactor/baselines/vast-contract.md", + "refactor/baselines/vast-release.json", + "refactor/changes/2026-09-12-PKG-VAST-01-contract.md", + "refactor/baselines/vast-contract-validation.json" ], "compatibleResolution": "@glomex/vast-ima-player loadImaSdk loads external IMA; preserve event/content/ad state and teardown, do not equate mocks with vendor service acceptance.", - "closureCriteria": "固定 SDK/外部资源版本和环境;成功/失败/切换/销毁有真实证据,设备和网络限制逐项记录。" + "closureCriteria": "固定 SDK/外部资源版本和环境;成功/失败/切换/销毁有真实证据,设备和网络限制逐项记录。", + "workspaceState": "VAST 1.0.0 and SDK 1.21.0/1.21.2 archives verified; Google IMA remote execution remains unverified. User allows recording/skipping only persistent VAST script loading failures caused by VPN rules, not Ads or other VAST code/type failures. This exception has not been used." }, { "id": "SDK-08", @@ -3159,6 +3164,80 @@ "test/ads-lifecycle.test.js" ], "resolutionRationale": "零/正/不可关闭阈值及错误输入由现有Node/媒体回归保持;main/legacy分别27项真实原生全屏(初始/外部退出/按钮)、静音、详情popup和窄屏翻译控件通过。截图已查看;手机设备策略仍由05媒体门槛承接。" + }, + { + "id": "VAST-CONTEXT-01", + "title": "Published VAST callback aliases and eager Player differ from the unpublished lazy workspace", + "confirmation": "reproduced", + "status": "open", + "owners": [ + "PKG-VAST-02", + "PKG-VAST-03", + "PKG-VAST-04" + ], + "evidence": [ + "refactor/baselines/vast-contract.md", + "refactor/baselines/vast-release.json", + "refactor/changes/2026-09-12-PKG-VAST-01-contract.md", + "refactor/baselines/vast-contract-validation.json" + ], + "compatibleResolution": "Restore published id/$container access and reconcile eager non-null Player with lazy workspace usage using explicit consumer evidence.", + "closureCriteria": "Published and workspace consumer fixtures cover all callback fields and initialization timing; any irreconcilable behavior has a concrete decision rather than implicit waiver." + }, + { + "id": "VAST-TYPE-01", + "title": "VAST declares an async factory as synchronous and imports an unavailable SDK type dependency", + "confirmation": "reproduced", + "status": "open", + "owners": [ + "PKG-VAST-04", + "PKG-VAST-06" + ], + "evidence": [ + "refactor/baselines/vast-contract.md", + "refactor/baselines/vast-release.json", + "refactor/changes/2026-09-12-PKG-VAST-01-contract.md", + "refactor/baselines/vast-contract-validation.json" + ], + "compatibleResolution": "Provide accurate Promise/context/IMA types and a self-contained installed declaration chain while checking legacy export= and consumers.", + "closureCriteria": "Strict isolated package consumers pass without skipLibCheck; synchronous Result inference differences are reproduced and resolved or separately approved. Ads approval does not apply." + }, + { + "id": "VAST-LIFE-01", + "title": "VAST SDK and container lack core destruction ownership and pending work cancellation", + "confirmation": "source-observed", + "status": "open", + "owners": [ + "PKG-VAST-02", + "PKG-VAST-03", + "PKG-VAST-05" + ], + "evidence": [ + "refactor/baselines/vast-contract.md", + "refactor/baselines/vast-release.json", + "refactor/changes/2026-09-12-PKG-VAST-01-contract.md", + "refactor/baselines/vast-contract-validation.json" + ], + "compatibleResolution": "Reproduce pending SDK/callback destruction, errors, duplicate IDs and stale ad events before adding compatible cleanup and state ownership.", + "closureCriteria": "Tests prove cleanup during load/callback rejection/core destruction and distinguish explicit plugin recreation from terminal core destruction; vendor media restoration is separately verified." + }, + { + "id": "VAST-DIST-01", + "title": "Published VAST CommonJS default namespace differs from the current callable distribution", + "confirmation": "reproduced", + "status": "open", + "owners": [ + "PKG-VAST-04", + "PKG-VAST-06" + ], + "evidence": [ + "refactor/baselines/vast-contract.md", + "refactor/baselines/vast-release.json", + "refactor/changes/2026-09-12-PKG-VAST-01-contract.md", + "refactor/baselines/vast-contract-validation.json" + ], + "compatibleResolution": "Preserve require(pkg).default alongside callable factory, script global and current ESM/legacy paths.", + "closureCriteria": "Real packed main/legacy/ESM, historical deep imports, export= consumers and local demo/editor all pass with immutable published comparisons." } ] } diff --git a/refactor/scripts/vast-contract.mjs b/refactor/scripts/vast-contract.mjs new file mode 100644 index 000000000..66258d1bb --- /dev/null +++ b/refactor/scripts/vast-contract.mjs @@ -0,0 +1,43 @@ +import assert from 'node:assert/strict' +import { execFileSync } from 'node:child_process' +import fs from 'node:fs' +import path from 'node:path' +import process from 'node:process' +import { fileURLToPath } from 'node:url' +import { archiveFiles, ensureArchive, hash, readMember, refactorDir } from './releases.mjs' + +export async function verifyVastContract() { + const baseline = JSON.parse(fs.readFileSync(path.join(refactorDir, 'baselines/vast-release.json'))) + const archives = new Map() + for (const release of [baseline.release, ...baseline.sdk]) { + const archive = await ensureArchive(release) + assert.deepEqual(archiveFiles(archive), Object.keys(release.files).sort()) + for (const [member, expected] of Object.entries(release.files)) + assert.equal(hash(readMember(archive, member)), expected, `${release.name}@${release.version}/${member}`) + assert.deepEqual(JSON.parse(readMember(archive, 'package/package.json')), release.manifest) + for (const field of ['main', 'module', 'types', 'legacy']) { + const target = release.manifest[field] + if (target) + assert(Object.hasOwn(release.files, `package/${target.replace(/^\.\//, '')}`)) + } + archives.set(`${release.name}@${release.version}`, archive) + } + const sources = new Map() + assert(/^[a-f\d]{40}$/.test(baseline.sourceCommit)) + for (const [file, expected] of Object.entries(baseline.source)) { + const content = execFileSync('git', ['show', `${baseline.sourceCommit}:${file}`]).toString().replaceAll('\r\n', '\n') + assert.equal(hash(content), expected, `Historical source changed: ${file}`) + sources.set(file, content) + } + const core = baseline.historicalCore + const manifest = execFileSync('git', ['show', `${core.commit}:${core.file}`]).toString().replaceAll('\r\n', '\n') + assert.equal(hash(manifest), core.sha256LF) + assert.equal(JSON.parse(manifest).version, core.version) + assert.equal(baseline.registryObservation.workspaceVersionPublished, false) + assert.deepEqual(baseline.registryObservation.versions, ['1.0.0']) + console.log('Verified VAST: published 1.0.0, two SDK archives, 60 members and frozen workspace/core association') + return { baseline, archives, sources } +} + +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) + await verifyVastContract() diff --git a/refactor/scripts/vast-contract.test.mjs b/refactor/scripts/vast-contract.test.mjs new file mode 100644 index 000000000..e8923bf5a --- /dev/null +++ b/refactor/scripts/vast-contract.test.mjs @@ -0,0 +1,137 @@ +import assert from 'node:assert/strict' +// eslint-disable-next-line test/no-import-node-test -- Verify immutable releases and wrapper contracts without external ad requests. +import test from 'node:test' +import vm from 'node:vm' +import { transform } from 'esbuild' +import { readMember } from './releases.mjs' +import { verifyVastContract } from './vast-contract.mjs' + +const contract = await verifyVastContract() +const archive = contract.archives.get('artplayer-plugin-vast@1.0.0') + +test('VAST published artifacts expose a CommonJS default namespace and browser global', () => { + for (const member of ['package/dist/artplayer-plugin-vast.js', 'package/dist/artplayer-plugin-vast.legacy.js']) { + const module = { exports: {} } + const window = {} + vm.runInNewContext(readMember(archive, member).toString(), { module, exports: module.exports, window, CustomEvent }, { timeout: 5000 }) + assert.equal(typeof module.exports, 'object') + assert.deepEqual(Object.keys(module.exports), ['default']) + assert.equal(typeof module.exports.default, 'function') + assert.equal(window.artplayerPluginVast, module.exports.default) + assert.equal(typeof module.exports.default(), 'function') + } +}) + +function sdkFixture() { + const players = [] + const ima = { AdsRenderingSettings: class {}, AdsRequest: class {} } + const sdk = { + loadImaSdk: async () => ima, + PlayerOptions: class {}, + Player: class { + constructor(...args) { + this.args = args + this.requests = [] + this.events = new Map() + players.push(this) + } + + addEventListener(name, callback) { this.events.set(name, callback) } + playAds(request) { this.requests.push(request) } + destroy() { this.destroyed = true } + }, + } + const node = () => ({ style: {}, parentNode: null }) + const parent = { + children: [], + appendChild(child) { + child.parentNode = this + this.children.push(child) + return child + }, + removeChild(child) { + child.parentNode = null + this.children.splice(this.children.indexOf(child), 1) + }, + } + const art = { template: { $player: parent, $video: {} }, constructor: { utils: { createElement: node, setStyles: (target, styles) => Object.assign(target.style, styles), append: (target, child) => target.appendChild(child) } } } + return { players, ima, sdk, art } +} + +async function loadSource(source, fixture) { + const module = { exports: {} } + const result = await transform(source, { loader: 'js', format: 'cjs', target: 'es2020' }) + vm.runInNewContext(result.code, { module, exports: module.exports, window: { google: { ima: fixture.ima } }, console, require(name) { + assert.equal(name, '@glomex/vast-ima-player') + return fixture.sdk + } }, { timeout: 5000 }) + return module.exports.default +} + +for (const family of ['published', 'workspace']) { + test(`VAST ${family}: frozen source shows async callback timing and its exact context/initialization contract`, async () => { + const fixture = sdkFixture() + const source = family === 'published' ? readMember(archive, 'package/src/index.js').toString() : contract.sources.get('packages/artplayer-plugin-vast/src/index.js') + const factory = await loadSource(source, fixture) + let release + let callbackStarted + const ready = new Promise((resolve) => { + callbackStarted = resolve + }) + const gate = new Promise((resolve) => { + release = resolve + }) + const attaching = factory(async (value) => { + callbackStarted(value) + await gate + })(fixture.art) + assert.equal(typeof attaching.then, 'function', 'The historical synchronous declaration is inaccurate') + const context = await ready + assert(context) + assert.equal(context.art, fixture.art) + if (family === 'published') { + assert.deepEqual(Object.keys(context), ['art', 'id', 'ima', 'imaPlayer', '$container', 'playUrl', 'playRes']) + assert.equal(fixture.players.length, 1) + assert.equal(context.imaPlayer, fixture.players[0]) + assert.equal(context.$container, fixture.art.template.$player.children[0]) + assert.equal(context.id, context.$container.id) + assert.match(context.id, /^art-\d+$/) + } + else { + assert.deepEqual(Object.keys(context), ['art', 'playUrl', 'playRes', 'init', 'ima', 'adsRenderingSettings', 'playerOptions', 'imaPlayer', 'container']) + assert.equal(fixture.players.length, 0) + assert.equal(context.imaPlayer, null) + assert.equal(context.container, null) + assert.equal(context.id, undefined) + assert.equal(context.$container, undefined) + assert.equal(context.init(), context.init()) + assert.equal(context.imaPlayer, fixture.players[0]) + assert.equal(context.container, fixture.art.template.$player.children[0]) + assert.equal(context.adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete, true) + assert.equal(context.adsRenderingSettings.enablePreloading, true) + } + assert.equal(context.playUrl('/ad.xml', { adTagUrl: '/override.xml', extra: 1 }), undefined) + assert.equal(fixture.players[0].requests[0].adTagUrl, family === 'published' ? '/ad.xml' : '/override.xml') + assert.equal(context.playRes(''), undefined) + assert.equal(fixture.players[0].requests[1].adsResponse, '') + release() + const result = await attaching + assert.deepEqual(Object.keys(result), family === 'published' ? ['name'] : ['name', 'destroy']) + assert.equal(result.name, 'artplayerPluginVast') + if (family === 'workspace') { + result.destroy() + assert.equal(fixture.players[0].destroyed, true) + assert.equal(context.container, null) + } + }) +} + +test('VAST wrapper SDK public declarations import a development-only dependency', () => { + for (const release of contract.baseline.sdk) { + assert.equal(release.manifest.dependencies?.['@alugha/ima'], undefined) + assert.equal(typeof release.manifest.devDependencies['@alugha/ima'], 'string') + const archive = contract.archives.get(`${release.name}@${release.version}`) + assert.match(readMember(archive, 'package/dist/index.d.ts').toString(), /from ['"]@alugha\/ima['"]/) + assert.match(readMember(archive, 'package/dist/vast-ima-player.d.ts').toString(), /from ['"]@alugha\/ima['"]/) + } +}) diff --git a/refactor/tasks.json b/refactor/tasks.json index ca06fee9c..d3ce4046d 100644 --- a/refactor/tasks.json +++ b/refactor/tasks.json @@ -2646,11 +2646,16 @@ "dependsOn": [ "BASE-05" ], - "status": "todo", + "status": "done", "risk": "H", - "deliverable": "异步工厂、callback context、playUrl/playRes/init 与 SDK 版本", + "deliverable": "异步工厂、callback context、playUrl/playRes/init 与 SDK 版本;实际 npm 1.0.0 与未发布 1.2.0、SDK 1.21.0/1.21.2 的60成员归档和发布关联核心5.1.7", "acceptance": "源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结", - "evidence": [] + "evidence": [ + "baselines/vast-contract.md", + "baselines/vast-release.json", + "changes/2026-09-12-PKG-VAST-01-contract.md", + "baselines/vast-contract-validation.json" + ] }, { "id": "PKG-VAST-02", @@ -2666,7 +2671,7 @@ ], "status": "todo", "risk": "H", - "deliverable": "SDK 失败、重复初始化、广告事件、内容恢复和销毁竞态", + "deliverable": "SDK 失败、重复初始化、广告事件、内容恢复和销毁竞态;发布 id/$container/eager 与工作区 lazy/config 覆盖、callback拒绝、加载中销毁、同毫秒多实例与晚到事件", "acceptance": "旧版本行为可重跑,成功/失败/切源/销毁有必要断言", "evidence": [] }, @@ -2684,7 +2689,7 @@ ], "status": "todo", "risk": "H", - "deliverable": "SDK loader、IMA adapter、广告状态和 DOM 清理分离", + "deliverable": "SDK loader、IMA adapter、广告状态和 DOM 清理分离;保持发布 callback 别名,明确初始化冲突;核心终止与显式 destroy 后重建区分", "acceptance": "结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过", "evidence": [] }, @@ -2703,7 +2708,7 @@ ], "status": "todo", "risk": "H", - "deliverable": "IMA/context/Promise 真实类型,外部 SDK 动态对象限定边界", + "deliverable": "IMA/context/Promise 真实类型,外部 SDK 动态对象限定边界;修复 @alugha/ima 声明依赖链,保留 export= 和 require.default 消费;同步错误声明的兼容决策单列", "acceptance": "严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容", "evidence": [] }, @@ -2720,7 +2725,7 @@ ], "status": "todo", "risk": "H", - "deliverable": "官方测试广告或受控 SDK 环境,错误后主视频状态恢复", + "deliverable": "官方测试广告或受控 SDK 环境,错误后主视频状态恢复;核验真实5.1.7/5.4.1/候选核心,VAST VPN外部脚本例外逐项记录,不将受控SDK当作实际IMA验收", "acceptance": "最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成", "evidence": [] }, @@ -2737,7 +2742,7 @@ ], "status": "todo", "risk": "H", - "deliverable": "vast.js、SDK 资源、声明消费和独立版本记录", + "deliverable": "vast.js、SDK 资源、声明消费和独立版本记录;核验实际发布 namespace default、历史深路径、ESM和准确声明,版本按政策2.0.0由REL-09统一处理", "acceptance": "tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录", "evidence": [] },