@selvajs/compute 3.1.0-beta.3 → 3.1.0-beta.5

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 (56) hide show
  1. package/dist/chunk-54O6BO24.js +4 -0
  2. package/dist/chunk-54O6BO24.js.map +1 -0
  3. package/dist/chunk-7KFBCOZM.cjs +2 -0
  4. package/dist/chunk-7KFBCOZM.cjs.map +1 -0
  5. package/dist/chunk-EZRBMUXT.cjs +62 -0
  6. package/dist/chunk-EZRBMUXT.cjs.map +1 -0
  7. package/dist/chunk-GF6ZIJWX.cjs +4 -0
  8. package/dist/chunk-GF6ZIJWX.cjs.map +1 -0
  9. package/dist/chunk-ORGJSKR4.js +62 -0
  10. package/dist/chunk-ORGJSKR4.js.map +1 -0
  11. package/dist/chunk-PJATLYMU.js +2 -0
  12. package/dist/chunk-PJATLYMU.js.map +1 -0
  13. package/dist/core.cjs +1 -1
  14. package/dist/core.d.cts +40 -3
  15. package/dist/core.d.ts +40 -3
  16. package/dist/core.js +1 -1
  17. package/dist/grasshopper.cjs +1 -1
  18. package/dist/grasshopper.cjs.map +1 -1
  19. package/dist/grasshopper.d.cts +220 -20
  20. package/dist/grasshopper.d.ts +220 -20
  21. package/dist/grasshopper.js +1 -1
  22. package/dist/{handle-files-9pkEgDIp.d.cts → handle-files-DMnx92gr.d.cts} +46 -10
  23. package/dist/{handle-files-9pkEgDIp.d.ts → handle-files-DMnx92gr.d.ts} +46 -10
  24. package/dist/index.cjs +1 -1
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +4 -4
  27. package/dist/index.d.ts +4 -4
  28. package/dist/index.js +1 -1
  29. package/dist/{types-V7G83j5m.d.cts → types-C9YpTbr3.d.cts} +27 -6
  30. package/dist/{types-V7G83j5m.d.ts → types-C9YpTbr3.d.ts} +27 -6
  31. package/dist/{types-B-JygDUF.d.ts → types-Ci8Z0-hF.d.cts} +92 -20
  32. package/dist/{types-DGeAQnkB.d.cts → types-DuOh5XqS.d.ts} +92 -20
  33. package/dist/visualization-C3YOGQTW.js +2 -0
  34. package/dist/visualization-CERJ6XDX.cjs +2 -0
  35. package/dist/visualization-CERJ6XDX.cjs.map +1 -0
  36. package/dist/visualization.cjs +1 -1
  37. package/dist/visualization.d.cts +104 -13
  38. package/dist/visualization.d.ts +104 -13
  39. package/dist/visualization.js +1 -1
  40. package/package.json +1 -1
  41. package/dist/chunk-CTZ4ZOSM.js +0 -2
  42. package/dist/chunk-CTZ4ZOSM.js.map +0 -1
  43. package/dist/chunk-EUMIGWRX.cjs +0 -4
  44. package/dist/chunk-EUMIGWRX.cjs.map +0 -1
  45. package/dist/chunk-HNG75VAI.js +0 -55
  46. package/dist/chunk-HNG75VAI.js.map +0 -1
  47. package/dist/chunk-JIE3W4KP.cjs +0 -2
  48. package/dist/chunk-JIE3W4KP.cjs.map +0 -1
  49. package/dist/chunk-L3CNT26S.cjs +0 -55
  50. package/dist/chunk-L3CNT26S.cjs.map +0 -1
  51. package/dist/chunk-QAOHGIFT.js +0 -4
  52. package/dist/chunk-QAOHGIFT.js.map +0 -1
  53. package/dist/visualization-SI2GEY4M.cjs +0 -2
  54. package/dist/visualization-SI2GEY4M.cjs.map +0 -1
  55. package/dist/visualization-UJZN4PJD.js +0 -2
  56. /package/dist/{visualization-UJZN4PJD.js.map → visualization-C3YOGQTW.js.map} +0 -0
package/dist/core.d.cts CHANGED
@@ -1,6 +1,6 @@
1
- import { C as ComputeConfig } from './types-V7G83j5m.cjs';
2
- export { R as RetryPolicy, a as RhinoModelUnit, S as ServerTiming } from './types-V7G83j5m.cjs';
3
- export { C as ComputeServerStats, E as ErrorCode, a as ErrorCodes, F as FileBaseInfo, b as FileData, P as ProcessedFile, R as RhinoComputeError, d as downloadFileData, e as extractFilesFromComputeResponse } from './handle-files-9pkEgDIp.cjs';
1
+ import { C as ComputeConfig } from './types-C9YpTbr3.cjs';
2
+ export { R as RetryPolicy, a as RhinoModelUnit, S as ServerTiming } from './types-C9YpTbr3.cjs';
3
+ export { C as ComputeServerStats, E as ErrorCode, a as ErrorCodes, F as FileBaseInfo, b as FileData, P as ProcessedFile, R as RhinoComputeError, d as downloadFileData, e as extractFilesFromComputeResponse } from './handle-files-DMnx92gr.cjs';
4
4
 
5
5
  /**
6
6
  * Generic Rhino Compute fetch function.
@@ -12,6 +12,20 @@ export { C as ComputeServerStats, E as ErrorCode, a as ErrorCodes, F as FileBase
12
12
  * given endpoint returns. Callers supply the response type via `R` (defaulting
13
13
  * to `unknown`, which forces an explicit narrowing before use).
14
14
  *
15
+ * Timeout semantics: `config.timeoutMs` is a PER-ATTEMPT timeout, re-armed for
16
+ * every retry. With `retry: { attempts: N }` the worst-case wall clock is
17
+ * `(N + 1) × timeoutMs` plus backoff sleeps (and a server `Retry-After` can
18
+ * stretch a single sleep up to 60s). For a hard overall deadline, pass
19
+ * `config.signal` (e.g. `AbortSignal.timeout(totalMs)`) — a caller abort wins
20
+ * immediately, including during backoff.
21
+ *
22
+ * Retry caveats: requests are POSTs, so a retry after a mid-flight connection
23
+ * loss may re-execute a request the server already ran (see {@link RetryPolicy}).
24
+ * A 2xx response that declares a non-JSON `Content-Type` (captive portal,
25
+ * proxy login page) fails immediately with `INVALID_RESPONSE` and is never
26
+ * retried; a body that fails to parse under a JSON content-type is treated as
27
+ * a truncated stream and is retried.
28
+ *
15
29
  * @typeParam R - The expected response shape. The caller names it at the call site.
16
30
  * @param endpoint - The Compute API endpoint (e.g., 'grasshopper', 'io', 'mesh').
17
31
  * @param args - Pre-prepared arguments for the request body.
@@ -97,6 +111,19 @@ declare function enableDebugLogging(): void;
97
111
 
98
112
  /**
99
113
  * Converts a string to camelCase.
114
+ *
115
+ * Handling of the awkward shapes (fixed 2026-07-12 — issue 109; behavior
116
+ * change from the earlier version, which only lowercased the first character
117
+ * and kept leading separators):
118
+ *
119
+ * - Leading/trailing separators are stripped: `'_foo'` → `'foo'`,
120
+ * `'__proto__'` → `'proto'` (the output can never contain `-`/`_`, so a
121
+ * camelCased key can never be `'__proto__'` — proto pollution via
122
+ * {@link camelcaseKeys} stays impossible).
123
+ * - Leading acronym runs are lowercased as a unit: `'URLPath'` → `'urlPath'`,
124
+ * `'IDNumber'` → `'idNumber'`, `'URL'` → `'url'` (previously `'uRLPath'`,
125
+ * `'iDNumber'`, `'uRL'`).
126
+ *
100
127
  * @param str - The string to convert
101
128
  * @param options - Options object
102
129
  * - preserveSpaces: If true, spaces are preserved (default: false)
@@ -107,6 +134,16 @@ declare function toCamelCase(str: string, options?: {
107
134
  /**
108
135
  * Recursively converts all object keys to camelCase.
109
136
  *
137
+ * Only PLAIN objects (prototype `Object.prototype`/`null`) and arrays are
138
+ * traversed; any other object — `Date`, `Map`, `Set`, `Uint8Array`/typed
139
+ * arrays, class instances, … — is passed through untouched (previously deep
140
+ * mode reduced a `Date` to `{}` and a `Uint8Array` to `{0: …, 1: …}`).
141
+ *
142
+ * When two keys camelCase to the same name (`inner_tree` + `innerTree`), the
143
+ * LAST one in key order wins (matching plain object-spread semantics) and a
144
+ * warning is logged naming both keys — the earlier value is silently
145
+ * unreachable otherwise.
146
+ *
110
147
  * @warning Do not use `deep: true` on Rhino Compute wire payloads: it
111
148
  * corrupts user-authored keys (value-list labels, item `data` JSON). Read the
112
149
  * specific fields you need case-insensitively with `readField`/`hasField` from
package/dist/core.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { C as ComputeConfig } from './types-V7G83j5m.js';
2
- export { R as RetryPolicy, a as RhinoModelUnit, S as ServerTiming } from './types-V7G83j5m.js';
3
- export { C as ComputeServerStats, E as ErrorCode, a as ErrorCodes, F as FileBaseInfo, b as FileData, P as ProcessedFile, R as RhinoComputeError, d as downloadFileData, e as extractFilesFromComputeResponse } from './handle-files-9pkEgDIp.js';
1
+ import { C as ComputeConfig } from './types-C9YpTbr3.js';
2
+ export { R as RetryPolicy, a as RhinoModelUnit, S as ServerTiming } from './types-C9YpTbr3.js';
3
+ export { C as ComputeServerStats, E as ErrorCode, a as ErrorCodes, F as FileBaseInfo, b as FileData, P as ProcessedFile, R as RhinoComputeError, d as downloadFileData, e as extractFilesFromComputeResponse } from './handle-files-DMnx92gr.js';
4
4
 
5
5
  /**
6
6
  * Generic Rhino Compute fetch function.
@@ -12,6 +12,20 @@ export { C as ComputeServerStats, E as ErrorCode, a as ErrorCodes, F as FileBase
12
12
  * given endpoint returns. Callers supply the response type via `R` (defaulting
13
13
  * to `unknown`, which forces an explicit narrowing before use).
14
14
  *
15
+ * Timeout semantics: `config.timeoutMs` is a PER-ATTEMPT timeout, re-armed for
16
+ * every retry. With `retry: { attempts: N }` the worst-case wall clock is
17
+ * `(N + 1) × timeoutMs` plus backoff sleeps (and a server `Retry-After` can
18
+ * stretch a single sleep up to 60s). For a hard overall deadline, pass
19
+ * `config.signal` (e.g. `AbortSignal.timeout(totalMs)`) — a caller abort wins
20
+ * immediately, including during backoff.
21
+ *
22
+ * Retry caveats: requests are POSTs, so a retry after a mid-flight connection
23
+ * loss may re-execute a request the server already ran (see {@link RetryPolicy}).
24
+ * A 2xx response that declares a non-JSON `Content-Type` (captive portal,
25
+ * proxy login page) fails immediately with `INVALID_RESPONSE` and is never
26
+ * retried; a body that fails to parse under a JSON content-type is treated as
27
+ * a truncated stream and is retried.
28
+ *
15
29
  * @typeParam R - The expected response shape. The caller names it at the call site.
16
30
  * @param endpoint - The Compute API endpoint (e.g., 'grasshopper', 'io', 'mesh').
17
31
  * @param args - Pre-prepared arguments for the request body.
@@ -97,6 +111,19 @@ declare function enableDebugLogging(): void;
97
111
 
98
112
  /**
99
113
  * Converts a string to camelCase.
114
+ *
115
+ * Handling of the awkward shapes (fixed 2026-07-12 — issue 109; behavior
116
+ * change from the earlier version, which only lowercased the first character
117
+ * and kept leading separators):
118
+ *
119
+ * - Leading/trailing separators are stripped: `'_foo'` → `'foo'`,
120
+ * `'__proto__'` → `'proto'` (the output can never contain `-`/`_`, so a
121
+ * camelCased key can never be `'__proto__'` — proto pollution via
122
+ * {@link camelcaseKeys} stays impossible).
123
+ * - Leading acronym runs are lowercased as a unit: `'URLPath'` → `'urlPath'`,
124
+ * `'IDNumber'` → `'idNumber'`, `'URL'` → `'url'` (previously `'uRLPath'`,
125
+ * `'iDNumber'`, `'uRL'`).
126
+ *
100
127
  * @param str - The string to convert
101
128
  * @param options - Options object
102
129
  * - preserveSpaces: If true, spaces are preserved (default: false)
@@ -107,6 +134,16 @@ declare function toCamelCase(str: string, options?: {
107
134
  /**
108
135
  * Recursively converts all object keys to camelCase.
109
136
  *
137
+ * Only PLAIN objects (prototype `Object.prototype`/`null`) and arrays are
138
+ * traversed; any other object — `Date`, `Map`, `Set`, `Uint8Array`/typed
139
+ * arrays, class instances, … — is passed through untouched (previously deep
140
+ * mode reduced a `Date` to `{}` and a `Uint8Array` to `{0: …, 1: …}`).
141
+ *
142
+ * When two keys camelCase to the same name (`inner_tree` + `innerTree`), the
143
+ * LAST one in key order wins (matching plain object-spread semantics) and a
144
+ * warning is logged naming both keys — the earlier value is silently
145
+ * unreachable otherwise.
146
+ *
110
147
  * @warning Do not use `deep: true` on Rhino Compute wire payloads: it
111
148
  * corrupts user-authored keys (value-list labels, item `data` JSON). Read the
112
149
  * specific fields you need case-insensitively with `readField`/`hasField` from
package/dist/core.js CHANGED
@@ -1,2 +1,2 @@
1
- import{c as a,d as b,e as c,f as d,g as e,l as f,n as g,o as h,p as i,q as j,r as k}from"./chunk-QAOHGIFT.js";export{g as ComputeServerStats,a as ErrorCodes,b as RhinoComputeError,i as camelcaseKeys,k as downloadFileData,e as enableDebugLogging,j as extractFilesFromComputeResponse,f as fetchRhinoCompute,c as getLogger,d as setLogger,h as toCamelCase};
1
+ import{c as a,d as b,e as c,f as d,g as e,l as f,n as g,o as h,p as i,s as j,t as k}from"./chunk-54O6BO24.js";export{g as ComputeServerStats,a as ErrorCodes,b as RhinoComputeError,i as camelcaseKeys,k as downloadFileData,e as enableDebugLogging,j as extractFilesFromComputeResponse,f as fetchRhinoCompute,c as getLogger,d as setLogger,h as toCamelCase};
2
2
  //# sourceMappingURL=core.js.map
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunkJIE3W4KPcjs = require('./chunk-JIE3W4KP.cjs');var _chunkEUMIGWRXcjs = require('./chunk-EUMIGWRX.cjs');exports.GrasshopperClient = _chunkJIE3W4KPcjs.c; exports.GrasshopperResponseProcessor = _chunkJIE3W4KPcjs.d; exports.RhinoComputeError = _chunkEUMIGWRXcjs.d; exports.SolveScheduler = _chunkJIE3W4KPcjs.b; exports.TreeBuilder = _chunkJIE3W4KPcjs.i; exports.downloadFileData = _chunkEUMIGWRXcjs.r; exports.extractFilesFromComputeResponse = _chunkEUMIGWRXcjs.q; exports.fetchDefinitionIO = _chunkJIE3W4KPcjs.g; exports.fetchParsedDefinitionIO = _chunkJIE3W4KPcjs.h; exports.processInput = _chunkJIE3W4KPcjs.e; exports.processInputs = _chunkJIE3W4KPcjs.f; exports.solveGrasshopperDefinition = _chunkJIE3W4KPcjs.a;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunk7KFBCOZMcjs = require('./chunk-7KFBCOZM.cjs');var _chunkGF6ZIJWXcjs = require('./chunk-GF6ZIJWX.cjs');exports.GrasshopperClient = _chunk7KFBCOZMcjs.g; exports.GrasshopperResponseProcessor = _chunk7KFBCOZMcjs.h; exports.RhinoComputeError = _chunkGF6ZIJWXcjs.d; exports.SolveScheduler = _chunk7KFBCOZMcjs.f; exports.TreeBuilder = _chunk7KFBCOZMcjs.m; exports.downloadFileData = _chunkGF6ZIJWXcjs.t; exports.extractFilesFromComputeResponse = _chunkGF6ZIJWXcjs.s; exports.fetchDefinitionIO = _chunk7KFBCOZMcjs.k; exports.fetchParsedDefinitionIO = _chunk7KFBCOZMcjs.l; exports.hashDefinition = _chunk7KFBCOZMcjs.e; exports.hashSolveInput = _chunk7KFBCOZMcjs.d; exports.isDefinitionRef = _chunk7KFBCOZMcjs.a; exports.processInput = _chunk7KFBCOZMcjs.i; exports.processInputs = _chunk7KFBCOZMcjs.j; exports.solveGrasshopperDefinition = _chunk7KFBCOZMcjs.b; exports.stableStringify = _chunk7KFBCOZMcjs.c;
2
2
  //# sourceMappingURL=grasshopper.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/selva-compute/selva-compute/dist/grasshopper.cjs"],"names":[],"mappings":"AAAA,iIAAgG,wDAAuD,gmBAAiT","file":"/home/runner/work/selva-compute/selva-compute/dist/grasshopper.cjs"}
1
+ {"version":3,"sources":["/home/runner/work/selva-compute/selva-compute/dist/grasshopper.cjs"],"names":[],"mappings":"AAAA,iIAA4H,wDAAuD,0xBAAmY","file":"/home/runner/work/selva-compute/selva-compute/dist/grasshopper.cjs"}
@@ -1,12 +1,56 @@
1
- import { R as RhinoComputeError, C as ComputeServerStats, F as FileBaseInfo } from './handle-files-9pkEgDIp.cjs';
2
- export { b as FileData, P as ProcessedFile, d as downloadFileData, e as extractFilesFromComputeResponse } from './handle-files-9pkEgDIp.cjs';
3
- import { R as RetryPolicy, C as ComputeConfig } from './types-V7G83j5m.cjs';
4
- export { a as RhinoModelUnit } from './types-V7G83j5m.cjs';
5
- import { f as GrasshopperComputeResponse, a as DataTree, e as GrasshopperComputeConfig, g as GrasshopperParsedIO, h as GrasshopperParsedIORaw, M as MeshExtractionOptions, k as InputParamSchema, j as InputParam, b as DataTreeDefault, c as DataTreePath } from './types-DGeAQnkB.cjs';
6
- export { B as BooleanInputType, D as DataItem, d as DefaultValue, F as FileInputType, G as GeometryInputType, i as GrasshopperRequestSchema, I as InnerTreeData, N as NumericInputType, O as OutputParamSchema, l as OutputType, T as TextInputType, V as ValueListInputType } from './types-DGeAQnkB.cjs';
1
+ import { R as RhinoComputeError, C as ComputeServerStats, F as FileBaseInfo } from './handle-files-DMnx92gr.cjs';
2
+ export { b as FileData, P as ProcessedFile, d as downloadFileData, e as extractFilesFromComputeResponse } from './handle-files-DMnx92gr.cjs';
3
+ import { R as RetryPolicy, C as ComputeConfig } from './types-C9YpTbr3.cjs';
4
+ export { a as RhinoModelUnit } from './types-C9YpTbr3.cjs';
5
+ import { f as GrasshopperComputeResponse, a as DataTree, e as GrasshopperComputeConfig, g as GrasshopperParsedIO, h as GrasshopperParsedIORaw, m as MeshExtractionOptions, k as InputParamSchema, j as InputParam, b as DataTreeDefault, c as DataTreePath } from './types-Ci8Z0-hF.cjs';
6
+ export { B as BooleanInputType, D as DataItem, d as DefaultValue, F as FileInputType, G as GeometryInputType, i as GrasshopperRequestSchema, I as InnerTreeData, N as NumericInputType, O as OutputParamSchema, l as OutputType, T as TextInputType, V as ValueListInputType } from './types-Ci8Z0-hF.cjs';
7
7
  import * as THREE from 'three';
8
8
  import 'rhino3dm';
9
9
 
10
+ /**
11
+ * By-reference definition form for solves.
12
+ *
13
+ * Lets a caller that already knows a definition's identity (e.g. a stored
14
+ * version's UUID) schedule solves without materializing the multi-MB bytes:
15
+ * cache keys and the server-pointer map are derived from `key` alone, and
16
+ * `load()` is only called when an upload is genuinely unavoidable (first solve
17
+ * of a definition, or a server-side pointer miss).
18
+ */
19
+ /**
20
+ * A definition identified by a stable key, with bytes materialized on demand.
21
+ *
22
+ * **Immutability contract — read this before constructing one.** `key` must
23
+ * identify IMMUTABLE bytes: every `load()` for a given `key` must return the
24
+ * same content, forever. All caching (the scheduler's result cache, the
25
+ * server-pointer map, and any durable cache built on these keys) trusts the
26
+ * key as the definition's identity WITHOUT looking at the bytes. If two
27
+ * different byte contents ever share a key, cached solves from one are served
28
+ * for the other — silent cache poisoning with no diagnostic. Use an identity
29
+ * that can never be reused for different content (e.g. a version UUID), never
30
+ * a mutable name or path.
31
+ */
32
+ interface DefinitionRef {
33
+ /**
34
+ * Identity of immutable bytes (e.g. a version UUID). Two different byte
35
+ * contents must never share a key — cache poisoning otherwise.
36
+ */
37
+ key: string;
38
+ /**
39
+ * Materialize the bytes. Called ONLY when an upload is unavoidable — inside
40
+ * the solve execution, so it counts toward the solve's abort semantics.
41
+ */
42
+ load: () => Promise<Uint8Array>;
43
+ }
44
+ /**
45
+ * Every definition form accepted by the solve entry points:
46
+ * - a URL, base64 string, or plain string (content-hashed for caching)
47
+ * - raw `.gh` bytes (content-hashed)
48
+ * - a {@link DefinitionRef} (identity-keyed; bytes loaded lazily)
49
+ */
50
+ type SolveDefinition = string | Uint8Array | DefinitionRef;
51
+ /** Narrow a {@link SolveDefinition} to the by-reference form. */
52
+ declare function isDefinitionRef(definition: SolveDefinition): definition is DefinitionRef;
53
+
10
54
  /**
11
55
  * Scheduling mode — controls how concurrent `solve()` calls interact.
12
56
  *
@@ -23,10 +67,36 @@ interface CacheOptions {
23
67
  maxEntries?: number;
24
68
  /** Time-to-live in ms. Set to `0` for no expiry (default). */
25
69
  ttlMs?: number;
70
+ /**
71
+ * Cache responses that carry Grasshopper `errors`. Default `true`: an
72
+ * errored solve is a valid, deterministic result — definitions raise GH
73
+ * errors by design (guarded components, validation branches), so replaying
74
+ * one from cache is correct. Set `false` for parity with Rhino's opt-in
75
+ * `cacheerroredsolves` server flag, e.g. when a definition's errors are
76
+ * transient (external data sources) rather than functions of the inputs.
77
+ */
78
+ cacheErroredSolves?: boolean;
26
79
  }
27
80
  interface SolveSchedulerOptions {
28
81
  mode?: SchedulerMode;
29
82
  maxConcurrent?: number;
83
+ /**
84
+ * Backpressure — cap on how many calls may wait in the FIFO queue (i.e.
85
+ * excluding the ones already in flight). When the queue is full, a new
86
+ * `solve()` is shed immediately with `code: QUEUE_FULL` (retryable, meant to
87
+ * map to HTTP 503 + Retry-After) instead of piling up unbounded. Bounds the
88
+ * miss path under load. Only applies to `queue` / `parallel` modes —
89
+ * `latest-wins` has an intrinsic depth of 1. Default: unbounded.
90
+ */
91
+ maxQueueDepth?: number;
92
+ /**
93
+ * Backpressure — max time (ms) a call may sit queued before it starts
94
+ * executing. If it's still waiting after this long it's shed with
95
+ * `code: QUEUE_TIMEOUT` rather than burning compute on a stale request.
96
+ * Bounds tail latency. Only applies to `queue` / `parallel` modes. Default:
97
+ * no deadline.
98
+ */
99
+ queueWaitMs?: number;
30
100
  timeoutMs?: number;
31
101
  retry?: RetryPolicy;
32
102
  /** Enable response caching keyed by hash of (definition, dataTree). */
@@ -81,7 +151,7 @@ type SolveResult = {
81
151
  * Adapter for the underlying solve function. Lets the scheduler be tested
82
152
  * without a real Compute server, and decouples it from the client class.
83
153
  */
84
- type SolveExecutor = (definition: string | Uint8Array, dataTree: DataTree[], config: GrasshopperComputeConfig) => Promise<GrasshopperComputeResponse>;
154
+ type SolveExecutor = (definition: SolveDefinition, dataTree: DataTree[], config: GrasshopperComputeConfig) => Promise<GrasshopperComputeResponse>;
85
155
  /**
86
156
  * Cache-key-aware executor. When `cacheKey` is provided, the executor solves by
87
157
  * reference (`pointer: cacheKey`) and falls back to a full upload on a server
@@ -93,7 +163,7 @@ type SolveExecutor = (definition: string | Uint8Array, dataTree: DataTree[], con
93
163
  * Supplied by the client (which owns the solve primitives); the scheduler stays
94
164
  * decoupled from the transport.
95
165
  */
96
- type CacheKeyExecutor = (definition: string | Uint8Array, dataTree: DataTree[], cacheKey: string | null, config: GrasshopperComputeConfig) => Promise<{
166
+ type CacheKeyExecutor = (definition: SolveDefinition, dataTree: DataTree[], cacheKey: string | null, config: GrasshopperComputeConfig) => Promise<{
97
167
  response: GrasshopperComputeResponse;
98
168
  cacheKey: string | null;
99
169
  missed: boolean;
@@ -104,6 +174,7 @@ type CacheKeyExecutor = (definition: string | Uint8Array, dataTree: DataTree[],
104
174
  * Sits between your application code and the underlying compute call,
105
175
  * adding:
106
176
  * - Configurable scheduling (latest-wins for sliders, queue for jobs)
177
+ * - Backpressure (bounded queue depth + queue-wait deadline) for the miss path
107
178
  * - In-flight cancellation (per-call signal + cancelAll)
108
179
  * - Optional response caching for repeated inputs
109
180
  * - Lifecycle hooks for UI indicators (start / settle / superseded)
@@ -134,11 +205,14 @@ declare class SolveScheduler {
134
205
  private readonly baseConfig;
135
206
  private readonly mode;
136
207
  private readonly maxConcurrent;
208
+ private readonly maxQueueDepth;
209
+ private readonly queueWaitMs;
137
210
  private readonly timeoutMs;
138
211
  private readonly retry;
139
212
  private readonly cacheEnabled;
140
213
  private readonly cacheMax;
141
214
  private readonly cacheTtl;
215
+ private readonly cacheErroredSolves;
142
216
  private readonly cache;
143
217
  /** Optional cache-key-aware executor and whether server-def-cache reuse is on. */
144
218
  private readonly cacheKeyExecutor?;
@@ -155,6 +229,10 @@ declare class SolveScheduler {
155
229
  private _lastResult;
156
230
  private _lastError;
157
231
  private _lastDurationMs;
232
+ /** Ordinal handed to each solve() call. */
233
+ private solveSeq;
234
+ /** seq of the solve that last wrote _lastResult/_lastError — see writeLastState. */
235
+ private lastStateSeq;
158
236
  private disposed;
159
237
  constructor(executor: SolveExecutor, baseConfig: GrasshopperComputeConfig, options?: SolveSchedulerOptions, cacheKeyExecutor?: CacheKeyExecutor);
160
238
  get isSolving(): boolean;
@@ -174,18 +252,59 @@ declare class SolveScheduler {
174
252
  * newer values arrived (latest-wins mode).
175
253
  * - Rejects with `code: ErrorCodes.ABORTED` when the call was canceled via
176
254
  * caller-supplied signal or `cancelAll()`.
255
+ * - Rejects with `code: ErrorCodes.QUEUE_FULL` when `maxQueueDepth` is set and
256
+ * the queue was already full (backpressure; `statusCode: 503`).
257
+ * - Rejects with `code: ErrorCodes.QUEUE_TIMEOUT` when `queueWaitMs` is set and
258
+ * the call sat queued longer than that before starting (`statusCode: 503`).
259
+ *
260
+ * Caller-supplied `signal` cancels just this call (rejects with `ABORTED`) —
261
+ * including while the call is still queued, before execution starts.
177
262
  *
178
- * Caller-supplied `signal` cancels just this call (rejects with `ABORTED`).
263
+ * A {@link DefinitionRef} definition is keyed by its `key` (result cache and
264
+ * server-pointer map alike) without materializing bytes — `load()` runs only
265
+ * when an upload is unavoidable. Its immutability contract is trusted here:
266
+ * a reused key serves the other content's cached solve.
267
+ *
268
+ * Responses served from the cache (and via `lastResult`) are shared objects,
269
+ * not copies — treat them as immutable. Mutating one poisons every later
270
+ * cache hit for that key.
179
271
  */
180
- solve(definition: string | Uint8Array, dataTree: DataTree[], options?: {
272
+ solve(definition: SolveDefinition, dataTree: DataTree[], options?: {
181
273
  signal?: AbortSignal;
182
274
  }): Promise<GrasshopperComputeResponse>;
275
+ /**
276
+ * Record last-result state, but only if no newer solve has written since —
277
+ * a slow solve settling late must not overwrite the state a newer solve
278
+ * (or cache hit) already published.
279
+ */
280
+ private writeLastState;
281
+ /** latest-wins: supersede the pending item and abort everything in flight. */
282
+ private supersedeCurrent;
283
+ /** Settle a still-queued item as ABORTED and remove it from its queue. */
284
+ private abortQueuedItem;
285
+ /**
286
+ * Backpressure: settle an incoming item as QUEUE_FULL without ever enqueuing
287
+ * it. The structured context lets an HTTP layer map it to 503 + Retry-After.
288
+ */
289
+ private shedAsQueueFull;
290
+ /**
291
+ * Arm the queue-wait deadline for an item about to be queued. If the item is
292
+ * still waiting when it fires, it's shed as QUEUE_TIMEOUT and removed from the
293
+ * queue. Cleared on execute/settle via {@link clearQueueWaitTimer}.
294
+ */
295
+ private armQueueWaitTimer;
296
+ /** Clear a queued item's wait-deadline timer, if one is pending. */
297
+ private clearQueueWaitTimer;
183
298
  private enqueue;
184
299
  private execute;
185
300
  /**
186
301
  * Run the solve, using the server-definition-cache fast path when it's
187
302
  * enabled and the definition is reusable. Learns/updates the definition's
188
303
  * server cache key from the result so later solves can reference it.
304
+ *
305
+ * `definitionHash` is the {@link hashDefinition} result already computed at
306
+ * `solve()` entry — threaded through rather than recomputed, so each solve
307
+ * pays exactly one linear pass over the definition (issue 57).
189
308
  */
190
309
  private runExecutor;
191
310
  private drainNext;
@@ -212,6 +331,8 @@ declare class SolveScheduler {
212
331
  * @returns `true` if this call settled the item; `false` if it was already settled.
213
332
  */
214
333
  private settleSuccess;
334
+ /** Detach the queued-phase abort listener, if one is still attached. */
335
+ private removeQueuedAbortHandler;
215
336
  private isAbortLikeError;
216
337
  private normalizeExecutionError;
217
338
  /** Cancel everything — in-flight and pending. */
@@ -295,9 +416,14 @@ declare class GrasshopperClient {
295
416
  *
296
417
  * @throws {RhinoComputeError} with code INVALID_INPUT if definition is empty
297
418
  * @throws {RhinoComputeError} with code NETWORK_ERROR if server is offline
298
- * @throws {RhinoComputeError} with code COMPUTATION_ERROR if computation fails
419
+ * @throws {RhinoComputeError} with code COMPUTATION_ERROR if computation fails.
420
+ * On a partial-success response (some outputs computed, some errored) the
421
+ * error's `context.values` carries the outputs that did compute — pass
422
+ * `{ values } as GrasshopperComputeResponse` to the response processors to
423
+ * render them. `context.inputSummary` describes the inputs (param names,
424
+ * item counts, byte sizes) without pinning the full data tree.
299
425
  */
300
- solve(definition: string | Uint8Array, dataTree: DataTree[], options?: SolveOptions): Promise<GrasshopperComputeResponse>;
426
+ solve(definition: SolveDefinition, dataTree: DataTree[], options?: SolveOptions): Promise<GrasshopperComputeResponse>;
301
427
  /**
302
428
  * Create a scheduler bound to this client. Use a scheduler for any UI surface
303
429
  * that fires solves frequently (sliders, live editors) or that needs cancel
@@ -344,6 +470,14 @@ interface GetValuesOptions {
344
470
  }
345
471
  interface GetValuesResult<T = ParsedContext> {
346
472
  values: T;
473
+ /**
474
+ * Free every rhino3dm WASM object decoded into `values`. When a `rhino`
475
+ * instance was passed, decoded geometry lives on the WASM heap and is never
476
+ * garbage-collected — call this once the values are consumed, or the heap
477
+ * grows monotonically across solves. Idempotent; a no-op when nothing was
478
+ * decoded. `values` must not be used after disposal.
479
+ */
480
+ dispose: () => void;
347
481
  }
348
482
 
349
483
  /**
@@ -443,6 +577,57 @@ declare class GrasshopperResponseProcessor {
443
577
  getAndDownloadFiles(folderName: string, additionalFiles?: FileBaseInfo[] | FileBaseInfo | null): Promise<void>;
444
578
  }
445
579
 
580
+ /**
581
+ * Stable hashing for solve deduplication and caching.
582
+ *
583
+ * The public keying surface here — {@link stableStringify}, {@link hashDefinition},
584
+ * {@link hashSolveInput} — is exported from the package barrel so a durable
585
+ * (app-layer) cache or a pre-solved bundle viewer can reproduce the *exact*
586
+ * canonicalize-and-hash path the scheduler's in-process cache uses. Key parity
587
+ * across the two layers is a correctness requirement: a durable-cache key that
588
+ * canonicalizes even slightly differently would miss every entry the scheduler
589
+ * wrote (harmless) or, worse, collide differently (not). The FNV plumbing
590
+ * (`fnv1a`/`fnv1aBytes`) and the pre-hashed-definition fast path
591
+ * (`hashSolveInputForDefinition`) stay internal.
592
+ */
593
+
594
+ /**
595
+ * Deterministic stringify with sorted keys. {a:1,b:2} and {b:2,a:1} produce
596
+ * the same string. Safely handles circular references and non-finite numbers.
597
+ *
598
+ * The output is a cache key, so the invariant that matters is: two payloads
599
+ * that serialize differently on the wire must stringify differently here
600
+ * (false misses are harmless; false hits serve the wrong cached solve).
601
+ */
602
+ declare function stableStringify(value: unknown): string;
603
+ /**
604
+ * Hash definition and data tree into a stable cache key.
605
+ *
606
+ * The definition is the *identity* of what we solve, so a binary definition is
607
+ * hashed over its full content (`fnv1aBytes`) — a length-only or sampled key
608
+ * would let two different `.gh` files collide and serve one's cached solve for
609
+ * the other. `.gh` files are small enough that a single linear pass is
610
+ * negligible. A {@link DefinitionRef} is keyed by its `key` alone — the
611
+ * caller-declared identity of immutable bytes — so no bytes are materialized
612
+ * or hashed at all.
613
+ *
614
+ * The key keeps the definition and tree hashes as separate parts rather than
615
+ * collapsing them into one 32-bit hash: a single FNV pass over the pair would
616
+ * birthday-collide quadratically in cache size, while requiring both 32-bit
617
+ * parts (plus lengths) to collide at once makes that negligible.
618
+ */
619
+ declare function hashSolveInput(definition: SolveDefinition, dataTree: unknown): string;
620
+ /**
621
+ * Stable identity of a definition alone (no inputs) — used to key the
622
+ * server-cache-key map so the same definition reuses its `pointer` across solves
623
+ * with different inputs. Same full-content hashing as {@link hashSolveInput}: a
624
+ * binary definition is hashed over all its bytes so two distinct `.gh` files of
625
+ * equal length can't share a cache key. A {@link DefinitionRef} is keyed by its
626
+ * `key` verbatim (refs are short identities like UUIDs, safe as Map keys) —
627
+ * its immutability contract makes the key equivalent to a content hash.
628
+ */
629
+ declare function hashDefinition(definition: SolveDefinition): string;
630
+
446
631
  /**
447
632
  * Runs a Rhino Compute job using the provided tree prototypes and Grasshopper definition.
448
633
  *
@@ -454,6 +639,7 @@ declare class GrasshopperResponseProcessor {
454
639
  * - A base64-encoded string of the .gh file
455
640
  * - A plain string (will be base64-encoded)
456
641
  * - A Uint8Array of the .gh file (will be base64-encoded)
642
+ * - A `DefinitionRef` (bytes are loaded via `ref.load()` for the upload)
457
643
  * @param config - Compute configuration (server URL, API key, etc. along with optional timeout, units, etc.)
458
644
  * @returns An object containing the compute result and extracted file data.
459
645
  *
@@ -468,15 +654,23 @@ declare class GrasshopperResponseProcessor {
468
654
  * const fileData = new Uint8Array([...]);
469
655
  * await solveGrasshopperDefinition(trees, fileData, config);
470
656
  */
471
- declare function solveGrasshopperDefinition(dataTree: DataTree[], definition: string | Uint8Array, config: GrasshopperComputeConfig): Promise<GrasshopperComputeResponse>;
657
+ declare function solveGrasshopperDefinition(dataTree: DataTree[], definition: SolveDefinition, config: GrasshopperComputeConfig): Promise<GrasshopperComputeResponse>;
472
658
 
473
659
  /**
474
660
  * Fetches raw input/output schemas from a Grasshopper definition.
475
- * Returns unprocessed data exactly as received from the Rhino Compute API (camelCased).
661
+ *
662
+ * "Raw" means no per-type parsing (no default coercion, no discriminated-union
663
+ * typing) — but NOT byte-for-byte wire data. The response IS normalized:
664
+ * per-param field KEYS are canonicalized to camelCase across server branches
665
+ * ({@link normalizeInputSchema} / {@link normalizeOutputSchema}, with honest
666
+ * fallbacks for missing required fields), missing/non-array `inputs`/`outputs`
667
+ * coerce to `[]`, and server load diagnostics surface as
668
+ * `loadWarnings`/`loadErrors` (coerced to strings). Field VALUES — notably
669
+ * `default` and the `values` dropdown-label map — pass through verbatim.
476
670
  *
477
671
  * @param definition - The Grasshopper definition (URL, base64 string, or Uint8Array)
478
672
  * @param config - Compute configuration (server URL, API key, etc.)
479
- * @returns Raw inputs and outputs with no type processing
673
+ * @returns Key-normalized inputs and outputs with no per-type processing
480
674
  * @throws {RhinoComputeError} If fetch fails or response is invalid
481
675
  *
482
676
  * @public Use `fetchParsedDefinitionIO()` for processed, type-safe inputs
@@ -655,17 +849,19 @@ declare class TreeBuilder {
655
849
  * - Use with `InnerTree[]` when modifying compute API results
656
850
  * - `DataTree` is the high-level builder; `InnerTree` is the Rhino Compute format
657
851
  *
852
+ * Copy-on-write: returns a new array; the caller's array is never mutated.
853
+ *
658
854
  * @overload For TreeBuilder instances (high-level builder)
659
- * @param trees - Array of TreeBuilder instances to modify
855
+ * @param trees - Array of TreeBuilder instances to read from (not mutated)
660
856
  * @param paramName - The parameter name to set or replace
661
857
  * @param newValue - The new value (scalar, array, or TreeBuilder structure)
662
- * @returns A new/modified TreeBuilder array with the updated parameter
858
+ * @returns A new TreeBuilder array with the updated parameter
663
859
  *
664
860
  * @overload For compiled InnerTree (low-level API format)
665
- * @param trees - The compiled InnerTree array (typically from `client.solve()`)
861
+ * @param trees - The compiled InnerTree array (typically from `client.solve()`; not mutated)
666
862
  * @param paramName - The parameter name to set or replace
667
863
  * @param newValue - The new value (scalar, array, or TreeBuilder structure)
668
- * @returns A new/modified InnerTree array with the updated parameter
864
+ * @returns A new InnerTree array with the updated parameter
669
865
  *
670
866
  * @example
671
867
  * ```ts
@@ -749,9 +945,13 @@ declare class TreeBuilder {
749
945
  private static readFromDataTrees;
750
946
  /**
751
947
  * Parse a TreeBuilder path string like "{0;1;2}" into [0, 1, 2].
948
+ * Negative indices ("{-1;2}") and the root path "{}" are valid.
752
949
  *
753
950
  * @param pathStr - Path string
754
951
  * @returns Array of path indices
952
+ * @throws Error when the path string is not a valid Grasshopper branch path.
953
+ * Malformed keys must never silently collapse to a default branch —
954
+ * two distinct unparseable keys would merge their items into one branch.
755
955
  */
756
956
  static parsePathString(pathStr: string): number[];
757
957
  /**
@@ -793,4 +993,4 @@ declare class TreeBuilder {
793
993
  private static clampValue;
794
994
  }
795
995
 
796
- export { type CacheOptions, ComputeConfig, DataTree, DataTreeDefault, DataTreePath, type DataTreeValue, FileBaseInfo, type GetValuesOptions, type GetValuesResult, GrasshopperClient, GrasshopperComputeConfig, GrasshopperComputeResponse, GrasshopperParsedIO, GrasshopperParsedIORaw, GrasshopperResponseProcessor, InputParam, InputParamSchema, type ParsedContext, RetryPolicy, RhinoComputeError, type SchedulerMode, type SolveContext, type SolveOptions, type SolveResult, SolveScheduler, type SolveSchedulerOptions, TreeBuilder, fetchDefinitionIO, fetchParsedDefinitionIO, processInput, processInputs, solveGrasshopperDefinition };
996
+ export { type CacheOptions, ComputeConfig, DataTree, DataTreeDefault, DataTreePath, type DataTreeValue, type DefinitionRef, FileBaseInfo, type GetValuesOptions, type GetValuesResult, GrasshopperClient, GrasshopperComputeConfig, GrasshopperComputeResponse, GrasshopperParsedIO, GrasshopperParsedIORaw, GrasshopperResponseProcessor, InputParam, InputParamSchema, type ParsedContext, RetryPolicy, RhinoComputeError, type SchedulerMode, type SolveContext, type SolveDefinition, type SolveOptions, type SolveResult, SolveScheduler, type SolveSchedulerOptions, TreeBuilder, fetchDefinitionIO, fetchParsedDefinitionIO, hashDefinition, hashSolveInput, isDefinitionRef, processInput, processInputs, solveGrasshopperDefinition, stableStringify };