import { lstat, readFile, readdir } from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { verifyCppPack } from "./cpp-engine-pack.mjs"; import { verifyDotNetPack } from "./dotnet-engine-pack.mjs"; import { verifyGoPack } from "./go-engine-pack.mjs"; import { verifyJavaPack } from "./java-engine-pack.mjs"; import { verifyPerlPack } from "./perl-engine-pack.mjs"; import { verifyPcre2Pack } from "./pcre2-engine-pack.mjs"; import { verifyPhpPack } from "./php-engine-pack.mjs"; import { verifyPythonPack } from "./python-engine-pack.mjs"; import { verifyRubyPack } from "./ruby-engine-pack.mjs"; import { verifyRustPack } from "./rust-engine-pack.mjs"; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const arguments_ = process.argv.slice(2); const enginePacks = [ { id: "cpp", argument: "--cpp-pack", verify: (pack) => verifyCppPack(pack, root), }, { id: "dotnet", argument: "--dotnet-pack", verify: (pack) => verifyDotNetPack(pack, root), }, { id: "go", argument: "--go-pack", verify: (pack) => verifyGoPack(pack, root), }, { id: "java", argument: "--java-pack", verify: (pack) => verifyJavaPack(pack, root), }, { id: "pcre2", argument: "--pcre2-pack", verify: (pack) => verifyPcre2Pack(pack, root), }, { id: "perl", argument: "--perl-pack", verify: (pack) => verifyPerlPack(pack, root), }, { id: "php", argument: "--php-pack", verify: (pack) => verifyPhpPack(pack, root), }, { id: "python", argument: "--python-pack", verify: (pack) => verifyPythonPack(pack, root), }, { id: "ruby", argument: "--ruby-pack", verify: (pack) => verifyRubyPack(pack, root), }, { id: "rust", argument: "--rust-pack", verify: (pack) => verifyRustPack(pack, root), }, ]; if (arguments_.length > 0) { const selected = enginePacks.find( ({ argument }) => argument === arguments_[0], ); if (arguments_.length !== 2 || !selected) { throw new Error( `Usage: node scripts/verify-engine-assets.mjs [${enginePacks .map(({ argument }) => argument) .join("|")} PATH]`, ); } const pack = path.resolve(root, arguments_[1]); const metadata = await selected.verify(pack); console.log( `Verified staged ${metadata.engineVersion ?? metadata.semanticIdentity} ${selected.id} engine pack.`, ); process.exit(0); } const engineDirectory = path.join(root, "dist", "engines"); const details = await lstat(engineDirectory); if (details.isSymbolicLink() || !details.isDirectory()) { throw new Error("dist/engines must be a real directory."); } const entries = (await readdir(engineDirectory, { withFileTypes: true })).sort( (left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0, ); const expectedEntries = [ { name: "README.md", directory: false }, ...enginePacks.map(({ id }) => ({ name: id, directory: true })), ].sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0, ); if ( entries.length !== expectedEntries.length || entries.some((entry, index) => { const expected = expectedEntries[index]; return ( !expected || entry.name !== expected.name || (expected.directory ? !entry.isDirectory() : !entry.isFile()) ); }) ) { throw new Error( `The production build must contain only README.md and the declared engine packs: ${enginePacks .map(({ id }) => id) .join(", ")}.`, ); } const notice = await readFile(path.join(engineDirectory, "README.md"), "utf8"); for (const identity of [ "native `RegExp`", "PCRE2 10.47", "PHP 8.5.8", "Perl 5.28.1", "Pyodide 314.0.3", "CRuby 4.0.0", "TeaVM 0.15.0", "Emscripten 6.0.4", "Go 1.26.5", "Rust `regex` crate 1.13.1", ".NET 10.0.10", "Scala", ]) { if (!notice.includes(identity)) { throw new Error( `The engine asset notice does not describe ${identity} in this release.`, ); } } for (const engine of enginePacks) { await engine.verify(path.join(engineDirectory, engine.id)); } console.log( `Verified native ECMAScript plus ${enginePacks .map(({ id }) => id) .join(", ")} checked-in engine packs.`, );