From 1d705b3056b3f1f8e975b773f2c22506a25ffdd6 Mon Sep 17 00:00:00 2001 From: Harvey Zhao Date: Thu, 10 Sep 2026 18:36:02 +0800 Subject: [PATCH] test(refactor): [BASE-01] freeze published core and chapter baselines --- .gitignore | 3 +- refactor/README.md | 3 +- refactor/baselines/README.md | 30 ++ refactor/baselines/releases.json | 301 ++++++++++++++++++ .../2026-09-10-BASE-01-published-baseline.md | 23 ++ refactor/plan.md | 5 +- refactor/progress.md | 8 +- refactor/scripts/releases.mjs | 70 ++++ refactor/scripts/releases.test.mjs | 11 + refactor/tasks.json | 7 +- 10 files changed, 454 insertions(+), 7 deletions(-) create mode 100644 refactor/baselines/README.md create mode 100644 refactor/baselines/releases.json create mode 100644 refactor/changes/2026-09-10-BASE-01-published-baseline.md create mode 100644 refactor/scripts/releases.mjs create mode 100644 refactor/scripts/releases.test.mjs diff --git a/.gitignore b/.gitignore index c66469c85..8012c2201 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ node_modules package-lock.json lerna-debug.log -yarn.lock \ No newline at end of file +yarn.lock +refactor/.cache/ diff --git a/refactor/README.md b/refactor/README.md index 216762af5..a58574779 100644 --- a/refactor/README.md +++ b/refactor/README.md @@ -8,7 +8,7 @@ - 工作分支:`codex/compatible-modernization`。 - 起点:`master`,`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。 - 清点范围:22 个 workspace 包,包括核心、16 个插件、2 个 proxy、2 个工具、1 个文档站;另含 React/Vue 示例、浏览器 demo、构建和发布脚本。 -- 当前完成的是计划与文档基础设施,生产代码重构尚未开始。提交以任务 ID 对应的 Git 历史为准;本地提交不代表已推送或发布。 +- 已进入实施,真实发布基线及当前执行结果见 progress.md;生产迁移与测试基础设施进度分别记录。提交以任务 ID 对应的 Git 历史为准;本地提交不代表已推送或发布。 - 本目录位于仓库根目录,避免被 `docs/` 的站点部署流程直接作为产品文档发布。 ## 从哪里开始 @@ -34,6 +34,7 @@ | [变更记录模板](changes/TEMPLATE.md) | 每次行为/类型/结构变化的详细记录 | | [任务数据](tasks.json) | 任务状态和依赖的唯一数据来源 | | [包基线](package-inventory.json) | 包版本、入口、声明、源码清单及对应 demo 的初始快照 | +| [发布基线与重跑说明](baselines/README.md) | 固定 npm tarball、完整性及已知源码/发布差异 | ## 维护规则 diff --git a/refactor/baselines/README.md b/refactor/baselines/README.md new file mode 100644 index 000000000..30d5fa1f6 --- /dev/null +++ b/refactor/baselines/README.md @@ -0,0 +1,30 @@ +# 已发布基线 + +BASE-01 于 2026-09-10 固定核心与 chapter 的 npm 发布内容,数据在 [releases.json](releases.json)。这不是全生态兼容通过报告。 + +| 包 | 工作区版本 | 已固定发布基线 | 后续目标 | +| --- | --- | --- | --- | +| artplayer | 5.4.1 | 5.4.0 | 6.0.0 | +| artplayer-plugin-chapter | 1.1.0 | 1.1.0 | 2.0.0 | + +查询 registry.npmjs.org 时 artplayer/5.4.1 返回 404,latest 为 5.4.0。工作区源码基线仍是 40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f;不能把两者混同,也不将 npm 观测永久视为最新状态。发布前重新检查目标版本占用。 + +两个发布版本的 registry gitHead 均为 daf133b22630b4a0eecfa3336bbddab0e9119d96,但该提交的核心/chapter manifest 是 5.3.1/1.0.3。Git tag 5.4.0 对应 65e51751cd0c0db0bd00b5d7deb925d52cfaf3ef,其 manifest 版本为 5.4.0/1.1.0。来源声明与版本匹配的 tag 分别登记,未证明任一源码可逐字重建发布 tarball;消费者行为的权威参照是冻结的 tarball。 + +## 已知支持边界 + +- 先以核心 5.4.0、chapter 1.1.0 建立首组发布对照,不等同把历史支持范围缩小到这两个版本。 +- 两个发布 manifest 的 browserslist 都是 last 1 Chrome version;没有固定浏览器版本或 engines/peerDependencies 的最低核心声明。不能据此虚构 Chrome 最低版本或断言 chapter 支持任意核心。 +- 当前构建脚本目标为现代 es2020、legacy es2015;它是源码构建目标,不是已验证的设备/codec 能力清单。BASE-08 将支持矩阵落实到版本和样本。 +- 当前文档说明 5.1.0 起核心只内置简体中文/英文,其他语言通过资源导入;BASE-05 必须保留相关子路径消费。 +- BASE-02/03/04/05 继续捕获运行时、事件、DOM、类型和旧消费契约。其余 20 包的发布基线在 JSON 中明确标为待核实,各包契约任务负责补齐。此阶段没有真实播放或旧核心/插件兼容结论。 + +## 重跑 + +运行 `node refactor/scripts/releases.mjs`。需要 Node 20+、PATH 中的 tar(本机 Windows bsdtar 3.8.8);首次从固定 npm URL 下载,缓存于忽略目录 refactor/.cache/releases。已缓存时离线验证 SHA-512 SRI、SHA-256、逐文件哈希、manifest 名称/版本和声明的 main/module/types/legacy 文件存在性。核心 41 文件,chapter 6 文件。 + +运行 `node --test refactor/scripts/releases.test.mjs` 验证损坏内容和不一致哈希会失败。缺 tar、网络失败或内容不一致明确退出非零,不自动更新基线或降级忽略。 + +脚本不安装或执行发布包,不修改生成产物,不把整个 tarball 解压到工作区。归档成员通过 tar 读到内存;后续浏览器服务可复用 ensureArchive/readMember。仅缓存写入,JSON 与说明为持久来源记录;没有新增生产依赖。 + +修改冻结基线必须通过独立任务和变更记录;不能直接覆盖哈希来消除回归。真实样本或报告一旦生成,绑定包版本/完整性和测试 ID。 diff --git a/refactor/baselines/releases.json b/refactor/baselines/releases.json new file mode 100644 index 000000000..75f7b89d4 --- /dev/null +++ b/refactor/baselines/releases.json @@ -0,0 +1,301 @@ +{ + "schemaVersion": 1, + "capturedAt": "2026-09-10T10:34:31.411Z", + "workspaceBaselineCommit": "40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f", + "captureStartCommit": "7a5354e356b7d6af5b3daf9280699e2e90e05a7d", + "registry": "https://registry.npmjs.org", + "observations": [ + { + "name": "artplayer", + "workspaceVersion": "5.4.1", + "requestedMetadataStatus": 404, + "observedLatestStable": "5.4.0", + "impact": "Use published 5.4.0 for consumers, preserve workspace 5.4.1 as separate source baseline; target remains 6.0.0." + }, + { + "issue": "Registry gitHead is stale for both releases", + "impact": "Keep registry claims and matching-version tag separately; do not assert tarball is reproduced by either source commit." + } + ], + "releases": [ + { + "name": "artplayer", + "version": "5.4.0", + "metadataUrl": "https://registry.npmjs.org/artplayer/5.4.0", + "tarball": "https://registry.npmjs.org/artplayer/-/artplayer-5.4.0.tgz", + "integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==", + "sha256": "42b269a66b9658d53f9a92572bb36d656ea9af56254a387b3a69c7ddea43979e", + "archiveBytes": 133746, + "manifest": { + "name": "artplayer", + "version": "5.4.0", + "description": "ArtPlayer.js is a modern and full featured HTML5 video player", + "author": "Harvey Zhao ", + "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.d.ts", + "import": "./dist/artplayer.mjs", + "require": "./dist/artplayer.js" + }, + "./legacy": { + "types": "./types/artplayer.d.ts", + "import": "./dist/artplayer.legacy.js", + "require": "./dist/artplayer.legacy.js" + }, + "./i18n/*": { + "types": "./types/i18n.d.ts", + "import": "./dist/i18n/*.mjs", + "require": "./dist/i18n/*.js" + } + }, + "main": "./dist/artplayer.js", + "module": "./dist/artplayer.mjs", + "types": "./types/artplayer.d.ts", + "typesVersions": { + "*": { + "i18n/*": [ + "types/i18n.d.ts" + ], + "legacy": [ + "types/artplayer.d.ts" + ] + } + }, + "legacy": "./dist/artplayer.legacy.js", + "browserslist": "last 1 Chrome version", + "dependencies": { + "option-validator": "^2.0.6" + } + }, + "files": { + "package/README.md": "b2e7985fe1cb42822e6a862acedf96bfebdd5a4800ae8e7817fd79e992881748", + "package/dist/artplayer.js": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f", + "package/dist/artplayer.legacy.js": "cef83f9dac8a343060329ea6c5d603f0e7811fd0fd6c1590ae1c47f9e0afc9bb", + "package/dist/artplayer.mjs": "a1800b37ac7549f52fa960fe28bc6e1c69ec18bd4bc9a7e2534aecc3df10eb3e", + "package/dist/i18n/ar.js": "61c43469ee95a00dc624583f418653e3dcbf48b24ae6a4c203ad986234852365", + "package/dist/i18n/ar.mjs": "68922fdc9f80d3321ea0654c8ff8f5875b6715103d2b14ffcf17f93738b39cf9", + "package/dist/i18n/cs.js": "ad7bbf8853204a6955ef84bfbd932cab3f141a7e73a8145f28ba7a5b2b83b7c0", + "package/dist/i18n/cs.mjs": "29a489be767fda9d46b59c9adbaad323f17a5e67daf7f7aa78b7ce8fb5b4d63c", + "package/dist/i18n/es.js": "aaa95cc9645b734c58fba06fdbcdda962c4cb3a0e7eb6206f06d6168208cd86b", + "package/dist/i18n/es.mjs": "331d2ef74da2eec8f3304fec0b5340c795d9c1eec24e08167c9a7b66bf30973b", + "package/dist/i18n/fa.js": "c998467261cbdfd2c3fb16c0ae4b2c48c409f42f41aea35ed94766f1e12819d5", + "package/dist/i18n/fa.mjs": "b4c98894b46975cebf5ea9088bcf0b5bdfd408bc3c82f4e2e9128133c7de2d4d", + "package/dist/i18n/fr.js": "6b06e16b1fb01c8695fb5c10b32a00421399180de5dc5d845ce17010f2fd3e66", + "package/dist/i18n/fr.mjs": "abe8e3548fa7304f302e2c74818a6176edee8c3b65e33cb6f62e96d130749ee5", + "package/dist/i18n/id.js": "a5832c4bfc03c7f16418bf4b324d9773895541dc823ece9114741794737aad74", + "package/dist/i18n/id.mjs": "a40a433be6ca1ac1ee1810fe805ee60784d349e59b9ad82ea80201d660db4e68", + "package/dist/i18n/pl.js": "2491487062bdfc91d60f17c6e9a83ee196c991216d15f525fe3536b98873da2c", + "package/dist/i18n/pl.mjs": "ad8ea2c03785edbfe1f35761bec21b8a04213f9fa88a794d850bb3973564930f", + "package/dist/i18n/ru.js": "2d3a9ee6533cb6fa961f0a24a25ce63cc76992fca17e1769b6fa75104f8a1529", + "package/dist/i18n/ru.mjs": "a89b8c36cfd7c181c2b31aa823deda3d7c37b56b2b81746338c52ea1c1d93352", + "package/dist/i18n/tr.js": "438b06a4c3f479a2901b0085eaa7bdba8eaef87bdb07ff3faa559c85403fe433", + "package/dist/i18n/tr.mjs": "cc96f9cbb9c1587d5cd8f25b0b9800678ad76e93de089ae42c38dee16e6fe8fa", + "package/dist/i18n/vi.js": "1ffbd62843a902ea2967eab46432d3fc548f5633e53eed104314b523cacc46a8", + "package/dist/i18n/vi.mjs": "7872ded84622bcda20e8c1d70c597c2ba071b50701900efa47cc4683ee17f1a2", + "package/dist/i18n/zh-tw.js": "2d43577f98456488196921e1ec721695a95e1b8c827077286b02ce3fb987e8ff", + "package/dist/i18n/zh-tw.mjs": "7ec638b4babe59d3ab1d3e64abdd74a96fefb260a50e2cd7240f8d50a1340925", + "package/package.json": "d3dd8bb2f4c0629b60a88ca8328f5813f33ab56502989b63de31446cc9516d99", + "package/types/artplayer.d.ts": "f2bc49e8b708ec27d9f5c138cf32099318abbb5e6135a7ee723b20511dab7026", + "package/types/component.d.ts": "08a960348dc459a9ff529ab8e5bdae5fc2fd76448028d7743f58480ea8414cbd", + "package/types/config.d.ts": "97322dce493ac3d1160f426e4fd0ba89fa746f7d66bfbeabd9f45454198560ad", + "package/types/cssVar.d.ts": "2ff9827aa10bf326f60f6cda7556a94fdcabafba529185a1ceae46d865f33697", + "package/types/events.d.ts": "3d9ee111908d93efb4c531cdb6c82d698526328d78ecf77e2488d4aa1d43a310", + "package/types/i18n.d.ts": "01e7d7e7f775166eed6dbc6b86feb26306302fcbfe33fa01b7e363a14b3ef2b0", + "package/types/icons.d.ts": "61ce1b0cb9bc7488263b644419b5fd5a13e72de1493c8113eee81d5174f1234b", + "package/types/option.d.ts": "b4735183a4c9dde7620e178950df9f9d1c18bbaa79ef8f9f85ec6c51a5e47662", + "package/types/player.d.ts": "bce1aa214bfb10b017574f6175a400c059b4d56041c4b2329cc0873c66ac94e4", + "package/types/quality.d.ts": "dcca8c0f5b5c25373f85825a274fc3ee8beeffd7c8b2a978468e039ff4946a9e", + "package/types/setting.d.ts": "a60ed81da780439a55ee22ebe2611d936bd45da0d98e6bf44c92e9a5d9047492", + "package/types/subtitle.d.ts": "92f711ad0f7924e609ad4425cd26d19c73eb027cccddc6d89fcf9b8e334db065", + "package/types/template.d.ts": "b5f9bff43b4816bd2e3531a3a1750b8014fcf63d421fbc786341cedb1429035c", + "package/types/utils.d.ts": "4405a0d40628eaf40357fe31286f26f21b09b14d74581fff32358bb78fd789f4" + }, + "sourceMapping": { + "registryGitHead": "daf133b22630b4a0eecfa3336bbddab0e9119d96", + "versionAtRegistryGitHead": "5.3.1", + "versionTag": "5.4.0", + "versionTagCommit": "65e51751cd0c0db0bd00b5d7deb925d52cfaf3ef", + "versionAtTag": "5.4.0", + "exactSourceReproduction": "not-yet-verified" + } + }, + { + "name": "artplayer-plugin-chapter", + "version": "1.1.0", + "metadataUrl": "https://registry.npmjs.org/artplayer-plugin-chapter/1.1.0", + "tarball": "https://registry.npmjs.org/artplayer-plugin-chapter/-/artplayer-plugin-chapter-1.1.0.tgz", + "integrity": "sha512-ji66Lr2K2S+OjFMuMby3yq+pefWlOC51Sbpop/DIr2N6w3F0smrF+YZbshN1Dy8/Tx1TYpT7N6WkkZWy5Jix1w==", + "sha256": "4c3173661854ff96f643a479c0f1c2db7ce9e54681523bd059a7dd9bd77eeada", + "archiveBytes": 4221, + "manifest": { + "name": "artplayer-plugin-chapter", + "version": "1.1.0", + "description": "chapter plugin for ArtPlayer", + "author": "Harvey Zhao ", + "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-chapter.d.ts", + "import": "./dist/artplayer-plugin-chapter.mjs", + "require": "./dist/artplayer-plugin-chapter.js" + }, + "./legacy": { + "types": "./types/artplayer-plugin-chapter.d.ts", + "import": "./dist/artplayer-plugin-chapter.legacy.js", + "require": "./dist/artplayer-plugin-chapter.legacy.js" + } + }, + "main": "./dist/artplayer-plugin-chapter.js", + "module": "./dist/artplayer-plugin-chapter.mjs", + "types": "./types/artplayer-plugin-chapter.d.ts", + "legacy": "./dist/artplayer-plugin-chapter.legacy.js", + "browserslist": "last 1 Chrome version" + }, + "files": { + "package/README.md": "04f1d3338acdf7c4f56ac3596d7c5c3282f508400208cb6142da064ced7e72dc", + "package/dist/artplayer-plugin-chapter.js": "c2aaee11bf1db765433b01d9f8dd3978e41fa4d39655db00ebd03fc0be3e35e0", + "package/dist/artplayer-plugin-chapter.legacy.js": "c2aaee11bf1db765433b01d9f8dd3978e41fa4d39655db00ebd03fc0be3e35e0", + "package/dist/artplayer-plugin-chapter.mjs": "61342a069b84e0731d44a2464d0dfe637d1514d0037c740371fc866c516d2209", + "package/package.json": "233ed9b732e14cb06955536157ba1650c4cffded74015a2417942e70ee5aca04", + "package/types/artplayer-plugin-chapter.d.ts": "4ce65f8a55ed8f53ddd7f1cd7324923aacefeaee209d58d5981e1434050a6962" + }, + "sourceMapping": { + "registryGitHead": "daf133b22630b4a0eecfa3336bbddab0e9119d96", + "versionAtRegistryGitHead": "1.0.3", + "versionTag": "5.4.0", + "versionTagCommit": "65e51751cd0c0db0bd00b5d7deb925d52cfaf3ef", + "versionAtTag": "1.1.0", + "exactSourceReproduction": "not-yet-verified" + } + } + ], + "remainingPackages": [ + { + "name": "artplayer-plugin-ads", + "workspaceVersion": "2.1.0", + "releaseBaseline": "not-yet-verified" + }, + { + "name": "artplayer-plugin-ambilight", + "workspaceVersion": "1.1.0", + "releaseBaseline": "not-yet-verified" + }, + { + "name": "artplayer-plugin-asr", + "workspaceVersion": "2.1.0", + "releaseBaseline": "not-yet-verified" + }, + { + "name": "artplayer-plugin-audio-track", + "workspaceVersion": "1.1.0", + "releaseBaseline": "not-yet-verified" + }, + { + "name": "artplayer-plugin-auto-thumbnail", + "workspaceVersion": "1.1.0", + "releaseBaseline": "not-yet-verified" + }, + { + "name": "artplayer-plugin-chromecast", + "workspaceVersion": "1.1.0", + "releaseBaseline": "not-yet-verified" + }, + { + "name": "artplayer-plugin-danmuku", + "workspaceVersion": "5.3.0", + "releaseBaseline": "not-yet-verified" + }, + { + "name": "artplayer-plugin-danmuku-mask", + "workspaceVersion": "1.1.0", + "releaseBaseline": "not-yet-verified" + }, + { + "name": "artplayer-plugin-dash-control", + "workspaceVersion": "1.1.0", + "releaseBaseline": "not-yet-verified" + }, + { + "name": "artplayer-plugin-document-pip", + "workspaceVersion": "1.1.0", + "releaseBaseline": "not-yet-verified" + }, + { + "name": "artplayer-plugin-hls-control", + "workspaceVersion": "1.1.0", + "releaseBaseline": "not-yet-verified" + }, + { + "name": "artplayer-plugin-jassub", + "workspaceVersion": "1.1.0", + "releaseBaseline": "not-yet-verified" + }, + { + "name": "artplayer-plugin-multiple-subtitles", + "workspaceVersion": "1.2.0", + "releaseBaseline": "not-yet-verified" + }, + { + "name": "artplayer-plugin-vast", + "workspaceVersion": "1.2.0", + "releaseBaseline": "not-yet-verified" + }, + { + "name": "artplayer-plugin-vtt-thumbnail", + "workspaceVersion": "1.1.0", + "releaseBaseline": "not-yet-verified" + }, + { + "name": "artplayer-proxy-canvas", + "workspaceVersion": "1.1.0", + "releaseBaseline": "not-yet-verified" + }, + { + "name": "artplayer-proxy-mediabunny", + "workspaceVersion": "1.2.0", + "releaseBaseline": "not-yet-verified" + }, + { + "name": "artplayer-tool-iframe", + "workspaceVersion": "1.1.0", + "releaseBaseline": "not-yet-verified" + }, + { + "name": "artplayer-tool-thumbnail", + "workspaceVersion": "4.4.0", + "releaseBaseline": "not-yet-verified" + }, + { + "name": "artplayer-vitepress", + "workspaceVersion": "1.1.0", + "releaseBaseline": "not-yet-verified" + } + ] +} diff --git a/refactor/changes/2026-09-10-BASE-01-published-baseline.md b/refactor/changes/2026-09-10-BASE-01-published-baseline.md new file mode 100644 index 000000000..eb761d0bc --- /dev/null +++ b/refactor/changes/2026-09-10-BASE-01-published-baseline.md @@ -0,0 +1,23 @@ +# BASE-01 核心与 chapter 发布基线 + +- 日期:2026-09-10;分支:codex/compatible-modernization;起点:7a5354e3。 +- 提交主题:`test(refactor): [BASE-01] freeze published core and chapter baselines`。 +- 关联:API-09、ADR-019;本任务是来源与可重跑验证基础,不修改生产 API。 + +## 实际交付 + +固定 artplayer@5.4.0 和 artplayer-plugin-chapter@1.1.0 的 metadata URL、SRI、archive SHA-256、manifest、47 个成员哈希以及来源差异。新增无第三方 Node 依赖的验证脚本和损坏负例,缓存忽略规则及基线维护说明。 + +artplayer@5.4.1 当前未发布(registry 返回 404);两个包的 registry gitHead 版本也与发布包不符,不能将它当成确定的发布源码。版本匹配的本地 tag 单独记录,精确构建重现仍待验证。其余 20 包及最低 TS/浏览器/核心支持范围尚未验证。 + +## 验证 + +- `node refactor/scripts/releases.mjs`:两个包 SHA-512/SHA-256、核心 41/chapter 6 成员哈希及主要 manifest 入口存在性通过。 +- `node --test refactor/scripts/releases.test.mjs`:1 项通过,含替换 bytes 和错误 SHA-256 的负例。 +- `node --check refactor/scripts/releases.mjs` 和计划检查通过。 +- 环境:Node 25.2.1,Windows bsdtar 3.8.8;来源和完整哈希见 baselines/releases.json。 +- 未运行生产播放/浏览器/类型回归;入口存在性不等于入口可运行,后续 BASE 任务继续验证。 + +## 兼容、交接与回退 + +没有修改 core/plugin 源码、manifest、锁文件或分发内容;目标仍为核心 6.0.0/chapter 2.0.0。验证脚本职责和命令见 baselines/README.md。可独立回退本提交;缓存是可重下载的本地文件。BASE-01 达到最小来源验收,后续先推进 BASE-02 和工程固定依赖。 diff --git a/refactor/plan.md b/refactor/plan.md index 7083e3c0d..8421af3d4 100644 --- a/refactor/plan.md +++ b/refactor/plan.md @@ -4,7 +4,7 @@ 基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 211 项,范围 22 个包及工作区/示例。 -状态:todo 199 / doing 0 / blocked 0 / done 12 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 +状态:todo 198 / doing 0 / blocked 0 / done 13 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。 @@ -58,7 +58,7 @@ | ID | 范围 / 步骤 | 前置依赖 | 交付物 | 验收条件 | 风险 | 状态 | | --- | --- | --- | --- | --- | --- | --- | -| BASE-01 | workspace
固定核心与试点的最小发布基线 | DOC-07 | 核心及 chapter 的 npm tarball/integrity、源码 SHA、已知支持窗口;其他包标待核对 | 核心/试点来源可重跑;不虚构全生态已验证,各包 01 步负责补齐自身历史发布基线 | H | todo | +| BASE-01 | workspace
固定核心与试点的最小发布基线 | DOC-07 | 核心及 chapter 的 npm tarball/integrity、源码 SHA、已知支持窗口;其他包标待核对 | 核心/试点来源可重跑;不虚构全生态已验证,各包 01 步负责补齐自身历史发布基线 | H | done | | BASE-02 | workspace
捕获公共 API 和属性描述符 | BASE-01 | 核心与试点的构造/默认值/静态和实例 API 快照,其余包由 01/02 步补齐;登记契约/版本/固定测试 ID/命令/报告与负责任务 | 真实发布包反射与旧用户调用可重跑,未验证项明确 | H | todo | | BASE-03 | workspace
捕获事件、异步和生命周期 | BASE-02 | ready/restart/destroy、Promise、切源/插件事件 trace;登记契约/版本/固定测试 ID/命令/报告与负责任务 | 成功、失败、重入和多实例有断言,历史缺陷有标记;使用已发布包和最小可重跑探针启动,不等待后续 ENG 服务 | H | todo | | BASE-04 | workspace
捕获 DOM、CSS 和官方 demo | BASE-02 | 核心模板/类名/变量、键盘/焦点/可访问名称基线及全 demo 路径映射;docs-browser-testing.md 中 HTML/编辑器的脚本来源与存储状态;登记契约/版本/固定测试 ID/命令/报告与负责任务 | 核心/试点用户样式和输入用法已记录,其他 demo 运行状态与未知项明确;使用已发布包和最小可重跑探针启动,不等待后续 ENG 服务 | M | todo | @@ -412,3 +412,4 @@ - DOC-10: [记录](changes/2026-09-10-DOC-10-github-ci-cd.md) - DOC-11: [记录](changes/2026-09-10-DOC-11-major-version-policy.md) - DOC-12: [记录](changes/2026-09-10-DOC-12-execution-readiness-review.md) +- BASE-01: [记录](changes/2026-09-10-BASE-01-published-baseline.md) [记录](baselines/releases.json) diff --git a/refactor/progress.md b/refactor/progress.md index c470e31fd..6a4fb129e 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -1,6 +1,12 @@ # 进度与证据 -## 当前阶段:DOC-12 开发前计划复审 +## 当前实施:BASE-01 已发布基线 + +2026-09-10,goal 开始执行。固定核心 5.4.0/chapter 1.1.0 的真实 tarball、47 个成员哈希和支持范围未知项。核心工作区 5.4.1 未发布,两包 registry gitHead 版本过旧,均已如实记录。详见 [BASE-01 交付](changes/2026-09-10-BASE-01-published-baseline.md)。 + +两个包完整性和入口存在性验证通过,损坏检测 1 项测试通过。完成 12 项规划及 1 项实施基础任务;198 项待办。没有生产 TS 迁移或浏览器通过结论,没有推送/发布。下一步 BASE-02 捕获公开 API,ENG-01 固定依赖与运行环境。 + +## 2026-09-10:DOC-12 开发前计划复审 2026-09-10,再次检查依赖和验收,修正首轮复盘间接等待 mask 设备、版本准备缺实施任务,补清基线启动、站点分发分类、SSR 边界与契约覆盖索引。详见 [DOC-12 记录](changes/2026-09-10-DOC-12-execution-readiness-review.md) 和 [执行门槛](execution-gates.md)。 diff --git a/refactor/scripts/releases.mjs b/refactor/scripts/releases.mjs new file mode 100644 index 000000000..1d03eb822 --- /dev/null +++ b/refactor/scripts/releases.mjs @@ -0,0 +1,70 @@ +import assert from 'node:assert/strict' +import { createHash } from 'node:crypto' +import { execFileSync } from 'node:child_process' +import fs from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +export const refactorDir = fileURLToPath(new URL('../', import.meta.url)) +export const cacheDir = path.join(refactorDir, '.cache/releases') +export const hash = (bytes, algorithm = 'sha256', encoding = 'hex') => createHash(algorithm).update(bytes).digest(encoding) + +export function verifyIntegrity(bytes, release) { + assert.equal(`sha512-${hash(bytes, 'sha512', 'base64')}`, release.integrity, `Integrity mismatch: ${release.name}`) + assert.equal(hash(bytes), release.sha256, `Archive hash mismatch: ${release.name}`) +} + +export function archiveFiles(archive) { + const names = execFileSync('tar', ['-tzf', archive], { encoding: 'utf8' }).trim().split(/\r?\n/) + assert.equal(new Set(names).size, names.length, 'Duplicate archive entries') + for (const name of names) { + assert(name.startsWith('package/') && !name.includes('\\') && !name.split('/').includes('..'), `Invalid archive path: ${name}`) + } + return names.filter(name => !name.endsWith('/')).sort() +} + +// Read a member to memory; never extract untrusted paths into the working tree. +export function readMember(archive, member) { + assert(member.startsWith('package/') && !member.includes('..') && !member.includes('\\'), 'Invalid member') + return execFileSync('tar', ['-xOzf', archive, member], { maxBuffer: 16 * 1024 * 1024 }) +} + +export async function ensureArchive(release) { + assert(/^[a-z0-9-]+$/.test(release.name) && /^\d+\.\d+\.\d+$/.test(release.version), 'Invalid release identifier') + const archive = path.join(cacheDir, `${release.name}-${release.version}.tgz`) + if (!fs.existsSync(archive)) { + const url = new URL(release.tarball) + assert.equal(url.origin, 'https://registry.npmjs.org', 'Unexpected registry') + const response = await fetch(url, { redirect: 'error', signal: AbortSignal.timeout(30000) }) + assert(response.ok, `Download failed: ${response.status} ${release.name}`) + const bytes = Buffer.from(await response.arrayBuffer()) + verifyIntegrity(bytes, release) + fs.mkdirSync(cacheDir, { recursive: true }) + fs.writeFileSync(archive, bytes) + } + verifyIntegrity(fs.readFileSync(archive), release) + return archive +} + +export async function verifyReleases() { + const baseline = JSON.parse(fs.readFileSync(path.join(refactorDir, 'baselines/releases.json'), 'utf8')) + for (const release of baseline.releases) { + const archive = await ensureArchive(release) + const names = archiveFiles(archive) + assert.deepEqual(names, Object.keys(release.files).sort(), `File inventory changed: ${release.name}`) + for (const name of names) { + assert.equal(hash(readMember(archive, name)), release.files[name], `File changed: ${name}`) + } + const manifest = JSON.parse(readMember(archive, 'package/package.json')) + assert.equal(manifest.name, release.name) + assert.equal(manifest.version, release.version) + for (const field of ['main', 'module', 'types', 'legacy']) { + if (manifest[field]) assert(names.includes(`package/${manifest[field].replace(/^\.\//, '')}`), `Missing ${field}: ${release.name}`) + } + console.log(`Verified ${release.name}@${release.version}: SHA-512, SHA-256, ${names.length} files and manifest entrypoints`) + } +} + +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + await verifyReleases() +} diff --git a/refactor/scripts/releases.test.mjs b/refactor/scripts/releases.test.mjs new file mode 100644 index 000000000..9fe31d6ca --- /dev/null +++ b/refactor/scripts/releases.test.mjs @@ -0,0 +1,11 @@ +import assert from 'node:assert/strict' +import { test } from 'node:test' +import { hash, verifyIntegrity } from './releases.mjs' + +test('release integrity rejects corrupted bytes and inconsistent frozen digests', () => { + const bytes = Buffer.from('known release artifact') + const release = { name: 'fixture', integrity: `sha512-${hash(bytes, 'sha512', 'base64')}`, sha256: hash(bytes) } + assert.doesNotThrow(() => verifyIntegrity(bytes, release)) + assert.throws(() => verifyIntegrity(Buffer.from('replaced artifact'), release), /Integrity mismatch/) + assert.throws(() => verifyIntegrity(bytes, { ...release, sha256: '0'.repeat(64) }), /Archive hash mismatch/) +}) diff --git a/refactor/tasks.json b/refactor/tasks.json index 004a7bdb4..9cddd8fa0 100644 --- a/refactor/tasks.json +++ b/refactor/tasks.json @@ -229,11 +229,14 @@ "dependsOn": [ "DOC-07" ], - "status": "todo", + "status": "done", "risk": "H", "deliverable": "核心及 chapter 的 npm tarball/integrity、源码 SHA、已知支持窗口;其他包标待核对", "acceptance": "核心/试点来源可重跑;不虚构全生态已验证,各包 01 步负责补齐自身历史发布基线", - "evidence": [] + "evidence": [ + "changes/2026-09-10-BASE-01-published-baseline.md", + "baselines/releases.json" + ] }, { "id": "BASE-02",