75 lines
4.9 KiB
JavaScript
75 lines
4.9 KiB
JavaScript
#!/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 { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
const p6 = path.join(root, "docs/p6");
|
|
const build = path.resolve(process.argv[2] ?? path.join(root, "freecad-source/build-web/fem-wasm/bin"));
|
|
const hash = value => crypto.createHash("sha256").update(value).digest("hex");
|
|
const matrixPath = path.join(p6, "object-contract-matrix.json");
|
|
const matrixBytes = fs.readFileSync(matrixPath);
|
|
const matrix = JSON.parse(matrixBytes);
|
|
const rows = matrix.records.filter(record => record.module === "Fem" && record.adapterFamily === "geometry-recompute");
|
|
const names = rows.map(record => record.name);
|
|
globalThis.__dirname = `${build}${path.sep}`;
|
|
globalThis.require = createRequire(import.meta.url);
|
|
const loader = await import(pathToFileURL(path.join(build, "FreeCADWebFemGenericFamilyProbe.js")));
|
|
const kernel = await loader.default({
|
|
wasmBinary: fs.readFileSync(path.join(build, "FreeCADWebFemGenericFamilyProbe.wasm")),
|
|
locateFile: file => path.join(build, file),
|
|
print: () => {},
|
|
printErr: () => {},
|
|
});
|
|
const dispatch = request => JSON.parse(String(kernel.ccall("fc_web_dispatch_json", "string", ["string"], [JSON.stringify(request)])));
|
|
const request = { cmd: "fem.geometryFamily", args: { records: names } };
|
|
const result = dispatch(request);
|
|
if (result.ok !== true || result.schema !== "freecad-web-fem/geometry-family/v1" || result.module !== "Fem" || result.source !== "freecad-source" || result.count !== names.length || result.resourcesReleased !== true) {
|
|
throw new Error(JSON.stringify({ code: "native_protocol_error", result }));
|
|
}
|
|
const nativeRecords = Array.isArray(result.records) ? result.records : [];
|
|
const byName = new Map(nativeRecords.map(record => [record.name, record]));
|
|
const records = rows.map(row => {
|
|
const native = byName.get(row.name);
|
|
const status = native?.status === "created_recomputed" ? "passed" : native?.status === "registered_boundary_non_document" ? "passed_boundary" : "blocked";
|
|
return { id: row.id, name: row.name, status, nativeStatus: native?.status ?? "missing", detail: native?.detail ?? "native response omitted record" };
|
|
});
|
|
const counts = Object.fromEntries(["passed", "passed_boundary", "blocked"].map(status => [status, records.filter(record => record.status === status).length]));
|
|
const kernelReport = {
|
|
schema: "freecad-web-p6-fem-geometry-family-kernel/v1",
|
|
taskId: "FW-P6-FPA-D1-FEM",
|
|
result: counts.blocked === 0 ? "pass" : "pass_with_blocked_records",
|
|
source: {
|
|
probe: "FreeCADWebFemGenericFamilyProbe",
|
|
cxx: "freecad-source/src/WebAssembly/FreeCADCoreProbe/FemGenericFamilyProbe.cpp",
|
|
js: path.relative(root, path.join(build, "FreeCADWebFemGenericFamilyProbe.js")),
|
|
wasm: path.relative(root, path.join(build, "FreeCADWebFemGenericFamilyProbe.wasm")),
|
|
jsSha256: hash(fs.readFileSync(path.join(build, "FreeCADWebFemGenericFamilyProbe.js"))),
|
|
wasmSha256: hash(fs.readFileSync(path.join(build, "FreeCADWebFemGenericFamilyProbe.wasm"))),
|
|
},
|
|
request,
|
|
requestSha256: hash(JSON.stringify(request)),
|
|
dispatch: result,
|
|
records,
|
|
counts: { inventory: rows.length, reported: nativeRecords.length, ...counts, resourcesReleased: result.resourcesReleased },
|
|
boundaries: ["Fem::FemMesh and Fem::PropertyFemMesh are native registered non-DocumentObject boundaries.", "Every DocumentObject record is created and recomputed by FreeCAD native code; failures remain blocked."],
|
|
};
|
|
fs.writeFileSync(path.join(p6, "fem-geometry-family-kernel.json"), `${JSON.stringify(kernelReport, null, 2)}\n`);
|
|
fs.writeFileSync(path.join(root, "docs/freecad-p6-fem-geometry-family-kernel.log"), `${JSON.stringify({ result: kernelReport.result, counts: kernelReport.counts, requestSha256: kernelReport.requestSha256 })}\n`);
|
|
const gateResult = counts.blocked === 0 ? "pass" : "pass_with_blocked_records";
|
|
const gateReport = {
|
|
schema: "freecad-web-p6-fem-geometry-family-gate/v1",
|
|
taskId: "FW-P6-FPA-D1-FEM",
|
|
result: gateResult,
|
|
coverage: { inventoryRecords: rows.length, probedRecords: records.length, counts, resourcesReleased: result.resourcesReleased },
|
|
records,
|
|
source: { kernel: "docs/p6/fem-geometry-family-kernel.json", kernelSha256: hash(fs.readFileSync(path.join(p6, "fem-geometry-family-kernel.json"))), matrix: "docs/p6/object-contract-matrix.json", matrixSha256: hash(matrixBytes) },
|
|
boundaries: kernelReport.boundaries,
|
|
};
|
|
fs.writeFileSync(path.join(p6, "fem-geometry-family-gate.json"), `${JSON.stringify(gateReport, null, 2)}\n`);
|
|
fs.writeFileSync(path.join(root, "docs/freecad-p6-fem-geometry-family-gate.log"), `${JSON.stringify({ result: gateResult, counts, blocked: records.filter(record => record.status === "blocked").map(record => record.name) })}\n`);
|
|
console.log(JSON.stringify({ result: gateResult, counts }));
|
|
if (gateResult === "pass_with_blocked_records") process.exitCode = 2;
|