mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-09 20:26:16 -08:00
build(site): [SITE-07] verify Monaco TypeScript worker and original notices
This commit is contained in:
1 parent
fb1faffbe9
commit
11296975f1
31 files changed
+6580
-8
No files matched your search
@@ -18,6 +18,7 @@ assert.deepEqual(manifest.groups.filter(group => group.name !== 'console').flatM
|
||||
'regenerator-runtime',
|
||||
'style-loader',
|
||||
'svelte',
|
||||
'typescript (Monaco worker)',
|
||||
'webpack',
|
||||
], 'Do not silently drop verified bundled component attribution')
|
||||
const consoleGroup = manifest.groups.find(group => group.name === 'console')
|
||||
@@ -69,6 +70,8 @@ assert.deepEqual(consoleGroup?.components?.map(component => component.name).sort
|
||||
], 'Do not silently drop verified console component attribution')
|
||||
assert.equal(consoleGroup?.notices.length, 47, 'Missing reviewed console notice')
|
||||
const vconsoleNotices = manifest.groups.find(group => group.name === 'vconsole')?.notices.map(notice => notice.target)
|
||||
const typeScriptNotices = manifest.groups.find(group => group.name === 'monaco-editor')?.components?.find(component => component.name === 'typescript (Monaco worker)')?.notices
|
||||
assert.deepEqual(typeScriptNotices?.map(target => target.split('/').pop()).sort(), ['ATTRIBUTION.md', 'CopyrightNotice.txt', 'LICENSE.txt', 'ThirdPartyNoticeText.txt'], 'Missing TypeScript component notice')
|
||||
for (const name of ['LICENSE', 'MIT-LICENSE', 'ATTRIBUTION.md'])
|
||||
assert(vconsoleNotices?.includes(`docs/licenses/vconsole/${name}`), `Missing vConsole notice: ${name}`)
|
||||
verifyConsoleNoticeSources(process.cwd(), manifest)
|
||||
|
||||
@@ -22,7 +22,7 @@ files retain their exact upstream bytes, including final blank lines.
|
||||
`../build-site-notices.mjs` provides `yarn build:site-notices` and read-only
|
||||
`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
|
||||
CLI prevents accidentally dropping any of the three groups, the eleven reviewed
|
||||
Monaco/vConsole components, the 44 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.
|
||||
@@ -68,3 +68,8 @@ limits are recorded in `refactor/console-notice-review.md`.
|
||||
Follow-up: finish Monaco's broader bundled-component notice audit and remaining
|
||||
fonts/media. Do not upgrade these assets
|
||||
without verifying globals, AMD/worker paths, CSS, consoleLog and user interaction.
|
||||
|
||||
Monaco's actual TypeScript 4.4.4 worker now has a separate source proof and notice
|
||||
supplement; its original notice's 2.7.2 label is retained and explained. See
|
||||
[Monaco maintenance](monaco/README.md) for the fixed six source adaptations,
|
||||
minifier reconstruction, exact-byte checks and remaining component review scope.
|
||||
@@ -920,6 +920,26 @@
|
||||
"source": "refactor/baselines/site-vendor/codicons-0.0.26/ATTRIBUTION.txt",
|
||||
"target": "docs/licenses/monaco-editor/codicons/ATTRIBUTION.md",
|
||||
"sha256": "ac4a57ce60aebaec1796b715d075273c4fd849853cd943fb92790c61ad038a5f"
|
||||
},
|
||||
{
|
||||
"source": "refactor/baselines/site-vendor/monaco-typescript/LICENSE.txt",
|
||||
"target": "docs/licenses/monaco-editor/typescript/LICENSE.txt",
|
||||
"sha256": "a7d00bfd54525bc694b6e32f64c7ebcf5e6b7ae3657be5cc12767bce74654a47"
|
||||
},
|
||||
{
|
||||
"source": "refactor/baselines/site-vendor/monaco-typescript/CopyrightNotice.txt",
|
||||
"target": "docs/licenses/monaco-editor/typescript/CopyrightNotice.txt",
|
||||
"sha256": "0d9e78b962c1f6215aa974365d263c50683b6855db45c6c0ed67ee1002974dc6"
|
||||
},
|
||||
{
|
||||
"source": "refactor/baselines/site-vendor/monaco-typescript/ThirdPartyNoticeText.txt",
|
||||
"target": "docs/licenses/monaco-editor/typescript/ThirdPartyNoticeText.txt",
|
||||
"sha256": "0a64d2681a1e3c7ccd1c2a146a68812d4a23e66ca7cc1dea0e30dbd81e80c369"
|
||||
},
|
||||
{
|
||||
"source": "refactor/baselines/site-vendor/monaco-typescript/ATTRIBUTION.txt",
|
||||
"target": "docs/licenses/monaco-editor/typescript/ATTRIBUTION.md",
|
||||
"sha256": "edb4b7526e86fadf7461ffda983f9f56a96472cf1ebf639947948e405ed95321"
|
||||
}
|
||||
],
|
||||
"review": "The complete upstream Monaco LICENSE/ThirdPartyNotices are retained. The unmodified bundled Codicons font exactly matches @vscode/codicons 0.0.26; its historical README, CC BY 4.0 content license, MIT code license and added attribution are distributed below. Other site assets remain subject to their separate provenance reviews.",
|
||||
@@ -937,6 +957,20 @@
|
||||
"docs/licenses/monaco-editor/codicons/README.md",
|
||||
"docs/licenses/monaco-editor/codicons/ATTRIBUTION.md"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "typescript (Monaco worker)",
|
||||
"version": "4.4.4",
|
||||
"tarball": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz",
|
||||
"assets": [
|
||||
"docs/assets/js/vs/language/typescript/tsWorker.js"
|
||||
],
|
||||
"notices": [
|
||||
"docs/licenses/monaco-editor/typescript/LICENSE.txt",
|
||||
"docs/licenses/monaco-editor/typescript/CopyrightNotice.txt",
|
||||
"docs/licenses/monaco-editor/typescript/ThirdPartyNoticeText.txt",
|
||||
"docs/licenses/monaco-editor/typescript/ATTRIBUTION.md"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# Monaco vendor maintenance
|
||||
|
||||
The site keeps Monaco 0.30.1 at its existing `assets/js/vs` paths. Its 99 files
|
||||
match the official archive, with the recorded CSS line-ending difference. Do not
|
||||
edit these assets directly or upgrade Monaco as part of a license correction.
|
||||
|
||||
## TypeScript worker
|
||||
|
||||
`typescript.ts` describes the six browser adaptations from the fixed upstream
|
||||
`monaco-typescript/scripts/importTypescript.js`, source-map removal and RequireJS's
|
||||
removal of the top-level strict directive. Each adaptation must occur once; strings
|
||||
containing emitted strict directives are preserved. The adapted 4.4.4 service is
|
||||
an exact 9,698,327-character segment in the archived development worker.
|
||||
|
||||
`reproduce-typescript.ts` verifies four archive SHA-512/SHA-256 fingerprints,
|
||||
three fixed Git files, source/member identities, and original notice bytes. It
|
||||
then executes only the pinned Terser 5.9.0 compiler with source-map 0.7.3 and the
|
||||
recorded options. The full minified result and fixed header must equal the shipped
|
||||
worker exactly. It does not execute TypeScript services, run npm install scripts,
|
||||
or rewrite runtime assets. Historical compilers are isolated below the ignored
|
||||
`refactor/.cache/monaco-review` directory, without changing root dependencies.
|
||||
|
||||
```sh
|
||||
yarn verify:monaco-typescript-source --fetch
|
||||
yarn verify:monaco-typescript-source
|
||||
yarn build:site-notices
|
||||
yarn check:site-notices
|
||||
node --test test/monaco-provenance.test.js test/site-notices.test.js
|
||||
yarn typecheck:docs-tools
|
||||
```
|
||||
|
||||
Use the first command to populate and reverify the network cache; the second is
|
||||
offline. Provenance and pinned build recipes are in
|
||||
`refactor/baselines/monaco-typescript-provenance.json`. The normal site build only
|
||||
copies verified notice files; it does not rerun the historical compiler.
|
||||
|
||||
Monaco's original ThirdPartyNotices names TypeScript 2.7.2; retain it verbatim.
|
||||
The supplement identifies the actual 4.4.4 worker and delivers its LICENSE,
|
||||
CopyrightNotice and ThirdPartyNoticeText. The last file includes upstream third-party
|
||||
terms, not just Apache 2.0. Preserve raw bytes, including encoding and line endings.
|
||||
The new attribution explains upstream modifications and the stale version label.
|
||||
|
||||
The other language services, localization shim, core embedded libraries and
|
||||
language definitions remain under VENDOR-06 review. A match for this worker does
|
||||
not close those components. Revalidate actual editor diagnostics/emission and
|
||||
notice HTTP delivery after changes; use the committed editor-types and site-vendor
|
||||
browser tests. Whole-site, physical-device and remote release gates remain separate.
|
||||
@@ -0,0 +1,102 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { Buffer } from 'node:buffer'
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import { createHash } from 'node:crypto'
|
||||
import fs from 'node:fs'
|
||||
import { createRequire } from 'node:module'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { verifyTypeScriptSource, workerHeader } from './typescript.ts'
|
||||
|
||||
interface Archive { name: string, version: string, tarball: string, integrity: string, sha256: string }
|
||||
interface Member { archive: string, member: string, sha256: string }
|
||||
interface Record {
|
||||
archives: Archive[]
|
||||
remotes: { source: string, sha256: string, gitBlobSha: string, apiUrl: string }[]
|
||||
source: Member
|
||||
worker: Member
|
||||
compiler: Member & { version: string, sourceMap: string, options: { output: { comments: string } } }
|
||||
target: { path: string, sha256: string }
|
||||
notices: { source: string, target: string, sha256: string, member: string | null }[]
|
||||
}
|
||||
const root = fileURLToPath(new URL('../../../', import.meta.url))
|
||||
const record: Record = JSON.parse(fs.readFileSync(path.join(root, 'refactor/baselines/monaco-typescript-provenance.json'), 'utf8'))
|
||||
assert(process.argv.slice(2).every(arg => arg === '--fetch'), 'Use reproduce-typescript.ts [--fetch]')
|
||||
assert.equal(process.version, `v${fs.readFileSync(path.join(root, '.node-version'), 'utf8').trim()}`, 'Use canonical Node')
|
||||
const cache = path.join(root, 'refactor/.cache/monaco-review')
|
||||
fs.mkdirSync(cache, { recursive: true })
|
||||
assert.equal(fs.realpathSync(cache).toLowerCase(), path.resolve(cache).toLowerCase(), 'Redirected Monaco cache')
|
||||
const hash = (bytes: Uint8Array) => createHash('sha256').update(bytes).digest('hex')
|
||||
async function download(url: string): Promise<Buffer> {
|
||||
const response = await fetch(url, { signal: AbortSignal.timeout(60000) })
|
||||
assert(response.ok, `${url}: HTTP ${response.status}`)
|
||||
return Buffer.from(await response.arrayBuffer())
|
||||
}
|
||||
for (const archive of record.archives) {
|
||||
const file = path.join(cache, `${archive.name}-${archive.version}.tgz`)
|
||||
const bytes = process.argv.includes('--fetch') ? await download(archive.tarball) : fs.readFileSync(file)
|
||||
assert.equal(hash(bytes), archive.sha256, 'Monaco archive changed')
|
||||
assert.equal(`sha512-${createHash('sha512').update(bytes).digest('base64')}`, archive.integrity, 'Monaco archive integrity changed')
|
||||
if (process.argv.includes('--fetch'))
|
||||
fs.writeFileSync(file, bytes)
|
||||
}
|
||||
for (const remote of record.remotes) {
|
||||
const verify = (bytes: Buffer) => {
|
||||
assert.equal(hash(bytes), remote.sha256, 'Monaco fixed Git content changed')
|
||||
assert.equal(createHash('sha1').update(`blob ${bytes.length}\0`).update(bytes).digest('hex'), remote.gitBlobSha, 'Monaco Git blob changed')
|
||||
}
|
||||
verify(fs.readFileSync(path.join(root, remote.source)))
|
||||
if (process.argv.includes('--fetch')) {
|
||||
const data: { encoding: string, content: string } = JSON.parse((await download(remote.apiUrl)).toString('utf8'))
|
||||
assert.equal(data.encoding, 'base64', 'Expected Git content encoding')
|
||||
verify(Buffer.from(data.content, 'base64'))
|
||||
}
|
||||
}
|
||||
function readMember(archive: string, member: string): Buffer {
|
||||
return execFileSync('tar', ['-xOzf', path.join(cache, `${archive}.tgz`), member], { maxBuffer: 20 * 1024 * 1024 })
|
||||
}
|
||||
function verifiedMember(member: Member): Buffer {
|
||||
const bytes = readMember(member.archive, member.member)
|
||||
assert.equal(hash(bytes), member.sha256, 'Monaco source member changed')
|
||||
return bytes
|
||||
}
|
||||
for (const notice of record.notices) {
|
||||
assert.equal(hash(fs.readFileSync(path.join(root, notice.source))), notice.sha256, 'TypeScript notice changed')
|
||||
if (notice.member)
|
||||
assert.equal(hash(readMember('typescript-4.4.4', notice.member)), notice.sha256, 'TypeScript upstream notice changed')
|
||||
}
|
||||
const source = verifiedMember(record.source).toString('utf8')
|
||||
const worker = verifiedMember(record.worker).toString('utf8')
|
||||
const matchedCharacters = verifyTypeScriptSource(source, worker)
|
||||
// Only the fixed minifier and its fixed source-map library execute; no install scripts.
|
||||
for (const name of ['terser', 'source-map']) {
|
||||
const archive = record.archives.find(item => item.name === name)!
|
||||
assert(archive, 'Missing historical compiler dependency')
|
||||
const members = execFileSync('tar', ['-tzf', path.join(cache, `${name}-${archive.version}.tgz`)], { encoding: 'utf8' }).trim().split(/\r?\n/)
|
||||
const directory = path.join(cache, 'compiler/node_modules', name)
|
||||
fs.mkdirSync(directory, { recursive: true })
|
||||
assert.equal(fs.realpathSync(directory).toLowerCase(), path.resolve(directory).toLowerCase(), 'Redirected compiler directory')
|
||||
for (const member of members.filter(member => !member.endsWith('/'))) {
|
||||
assert(member.startsWith('package/') && !member.includes('\\') && !member.split('/').includes('..'), 'Unsafe compiler archive member')
|
||||
const destination = path.resolve(directory, member.slice('package/'.length))
|
||||
assert(destination.startsWith(`${directory}${path.sep}`), 'Compiler member escapes cache')
|
||||
fs.mkdirSync(path.dirname(destination), { recursive: true })
|
||||
assert.equal(fs.realpathSync(path.dirname(destination)).toLowerCase(), path.dirname(destination).toLowerCase(), 'Redirected compiler member directory')
|
||||
assert(!fs.existsSync(destination) || !fs.lstatSync(destination).isSymbolicLink(), 'Redirected compiler file')
|
||||
fs.writeFileSync(destination, readMember(`${name}-${archive.version}`, member))
|
||||
}
|
||||
}
|
||||
const require = createRequire(path.join(cache, 'compiler/entry.cjs'))
|
||||
assert.equal(require('terser/package.json').version, record.compiler.version, 'Wrong Terser version')
|
||||
|
||||
assert.equal(require('source-map/package.json').version, record.compiler.sourceMap, 'Wrong source-map version')
|
||||
|
||||
assert.equal(hash(fs.readFileSync(path.join(cache, 'compiler/node_modules/terser/dist/bundle.min.js'))), record.compiler.sha256, 'Wrong Terser compiler bytes')
|
||||
const compiler = require('terser') as { minify: (source: string, options: Record['compiler']['options']) => Promise<{ code: string }> }
|
||||
|
||||
const { code } = await compiler.minify(worker, record.compiler.options)
|
||||
const target = fs.readFileSync(path.join(root, record.target.path))
|
||||
assert.equal(hash(target), record.target.sha256, 'Current Monaco TypeScript worker changed')
|
||||
assert.equal(workerHeader + code, target.toString('utf8'), 'Minified Monaco TypeScript worker differs')
|
||||
console.log(JSON.stringify({ archives: record.archives.length, fixedGitSources: record.remotes.length, typeScriptVersion: '4.4.4', sourceCharacters: matchedCharacters, exactWorker: true, workerSha256: hash(target), notices: record.notices.length, otherMonacoComponentsReviewed: false }))
|
||||
@@ -0,0 +1,39 @@
|
||||
import assert from 'node:assert/strict'
|
||||
|
||||
// Fixed Monaco 0.30.1 importTypescript.js adaptations, followed by RequireJS's
|
||||
// removal of the top-level strict directive. Never apply to arbitrary versions.
|
||||
export function browserTypeScript(source: string): string {
|
||||
const replacements: [RegExp, string][] = [
|
||||
[/\n {4}ts\.sys =([\s\S]*)\n {4}\}\)\(\);/, '\n // MONACOCHANGE\n ts.sys = undefined;\n // END MONACOCHANGE'],
|
||||
[/^( +)etwModule = require\(.*$/m, '$1// MONACOCHANGE\n$1etwModule = undefined;\n$1// END MONACOCHANGE'],
|
||||
[/^( +)var result = ts\.sys\.require\(.*$/m, '$1// MONACOCHANGE\n$1var result = undefined;\n$1// END MONACOCHANGE'],
|
||||
[/^( +)fs = require\("fs"\);$/m, '$1// MONACOCHANGE\n$1fs = undefined;\n$1// END MONACOCHANGE'],
|
||||
[/^( +)debugger;$/m, '$1// MONACOCHANGE\n$1// debugger;\n$1// END MONACOCHANGE'],
|
||||
[/= require\("perf_hooks"\)/, '/* MONACOCHANGE */= {}/* END MONACOCHANGE */'],
|
||||
]
|
||||
for (const [pattern, replacement] of replacements) {
|
||||
assert.equal([...source.matchAll(new RegExp(pattern.source, `${pattern.flags}g`))].length, 1, 'Unexpected TypeScript adaptation boundary')
|
||||
source = source.replace(pattern, replacement)
|
||||
}
|
||||
const map = /\/\/# sourceMappingURL[^\n]+/g
|
||||
assert.equal([...source.matchAll(map)].length, 1, 'Unexpected TypeScript source map boundary')
|
||||
const strict = /^"use strict";$/gm
|
||||
assert.equal([...source.matchAll(strict)].length, 1, 'Unexpected TypeScript strict directive')
|
||||
return source.replace(map, '').replace(strict, '')
|
||||
}
|
||||
|
||||
export function verifyTypeScriptSource(source: string, worker: string): number {
|
||||
const adapted = browserTypeScript(source)
|
||||
const offset = worker.indexOf(adapted)
|
||||
assert.equal(offset, 98, 'Adapted TypeScript differs from the archived worker')
|
||||
assert.equal(worker.indexOf(adapted, offset + 1), -1, 'Duplicate TypeScript source in worker')
|
||||
return adapted.length
|
||||
}
|
||||
|
||||
export const workerHeader = `/*!-----------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* monaco-typescript version: 0.30.1(5a7ba61be909ae9e4889768a3453ebb0dec392e2)
|
||||
* Released under the MIT license
|
||||
* https://github.com/Microsoft/monaco-typescript/blob/master/LICENSE.md
|
||||
*-----------------------------------------------------------------------------*/
|
||||
`
|
||||
Reference in new issue
Block a user