mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-26 17:01:20 -08:00
fix(thumbnail): [PKG-TOOL-THUMB-04] supply public types and installed module entries
This commit is contained in:
1 parent
15485b6420
commit
3bff236145
33 files changed
+2466
-164
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,63 @@
|
||||
# PKG-TOOL-THUMB-04 public types and installed entry checkpoint
|
||||
|
||||
Task remains doing while THUMB-COMPAT-01's default policy is pending. Starts from
|
||||
15485b6420dece2b04503bd4fa99a9da0f9c8ba0. Public declarations and installed consumption
|
||||
are implemented; this does not declare package release or full refactor readiness.
|
||||
|
||||
## Public contracts and distribution
|
||||
|
||||
The missing d.ts entry is replaced with a CommonJS class/namespace declaration,
|
||||
paired d.cts/d.mts wrappers and type-only names. Root require/default keeps the
|
||||
direct constructor. Root import/module targets the actually generated .mjs, main
|
||||
is explicit, and the new legacy subpath includes old TS resolution. No .default
|
||||
self-alias is invented. Existing frozen dist files remain packed. The frozen
|
||||
.esm.js target was absent; fixing root import does not restore a nonexistent file.
|
||||
Historical 3.5.31 CSS disposition remains a 06 question under BASE-DIST-01.
|
||||
|
||||
A files allowlist includes dist, declarations, README, architecture and full MIT
|
||||
notices while excluding implementation/config/dependencies. No new dependency,
|
||||
lockfile, runtime target, version or package-manager change is introduced.
|
||||
Known tuples, ctx, coordinates, partial options, lazy fields, returns and old
|
||||
method spellings are declared. Custom event callbacks infer application tuples
|
||||
without any. Error payloads are unknown because arbitrary throws produce numeric,
|
||||
object or absent message values; a new runtime test verifies delivery and identical
|
||||
Promise rejection, including primitive/null throws. Source changes since the prior
|
||||
checkpoint are erased types; all final built bundles are compared byte-for-byte.
|
||||
|
||||
## Editor generator issue found and fixed
|
||||
|
||||
Full build:ts exposed an existing regression: MediaBunny's auxiliary media.d.ts was
|
||||
globbed into an invalid standalone media global, while the proxy declaration kept
|
||||
an unresolved ./media re-export. Generation now selects package-named entries.
|
||||
Explicit local type re-exports require supplied complete matching type sources;
|
||||
missing/renamed/partial definitions fail rather than disappearing. Callable
|
||||
variables and CommonJS classes preserve namespaces. MediaBunny's source declarations
|
||||
are unchanged; its editor output now includes opt-in media types. Thumbnail's exact
|
||||
uppercase global is generated and added to the real docs library list.
|
||||
|
||||
Tests reproduce the old missing-relative-type diagnostic, compile the new types
|
||||
with current/old TS and reject incomplete inputs. The real Monaco worker consumes
|
||||
core/Thumbnail/MediaBunny declarations, rejects invalid selector input, emits and
|
||||
runs the Thumbnail constructor, custom event and repeated native DOM cleanup.
|
||||
This is not a file extraction test.
|
||||
|
||||
## Evidence and continuation
|
||||
|
||||
The installed runner creates a fixture from frozen Git inputs and a candidate Yarn
|
||||
tarball. The former is explicitly not an original npm archive. Frozen require
|
||||
works, import reports ERR_MODULE_NOT_FOUND and strict compilers report missing
|
||||
entry/declarations. Candidate installation is outside the workspace, offline with
|
||||
an unchanged frozen lock and verification of every installed byte. Current/old TS,
|
||||
no-interop CJS, root/legacy runtime and complete notices are covered. Final evidence
|
||||
distinguishes exploratory and final packs after package documentation changes.
|
||||
|
||||
The pending user question describes the incompatible defaults: published recovered
|
||||
3.5.31 delay/fixed height versus unpublished workspace 4.4.0 synchronous video and
|
||||
aspect-derived height. No answer is inferred from elapsed time and these defaults
|
||||
remain unchanged. Continue the chosen policy/regressions before closing 04.
|
||||
Inherited emitter event-name handling still needs behavior review; 05/06 retain
|
||||
full demo/core combinations and supported Safari/WebKit file extraction.
|
||||
|
||||
Revert this checkpoint to undo declaration/entry/generator changes, then run normal
|
||||
builds. Preserve prior 03 lifecycle fixes and upstream attribution. No push, tag,
|
||||
npm publication or hosted CI result is claimed.
|
||||
+1
-1
@@ -538,5 +538,5 @@
|
||||
- PKG-TOOL-THUMB-01: [记录](baselines/thumbnail-release.json) [记录](baselines/thumbnail-contract.md) [记录](changes/2026-09-13-PKG-TOOL-THUMB-01-contract.md) [记录](baselines/thumbnail-contract-validation.json)
|
||||
- PKG-TOOL-THUMB-02: [记录](baselines/thumbnail-behavior-validation.json) [记录](changes/2026-09-13-PKG-TOOL-THUMB-02-behavior.md)
|
||||
- PKG-TOOL-THUMB-03: [记录](changes/2026-09-13-PKG-TOOL-THUMB-03-input-checkpoint.md) [记录](baselines/thumbnail-input-checkpoint.json) [记录](changes/2026-09-13-PKG-TOOL-THUMB-03-lifecycle.md) [记录](baselines/thumbnail-lifecycle-validation.json)
|
||||
- PKG-TOOL-THUMB-04: [记录](changes/2026-09-13-PKG-TOOL-THUMB-04-runtime-types.md) [记录](baselines/thumbnail-runtime-types-validation.json)
|
||||
- PKG-TOOL-THUMB-04: [记录](changes/2026-09-13-PKG-TOOL-THUMB-04-runtime-types.md) [记录](baselines/thumbnail-runtime-types-validation.json) [记录](changes/2026-09-13-PKG-TOOL-THUMB-04-public-types.md) [记录](baselines/thumbnail-public-types-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)
|
||||
@@ -1,5 +1,19 @@
|
||||
# 进度与证据
|
||||
|
||||
## PKG-TOOL-THUMB-04 公开类型与真实安装检查点(doing)
|
||||
|
||||
补齐class/namespace及d.cts/d.mts,修复root ESM并保留直接CJS构造器,新增legacy类型路径。
|
||||
仓库外实际安装验证7种候选编译模式、52项反例和全部文件;冻结Git夹具保留5种缺声明/
|
||||
入口失败对照。明确该夹具不是原始npm包。完整MIT通知在实际tarball验证,相关来源风险关闭。
|
||||
完整build:ts发现并修复MediaBunny辅助类型被误当独立插件的既有问题;原不支持runtime
|
||||
re-export的错误提示在首次CI发现变化后恢复,未削弱测试。三浏览器真实Monaco通过,
|
||||
同时消费核心/Thumbnail/MediaBunny类型并执行工具构造/清理。最终CI1658、339生产TS、
|
||||
3导入通过;main/legacy各49项,运行文件和源码UMD与上个检查点完全一致,171项抽帧证据
|
||||
按哈希复用并明确没有重跑。用户默认行为策略问题仍待答复,04不标完成;05/06继续设备/
|
||||
完整demo/core与历史CSS分发。见[变更](changes/2026-09-13-PKG-TOOL-THUMB-04-public-types.md)
|
||||
和[验证](baselines/thumbnail-public-types-validation.json)。222项:112 done、11 doing、99 todo。
|
||||
|
||||
|
||||
## PKG-TOOL-THUMB-04 运行时 TS 与 emitter 来源检查点(doing)
|
||||
|
||||
8个运行时模块已迁为严格TS,新增类型模块明确option/事件/帧/job/资源状态;declare保留
|
||||
|
||||
@@ -179,6 +179,6 @@
|
||||
| IFRAME-DIST-01 | open / 已复现 | Iframe tool name is unavailable at registry observation and old archive includes distinct helper exports | PKG-IFRAME-04, PKG-IFRAME-06 |
|
||||
| THUMB-COMPAT-01 | open / 已复现 | Thumbnail recovered 3.5.31 and workspace 4.4.0 have conflicting height and event timing defaults | PKG-TOOL-THUMB-04, PKG-TOOL-THUMB-05 |
|
||||
| THUMB-LIFE-01 | open / 已复现 | Thumbnail drop binding and repeated destroy fail; pending work and replacement resource ownership need repair | PKG-TOOL-THUMB-02, PKG-TOOL-THUMB-03, PKG-TOOL-THUMB-05 |
|
||||
| THUMB-PROVENANCE-01 | open / 源码/产物事实 | Thumbnail local emitter attribution and historical distribution completeness remain unverified | PKG-TOOL-THUMB-04, PKG-TOOL-THUMB-06 |
|
||||
| THUMB-PROVENANCE-01 | resolved / 源码/产物事实 | Thumbnail local emitter attribution and historical distribution completeness remain unverified | PKG-TOOL-THUMB-04, PKG-TOOL-THUMB-06 |
|
||||
| THUMB-MEDIA-01 | open / 已复现 | Windows WebKit native Blob URLs cannot load tested MP4 media despite successful HTTP controls | PKG-TOOL-THUMB-05, PKG-TOOL-THUMB-06 |
|
||||
| VENDOR-09 | open / 源码/产物事实 | Thumbnail tiny-emitter 改写来源与最终分发许可 | PKG-TOOL-THUMB-04, PKG-TOOL-THUMB-06 |
|
||||
| VENDOR-09 | resolved / 源码/产物事实 | Thumbnail tiny-emitter 改写来源与最终分发许可 | PKG-TOOL-THUMB-04, PKG-TOOL-THUMB-06 |
|
||||
+29
-10
@@ -370,10 +370,12 @@
|
||||
"refactor/baselines/distribution.json",
|
||||
"refactor/baselines/thumbnail-contract.md",
|
||||
"refactor/baselines/thumbnail-release.json",
|
||||
"refactor/scripts/thumbnail-contract.test.mjs"
|
||||
"refactor/scripts/thumbnail-contract.test.mjs",
|
||||
"refactor/baselines/thumbnail-public-types-validation.json"
|
||||
],
|
||||
"compatibleResolution": "Recovered 3.5.31 has main and a CSS entry; baseline workspace lacks main and points to missing .esm.js/types. Recoverable CDN main is verified against Git, not an original tarball. Preserve supported paths, decide historical CSS disposition, add real declarations and verify isolated installs in 04/06.",
|
||||
"closureCriteria": "隔离打包消费者能加载所有原承诺路径,dist 不作为源码直接手改。"
|
||||
"closureCriteria": "隔离打包消费者能加载所有原承诺路径,dist 不作为源码直接手改。",
|
||||
"workspaceState": "Current root ESM, direct CJS, legacy and declarations pass isolated installs. Missing historical .esm.js was replaced by the real root .mjs entry. Historical 3.5.31 CSS path/disposition and complete original promised-path review remain 06; item stays open."
|
||||
},
|
||||
{
|
||||
"id": "BASE-PERF-01",
|
||||
@@ -3867,8 +3869,10 @@
|
||||
"refactor/changes/2026-09-13-PKG-TOOL-THUMB-02-behavior.md",
|
||||
"test/thumbnail.test.js",
|
||||
"test/browser/thumbnail-tool.spec.js",
|
||||
"refactor/changes/2026-09-13-PKG-TOOL-THUMB-03-lifecycle.md"
|
||||
]
|
||||
"refactor/changes/2026-09-13-PKG-TOOL-THUMB-03-lifecycle.md",
|
||||
"refactor/changes/2026-09-13-PKG-TOOL-THUMB-04-public-types.md"
|
||||
],
|
||||
"workspaceState": "Explicit user question pending: prefer published 3.5.31 defaults with opt-in 4.4 behavior, or accept the documented old-version differences and retain 4.4 defaults. No answer inferred; runtime unchanged."
|
||||
},
|
||||
{
|
||||
"id": "THUMB-LIFE-01",
|
||||
@@ -3910,12 +3914,20 @@
|
||||
],
|
||||
"compatibleResolution": "Historical release declares tiny-emitter; review local emitter provenance/license before TS migration. Keep recovered individual CDN files distinct from full npm archive evidence.",
|
||||
"closureCriteria": "Reviewed emitter attribution and license ledger plus explicit historical artifact recovery limits and complete candidate tarball evidence.",
|
||||
"status": "open",
|
||||
"status": "resolved",
|
||||
"evidence": [
|
||||
"refactor/baselines/thumbnail-contract.md",
|
||||
"refactor/baselines/thumbnail-release.json",
|
||||
"refactor/scripts/thumbnail-contract.test.mjs"
|
||||
]
|
||||
"refactor/scripts/thumbnail-contract.test.mjs",
|
||||
"refactor/baselines/thumbnail-public-types-validation.json"
|
||||
],
|
||||
"resolutionEvidence": [
|
||||
"refactor/baselines/thumbnail-public-types-validation.json",
|
||||
"refactor/baselines/thumbnail-vendor/sources.json",
|
||||
"packages/artplayer-tool-thumbnail/THIRD_PARTY_NOTICES"
|
||||
],
|
||||
"resolutionRationale": "Fixed 2.1.0 upstream comparison and local adaptation reviewed; complete MIT notice verified in every standalone bundle and every byte of the actual installed candidate pack. Exact original copied revision and complete historical npm archive remain unknown and are explicitly bounded, not claimed recovered. Historical CSS/paths remain independently open under BASE-DIST-01.",
|
||||
"workspaceState": "Current candidate source/bundle/packed attribution verified; repeat installed artifact verification when release contents change."
|
||||
},
|
||||
{
|
||||
"id": "THUMB-MEDIA-01",
|
||||
@@ -3939,7 +3951,7 @@
|
||||
"id": "VENDOR-09",
|
||||
"title": "Thumbnail tiny-emitter 改写来源与最终分发许可",
|
||||
"confirmation": "source-observed",
|
||||
"status": "open",
|
||||
"status": "resolved",
|
||||
"owners": [
|
||||
"PKG-TOOL-THUMB-04",
|
||||
"PKG-TOOL-THUMB-06"
|
||||
@@ -3948,11 +3960,18 @@
|
||||
"refactor/baselines/thumbnail-vendor/sources.json",
|
||||
"packages/artplayer-tool-thumbnail/src/emitter.ts",
|
||||
"packages/artplayer-tool-thumbnail/THIRD_PARTY_NOTICES",
|
||||
"test/thumbnail-vendor.test.js"
|
||||
"test/thumbnail-vendor.test.js",
|
||||
"refactor/baselines/thumbnail-public-types-validation.json"
|
||||
],
|
||||
"compatibleResolution": "Preserve original attribution, typed local dispatch compatibility and complete MIT notice in source/package/standalone bundles.",
|
||||
"closureCriteria": "Pinned upstream reference and local adaptation reviewed; all bundle and actual packed distribution notices verified. Do not claim the exact original copied revision or a recovered complete historical archive.",
|
||||
"workspaceState": "TS adaptation and pinned 2.1.0 source/license references recorded; normal build includes package notices. Final installed/packed distribution evidence remains with 04/06."
|
||||
"workspaceState": "Current candidate source/bundle/packed attribution verified; repeat installed artifact verification when release contents change.",
|
||||
"resolutionEvidence": [
|
||||
"refactor/baselines/thumbnail-public-types-validation.json",
|
||||
"refactor/baselines/thumbnail-vendor/sources.json",
|
||||
"packages/artplayer-tool-thumbnail/THIRD_PARTY_NOTICES"
|
||||
],
|
||||
"resolutionRationale": "Fixed 2.1.0 upstream comparison and local adaptation reviewed; complete MIT notice verified in every standalone bundle and every byte of the actual installed candidate pack. Exact original copied revision and complete historical npm archive remain unknown and are explicitly bounded, not claimed recovered. Historical CSS/paths remain independently open under BASE-DIST-01."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import ts from 'typescript'
|
||||
import compat from 'typescript-compat'
|
||||
import { checkFiles, packedFiles } from '../../scripts/package-check.mjs'
|
||||
import { consumerDirectory, removeConsumer, run, workspace, writeJson } from '../../scripts/package-consumer.mjs'
|
||||
import { verifyThumbnailContract } from './thumbnail-contract.mjs'
|
||||
import { hash, readMember } from './releases.mjs'
|
||||
|
||||
async function main() {
|
||||
assert.equal(process.env.npm_config_user_agent?.split(' ')[0], 'yarn/1.22.22', 'Use yarn test:thumbnail-types-package')
|
||||
const yarn = process.env.npm_execpath
|
||||
assert(yarn && fs.existsSync(yarn))
|
||||
const output = fs.mkdtempSync(path.join(workspace, 'refactor/.cache/thumbnail-package-types-'))
|
||||
console.log(`Thumbnail installed package evidence: ${output}`)
|
||||
const contract = verifyThumbnailContract()
|
||||
const frozen = path.join(output, 'frozen-workspace')
|
||||
fs.mkdirSync(frozen)
|
||||
const prefix = 'packages/artplayer-tool-thumbnail/'
|
||||
for (const [file, text] of contract.source) {
|
||||
if (!file.startsWith(prefix)) continue
|
||||
const target = path.join(frozen, file.slice(prefix.length))
|
||||
fs.mkdirSync(path.dirname(target), { recursive: true })
|
||||
fs.writeFileSync(target, text)
|
||||
}
|
||||
const packages = []
|
||||
const matrix = []
|
||||
const runtime = []
|
||||
for (const [label, folder] of [['workspace-fixture', frozen], ['candidate', path.join(workspace, 'packages/artplayer-tool-thumbnail')]]) {
|
||||
const archive = path.join(output, `${label}.tgz`)
|
||||
fs.writeFileSync(path.join(output, `${label}-pack.log`), run([yarn, 'pack', '--filename', archive], folder))
|
||||
const manifest = JSON.parse(readMember(archive, 'package/package.json'))
|
||||
const files = packedFiles(archive)
|
||||
if (label === 'candidate') {
|
||||
checkFiles(manifest, files, [...contract.source.keys()].filter(file => file.startsWith(`${prefix}dist/`)).map(file => `package/${file.slice(prefix.length)}`))
|
||||
assert(files.includes('package/THIRD_PARTY_NOTICES'))
|
||||
assert.equal(readMember(archive, 'package/THIRD_PARTY_NOTICES').toString(), fs.readFileSync(path.join(folder, 'THIRD_PARTY_NOTICES'), 'utf8'))
|
||||
}
|
||||
else {
|
||||
assert.equal(manifest.main, undefined)
|
||||
assert(!files.includes(`package/${manifest.module}`))
|
||||
assert(!files.includes(`package/${manifest.types}`))
|
||||
}
|
||||
const pkg = { label, name: manifest.name, archive, sha256: hash(fs.readFileSync(archive)), manifest, files: Object.fromEntries(files.map(member => [member, hash(readMember(archive, member))])) }
|
||||
packages.push(pkg)
|
||||
const consumer = consumerDirectory()
|
||||
try {
|
||||
writeJson(path.join(consumer, 'package.json'), { name: 'thumbnail-isolated-consumer', private: true, dependencies: { [pkg.name]: `file:${archive.replaceAll('\\', '/')}` } })
|
||||
fs.writeFileSync(path.join(output, `${label}-install.log`), run([yarn, 'install', '--offline', '--ignore-scripts', '--non-interactive'], consumer))
|
||||
const lock = fs.readFileSync(path.join(consumer, 'yarn.lock'))
|
||||
fs.writeFileSync(path.join(output, `${label}-frozen-install.log`), run([yarn, 'install', '--offline', '--frozen-lockfile', '--ignore-scripts', '--non-interactive'], consumer))
|
||||
assert.deepEqual(fs.readFileSync(path.join(consumer, 'yarn.lock')), lock)
|
||||
fs.writeFileSync(path.join(output, `${label}-yarn.lock`), lock)
|
||||
const installed = path.join(consumer, 'node_modules', pkg.name)
|
||||
assert.equal(fs.realpathSync(installed), installed, 'Consumer must not use workspace links')
|
||||
for (const [member, expected] of Object.entries(pkg.files))
|
||||
assert.equal(hash(fs.readFileSync(path.join(installed, member.slice(8)))), expected, member)
|
||||
const runtimeFile = path.join(consumer, 'runtime.cjs')
|
||||
fs.writeFileSync(runtimeFile, `const assert = require('node:assert/strict');
|
||||
const value = require('${pkg.name}');
|
||||
assert.equal(typeof value, 'function');
|
||||
assert.equal(value.default, undefined);
|
||||
assert.equal(value.DEFAULTS.number, 60);
|
||||
${label === 'candidate' ? `const legacy = require('${pkg.name}/legacy'); assert.equal(typeof legacy, 'function'); assert.equal(legacy.default, undefined);` : ''}
|
||||
(async () => {
|
||||
let imported;
|
||||
try { imported = await import('${pkg.name}'); }
|
||||
catch (error) { ${label === 'candidate' ? 'throw error;' : `assert.equal(error.code, 'ERR_MODULE_NOT_FOUND'); console.log(JSON.stringify({ commonjs: 'function', esm: error.code, typesMissing: true })); return;`} }
|
||||
${label === 'candidate' ? `assert.equal(typeof imported.default, 'function'); assert.deepEqual(Object.keys(imported), ['default']); const legacy = await import('${pkg.name}/legacy'); assert.equal(typeof legacy.default, 'function'); console.log(JSON.stringify({ commonjs: 'function', esm: 'function', legacy: 'function', ssrImport: true }));` : `throw new Error('Frozen missing ESM unexpectedly loaded');`}
|
||||
})().catch(error => { console.error(error); process.exitCode = 1; });`)
|
||||
const observation = run([runtimeFile], consumer)
|
||||
fs.writeFileSync(path.join(output, `${label}-runtime.log`), observation)
|
||||
runtime.push({ package: label, ...JSON.parse(observation.trim()) })
|
||||
const modes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']]
|
||||
if (label === 'candidate') modes.push([ts, 'nodenext-cjs-no-interop'], [compat, 'node10-commonjs-no-interop'])
|
||||
for (const [compiler, mode] of modes) {
|
||||
const next = mode.startsWith('nodenext')
|
||||
const noInterop = mode.endsWith('no-interop')
|
||||
const filename = path.join(consumer, `consumer.${next ? mode.includes('-cjs') ? 'cts' : 'mts' : 'ts'}`)
|
||||
const options = { strict: true, noEmit: true, skipLibCheck: false, types: [], esModuleInterop: !noInterop, target: compiler.ScriptTarget.ES2020, lib: ['lib.es2020.d.ts', 'lib.dom.d.ts'], module: next ? compiler.ModuleKind.NodeNext : mode === 'bundler-esm' ? compiler.ModuleKind.ESNext : compiler.ModuleKind.CommonJS, moduleResolution: next ? compiler.ModuleResolutionKind.NodeNext : mode === 'bundler-esm' ? compiler.ModuleResolutionKind.Bundler : compiler.ModuleResolutionKind.NodeJs }
|
||||
const source = label === 'candidate' ? fs.readFileSync(path.join(workspace, noInterop ? 'test/types/thumbnail-commonjs.cts' : 'test/types/thumbnail.ts'), 'utf8') : `import Thumbnail from '${pkg.name}'; new Thumbnail({ fileInput: document.createElement('input') });`
|
||||
function compile(code) {
|
||||
fs.writeFileSync(filename, code)
|
||||
const program = compiler.createProgram([filename], options)
|
||||
for (const file of program.getSourceFiles()) {
|
||||
const real = fs.realpathSync(file.fileName)
|
||||
assert(real.startsWith(fs.realpathSync(consumer) + path.sep) || (program.isSourceFileDefaultLibrary(file) && path.dirname(real) === fs.realpathSync(path.dirname(compiler.sys.getExecutingFilePath()))), `Type escaped installed consumer: ${real}`)
|
||||
}
|
||||
return compiler.getPreEmitDiagnostics(program).map(item => ({ code: item.code, message: compiler.flattenDiagnosticMessageText(item.messageText, '\n').replaceAll(consumer.replaceAll('\\', '/'), '<consumer>') }))
|
||||
}
|
||||
const diagnostics = compile(source)
|
||||
writeJson(path.join(output, `${label}-${compiler.version}-${mode}.json`), diagnostics)
|
||||
if (label === 'candidate') assert.deepEqual(diagnostics, [], `${compiler.version} ${mode}`)
|
||||
else {
|
||||
assert.deepEqual(diagnostics.map(item => item.code), [mode === 'node10-commonjs' ? 2307 : 7016], 'Only the exact frozen missing entry/declaration diagnostic is expected')
|
||||
}
|
||||
const negative = label === 'candidate' ? compile(source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')) : []
|
||||
if (label === 'candidate') assert.equal(negative.length, noInterop ? 1 : 10)
|
||||
matrix.push({ package: label, compiler: compiler.version, mode, diagnostics, negative })
|
||||
}
|
||||
if (label === 'candidate') fs.cpSync(installed, path.join(output, 'installed-artifacts'), { recursive: true })
|
||||
}
|
||||
finally { removeConsumer(consumer) }
|
||||
}
|
||||
writeJson(path.join(output, 'report.json'), { task: 'PKG-TOOL-THUMB-04', scope: 'Frozen Git input fixture and candidate Yarn pack installed outside workspace. The frozen fixture is not an original npm archive; no recovered 3.5.31 complete archive is claimed. Candidate root/legacy runtime, offline frozen install, all files and strict consumers verified.', packages, matrix, runtime })
|
||||
console.log(`Thumbnail installed types verified: ${matrix.length} compiler cases; ${output}`)
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error)
|
||||
process.exitCode = 1
|
||||
})
|
||||
@@ -0,0 +1,74 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
// eslint-disable-next-line test/no-import-node-test -- Public type and editor contract runner.
|
||||
import test from 'node:test'
|
||||
import ts from 'typescript'
|
||||
import compat from 'typescript-compat'
|
||||
import { ESLint } from 'eslint'
|
||||
import { checkConsumer } from '../../scripts/typecheck.mjs'
|
||||
import { checkPluginEditorDeclaration, generatePluginEditorDeclaration } from '../../scripts/plugin-editor-types.mjs'
|
||||
|
||||
const modes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']]
|
||||
const invalid = source => source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')
|
||||
|
||||
test('Thumbnail public declarations check actual callbacks, custom events and invalid calls in current and old TS', () => {
|
||||
const source = fs.readFileSync('test/types/thumbnail.ts', 'utf8')
|
||||
for (const [compiler, mode] of modes) {
|
||||
assert.deepEqual(checkConsumer(compiler, mode, source), [], `${compiler.version} ${mode}`)
|
||||
assert.equal(checkConsumer(compiler, mode, invalid(source)).length, 10, `${compiler.version} ${mode} negatives`)
|
||||
}
|
||||
const commonjs = fs.readFileSync('test/types/thumbnail-commonjs.cts', 'utf8')
|
||||
for (const [compiler, mode] of [[ts, 'nodenext-cjs'], [compat, 'node10-commonjs']]) {
|
||||
assert.deepEqual(checkConsumer(compiler, mode, commonjs), [])
|
||||
assert.equal(checkConsumer(compiler, mode, invalid(commonjs)).length, 1)
|
||||
}
|
||||
assert.deepEqual(checkConsumer(ts, 'node10-commonjs', fs.readFileSync('test/types/thumbnail-source.ts', 'utf8')), [], 'Source and declaration constructors must be mutually assignable')
|
||||
})
|
||||
|
||||
test('Thumbnail editor declaration is generated with its exact uppercase global and works in old TS', async () => {
|
||||
const source = fs.readFileSync('packages/artplayer-tool-thumbnail/types/artplayer-tool-thumbnail.d.ts', 'utf8')
|
||||
const code = generatePluginEditorDeclaration(source, 'ArtplayerToolThumbnail')
|
||||
const file = 'docs/assets/ts/artplayer-tool-thumbnail.d.ts'
|
||||
const [formatted] = await new ESLint({ fix: true, fixTypes: ['layout'] }).lintText(code, { filePath: file })
|
||||
assert.equal(formatted.errorCount, 0)
|
||||
assert.equal(fs.readFileSync(file, 'utf8').replaceAll('\r\n', '\n'), formatted.output || code)
|
||||
const consumer = `const input = document.createElement('input');
|
||||
const options: ArtplayerToolThumbnail.Option = { fileInput: input };
|
||||
const tool: ArtplayerToolThumbnail = new ArtplayerToolThumbnail(options);
|
||||
tool.on('update', (url, progress) => url + progress.toFixed());
|
||||
tool.on('custom', (value: number) => value.toFixed());
|
||||
const promise: Promise<void> = tool.start();
|
||||
// @ts-expect-error No lowercase global exists.
|
||||
const bad = new artplayerToolThumbnail(options);
|
||||
// @ts-expect-error Invalid known event payload.
|
||||
tool.emit('update', 12, 'url');`
|
||||
for (const compiler of [ts, compat]) {
|
||||
const core = fs.readFileSync('docs/assets/ts/artplayer.d.ts', 'utf8')
|
||||
assert.deepEqual(checkPluginEditorDeclaration(code, core, consumer, compiler), [])
|
||||
assert.equal(checkPluginEditorDeclaration(code, core, invalid(consumer), compiler).length, 2)
|
||||
}
|
||||
assert.throws(() => generatePluginEditorDeclaration(source.replace('declare class ArtplayerToolThumbnail', 'declare class WrongClass'), 'ArtplayerToolThumbnail'), /Unexpected CommonJS editor class/)
|
||||
})
|
||||
|
||||
test('Full editor generation bundles explicit local MediaBunny types instead of publishing an invalid media global', () => {
|
||||
const source = fs.readFileSync('packages/artplayer-proxy-mediabunny/types/artplayer-proxy-mediabunny.d.ts', 'utf8')
|
||||
const media = fs.readFileSync('packages/artplayer-proxy-mediabunny/types/media.d.ts', 'utf8')
|
||||
const core = fs.readFileSync('docs/assets/ts/artplayer.d.ts', 'utf8')
|
||||
const frozenGenerator = `${source.replace(/^import.*$/gm, '')}\nexport = artplayerProxyMediabunny;\nexport as namespace artplayerProxyMediabunny;`
|
||||
const code = generatePluginEditorDeclaration(source, 'artplayerProxyMediabunny', { './media': media })
|
||||
const consumer = `declare const player: artplayerProxyMediabunny.MediaBunnyPlayer;
|
||||
const canvas: (art: Artplayer) => HTMLCanvasElement = artplayerProxyMediabunny();
|
||||
const time: number | undefined = player.mediabunny?.currentTime;
|
||||
// @ts-expect-error HLS levels are read from state, not directly from the player alias.
|
||||
const bad = player.mediabunny?.levels;`
|
||||
for (const compiler of [ts, compat]) {
|
||||
assert(checkPluginEditorDeclaration(frozenGenerator, core, '', compiler).some(item => item.code === 2307), 'The old generator leaves a missing relative type import')
|
||||
assert.deepEqual(checkPluginEditorDeclaration(code, core, consumer, compiler), [])
|
||||
assert.equal(checkPluginEditorDeclaration(code, core, invalid(consumer), compiler).length, 1)
|
||||
}
|
||||
assert.throws(() => generatePluginEditorDeclaration(source, 'artplayerProxyMediabunny'), /Unsupported editor type re-export/)
|
||||
assert.throws(() => generatePluginEditorDeclaration(source, 'artplayerProxyMediabunny', { './media': media + '\nexport interface Extra {}' }), /explicitly cover/)
|
||||
const common = fs.readFileSync('docs/assets/js/common.js', 'utf8')
|
||||
assert(common.includes('./assets/ts/artplayer-tool-thumbnail.d.ts'))
|
||||
assert(!common.includes('./assets/ts/media.d.ts'))
|
||||
})
|
||||
+3
-1
@@ -3815,7 +3815,9 @@
|
||||
"acceptance": "严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容;为 BASE-DIST-01 提供兼容入口/声明及消费者验证",
|
||||
"evidence": [
|
||||
"changes/2026-09-13-PKG-TOOL-THUMB-04-runtime-types.md",
|
||||
"baselines/thumbnail-runtime-types-validation.json"
|
||||
"baselines/thumbnail-runtime-types-validation.json",
|
||||
"changes/2026-09-13-PKG-TOOL-THUMB-04-public-types.md",
|
||||
"baselines/thumbnail-public-types-validation.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1037,7 +1037,7 @@
|
||||
"PKG-TOOL-THUMB-06"
|
||||
],
|
||||
"sourceStatus": "Local on/once/emit/off structurally correspond to the fixed tiny-emitter 2.1.0 reference; recovered 3.5.31 declared ^2.1.0. Exact original copied revision is unknown. This migration adds types while preserving the local class and dispatch behavior.",
|
||||
"licenseStatus": "Pinned upstream MIT notice copied verbatim into package THIRD_PARTY_NOTICES and included by the normal standalone build banner. Final packed distribution verification remains 06.",
|
||||
"licenseStatus": "Full pinned upstream MIT notice verified in package, all standalone bundles and actual installed candidate tarball. Repeat on changed release candidates; original copy revision remains unknown.",
|
||||
"upstreamSources": [
|
||||
"https://raw.githubusercontent.com/scottcorgan/tiny-emitter/2.1.0/index.js",
|
||||
"https://raw.githubusercontent.com/scottcorgan/tiny-emitter/2.1.0/LICENSE",
|
||||
@@ -1049,14 +1049,16 @@
|
||||
{
|
||||
"path": "packages/artplayer-tool-thumbnail/src/emitter.ts",
|
||||
"algorithm": "sha256-lf",
|
||||
"sha256": "2844fa8d279d35d676f61eef4f8a3e173146548c64b8e49f2dabfc25aec56668"
|
||||
"sha256": "bd4d5472361edd2e31a6ae1aecce01e310e4086edebe434733d0f5b5eea37a0b"
|
||||
}
|
||||
],
|
||||
"reviewEvidence": [
|
||||
"refactor/baselines/thumbnail-vendor/sources.json",
|
||||
"test/thumbnail-runtime.test.js",
|
||||
"test/thumbnail-vendor.test.js",
|
||||
"refactor/changes/2026-09-13-PKG-TOOL-THUMB-04-runtime-types.md"
|
||||
"refactor/changes/2026-09-13-PKG-TOOL-THUMB-04-runtime-types.md",
|
||||
"refactor/changes/2026-09-13-PKG-TOOL-THUMB-04-public-types.md",
|
||||
"refactor/baselines/thumbnail-public-types-validation.json"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -99,3 +99,19 @@ Audio Track 的自有源码由 packages/artplayer-plugin-audio-track/tsconfig.js
|
||||
详见包内 ARCHITECTURE.md;不能将部分参数直接覆盖默认签名而破坏旧推断。
|
||||
|
||||
新迁移包复制分包配置并按实际运行环境调整,加入特有正反例;编译器入口自动发现包配置。TS 构建入口与声明生成由 ENG-06/07 和各包迁移处理;不能只增加一个空 config 就宣布包迁移完成。所有生产变更仍按每任务独立提交和包内架构文档规则交付。
|
||||
|
||||
## Thumbnail 与完整编辑器声明生成
|
||||
|
||||
Thumbnail 的 public d.ts 是 CommonJS class/namespace,d.cts/d.mts 共享其构造器身份。
|
||||
源码和声明双向检查;自定义事件可推导调用方元组,任意回调抛出的 message 保持 unknown。
|
||||
`yarn test:thumbnail-types-package` 在仓库外安装实际 Yarn tarball,检查当前/旧 TS、
|
||||
无 interop 的 CommonJS、root/legacy 导入、全部文件指纹和许可。旧 Git 夹具的 ESM/声明
|
||||
失败保留为对照,不声称它是原始 npm 包。完整默认兼容策略仍由 THUMB-COMPAT-01 决定。
|
||||
|
||||
`yarn build:ts` 只枚举与包名对应的公开声明入口,辅助类型不作为独立全局导入。
|
||||
语义生成器支持 CommonJS 类、函数及 callable const;显式的相对 type re-export
|
||||
只接受调用方提供且完整匹配导出列表的本地类型源。MediaBunny 的 media.d.ts 使用此路径
|
||||
合入主编辑器声明,避免生成无效 media 全局或遗留失效的相对 import。
|
||||
`refactor/scripts/thumbnail-types.test.mjs` 覆盖该历史失败与旧/新编译器;
|
||||
`test/browser/thumbnail-editor-types.spec.js` 使用真实 Monaco worker 验证核心、工具与
|
||||
MediaBunny 声明并执行工具构造/清理。媒体解码和完整 demo 组合仍须独立验证。
|
||||
Reference in new issue
Block a user