// Static AST decoding of retained REA sources; no recovered code is executed. const fs = require('node:fs') const path = require('node:path') const assert = require('node:assert/strict') const crypto = require('node:crypto') const parser = require('@babel/parser') const ts = { isStringLiteral: n => n?.type === 'StringLiteral', isNoSubstitutionTemplateLiteral: n => n?.type === 'TemplateLiteral' && n.expressions.length === 0, isArrayLiteralExpression: n => n?.type === 'ArrayExpression', isObjectLiteralExpression: n => n?.type === 'ObjectExpression', isPropertyAssignment: n => n?.type === 'ObjectProperty', isBinaryExpression: n => n?.type === 'AssignmentExpression', isIdentifier: n => n?.type === 'Identifier', forEachChild: (node, callback) => { for (const value of Object.values(node)) { if (Array.isArray(value)) value.forEach(item => { if (item?.type) callback(item) }); else if (value?.type) callback(value) } } } const literal = node => node.type === 'TemplateLiteral' ? node.quasis[0].value.cooked : node.value const evidence = JSON.parse(fs.readFileSync('.tmp/rea-todo20-project-page.json', 'utf8').replace(/^\uFEFF/, '')) assert.equal(evidence.evidence_id, 'ev_89843a8b67f411d3297b5f3e428830393cd0fe8712603816cf8b7bbe442064d9') function source(name) { const item = evidence.normalized_result.scripts.items.find(item => item.file_path.endsWith(name)) assert.ok(item?.source.included) const text = item.source.artifact.text assert.equal(crypto.createHash('sha256').update(text).digest('hex'), item.source.artifact.sha256) return parser.parse(text, { sourceType: 'module' }) } function decode(node) { if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return literal(node) if (ts.isArrayLiteralExpression(node)) return node.elements.map(decode) if (ts.isObjectLiteralExpression(node)) { const result = {} for (const property of node.properties) { assert.ok(ts.isPropertyAssignment(property)) const key = property.key.name ?? property.key.value if (key !== 'icon') result[key] = decode(property.value) } return result } throw Error(`Nonliteral template node: ${node.type}`) } const definitions = new Map() function visit(node) { if (ts.isBinaryExpression(node) && node.operator === '=' && ts.isIdentifier(node.left) && ['vt', 'gt', 'St', 'bt', 'mt'].includes(node.left.name) && ts.isObjectLiteralExpression(node.right)) definitions.set(node.left.name, decode(node.right)) ts.forEachChild(node, visit) } visit(source('project-list-page-DpHRTtk8.js')) assert.equal(definitions.size, 5) const translations = new Map() function strings(node) { if (ts.isPropertyAssignment(node) && ts.isStringLiteral(node.key) && node.key.value.startsWith('collab.list.template.') && (ts.isStringLiteral(node.value) || ts.isNoSubstitutionTemplateLiteral(node.value))) translations.set(node.key.value, literal(node.value)) ts.forEachChild(node, strings) } strings(source('zh-cn-C7BTHfrQ.js')) const templates = ['vt', 'gt', 'St', 'bt', 'mt'].map(key => { const item = definitions.get(key) for (const field of ['title', 'desc', 'prompt']) { assert.ok(translations.has(item[field]), `Missing translation: ${item[field]}`) item[field] = translations.get(item[field]) } return item }) const output = JSON.stringify(templates, null, 2) + '\n' const destination = path.resolve('src/renderer/src/projects/project-templates.json') if (process.argv.includes('--check')) assert.equal(fs.readFileSync(destination, 'utf8'), output) else fs.writeFileSync(destination, output) console.log(`PASS project templates: ${templates.length} source configurations and Chinese instructions recovered`)