mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 10:56:15 -08:00
test(refactor): [ENG-COVERAGE-01] index public contracts and exact candidate evidence
This commit is contained in:
1 parent
6abac891ce
commit
f35fb87615
22 files changed
+11007
-7
No files matched your search
+4
-2
@@ -47,7 +47,7 @@
|
||||
"check:plan": "node refactor/scripts/plan.mjs --check",
|
||||
"test:node": "yarn test:unit && node --test test/toolchain.test.js test/build-docs.test.js test/package-check.test.js test/declarations.test.js test/editor-types.test.js test/coverage.test.js test/performance-report.test.js test/media-gate.test.js",
|
||||
"test:baseline": "node --test refactor/scripts/*.test.mjs",
|
||||
"ci:check": "yarn check:toolchain --strict && yarn check:commits --report && yarn check:impact --report && yarn check:plan && yarn lint && yarn check:types && yarn typecheck && yarn test",
|
||||
"ci:check": "yarn check:toolchain --strict && yarn check:commits --report && yarn check:impact --report && yarn test:contracts && yarn check:contracts --report && yarn check:plan && yarn lint && yarn check:types && yarn typecheck && yarn test",
|
||||
"ci:build": "yarn build:types && yarn build all && yarn build:i18n && yarn build:ts && yarn build:docs && yarn test:imports",
|
||||
"test:imports": "node --test test/esm.test.js test/i18n.test.js test/ssr.test.js",
|
||||
"typecheck": "node scripts/typecheck.mjs",
|
||||
@@ -67,7 +67,9 @@
|
||||
"test:ads-native-visibility": "node refactor/scripts/ads-native-visibility.mjs",
|
||||
"test:vast": "node --test test/vast.test.js test/vast-lifecycle.test.js",
|
||||
"check:commits": "node refactor/scripts/commit-audit.mjs",
|
||||
"check:impact": "node refactor/scripts/impact.mjs"
|
||||
"check:impact": "node refactor/scripts/impact.mjs",
|
||||
"check:contracts": "node refactor/scripts/contracts.mjs",
|
||||
"test:contracts": "node refactor/scripts/run-contracts.mjs"
|
||||
},
|
||||
"browserslist": "last 1 Chrome version",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
| [阶段路线](roadmap.md) | 执行顺序、里程碑、范围和阶段出口 |
|
||||
| [执行门槛与分批交付](execution-gates.md) | 最小启动基线、早期试点、历史失败、设备门槛和分包准入 |
|
||||
| [兼容性契约](compatibility.md) | 不可静默改变的接口、行为、类型、样式与包分发约定 |
|
||||
| [契约覆盖与证据索引](contract-coverage.md) | 22包逐类责任、稳定测试ID、精确版本依据和候选/历史报告对应 |
|
||||
| [核心设计](core-design.md) | 核心各模块的目标职责、迁移方式和生命周期设计 |
|
||||
| [核心阶段验收范围](core-acceptance.md) | 核心契约、已处置差异、性能风险与后续生态/发布门槛 |
|
||||
| [包迁移规范](packages.md) | 全生态统一流程、组合矩阵及包特有风险 |
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# AI 协作与文档维护
|
||||
|
||||
契约修改同时维护 [覆盖索引](contract-coverage.md):固定测试ID、精确版本、断言范围
|
||||
和候选报告必须对应;旧报告、skip及未索引用例不能折算成当前整包通过。
|
||||
|
||||
AI 用于源码分析、任务拆分、实现、测试和差异审查。现代化的验收是可验证的类型、行为和构建质量,不以生成代码量为指标。
|
||||
|
||||
## 开始任务
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"task": "ENG-COVERAGE-01",
|
||||
"status": "done",
|
||||
"sourceCommit": "6abac891ceb42bbff2c285befab3fda85490c3e9",
|
||||
"model": {
|
||||
"packages": 22,
|
||||
"contractFamilies": 12,
|
||||
"ownerRows": 264,
|
||||
"workspaceVersionPoints": 22,
|
||||
"publishedVersionPoints": 7,
|
||||
"indexedCases": 10,
|
||||
"rowsAwaitingTestIndexing": 255
|
||||
},
|
||||
"checks": [
|
||||
{
|
||||
"command": "node --test refactor/scripts/contracts.test.mjs",
|
||||
"result": "pass",
|
||||
"tests": 7,
|
||||
"log": {
|
||||
"file": "refactor/.cache/contracts-tests-final-guards.log",
|
||||
"sha256": "3d4c38f9710755ff6f825171d4798b0f53d516e2422c02a688315f10fe0e7d78"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "node --test --test-reporter=D:/github/ArtPlayer/refactor/scripts/contract-reporter.mjs test/chapter.test.js",
|
||||
"result": "Windows absolute ESM specifier failure reproduced; fixture fixed with file URL",
|
||||
"log": {
|
||||
"file": "refactor/.cache/contracts-reporter-path-before.log",
|
||||
"sha256": "5009c96b9108020b163f72bf9ae50d9e7336616977e023f79e1ff3368c3b205f"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "eslint contract index/model/runner/reporter/tests --no-fix",
|
||||
"result": "pass",
|
||||
"log": {
|
||||
"file": "refactor/.cache/contracts-lint-verified.log",
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "node refactor/scripts/run-contracts.mjs (final targeted verification)",
|
||||
"result": "pass",
|
||||
"executedTests": 44,
|
||||
"indexedAssertions": 10,
|
||||
"scope": "Five existing Node files; repeated executions are not new functional coverage",
|
||||
"report": "refactor/baselines/contract-observations/2026-09-12-source-final.json",
|
||||
"log": {
|
||||
"file": "refactor/.cache/contracts-run-verified.log",
|
||||
"sha256": "d34a6bfaa5bef99e48622886612aef299c8a7c475ab69baf721f951019aa14a4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "yarn ci:check",
|
||||
"result": "pass",
|
||||
"tests": {
|
||||
"unit": 832,
|
||||
"engineering": 14,
|
||||
"baseline": 72,
|
||||
"mainTestChainTotal": 918,
|
||||
"additionalRepeatedContractExecutions": 44
|
||||
},
|
||||
"log": {
|
||||
"file": "refactor/.cache/contracts-ci-verified.log",
|
||||
"sha256": "3695a31e9952fc7d24d4221b306e8e61eaf44e86d945a681e2e05f290362085a"
|
||||
}
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"file": "package.json",
|
||||
"sha256LF": "a114d0f1c1532e90984a93cc703dfcb250107f5de56b5a62bf4c30ad4f64c25b"
|
||||
},
|
||||
{
|
||||
"file": "refactor/contract-policy.json",
|
||||
"sha256LF": "ca545a3e721313f0dcb59c549c14589c108e064ff396a72a2c14e4ee993b1a04"
|
||||
},
|
||||
{
|
||||
"file": "refactor/scripts/contracts-model.mjs",
|
||||
"sha256LF": "99b3a774de9d4bbfbf6bc7728b74bf8784883e13ac24f5b9b7e5aff81367ca8a"
|
||||
},
|
||||
{
|
||||
"file": "refactor/scripts/contracts.mjs",
|
||||
"sha256LF": "66dd75e6624574dd2648317d087d67fd17f4a310066fc63b3eb32b90f68fcf36"
|
||||
},
|
||||
{
|
||||
"file": "refactor/scripts/contracts.test.mjs",
|
||||
"sha256LF": "022c46ac41b87b28fabded74dcda2658039b4487dfc1ff4d4d70edfbc08b8da3"
|
||||
},
|
||||
{
|
||||
"file": "refactor/scripts/contract-reporter.mjs",
|
||||
"sha256LF": "9fdd6cd1814e12df18a294558da9a4a561b5b8eee8149b21902c1b99b855b8c9"
|
||||
},
|
||||
{
|
||||
"file": "refactor/scripts/run-contracts.mjs",
|
||||
"sha256LF": "de7a44af890672605d6ce00c142420d146c4ba45b398786957b65edc3c25afd3"
|
||||
}
|
||||
],
|
||||
"limitations": [
|
||||
"Ownership index covers the twelve declared contract families, not exhaustive testing of every public member",
|
||||
"255 rows lack exact test indexing; recorded historical tests are not thereby absent or failed",
|
||||
"Only Node literal assertions normalized so far; browser/type/device/vendor evidence remains in its original reports and package/review tasks",
|
||||
"Exact baseline versions do not establish continuous support ranges",
|
||||
"Local CI only, no remote GitHub run; no npm release acceptance",
|
||||
"Existing generated-doc lint warning and Yarn url.parse deprecation remain outside this engineering change"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
# ENG-COVERAGE-01 契约覆盖与报告对应
|
||||
|
||||
起点6abac891。创建22包×12契约归属、29个版本依据(22工作区/7发布),从真实断言
|
||||
逐条登记10个稳定ID。其他用例待索引,历史JSON按任务列出,不把旧总数当作当前契约
|
||||
通过。维护方式见 [契约覆盖维护](../contract-coverage.md)。
|
||||
|
||||
check:contracts/test:contracts接入ci:check,五文件44项产生10个精确观察,保留候选
|
||||
字节指纹、实际事件、当时定义及来源;输入/定义变化标旧,skip/todo/失败不算通过。
|
||||
没有生产源码或运行时依赖修改。
|
||||
|
||||
七组测试覆盖路径/版本/任务/ID/报告反例,并启动真实Node验证失败、skip/todo。首轮
|
||||
发现Windows绝对路径不能直接作ESM reporter specifier,独立复现后改file URL。生产
|
||||
runner原本用相对specifier,不受影响。失败日志保留,完整结果在完成前归档。
|
||||
|
||||
文档入口明确早期未运行描述是历史目录,后续查progress与包记录,没有批量改成通过。
|
||||
未运行远端Actions或发布。回退本步移除CI两个命令及相应脚本/策略/报告/文档,原有
|
||||
包行为和测试不受影响。逐成员映射、浏览器/类型解析和设备/SDK验收由包任务及REVIEW/
|
||||
REL继续;本任务不批准发布。
|
||||
|
||||
最终七组反例通过,主测试链918项及44项重复观察执行通过,专项lint通过。补齐空清单
|
||||
拒绝和不同ID重复同一断言的拒绝,避免隐式发现或虚增索引。两个阶段实际报告分别
|
||||
归档,旧候选保持历史;完整记录见 [验证证据](../baselines/contracts-validation.json)。
|
||||
@@ -15,6 +15,8 @@
|
||||
| `yarn test:browser` | 隔离服务执行 Chromium/Firefox/WebKit 真实播放;首次先运行 test:browser:install,详见 [入口](../test/browser/README.md) |
|
||||
| `yarn test:imports` | 3 个既有包导入 smoke,构建后执行以使用新产物;不把 console 示例视为完整行为断言 |
|
||||
| `yarn test:baseline` | 固定发布包完整性及本地 HTTP 基线测试;首次可能下载已固定归档到缓存 |
|
||||
| `yarn test:contracts` | 重跑已登记断言的Node文件并采集精确事件/候选指纹,当前44项包含10个索引断言 |
|
||||
| `yarn check:contracts --report` | 校验12类契约/22包归属、版本及报告对应;--write更新静态表,详见 [维护说明](contract-coverage.md) |
|
||||
| `yarn ci:check` | 严格 Node/Yarn/锁检查、计划、只读 lint、类型、Node 和基线测试;允许写忽略缓存,不修改源码 |
|
||||
| `yarn ci:build` | 21 库包、i18n、编辑器声明和文档站构建,以及构建后包导入 smoke;会生成 dist 和 docs 内容 |
|
||||
| `yarn check:impact --report` | 读取实际依赖/验证关系和Git变更,核对workflow必需命令,写CI影响报告;已接入ci:check,见[影响映射](impact-analysis.md) |
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# 契约、版本与测试证据维护
|
||||
|
||||
ENG-COVERAGE-01建立可校验的归属和证据索引,不把索引建成当作全项目兼容通过。
|
||||
[生成的索引](contract-index.md)覆盖compatibility.md的12类契约及实际22包,共264行。
|
||||
每行有责任任务;未整理的测试、精确支持范围、设备和发布包验证保持缺口。站点契约
|
||||
适用性由SITE审查,不给文档站编造播放器构造器。逐成员复盘仍属各包契约及REVIEW-02;
|
||||
宽泛API编号不是每个方法、参数、事件都已测过的证明。
|
||||
|
||||
## 数据与版本
|
||||
|
||||
`contract-policy.json`维护逐包逐类责任、版本依据及稳定测试ID。版本用JSON pointer
|
||||
指向冻结清单,核对包名、版本和发布archive integrity;工作区单独标识。当前22个
|
||||
工作区、7个发布对照点,不由单个版本推断连续支持区间。
|
||||
|
||||
`contract-runs.json`引用归档报告及LF规范化SHA-256。报告保存当时定义、实际事件、
|
||||
输入指纹、命令、HEAD和Node/OS。其他tasks.json历史JSON证据列为recorded-not-normalized,
|
||||
保留原文及任务,不能按通过总数猜具体断言已执行。
|
||||
|
||||
本批逐条核对10个已有断言,涉及核心配置、注册、事件、存储及chapter归一化。五个
|
||||
文件执行44项,仅精确匹配的10项作索引观察,其余34项不自动分配契约。255行尚未
|
||||
录入测试ID,表示索引缺口,不表示这些功能都没有测试;历史事实仍查包记录及changes。
|
||||
|
||||
## 命令与CI
|
||||
|
||||
```sh
|
||||
yarn test:contracts
|
||||
yarn check:contracts --write
|
||||
yarn check:contracts --report
|
||||
```
|
||||
|
||||
执行器使用独立Node进程,固定文件列表且不接受过滤器;清除ARTPLAYER产物覆盖及
|
||||
NODE_OPTIONS,避免把临时指定产物误记为当前源码。执行前后核对包源码/声明/manifest、
|
||||
测试/脚本/夹具、工具锁及版本依据;输入变化拒绝结果。指纹是实际字节快照,换行或
|
||||
相关脚本变化也会保守判旧。
|
||||
|
||||
Reporter记录Node实际pass/fail/summary的文件、名称、skip/todo及整体结果,不按数量
|
||||
猜通过。接口依据[Node24官方文档](https://nodejs.org/docs/latest-v24.x/api/test.html#custom-reporters)。
|
||||
测试ID须唯一对应源码中的Node test字面量调用;参数化、浏览器和类型报告仍保留
|
||||
历史原始记录,后续增加各自解析器及反例,不能用当前适配器假装已经覆盖。
|
||||
|
||||
CI先执行观察,再检查索引。`refactor/.cache/ci/contracts-run.json`保存本轮结果,
|
||||
`contracts.json`保存动态索引,随已有checks artifact收集。原始事件和stderr另存
|
||||
`refactor/.cache/contracts/run-*/`。原有测试不减少;44项重复执行是链路验证,不算
|
||||
新增44种功能覆盖。
|
||||
|
||||
## 状态
|
||||
|
||||
| 状态 | 含义 |
|
||||
| --- | --- |
|
||||
| tests-not-indexed | 尚无精确映射,仍有责任任务 |
|
||||
| planned-not-observed | 映射存在,没有结构化执行报告 |
|
||||
| partial-observations | 有具体结果,不代表整个契约类或包通过 |
|
||||
| current-inputs | 定义及输入指纹与当前一致 |
|
||||
| historical-inputs | 定义一致,源码/测试/工具等输入已变化 |
|
||||
| historical-definition | 定义已变化,保留旧定义实际范围 |
|
||||
| skipped / failed | 原始结果原样保留,不折算成通过 |
|
||||
|
||||
无效路径、重复ID、错误任务、错包/版本/integrity、无实际测试、报告被修改或结果
|
||||
矛盾会失败。旧输入、未索引或缺环境明确列为缺口,不阻止无关迁移;对应发布由REL/
|
||||
REVIEW把关。哈希和结构只能证明对应关系,不能证明断言充分,也不是证据数字签名。
|
||||
|
||||
## 后续AI维护
|
||||
|
||||
1. 修改前检查包的12行及历史报告,定位具体公开成员/参数/事件并核对适用性。
|
||||
2. 登记稳定ID、精确文件/标题、命令、版本依据、断言范围和限制。ID不能改归其他包;
|
||||
旧报告保留旧定义,不能重写历史消除差异。
|
||||
3. 修改后执行test:contracts。归档时复制实际report.json到baselines/contract-observations,
|
||||
将LF哈希加入runs;核对输入、skip/todo、整体失败及语义,不手写passed。
|
||||
4. --write更新静态表,--report检查动态结果,再跑ci:check。done及独立commit遵循
|
||||
commit-audit;不能拿旧CI总数批准新实现。
|
||||
|
||||
支持窗口仍需包契约、旧核心组合和环境证明。VAST的VPN例外不改变类型、生命周期或
|
||||
其他包结论;当前没有据此新增skip。
|
||||
@@ -0,0 +1,318 @@
|
||||
# 公开契约覆盖索引
|
||||
|
||||
> 由 contract-policy.json 生成;`yarn check:contracts --write` 更新。动态候选/报告状态见 `yarn check:contracts --report`。
|
||||
|
||||
覆盖12类兼容契约与22包的验证归属;不是每个公开成员的穷尽测试证明。没有固定测试ID的行仍需逐成员补齐。
|
||||
版本均为精确冻结对照点,不能推导连续支持区间。历史报告保留原状态,只有精确用例和候选指纹对应时才列为观察结果。
|
||||
|
||||
| 包 | 契约 | 责任任务 | 固定测试ID |
|
||||
| --- | --- | --- | --- |
|
||||
| artplayer | API-01 构造与配置 | REVIEW-02 | CT-CORE-OPTIONS-001, CT-CORE-OPTIONS-002 |
|
||||
| artplayer | API-02 属性与方法 | REVIEW-02 | CT-CORE-PLUGINS-002, CT-CORE-STORAGE-001 |
|
||||
| artplayer | API-03 属性描述符 | REVIEW-02 | CT-CORE-PLUGINS-001 |
|
||||
| artplayer | API-04 事件 | REVIEW-02 | CT-CORE-EVENTS-001 |
|
||||
| artplayer | API-05 生命周期 | REVIEW-02 | CT-CORE-PLUGINS-003 |
|
||||
| artplayer | API-06 插件注册 | REVIEW-02 | CT-CORE-PLUGINS-001, CT-CORE-PLUGINS-002, CT-CORE-PLUGINS-003 |
|
||||
| artplayer | API-07 生态集成 | REVIEW-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer | API-08 DOM/CSS | REVIEW-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer | API-09 包分发 | REVIEW-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer | API-11 TypeScript | REVIEW-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer | API-12 持久状态与协议 | REVIEW-02 | CT-CORE-STORAGE-001, CT-CORE-STORAGE-002 |
|
||||
| artplayer-plugin-ads | API-01 构造与配置 | PKG-ADS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ads | API-02 属性与方法 | PKG-ADS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ads | API-03 属性描述符 | PKG-ADS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ads | API-04 事件 | PKG-ADS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ads | API-05 生命周期 | PKG-ADS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ads | API-06 插件注册 | PKG-ADS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ads | API-07 生态集成 | PKG-ADS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ads | API-08 DOM/CSS | PKG-ADS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ads | API-09 包分发 | PKG-ADS-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ads | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ads | API-11 TypeScript | PKG-ADS-04 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ads | API-12 持久状态与协议 | PKG-ADS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ambilight | API-01 构造与配置 | PKG-AMBILIGHT-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ambilight | API-02 属性与方法 | PKG-AMBILIGHT-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ambilight | API-03 属性描述符 | PKG-AMBILIGHT-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ambilight | API-04 事件 | PKG-AMBILIGHT-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ambilight | API-05 生命周期 | PKG-AMBILIGHT-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ambilight | API-06 插件注册 | PKG-AMBILIGHT-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ambilight | API-07 生态集成 | PKG-AMBILIGHT-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ambilight | API-08 DOM/CSS | PKG-AMBILIGHT-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ambilight | API-09 包分发 | PKG-AMBILIGHT-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ambilight | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ambilight | API-11 TypeScript | PKG-AMBILIGHT-04 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-ambilight | API-12 持久状态与协议 | PKG-AMBILIGHT-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-asr | API-01 构造与配置 | PKG-ASR-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-asr | API-02 属性与方法 | PKG-ASR-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-asr | API-03 属性描述符 | PKG-ASR-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-asr | API-04 事件 | PKG-ASR-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-asr | API-05 生命周期 | PKG-ASR-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-asr | API-06 插件注册 | PKG-ASR-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-asr | API-07 生态集成 | PKG-ASR-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-asr | API-08 DOM/CSS | PKG-ASR-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-asr | API-09 包分发 | PKG-ASR-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-asr | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-asr | API-11 TypeScript | PKG-ASR-04 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-asr | API-12 持久状态与协议 | PKG-ASR-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-audio-track | API-01 构造与配置 | PKG-AUDIO-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-audio-track | API-02 属性与方法 | PKG-AUDIO-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-audio-track | API-03 属性描述符 | PKG-AUDIO-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-audio-track | API-04 事件 | PKG-AUDIO-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-audio-track | API-05 生命周期 | PKG-AUDIO-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-audio-track | API-06 插件注册 | PKG-AUDIO-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-audio-track | API-07 生态集成 | PKG-AUDIO-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-audio-track | API-08 DOM/CSS | PKG-AUDIO-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-audio-track | API-09 包分发 | PKG-AUDIO-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-audio-track | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-audio-track | API-11 TypeScript | PKG-AUDIO-04 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-audio-track | API-12 持久状态与协议 | PKG-AUDIO-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-auto-thumbnail | API-01 构造与配置 | PKG-AUTO-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-auto-thumbnail | API-02 属性与方法 | PKG-AUTO-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-auto-thumbnail | API-03 属性描述符 | PKG-AUTO-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-auto-thumbnail | API-04 事件 | PKG-AUTO-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-auto-thumbnail | API-05 生命周期 | PKG-AUTO-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-auto-thumbnail | API-06 插件注册 | PKG-AUTO-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-auto-thumbnail | API-07 生态集成 | PKG-AUTO-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-auto-thumbnail | API-08 DOM/CSS | PKG-AUTO-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-auto-thumbnail | API-09 包分发 | PKG-AUTO-THUMB-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-auto-thumbnail | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-auto-thumbnail | API-11 TypeScript | PKG-AUTO-THUMB-04 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-auto-thumbnail | API-12 持久状态与协议 | PKG-AUTO-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chapter | API-01 构造与配置 | PKG-CHAPTER-02 | CT-CHAPTER-NORMALIZE-001 |
|
||||
| artplayer-plugin-chapter | API-02 属性与方法 | PKG-CHAPTER-02 | CT-CHAPTER-NORMALIZE-001, CT-CHAPTER-NORMALIZE-002 |
|
||||
| artplayer-plugin-chapter | API-03 属性描述符 | PKG-CHAPTER-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chapter | API-04 事件 | PKG-CHAPTER-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chapter | API-05 生命周期 | PKG-CHAPTER-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chapter | API-06 插件注册 | PKG-CHAPTER-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chapter | API-07 生态集成 | PKG-CHAPTER-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chapter | API-08 DOM/CSS | PKG-CHAPTER-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chapter | API-09 包分发 | PKG-CHAPTER-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chapter | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chapter | API-11 TypeScript | PKG-CHAPTER-04 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chapter | API-12 持久状态与协议 | PKG-CHAPTER-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chromecast | API-01 构造与配置 | PKG-CAST-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chromecast | API-02 属性与方法 | PKG-CAST-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chromecast | API-03 属性描述符 | PKG-CAST-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chromecast | API-04 事件 | PKG-CAST-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chromecast | API-05 生命周期 | PKG-CAST-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chromecast | API-06 插件注册 | PKG-CAST-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chromecast | API-07 生态集成 | PKG-CAST-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chromecast | API-08 DOM/CSS | PKG-CAST-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chromecast | API-09 包分发 | PKG-CAST-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chromecast | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chromecast | API-11 TypeScript | PKG-CAST-04 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-chromecast | API-12 持久状态与协议 | PKG-CAST-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku | API-01 构造与配置 | PKG-DANMUKU-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku | API-02 属性与方法 | PKG-DANMUKU-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku | API-03 属性描述符 | PKG-DANMUKU-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku | API-04 事件 | PKG-DANMUKU-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku | API-05 生命周期 | PKG-DANMUKU-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku | API-06 插件注册 | PKG-DANMUKU-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku | API-07 生态集成 | PKG-DANMUKU-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku | API-08 DOM/CSS | PKG-DANMUKU-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku | API-09 包分发 | PKG-DANMUKU-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku | API-11 TypeScript | PKG-DANMUKU-04 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku | API-12 持久状态与协议 | PKG-DANMUKU-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku-mask | API-01 构造与配置 | PKG-MASK-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku-mask | API-02 属性与方法 | PKG-MASK-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku-mask | API-03 属性描述符 | PKG-MASK-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku-mask | API-04 事件 | PKG-MASK-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku-mask | API-05 生命周期 | PKG-MASK-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku-mask | API-06 插件注册 | PKG-MASK-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku-mask | API-07 生态集成 | PKG-MASK-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku-mask | API-08 DOM/CSS | PKG-MASK-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku-mask | API-09 包分发 | PKG-MASK-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku-mask | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku-mask | API-11 TypeScript | PKG-MASK-04 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-danmuku-mask | API-12 持久状态与协议 | PKG-MASK-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-dash-control | API-01 构造与配置 | PKG-DASH-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-dash-control | API-02 属性与方法 | PKG-DASH-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-dash-control | API-03 属性描述符 | PKG-DASH-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-dash-control | API-04 事件 | PKG-DASH-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-dash-control | API-05 生命周期 | PKG-DASH-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-dash-control | API-06 插件注册 | PKG-DASH-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-dash-control | API-07 生态集成 | PKG-DASH-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-dash-control | API-08 DOM/CSS | PKG-DASH-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-dash-control | API-09 包分发 | PKG-DASH-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-dash-control | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-dash-control | API-11 TypeScript | PKG-DASH-04 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-dash-control | API-12 持久状态与协议 | PKG-DASH-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-document-pip | API-01 构造与配置 | PKG-DPIP-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-document-pip | API-02 属性与方法 | PKG-DPIP-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-document-pip | API-03 属性描述符 | PKG-DPIP-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-document-pip | API-04 事件 | PKG-DPIP-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-document-pip | API-05 生命周期 | PKG-DPIP-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-document-pip | API-06 插件注册 | PKG-DPIP-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-document-pip | API-07 生态集成 | PKG-DPIP-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-document-pip | API-08 DOM/CSS | PKG-DPIP-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-document-pip | API-09 包分发 | PKG-DPIP-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-document-pip | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-document-pip | API-11 TypeScript | PKG-DPIP-04 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-document-pip | API-12 持久状态与协议 | PKG-DPIP-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-hls-control | API-01 构造与配置 | PKG-HLS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-hls-control | API-02 属性与方法 | PKG-HLS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-hls-control | API-03 属性描述符 | PKG-HLS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-hls-control | API-04 事件 | PKG-HLS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-hls-control | API-05 生命周期 | PKG-HLS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-hls-control | API-06 插件注册 | PKG-HLS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-hls-control | API-07 生态集成 | PKG-HLS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-hls-control | API-08 DOM/CSS | PKG-HLS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-hls-control | API-09 包分发 | PKG-HLS-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-hls-control | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-hls-control | API-11 TypeScript | PKG-HLS-04 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-hls-control | API-12 持久状态与协议 | PKG-HLS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-jassub | API-01 构造与配置 | PKG-JASSUB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-jassub | API-02 属性与方法 | PKG-JASSUB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-jassub | API-03 属性描述符 | PKG-JASSUB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-jassub | API-04 事件 | PKG-JASSUB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-jassub | API-05 生命周期 | PKG-JASSUB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-jassub | API-06 插件注册 | PKG-JASSUB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-jassub | API-07 生态集成 | PKG-JASSUB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-jassub | API-08 DOM/CSS | PKG-JASSUB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-jassub | API-09 包分发 | PKG-JASSUB-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-jassub | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-jassub | API-11 TypeScript | PKG-JASSUB-04 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-jassub | API-12 持久状态与协议 | PKG-JASSUB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-multiple-subtitles | API-01 构造与配置 | PKG-MULTI-SUB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-multiple-subtitles | API-02 属性与方法 | PKG-MULTI-SUB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-multiple-subtitles | API-03 属性描述符 | PKG-MULTI-SUB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-multiple-subtitles | API-04 事件 | PKG-MULTI-SUB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-multiple-subtitles | API-05 生命周期 | PKG-MULTI-SUB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-multiple-subtitles | API-06 插件注册 | PKG-MULTI-SUB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-multiple-subtitles | API-07 生态集成 | PKG-MULTI-SUB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-multiple-subtitles | API-08 DOM/CSS | PKG-MULTI-SUB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-multiple-subtitles | API-09 包分发 | PKG-MULTI-SUB-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-multiple-subtitles | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-multiple-subtitles | API-11 TypeScript | PKG-MULTI-SUB-04 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-multiple-subtitles | API-12 持久状态与协议 | PKG-MULTI-SUB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vast | API-01 构造与配置 | PKG-VAST-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vast | API-02 属性与方法 | PKG-VAST-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vast | API-03 属性描述符 | PKG-VAST-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vast | API-04 事件 | PKG-VAST-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vast | API-05 生命周期 | PKG-VAST-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vast | API-06 插件注册 | PKG-VAST-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vast | API-07 生态集成 | PKG-VAST-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vast | API-08 DOM/CSS | PKG-VAST-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vast | API-09 包分发 | PKG-VAST-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vast | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vast | API-11 TypeScript | PKG-VAST-04 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vast | API-12 持久状态与协议 | PKG-VAST-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vtt-thumbnail | API-01 构造与配置 | PKG-VTT-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vtt-thumbnail | API-02 属性与方法 | PKG-VTT-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vtt-thumbnail | API-03 属性描述符 | PKG-VTT-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vtt-thumbnail | API-04 事件 | PKG-VTT-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vtt-thumbnail | API-05 生命周期 | PKG-VTT-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vtt-thumbnail | API-06 插件注册 | PKG-VTT-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vtt-thumbnail | API-07 生态集成 | PKG-VTT-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vtt-thumbnail | API-08 DOM/CSS | PKG-VTT-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vtt-thumbnail | API-09 包分发 | PKG-VTT-THUMB-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vtt-thumbnail | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vtt-thumbnail | API-11 TypeScript | PKG-VTT-THUMB-04 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-plugin-vtt-thumbnail | API-12 持久状态与协议 | PKG-VTT-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-canvas | API-01 构造与配置 | PKG-CANVAS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-canvas | API-02 属性与方法 | PKG-CANVAS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-canvas | API-03 属性描述符 | PKG-CANVAS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-canvas | API-04 事件 | PKG-CANVAS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-canvas | API-05 生命周期 | PKG-CANVAS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-canvas | API-06 插件注册 | PKG-CANVAS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-canvas | API-07 生态集成 | PKG-CANVAS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-canvas | API-08 DOM/CSS | PKG-CANVAS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-canvas | API-09 包分发 | PKG-CANVAS-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-canvas | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-canvas | API-11 TypeScript | PKG-CANVAS-04 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-canvas | API-12 持久状态与协议 | PKG-CANVAS-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-mediabunny | API-01 构造与配置 | PKG-MB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-mediabunny | API-02 属性与方法 | PKG-MB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-mediabunny | API-03 属性描述符 | PKG-MB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-mediabunny | API-04 事件 | PKG-MB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-mediabunny | API-05 生命周期 | PKG-MB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-mediabunny | API-06 插件注册 | PKG-MB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-mediabunny | API-07 生态集成 | PKG-MB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-mediabunny | API-08 DOM/CSS | PKG-MB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-mediabunny | API-09 包分发 | PKG-MB-10 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-mediabunny | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-mediabunny | API-11 TypeScript | PKG-MB-08 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-proxy-mediabunny | API-12 持久状态与协议 | PKG-MB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-iframe | API-01 构造与配置 | PKG-IFRAME-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-iframe | API-02 属性与方法 | PKG-IFRAME-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-iframe | API-03 属性描述符 | PKG-IFRAME-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-iframe | API-04 事件 | PKG-IFRAME-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-iframe | API-05 生命周期 | PKG-IFRAME-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-iframe | API-06 插件注册 | PKG-IFRAME-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-iframe | API-07 生态集成 | PKG-IFRAME-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-iframe | API-08 DOM/CSS | PKG-IFRAME-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-iframe | API-09 包分发 | PKG-IFRAME-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-iframe | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-iframe | API-11 TypeScript | PKG-IFRAME-04 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-iframe | API-12 持久状态与协议 | PKG-IFRAME-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-thumbnail | API-01 构造与配置 | PKG-TOOL-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-thumbnail | API-02 属性与方法 | PKG-TOOL-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-thumbnail | API-03 属性描述符 | PKG-TOOL-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-thumbnail | API-04 事件 | PKG-TOOL-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-thumbnail | API-05 生命周期 | PKG-TOOL-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-thumbnail | API-06 插件注册 | PKG-TOOL-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-thumbnail | API-07 生态集成 | PKG-TOOL-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-thumbnail | API-08 DOM/CSS | PKG-TOOL-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-thumbnail | API-09 包分发 | PKG-TOOL-THUMB-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-thumbnail | API-10 浏览器能力 | REL-03 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-thumbnail | API-11 TypeScript | PKG-TOOL-THUMB-04 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-tool-thumbnail | API-12 持久状态与协议 | PKG-TOOL-THUMB-02 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-vitepress | API-01 构造与配置 | SITE-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-vitepress | API-02 属性与方法 | SITE-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-vitepress | API-03 属性描述符 | SITE-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-vitepress | API-04 事件 | SITE-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-vitepress | API-05 生命周期 | SITE-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-vitepress | API-06 插件注册 | SITE-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-vitepress | API-07 生态集成 | SITE-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-vitepress | API-08 DOM/CSS | SITE-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-vitepress | API-09 包分发 | SITE-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-vitepress | API-10 浏览器能力 | SITE-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-vitepress | API-11 TypeScript | SITE-06 | 待索引;不代表没有历史测试 |
|
||||
| artplayer-vitepress | API-12 持久状态与协议 | SITE-06 | 待索引;不代表没有历史测试 |
|
||||
|
||||
## 精确测试及限制
|
||||
|
||||
- CT-CORE-OPTIONS-001: [test/options.test.js](../test/options.test.js) — default values, keys and nested order match the published getter。Current option getter compared with archived published 5.4.0。限制:Source/controlled-host assertions only; no whole-family acceptance, installed tarball, browser, device or continuous support-range claim。命令:`yarn test:contracts`。版本依据:WS-artplayer, PUB-artplayer-5.4.0。
|
||||
- CT-CORE-OPTIONS-002: [test/options.test.js](../test/options.test.js) — invalid JS input keeps the published validation path, first error and thrown kind。Nine invalid input patches compare first error name/message with published 5.4.0。限制:Source/controlled-host assertions only; no whole-family acceptance, installed tarball, browser, device or continuous support-range claim。命令:`yarn test:contracts`。版本依据:WS-artplayer, PUB-artplayer-5.4.0。
|
||||
- CT-CORE-PLUGINS-001: [test/plugins.test.js](../test/plugins.test.js) — plugin registration keeps receiver, arguments, id, descriptors and naming precedence。Controlled registry checks receiver, identity, hidden nonwritable descriptor and naming conflicts。限制:Source/controlled-host assertions only; no whole-family acceptance, installed tarball, browser, device or continuous support-range claim。命令:`yarn test:contracts`。版本依据:WS-artplayer。
|
||||
- CT-CORE-PLUGINS-002: [test/plugins.test.js](../test/plugins.test.js) — async factories preserve pending visibility, completion-time fallback and registry fulfillment。Controlled asynchronous factory visibility and returned registry。限制:Source/controlled-host assertions only; no whole-family acceptance, installed tarball, browser, device or continuous support-range claim。命令:`yarn test:contracts`。版本依据:WS-artplayer。
|
||||
- CT-CORE-PLUGINS-003: [test/plugins.test.js](../test/plugins.test.js) — closed registries ignore late results without inspecting or destroying them and reject new factories。Disposed scope suppresses late result getters and disallows further factory execution。限制:Source/controlled-host assertions only; no whole-family acceptance, installed tarball, browser, device or continuous support-range claim。命令:`yarn test:contracts`。版本依据:WS-artplayer。
|
||||
- CT-CORE-EVENTS-001: [test/public-behavior.test.js](../test/public-behavior.test.js) — EVENT.once-nested-snapshot: candidate consumes a once registration across nested dispatch snapshots。Published duplicate nested once dispatch is reproduced; current consumes once exactly once。限制:Source/controlled-host assertions only; no whole-family acceptance, installed tarball, browser, device or continuous support-range claim。命令:`yarn test:contracts`。版本依据:WS-artplayer, PUB-artplayer-5.4.0。
|
||||
- CT-CORE-STORAGE-001: [test/storage.test.js](../test/storage.test.js) — storage preserves envelope, mutable name/settings fields and synchronous returns。Controlled localStorage verifies exact key/envelope and synchronous returns。限制:Source/controlled-host assertions only; no whole-family acceptance, installed tarball, browser, device or continuous support-range claim。命令:`yarn test:contracts`。版本依据:WS-artplayer。
|
||||
- CT-CORE-STORAGE-002: [test/storage.test.js](../test/storage.test.js) — storage retains truthy key selection and historical JSON payload shapes。Array/scalar/null/malformed JSON cases retain historical fallback semantics。限制:Source/controlled-host assertions only; no whole-family acceptance, installed tarball, browser, device or continuous support-range claim。命令:`yarn test:contracts`。版本依据:WS-artplayer。
|
||||
- CT-CHAPTER-NORMALIZE-001: [test/chapter.test.js](../test/chapter.test.js) — Chapter normalization preserves caller array and objects while filling every gap。Current normalizer retains caller array and chapter identity across repeated normalization。限制:Source/controlled-host assertions only; no whole-family acceptance, installed tarball, browser, device or continuous support-range claim。命令:`yarn test:contracts`。版本依据:WS-artplayer-plugin-chapter。
|
||||
- CT-CHAPTER-NORMALIZE-002: [test/chapter.test.js](../test/chapter.test.js) — Chapter endpoints, adjacency and Infinity normalization retain historical behavior。Current normalizer retains adjacent endpoints and resolves Infinity against duration。限制:Source/controlled-host assertions only; no whole-family acceptance, installed tarball, browser, device or continuous support-range claim。命令:`yarn test:contracts`。版本依据:WS-artplayer-plugin-chapter。
|
||||
|
||||
## 版本依据
|
||||
|
||||
- WS-artplayer: artplayer@5.4.1 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/0`。
|
||||
- WS-artplayer-plugin-ads: artplayer-plugin-ads@2.1.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/1`。
|
||||
- WS-artplayer-plugin-ambilight: artplayer-plugin-ambilight@1.1.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/2`。
|
||||
- WS-artplayer-plugin-asr: artplayer-plugin-asr@2.1.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/3`。
|
||||
- WS-artplayer-plugin-audio-track: artplayer-plugin-audio-track@1.1.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/4`。
|
||||
- WS-artplayer-plugin-auto-thumbnail: artplayer-plugin-auto-thumbnail@1.1.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/5`。
|
||||
- WS-artplayer-plugin-chapter: artplayer-plugin-chapter@1.1.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/6`。
|
||||
- WS-artplayer-plugin-chromecast: artplayer-plugin-chromecast@1.1.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/7`。
|
||||
- WS-artplayer-plugin-danmuku: artplayer-plugin-danmuku@5.3.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/8`。
|
||||
- WS-artplayer-plugin-danmuku-mask: artplayer-plugin-danmuku-mask@1.1.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/9`。
|
||||
- WS-artplayer-plugin-dash-control: artplayer-plugin-dash-control@1.1.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/10`。
|
||||
- WS-artplayer-plugin-document-pip: artplayer-plugin-document-pip@1.1.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/11`。
|
||||
- WS-artplayer-plugin-hls-control: artplayer-plugin-hls-control@1.1.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/12`。
|
||||
- WS-artplayer-plugin-jassub: artplayer-plugin-jassub@1.1.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/13`。
|
||||
- WS-artplayer-plugin-multiple-subtitles: artplayer-plugin-multiple-subtitles@1.2.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/14`。
|
||||
- WS-artplayer-plugin-vast: artplayer-plugin-vast@1.2.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/15`。
|
||||
- WS-artplayer-plugin-vtt-thumbnail: artplayer-plugin-vtt-thumbnail@1.1.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/16`。
|
||||
- WS-artplayer-proxy-canvas: artplayer-proxy-canvas@1.1.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/17`。
|
||||
- WS-artplayer-proxy-mediabunny: artplayer-proxy-mediabunny@1.2.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/18`。
|
||||
- WS-artplayer-tool-iframe: artplayer-tool-iframe@1.1.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/19`。
|
||||
- WS-artplayer-tool-thumbnail: artplayer-tool-thumbnail@4.4.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/20`。
|
||||
- WS-artplayer-vitepress: artplayer-vitepress@1.1.0 (workspace-baseline);[来源](../refactor/package-inventory.json) JSON pointer `/packages/21`。
|
||||
- PUB-artplayer-5.4.0: artplayer@5.4.0 (published-baseline);[来源](../refactor/baselines/releases.json) JSON pointer `/releases/0`。
|
||||
- PUB-artplayer-plugin-chapter-1.1.0: artplayer-plugin-chapter@1.1.0 (published-baseline);[来源](../refactor/baselines/releases.json) JSON pointer `/releases/1`。
|
||||
- PUB-artplayer-plugin-audio-track-1.1.0: artplayer-plugin-audio-track@1.1.0 (published-baseline);[来源](../refactor/baselines/audio-track-release.json) JSON pointer `/release`。
|
||||
- PUB-artplayer-plugin-hls-control-1.1.0: artplayer-plugin-hls-control@1.1.0 (published-baseline);[来源](../refactor/baselines/hls-control-release.json) JSON pointer `/release`。
|
||||
- PUB-artplayer-plugin-dash-control-1.1.0: artplayer-plugin-dash-control@1.1.0 (published-baseline);[来源](../refactor/baselines/dash-control-release.json) JSON pointer `/release`。
|
||||
- PUB-artplayer-plugin-ads-1.0.6: artplayer-plugin-ads@1.0.6 (published-baseline);[来源](../refactor/baselines/ads-release.json) JSON pointer `/release`。
|
||||
- PUB-artplayer-plugin-vast-1.0.0: artplayer-plugin-vast@1.0.0 (published-baseline);[来源](../refactor/baselines/vast-release.json) JSON pointer `/release`。
|
||||
@@ -0,0 +1,957 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"families": [
|
||||
{
|
||||
"id": "API-01",
|
||||
"name": "构造与配置"
|
||||
},
|
||||
{
|
||||
"id": "API-02",
|
||||
"name": "属性与方法"
|
||||
},
|
||||
{
|
||||
"id": "API-03",
|
||||
"name": "属性描述符"
|
||||
},
|
||||
{
|
||||
"id": "API-04",
|
||||
"name": "事件"
|
||||
},
|
||||
{
|
||||
"id": "API-05",
|
||||
"name": "生命周期"
|
||||
},
|
||||
{
|
||||
"id": "API-06",
|
||||
"name": "插件注册"
|
||||
},
|
||||
{
|
||||
"id": "API-07",
|
||||
"name": "生态集成"
|
||||
},
|
||||
{
|
||||
"id": "API-08",
|
||||
"name": "DOM/CSS"
|
||||
},
|
||||
{
|
||||
"id": "API-09",
|
||||
"name": "包分发"
|
||||
},
|
||||
{
|
||||
"id": "API-10",
|
||||
"name": "浏览器能力"
|
||||
},
|
||||
{
|
||||
"id": "API-11",
|
||||
"name": "TypeScript"
|
||||
},
|
||||
{
|
||||
"id": "API-12",
|
||||
"name": "持久状态与协议"
|
||||
}
|
||||
],
|
||||
"packages": [
|
||||
{
|
||||
"name": "artplayer",
|
||||
"owners": {
|
||||
"API-01": "REVIEW-02",
|
||||
"API-02": "REVIEW-02",
|
||||
"API-03": "REVIEW-02",
|
||||
"API-04": "REVIEW-02",
|
||||
"API-05": "REVIEW-02",
|
||||
"API-06": "REVIEW-02",
|
||||
"API-07": "REVIEW-02",
|
||||
"API-08": "REVIEW-02",
|
||||
"API-09": "REVIEW-02",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "REVIEW-02",
|
||||
"API-12": "REVIEW-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer",
|
||||
"PUB-artplayer-5.4.0"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-plugin-ads",
|
||||
"owners": {
|
||||
"API-01": "PKG-ADS-02",
|
||||
"API-02": "PKG-ADS-02",
|
||||
"API-03": "PKG-ADS-02",
|
||||
"API-04": "PKG-ADS-02",
|
||||
"API-05": "PKG-ADS-02",
|
||||
"API-06": "PKG-ADS-02",
|
||||
"API-07": "PKG-ADS-02",
|
||||
"API-08": "PKG-ADS-02",
|
||||
"API-09": "PKG-ADS-06",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "PKG-ADS-04",
|
||||
"API-12": "PKG-ADS-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-plugin-ads",
|
||||
"PUB-artplayer-plugin-ads-1.0.6"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-plugin-ambilight",
|
||||
"owners": {
|
||||
"API-01": "PKG-AMBILIGHT-02",
|
||||
"API-02": "PKG-AMBILIGHT-02",
|
||||
"API-03": "PKG-AMBILIGHT-02",
|
||||
"API-04": "PKG-AMBILIGHT-02",
|
||||
"API-05": "PKG-AMBILIGHT-02",
|
||||
"API-06": "PKG-AMBILIGHT-02",
|
||||
"API-07": "PKG-AMBILIGHT-02",
|
||||
"API-08": "PKG-AMBILIGHT-02",
|
||||
"API-09": "PKG-AMBILIGHT-06",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "PKG-AMBILIGHT-04",
|
||||
"API-12": "PKG-AMBILIGHT-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-plugin-ambilight"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-plugin-asr",
|
||||
"owners": {
|
||||
"API-01": "PKG-ASR-02",
|
||||
"API-02": "PKG-ASR-02",
|
||||
"API-03": "PKG-ASR-02",
|
||||
"API-04": "PKG-ASR-02",
|
||||
"API-05": "PKG-ASR-02",
|
||||
"API-06": "PKG-ASR-02",
|
||||
"API-07": "PKG-ASR-02",
|
||||
"API-08": "PKG-ASR-02",
|
||||
"API-09": "PKG-ASR-06",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "PKG-ASR-04",
|
||||
"API-12": "PKG-ASR-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-plugin-asr"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-plugin-audio-track",
|
||||
"owners": {
|
||||
"API-01": "PKG-AUDIO-02",
|
||||
"API-02": "PKG-AUDIO-02",
|
||||
"API-03": "PKG-AUDIO-02",
|
||||
"API-04": "PKG-AUDIO-02",
|
||||
"API-05": "PKG-AUDIO-02",
|
||||
"API-06": "PKG-AUDIO-02",
|
||||
"API-07": "PKG-AUDIO-02",
|
||||
"API-08": "PKG-AUDIO-02",
|
||||
"API-09": "PKG-AUDIO-06",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "PKG-AUDIO-04",
|
||||
"API-12": "PKG-AUDIO-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-plugin-audio-track",
|
||||
"PUB-artplayer-plugin-audio-track-1.1.0"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-plugin-auto-thumbnail",
|
||||
"owners": {
|
||||
"API-01": "PKG-AUTO-THUMB-02",
|
||||
"API-02": "PKG-AUTO-THUMB-02",
|
||||
"API-03": "PKG-AUTO-THUMB-02",
|
||||
"API-04": "PKG-AUTO-THUMB-02",
|
||||
"API-05": "PKG-AUTO-THUMB-02",
|
||||
"API-06": "PKG-AUTO-THUMB-02",
|
||||
"API-07": "PKG-AUTO-THUMB-02",
|
||||
"API-08": "PKG-AUTO-THUMB-02",
|
||||
"API-09": "PKG-AUTO-THUMB-06",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "PKG-AUTO-THUMB-04",
|
||||
"API-12": "PKG-AUTO-THUMB-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-plugin-auto-thumbnail"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-plugin-chapter",
|
||||
"owners": {
|
||||
"API-01": "PKG-CHAPTER-02",
|
||||
"API-02": "PKG-CHAPTER-02",
|
||||
"API-03": "PKG-CHAPTER-02",
|
||||
"API-04": "PKG-CHAPTER-02",
|
||||
"API-05": "PKG-CHAPTER-02",
|
||||
"API-06": "PKG-CHAPTER-02",
|
||||
"API-07": "PKG-CHAPTER-02",
|
||||
"API-08": "PKG-CHAPTER-02",
|
||||
"API-09": "PKG-CHAPTER-06",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "PKG-CHAPTER-04",
|
||||
"API-12": "PKG-CHAPTER-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-plugin-chapter",
|
||||
"PUB-artplayer-plugin-chapter-1.1.0"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-plugin-chromecast",
|
||||
"owners": {
|
||||
"API-01": "PKG-CAST-02",
|
||||
"API-02": "PKG-CAST-02",
|
||||
"API-03": "PKG-CAST-02",
|
||||
"API-04": "PKG-CAST-02",
|
||||
"API-05": "PKG-CAST-02",
|
||||
"API-06": "PKG-CAST-02",
|
||||
"API-07": "PKG-CAST-02",
|
||||
"API-08": "PKG-CAST-02",
|
||||
"API-09": "PKG-CAST-06",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "PKG-CAST-04",
|
||||
"API-12": "PKG-CAST-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-plugin-chromecast"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-plugin-danmuku",
|
||||
"owners": {
|
||||
"API-01": "PKG-DANMUKU-02",
|
||||
"API-02": "PKG-DANMUKU-02",
|
||||
"API-03": "PKG-DANMUKU-02",
|
||||
"API-04": "PKG-DANMUKU-02",
|
||||
"API-05": "PKG-DANMUKU-02",
|
||||
"API-06": "PKG-DANMUKU-02",
|
||||
"API-07": "PKG-DANMUKU-02",
|
||||
"API-08": "PKG-DANMUKU-02",
|
||||
"API-09": "PKG-DANMUKU-06",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "PKG-DANMUKU-04",
|
||||
"API-12": "PKG-DANMUKU-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-plugin-danmuku"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-plugin-danmuku-mask",
|
||||
"owners": {
|
||||
"API-01": "PKG-MASK-02",
|
||||
"API-02": "PKG-MASK-02",
|
||||
"API-03": "PKG-MASK-02",
|
||||
"API-04": "PKG-MASK-02",
|
||||
"API-05": "PKG-MASK-02",
|
||||
"API-06": "PKG-MASK-02",
|
||||
"API-07": "PKG-MASK-02",
|
||||
"API-08": "PKG-MASK-02",
|
||||
"API-09": "PKG-MASK-06",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "PKG-MASK-04",
|
||||
"API-12": "PKG-MASK-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-plugin-danmuku-mask"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-plugin-dash-control",
|
||||
"owners": {
|
||||
"API-01": "PKG-DASH-02",
|
||||
"API-02": "PKG-DASH-02",
|
||||
"API-03": "PKG-DASH-02",
|
||||
"API-04": "PKG-DASH-02",
|
||||
"API-05": "PKG-DASH-02",
|
||||
"API-06": "PKG-DASH-02",
|
||||
"API-07": "PKG-DASH-02",
|
||||
"API-08": "PKG-DASH-02",
|
||||
"API-09": "PKG-DASH-06",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "PKG-DASH-04",
|
||||
"API-12": "PKG-DASH-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-plugin-dash-control",
|
||||
"PUB-artplayer-plugin-dash-control-1.1.0"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-plugin-document-pip",
|
||||
"owners": {
|
||||
"API-01": "PKG-DPIP-02",
|
||||
"API-02": "PKG-DPIP-02",
|
||||
"API-03": "PKG-DPIP-02",
|
||||
"API-04": "PKG-DPIP-02",
|
||||
"API-05": "PKG-DPIP-02",
|
||||
"API-06": "PKG-DPIP-02",
|
||||
"API-07": "PKG-DPIP-02",
|
||||
"API-08": "PKG-DPIP-02",
|
||||
"API-09": "PKG-DPIP-06",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "PKG-DPIP-04",
|
||||
"API-12": "PKG-DPIP-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-plugin-document-pip"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-plugin-hls-control",
|
||||
"owners": {
|
||||
"API-01": "PKG-HLS-02",
|
||||
"API-02": "PKG-HLS-02",
|
||||
"API-03": "PKG-HLS-02",
|
||||
"API-04": "PKG-HLS-02",
|
||||
"API-05": "PKG-HLS-02",
|
||||
"API-06": "PKG-HLS-02",
|
||||
"API-07": "PKG-HLS-02",
|
||||
"API-08": "PKG-HLS-02",
|
||||
"API-09": "PKG-HLS-06",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "PKG-HLS-04",
|
||||
"API-12": "PKG-HLS-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-plugin-hls-control",
|
||||
"PUB-artplayer-plugin-hls-control-1.1.0"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-plugin-jassub",
|
||||
"owners": {
|
||||
"API-01": "PKG-JASSUB-02",
|
||||
"API-02": "PKG-JASSUB-02",
|
||||
"API-03": "PKG-JASSUB-02",
|
||||
"API-04": "PKG-JASSUB-02",
|
||||
"API-05": "PKG-JASSUB-02",
|
||||
"API-06": "PKG-JASSUB-02",
|
||||
"API-07": "PKG-JASSUB-02",
|
||||
"API-08": "PKG-JASSUB-02",
|
||||
"API-09": "PKG-JASSUB-06",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "PKG-JASSUB-04",
|
||||
"API-12": "PKG-JASSUB-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-plugin-jassub"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-plugin-multiple-subtitles",
|
||||
"owners": {
|
||||
"API-01": "PKG-MULTI-SUB-02",
|
||||
"API-02": "PKG-MULTI-SUB-02",
|
||||
"API-03": "PKG-MULTI-SUB-02",
|
||||
"API-04": "PKG-MULTI-SUB-02",
|
||||
"API-05": "PKG-MULTI-SUB-02",
|
||||
"API-06": "PKG-MULTI-SUB-02",
|
||||
"API-07": "PKG-MULTI-SUB-02",
|
||||
"API-08": "PKG-MULTI-SUB-02",
|
||||
"API-09": "PKG-MULTI-SUB-06",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "PKG-MULTI-SUB-04",
|
||||
"API-12": "PKG-MULTI-SUB-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-plugin-multiple-subtitles"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-plugin-vast",
|
||||
"owners": {
|
||||
"API-01": "PKG-VAST-02",
|
||||
"API-02": "PKG-VAST-02",
|
||||
"API-03": "PKG-VAST-02",
|
||||
"API-04": "PKG-VAST-02",
|
||||
"API-05": "PKG-VAST-02",
|
||||
"API-06": "PKG-VAST-02",
|
||||
"API-07": "PKG-VAST-02",
|
||||
"API-08": "PKG-VAST-02",
|
||||
"API-09": "PKG-VAST-06",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "PKG-VAST-04",
|
||||
"API-12": "PKG-VAST-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-plugin-vast",
|
||||
"PUB-artplayer-plugin-vast-1.0.0"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-plugin-vtt-thumbnail",
|
||||
"owners": {
|
||||
"API-01": "PKG-VTT-THUMB-02",
|
||||
"API-02": "PKG-VTT-THUMB-02",
|
||||
"API-03": "PKG-VTT-THUMB-02",
|
||||
"API-04": "PKG-VTT-THUMB-02",
|
||||
"API-05": "PKG-VTT-THUMB-02",
|
||||
"API-06": "PKG-VTT-THUMB-02",
|
||||
"API-07": "PKG-VTT-THUMB-02",
|
||||
"API-08": "PKG-VTT-THUMB-02",
|
||||
"API-09": "PKG-VTT-THUMB-06",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "PKG-VTT-THUMB-04",
|
||||
"API-12": "PKG-VTT-THUMB-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-plugin-vtt-thumbnail"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-proxy-canvas",
|
||||
"owners": {
|
||||
"API-01": "PKG-CANVAS-02",
|
||||
"API-02": "PKG-CANVAS-02",
|
||||
"API-03": "PKG-CANVAS-02",
|
||||
"API-04": "PKG-CANVAS-02",
|
||||
"API-05": "PKG-CANVAS-02",
|
||||
"API-06": "PKG-CANVAS-02",
|
||||
"API-07": "PKG-CANVAS-02",
|
||||
"API-08": "PKG-CANVAS-02",
|
||||
"API-09": "PKG-CANVAS-06",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "PKG-CANVAS-04",
|
||||
"API-12": "PKG-CANVAS-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-proxy-canvas"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-proxy-mediabunny",
|
||||
"owners": {
|
||||
"API-01": "PKG-MB-02",
|
||||
"API-02": "PKG-MB-02",
|
||||
"API-03": "PKG-MB-02",
|
||||
"API-04": "PKG-MB-02",
|
||||
"API-05": "PKG-MB-02",
|
||||
"API-06": "PKG-MB-02",
|
||||
"API-07": "PKG-MB-02",
|
||||
"API-08": "PKG-MB-02",
|
||||
"API-09": "PKG-MB-10",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "PKG-MB-08",
|
||||
"API-12": "PKG-MB-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-proxy-mediabunny"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-tool-iframe",
|
||||
"owners": {
|
||||
"API-01": "PKG-IFRAME-02",
|
||||
"API-02": "PKG-IFRAME-02",
|
||||
"API-03": "PKG-IFRAME-02",
|
||||
"API-04": "PKG-IFRAME-02",
|
||||
"API-05": "PKG-IFRAME-02",
|
||||
"API-06": "PKG-IFRAME-02",
|
||||
"API-07": "PKG-IFRAME-02",
|
||||
"API-08": "PKG-IFRAME-02",
|
||||
"API-09": "PKG-IFRAME-06",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "PKG-IFRAME-04",
|
||||
"API-12": "PKG-IFRAME-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-tool-iframe"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-tool-thumbnail",
|
||||
"owners": {
|
||||
"API-01": "PKG-TOOL-THUMB-02",
|
||||
"API-02": "PKG-TOOL-THUMB-02",
|
||||
"API-03": "PKG-TOOL-THUMB-02",
|
||||
"API-04": "PKG-TOOL-THUMB-02",
|
||||
"API-05": "PKG-TOOL-THUMB-02",
|
||||
"API-06": "PKG-TOOL-THUMB-02",
|
||||
"API-07": "PKG-TOOL-THUMB-02",
|
||||
"API-08": "PKG-TOOL-THUMB-02",
|
||||
"API-09": "PKG-TOOL-THUMB-06",
|
||||
"API-10": "REL-03",
|
||||
"API-11": "PKG-TOOL-THUMB-04",
|
||||
"API-12": "PKG-TOOL-THUMB-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-tool-thumbnail"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "Each public member, callback, option, event and capability requires package contract review; family ownership alone is not exhaustive coverage"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-vitepress",
|
||||
"owners": {
|
||||
"API-01": "SITE-06",
|
||||
"API-02": "SITE-06",
|
||||
"API-03": "SITE-06",
|
||||
"API-04": "SITE-06",
|
||||
"API-05": "SITE-06",
|
||||
"API-06": "SITE-06",
|
||||
"API-07": "SITE-06",
|
||||
"API-08": "SITE-06",
|
||||
"API-09": "SITE-06",
|
||||
"API-10": "SITE-06",
|
||||
"API-11": "SITE-06",
|
||||
"API-12": "SITE-06"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-vitepress"
|
||||
],
|
||||
"supportWindow": "not-inferred-from-baseline",
|
||||
"scopeReview": "SITE-01/06 must distinguish website behavior from npm library APIs; all families retain review ownership, no invented player constructor"
|
||||
}
|
||||
],
|
||||
"versions": [
|
||||
{
|
||||
"id": "WS-artplayer",
|
||||
"package": "artplayer",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "5.4.1",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/0"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-plugin-ads",
|
||||
"package": "artplayer-plugin-ads",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "2.1.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/1"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-plugin-ambilight",
|
||||
"package": "artplayer-plugin-ambilight",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "1.1.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/2"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-plugin-asr",
|
||||
"package": "artplayer-plugin-asr",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "2.1.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/3"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-plugin-audio-track",
|
||||
"package": "artplayer-plugin-audio-track",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "1.1.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/4"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-plugin-auto-thumbnail",
|
||||
"package": "artplayer-plugin-auto-thumbnail",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "1.1.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/5"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-plugin-chapter",
|
||||
"package": "artplayer-plugin-chapter",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "1.1.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/6"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-plugin-chromecast",
|
||||
"package": "artplayer-plugin-chromecast",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "1.1.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/7"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-plugin-danmuku",
|
||||
"package": "artplayer-plugin-danmuku",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "5.3.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/8"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-plugin-danmuku-mask",
|
||||
"package": "artplayer-plugin-danmuku-mask",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "1.1.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/9"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-plugin-dash-control",
|
||||
"package": "artplayer-plugin-dash-control",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "1.1.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/10"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-plugin-document-pip",
|
||||
"package": "artplayer-plugin-document-pip",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "1.1.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/11"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-plugin-hls-control",
|
||||
"package": "artplayer-plugin-hls-control",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "1.1.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/12"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-plugin-jassub",
|
||||
"package": "artplayer-plugin-jassub",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "1.1.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/13"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-plugin-multiple-subtitles",
|
||||
"package": "artplayer-plugin-multiple-subtitles",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "1.2.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/14"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-plugin-vast",
|
||||
"package": "artplayer-plugin-vast",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "1.2.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/15"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-plugin-vtt-thumbnail",
|
||||
"package": "artplayer-plugin-vtt-thumbnail",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "1.1.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/16"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-proxy-canvas",
|
||||
"package": "artplayer-proxy-canvas",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "1.1.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/17"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-proxy-mediabunny",
|
||||
"package": "artplayer-proxy-mediabunny",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "1.2.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/18"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-tool-iframe",
|
||||
"package": "artplayer-tool-iframe",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "1.1.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/19"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-tool-thumbnail",
|
||||
"package": "artplayer-tool-thumbnail",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "4.4.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/20"
|
||||
},
|
||||
{
|
||||
"id": "WS-artplayer-vitepress",
|
||||
"package": "artplayer-vitepress",
|
||||
"kind": "workspace-baseline",
|
||||
"version": "1.1.0",
|
||||
"file": "refactor/package-inventory.json",
|
||||
"pointer": "/packages/21"
|
||||
},
|
||||
{
|
||||
"id": "PUB-artplayer-5.4.0",
|
||||
"package": "artplayer",
|
||||
"kind": "published-baseline",
|
||||
"version": "5.4.0",
|
||||
"file": "refactor/baselines/releases.json",
|
||||
"pointer": "/releases/0",
|
||||
"archiveSha256": "42b269a66b9658d53f9a92572bb36d656ea9af56254a387b3a69c7ddea43979e"
|
||||
},
|
||||
{
|
||||
"id": "PUB-artplayer-plugin-chapter-1.1.0",
|
||||
"package": "artplayer-plugin-chapter",
|
||||
"kind": "published-baseline",
|
||||
"version": "1.1.0",
|
||||
"file": "refactor/baselines/releases.json",
|
||||
"pointer": "/releases/1",
|
||||
"archiveSha256": "4c3173661854ff96f643a479c0f1c2db7ce9e54681523bd059a7dd9bd77eeada"
|
||||
},
|
||||
{
|
||||
"id": "PUB-artplayer-plugin-audio-track-1.1.0",
|
||||
"package": "artplayer-plugin-audio-track",
|
||||
"kind": "published-baseline",
|
||||
"version": "1.1.0",
|
||||
"file": "refactor/baselines/audio-track-release.json",
|
||||
"pointer": "/release",
|
||||
"archiveSha256": "7e84f6a2f4bfada428a77ef04b749bf3db53e0149fa1afe7bf5749d925e801ff"
|
||||
},
|
||||
{
|
||||
"id": "PUB-artplayer-plugin-hls-control-1.1.0",
|
||||
"package": "artplayer-plugin-hls-control",
|
||||
"kind": "published-baseline",
|
||||
"version": "1.1.0",
|
||||
"file": "refactor/baselines/hls-control-release.json",
|
||||
"pointer": "/release",
|
||||
"archiveSha256": "598c354d78d3cb77965136fa61dfc0f94cdea6906a91a30781c1acef2b0c3e8b"
|
||||
},
|
||||
{
|
||||
"id": "PUB-artplayer-plugin-dash-control-1.1.0",
|
||||
"package": "artplayer-plugin-dash-control",
|
||||
"kind": "published-baseline",
|
||||
"version": "1.1.0",
|
||||
"file": "refactor/baselines/dash-control-release.json",
|
||||
"pointer": "/release",
|
||||
"archiveSha256": "f4047a569d99b6a7de57b3719b7ad40109f2a1d360566a840f50ab32aacefcd9"
|
||||
},
|
||||
{
|
||||
"id": "PUB-artplayer-plugin-ads-1.0.6",
|
||||
"package": "artplayer-plugin-ads",
|
||||
"kind": "published-baseline",
|
||||
"version": "1.0.6",
|
||||
"file": "refactor/baselines/ads-release.json",
|
||||
"pointer": "/release",
|
||||
"archiveSha256": "ff132ff8a7108d8b26421621e4aac1d7edf8c99099a5ab4fd2190bd79b8636cd"
|
||||
},
|
||||
{
|
||||
"id": "PUB-artplayer-plugin-vast-1.0.0",
|
||||
"package": "artplayer-plugin-vast",
|
||||
"kind": "published-baseline",
|
||||
"version": "1.0.0",
|
||||
"file": "refactor/baselines/vast-release.json",
|
||||
"pointer": "/release",
|
||||
"archiveSha256": "35681d60fa3fa09b92f0f30887e39c73cf55e7b14d51a1c38fccbc6c1f253bab"
|
||||
}
|
||||
],
|
||||
"cases": [
|
||||
{
|
||||
"id": "CT-CORE-OPTIONS-001",
|
||||
"package": "artplayer",
|
||||
"contracts": [
|
||||
"API-01"
|
||||
],
|
||||
"runner": "node",
|
||||
"command": "yarn test:contracts",
|
||||
"file": "test/options.test.js",
|
||||
"title": "default values, keys and nested order match the published getter",
|
||||
"versionIds": [
|
||||
"WS-artplayer",
|
||||
"PUB-artplayer-5.4.0"
|
||||
],
|
||||
"scope": "Current option getter compared with archived published 5.4.0",
|
||||
"limitations": "Source/controlled-host assertions only; no whole-family acceptance, installed tarball, browser, device or continuous support-range claim"
|
||||
},
|
||||
{
|
||||
"id": "CT-CORE-OPTIONS-002",
|
||||
"package": "artplayer",
|
||||
"contracts": [
|
||||
"API-01"
|
||||
],
|
||||
"runner": "node",
|
||||
"command": "yarn test:contracts",
|
||||
"file": "test/options.test.js",
|
||||
"title": "invalid JS input keeps the published validation path, first error and thrown kind",
|
||||
"versionIds": [
|
||||
"WS-artplayer",
|
||||
"PUB-artplayer-5.4.0"
|
||||
],
|
||||
"scope": "Nine invalid input patches compare first error name/message with published 5.4.0",
|
||||
"limitations": "Source/controlled-host assertions only; no whole-family acceptance, installed tarball, browser, device or continuous support-range claim"
|
||||
},
|
||||
{
|
||||
"id": "CT-CORE-PLUGINS-001",
|
||||
"package": "artplayer",
|
||||
"contracts": [
|
||||
"API-03",
|
||||
"API-06"
|
||||
],
|
||||
"runner": "node",
|
||||
"command": "yarn test:contracts",
|
||||
"file": "test/plugins.test.js",
|
||||
"title": "plugin registration keeps receiver, arguments, id, descriptors and naming precedence",
|
||||
"versionIds": [
|
||||
"WS-artplayer"
|
||||
],
|
||||
"scope": "Controlled registry checks receiver, identity, hidden nonwritable descriptor and naming conflicts",
|
||||
"limitations": "Source/controlled-host assertions only; no whole-family acceptance, installed tarball, browser, device or continuous support-range claim"
|
||||
},
|
||||
{
|
||||
"id": "CT-CORE-PLUGINS-002",
|
||||
"package": "artplayer",
|
||||
"contracts": [
|
||||
"API-02",
|
||||
"API-06"
|
||||
],
|
||||
"runner": "node",
|
||||
"command": "yarn test:contracts",
|
||||
"file": "test/plugins.test.js",
|
||||
"title": "async factories preserve pending visibility, completion-time fallback and registry fulfillment",
|
||||
"versionIds": [
|
||||
"WS-artplayer"
|
||||
],
|
||||
"scope": "Controlled asynchronous factory visibility and returned registry",
|
||||
"limitations": "Source/controlled-host assertions only; no whole-family acceptance, installed tarball, browser, device or continuous support-range claim"
|
||||
},
|
||||
{
|
||||
"id": "CT-CORE-PLUGINS-003",
|
||||
"package": "artplayer",
|
||||
"contracts": [
|
||||
"API-05",
|
||||
"API-06"
|
||||
],
|
||||
"runner": "node",
|
||||
"command": "yarn test:contracts",
|
||||
"file": "test/plugins.test.js",
|
||||
"title": "closed registries ignore late results without inspecting or destroying them and reject new factories",
|
||||
"versionIds": [
|
||||
"WS-artplayer"
|
||||
],
|
||||
"scope": "Disposed scope suppresses late result getters and disallows further factory execution",
|
||||
"limitations": "Source/controlled-host assertions only; no whole-family acceptance, installed tarball, browser, device or continuous support-range claim"
|
||||
},
|
||||
{
|
||||
"id": "CT-CORE-EVENTS-001",
|
||||
"package": "artplayer",
|
||||
"contracts": [
|
||||
"API-04"
|
||||
],
|
||||
"runner": "node",
|
||||
"command": "yarn test:contracts",
|
||||
"file": "test/public-behavior.test.js",
|
||||
"title": "EVENT.once-nested-snapshot: candidate consumes a once registration across nested dispatch snapshots",
|
||||
"versionIds": [
|
||||
"WS-artplayer",
|
||||
"PUB-artplayer-5.4.0"
|
||||
],
|
||||
"scope": "Published duplicate nested once dispatch is reproduced; current consumes once exactly once",
|
||||
"limitations": "Source/controlled-host assertions only; no whole-family acceptance, installed tarball, browser, device or continuous support-range claim"
|
||||
},
|
||||
{
|
||||
"id": "CT-CORE-STORAGE-001",
|
||||
"package": "artplayer",
|
||||
"contracts": [
|
||||
"API-02",
|
||||
"API-12"
|
||||
],
|
||||
"runner": "node",
|
||||
"command": "yarn test:contracts",
|
||||
"file": "test/storage.test.js",
|
||||
"title": "storage preserves envelope, mutable name/settings fields and synchronous returns",
|
||||
"versionIds": [
|
||||
"WS-artplayer"
|
||||
],
|
||||
"scope": "Controlled localStorage verifies exact key/envelope and synchronous returns",
|
||||
"limitations": "Source/controlled-host assertions only; no whole-family acceptance, installed tarball, browser, device or continuous support-range claim"
|
||||
},
|
||||
{
|
||||
"id": "CT-CORE-STORAGE-002",
|
||||
"package": "artplayer",
|
||||
"contracts": [
|
||||
"API-12"
|
||||
],
|
||||
"runner": "node",
|
||||
"command": "yarn test:contracts",
|
||||
"file": "test/storage.test.js",
|
||||
"title": "storage retains truthy key selection and historical JSON payload shapes",
|
||||
"versionIds": [
|
||||
"WS-artplayer"
|
||||
],
|
||||
"scope": "Array/scalar/null/malformed JSON cases retain historical fallback semantics",
|
||||
"limitations": "Source/controlled-host assertions only; no whole-family acceptance, installed tarball, browser, device or continuous support-range claim"
|
||||
},
|
||||
{
|
||||
"id": "CT-CHAPTER-NORMALIZE-001",
|
||||
"package": "artplayer-plugin-chapter",
|
||||
"contracts": [
|
||||
"API-01",
|
||||
"API-02"
|
||||
],
|
||||
"runner": "node",
|
||||
"command": "yarn test:contracts",
|
||||
"file": "test/chapter.test.js",
|
||||
"title": "Chapter normalization preserves caller array and objects while filling every gap",
|
||||
"versionIds": [
|
||||
"WS-artplayer-plugin-chapter"
|
||||
],
|
||||
"scope": "Current normalizer retains caller array and chapter identity across repeated normalization",
|
||||
"limitations": "Source/controlled-host assertions only; no whole-family acceptance, installed tarball, browser, device or continuous support-range claim"
|
||||
},
|
||||
{
|
||||
"id": "CT-CHAPTER-NORMALIZE-002",
|
||||
"package": "artplayer-plugin-chapter",
|
||||
"contracts": [
|
||||
"API-02"
|
||||
],
|
||||
"runner": "node",
|
||||
"command": "yarn test:contracts",
|
||||
"file": "test/chapter.test.js",
|
||||
"title": "Chapter endpoints, adjacency and Infinity normalization retain historical behavior",
|
||||
"versionIds": [
|
||||
"WS-artplayer-plugin-chapter"
|
||||
],
|
||||
"scope": "Current normalizer retains adjacent endpoints and resolves Infinity against duration",
|
||||
"limitations": "Source/controlled-host assertions only; no whole-family acceptance, installed tarball, browser, device or continuous support-range claim"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"runs": [
|
||||
{
|
||||
"file": "refactor/baselines/contract-observations/2026-09-12-source.json",
|
||||
"sha256LF": "53e8b6f0483b9fcd555790faf454d17b652f15c30e82997d82fda69012061096"
|
||||
},
|
||||
{
|
||||
"file": "refactor/baselines/contract-observations/2026-09-12-source-final.json",
|
||||
"sha256LF": "9700e66ba9b33b811d2178c8c6c0c2b58c6cc7d802f33e929d6aa447204b6c42"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
# 消费者与真实环境矩阵
|
||||
|
||||
时间范围说明:BASE-08初始状态及分段补充保留采集时事实,不是当前全局进度快照。
|
||||
早期“未运行”不能覆盖后续证据;当前查progress和包validation。逐包契约、版本、
|
||||
测试及历史报告入口见 [契约覆盖索引](contract-coverage.md)。
|
||||
|
||||
BASE-08;2026-09-10;来源提交 ed293231。矩阵建立完成,矩阵内未运行的检查仍待完成。版本与路径以 [发布冻结](baselines/releases.json)、[包初始快照](package-inventory.json)、[分发观察](baselines/distribution.json)、[demo 清单](baselines/demo-inventory.json) 和 [第三方清单](third-party.json) 为依据。
|
||||
|
||||
## 版本和分发维度
|
||||
|
||||
+3
-2
@@ -4,7 +4,7 @@
|
||||
|
||||
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 220 项,范围 22 个包及工作区/示例。
|
||||
|
||||
状态:todo 132 / doing 6 / blocked 0 / done 82 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
状态:todo 131 / doing 6 / blocked 0 / done 83 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
|
||||
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
| ENG-08 | workspace<br>增加覆盖率、资源与性能报告 | ENG-03, ENG-05, BASE-06 | 覆盖率基线、资源清理断言、性能报告与阈值 | 关键生命周期分支有门槛,报告不靠无意义断言堆数量;将 BASE-06 的同环境多组配对、计时/压缩审查阈值与资源异常分开接入候选,不把历史现象冻结成正常要求 | M | done |
|
||||
| ENG-AUDIT-01 | workspace<br>核对每任务完成提交的真实Git历史 | ENG-07, DOC-04 | 独立完成提交、初始DOC例外、父分支合并和证据共提交审计;CI完整历史与报告 | 真实仓库及隔离Git反例通过,done任务不允许缺独立commit,固定DOC-01~04例外可追溯;提交后验证自身 | M | done |
|
||||
| ENG-IMPACT-01 | workspace<br>建立全包依赖与共享变更影响映射 | ENG-07, DOC-04 | 核心/构建/类型/锁文件到受影响包和必需生态检查的映射及CI接入 | 核心变化触发必需生态检查;新增包、未知共享文件或失配依赖不能静默漏检;解释受影响路径和测试命令 | M | done |
|
||||
| ENG-COVERAGE-01 | workspace<br>建立公开契约与版本测试证据覆盖索引 | BASE-08, CORE-22, ENG-07 | 契约-包-支持版本-固定测试ID-命令-候选/报告-责任任务索引和文档检查 | 计划、已执行和缺证据状态分开,所有公开契约都有验证归属;无效路径、版本依据或报告对应不明确会被识别 | M | todo |
|
||||
| ENG-COVERAGE-01 | workspace<br>建立公开契约与版本测试证据覆盖索引 | BASE-08, CORE-22, ENG-07 | 契约-包-支持版本-固定测试ID-命令-候选/报告-责任任务索引和文档检查 | 计划、已执行和缺证据状态分开,所有公开契约都有验证归属;无效路径、版本依据或报告对应不明确会被识别 | M | done |
|
||||
| ENG-09 | workspace<br>建立全包依赖影响和文档检查 | ENG-07, DOC-04, ENG-AUDIT-01, ENG-IMPACT-01, ENG-COVERAGE-01 | 共享核心/构建影响映射、文档及每任务完成提交的 Git 审计接入;契约-支持版本-测试 ID-命令-候选/报告-任务的覆盖索引 | 核心变化触发必需生态检查;原有 DOC-01 至 04 基线例外明确,后续 done 任务不能缺失独立 commit;计划/已执行/缺证据分开,识别缺少验证归属的公开契约 | M | todo |
|
||||
| ENG-10 | workspace<br>建立历史失败分级和测试可靠性规则 | ENG-03, ENG-04, ENG-05, ENG-07, BASE-07 | 历史失败 ID/环境/旧版复现/负责修复任务、逐模块门槛、受控等待与 trace/retry 规则 | 不靠全局忽略或无理由 skip 隐藏问题,新增回归阻止交付,设备缺口和偶发失败单独可见;以 risks.json 为统一差异索引;关闭必须有 resolutionEvidence/rationale,已复现、源码事实、未验证分开,登记不等于豁免 | M | done |
|
||||
| ENG-11 | workspace<br>增加不改变产物的模块构建分析 | ENG-03, ENG-08 | 正式构建的可选模块归因报告及源码/产物指纹 | 相同构建开关前后三格式字节一致,报告不进入分发包;区分 Rollup 渲染字节与压缩体积,并保留真实工作区/发布来源 | L | done |
|
||||
@@ -442,6 +442,7 @@
|
||||
- ENG-08: [记录](coverage-performance.md) [记录](baselines/quality-validation.json) [记录](changes/2026-09-11-ENG-08-quality-reports.md)
|
||||
- ENG-AUDIT-01: [记录](commit-audit.md) [记录](changes/2026-09-12-ENG-AUDIT-01-commits.md) [记录](baselines/commit-audit-validation.json)
|
||||
- ENG-IMPACT-01: [记录](impact-analysis.md) [记录](changes/2026-09-12-ENG-IMPACT-01-mapping.md) [记录](baselines/impact-validation.json)
|
||||
- ENG-COVERAGE-01: [记录](contract-coverage.md) [记录](contract-index.md) [记录](changes/2026-09-12-ENG-COVERAGE-01-index.md) [记录](baselines/contracts-validation.json)
|
||||
- ENG-10: [记录](changes/2026-09-10-ENG-10-test-reliability.md) [记录](test-reliability.md)
|
||||
- ENG-11: [记录](build-analysis.md) [记录](baselines/bundle-attribution.json) [记录](changes/2026-09-11-ENG-11-build-analysis.md)
|
||||
- PILOT-01: [记录](changes/2026-09-10-PILOT-01-chapter.md) [记录](baselines/pilot-validation.json)
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
# 进度与证据
|
||||
|
||||
## 最新完成:ENG-COVERAGE-01 契约归属及精确证据索引
|
||||
|
||||
22包×12类契约共264行已分配验证任务,29个冻结版本对照点可校验。逐条核对10个已有
|
||||
断言并接入Node结构化执行;44项重跑通过,不当成新增功能覆盖。旧报告、旧输入、
|
||||
旧定义、skip/失败和未索引状态分别记录;255行仍待精确测试索引,不表示没有历史测试。
|
||||
7组反例及完整CI主测试链918项通过;另有44项报告链路重复执行。修复Windows reporter
|
||||
specifier,补上空清单与重复断言防护,完整失败/通过证据归档。当前220项:83 done、
|
||||
6 doing、131 todo。父ENG-09仍待整合核验;VAST初始化选择待确认,其他包实施继续。
|
||||
本步独立本地提交,随后审计自身;未推送/发布。见 [实施记录](changes/2026-09-12-ENG-COVERAGE-01-index.md)
|
||||
和 [验证记录](baselines/contracts-validation.json)。
|
||||
|
||||
## 最新完成:ENG-IMPACT-01 全包影响与必需检查映射
|
||||
|
||||
实际22包依赖/验证关系、共享构建/类型/锁文件及示例映射已接入ci:check;Git变更包含
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export default async function* contractReporter(events) {
|
||||
for await (const event of events) {
|
||||
if (['test:pass', 'test:fail', 'test:summary'].includes(event.type)) {
|
||||
const { name, file, nesting, skip, todo, counts, success, details } = event.data
|
||||
yield `${JSON.stringify({ type: event.type, name, file, nesting, skip, todo, counts, success, kind: details?.type, error: details?.error?.message })}\n`
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import { createHash } from 'node:crypto'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import ts from 'typescript'
|
||||
import { repositoryPath } from './impact-model.mjs'
|
||||
|
||||
export const sha256 = value => createHash('sha256').update(value).digest('hex')
|
||||
export const readJson = (directory, file) => JSON.parse(fs.readFileSync(localFile(directory, file), 'utf8'))
|
||||
|
||||
export function localFile(directory, file) {
|
||||
assert.equal(repositoryPath(file), file, `Noncanonical contract path: ${file}`)
|
||||
const absolute = path.join(directory, file)
|
||||
const relative = path.relative(fs.realpathSync(directory), fs.realpathSync(absolute))
|
||||
assert(relative && !relative.startsWith('..') && !path.isAbsolute(relative), `Redirected contract path: ${file}`)
|
||||
assert(fs.statSync(absolute).isFile(), `Contract evidence is not a file: ${file}`)
|
||||
return absolute
|
||||
}
|
||||
|
||||
export function pointer(document, value) {
|
||||
assert(/^\//.test(value), 'Expected a JSON pointer')
|
||||
return value.slice(1).split('/').reduce((item, part) => {
|
||||
const key = part.replaceAll('~1', '/').replaceAll('~0', '~')
|
||||
assert(item && Object.hasOwn(item, key), `Missing version evidence pointer: ${value}`)
|
||||
return item[key]
|
||||
}, document)
|
||||
}
|
||||
|
||||
export function candidateInputs(directory, model) {
|
||||
const files = new Set(['package.json', 'yarn.lock', 'refactor/contract-policy.json', '.node-version'])
|
||||
const walk = (folder) => {
|
||||
if (!fs.existsSync(path.join(directory, folder)))
|
||||
return
|
||||
for (const entry of fs.readdirSync(path.join(directory, folder), { withFileTypes: true })) {
|
||||
const file = `${folder}/${entry.name}`
|
||||
assert(!entry.isSymbolicLink(), `Review redirected contract input: ${file}`)
|
||||
if (entry.isDirectory())
|
||||
walk(file)
|
||||
else files.add(file)
|
||||
}
|
||||
}
|
||||
for (const pkg of model.policy.packages) {
|
||||
files.add(`packages/${pkg.name}/package.json`)
|
||||
for (const folder of ['src', 'types', 'public']) walk(`packages/${pkg.name}/${folder}`)
|
||||
}
|
||||
for (const folder of ['scripts', 'test', 'types', 'refactor/scripts', 'refactor/fixtures']) walk(folder)
|
||||
for (const version of model.policy.versions) files.add(version.file)
|
||||
const inputs = [...files].sort().map(file => ({ file, sha256: sha256(fs.readFileSync(localFile(directory, file))) }))
|
||||
return { digest: sha256(JSON.stringify(inputs)), inputs }
|
||||
}
|
||||
|
||||
function validateTest(directory, item) {
|
||||
assert(/^CT-[A-Z0-9-]+$/.test(item.id), 'Invalid stable contract test ID')
|
||||
assert(item.command === 'yarn test:contracts' && item.runner === 'node', 'Unsupported contract runner/command')
|
||||
const source = ts.createSourceFile(item.file, fs.readFileSync(localFile(directory, item.file), 'utf8'), ts.ScriptTarget.Latest, true)
|
||||
assert.equal(source.parseDiagnostics.length, 0, `Malformed contract test: ${item.file}`)
|
||||
const imported = source.statements.some(statement => ts.isImportDeclaration(statement) && statement.moduleSpecifier.text === 'node:test'
|
||||
&& (statement.importClause?.name?.text === 'test' || statement.importClause?.namedBindings?.elements?.some(element => element.name.text === 'test' && (!element.propertyName || element.propertyName.text === 'test'))))
|
||||
assert(imported, `Contract case requires the Node test binding: ${item.id}`)
|
||||
const matches = []
|
||||
function visit(node) {
|
||||
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === 'test' && node.arguments[0] && ts.isStringLiteralLike(node.arguments[0]) && node.arguments[0].text === item.title)
|
||||
matches.push(node)
|
||||
ts.forEachChild(node, visit)
|
||||
}
|
||||
visit(source)
|
||||
assert.equal(matches.length, 1, `Contract test title must identify exactly one literal test: ${item.id}`)
|
||||
assert(item.scope?.length && item.limitations?.length, `Missing assertion scope/limits: ${item.id}`)
|
||||
}
|
||||
|
||||
export function readContractModel(directory) {
|
||||
const policy = readJson(directory, 'refactor/contract-policy.json')
|
||||
assert.equal(policy.schemaVersion, 1)
|
||||
const scripts = readJson(directory, 'package.json').scripts
|
||||
assert.equal(scripts?.['test:contracts'], 'node refactor/scripts/run-contracts.mjs', 'Contract runner command changed; review its evidence adapter')
|
||||
assert.equal(scripts?.['check:contracts'], 'node refactor/scripts/contracts.mjs', 'Contract index command changed')
|
||||
const stages = scripts['ci:check']?.split(' && ') || []
|
||||
assert(stages.includes('yarn test:contracts') && stages.indexOf('yarn check:contracts --report') > stages.indexOf('yarn test:contracts'), 'CI must run contract observations before checking their report')
|
||||
const tasks = readJson(directory, 'refactor/tasks.json').tasks
|
||||
const inventory = readJson(directory, 'refactor/package-inventory.json').packages
|
||||
const families = [...fs.readFileSync(localFile(directory, 'refactor/compatibility.md'), 'utf8').matchAll(/^\| (API-\d{2}) \| ([^|]+) \|/gm)].map(match => ({ id: match[1], name: match[2].trim() }))
|
||||
assert(families.length > 0)
|
||||
assert.deepEqual(policy.families, families, 'Public contract families changed; update their coverage ownership')
|
||||
const names = inventory.map(pkg => pkg.name).sort()
|
||||
assert.deepEqual(policy.packages.map(pkg => pkg.name).sort(), names, 'Contract package ownership is incomplete or duplicated')
|
||||
const actual = fs.readdirSync(path.join(directory, 'packages')).filter(name => fs.existsSync(path.join(directory, 'packages', name, 'package.json'))).sort()
|
||||
assert.deepEqual(actual, names, 'New package must be registered in inventory and contracts')
|
||||
const unique = (items, label) => assert.equal(new Set(items.map(item => item.id)).size, items.length, `Duplicate ${label} ID`)
|
||||
unique(policy.versions, 'version')
|
||||
unique(policy.cases, 'case')
|
||||
assert(policy.cases.length > 0, 'Contract runner requires explicit cases; empty selection must not discover unrelated tests')
|
||||
assert.equal(new Set(policy.cases.map(item => JSON.stringify([item.file, item.title]))).size, policy.cases.length, 'Duplicate file/title under different contract IDs')
|
||||
for (const version of policy.versions) {
|
||||
assert(names.includes(version.package), 'Version references unknown package')
|
||||
const origin = pointer(readJson(directory, version.file), version.pointer)
|
||||
assert.equal(origin.name, version.package, `Wrong package in version evidence: ${version.id}`)
|
||||
assert.equal(origin.version, version.version, `Wrong version evidence: ${version.id}`)
|
||||
if (version.kind === 'published-baseline') {
|
||||
assert(/^[a-f\d]{64}$/.test(origin.sha256) && origin.integrity?.startsWith('sha512-'), `Published version needs archived integrity: ${version.id}`)
|
||||
assert.equal(version.archiveSha256, origin.sha256, `Published archive identity changed: ${version.id}`)
|
||||
}
|
||||
else {
|
||||
assert.equal(version.kind, 'workspace-baseline')
|
||||
assert.equal(version.file, 'refactor/package-inventory.json', 'Workspace version must use the frozen inventory')
|
||||
}
|
||||
}
|
||||
for (const pkg of policy.packages) {
|
||||
assert.deepEqual(Object.keys(pkg.owners).sort(), families.map(family => family.id).sort(), `Missing contract owner: ${pkg.name}`)
|
||||
for (const owner of Object.values(pkg.owners)) {
|
||||
const task = tasks.find(task => task.id === owner)
|
||||
assert(task && (task.scope.includes(pkg.name) || task.scope.includes('workspace')), `Invalid contract responsibility: ${pkg.name}/${owner}`)
|
||||
}
|
||||
assert(pkg.versionIds.length && pkg.versionIds.every(id => policy.versions.some(version => version.id === id && version.package === pkg.name)), `Missing package version basis: ${pkg.name}`)
|
||||
assert.equal(pkg.supportWindow, 'not-inferred-from-baseline', 'A baseline must not become an invented supported version range')
|
||||
assert(pkg.scopeReview?.length, 'Contract applicability must remain explicit')
|
||||
}
|
||||
for (const item of policy.cases) {
|
||||
const pkg = policy.packages.find(pkg => pkg.name === item.package)
|
||||
assert(pkg && item.contracts.length && item.contracts.every(id => families.some(family => family.id === id)), 'Unknown case package/contract')
|
||||
assert(item.versionIds.length && item.versionIds.every(id => policy.versions.some(version => version.id === id && version.package === item.package)), 'Unknown test version basis')
|
||||
validateTest(directory, item)
|
||||
}
|
||||
const historical = tasks.flatMap(task => task.evidence.filter(file => /\.json$/.test(file)).map(file => ({ task: task.id, scope: task.scope, file: `refactor/${file}` })))
|
||||
for (const item of historical) localFile(directory, item.file)
|
||||
return { policy, families, tasks, historical }
|
||||
}
|
||||
|
||||
export function evaluateRun(model, report, currentDigest) {
|
||||
assert.equal(report.schemaVersion, 1)
|
||||
assert.equal(report.command, 'yarn test:contracts')
|
||||
assert(/^[a-f\d]{40}$/.test(report.head) && report.environment?.node && report.environment?.platform, 'Run lacks candidate/environment provenance')
|
||||
assert.equal(report.candidate.digest, sha256(JSON.stringify(report.candidate.inputs)), 'Candidate input fingerprint mismatch')
|
||||
assert(report.candidate.inputs.length && report.candidate.inputs.every(input => /^[a-f\d]{64}$/.test(input.sha256)), 'Invalid candidate inputs')
|
||||
for (const input of report.candidate.inputs) repositoryPath(input.file)
|
||||
assert.equal(new Set(report.cases.map(item => item.id)).size, report.cases.length, 'Duplicate run case identity')
|
||||
assert(report.events.length && ['passed', 'failed'].includes(report.outcome), 'Run is missing test events/outcome')
|
||||
const summaries = report.events.filter(event => event.type === 'test:summary' && !event.file)
|
||||
assert.equal(summaries.length, 1, 'Run requires one process summary')
|
||||
assert.equal(report.outcome === 'passed', summaries[0].success === true, 'Run outcome contradicts process summary')
|
||||
if (report.outcome === 'passed')
|
||||
assert(!report.events.some(event => event.type === 'test:fail' && !event.todo), 'Passed run contains a failed test')
|
||||
return report.cases.map((item) => {
|
||||
const defined = model.policy.cases.find(candidate => candidate.id === item.id)
|
||||
assert(defined, `Unknown reported test ID: ${item.id}`)
|
||||
assert.equal(item.definition?.id, item.id, `Wrong recorded definition: ${item.id}`)
|
||||
assert.equal(item.definitionSha256, sha256(JSON.stringify(item.definition)), `Reported test definition fingerprint changed: ${item.id}`)
|
||||
assert.equal(item.definition.package, defined.package, `Stable test ID was reassigned to another package: ${item.id}`)
|
||||
const matches = report.events.filter(event => ['test:pass', 'test:fail'].includes(event.type) && event.file === item.definition.file && event.name === item.definition.title)
|
||||
assert.equal(matches.length, 1, `Missing or ambiguous executed test: ${item.id}`)
|
||||
const event = matches[0]
|
||||
const status = event.skip || event.todo ? 'skipped' : event.type === 'test:fail' ? 'failed' : 'passed'
|
||||
assert.equal(item.status, status, `Reported test outcome mismatch: ${item.id}`)
|
||||
return { id: item.id, package: item.definition.package, contracts: item.definition.contracts, versionIds: item.definition.versionIds, status, freshness: item.definitionSha256 !== sha256(JSON.stringify(defined)) ? 'historical-definition' : report.candidate.digest === currentDigest ? 'current-inputs' : 'historical-inputs', scope: item.definition.scope }
|
||||
})
|
||||
}
|
||||
|
||||
export function contractStatus(directory, model) {
|
||||
const current = candidateInputs(directory, model)
|
||||
const registry = readJson(directory, 'refactor/contract-runs.json')
|
||||
assert.equal(registry.schemaVersion, 1)
|
||||
assert.equal(new Set(registry.runs.map(run => run.file)).size, registry.runs.length, 'Duplicate registered report')
|
||||
const observations = []
|
||||
for (const ref of registry.runs) {
|
||||
const bytes = fs.readFileSync(localFile(directory, ref.file))
|
||||
assert.equal(sha256(bytes.toString('utf8').replaceAll('\r\n', '\n')), ref.sha256LF, `Contract report changed: ${ref.file}`)
|
||||
const report = JSON.parse(bytes)
|
||||
observations.push({ file: ref.file, outcome: report.outcome, cases: evaluateRun(model, report, current.digest) })
|
||||
}
|
||||
const latest = 'refactor/.cache/ci/contracts-run.json'
|
||||
if (fs.existsSync(path.join(directory, latest))) {
|
||||
const report = readJson(directory, latest)
|
||||
observations.push({ file: latest, outcome: report.outcome, cases: evaluateRun(model, report, current.digest) })
|
||||
}
|
||||
const rows = model.policy.packages.flatMap(pkg => model.families.map((family) => {
|
||||
const cases = model.policy.cases.filter(item => item.package === pkg.name && item.contracts.includes(family.id))
|
||||
const results = observations.flatMap(run => run.cases.filter(item => item.package === pkg.name && item.contracts.includes(family.id)).map(item => ({ ...item, report: run.file, runOutcome: run.outcome })))
|
||||
return { package: pkg.name, contract: family.id, owner: pkg.owners[family.id], versionIds: pkg.versionIds, supportWindow: pkg.supportWindow, state: results.length ? 'partial-observations' : cases.length ? 'planned-not-observed' : 'tests-not-indexed', cases: cases.map(item => item.id), observations: results, remaining: 'Family-wide acceptance, version combinations and environment scope are not implied by individual test observations' }
|
||||
}))
|
||||
return { schemaVersion: 1, candidateDigest: current.digest, rows, observations, historicalRecords: model.historical.map(item => ({ ...item, state: 'recorded-not-normalized' })), limitation: 'Index status is not package compatibility or npm release approval' }
|
||||
}
|
||||
|
||||
export function renderContracts(model) {
|
||||
const lines = ['# 公开契约覆盖索引', '', '> 由 contract-policy.json 生成;`yarn check:contracts --write` 更新。动态候选/报告状态见 `yarn check:contracts --report`。', '', '覆盖12类兼容契约与22包的验证归属;不是每个公开成员的穷尽测试证明。没有固定测试ID的行仍需逐成员补齐。', '版本均为精确冻结对照点,不能推导连续支持区间。历史报告保留原状态,只有精确用例和候选指纹对应时才列为观察结果。', '', '| 包 | 契约 | 责任任务 | 固定测试ID |', '| --- | --- | --- | --- |']
|
||||
for (const pkg of model.policy.packages) {
|
||||
for (const family of model.families) {
|
||||
const cases = model.policy.cases.filter(item => item.package === pkg.name && item.contracts.includes(family.id))
|
||||
lines.push(`| ${pkg.name} | ${family.id} ${family.name} | ${pkg.owners[family.id]} | ${cases.map(item => item.id).join(', ') || '待索引;不代表没有历史测试'} |`)
|
||||
}
|
||||
}
|
||||
lines.push('', '## 精确测试及限制', '')
|
||||
for (const item of model.policy.cases)
|
||||
lines.push(`- ${item.id}: [${item.file}](../${item.file}) — ${item.title}。${item.scope}。限制:${item.limitations}。命令:\`${item.command}\`。版本依据:${item.versionIds.join(', ')}。`)
|
||||
lines.push('', '## 版本依据', '')
|
||||
for (const version of model.policy.versions)
|
||||
lines.push(`- ${version.id}: ${version.package}@${version.version} (${version.kind});[来源](../${version.file}) JSON pointer \`${version.pointer}\`。`)
|
||||
return `${lines.join('\n')}\n`
|
||||
}
|
||||
|
||||
export const currentHead = directory => execFileSync('git', ['rev-parse', 'HEAD'], { cwd: directory, encoding: 'utf8' }).trim()
|
||||
@@ -0,0 +1,23 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { contractStatus, readContractModel, renderContracts } from './contracts-model.mjs'
|
||||
|
||||
const root = fileURLToPath(new URL('../../', import.meta.url))
|
||||
const args = process.argv.slice(2)
|
||||
assert(args.every(arg => ['--write', '--report'].includes(arg)), 'Use --write and/or --report')
|
||||
const model = readContractModel(root)
|
||||
const markdown = renderContracts(model)
|
||||
const file = path.join(root, 'refactor/contract-index.md')
|
||||
if (args.includes('--write'))
|
||||
fs.writeFileSync(file, markdown)
|
||||
else assert.equal(fs.readFileSync(file, 'utf8').replaceAll('\r\n', '\n'), markdown, 'Contract index is stale; run yarn check:contracts --write')
|
||||
const report = contractStatus(root, model)
|
||||
if (args.includes('--report')) {
|
||||
fs.mkdirSync(path.join(root, 'refactor/.cache/ci'), { recursive: true })
|
||||
fs.writeFileSync(path.join(root, 'refactor/.cache/ci/contracts.json'), `${JSON.stringify(report, null, 2)}\n`)
|
||||
}
|
||||
console.log(`Contracts: ${model.policy.packages.length} packages, ${report.rows.length} assigned families, ${model.policy.cases.length} indexed cases, ${report.observations.length} structured runs`)
|
||||
console.log(`${report.rows.filter(row => row.state === 'tests-not-indexed').length} family rows need test indexing; historical records are not auto-promoted to current acceptance`)
|
||||
@@ -0,0 +1,185 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
// eslint-disable-next-line test/no-import-node-test -- Contract evidence validation uses real Node runner results and isolated files.
|
||||
import test from 'node:test'
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url'
|
||||
import { candidateInputs, contractStatus, evaluateRun, localFile, pointer, readContractModel, renderContracts, sha256 } from './contracts-model.mjs'
|
||||
|
||||
const root = fileURLToPath(new URL('../../', import.meta.url))
|
||||
const actual = readContractModel(root)
|
||||
const cache = path.join(root, 'refactor/.cache')
|
||||
|
||||
function fixture(t) {
|
||||
fs.mkdirSync(cache, { recursive: true })
|
||||
const directory = fs.mkdtempSync(path.join(cache, 'contracts-test-'))
|
||||
t.after(() => {
|
||||
const relative = path.relative(cache, directory)
|
||||
assert(relative.startsWith('contracts-test-') && !relative.includes(path.sep))
|
||||
fs.rmSync(directory, { recursive: true, force: true })
|
||||
})
|
||||
const write = (file, value) => {
|
||||
fs.mkdirSync(path.dirname(path.join(directory, file)), { recursive: true })
|
||||
fs.writeFileSync(path.join(directory, file), typeof value === 'string' ? value : JSON.stringify(value))
|
||||
}
|
||||
const policy = structuredClone(actual.policy)
|
||||
const save = () => write('refactor/contract-policy.json', policy)
|
||||
save()
|
||||
write('refactor/tasks.json', { tasks: actual.tasks.map(task => ({ ...task, evidence: [] })) })
|
||||
for (const file of new Set(['refactor/package-inventory.json', 'refactor/compatibility.md', ...policy.versions.map(version => version.file)]))
|
||||
write(file, fs.readFileSync(path.join(root, file), 'utf8'))
|
||||
for (const pkg of policy.packages) {
|
||||
write(`packages/${pkg.name}/package.json`, { name: pkg.name })
|
||||
write(`packages/${pkg.name}/src/index.ts`, 'export const value = 1')
|
||||
}
|
||||
for (const file of new Set(policy.cases.map(item => item.file)))
|
||||
write(file, `import test from 'node:test'\n${policy.cases.filter(item => item.file === file).map(item => `test(${JSON.stringify(item.title)}, () => {})`).join('\n')}`)
|
||||
for (const file of ['package.json', 'yarn.lock', '.node-version']) write(file, fs.readFileSync(path.join(root, file), 'utf8'))
|
||||
write('refactor/contract-runs.json', { schemaVersion: 1, runs: [] })
|
||||
return { directory, policy, save, write, model: () => readContractModel(directory) }
|
||||
}
|
||||
|
||||
test('Actual contract model assigns all 264 family rows and separates exact published versions from workspace snapshots', () => {
|
||||
assert.equal(actual.policy.packages.length, 22)
|
||||
assert.equal(actual.families.length, 12)
|
||||
assert.equal(actual.policy.versions.filter(version => version.kind === 'published-baseline').length, 7)
|
||||
assert.equal(actual.policy.versions.filter(version => version.kind === 'workspace-baseline').length, 22)
|
||||
assert.equal(actual.policy.cases.length, 10)
|
||||
const markdown = renderContracts(actual)
|
||||
assert.equal(markdown.split('\n').filter(line => /^\| artplayer[^|]* \| API-/.test(line)).length, 264)
|
||||
assert.equal(pointer({ 'a/b': { '~': 2 } }, '/a~1b/~0'), 2)
|
||||
})
|
||||
|
||||
test('Missing families, package owners, tasks and unsupported version claims cannot silently pass', (t) => {
|
||||
for (const edit of [
|
||||
(policy) => { policy.families.pop() },
|
||||
(policy) => { policy.packages.pop() },
|
||||
(policy) => { delete policy.packages[0].owners['API-01'] },
|
||||
(policy) => { policy.packages[0].owners['API-01'] = 'NONEXISTENT' },
|
||||
(policy) => { policy.packages[0].supportWindow = 'all-historical-versions' },
|
||||
(policy) => { policy.packages[0].versionIds = ['invented'] },
|
||||
]) {
|
||||
const repo = fixture(t)
|
||||
edit(repo.policy)
|
||||
repo.save()
|
||||
assert.throws(repo.model)
|
||||
}
|
||||
const repo = fixture(t)
|
||||
const manifest = JSON.parse(fs.readFileSync(path.join(repo.directory, 'package.json'), 'utf8'))
|
||||
manifest.scripts['ci:check'] = manifest.scripts['ci:check'].replace('yarn test:contracts && ', '')
|
||||
repo.write('package.json', manifest)
|
||||
assert.throws(repo.model, /CI must run/)
|
||||
})
|
||||
|
||||
test('Version evidence checks JSON pointer, package identity, exact version and archive integrity', (t) => {
|
||||
for (const edit of [
|
||||
(version) => { version.pointer = '/releases/99' },
|
||||
(version) => { version.version = '99.0.0' },
|
||||
(version) => { version.package = 'artplayer-plugin-chapter' },
|
||||
(version) => { version.archiveSha256 = 'a'.repeat(64) },
|
||||
(version) => { version.file = '../outside.json' },
|
||||
]) {
|
||||
const repo = fixture(t)
|
||||
edit(repo.policy.versions.find(version => version.kind === 'published-baseline'))
|
||||
repo.save()
|
||||
assert.throws(repo.model)
|
||||
}
|
||||
})
|
||||
|
||||
test('Stable IDs require an actual unique literal test and valid paths; comments, skips and renamed assertions are not enough', (t) => {
|
||||
for (const source of [
|
||||
title => `import test from 'node:test'; // test(${JSON.stringify(title)}, () => {})`,
|
||||
title => `import test from 'node:test'; test.skip(${JSON.stringify(title)}, () => {})`,
|
||||
() => `import test from 'node:test'; test('renamed', () => {})`,
|
||||
title => `import test from 'node:test'; test(${JSON.stringify(title)}, () => {}); test(${JSON.stringify(title)}, () => {})`,
|
||||
]) {
|
||||
const repo = fixture(t)
|
||||
repo.write(repo.policy.cases[0].file, source(repo.policy.cases[0].title))
|
||||
assert.throws(repo.model, /exactly one literal/)
|
||||
}
|
||||
const repo = fixture(t)
|
||||
repo.policy.cases.push(repo.policy.cases[0])
|
||||
repo.save()
|
||||
assert.throws(repo.model, /Duplicate case/)
|
||||
repo.policy.cases[repo.policy.cases.length - 1] = { ...repo.policy.cases[0], id: 'CT-DUPLICATE-ASSERTION' }
|
||||
repo.save()
|
||||
assert.throws(repo.model, /Duplicate file\/title/)
|
||||
repo.policy.cases = []
|
||||
repo.save()
|
||||
assert.throws(repo.model, /requires explicit cases/)
|
||||
for (const file of ['../outside', 'D:/outside', '/absolute', 'test\\outside.js'])
|
||||
assert.throws(() => localFile(repo.directory, file))
|
||||
})
|
||||
|
||||
function observation(model, candidate, item, status = 'passed') {
|
||||
const event = { type: status === 'failed' ? 'test:fail' : 'test:pass', name: item.title, file: item.file, ...(status === 'skipped' ? { skip: 'unsupported fixture' } : {}) }
|
||||
return { schemaVersion: 1, command: 'yarn test:contracts', head: 'a'.repeat(40), environment: { node: process.version, platform: process.platform }, candidate, outcome: status === 'failed' ? 'failed' : 'passed', events: [event, { type: 'test:summary', success: status !== 'failed' }], cases: [{ id: item.id, definition: item, definitionSha256: sha256(JSON.stringify(item)), status }] }
|
||||
}
|
||||
|
||||
test('Evidence distinguishes current, stale, skipped and failed; wrong titles, fingerprints and process outcomes fail', (t) => {
|
||||
const repo = fixture(t)
|
||||
const model = repo.model()
|
||||
const candidate = candidateInputs(repo.directory, model)
|
||||
const item = model.policy.cases[0]
|
||||
const passed = observation(model, candidate, item)
|
||||
assert.equal(evaluateRun(model, passed, candidate.digest)[0].freshness, 'current-inputs')
|
||||
repo.write('packages/artplayer/src/index.ts', 'export const value = 2')
|
||||
assert.equal(evaluateRun(model, passed, candidateInputs(repo.directory, model).digest)[0].freshness, 'historical-inputs')
|
||||
for (const status of ['skipped', 'failed'])
|
||||
assert.equal(evaluateRun(model, observation(model, candidate, item, status), candidate.digest)[0].status, status)
|
||||
for (const edit of [
|
||||
(report) => { report.events[0].name = 'different assertion' },
|
||||
(report) => { report.cases[0].status = 'skipped' },
|
||||
(report) => { report.candidate.digest = 'a'.repeat(64) },
|
||||
(report) => { report.cases[0].definitionSha256 = 'b'.repeat(64) },
|
||||
(report) => { report.events.at(-1).success = false },
|
||||
(report) => { report.events.push(report.events[0]) },
|
||||
]) {
|
||||
const report = structuredClone(passed)
|
||||
edit(report)
|
||||
assert.throws(() => evaluateRun(model, report, candidate.digest))
|
||||
}
|
||||
const modified = structuredClone(model)
|
||||
modified.policy.cases[0].scope = 'A revised assertion scope'
|
||||
assert.equal(evaluateRun(modified, passed, candidate.digest)[0].freshness, 'historical-definition')
|
||||
})
|
||||
|
||||
test('Report registry checks immutable bytes and never upgrades individual observations into complete families', (t) => {
|
||||
const repo = fixture(t)
|
||||
const model = repo.model()
|
||||
const report = observation(model, candidateInputs(repo.directory, model), model.policy.cases[0])
|
||||
const file = 'refactor/observation.json'
|
||||
repo.write(file, report)
|
||||
const bytes = fs.readFileSync(path.join(repo.directory, file))
|
||||
repo.write('refactor/contract-runs.json', { schemaVersion: 1, runs: [{ file, sha256LF: sha256(bytes) }] })
|
||||
const result = contractStatus(repo.directory, model)
|
||||
assert.equal(result.rows.length, 264)
|
||||
assert.equal(result.rows.find(row => row.package === 'artplayer' && row.contract === 'API-01').state, 'partial-observations')
|
||||
assert(result.rows.some(row => row.state === 'planned-not-observed'))
|
||||
assert(result.rows.some(row => row.state === 'tests-not-indexed'))
|
||||
repo.policy.cases[0].contracts = ['API-09']
|
||||
repo.save()
|
||||
const revised = contractStatus(repo.directory, repo.model())
|
||||
assert.equal(revised.rows.find(row => row.package === 'artplayer' && row.contract === 'API-09').observations.length, 0)
|
||||
assert.equal(revised.rows.find(row => row.package === 'artplayer' && row.contract === 'API-01').observations[0].freshness, 'historical-definition')
|
||||
repo.write(file, { ...report, outcome: 'failed' })
|
||||
assert.throws(() => contractStatus(repo.directory, model), /Contract report changed/)
|
||||
})
|
||||
|
||||
test('Real Node reporter retains exact file/name, failure, skip, todo and overall process failure', (t) => {
|
||||
const repo = fixture(t)
|
||||
repo.write('runner.test.mjs', 'import test from \'node:test\';test(\'passing\',()=>{});test(\'failure\',()=>{throw Error(\'fixture failure\')});test(\'skipped\',{skip:\'no environment\'},()=>{});test(\'todo\',{todo:\'pending\'},()=>{});')
|
||||
const env = { ...process.env }
|
||||
delete env.NODE_TEST_CONTEXT
|
||||
delete env.NODE_OPTIONS
|
||||
const result = spawnSync(process.execPath, ['--test', `--test-reporter=${pathToFileURL(path.join(root, 'refactor/scripts/contract-reporter.mjs')).href}`, 'runner.test.mjs'], { cwd: repo.directory, env, encoding: 'utf8' })
|
||||
assert.equal(result.status, 1, result.stderr)
|
||||
const events = result.stdout.trim().split(/\r?\n/).map(line => JSON.parse(line))
|
||||
assert(events.some(event => event.name === 'passing' && event.type === 'test:pass' && event.file === path.join(repo.directory, 'runner.test.mjs')))
|
||||
assert(events.some(event => event.name === 'failure' && event.type === 'test:fail'))
|
||||
assert(events.some(event => event.name === 'skipped' && event.skip))
|
||||
assert(events.some(event => event.name === 'todo' && event.todo))
|
||||
assert.equal(events.find(event => event.type === 'test:summary' && !event.file).success, false)
|
||||
})
|
||||
@@ -0,0 +1,54 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { spawnSync } 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 { candidateInputs, currentHead, evaluateRun, readContractModel, sha256 } from './contracts-model.mjs'
|
||||
|
||||
const root = fileURLToPath(new URL('../../', import.meta.url))
|
||||
assert.equal(process.argv.length, 2, 'Contract runner does not accept filters or alternate artifacts')
|
||||
const model = readContractModel(root)
|
||||
const candidate = candidateInputs(root, model)
|
||||
const head = currentHead(root)
|
||||
const directory = path.join(root, 'refactor/.cache/contracts')
|
||||
fs.mkdirSync(directory, { recursive: true })
|
||||
const output = fs.mkdtempSync(path.join(directory, 'run-'))
|
||||
const files = [...new Set(model.policy.cases.map(item => item.file))]
|
||||
const env = { ...process.env }
|
||||
for (const key of Object.keys(env)) {
|
||||
if (key.startsWith('ARTPLAYER_') || ['NODE_OPTIONS', 'NODE_TEST_CONTEXT', 'NODE_V8_COVERAGE'].includes(key))
|
||||
delete env[key]
|
||||
}
|
||||
const command = ['--test', '--test-reporter=./refactor/scripts/contract-reporter.mjs', ...files]
|
||||
const executed = spawnSync(process.execPath, command, { cwd: root, env, encoding: 'utf8', timeout: 180000, maxBuffer: 16 * 1024 * 1024 })
|
||||
fs.writeFileSync(path.join(output, 'events.jsonl'), executed.stdout || '')
|
||||
fs.writeFileSync(path.join(output, 'stderr.log'), executed.stderr || '')
|
||||
assert(!executed.error, `Contract process failed: ${executed.error?.message}; evidence ${output}`)
|
||||
assert.equal(candidateInputs(root, model).digest, candidate.digest, 'Contract inputs changed during execution')
|
||||
const events = executed.stdout.split(/\r?\n/).filter(Boolean).map(line => JSON.parse(line)).map(event => ({ ...event, ...(event.file ? { file: path.relative(root, event.file).split(path.sep).join('/') } : {}) }))
|
||||
const report = {
|
||||
schemaVersion: 1,
|
||||
command: 'yarn test:contracts',
|
||||
invocation: [process.execPath, ...command],
|
||||
head,
|
||||
capturedAt: new Date().toISOString(),
|
||||
environment: { node: process.version, platform: process.platform, arch: process.arch, artifactOverrides: 'cleared' },
|
||||
candidate,
|
||||
events,
|
||||
outcome: executed.status === 0 ? 'passed' : 'failed',
|
||||
cases: model.policy.cases.map((item) => {
|
||||
const matches = events.filter(event => ['test:pass', 'test:fail'].includes(event.type) && event.file === item.file && event.name === item.title)
|
||||
assert.equal(matches.length, 1, `Missing or ambiguous runner result: ${item.id}; evidence ${output}`)
|
||||
const event = matches[0]
|
||||
return { id: item.id, definition: item, definitionSha256: sha256(JSON.stringify(item)), status: event.skip || event.todo ? 'skipped' : event.type === 'test:pass' ? 'passed' : 'failed' }
|
||||
}),
|
||||
limitations: 'Selected source/controlled-host assertions only; not full family, package tarball, browser, device, SDK or support-range acceptance',
|
||||
}
|
||||
evaluateRun(model, report, candidate.digest)
|
||||
fs.writeFileSync(path.join(output, 'report.json'), `${JSON.stringify(report, null, 2)}\n`)
|
||||
fs.mkdirSync(path.join(root, 'refactor/.cache/ci'), { recursive: true })
|
||||
fs.writeFileSync(path.join(root, 'refactor/.cache/ci/contracts-run.json'), `${JSON.stringify(report, null, 2)}\n`)
|
||||
fs.writeFileSync(path.join(directory, 'latest.json'), `${JSON.stringify({ output: path.relative(root, output).split(path.sep).join('/') }, null, 2)}\n`)
|
||||
console.log(`Contract observations: ${report.cases.length} indexed cases; process ${report.outcome}; ${path.relative(root, output)}`)
|
||||
process.exitCode = executed.status === 0 && report.cases.every(item => item.status === 'passed') ? 0 : 1
|
||||
+7
-2
@@ -654,7 +654,7 @@
|
||||
"risk": "M",
|
||||
"id": "ENG-COVERAGE-01",
|
||||
"title": "建立公开契约与版本测试证据覆盖索引",
|
||||
"status": "todo",
|
||||
"status": "done",
|
||||
"dependsOn": [
|
||||
"BASE-08",
|
||||
"CORE-22",
|
||||
@@ -662,7 +662,12 @@
|
||||
],
|
||||
"deliverable": "契约-包-支持版本-固定测试ID-命令-候选/报告-责任任务索引和文档检查",
|
||||
"acceptance": "计划、已执行和缺证据状态分开,所有公开契约都有验证归属;无效路径、版本依据或报告对应不明确会被识别",
|
||||
"evidence": []
|
||||
"evidence": [
|
||||
"contract-coverage.md",
|
||||
"contract-index.md",
|
||||
"changes/2026-09-12-ENG-COVERAGE-01-index.md",
|
||||
"baselines/contracts-validation.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ENG-09",
|
||||
|
||||
+4
-1
@@ -1,5 +1,8 @@
|
||||
# 测试与验收
|
||||
|
||||
本清单是验收目录,早期未勾选项不代表当前未实施。计划、历史执行与精确候选观察
|
||||
见tasks/progress及 [契约覆盖索引](contract-coverage.md),不能以总通过数代替契约验证。
|
||||
|
||||
核心/chapter 的 Node TS 覆盖率使用 `yarn test:coverage`。源映射防伪验证、关键生命周期门槛、报告位置及资源/性能实施状态见 [覆盖率与性能维护说明](coverage-performance.md)。
|
||||
|
||||
## 测试层次
|
||||
@@ -79,4 +82,4 @@ ENG-02 先交付仓库内可执行的 CI 配置;GitHub required checks 的实
|
||||
|
||||
## 本计划不代表已有测试
|
||||
|
||||
以上新增命令、测试目录和矩阵都是待实现内容。现有测试事实及评估范围见 [progress.md](progress.md)。
|
||||
以上目录源自初始计划,部分命令、测试和矩阵已实施;当前事实及范围见 [progress.md](progress.md)。
|
||||
Reference in new issue
Block a user