build(site): [SITE-07] verify modified forks and preserve upstream author notices

This commit is contained in:
Harvey Zhao committed 2026-09-15 07:50:12 +08:00
1 parent d2a83f8a69
commit 3136eb2402
31 files changed
+2567 -23

No files matched your search

+5 -1
View File
@@ -45,6 +45,8 @@ assert.deepEqual(consoleGroup?.components?.map(component => component.name).sort
'is-object',
'is-window',
'linkifyjs',
'murmurhash-js (Gary Court)',
'murmurhash2 (Austin Appleby)',
'object-assign',
'parcel-bundler',
'process',
@@ -54,13 +56,15 @@ assert.deepEqual(consoleGroup?.components?.map(component => component.name).sort
'react-inspector',
'react-is',
'regenerator-runtime',
'replicator (console-feed fork)',
'scheduler',
'shallowequal',
'simple-html-tokenizer',
'styled-components',
'stylis (Emotion fork)',
'stylis-rule-sheet',
], 'Do not silently drop verified console component attribution')
assert.equal(consoleGroup?.notices.length, 38, 'Missing reviewed console notice')
assert.equal(consoleGroup?.notices.length, 42, 'Missing reviewed console notice')
const vconsoleNotices = manifest.groups.find(group => group.name === 'vconsole')?.notices.map(notice => notice.target)
for (const name of ['LICENSE', 'MIT-LICENSE', 'ATTRIBUTION.md'])
assert(vconsoleNotices?.includes(`docs/licenses/vconsole/${name}`), `Missing vConsole notice: ${name}`)
+2 -2
View File
@@ -23,7 +23,7 @@ files retain their exact upstream bytes, including final blank lines.
`yarn check:site-notices`. The write command cannot bless altered vendor assets;
review the new archive and license evidence before changing the manifest. The
CLI prevents accidentally dropping any of the three groups, the ten reviewed
Monaco/vConsole components, the 38 identified console components or their notice
Monaco/vConsole components, the 42 identified console components or their notice
count, and vConsole's original license, supplemental MIT body and attribution.
Component references require their runtime assets and every notice before writing.
Source notices
@@ -60,7 +60,7 @@ The desktop console's owned TS entry/view and lifecycle now build through
`build:console` / `check:console`; see [console maintenance](console/README.md).
Its other 100 Parcel modules remain frozen and now reproduce exactly from fixed
archives, including the Parcel loader. Its package/loader licenses, two embedded
headers and three additional embedded dependency licenses now ship as 38 files
headers, embedded dependency licenses and upstream author notices now ship as 42 files
under `docs/licenses/console/`. Full embedded
attribution remains open; the generated index explicitly preserves that boundary.
+23 -6
View File
@@ -163,11 +163,28 @@ tokenizer members exactly reproduce with the self-contained Babel 6.26.0 compile
and es2015 loose preset. Archive, compiler, source and output bytes are checked.
The Git archive's SHA-512 is a measured fingerprint, not an npm registry SRI;
the recorded immutable URL and dependency string retain its actual provenance.
These three licenses are included in the 38 console notice outputs. The normal
These three licenses are included in the console notice outputs. The normal
site build still executes only the owned TS build; historical reproduction now
fetches 39 archives only when explicitly run with `--fetch`.
fetches 42 archives and fixed Git sources only when explicitly run with `--fetch`.
This is partial embedded review. Remaining follow-ups are replicator, Emotion
stylis/hash/cache and the Component Stack Overflow reference. Source-map names and
comment links alone are leads, not proof of complete notice coverage. Do not close
VENDOR-08 solely because all Parcel modules reproduce.
`attribution.ts` verifies immutable Git content against SHA-256 and Git blob SHA-1,
and compares compiled forks with the archived runtime before its single terminal
inline source map. `console-derived-attribution.json` pins the console-feed
replicator TS revision and TypeScript 4.1.2 ES3/CommonJS/LF compiler options. The
fork's runtime matches exactly; it is not a verbatim npm replicator release.
Ivan Nikulin's original MIT notice is retained from the 1.0.x source family.
Historical compiler extraction permits 16 MiB because this fixed compiler member
is 9,002,076 bytes; it is not an installed workspace dependency.
Emotion's Stylis source matches its fixed tag commit and declares Stylis 3.5.4.
The original build recipe modifies Stylis and uses an unversioned online Closure
service. We preserve that recipe and the matching generated source without claiming
to rerun the earlier service. Sultan Tarimo's MIT notice covers the upstream
attribution, including the separately identified rule-sheet reference in cache.
Gary Court's original README/MIT text and Austin Appleby's public-domain header
are preserved for the two explicit MurmurHash source references. Original sources
and excerpts are hashed; attribution is never inferred from the outer Emotion MIT.
There are now 42 console notice outputs. Remaining follow-ups are the Component
Stack Overflow reference and final embedded-source review. Do not close VENDOR-08
solely because all Parcel modules reproduce.
@@ -0,0 +1,29 @@
import assert from 'node:assert/strict'
import { Buffer } from 'node:buffer'
import { createHash } from 'node:crypto'
import { hash } from './provenance.ts'
export interface GitSource { source: string, sha256: string, gitBlobSha: string, apiUrl: string }
export function verifyGitSource(bytes: Uint8Array, source: GitSource): void {
assert.equal(hash(bytes), source.sha256, 'Fixed Git source content changed')
const blob = createHash('sha1').update(`blob ${bytes.byteLength}\0`).update(bytes).digest('hex')
assert.equal(blob, source.gitBlobSha, 'Fixed Git blob identity differs')
}
export function decodeGitSource(response: Uint8Array, source: GitSource): Uint8Array {
const data: { encoding: string, content: string } = JSON.parse(Buffer.from(response).toString('utf8'))
assert.equal(data.encoding, 'base64', 'Unexpected Git source encoding')
assert.equal(typeof data.content, 'string', 'Missing Git source content')
const bytes = Buffer.from(data.content, 'base64')
verifyGitSource(bytes, source)
return bytes
}
export function verifyForkOutput(compiled: string, target: string): void {
const marker = '//# sourceMappingURL=data:application/json;base64,'
const start = target.indexOf(marker)
assert(start >= 0 && !target.includes(marker, start + 1), 'Expected one terminal inline source map')
assert(/^[A-Z0-9+/=]+$/i.test(target.slice(start + marker.length)), 'Unexpected inline source map trailer')
assert.equal(compiled, target.slice(0, start), 'Fork compiled runtime differs')
}
+59 -4
View File
@@ -1,3 +1,4 @@
import type { GitSource } from './attribution.ts'
import type { EmbeddedNotice } from './embedded-notices.ts'
import type { Member, SourceMapRecord, TransformedSource } from './embedded-sources.ts'
import type { Archive, External, Source } from './provenance.ts'
@@ -10,6 +11,7 @@ import { createRequire } from 'node:module'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
import { decodeGitSource, verifyForkOutput, verifyGitSource } from './attribution.ts'
import { extractNotice } from './embedded-notices.ts'
import { verifyMappedSources, verifyTransformedSources } from './embedded-sources.ts'
import { hash, parcelModules, verifyArchive, verifyModules, verifyPackageEdges } from './provenance.ts'
@@ -46,12 +48,23 @@ interface EmbeddedProvenance {
tokenizer: { dependency: Member, value: string, sources: TransformedSource[] }
}
interface HistoricalBabel { version: string, transform: (source: string, options: EmbeddedProvenance['compiler']['options']) => { code: string } }
interface DerivedProvenance {
archives: (Archive & { id: string, notices: Notice[] })[]
compiler: { archive: Archive, member: string, sha256: string, version: string, options: { target: number, module: number, newLine: number } }
remotes: (GitSource & { id: string })[]
replicator: { source: string, target: Member, upstream: Member }
stylis: { source: string, target: Member, dependency: Member, dependencyVersion: string, recipe: string }
hash: { source: string, notice: EmbeddedNotice, reference: Member, references: string[] }
cache: { reference: Member, upstreamUrl: string, notice: string }
}
interface HistoricalTypeScript { version: string, transpileModule: (source: string, options: { compilerOptions: DerivedProvenance['compiler']['options'] }) => { outputText: string } }
const root = fileURLToPath(new URL('../../../', import.meta.url))
const record: Provenance = JSON.parse(fs.readFileSync(path.join(root, 'refactor/baselines/console-feed-provenance.json'), 'utf8'))
const common: SourceGroup<CommonSource> = JSON.parse(fs.readFileSync(path.join(root, 'refactor/baselines/console-commonjs-provenance.json'), 'utf8'))
const esm: EsmProvenance = JSON.parse(fs.readFileSync(path.join(root, 'refactor/baselines/console-esm-provenance.json'), 'utf8'))
const embeddedSources: EmbeddedProvenance = JSON.parse(fs.readFileSync(path.join(root, 'refactor/baselines/console-embedded-sources.json'), 'utf8'))
const derived: DerivedProvenance = JSON.parse(fs.readFileSync(path.join(root, 'refactor/baselines/console-derived-attribution.json'), 'utf8'))
assert(process.argv.slice(2).every(arg => arg === '--fetch'), 'Use reproduce.ts [--fetch]')
assert.equal(process.version, `v${fs.readFileSync(path.join(root, '.node-version'), 'utf8').trim()}`, 'Use canonical Node')
const cacheRoot = fs.realpathSync(path.join(root, 'refactor/.cache'))
@@ -70,7 +83,7 @@ async function download(url: string) {
throw error
}
}
const allArchives = [record.archive, record.compiler.archive, ...common.archives, ...esm.archives, esm.babel.archive, esm.parcel.archive, ...embeddedSources.archives, embeddedSources.compiler.archive]
const allArchives = [record.archive, record.compiler.archive, ...common.archives, ...esm.archives, esm.babel.archive, esm.parcel.archive, ...embeddedSources.archives, embeddedSources.compiler.archive, ...derived.archives, derived.compiler.archive]
const allArchiveIds = new Set(allArchives.map(archive => `${encodeURIComponent(archive.name)}-${archive.version}`))
assert.equal(allArchiveIds.size, allArchives.length, 'Duplicate reproduction archive')
for (const archive of allArchives) {
@@ -82,7 +95,7 @@ for (const archive of allArchives) {
}
verifyArchive(fs.readFileSync(target), archive)
}
const readMember = (name: string, member: string) => execFileSync('tar', ['-xOzf', path.join(cache, name), member], { maxBuffer: 8 * 1024 * 1024 })
const readMember = (name: string, member: string) => execFileSync('tar', ['-xOzf', path.join(cache, name), member], { maxBuffer: 16 * 1024 * 1024 })
const compilerBytes = readMember('terser-3.17.0.tgz', record.compiler.member)
assert.equal(hash(compilerBytes), record.compiler.sha256, 'Compiler member changed')
const compilerPath = path.join(cache, 'terser.cjs')
@@ -195,7 +208,7 @@ function readEmbedded(source: Member) {
assert(allArchiveIds.has(source.archive), 'Unknown embedded source archive')
return readMember(`${source.archive}.tgz`, source.member)
}
for (const archive of embeddedSources.archives) {
for (const archive of [...embeddedSources.archives, ...derived.archives]) {
assert.equal(archive.id, `${encodeURIComponent(archive.name)}-${archive.version}`, 'Embedded archive ID differs')
for (const notice of archive.notices) {
assert.equal(hash(readMember(`${archive.id}.tgz`, notice.member)), notice.sha256, 'Embedded upstream license changed')
@@ -220,4 +233,46 @@ assert.equal(legacyBabel.version, compiler.version, 'Embedded compiler version c
assert.deepEqual(compiler.options, { presets: [['es2015', { loose: true }]] }, 'Embedded compiler options changed')
const transformedCount = verifyTransformedSources(tokenizer.sources, readEmbedded, source => legacyBabel.transform(source, compiler.options).code)
assert.equal(transformedCount, 7, 'Incomplete tokenizer scope')
console.log(JSON.stringify({ exactModules: identified.size, consoleFeed: count, commonjs: commonCount, esm: esmCount, parcelPrelude: true, packageEdges: true, embeddedNotices: embedded.notices.length, embeddedMappedSources: mappedCount, embeddedTransformedSources: transformedCount, unresolved: 0, licenseClosure: false }))
const gitSources = new Map<string, Buffer>()
assert.equal(derived.remotes.length, 5, 'Incomplete derived Git source scope')
for (const source of derived.remotes) {
assert(!gitSources.has(source.id), 'Duplicate derived Git source')
const bytes = fs.readFileSync(path.join(root, source.source))
verifyGitSource(bytes, source)
if (process.argv.includes('--fetch'))
decodeGitSource(await download(source.apiUrl), source)
gitSources.set(source.id, bytes)
}
function gitSource(id: string): Buffer {
const bytes = gitSources.get(id)
assert(bytes, `Missing derived Git source: ${id}`)
return bytes
}
function derivedMember(member: Member): Buffer {
const bytes = readEmbedded(member)
assert.equal(hash(bytes), member.sha256, 'Derived source member changed')
return bytes
}
const tsCompiler = derived.compiler
const tsBytes = readMember(`${tsCompiler.archive.name}-${tsCompiler.archive.version}.tgz`, tsCompiler.member)
assert.equal(hash(tsBytes), tsCompiler.sha256, 'Historical TypeScript compiler changed')
const tsPath = path.join(cache, 'typescript4.cjs')
fs.writeFileSync(tsPath, tsBytes)
const historicalTs = require(tsPath) as HistoricalTypeScript
assert.equal(historicalTs.version, tsCompiler.version, 'Historical TypeScript version changed')
assert.deepEqual(tsCompiler.options, { target: 0, module: 1, newLine: 1 }, 'Historical TypeScript options changed')
const fork = historicalTs.transpileModule(gitSource(derived.replicator.source).toString('utf8'), { compilerOptions: tsCompiler.options }).outputText
verifyForkOutput(fork, derivedMember(derived.replicator.target).toString('utf8'))
derivedMember(derived.replicator.upstream)
assert(gitSource(derived.stylis.source).equals(derivedMember(derived.stylis.target)), 'Emotion Git/npm source differs')
const stylisPackage: { devDependencies: { stylis: string } } = JSON.parse(derivedMember(derived.stylis.dependency).toString('utf8'))
assert.equal(stylisPackage.devDependencies.stylis, derived.stylis.dependencyVersion, 'Emotion Stylis dependency changed')
assert(gitSource(derived.stylis.recipe).toString('utf8').includes('https://closure-compiler.appspot.com/compile'), 'Historical Closure recipe changed')
const murmurSource = derivedMember(derived.hash.reference).toString('utf8')
for (const reference of derived.hash.references)
assert(murmurSource.includes(reference), 'MurmurHash source attribution changed')
const publicDomain = extractNotice(gitSource(derived.hash.source), derived.hash.notice)
assert.equal(fs.readFileSync(path.join(root, derived.hash.notice.source), 'utf8'), publicDomain, 'Public domain notice changed')
assert(derivedMember(derived.cache.reference).toString('utf8').includes(derived.cache.upstreamUrl), 'Rule-sheet source attribution changed')
assert(fs.readFileSync(path.join(root, derived.cache.notice), 'utf8').includes('Copyright (c) 2016 Sultan Tarimo'), 'Rule-sheet attribution missing')
console.log(JSON.stringify({ exactModules: identified.size, consoleFeed: count, commonjs: commonCount, esm: esmCount, parcelPrelude: true, packageEdges: true, embeddedNotices: embedded.notices.length, embeddedMappedSources: mappedCount, embeddedTransformedSources: transformedCount, derivedGitSources: gitSources.size, replicatorFork: true, emotionStylisSource: true, unresolved: 0, licenseClosure: false }))
+65 -1
View File
@@ -944,7 +944,7 @@
"name": "console",
"version": "legacy-vendor-with-TS-adapter",
"tarball": "https://github.com/zhw2590582/ArtPlayer/blob/5d6b2f22bc75213ac97cff0bcffa4a1ce2bc786c/docs/assets/js/console.js",
"review": "The 100 third-party Parcel modules and loader have exact source reproduction. The two owned adapter modules are built from TypeScript. Preserved notices also cover two embedded license headers, all 17 react-inspector external source-map members, and all seven HTML tokenizer sources from its fixed Git dependency. Embedded attribution review is still incomplete; this inventory is not publication clearance.",
"review": "The 100 third-party Parcel modules and loader have exact source reproduction. The two owned adapter modules are built from TypeScript. Preserved notices also cover two embedded license headers, all 17 react-inspector external source-map members, and all seven HTML tokenizer sources from its fixed Git dependency. Embedded attribution review is still incomplete; this inventory is not publication clearance. Modified replicator and Emotion/Stylis copies retain upstream author licenses; the original Emotion online Closure stage is not claimed to be reproducible. MurmurHash credits include Gary Court and Austin Appleby.",
"roots": [
"docs/assets/js/console.js"
],
@@ -1145,6 +1145,26 @@
"source": "refactor/baselines/site-vendor/console-embedded/simple-html-tokenizer-04799f4-LICENSE.txt",
"target": "docs/licenses/console/simple-html-tokenizer/LICENSE",
"sha256": "c41881de2a9f2200936648343500524be154eb79f649582f9582dd251b051b11"
},
{
"source": "refactor/baselines/site-vendor/console-attribution/replicator-1.0.1-LICENSE.txt",
"target": "docs/licenses/console/replicator/LICENSE",
"sha256": "52f634086a97b3861a35afff7acf703ddbd5add8e617f6a1f540ae2f2c621284"
},
{
"source": "refactor/baselines/site-vendor/console-attribution/stylis-3.5.4-LICENSE.txt",
"target": "docs/licenses/console/stylis/LICENSE",
"sha256": "045109dfd0547aa898408d08593e1adc41e9705519ba8a20baff89e7c6c52e91"
},
{
"source": "refactor/baselines/site-vendor/console-attribution/gary-readme.txt",
"target": "docs/licenses/console/murmurhash-js/README.md",
"sha256": "73b67c593a2a0d6395bfe90d3bad32556ed5e7ce71883062efebc643d4e4b7b4"
},
{
"source": "refactor/baselines/site-vendor/console-attribution/murmurhash2-public-domain.txt",
"target": "docs/licenses/console/murmurhash2/PUBLIC-DOMAIN.txt",
"sha256": "50e13b0a1963f0b382255d2425df245dbdfd209201621e8c157687caedbbb949"
}
],
"components": [
@@ -1565,6 +1585,50 @@
"notices": [
"docs/licenses/console/simple-html-tokenizer/LICENSE"
]
},
{
"name": "replicator (console-feed fork)",
"version": "modified; upstream 1.0.x license",
"tarball": "https://registry.npmjs.org/replicator/-/replicator-1.0.1.tgz",
"assets": [
"docs/assets/js/console.js"
],
"notices": [
"docs/licenses/console/replicator/LICENSE"
]
},
{
"name": "stylis (Emotion fork)",
"version": "3.5.4-derived",
"tarball": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz",
"assets": [
"docs/assets/js/console.js"
],
"notices": [
"docs/licenses/console/stylis/LICENSE"
]
},
{
"name": "murmurhash-js (Gary Court)",
"version": "license commit 23dc2e7",
"tarball": "https://github.com/garycourt/murmurhash-js/blob/23dc2e7008582ca3ef69d8623f02bba4c8dd05e5/README.md",
"assets": [
"docs/assets/js/console.js"
],
"notices": [
"docs/licenses/console/murmurhash-js/README.md"
]
},
{
"name": "murmurhash2 (Austin Appleby)",
"version": "61a0530",
"tarball": "https://github.com/aappleby/smhasher/blob/61a0530f28277f2e850bfc39600ce61d02b518de/src/MurmurHash2.cpp",
"assets": [
"docs/assets/js/console.js"
],
"notices": [
"docs/licenses/console/murmurhash2/PUBLIC-DOMAIN.txt"
]
}
]
}