From c6b82c3392f61bb3df45ada8ae9854f4eb9e9449 Mon Sep 17 00:00:00 2001 From: Harvey Zhao Date: Sat, 12 Sep 2026 07:05:00 +0800 Subject: [PATCH] test(ads): [PKG-ADS-01] freeze published contracts and legacy compatibility gaps --- .../baselines/ads-contract-validation.json | 86 ++++++ refactor/baselines/ads-contract.md | 65 ++++ refactor/baselines/ads-release.json | 280 ++++++++++++++++++ .../changes/2026-09-12-PKG-ADS-01-contract.md | 25 ++ refactor/environment-matrix.md | 2 +- refactor/plan.md | 15 +- refactor/progress.md | 13 + refactor/risk-table.md | 6 + refactor/risks.json | 60 ++++ refactor/scripts/ads-contract.mjs | 49 +++ refactor/scripts/ads-contract.test.mjs | 58 ++++ refactor/tasks.json | 22 +- 12 files changed, 665 insertions(+), 16 deletions(-) create mode 100644 refactor/baselines/ads-contract-validation.json create mode 100644 refactor/baselines/ads-contract.md create mode 100644 refactor/baselines/ads-release.json create mode 100644 refactor/changes/2026-09-12-PKG-ADS-01-contract.md create mode 100644 refactor/scripts/ads-contract.mjs create mode 100644 refactor/scripts/ads-contract.test.mjs diff --git a/refactor/baselines/ads-contract-validation.json b/refactor/baselines/ads-contract-validation.json new file mode 100644 index 000000000..7490bdfde --- /dev/null +++ b/refactor/baselines/ads-contract-validation.json @@ -0,0 +1,86 @@ +{ + "schemaVersion": 1, + "task": "PKG-ADS-01", + "sourceCommit": "bafbf2c8", + "inputs": [ + { + "file": "refactor/baselines/ads-release.json", + "sha256LF": "e5b47f7d04fe043aaeb40390da4b4c19d80ff6f8889e8e0f5c806ffa44306b69" + }, + { + "file": "refactor/scripts/ads-contract.mjs", + "sha256LF": "f3c23c5298c53d8ed5aff9bf4653771303394daea3475e42d3089d0c03952091" + }, + { + "file": "refactor/scripts/ads-contract.test.mjs", + "sha256LF": "3c73bf0f6d34fb7078bf3e581238d46f596d8c3c9461ac073d3189a44349ecd5" + } + ], + "checks": [ + { + "file": "refactor/.cache/ads01-contract-validation.log", + "tests": 2, + "passed": 2, + "failed": 0, + "sha256": "4775f4eaab9656b23ac7e41f71f05824631c631dd8f1ddc25c2cc0d2a7c8290c", + "bytes": 567 + }, + { + "file": "refactor/.cache/ads01-baseline-validation.log", + "tests": 45, + "passed": 45, + "failed": 0, + "sha256": "e007f11bb871ca18a25ff695d4db3f811ba363733f049db1146184aec37328f4", + "bytes": 5520 + } + ], + "unchanged": [ + { + "file": "packages/artplayer-plugin-ads/src/index.js", + "sha256LF": "dcb19c5b294405cc08601c281b77ab44ff70f49b70f9332e18d1ee0873b91fea", + "unchangedFrom": "bafbf2c8" + }, + { + "file": "packages/artplayer-plugin-ads/src/style.less", + "sha256LF": "0eeed3f29ccafc6836f5bc4338f58f784edd114d2b100fda7fc395329031a406", + "unchangedFrom": "bafbf2c8" + }, + { + "file": "packages/artplayer-plugin-ads/package.json", + "sha256LF": "5b7a876d58b8e88c994c3a72754ce6d7dbea0100a35928a99d66641de7c82e0d", + "unchangedFrom": "bafbf2c8" + }, + { + "file": "packages/artplayer-plugin-ads/types/artplayer-plugin-ads.d.ts", + "sha256LF": "1cc5c1fc04727fc81398e03e384fbbd3ac78a5a02d55da71279b07b0afb08d98", + "unchangedFrom": "bafbf2c8" + }, + { + "file": "docs/assets/example/ads.js", + "sha256LF": "c84a97d2b1970c323a0c2538b846312bff38513e10de271fe5f239953da88cd3", + "unchangedFrom": "bafbf2c8" + }, + { + "file": "packages/artplayer-plugin-ads/dist/artplayer-plugin-ads.js", + "sha256LF": "ceb2d8a23a6735c161b31a4a427d8a757679aaf8a5a106ef40a6f047061f1df8", + "unchangedFrom": "bafbf2c8" + }, + { + "file": "packages/artplayer-plugin-ads/dist/artplayer-plugin-ads.legacy.js", + "sha256LF": "1e10c3a6b51de18e54cd02dd342ab4010fc466de6257659cf1d2e087642aa9a3", + "unchangedFrom": "bafbf2c8" + }, + { + "file": "packages/artplayer-plugin-ads/dist/artplayer-plugin-ads.mjs", + "sha256LF": "0919678423424047b28d9d803835d9ebc98f4477e04e58d497e8ed8c48b95008", + "unchangedFrom": "bafbf2c8" + } + ], + "boundaries": [ + "45 baseline tests include the 2 Ads checks", + "No full engineering CI or browser media acceptance claimed", + "Five actual published archives; workspace 2.1.0 has no published baseline", + "Core 4.5.5 association is historical source evidence, not a tested runtime claim", + "No source migration, production dependency/version change, push or publication" + ] +} diff --git a/refactor/baselines/ads-contract.md b/refactor/baselines/ads-contract.md new file mode 100644 index 000000000..0b73915bb --- /dev/null +++ b/refactor/baselines/ads-contract.md @@ -0,0 +1,65 @@ +# Ads 发布与工作区契约 + +PKG-ADS-01,源码起点 `bafbf2c8`。来源与全部字节指纹见 [发布记录](ads-release.json), +可运行 `node --test refactor/scripts/ads-contract.test.mjs` 重新校验。 + +## 来源和支持范围 + +本次 registry 观察到 1.0.0/1.0.2/1.0.3/1.0.4/1.0.6,全部五个归档的 SHA512/SHA256、 +30 个文件与 manifest 已校验。最后发布版本是 1.0.6;工作区 2.1.0 没有对应 npm 版本。 +冻结版本观察不等于未来 latest。版本政策仍按工作区 2.1.0 → 3.0.0,REL-01/09 复核落地。 + +1.0.6 为主要行为基线;更早归档用于确定公共参数和历史差异,不能把早期重复监听等缺陷 +升级为新实现必须保留的规则。五个版本运行时都是 html/video/url,从未实现 source/type。 +1.0.6 的 registry gitHead 对应仓库核心 manifest 为 4.5.5,无 plugin peer 或版本检查; +当前源码新增核心 >=5 检查,并把原生 document visibility 订阅换成核心事件桥。 +4.5.5 是有来源的旧核心组合候选,实际 npm/core 浏览器验收仍由 05 完成,不能宣称全部 4.x +已支持或以当前未发布源码的版本门槛证明已发布插件也有此限制。 + +## 公开形状 + +| 范围 | npm 1.0.6 / 当前工作区 | +| --- | --- | +| 工厂 | artplayerPluginAds(option)(art),同步返回 name/skip/pause/play;JS 省略 option 可合并默认值 | +| 参数 | html/video/url 默认空字符串;非空 video 优先于 html;图片通过 html 中的 img 展示 | +| 时长 | playDuration=5、totalDuration=10,运行时校验为 number,按一秒倒计时;不得擅自更改广告时长产品规则 | +| 静音 | muted=false;1.0.6 支持初始值及音量按钮,1.0.0 未包含该项 | +| i18n | close/countdown/detail/canBeClosed 四字段,整体浅合并;部分对象不能当作已实现的深合并 | +| 启动 | ready 后订阅首次 play/video:playing,init 只一次;show 后暂停主内容 | +| 视频广告 | loop/playsInline;metadata 后开始倒计时和 play;媒体 error 走 skip;广告时长不等同素材 duration | +| HTML 广告 | 使用调用方 HTML,立即倒计时;不是内置广告服务,不访问实际计费追踪 | +| 结果方法 | play/pause 管理倒计时,未同步暂停/恢复广告 video;skip 绕过按钮阈值并请求主内容播放 | +| 事件 | artplayerPluginAds:skip 与 :click,参数为校验合并后的 option;无 URL 点击广告内容仍 emit click,详情入口隐藏 | +| DOM/CSS | template.$ads、artplayer-plugin-ads 及其 html/video/loading/timer/close/countdown/control/detail/muted/fullscreen 类,style ID 相同 | +| 静态 | 已发布 bundle 的 env/version/build 和 window.artplayerPluginAds;注入一次样式,SSR 无 document 时不注入 | + +skip 目前先 art.play,再暂停广告 video、隐藏容器、emit skip。正常用户调用、事件时序与 +同步返回需在 02 固定。重复/过早调用、销毁后的工作属于待复现问题,不能从源码有函数 +就宣称这些路径可用。options 被重赋给外层工厂变量,复用工厂多实例的身份也需要测试。 + +## 类型与入口冲突 + +| 来源 | 声明与实际内容 | 后续 | +| --- | --- | --- | +| npm 五版声明 | export=、export as namespace;html/video/url;totalDuration 错写 string;没有 i18n | 04 保留旧类型消费者接受方式,同时表达真实数字/i18n、工厂可选参数 | +| 工作区声明 | default export;必填 source/type,totalDuration number,没有 html/video/url/i18n | source/type 是已存在的工作区类型接受面,但不是发布 JS 接口;不能据此删旧真实字段或悄悄伪称原本支持 image 类型 | +| npm bundle | Parcel CommonJS 是 {default: factory},浏览器显式 window 全局;没有 module/exports/legacy 字段 | 06 保留 require(pkg).default/既有 dist 文件路径,并兼容当前 callable/ESM/legacy 入口 | +| 工作区 bundle/manifest | main/module/legacy 三格式、条件 exports;类型路径沿用但内容已变 | 不能将新文件布局直接当作已发布布局;隔离 tarball 消费证明入口 | + +实际 npm bundle 的 namespace、全局工厂、静态字段、延迟 ready 注册及结果形状已通过 Node +加载检查。校验器在形状用例中只记录参数/schema;没有模拟真实播放或把校验器桩当成媒体测试。 +具体合法/非法值及类型编译矩阵由 02/04 补齐。源文件也在旧归档内,但带 Parcel 专属 +bundle-text 导入;私有打包器标识不属于稳定 API,不能把这段 raw source 直接当 Node 入口。 + +## 风险与执行顺序 + +- ADS-TYPE-01:公开声明和 JS 输入双向漂移,旧 string 时长的类型兼容与运行时拒绝分开处理。 +- ADS-DIST-01:旧 default namespace/路径和当前 callable/exports 不同;保留两侧可用调用。 +- ADS-CORE-01:已发布包没有 >=5 门槛;以能力 fallback 和真实 4.5.5/5.4.1/候选核心验证边界。 +- ADS-LIFE-01:重复 play 可建立多个计时链,skip 不清计时器,无 destroy 清理;工厂复用、异常/过早调用待测。 +- ADS-MEDIA-01:metadata 时序、广告与主内容 play 拒绝、隐藏页媒体状态/换源/错误恢复待测。 +- ADS-UI-01:playDuration<=0 初始文案可关闭,但 isCanClose 直到首 tick 才真;全屏按钮图标和外部状态待核验。 + +上述除实际 export 形状外,运行路径问题先记 source-observed,由 02 复现再在 03 修正。 +03 按配置/状态与计时/视图/内容恢复拆分;04 严格 TS 与公开类型;05 真实媒体/旧核心/ +多实例;06 demo、文档和完整分发。保持本地广告素材,不触发真实投放、跳转或计费服务。 diff --git a/refactor/baselines/ads-release.json b/refactor/baselines/ads-release.json new file mode 100644 index 000000000..d8e1b2354 --- /dev/null +++ b/refactor/baselines/ads-release.json @@ -0,0 +1,280 @@ +{ + "schemaVersion": 1, + "task": "PKG-ADS-01", + "capturedAt": "2026-09-11T22:53:57.648Z", + "sourceCommit": "bafbf2c8b03c38e32f639eda8ce974bff4bb6935", + "registryObservation": { + "registryUrl": "https://registry.npmjs.org/artplayer-plugin-ads", + "distTags": { + "latest": "1.0.6" + }, + "versions": [ + "1.0.0", + "1.0.2", + "1.0.3", + "1.0.4", + "1.0.6" + ], + "publishedAt": "2022-09-19T02:02:50.472Z", + "workspaceVersion": "2.1.0", + "workspaceVersionPublished": false, + "note": "Observed metadata, not support for all historical releases or a future latest guarantee." + }, + "release": { + "name": "artplayer-plugin-ads", + "version": "1.0.6", + "metadataUrl": "https://registry.npmjs.org/artplayer-plugin-ads/1.0.6", + "tarball": "https://registry.npmjs.org/artplayer-plugin-ads/-/artplayer-plugin-ads-1.0.6.tgz", + "integrity": "sha512-KMGGf7lhgj6DQXHPOg28jWYkVmDfOZJ4Gr4iF28Wy2GXNPh2ZJtlsFQ2IuWqXCElRPvSLzYK5HugF0OUnVszaw==", + "sha256": "ff132ff8a7108d8b26421621e4aac1d7edf8c99099a5ab4fd2190bd79b8636cd", + "archiveBytes": 5423, + "registryGitHead": "0b0aad78a56c024f00924c95f259bd545c29f196", + "files": { + "package/README.md": "9ef828936612264232159ce5c34ae7de708872b5d2fd54b7b4ee1fdbd9f17e39", + "package/dist/artplayer-plugin-ads.js": "7b53d6813811173f2ece3a32f934cbee24493ac43594aac179f1307a764abaee", + "package/package.json": "3a068fed095fbc3a38e8c1dd26c20bc865399bd4fff4b80909007ec9adb507b4", + "package/src/index.js": "89bc886fc54c191154c55d228fbdbc18e8effe1e6c881ab6760302d71092423e", + "package/src/style.less": "ab9649182ed3df9ed4f8e2347696ca9da173d11204a70a016648d1d54aac5441", + "package/types/artplayer-plugin-ads.d.ts": "b0fff3c7f5ac0f3b1dede8e827aa00104f397a0f26f2aea4aa788077b91b05b1" + }, + "manifest": { + "license": "MIT", + "version": "1.0.6", + "name": "artplayer-plugin-ads", + "types": "types/artplayer-plugin-ads.d.ts", + "description": "Ads plugin for ArtPlayer", + "main": "dist/artplayer-plugin-ads.js", + "author": "Harvey Zack ", + "browserslist": "> 0.5%, last 2 versions, not dead", + "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" + ] + } + }, + "source": { + "packages/artplayer-plugin-ads/src/index.js": "dcb19c5b294405cc08601c281b77ab44ff70f49b70f9332e18d1ee0873b91fea", + "packages/artplayer-plugin-ads/src/style.less": "0eeed3f29ccafc6836f5bc4338f58f784edd114d2b100fda7fc395329031a406", + "packages/artplayer-plugin-ads/types/artplayer-plugin-ads.d.ts": "1cc5c1fc04727fc81398e03e384fbbd3ac78a5a02d55da71279b07b0afb08d98", + "packages/artplayer-plugin-ads/README.md": "b507ae8bc65ea0982aa209be59a8c194e815509979636c060e858ce599a16473", + "packages/artplayer-plugin-ads/package.json": "5b7a876d58b8e88c994c3a72754ce6d7dbea0100a35928a99d66641de7c82e0d", + "packages/artplayer-plugin-ads/dist/artplayer-plugin-ads.mjs": "0919678423424047b28d9d803835d9ebc98f4477e04e58d497e8ed8c48b95008", + "docs/assets/example/ads.js": "c84a97d2b1970c323a0c2538b846312bff38513e10de271fe5f239953da88cd3", + "packages/artplayer-plugin-ads/dist/artplayer-plugin-ads.js": "ceb2d8a23a6735c161b31a4a427d8a757679aaf8a5a106ef40a6f047061f1df8", + "packages/artplayer-plugin-ads/dist/artplayer-plugin-ads.legacy.js": "1e10c3a6b51de18e54cd02dd342ab4010fc466de6257659cf1d2e087642aa9a3" + }, + "comparisons": [ + { + "file": "package.json", + "publishedLF": "3a068fed095fbc3a38e8c1dd26c20bc865399bd4fff4b80909007ec9adb507b4", + "workspaceLF": "5b7a876d58b8e88c994c3a72754ce6d7dbea0100a35928a99d66641de7c82e0d", + "equal": false + }, + { + "file": "README.md", + "publishedLF": "9ef828936612264232159ce5c34ae7de708872b5d2fd54b7b4ee1fdbd9f17e39", + "workspaceLF": "b507ae8bc65ea0982aa209be59a8c194e815509979636c060e858ce599a16473", + "equal": false + }, + { + "file": "types/artplayer-plugin-ads.d.ts", + "publishedLF": "b0fff3c7f5ac0f3b1dede8e827aa00104f397a0f26f2aea4aa788077b91b05b1", + "workspaceLF": "1cc5c1fc04727fc81398e03e384fbbd3ac78a5a02d55da71279b07b0afb08d98", + "equal": false + }, + { + "file": "src/index.js", + "publishedLF": "89bc886fc54c191154c55d228fbdbc18e8effe1e6c881ab6760302d71092423e", + "workspaceLF": "dcb19c5b294405cc08601c281b77ab44ff70f49b70f9332e18d1ee0873b91fea", + "equal": false + }, + { + "file": "src/style.less", + "publishedLF": "ab9649182ed3df9ed4f8e2347696ca9da173d11204a70a016648d1d54aac5441", + "workspaceLF": "0eeed3f29ccafc6836f5bc4338f58f784edd114d2b100fda7fc395329031a406", + "equal": false + } + ], + "olderReleases": [ + { + "name": "artplayer-plugin-ads", + "version": "1.0.0", + "metadataUrl": "https://registry.npmjs.org/artplayer-plugin-ads/1.0.0", + "tarball": "https://registry.npmjs.org/artplayer-plugin-ads/-/artplayer-plugin-ads-1.0.0.tgz", + "integrity": "sha512-c+OE+c8sm10kkpVKZXd3nKLEKZ7JuQ9kGxzpCdDaBD+6cs+fEvtbwIq14OMtdthOKRomqh7DAHZDxlGNovACOA==", + "sha256": "8c45d55e516377bc099d5df558cdeab8d4c134b39200ad7f1c3969a3e3ec340a", + "archiveBytes": 5104, + "registryGitHead": "d1ead6c5436cad30149711d55e3819376dbf0c13", + "files": { + "package/README.md": "9ef828936612264232159ce5c34ae7de708872b5d2fd54b7b4ee1fdbd9f17e39", + "package/dist/artplayer-plugin-ads.js": "6763a4534940535bd615d68bc5442cc089407940e4c96f728273413984344538", + "package/package.json": "1a20a5a35061279678d7998dcd836a16b39eaf0072b5cbfac61c094084c91e03", + "package/src/index.js": "adc062c261d96ad52e07fb478c8dcaeb9b4b31b9a38321744a10716c2462d7ef", + "package/src/style.less": "e7f836cae68cb5d5500e995b5094cec033545e1eeadc40bcdbf1a5b4afa7cdba", + "package/types/artplayer-plugin-ads.d.ts": "c0e92e646840136e556388159c729f967568f96ccb42cab82250a891fd9490e0" + }, + "manifest": { + "license": "MIT", + "version": "1.0.0", + "name": "artplayer-plugin-ads", + "types": "types/artplayer-plugin-ads.d.ts", + "description": "Ads plugin for ArtPlayer", + "main": "dist/artplayer-plugin-ads.js", + "author": "Harvey Zack ", + "browserslist": "> 0.5%, last 2 versions, not dead", + "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" + ] + } + }, + { + "name": "artplayer-plugin-ads", + "version": "1.0.2", + "metadataUrl": "https://registry.npmjs.org/artplayer-plugin-ads/1.0.2", + "tarball": "https://registry.npmjs.org/artplayer-plugin-ads/-/artplayer-plugin-ads-1.0.2.tgz", + "integrity": "sha512-MsjG41tmDRsjLCIYwNxekLFFKBR4ZhzvIO9NjT6Z+kcAslrLramSUNlPpaT+Y19gosSCvbn9rBPkQmYHclEWCg==", + "sha256": "d922a323dbdc3d67f2beb5e70c228ffeafabedec02fd37ff02cc990270e05b38", + "archiveBytes": 5196, + "registryGitHead": "d81f9d4f65d0701f24616650c1efeff505651562", + "files": { + "package/README.md": "9ef828936612264232159ce5c34ae7de708872b5d2fd54b7b4ee1fdbd9f17e39", + "package/dist/artplayer-plugin-ads.js": "1b5410db99249b5d23b979d507f7b5b0cf9796f7bfbd757d6789e1ea86a44dc6", + "package/package.json": "255bc4623c65d11537373b32c8cf99498b73cedbb30f454032c40996804f44ff", + "package/src/index.js": "6d99b31b9df39ce938439c5c4c7c69bccce2b928651b8e66b9c2c3dff76c42c4", + "package/src/style.less": "ab9649182ed3df9ed4f8e2347696ca9da173d11204a70a016648d1d54aac5441", + "package/types/artplayer-plugin-ads.d.ts": "c0e92e646840136e556388159c729f967568f96ccb42cab82250a891fd9490e0" + }, + "manifest": { + "license": "MIT", + "version": "1.0.2", + "name": "artplayer-plugin-ads", + "types": "types/artplayer-plugin-ads.d.ts", + "description": "Ads plugin for ArtPlayer", + "main": "dist/artplayer-plugin-ads.js", + "author": "Harvey Zack ", + "browserslist": "> 0.5%, last 2 versions, not dead", + "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" + ] + } + }, + { + "name": "artplayer-plugin-ads", + "version": "1.0.3", + "metadataUrl": "https://registry.npmjs.org/artplayer-plugin-ads/1.0.3", + "tarball": "https://registry.npmjs.org/artplayer-plugin-ads/-/artplayer-plugin-ads-1.0.3.tgz", + "integrity": "sha512-kJYeX+WYia8qpOl1GAktIVZwaqMv1OYOUYyWQgGiXSJYOT1YZGIdk6XSs5DOjJJQ+zVwZfqDVVUPZRrnuXKuoA==", + "sha256": "9859f8804d0e24c5688670507e20ca2720cbb5b701d3a68f5b6b05b03978c258", + "archiveBytes": 5222, + "registryGitHead": "1d4b2d58acca0226ecfaf3a60a12691e76e31e02", + "files": { + "package/README.md": "9ef828936612264232159ce5c34ae7de708872b5d2fd54b7b4ee1fdbd9f17e39", + "package/dist/artplayer-plugin-ads.js": "d8921a872f05bb12a7f6011f20c2350b3509d6a121c90d4ddbe411bc33afc6a9", + "package/package.json": "b1d32fa4ec524fcde30e471eba85fa41d4ef431f27223353268ccf8dc9514e30", + "package/src/index.js": "94681f237ba3cff0d6e2e43b0c4794e4ca5200f1a1a88107963c79a28240155b", + "package/src/style.less": "ab9649182ed3df9ed4f8e2347696ca9da173d11204a70a016648d1d54aac5441", + "package/types/artplayer-plugin-ads.d.ts": "c0e92e646840136e556388159c729f967568f96ccb42cab82250a891fd9490e0" + }, + "manifest": { + "license": "MIT", + "version": "1.0.3", + "name": "artplayer-plugin-ads", + "types": "types/artplayer-plugin-ads.d.ts", + "description": "Ads plugin for ArtPlayer", + "main": "dist/artplayer-plugin-ads.js", + "author": "Harvey Zack ", + "browserslist": "> 0.5%, last 2 versions, not dead", + "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" + ] + } + }, + { + "name": "artplayer-plugin-ads", + "version": "1.0.4", + "metadataUrl": "https://registry.npmjs.org/artplayer-plugin-ads/1.0.4", + "tarball": "https://registry.npmjs.org/artplayer-plugin-ads/-/artplayer-plugin-ads-1.0.4.tgz", + "integrity": "sha512-RsIF7dVjRrfdUmx0GkKsDznAgpFCN9XVFy3PL/zS1/LzJ8P1Un1QYSte8Y/AKVgH53J299kM4WNZzLUTiMZo3w==", + "sha256": "eece3d3e584574781bb2cf8850dd4fc2b8fece50e1a00568a59af3e4e9d05a5f", + "archiveBytes": 5292, + "registryGitHead": "1d4b2d58acca0226ecfaf3a60a12691e76e31e02", + "files": { + "package/README.md": "9ef828936612264232159ce5c34ae7de708872b5d2fd54b7b4ee1fdbd9f17e39", + "package/dist/artplayer-plugin-ads.js": "284413cb5bb20dfebdc19b8eb1424b34cc763ccc32132068711352375320b12c", + "package/package.json": "db678bc8101d3c7a7f8e767b78cdf4b6733c585b8a46a6cf64753e52dac9c49c", + "package/src/index.js": "277dd9d4e4e590bc23913ed17c3ebf8033588eea91a01478aedd0fdf89fb1a40", + "package/src/style.less": "ab9649182ed3df9ed4f8e2347696ca9da173d11204a70a016648d1d54aac5441", + "package/types/artplayer-plugin-ads.d.ts": "c0e92e646840136e556388159c729f967568f96ccb42cab82250a891fd9490e0" + }, + "manifest": { + "license": "MIT", + "version": "1.0.4", + "name": "artplayer-plugin-ads", + "types": "types/artplayer-plugin-ads.d.ts", + "description": "Ads plugin for ArtPlayer", + "main": "dist/artplayer-plugin-ads.js", + "author": "Harvey Zack ", + "browserslist": "> 0.5%, last 2 versions, not dead", + "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" + ] + } + } + ], + "historicalCore": { + "commit": "0b0aad78a56c024f00924c95f259bd545c29f196", + "file": "packages/artplayer/package.json", + "version": "4.5.5", + "sha256LF": "d559f1c2a6b308842517f025553b38b29e7948241458a93fc573533856224eeb", + "scope": "Source association only; actual 4.5.5 package and browser compatibility still require PKG-ADS-05 verification" + } +} diff --git a/refactor/changes/2026-09-12-PKG-ADS-01-contract.md b/refactor/changes/2026-09-12-PKG-ADS-01-contract.md new file mode 100644 index 000000000..3e78cde59 --- /dev/null +++ b/refactor/changes/2026-09-12-PKG-ADS-01-contract.md @@ -0,0 +1,25 @@ +# PKG-ADS-01:Ads 实际发布基线 + +从 `bafbf2c8` 开始核对 Ads。发现工作区 2.1.0 未发布,实际 npm 仅五个 1.0.x 版本, +最新观察为 1.0.6。已下载并校验所有五个归档、30 个成员,记录原始 manifest、Git 关联、 +源码/声明/样式/README 差异。初始采集错误地假设 2.1.0 存在且有 mjs,读取失败; +随后依据 registry 版本列表和实际 tar inventory 修正,没有生成假的 2.1.0 发布基线。 + +五版运行时始终为 html/video/url,当前声明的 source/type 并非已发布参数;旧声明还将 +number 时长误标 string。真实 CommonJS 返回 default namespace,与当前 callable 入口 +不同;1.0.6 的关联提交核心为 4.5.5,而当前源码增加了 >=5 检查。这些都进入兼容计划, +不能仅保留当前错误声明或要求全部旧消费者升级核心。 + +新增 [契约](../baselines/ads-contract.md)、[来源数据](../baselines/ads-release.json) 和 +`ads-contract.mjs` 校验器。两项 Node 检查通过:全部发布/历史文件指纹;实际 npm 1.0.6 +CommonJS/浏览器工厂、静态字段、延迟 ready 订阅与同步结果形状。测试没有执行广告媒体 +或伪装成浏览器验收;后续 02 建立倒计时、生命周期、广告媒体和错误用例。 + +ADS-TYPE/DIST/CORE/LIFE/MEDIA/UI 六项差异分别指派到 02~06;类型、模块、timer、DOM、 +主片恢复和旧核心矩阵的后续交付物已细化。保留大版本目标 3.0.0,未改版本、生产源码、 +依赖、样式、demo 或 dist。任务完成指的是来源/契约核对,插件重构仍未完成。 + +局部 lint、两项 Ads 检查及完整 45 项基线检查通过;计划/风险链接检查通过。45 项包含 +这两项 Ads 测试,不重复累计为 47;未重新运行完整工程 CI 或浏览器播放。 +执行指纹见 [验证记录](../baselines/ads-contract-validation.json)。PKG-ADS-01 完成后为 +217 项:75 done、4 doing、138 todo,下一项 PKG-ADS-02。本任务独立本地提交,不推送发布。 diff --git a/refactor/environment-matrix.md b/refactor/environment-matrix.md index f73d6f8ad..e748dd01a 100644 --- a/refactor/environment-matrix.md +++ b/refactor/environment-matrix.md @@ -33,7 +33,7 @@ Node10 是 TS 模块解析模式名,不代表支持 Node.js 10。现代 es2020 | 包 / 工作区版本 | 能力与固定样本/资源要求 | 当前状态、待补环境 | 负责验证任务 | | --- | --- | --- | --- | | artplayer / 5.4.1 | 原生媒体、字幕、切源、控件/设置、全屏/PiP、SSR;ENV-MEDIA + VTT/SRT | 发布 5.4.0 已冻结;桌面部分基线,移动/原生全屏/PiP/多引擎待测 | CORE-07/16/17/18/22/23、ENG-05 | -| artplayer-plugin-ads / 2.1.0 | 广告图片/视频、本片恢复、跳过、错误/销毁;本地短广告和主片 | 发布范围及所有浏览器场景待;无需借 IMA 结果代替自有广告逻辑 | PKG-ADS-01/02/05/06 | +| artplayer-plugin-ads / npm 1.0.6、工作区 2.1.0 未发布 | 五个 1.0.x 归档已冻结;HTML/视频、本片恢复、跳过、错误/销毁;本地短广告和主片 | 关联历史核心 4.5.5 尚待实际归档/浏览器验证;与 5.4.1/候选核心组合均待。无需借 IMA 结果替代自有逻辑 | PKG-ADS-01/02/05/06 | | artplayer-plugin-ambilight / 1.1.0 | Canvas 采样、CORS、resize、后台/销毁;同源及受控跨源 MP4 | 待真实画布和资源测量 | PKG-AMBILIGHT-01/02/05/06 | | artplayer-plugin-asr / 2.1.0 | PCM 音频输出/暂停/销毁;AudioContext + 调用方服务 | 待音频环境与调用方 WebSocket 验证;示例服务版本未知,不在 PR 调线上服务 | PKG-ASR-01/02/05/06、EX-03 | | artplayer-plugin-audio-track / 1.1.0 | 独立外部 Audio 与主视频时钟同步;本地 MP4+AAC,偏移/切源/缓冲/失败样本 | 1.1.0 发布及调用契约见 [包基线](baselines/audio-track-contract.md);旧核心 5.4.0 为具体对照点,桌面/Safari 实测待完成 | PKG-AUDIO-01/02/05/06 | diff --git a/refactor/plan.md b/refactor/plan.md index d45e20aea..c4a24551b 100644 --- a/refactor/plan.md +++ b/refactor/plan.md @@ -4,7 +4,7 @@ 基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 217 项,范围 22 个包及工作区/示例。 -状态:todo 139 / doing 4 / blocked 0 / done 74 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 +状态:todo 138 / doing 4 / blocked 0 / done 75 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。 @@ -256,12 +256,12 @@ | ID | 范围 / 步骤 | 前置依赖 | 交付物 | 验收条件 | 风险 | 状态 | | --- | --- | --- | --- | --- | --- | --- | -| PKG-ADS-01 | artplayer-plugin-ads
核对包契约与历史用法 | BASE-05 | 核对 html/video/url 与 source/type 声明差异,结果方法和倒计时 | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结 | H | todo | -| PKG-ADS-02 | artplayer-plugin-ads
建立特有行为与错误测试 | PKG-ADS-01, ENG-03, ENG-05 | 视频/HTML/图片实际支持路径、跳过阈值、暂停恢复、加载失败 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | todo | -| PKG-ADS-03 | artplayer-plugin-ads
整理内部职责与资源 | PKG-ADS-02, CORE-10, CORE-13 | 广告状态/计时/视图/主视频恢复分离,不改变广告产品规则 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | todo | -| PKG-ADS-04 | artplayer-plugin-ads
迁移自有源码和公开类型 | PKG-ADS-03, ENG-04, ENG-06, CORE-07 | 旧参数契约和真实返回方法的兼容类型;错误声明单独处理 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | todo | -| PKG-ADS-05 | artplayer-plugin-ads
验证新旧核心和组合 | PKG-ADS-04, CORE-22 | 本地广告 fixture、多实例、正常结束/跳过/销毁恢复内容 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo | -| PKG-ADS-06 | artplayer-plugin-ads
验证分发并同步文档 | PKG-ADS-05, ENG-07 | ads.js、README/类型一致性、旧参数保留与变更日志 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo | +| PKG-ADS-01 | artplayer-plugin-ads
核对包契约与历史用法 | BASE-05 | 核对 npm 1.0.0~1.0.6 与未发布工作区 2.1.0 的真实参数、类型、结果、倒计时、分发和旧核心来源 | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结 | H | done | +| PKG-ADS-02 | artplayer-plugin-ads
建立特有行为与错误测试 | PKG-ADS-01, ENG-03, ENG-05 | 固定 npm 1.0.6 与工作区契约;HTML/视频/图片 HTML、跳过阈值、计时链、工厂复用、多实例、过早/重复方法、播放拒绝与销毁 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | todo | +| PKG-ADS-03 | artplayer-plugin-ads
整理内部职责与资源 | PKG-ADS-02, CORE-10, CORE-13 | 配置/广告状态/计时/视图/主视频恢复分离,保留产品规则;资源与版本检查改用经验证的旧核心能力边界 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | todo | +| PKG-ADS-04 | artplayer-plugin-ads
迁移自有源码和公开类型 | PKG-ADS-03, ENG-04, ENG-06, CORE-07 | 兼容真实 html/video/url/i18n、旧 export= 与工作区 source/type 类型接受面;时长 string/number 漂移与未实现别名单独处置 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | todo | +| PKG-ADS-05 | artplayer-plugin-ads
验证新旧核心和组合 | PKG-ADS-04, CORE-22 | 本地广告 fixture、多实例、正常结束/跳过/销毁;核验历史关联核心 4.5.5、发布 5.4.1 与候选核心的实际归档/能力/媒体组合 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo | +| PKG-ADS-06 | artplayer-plugin-ads
验证分发并同步文档 | PKG-ADS-05, ENG-07 | ads.js/README/声明一致;旧 require(pkg).default、原 dist 路径、当前 callable/ESM/legacy 的隔离 tarball 及浏览器验收 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo | ## 5 包迁移:artplayer-plugin-vast @@ -484,3 +484,4 @@ - PKG-DASH-03: [记录](changes/2026-09-12-PKG-DASH-03-runtime.md) [记录](baselines/dash-runtime.json) [记录](dash-validation.md) - PKG-DASH-04: [记录](changes/2026-09-12-PKG-DASH-04-types.md) [记录](baselines/dash-types-validation.json) [记录](dash-validation.md) - PKG-DASH-05: [记录](changes/2026-09-12-PKG-DASH-05-sdk-checkpoint.md) [记录](baselines/dash-sdk.json) [记录](baselines/dash-sdk-checkpoint.json) [记录](dash-validation.md) [记录](changes/2026-09-12-PKG-DASH-05-sdk-types.md) [记录](baselines/dash-sdk-types-validation.json) [记录](changes/2026-09-12-PKG-DASH-05-sdk-events.md) [记录](baselines/dash-sdk-events-validation.json) [记录](changes/2026-09-12-PKG-DASH-05-seek-diagnosis.md) [记录](baselines/dash-seek-diagnosis.json) +- PKG-ADS-01: [记录](baselines/ads-release.json) [记录](baselines/ads-contract.md) [记录](baselines/ads-contract-validation.json) [记录](changes/2026-09-12-PKG-ADS-01-contract.md) [记录](scripts/ads-contract.test.mjs) diff --git a/refactor/progress.md b/refactor/progress.md index 74d48c1ed..9e5979755 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -1,5 +1,18 @@ # 进度与证据 +## 最新完成:Ads-01 实际发布契约核对 + +工作区 Ads 2.1.0 未发布;实际 npm 五个 1.0.x 版本、30 个成员已校验冻结,主要行为基线 +为 1.0.6。五版均使用 html/video/url,当前 source/type 声明并非实际发布接口;旧声明的 +totalDuration:string、旧 CommonJS default namespace 与当前 callable 的差异分别记录。 +1.0.6 关联核心为 4.5.5,当前源码新增 >=5 门槛不能作为旧发布兼容证据;旧核心实际组合待测。 + +六项类型/分发/旧核心/生命周期/媒体/UI 风险已指派,02~06 交付物已细化。局部 lint、 +两项 Ads 检查及完整 45 项基线检查通过,没有修改生产源码、依赖、版本或产物。 +当前 217 项:75 done、4 doing、138 todo,下一项 Ads-02 建立时钟/媒体/错误回归。 +见 [契约](baselines/ads-contract.md)、[本步说明](changes/2026-09-12-PKG-ADS-01-contract.md) +及 [执行证据](baselines/ads-contract-validation.json)。DASH 等开放媒体/设备风险仍保留。 + ## 当前实施:DASH-05 暂停 seek 根因已定位 真实 SourceBuffer、SDK seekTime/缓冲事件和处理器状态表明,SDK 4.5.2 在空裁剪分支未更新 diff --git a/refactor/risk-table.md b/refactor/risk-table.md index 46cc135f4..a30fd578b 100644 --- a/refactor/risk-table.md +++ b/refactor/risk-table.md @@ -141,3 +141,9 @@ | DASH-TYPE-01 | resolved / 源码/产物事实 | DASH 类型未表达默认工厂参数、SDK 回调对象和现代模块入口 | PKG-DASH-04 | | DASH-DEMO-01 | open / 源码/产物事实 | DASH 示例仅新 SDK 且重复安装会累积销毁回调 | PKG-DASH-06 | | DASH-SEEK-01 | open / 已复现 | dash.js 4.5.2 空裁剪漏更新缓冲量,稳定暂停 seek 后调度停滞 | PKG-DASH-05, REVIEW-02 | +| ADS-TYPE-01 | open / 源码/产物事实 | Ads 声明丢失真实 html/video/url,旧 totalDuration 为 string,当前 source/type 未实现 | PKG-ADS-02, PKG-ADS-04 | +| ADS-DIST-01 | open / 已复现 | Ads npm 1.0.6 default namespace 与未发布 2.1.0 callable/exports 分发不同 | PKG-ADS-04, PKG-ADS-06, REL-01 | +| ADS-CORE-01 | open / 源码/产物事实 | Ads 旧发布关联核心 4.5.5,当前源码新增 >=5 门槛和 document 事件桥依赖 | PKG-ADS-02, PKG-ADS-03, PKG-ADS-05 | +| ADS-LIFE-01 | open / 源码/产物事实 | 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 | open / 源码/产物事实 | Ads 零等待跳过与初始状态不符,全屏图标/外部状态需核验 | PKG-ADS-02, PKG-ADS-03, PKG-ADS-05 | diff --git a/refactor/risks.json b/refactor/risks.json index ef12a7bf7..917602d7c 100644 --- a/refactor/risks.json +++ b/refactor/risks.json @@ -2981,6 +2981,66 @@ ], "compatibleResolution": "已用原生 SDK、SourceBuffer/调度指标和 SDK 5 空裁剪刷新的一行诊断回植确认根因。兼容处置尚未落地;不在核心伪造 timeupdate、不偏移 seek、不强制所有旧用户升级 SDK,也不将诊断补丁计为正式上游通过。", "closureCriteria": "原失败的新旧核心/插件组合有确定根因与对应修复/可验证环境边界;原生对照及原顺序、原目标回归成立,后续偶然通过不能关闭。" + }, + { + "id": "ADS-TYPE-01", + "title": "Ads 声明丢失真实 html/video/url,旧 totalDuration 为 string,当前 source/type 未实现", + "confirmation": "source-observed", + "status": "open", + "owners": ["PKG-ADS-02", "PKG-ADS-04"], + "evidence": ["refactor/baselines/ads-release.json", "refactor/baselines/ads-contract.md", "packages/artplayer-plugin-ads/types/artplayer-plugin-ads.d.ts", "packages/artplayer-plugin-ads/src/index.js"], + "compatibleResolution": "同时保护实际 npm 字段/旧声明消费者和当前类型接受面;真实数字时长、i18n 与 source/type 别名的行为差异单独登记,不伪称旧实现支持别名。", + "closureCriteria": "两套旧消费者、可选工厂、合法运行字段和错误参数有严格正反例,声明与真实方法一致。" + }, + { + "id": "ADS-DIST-01", + "title": "Ads npm 1.0.6 default namespace 与未发布 2.1.0 callable/exports 分发不同", + "confirmation": "reproduced", + "status": "open", + "owners": ["PKG-ADS-04", "PKG-ADS-06", "REL-01"], + "evidence": ["refactor/baselines/ads-release.json", "refactor/baselines/ads-contract.md", "refactor/scripts/ads-contract.test.mjs", "packages/artplayer-plugin-ads/package.json"], + "compatibleResolution": "保留旧 require(pkg).default、global 与有效 dist 路径,同时支持当前 callable/ESM/legacy。工作区 2.1.0 不冒充实际已发布版本,正式目标仍按计划 3.0.0。", + "closureCriteria": "工作区外真实 tarball 的旧/新导入、浏览器与声明入口全部验证,版本占用再次核实。" + }, + { + "id": "ADS-CORE-01", + "title": "Ads 旧发布关联核心 4.5.5,当前源码新增 >=5 门槛和 document 事件桥依赖", + "confirmation": "source-observed", + "status": "open", + "owners": ["PKG-ADS-02", "PKG-ADS-03", "PKG-ADS-05"], + "evidence": ["refactor/baselines/ads-release.json", "refactor/baselines/ads-contract.md", "packages/artplayer-plugin-ads/src/index.js"], + "compatibleResolution": "先核验实际核心归档和能力,通过本地 fallback 保留原用户调用;不能用未发布源码门槛强制全部旧消费者升级,也不泛称全部 4.x 通过。", + "closureCriteria": "4.5.5 关联来源和实际可用组合明确,5.4.1/候选核心与原生 visibility/事件桥路径有真实浏览器证据。" + }, + { + "id": "ADS-LIFE-01", + "title": "Ads 计时链、重复/过早调用、工厂复用与销毁缺乏资源边界", + "confirmation": "source-observed", + "status": "open", + "owners": ["PKG-ADS-02", "PKG-ADS-03", "PKG-ADS-05"], + "evidence": ["refactor/baselines/ads-contract.md", "packages/artplayer-plugin-ads/src/index.js"], + "compatibleResolution": "先复现重复 play 的计时链、skip 后计时、destroy 后恢复与复用工厂 options;以实例所有权和幂等清理修正,不更改广告时长/跳过规则。", + "closureCriteria": "时钟/订阅/DOM 与主片恢复的成功、异常、重入、销毁、复用和多实例回归通过。" + }, + { + "id": "ADS-MEDIA-01", + "title": "Ads metadata 与 play 拒绝、隐藏页媒体状态和内容恢复仍缺真实验证", + "confirmation": "source-observed", + "status": "open", + "owners": ["PKG-ADS-02", "PKG-ADS-03", "PKG-ADS-05"], + "evidence": ["refactor/baselines/ads-contract.md", "packages/artplayer-plugin-ads/src/index.js", "docs/assets/example/ads.js"], + "compatibleResolution": "用本地媒体验证事件安装顺序、加载失败与拒绝、倒计时和广告播放的独立语义,恢复主片时不吞公共拒绝或在销毁后播放。", + "closureCriteria": "实际广告/主片的加载、暂停/恢复、失败/换源/销毁和隐藏状态有新旧核心组合证据。" + }, + { + "id": "ADS-UI-01", + "title": "Ads 零等待跳过与初始状态不符,全屏图标/外部状态需核验", + "confirmation": "source-observed", + "status": "open", + "owners": ["PKG-ADS-02", "PKG-ADS-03", "PKG-ADS-05"], + "evidence": ["refactor/baselines/ads-contract.md", "packages/artplayer-plugin-ads/src/index.js", "docs/assets/example/ads.js"], + "compatibleResolution": "复现 playDuration<=0 文案可关闭但 isCanClose 尚为 false;按既有 demo 规则修复,验证全屏按钮和外部退出状态,保留 CSS/DOM 钩子。", + "closureCriteria": "零/正/不可跳阈值、真实点击、倒计时/详情/静音/全屏与外部状态组合均有回归。" } ] } diff --git a/refactor/scripts/ads-contract.mjs b/refactor/scripts/ads-contract.mjs new file mode 100644 index 000000000..944691199 --- /dev/null +++ b/refactor/scripts/ads-contract.mjs @@ -0,0 +1,49 @@ +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' + +const root = path.resolve(refactorDir, '..') +const normalizeLF = value => value.toString().replaceAll('\r\n', '\n') + +export async function verifyAdsContract() { + const baseline = JSON.parse(fs.readFileSync(path.join(refactorDir, 'baselines/ads-release.json'))) + assert.equal(baseline.task, 'PKG-ADS-01') + const releases = [...baseline.olderReleases, baseline.release] + assert.deepEqual(releases.map(release => release.version), ['1.0.0', '1.0.2', '1.0.3', '1.0.4', '1.0.6']) + const archives = new Map() + for (const release of releases) { + assert.equal(release.name, 'artplayer-plugin-ads') + 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, `Changed Ads release member: ${release.version}/${member}`) + assert.deepEqual(JSON.parse(readMember(archive, 'package/package.json')), release.manifest) + for (const entry of [release.manifest.main, release.manifest.types]) + assert(Object.hasOwn(release.files, `package/${entry.replace(/^\.\//, '')}`)) + archives.set(release.version, archive) + } + 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}`], { cwd: root }) + assert.equal(hash(normalizeLF(content)), expected, `Changed historical Ads source: ${file}`) + } + for (const comparison of baseline.comparisons) { + const published = hash(normalizeLF(readMember(archives.get('1.0.6'), `package/${comparison.file}`))) + assert.equal(published, comparison.publishedLF) + assert.equal(baseline.source[`packages/artplayer-plugin-ads/${comparison.file}`], comparison.workspaceLF) + assert.equal(published === comparison.workspaceLF, comparison.equal) + } + const core = baseline.historicalCore + const content = execFileSync('git', ['show', `${core.commit}:${core.file}`], { cwd: root }) + assert.equal(hash(normalizeLF(content)), core.sha256LF) + assert.equal(JSON.parse(content).version, core.version) + console.log('Verified Ads: five published archives, 30 members, historical source and core-version association') + return { baseline, archives } +} + +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) + await verifyAdsContract() diff --git a/refactor/scripts/ads-contract.test.mjs b/refactor/scripts/ads-contract.test.mjs new file mode 100644 index 000000000..614f547ee --- /dev/null +++ b/refactor/scripts/ads-contract.test.mjs @@ -0,0 +1,58 @@ +import assert from 'node:assert/strict' +import { execFileSync } from 'node:child_process' +// eslint-disable-next-line test/no-import-node-test -- Validate frozen package provenance and actual published exports. +import test from 'node:test' +import vm from 'node:vm' +import { verifyAdsContract } from './ads-contract.mjs' +import { readMember } from './releases.mjs' + +test('Ads archives and historical contract remain immutable, including the unpublished workspace version boundary', verifyAdsContract) + +test('actual Ads 1.0.6 exposes a CommonJS default namespace, browser factory and deferred synchronous result', async () => { + const { baseline, archives } = await verifyAdsContract() + const module = { exports: {} } + const browser = {} + vm.runInNewContext(readMember(archives.get('1.0.6'), 'package/dist/artplayer-plugin-ads.js').toString(), { module, exports: module.exports, window: browser }, { timeout: 5000 }) + assert.equal(typeof module.exports, 'object') + assert.deepEqual(Object.keys(module.exports), ['default']) + const factory = module.exports.default + assert.equal(browser.artplayerPluginAds, factory) + assert.equal(factory.version, '1.0.6') + assert.equal(factory.env, 'production') + assert.equal(typeof factory.build, 'string') + const current = { exports: {} } + vm.runInNewContext(execFileSync('git', ['show', `${baseline.sourceCommit}:packages/artplayer-plugin-ads/dist/artplayer-plugin-ads.js`]).toString(), { module: current, exports: current.exports }, { timeout: 5000 }) + assert.equal(typeof current.exports, 'function') + assert.equal(current.exports.default, undefined) + let options + let schema + const subscriptions = [] + const art = { + template: { $player: {} }, + icons: {}, + constructor: { + validator(value, rules) { + options = value + schema = rules + return value + }, + utils: {}, + }, + on(name) { subscriptions.push(name) }, + } + const result = factory({ html: 'ad', muted: true })(art) + assert.deepEqual(Object.keys(result), ['name', 'skip', 'pause', 'play']) + assert.equal(result.name, 'artplayerPluginAds') + for (const method of ['skip', 'pause', 'play']) assert.equal(typeof result[method], 'function') + assert.deepEqual(subscriptions, ['ready']) + assert.equal(options.html, 'ad') + assert.equal(options.video, '') + assert.equal(options.url, '') + assert.equal(options.playDuration, 5) + assert.equal(options.totalDuration, 10) + assert.equal(options.muted, true) + assert.equal(options.i18n.countdown, '%s秒') + assert.equal(schema.totalDuration, 'number') + assert(!Object.hasOwn(schema, 'source')) + assert(!Object.hasOwn(schema, 'type')) +}) diff --git a/refactor/tasks.json b/refactor/tasks.json index 17d1754f4..39ae35d00 100644 --- a/refactor/tasks.json +++ b/refactor/tasks.json @@ -2521,11 +2521,17 @@ "dependsOn": [ "BASE-05" ], - "status": "todo", + "status": "done", "risk": "H", - "deliverable": "核对 html/video/url 与 source/type 声明差异,结果方法和倒计时", + "deliverable": "核对 npm 1.0.0~1.0.6 与未发布工作区 2.1.0 的真实参数、类型、结果、倒计时、分发和旧核心来源", "acceptance": "源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结", - "evidence": [] + "evidence": [ + "baselines/ads-release.json", + "baselines/ads-contract.md", + "baselines/ads-contract-validation.json", + "changes/2026-09-12-PKG-ADS-01-contract.md", + "scripts/ads-contract.test.mjs" + ] }, { "id": "PKG-ADS-02", @@ -2541,7 +2547,7 @@ ], "status": "todo", "risk": "H", - "deliverable": "视频/HTML/图片实际支持路径、跳过阈值、暂停恢复、加载失败", + "deliverable": "固定 npm 1.0.6 与工作区契约;HTML/视频/图片 HTML、跳过阈值、计时链、工厂复用、多实例、过早/重复方法、播放拒绝与销毁", "acceptance": "旧版本行为可重跑,成功/失败/切源/销毁有必要断言", "evidence": [] }, @@ -2559,7 +2565,7 @@ ], "status": "todo", "risk": "H", - "deliverable": "广告状态/计时/视图/主视频恢复分离,不改变广告产品规则", + "deliverable": "配置/广告状态/计时/视图/主视频恢复分离,保留产品规则;资源与版本检查改用经验证的旧核心能力边界", "acceptance": "结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过", "evidence": [] }, @@ -2578,7 +2584,7 @@ ], "status": "todo", "risk": "H", - "deliverable": "旧参数契约和真实返回方法的兼容类型;错误声明单独处理", + "deliverable": "兼容真实 html/video/url/i18n、旧 export= 与工作区 source/type 类型接受面;时长 string/number 漂移与未实现别名单独处置", "acceptance": "严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容", "evidence": [] }, @@ -2595,7 +2601,7 @@ ], "status": "todo", "risk": "H", - "deliverable": "本地广告 fixture、多实例、正常结束/跳过/销毁恢复内容", + "deliverable": "本地广告 fixture、多实例、正常结束/跳过/销毁;核验历史关联核心 4.5.5、发布 5.4.1 与候选核心的实际归档/能力/媒体组合", "acceptance": "最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成", "evidence": [] }, @@ -2612,7 +2618,7 @@ ], "status": "todo", "risk": "H", - "deliverable": "ads.js、README/类型一致性、旧参数保留与变更日志", + "deliverable": "ads.js/README/声明一致;旧 require(pkg).default、原 dist 路径、当前 callable/ESM/legacy 的隔离 tarball 及浏览器验收", "acceptance": "tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录", "evidence": [] },