import { spawn } from "node:child_process"; import { createHash } from "node:crypto"; import { access, constants as fsConstants, copyFile, lstat, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, writeFile, } from "node:fs/promises"; import path from "node:path"; export const RUST_LOCK = Object.freeze({ bridgeAbi: 1, engineVersion: "1.13.1", rustcVersion: "1.97.1", rustcIdentity: "rustc 1.97.1 (8bab26f4f 2026-07-14)", rustcCommit: "8bab26f4f68e0e26f0bb7960be334d5b520ea452", cargoIdentity: "cargo 1.97.1 (c980f4866 2026-06-30)", llvmVersion: "22.1.6", wasmBindgenVersion: "0.2.126", wasmBindgenCrateChecksum: "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4", wasmBindgenCliChecksum: "8e2ebf6eef87c34e662347f1dddfbea541304bb7294711ed2c2acd8746cc5b50", sourceDateEpoch: 1_784_146_753, source: Object.freeze({ repository: "https://github.com/rust-lang/regex", tag: "1.13.1", tagObject: "7c28ee4dc4aef53571c96757f4825c51ac0c3d44", commit: "2b527599eb9eea0dcc288c704584f242f26a5c61", tree: "25bea9f6f55a4cf528afefaa00f5335c595c182c", crateChecksum: "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d", license: "MIT OR Apache-2.0", }), distribution: Object.freeze({ manifest: "https://static.rust-lang.org/dist/channel-rust-1.97.1.toml", manifestSha256: "03569b1886ceb5c05276b50c8431ab111de944cd6140fe1fa7d821dd8e0f29cf", rustcArchiveSha256: "1c441e430c1cca49dff54a8d59c41038bf6f79f7b8756596cb2f36511a015eba", wasmStdArchiveSha256: "13902d5573eeea50701d75acc774b6df2dfb4942ec88cdbe40bb07e448c307ea", licenseHashes: Object.freeze({ apache: "074e6e32c86a4c0ef8b3ed25b721ca23aca83df277cd88106ef7177c354615ff", mit: "b85dcd3e453d05982552c52b5fc9e0bdd6d23c6f8e844b984a88af32570b0cc0", unicode: "f5062c9a188d81dfe66b56db4182dcf9e4b17c0d9b0d311a8e20b3a1b075c443", stdlibCopyright: "0a65bb747c49c7bb816cbc7188319bd6e4e8d08091c1190b8a3c0971c47968ed", }), }), limits: Object.freeze({ maximumPatternBytes: 262_144, maximumSubjectBytes: 16_777_216, maximumReplacementBytes: 262_144, maximumMatches: 10_000, maximumCaptureRows: 100_000, maximumCaptureGroups: 1_000, maximumOutputBytes: 67_108_864, maximumRequestJSONBytes: 103_874_560, compiledSizeLimitBytes: 10_485_760, dfaSizeLimitBytes: 2_097_152, }), }); const PACK_FILES = Object.freeze([ "LICENSE-Apache-2.0.txt", "LICENSE-MIT.txt", "LICENSE-Unicode-3.0.txt", "RUST-STDLIB-COPYRIGHT.html", "SHA256SUMS", "THIRD_PARTY_LICENSES.txt", "engine-metadata.json", "rust-regex.mjs", "rust-regex_bg.wasm", ]); const CHECKSUM_FILES = Object.freeze( PACK_FILES.filter((name) => name !== "SHA256SUMS"), ); const SOURCE_FILES = Object.freeze([ "engines/rust/Cargo.lock", "engines/rust/Cargo.toml", "engines/rust/README.md", "engines/rust/rust-toolchain.toml", "engines/rust/src/lib.rs", ]); function sha256(value) { return createHash("sha256").update(value).digest("hex"); } async function sha256File(file) { return sha256(await readFile(file)); } async function assertRealDirectory(candidate, label) { const details = await lstat(candidate).catch(() => null); if (!details?.isDirectory() || details.isSymbolicLink()) { throw new Error(`${label} must be a real directory: ${candidate}`); } return realpath(candidate); } async function assertRegularFile(candidate, label) { const details = await lstat(candidate).catch(() => null); if (!details?.isFile() || details.isSymbolicLink()) { throw new Error(`${label} must be a regular file: ${candidate}`); } return realpath(candidate); } function commandLabel(command, arguments_) { return [command, ...arguments_] .map((part) => /^[A-Za-z0-9_./:=,+-]+$/u.test(part) ? part : JSON.stringify(part), ) .join(" "); } async function run(command, arguments_, options = {}) { const child = spawn(command, arguments_, { cwd: options.cwd, env: options.env, stdio: ["ignore", "pipe", "pipe"], }); const stdout = []; const stderr = []; child.stdout.on("data", (chunk) => stdout.push(chunk)); child.stderr.on("data", (chunk) => stderr.push(chunk)); const result = await new Promise((resolve, reject) => { child.once("error", reject); child.once("close", (code, signal) => resolve({ code, signal })); }); const output = Buffer.concat(stdout).toString("utf8"); const errorOutput = Buffer.concat(stderr).toString("utf8"); if (result.code !== 0) { const detail = [output, errorOutput].filter(Boolean).join("\n").trim(); throw new Error( `${commandLabel(command, arguments_)} failed with ${ result.signal ? `signal ${result.signal}` : `exit code ${result.code}` }${detail ? `:\n${detail}` : "."}`, ); } return { stdout: output, stderr: errorOutput }; } function parseCargoLock(contents) { const packages = []; for (const block of contents.split("[[package]]").slice(1)) { const field = (name) => { const match = block.match(new RegExp(`^${name} = "([^"]+)"`, "mu")); return match?.[1]; }; const name = field("name"); const version = field("version"); if (!name || !version) { throw new Error("Cargo.lock contains a malformed package block."); } const source = field("source"); const checksum = field("checksum"); packages.push({ name, version, ...(source ? { source } : {}), ...(checksum ? { checksum } : {}), }); } return packages; } async function findRegistryCrate(cargoHome, package_) { const registrySource = path.join(cargoHome, "registry", "src"); const indexes = await readdir(registrySource, { withFileTypes: true }); const matches = []; for (const index of indexes) { if (!index.isDirectory() || index.isSymbolicLink()) continue; const candidate = path.join( registrySource, index.name, `${package_.name}-${package_.version}`, ); const details = await lstat(candidate).catch(() => null); if (details?.isDirectory() && !details.isSymbolicLink()) { matches.push(await realpath(candidate)); } } if (matches.length !== 1) { throw new Error( `Expected one cached source directory for ${package_.name} ${package_.version}.`, ); } return matches[0]; } async function thirdPartyLicenses(cargoHome, packages) { const sections = [ "Regex Tools Rust engine third-party source notices", "Generated from the checked-in Cargo.lock; package order is deterministic.", "", ]; for (const package_ of packages.filter(({ source }) => source)) { const crate = await findRegistryCrate(cargoHome, package_); const entries = (await readdir(crate, { withFileTypes: true })) .filter( (entry) => entry.isFile() && !entry.isSymbolicLink() && /^(?:COPYING|COPYRIGHT|LICENSE|UNLICENSE)/iu.test(entry.name), ) .sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0, ); sections.push( `===============================================================================`, `${package_.name} ${package_.version}`, `Cargo source: ${package_.source}`, `Cargo checksum: ${package_.checksum ?? "not recorded"}`, ); if (entries.length === 0) { sections.push( "No root licence file was present in the packaged crate source.", "", ); continue; } for (const entry of entries) { sections.push( `-------------------------------------------------------------------------------`, entry.name, "", (await readFile(path.join(crate, entry.name), "utf8")).trimEnd(), "", ); } } return `${sections.join("\n")}\n`; } async function verifyToolchain(toolchainRootDirectory) { const root = await assertRealDirectory( toolchainRootDirectory, "Rust 1.97.1 toolchain", ); const cargoHome = await assertRealDirectory( path.join(root, "cargo"), "Rust Cargo home", ); const rustupHome = await assertRealDirectory( path.join(root, "rustup"), "Rustup home", ); const toolchain = await assertRealDirectory( path.join(rustupHome, "toolchains", "1.97.1-x86_64-unknown-linux-gnu"), "Rust distribution toolchain", ); const cargo = await assertRegularFile( path.join(toolchain, "bin", "cargo"), "cargo", ); const rustc = await assertRegularFile( path.join(toolchain, "bin", "rustc"), "rustc", ); const wasmBindgen = await assertRegularFile( path.join(cargoHome, "bin", "wasm-bindgen"), "wasm-bindgen", ); for (const executable of [cargo, rustc, wasmBindgen]) { await access(executable, fsConstants.X_OK); } const baseEnvironment = { ...process.env, CARGO_HOME: cargoHome, RUSTUP_HOME: rustupHome, PATH: [ path.join(toolchain, "bin"), path.join(cargoHome, "bin"), process.env.PATH ?? "", ].join(path.delimiter), }; const rustcVersion = await run(rustc, ["--version", "--verbose"], { env: baseEnvironment, }); if ( !rustcVersion.stdout.includes(`release: ${RUST_LOCK.rustcVersion}`) || !rustcVersion.stdout.includes(`commit-hash: ${RUST_LOCK.rustcCommit}`) || !rustcVersion.stdout.includes(`LLVM version: ${RUST_LOCK.llvmVersion}`) ) { throw new Error("The Rust engine requires pinned rustc 1.97.1."); } const cargoVersion = await run(cargo, ["--version"], { env: baseEnvironment, }); if (cargoVersion.stdout.trim() !== RUST_LOCK.cargoIdentity) { throw new Error("The Rust engine requires pinned Cargo 1.97.1."); } const bindgenVersion = await run(wasmBindgen, ["--version"], { env: baseEnvironment, }); if ( bindgenVersion.stdout.trim() !== `wasm-bindgen ${RUST_LOCK.wasmBindgenVersion}` ) { throw new Error("The Rust engine requires pinned wasm-bindgen 0.2.126."); } const docs = path.join(toolchain, "share", "doc", "rust"); const files = { apache: await assertRegularFile( path.join(docs, "licenses", "Apache-2.0.txt"), "Rust Apache licence", ), mit: await assertRegularFile( path.join(docs, "licenses", "MIT.txt"), "Rust MIT licence", ), unicode: await assertRegularFile( path.join(docs, "licenses", "Unicode-3.0.txt"), "Rust Unicode licence", ), stdlibCopyright: await assertRegularFile( path.join(docs, "COPYRIGHT-library.html"), "Rust standard-library copyright notice", ), }; for (const [name, file] of Object.entries(files)) { if ( (await sha256File(file)) !== RUST_LOCK.distribution.licenseHashes[name] ) { throw new Error(`The pinned Rust ${name} notice does not match 1.97.1.`); } } return { root, cargoHome, rustupHome, cargo, rustc, wasmBindgen, baseEnvironment, files, }; } function buildEnvironment(toolchain, repositoryRoot, targetDirectory) { const environment = { ...toolchain.baseEnvironment }; for (const variable of [ "CARGO_BUILD_RUSTC", "CARGO_ENCODED_RUSTFLAGS", "CARGO_PROFILE_RELEASE_DEBUG", "RUSTC", "RUSTDOC", "RUSTDOCFLAGS", "RUSTFLAGS", ]) { delete environment[variable]; } environment.CARGO_INCREMENTAL = "0"; environment.CARGO_NET_OFFLINE = "true"; environment.CARGO_TARGET_DIR = targetDirectory; environment.LANG = "C"; environment.LC_ALL = "C"; environment.REGEX_TOOLS_RUSTC_VERSION = RUST_LOCK.rustcIdentity; environment.RUST_BACKTRACE = "0"; environment.RUSTUP_TOOLCHAIN = "1.97.1-x86_64-unknown-linux-gnu"; environment.RUSTFLAGS = [ `--remap-path-prefix=${repositoryRoot}=.`, `--remap-path-prefix=${toolchain.cargoHome}=./cargo-home`, ].join(" "); environment.SOURCE_DATE_EPOCH = String(RUST_LOCK.sourceDateEpoch); environment.TZ = "UTC"; return environment; } async function sourceMetadata(root) { return Promise.all( SOURCE_FILES.map(async (relativePath) => { const contents = await readFile(path.join(root, relativePath)); return { path: relativePath, sha256: sha256(contents), bytes: contents.byteLength, }; }), ); } async function fileMetadata(pack, name) { const contents = await readFile(path.join(pack, name)); return { path: name, sha256: sha256(contents), bytes: contents.byteLength }; } async function lockedPackages(root) { const contents = await readFile( path.join(root, "engines", "rust", "Cargo.lock"), "utf8", ); return parseCargoLock(contents) .filter(({ source }) => source) .map(({ name, version, checksum }) => ({ name, version, checksum })); } async function expectedMetadata(root, pack) { return { schemaVersion: 1, engine: "rust", engineName: "Rust regex crate", engineVersion: RUST_LOCK.engineVersion, semanticIdentity: "Rust regex crate 1.13.1 compiled to WebAssembly with rustc 1.97.1", status: "production-native-runtime", bridge: { abiVersion: RUST_LOCK.bridgeAbi, offsetUnit: "utf8-byte", requestEncoding: "Exact UTF-8-bounded JSON values with worst-case escape allowance; fixed exports; base64 UTF-8 replacement output; no source evaluation", supportedApplicationFlags: ["g", "i", "m", "s", "U", "u", "x", "R"], unicode: "Enabled by default, matching the Rust regex crate; u explicitly affirms that default.", replacement: "Bounded streaming expansion with regex::Captures::expand grammar, parity-checked against the native API.", limits: { ...RUST_LOCK.limits }, sourceFiles: await sourceMetadata(root), }, source: { repository: RUST_LOCK.source.repository, tag: RUST_LOCK.source.tag, tagObjectSha1: RUST_LOCK.source.tagObject, commitSha1: RUST_LOCK.source.commit, treeSha1: RUST_LOCK.source.tree, crateChecksumSha256: RUST_LOCK.source.crateChecksum, license: RUST_LOCK.source.license, }, toolchain: { rustcVersion: RUST_LOCK.rustcVersion, rustcIdentity: RUST_LOCK.rustcIdentity, rustcCommitSha1: RUST_LOCK.rustcCommit, llvmVersion: RUST_LOCK.llvmVersion, cargoIdentity: RUST_LOCK.cargoIdentity, target: "wasm32-unknown-unknown", wasmBindgenVersion: RUST_LOCK.wasmBindgenVersion, wasmBindgenCrateChecksumSha256: RUST_LOCK.wasmBindgenCrateChecksum, wasmBindgenCliChecksumSha256: RUST_LOCK.wasmBindgenCliChecksum, distributionManifest: RUST_LOCK.distribution.manifest, distributionManifestSha256: RUST_LOCK.distribution.manifestSha256, rustcArchiveSha256: RUST_LOCK.distribution.rustcArchiveSha256, wasmStdArchiveSha256: RUST_LOCK.distribution.wasmStdArchiveSha256, cargoLockPackages: await lockedPackages(root), }, sourceDateEpoch: RUST_LOCK.sourceDateEpoch, files: await Promise.all( CHECKSUM_FILES.filter((name) => name !== "engine-metadata.json").map( (name) => fileMetadata(pack, name), ), ), }; } async function checksumText(pack) { const lines = await Promise.all( CHECKSUM_FILES.map( async (name) => `${await sha256File(path.join(pack, name))} ${name}`, ), ); return `${lines.join("\n")}\n`; } function equalJson(left, right) { return JSON.stringify(left) === JSON.stringify(right); } async function smokePack(pack) { const moduleUrl = new URL(`file://${path.join(pack, "rust-regex.mjs")}`).href; const wasmPath = path.join(pack, "rust-regex_bg.wasm"); const smoke = ` import { readFile } from "node:fs/promises"; const engine = await import(process.argv[1]); engine.initSync({ module: await WebAssembly.compile(await readFile(process.argv[2])), }); const identity = JSON.parse(engine.identity()); if ( identity.abi !== 1 || identity.ok !== true || identity.identity.engineVersion !== "1.13.1" || identity.identity.rustcVersion !== "rustc 1.97.1 (8bab26f4f 2026-07-14)" || identity.identity.selfTest !== true ) throw new Error("Rust identity self-test failed."); const result = JSON.parse(engine.run(JSON.stringify({ abi: 1, operation: "replace", pattern: "(?P😀+)", flags: "g", options: {}, subject: "x😀😀", scanAll: false, maximumMatches: 10, maximumCaptureRows: 10, replacement: "\${word}-$$", maximumOutputBytes: 100, }))); if ( result.ok !== true || result.matches[0].span[0] !== 1 || result.matches[0].span[1] !== 9 || Buffer.from(result.outputBase64, "base64").toString("utf8") !== "x😀😀-$" ) throw new Error("Rust execution self-test failed."); `; await run(process.execPath, [ "--input-type=module", "--eval", smoke, moduleUrl, wasmPath, ]); } export async function verifyRustPack(packDirectory, root) { const repositoryRoot = await assertRealDirectory( root, "Regex Tools repository", ); const pack = await assertRealDirectory(packDirectory, "Rust engine pack"); const entries = (await readdir(pack, { withFileTypes: true })).sort( (left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0, ); if ( entries.length !== PACK_FILES.length || entries.some( (entry, index) => entry.name !== PACK_FILES[index] || !entry.isFile() || entry.isSymbolicLink(), ) ) { throw new Error( `The Rust pack must contain only: ${PACK_FILES.join(", ")}.`, ); } const licenseFiles = { "LICENSE-Apache-2.0.txt": RUST_LOCK.distribution.licenseHashes.apache, "LICENSE-MIT.txt": RUST_LOCK.distribution.licenseHashes.mit, "LICENSE-Unicode-3.0.txt": RUST_LOCK.distribution.licenseHashes.unicode, "RUST-STDLIB-COPYRIGHT.html": RUST_LOCK.distribution.licenseHashes.stdlibCopyright, }; for (const [name, digest] of Object.entries(licenseFiles)) { if ((await sha256File(path.join(pack, name))) !== digest) { throw new Error(`${name} does not match the Rust 1.97.1 distribution.`); } } const wasm = await readFile(path.join(pack, "rust-regex_bg.wasm")); if ( wasm.byteLength < 100_000 || wasm.subarray(0, 4).toString("hex") !== "0061736d" ) { throw new Error("The staged Rust WebAssembly module is invalid."); } const moduleText = await readFile(path.join(pack, "rust-regex.mjs"), "utf8"); if ( moduleText.includes("sourceMappingURL") || moduleText.includes("/mnt/") || moduleText.includes("/home/") || !moduleText.includes("export function identity") || !moduleText.includes("export function run") || !moduleText.includes("export { initSync") ) { throw new Error( "rust-regex.mjs leaks a build path, has a source map, or lacks bridge exports.", ); } const packages = await lockedPackages(repositoryRoot); const notices = await readFile( path.join(pack, "THIRD_PARTY_LICENSES.txt"), "utf8", ); for (const package_ of packages) { if ( !notices.includes(`${package_.name} ${package_.version}\n`) || !notices.includes(`Cargo checksum: ${package_.checksum}`) ) { throw new Error( `Third-party notices omit ${package_.name} ${package_.version}.`, ); } } let metadata; try { metadata = JSON.parse( await readFile(path.join(pack, "engine-metadata.json"), "utf8"), ); } catch (error) { throw new Error("Rust engine-metadata.json is invalid JSON.", { cause: error, }); } if (!equalJson(metadata, await expectedMetadata(repositoryRoot, pack))) { throw new Error( "The Rust metadata does not match the pinned build contract.", ); } if ( (await readFile(path.join(pack, "SHA256SUMS"), "utf8")) !== (await checksumText(pack)) ) { throw new Error("The Rust SHA256SUMS file is incorrect."); } await smokePack(pack); return metadata; } async function replaceDirectory(stage, target, label) { const details = await lstat(target).catch(() => null); if (details && (!details.isDirectory() || details.isSymbolicLink())) { throw new Error(`${label} must be a real directory.`); } if (!details) { await rename(stage, target); return; } const backup = `${target}.replaced-${process.pid}`; await rename(target, backup); try { await rename(stage, target); } catch (error) { await rename(backup, target); throw error; } await rm(backup, { recursive: true }); } async function bindgen(toolchain, rawWasm, output) { await mkdir(output, { recursive: true }); await run( toolchain.wasmBindgen, [ "--target", "web", "--no-typescript", "--out-name", "rust-regex", "--out-dir", output, rawWasm, ], { env: toolchain.baseEnvironment }, ); await rename( path.join(output, "rust-regex.js"), path.join(output, "rust-regex.mjs"), ); } export async function buildRustPack( root, toolchainRootDirectory, outputDirectory, ) { const repositoryRoot = await assertRealDirectory( root, "Regex Tools repository", ); const engine = await assertRealDirectory( path.join(repositoryRoot, "engines", "rust"), "Rust engine source", ); const toolchain = await verifyToolchain(toolchainRootDirectory); const lockPackages = parseCargoLock( await readFile(path.join(engine, "Cargo.lock"), "utf8"), ); const regexPackage = lockPackages.find( ({ name, version }) => name === "regex" && version === "1.13.1", ); const bindgenPackage = lockPackages.find( ({ name, version }) => name === "wasm-bindgen" && version === RUST_LOCK.wasmBindgenVersion, ); if ( regexPackage?.checksum !== RUST_LOCK.source.crateChecksum || bindgenPackage?.checksum !== RUST_LOCK.wasmBindgenCrateChecksum ) { throw new Error("Cargo.lock does not contain the pinned engine crates."); } const buildParent = path.join(repositoryRoot, ".engine-build"); await mkdir(buildParent, { recursive: true }); const temporary = await mkdtemp(path.join(buildParent, "rust-build-")); try { const generated = []; for (const label of ["first", "second"]) { const target = path.join(temporary, `${label}-target`); const environment = buildEnvironment(toolchain, repositoryRoot, target); await run( toolchain.cargo, [ "build", "--locked", "--offline", "--release", "--target", "wasm32-unknown-unknown", ], { cwd: engine, env: environment }, ); const rawWasm = await assertRegularFile( path.join( target, "wasm32-unknown-unknown", "release", "regex_tools_rust_engine.wasm", ), "compiled Rust WebAssembly", ); const output = path.join(temporary, `${label}-bindgen`); await bindgen(toolchain, rawWasm, output); generated.push(output); } for (const name of ["rust-regex.mjs", "rust-regex_bg.wasm"]) { if ( (await sha256File(path.join(generated[0], name))) !== (await sha256File(path.join(generated[1], name))) ) { throw new Error( `Two pinned Rust builds produced different ${name} hashes.`, ); } } const target = path.resolve( repositoryRoot, outputDirectory ?? path.join(".engine-build", "rust"), ); const stage = await mkdtemp(path.join(buildParent, "rust-pack-")); await copyFile( toolchain.files.apache, path.join(stage, "LICENSE-Apache-2.0.txt"), ); await copyFile(toolchain.files.mit, path.join(stage, "LICENSE-MIT.txt")); await copyFile( toolchain.files.unicode, path.join(stage, "LICENSE-Unicode-3.0.txt"), ); await copyFile( toolchain.files.stdlibCopyright, path.join(stage, "RUST-STDLIB-COPYRIGHT.html"), ); await copyFile( path.join(generated[0], "rust-regex.mjs"), path.join(stage, "rust-regex.mjs"), ); await copyFile( path.join(generated[0], "rust-regex_bg.wasm"), path.join(stage, "rust-regex_bg.wasm"), ); await writeFile( path.join(stage, "THIRD_PARTY_LICENSES.txt"), await thirdPartyLicenses(toolchain.cargoHome, lockPackages), "utf8", ); const metadata = await expectedMetadata(repositoryRoot, stage); await writeFile( path.join(stage, "engine-metadata.json"), `${JSON.stringify(metadata, null, 2)}\n`, "utf8", ); await writeFile(path.join(stage, "SHA256SUMS"), await checksumText(stage)); await verifyRustPack(stage, repositoryRoot); await mkdir(path.dirname(target), { recursive: true }); await replaceDirectory(stage, target, "Rust staged pack"); return { metadata, output: target }; } finally { await rm(temporary, { recursive: true }); } } export async function installRustPack(sourceDirectory, root) { const repositoryRoot = await assertRealDirectory( root, "Regex Tools repository", ); const source = await assertRealDirectory(sourceDirectory, "Rust staged pack"); const metadata = await verifyRustPack(source, repositoryRoot); const engineParent = path.join(repositoryRoot, "public", "engines"); await mkdir(engineParent, { recursive: true }); const stage = await mkdtemp(path.join(engineParent, "rust-install-")); for (const name of PACK_FILES) { await copyFile(path.join(source, name), path.join(stage, name)); } await verifyRustPack(stage, repositoryRoot); const output = path.join(engineParent, "rust"); await replaceDirectory(stage, output, "Installed Rust engine pack"); return { metadata, output }; }