okgeometry-api 1.26.1 → 1.27.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 (99) hide show
  1. package/dist/Arc.js +1 -1
  2. package/dist/Arc.js.map +1 -1
  3. package/dist/Brep.js +1 -1
  4. package/dist/Brep.js.map +1 -1
  5. package/dist/Circle.js +1 -1
  6. package/dist/Circle.js.map +1 -1
  7. package/dist/Line.js +1 -1
  8. package/dist/Line.js.map +1 -1
  9. package/dist/Mesh.d.ts +41 -0
  10. package/dist/Mesh.d.ts.map +1 -1
  11. package/dist/Mesh.js +235 -21
  12. package/dist/Mesh.js.map +1 -1
  13. package/dist/MeshSurface.js +1 -1
  14. package/dist/MeshSurface.js.map +1 -1
  15. package/dist/NurbsCurve.js +1 -1
  16. package/dist/NurbsCurve.js.map +1 -1
  17. package/dist/NurbsSurface.js +1 -1
  18. package/dist/NurbsSurface.js.map +1 -1
  19. package/dist/PolyCurve.js +1 -1
  20. package/dist/PolyCurve.js.map +1 -1
  21. package/dist/Polyline.js +1 -1
  22. package/dist/Polyline.js.map +1 -1
  23. package/dist/Ray.js +1 -1
  24. package/dist/Ray.js.map +1 -1
  25. package/dist/SculptSession.js +1 -1
  26. package/dist/SculptSession.js.map +1 -1
  27. package/dist/engine-threaded.d.ts +2 -0
  28. package/dist/engine-threaded.d.ts.map +1 -0
  29. package/dist/engine-threaded.js +33 -0
  30. package/dist/engine-threaded.js.map +1 -0
  31. package/dist/engine.d.ts +21 -0
  32. package/dist/engine.d.ts.map +1 -1
  33. package/dist/engine.js +42 -0
  34. package/dist/engine.js.map +1 -1
  35. package/dist/index.d.ts +1 -1
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +1 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/mesh-boolean.pool.d.ts +23 -1
  40. package/dist/mesh-boolean.pool.d.ts.map +1 -1
  41. package/dist/mesh-boolean.pool.js +97 -12
  42. package/dist/mesh-boolean.pool.js.map +1 -1
  43. package/dist/mesh-boolean.protocol.d.ts +21 -1
  44. package/dist/mesh-boolean.protocol.d.ts.map +1 -1
  45. package/dist/mesh-boolean.protocol.js.map +1 -1
  46. package/dist/mesh-boolean.worker.d.ts.map +1 -1
  47. package/dist/mesh-boolean.worker.js +94 -1
  48. package/dist/mesh-boolean.worker.js.map +1 -1
  49. package/dist/wasm-active.d.ts +20 -0
  50. package/dist/wasm-active.d.ts.map +1 -0
  51. package/dist/wasm-active.js +26 -0
  52. package/dist/wasm-active.js.map +1 -0
  53. package/dist/wasm-base64-threaded.d.ts +2 -0
  54. package/dist/wasm-base64-threaded.d.ts.map +1 -0
  55. package/dist/wasm-base64-threaded.js +3 -0
  56. package/dist/wasm-base64-threaded.js.map +1 -0
  57. package/dist/wasm-base64.d.ts +1 -1
  58. package/dist/wasm-base64.d.ts.map +1 -1
  59. package/dist/wasm-base64.js +1 -1
  60. package/dist/wasm-base64.js.map +1 -1
  61. package/dist/wasm-bindings-threaded.d.ts +2664 -0
  62. package/dist/wasm-bindings-threaded.d.ts.map +1 -0
  63. package/dist/wasm-bindings-threaded.js +5008 -0
  64. package/dist/wasm-bindings-threaded.js.map +1 -0
  65. package/dist/wasm-bindings.d.ts +130 -0
  66. package/dist/wasm-bindings.d.ts.map +1 -1
  67. package/dist/wasm-bindings.js +208 -0
  68. package/dist/wasm-bindings.js.map +1 -1
  69. package/dist/workerHelpers-threaded.d.ts +2 -0
  70. package/dist/workerHelpers-threaded.d.ts.map +1 -0
  71. package/dist/workerHelpers-threaded.js +100 -0
  72. package/dist/workerHelpers-threaded.js.map +1 -0
  73. package/package.json +8 -3
  74. package/src/Arc.ts +117 -117
  75. package/src/Brep.ts +1 -1
  76. package/src/Circle.ts +166 -166
  77. package/src/Line.ts +171 -171
  78. package/src/Mesh.ts +300 -45
  79. package/src/MeshSurface.ts +1 -1
  80. package/src/NurbsCurve.ts +655 -655
  81. package/src/NurbsSurface.ts +721 -721
  82. package/src/PolyCurve.ts +203 -203
  83. package/src/Polyline.ts +316 -316
  84. package/src/Ray.ts +90 -90
  85. package/src/SculptSession.ts +377 -377
  86. package/src/engine-threaded.ts +39 -0
  87. package/src/engine.ts +46 -0
  88. package/src/index.ts +1 -1
  89. package/src/mesh-boolean.pool.ts +208 -86
  90. package/src/mesh-boolean.protocol.ts +44 -1
  91. package/src/mesh-boolean.worker.ts +170 -56
  92. package/src/wasm-active.ts +31 -0
  93. package/src/wasm-base64-threaded.ts +2 -0
  94. package/src/wasm-base64.ts +1 -1
  95. package/src/wasm-bindings-threaded.d.ts +1989 -0
  96. package/src/wasm-bindings-threaded.js +5235 -0
  97. package/src/wasm-bindings.d.ts +104 -0
  98. package/src/wasm-bindings.js +225 -0
  99. package/src/workerHelpers-threaded.js +107 -0
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Threaded engine loader (KERNEL-PERF-ARC lever 3).
3
+ *
4
+ * Loaded LAZILY (dynamic import from engine.ts) so the threaded wasm's
5
+ * inlined bytes are only fetched on pages that can actually use them
6
+ * (cross-origin isolated, SharedArrayBuffer available). Instantiates the
7
+ * rayon-enabled artifact, starts the worker thread pool, and swaps the
8
+ * active bindings module. Never call directly — use `initThreads()` from
9
+ * engine.ts, which enforces the boot-time ordering rules.
10
+ */
11
+ import { WASM_THREADED_B64 } from "./wasm-base64-threaded.js";
12
+ import { setActiveWasmModule } from "./wasm-active.js";
13
+ import threadedInit, { initThreadPool } from "./wasm-bindings-threaded.js";
14
+ import * as threadedModule from "./wasm-bindings-threaded.js";
15
+
16
+ export async function initThreadedEngine(threads?: number): Promise<boolean> {
17
+ if (!WASM_THREADED_B64) return false; // artifact absent at build time
18
+
19
+ const raw = atob(WASM_THREADED_B64);
20
+ const bytes = new Uint8Array(raw.length);
21
+ for (let i = 0; i < raw.length; i++) bytes[i] = raw.charCodeAt(i);
22
+
23
+ await threadedInit({ module_or_path: bytes });
24
+
25
+ // Default pool: compute-bound kernel loops saturate well before core
26
+ // count, and each worker holds a wasm instance — cap at 8.
27
+ const requested =
28
+ threads ??
29
+ Math.min(
30
+ 8,
31
+ typeof navigator !== "undefined" && Number.isFinite(navigator.hardwareConcurrency)
32
+ ? Math.max(1, Math.floor(navigator.hardwareConcurrency))
33
+ : 4,
34
+ );
35
+ await initThreadPool(requested);
36
+
37
+ setActiveWasmModule(threadedModule as unknown as Parameters<typeof setActiveWasmModule>[0]);
38
+ return true;
39
+ }
package/src/engine.ts CHANGED
@@ -31,3 +31,49 @@ export function ensureInit(): void {
31
31
  export function isInitialized(): boolean {
32
32
  return initialized;
33
33
  }
34
+
35
+ /**
36
+ * Initialize the THREADED engine (KERNEL-PERF-ARC lever 3): rayon worker
37
+ * pool over shared WebAssembly memory. Requires a cross-origin isolated
38
+ * page (COOP: same-origin + COEP; check `crossOriginIsolated`). Returns
39
+ * false — without touching engine state — when the environment cannot run
40
+ * threads or the threaded artifact is unavailable, so callers can fall
41
+ * back to `init()`.
42
+ *
43
+ * MUST be the first init call: stored meshes and sculpt sessions are
44
+ * per-wasm-instance state, so the module cannot be swapped after use.
45
+ */
46
+ export async function initThreads(options: { threads?: number } = {}): Promise<boolean> {
47
+ if (initialized) {
48
+ throw new Error(
49
+ "OkGeometry already initialized — initThreads() must run INSTEAD of init(), before any geometry use.",
50
+ );
51
+ }
52
+ const g = globalThis as typeof globalThis & {
53
+ crossOriginIsolated?: boolean;
54
+ SharedArrayBuffer?: unknown;
55
+ };
56
+ if (!g.crossOriginIsolated || typeof g.SharedArrayBuffer === "undefined") {
57
+ return false;
58
+ }
59
+ try {
60
+ const mod = await import("./engine-threaded.js");
61
+ const ok = await mod.initThreadedEngine(options.threads);
62
+ if (!ok) return false;
63
+ initialized = true;
64
+ return true;
65
+ } catch (error) {
66
+ console.warn("okgeometry threaded init failed; falling back to single-thread.", error);
67
+ return false;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Convenience boot: try the threaded engine, fall back to the standard
73
+ * single-thread engine. Returns which one is active.
74
+ */
75
+ export async function initAuto(options: { threads?: number } = {}): Promise<"threaded" | "standard"> {
76
+ if (await initThreads(options)) return "threaded";
77
+ await init();
78
+ return "standard";
79
+ }
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  // Core initialization
2
- export { init, isInitialized } from "./engine.js";
2
+ export { init, initThreads, initAuto, isInitialized } from "./engine.js";
3
3
 
4
4
  // Shared types and utilities
5
5
  export type {
@@ -1,36 +1,49 @@
1
- import {
2
- MeshBooleanExecutionError,
3
- type MeshBooleanAsyncOptions,
4
- type MeshBooleanErrorPayload,
5
- type MeshBooleanOperation,
6
- type MeshBooleanOptions,
7
- type MeshBooleanProgressEvent,
8
- type MeshBooleanWorkerRequest,
9
- type MeshBooleanWorkerResponse,
10
- } from "./mesh-boolean.protocol.js";
1
+ import {
2
+ MeshBooleanExecutionError,
3
+ type MeshBooleanAsyncOptions,
4
+ type MeshBooleanErrorPayload,
5
+ type MeshBooleanOperation,
6
+ type MeshBooleanOptions,
7
+ type MeshBooleanProgressEvent,
8
+ type MeshBooleanWorkerRequest,
9
+ type MeshBooleanWorkerResponse,
10
+ type MeshHeavyMeta,
11
+ type MeshHeavyOperation,
12
+ type MeshHeavyParams,
13
+ type MeshHeavyWorkerRequest,
14
+ } from "./mesh-boolean.protocol.js";
11
15
 
12
16
  interface PoolAcquireOptions {
13
17
  size?: number;
14
18
  workerFactory?: () => Worker;
15
19
  }
16
20
 
17
- interface BooleanJob {
18
- id: number;
19
- operation: MeshBooleanOperation;
20
- options: MeshBooleanOptions;
21
- onProgress?: (event: MeshBooleanProgressEvent) => void;
22
- signal?: AbortSignal;
23
- timeoutMs?: number;
24
- bufferA: ArrayBuffer;
21
+ interface BooleanJob {
22
+ id: number;
23
+ operation: MeshBooleanOperation | MeshHeavyOperation;
24
+ options: MeshBooleanOptions;
25
+ onProgress?: (event: MeshBooleanProgressEvent) => void;
26
+ signal?: AbortSignal;
27
+ timeoutMs?: number;
28
+ bufferA: ArrayBuffer;
25
29
  bufferB: ArrayBuffer;
26
30
  trustedInputA: boolean;
27
31
  trustedInputB: boolean;
32
+ /** Heavy multi-buffer job payload (KERNEL-PERF-ARC lever 4); when set,
33
+ * bufferA/bufferB are unused empty buffers. */
34
+ heavy?: {
35
+ buffers: ArrayBuffer[];
36
+ trusted?: boolean[];
37
+ params?: MeshHeavyParams;
38
+ };
39
+ /** Receives structured extras from heavy job responses before resolve. */
40
+ onMeta?: (meta: MeshHeavyMeta | undefined) => void;
28
41
  cancelView?: Int32Array;
29
- timeoutToken?: number;
30
- abortHandler?: () => void;
31
- resolve: (value: Float64Array) => void;
32
- reject: (reason: Error) => void;
33
- }
42
+ timeoutToken?: number;
43
+ abortHandler?: () => void;
44
+ resolve: (value: Float64Array) => void;
45
+ reject: (reason: Error) => void;
46
+ }
34
47
 
35
48
  interface WorkerSlot {
36
49
  id: number;
@@ -127,22 +140,22 @@ export class MeshBooleanWorkerPool {
127
140
  const job: BooleanJob = {
128
141
  id: jobId,
129
142
  operation,
130
- options: {
131
- allowUnsafe: options?.allowUnsafe ?? true,
132
- limits: options?.limits,
133
- debugForceFaceID: options?.debugForceFaceID,
134
- },
135
- onProgress: options?.onProgress,
136
- signal: options?.signal,
137
- timeoutMs: options?.timeoutMs,
138
- bufferA: new Float64Array(bufferA).buffer,
143
+ options: {
144
+ allowUnsafe: options?.allowUnsafe ?? true,
145
+ limits: options?.limits,
146
+ debugForceFaceID: options?.debugForceFaceID,
147
+ },
148
+ onProgress: options?.onProgress,
149
+ signal: options?.signal,
150
+ timeoutMs: options?.timeoutMs,
151
+ bufferA: new Float64Array(bufferA).buffer,
139
152
  bufferB: new Float64Array(bufferB).buffer,
140
- trustedInputA,
141
- trustedInputB,
142
- cancelView: createCancellationView(),
143
- resolve,
144
- reject,
145
- };
153
+ trustedInputA,
154
+ trustedInputB,
155
+ cancelView: createCancellationView(),
156
+ resolve,
157
+ reject,
158
+ };
146
159
 
147
160
  if (job.signal?.aborted) {
148
161
  this.rejectJob(job, rejectPayload("worker_aborted", `Boolean ${operation} aborted.`));
@@ -162,6 +175,78 @@ export class MeshBooleanWorkerPool {
162
175
  });
163
176
  }
164
177
 
178
+ /**
179
+ * Run a heavy multi-buffer kernel job (KERNEL-PERF-ARC lever 4) on the
180
+ * pool. Buffers are COPIED before transfer, so callers keep their meshes.
181
+ */
182
+ runHeavy(
183
+ operation: MeshHeavyOperation,
184
+ buffers: Float64Array[],
185
+ heavyOptions?: MeshBooleanAsyncOptions & {
186
+ trusted?: boolean[];
187
+ params?: MeshHeavyParams;
188
+ },
189
+ ): Promise<{ buffer: Float64Array; meta?: MeshHeavyMeta }> {
190
+ if (this.disposed) {
191
+ return Promise.reject(
192
+ new MeshBooleanExecutionError(
193
+ rejectPayload("worker_unavailable", "Boolean worker pool has been disposed."),
194
+ ),
195
+ );
196
+ }
197
+
198
+ this.ensureWorkers();
199
+ const jobId = this.nextJobId++;
200
+ const empty = new ArrayBuffer(0);
201
+
202
+ return new Promise((resolve, reject) => {
203
+ let meta: MeshHeavyMeta | undefined;
204
+ const job: BooleanJob = {
205
+ id: jobId,
206
+ operation,
207
+ options: {
208
+ allowUnsafe: heavyOptions?.allowUnsafe ?? true,
209
+ limits: heavyOptions?.limits,
210
+ debugForceFaceID: heavyOptions?.debugForceFaceID,
211
+ },
212
+ onProgress: heavyOptions?.onProgress,
213
+ signal: heavyOptions?.signal,
214
+ timeoutMs: heavyOptions?.timeoutMs,
215
+ bufferA: empty,
216
+ bufferB: empty,
217
+ trustedInputA: false,
218
+ trustedInputB: false,
219
+ heavy: {
220
+ buffers: buffers.map((b) => new Float64Array(b).buffer),
221
+ trusted: heavyOptions?.trusted,
222
+ params: heavyOptions?.params,
223
+ },
224
+ onMeta: (m) => {
225
+ meta = m;
226
+ },
227
+ cancelView: createCancellationView(),
228
+ resolve: (buffer) => resolve({ buffer, meta }),
229
+ reject,
230
+ };
231
+
232
+ if (job.signal?.aborted) {
233
+ this.rejectJob(job, rejectPayload("worker_aborted", `${operation} aborted.`));
234
+ return;
235
+ }
236
+
237
+ if (job.signal) {
238
+ job.abortHandler = () => {
239
+ this.cancelJob(job.id, "worker_aborted", `${job.operation} aborted.`);
240
+ };
241
+ job.signal.addEventListener("abort", job.abortHandler, { once: true });
242
+ }
243
+
244
+ this.queue.push(job);
245
+ this.emitProgress(job, "queued");
246
+ this.dispatch();
247
+ });
248
+ }
249
+
165
250
  dispose(): void {
166
251
  if (this.disposed) return;
167
252
  this.disposed = true;
@@ -239,9 +324,9 @@ export class MeshBooleanWorkerPool {
239
324
  continue;
240
325
  }
241
326
 
242
- slot.currentJobId = job.id;
243
- this.running.set(job.id, job);
244
- this.emitProgress(job, "running", slot.id);
327
+ slot.currentJobId = job.id;
328
+ this.running.set(job.id, job);
329
+ this.emitProgress(job, "running", slot.id);
245
330
 
246
331
  if (job.timeoutMs !== undefined && Number.isFinite(job.timeoutMs) && job.timeoutMs > 0) {
247
332
  job.timeoutToken = setTimeout(() => {
@@ -249,20 +334,37 @@ export class MeshBooleanWorkerPool {
249
334
  }, job.timeoutMs) as unknown as number;
250
335
  }
251
336
 
252
- const request: MeshBooleanWorkerRequest = {
253
- kind: "mesh-boolean-request",
254
- jobId: job.id,
255
- operation: job.operation,
256
- bufferA: job.bufferA,
257
- bufferB: job.bufferB,
258
- trustedInputA: job.trustedInputA,
259
- trustedInputB: job.trustedInputB,
260
- cancelBuffer: job.cancelView?.buffer as SharedArrayBuffer | undefined,
261
- options: job.options,
262
- };
337
+ let request: MeshBooleanWorkerRequest | MeshHeavyWorkerRequest;
338
+ let transfers: ArrayBuffer[];
339
+ if (job.heavy) {
340
+ request = {
341
+ kind: "mesh-heavy-request",
342
+ jobId: job.id,
343
+ operation: job.operation as MeshHeavyOperation,
344
+ buffers: job.heavy.buffers,
345
+ trusted: job.heavy.trusted,
346
+ params: job.heavy.params,
347
+ cancelBuffer: job.cancelView?.buffer as SharedArrayBuffer | undefined,
348
+ options: job.options,
349
+ };
350
+ transfers = job.heavy.buffers;
351
+ } else {
352
+ request = {
353
+ kind: "mesh-boolean-request",
354
+ jobId: job.id,
355
+ operation: job.operation as MeshBooleanOperation,
356
+ bufferA: job.bufferA,
357
+ bufferB: job.bufferB,
358
+ trustedInputA: job.trustedInputA,
359
+ trustedInputB: job.trustedInputB,
360
+ cancelBuffer: job.cancelView?.buffer as SharedArrayBuffer | undefined,
361
+ options: job.options,
362
+ };
363
+ transfers = [request.bufferA, request.bufferB];
364
+ }
263
365
 
264
366
  try {
265
- slot.worker.postMessage(request, [request.bufferA, request.bufferB]);
367
+ slot.worker.postMessage(request, transfers);
266
368
  } catch (error) {
267
369
  const details = error instanceof Error ? error.message : String(error);
268
370
  this.finishRunningJob(
@@ -300,18 +402,19 @@ export class MeshBooleanWorkerPool {
300
402
  return;
301
403
  }
302
404
 
303
- if (data.ok) {
304
- this.finishRunningJob(slot, job, undefined, new Float64Array(data.buffer));
305
- return;
306
- }
307
-
308
- this.finishRunningJob(
309
- slot,
310
- job,
311
- new MeshBooleanExecutionError(data.error),
312
- undefined,
313
- );
314
- }
405
+ if (data.ok) {
406
+ job.onMeta?.(data.meta);
407
+ this.finishRunningJob(slot, job, undefined, new Float64Array(data.buffer));
408
+ return;
409
+ }
410
+
411
+ this.finishRunningJob(
412
+ slot,
413
+ job,
414
+ new MeshBooleanExecutionError(data.error),
415
+ undefined,
416
+ );
417
+ }
315
418
 
316
419
  private failWorker(slot: WorkerSlot, payload: MeshBooleanErrorPayload): void {
317
420
  const jobId = slot.currentJobId;
@@ -371,18 +474,18 @@ export class MeshBooleanWorkerPool {
371
474
  this.dispatch();
372
475
  }
373
476
 
374
- private finishRunningJob(
375
- slot: WorkerSlot,
376
- job: BooleanJob,
377
- error: Error | undefined,
378
- result: Float64Array | undefined,
379
- ): void {
380
- this.running.delete(job.id);
381
- slot.currentJobId = null;
382
- this.cleanupJob(job);
383
-
384
- if (error) {
385
- job.reject(error);
477
+ private finishRunningJob(
478
+ slot: WorkerSlot,
479
+ job: BooleanJob,
480
+ error: Error | undefined,
481
+ result: Float64Array | undefined,
482
+ ): void {
483
+ this.running.delete(job.id);
484
+ slot.currentJobId = null;
485
+ this.cleanupJob(job);
486
+
487
+ if (error) {
488
+ job.reject(error);
386
489
  } else if (result) {
387
490
  job.resolve(result);
388
491
  } else {
@@ -410,15 +513,15 @@ export class MeshBooleanWorkerPool {
410
513
  }
411
514
 
412
515
  private emitProgress(job: BooleanJob, stage: MeshBooleanProgressEvent["stage"], workerId?: number): void {
413
- if (!job.onProgress) return;
414
- job.onProgress({
415
- stage,
416
- jobId: job.id,
417
- workerId,
418
- queueSize: this.queue.length,
419
- });
420
- }
421
- }
516
+ if (!job.onProgress) return;
517
+ job.onProgress({
518
+ stage,
519
+ jobId: job.id,
520
+ workerId,
521
+ queueSize: this.queue.length,
522
+ });
523
+ }
524
+ }
422
525
 
423
526
  let defaultPoolSizeOverride: number | undefined;
424
527
  const defaultPools = new Map<number, MeshBooleanWorkerPool>();
@@ -489,3 +592,22 @@ export function runMeshBooleanInWorkerPool(
489
592
  });
490
593
  return pool.run(operation, bufferA, bufferB, trustedInputA, trustedInputB, options);
491
594
  }
595
+
596
+ /**
597
+ * Run a heavy multi-buffer kernel job on the shared worker pool
598
+ * (KERNEL-PERF-ARC lever 4). See MeshHeavyWorkerRequest for buffer shapes.
599
+ */
600
+ export function runMeshHeavyInWorkerPool(
601
+ operation: MeshHeavyOperation,
602
+ buffers: Float64Array[],
603
+ options?: MeshBooleanAsyncOptions & {
604
+ trusted?: boolean[];
605
+ params?: MeshHeavyParams;
606
+ },
607
+ ): Promise<{ buffer: Float64Array; meta?: MeshHeavyMeta }> {
608
+ const pool = getMeshBooleanWorkerPool({
609
+ workerFactory: options?.workerFactory,
610
+ size: options?.poolSize,
611
+ });
612
+ return pool.runHeavy(operation, buffers, options);
613
+ }
@@ -90,13 +90,18 @@ export interface MeshBooleanWorkerCancel {
90
90
  jobId: number;
91
91
  }
92
92
 
93
- export type MeshBooleanWorkerInbound = MeshBooleanWorkerRequest | MeshBooleanWorkerCancel;
93
+ export type MeshBooleanWorkerInbound =
94
+ | MeshBooleanWorkerRequest
95
+ | MeshHeavyWorkerRequest
96
+ | MeshBooleanWorkerCancel;
94
97
 
95
98
  export interface MeshBooleanWorkerSuccessResponse {
96
99
  kind: "mesh-boolean-result";
97
100
  jobId: number;
98
101
  ok: true;
99
102
  buffer: ArrayBuffer;
103
+ /** Structured extras for heavy jobs (e.g. makeWatertight method). */
104
+ meta?: Record<string, string | number | boolean>;
100
105
  }
101
106
 
102
107
  export interface MeshBooleanWorkerErrorResponse {
@@ -119,3 +124,41 @@ export class MeshBooleanExecutionError extends Error {
119
124
  this.details = payload.details;
120
125
  }
121
126
  }
127
+
128
+ // ---- Heavy kernel jobs (KERNEL-PERF-ARC lever 4) ---------------------------
129
+ //
130
+ // Multi-second kernel operations beyond pairwise booleans run on the same
131
+ // worker pool: batch booleans, watertight repair, voxel remesh, solid edge
132
+ // chamfer. Buffers are op-specific:
133
+ // unionAll: [mesh0, mesh1, ...]
134
+ // differenceAll: [host, cutter0, cutter1, ...]
135
+ // makeWatertight: [mesh]
136
+ // voxelRemesh: [mesh]
137
+ // chamferEdges: [mesh, edgeSegments] (flat [ax,ay,az,bx,by,bz] per edge)
138
+
139
+ export type MeshHeavyOperation =
140
+ | "unionAll"
141
+ | "differenceAll"
142
+ | "makeWatertight"
143
+ | "voxelRemesh"
144
+ | "chamferEdges";
145
+
146
+ export interface MeshHeavyParams {
147
+ resolution?: number;
148
+ iso?: number;
149
+ distance?: number;
150
+ }
151
+
152
+ export interface MeshHeavyWorkerRequest {
153
+ kind: "mesh-heavy-request";
154
+ jobId: number;
155
+ operation: MeshHeavyOperation;
156
+ buffers: ArrayBuffer[];
157
+ trusted?: boolean[];
158
+ params?: MeshHeavyParams;
159
+ cancelBuffer?: SharedArrayBuffer;
160
+ options: MeshBooleanOptions;
161
+ }
162
+
163
+ /** Structured extras for heavy results (e.g. makeWatertight method). */
164
+ export type MeshHeavyMeta = Record<string, string | number | boolean>;