test(danmuku): [PKG-DANMUKU-01] freeze public contracts and release provenance

This commit is contained in:
Harvey Zhao committed 2026-09-13 17:38:25 +08:00
1 parent 7d44bce06f
commit d81b881085
11 files changed
+4229 -6

No files matched your search

@@ -0,0 +1,112 @@
{
"schemaVersion": 1,
"task": "PKG-DANMUKU-01",
"capturedAt": "2026-09-13T09:35:02.032Z",
"sourceCommit": "b0cfbfe3a09a84a295a257e45e4577847588d1cb",
"environment": {
"node": "v24.21.0",
"platform": "win32",
"architecture": "x64",
"executable": "refactor/.cache/toolchains/node-v24.21.0-win-x64/node.exe",
"yarnPolicy": "1.22.22; no install or lockfile changes"
},
"inputs": {
"refactor/baselines/danmuku-release.json": {
"sha256LF": "82a0ab2e87f108cbb1d09ce1b6647cfefc896d974da1cb60b2668fded18a4b2f"
},
"refactor/scripts/danmuku-contract.mjs": {
"sha256LF": "8ca0996605cfbcd732030708bd67ecedff5018d2b7436fa7247de7bbb39d7573"
},
"refactor/scripts/danmuku-contract.test.mjs": {
"sha256LF": "37836083e849cd8b7d5d47a4e9d952949d3b28bd885225e3197fb6715febf44e"
},
"refactor/baselines/danmuku-contract.md": {
"sha256LF": "ff51611322cfbb5ad744a37eebc555b74e4d9853b59c91bf13547889fa976bb0"
},
"refactor/changes/2026-09-13-PKG-DANMUKU-01-contracts.md": {
"sha256LF": "fb7355058ee1b13688c2e8c713118f6b802aa086209cb8a6e236694f2764abd9"
}
},
"registry": {
"url": "https://registry.npmjs.org/artplayer-plugin-danmuku",
"status": 200,
"catalogStable": 81,
"archivedReleases": 15,
"unverifiedCatalogArchives": 66,
"bodySha256": "6fccd0198b2322827b3271a807b60f73a3e4d6f46aeb5494a86aade409caf1d6"
},
"evidence": [
{
"command": "node refactor/scripts/danmuku-contract.mjs",
"log": "refactor/.cache/danmuku01/verify.log",
"exitCode": 0,
"logSha256": "655d90ee59df53dd2136c79f14886bd5b281ea142bc0032e4b7e52b13ee7f5bd"
},
{
"command": "node --test refactor/scripts/danmuku-contract.test.mjs",
"log": "refactor/.cache/danmuku01/contract-test.log",
"exitCode": 0,
"passed": 44,
"failed": 0,
"skipped": 0,
"logSha256": "492c7313c98f197a8241e370854860a0980ada771d1c86b26e5e2303920fb6a0"
},
{
"command": "node node_modules/eslint/bin/eslint.js refactor/scripts/danmuku-contract.mjs refactor/scripts/danmuku-contract.test.mjs",
"log": "refactor/.cache/danmuku01/lint.log",
"exitCode": 0,
"logSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
],
"coverage": {
"immutableSourceFiles": 28,
"commonJsModulesExecuted": 28,
"manifestEsmModulesExecuted": 3,
"esmMethod": "Dynamic data URL imports of the actual archive bytes, not package-resolution tests",
"runtimeFixtures": [
"frozen-Git-source with controlled Worker/CSS loader",
"real npm 5.3.0 main",
"real npm 5.3.0 legacy"
],
"runtimeCasesPerFixture": 4,
"parser": "Frozen actual Bilibili parser exercised through URL adapter with controlled fetch and absent-Worker fallback",
"validator": {
"name": "artplayer",
"version": "5.4.0",
"tarballSha256": "42b269a66b9658d53f9a92572bb36d656ea9af56254a387b3a69c7ddea43979e",
"source": "Actual frozen published core bundle static validator, not a replacement validator"
}
},
"reproduced": [
"Actual CJS namespace.default/direct function changes and 12 enumerable writable static icons from 5.1.0.",
"Latest and frozen source registration synchronous; enumerable facade/getter keys and defaults stable.",
"emit returns Promise; chain methods and load resolve the internal Danmuku rather than the facade described by Result.",
"Initial Promise danmuku is rejected by published core validator; load(Promise) works in the same realm.",
"time:0 becomes currentTime+0.5 and input fields are mutated; public emit bypasses beforeEmit.",
"load() replacement, load(target) append, filter this and loaded queue reference identity.",
"Function-only filter config updates are ignored by JSON comparison; load rejection emits and rejects the same error.",
"Empty-array synchronous show/config/reset/loaded ordering and video play/pause state hooks.",
"Settings beforeEmit callback receives trimmed input and option this; border/time reset and lock timer observed.",
"destroy leaves registered resize handlers and a started settings lock timer.",
"Bilibili fallback parser mode/entity/metadata behavior and unpadded hexadecimal color."
],
"observedNotReproduced": [
"id/icons declaration omissions; Slider value/hide and heatmap points declaration drift",
"Bilibili async Promise executor/fetch rejection and unrevoked URL",
"worker onmessage replacement and Date.now ids; late callback/duplicate-start cases",
"heatmap zero sampling, option.points not consumed, repeated SVG ids",
"12 SVG source authorization not separately established"
],
"limitations": [
"No browser playback/layout/Worker/CSP/device validation",
"No actual Bilibili request, media fetch, load/performance/long-running memory validation",
"No package-installed NodeNext/Node10/TypeScript or AMD/global matrix",
"Older selected archives have module-shape coverage, not full old-major runtime compatibility",
"Historical defect assertions are observations and must not become candidate normal expectations",
"Registry signatures and publisher authorization not independently verified"
],
"sourceChanged": false,
"browserValidated": false,
"installedConsumerValidated": false,
"releaseReady": false
}
+196
View File
@@ -0,0 +1,196 @@
# Danmuku 公共契约基线
PKG-DANMUKU-01;冻结提交 `b0cfbfe3a09a84a295a257e45e4577847588d1cb`。
实际输入及逐文件 SHA 见 [danmuku-release.json](danmuku-release.json),执行结果见
[danmuku-contract-validation.json](danmuku-contract-validation.json)。本次不改生产代码。
## 发布依据与覆盖范围
2026-09-13 直接读取 [npm registry](https://registry.npmjs.org/artplayer-plugin-danmuku),
HTTP 200,目录有 81 个稳定版,latest 为 5.3.0。冻结全部版本的发布时间、tarball、
integrity、gitHead;实际下载并校验全部 13 个 5.x 版,另加前两代末版 4.4.11、3.5.31,
共 15 个归档。其余 66 个版本只有目录证据,不能说其运行时已验证或全部兼容。
这是代表性历史覆盖,不是新最低支持版本,也不把旧 major 的产品行为自动带入候选。
当前包全部跟踪文件、12 个 SVG、三个 dist、声明、README、可运行 demo 和 VitePress
弹幕文档共 28 个输入冻结。最新归档中的 README、manifest、声明及三个 dist 与冻结
Git 文件逐字节相同;源码行为另通过冻结 Git 模块受控执行对照,不把可读源码等同于
历史打包过程已可重复构建。
| 归档版本 | registry gitHead 的插件版本 | 同提交核心版本 | 说明 |
| --- | --- | --- | --- |
| 3.5.31 | 3.5.31 | 3.5.31 | 历史 major 对照 |
| 4.4.11 | 4.4.10 | 4.5.8 | manifest 版本不一致 |
| 5.0.0 / 5.0.1 | 5.0.0 / 5.0.1 | 5.0.0 / 5.0.5 | 分别冻结 |
| 5.1.0 / 5.1.1 | 5.1.0 / 5.1.0 | 5.1.2 / 5.1.5 | 5.1.1 版本不一致 |
| 5.1.2 / 5.1.3 / 5.1.4 | 同归档版本 | 5.1.6 | 分别冻结 |
| 5.1.5 / 5.1.6 | 5.1.4 / 5.1.6 | 5.2.2 | 5.1.5 版本不一致 |
| 5.1.7 | 缺 gitHead | 未知 | 不推测来源提交 |
| 5.1.8 | 5.1.7 | 5.2.4 | manifest 版本不一致 |
| 5.2.0 | 5.1.9 | 5.3.0-beta.3 | 核心是提交关联预发布号 |
| 5.3.0 | 5.2.1 | 5.3.1 | 不宣称核心 5.3.1 是 npm 稳定版 |
真实 manifest 是归档身份依据;提交关联不是最低核心支持声明。归档与关联 Git 路径
各成员的匹配/不匹配均在 JSON 中保存。registry 缺失的提交信息保持缺失。
未独立验证 npm 签名及发布者授权。5.1.7 归档的目录头没有尾随斜线;专属 verifier
用 tar 条目类型区分目录与普通文件,拒绝重复、越界和非普通文件,不改共享解包函数。
## 分发与静态属性
| 版本 | 实际 CommonJS | manifest 分发入口 | 静态 icons |
| --- | --- | --- | --- |
| 3.5.31 | 直接函数 | main;未声明 types/module/legacy | 无 |
| 4.4.11 | 对象.default | main/types | 无 |
| 5.0.0–5.0.1 | 对象.default | main/types/legacy | 无 |
| 5.1.0–5.1.6、5.1.8 | 对象.default | main/types/legacy;无 module/exports | 12 项 |
| 5.1.7 | 对象.default | module 是 `.esm.js`;平铺 import/require/default exports | 12 项 |
| 5.2.0 | 对象.default | `.mjs`、root/legacy 条件 exports;types 条件在后 | 12 项 |
| 5.3.0 | 直接函数 | `.mjs`、root/legacy 条件 exports;types 条件在前 | 12 项 |
main/legacy 共 28 个实际模块已在 Node VM 执行;三个 manifest ESM 的实际字节通过
data URL 动态 import 执行。此项只证明模块代码能求值,不等于在消费者目录通过
Node 包解析;真实安装、NodeNext/Node10、AMD/global/script、编辑器仍由 06/09 验证。
`artplayerPluginDanmuku.icons` 是可写、可配置、可枚举的对象值,顺序为:
`$on/$off/$config/$style/$mode_0_off/$mode_0_on/$mode_1_off/$mode_1_on/`
`$mode_2_off/$mode_2_on/$check_on/$check_off`。静态 icons 不是 getter;设置模板使用
模块内 SVG 字符串,没有读取工厂 icons 的后续改写。这一读码事实尚未验证热替换用法。
## 最新版工厂与方法
`factory(option)(art)` 同步返回门面;字段按 `name/emit/load/config/hide/show/reset/`
`mount/option/isHide/isStop` 顺序枚举,name 为 `artplayerPluginDanmuku`。
后三项是可枚举、可配置、没有 setter 的 getter,读取内部当前值。方法已 bind,可抽取调用。
工厂 option 没有默认对象;`{ danmuku: [] }` 是基准合法调用。初始化不等待异步载入完成。
| API | 最新真实行为 | 声明/兼容注意 |
| --- | --- | --- |
| `emit(danmu)` | async;验证并修改传入对象,filter 通过后同步入队;Promise 最终返回内部 Danmuku | 旧声明错误写同步 Result;不自动改旧根类型 |
| `load()` | 读当前 option.danmuku,reset 后清空队列再逐条 emit | Promise 返回内部对象;不是门面 |
| `load(target)` | 追加到现有队列,不自动改 option.danmuku | 包括数组、函数、同 realm Promise、XML URL |
| `config(partial)` | 合并默认值、当前值与 partial,验证、钳位;变化比较用 JSON.stringify,单独更换函数会被忽略 | 旧类型要求完整 Option,实际 partial 有效 |
| `hide/show` | 更新 isHide、层 opacity、option.visible,并发事件 | 返回内部对象;重复调用也发事件 |
| `reset()` | 全队列回到 wait,回收已分配 DOM,再发 reset | 返回内部对象,不清空队列 |
| `mount(target)` | selector 或元素;移动设置输入框并 reset 设置 UI | 返回 undefined;声明允许省略,但运行时无目标会抛错 |
| `option` | 当前合并后的对象;config 可能更换其引用 | 声明可写,实际仅 getter |
| `isHide/isStop` | 初始 false;show/hide 与 video start/stop 更新 | 不应解释为 video.paused 或 layer DOM 是否存在 |
内部 Danmuku 没有公开门面的 `name/mount`,但历史链式返回会将其暴露给调用方。
后续整理必须先评估返回身份,不能为了匹配旧错误声明静默返回门面。
`load(undefined)` 清空;其他显式 falsy 值通过 `target = argument || option.danmuku`
回退但不走清空分支;这只是读码事实,不把未声明参数纳入必须支持的接口。
## 全部配置与弹幕项
| 配置 | 默认值/范围 |
| --- | --- |
| danmuku | `[]`;声明含数组、URL、返回 Promise 的函数、Promise;初始 Promise 被实际 validator 拒绝,load(Promise) 可用 |
| speed / margin / opacity | `5`,钳位 1–10;`[10,'25%']`;`1`,钳位 0–1 |
| color / mode / modes | `#FFFFFF`;`0`,钳位 0–2;`[0,1,2]`,显示模式由 dataset/CSS 控制 |
| fontSize | `25`;数值或百分比,使用时钳位 12 到播放器高度 |
| antiOverlap / synchronousPlayback | `true` / `false` |
| mount / heatmap / width / points | controlsCenter / false / 512 / [] |
| filter / beforeEmit / beforeVisible | 默认均返回 true;filter 同步,另外两个支持异步 |
| visible / emitter | true / true |
| maxLength / lockTime / theme | 200(1–1000)/ 5(1–60)/ dark |
| OPACITY / FONT_SIZE / MARGIN / SPEED / COLOR | `{}` / `{}` / `{}` / `{}` / `[]`,设置组件再补默认滑块与颜色 |
Danmu 声明成员为 `text/mode/color/time/border/style`;runtime 还验证可选 string `id`,
并传播额外字段。mode 只入队 0/1/2,空白 text 与 filter 拒绝项忽略。time 不存在或为 0
时写为 currentTime + 0.5;负数钳到 0。入队时额外生成 `$state/$index/$ref/$restTime/`
`$lastStartTime`,loaded/visible 事件会暴露这些字段和对象引用。
直接 emit/load 调用 filter,以当前 option 为 this,不经过 beforeEmit。设置 UI 的
发送按钮/Enter 先 trim 输入,防锁定/重复提交,await beforeEmit;只有严格 true 继续,
加 border、删除 time、再调用 emit,清输入并锁定。beforeVisible 在准备显示前调用,
也以 option 为 this。三者不能合并成一个全局异步过滤器。
最新声明有六个命名类型 `Mode/Danmuku/Slider/Danmu/Option/Result`。
4.4.11/5.0.x 旧声明中 Danmuku 表示结果,而 5.1.x 起表示输入,旧配置还出现
useWorker/minWidth/maxWidth;不能将较早声明与最新声明拼成未经评估的根类型。
06 按已批准的 [统一类型规则](../type-compatibility-policy.md) 处理:最新 npm 根声明的
合法完整工厂赋值、Parameters/ReturnType、NodeNext 历史模块形态都需安装消费者证据;
准确异步类型可用独立 runtime 入口,较早冲突写迁移说明。本次没有改类型或声称编译矩阵通过。
## 事件、DOM、设置与热力图
| 事件 | 参数/时机 |
| --- | --- |
| artplayerPluginDanmuku:config | 当前 option 引用;show/hide 在它之前发生 |
| artplayerPluginDanmuku:loaded | 完成当前 load 后的实际 queue 引用;空数组初始化可在工厂返回前同步发生 |
| artplayerPluginDanmuku:error | load 捕获的原错误,然后再次抛出;直接 emit 不自动发这个事件 |
| artplayerPluginDanmuku:visible | worker 返回位置且允许显示后,实际内部 danmu |
| artplayerPluginDanmuku:start / stop | 无参数,video:play/playing / pause/waiting 对应触发 |
| artplayerPluginDanmuku:show / hide / reset | 无参数,对应方法更新状态之后 |
| artplayerPluginDanmuku:destroy | stop、worker terminate 和解除部分监听之后 |
| artplayerPluginDanmuku:points | 消费方发入的热力图数据事件,插件不主动 emit |
这些是十个输出事件和一个输入事件;不新增虚构的 `:emit` 事件。
[] 初始化的已复现顺序为 show → config → reset → loaded;非空/异步输入顺序和错误
重入仍需 02 扩展。Setting 还订阅 resize/fullscreen/fullscreenWeb/document:pointermove/
document:pointerup/show/hide。热力图订阅 ready/resize/loaded/timeupdate/setBar/points。
设置入口是 `new Setting` 创建的独立 `.artplayer-plugin-danmuku` 输入框和弹出面板,
不是核心 setting registry 的单个项目。内部使用 `.apd-*` 类、mode/color/state/id 数据,
外部挂载可在 fullscreen/fullscreenWeb 时移回控制栏;默认挂载在宽度低于 width 时移到
播放器底部。默认不透明度 0–100、字号 12–120;MARGIN 四档 `[10,'75%']/[10,'50%']/`
`[10,'25%']/[10,10]`;SPEED 五档 10/7.5/5/2.5/1。滑块读取 step.hide,而声明写 show;
MARGIN step.value 是数组,声明只有 number|string。
设置 style 在模块求值时插入 id=`artplayer-plugin-danmuku` 的全局 style;完整类名、
选择器、SVG 和样式源码已整体哈希冻结。视觉、CSS 覆盖、重复 style 与外部节点回收
未在本次 mock 中模拟,交 02/05/08 的真实页面验证。
heatmap=true/object 创建 name=`heatmap`、position=`top` 的控件。源码按 `[x,y]` 二元组
使用 points 事件数据;`option.points` 当前只存储,未被 heatmap update 消费,声明却为
`{time,value}[]`。初始化开启 heatmap 后 config 开关不会自动新增/移除控件。上面两项为
读码事实,尚未浏览器复现。窄尺寸下默认 sampling=floor(width/100) 可为 0;需 02 用
独立超时保护测试确认循环风险,不在 UI 主线程盲跑。
## Bilibili XML 与 worker
string 输入调用 fetch(url) → response.text(),没有显式 HTTP 状态判断。XML 用正则读
`<d p="...">...</d>`,要求至少八个逗号分隔字段;模式 1/2/3→0、4→2、5→1,其他→0;
trim 后按 quot/apos/lt/gt/amp 顺序解实体。输出 text/time/mode/fontSize/color/timestamp/
pool/userID/rowID;color 用十六进制字符串,不补齐六位,数字异常也不额外过滤。
优先生成 Blob worker,message 含 `{xml,id}`,结果 `{danmus,id}`,成功后 terminate;
Worker 构建同步失败时 console.error 并退回相同 parser。正则和 fallback 已执行;真实
网络、Worker 消息和 CSP/Blob 能力未验证。源码用 async Promise executor,fetch/text
拒绝发生在 try 之外,且未 revokeObjectURL;空 XML/worker error/销毁竞态是待 02 复现项。
调度 worker 接口 `{type:'getDanmuTop',id,target,visibles,antiOverlap,clientWidth,clientHeight,marginBottom,marginTop}`
→ `{result,id}`,当前 Date.now 作为 id 并覆盖 onmessage;并发/旧回复仍待测试。
## 已复现差异与后续责任
| 编号 | 证据级别 | 内容 | 后续任务 |
| --- | --- | --- | --- |
| DANMUKU-TYPE-01 | 运行时复现 + 逐字声明 | emit Promise、内部返回身份、getter、mount 省略、初始 Promise 和声明不一致 | 03/06 |
| DANMUKU-INPUT-01 | 冻结源码和真实 5.3 main/legacy 复现 | time:0 被改为 current+0.5;输入对象被修改 | 02/03,分别评估修正和保留 |
| DANMUKU-CONFIG-01 | 同上 | 单独 config({filter:新函数}) 被 JSON.stringify 比较判为未变化 | 02/03 |
| DANMUKU-CLEANUP-01 | 同上 | resize 注册 this.resize,销毁 off(this.reset);设置锁定 timer 未清 | 02/05 |
| DANMUKU-TYPE-02 | 读码/声明观察 | id/icons 缺失、Slider value/hide、points 类型与使用漂移、config partial | 05/06 |
| DANMUKU-ASYNC-01 | 待复现 | Bilibili fetch/text/worker 错误、load 乱序、beforeVisible/worker 晚回调、重复 start | 02/03/04/05 |
| DANMUKU-HEATMAP-01 | 待复现 | 小于 100px 时 sampling=0、事件 tuple/option.points、重复实例 SVG id | 02/05 |
| DANMUKU-SOURCE-01 | 归档与 Git 核验 | gitHead 版本错位、5.1.7 无 gitHead、12 SVG 无单独来源授权证据 | 09 |
历史缺陷断言仅冻结观察,不要求候选保留。修复必须有独立候选正常断言与明确迁移影响。
包 README 只有简介/demo/license,主要公开说明来自冻结 VitePress 文档和 demo。
demo 使用本地 video.mp4/danmuku.xml,适合作为后续 8082/Monaco/Run 的本地输入。
本次不证明实际播放、密集弹幕碰撞/时钟、mask/全屏/PiP、真实 WebKit/设备或 npm 发布可行。
## 重放
在固定 Node 24.21.0 / Yarn 1.22.22 与已安装根依赖下运行:
```text
node refactor/scripts/danmuku-contract.mjs
node --test refactor/scripts/danmuku-contract.test.mjs
node node_modules/eslint/bin/eslint.js refactor/scripts/danmuku-contract.mjs refactor/scripts/danmuku-contract.test.mjs
```
缺缓存时 verifier 仅从冻结 npm URL 下载并验证 SHA512/SHA256;不执行 registry 包脚本。
完整目录可再次读取 `https://registry.npmjs.org/artplayer-plugin-danmuku`,但返回的新目录
不应覆盖本次不可变基线。原始 registry body 的 SHA 保存在 JSON,原始响应在专属 cache。
测试使用真实发布核心 5.4.0 的 validator、受控 DOM/计时器/Worker,仅源码编译在内存中
完成,不生成或修改 dist/docs。仅访问 npm 元数据及归档,未请求媒体或 Bilibili 资源。
File diff suppressed because it is too large. Load diff