Files
FreeCADWasmDev/tools/run_p6_sketcher_property_module_batch_gate.mjs
wangdequan 9a7c4f0335 Save FreeCAD Web workspace sources and user validation evidence
IFC native binding remains incomplete; preserve accurate test results.
2026-09-15 22:01:58 +08:00

25 lines
5.8 KiB
JavaScript

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="/home/mes123456/FreeCADWasmDev/working/FreeCAD";
const build=path.resolve(process.argv[2]??path.join(root,"freecad-source/build-web/configure/bin"));
const batch=JSON.parse(fs.readFileSync(path.join(root,"docs/current/module-batch-manifest.json"),"utf8"));
if(batch.status!=="ready"||batch.taskId!=="FW-P6-FPA-Y"||batch.selected?.module!=="Sketcher"||batch.selected?.tier!=="tier-3-stateful"||batch.selected?.family!=="property") throw Error("unexpected Sketcher property batch");
const records=batch.records.map(([apiId,name,disposition,sourcePath,sourceLine])=>({apiId,name,family:"property",sourcePath,sourceLine,disposition}));
const out=fs.mkdtempSync("/tmp/sketcher-property-");
const req=await import(pathToFileURL(path.join(root,"web/src/sketcher-python-family-facade.ts"))).catch(()=>null);
const {execFileSync}=await import("node:child_process");
execFileSync(path.join(root,"web/node_modules/.bin/tsc"),["--target","ES2022","--module","NodeNext","--moduleResolution","NodeNext","--skipLibCheck","--outDir",out,path.join(root,"web/src/facade-envelope.ts"),path.join(root,"web/src/sketcher-python-family-facade.ts")],{cwd:root,stdio:"inherit"});
const facade=await import(pathToFileURL(path.join(out,"sketcher-python-family-facade.js"))); const env=await import(pathToFileURL(path.join(out,"facade-envelope.js")));
const stage=fs.mkdtempSync("/tmp/sketcher-property-runtime-"); fs.writeFileSync(path.join(stage,"package.json"),'{"type":"module"}\n'); fs.copyFileSync(path.join(build,"FreeCADWebSketcherProbe.wasm"),path.join(stage,"FreeCADWebSketcherProbe.wasm")); fs.writeFileSync(path.join(stage,"FreeCADWebSketcherProbe.js"),fs.readFileSync(path.join(build,"FreeCADWebSketcherProbe.js"),"utf8").replaceAll("FreeCADWebSketcherProbe.worker.js","FreeCADWebSketcherProbe.worker.cjs")); fs.copyFileSync(path.join(build,"FreeCADWebSketcherProbe.worker.js"),path.join(stage,"FreeCADWebSketcherProbe.worker.cjs"));
globalThis.__dirname=`${stage}${path.sep}`; globalThis.require=createRequire(import.meta.url); const loader=await import(pathToFileURL(path.join(stage,"FreeCADWebSketcherProbe.js"))); const kernel=await loader.default({wasmBinary:fs.readFileSync(path.join(stage,"FreeCADWebSketcherProbe.wasm")),locateFile:f=>path.join(stage,f)});
const q=new env.FacadeSerialQueue(); const request=facade.createSketcherPythonFamilyRequest("sketcher-property-success",records,"object-contract"); const success=await facade.dispatchSketcherPythonFamily(q,request,kernel); const repeat=await facade.dispatchSketcherPythonFamily(q,facade.createSketcherPythonFamilyRequest("sketcher-property-repeat",records,"object-contract"),kernel); if(success.kind!=="result"||repeat.kind!=="result") throw Error(JSON.stringify({success,repeat}));
const result=success.result; if(result.count!==records.length||result.resourcesReleased!==true||JSON.stringify(result)!==JSON.stringify(repeat.result)) throw Error("property result/replay invalid");
const hash=v=>crypto.createHash("sha256").update(typeof v==="string"?v:JSON.stringify(v)).digest("hex"); const counts=Object.fromEntries(["exposed","restricted","nonportable","not_implemented"].map(s=>[s,result.records.filter(r=>r.status===s).length]));
const manifestPath=path.join(root,"docs/p6/python-api-manifest.json"); const manifest=JSON.parse(fs.readFileSync(manifestPath,"utf8")); const byId=new Map(result.records.map(r=>[r.apiId,r])); for(const row of manifest.records){const n=byId.get(row.id); if(n){row.disposition=n.status;row.dispositionEvidence=[`docs/p6/sketcher-property-module-batch-${batch.batchId}-runtime.json`];row.dispositionReason=n.message;}} fs.writeFileSync(manifestPath,JSON.stringify(manifest,null,2)+"\n");
const modules=JSON.parse(fs.readFileSync(path.join(root,"web/public/modules/manifest.json"),"utf8")); const mod=modules.modules?.Sketcher??modules.Sketcher; const wk=mod?.workers?.["sketcher-python.worker.ts"]; if(!mod?.revision||!wk?.runtimeUrl||!wk?.wasmUrl) throw Error("artifact manifest incomplete");
const report={schema:"freecad-web-sketcher-property-module-batch-runtime/v1",taskId:"FW-P6-FPA-Y",batchId:batch.batchId,module:"Sketcher",tier:"tier-3-stateful",family:"property",status:"pass_with_boundaries",dispatcher:"fc_web_dispatch_json",route:{command:facade.SKETCHER_PYTHON_FAMILY_COMMAND,native:"freecad-source/src/WebAssembly/FreeCADCoreProbe/SketcherProbe.cpp:fc_web_dispatch_json",facade:"web/src/sketcher-python-family-facade.ts",worker:"web/src/sketcher-python.worker.ts"},input:{cmd:facade.SKETCHER_PYTHON_FAMILY_COMMAND,args:{tier:"object-contract",records}},inputSha256:hash(records),result,resultSha256:hash(result),replay:{equal:true,resultSha256:hash(repeat.result)},dispositionCounts:counts,resources:{resourcesReleased:true,documentMutation:false},artifacts:{module:"Sketcher",worker:"sketcher-python.worker.ts",revision:mod.revision,runtimeUrl:wk.runtimeUrl,wasmUrl:wk.wasmUrl,dataUrl:wk.dataUrl??null,assetBaseUrl:mod.assetBaseUrl,workerRevision:wk.revision,runtimeSha256:wk.hashes?.runtime??null,wasmSha256:wk.hashes?.wasm??null},boundaries:["Property APIs remain explicit native fixture boundaries; statuses derive from compiled FreeCAD through fc_web_dispatch_json."]};
fs.writeFileSync(path.join(root,`docs/p6/sketcher-property-module-batch-${batch.batchId}-runtime.json`),JSON.stringify(report,null,2)+"\n"); fs.writeFileSync(path.join(root,`docs/freecad-p6-sketcher-property-module-batch-${batch.batchId}.log`),JSON.stringify({status:report.status,batchId:batch.batchId,count:records.length,dispositionCounts:counts,resultSha256:report.resultSha256})+"\n"); console.log(JSON.stringify({status:report.status,batchId:batch.batchId,count:records.length,dispositionCounts:counts,resultSha256:report.resultSha256})); process.exit(0);