mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 10:56:15 -08:00
test(ambilight): [PKG-AMBILIGHT-01] freeze actual release and invocation contracts
This commit is contained in:
1 parent
c57c7e4b16
commit
06032a6906
17 files changed
+638
-7
No files matched your search
@@ -0,0 +1,13 @@
|
||||
# Ambilight 测试维护
|
||||
|
||||
先读 [发布契约](baselines/ambilight-contract.md)。ambilight-release.json冻结两个真实
|
||||
发布及工作区Git来源,verifyAmbilightContract逐成员核对,不随候选更新历史哈希。
|
||||
|
||||
`node --test refactor/scripts/ambilight-contract.test.mjs`执行7组:归档、CJS/script/legacy、
|
||||
原生ESM、声明形状及三实现的配置/样式/坐标/同步返回。test/helpers/ambilight.js提供
|
||||
受控DOM/Canvas/RAF;模拟RGB只检查取样及写入顺序,不证明浏览器解码或跨域取色。
|
||||
|
||||
02复现频率/暂停/零尺寸/重复start-stop/销毁/异常;03拆分视图、取色和调度,修复已
|
||||
复现问题;04兼容类型及旧CJS.default;05/06做真实媒体、代理、浏览器和安装包验证。
|
||||
stop保留最后画面,核心destroy终止资源。zIndex仍忽略,参数省略及两版声明差异分别
|
||||
处理;Git关联不构成连续核心支持范围。
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"task": "PKG-AMBILIGHT-01",
|
||||
"status": "done",
|
||||
"sourceCommit": "c57c7e4b164557d111bb92a9df17c2f28d2ee657",
|
||||
"checks": [
|
||||
{
|
||||
"command": "node --test refactor/scripts/ambilight-contract.test.mjs refactor/scripts/contracts.test.mjs",
|
||||
"result": "pass",
|
||||
"tests": 14,
|
||||
"ambilightTests": 7,
|
||||
"indexTests": 7,
|
||||
"log": "refactor/.cache/ambilight-contract-final.log"
|
||||
},
|
||||
{
|
||||
"command": "eslint Ambilight helper/contract scripts and updated contracts test --no-fix",
|
||||
"result": "pass",
|
||||
"log": "refactor/.cache/ambilight-lint-final.log"
|
||||
},
|
||||
{
|
||||
"command": "yarn ci:check",
|
||||
"result": "pass",
|
||||
"tests": {
|
||||
"unit": 832,
|
||||
"engineering": 14,
|
||||
"baseline": 79,
|
||||
"mainTotal": 925,
|
||||
"repeatedContractExecutions": 44
|
||||
},
|
||||
"log": "refactor/.cache/ambilight01-ci.log"
|
||||
}
|
||||
],
|
||||
"logs": [
|
||||
{
|
||||
"file": "refactor/.cache/ambilight-contract-final.log",
|
||||
"sha256": "a28f0c8a6b8b087174aeb39b1cb1ea3c792ed25fef86e8f636ffdb2349652177"
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ambilight-lint-final.log",
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ambilight01-ci.log",
|
||||
"sha256": "71fd0363f2e34df800730bdf5fc6e9d1c40819ae119f83ce9c5add3310b91ec2"
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"file": "refactor/scripts/ambilight-contract.mjs",
|
||||
"sha256LF": "50cd644d2715812ae57fad7e8babab85269b8693fddd6a7600fd4e3446927867"
|
||||
},
|
||||
{
|
||||
"file": "refactor/scripts/ambilight-contract.test.mjs",
|
||||
"sha256LF": "38b626131836a6087efb9bccdff8f46e907ed54d064fbcb4927d931ac39bb9d3"
|
||||
},
|
||||
{
|
||||
"file": "test/helpers/ambilight.js",
|
||||
"sha256LF": "7775f147355d2aa08615a82079760924ae151beb7d9bdc94cccfd8195f5b19d8"
|
||||
},
|
||||
{
|
||||
"file": "refactor/contract-policy.json",
|
||||
"sha256LF": "3da55d2e6e656831f2cc63678e8b78c9499640d8471463afd48369a02148aeaf"
|
||||
},
|
||||
{
|
||||
"file": "refactor/scripts/contracts.test.mjs",
|
||||
"sha256LF": "1db5b256464bf17e969070375558c6ff854f116a57e5283b04e9c1327dd9c1e3"
|
||||
}
|
||||
],
|
||||
"limitations": [
|
||||
"Node-controlled Canvas/RAF is not real browser/media validation",
|
||||
"Declarations inspected, not full installed compiler acceptance",
|
||||
"Git core association is not a supported version interval",
|
||||
"Lifecycle issues remain source observations until PKG-AMBILIGHT-02 reproduces them",
|
||||
"No production source/type/artifact changes or remote publish"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
# Ambilight 实际发布与工作区契约
|
||||
|
||||
PKG-AMBILIGHT-01;来源c57c7e4b。实际npm有1.0.0、1.1.0,后者为采集时latest。
|
||||
两份tarball各6成员,完整哈希见ambilight-release.json;1.1.0不含src,直接核对dist。
|
||||
工作区源码、声明、README、manifest和demo另以Git冻结。目标2.0.0由REL-09修改。
|
||||
|
||||
## 运行时
|
||||
|
||||
注册时才读取配置,默认blur=50px、opacity=0.5、frequency=10、duration=0.3。同步返回
|
||||
{name,start,stop},两方法返回undefined。省略option可用,null在注册时抛错;创建工厂
|
||||
后、注册前的配置修改可观察。没有额外公开事件或destroy方法。
|
||||
|
||||
video之前插入artplayer-plugin-ambilight,绝对定位3×3网格,zIndex固定9;声明里的
|
||||
zIndex从未读取。9子节点保留filter、opacity和background-color transition。ready自动
|
||||
start;stop停止更新并保留最后颜色,start可恢复。playing且达到频率间隔时,将9个
|
||||
视频区域分别缩至1×1读取RGB。暂停仍调度RAF、不取色。特殊频率语义由02补齐。
|
||||
|
||||
## 公开差异
|
||||
|
||||
| 维度 | npm1.0.0 | npm1.1.0/工作区 | 迁移处理 |
|
||||
| --- | --- | --- | --- |
|
||||
| CJS | namespace.default | 可调用工厂 | 保留两种调用形式 |
|
||||
| ESM | 无独立mjs | mjs默认导出 | 保留真实ESM入口 |
|
||||
| 类型导出 | export=、全局声明 | export default | 04验证旧require/import=及新默认导入 |
|
||||
| Option字段 | 五字段必需 | 五字段可选 | 这是已发布历史变化,不是候选修正 |
|
||||
| 工厂参数 | 声明要求option | 同左 | 运行时允许省略;兼容补齐声明 |
|
||||
| zIndex | 声明有、运行时忽略 | 同左 | 继续接受旧输入并保留固定层级 |
|
||||
|
||||
README只有demo链接;当前demo明确传blur/opacity/frequency/duration。不能凭声明里的
|
||||
zIndex悄悄启用新布局行为。01核对真实声明形状,不把正则检查冒充完整消费者编译。
|
||||
|
||||
## 后续验证
|
||||
|
||||
destroy只stop,未清DOM/监听器;逃逸start可能重启;RAF的truthy检查会混淆id=0;
|
||||
空context、零尺寸和取色异常可能中断循环。目前登记源码观察,02必须复现、03修复。
|
||||
stop保留视图与核心destroy终止资源应分开,不能新增用户必须调用的方法。
|
||||
|
||||
registry gitHead对应核心5.1.7/5.3.1,仅是Git关联,不是tarball可复现或支持区间证明。
|
||||
未声明peerDependencies,05须实际验证旧核心/候选、Canvas代理及媒体能力。Canvas
|
||||
依赖可取色媒体、2Dcontext和origin-clean;Node记录器不证明浏览器像素或真机行为。
|
||||
01共7组检查,维护入口见 [Ambilight测试说明](../ambilight-validation.md)。
|
||||
@@ -0,0 +1,137 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"task": "PKG-AMBILIGHT-01",
|
||||
"capturedAt": "2026-09-12T02:42:35.458Z",
|
||||
"sourceCommit": "c57c7e4b164557d111bb92a9df17c2f28d2ee657",
|
||||
"registryObservation": {
|
||||
"versions": [
|
||||
"1.0.0",
|
||||
"1.1.0"
|
||||
],
|
||||
"latest": "1.1.0",
|
||||
"workspaceVersion": "1.1.0"
|
||||
},
|
||||
"release": {
|
||||
"name": "artplayer-plugin-ambilight",
|
||||
"version": "1.1.0",
|
||||
"metadataUrl": "https://registry.npmjs.org/artplayer-plugin-ambilight/1.1.0",
|
||||
"tarball": "https://registry.npmjs.org/artplayer-plugin-ambilight/-/artplayer-plugin-ambilight-1.1.0.tgz",
|
||||
"integrity": "sha512-FkMI7bn9HovJMfEdgkKbo7/vPt6vrzcpELTaC/vM7B99FPSMWzAf6GAE0xnNxU3KibYcBV5gXnBgvHgpG2xq2A==",
|
||||
"sha256": "e95bd60fd7bce52142cf0041bad811452305029cbe1129c7d89246eb260c5c86",
|
||||
"archiveBytes": 2638,
|
||||
"publishedAt": "2026-03-12T14:22:20.888Z",
|
||||
"registryGitHead": "daf133b22630b4a0eecfa3336bbddab0e9119d96",
|
||||
"files": {
|
||||
"package/README.md": "53946ecc0fca163d52465515dfa13de3619a01c3b89d7f966eafe0610584dede",
|
||||
"package/dist/artplayer-plugin-ambilight.js": "8f32290c65a13b0a3026dee0045fef4125a9c9ac788ecb1675aef05f79172693",
|
||||
"package/dist/artplayer-plugin-ambilight.legacy.js": "8f32290c65a13b0a3026dee0045fef4125a9c9ac788ecb1675aef05f79172693",
|
||||
"package/dist/artplayer-plugin-ambilight.mjs": "77486db6d24f50420c952de5b37d226e404089730c7c1c6be226c836f4e85f9c",
|
||||
"package/package.json": "39aa2790a79cbc2a62ba925ee5ff53bbb649440a5d7b9f526b3de819ece4f792",
|
||||
"package/types/artplayer-plugin-ambilight.d.ts": "a36998b4d96d9a3200022029bf92918cc516d5ae4f38dc78a1a76b53a736d086"
|
||||
},
|
||||
"manifest": {
|
||||
"name": "artplayer-plugin-ambilight",
|
||||
"version": "1.1.0",
|
||||
"description": "ambilight plugin for ArtPlayer",
|
||||
"author": "Harvey Zhao <laozhaochaguan@gmail.com>",
|
||||
"license": "MIT",
|
||||
"homepage": "https://artplayer.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/zhw2590582/ArtPlayer.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/zhw2590582/ArtPlayer/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"html5",
|
||||
"video",
|
||||
"player"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./types/artplayer-plugin-ambilight.d.ts",
|
||||
"import": "./dist/artplayer-plugin-ambilight.mjs",
|
||||
"require": "./dist/artplayer-plugin-ambilight.js"
|
||||
},
|
||||
"./legacy": {
|
||||
"types": "./types/artplayer-plugin-ambilight.d.ts",
|
||||
"import": "./dist/artplayer-plugin-ambilight.legacy.js",
|
||||
"require": "./dist/artplayer-plugin-ambilight.legacy.js"
|
||||
}
|
||||
},
|
||||
"main": "./dist/artplayer-plugin-ambilight.js",
|
||||
"module": "./dist/artplayer-plugin-ambilight.mjs",
|
||||
"types": "./types/artplayer-plugin-ambilight.d.ts",
|
||||
"legacy": "./dist/artplayer-plugin-ambilight.legacy.js",
|
||||
"browserslist": "last 1 Chrome version"
|
||||
},
|
||||
"historicalCore": {
|
||||
"commit": "daf133b22630b4a0eecfa3336bbddab0e9119d96",
|
||||
"file": "packages/artplayer/package.json",
|
||||
"version": "5.3.1",
|
||||
"sha256LF": "862f3697876fe915dd0ada178da766ce823aae7563ed30b3e5b11b811202929c",
|
||||
"limitation": "Git association only; not a proof of support range or rebuilt tarball identity"
|
||||
}
|
||||
},
|
||||
"previous": [
|
||||
{
|
||||
"name": "artplayer-plugin-ambilight",
|
||||
"version": "1.0.0",
|
||||
"metadataUrl": "https://registry.npmjs.org/artplayer-plugin-ambilight/1.0.0",
|
||||
"tarball": "https://registry.npmjs.org/artplayer-plugin-ambilight/-/artplayer-plugin-ambilight-1.0.0.tgz",
|
||||
"integrity": "sha512-7UxcQLI1XZXJBfv4gZsTd1YKNL9Y7Uw2w13FyqgHqgtKWT06+EEl72UHl/C/Je1s1GHlTnw+Hdg6CphZbQ9k/g==",
|
||||
"sha256": "a95aa8c232ab48493e6b4d46b36edc8091230cbde5d18d7ec41912a4fa2b8956",
|
||||
"archiveBytes": 4751,
|
||||
"publishedAt": "2024-10-19T06:09:24.603Z",
|
||||
"registryGitHead": "9221d42964700e6a3190943d288a6e6822596790",
|
||||
"files": {
|
||||
"package/README.md": "f9efabb68702c9c9cafdaa6b82a919ef09c2fb511568764c79854a955503cb98",
|
||||
"package/dist/artplayer-plugin-ambilight.js": "caef43df8a2d4bd7aa5b42db2a16508a1f5b92443b8b76974e5e24b2a2c83911",
|
||||
"package/dist/artplayer-plugin-ambilight.legacy.js": "c6eb26f3e17432f62ac24a86f78789d3dcd2a7ca03c3701b65574cc916387c49",
|
||||
"package/package.json": "84c90d6d521c9d14a8140bef65171dce443b318630742f9901eb7c5ef5d10a3b",
|
||||
"package/src/index.js": "7e9eff950bbde38bfc63913f2ed6e47217d70cf0ba918eed3389df37d9b94a4f",
|
||||
"package/types/artplayer-plugin-ambilight.d.ts": "f08f24f814395cfd5f02bbcb1a9c99a02ed4692e9137d7a393c3ed395bba1515"
|
||||
},
|
||||
"manifest": {
|
||||
"license": "MIT",
|
||||
"version": "1.0.0",
|
||||
"name": "artplayer-plugin-ambilight",
|
||||
"types": "types/artplayer-plugin-ambilight.d.ts",
|
||||
"description": "ambilight plugin for ArtPlayer",
|
||||
"main": "dist/artplayer-plugin-ambilight.js",
|
||||
"legacy": "dist/artplayer-plugin-ambilight.legacy.js",
|
||||
"author": "Harvey Zack <laozhaochaguan@gmail.com>",
|
||||
"browserslist": "last 1 Chrome version",
|
||||
"homepage": "https://artplayer.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/zhw2590582/ArtPlayer.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/zhw2590582/ArtPlayer/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"html5",
|
||||
"video",
|
||||
"player"
|
||||
]
|
||||
},
|
||||
"historicalCore": {
|
||||
"commit": "9221d42964700e6a3190943d288a6e6822596790",
|
||||
"file": "packages/artplayer/package.json",
|
||||
"version": "5.1.7",
|
||||
"sha256LF": "8bd532103099c367fb2c289e0d410f144fdeb00b1b62cf850762a24b61e63c8a",
|
||||
"limitation": "Git association only; not a proof of support range or rebuilt tarball identity"
|
||||
}
|
||||
}
|
||||
],
|
||||
"source": {
|
||||
"packages/artplayer-plugin-ambilight/src/index.js": "c56e0a1c33a2ff54b20e6bc7eafd3e14b163e3f967cce86d48e594a2f6d1f6fe",
|
||||
"packages/artplayer-plugin-ambilight/types/artplayer-plugin-ambilight.d.ts": "a36998b4d96d9a3200022029bf92918cc516d5ae4f38dc78a1a76b53a736d086",
|
||||
"packages/artplayer-plugin-ambilight/package.json": "39aa2790a79cbc2a62ba925ee5ff53bbb649440a5d7b9f526b3de819ece4f792",
|
||||
"packages/artplayer-plugin-ambilight/README.md": "53946ecc0fca163d52465515dfa13de3619a01c3b89d7f966eafe0610584dede",
|
||||
"docs/assets/example/ambilight.js": "f97c1404886ba6474b4e14339c563fd2de50b7d50fdc4c9d59d85a3142a1ad52"
|
||||
},
|
||||
"limitation": "Frozen actual archives and workspace source; runtime/type compatibility and supported core ranges require separate tests"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
# PKG-AMBILIGHT-01 实际发布契约
|
||||
|
||||
冻结npm1.0.0/1.1.0共12成员及c57c7e4b源码/声明/README/demo。旧CJS.default、新
|
||||
可调用CJS、原生ESM和全局实际执行;三实现配置读取、9格坐标、样式及同步返回有
|
||||
受控断言。声明只核对真实文件形状,不冒充完整消费者编译。
|
||||
|
||||
zIndex一直忽略、运行时可省略参数而声明要求参数;1.0.0至1.1.0已发生导出形式及
|
||||
字段可选性变化。见 [契约](../baselines/ambilight-contract.md)。Git关联核心5.1.7/5.3.1
|
||||
不作支持区间。生命周期源码观察交02复现、03修复。索引新增本包两个实际发布点。
|
||||
|
||||
本步无生产源码/声明/产物修改。7组专项和完整CI记录在验证文件;独立提交后继续02。
|
||||
回退仅移除本包新增契约/测试/辅助记录及索引项,不回写旧发布文件。未推送或发布。
|
||||
@@ -10,7 +10,7 @@ ENG-COVERAGE-01建立可校验的归属和证据索引,不把索引建成当
|
||||
|
||||
`contract-policy.json`维护逐包逐类责任、版本依据及稳定测试ID。版本用JSON pointer
|
||||
指向冻结清单,核对包名、版本和发布archive integrity;工作区单独标识。当前22个
|
||||
工作区、7个发布对照点,不由单个版本推断连续支持区间。
|
||||
工作区、9个发布对照点(Ambilight新增两个),不由单个版本推断连续支持区间。
|
||||
|
||||
`contract-runs.json`引用归档报告及LF规范化SHA-256。报告保存当时定义、实际事件、
|
||||
输入指纹、命令、HEAD和Node/OS。其他tasks.json历史JSON证据列为recorded-not-normalized,
|
||||
|
||||
@@ -316,3 +316,5 @@
|
||||
- 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`。
|
||||
- PUB-artplayer-plugin-ambilight-1.1.0: artplayer-plugin-ambilight@1.1.0 (published-baseline);[来源](../refactor/baselines/ambilight-release.json) JSON pointer `/release`。
|
||||
- PUB-artplayer-plugin-ambilight-1.0.0: artplayer-plugin-ambilight@1.0.0 (published-baseline);[来源](../refactor/baselines/ambilight-release.json) JSON pointer `/previous/0`。
|
||||
@@ -114,7 +114,9 @@
|
||||
"API-12": "PKG-AMBILIGHT-02"
|
||||
},
|
||||
"versionIds": [
|
||||
"WS-artplayer-plugin-ambilight"
|
||||
"WS-artplayer-plugin-ambilight",
|
||||
"PUB-artplayer-plugin-ambilight-1.1.0",
|
||||
"PUB-artplayer-plugin-ambilight-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"
|
||||
@@ -782,6 +784,24 @@
|
||||
"file": "refactor/baselines/vast-release.json",
|
||||
"pointer": "/release",
|
||||
"archiveSha256": "35681d60fa3fa09b92f0f30887e39c73cf55e7b14d51a1c38fccbc6c1f253bab"
|
||||
},
|
||||
{
|
||||
"id": "PUB-artplayer-plugin-ambilight-1.1.0",
|
||||
"package": "artplayer-plugin-ambilight",
|
||||
"kind": "published-baseline",
|
||||
"version": "1.1.0",
|
||||
"file": "refactor/baselines/ambilight-release.json",
|
||||
"pointer": "/release",
|
||||
"archiveSha256": "e95bd60fd7bce52142cf0041bad811452305029cbe1129c7d89246eb260c5c86"
|
||||
},
|
||||
{
|
||||
"id": "PUB-artplayer-plugin-ambilight-1.0.0",
|
||||
"package": "artplayer-plugin-ambilight",
|
||||
"kind": "published-baseline",
|
||||
"version": "1.0.0",
|
||||
"file": "refactor/baselines/ambilight-release.json",
|
||||
"pointer": "/previous/0",
|
||||
"archiveSha256": "a95aa8c232ab48493e6b4d46b36edc8091230cbde5d18d7ec41912a4fa2b8956"
|
||||
}
|
||||
],
|
||||
"cases": [
|
||||
|
||||
+3
-2
@@ -4,7 +4,7 @@
|
||||
|
||||
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 220 项,范围 22 个包及工作区/示例。
|
||||
|
||||
状态:todo 130 / doing 6 / blocked 0 / done 84 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
状态:todo 129 / doing 6 / blocked 0 / done 85 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
|
||||
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
|
||||
|
||||
@@ -148,7 +148,7 @@
|
||||
|
||||
| ID | 范围 / 步骤 | 前置依赖 | 交付物 | 验收条件 | 风险 | 状态 |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| PKG-AMBILIGHT-01 | artplayer-plugin-ambilight<br>核对包契约与历史用法 | BASE-05 | blur/opacity/frequency/duration、start/stop 与挂载样式 | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结 | M | todo |
|
||||
| PKG-AMBILIGHT-01 | artplayer-plugin-ambilight<br>核对包契约与历史用法 | BASE-05 | blur/opacity/frequency/duration、start/stop 与挂载样式 | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结 | M | done |
|
||||
| PKG-AMBILIGHT-02 | artplayer-plugin-ambilight<br>建立特有行为与错误测试 | PKG-AMBILIGHT-01, ENG-03, ENG-05 | 跨域 canvas 失败、零尺寸、暂停、重复 start/stop 和 destroy | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | M | todo |
|
||||
| PKG-AMBILIGHT-03 | artplayer-plugin-ambilight<br>整理内部职责与资源 | PKG-AMBILIGHT-02, CORE-03, CORE-12 | 取色计算与绘帧分离,统一 RAF 和 DOM 清理 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | M | todo |
|
||||
| PKG-AMBILIGHT-04 | artplayer-plugin-ambilight<br>迁移自有源码和公开类型 | PKG-AMBILIGHT-03, ENG-04, ENG-06, CORE-07 | canvas 上下文、参数及 start/stop 的明确类型 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | M | todo |
|
||||
@@ -476,6 +476,7 @@
|
||||
- PKG-CHAPTER-03: [记录](changes/2026-09-10-PKG-CHAPTER-03-typescript-modules.md) [记录](baselines/chapter-migration-validation.json)
|
||||
- PKG-CHAPTER-04: [记录](changes/2026-09-10-PKG-CHAPTER-04-public-types.md) [记录](baselines/chapter-types-validation.json)
|
||||
- PKG-CHAPTER-05: [记录](changes/2026-09-12-PKG-CHAPTER-05-combinations.md) [记录](baselines/chapter-combinations-checkpoint.json)
|
||||
- PKG-AMBILIGHT-01: [记录](changes/2026-09-12-PKG-AMBILIGHT-01-contract.md) [记录](baselines/ambilight-contract.md) [记录](baselines/ambilight-release.json) [记录](baselines/ambilight-contract-validation.json)
|
||||
- PKG-AUDIO-01: [记录](changes/2026-09-12-PKG-AUDIO-01-contract.md) [记录](baselines/audio-track-contract.md) [记录](baselines/audio-track-release.json)
|
||||
- PKG-AUDIO-02: [记录](changes/2026-09-12-PKG-AUDIO-02-tests.md) [记录](audio-validation.md) [记录](baselines/audio-validation.json)
|
||||
- PKG-AUDIO-03: [记录](changes/2026-09-12-PKG-AUDIO-03-lifecycle.md) [记录](baselines/audio-lifecycle-validation.json)
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# 进度与证据
|
||||
|
||||
## 最新完成:Ambilight-01 实际发布契约
|
||||
|
||||
冻结npm1.0.0/1.1.0共12成员及工作区源码/声明/demo;真实执行旧CJS.default、新CJS/
|
||||
ESM/全局和三实现正常取色契约。zIndex一直忽略,参数省略与声明、历史导出形状的
|
||||
差异已登记。7组专项及7组索引反例通过,完整CI主链925项通过(另44项重复观察)。
|
||||
生命周期仍是源码观察,02复现、03修复;尚无生产源码改动或实际浏览器验收。当前
|
||||
220项:85 done、6 doing、129 todo。见 [实施记录](changes/2026-09-12-PKG-AMBILIGHT-01-contract.md)
|
||||
及 [验证证据](baselines/ambilight-contract-validation.json)。下一步Ambilight-02/03。
|
||||
|
||||
## 最新完成:ENG-09 工程保障整合
|
||||
|
||||
提交审计、核心到22包影响映射、264行契约归属和文档检查整合核验通过;三个子项已有
|
||||
|
||||
@@ -151,3 +151,6 @@
|
||||
| VAST-TYPE-01 | open / 已复现 | VAST declares an async factory as synchronous and imports an unavailable SDK type dependency | PKG-VAST-04, PKG-VAST-06 |
|
||||
| VAST-LIFE-01 | open / 已复现 | VAST SDK and container lack core destruction ownership and pending work cancellation | PKG-VAST-02, PKG-VAST-03, PKG-VAST-05 |
|
||||
| VAST-DIST-01 | open / 已复现 | Published VAST CommonJS default namespace differs from the current callable distribution | PKG-VAST-04, PKG-VAST-06 |
|
||||
| AMBILIGHT-LIFE-01 | open / 源码/产物事实 | Ambilight sampling and RAF/DOM ownership lack terminal/error handling | PKG-AMBILIGHT-02, PKG-AMBILIGHT-03, PKG-AMBILIGHT-05 |
|
||||
| AMBILIGHT-TYPE-01 | open / 源码/产物事实 | Ambilight declarations disagree with optional factory argument and differ across published generations | PKG-AMBILIGHT-04, PKG-AMBILIGHT-06 |
|
||||
| AMBILIGHT-DIST-01 | open / 已复现 | Ambilight 1.0.0 CommonJS default namespace differs from callable 1.1.0 | PKG-AMBILIGHT-04, PKG-AMBILIGHT-06 |
|
||||
@@ -3251,6 +3251,58 @@
|
||||
],
|
||||
"compatibleResolution": "Preserve require(pkg).default alongside callable factory, script global and current ESM/legacy paths.",
|
||||
"closureCriteria": "Real packed main/legacy/ESM, historical deep imports, export= consumers and local demo/editor all pass with immutable published comparisons."
|
||||
},
|
||||
{
|
||||
"id": "AMBILIGHT-LIFE-01",
|
||||
"title": "Ambilight sampling and RAF/DOM ownership lack terminal/error handling",
|
||||
"confirmation": "source-observed",
|
||||
"owners": [
|
||||
"PKG-AMBILIGHT-02",
|
||||
"PKG-AMBILIGHT-03",
|
||||
"PKG-AMBILIGHT-05"
|
||||
],
|
||||
"compatibleResolution": "Reproduce errors, zero sizes, RAF id zero, escaped methods and destroy before adding plugin-local ownership without changing stop semantics.",
|
||||
"closureCriteria": "Published failures reproduced, candidate cleanup and real canvas/media/source-switch checks pass.",
|
||||
"status": "open",
|
||||
"evidence": [
|
||||
"refactor/baselines/ambilight-contract.md",
|
||||
"refactor/baselines/ambilight-release.json",
|
||||
"refactor/changes/2026-09-12-PKG-AMBILIGHT-01-contract.md"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "AMBILIGHT-TYPE-01",
|
||||
"title": "Ambilight declarations disagree with optional factory argument and differ across published generations",
|
||||
"confirmation": "source-observed",
|
||||
"owners": [
|
||||
"PKG-AMBILIGHT-04",
|
||||
"PKG-AMBILIGHT-06"
|
||||
],
|
||||
"compatibleResolution": "Preserve legal old/new calls and ignored zIndex, validate export= and default consumers without narrowing inferred options.",
|
||||
"closureCriteria": "Installed old/current compiler consumers and documented historical limitations verified.",
|
||||
"status": "open",
|
||||
"evidence": [
|
||||
"refactor/baselines/ambilight-contract.md",
|
||||
"refactor/baselines/ambilight-release.json",
|
||||
"refactor/changes/2026-09-12-PKG-AMBILIGHT-01-contract.md"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "AMBILIGHT-DIST-01",
|
||||
"title": "Ambilight 1.0.0 CommonJS default namespace differs from callable 1.1.0",
|
||||
"confirmation": "reproduced",
|
||||
"owners": [
|
||||
"PKG-AMBILIGHT-04",
|
||||
"PKG-AMBILIGHT-06"
|
||||
],
|
||||
"compatibleResolution": "Keep callable factory with compatible self default alias.",
|
||||
"closureCriteria": "Packed root/legacy/global/ESM and old namespace consumers pass.",
|
||||
"status": "open",
|
||||
"evidence": [
|
||||
"refactor/baselines/ambilight-contract.md",
|
||||
"refactor/baselines/ambilight-release.json",
|
||||
"refactor/changes/2026-09-12-PKG-AMBILIGHT-01-contract.md"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { archiveFiles, ensureArchive, hash, readMember, refactorDir } from './releases.mjs'
|
||||
|
||||
export async function verifyAmbilightContract() {
|
||||
const baseline = JSON.parse(fs.readFileSync(path.join(refactorDir, 'baselines/ambilight-release.json'), 'utf8'))
|
||||
const archives = new Map()
|
||||
for (const release of [baseline.release, ...baseline.previous]) {
|
||||
const archive = await ensureArchive(release)
|
||||
assert.deepEqual(archiveFiles(archive), Object.keys(release.files).sort())
|
||||
for (const [file, expected] of Object.entries(release.files))
|
||||
assert.equal(hash(readMember(archive, file)), expected, `${release.version}/${file}`)
|
||||
assert.deepEqual(JSON.parse(readMember(archive, 'package/package.json')), release.manifest)
|
||||
for (const field of ['main', 'module', 'types', 'legacy']) {
|
||||
const entry = release.manifest[field]
|
||||
if (entry)
|
||||
assert(Object.hasOwn(release.files, `package/${entry.replace(/^\.\//, '')}`))
|
||||
}
|
||||
if (release.historicalCore) {
|
||||
const core = release.historicalCore
|
||||
const content = execFileSync('git', ['show', `${core.commit}:${core.file}`], { encoding: 'utf8' }).replaceAll('\r\n', '\n')
|
||||
assert.equal(hash(content), core.sha256LF)
|
||||
assert.equal(JSON.parse(content).version, core.version)
|
||||
}
|
||||
archives.set(release.version, archive)
|
||||
}
|
||||
const sources = new Map()
|
||||
for (const [file, expected] of Object.entries(baseline.source)) {
|
||||
const content = execFileSync('git', ['show', `${baseline.sourceCommit}:${file}`], { encoding: 'utf8' }).replaceAll('\r\n', '\n')
|
||||
assert.equal(hash(content), expected, `Ambilight frozen source: ${file}`)
|
||||
sources.set(file, content)
|
||||
}
|
||||
return { baseline, archives, sources }
|
||||
}
|
||||
|
||||
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
||||
const { baseline } = await verifyAmbilightContract()
|
||||
console.log(`Ambilight verified: ${[baseline.release, ...baseline.previous].map(release => release.version).join(', ')}; 12 members, frozen workspace and Git core associations`)
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import assert from 'node:assert/strict'
|
||||
// eslint-disable-next-line test/no-import-node-test -- Freeze actual published plugin contracts before migration.
|
||||
import test from 'node:test'
|
||||
import vm from 'node:vm'
|
||||
import { ambilightEnvironment, ambilightHistorical } from '../../test/helpers/ambilight.js'
|
||||
import { verifyAmbilightContract } from './ambilight-contract.mjs'
|
||||
import { readMember } from './releases.mjs'
|
||||
|
||||
const contract = await verifyAmbilightContract()
|
||||
|
||||
test('Ambilight archives, manifests and source snapshots retain their exact identities', () => {
|
||||
assert.equal(contract.baseline.release.version, '1.1.0')
|
||||
assert.equal(contract.baseline.previous[0].version, '1.0.0')
|
||||
assert.equal([...contract.archives].length, 2)
|
||||
assert.equal(contract.baseline.release.historicalCore.version, '5.3.1')
|
||||
assert.equal(contract.baseline.previous[0].historicalCore.version, '5.1.7')
|
||||
})
|
||||
|
||||
test('Ambilight published CommonJS and script globals preserve their distinct export shapes', () => {
|
||||
for (const release of [contract.baseline.release, ...contract.baseline.previous]) {
|
||||
for (const field of ['main', 'legacy']) {
|
||||
const code = readMember(contract.archives.get(release.version), `package/${release.manifest[field].replace(/^\.\//, '')}`).toString()
|
||||
const module = { exports: {} }
|
||||
const window = {}
|
||||
vm.runInNewContext(code, { module, exports: module.exports, window }, { timeout: 5000 })
|
||||
assert.equal(typeof module.exports, release.version === '1.0.0' ? 'object' : 'function')
|
||||
assert.equal(typeof (module.exports.default || module.exports)(), 'function')
|
||||
const global = { window: {} }
|
||||
vm.runInNewContext(code, global, { timeout: 5000 })
|
||||
assert.equal(typeof (global.artplayerPluginAmbilight || global.window.artplayerPluginAmbilight), 'function')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
test('Ambilight published declarations retain the required options argument and ignored zIndex field', () => {
|
||||
const old = readMember(contract.archives.get('1.0.0'), 'package/types/artplayer-plugin-ambilight.d.ts').toString()
|
||||
const current = readMember(contract.archives.get('1.1.0'), 'package/types/artplayer-plugin-ambilight.d.ts').toString()
|
||||
assert.match(old, /export = artplayerPluginAmbilight/)
|
||||
assert.match(current, /export default artplayerPluginAmbilight/)
|
||||
for (const source of [old, current]) {
|
||||
assert.match(source, /\(option: Option\)/)
|
||||
assert.match(source, /zIndex\??: number/)
|
||||
}
|
||||
assert.match(current, /blur\?: string/)
|
||||
assert.match(old, /blur: string/)
|
||||
})
|
||||
|
||||
test('Ambilight 1.1.0 native ESM import exposes the default factory', async () => {
|
||||
const source = readMember(contract.archives.get('1.1.0'), 'package/dist/artplayer-plugin-ambilight.mjs').toString()
|
||||
const namespace = await import(`data:text/javascript,${encodeURIComponent(source)}`)
|
||||
assert.deepEqual(Object.keys(namespace), ['default'])
|
||||
assert.equal(typeof namespace.default, 'function')
|
||||
assert.equal(typeof namespace.default(), 'function')
|
||||
})
|
||||
|
||||
for (const implementation of await ambilightHistorical()) {
|
||||
test(`Ambilight ${implementation.name}: defaults, option-read timing, styling, grid sampling and public returns`, async () => {
|
||||
const env = await ambilightEnvironment(implementation)
|
||||
const option = { opacity: 0.1, zIndex: -99 }
|
||||
const attach = env.factory(option)
|
||||
option.opacity = 0.7
|
||||
const result = attach(env.art)
|
||||
const element = env.parent.children[0]
|
||||
assert.equal(element.className, 'artplayer-plugin-ambilight')
|
||||
assert.equal(element.children.length, 9)
|
||||
assert.equal(element.style.zIndex, 9, 'Historical zIndex option is ignored')
|
||||
assert.equal(element.children[0].style.opacity, 0.7)
|
||||
assert.equal(element.children[0].style.filter, 'blur(50px)')
|
||||
assert.equal(element.children[0].style.transition, 'background-color 0.3s ease')
|
||||
assert.deepEqual(Object.keys(result), ['name', 'start', 'stop'])
|
||||
assert.equal(result.name, 'artplayerPluginAmbilight')
|
||||
assert.equal(result.start(), undefined)
|
||||
assert.equal(env.frames.size, 1)
|
||||
assert.equal(env.draws.length, 0)
|
||||
env.art.playing = true
|
||||
env.frame(100)
|
||||
assert.equal(env.draws.length, 9)
|
||||
assert.deepEqual([...env.draws[0].slice(1)], [0, 0, 100, 50, 0, 0, 1, 1])
|
||||
assert.deepEqual([...env.draws[8].slice(1)], [200, 100, 100, 50, 0, 0, 1, 1])
|
||||
assert.equal(element.children[8].style.backgroundColor, 'rgb(9, 20, 30)')
|
||||
assert.equal(result.stop(), undefined)
|
||||
assert.equal(env.frames.size, 0)
|
||||
assert.equal(typeof env.factory(), 'function', 'Runtime accepts omitted options despite the old declaration')
|
||||
})
|
||||
}
|
||||
@@ -44,7 +44,7 @@ function fixture(t) {
|
||||
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 === 'published-baseline').length, 9)
|
||||
assert.equal(actual.policy.versions.filter(version => version.kind === 'workspace-baseline').length, 22)
|
||||
assert.equal(actual.policy.cases.length, 10)
|
||||
const markdown = renderContracts(actual)
|
||||
|
||||
+7
-2
@@ -1458,11 +1458,16 @@
|
||||
"dependsOn": [
|
||||
"BASE-05"
|
||||
],
|
||||
"status": "todo",
|
||||
"status": "done",
|
||||
"risk": "M",
|
||||
"deliverable": "blur/opacity/frequency/duration、start/stop 与挂载样式",
|
||||
"acceptance": "源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结",
|
||||
"evidence": []
|
||||
"evidence": [
|
||||
"changes/2026-09-12-PKG-AMBILIGHT-01-contract.md",
|
||||
"baselines/ambilight-contract.md",
|
||||
"baselines/ambilight-release.json",
|
||||
"baselines/ambilight-contract-validation.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "PKG-AMBILIGHT-02",
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import vm from 'node:vm'
|
||||
import { transform } from 'esbuild'
|
||||
import { verifyAmbilightContract } from '../../refactor/scripts/ambilight-contract.mjs'
|
||||
import { readMember } from '../../refactor/scripts/releases.mjs'
|
||||
|
||||
export async function ambilightHistorical() {
|
||||
const contract = await verifyAmbilightContract()
|
||||
return [
|
||||
...[contract.baseline.release, ...contract.baseline.previous].map(release => ({ name: `published-${release.version}`, source: readMember(contract.archives.get(release.version), `package/${release.manifest.main.replace(/^\.\//, '')}`).toString(), format: 'artifact' })),
|
||||
{ name: 'frozen-workspace', source: contract.sources.get('packages/artplayer-plugin-ambilight/src/index.js'), format: 'source' },
|
||||
]
|
||||
}
|
||||
|
||||
export async function ambilightEnvironment(implementation, settings = {}) {
|
||||
let time = 0
|
||||
let nextFrame = settings.firstFrame ?? 1
|
||||
const frames = new Map()
|
||||
const draws = []
|
||||
const cancelled = []
|
||||
const nodes = []
|
||||
const listeners = new Map()
|
||||
const state = { drawError: null, readError: null, draws, frames, cancelled, nodes, time: value => time = value }
|
||||
const context = settings.noContext
|
||||
? null
|
||||
: {
|
||||
drawImage(...args) {
|
||||
draws.push(args)
|
||||
settings.onDraw?.()
|
||||
if (state.drawError)
|
||||
throw state.drawError
|
||||
},
|
||||
getImageData() {
|
||||
if (state.readError)
|
||||
throw state.readError
|
||||
return { data: [draws.length, 20, 30, 255] }
|
||||
},
|
||||
}
|
||||
function node(tag) {
|
||||
let markup = ''
|
||||
const value = {
|
||||
tag,
|
||||
children: [],
|
||||
parentNode: null,
|
||||
style: {},
|
||||
className: '',
|
||||
width: 300,
|
||||
height: 150,
|
||||
get innerHTML() { return markup },
|
||||
set innerHTML(value) {
|
||||
markup = value
|
||||
this.children = Array.from({ length: (value.match(/<div>/g) || []).length }, () => node('div'))
|
||||
},
|
||||
insertBefore(child, before) {
|
||||
assert.equal(before.parentNode, this)
|
||||
const index = this.children.indexOf(before)
|
||||
this.children.splice(index, 0, child)
|
||||
child.parentNode = this
|
||||
},
|
||||
removeChild(child) {
|
||||
assert.equal(child.parentNode, this)
|
||||
this.children.splice(this.children.indexOf(child), 1)
|
||||
child.parentNode = null
|
||||
return child
|
||||
},
|
||||
remove() { this.parentNode?.removeChild(this) },
|
||||
getContext: () => context,
|
||||
}
|
||||
nodes.push(value)
|
||||
return value
|
||||
}
|
||||
const parent = node('div')
|
||||
const video = Object.assign(node('video'), { videoWidth: 300, videoHeight: 150 })
|
||||
parent.children.push(video)
|
||||
video.parentNode = parent
|
||||
const art = {
|
||||
template: { $video: video },
|
||||
playing: false,
|
||||
isDestroy: false,
|
||||
constructor: { utils: { createElement: node, addClass: (element, name) => element.className = name, setStyles: (element, styles) => Object.assign(element.style, styles) } },
|
||||
on(name, callback) {
|
||||
if (!listeners.has(name))
|
||||
listeners.set(name, new Set())
|
||||
listeners.get(name).add(callback)
|
||||
return art
|
||||
},
|
||||
off(name, callback) {
|
||||
listeners.get(name)?.delete(callback)
|
||||
return art
|
||||
},
|
||||
emit(name, ...args) { for (const callback of [...(listeners.get(name) || [])]) callback(...args) },
|
||||
destroy() {
|
||||
art.isDestroy = true
|
||||
art.emit('destroy')
|
||||
},
|
||||
}
|
||||
const module = { exports: {} }
|
||||
const source = implementation.format === 'source' ? (await transform(implementation.source, { loader: 'js', format: 'cjs', target: 'es2020' })).code : implementation.source
|
||||
vm.runInNewContext(source, {
|
||||
module,
|
||||
exports: module.exports,
|
||||
window: {},
|
||||
performance: { now: () => time },
|
||||
requestAnimationFrame(callback) {
|
||||
const id = nextFrame++
|
||||
frames.set(id, callback)
|
||||
return id
|
||||
},
|
||||
cancelAnimationFrame(id) {
|
||||
cancelled.push(id)
|
||||
frames.delete(id)
|
||||
},
|
||||
}, { timeout: 5000 })
|
||||
const factory = module.exports.default || module.exports
|
||||
return {
|
||||
...state,
|
||||
art,
|
||||
parent,
|
||||
video,
|
||||
listeners,
|
||||
context,
|
||||
factory,
|
||||
frame(now) {
|
||||
time = now
|
||||
const pending = [...frames]
|
||||
frames.clear()
|
||||
for (const [, callback] of pending) callback(now)
|
||||
},
|
||||
setDrawError(error) { state.drawError = error },
|
||||
setReadError(error) { state.readError = error },
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user