brepjs-cad 0.175.0 → 0.177.0
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/CHANGELOG.md +19 -0
- package/README.md +2 -1
- package/dist/brepjs-cad.cjs +12 -11
- package/dist/brepjs-cad.js +2 -1
- package/dist/cli/exportPart.d.ts +1 -0
- package/dist/cli/main.cjs +77 -183
- package/dist/cli/main.js +54 -160
- package/dist/cli/watch.d.ts +10 -1
- package/dist/diff-CBp5KDNZ.cjs +154 -0
- package/dist/diff-DmFtXXiX.js +143 -0
- package/dist/disposeShape-B4lsczOO.js +7 -0
- package/dist/disposeShape-Bui9k40G.cjs +12 -0
- package/dist/evaluate-DDwQkzN-.cjs +11832 -0
- package/dist/evaluate-TZ0h4RP0.js +11816 -0
- package/dist/export3dm/export3dm.cjs +144 -0
- package/dist/export3dm/export3dm.d.ts +10 -0
- package/dist/export3dm/export3dm.js +141 -0
- package/dist/loader/brepjsResolve.mjs +10 -4
- package/dist/mcp/server.cjs +2 -2
- package/dist/mcp/server.js +2 -2
- package/dist/openBrowser-Bip99cQv.cjs +211 -0
- package/dist/openBrowser-Dg9oDOte.js +170 -0
- package/dist/preview/evaluate.d.ts +35 -0
- package/dist/preview/preview.cjs +170 -0
- package/dist/preview/preview.d.ts +9 -0
- package/dist/preview/preview.js +169 -0
- package/dist/preview/server.d.ts +17 -0
- package/dist/{rolldown-runtime-VH7oDXx4.cjs → rolldown-runtime-y5ARvAq5.cjs} +7 -0
- package/dist/snapshot/shoot.cjs +1 -1
- package/dist/snapshot/static.cjs +10 -1
- package/dist/snapshot/static.d.ts +4 -0
- package/dist/snapshot/static.js +8 -2
- package/dist/{diff-B1sSWjLa.js → usingCtx-BDYtpr4t.js} +21 -153
- package/dist/{diff-DX9zgrss.cjs → usingCtx-LY0tPCbQ.cjs} +51 -165
- package/dist/verify/runPart.d.ts +5 -0
- package/dist/verify/typecheck.d.ts +8 -0
- package/package.json +3 -2
- package/viewer/dist/assets/index-Cq6KRusM.js +4208 -0
- package/viewer/dist/index.html +1 -1
- package/viewer/dist/assets/index-CH09YZ3K.js +0 -4208
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.177.0](https://github.com/andymai/brepjs/compare/brepjs-cad-v0.176.0...brepjs-cad-v0.177.0) (2026-08-26)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **brepjs-cad:** brep export --3dm — element-aware Rhino export ([#2236](https://github.com/andymai/brepjs/issues/2236)) ([91b0235](https://github.com/andymai/brepjs/commit/91b02355d9ba005f69dd4c72002a946600179398))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **brepjs-cad:** quality pass over the preview/loader/3dm/scaffold delta ([#2243](https://github.com/andymai/brepjs/issues/2243)) ([b6ae66e](https://github.com/andymai/brepjs/commit/b6ae66e6a1c4175bfd13846b382972e1d7356adc))
|
|
14
|
+
|
|
15
|
+
## [0.176.0](https://github.com/andymai/brepjs/compare/brepjs-cad-v0.175.0...brepjs-cad-v0.176.0) (2026-08-26)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* **brepjs-cad:** brep preview — element-aware model viewer with live watch ([#2231](https://github.com/andymai/brepjs/issues/2231)) ([002a20f](https://github.com/andymai/brepjs/commit/002a20f9ad8d38cb812f00a875583bdb850a004d))
|
|
21
|
+
|
|
3
22
|
## [0.175.0](https://github.com/andymai/brepjs/compare/brepjs-cad-v0.174.0...brepjs-cad-v0.175.0) (2026-08-26)
|
|
4
23
|
|
|
5
24
|
|
package/README.md
CHANGED
|
@@ -66,7 +66,8 @@ The `brep` bin is a multi-command CLI. `verify` is the default command, so `brep
|
|
|
66
66
|
| `brep verify <files...>` | Default command. Loads the part, runs deterministic checks, prints the JSON report. Flags: `--check`, `--json <out\|->`, `--step <out>`, `--glb <out>`, `--snapshot <dir>`, `--serve`, `--no-open`, `--expect-code <CODE>` / `--expect-invalid` (assert a known-bad part fails the right way — for fixtures/eval). Multiple files or a quoted glob → a validity-only JSON array. Exits non-zero when not `ok` (unless `--serve`). |
|
|
67
67
|
| `brep init <name>` | Scaffolds a parameterized `<name>.brep.ts` + `tsconfig.json` + `README.md` into `./<name>` (or `--out <dir>`). Never overwrites existing files. |
|
|
68
68
|
| `brep watch <file>` | Re-verifies on every save until Ctrl-C (debounced; watches the parent dir to survive editor rename-on-save). |
|
|
69
|
-
| `brep
|
|
69
|
+
| `brep preview <file>` | Element-aware live viewer for a model module (families element tree, resolved tree, or plain shape): key-path picking, failed elements listed, real edges. `--watch` re-evaluates on save and pushes to the open tab; `--write-only` (CI), `--glb <out>`, `--json <out>`, `--port <n>`, `--no-open`. |
|
|
70
|
+
| `brep export <file>` | Batch artifacts behind a validity gate: `--step`, `--glb`, `--stl`, or `--all`; `--3dm` writes an element-aware Rhino file (one named object per element, layered; needs the optional `rhino3dm` install; not combinable with the other formats); `--out <dir>` (default `.`). Exits non-zero on failure. |
|
|
70
71
|
| `brep measure <a> [b]` | Measurements for one part; with a second module, the distance between the two parts. |
|
|
71
72
|
| `brep diff <a> <b>` | Compares the measurements of a baseline and a comparison module. |
|
|
72
73
|
| `brep snapshot <file>` | Standalone multi-view PNG capture (no report assertions): `--out <dir>`, `--label <tag>` (subfolder, for pre/post A/B pairing). Renders on a private ephemeral server so parallel snapshots don't contend. Requires the optional `puppeteer`/Chrome dependency; degrades with a clear message when absent. (Also available inline via `verify --snapshot <dir>`.) |
|
package/dist/brepjs-cad.cjs
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
exports.
|
|
5
|
-
exports.
|
|
6
|
-
exports.
|
|
7
|
-
exports.
|
|
8
|
-
exports.
|
|
9
|
-
exports.
|
|
2
|
+
const require_usingCtx = require("./usingCtx-LY0tPCbQ.cjs");
|
|
3
|
+
const require_diff = require("./diff-CBp5KDNZ.cjs");
|
|
4
|
+
exports.DEFAULT_TOLERANCE_PCT = require_usingCtx.DEFAULT_TOLERANCE_PCT;
|
|
5
|
+
exports.TYPECHECK_CODE = require_usingCtx.TYPECHECK_CODE;
|
|
6
|
+
exports.emptyReport = require_usingCtx.emptyReport;
|
|
7
|
+
exports.evaluateExpected = require_usingCtx.evaluateExpected;
|
|
8
|
+
exports.isExpectedDims = require_usingCtx.isExpectedDims;
|
|
9
|
+
exports.pctDelta = require_usingCtx.pctDelta;
|
|
10
|
+
exports.runChecks = require_usingCtx.runChecks;
|
|
10
11
|
exports.runDiff = require_diff.runDiff;
|
|
11
12
|
exports.runMeasure = require_diff.runMeasure;
|
|
12
|
-
exports.runPart =
|
|
13
|
-
exports.serializeReport =
|
|
14
|
-
exports.typecheckPart =
|
|
13
|
+
exports.runPart = require_usingCtx.runPart;
|
|
14
|
+
exports.serializeReport = require_usingCtx.serializeReport;
|
|
15
|
+
exports.typecheckPart = require_usingCtx.typecheckPart;
|
package/dist/brepjs-cad.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as DEFAULT_TOLERANCE_PCT, d as pctDelta, f as runChecks, g as serializeReport, i as TYPECHECK_CODE, l as evaluateExpected, p as emptyReport, r as runPart, s as typecheckPart, u as isExpectedDims } from "./usingCtx-BDYtpr4t.js";
|
|
2
|
+
import { n as runMeasure, t as runDiff } from "./diff-DmFtXXiX.js";
|
|
2
3
|
export { DEFAULT_TOLERANCE_PCT, TYPECHECK_CODE, emptyReport, evaluateExpected, isExpectedDims, pctDelta, runChecks, runDiff, runMeasure, runPart, serializeReport, typecheckPart };
|
package/dist/cli/exportPart.d.ts
CHANGED
package/dist/cli/main.cjs
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const
|
|
3
|
+
const require_usingCtx = require("../usingCtx-LY0tPCbQ.cjs");
|
|
4
|
+
const require_diff = require("../diff-CBp5KDNZ.cjs");
|
|
5
|
+
const require_openBrowser = require("../openBrowser-Bip99cQv.cjs");
|
|
6
|
+
const require_disposeShape = require("../disposeShape-Bui9k40G.cjs");
|
|
4
7
|
let node_url = require("node:url");
|
|
5
8
|
let node_fs = require("node:fs");
|
|
6
9
|
let node_path = require("node:path");
|
|
7
10
|
let commander = require("commander");
|
|
8
11
|
let node_os = require("node:os");
|
|
9
|
-
let node_child_process = require("node:child_process");
|
|
10
12
|
//#region src/cli/scaffold.ts
|
|
11
13
|
function partTemplate(name) {
|
|
12
14
|
return `import { box, cut, unwrap } from 'brepjs';
|
|
@@ -97,114 +99,20 @@ function scaffoldPart(name, dir) {
|
|
|
97
99
|
files
|
|
98
100
|
};
|
|
99
101
|
}
|
|
100
|
-
function debounce(fn, delayMs = 150) {
|
|
101
|
-
let timer;
|
|
102
|
-
const cancel = () => {
|
|
103
|
-
if (timer) {
|
|
104
|
-
clearTimeout(timer);
|
|
105
|
-
timer = void 0;
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
const trigger = () => {
|
|
109
|
-
cancel();
|
|
110
|
-
timer = setTimeout(() => {
|
|
111
|
-
timer = void 0;
|
|
112
|
-
fn();
|
|
113
|
-
}, delayMs);
|
|
114
|
-
};
|
|
115
|
-
return {
|
|
116
|
-
trigger,
|
|
117
|
-
cancel
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
var SOURCE_FILE_RE = /\.(?:m?[jt]s|[jt]sx)$/;
|
|
121
|
-
function isWatchRelevant(filename) {
|
|
122
|
-
if (filename === void 0 || filename === null) return true;
|
|
123
|
-
const name = filename.toString();
|
|
124
|
-
if (/(?:^|[\\/])tsconfig(?:\..+)?\.json$/.test(name)) return true;
|
|
125
|
-
return SOURCE_FILE_RE.test(name);
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Root the watcher at the project, not the entry's own directory: with the common
|
|
129
|
-
* `src/main.ts` layout, tsconfig.json (whose JSX options change how sources transpile)
|
|
130
|
-
* and `../` imports live one level up. Nearest package.json or tsconfig.json walking up
|
|
131
|
-
* wins; entries with no project marker fall back to their own directory.
|
|
132
|
-
*/
|
|
133
|
-
function watchRootFor(entryPath) {
|
|
134
|
-
const start = (0, node_path.dirname)(entryPath);
|
|
135
|
-
let dir = start;
|
|
136
|
-
for (;;) {
|
|
137
|
-
if ((0, node_fs.existsSync)((0, node_path.join)(dir, "package.json")) || (0, node_fs.existsSync)((0, node_path.join)(dir, "tsconfig.json"))) return dir;
|
|
138
|
-
const parent = (0, node_path.dirname)(dir);
|
|
139
|
-
if (parent === dir) return start;
|
|
140
|
-
dir = parent;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
var IGNORED_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
144
|
-
"node_modules",
|
|
145
|
-
"dist",
|
|
146
|
-
".git"
|
|
147
|
-
]);
|
|
148
|
-
/**
|
|
149
|
-
* Watch a directory tree with one non-recursive `fs.watch` per directory, skipping
|
|
150
|
-
* node_modules/dist/.git and never following symlinks. `fs.watch({recursive})` has no
|
|
151
|
-
* exclusion API, so on a real project it would descend into node_modules — tens of
|
|
152
|
-
* thousands of inotify watches. Whenever an event fires in a directory it is re-scanned,
|
|
153
|
-
* which picks up newly created subdirectories. Returns a stop function.
|
|
154
|
-
*/
|
|
155
|
-
function watchTree(root, onEvent) {
|
|
156
|
-
const watchers = /* @__PURE__ */ new Map();
|
|
157
|
-
const add = (dir) => {
|
|
158
|
-
if (watchers.has(dir)) return;
|
|
159
|
-
try {
|
|
160
|
-
watchers.set(dir, (0, node_fs.watch)(dir, (_event, filename) => {
|
|
161
|
-
scan(dir);
|
|
162
|
-
onEvent(filename);
|
|
163
|
-
}));
|
|
164
|
-
} catch {}
|
|
165
|
-
};
|
|
166
|
-
const scan = (dir) => {
|
|
167
|
-
let entries;
|
|
168
|
-
try {
|
|
169
|
-
entries = (0, node_fs.readdirSync)(dir, { withFileTypes: true });
|
|
170
|
-
} catch {
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
for (const entry of entries) {
|
|
174
|
-
if (!entry.isDirectory() || IGNORED_DIR_NAMES.has(entry.name)) continue;
|
|
175
|
-
const child = (0, node_path.join)(dir, entry.name);
|
|
176
|
-
if (watchers.has(child)) continue;
|
|
177
|
-
add(child);
|
|
178
|
-
scan(child);
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
|
-
add(root);
|
|
182
|
-
scan(root);
|
|
183
|
-
return () => {
|
|
184
|
-
for (const watcher of watchers.values()) watcher.close();
|
|
185
|
-
watchers.clear();
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
//#endregion
|
|
189
|
-
//#region src/disposeShape.ts
|
|
190
|
-
function disposeShape(shape) {
|
|
191
|
-
const disposer = shape?.[Symbol.dispose];
|
|
192
|
-
if (typeof disposer === "function") disposer.call(shape);
|
|
193
|
-
}
|
|
194
102
|
//#endregion
|
|
195
103
|
//#region src/cli/exportPart.ts
|
|
196
104
|
function stem(file) {
|
|
197
|
-
return (0, node_path.basename)(file).replace(/\.brep\.ts$/, "").replace(/\.ts$/, "");
|
|
105
|
+
return (0, node_path.basename)(file).replace(/\.brep\.ts$/, "").replace(/\.(?:m?ts|tsx)$/, "");
|
|
198
106
|
}
|
|
199
107
|
async function exportPart(modulePath, formats, outDir) {
|
|
200
|
-
const { shape, report, step, glb } = await
|
|
108
|
+
const { shape, report, step, glb } = await require_usingCtx.runPart(modulePath, {
|
|
201
109
|
step: Boolean(formats.step),
|
|
202
110
|
glb: Boolean(formats.glb)
|
|
203
111
|
});
|
|
204
112
|
const errors = [];
|
|
205
113
|
const written = [];
|
|
206
|
-
if (!(
|
|
207
|
-
disposeShape(shape);
|
|
114
|
+
if (!(require_usingCtx.reportOk(report) && shape !== null)) {
|
|
115
|
+
require_disposeShape.disposeShape(shape);
|
|
208
116
|
const failures = report.errorInfos.map((e) => e.message);
|
|
209
117
|
return {
|
|
210
118
|
ok: false,
|
|
@@ -231,7 +139,7 @@ async function exportPart(modulePath, formats, outDir) {
|
|
|
231
139
|
} else errors.push("GLB export produced no data");
|
|
232
140
|
}
|
|
233
141
|
if (formats.stl) {
|
|
234
|
-
const { exportSTL, isOk } = await
|
|
142
|
+
const { exportSTL, isOk } = await require_usingCtx.loadBrep();
|
|
235
143
|
const r = exportSTL(shape);
|
|
236
144
|
if (isOk(r)) {
|
|
237
145
|
const p = (0, node_path.join)(outDir, `${base}.stl`);
|
|
@@ -240,7 +148,7 @@ async function exportPart(modulePath, formats, outDir) {
|
|
|
240
148
|
} else errors.push(`STL export: ${r.error.message}`);
|
|
241
149
|
}
|
|
242
150
|
} finally {
|
|
243
|
-
disposeShape(shape);
|
|
151
|
+
require_disposeShape.disposeShape(shape);
|
|
244
152
|
}
|
|
245
153
|
return {
|
|
246
154
|
ok: errors.length === 0,
|
|
@@ -250,43 +158,6 @@ async function exportPart(modulePath, formats, outDir) {
|
|
|
250
158
|
};
|
|
251
159
|
}
|
|
252
160
|
//#endregion
|
|
253
|
-
//#region src/cli/openBrowser.ts
|
|
254
|
-
/**
|
|
255
|
-
* Whether `--serve` should auto-open the browser for the current environment.
|
|
256
|
-
*
|
|
257
|
-
* Opens only for an interactive session — suppressed under CI, when stderr is
|
|
258
|
-
* not a TTY (agent/piped runs), or on Linux without a display server — so
|
|
259
|
-
* automation never spawns a browser unexpectedly. An explicit `--no-open` is a
|
|
260
|
-
* separate, always-on override handled by the caller.
|
|
261
|
-
*/
|
|
262
|
-
function shouldAutoOpen({ env = process.env, platform = process.platform, isTTY = Boolean(process.stderr.isTTY) } = {}) {
|
|
263
|
-
if (env["CI"]) return false;
|
|
264
|
-
if (!isTTY) return false;
|
|
265
|
-
if (platform === "linux" && !env["DISPLAY"] && !env["WAYLAND_DISPLAY"]) return false;
|
|
266
|
-
return true;
|
|
267
|
-
}
|
|
268
|
-
/** The platform-specific command that opens `url` in the default browser. */
|
|
269
|
-
function browserCommand(url, platform) {
|
|
270
|
-
if (platform === "darwin") return ["open", [url]];
|
|
271
|
-
if (platform === "win32") return ["rundll32", ["url.dll,FileProtocolHandler", url]];
|
|
272
|
-
return ["xdg-open", [url]];
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Best-effort open of `url` in the default browser. Never throws and never
|
|
276
|
-
* blocks — a missing opener just leaves the printed URL as the fallback.
|
|
277
|
-
*/
|
|
278
|
-
function openBrowser(url, platform = process.platform) {
|
|
279
|
-
try {
|
|
280
|
-
const [cmd, args] = browserCommand(url, platform);
|
|
281
|
-
const child = (0, node_child_process.spawn)(cmd, args, {
|
|
282
|
-
stdio: "ignore",
|
|
283
|
-
detached: true
|
|
284
|
-
});
|
|
285
|
-
child.on("error", () => {});
|
|
286
|
-
child.unref();
|
|
287
|
-
} catch {}
|
|
288
|
-
}
|
|
289
|
-
//#endregion
|
|
290
161
|
//#region src/snapshot/aiming.ts
|
|
291
162
|
/**
|
|
292
163
|
* Kernel-anchored marks for the judge: `B<index>` at each body's bbox centroid (only for multi-body
|
|
@@ -372,14 +243,14 @@ program.command("verify", { isDefault: true }).argument("<files...>", "path(s) o
|
|
|
372
243
|
if (opts.step || opts.serve || opts.snapshot || opts.glb || opts.expectCode || opts.expectInvalid) process.stderr.write("batch mode: --step/--glb/--serve/--snapshot/--expect-code/--expect-invalid ignored (single-file only)\n");
|
|
373
244
|
const results = [];
|
|
374
245
|
for (const f of targets) {
|
|
375
|
-
const { report, shape } = await
|
|
246
|
+
const { report, shape } = await require_usingCtx.runPart(f, {
|
|
376
247
|
check: Boolean(opts.check),
|
|
377
248
|
metrics: Boolean(opts.metrics)
|
|
378
249
|
});
|
|
379
|
-
disposeShape(shape);
|
|
250
|
+
require_disposeShape.disposeShape(shape);
|
|
380
251
|
results.push({
|
|
381
252
|
file: f,
|
|
382
|
-
ok:
|
|
253
|
+
ok: require_usingCtx.reportOk(report),
|
|
383
254
|
report
|
|
384
255
|
});
|
|
385
256
|
}
|
|
@@ -391,7 +262,7 @@ program.command("verify", { isDefault: true }).argument("<files...>", "path(s) o
|
|
|
391
262
|
}
|
|
392
263
|
const file = targets[0] ?? files[0] ?? "";
|
|
393
264
|
const wantStep = Boolean(opts.step) || Boolean(opts.snapshot) || Boolean(opts.serve);
|
|
394
|
-
const { report, step, glb, shape } = await
|
|
265
|
+
const { report, step, glb, shape } = await require_usingCtx.runPart((0, node_path.resolve)(file), {
|
|
395
266
|
step: wantStep,
|
|
396
267
|
glb: Boolean(opts.glb),
|
|
397
268
|
check: Boolean(opts.check),
|
|
@@ -419,27 +290,27 @@ program.command("verify", { isDefault: true }).argument("<files...>", "path(s) o
|
|
|
419
290
|
}
|
|
420
291
|
} else if (opts.snapshot) process.stderr.write("snapshot skipped: STEP export produced no artifact\n");
|
|
421
292
|
} catch (e) {
|
|
422
|
-
|
|
293
|
+
require_usingCtx.pushError(report, { message: `artifact write failed: ${e.message}` });
|
|
423
294
|
} finally {
|
|
424
|
-
disposeShape(shape);
|
|
295
|
+
require_disposeShape.disposeShape(shape);
|
|
425
296
|
}
|
|
426
|
-
const json =
|
|
297
|
+
const json = require_usingCtx.serializeReport(report);
|
|
427
298
|
if (opts.json && opts.json !== "-") (0, node_fs.writeFileSync)(opts.json, json);
|
|
428
299
|
process.stdout.write(json + "\n");
|
|
429
|
-
if (!
|
|
300
|
+
if (!require_usingCtx.reportOk(report)) process.exitCode = 1;
|
|
430
301
|
if (opts.expectInvalid || opts.expectCode) {
|
|
431
302
|
const codes = [...new Set(report.errorInfos.map((e) => e.code).filter((c) => Boolean(c)))];
|
|
432
|
-
const valid =
|
|
303
|
+
const valid = require_usingCtx.reportOk(report);
|
|
433
304
|
const pass = (!opts.expectInvalid || !valid) && (!opts.expectCode || codes.includes(opts.expectCode));
|
|
434
305
|
process.exitCode = pass ? 0 : 1;
|
|
435
306
|
process.stderr.write(`expect: ${pass ? "PASS" : "FAIL"}` + (opts.expectCode ? ` code=${opts.expectCode}` : "") + (opts.expectInvalid ? " invalid" : "") + ` (ok=${valid}, codes=${codes.join(",") || "none"})\n`);
|
|
436
307
|
return;
|
|
437
308
|
}
|
|
438
|
-
if (Boolean(opts.serve) && stepPath !== void 0 &&
|
|
309
|
+
if (Boolean(opts.serve) && stepPath !== void 0 && require_usingCtx.reportOk(report) && stepPath) {
|
|
439
310
|
const { serve } = await Promise.resolve().then(() => require("../snapshot/serve.cjs"));
|
|
440
311
|
const { url, reused } = await serve({ file: stepPath });
|
|
441
312
|
process.stderr.write(`viewer: ${url}\n`);
|
|
442
|
-
if (!reused && opts.open && shouldAutoOpen()) openBrowser(url);
|
|
313
|
+
if (!reused && opts.open && require_openBrowser.shouldAutoOpen()) require_openBrowser.openBrowser(url);
|
|
443
314
|
}
|
|
444
315
|
});
|
|
445
316
|
program.command("measure").argument("<a>", "path to a .brep.ts module").argument("[b]", "optional second module; if given, measures distance between the two parts").action(async (a, b) => {
|
|
@@ -462,14 +333,14 @@ program.command("snapshot").argument("<file>", "path to a .brep.ts module; rende
|
|
|
462
333
|
const resolved = (0, node_path.resolve)(file);
|
|
463
334
|
const base = opts.out ? (0, node_path.resolve)(opts.out) : `${resolved}-shots`;
|
|
464
335
|
const outDir = opts.label ? (0, node_path.join)(base, opts.label) : base;
|
|
465
|
-
const { report, step, shape } = await
|
|
336
|
+
const { report, step, shape } = await require_usingCtx.runPart(resolved, {
|
|
466
337
|
step: true,
|
|
467
338
|
metrics: true
|
|
468
339
|
});
|
|
469
340
|
try {
|
|
470
341
|
if (!step) {
|
|
471
342
|
process.stderr.write("snapshot: part produced no STEP — nothing to render\n");
|
|
472
|
-
process.stdout.write(
|
|
343
|
+
process.stdout.write(require_usingCtx.serializeReport(report) + "\n");
|
|
473
344
|
process.exitCode = 1;
|
|
474
345
|
return;
|
|
475
346
|
}
|
|
@@ -477,7 +348,7 @@ program.command("snapshot").argument("<file>", "path to a .brep.ts module; rende
|
|
|
477
348
|
(0, node_fs.writeFileSync)(stepPath, Buffer.from(step));
|
|
478
349
|
const shoot = await loadSnapshotShoot();
|
|
479
350
|
if (!shoot) {
|
|
480
|
-
process.stdout.write(
|
|
351
|
+
process.stdout.write(require_usingCtx.serializeReport(report) + "\n");
|
|
481
352
|
return;
|
|
482
353
|
}
|
|
483
354
|
const section = aimedSection(report.manufacturability?.bores ?? [], report.measurements.bounds);
|
|
@@ -491,7 +362,7 @@ program.command("snapshot").argument("<file>", "path to a .brep.ts module; rende
|
|
|
491
362
|
});
|
|
492
363
|
for (const p of pngs) process.stdout.write(`${p}\n`);
|
|
493
364
|
} finally {
|
|
494
|
-
disposeShape(shape);
|
|
365
|
+
require_disposeShape.disposeShape(shape);
|
|
495
366
|
}
|
|
496
367
|
});
|
|
497
368
|
program.command("init").argument("<name>", "part name; scaffolds <name>.brep.ts + tsconfig.json + README.md").option("--out <dir>", "target directory (defaults to ./<name>)").action((name, opts) => {
|
|
@@ -506,40 +377,22 @@ program.command("watch").argument("<file>", "path to a .brep.ts module; re-verif
|
|
|
506
377
|
const path = (0, node_path.resolve)(file);
|
|
507
378
|
const run = async (fresh) => {
|
|
508
379
|
try {
|
|
509
|
-
const { report, shape } = await
|
|
380
|
+
const { report, shape } = await require_usingCtx.runPart(path, { freshImport: fresh });
|
|
510
381
|
try {
|
|
511
|
-
process.stdout.write(
|
|
382
|
+
process.stdout.write(require_usingCtx.serializeReport(report) + "\n");
|
|
512
383
|
} finally {
|
|
513
|
-
disposeShape(shape);
|
|
384
|
+
require_disposeShape.disposeShape(shape);
|
|
514
385
|
}
|
|
515
386
|
} catch (e) {
|
|
516
387
|
process.stderr.write(`watch run failed: ${e.message}\n`);
|
|
517
388
|
}
|
|
518
389
|
};
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
const start = (fresh) => {
|
|
522
|
-
inFlight = true;
|
|
523
|
-
run(fresh).finally(() => {
|
|
524
|
-
inFlight = false;
|
|
525
|
-
if (rerunQueued) {
|
|
526
|
-
rerunQueued = false;
|
|
527
|
-
start(true);
|
|
528
|
-
}
|
|
529
|
-
});
|
|
530
|
-
};
|
|
531
|
-
const schedule = () => {
|
|
532
|
-
if (inFlight) {
|
|
533
|
-
rerunQueued = true;
|
|
534
|
-
return;
|
|
535
|
-
}
|
|
536
|
-
start(true);
|
|
537
|
-
};
|
|
538
|
-
const { trigger } = debounce(schedule, 150);
|
|
390
|
+
const runner = require_openBrowser.createSerialRunner(run);
|
|
391
|
+
const { trigger } = require_openBrowser.debounce(runner.trigger, 150);
|
|
539
392
|
process.stderr.write(`watching ${path} (Ctrl-C to stop)\n`);
|
|
540
|
-
start(false);
|
|
541
|
-
const stopWatching = watchTree(watchRootFor(path), (filename) => {
|
|
542
|
-
if (isWatchRelevant(filename)) trigger();
|
|
393
|
+
runner.start(false);
|
|
394
|
+
const stopWatching = require_openBrowser.watchTree(require_openBrowser.watchRootFor(path), (filename) => {
|
|
395
|
+
if (require_openBrowser.isWatchRelevant(filename)) trigger();
|
|
543
396
|
});
|
|
544
397
|
const stop = () => {
|
|
545
398
|
stopWatching();
|
|
@@ -548,7 +401,48 @@ program.command("watch").argument("<file>", "path to a .brep.ts module; re-verif
|
|
|
548
401
|
process.on("SIGINT", stop);
|
|
549
402
|
process.on("SIGTERM", stop);
|
|
550
403
|
});
|
|
551
|
-
program.command("
|
|
404
|
+
program.command("preview").argument("<file>", "path to a model module (.ts/.tsx); a families element tree renders per element").option("--watch", "re-evaluate on save and push updates to the open viewer tab").option("--write-only", "write artifacts without starting the viewer server (exits unless --watch)").option("--glb <out>", "write a merged GLB of the whole model").option("--json <out>", "write the JSON summary to this path").option("--port <n>", "viewer server port (default: OS-assigned)", (v) => parseInt(v, 10)).option("--no-open", "do not auto-open the browser").action(async (file, opts) => {
|
|
405
|
+
const { runPreview } = await Promise.resolve().then(() => require("../preview/preview.cjs"));
|
|
406
|
+
try {
|
|
407
|
+
await runPreview((0, node_path.resolve)(file), opts);
|
|
408
|
+
} catch (e) {
|
|
409
|
+
process.stderr.write(`preview failed: ${e.message}\n`);
|
|
410
|
+
process.exitCode = 1;
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
program.command("export").argument("<file>", "path to a part (.brep.ts) module; --3dm also accepts a model (.ts/.tsx) default-exporting a families element tree").option("--step", "write a STEP artifact").option("--glb", "write a GLB artifact").option("--stl", "write an STL artifact").option("--all", "write STEP + GLB + STL").option("--3dm", "write a Rhino .3dm (element-aware: one named object per element, layered)").option("--out <dir>", "output directory", ".").action(async (file, opts) => {
|
|
414
|
+
const path = (0, node_path.resolve)(file);
|
|
415
|
+
if (opts["3dm"] && (opts.step || opts.glb || opts.stl || opts.all)) {
|
|
416
|
+
process.stderr.write("--3dm cannot be combined with --step/--glb/--stl/--all\n");
|
|
417
|
+
process.exitCode = 1;
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
if (opts["3dm"]) {
|
|
421
|
+
let run;
|
|
422
|
+
try {
|
|
423
|
+
({export3dm: run} = await Promise.resolve().then(() => require("../export3dm/export3dm.cjs")));
|
|
424
|
+
} catch {
|
|
425
|
+
process.stderr.write(".3dm export needs rhino3dm — run: npm i rhino3dm\n");
|
|
426
|
+
process.exitCode = 1;
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
const outPath = (0, node_path.join)((0, node_path.resolve)(opts.out), stem(path) + ".3dm");
|
|
430
|
+
try {
|
|
431
|
+
const r = await run(path, outPath);
|
|
432
|
+
process.stderr.write(`wrote: ${outPath}\n`);
|
|
433
|
+
process.stdout.write(JSON.stringify({
|
|
434
|
+
ok: r.failed.length === 0,
|
|
435
|
+
written: [outPath],
|
|
436
|
+
elements: r.elements,
|
|
437
|
+
failed: r.failed
|
|
438
|
+
}, null, 2) + "\n");
|
|
439
|
+
if (r.failed.length > 0) process.exitCode = 1;
|
|
440
|
+
} catch (e) {
|
|
441
|
+
process.stderr.write(`3dm export failed: ${e.message}\n`);
|
|
442
|
+
process.exitCode = 1;
|
|
443
|
+
}
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
552
446
|
const formats = opts.all ? {
|
|
553
447
|
step: true,
|
|
554
448
|
glb: true,
|
|
@@ -559,11 +453,11 @@ program.command("export").argument("<file>", "path to a .brep.ts module").option
|
|
|
559
453
|
stl: Boolean(opts.stl)
|
|
560
454
|
};
|
|
561
455
|
if (!formats.step && !formats.glb && !formats.stl) {
|
|
562
|
-
process.stderr.write("no formats requested — pass --step/--glb/--stl or --all\n");
|
|
456
|
+
process.stderr.write("no formats requested — pass --step/--glb/--stl/--3dm or --all\n");
|
|
563
457
|
process.exitCode = 1;
|
|
564
458
|
return;
|
|
565
459
|
}
|
|
566
|
-
const result = await exportPart(
|
|
460
|
+
const result = await exportPart(path, formats, (0, node_path.resolve)(opts.out));
|
|
567
461
|
for (const p of result.written) process.stderr.write(`wrote: ${p}\n`);
|
|
568
462
|
for (const e of result.errors) process.stderr.write(`error: ${e}\n`);
|
|
569
463
|
process.stdout.write(JSON.stringify({
|