@faicad/faijs 0.13.1 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/api-namespace.d.ts.map +1 -1
- package/dist/api/api-namespace.js +30 -4
- package/dist/api/api-namespace.js.map +1 -1
- package/dist/api/boolean.d.ts +19 -0
- package/dist/api/boolean.d.ts.map +1 -1
- package/dist/api/boolean.js +51 -10
- package/dist/api/boolean.js.map +1 -1
- package/dist/api/chamfer.d.ts.map +1 -1
- package/dist/api/chamfer.js +8 -7
- package/dist/api/chamfer.js.map +1 -1
- package/dist/api/compound-geom.d.ts +45 -0
- package/dist/api/compound-geom.d.ts.map +1 -0
- package/dist/api/compound-geom.js +85 -0
- package/dist/api/compound-geom.js.map +1 -0
- package/dist/api/compound.d.ts +2 -0
- package/dist/api/compound.d.ts.map +1 -1
- package/dist/api/compound.js +2 -0
- package/dist/api/compound.js.map +1 -1
- package/dist/api/copy.d.ts +1 -0
- package/dist/api/copy.d.ts.map +1 -1
- package/dist/api/copy.js +4 -2
- package/dist/api/copy.js.map +1 -1
- package/dist/api/edge-ref.d.ts +3 -2
- package/dist/api/edge-ref.d.ts.map +1 -1
- package/dist/api/edge-ref.js +10 -6
- package/dist/api/edge-ref.js.map +1 -1
- package/dist/api/engrave.d.ts.map +1 -1
- package/dist/api/engrave.js +1 -0
- package/dist/api/engrave.js.map +1 -1
- package/dist/api/extrude.d.ts.map +1 -1
- package/dist/api/extrude.js +131 -3
- package/dist/api/extrude.js.map +1 -1
- package/dist/api/face-ref.d.ts.map +1 -1
- package/dist/api/face-ref.js +7 -4
- package/dist/api/face-ref.js.map +1 -1
- package/dist/api/fai_drill.d.ts +1 -1
- package/dist/api/fai_drill.d.ts.map +1 -1
- package/dist/api/fai_drill.js +31 -18
- package/dist/api/fai_drill.js.map +1 -1
- package/dist/api/fai_extrude.d.ts +1 -1
- package/dist/api/fai_extrude.d.ts.map +1 -1
- package/dist/api/fai_extrude.js +2 -1
- package/dist/api/fai_extrude.js.map +1 -1
- package/dist/api/fai_split.d.ts +1 -1
- package/dist/api/fai_split.d.ts.map +1 -1
- package/dist/api/fai_split.js +2 -1
- package/dist/api/fai_split.js.map +1 -1
- package/dist/api/fillet.d.ts.map +1 -1
- package/dist/api/fillet.js +6 -5
- package/dist/api/fillet.js.map +1 -1
- package/dist/api/generated/operations.js +16 -16
- package/dist/api/generated/operations.js.map +1 -1
- package/dist/api/generated/script-face-manifest.d.ts.map +1 -1
- package/dist/api/generated/script-face-manifest.js +0 -1
- package/dist/api/generated/script-face-manifest.js.map +1 -1
- package/dist/api/generated/script-face.d.ts +1 -4
- package/dist/api/generated/script-face.d.ts.map +1 -1
- package/dist/api/generated/script-face.js +2 -3
- package/dist/api/generated/script-face.js.map +1 -1
- package/dist/api/generated/sketching.js +1 -1
- package/dist/api/generated/sketching.js.map +1 -1
- package/dist/api/generated/topology.d.ts +0 -48
- package/dist/api/generated/topology.d.ts.map +1 -1
- package/dist/api/generated/topology.js +20 -62
- package/dist/api/generated/topology.js.map +1 -1
- package/dist/api/import-brep.d.ts +36 -0
- package/dist/api/import-brep.d.ts.map +1 -0
- package/dist/api/import-brep.js +72 -0
- package/dist/api/import-brep.js.map +1 -0
- package/dist/api/import-step.d.ts +28 -0
- package/dist/api/import-step.d.ts.map +1 -0
- package/dist/api/import-step.js +57 -0
- package/dist/api/import-step.js.map +1 -0
- package/dist/api/index.d.ts +7 -2
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +19 -5
- package/dist/api/index.js.map +1 -1
- package/dist/api/internal/compat-op.d.ts +2 -0
- package/dist/api/internal/compat-op.d.ts.map +1 -1
- package/dist/api/internal/compat-op.js +10 -2
- package/dist/api/internal/compat-op.js.map +1 -1
- package/dist/api/knurl.d.ts.map +1 -1
- package/dist/api/knurl.js +1 -0
- package/dist/api/knurl.js.map +1 -1
- package/dist/api/load.d.ts +3 -1
- package/dist/api/load.d.ts.map +1 -1
- package/dist/api/load.js +3 -1
- package/dist/api/load.js.map +1 -1
- package/dist/api/pattern.d.ts +32 -0
- package/dist/api/pattern.d.ts.map +1 -0
- package/dist/api/pattern.js +121 -0
- package/dist/api/pattern.js.map +1 -0
- package/dist/api/place.d.ts +35 -0
- package/dist/api/place.d.ts.map +1 -0
- package/dist/api/place.js +113 -0
- package/dist/api/place.js.map +1 -0
- package/dist/api/primitives.d.ts.map +1 -1
- package/dist/api/primitives.js +10 -3
- package/dist/api/primitives.js.map +1 -1
- package/dist/api/screw.d.ts.map +1 -1
- package/dist/api/screw.js +1 -0
- package/dist/api/screw.js.map +1 -1
- package/dist/api/sdf.d.ts.map +1 -1
- package/dist/api/sdf.js +1 -0
- package/dist/api/sdf.js.map +1 -1
- package/dist/api/sketch.d.ts.map +1 -1
- package/dist/api/sketch.js +1 -0
- package/dist/api/sketch.js.map +1 -1
- package/dist/api/split.d.ts +31 -0
- package/dist/api/split.d.ts.map +1 -0
- package/dist/api/split.js +96 -0
- package/dist/api/split.js.map +1 -0
- package/dist/api/surface/arg-spec.d.ts +9 -0
- package/dist/api/surface/arg-spec.d.ts.map +1 -1
- package/dist/api/surface/arg-spec.js +49 -30
- package/dist/api/surface/arg-spec.js.map +1 -1
- package/dist/api/svgExtrude.d.ts.map +1 -1
- package/dist/api/svgExtrude.js +1 -0
- package/dist/api/svgExtrude.js.map +1 -1
- package/dist/api/text.d.ts.map +1 -1
- package/dist/api/text.js +1 -0
- package/dist/api/text.js.map +1 -1
- package/dist/api/topo-resolve.d.ts.map +1 -1
- package/dist/api/topo-resolve.js +65 -3
- package/dist/api/topo-resolve.js.map +1 -1
- package/dist/api/transform.d.ts +4 -0
- package/dist/api/transform.d.ts.map +1 -1
- package/dist/api/transform.js +44 -12
- package/dist/api/transform.js.map +1 -1
- package/dist/brep/brep-ops.d.ts +34 -1
- package/dist/brep/brep-ops.d.ts.map +1 -1
- package/dist/brep/brep-ops.js +62 -22
- package/dist/brep/brep-ops.js.map +1 -1
- package/dist/brep/engine/adapters/brep-mock.d.ts.map +1 -1
- package/dist/brep/engine/adapters/brep-mock.js +8 -0
- package/dist/brep/engine/adapters/brep-mock.js.map +1 -1
- package/dist/brep/engine/adapters/brepkit.d.ts.map +1 -1
- package/dist/brep/engine/adapters/brepkit.js +6 -1
- package/dist/brep/engine/adapters/brepkit.js.map +1 -1
- package/dist/brep/engine/adapters/occt.d.ts.map +1 -1
- package/dist/brep/engine/adapters/occt.js +24 -1
- package/dist/brep/engine/adapters/occt.js.map +1 -1
- package/dist/brep/engine/primitives.d.ts +17 -0
- package/dist/brep/engine/primitives.d.ts.map +1 -1
- package/dist/brep/engine/primitives.js.map +1 -1
- package/dist/brep/engine/types.d.ts +49 -6
- package/dist/brep/engine/types.d.ts.map +1 -1
- package/dist/brep/engine/types.js.map +1 -1
- package/dist/brep/export/step.d.ts.map +1 -1
- package/dist/brep/export/step.js +6 -1
- package/dist/brep/export/step.js.map +1 -1
- package/dist/brep/face-evolution.d.ts +78 -8
- package/dist/brep/face-evolution.d.ts.map +1 -1
- package/dist/brep/face-evolution.js +130 -17
- package/dist/brep/face-evolution.js.map +1 -1
- package/dist/brepkit-kernel/brepkitKernel.d.ts +1 -1
- package/dist/brepkit-kernel/brepkitKernel.d.ts.map +1 -1
- package/dist/brepkit-kernel/brepkitKernel.js +27 -1
- package/dist/brepkit-kernel/brepkitKernel.js.map +1 -1
- package/dist/browser.d.ts +0 -2
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +2 -2
- package/dist/browser.js.map +1 -1
- package/dist/cad-runtime/admit-compat-lib.d.ts +19 -1
- package/dist/cad-runtime/admit-compat-lib.d.ts.map +1 -1
- package/dist/cad-runtime/admit-compat-lib.js +21 -1
- package/dist/cad-runtime/admit-compat-lib.js.map +1 -1
- package/dist/cad-runtime/backend-dispatch.d.ts +44 -3
- package/dist/cad-runtime/backend-dispatch.d.ts.map +1 -1
- package/dist/cad-runtime/backend-dispatch.js +46 -3
- package/dist/cad-runtime/backend-dispatch.js.map +1 -1
- package/dist/cad-runtime/browser-lib-loader.d.ts +2 -0
- package/dist/cad-runtime/browser-lib-loader.d.ts.map +1 -1
- package/dist/cad-runtime/browser-lib-loader.js +9 -1
- package/dist/cad-runtime/browser-lib-loader.js.map +1 -1
- package/dist/cad-runtime/direct-executor.d.ts.map +1 -1
- package/dist/cad-runtime/direct-executor.js +14 -4
- package/dist/cad-runtime/direct-executor.js.map +1 -1
- package/dist/cad-runtime/module-registry.d.ts +3 -1
- package/dist/cad-runtime/module-registry.d.ts.map +1 -1
- package/dist/cad-runtime/module-registry.js +13 -1
- package/dist/cad-runtime/module-registry.js.map +1 -1
- package/dist/cad-runtime/ports.d.ts +7 -0
- package/dist/cad-runtime/ports.d.ts.map +1 -1
- package/dist/cad-runtime/ports.js.map +1 -1
- package/dist/cad-runtime/runtime-concurrent-error.d.ts +38 -0
- package/dist/cad-runtime/runtime-concurrent-error.d.ts.map +1 -0
- package/dist/cad-runtime/runtime-concurrent-error.js +43 -0
- package/dist/cad-runtime/runtime-concurrent-error.js.map +1 -0
- package/dist/cad-runtime/runtime.d.ts +54 -2
- package/dist/cad-runtime/runtime.d.ts.map +1 -1
- package/dist/cad-runtime/runtime.js +186 -10
- package/dist/cad-runtime/runtime.js.map +1 -1
- package/dist/define-op.d.ts +22 -0
- package/dist/define-op.d.ts.map +1 -1
- package/dist/define-op.js +82 -12
- package/dist/define-op.js.map +1 -1
- package/dist/fcstd/attachment.d.ts +72 -0
- package/dist/fcstd/attachment.d.ts.map +1 -0
- package/dist/fcstd/attachment.js +93 -0
- package/dist/fcstd/attachment.js.map +1 -0
- package/dist/fcstd/build-fai-zip.d.ts +11 -0
- package/dist/fcstd/build-fai-zip.d.ts.map +1 -1
- package/dist/fcstd/build-fai-zip.js +21 -8
- package/dist/fcstd/build-fai-zip.js.map +1 -1
- package/dist/fcstd/cli.d.ts +3 -0
- package/dist/fcstd/cli.d.ts.map +1 -0
- package/dist/fcstd/cli.js +37 -0
- package/dist/fcstd/cli.js.map +1 -0
- package/dist/fcstd/codegen.d.ts +23 -1
- package/dist/fcstd/codegen.d.ts.map +1 -1
- package/dist/fcstd/codegen.js +159 -48
- package/dist/fcstd/codegen.js.map +1 -1
- package/dist/fcstd/container.d.ts +16 -1
- package/dist/fcstd/container.d.ts.map +1 -1
- package/dist/fcstd/container.js +12 -1
- package/dist/fcstd/container.js.map +1 -1
- package/dist/fcstd/contour.d.ts +9 -1
- package/dist/fcstd/contour.d.ts.map +1 -1
- package/dist/fcstd/contour.js +28 -18
- package/dist/fcstd/contour.js.map +1 -1
- package/dist/fcstd/convert.d.ts +31 -2
- package/dist/fcstd/convert.d.ts.map +1 -1
- package/dist/fcstd/convert.js +73 -22
- package/dist/fcstd/convert.js.map +1 -1
- package/dist/fcstd/document.d.ts +8 -1
- package/dist/fcstd/document.d.ts.map +1 -1
- package/dist/fcstd/document.js +4 -1
- package/dist/fcstd/document.js.map +1 -1
- package/dist/fcstd/expressions.d.ts +14 -1
- package/dist/fcstd/expressions.d.ts.map +1 -1
- package/dist/fcstd/expressions.js +11 -1
- package/dist/fcstd/expressions.js.map +1 -1
- package/dist/fcstd/external-geo.d.ts +27 -2
- package/dist/fcstd/external-geo.d.ts.map +1 -1
- package/dist/fcstd/external-geo.js +23 -2
- package/dist/fcstd/external-geo.js.map +1 -1
- package/dist/fcstd/feature-translate.d.ts +85 -3
- package/dist/fcstd/feature-translate.d.ts.map +1 -1
- package/dist/fcstd/feature-translate.js +189 -9
- package/dist/fcstd/feature-translate.js.map +1 -1
- package/dist/fcstd/index.d.ts +23 -0
- package/dist/fcstd/index.d.ts.map +1 -0
- package/dist/fcstd/index.js +19 -0
- package/dist/fcstd/index.js.map +1 -0
- package/dist/fcstd/placement.d.ts +49 -6
- package/dist/fcstd/placement.d.ts.map +1 -1
- package/dist/fcstd/placement.js +46 -6
- package/dist/fcstd/placement.js.map +1 -1
- package/dist/fcstd/planegcs-backend.d.ts +16 -1
- package/dist/fcstd/planegcs-backend.d.ts.map +1 -1
- package/dist/fcstd/planegcs-backend.js +16 -1
- package/dist/fcstd/planegcs-backend.js.map +1 -1
- package/dist/fcstd/sketch-parse.d.ts +43 -0
- package/dist/fcstd/sketch-parse.d.ts.map +1 -1
- package/dist/fcstd/sketch-parse.js +26 -0
- package/dist/fcstd/sketch-parse.js.map +1 -1
- package/dist/fcstd/sketch-solver.d.ts +20 -2
- package/dist/fcstd/sketch-solver.d.ts.map +1 -1
- package/dist/fcstd/sketch-solver.js +6 -1
- package/dist/fcstd/sketch-solver.js.map +1 -1
- package/dist/fcstd/sketch-verify.d.ts +19 -1
- package/dist/fcstd/sketch-verify.d.ts.map +1 -1
- package/dist/fcstd/sketch-verify.js +14 -1
- package/dist/fcstd/sketch-verify.js.map +1 -1
- package/dist/fcstd/structural-types.d.ts +83 -0
- package/dist/fcstd/structural-types.d.ts.map +1 -0
- package/dist/fcstd/structural-types.js +116 -0
- package/dist/fcstd/structural-types.js.map +1 -0
- package/dist/fcstd/unpack.d.ts +21 -2
- package/dist/fcstd/unpack.d.ts.map +1 -1
- package/dist/fcstd/unpack.js +11 -2
- package/dist/fcstd/unpack.js.map +1 -1
- package/dist/fcstd-convert.d.ts +30 -0
- package/dist/fcstd-convert.d.ts.map +1 -0
- package/dist/fcstd-convert.js +27 -0
- package/dist/fcstd-convert.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lang/determinism-scanner.d.ts +62 -0
- package/dist/lang/determinism-scanner.d.ts.map +1 -0
- package/dist/lang/determinism-scanner.js +619 -0
- package/dist/lang/determinism-scanner.js.map +1 -0
- package/dist/lang/symbol-table.generated.d.ts +7 -3
- package/dist/lang/symbol-table.generated.d.ts.map +1 -1
- package/dist/lang/symbol-table.generated.js +7 -3
- package/dist/lang/symbol-table.generated.js.map +1 -1
- package/dist/mesh/index.d.ts.map +1 -1
- package/dist/mesh/index.js +3 -2
- package/dist/mesh/index.js.map +1 -1
- package/dist/node-host/cli.d.ts.map +1 -1
- package/dist/node-host/cli.js +56 -2
- package/dist/node-host/cli.js.map +1 -1
- package/dist/runtime-state.d.ts +18 -6
- package/dist/runtime-state.d.ts.map +1 -1
- package/dist/runtime-state.js +9 -1
- package/dist/runtime-state.js.map +1 -1
- package/dist/shape.d.ts +15 -0
- package/dist/shape.d.ts.map +1 -1
- package/dist/shape.js +26 -3
- package/dist/shape.js.map +1 -1
- package/dist/topology/naming/build-naming.d.ts +14 -30
- package/dist/topology/naming/build-naming.d.ts.map +1 -1
- package/dist/topology/naming/build-naming.js +21 -54
- package/dist/topology/naming/build-naming.js.map +1 -1
- package/dist/topology/naming/capture-topo-ref.d.ts.map +1 -1
- package/dist/topology/naming/capture-topo-ref.js +5 -0
- package/dist/topology/naming/capture-topo-ref.js.map +1 -1
- package/dist/topology/naming/index.d.ts +3 -0
- package/dist/topology/naming/index.d.ts.map +1 -1
- package/dist/topology/naming/index.js +3 -0
- package/dist/topology/naming/index.js.map +1 -1
- package/dist/topology/naming/lineage-resolve.d.ts +80 -0
- package/dist/topology/naming/lineage-resolve.d.ts.map +1 -0
- package/dist/topology/naming/lineage-resolve.js +120 -0
- package/dist/topology/naming/lineage-resolve.js.map +1 -0
- package/dist/topology/naming/lineage.d.ts +349 -0
- package/dist/topology/naming/lineage.d.ts.map +1 -0
- package/dist/topology/naming/lineage.js +398 -0
- package/dist/topology/naming/lineage.js.map +1 -0
- package/dist/topology/naming/migrate.d.ts +41 -0
- package/dist/topology/naming/migrate.d.ts.map +1 -0
- package/dist/topology/naming/migrate.js +88 -0
- package/dist/topology/naming/migrate.js.map +1 -0
- package/dist/topology/naming/role-name.d.ts +176 -0
- package/dist/topology/naming/role-name.d.ts.map +1 -0
- package/dist/topology/naming/role-name.js +344 -0
- package/dist/topology/naming/role-name.js.map +1 -0
- package/dist/topology/naming/roles.d.ts +20 -22
- package/dist/topology/naming/roles.d.ts.map +1 -1
- package/dist/topology/naming/roles.js +51 -49
- package/dist/topology/naming/roles.js.map +1 -1
- package/dist/topology/naming/types.d.ts +65 -18
- package/dist/topology/naming/types.d.ts.map +1 -1
- package/dist/topology/naming/types.js.map +1 -1
- package/dist/weapp.d.ts +22 -0
- package/dist/weapp.d.ts.map +1 -0
- package/dist/weapp.js +23 -0
- package/dist/weapp.js.map +1 -0
- package/package.json +18 -5
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* fcstd CLI — batch driver entry: .FCStd → .fai.zip with a machine contract.
|
|
4
|
+
*
|
|
5
|
+
* Exit codes (plan §5.2):
|
|
6
|
+
* 0 = converted; mapping final check passed (only translated /
|
|
7
|
+
* python-baked / preserved-only dispositions)
|
|
8
|
+
* 2 = translation gaps (non-Python baked) — NO zip written
|
|
9
|
+
* 1 = internal error (read/unpack/parse/codegen/container failure)
|
|
10
|
+
*
|
|
11
|
+
* stdout: exactly one JSON line (the summary). Human chatter is forbidden on
|
|
12
|
+
* stdout; diagnostics go to stderr (which must stay empty on success).
|
|
13
|
+
*
|
|
14
|
+
* Run (published): faijs-fcstd-convert <in.FCStd> [out.fai.zip]
|
|
15
|
+
* Run (in-repo): npm run fcstd:convert -w @faicad/faijs -- <in.FCStd> [out.fai.zip]
|
|
16
|
+
* With no <out>, acts as a dry audit (no file written).
|
|
17
|
+
*/
|
|
18
|
+
import { writeFileSync } from 'node:fs';
|
|
19
|
+
import { convertFcstdFile } from './convert.js';
|
|
20
|
+
const [input, output] = process.argv.slice(2);
|
|
21
|
+
if (!input) {
|
|
22
|
+
console.error('usage: faijs-fcstd-convert <in.FCStd> [out.fai.zip]');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
const summary = await convertFcstdFile(input);
|
|
26
|
+
if (summary.ok && output && summary.zip) {
|
|
27
|
+
writeFileSync(output, summary.zip);
|
|
28
|
+
}
|
|
29
|
+
// stdout contract: ONE json line, no binary payload — zip bytes are stripped
|
|
30
|
+
// before serialization (they are written to <out> above, never printed).
|
|
31
|
+
const { zip: _zip, ...report } = summary;
|
|
32
|
+
void _zip;
|
|
33
|
+
console.log(JSON.stringify(report));
|
|
34
|
+
if (!summary.ok) {
|
|
35
|
+
process.exit(summary.gaps.length > 0 ? 2 : 1);
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/fcstd/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;IACX,OAAO,CAAC,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,KAAK,CAAC,CAAC;AAC9C,IAAI,OAAO,CAAC,EAAE,IAAI,MAAM,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACxC,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;AACrC,CAAC;AACD,6EAA6E;AAC7E,yEAAyE;AACzE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC;AACzC,KAAK,IAAI,CAAC;AACV,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;AAEpC,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;IAChB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChD,CAAC","sourcesContent":["#!/usr/bin/env node\n/**\n * fcstd CLI — batch driver entry: .FCStd → .fai.zip with a machine contract.\n *\n * Exit codes (plan §5.2):\n * 0 = converted; mapping final check passed (only translated /\n * python-baked / preserved-only dispositions)\n * 2 = translation gaps (non-Python baked) — NO zip written\n * 1 = internal error (read/unpack/parse/codegen/container failure)\n *\n * stdout: exactly one JSON line (the summary). Human chatter is forbidden on\n * stdout; diagnostics go to stderr (which must stay empty on success).\n *\n * Run (published): faijs-fcstd-convert <in.FCStd> [out.fai.zip]\n * Run (in-repo): npm run fcstd:convert -w @faicad/faijs -- <in.FCStd> [out.fai.zip]\n * With no <out>, acts as a dry audit (no file written).\n */\nimport { writeFileSync } from 'node:fs';\nimport { convertFcstdFile } from './convert.js';\n\nconst [input, output] = process.argv.slice(2);\nif (!input) {\n console.error('usage: faijs-fcstd-convert <in.FCStd> [out.fai.zip]');\n process.exit(1);\n}\n\nconst summary = await convertFcstdFile(input);\nif (summary.ok && output && summary.zip) {\n writeFileSync(output, summary.zip);\n}\n// stdout contract: ONE json line, no binary payload — zip bytes are stripped\n// before serialization (they are written to <out> above, never printed).\nconst { zip: _zip, ...report } = summary;\nvoid _zip;\nconsole.log(JSON.stringify(report));\n\nif (!summary.ok) {\n process.exit(summary.gaps.length > 0 ? 2 : 1);\n}\n"]}
|
package/dist/fcstd/codegen.d.ts
CHANGED
|
@@ -6,13 +6,21 @@
|
|
|
6
6
|
* heuristics — explicit downgrade per plan §12).
|
|
7
7
|
* M5.2 lowering: params → JS constants; calls → `const partN = await cad.x(...)`;
|
|
8
8
|
* statement ids sN. Sketch contours enter as blueprint literals.
|
|
9
|
+
*
|
|
10
|
+
* 本文件为 Placement 发射平台 `cad.place`(旋转四元数 + 平移,单点)、为产物聚合发射
|
|
11
|
+
* 平台 `cad.compound`(几何复合体,内核 `makeCompound`,持 OCCT 句柄)。这两个是 faijs
|
|
12
|
+
* 平台几何 op(H11,方案 §4),不再借用 `../3d_editor` 的 `cad.group` /
|
|
13
|
+
* `cad.translate` / `cad.rotate_euler`(编辑器交互 op,JSDoc 已 @deprecated)。
|
|
9
14
|
*/
|
|
10
15
|
import type { FcstdDocument } from './document.js';
|
|
11
16
|
import type { CadCall } from './feature-translate.js';
|
|
12
17
|
import type { Contour } from './contour.js';
|
|
13
18
|
import { type Placement } from './placement.js';
|
|
19
|
+
/** Per-object codegen outcome: what was emitted for one FCStd object. */
|
|
14
20
|
export interface GenObjectResult {
|
|
21
|
+
/** FCStd object name */
|
|
15
22
|
name: string;
|
|
23
|
+
/** FCStd type id */
|
|
16
24
|
type: string;
|
|
17
25
|
/** variable name bound in generated code (partN) or undefined when baked */
|
|
18
26
|
variable?: string;
|
|
@@ -26,6 +34,7 @@ export interface GenObjectResult {
|
|
|
26
34
|
loopCount?: number;
|
|
27
35
|
};
|
|
28
36
|
}
|
|
37
|
+
/** Full codegen output: lowered calls, per-object ledger and generated script(s). */
|
|
29
38
|
export interface GenResult {
|
|
30
39
|
/** ordered translated calls (dependency order) */
|
|
31
40
|
calls: CadCall[];
|
|
@@ -46,6 +55,15 @@ export interface GenResult {
|
|
|
46
55
|
* M5.1/M5.2 — translate every object in dependency order and lower to JS.
|
|
47
56
|
* `sketchVerdict` supplies the M3 outcome per sketch object name; sketches
|
|
48
57
|
* whose contour feeds a Pad/Pocket appear as inputs.
|
|
58
|
+
*
|
|
59
|
+
* @param doc parsed FCStd object graph to translate
|
|
60
|
+
* @param sketchVerdict per-sketch M3 verdict keyed by object name
|
|
61
|
+
* @param sketchContours solved contours per L0 sketch, keyed by object name
|
|
62
|
+
* @param baseName source base name used in generated file headers/labels
|
|
63
|
+
* @param placements per-object Placement used to re-orient placed geometry; missing → identity
|
|
64
|
+
* @param shapeCarriers objects whose Shape is a ZIP .brp member (pure-Shape carriers → shape-asset)
|
|
65
|
+
* @param brokenShapeAssets objects whose Shape `file` attribute points at a missing/empty member (explicit gap)
|
|
66
|
+
* @returns the lowered call plan, per-object dispositions and generated code
|
|
49
67
|
*/
|
|
50
68
|
export declare function generateModel(doc: FcstdDocument, sketchVerdict: Map<string, {
|
|
51
69
|
level: 'L0' | 'L1' | 'L2';
|
|
@@ -53,5 +71,9 @@ export declare function generateModel(doc: FcstdDocument, sketchVerdict: Map<str
|
|
|
53
71
|
loopCount?: number;
|
|
54
72
|
}>, sketchContours: Map<string, Contour[]>, baseName: string,
|
|
55
73
|
/** M8.3: per-object Placement (sketches + features); missing → identity */
|
|
56
|
-
placements?: Map<string, Placement
|
|
74
|
+
placements?: Map<string, Placement>,
|
|
75
|
+
/** H7: objects whose Shape is a ZIP .brp member (pure-Shape carriers → shape-asset). */
|
|
76
|
+
shapeCarriers?: ReadonlySet<string>,
|
|
77
|
+
/** E4: objects whose Shape `file` attribute points at a missing/empty member (explicit gap). */
|
|
78
|
+
brokenShapeAssets?: ReadonlySet<string>): GenResult;
|
|
57
79
|
//# sourceMappingURL=codegen.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../../src/fcstd/codegen.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../../src/fcstd/codegen.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,OAAO,EAAoB,MAAM,wBAAwB,CAAC;AAExE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,KAAK,SAAS,EAA6C,MAAM,gBAAgB,CAAC;AAG3F,yEAAyE;AACzE,MAAM,WAAW,eAAe;IAC9B,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,WAAW,EAAE,YAAY,GAAG,OAAO,GAAG,gBAAgB,CAAC;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7E;AAED,qFAAqF;AACrF,MAAM,WAAW,SAAS;IACxB,kDAAkD;IAClD,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb;wFACoF;IACpF,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtD,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAgED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,aAAa,EAClB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EAC9F,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,EACtC,QAAQ,EAAE,MAAM;AAChB,2EAA2E;AAC3E,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC;AACnC,wFAAwF;AACxF,aAAa,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC;AACnC,gGAAgG;AAChG,iBAAiB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GACtC,SAAS,CAwZX"}
|
package/dist/fcstd/codegen.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { translateObject, isJsExpr, jsExpr } from './feature-translate.js';
|
|
2
|
-
import { isIdentityPlacement,
|
|
1
|
+
import { translateObject, isJsExpr, jsExpr, BODY_CHAIN_BASE } from './feature-translate.js';
|
|
2
|
+
import { isIdentityPlacement, invertApplyPlacement } from './placement.js';
|
|
3
|
+
import { isNonModelingType } from './structural-types.js';
|
|
3
4
|
/** Extract link dependencies relevant for ordering. */
|
|
4
5
|
function depsOf(obj) {
|
|
5
6
|
const out = [];
|
|
@@ -10,9 +11,21 @@ function depsOf(obj) {
|
|
|
10
11
|
if (v)
|
|
11
12
|
out.push(v);
|
|
12
13
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
// GOTCHA (ArchDetail corpus, 2026-09-21): App::PropertyLinkList properties.
|
|
15
|
+
// `Shapes` is the multi-input geometry list (Part::MultiFuse / MultiCommon);
|
|
16
|
+
// `Links` is Part::Compound's member list. Neither is a single-value link
|
|
17
|
+
// property, so missing it here left the Compound with NO dependency on its
|
|
18
|
+
// members — Kahn then placed it at its document position, and ArchDetail's
|
|
19
|
+
// compounds sit at doc index 10-14 while every member sits at 269+ (Draft
|
|
20
|
+
// emits the wire first, the compound last, but the file order is sorted by
|
|
21
|
+
// name). inputVar() found nothing yet → `compound-missing-members` for all
|
|
22
|
+
// five compounds, for a pure ordering reason.
|
|
23
|
+
const linkLists = ['Shapes', 'Links'];
|
|
24
|
+
for (const p of linkLists) {
|
|
25
|
+
const el = obj.properties.get(p)?.children[0];
|
|
26
|
+
if (!el)
|
|
27
|
+
continue;
|
|
28
|
+
for (const link of el.children) {
|
|
16
29
|
const v = link.attributes['value'];
|
|
17
30
|
if (v)
|
|
18
31
|
out.push(v);
|
|
@@ -50,14 +63,45 @@ function bodyFeatureNames(obj) {
|
|
|
50
63
|
* M5.1/M5.2 — translate every object in dependency order and lower to JS.
|
|
51
64
|
* `sketchVerdict` supplies the M3 outcome per sketch object name; sketches
|
|
52
65
|
* whose contour feeds a Pad/Pocket appear as inputs.
|
|
66
|
+
*
|
|
67
|
+
* @param doc parsed FCStd object graph to translate
|
|
68
|
+
* @param sketchVerdict per-sketch M3 verdict keyed by object name
|
|
69
|
+
* @param sketchContours solved contours per L0 sketch, keyed by object name
|
|
70
|
+
* @param baseName source base name used in generated file headers/labels
|
|
71
|
+
* @param placements per-object Placement used to re-orient placed geometry; missing → identity
|
|
72
|
+
* @param shapeCarriers objects whose Shape is a ZIP .brp member (pure-Shape carriers → shape-asset)
|
|
73
|
+
* @param brokenShapeAssets objects whose Shape `file` attribute points at a missing/empty member (explicit gap)
|
|
74
|
+
* @returns the lowered call plan, per-object dispositions and generated code
|
|
53
75
|
*/
|
|
54
76
|
export function generateModel(doc, sketchVerdict, sketchContours, baseName,
|
|
55
77
|
/** M8.3: per-object Placement (sketches + features); missing → identity */
|
|
56
|
-
placements
|
|
78
|
+
placements,
|
|
79
|
+
/** H7: objects whose Shape is a ZIP .brp member (pure-Shape carriers → shape-asset). */
|
|
80
|
+
shapeCarriers,
|
|
81
|
+
/** E4: objects whose Shape `file` attribute points at a missing/empty member (explicit gap). */
|
|
82
|
+
brokenShapeAssets) {
|
|
57
83
|
const byName = new Map(doc.objects.map((o) => [o.name, o]));
|
|
84
|
+
// GOTCHA (test_geomop corpus, 2026-09-20): a dependency on a Body
|
|
85
|
+
// CONTAINER (Part::Cut with Tool→Body) really waits for the Body's
|
|
86
|
+
// features to have built the chain — expand the dep to the member
|
|
87
|
+
// features so Kahn orders the consumer AFTER the chain exists (and
|
|
88
|
+
// chainVar.get(Body) resolves). Treating the container itself as
|
|
89
|
+
// satisfiable instead let the Cut run before the chain was built.
|
|
58
90
|
const nodes = new Map();
|
|
59
91
|
for (const obj of doc.objects) {
|
|
60
|
-
|
|
92
|
+
const deps = new Set();
|
|
93
|
+
for (const d of depsOf(obj)) {
|
|
94
|
+
const target = byName.get(d);
|
|
95
|
+
if (target?.type === 'PartDesign::Body') {
|
|
96
|
+
for (const m of bodyFeatureNames(target))
|
|
97
|
+
if (byName.has(m))
|
|
98
|
+
deps.add(m);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
deps.add(d);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
nodes.set(obj.name, { name: obj.name, obj, deps });
|
|
61
105
|
}
|
|
62
106
|
// Kahn topological sort; objects with unbuilt deps fall back to insertion
|
|
63
107
|
// order iteration until progress stalls (remaining are baked with reason).
|
|
@@ -86,7 +130,7 @@ placements) {
|
|
|
86
130
|
const node = nodes.get(name);
|
|
87
131
|
if (built.has(node.name))
|
|
88
132
|
continue;
|
|
89
|
-
if ([...node.deps].every((d) => built.has(d) || !byName.has(d))) {
|
|
133
|
+
if ([...node.deps].every((d) => built.has(d) || !byName.has(d) || byName.get(d).type === 'PartDesign::Body')) {
|
|
90
134
|
built.add(node.name);
|
|
91
135
|
order.push(node.name);
|
|
92
136
|
progress = true;
|
|
@@ -147,16 +191,28 @@ placements) {
|
|
|
147
191
|
}
|
|
148
192
|
continue;
|
|
149
193
|
}
|
|
150
|
-
|
|
151
|
-
|
|
194
|
+
// Non-modeling objects are preserved-only and MUST be short-circuited
|
|
195
|
+
// BEFORE translation. GOTCHA (PadTest, 2026-09-21): this used to list only
|
|
196
|
+
// `App::Origin/Plane/Line`, so a Body's `PartDesign::Plane` went through
|
|
197
|
+
// the translator, matched the shape-asset rule (datum planes store a
|
|
198
|
+
// `Shape` .brp — the plane face) and was folded into the Body's chain:
|
|
199
|
+
// `cad.union(pad, datumPlane)`. That both corrupts the solid and fails at
|
|
200
|
+
// run time (`cad.load` requires a solid; a plane face has none). The
|
|
201
|
+
// predicate is shared with the C4 audit (`structural-types.ts`) so codegen
|
|
202
|
+
// and the disposition ledger can never disagree about what is non-modeling.
|
|
203
|
+
if (isNonModelingType(obj.type)) {
|
|
204
|
+
results.push({ name, type: obj.type, calls: [], disposition: 'preserved-only', reason: 'structural' });
|
|
152
205
|
continue;
|
|
153
206
|
}
|
|
154
207
|
// Sketches are now real face variables (see the Sketcher::SketchObject
|
|
155
208
|
// branch above), so every dependency that resolves to one flows through.
|
|
156
209
|
const verdict = translateObject(obj, (dep) => {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
210
|
+
// GOTCHA (test_geomop corpus, 2026-09-20): a dependency on a Body
|
|
211
|
+
// CONTAINER (Part::Cut with Tool→Body) resolves against the Body's
|
|
212
|
+
// accumulated chain head, not `variables` — the container name is
|
|
213
|
+
// never registered there (its result lives in chainVar).
|
|
214
|
+
return variables.get(dep) ?? chainVar.get(dep);
|
|
215
|
+
}, doc.objects, shapeCarriers, brokenShapeAssets);
|
|
160
216
|
node.verdict = verdict;
|
|
161
217
|
if (verdict.kind === 'translated') {
|
|
162
218
|
// rename output vars to partN sequence
|
|
@@ -165,6 +221,15 @@ placements) {
|
|
|
165
221
|
variables.set(call.out, v);
|
|
166
222
|
// remap inputs that were intermediate (Pocket_cut) or named outputs
|
|
167
223
|
call.inputs = call.inputs.map((i) => variables.get(i) ?? i);
|
|
224
|
+
// GOTCHA (ArchDetail s444, 2026-09-21): `cad.compound` passes its
|
|
225
|
+
// members as LEXICAL VARIABLE NAMES inside `params.members` — they
|
|
226
|
+
// must be remapped like `inputs`, otherwise renderArgs emits them as
|
|
227
|
+
// string literals and the op receives strings (no BREP handle) and
|
|
228
|
+
// throws "compound members are not all on the BREP chain".
|
|
229
|
+
const members = call.params?.members;
|
|
230
|
+
if (Array.isArray(members)) {
|
|
231
|
+
call.params.members = members.map((m) => typeof m === 'string' ? (variables.get(m) ?? m) : m);
|
|
232
|
+
}
|
|
168
233
|
call.out = v;
|
|
169
234
|
calls.push(call);
|
|
170
235
|
}
|
|
@@ -185,24 +250,17 @@ placements) {
|
|
|
185
250
|
const sketchLink = obj.properties.get('Sketch')?.children[0]?.attributes['value'];
|
|
186
251
|
const pl = (sketchLink ? placements?.get(sketchLink) : undefined) ?? placements?.get(name);
|
|
187
252
|
if (lastVar && pl && !isIdentityPlacement(pl)) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
const needsTranslate = Math.abs(pl.p[0]) > 1e-9 || Math.abs(pl.p[1]) > 1e-9 || Math.abs(pl.p[2]) > 1e-9;
|
|
198
|
-
if (needsTranslate) {
|
|
199
|
-
const tv = newVar();
|
|
200
|
-
variables.set(cur, tv);
|
|
201
|
-
calls.push({ out: tv, op: 'cad.translate', source: name, inputs: [cur], params: { offset: [...pl.p] } });
|
|
202
|
-
cur = tv;
|
|
203
|
-
}
|
|
253
|
+
// 单个刚性放置:旋转(四元数,绕局部原点)+ 平移 = FreeCAD Placement(P,Q)。
|
|
254
|
+
// 直接发 cad.place,避免 euler 往返损失精度(方案 §4.7:两语句合一)。
|
|
255
|
+
const cur = lastVar;
|
|
256
|
+
const rv = newVar();
|
|
257
|
+
variables.set(cur, rv); // map old name → placed var for consumers
|
|
258
|
+
calls.push({
|
|
259
|
+
out: rv, op: 'cad.place', source: name, inputs: [cur],
|
|
260
|
+
params: { rotation: [...pl.q], position: [...pl.p] },
|
|
261
|
+
});
|
|
204
262
|
// the object's variable is now the fully placed result
|
|
205
|
-
variables.set(name,
|
|
263
|
+
variables.set(name, rv);
|
|
206
264
|
}
|
|
207
265
|
// M9.4 (D-C): fold the feature into its Body's chain — AFTER the placement
|
|
208
266
|
// step so the chain accumulates the PLACED feature shape. Pocket/Cut
|
|
@@ -225,6 +283,14 @@ placements) {
|
|
|
225
283
|
// (bbox already exact at delta 0).
|
|
226
284
|
if (prev) {
|
|
227
285
|
for (const c of verdict.calls) {
|
|
286
|
+
// H7 (hole_puzzle corpus): the Pocket had NO BaseFeature property —
|
|
287
|
+
// the translator emitted BODY_CHAIN_BASE as the subtract's base
|
|
288
|
+
// input; retarget it at the chain head here (after the earlier
|
|
289
|
+
// inputs remap, which leaves the marker untouched since it is not
|
|
290
|
+
// a variable name).
|
|
291
|
+
if (c.inputs) {
|
|
292
|
+
c.inputs = c.inputs.map((i) => (i === BODY_CHAIN_BASE ? prev : i));
|
|
293
|
+
}
|
|
228
294
|
if (c.op === 'cad.extrude' && c.params.baseFeature !== undefined) {
|
|
229
295
|
// GOTCHA (PadTest V6 residual): the up-to extrude builds its
|
|
230
296
|
// prism in the SKETCH-LOCAL frame, but the chain head (prev)
|
|
@@ -235,25 +301,24 @@ placements) {
|
|
|
235
301
|
// AddShape 48199 vs rebuilt deficit ~9108 mm^3 → 3.2% total).
|
|
236
302
|
const needsInverse = pl !== undefined && !isIdentityPlacement(pl);
|
|
237
303
|
if (needsInverse && pl) {
|
|
304
|
+
// 逆向刚性放置(单点发射,方案 §4.7):rotation 取共轭四元数、
|
|
305
|
+
// position 取 invertApplyPlacement(pl, 0) = -(invQ·p),合成为
|
|
306
|
+
// 一个 cad.place,避免 euler 往返。语义 = invertApplyPlacement(pl, prev)。
|
|
238
307
|
const invQ = [
|
|
239
308
|
-pl.q[0], -pl.q[1], -pl.q[2], pl.q[3],
|
|
240
309
|
];
|
|
241
|
-
const
|
|
242
|
-
const tv = newVar();
|
|
243
|
-
const tvCall = {
|
|
244
|
-
out: tv, op: 'cad.translate', source: name,
|
|
245
|
-
inputs: [prev], params: { offset: [-pl.p[0], -pl.p[1], -pl.p[2]] },
|
|
246
|
-
};
|
|
310
|
+
const invPos = invertApplyPlacement(pl, [0, 0, 0]);
|
|
247
311
|
const rv = newVar();
|
|
248
312
|
const rvCall = {
|
|
249
|
-
out: rv, op: 'cad.
|
|
250
|
-
inputs: [
|
|
313
|
+
out: rv, op: 'cad.place', source: name,
|
|
314
|
+
inputs: [prev],
|
|
315
|
+
params: { rotation: invQ, position: invPos },
|
|
251
316
|
};
|
|
252
317
|
// insert BEFORE the extrude call: faijs is a statement
|
|
253
318
|
// language — `baseFeature: partN` referencing a later
|
|
254
319
|
// statement is E_REFERENCE (parser rejects forward refs).
|
|
255
320
|
const at = calls.indexOf(c);
|
|
256
|
-
calls.splice(at < 0 ? calls.length : at, 0,
|
|
321
|
+
calls.splice(at < 0 ? calls.length : at, 0, rvCall);
|
|
257
322
|
c.params.baseFeature = jsExpr(rv);
|
|
258
323
|
}
|
|
259
324
|
else {
|
|
@@ -283,6 +348,14 @@ placements) {
|
|
|
283
348
|
chainVar.set(body, nv);
|
|
284
349
|
}
|
|
285
350
|
}
|
|
351
|
+
// H7 guard (motor_mount_inch corpus): a feature outside a Body (or with
|
|
352
|
+
// no chain head yet) never gets its BODY_CHAIN_BASE retargeted — the
|
|
353
|
+
// marker would leak into the generated JS as an illegal identifier
|
|
354
|
+
// (parser kills the whole file). Downgrade to an explicit gap instead.
|
|
355
|
+
if (verdict.calls.some((c) => c.inputs?.includes(BODY_CHAIN_BASE))) {
|
|
356
|
+
results.push({ name, type: obj.type, calls: [], disposition: 'baked', reason: 'pocket-missing-dependency' });
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
286
359
|
results.push({ name, type: obj.type, variable: verdict.calls.at(-1)?.out, calls: verdict.calls, disposition: 'translated', reason: verdict.reason });
|
|
287
360
|
}
|
|
288
361
|
else if (verdict.kind === 'baked') {
|
|
@@ -302,7 +375,7 @@ placements) {
|
|
|
302
375
|
// (including the chain union/subtract calls emitted for it) go to
|
|
303
376
|
// model/<BodyName>.fai.js; everything else (loose Part features) stays in
|
|
304
377
|
// main.fai.js. When no Body has geometry, everything lands in main (the
|
|
305
|
-
// single-file shape). Aggregate entry
|
|
378
|
+
// single-file shape). Aggregate entry compounds per-file roots via cad.compound.
|
|
306
379
|
const callBody = new Map();
|
|
307
380
|
for (const r of results) {
|
|
308
381
|
const b = memberToBody.get(r.name);
|
|
@@ -330,20 +403,49 @@ placements) {
|
|
|
330
403
|
// further down (`let` TDZ ReferenceError at run time: `part8` used `part6`).
|
|
331
404
|
// Filtering `calls` in place preserves the topological order.
|
|
332
405
|
const assigned = new Map(); // call.out → Body file name
|
|
406
|
+
// chain-head var → terminal alias (and owning Body) — needed DURING
|
|
407
|
+
// routing: a foreign chain head is never in `assigned` (it is a Body's
|
|
408
|
+
// accumulated var, not a call output), so input-body checks must consult
|
|
409
|
+
// this map to detect cross-Body references.
|
|
410
|
+
const headToTerminal = new Map();
|
|
411
|
+
const headToBody = new Map();
|
|
412
|
+
for (const b of bodiesWithGeo) {
|
|
413
|
+
headToTerminal.set(chainVar.get(b), `${b}_out`);
|
|
414
|
+
headToBody.set(chainVar.get(b), b);
|
|
415
|
+
}
|
|
416
|
+
const mainPending = [];
|
|
417
|
+
const mainOuts = new Set();
|
|
333
418
|
for (const c of calls) {
|
|
334
419
|
const own = callBody.get(c.out);
|
|
335
|
-
const
|
|
420
|
+
const inputBodies = c.inputs.map((inp) => assigned.get(inp) ?? headToBody.get(inp)).filter((b) => b !== undefined);
|
|
421
|
+
const viaInput = inputBodies[0];
|
|
336
422
|
const b = own !== undefined && bodiesWithGeo.has(own) ? own : viaInput;
|
|
337
|
-
|
|
423
|
+
// GOTCHA (test_geomop corpus, 2026-09-20): a call whose own Body
|
|
424
|
+
// differs from an input's Body (Part::Cut with Base in another Body's
|
|
425
|
+
// chain) must NOT be emitted into its own Body file — the input
|
|
426
|
+
// variable lives in ANOTHER module and is not declared there
|
|
427
|
+
// (SEC_FREE_IDENT at check time). Same for inputs that will live in
|
|
428
|
+
// MAIN (loose Part boxes): routing is a forward pass over the
|
|
429
|
+
// topologically sorted calls, so main outputs are already known.
|
|
430
|
+
// NOTE: Body-chain fold calls (union/subtract emitted during folding)
|
|
431
|
+
// are NOT in `results`, so `own` is undefined for them — the main-input
|
|
432
|
+
// check must not depend on `own`.
|
|
433
|
+
const mixed = inputBodies.some((ib) => ib !== b) || (own !== undefined && viaInput !== undefined && viaInput !== own);
|
|
434
|
+
const touchesMain = c.inputs.some((i) => mainOuts.has(i));
|
|
435
|
+
if (b !== undefined && bodiesWithGeo.has(b) && !mixed && !touchesMain && (own === undefined || own === b)) {
|
|
338
436
|
if (!perBody.has(b))
|
|
339
437
|
perBody.set(b, []);
|
|
340
438
|
perBody.get(b).push(c);
|
|
341
439
|
assigned.set(c.out, b);
|
|
342
440
|
}
|
|
343
441
|
else {
|
|
344
|
-
|
|
442
|
+
mainPending.push(c);
|
|
443
|
+
mainOuts.add(c.out);
|
|
345
444
|
}
|
|
346
445
|
}
|
|
446
|
+
for (const c of mainPending) {
|
|
447
|
+
mainCalls.push({ ...c, inputs: c.inputs.map((i) => headToTerminal.get(i) ?? i) });
|
|
448
|
+
}
|
|
347
449
|
for (const b of bodiesWithGeo) {
|
|
348
450
|
const bodyCalls = perBody.get(b) ?? [];
|
|
349
451
|
// terminal: alias the chain head to <Body>_out so the aggregate entry
|
|
@@ -355,7 +457,7 @@ placements) {
|
|
|
355
457
|
];
|
|
356
458
|
files.push({ path: `model/${b}.fai.js`, code: lowerBody(withTerminal, `${baseName}/${b}`, b), body: b });
|
|
357
459
|
}
|
|
358
|
-
// main.fai.js: aggregate the per-Body terminals via cad.
|
|
460
|
+
// main.fai.js: aggregate the per-Body terminals via cad.compound. M10c:
|
|
359
461
|
// cross-file references close through the standard relative-import
|
|
360
462
|
// contract (module-registry D6) — each Body module's terminal alias
|
|
361
463
|
// `<Body>_out` is its live shape, so a named import binds it.
|
|
@@ -364,7 +466,7 @@ placements) {
|
|
|
364
466
|
lines.push(`// Units: mm (faijs contract; FCStd internal units are mm)`);
|
|
365
467
|
const members = [...bodiesWithGeo].map((b) => `${b}_out`);
|
|
366
468
|
// Always import the per-Body terminals: the aggregate entry references
|
|
367
|
-
// `<Body>_out` in BOTH the cad.
|
|
469
|
+
// `<Body>_out` in BOTH the cad.compound (multi-Body) and the single-Body alias
|
|
368
470
|
// path, so a missing import is a SEC_FREE_IDENT parse error (GOTCHA: the
|
|
369
471
|
// single-Body branch previously emitted `let part_out = <Body>_out;` with
|
|
370
472
|
// no import). `mainCalls` may still be empty here.
|
|
@@ -379,7 +481,7 @@ placements) {
|
|
|
379
481
|
}
|
|
380
482
|
if (members.length > 1) {
|
|
381
483
|
rootVar = 'part_out';
|
|
382
|
-
lines.push(`let part_out = cad.
|
|
484
|
+
lines.push(`let part_out = cad.compound({ members: [${members.join(', ')}] });`);
|
|
383
485
|
}
|
|
384
486
|
else if (members.length === 1) {
|
|
385
487
|
// single Body: the imported terminal IS the result — alias keeps a
|
|
@@ -428,7 +530,7 @@ function lower(calls, baseName) {
|
|
|
428
530
|
const consumed = new Set(calls.flatMap((c) => c.inputs));
|
|
429
531
|
const roots = calls.filter((c) => !consumed.has(c.out)).map((c) => c.out);
|
|
430
532
|
if (roots.length > 1) {
|
|
431
|
-
lines.push(`let part_out = cad.
|
|
533
|
+
lines.push(`let part_out = cad.compound({ members: [${roots.join(', ')}] });`);
|
|
432
534
|
}
|
|
433
535
|
else if (roots.length === 0) {
|
|
434
536
|
lines.push(`// no translated geometry (all baked)`);
|
|
@@ -469,13 +571,22 @@ function containsJsExpr(v) {
|
|
|
469
571
|
}
|
|
470
572
|
function renderArgs(call) {
|
|
471
573
|
const positional = [
|
|
472
|
-
...call.inputs.map((i) => i),
|
|
574
|
+
...(call.noPositionalArgs ? [] : call.inputs.map((i) => i)),
|
|
473
575
|
...(call.literals ?? []).map((l) => renderValue(l)),
|
|
474
576
|
].filter((s) => s.length > 0); // M7.1: drop empty entries so we never emit `(, `
|
|
475
577
|
const named = [];
|
|
476
578
|
for (const [k, v] of Object.entries(call.params ?? {})) {
|
|
477
579
|
if (v === undefined)
|
|
478
580
|
continue;
|
|
581
|
+
// GOTCHA (ArchDetail s444, 2026-09-21): `cad.compound`'s `members` are
|
|
582
|
+
// LEXICAL VARIABLE NAMES, not data strings — render them bare, like
|
|
583
|
+
// positional inputs. renderValue would quote them and the op would
|
|
584
|
+
// receive strings with no BREP handle
|
|
585
|
+
// ("compound members are not all on the BREP chain").
|
|
586
|
+
if (k === 'members' && Array.isArray(v)) {
|
|
587
|
+
named.push(`members: [${v.join(', ')}]`);
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
479
590
|
named.push(`${k}: ${renderValue(v)}`);
|
|
480
591
|
}
|
|
481
592
|
const namedBlock = named.length ? `{ ${named.join(', ')} }` : '';
|