#!/usr/bin/env node 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 build = path.resolve(process.argv[2] || path.join(root, "freecad-source/build-web/configure/bin")); const methods = [ "Measurement.addReference3D", "Measurement.has3DReferences", "Measurement.volume", "Measurement.clear", "Measurement.length", "Measurement.area", "Measurement.delta", "Measurement.com", "Measurement.angle", "Measurement.lineLineDistance", "Measurement.planePlaneDistance", "Measurement.radius", ]; globalThis.__dirname = `${build}${path.sep}`; globalThis.require = createRequire(import.meta.url); const loader = await import(pathToFileURL(path.join(build, "FreeCADWebMeasureKernelProbe.js"))); const kernel = await loader.default({ wasmBinary: fs.readFileSync(path.join(build, "FreeCADWebMeasureKernelProbe.wasm")) }); const dispatch = (request) => JSON.parse(String(kernel.ccall("fc_web_dispatch_json", "string", ["string"], [JSON.stringify(request)]))); const request = { cmd: "measure.measurementApi", args: { methods } }; const first = dispatch(request); const second = dispatch(request); const malformed = dispatch({ cmd: request.cmd, args: { methods: ["Measurement.unknown"] } }); if (first.ok !== true || first.schema !== "freecad-web-measure/measurement-api/v1" || first.module !== "Measure" || first.action !== "measurement-api" || first.resourcesReleased !== true || JSON.stringify(first.methods) !== JSON.stringify(methods)) throw new Error(`native measurement dispatch failed: ${JSON.stringify(first)}`); if (JSON.stringify(first) !== JSON.stringify(second)) throw new Error("measurement dispatch replay changed the native result"); const values = first.values || {}; if (values["Measurement.addReference3D"] !== null || values["Measurement.has3DReferences"] !== true || values["Measurement.clear"] !== null || Math.abs(Number(values["Measurement.volume"]) - 24) > 1e-9 || values["Measurement.length"] !== 4 || values["Measurement.area"] !== 12 || values["Measurement.radius"] !== 5 || !Array.isArray(values["Measurement.delta"]) || !Array.isArray(values["Measurement.com"])) throw new Error(`measurement values failed: ${JSON.stringify(values)}`); if (malformed.ok !== false || malformed.code !== "unsupported_method") throw new Error(`unsupported method was accepted: ${JSON.stringify(malformed)}`); const hash = (value) => crypto.createHash("sha256").update(JSON.stringify(value)).digest("hex"); const report = { schema: "freecad-web-p6-measure-measurement-api-dispatch/v1", taskId: "FW-P6-FPA-Y", result: "pass", route: { command: "measure.measurementApi", native: "freecad-source/src/WebAssembly/FreeCADCoreProbe/MeasureKernelProbe.cpp:fc_web_dispatch_json", facade: "web/src/measure-measurement-facade.ts", worker: "web/src/measure.worker.ts" }, request, requestSha256: hash(request), response: first, responseSha256: hash(first), replay: { equal: true, responseSha256: hash(second) }, unsupportedMethod: { response: malformed, responseSha256: hash(malformed) }, semantics: { volume: values["Measurement.volume"], length: values["Measurement.length"], area: values["Measurement.area"], radius: values["Measurement.radius"], resourcesReleased: first.resourcesReleased }, boundaries: ["The native fixture executes Measure::Measurement against Part solids; method parameters remain the fixed source-backed fixture contract.", "Unsupported method names are rejected by the native dispatcher before a Worker result is returned."], }; fs.writeFileSync(path.join(root, "docs/p6/measure-measurement-api-dispatch.json"), `${JSON.stringify(report, null, 2)}\n`); fs.writeFileSync(path.join(root, "docs/freecad-p6-measure-measurement-api-dispatch.log"), `${JSON.stringify({ result: report.result, responseSha256: report.responseSha256, replayEqual: true, unsupportedMethod: malformed.code })}\n`); console.log(JSON.stringify({ result: report.result, responseSha256: report.responseSha256, replayEqual: true, unsupportedMethod: malformed.code }));