import crypto from "node:crypto"; import fs from "node:fs"; import path from "node:path"; import { createRequire } from "node:module"; import { pathToFileURL } from "node:url"; const root = path.resolve(path.dirname(new URL(import.meta.url).pathname), ".."); const docs = path.join(root, "docs/p6"); const build = path.resolve(process.argv[2] ?? path.join(root, "freecad-source/build-web/configure/bin")); globalThis.__dirname = `${build}${path.sep}`; globalThis.require = createRequire(import.meta.url); const loader = await import(pathToFileURL(path.join(build, "FreeCADWebReverseEngineeringRuntimeProbe.js"))); const kernel = await loader.default({ wasmBinary: fs.readFileSync(path.join(build, "FreeCADWebReverseEngineeringRuntimeProbe.wasm")), print: () => {}, printErr: () => {} }); const c = (name, type = "number", argTypes = [], args = []) => kernel.ccall(name, type, argTypes, args); const entry = "src/Mod/ReverseEngineering/App/AppReverseEngineering.cpp"; const inspect = () => ({ returnCode: c("fc_web_reverseengineering_source_inventory_validate", "number", ["string"], [entry]), sourceEntry: c("fc_web_reverseengineering_source_entry", "string"), summary: c("fc_web_reverseengineering_source_summary", "string"), registeredTypeCount: c("fc_web_reverseengineering_registered_type_count"), pythonBindingCount: c("fc_web_reverseengineering_python_binding_count"), pythonApiNames: c("fc_web_reverseengineering_api_names", "string"), resourcesReleased: c("fc_web_reverseengineering_inventory_resources_released"), error: c("fc_web_reverseengineering_error", "string") }); const first = inspect(); const second = inspect(); const invalidEntry = "src/Mod/ReverseEngineering/App/Missing.cpp"; const rejected = { returnCode: c("fc_web_reverseengineering_source_inventory_validate", "number", ["string"], [invalidEntry]), error: c("fc_web_reverseengineering_error", "string") }; const recovered = inspect(); const fixture = { module: "ReverseEngineering", action: "inspect/source-inventory", sourceEntry: entry, pythonBindingCount: 2 }; const hash = value => crypto.createHash("sha256").update(value).digest("hex"); const baselineHash = hash(JSON.stringify(first)); const recoveredHash = hash(JSON.stringify(recovered)); const result = { first, second, rejected, recovered, deterministic: JSON.stringify(first) === JSON.stringify(second), recoveryStable: baselineHash === recoveredHash, pass: first.returnCode === 1 && second.returnCode === 1 && rejected.returnCode === 0 && rejected.error === "revision_mismatch" && recovered.returnCode === 1 && first.sourceEntry === entry && first.pythonBindingCount === 2 && first.resourcesReleased === 1 }; const wasmPath = path.join(build, "FreeCADWebReverseEngineeringRuntimeProbe.wasm"); const report = { schema: "freecad-web-p6-reverseengineering-action-02-kernel/v1", taskId: "FW-P6-REV-ACT-02-C", module: "ReverseEngineering", action: "inspect/source-inventory", fixture: { ...fixture, inputSha256: hash(JSON.stringify(fixture)) }, result: result.pass && result.deterministic && result.recoveryStable ? "pass" : "fail", replay: { ...result, baselineSha256: baselineHash, recoveredSha256: recoveredHash, resultSha256: hash(JSON.stringify(result)) }, commands: ["ninja -C freecad-source/build-web/configure FreeCADWebReverseEngineeringRuntimeProbe -j8", "node tools/run_p6_reverseengineering_action02_kernel.mjs freecad-source/build-web/configure/bin"], artifacts: { wasm: { path: "freecad-source/build-web/configure/bin/FreeCADWebReverseEngineeringRuntimeProbe.wasm", sha256: hash(fs.readFileSync(wasmPath)) }, workerLog: "docs/freecad-p6-reverseengineering-action-02-c-kernel.log" }, boundaries: ["ReverseEngineering App has no persistent DocumentObject registrations in this GUI-free slice; the inventory reports zero native object types and two Python bindings.", "PCL-only APIs and ReverseEngineeringGui remain outside this action."] }; fs.writeFileSync(path.join(docs, "reverseengineering-action-02-kernel.json"), `${JSON.stringify(report, null, 2)}\n`); fs.writeFileSync(path.join(root, "docs/freecad-p6-reverseengineering-action-02-c-kernel.log"), `${JSON.stringify({ result: report.result, deterministic: result.deterministic, recoveryStable: result.recoveryStable, pythonBindingCount: first.pythonBindingCount })}\n`); console.log(JSON.stringify({ result: report.result, deterministic: result.deterministic, recoveryStable: result.recoveryStable, pythonBindingCount: first.pythonBindingCount })); if (report.result !== "pass") process.exitCode = 1;