mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 10:56:15 -08:00
docs(release): [REL-01] freeze independent major version and migration plans
This commit is contained in:
1 parent
267b21226a
commit
69ad6fb17e
14 files changed
+6747
-9
No files matched your search
@@ -37,6 +37,7 @@
|
||||
| [已实现的开发环境](toolchain-setup.md) | Node/Yarn 固定版本、锁文件、安装命令和实际验证范围 |
|
||||
| [Bun 隔离安装评估](bun-evaluation.md) | 固定版本对比、传递依赖/资源差异及继续使用 Yarn 的依据 |
|
||||
| [全包大版本策略](version-policy.md) | 每包分别升级一个 major 的目标清单、兼容要求和版本落地步骤 |
|
||||
| [分包版本与迁移方案](version-plan.md) | REL-01冻结的22包目标、registry观察、变更日志范围、依赖与示例计划 |
|
||||
| [已实现的 CI 入口](ci-setup.md) | Yarn 检查/构建、只读 lint、Pages 隔离及远端待验收状态 |
|
||||
| [GitHub CI/CD](github-ci-cd.md) | PR/兼容矩阵、构建报告、Pages、npm 发布及远端准入验证 |
|
||||
| [Pages部署与恢复](pages-deployment.md) | 独立暂存、旧URL/域名预检、只读远端快照与人工切换恢复顺序 |
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,53 @@
|
||||
# REL-01 分包版本与差异方案
|
||||
|
||||
起点267b21226a11930b1dd89508f326956c1e106928。所有源码/声明决策前置任务已完成,
|
||||
包括Thumbnail批准的默认策略;本任务完成22包版本准备方案,不改变manifest,
|
||||
不宣称包已通过发布验收,不执行复盘、推送、npm或标签操作。
|
||||
|
||||
## 交付
|
||||
|
||||
- [version-plan.json](../version-plan.json)冻结每包源版本、下一major目标、分发类别、
|
||||
变更日志路径/内容范围、声明规则、原依赖字段、维护文档和剩余验收责任。
|
||||
[可读方案](../version-plan.md)说明本地候选和后续版本/标签步骤。
|
||||
- [registry快照](../baselines/version-registry-2026-09-16.json)为本次真实只读网络请求,
|
||||
覆盖22名和Iframe旧名。记录状态、URL、时间、响应SHA256、active/time/unpublished
|
||||
版本和dist-tags;未观察到目标版本或目标/更高major稳定版占用。
|
||||
Iframe新名404、Thumbnail撤包、VitePress无npm记录分别保留含义与门槛。
|
||||
- [version-registry.mjs](../scripts/version-registry.mjs)提供后续刷新入口,只写新的
|
||||
本地证据文件。并行上限4;未知HTTP/JSON/名称响应不会转成版本可用。
|
||||
一次初始路径检查因Windows根路径尾部分隔符拒绝执行,未发网络请求/写快照;
|
||||
根路径规范化后成功完成23次观察,不放宽目录范围与覆盖保护。
|
||||
- [version-plan.mjs](../scripts/version-plan.mjs)区分方案一致性和版本已经落实。
|
||||
prepared要求22包目标版本、各包目标CHANGELOG和两套示例依赖,不以静态方案
|
||||
冒充版本更改;所有检查都不授权发布。
|
||||
|
||||
## 冻结决策
|
||||
|
||||
本轮本地候选使用用户要求的精确M+1.0.0版本,不增加rc后缀。它是尚未上传的
|
||||
tarball。未来经授权公开候选用next,正式推广用latest;next不会隔离明确请求
|
||||
该版本的用户,也不是发布权限。已经公开的版本不能覆盖,若需改内容则必须
|
||||
新版本与重新验证。其余复盘、设备、回退、许可和远端CI门槛均保留。
|
||||
|
||||
22包没有相互声明的workspace依赖;已有外部dependency/peer字段保持,不能
|
||||
因core6.0.0增加只接受6的peer限制。React/Vue源示例计划把三个本仓库包依赖改成
|
||||
本地file路径以消费未发布版本;冻结历史消费者和隔离tarball测试保留。REL-09
|
||||
核验实际安装/类型/构建流程,只维护根yarn.lock。根private包与示例不升版本。
|
||||
|
||||
VitePress仍仅站点分发;Iframe旧名与新名的恢复/权限不混淆;Thumbnail历史归档
|
||||
缺口不被major升级掩盖。已接受的Ads/VAST/Thumbnail和其他类型差异均要求继续
|
||||
记录。公开入口/声明的确切文件沿用现状与逐包维护指南,不统一改成一种导出。
|
||||
|
||||
方案内准入观察来自真实release-ledger计算,22包均blocked。它是带时间的规划
|
||||
快照,后续当前状态仍由原台账计算;REL-01完成不会自动消除这些阻塞。
|
||||
|
||||
## 验证与后续
|
||||
|
||||
固定Node24.21.0/Yarn1.22.22。真实registry捕获退出0;5项注册表规则测试通过,
|
||||
覆盖已发布/撤包版本不可复用、更高major冲突、404非所有权、错误HTTP/格式、
|
||||
请求目的地与返回包名。变更脚本lint通过;version-plan --check通过;
|
||||
--prepared正确退出1并指出artplayer仍是5.4.1,没有误报已经完成major升级。
|
||||
版本目标同时与初始清单和release-ledger逐包交叉校验。
|
||||
|
||||
REL-09落实所有版本、适用示例/依赖/锁和CHANGELOG,单独提交。
|
||||
REL-02随后重建目标版本候选并绑定新的证据;旧版本的测试报告只保留作历史依据。
|
||||
工具链发布文档同时修正为实施后等待用户指导复盘,消除自动启动的旧表述。
|
||||
+3
-2
@@ -4,7 +4,7 @@
|
||||
|
||||
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 283 项,范围 22 个包及工作区/示例。
|
||||
|
||||
状态:todo 43 / doing 20 / blocked 0 / done 220 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
状态:todo 42 / doing 20 / blocked 0 / done 221 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
|
||||
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
|
||||
|
||||
@@ -450,7 +450,7 @@
|
||||
| ID | 范围 / 步骤 | 前置依赖 | 交付物 | 验收条件 | 风险 | 状态 |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| REL-08 | workspace<br>提前建立逐包发布准入台账 | BASE-08, ENG-07 | 每批包/版本/源码/锁文件/工具/tarball integrity、必需测试/设备证据、限制和回退映射 | 受影响能力缺证据明确阻止对应批次;无关批次可独立准备,旧证据在候选内容变化后失效;站点采用真实构建/URL/资源验收,npm 分发依据历史核实,不从版本清单推断新增发布范围;关联 risks.json 和 third-party.json;受影响 bundle/worker/WASM/font/模型的来源与许可通知缺口必须有审查结论,未决项阻止对应批次 | H | done |
|
||||
| REL-01 | workspace<br>提前确定分包版本与差异方案 | REL-08, CORE-21, SITE-03, PKG-CHAPTER-04, PKG-AMBILIGHT-04, PKG-AUDIO-04, PKG-AUTO-THUMB-04, PKG-VTT-THUMB-04, PKG-HLS-04, PKG-DASH-04, PKG-MULTI-SUB-04, PKG-JASSUB-04, PKG-MASK-04, PKG-ASR-04, PKG-ADS-04, PKG-VAST-04, PKG-CAST-04, PKG-DPIP-04, PKG-CANVAS-04, PKG-IFRAME-04, PKG-TOOL-THUMB-04, PKG-DANMUKU-06, PKG-MB-08, PKG-FACTORY-01 | 每包版本/变更日志/依赖/类型差异方案和独立准入状态;按 version-policy.md 冻结各包下一 major(minor/patch 归零)并核实 registry 占用 | 所有包有方案和剩余门槛,未决项明确阻止相应发布;本步骤不声称已经可发布;全部包有 major 目标,版本冲突明确处理,独立准备任务同步 manifest/锁/依赖/日志;按 BASE-05 区分 21 库与文档站分发,不机械把文档站当库上传 npm | H | todo |
|
||||
| REL-01 | workspace<br>提前确定分包版本与差异方案 | REL-08, CORE-21, SITE-03, PKG-CHAPTER-04, PKG-AMBILIGHT-04, PKG-AUDIO-04, PKG-AUTO-THUMB-04, PKG-VTT-THUMB-04, PKG-HLS-04, PKG-DASH-04, PKG-MULTI-SUB-04, PKG-JASSUB-04, PKG-MASK-04, PKG-ASR-04, PKG-ADS-04, PKG-VAST-04, PKG-CAST-04, PKG-DPIP-04, PKG-CANVAS-04, PKG-IFRAME-04, PKG-TOOL-THUMB-04, PKG-DANMUKU-06, PKG-MB-08, PKG-FACTORY-01 | 每包版本/变更日志/依赖/类型差异方案和独立准入状态;按 version-policy.md 冻结各包下一 major(minor/patch 归零)并核实 registry 占用 | 所有包有方案和剩余门槛,未决项明确阻止相应发布;本步骤不声称已经可发布;全部包有 major 目标,版本冲突明确处理,独立准备任务同步 manifest/锁/依赖/日志;按 BASE-05 区分 21 库与文档站分发,不机械把文档站当库上传 npm | H | done |
|
||||
| REL-09 | workspace<br>落实全包下一 major 版本及依赖元数据 | REL-01, DOC-11 | 22 包版本目标落实、适用锁文件/依赖范围/示例/变更日志同步,核实 registry 版本占用 | 版本与 policy 一致,旧核心支持保留;必要拆子任务各自提交,候选构建前完成,不执行 publish | H | todo |
|
||||
| REL-02 | workspace<br>生成候选 tarball 并验证新旧组合 | REL-01, CORE-22, ENG-07, REL-09, REL-04 | 各候选本地 tarball/integrity 与新旧核心/插件消费者验证报告,外部门槛单独标注 | 隔离安装和可自动化组合通过;设备结论不伪造,最终发布绑定同一候选内容;在目标 major 版本确定后构建 pack,不在测试后改版本;逐包核验完整回退产物、实际候选版本/integrity、应用导入与冻结锁,重跑对应回退并保留报告;缺失历史分发不准入 | H | todo |
|
||||
| REL-03 | workspace<br>完成真机、外部 SDK 与压力验收 | REL-02, EX-03, SITE-06, MOD-05 | Safari/移动/PiP/Cast/IMA/模型/长播放完整报告 | 包集成与全项目真机/SDK/压力结论齐全才完成本汇总;独立批次先建立自己的完整门槛子任务 | H | todo |
|
||||
@@ -694,6 +694,7 @@
|
||||
- MOD-DEV-01: [记录](changes/2026-09-14-MOD-DEV-01-dev-server.md) [记录](baselines/dev-server-validation.json)
|
||||
- MOD-03: [记录](changes/2026-09-14-MOD-03-slider-updates.md) [记录](baselines/slider-updates-validation.json) [记录](changes/2026-09-14-MOD-03-performance-acceptance.md) [记录](baselines/core-performance-validation.json)
|
||||
- REL-08: [记录](changes/2026-09-14-REL-08-release-ledger.md) [记录](baselines/release-ledger-validation.json) [记录](release-ledger.md) [记录](release-ledger.json)
|
||||
- REL-01: [记录](version-plan.md) [记录](version-plan.json) [记录](baselines/version-registry-2026-09-16.json) [记录](changes/2026-09-16-REL-01-version-plan.md)
|
||||
- REL-04: [记录](changes/2026-09-15-REL-04-consumer-checkpoint.md) [记录](rollback-rehearsal.md) [记录](baselines/rollback-consumer-validation.json) [记录](changes/2026-09-15-REL-04-workflows-checkpoint.md) [记录](rollback-inventory.md) [记录](baselines/rollback-workflows-validation.json) [记录](changes/2026-09-15-REL-04-pages-recovery.md) [记录](baselines/pages-recovery-validation.json) [记录](changes/2026-09-15-REL-04-acceptance.md) [记录](baselines/rollback-acceptance-validation.json)
|
||||
- PKG-FACTORY-01: [记录](baselines/factory-assignment-gaps.json) [记录](baselines/factory-compatibility-proposals.json) [记录](factory-compatibility-decision.md) [记录](changes/2026-09-12-PKG-FACTORY-01-decision.md) [记录](type-compatibility-policy.md) [记录](baselines/factory-compatibility-validation.json) [记录](changes/2026-09-13-PKG-FACTORY-01-compatible-types.md)
|
||||
- CORE-25: [记录](changes/2026-09-13-CORE-25-defaults-ssr.md) [记录](baselines/defaults-ssr-validation.json)
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
# 进度与证据
|
||||
|
||||
## REL-01 全包版本方案冻结
|
||||
|
||||
22包下一major、变更日志范围、依赖/类型规则、示例计划与剩余准入已写入
|
||||
[方案](version-plan.md)。真实registry查询22名及Iframe旧名,未观察到目标或
|
||||
更高major稳定版占用;404/撤包不作为名称权限证明。全部包发布准入仍blocked。
|
||||
5项registry规则测试、方案校验及lint通过,prepared检查正确拒绝尚未升级的
|
||||
manifest。见[记录](changes/2026-09-16-REL-01-version-plan.md)。
|
||||
本次没有修改版本号。REL-01 done;221 done、20 doing、42 todo,共283项。
|
||||
下一步REL-09落实22包版本、变更日志与本地示例消费,再重建候选。
|
||||
完成实施后等待用户指导复盘,不推送、不发布。
|
||||
|
||||
## PKG-DANMUKU-START-01 首帧漏显修复完成
|
||||
|
||||
已解释原安装 WebKit 的首个 readys 在CPU间隔之后才运行的漏显:私有采样窗口
|
||||
|
||||
@@ -24,8 +24,9 @@ REL-08 建立台账及机械校验,不代表任何包已经可以发布。唯
|
||||
|
||||
## 分发和版本边界
|
||||
|
||||
每行明确记录独立下一major目标,初始依据是package-inventory.json。正式版本占用、
|
||||
预发布标识和变更日志仍由REL-01/09落实;本步不修改任何包版本。
|
||||
每行明确记录独立下一major目标,初始依据是package-inventory.json。REL-01已冻结
|
||||
[版本方案](version-plan.md)及registry快照:本地候选使用目标正式数字版本,
|
||||
实际上传仍需授权、最新占用和名称权限检查。REL-09落实manifest与变更日志。
|
||||
|
||||
- 普通npm包使用各自冻结发布记录的name/version/integrity/SHA-256/URL作为历史依据。
|
||||
core和chapter引用releases.json各自成员;不把工作区版本当作npm版本。
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
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'
|
||||
|
||||
const root = path.resolve(fileURLToPath(new URL('../../', import.meta.url)))
|
||||
const read = file => JSON.parse(fs.readFileSync(path.join(root, file), 'utf8'))
|
||||
const mode = process.argv[2]
|
||||
assert(['--check', '--prepared'].includes(mode) && process.argv.length === 3, 'Use --check or --prepared')
|
||||
const plan = read('refactor/version-plan.json')
|
||||
const ledger = read('refactor/release-ledger.json')
|
||||
const inventory = read('refactor/package-inventory.json').packages
|
||||
const registry = read(plan.registrySnapshot)
|
||||
assert.equal(plan.schemaVersion, 1)
|
||||
assert.equal(plan.policy.publicationAuthorized, false)
|
||||
assert.equal(plan.policy.siteNpmPublication, false)
|
||||
assert.equal(plan.policy.prereleaseSuffix, null)
|
||||
assert.deepEqual(plan.packages.map(item => item.name).sort(), ledger.packages.map(item => item.name).sort())
|
||||
for (const item of plan.packages) {
|
||||
const original = inventory.find(pkg => pkg.name === item.name)
|
||||
const row = ledger.packages.find(pkg => pkg.name === item.name)
|
||||
const observation = registry.observations.find(pkg => pkg.name === item.name)
|
||||
const manifest = read(`packages/${item.name}/package.json`)
|
||||
assert.equal(item.sourceVersion, original.version)
|
||||
assert.equal(item.targetVersion, row.targetVersion)
|
||||
assert.equal(item.targetVersion, `${Number(original.version.split('.')[0]) + 1}.0.0`)
|
||||
assert.equal(item.distribution, row.distribution)
|
||||
assert.equal(observation.target, item.targetVersion)
|
||||
assert.equal(observation.targetState, 'not-observed', `Target was used: ${item.name}`)
|
||||
assert.deepEqual(observation.majorConflicts, [], `Registry major conflict: ${item.name}`)
|
||||
assert.equal(item.registry.ownershipVerified, false)
|
||||
assert(item.changelog.changes.length && item.maintenanceDocs.length)
|
||||
assert.equal(item.changelog.path, `packages/${item.name}/CHANGELOG.md`)
|
||||
assert.equal(item.changelog.heading, `${item.targetVersion} (unreleased)`)
|
||||
for (const file of item.maintenanceDocs)
|
||||
assert(fs.existsSync(path.join(root, file)), `Missing maintenance guide: ${file}`)
|
||||
for (const field of ['dependencies', 'optionalDependencies', 'peerDependencies', 'peerDependenciesMeta', 'devDependencies'])
|
||||
assert.deepEqual(manifest[field], item.dependencyPolicy.sourceFields[field], `Unplanned dependency change: ${item.name}/${field}`)
|
||||
assert([item.sourceVersion, item.targetVersion].includes(manifest.version), `Unplanned current version: ${item.name}`)
|
||||
if (mode === '--prepared') {
|
||||
assert.equal(manifest.version, item.targetVersion, `Version not prepared: ${item.name}`)
|
||||
assert(fs.readFileSync(path.join(root, item.changelog.path), 'utf8').includes(item.changelog.heading), `Missing target changelog: ${item.name}`)
|
||||
}
|
||||
}
|
||||
for (const example of plan.examples) {
|
||||
const manifest = read(example.manifest)
|
||||
assert.equal(manifest.private, true)
|
||||
for (const [name, range] of Object.entries(example.dependencies))
|
||||
assert((mode === '--prepared' ? [range.planned] : [range.before, range.planned]).includes(manifest.dependencies[name]), `Example dependency not prepared: ${example.manifest}/${name}`)
|
||||
}
|
||||
console.log(`Version plan ${mode}: ${plan.packages.length} independent major targets; registry snapshot checked, publication remains unauthorized`)
|
||||
@@ -0,0 +1,79 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { createHash } from 'node:crypto'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const root = path.resolve(fileURLToPath(new URL('../../', import.meta.url)))
|
||||
const digest = bytes => createHash('sha256').update(bytes).digest('hex')
|
||||
|
||||
export function versionObservation(status, metadata, target) {
|
||||
assert(status === 200 || status === 404, `Registry status is not an absence observation: ${status}`)
|
||||
assert(metadata && typeof metadata === 'object' && !Array.isArray(metadata), 'Malformed registry metadata')
|
||||
if (status === 404) {
|
||||
assert(metadata.error === 'Not found', 'Unexpected registry 404 response')
|
||||
return { nameState: 'not-found', targetState: 'not-observed', tags: {}, activeVersions: [], historicalVersions: [], majorConflicts: [], ownershipVerified: false }
|
||||
}
|
||||
assert(typeof metadata.name === 'string', 'Registry metadata has no package name')
|
||||
assert((metadata.versions && typeof metadata.versions === 'object' && !Array.isArray(metadata.versions)) || metadata.time?.unpublished, 'Registry metadata has no versions or unpublish record')
|
||||
const activeVersions = Object.keys(metadata.versions || {})
|
||||
const historicalVersions = [...new Set([
|
||||
...Object.keys(metadata.time || {}).filter(version => /^\d+\.\d+\.\d+(?:-.+)?$/.test(version)),
|
||||
...metadata.time?.unpublished?.versions || [],
|
||||
])]
|
||||
const used = new Set([...activeVersions, ...historicalVersions])
|
||||
const major = Number(target.split('.')[0])
|
||||
return {
|
||||
nameState: metadata.time?.unpublished ? 'unpublished' : 'published',
|
||||
targetState: activeVersions.includes(target) ? 'published' : historicalVersions.includes(target) ? 'previously-used' : 'not-observed',
|
||||
tags: metadata['dist-tags'] || {},
|
||||
activeVersions,
|
||||
historicalVersions,
|
||||
majorConflicts: [...used].filter(version => /^\d+\.\d+\.\d+$/.test(version) && Number(version.split('.')[0]) >= major),
|
||||
prereleasesInTargetMajor: [...used].filter(version => version.startsWith(`${major}.`) && version.includes('-')),
|
||||
unpublished: metadata.time?.unpublished || null,
|
||||
ownershipVerified: false,
|
||||
}
|
||||
}
|
||||
|
||||
export async function observePackage(name, target, fetcher = fetch) {
|
||||
assert(/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/.test(name), 'Unexpected package name')
|
||||
assert(/^\d+\.0\.0$/.test(target), 'Expected a frozen next-major version')
|
||||
const url = `https://registry.npmjs.org/${name}`
|
||||
const response = await fetcher(url, { headers: { 'accept': 'application/json', 'cache-control': 'no-cache' }, redirect: 'error', signal: AbortSignal.timeout(30000) })
|
||||
const bytes = new Uint8Array(await response.arrayBuffer())
|
||||
const metadata = JSON.parse(new TextDecoder().decode(bytes))
|
||||
if (response.status === 200)
|
||||
assert.equal(metadata.name, name, 'Registry returned a different package')
|
||||
return { name, target, url, status: response.status, observedAt: new Date().toISOString(), responseSha256: digest(bytes), responseBytes: bytes.length, ...versionObservation(response.status, metadata, target) }
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const [flag, output, ...extra] = process.argv.slice(2)
|
||||
assert(flag === '--capture' && output && !extra.length, 'Use --capture <new repository-local JSON file>')
|
||||
const destination = path.resolve(root, output)
|
||||
assert(destination.startsWith(root + path.sep) && !fs.existsSync(destination), 'Snapshot must be a new repository-local file')
|
||||
const ledger = JSON.parse(fs.readFileSync(path.join(root, 'refactor/release-ledger.json'), 'utf8'))
|
||||
const targets = ledger.packages.map(row => ({ name: row.name, target: row.targetVersion, distribution: row.distribution }))
|
||||
targets.push({ name: 'artplayer-plugin-iframe', target: '2.0.0', distribution: 'historical-name-only' })
|
||||
const observations = []
|
||||
for (let index = 0; index < targets.length; index += 4) {
|
||||
const batch = targets.slice(index, index + 4)
|
||||
const results = await Promise.allSettled(batch.map(async item => ({ ...await observePackage(item.name, item.target), distribution: item.distribution })))
|
||||
for (let offset = 0; offset < results.length; offset++) {
|
||||
const result = results[offset]
|
||||
observations.push(result.status === 'fulfilled' ? result.value : { ...batch[offset], error: String(result.reason), targetState: 'unknown' })
|
||||
}
|
||||
}
|
||||
const report = { schemaVersion: 1, capturedAt: new Date().toISOString(), registry: 'https://registry.npmjs.org', policy: 'Absence is an observation, not name ownership, reservation or publication authorization. Previously used versions cannot be reused even after unpublish.', observations }
|
||||
fs.mkdirSync(path.dirname(destination), { recursive: true })
|
||||
fs.writeFileSync(destination, `${JSON.stringify(report, null, 2)}\n`)
|
||||
for (const row of observations)
|
||||
console.log(`${row.name}: ${row.nameState || 'error'}, latest=${row.tags?.latest || '-'}, target=${row.target} ${row.targetState}, major-conflicts=${row.majorConflicts?.length ?? '?'}`)
|
||||
if (observations.some(row => row.error || (!['site', 'historical-name-only'].includes(row.distribution) && (row.targetState !== 'not-observed' || row.majorConflicts.length))))
|
||||
process.exitCode = 1
|
||||
}
|
||||
|
||||
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url))
|
||||
await main()
|
||||
@@ -0,0 +1,51 @@
|
||||
import assert from 'node:assert/strict'
|
||||
// eslint-disable-next-line test/no-import-node-test -- Release version reuse and registry failure boundaries.
|
||||
import test from 'node:test'
|
||||
import { observePackage, versionObservation } from './version-registry.mjs'
|
||||
|
||||
test('Registry version planning rejects active and unpublished version reuse', () => {
|
||||
assert.equal(versionObservation(200, { name: 'test', versions: { '2.0.0': {} } }, '2.0.0').targetState, 'published')
|
||||
const unpublished = versionObservation(200, { name: 'test', time: { unpublished: { versions: ['2.0.0'] } } }, '2.0.0')
|
||||
assert.equal(unpublished.targetState, 'previously-used')
|
||||
assert.deepEqual(unpublished.majorConflicts, ['2.0.0'])
|
||||
assert.equal(versionObservation(200, { name: 'test', versions: {}, time: { '2.0.0': '2025-01-01' } }, '2.0.0').targetState, 'previously-used')
|
||||
})
|
||||
|
||||
test('A different stable release in the target major prevents silently using an older major plan', () => {
|
||||
const value = versionObservation(200, { name: 'test', versions: { '2.1.0': {}, '3.0.0': {}, '2.0.0-rc.1': {} } }, '2.0.0')
|
||||
assert.equal(value.targetState, 'not-observed')
|
||||
assert.deepEqual(value.majorConflicts, ['2.1.0', '3.0.0'])
|
||||
assert.deepEqual(value.prereleasesInTargetMajor, ['2.0.0-rc.1'])
|
||||
})
|
||||
|
||||
test('Missing names and empty version histories never establish ownership or reservation', () => {
|
||||
const missing = versionObservation(404, { error: 'Not found' }, '2.0.0')
|
||||
assert.equal(missing.nameState, 'not-found')
|
||||
assert.equal(missing.targetState, 'not-observed')
|
||||
assert.equal(missing.ownershipVerified, false)
|
||||
assert.equal(versionObservation(200, { name: 'test', versions: {} }, '2.0.0').ownershipVerified, false)
|
||||
})
|
||||
|
||||
test('Registry authentication, throttling, server failures and malformed responses are not availability evidence', () => {
|
||||
for (const status of [401, 403, 429, 500])
|
||||
assert.throws(() => versionObservation(status, { error: 'Not found' }, '2.0.0'), /not an absence/)
|
||||
for (const data of [null, [], {}, { name: 'test' }])
|
||||
assert.throws(() => versionObservation(200, data, '2.0.0'))
|
||||
assert.throws(() => versionObservation(404, { error: 'Forbidden' }, '2.0.0'))
|
||||
})
|
||||
|
||||
test('Live probe uses only the public registry and verifies response identity', async () => {
|
||||
const urls = []
|
||||
const fetcher = async (url, options) => {
|
||||
urls.push(url)
|
||||
assert.equal(options.redirect, 'error')
|
||||
assert.equal(options.headers.authorization, undefined)
|
||||
return new Response(JSON.stringify({ name: 'artplayer', versions: { '5.4.0': {} } }), { status: 200 })
|
||||
}
|
||||
const result = await observePackage('artplayer', '6.0.0', fetcher)
|
||||
assert.deepEqual(urls, ['https://registry.npmjs.org/artplayer'])
|
||||
assert.equal(result.targetState, 'not-observed')
|
||||
assert.match(result.responseSha256, /^[a-f0-9]{64}$/)
|
||||
await assert.rejects(observePackage('../private', '6.0.0', fetcher), /Unexpected package/)
|
||||
await assert.rejects(observePackage('other', '6.0.0', fetcher), /different package/)
|
||||
})
|
||||
+7
-2
@@ -4960,11 +4960,16 @@
|
||||
"PKG-MB-08",
|
||||
"PKG-FACTORY-01"
|
||||
],
|
||||
"status": "todo",
|
||||
"status": "done",
|
||||
"risk": "H",
|
||||
"deliverable": "每包版本/变更日志/依赖/类型差异方案和独立准入状态;按 version-policy.md 冻结各包下一 major(minor/patch 归零)并核实 registry 占用",
|
||||
"acceptance": "所有包有方案和剩余门槛,未决项明确阻止相应发布;本步骤不声称已经可发布;全部包有 major 目标,版本冲突明确处理,独立准备任务同步 manifest/锁/依赖/日志;按 BASE-05 区分 21 库与文档站分发,不机械把文档站当库上传 npm",
|
||||
"evidence": []
|
||||
"evidence": [
|
||||
"version-plan.md",
|
||||
"version-plan.json",
|
||||
"baselines/version-registry-2026-09-16.json",
|
||||
"changes/2026-09-16-REL-01-version-plan.md"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "REL-09",
|
||||
|
||||
@@ -60,7 +60,7 @@ MOD-01 已完成固定版本隔离评估,结论为保留 Yarn。Bun 1.4.2 普
|
||||
|
||||
GitHub CI/CD 的优化属于本次重构范围,按 [CI/CD 规范](github-ci-cd.md) 分离 PR 检查、候选准备、Pages 部署与 npm 发布。新增 CI-01 至 CI-04 交付矩阵/报告、部署、分包发布准备及远端准入验收;第三轮复盘需核对这些结果。
|
||||
|
||||
实现完成后必须执行 [三轮全局复盘](release-reviews.md)(REVIEW-01/02/03),分别核对架构兼容、真实环境和最终 npm 候选内容。REL-05 依赖三轮通过;候选反馈修复后的正式发布重新核对受影响证据,不能直接复用旧候选的验收结果。
|
||||
实现完成后等待用户指导启动 [三轮全局复盘](release-reviews.md)(REVIEW-01/02/03),不得自动开始;各轮分别核对架构兼容、真实环境和最终 npm 候选内容。REL-05 依赖三轮通过;候选反馈修复后的正式发布重新核对受影响证据,不能直接复用旧候选的验收结果。
|
||||
|
||||
1. BASE-01 确定旧核心和插件的支持矩阵,登记 npm tarball integrity。起点 SHA 不能代替消费者产物基线。
|
||||
2. REL 阶段为每个发生变化的包生成候选 tarball,用隔离消费者验证,无需先公开发布。
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,69 @@
|
||||
# 分包版本与迁移方案(REL-01)
|
||||
|
||||
2026-09-16 冻结方案。机器源为 [version-plan.json](version-plan.json),目标沿用用户批准的 [大版本政策](version-policy.md)。这是版本准备方案,22包当前均被正式发布台账阻止发布;任务、风险和实际准入继续以 tasks.json、risks.json、release-ledger.json 为准,不手工维护另一份“已经通过”清单。
|
||||
|
||||
## 版本和候选编号
|
||||
|
||||
REL-09 将22个 workspace manifest 设为各自下一 major 的正式数字版本,minor/patch 归零,不给根 private 管理包或两个 private 示例提升版本。本轮本地候选就是未上传的目标版本 tarball,不加 -rc 后缀;不能将此称为已经发布的预发布版本。
|
||||
|
||||
后续经用户授权公开候选时使用 next 标签,正式推广使用 latest;相同版本只能上传一次,标签推广应使用已验证的同一内容。next 标签不是版本隔离或发布授权,明确选择新版本的消费者仍可取得它。若公开后需要改内容,必须准备新版本并重新验证,不能覆盖或撤包后复用原版本。所有实际发布及标签操作仍等待三轮复盘和单独授权。
|
||||
|
||||
## 逐包目标与变更日志范围
|
||||
|
||||
表中源码版本是冻结快照;npm latest 是本次只读 registry 观察,并非新的兼容性基线。REL-09 为每包维护 CHANGELOG.md 的目标版本 unreleased 条目;每条的具体变更、声明策略、维护文档、依赖字段和剩余门槛见机器源。
|
||||
|
||||
| 包 | 源码 → 目标 | npm latest / 状态 | 变更日志要点 |
|
||||
| --- | --- | --- | --- |
|
||||
| artplayer | 5.4.1 → 6.0.0 | 5.4.0 | TypeScript subsystems and generated public declarations; preserve root APIs and offer precise /runtime types. Lifecycle, source/event ownership, components, subtitles, playback/display and accessibility fixes; historical consumer matrix retained. |
|
||||
| artplayer-plugin-ads | 2.1.0 → 3.0.0 | 1.0.6 | Typed ad lifecycle and cleanup with preserved published defaults. Approved totalDuration type inference correction; migration explanation remains mandatory. |
|
||||
| artplayer-plugin-ambilight | 1.1.0 → 2.0.0 | 1.1.0 | Typed rendering and cleanup with legacy factory behavior. Published root types retained; precise /runtime entry and older conflicting declaration migration. |
|
||||
| artplayer-plugin-asr | 2.1.0 → 3.0.0 | 2.1.0 | Typed recognition, media/audio ownership, fallback routing and explicit capture. Preserve capability errors and cleanup; supported devices and WebAudio remain release gates. |
|
||||
| artplayer-plugin-audio-track | 1.1.0 → 2.0.0 | 1.1.0 | Typed dual-media synchronization and owned track listeners. Buffer starvation/recovery validation remains open; do not claim a fully green playback matrix. |
|
||||
| artplayer-plugin-auto-thumbnail | 1.1.0 → 2.0.0 | 1.1.0 | Separated metadata, frame capture, encoding and plugin cleanup in TypeScript. Native frame readiness/pixel issues remain tracked; migration does not claim all browsers passed. |
|
||||
| artplayer-plugin-chapter | 1.1.0 → 2.0.0 | 1.1.0 | Typed parsing, chapter UI and lifecycle integration. Preserve chapter selection/events; remaining restart timing and distribution gates are explicit. |
|
||||
| artplayer-plugin-chromecast | 1.1.0 → 2.0.0 | 1.1.0 | Typed SDK initialization, session/receiver state and failure ownership. Keep old public plugin surface; physical Cast/receiver verification is separate. |
|
||||
| artplayer-plugin-danmuku | 5.3.0 → 6.0.0 | 5.3.0 | Typed input, scheduling, Worker placement, renderer, UI and resource ownership. Fix issue #958 heatmap density and CPU/async/first-frame scheduling loss; retain root published types with precise /runtime. |
|
||||
| artplayer-plugin-danmuku-mask | 1.1.0 → 2.0.0 | 1.1.0 | Typed model/SDK lifecycle, rendering and cancellation. Preserve model behavior; combined native load and final SDK/device resource evidence stay explicit. |
|
||||
| artplayer-plugin-dash-control | 1.1.0 → 2.0.0 | 1.1.0 | Typed SDK 4.x/5.x adapter, selector reconciliation and cleanup. Preserve published options/events; distinguish bare-SDK failures from candidate behavior. |
|
||||
| artplayer-plugin-document-pip | 1.1.0 → 2.0.0 | 1.1.0 | Typed document/window lifetime and content restoration. Preserve existing API and DOM ownership; actual document-PiP/device gates remain. |
|
||||
| artplayer-plugin-hls-control | 1.1.0 → 2.0.0 | 1.1.0 | Typed track/quality control and listener/selector lifecycle. Preserve supported hls.js combinations and source-topology cleanup; native worker/device gaps remain. |
|
||||
| artplayer-plugin-jassub | 1.1.0 → 2.0.0 | 1.1.0 | Typed renderer/Worker/font ownership and native drawing compatibility. Preserve root entry and precise /runtime; hybrid late drawing issue remains an explicit blocker. |
|
||||
| artplayer-plugin-multiple-subtitles | 1.2.0 → 2.0.0 | 1.2.0 | Typed caption parsing, ASS conversion, merging and load cancellation. Retain published root declaration; /runtime describes actual behavior and conflicting older extraction migration. |
|
||||
| artplayer-plugin-vast | 1.2.0 → 2.0.0 | 1.0.0 | Typed SDK/loading/disposal and published initialization timing by default. Approved workspace-1.2 opt-in timing and /runtime type migration; SDK/network and device evidence remain separate. |
|
||||
| artplayer-plugin-vtt-thumbnail | 1.1.0 → 2.0.0 | 1.1.0 | Typed VTT parsing, sprite lifecycle and cleanup. Preserve published root types and precise /runtime; document supported old-core combinations and name conflicts. |
|
||||
| artplayer-proxy-canvas | 1.1.0 → 2.0.0 | 1.1.0 | Typed canvas/media shim, source and native subtitle-track ownership. Retain published root types with precise /runtime and documented older type conflicts. |
|
||||
| artplayer-proxy-mediabunny | 1.2.0 → 2.0.0 | 1.2.0 | Typed engine/shim, input/HLS, audio/video pipelines and track controls. Preserve media-like event ordering and published entries; native codec/long-duration/device evidence remains scoped. |
|
||||
| artplayer-tool-iframe | 1.1.0 → 2.0.0 | not-found | Typed cross-frame protocol, navigation generations, origin checks and teardown. Preserve the documented migration from artplayer-plugin-iframe; new-name rights and distribution rollback are not inferred from 404. |
|
||||
| artplayer-tool-thumbnail | 4.4.0 → 5.0.0 | unpublished | Typed source/extraction/input/emitter ownership and actual constructor declarations. Approved published-3.5 default with workspace-4.4 opt-in; preserve delay/height, event and input policies and historical creat* names. |
|
||||
| artplayer-vitepress | 1.1.0 → 2.0.0 | not-found | Typed site/editor/documentation/build modules and local library declaration generation. Preserve URLs, examples and editor globals; font provenance, site verification and Pages gates remain. No new npm distribution. |
|
||||
|
||||
## 依赖、示例与分发
|
||||
|
||||
当前22包没有互相声明的 workspace dependencies/peerDependencies。保留已有外部依赖与版本范围,尤其不因核心升到6而新设只支持6的peer限制;声明中的核心类型引用和实际旧核心支持仍由各包测试验证。VitePress开发依赖、MediaBunny、Mask和VAST运行依赖保持现有范围。
|
||||
|
||||
React/Vue示例各有三项已发布版本依赖:core、Danmuku和Document PiP。REL-09 将仓库内示例指向对应本地包(file:../../packages/<name>),使未发布新版本可以真实安装/构建;原始历史消费夹具保留,隔离tarball验证继续替换其测试来源。示例README同步先构建本地包的步骤;只维护根yarn.lock,不生成示例锁文件。新旧框架消费脚本需重新验证路径处理,不能认为修改package.json就已通过。
|
||||
|
||||
21个库分别核对原入口、exports/typesVersions、legacy、声明与资源,不机械统一为一种导出形状。核心声明继续从public/生成;已有/runtime入口和已批准的历史类型冲突说明保持。工具Thumbnail保留npm默认和显式workspace模式;VAST保留npm初始化与显式workspace模式;其他已接受的类型差异不借major升级扩大。
|
||||
|
||||
artplayer-vitepress升至2.0.0,但仍是站点分发,不新增npm发布。Iframe保留旧发布名artplayer-plugin-iframe的冻结来源和回退关联;新名404不证明名称可由当前账号使用。Thumbnail撤包后的历史归档/CSS/恢复门槛仍需完成,不能恢复上传曾使用过的版本。
|
||||
|
||||
## Registry证据与刷新
|
||||
|
||||
[2026-09-16只读快照](baselines/version-registry-2026-09-16.json)覆盖22个workspace名及Iframe旧名。所有目标版本均为not-observed,未观察到目标或更高major的稳定版本占用。未观察到占用不等于取得名称权限、保留版本或批准发布;CI-03/04需在实际上传前重新查验并验证发布身份。
|
||||
|
||||
查询同时读取versions、time及unpublished历史;网络、权限、限流、服务器和格式错误不能当作不存在。npm规定已使用的name@version即使撤包也不能重复使用,见[npm Unpublish Policy](https://docs.npmjs.com/policies/unpublish/)。
|
||||
|
||||
```sh
|
||||
node refactor/scripts/version-registry.mjs --capture refactor/.cache/version-registry-<new-id>.json
|
||||
node refactor/scripts/version-plan.mjs --check
|
||||
node refactor/scripts/version-plan.mjs --prepared
|
||||
```
|
||||
|
||||
固定Node24.21.0/Yarn1.22.22。capture只读registry并写新的本地证据,不覆盖旧快照;遇到已使用目标、更高major或未知响应退出1。check验证方案与当前清单;prepared还要求22包目标版本、各包CHANGELOG和两个示例依赖全部落地,目前应失败。它们均不代替严格release:preflight,也不执行publish。
|
||||
|
||||
## 实施顺序与失效规则
|
||||
|
||||
1. 本任务冻结方案和registry证据,独立提交。
|
||||
2. REL-09更新版本、变更日志和适用示例/依赖/锁文件,冻结安装、严格类型和必要构建验证;单独提交。
|
||||
3. REL-02重新构建目标版本产物,检查所有入口/资源及新旧消费者并绑定真实候选。旧版本的绿色报告保留为历史证据,不自动适用于新版本产物。
|
||||
4. 继续各包组合/分发、设备、CI/CD与站点门槛。完成实施后由用户指导启动复盘,不能自动开展三轮复盘或发布。
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## 目标清单
|
||||
|
||||
以下来自源码提交 a9197f59 的 22 个 package.json,是本次规划快照,并非已核实的 npm 最新版本;package-inventory.json 历史基线保持不变。本任务不修改 manifest。
|
||||
以下来自源码提交 a9197f59 的 22 个 package.json,是本次规划快照,并非 npm 最新版本;package-inventory.json 历史基线保持不变。REL-01 已在 [分包版本方案](version-plan.md) 冻结目标、迁移/依赖方案和2026-09-16 registry观察;manifest 由 REL-09 更新。
|
||||
|
||||
| 包 | 当前源码版本 | 重构正式目标 |
|
||||
| --- | --- | --- |
|
||||
@@ -43,4 +43,4 @@
|
||||
6. 候选版本必须在最终构建/pack/验证前确定,CI-03 校验逐包版本、包内容和发布 tag;不能测试旧版本包后临发布再改版本。版本或依赖变化使相关候选证据失效,必须重跑验证。
|
||||
7. REVIEW-03 逐包核对目标、声明、入口、版本组合、旧调用及回退产物;release notes 说明本次工程重构采用新 major,但不能笼统声明所有旧 API 已兼容而缺少证据。
|
||||
|
||||
本次决定替代 toolchain-release.md 和 github-ci-cd.md 原先“不自动提升全部 major”的规则。兼容契约与三轮复盘门槛不变;这是版本策略记录,尚未实施升级或发布。
|
||||
本次决定替代 toolchain-release.md 和 github-ci-cd.md 原先“不自动提升全部 major”的规则。兼容契约与三轮复盘门槛不变;REL-01已冻结方案,REL-09尚未实施升级,没有发布授权。实施完成后等待用户指导启动复盘。
|
||||
Reference in new issue
Block a user