chore: establish YCode replication baseline and TODO
This commit is contained in:
commit
6dc59f89ba
1118 files changed
+196002
No files matched your search
@@ -0,0 +1,150 @@
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { createHash } from 'node:crypto'
|
||||
import { parse } from '@babel/parser'
|
||||
import traverseModule from '@babel/traverse'
|
||||
import { exportSelectedArtifactText } from '../../.tmp/rea-css-runtime/dist/artifacts/SelectedArtifactText.js'
|
||||
const traverse = traverseModule.default, hash = value => createHash('sha256').update(value).digest('hex')
|
||||
const rootSha = '035254bc76310ea46317433e4840602094543ef323d2d5c5aff7c131729de918'
|
||||
const archive = 'C:/Users/Administrator/AppData/Local/Programs/WorkBuddy/resources/app.asar'
|
||||
const target = path.resolve('src/renderer/src/workbuddy/workbench/source-code-assets')
|
||||
const runtime = JSON.parse(await fs.readFile('.tmp/rea-runtime-scripts.json', 'utf8'))
|
||||
const selected = JSON.parse(await fs.readFile('.tmp/rea-selected-editor-evidence.json', 'utf8'))
|
||||
const cssManifest = JSON.parse(await fs.readFile('.tmp/rea-css-recovered/manifest.json', 'utf8'))
|
||||
if (selected.normalized_result.root_sha256 !== rootSha) throw Error('Source archive identity changed')
|
||||
const files = new Map(), graph = {}, records = []
|
||||
for (const item of runtime.items) {
|
||||
const a = item.source?.artifact
|
||||
if (!a?.text) continue
|
||||
if (hash(a.text) !== a.sha256) throw Error('Runtime source digest changed')
|
||||
files.set(path.basename(item.file_path.replaceAll('\\', '/')), { text: a.text, sha256: a.sha256, evidenceId: runtime.evidence_id })
|
||||
}
|
||||
for (const f of selected.normalized_result.files) {
|
||||
if (hash(f.text) !== f.sha256) throw Error('Selected editor digest changed')
|
||||
files.set(path.basename(f.path), { text: f.text, sha256: f.sha256, evidenceId: selected.evidence_id })
|
||||
}
|
||||
for (const name of (await fs.readdir('.tmp')).filter(name => /^rea-monaco-chunks-\d+\.json$/.test(name)).sort()) {
|
||||
const evidencePath = path.resolve('.tmp', name), evidence = JSON.parse(await fs.readFile(evidencePath, 'utf8'))
|
||||
if (evidence.authority !== 'shipped-artifact' || evidence.normalized_result.root_sha256 !== rootSha) throw Error('Cached REA identity changed')
|
||||
records.push({ evidenceId: evidence.evidence_id, path: evidencePath })
|
||||
for (const f of evidence.normalized_result.files) {
|
||||
const body = f.text ?? Buffer.from(f.data_base64, 'base64')
|
||||
if (hash(body) !== f.sha256) throw Error('Cached REA source digest changed')
|
||||
files.set(path.basename(f.path), { [f.text === undefined ? 'binary' : 'text']: body, sha256: f.sha256, evidenceId: evidence.evidence_id })
|
||||
}
|
||||
}
|
||||
for (const f of cssManifest.files) {
|
||||
const text = await fs.readFile(path.join('.tmp/rea-css-recovered', f.file), 'utf8')
|
||||
if (hash(text) !== f.sha256) throw Error('CSS digest changed')
|
||||
files.set(f.file, { text, sha256: f.sha256, evidenceId: cssManifest.sourceEvidenceId })
|
||||
}
|
||||
function dependencies(name, text) {
|
||||
const deps = new Set()
|
||||
const add = value => { if (value?.startsWith('./') && /\.(?:js|css|ttf|woff2?)$/.test(value)) deps.add(value.slice(2)) }
|
||||
if (name.endsWith('.css')) for (const m of text.matchAll(/url\(['"]?(\.\/[^'"\)]+)['"]?\)/g)) add(m[1])
|
||||
else if (name.endsWith('.js')) {
|
||||
const ast = parse(text, { sourceType: 'module' })
|
||||
traverse(ast, { ImportDeclaration(p) { add(p.node.source.value) }, NewExpression(p) {
|
||||
if (p.node.callee.type !== 'Identifier' || p.node.callee.name !== 'URL') return
|
||||
const n = p.node.arguments[0], value = n?.type === 'StringLiteral' ? n.value : n?.type === 'TemplateLiteral' && !n.expressions.length ? n.quasis[0].value.cooked : undefined
|
||||
if (value && /^[\w.-]+\.(?:js|css)$/.test(value)) add('./' + value)
|
||||
else add(value)
|
||||
}, CallExpression(p) {
|
||||
if (p.node.callee.type !== 'Import') return
|
||||
const n = p.node.arguments[0]; add(n.type === 'StringLiteral' ? n.value : n.type === 'TemplateLiteral' && !n.expressions.length ? n.quasis[0].value.cooked : undefined)
|
||||
}, AssignmentExpression(p) {
|
||||
if (p.node.left.type === 'MemberExpression' && p.node.left.property.name === 'f' && p.node.right.type === 'ArrayExpression') for (const n of p.node.right.elements) if (n?.type === 'StringLiteral') add(n.value)
|
||||
} })
|
||||
}
|
||||
return [...deps].filter(name => name !== 'react-z__Ncn__.js')
|
||||
}
|
||||
const wanted = new Set(['editor.main-POO0Vbhx.js', 'monaco-init-CE4MbM4x.js', 'editor-gwlzV742.css', 'codeEditor-Bxmo_AI3.css'])
|
||||
for (;;) {
|
||||
const missing = [...wanted].filter(name => !files.has(name))
|
||||
for (let offset = 0; offset < missing.length; offset += 16) {
|
||||
const batch = missing.slice(offset, offset + 16)
|
||||
for (const binary of [false, true]) {
|
||||
const names = batch.filter(name => /\.(?:ttf|woff2?)$/.test(name) === binary)
|
||||
if (!names.length) continue
|
||||
const evidence = await exportSelectedArtifactText(archive, rootSha, names.map(name => 'renderer/assets/' + name), binary)
|
||||
const evidencePath = path.resolve(`.tmp/rea-monaco-chunks-${records.length.toString().padStart(3, '0')}.json`)
|
||||
await fs.writeFile(evidencePath, JSON.stringify(evidence))
|
||||
records.push({ evidenceId: evidence.evidence_id, path: evidencePath })
|
||||
for (const f of evidence.normalized_result.files) {
|
||||
const body = binary ? Buffer.from(f.data_base64, 'base64') : f.text
|
||||
if (hash(body) !== f.sha256) throw Error('REA export digest mismatch')
|
||||
files.set(path.basename(f.path), { [binary ? 'binary' : 'text']: body, sha256: f.sha256, evidenceId: evidence.evidence_id })
|
||||
}
|
||||
}
|
||||
}
|
||||
const before = wanted.size
|
||||
for (const name of wanted) {
|
||||
const file = files.get(name)
|
||||
if (!file) continue
|
||||
graph[name] = file.text ? dependencies(name, file.text) : []
|
||||
for (const dep of graph[name]) wanted.add(dep)
|
||||
}
|
||||
if (wanted.size === before && [...wanted].every(name => files.has(name))) break
|
||||
}
|
||||
await fs.mkdir(target, { recursive: true })
|
||||
const assets = []
|
||||
for (const name of wanted) {
|
||||
const file = files.get(name); let text = file.text, adapters = []
|
||||
if (name.endsWith('.js') && text.includes('__vite__mapDeps')) {
|
||||
const ast = parse(text, { sourceType: 'module' }), edits = []
|
||||
traverse(ast, { VariableDeclarator(p) {
|
||||
if (p.node.id.name === '__vite__mapDeps') edits.push({ start: p.node.start, end: p.node.end, text: '__ycodeMonacoMapDeps=()=>[]' })
|
||||
}, Identifier(p) {
|
||||
if (p.node.name === '__vite__mapDeps' && p.parent.type === 'CallExpression' && p.parent.callee === p.node) edits.push({ start: p.node.start, end: p.node.end, text: '__ycodeMonacoMapDeps' })
|
||||
} })
|
||||
for (const edit of edits.sort((a, b) => b.start - a.start)) text = text.slice(0, edit.start) + edit.text + text.slice(edit.end)
|
||||
adapters.push('Original bundler preload table replaced by empty host preload adapter; Vite owns imports/CSS, avoiding its reserved helper binding and obsolete original URLs.')
|
||||
}
|
||||
if (name === 'monaco-init-CE4MbM4x.js') {
|
||||
const fallback = 'P.config({paths:{vs:`https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs`}})'
|
||||
if (text.split(fallback).length !== 3) throw Error('Monaco local-only fallback boundary changed')
|
||||
text = text.replaceAll(fallback, '(()=>{throw Error(`YCode local Monaco assets failed to load`)})()')
|
||||
adapters.push('Two CDN fallback branches fail locally; source local loader/theme/React host preserved.')
|
||||
}
|
||||
const bytes = file.binary ?? text
|
||||
await fs.writeFile(path.join(target, name), bytes)
|
||||
assets.push({ name, sourceSha256: file.sha256, outputSha256: hash(bytes), evidenceId: file.evidenceId, adapters })
|
||||
}
|
||||
// Use YCode's existing React instance, never initialize the original application's React globals.
|
||||
await fs.writeFile(path.join(target, 'react-z__Ncn__.js'), "import React from 'react';export function t(){return React}\n")
|
||||
const source = files.get('codeEditor-D3P9alks.js').text
|
||||
const ast = parse(source, { sourceType: 'module' }), snippets = {}
|
||||
traverse(ast, { AssignmentExpression(p) {
|
||||
const n = p.node, name = n.left.type === 'Identifier' ? n.left.name : ''
|
||||
if ((name === 're' && n.start > 14000 && n.start < 15000) || (name === 'ee' && n.start > 11000 && n.start < 12000)
|
||||
|| (['R', 'B'].includes(name) && n.start > 8000 && n.start < 11624) || (name === 'q' && n.start > 14287 && n.start < 14737)
|
||||
|| ['UpArrowIcon', 'DownArrowIcon'].includes(name)) snippets[name] = { text: source.slice(n.start, n.end), start: n.start, end: n.end }
|
||||
}, FunctionDeclaration(p) { if (['getMonacoLanguage', 'getFontSizeOffset$1'].includes(p.node.id?.name)) snippets[p.node.id.name] = { text: source.slice(p.node.start, p.node.end), start: p.node.start, end: p.node.end } } })
|
||||
for (const name of ['re', 'ee', 'R', 'B', 'q', 'UpArrowIcon', 'DownArrowIcon', 'getMonacoLanguage', 'getFontSizeOffset$1']) if (!snippets[name]) throw Error('Missing source closure ' + name)
|
||||
const locale = files.get('zh-cn-Dfzn8myY.js'), labels = {}
|
||||
for (const m of locale.text.matchAll(/"(search\.[^"]+)":(`(?:\\.|[^`])*`)/g)) {
|
||||
const literal = parse(m[2], { sourceType: 'module' }).program.body[0].expression
|
||||
if (literal.type !== 'TemplateLiteral' || literal.expressions.length) throw Error('Locale must be a static string')
|
||||
labels[m[1]] = literal.quasis[0].value.cooked
|
||||
}
|
||||
const media = files.get('media-preview-DMz4TW4q.js').text
|
||||
let extensionBody
|
||||
traverse(parse(media, { sourceType: 'module' }), { AssignmentExpression(p) { if (p.node.left.name === 'getLanguageFromFileName') extensionBody = media.slice(p.node.start, p.node.end) } })
|
||||
if (!extensionBody || hash(extensionBody) !== '666dfd63d32a89ed66020d0a4fb6d1a41c25be41692c3538e6b167c157fa1465') throw Error('Language source body changed')
|
||||
await fs.writeFile(path.join(target, 'extension.mjs'), 'let getLanguageFromFileName;' + extensionBody + ';export {getLanguageFromFileName};\n')
|
||||
await fs.writeFile(path.join(target, 'extension.d.mts'), 'export function getLanguageFromFileName(name:string):string;\n')
|
||||
const wrapper = `// Generated source bodies; host bindings are explicit adapters below. No WorkBuddy services.\nimport * as K from 'react';import * as ne from 'react/jsx-runtime';import {c as O,d as A,r as F} from './monaco-init-CE4MbM4x.js';\nconst U=K,W=ne;const classes=(...a)=>a.flatMap(x=>typeof x==='string'?[x]:x&&typeof x==='object'?Object.keys(x).filter(k=>x[k]):[]).join(' ');const H={default:classes},te=H;const labels=${JSON.stringify(labels)};function u(){return key=>labels[key]||key}\nconst g=K.forwardRef(({value,onChange,placeholder,className},ref)=>ne.jsx('span',{className:'wb-input-wrapper wb-input-wrapper--small wb-input-wrapper--borderless '+className,children:ne.jsx('input',{ref,value,placeholder,className:'wb-input__inner',onChange:e=>onChange(e.target.value)})}));\nconst p=({variant='ghost',size='small',iconOnly,className,children,...props})=>ne.jsx('button',{...props,type:'button',className:classes('wb-button','wb-button--'+variant,'wb-button--'+size,iconOnly&&'wb-button--icon-only',className),children});const m=p;const h=()=>ne.jsx('svg',{width:16,height:16,viewBox:'0 0 16 16',fill:'none',children:ne.jsx('path',{d:'M4 4l8 8M12 4l-8 8',stroke:'currentColor',strokeWidth:1.2,strokeLinecap:'round'})});\nlet R,B,q,ee,re,UpArrowIcon,DownArrowIcon;\n${['R','B','q','UpArrowIcon','DownArrowIcon','ee','getMonacoLanguage','getFontSizeOffset$1','re'].map(n=>snippets[n].text+';').join('\n')}\nexport {re as MonacoEditor,O as configureMonaco,F as themeManager};\n`
|
||||
const sourceUi = files.get('src-0FGg1seX.js').text
|
||||
let closeBody
|
||||
traverse(parse(sourceUi, { sourceType: 'module' }), { AssignmentExpression(p) { if (p.node.left.name === 'CloseIcon$2' && p.node.start === 245582) closeBody = sourceUi.slice(p.node.start, p.node.end) } })
|
||||
if (!closeBody) throw Error('Source close icon changed')
|
||||
const closeAdapter = `const Lm=ne;let CloseIcon$2;${closeBody};const h=CloseIcon$2;`
|
||||
const finalWrapper = wrapper.replace(/const h=\(\)=>ne\.jsx\('svg',[^\n]+?\);\nlet R/, closeAdapter + '\nlet R')
|
||||
if (finalWrapper === wrapper) throw Error('Close icon adapter replacement failed')
|
||||
await fs.writeFile(path.join(target, 'wrapper.mjs'), finalWrapper)
|
||||
await fs.writeFile(path.join(target, 'wrapper.d.mts'), `import type {ComponentType} from 'react';export const MonacoEditor:ComponentType<{value:string;language?:string;readOnly?:boolean;minimap?:boolean;lineNumbers?:string;wordWrap?:string;fontSize?:number;height?:string;onMount?:(editor:any,monaco:any)=>void}>;export function configureMonaco():Promise<void>;export function themeManager():{setTheme(name:string):void};\n`)
|
||||
await fs.writeFile(path.join(target, 'manifest.json'), JSON.stringify({ rootSha256: rootSha, version: '5.8.2', monacoPackageVersion: 'unconfirmed; exact shipped chunks used', records, assets, graph,
|
||||
wrapper: Object.fromEntries(Object.entries(snippets).map(([name, body]) => [name, { start: body.start, end: body.end, sha256: hash(body.text) }])),
|
||||
sourceCloseIcon: { offset: 245582, sha256: hash(closeBody), evidenceId: runtime.evidence_id },
|
||||
hostAdapters: ['YCode React singleton', 'native input/button/className/i18n bindings', 'local-only failure instead of CDN fallback', 'Vite owns preloading paths'] }, null, 2))
|
||||
console.log(JSON.stringify({ result: 'PASS', assets: assets.length, newEvidence: records.length, target, wrapperBody: snippets.re.end - snippets.re.start }))
|
||||
Reference in new issue
Block a user