44 lines
2.4 KiB
JavaScript
44 lines
2.4 KiB
JavaScript
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { createHash } from 'node:crypto';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { copyArtifactAtomically, writeManifestIfChanged } from '../web/tools/artifact-assets.mjs';
|
|
|
|
const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
|
|
const publicRoot=path.join(root,'web/public'), dist=path.join(root,'web/dist');
|
|
const modules=[...new Set(process.argv.slice(2))];
|
|
if(!modules.length) throw Error('Specify modules to synchronize into an existing production preview');
|
|
const source=JSON.parse(fs.readFileSync(path.join(publicRoot,'modules/manifest.json')));
|
|
const targetPath=path.join(dist,'modules/manifest.json');
|
|
const target=JSON.parse(fs.readFileSync(targetPath));
|
|
if(source.schema!==target.schema) throw Error('Preview manifest schema differs; run the full frontend build');
|
|
const hash=file=>createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
const assets=new Map();
|
|
const fields={runtimeUrl:'runtime',wasmUrl:'wasm',dataUrl:'data',pthreadWorkerUrl:'pthreadWorker'};
|
|
for(const name of modules) {
|
|
const entry=source.modules[name];
|
|
if(!entry || !target.modules[name]) throw Error(`Module is not present in both manifests: ${name}`);
|
|
for(const artifact of [entry,...Object.values(entry.workers??{})]) for(const [field,key] of Object.entries(fields)) {
|
|
if(!artifact[field]) continue;
|
|
const url=artifact[field];
|
|
if(!/^\/kernel\/[A-Za-z0-9_.-]+$/.test(url)) throw Error(`Unsupported local artifact path: ${url}`);
|
|
const expected=artifact.hashes?.[key];
|
|
if(!expected || hash(path.join(publicRoot,url.slice(1)))!==expected) throw Error(`Published artifact hash mismatch: ${url}`);
|
|
assets.set(url.slice(1),expected);
|
|
}
|
|
}
|
|
let copied=0;
|
|
for(const [name,expected] of assets) {
|
|
const destination=path.join(dist,name);
|
|
if(fs.existsSync(destination) && hash(destination)===expected) continue;
|
|
fs.mkdirSync(path.dirname(destination),{recursive:true});
|
|
copyArtifactAtomically(path.join(publicRoot,name),destination);
|
|
if(hash(destination)!==expected) throw Error(`Preview copy verification failed: ${name}`);
|
|
copied++;
|
|
}
|
|
// Publish revision metadata only after every selected runtime file is verified.
|
|
for(const name of modules) target.modules[name]=source.modules[name];
|
|
target.generatedAt=new Date().toISOString();
|
|
writeManifestIfChanged(targetPath,target);
|
|
console.log(JSON.stringify({modules,verifiedArtifacts:assets.size,copied,manifest:'web/dist/modules/manifest.json'}));
|