brepjs-cad 0.1.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/LICENSE +191 -0
  3. package/README.md +85 -0
  4. package/dist/brepjs-cad.cjs +8 -0
  5. package/dist/brepjs-cad.js +2 -0
  6. package/dist/cli/exportPart.d.ts +13 -0
  7. package/dist/cli/main.cjs +317 -0
  8. package/dist/cli/main.d.ts +2 -0
  9. package/dist/cli/main.js +316 -0
  10. package/dist/cli/scaffold.d.ts +9 -0
  11. package/dist/cli/watch.d.ts +10 -0
  12. package/dist/diff-4UNdqx4k.cjs +550 -0
  13. package/dist/diff-ByiVwVrr.js +509 -0
  14. package/dist/disposeShape.d.ts +6 -0
  15. package/dist/index.d.ts +5 -0
  16. package/dist/snapshot/registry.cjs +50 -0
  17. package/dist/snapshot/registry.d.ts +12 -0
  18. package/dist/snapshot/registry.js +48 -0
  19. package/dist/snapshot/serve.cjs +27 -0
  20. package/dist/snapshot/serve.d.ts +12 -0
  21. package/dist/snapshot/serve.js +26 -0
  22. package/dist/snapshot/shoot.cjs +85 -0
  23. package/dist/snapshot/shoot.d.ts +14 -0
  24. package/dist/snapshot/shoot.js +61 -0
  25. package/dist/snapshot/static.cjs +99 -0
  26. package/dist/snapshot/static.d.ts +16 -0
  27. package/dist/snapshot/static.js +97 -0
  28. package/dist/verify/checks.d.ts +3 -0
  29. package/dist/verify/diff.d.ts +2 -0
  30. package/dist/verify/measure.d.ts +6 -0
  31. package/dist/verify/report.d.ts +63 -0
  32. package/dist/verify/runPart.d.ts +18 -0
  33. package/package.json +79 -0
  34. package/viewer/dist/assets/brepjs-CI5VXw8W.js +57 -0
  35. package/viewer/dist/assets/index-DivdJhNC.js +4167 -0
  36. package/viewer/dist/assets/kernelWorker-BtcMpY8t.js +1 -0
  37. package/viewer/dist/index.html +22 -0
  38. package/viewer/dist/wasm/occt-wasm.js +2 -0
  39. package/viewer/dist/wasm/occt-wasm.wasm +0 -0
@@ -0,0 +1,509 @@
1
+ import { cut, exportGlb, exportSTEP, getBounds, init, isCompSolid, isCompound, isEdge, isFace, isOk, isShape3D, isShell, isSolid, isVertex, isWire, measureArea, measureDistance, measureLength, measureVolume, mesh, validSolid } from "brepjs";
2
+ //#region src/verify/report.ts
3
+ function emptyReport() {
4
+ return {
5
+ shapeType: null,
6
+ checks: [],
7
+ measurements: {},
8
+ errors: [],
9
+ errorInfos: [],
10
+ hints: []
11
+ };
12
+ }
13
+ /** Record a failure on the report, keeping the flat `errors` string list and structured `errorInfos` in sync. */
14
+ function pushError(r, info) {
15
+ r.errors.push(info.message);
16
+ r.errorInfos.push(info);
17
+ }
18
+ function reportOk(r) {
19
+ return r.errors.length === 0 && r.checks.every((c) => c.passed);
20
+ }
21
+ /**
22
+ * Local, brepjs-cad-owned advice keyed on `BrepErrorCode` values (see `brepjs`'s public
23
+ * `BrepErrorCode`). Intentionally not importing the library's internal `getSuggestionForCode`:
24
+ * this table is the agent loop's own actionable `fix` + `nextStep` guidance, and the library's
25
+ * public `BrepError.suggestion` is still surfaced alongside it on each hint.
26
+ */
27
+ var HINT_TABLE = {
28
+ FILLET_NO_EDGES: {
29
+ fix: "Select real edges before filleting — pass an edge query (e.g. find edges by direction/position) or a non-empty edge list, not the whole solid.",
30
+ nextStep: "List the solid’s edges, pick the ones to round, then call fillet(solid, radius, edges)."
31
+ },
32
+ CHAMFER_NO_EDGES: {
33
+ fix: "Select real edges before chamfering — pass a non-empty edge query/list rather than relying on a default that matched nothing.",
34
+ nextStep: "Enumerate the solid’s edges, choose the target edges, then call chamfer(solid, distance, edges)."
35
+ },
36
+ INVALID_FILLET_RADIUS: {
37
+ fix: "Use a fillet radius that is > 0 and small enough to fit the adjacent faces (well under half the thinnest wall).",
38
+ nextStep: "Reduce the radius (try a fraction of the smallest local feature size) and re-verify."
39
+ },
40
+ INVALID_CHAMFER_DISTANCE: {
41
+ fix: "Use a chamfer distance that is > 0 and smaller than the adjacent edge lengths.",
42
+ nextStep: "Lower the distance below the shortest adjacent edge and re-verify."
43
+ },
44
+ INVALID_THICKNESS: {
45
+ fix: "Use a shell/wall thickness that is > 0 and less than half the smallest cross-section.",
46
+ nextStep: "Reduce the thickness and re-verify, or remove the offending face from the removed-faces set."
47
+ },
48
+ ZERO_LENGTH_EXTRUSION: {
49
+ fix: "Extrude by a non-zero distance — a length of 0 produces no solid.",
50
+ nextStep: "Set a positive extrusion height (units: mm) and re-verify."
51
+ },
52
+ ZERO_OFFSET: {
53
+ fix: "Offset by a non-zero amount — an offset of 0 is a no-op the kernel rejects.",
54
+ nextStep: "Use a small non-zero offset (positive grows, negative shrinks) and re-verify."
55
+ },
56
+ FILLET_NOT_3D: {
57
+ fix: "fillet needs a 3D solid. Build the solid (extrude/revolve/box) before rounding edges.",
58
+ nextStep: "Move the fillet after the solid is created, then fillet the solid’s edges."
59
+ },
60
+ CHAMFER_NOT_3D: {
61
+ fix: "chamfer needs a 3D solid. Build the solid first, then chamfer its edges.",
62
+ nextStep: "Reorder so chamfer runs on the finished solid, not a sketch/wire/face."
63
+ },
64
+ FUSE_NOT_3D: {
65
+ fix: "fuse needs two 3D solids. Ensure both operands are solids before unioning.",
66
+ nextStep: "Extrude/loft each profile into a solid first, then fuse and unwrap the Result."
67
+ },
68
+ CUT_NOT_3D: {
69
+ fix: "cut needs 3D solids for both the base and the tool. Build both as solids first.",
70
+ nextStep: "Make the tool a solid (e.g. a box/cylinder), then cut(base, tool) and unwrap the Result."
71
+ },
72
+ INTERSECT_NOT_3D: {
73
+ fix: "intersect needs two 3D solids. Build both operands as solids first.",
74
+ nextStep: "Ensure both inputs are solids, then intersect(a, b) and unwrap the Result."
75
+ },
76
+ SHELL_NOT_3D: {
77
+ fix: "shell needs a 3D solid. Create the solid before hollowing it.",
78
+ nextStep: "Build the solid first, then shell it with a thickness and the faces to remove."
79
+ },
80
+ OFFSET_NOT_3D: {
81
+ fix: "This offset needs a 3D solid. Build the solid before offsetting.",
82
+ nextStep: "Reorder so offset runs on the solid, then re-verify."
83
+ },
84
+ SWEEP_NOT_3D: {
85
+ fix: "sweep needs a 3D result context — check the profile and path produce a solid sweep.",
86
+ nextStep: "Verify the profile is a closed wire/face and the path is a valid wire, then re-sweep."
87
+ },
88
+ LOFT_NOT_3D: {
89
+ fix: "loft needs 3D-capable sections. Use closed profiles that can form a solid.",
90
+ nextStep: "Provide at least two closed section wires/faces, then loft and unwrap the Result."
91
+ },
92
+ REVOLUTION_NOT_3D: {
93
+ fix: "revolve needs a 2D profile revolved about an axis. Pass a face/closed wire.",
94
+ nextStep: "Use a closed profile and a valid axis, then revolve and unwrap the Result."
95
+ },
96
+ LOFT_FAILED: {
97
+ fix: "The loft could not be built — usually mismatched, self-intersecting, or out-of-order sections.",
98
+ nextStep: "Make the sections consistent (same orientation, non-self-intersecting, ordered along the loft) and retry."
99
+ },
100
+ LOFT_EMPTY: {
101
+ fix: "loft received too few sections. Provide at least two profiles.",
102
+ nextStep: "Add the missing section wires/faces and loft again."
103
+ },
104
+ SWEEP_FAILED: {
105
+ fix: "The sweep failed — usually a path with sharp corners/self-intersection or a profile too large for the path curvature.",
106
+ nextStep: "Smooth or simplify the path, shrink the profile, then re-sweep."
107
+ },
108
+ FUSE_FAILED: {
109
+ fix: "The boolean union failed — often touching-but-not-overlapping solids or tolerance issues.",
110
+ nextStep: "Make the operands overlap slightly (or heal/translate one), then re-fuse."
111
+ },
112
+ CUT_FAILED: {
113
+ fix: "The boolean subtraction failed — often a tool that does not actually intersect the base, or tolerance issues.",
114
+ nextStep: "Confirm the tool overlaps the base, optionally heal the inputs, then re-cut."
115
+ },
116
+ BOOLEAN_HAS_ERRORS: {
117
+ fix: "The boolean ran but the kernel reported errors (often coincident faces or near-tangent contact).",
118
+ nextStep: "Perturb one operand slightly so contact is a clean overlap, or heal the inputs, then retry."
119
+ },
120
+ STEP_EXPORT_CRASHED: {
121
+ fix: "STEP export crashed in the kernel — frequently a disjoint/degenerate fuse or an invalid solid reaching the exporter.",
122
+ nextStep: "Run validity checks first, heal/simplify the shape (or avoid fusing disjoint solids), then re-export."
123
+ },
124
+ STEP_EXPORT_FAILED: {
125
+ fix: "STEP export failed. The shape is likely invalid or non-manifold.",
126
+ nextStep: "Fix validity errors (heal/sew) until the solid is valid, then re-export."
127
+ },
128
+ STL_EXPORT_CRASHED: {
129
+ fix: "STL export crashed — usually an invalid or non-manifold mesh source.",
130
+ nextStep: "Verify the solid is valid and watertight, then re-export."
131
+ },
132
+ STL_EXPORT_FAILED: {
133
+ fix: "STL export failed. The shape is likely invalid or empty.",
134
+ nextStep: "Fix validity errors first, then re-export."
135
+ },
136
+ NULL_SHAPE_INPUT: {
137
+ fix: "An operation received a null/empty shape. Ensure the previous step actually produced a shape.",
138
+ nextStep: "Check the upstream Result was unwrapped (not an Err) before passing it on."
139
+ },
140
+ NULL_SHAPE: {
141
+ fix: "An operation produced or received a null shape. A prior step likely failed silently.",
142
+ nextStep: "Verify each intermediate shape is non-null before chaining the next operation."
143
+ },
144
+ VALIDATION_FAILED: {
145
+ fix: "The shape failed validity (BRepCheck). It is non-manifold, self-intersecting, or has bad geometry.",
146
+ nextStep: "Heal/sew the shape, or revisit the operation that produced it, until validSolid passes."
147
+ }
148
+ };
149
+ /** Synthetic code attached to validity-check failures (validSolid returns a plain string error). */
150
+ var VALIDITY_FAILURE_CODE = "VALIDATION_FAILED";
151
+ function hintFor(info) {
152
+ if (!info.code) return null;
153
+ const entry = HINT_TABLE[info.code];
154
+ const fix = entry?.fix ?? info.suggestion ?? "No specific fix available; inspect the error and the operation that produced it.";
155
+ const nextStep = entry?.nextStep ?? "Adjust the failing operation per the message/suggestion, then re-verify.";
156
+ return {
157
+ code: info.code,
158
+ message: info.message,
159
+ fix,
160
+ nextStep
161
+ };
162
+ }
163
+ function buildHints(r) {
164
+ const hints = [];
165
+ const seen = /* @__PURE__ */ new Set();
166
+ for (const info of r.errorInfos) {
167
+ const hint = hintFor(info);
168
+ if (!hint) continue;
169
+ const key = `${hint.code}${hint.message}`;
170
+ if (seen.has(key)) continue;
171
+ seen.add(key);
172
+ hints.push(hint);
173
+ }
174
+ return hints;
175
+ }
176
+ function serializeReport(r) {
177
+ return JSON.stringify({
178
+ ok: reportOk(r),
179
+ ...r
180
+ }, null, 2);
181
+ }
182
+ //#endregion
183
+ //#region src/verify/checks.ts
184
+ function shapeTypeOf(s) {
185
+ if (isSolid(s)) return "Solid";
186
+ if (isCompSolid(s)) return "CompSolid";
187
+ if (isCompound(s)) return "Compound";
188
+ if (isShell(s)) return "Shell";
189
+ if (isFace(s)) return "Face";
190
+ if (isWire(s)) return "Wire";
191
+ if (isEdge(s)) return "Edge";
192
+ if (isVertex(s)) return "Vertex";
193
+ return "Unknown";
194
+ }
195
+ function runChecks(shape) {
196
+ const r = emptyReport();
197
+ r.shapeType = shapeTypeOf(shape);
198
+ if (isSolid(shape)) {
199
+ const valid = validSolid(shape);
200
+ const validCheck = {
201
+ name: "isValidSolid",
202
+ passed: isOk(valid)
203
+ };
204
+ if (!isOk(valid)) {
205
+ validCheck.detail = valid.error;
206
+ r.errorInfos.push({
207
+ message: `isValidSolid: ${valid.error}`,
208
+ code: VALIDITY_FAILURE_CODE
209
+ });
210
+ }
211
+ r.checks.push(validCheck);
212
+ }
213
+ if (isShape3D(shape)) {
214
+ const vol = measureVolume(shape);
215
+ if (isOk(vol)) {
216
+ r.measurements.volume = vol.value;
217
+ r.checks.push({
218
+ name: "positiveVolume",
219
+ passed: vol.value > 0
220
+ });
221
+ } else pushError(r, {
222
+ message: `measureVolume: ${vol.error.message}`,
223
+ code: vol.error.code,
224
+ suggestion: vol.error.suggestion
225
+ });
226
+ }
227
+ if (isFace(shape) || isShape3D(shape)) {
228
+ const area = measureArea(shape);
229
+ if (isOk(area)) r.measurements.area = area.value;
230
+ }
231
+ try {
232
+ r.measurements.bounds = getBounds(shape);
233
+ } catch (e) {
234
+ pushError(r, { message: `getBounds: ${e.message}` });
235
+ }
236
+ r.hints = buildHints(r);
237
+ return r;
238
+ }
239
+ //#endregion
240
+ //#region src/verify/runPart.ts
241
+ function isResult(v) {
242
+ return typeof v === "object" && v !== null && "ok" in v && typeof v.ok === "boolean";
243
+ }
244
+ function isBrepError(v) {
245
+ if (typeof v !== "object" || v === null) return false;
246
+ const rec = v;
247
+ return typeof rec["code"] === "string" && typeof rec["message"] === "string";
248
+ }
249
+ /** Pull structured `{ message, code, suggestion }` out of a `BrepError`, a thrown `Error`, or anything. */
250
+ function toErrorInfo(prefix, e) {
251
+ if (isBrepError(e)) return {
252
+ message: `${prefix}: ${e.message}`,
253
+ code: e.code,
254
+ suggestion: e.suggestion
255
+ };
256
+ if (e instanceof Error) return { message: `${prefix}: ${e.message}` };
257
+ return { message: `${prefix}: ${String(e)}` };
258
+ }
259
+ function finalize(result) {
260
+ result.report.hints = buildHints(result.report);
261
+ return result;
262
+ }
263
+ async function runPart(modulePath, opts = {}) {
264
+ await init();
265
+ const report = emptyReport();
266
+ let mod;
267
+ try {
268
+ mod = await import(modulePath);
269
+ } catch (e) {
270
+ pushError(report, toErrorInfo("import failed", e));
271
+ return finalize({
272
+ shape: null,
273
+ report
274
+ });
275
+ }
276
+ if (typeof mod.default !== "function") {
277
+ pushError(report, { message: "module has no default-exported part function" });
278
+ return finalize({
279
+ shape: null,
280
+ report
281
+ });
282
+ }
283
+ let out;
284
+ try {
285
+ out = await mod.default();
286
+ } catch (e) {
287
+ pushError(report, toErrorInfo("part threw", e));
288
+ return finalize({
289
+ shape: null,
290
+ report
291
+ });
292
+ }
293
+ let shape;
294
+ if (isResult(out)) if (isOk(out)) shape = out.value;
295
+ else {
296
+ pushError(report, toErrorInfo("part returned Err", out.error));
297
+ return finalize({
298
+ shape: null,
299
+ report
300
+ });
301
+ }
302
+ else shape = out;
303
+ if (!shape) {
304
+ pushError(report, { message: "part produced no shape" });
305
+ return finalize({
306
+ shape: null,
307
+ report
308
+ });
309
+ }
310
+ const result = runChecks(shape);
311
+ let glb;
312
+ let step;
313
+ if (opts.glb) try {
314
+ glb = exportGlb(mesh(shape));
315
+ } catch (e) {
316
+ pushError(result, toErrorInfo("exportGlb", e));
317
+ }
318
+ if (opts.step) {
319
+ const r = exportSTEP(shape);
320
+ if (isOk(r)) step = await r.value.arrayBuffer();
321
+ else pushError(result, toErrorInfo("exportSTEP", r.error));
322
+ }
323
+ return finalize({
324
+ shape,
325
+ report: result,
326
+ step,
327
+ glb
328
+ });
329
+ }
330
+ //#endregion
331
+ //#region \0@oxc-project+runtime@0.132.0/helpers/usingCtx.js
332
+ function _usingCtx() {
333
+ var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
334
+ var n = Error();
335
+ return n.name = "SuppressedError", n.error = r, n.suppressed = e, n;
336
+ }, e = {}, n = [];
337
+ function using(r, e) {
338
+ if (null != e) {
339
+ if (Object(e) !== e) throw new TypeError("using declarations can only be used with objects, functions, null, or undefined.");
340
+ if (r) var o = e[Symbol.asyncDispose || Symbol["for"]("Symbol.asyncDispose")];
341
+ if (void 0 === o && (o = e[Symbol.dispose || Symbol["for"]("Symbol.dispose")], r)) var t = o;
342
+ if ("function" != typeof o) throw new TypeError("Object is not disposable.");
343
+ t && (o = function o() {
344
+ try {
345
+ t.call(e);
346
+ } catch (r) {
347
+ return Promise.reject(r);
348
+ }
349
+ }), n.push({
350
+ v: e,
351
+ d: o,
352
+ a: r
353
+ });
354
+ } else r && n.push({
355
+ d: e,
356
+ a: r
357
+ });
358
+ return e;
359
+ }
360
+ return {
361
+ e,
362
+ u: using.bind(null, !1),
363
+ a: using.bind(null, !0),
364
+ d: function d() {
365
+ var o, t = this.e, s = 0;
366
+ function next() {
367
+ for (; o = n.pop();) try {
368
+ if (!o.a && 1 === s) return s = 0, n.push(o), Promise.resolve().then(next);
369
+ if (o.d) {
370
+ var r = o.d.call(o.v);
371
+ if (o.a) return s |= 2, Promise.resolve(r).then(next, err);
372
+ } else s |= 1;
373
+ } catch (r) {
374
+ return err(r);
375
+ }
376
+ if (1 === s) return t !== e ? Promise.reject(t) : Promise.resolve();
377
+ if (t !== e) throw t;
378
+ }
379
+ function err(n) {
380
+ return t = t !== e ? new r(n, t) : n, next();
381
+ }
382
+ return next();
383
+ }
384
+ };
385
+ }
386
+ //#endregion
387
+ //#region src/verify/measure.ts
388
+ async function runMeasure(aPath, bPath) {
389
+ try {
390
+ var _usingCtx$2 = _usingCtx();
391
+ const errors = [];
392
+ const a = await runPart(aPath);
393
+ errors.push(...a.report.errors);
394
+ if (!a.shape) return { errors };
395
+ const sa = _usingCtx$2.u(a.shape);
396
+ if (bPath === void 0) {
397
+ const len = measureLength(sa);
398
+ if (isOk(len)) return {
399
+ length: len.value,
400
+ errors
401
+ };
402
+ errors.push(`measureLength: ${len.error.message}`);
403
+ return { errors };
404
+ }
405
+ const b = await runPart(bPath);
406
+ errors.push(...b.report.errors);
407
+ if (!b.shape) return { errors };
408
+ const dist = measureDistance(sa, _usingCtx$2.u(b.shape));
409
+ if (isOk(dist)) return {
410
+ distance: dist.value,
411
+ errors
412
+ };
413
+ errors.push(`measureDistance: ${dist.error.message}`);
414
+ return { errors };
415
+ } catch (_) {
416
+ _usingCtx$2.e = _;
417
+ } finally {
418
+ _usingCtx$2.d();
419
+ }
420
+ }
421
+ //#endregion
422
+ //#region src/verify/diff.ts
423
+ function emptyDiff(errors) {
424
+ return {
425
+ volumeDelta: 0,
426
+ areaDelta: 0,
427
+ bboxDelta: {
428
+ xMin: 0,
429
+ xMax: 0,
430
+ yMin: 0,
431
+ yMax: 0,
432
+ zMin: 0,
433
+ zMax: 0
434
+ },
435
+ symmetricDifferenceVolume: 0,
436
+ errors
437
+ };
438
+ }
439
+ function volumeOf(shape, errors) {
440
+ const v = measureVolume(shape);
441
+ if (isOk(v)) return v.value;
442
+ errors.push(`measureVolume: ${v.error.message}`);
443
+ return 0;
444
+ }
445
+ function areaOf(shape, errors) {
446
+ if (!isShape3D(shape)) return 0;
447
+ const a = measureArea(shape);
448
+ if (isOk(a)) return a.value;
449
+ errors.push(`measureArea: ${a.error.message}`);
450
+ return 0;
451
+ }
452
+ function cutVolume(x, y, errors) {
453
+ try {
454
+ var _usingCtx$1 = _usingCtx();
455
+ const r = cut(x, y);
456
+ if (!isOk(r)) {
457
+ errors.push(`cut: ${r.error.message}`);
458
+ return 0;
459
+ }
460
+ return volumeOf(_usingCtx$1.u(r.value), errors);
461
+ } catch (_) {
462
+ _usingCtx$1.e = _;
463
+ } finally {
464
+ _usingCtx$1.d();
465
+ }
466
+ }
467
+ async function runDiff(aPath, bPath) {
468
+ try {
469
+ var _usingCtx3 = _usingCtx();
470
+ const errors = [];
471
+ const a = await runPart(aPath);
472
+ errors.push(...a.report.errors);
473
+ const b = await runPart(bPath);
474
+ errors.push(...b.report.errors);
475
+ if (!a.shape || !b.shape) return emptyDiff(errors);
476
+ const sa = _usingCtx3.u(a.shape);
477
+ const sb = _usingCtx3.u(b.shape);
478
+ const ba = getBounds(sa);
479
+ const bb = getBounds(sb);
480
+ const bboxDelta = {
481
+ xMin: bb.xMin - ba.xMin,
482
+ xMax: bb.xMax - ba.xMax,
483
+ yMin: bb.yMin - ba.yMin,
484
+ yMax: bb.yMax - ba.yMax,
485
+ zMin: bb.zMin - ba.zMin,
486
+ zMax: bb.zMax - ba.zMax
487
+ };
488
+ const areaDelta = areaOf(sb, errors) - areaOf(sa, errors);
489
+ let volumeDelta = 0;
490
+ let symmetricDifferenceVolume = 0;
491
+ if (isShape3D(sa) && isShape3D(sb)) {
492
+ volumeDelta = volumeOf(sb, errors) - volumeOf(sa, errors);
493
+ symmetricDifferenceVolume = cutVolume(sa, sb, errors) + cutVolume(sb, sa, errors);
494
+ }
495
+ return {
496
+ volumeDelta,
497
+ areaDelta,
498
+ bboxDelta,
499
+ symmetricDifferenceVolume,
500
+ errors
501
+ };
502
+ } catch (_) {
503
+ _usingCtx3.e = _;
504
+ } finally {
505
+ _usingCtx3.d();
506
+ }
507
+ }
508
+ //#endregion
509
+ export { emptyReport as a, runChecks as i, runMeasure as n, reportOk as o, runPart as r, serializeReport as s, runDiff as t };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Release a live WASM-backed kernel shape handle returned by `runPart`.
3
+ * No-op for null/undefined or shapes without a disposer, so callers can pass
4
+ * `result.shape` unconditionally. WASM memory accumulates without this.
5
+ */
6
+ export declare function disposeShape(shape: unknown): void;
@@ -0,0 +1,5 @@
1
+ export { runPart, type RunPartOptions, type RunPartResult } from './verify/runPart.js';
2
+ export { runChecks } from './verify/checks.js';
3
+ export { runMeasure, type MeasureReport } from './verify/measure.js';
4
+ export { runDiff } from './verify/diff.js';
5
+ export { serializeReport, emptyReport, type VerifyReport, type VerifyCheck, type VerifyMeasurements, type DiffReport, type BoundsDelta, } from './verify/report.js';
@@ -0,0 +1,50 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_snapshot_static = require("./static.cjs");
3
+ //#region src/snapshot/registry.ts
4
+ var DEFAULT_PORT = 7373;
5
+ var PROBE_SPAN = 8;
6
+ var DEFAULT_SHUTDOWN_AFTER_MS = 720 * 60 * 1e3;
7
+ async function probe(port) {
8
+ const ctrl = new AbortController();
9
+ const timer = setTimeout(() => ctrl.abort(), 400);
10
+ try {
11
+ const res = await fetch(`http://127.0.0.1:${port}/__cad/server`, { signal: ctrl.signal });
12
+ if (!res.ok) return false;
13
+ const d = await res.json();
14
+ return d.app === "brepjs-cad-viewer" && d.dynamicRoot === true && typeof d.serverApiVersion === "number" && d.serverApiVersion >= 1;
15
+ } catch {
16
+ return false;
17
+ } finally {
18
+ clearTimeout(timer);
19
+ }
20
+ }
21
+ async function acquireServer(opts = {}) {
22
+ const ports = opts.port !== void 0 ? [opts.port] : Array.from({ length: PROBE_SPAN }, (_, i) => DEFAULT_PORT + i);
23
+ for (const port of ports) if (await probe(port)) return {
24
+ port,
25
+ url: `http://127.0.0.1:${port}`,
26
+ reused: true,
27
+ close: () => Promise.resolve()
28
+ };
29
+ let server;
30
+ for (const port of ports) try {
31
+ server = await require_snapshot_static.startStaticServer({ port });
32
+ break;
33
+ } catch {}
34
+ if (!server) throw new Error(`no free port in ${ports[0]}..${ports[ports.length - 1]}`);
35
+ const timer = setTimeout(() => void server?.close(), opts.shutdownAfterMs ?? 432e5);
36
+ timer.unref();
37
+ const started = server;
38
+ return {
39
+ port: started.port,
40
+ url: started.url,
41
+ reused: false,
42
+ close: async () => {
43
+ clearTimeout(timer);
44
+ await started.close();
45
+ }
46
+ };
47
+ }
48
+ //#endregion
49
+ exports.DEFAULT_SHUTDOWN_AFTER_MS = DEFAULT_SHUTDOWN_AFTER_MS;
50
+ exports.acquireServer = acquireServer;
@@ -0,0 +1,12 @@
1
+ export declare const DEFAULT_SHUTDOWN_AFTER_MS: number;
2
+ export interface AcquireOptions {
3
+ port?: number;
4
+ shutdownAfterMs?: number;
5
+ }
6
+ export interface AcquiredServer {
7
+ port: number;
8
+ url: string;
9
+ reused: boolean;
10
+ close(): Promise<void>;
11
+ }
12
+ export declare function acquireServer(opts?: AcquireOptions): Promise<AcquiredServer>;
@@ -0,0 +1,48 @@
1
+ import { startStaticServer } from "./static.js";
2
+ //#region src/snapshot/registry.ts
3
+ var DEFAULT_PORT = 7373;
4
+ var PROBE_SPAN = 8;
5
+ var DEFAULT_SHUTDOWN_AFTER_MS = 720 * 60 * 1e3;
6
+ async function probe(port) {
7
+ const ctrl = new AbortController();
8
+ const timer = setTimeout(() => ctrl.abort(), 400);
9
+ try {
10
+ const res = await fetch(`http://127.0.0.1:${port}/__cad/server`, { signal: ctrl.signal });
11
+ if (!res.ok) return false;
12
+ const d = await res.json();
13
+ return d.app === "brepjs-cad-viewer" && d.dynamicRoot === true && typeof d.serverApiVersion === "number" && d.serverApiVersion >= 1;
14
+ } catch {
15
+ return false;
16
+ } finally {
17
+ clearTimeout(timer);
18
+ }
19
+ }
20
+ async function acquireServer(opts = {}) {
21
+ const ports = opts.port !== void 0 ? [opts.port] : Array.from({ length: PROBE_SPAN }, (_, i) => DEFAULT_PORT + i);
22
+ for (const port of ports) if (await probe(port)) return {
23
+ port,
24
+ url: `http://127.0.0.1:${port}`,
25
+ reused: true,
26
+ close: () => Promise.resolve()
27
+ };
28
+ let server;
29
+ for (const port of ports) try {
30
+ server = await startStaticServer({ port });
31
+ break;
32
+ } catch {}
33
+ if (!server) throw new Error(`no free port in ${ports[0]}..${ports[ports.length - 1]}`);
34
+ const timer = setTimeout(() => void server?.close(), opts.shutdownAfterMs ?? 432e5);
35
+ timer.unref();
36
+ const started = server;
37
+ return {
38
+ port: started.port,
39
+ url: started.url,
40
+ reused: false,
41
+ close: async () => {
42
+ clearTimeout(timer);
43
+ await started.close();
44
+ }
45
+ };
46
+ }
47
+ //#endregion
48
+ export { DEFAULT_SHUTDOWN_AFTER_MS, acquireServer };
@@ -0,0 +1,27 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_snapshot_registry = require("./registry.cjs");
3
+ let node_path = require("node:path");
4
+ //#region src/snapshot/serve.ts
5
+ function viewerUrl(base, file) {
6
+ if (!file) return base;
7
+ const abs = (0, node_path.resolve)(file);
8
+ return `${base}/?dir=${encodeURIComponent((0, node_path.dirname)(abs))}&file=${encodeURIComponent((0, node_path.basename)(abs))}`;
9
+ }
10
+ /** Acquire (reuse or start) the persistent server and return its viewer URL. */
11
+ async function serve(opts = {}) {
12
+ const server = await require_snapshot_registry.acquireServer(opts);
13
+ const url = viewerUrl(server.url, opts.file);
14
+ if (!server.reused) {
15
+ const onSig = () => void server.close().then(() => process.exit(0));
16
+ process.once("SIGINT", onSig);
17
+ process.once("SIGTERM", onSig);
18
+ }
19
+ return {
20
+ port: server.port,
21
+ url,
22
+ reused: server.reused,
23
+ close: () => server.close()
24
+ };
25
+ }
26
+ //#endregion
27
+ exports.serve = serve;
@@ -0,0 +1,12 @@
1
+ import { AcquireOptions } from './registry.js';
2
+ export interface ServeOptions extends AcquireOptions {
3
+ file?: string;
4
+ }
5
+ export interface ServeHandle {
6
+ port: number;
7
+ url: string;
8
+ reused: boolean;
9
+ close(): Promise<void>;
10
+ }
11
+ /** Acquire (reuse or start) the persistent server and return its viewer URL. */
12
+ export declare function serve(opts?: ServeOptions): Promise<ServeHandle>;