Spaces:
Running
Running
| // Re-hashes every file listed in MANIFEST.json and fails on mismatch, | |
| // missing file, or extra file in the build tree. | |
| import { createHash } from "node:crypto"; | |
| import { readFileSync, readdirSync, statSync } from "node:fs"; | |
| import { join, relative, sep } from "node:path"; | |
| const buildDir = process.argv[2] ?? join(process.cwd(), "build"); | |
| const manifestPath = join(buildDir, "MANIFEST.json"); | |
| let manifest; | |
| try { | |
| manifest = JSON.parse(readFileSync(manifestPath, "utf8")); | |
| } catch (err) { | |
| console.error(`[verify] cannot read ${manifestPath}: ${err.message}`); | |
| process.exit(2); | |
| } | |
| function walk(dir, acc = []) { | |
| for (const entry of readdirSync(dir)) { | |
| const full = join(dir, entry); | |
| const st = statSync(full); | |
| if (st.isDirectory()) walk(full, acc); | |
| else if (st.isFile()) acc.push(full); | |
| } | |
| return acc; | |
| } | |
| const onDisk = new Set( | |
| walk(buildDir) | |
| .map((abs) => relative(buildDir, abs).split(sep).join("/")) | |
| .filter((rel) => rel !== "MANIFEST.json"), | |
| ); | |
| const declared = new Set(manifest.files.map((f) => f.path)); | |
| const failures = []; | |
| for (const entry of manifest.files) { | |
| const abs = join(buildDir, entry.path); | |
| let buf; | |
| try { | |
| buf = readFileSync(abs); | |
| } catch (err) { | |
| failures.push(`MISSING ${entry.path}: ${err.message}`); | |
| continue; | |
| } | |
| const sha = createHash("sha256").update(buf).digest("hex"); | |
| if (sha !== entry.sha256) { | |
| failures.push( | |
| `HASH MISMATCH ${entry.path}: expected=${entry.sha256} got=${sha}`, | |
| ); | |
| } | |
| if (buf.byteLength !== entry.size) { | |
| failures.push( | |
| `SIZE MISMATCH ${entry.path}: expected=${entry.size} got=${buf.byteLength}`, | |
| ); | |
| } | |
| } | |
| for (const rel of onDisk) { | |
| if (!declared.has(rel)) failures.push(`EXTRA FILE ${rel} not in manifest`); | |
| } | |
| if (failures.length > 0) { | |
| console.error(`[verify] FAILED with ${failures.length} issue(s):`); | |
| for (const f of failures) console.error(" - " + f); | |
| process.exit(1); | |
| } | |
| console.log( | |
| `[verify] OK — ${manifest.files.length} files verified (${manifest.totalBytes} bytes, sha256)`, | |
| ); | |