@selvajs/compute 4.0.0-beta.3 → 4.0.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.
@@ -1,6 +1,5 @@
1
- import { C as ComputeConfig, R as RetryPolicy, a as ComputeError, e as SolveDefinition, F as FileBaseInfo } from './handle-files-DMK9bCOW.js';
2
- export { D as DefinitionRef, c as FileData, P as ProcessedFile, f as downloadFileData, g as extractFilesFromComputeResponse, i as isDefinitionRef } from './handle-files-DMK9bCOW.js';
3
-
1
+ import { C as RetryPolicy, E as ComputeError, S as ComputeConfig, a as ProcessedFile, d as isDefinitionRef, i as FileData, l as DefinitionRef, n as extractFilesFromComputeResponse, r as FileBaseInfo, t as downloadFileData, u as SolveDefinition } from "./index-BNsid0_9.js";
2
+ //#region src/grasshopper/server/compute-server-stats.d.ts
4
3
  /**
5
4
  * ComputeServerStats provides methods to query Rhino Compute server statistics.
6
5
  *
@@ -23,300 +22,297 @@ export { D as DefinitionRef, c as FileData, P as ProcessedFile, f as downloadFil
23
22
  * ```
24
23
  */
25
24
  declare class ComputeServerStats {
26
- private readonly serverUrl;
27
- private readonly apiKey?;
28
- private disposed;
29
- private activeMonitors;
30
- private activeTimeouts;
31
- /** Timeout (ms) for the fast read/monitoring endpoints. */
32
- private static readonly DEFAULT_TIMEOUT_MS;
33
- /** Timeout (ms) for child-lifecycle POSTs — a cold Windows child can take ~30s to spawn. */
34
- private static readonly LIFECYCLE_TIMEOUT_MS;
35
- /** Floor for `monitor()`'s `intervalMs` — anything lower hot-loops the server. */
36
- private static readonly MIN_MONITOR_INTERVAL_MS;
37
- /**
38
- * @param serverUrl - Base URL of the Rhino Compute server with http:// or https:// scheme (e.g., 'http://localhost:6500')
39
- * @param apiKey - Optional API key for authentication
40
- */
41
- constructor(serverUrl: string, apiKey?: string);
42
- /**
43
- * Build request headers with optional API key.
44
- */
45
- private buildHeaders;
46
- /**
47
- * `fetch` wrapper that aborts after `timeoutMs` so a hung connection can't stall
48
- * a probe (or the `monitor()` loop) forever. Pass `0` to disable the timeout.
49
- */
50
- private fetchWithTimeout;
51
- /**
52
- * Check if the server is online.
53
- *
54
- * This is a single-sample probe: it returns `true` only on a 2xx from the
55
- * proxy liveness root `/`, and `false` for every other outcome (non-2xx,
56
- * network error, or timeout). A cold or briefly-busy-but-up server can therefore
57
- * read as offline — callers that gate on this (e.g. client construction)
58
- * should retry rather than treat a single `false` as authoritative.
59
- *
60
- * @param timeoutMs - Abort the probe after this many ms (default: 5000).
61
- * Pass `0` to disable the timeout. Prevents a hung connection from
62
- * stalling the caller indefinitely.
63
- */
64
- isServerOnline(timeoutMs?: number): Promise<boolean>;
65
- /**
66
- * Get the number of active child processes on the server.
67
- *
68
- * By default the proxy's `/activechildren` endpoint will *spawn* children up
69
- * to the configured count if none are running, then report the count — which
70
- * wakes (and bills) an idle server. Pass `{ initialize: false }` for a passive
71
- * read that reports the current count without spawning; use this for
72
- * monitoring or before a purge/probe where you must not wake the server.
73
- *
74
- * @param options.initialize - When `false`, append `?initialize=false` so the
75
- * server reports without spawning. Defaults to `true` (the server's default).
76
- * @returns Number of active children, or null if unavailable
77
- */
78
- getActiveChildren(options?: {
79
- initialize?: boolean;
80
- }): Promise<number | null>;
81
- /**
82
- * Get the server version information.
83
- *
84
- * @returns Version object with rhino, compute, and git_sha, or null if unavailable
85
- */
86
- getVersion(): Promise<{
87
- rhino: string;
88
- compute: string;
89
- git_sha: string | null;
90
- } | null>;
91
- /**
92
- * Get the plugins installed on the server.
93
- *
94
- * Returns a `name → version` map of non-core plugins the server has loaded,
95
- * or `null` if the request failed. Pass `kind` to choose which inventory:
96
- * `'gh'` (default) lists Grasshopper add-on assemblies via
97
- * `/plugins/gh/installed`; `'rhino'` lists Rhino plugins via
98
- * `/plugins/rhino/installed`. Plugins that ship with Rhino / are core
99
- * libraries are excluded by the server.
100
- *
101
- * @param kind - `'gh'` for Grasshopper add-ons (default) or `'rhino'` for Rhino plugins.
102
- * @returns Map of plugin name to version, or `null` on failure.
103
- *
104
- * @example
105
- * ```ts
106
- * const gh = await stats.getInstalledPlugins(); // Grasshopper add-ons
107
- * const selvaVersion = gh?.['Selva'] ?? null;
108
- * ```
109
- */
110
- getInstalledPlugins(kind?: 'gh' | 'rhino'): Promise<Record<string, string> | null>;
111
- /**
112
- * Get comprehensive server statistics.
113
- * Fetches all available server information in parallel.
114
- *
115
- * @returns Object containing server status and available stats
116
- */
117
- getServerStats(): Promise<{
118
- isOnline: boolean;
119
- version?: {
120
- rhino: string;
121
- compute: string;
122
- git_sha: string | null;
123
- };
124
- activeChildren?: number;
125
- }>;
126
- /**
127
- * Purge the server's solve-results / URL-data cache.
128
- *
129
- * POSTs to `cache/purge` and returns the number of entries removed, or `null`
130
- * if the request failed. This clears cached solve responses and fetched
131
- * definition-URL data; it does NOT evict the definition cache (active
132
- * `pointer` references stay valid).
133
- *
134
- * **Caveat:** `cache/purge` is forwarded by the rhino.compute proxy to a
135
- * single round-robin-selected child, so in a multi-child deployment one call
136
- * purges one child's cache. Call repeatedly (or size the pool to 1) if you
137
- * need a fleet-wide purge.
138
- *
139
- * @returns Number of entries removed, or `null` on failure.
140
- *
141
- * @example
142
- * ```ts
143
- * const removed = await stats.purgeCache();
144
- * if (removed !== null) console.log(`Purged ${removed} cached solves`);
145
- * ```
146
- */
147
- purgeCache(): Promise<number | null>;
148
- /**
149
- * Best-effort fleet-wide cache purge across a multi-child deployment.
150
- *
151
- * A single {@link purgeCache} POST is forwarded by the rhino.compute proxy to
152
- * just ONE round-robin-selected child, so the other children keep serving
153
- * stale cached solves. There is no proxy endpoint that addresses children
154
- * individually, so this method reads the active child count (passively, never
155
- * spawning) and fires `2 × count` sequential purges, relying on the proxy's
156
- * round-robin to spread the hits across the pool.
157
- *
158
- * **This is best-effort, not a guarantee.** Round-robin can revisit one child
159
- * and skip another; under concurrent traffic the rotation drifts. The result's
160
- * `confident` flag is `true` only when the server reports a single child (where
161
- * one purge is exact) — surface it so callers don't over-promise. For a hard
162
- * fleet-wide guarantee, run the deployment at `--childcount 1` or add a
163
- * server-side fan-out endpoint.
164
- *
165
- * @returns `{ totalPurged, calls, children, confident }`, or `null` if the
166
- * child count couldn't be read (server unreachable). `totalPurged` sums the
167
- * per-call counts; `calls` is how many purges were issued; `children` is the
168
- * reported pool size; `confident` is `true` only at a single-child pool.
169
- *
170
- * @example
171
- * ```ts
172
- * const r = await stats.purgeAllChildren();
173
- * if (r && !r.confident) {
174
- * console.warn(`Purged ~${r.totalPurged} across ${r.children} children (best-effort)`);
175
- * }
176
- * ```
177
- */
178
- purgeAllChildren(): Promise<{
179
- totalPurged: number;
180
- calls: number;
181
- children: number;
182
- confident: boolean;
183
- } | null>;
184
- /**
185
- * Get the server's current UTC clock.
186
- *
187
- * GETs `/servertime`, which the server emits as a JSON-encoded ISO-8601
188
- * timestamp (e.g. `"2026-06-18T08:30:00Z"`). Useful for detecting clock skew
189
- * between caller and server. Returns `null` if the request failed or the body
190
- * isn't a parseable date.
191
- *
192
- * @returns A `Date` for the server's UTC time, or `null` on failure.
193
- */
194
- getServerTime(): Promise<Date | null>;
195
- /**
196
- * Get how long the rhino.compute proxy has been idle.
197
- *
198
- * GETs `/idlespan` on the proxy, which returns the seconds elapsed since the
199
- * last request was forwarded to a compute child. This is a proxy-level metric
200
- * (not proxied to a child) used by autoscalers to decide when a node can be
201
- * drained. Returns `null` if unavailable.
202
- *
203
- * @returns Idle time in seconds, or `null` on failure.
204
- */
205
- getIdleSpan(): Promise<number | null>;
206
- /**
207
- * Fill the compute child pool up to the server's configured baseline.
208
- *
209
- * POSTs `/launch-children`. No-op when the pool is already at or above the
210
- * configured `--childcount`. Returns `{ spawned, active }` — how many children
211
- * were started and the resulting child count — or `null` on failure.
212
- *
213
- * To raise capacity above the baseline use {@link launchChild}; the baseline
214
- * itself can only be changed by restarting rhino.compute.
215
- *
216
- * @returns `{ spawned, active }`, or `null` on failure.
217
- */
218
- launchChildren(): Promise<{
219
- spawned: number[];
220
- active: number;
221
- } | null>;
222
- /**
223
- * Add a single compute child to the pool, optionally on a specific port.
224
- *
225
- * POSTs `/launch-child` (with `?port=N` when `port` is given). Unlike
226
- * {@link launchChildren}, this can push the pool above the baseline, up to the
227
- * server's `MaxChildren` cap. Returns `{ spawned: [port] }` on success, or
228
- * `null` on failure (server replies 400 for a bad port, 409 if the port is in
229
- * use, 503 at the max-children cap).
230
- *
231
- * @param port - Optional specific port to launch on; otherwise the next free one.
232
- * @returns `{ spawned }` listing the launched port, or `null` on failure.
233
- */
234
- launchChild(port?: number): Promise<{
235
- spawned: number[];
236
- } | null>;
237
- /**
238
- * Gracefully shut down compute children without respawning them.
239
- *
240
- * POSTs `/shutdown-children`. With no `port` it shuts down every child; with
241
- * `port` it targets just that one. Children do not respawn, but the next
242
- * `/grasshopper` request auto-spawns the pool back to the baseline. Returns
243
- * `{ shutdown, active }` — how many were stopped and the remaining count — or
244
- * `null` on failure.
245
- *
246
- * @param port - Optional port to target; omit to shut down all children.
247
- * @returns `{ shutdown, active }`, or `null` on failure.
248
- */
249
- shutdownChildren(port?: number): Promise<{
250
- shutdown: number;
251
- active: number;
252
- } | null>;
253
- /**
254
- * Shut down compute children and respawn replacements (rolling restart).
255
- *
256
- * POSTs `/recycle-children`. With no `port` it recycles every child; with
257
- * `port` it recycles just that one. The server recycles sequentially (each
258
- * replacement is serving before the next child is stopped) so the pool never
259
- * drops to zero mid-recycle. Returns `{ shutdown, spawned, active }`, or
260
- * `null` on failure.
261
- *
262
- * @param port - Optional port to target; omit to recycle all children.
263
- * @returns `{ shutdown, spawned, active }`, or `null` on failure.
264
- */
265
- recycleChildren(port?: number): Promise<{
266
- shutdown: number;
267
- spawned: number[];
268
- active: number;
269
- } | null>;
270
- /**
271
- * POST a control endpoint that replies with a JSON object and return it,
272
- * degrading to `null` on any non-2xx, non-JSON, or network failure. Shared by
273
- * the child-lifecycle methods so their failure semantics stay identical. Uses
274
- * the longer lifecycle timeout since a spawn/recycle can take ~30s.
275
- */
276
- private postJson;
277
- /**
278
- * Continuously monitor server stats at specified interval.
279
- *
280
- * @param callback - Function called with stats on each interval
281
- * @param intervalMs - Milliseconds between checks (default: 5000). Must be a
282
- * finite number of at least 100 ms — lower values would hot-loop the server.
283
- * @returns Function to stop monitoring
284
- * @throws {RangeError} If `intervalMs` is not a finite number >= 100.
285
- *
286
- * @example
287
- * ```typescript
288
- * const stopMonitoring = stats.monitor((data) => {
289
- * console.log('Server stats:', data);
290
- * }, 3000);
291
- *
292
- * // Later...
293
- * stopMonitoring();
294
- * ```
295
- */
296
- monitor(callback: (stats: Awaited<ReturnType<typeof this.getServerStats>>) => void, intervalMs?: number): () => void;
297
- /**
298
- * Disposes of all resources and stops all active monitors.
299
- * Call this when you're done using the stats instance.
300
- */
301
- dispose(): Promise<void>;
302
- /**
303
- * Ensures the instance hasn't been disposed.
304
- */
305
- private ensureNotDisposed;
25
+ private readonly serverUrl;
26
+ private readonly apiKey?;
27
+ private disposed;
28
+ private activeMonitors;
29
+ private activeTimeouts;
30
+ /** Timeout (ms) for the fast read/monitoring endpoints. */
31
+ private static readonly DEFAULT_TIMEOUT_MS;
32
+ /** Timeout (ms) for child-lifecycle POSTs — a cold Windows child can take ~30s to spawn. */
33
+ private static readonly LIFECYCLE_TIMEOUT_MS;
34
+ /** Floor for `monitor()`'s `intervalMs` — anything lower hot-loops the server. */
35
+ private static readonly MIN_MONITOR_INTERVAL_MS;
36
+ /**
37
+ * @param serverUrl - Base URL of the Rhino Compute server with http:// or https:// scheme (e.g., 'http://localhost:6500')
38
+ * @param apiKey - Optional API key for authentication
39
+ */
40
+ constructor(serverUrl: string, apiKey?: string);
41
+ /**
42
+ * Build request headers with optional API key.
43
+ */
44
+ private buildHeaders;
45
+ /**
46
+ * `fetch` wrapper that aborts after `timeoutMs` so a hung connection can't stall
47
+ * a probe (or the `monitor()` loop) forever. Pass `0` to disable the timeout.
48
+ */
49
+ private fetchWithTimeout;
50
+ /**
51
+ * Check if the server is online.
52
+ *
53
+ * This is a single-sample probe: it returns `true` only on a 2xx from the
54
+ * proxy liveness root `/`, and `false` for every other outcome (non-2xx,
55
+ * network error, or timeout). A cold or briefly-busy-but-up server can therefore
56
+ * read as offline — callers that gate on this (e.g. client construction)
57
+ * should retry rather than treat a single `false` as authoritative.
58
+ *
59
+ * @param timeoutMs - Abort the probe after this many ms (default: 5000).
60
+ * Pass `0` to disable the timeout. Prevents a hung connection from
61
+ * stalling the caller indefinitely.
62
+ */
63
+ isServerOnline(timeoutMs?: number): Promise<boolean>;
64
+ /**
65
+ * Get the number of active child processes on the server.
66
+ *
67
+ * By default the proxy's `/activechildren` endpoint will *spawn* children up
68
+ * to the configured count if none are running, then report the count — which
69
+ * wakes (and bills) an idle server. Pass `{ initialize: false }` for a passive
70
+ * read that reports the current count without spawning; use this for
71
+ * monitoring or before a purge/probe where you must not wake the server.
72
+ *
73
+ * @param options.initialize - When `false`, append `?initialize=false` so the
74
+ * server reports without spawning. Defaults to `true` (the server's default).
75
+ * @returns Number of active children, or null if unavailable
76
+ */
77
+ getActiveChildren(options?: {
78
+ initialize?: boolean;
79
+ }): Promise<number | null>;
80
+ /**
81
+ * Get the server version information.
82
+ *
83
+ * @returns Version object with rhino, compute, and git_sha, or null if unavailable
84
+ */
85
+ getVersion(): Promise<{
86
+ rhino: string;
87
+ compute: string;
88
+ git_sha: string | null;
89
+ } | null>;
90
+ /**
91
+ * Get the plugins installed on the server.
92
+ *
93
+ * Returns a `name → version` map of non-core plugins the server has loaded,
94
+ * or `null` if the request failed. Pass `kind` to choose which inventory:
95
+ * `'gh'` (default) lists Grasshopper add-on assemblies via
96
+ * `/plugins/gh/installed`; `'rhino'` lists Rhino plugins via
97
+ * `/plugins/rhino/installed`. Plugins that ship with Rhino / are core
98
+ * libraries are excluded by the server.
99
+ *
100
+ * @param kind - `'gh'` for Grasshopper add-ons (default) or `'rhino'` for Rhino plugins.
101
+ * @returns Map of plugin name to version, or `null` on failure.
102
+ *
103
+ * @example
104
+ * ```ts
105
+ * const gh = await stats.getInstalledPlugins(); // Grasshopper add-ons
106
+ * const selvaVersion = gh?.['Selva'] ?? null;
107
+ * ```
108
+ */
109
+ getInstalledPlugins(kind?: 'gh' | 'rhino'): Promise<Record<string, string> | null>;
110
+ /**
111
+ * Get comprehensive server statistics.
112
+ * Fetches all available server information in parallel.
113
+ *
114
+ * @returns Object containing server status and available stats
115
+ */
116
+ getServerStats(): Promise<{
117
+ isOnline: boolean;
118
+ version?: {
119
+ rhino: string;
120
+ compute: string;
121
+ git_sha: string | null;
122
+ };
123
+ activeChildren?: number;
124
+ }>;
125
+ /**
126
+ * Purge the server's solve-results / URL-data cache.
127
+ *
128
+ * POSTs to `cache/purge` and returns the number of entries removed, or `null`
129
+ * if the request failed. This clears cached solve responses and fetched
130
+ * definition-URL data; it does NOT evict the definition cache (active
131
+ * `pointer` references stay valid).
132
+ *
133
+ * **Caveat:** `cache/purge` is forwarded by the rhino.compute proxy to a
134
+ * single round-robin-selected child, so in a multi-child deployment one call
135
+ * purges one child's cache. Call repeatedly (or size the pool to 1) if you
136
+ * need a fleet-wide purge.
137
+ *
138
+ * @returns Number of entries removed, or `null` on failure.
139
+ *
140
+ * @example
141
+ * ```ts
142
+ * const removed = await stats.purgeCache();
143
+ * if (removed !== null) console.log(`Purged ${removed} cached solves`);
144
+ * ```
145
+ */
146
+ purgeCache(): Promise<number | null>;
147
+ /**
148
+ * Best-effort fleet-wide cache purge across a multi-child deployment.
149
+ *
150
+ * A single {@link purgeCache} POST is forwarded by the rhino.compute proxy to
151
+ * just ONE round-robin-selected child, so the other children keep serving
152
+ * stale cached solves. There is no proxy endpoint that addresses children
153
+ * individually, so this method reads the active child count (passively, never
154
+ * spawning) and fires `2 × count` sequential purges, relying on the proxy's
155
+ * round-robin to spread the hits across the pool.
156
+ *
157
+ * **This is best-effort, not a guarantee.** Round-robin can revisit one child
158
+ * and skip another; under concurrent traffic the rotation drifts. The result's
159
+ * `confident` flag is `true` only when the server reports a single child (where
160
+ * one purge is exact) — surface it so callers don't over-promise. For a hard
161
+ * fleet-wide guarantee, run the deployment at `--childcount 1` or add a
162
+ * server-side fan-out endpoint.
163
+ *
164
+ * @returns `{ totalPurged, calls, children, confident }`, or `null` if the
165
+ * child count couldn't be read (server unreachable). `totalPurged` sums the
166
+ * per-call counts; `calls` is how many purges were issued; `children` is the
167
+ * reported pool size; `confident` is `true` only at a single-child pool.
168
+ *
169
+ * @example
170
+ * ```ts
171
+ * const r = await stats.purgeAllChildren();
172
+ * if (r && !r.confident) {
173
+ * console.warn(`Purged ~${r.totalPurged} across ${r.children} children (best-effort)`);
174
+ * }
175
+ * ```
176
+ */
177
+ purgeAllChildren(): Promise<{
178
+ totalPurged: number;
179
+ calls: number;
180
+ children: number;
181
+ confident: boolean;
182
+ } | null>;
183
+ /**
184
+ * Get the server's current UTC clock.
185
+ *
186
+ * GETs `/servertime`, which the server emits as a JSON-encoded ISO-8601
187
+ * timestamp (e.g. `"2026-06-18T08:30:00Z"`). Useful for detecting clock skew
188
+ * between caller and server. Returns `null` if the request failed or the body
189
+ * isn't a parseable date.
190
+ *
191
+ * @returns A `Date` for the server's UTC time, or `null` on failure.
192
+ */
193
+ getServerTime(): Promise<Date | null>;
194
+ /**
195
+ * Get how long the rhino.compute proxy has been idle.
196
+ *
197
+ * GETs `/idlespan` on the proxy, which returns the seconds elapsed since the
198
+ * last request was forwarded to a compute child. This is a proxy-level metric
199
+ * (not proxied to a child) used by autoscalers to decide when a node can be
200
+ * drained. Returns `null` if unavailable.
201
+ *
202
+ * @returns Idle time in seconds, or `null` on failure.
203
+ */
204
+ getIdleSpan(): Promise<number | null>;
205
+ /**
206
+ * Fill the compute child pool up to the server's configured baseline.
207
+ *
208
+ * POSTs `/launch-children`. No-op when the pool is already at or above the
209
+ * configured `--childcount`. Returns `{ spawned, active }` — how many children
210
+ * were started and the resulting child count — or `null` on failure.
211
+ *
212
+ * To raise capacity above the baseline use {@link launchChild}; the baseline
213
+ * itself can only be changed by restarting rhino.compute.
214
+ *
215
+ * @returns `{ spawned, active }`, or `null` on failure.
216
+ */
217
+ launchChildren(): Promise<{
218
+ spawned: number[];
219
+ active: number;
220
+ } | null>;
221
+ /**
222
+ * Add a single compute child to the pool, optionally on a specific port.
223
+ *
224
+ * POSTs `/launch-child` (with `?port=N` when `port` is given). Unlike
225
+ * {@link launchChildren}, this can push the pool above the baseline, up to the
226
+ * server's `MaxChildren` cap. Returns `{ spawned: [port] }` on success, or
227
+ * `null` on failure (server replies 400 for a bad port, 409 if the port is in
228
+ * use, 503 at the max-children cap).
229
+ *
230
+ * @param port - Optional specific port to launch on; otherwise the next free one.
231
+ * @returns `{ spawned }` listing the launched port, or `null` on failure.
232
+ */
233
+ launchChild(port?: number): Promise<{
234
+ spawned: number[];
235
+ } | null>;
236
+ /**
237
+ * Gracefully shut down compute children without respawning them.
238
+ *
239
+ * POSTs `/shutdown-children`. With no `port` it shuts down every child; with
240
+ * `port` it targets just that one. Children do not respawn, but the next
241
+ * `/grasshopper` request auto-spawns the pool back to the baseline. Returns
242
+ * `{ shutdown, active }` — how many were stopped and the remaining count — or
243
+ * `null` on failure.
244
+ *
245
+ * @param port - Optional port to target; omit to shut down all children.
246
+ * @returns `{ shutdown, active }`, or `null` on failure.
247
+ */
248
+ shutdownChildren(port?: number): Promise<{
249
+ shutdown: number;
250
+ active: number;
251
+ } | null>;
252
+ /**
253
+ * Shut down compute children and respawn replacements (rolling restart).
254
+ *
255
+ * POSTs `/recycle-children`. With no `port` it recycles every child; with
256
+ * `port` it recycles just that one. The server recycles sequentially (each
257
+ * replacement is serving before the next child is stopped) so the pool never
258
+ * drops to zero mid-recycle. Returns `{ shutdown, spawned, active }`, or
259
+ * `null` on failure.
260
+ *
261
+ * @param port - Optional port to target; omit to recycle all children.
262
+ * @returns `{ shutdown, spawned, active }`, or `null` on failure.
263
+ */
264
+ recycleChildren(port?: number): Promise<{
265
+ shutdown: number;
266
+ spawned: number[];
267
+ active: number;
268
+ } | null>;
269
+ /**
270
+ * POST a control endpoint that replies with a JSON object and return it,
271
+ * degrading to `null` on any non-2xx, non-JSON, or network failure. Shared by
272
+ * the child-lifecycle methods so their failure semantics stay identical. Uses
273
+ * the longer lifecycle timeout since a spawn/recycle can take ~30s.
274
+ */
275
+ private postJson;
276
+ /**
277
+ * Continuously monitor server stats at specified interval.
278
+ *
279
+ * @param callback - Function called with stats on each interval
280
+ * @param intervalMs - Milliseconds between checks (default: 5000). Must be a
281
+ * finite number of at least 100 ms — lower values would hot-loop the server.
282
+ * @returns Function to stop monitoring
283
+ * @throws {RangeError} If `intervalMs` is not a finite number >= 100.
284
+ *
285
+ * @example
286
+ * ```typescript
287
+ * const stopMonitoring = stats.monitor((data) => {
288
+ * console.log('Server stats:', data);
289
+ * }, 3000);
290
+ *
291
+ * // Later...
292
+ * stopMonitoring();
293
+ * ```
294
+ */
295
+ monitor(callback: (stats: Awaited<ReturnType<typeof this.getServerStats>>) => void, intervalMs?: number): () => void;
296
+ /**
297
+ * Disposes of all resources and stops all active monitors.
298
+ * Call this when you're done using the stats instance.
299
+ */
300
+ dispose(): Promise<void>;
301
+ /**
302
+ * Ensures the instance hasn't been disposed.
303
+ */
304
+ private ensureNotDisposed;
306
305
  }
307
-
306
+ //#endregion
307
+ //#region src/grasshopper/types/inputs.d.ts
308
308
  type DataTreePath = `{${string}}`;
309
309
  interface DataItem {
310
- type: string;
311
- data: string;
312
- id: string;
310
+ type: string;
311
+ data: string;
312
+ id: string;
313
313
  }
314
- type DataTreeDefault<T = any> = {
315
- [K in DataTreePath]?: T[];
316
- };
317
- type InnerTreeData = {
318
- [path in DataTreePath]: DataItem[];
319
- };
314
+ type DataTreeDefault<T = any> = { [K in DataTreePath]?: T[]; };
315
+ type InnerTreeData = { [path in DataTreePath]: DataItem[]; };
320
316
  /**
321
317
  * Tree with parameter metadata (used in compute requests and responses).
322
318
  *
@@ -333,14 +329,14 @@ type InnerTreeData = {
333
329
  * access — `warnOnEmptyInnerTrees` in `solve.ts` is the reference example.
334
330
  */
335
331
  interface DataTree {
336
- /** PascalCase — stock mcneel servers, and the request shape this client sends. */
337
- InnerTree: InnerTreeData;
338
- /** PascalCase — stock mcneel servers, and the request shape this client sends. */
339
- ParamName: string;
340
- /** camelCase — sent instead of `InnerTree` by camelCase server forks. */
341
- innerTree?: InnerTreeData;
342
- /** camelCase — sent instead of `ParamName` by camelCase server forks. */
343
- paramName?: string;
332
+ /** PascalCase — stock mcneel servers, and the request shape this client sends. */
333
+ InnerTree: InnerTreeData;
334
+ /** PascalCase — stock mcneel servers, and the request shape this client sends. */
335
+ ParamName: string;
336
+ /** camelCase — sent instead of `InnerTree` by camelCase server forks. */
337
+ innerTree?: InnerTreeData;
338
+ /** camelCase — sent instead of `ParamName` by camelCase server forks. */
339
+ paramName?: string;
344
340
  }
345
341
  type OutputType = 'System.String' | 'System.Double' | 'System.Int32' | 'System.Boolean' | 'Rhino.Geometry.Point3d' | 'Rhino.Geometry.Line' | 'Rhino.Geometry.Circle' | 'Rhino.Geometry.Arc' | 'Rhino.Geometry.NurbsCurve' | 'Rhino.Geometry.Brep' | 'Rhino.Geometry.Mesh' | 'Rhino.Geometry.Vector3d' | 'Rhino.Geometry.Plane' | 'Rhino.Geometry.Box' | (string & {});
346
342
  /**
@@ -352,117 +348,118 @@ type DefaultValue<T> = T | T[] | DataTreeDefault<T> | undefined | null;
352
348
  * Note: `groupName` and `id` require the custom Rhino Compute branch.
353
349
  */
354
350
  interface BaseInputType {
355
- description: string;
356
- name: string;
357
- nickname: string | null;
358
- treeAccess: boolean;
359
- /**
360
- * Name of the group this parameter belongs to.
361
- * @requires Custom branch of compute.rhino3d
362
- */
363
- groupName?: string;
364
- /**
365
- * Unique identifier for the parameter.
366
- * @requires Custom branch of compute.rhino3d
367
- */
368
- id?: string;
351
+ description: string;
352
+ name: string;
353
+ nickname: string | null;
354
+ treeAccess: boolean;
355
+ /**
356
+ * Name of the group this parameter belongs to.
357
+ * @requires Custom branch of compute.rhino3d
358
+ */
359
+ groupName?: string;
360
+ /**
361
+ * Unique identifier for the parameter.
362
+ * @requires Custom branch of compute.rhino3d
363
+ */
364
+ id?: string;
369
365
  }
370
366
  /**
371
367
  * Numeric input type (Number or Integer)
372
368
  */
373
369
  interface NumericInputType extends BaseInputType {
374
- paramType: 'Number' | 'Integer';
375
- minimum?: number | null;
376
- maximum?: number | null;
377
- atLeast?: number | null;
378
- atMost?: number | null;
379
- stepSize?: number | null;
380
- default: DefaultValue<number>;
370
+ paramType: 'Number' | 'Integer';
371
+ minimum?: number | null;
372
+ maximum?: number | null;
373
+ atLeast?: number | null;
374
+ atMost?: number | null;
375
+ stepSize?: number | null;
376
+ default: DefaultValue<number>;
381
377
  }
382
378
  /**
383
379
  * Text input type
384
380
  */
385
381
  interface TextInputType extends BaseInputType {
386
- paramType: 'Text';
387
- default: DefaultValue<string>;
382
+ paramType: 'Text';
383
+ default: DefaultValue<string>;
388
384
  }
389
385
  /**
390
386
  * Boolean input type
391
387
  */
392
388
  interface BooleanInputType extends BaseInputType {
393
- paramType: 'Boolean';
394
- default: DefaultValue<boolean>;
389
+ paramType: 'Boolean';
390
+ default: DefaultValue<boolean>;
395
391
  }
396
392
  /**
397
393
  * Geometry input type (generic geometry)
398
394
  */
399
395
  interface GeometryInputType extends BaseInputType {
400
- paramType: 'Geometry';
401
- default: DefaultValue<object | string>;
396
+ paramType: 'Geometry';
397
+ default: DefaultValue<object | string>;
402
398
  }
403
399
  /**
404
400
  * ValueList input type (dropdown/select)
405
401
  */
406
402
  interface ValueListInputType extends BaseInputType {
407
- paramType: 'ValueList';
408
- values: Record<string, string>;
409
- default?: string;
403
+ paramType: 'ValueList';
404
+ values: Record<string, string>;
405
+ default?: string;
410
406
  }
411
407
  /**
412
408
  * File input type
413
409
  */
414
410
  interface FileInputType extends BaseInputType {
415
- paramType: 'File';
416
- acceptedFormats?: string[];
417
- default: DefaultValue<object | string>;
411
+ paramType: 'File';
412
+ acceptedFormats?: string[];
413
+ default: DefaultValue<object | string>;
418
414
  }
419
415
  /**
420
416
  * Color input type (stored as hex string)
421
417
  */
422
418
  interface ColorInputType extends BaseInputType {
423
- paramType: 'Color';
424
- default: DefaultValue<string>;
419
+ paramType: 'Color';
420
+ default: DefaultValue<string>;
425
421
  }
426
422
  /**
427
423
  * Discriminated union of all input parameter types
428
424
  */
429
425
  type InputParam = NumericInputType | BooleanInputType | TextInputType | ValueListInputType | GeometryInputType | FileInputType | ColorInputType;
430
-
426
+ //#endregion
427
+ //#region src/grasshopper/types/schema.d.ts
431
428
  type RhinoModelUnit = 'None' | 'Microns' | 'Millimeters' | 'Centimeters' | 'Decimeters' | 'Meters' | 'Kilometers' | 'Mils' | 'Inches' | 'Feet' | 'Yards' | 'Miles' | 'CustomUnits' | 'Unset';
432
429
  /**
433
430
  * Base Grasshopper schema properties shared by config, args, and response
434
431
  */
435
432
  interface GrasshopperBaseSchema {
436
- /** Absolute tolerance used in computation */
437
- absolutetolerance?: number | null;
438
- /** Angular tolerance used in computation */
439
- angletolerance?: number | null;
440
- /** Model units used */
441
- modelunits?: RhinoModelUnit | null;
442
- /** Data version (7 or 8) */
443
- dataversion?: 7 | 8 | null;
444
- /** Whether to use cached solution */
445
- cachesolve?: boolean | null;
446
- /**
447
- * Opt-in: cache a solve even when the definition reported GH errors. Default
448
- * (false/unset) means errored solves are never cached server-side. Some
449
- * definitions throw GH errors by design (a guarded Python component, a
450
- * filtered branch) while still producing correct geometry — set this so those
451
- * still benefit from the server's solve cache. Only meaningful with
452
- * `cachesolve`. Requires a server that honors it (VektorNode fork).
453
- */
454
- cacheerroredsolves?: boolean | null;
433
+ /** Absolute tolerance used in computation */
434
+ absolutetolerance?: number | null;
435
+ /** Angular tolerance used in computation */
436
+ angletolerance?: number | null;
437
+ /** Model units used */
438
+ modelunits?: RhinoModelUnit | null;
439
+ /** Data version (7 or 8) */
440
+ dataversion?: 7 | 8 | null;
441
+ /** Whether to use cached solution */
442
+ cachesolve?: boolean | null;
443
+ /**
444
+ * Opt-in: cache a solve even when the definition reported GH errors. Default
445
+ * (false/unset) means errored solves are never cached server-side. Some
446
+ * definitions throw GH errors by design (a guarded Python component, a
447
+ * filtered branch) while still producing correct geometry — set this so those
448
+ * still benefit from the server's solve cache. Only meaningful with
449
+ * `cachesolve`. Requires a server that honors it (VektorNode fork).
450
+ */
451
+ cacheerroredsolves?: boolean | null;
455
452
  }
456
453
  /**
457
454
  * Definition source (used in args and response)
458
455
  */
459
456
  interface GrasshopperDefinitionSource {
460
- /** Base64 encoded algorithm (if embedded) */
461
- algo?: string | null;
462
- /** URL pointer to definition file */
463
- pointer?: string | null;
464
- /** Filename of the definition */
465
- filename?: string | null;
457
+ /** Base64 encoded algorithm (if embedded) */
458
+ algo?: string | null;
459
+ /** URL pointer to definition file */
460
+ pointer?: string | null;
461
+ /** Filename of the definition */
462
+ filename?: string | null;
466
463
  }
467
464
  /**
468
465
  * Configuration for Grasshopper compute operations
@@ -472,30 +469,30 @@ interface GrasshopperDefinitionSource {
472
469
  * Instead, pass the definition directly to methods like solve(), getIO(), etc.
473
470
  */
474
471
  interface GrasshopperComputeConfig extends ComputeConfig {
475
- /** Absolute tolerance used in computation */
476
- absolutetolerance?: number | null;
477
- /** Angular tolerance used in computation */
478
- angletolerance?: number | null;
479
- /** Model units used */
480
- modelunits?: RhinoModelUnit | null;
481
- /** Data version (7 or 8) */
482
- dataversion?: 7 | 8 | null;
483
- /** Whether to use cached solution */
484
- cachesolve?: boolean | null;
485
- /**
486
- * Opt-in: cache a solve even when the definition reported GH errors. See
487
- * {@link GrasshopperBaseSchema.cacheerroredsolves}. Only meaningful with
488
- * `cachesolve`.
489
- */
490
- cacheerroredsolves?: boolean | null;
472
+ /** Absolute tolerance used in computation */
473
+ absolutetolerance?: number | null;
474
+ /** Angular tolerance used in computation */
475
+ angletolerance?: number | null;
476
+ /** Model units used */
477
+ modelunits?: RhinoModelUnit | null;
478
+ /** Data version (7 or 8) */
479
+ dataversion?: 7 | 8 | null;
480
+ /** Whether to use cached solution */
481
+ cachesolve?: boolean | null;
482
+ /**
483
+ * Opt-in: cache a solve even when the definition reported GH errors. See
484
+ * {@link GrasshopperBaseSchema.cacheerroredsolves}. Only meaningful with
485
+ * `cachesolve`.
486
+ */
487
+ cacheerroredsolves?: boolean | null;
491
488
  }
492
489
  /**
493
490
  * Arguments sent to Grasshopper compute endpoint
494
491
  * Includes config options + definition source + input values
495
492
  */
496
493
  interface GrasshopperRequestSchema extends GrasshopperBaseSchema, GrasshopperDefinitionSource {
497
- /** Input values organized by parameter */
498
- values?: DataTree[];
494
+ /** Input values organized by parameter */
495
+ values?: DataTree[];
499
496
  }
500
497
  /**
501
498
  * Response from Grasshopper compute server
@@ -512,73 +509,74 @@ interface GrasshopperRequestSchema extends GrasshopperBaseSchema, GrasshopperDef
512
509
  * rely on them without a fallback.
513
510
  */
514
511
  interface GrasshopperComputeResponse extends GrasshopperBaseSchema, Omit<GrasshopperDefinitionSource, 'pointer'> {
515
- /**
516
- * Model units the definition was solved in. Every conforming server response
517
- * carries this (it drives downstream scaling, e.g. the webdisplay parser),
518
- * but note it is not validated client-side — a non-conforming server or
519
- * hand-built mock may omit it at runtime.
520
- */
521
- modelunits: RhinoModelUnit;
522
- /**
523
- * The server echoes the request's full base64 definition back as `algo`, but the client strips
524
- * it before returning — retaining it would pin a multi-MB copy of the definition per response,
525
- * multiplied by every cache that holds responses. Always `undefined` on client-returned
526
- * responses (inherited optional field from {@link GrasshopperDefinitionSource}).
527
- */
528
- algo?: string | null;
529
- /** Filename of the definition (always present in response) */
530
- filename: string | null;
531
- /** Recursion level used */
532
- recursionlevel?: number;
533
- /** Output values organized by parameter */
534
- values: DataTree[];
535
- /** Computation errors */
536
- errors?: string[];
537
- /** Computation warnings */
538
- warnings?: string[];
512
+ /**
513
+ * Model units the definition was solved in. Every conforming server response
514
+ * carries this (it drives downstream scaling, e.g. the webdisplay parser),
515
+ * but note it is not validated client-side — a non-conforming server or
516
+ * hand-built mock may omit it at runtime.
517
+ */
518
+ modelunits: RhinoModelUnit;
519
+ /**
520
+ * The server echoes the request's full base64 definition back as `algo`, but the client strips
521
+ * it before returning — retaining it would pin a multi-MB copy of the definition per response,
522
+ * multiplied by every cache that holds responses. Always `undefined` on client-returned
523
+ * responses (inherited optional field from {@link GrasshopperDefinitionSource}).
524
+ */
525
+ algo?: string | null;
526
+ /** Filename of the definition (always present in response) */
527
+ filename: string | null;
528
+ /** Recursion level used */
529
+ recursionlevel?: number;
530
+ /** Output values organized by parameter */
531
+ values: DataTree[];
532
+ /** Computation errors */
533
+ errors?: string[];
534
+ /** Computation warnings */
535
+ warnings?: string[];
539
536
  }
540
537
  /**
541
538
  * Output parameter
542
539
  */
543
540
  interface OutputParamSchema {
544
- name: string;
545
- nickname: string | null;
546
- paramType: string;
547
- /**
548
- * Grasshopper parameter instance GUID
549
- */
550
- id: string;
541
+ name: string;
542
+ nickname: string | null;
543
+ paramType: string;
544
+ /**
545
+ * Grasshopper parameter instance GUID
546
+ */
547
+ id: string;
551
548
  }
552
549
  /**
553
550
  * Input parameter
554
551
  */
555
552
  interface InputParamSchema {
556
- /**
557
- * Grasshopper parameter instance GUID
558
- */
559
- id: string;
560
- name: string;
561
- nickname: string | null;
562
- description: string;
563
- paramType: string;
564
- treeAccess: boolean;
565
- minimum: number | null;
566
- maximum: number | null;
567
- atLeast: number;
568
- atMost: number;
569
- stepSize?: number;
570
- default: any;
571
- /**
572
- * Key-value pairs for dropdown options
573
- */
574
- values?: Record<string, string>;
575
- /**
576
- * Accepted file formats for File input
577
- */
578
- acceptedFormats?: string[];
579
- groupName?: string | null;
553
+ /**
554
+ * Grasshopper parameter instance GUID
555
+ */
556
+ id: string;
557
+ name: string;
558
+ nickname: string | null;
559
+ description: string;
560
+ paramType: string;
561
+ treeAccess: boolean;
562
+ minimum: number | null;
563
+ maximum: number | null;
564
+ atLeast: number;
565
+ atMost: number;
566
+ stepSize?: number;
567
+ default: any;
568
+ /**
569
+ * Key-value pairs for dropdown options
570
+ */
571
+ values?: Record<string, string>;
572
+ /**
573
+ * Accepted file formats for File input
574
+ */
575
+ acceptedFormats?: string[];
576
+ groupName?: string | null;
580
577
  }
581
-
578
+ //#endregion
579
+ //#region src/grasshopper/types/outputs.d.ts
582
580
  /**
583
581
  * Parsed input/output structure with raw schemas.
584
582
  *
@@ -588,12 +586,12 @@ interface InputParamSchema {
588
586
  * the server loading the definition, those from the client typing an input.
589
587
  */
590
588
  interface GrasshopperParsedIORaw {
591
- inputs: InputParamSchema[];
592
- outputs: OutputParamSchema[];
593
- /** Server-side definition-load warnings, if any. */
594
- loadWarnings?: string[];
595
- /** Server-side definition-load errors, if any (e.g. missing plugin). */
596
- loadErrors?: string[];
589
+ inputs: InputParamSchema[];
590
+ outputs: OutputParamSchema[];
591
+ /** Server-side definition-load warnings, if any. */
592
+ loadWarnings?: string[];
593
+ /** Server-side definition-load errors, if any (e.g. missing plugin). */
594
+ loadErrors?: string[];
597
595
  }
598
596
  /**
599
597
  * Per-input parse failure. The corresponding entry in `inputs` was filled
@@ -602,14 +600,14 @@ interface GrasshopperParsedIORaw {
602
600
  * misconfigured parameter.
603
601
  */
604
602
  interface InputParseError {
605
- /** The input's `name` (or `'unknown'` if the schema didn't have one). */
606
- inputName: string;
607
- /** The declared paramType from the raw schema. */
608
- paramType: string;
609
- /** Human-readable reason from the underlying ComputeError. */
610
- message: string;
611
- /** Error code from the underlying ComputeError, if available. */
612
- code?: string;
603
+ /** The input's `name` (or `'unknown'` if the schema didn't have one). */
604
+ inputName: string;
605
+ /** The declared paramType from the raw schema. */
606
+ paramType: string;
607
+ /** Human-readable reason from the underlying ComputeError. */
608
+ message: string;
609
+ /** Error code from the underlying ComputeError, if available. */
610
+ code?: string;
613
611
  }
614
612
  /**
615
613
  * Parsed input/output structure with processed types.
@@ -619,23 +617,24 @@ interface InputParseError {
619
617
  * surface these so the user can fix their definition.
620
618
  */
621
619
  interface GrasshopperParsedIO {
622
- inputs: InputParam[];
623
- outputs: OutputParamSchema[];
624
- parseErrors?: InputParseError[];
625
- /**
626
- * Server-side definition-load warnings from the `/io` response (e.g. an
627
- * obsolete component). Surface these so the user understands a degraded IO
628
- * list. Distinct from `parseErrors` (client-side input typing failures).
629
- */
630
- loadWarnings?: string[];
631
- /**
632
- * Server-side definition-load errors from the `/io` response (e.g. a missing
633
- * plugin that left inputs unresolved). When present, the inputs/outputs may
634
- * be incomplete — the user needs to fix their server/definition.
635
- */
636
- loadErrors?: string[];
620
+ inputs: InputParam[];
621
+ outputs: OutputParamSchema[];
622
+ parseErrors?: InputParseError[];
623
+ /**
624
+ * Server-side definition-load warnings from the `/io` response (e.g. an
625
+ * obsolete component). Surface these so the user understands a degraded IO
626
+ * list. Distinct from `parseErrors` (client-side input typing failures).
627
+ */
628
+ loadWarnings?: string[];
629
+ /**
630
+ * Server-side definition-load errors from the `/io` response (e.g. a missing
631
+ * plugin that left inputs unresolved). When present, the inputs/outputs may
632
+ * be incomplete — the user needs to fix their server/definition.
633
+ */
634
+ loadErrors?: string[];
637
635
  }
638
-
636
+ //#endregion
637
+ //#region src/grasshopper/scheduler/types.d.ts
639
638
  /**
640
639
  * Scheduling mode — controls how concurrent `solve()` calls interact.
641
640
  *
@@ -648,113 +647,113 @@ interface GrasshopperParsedIO {
648
647
  */
649
648
  type SchedulerMode = 'latest-wins' | 'queue' | 'parallel';
650
649
  interface CacheOptions {
651
- /**
652
- * Total byte budget for retained responses, evicted LRU. The only size bound
653
- * there is: responses range KB→100s of MB, so memory is the constraint that
654
- * matters and an entry count would only obscure it.
655
- *
656
- * Sizing uses the response's wire size (JSON text length, recorded at the
657
- * fetch boundary — no re-serialization); a response without that hint
658
- * (custom executor) is sized by a one-off `JSON.stringify`. A single
659
- * response larger than the whole budget is served but never retained.
660
- *
661
- * Required and must be > 0 — to disable caching, pass `cache: false`.
662
- */
663
- maxBytes: number;
664
- /**
665
- * Time-to-live in ms. Set to `0` for no expiry (default, and the right
666
- * choice for a solve keyed by immutable definition+inputs — expiry there
667
- * only buys a paid re-solve of an identical answer). Meaningful only when a
668
- * definition reaches outside its inputs (external data source, clock), where
669
- * a stale result is genuinely wrong rather than merely old.
670
- *
671
- * Expiry is evaluated lazily on read: an expired entry keeps its bytes
672
- * counted against `maxBytes` until that exact key is next consulted.
673
- */
674
- ttlMs?: number;
675
- /**
676
- * Cache responses that carry Grasshopper `errors`. Default `true`: an
677
- * errored solve is a valid, deterministic result — definitions raise GH
678
- * errors by design (guarded components, validation branches), so replaying
679
- * one from cache is correct. Set `false` for parity with Rhino's opt-in
680
- * `cacheerroredsolves` server flag, e.g. when a definition's errors are
681
- * transient (external data sources) rather than functions of the inputs.
682
- */
683
- cacheErroredSolves?: boolean;
650
+ /**
651
+ * Total byte budget for retained responses, evicted LRU. The only size bound
652
+ * there is: responses range KB→100s of MB, so memory is the constraint that
653
+ * matters and an entry count would only obscure it.
654
+ *
655
+ * Sizing uses the response's wire size (JSON text length, recorded at the
656
+ * fetch boundary — no re-serialization); a response without that hint
657
+ * (custom executor) is sized by a one-off `JSON.stringify`. A single
658
+ * response larger than the whole budget is served but never retained.
659
+ *
660
+ * Required and must be > 0 — to disable caching, pass `cache: false`.
661
+ */
662
+ maxBytes: number;
663
+ /**
664
+ * Time-to-live in ms. Set to `0` for no expiry (default, and the right
665
+ * choice for a solve keyed by immutable definition+inputs — expiry there
666
+ * only buys a paid re-solve of an identical answer). Meaningful only when a
667
+ * definition reaches outside its inputs (external data source, clock), where
668
+ * a stale result is genuinely wrong rather than merely old.
669
+ *
670
+ * Expiry is evaluated lazily on read: an expired entry keeps its bytes
671
+ * counted against `maxBytes` until that exact key is next consulted.
672
+ */
673
+ ttlMs?: number;
674
+ /**
675
+ * Cache responses that carry Grasshopper `errors`. Default `true`: an
676
+ * errored solve is a valid, deterministic result — definitions raise GH
677
+ * errors by design (guarded components, validation branches), so replaying
678
+ * one from cache is correct. Set `false` for parity with Rhino's opt-in
679
+ * `cacheerroredsolves` server flag, e.g. when a definition's errors are
680
+ * transient (external data sources) rather than functions of the inputs.
681
+ */
682
+ cacheErroredSolves?: boolean;
684
683
  }
685
684
  interface SolveSchedulerOptions {
686
- mode?: SchedulerMode;
687
- maxConcurrent?: number;
688
- /**
689
- * Backpressure — cap on how many calls may wait in the FIFO queue (i.e.
690
- * excluding the ones already in flight). When the queue is full, a new
691
- * `solve()` is rejected immediately with `code: QUEUE_FULL` (retryable, meant to
692
- * map to HTTP 503 + Retry-After) instead of piling up unbounded. Bounds the
693
- * miss path under load. Only applies to `queue` / `parallel` modes —
694
- * `latest-wins` has an intrinsic depth of 1. Default: unbounded.
695
- */
696
- maxQueueDepth?: number;
697
- /**
698
- * Backpressure — max time (ms) a call may sit queued before it starts
699
- * executing. If it's still waiting after this long it's rejected with
700
- * `code: QUEUE_TIMEOUT` rather than burning compute on a stale request.
701
- * Bounds tail latency. Only applies to `queue` / `parallel` modes. Default:
702
- * no deadline.
703
- */
704
- queueWaitMs?: number;
705
- timeoutMs?: number;
706
- retry?: RetryPolicy;
707
- /**
708
- * Response caching keyed by hash of (definition, dataTree). Omit or pass
709
- * `false` to disable; otherwise a byte budget is required, so an unbounded
710
- * cache can't be enabled by accident.
711
- */
712
- cache?: false | CacheOptions;
713
- /**
714
- * Reuse the server's definition cache key so a large (base64/binary)
715
- * definition is uploaded once and subsequent solves reference it by
716
- * `pointer` instead of re-sending the full payload. Hugely cheaper for
717
- * multi-MB definitions on a live UI (slider scrubs, etc.).
718
- *
719
- * Requires a `cacheKeyExecutor` to be supplied (the client wires one). Has no
720
- * effect for URL-pointer definitions (already a reference). On a server-side
721
- * cache miss the executor transparently falls back to a full upload, so this
722
- * is safe to leave on. Default: `true` when a `cacheKeyExecutor` is present.
723
- */
724
- reuseServerDefinitionCache?: boolean;
725
- /** Lifecycle hooks — fired in order. Errors thrown by hooks are logged, not rethrown. */
726
- onStart?: (ctx: SolveContext) => void;
727
- onSettle?: (ctx: SolveContext, result: SolveResult) => void;
728
- onSuperseded?: (ctx: SolveContext) => void;
685
+ mode?: SchedulerMode;
686
+ maxConcurrent?: number;
687
+ /**
688
+ * Backpressure — cap on how many calls may wait in the FIFO queue (i.e.
689
+ * excluding the ones already in flight). When the queue is full, a new
690
+ * `solve()` is rejected immediately with `code: QUEUE_FULL` (retryable, meant to
691
+ * map to HTTP 503 + Retry-After) instead of piling up unbounded. Bounds the
692
+ * miss path under load. Only applies to `queue` / `parallel` modes —
693
+ * `latest-wins` has an intrinsic depth of 1. Default: unbounded.
694
+ */
695
+ maxQueueDepth?: number;
696
+ /**
697
+ * Backpressure — max time (ms) a call may sit queued before it starts
698
+ * executing. If it's still waiting after this long it's rejected with
699
+ * `code: QUEUE_TIMEOUT` rather than burning compute on a stale request.
700
+ * Bounds tail latency. Only applies to `queue` / `parallel` modes. Default:
701
+ * no deadline.
702
+ */
703
+ queueWaitMs?: number;
704
+ timeoutMs?: number;
705
+ retry?: RetryPolicy;
706
+ /**
707
+ * Response caching keyed by hash of (definition, dataTree). Omit or pass
708
+ * `false` to disable; otherwise a byte budget is required, so an unbounded
709
+ * cache can't be enabled by accident.
710
+ */
711
+ cache?: false | CacheOptions;
712
+ /**
713
+ * Reuse the server's definition cache key so a large (base64/binary)
714
+ * definition is uploaded once and subsequent solves reference it by
715
+ * `pointer` instead of re-sending the full payload. Hugely cheaper for
716
+ * multi-MB definitions on a live UI (slider scrubs, etc.).
717
+ *
718
+ * Requires a `cacheKeyExecutor` to be supplied (the client wires one). Has no
719
+ * effect for URL-pointer definitions (already a reference). On a server-side
720
+ * cache miss the executor transparently falls back to a full upload, so this
721
+ * is safe to leave on. Default: `true` when a `cacheKeyExecutor` is present.
722
+ */
723
+ reuseServerDefinitionCache?: boolean;
724
+ /** Lifecycle hooks — fired in order. Errors thrown by hooks are logged, not rethrown. */
725
+ onStart?: (ctx: SolveContext) => void;
726
+ onSettle?: (ctx: SolveContext, result: SolveResult) => void;
727
+ onSuperseded?: (ctx: SolveContext) => void;
729
728
  }
730
729
  interface SolveContext {
731
- /** Stable hash of (definition, dataTree). */
732
- key: string;
733
- /** Timestamp when scheduler.solve() was called. */
734
- enqueuedAt: number;
735
- /** Timestamp when execution actually started (after queueing). */
736
- startedAt: number | null;
730
+ /** Stable hash of (definition, dataTree). */
731
+ key: string;
732
+ /** Timestamp when scheduler.solve() was called. */
733
+ enqueuedAt: number;
734
+ /** Timestamp when execution actually started (after queueing). */
735
+ startedAt: number | null;
737
736
  }
738
737
  type SolveResult = {
739
- status: 'success';
740
- response: GrasshopperComputeResponse;
741
- durationMs: number;
742
- fromCache: boolean;
743
- /**
744
- * Definition-cache telemetry for a real compute call (not a Selva-cache
745
- * `fromCache` hit). `false` → the server reused its cached definition via
746
- * the pointer (no upload); `true` → the pointer was cold/stale so the full
747
- * definition was re-uploaded. `undefined` when the server-definition-cache
748
- * fast path didn't run (reuse disabled, or a non-reusable definition such
749
- * as a remote URL).
750
- */
751
- definitionReuploaded?: boolean;
738
+ status: 'success';
739
+ response: GrasshopperComputeResponse;
740
+ durationMs: number;
741
+ fromCache: boolean;
742
+ /**
743
+ * Definition-cache telemetry for a real compute call (not a Selva-cache
744
+ * `fromCache` hit). `false` → the server reused its cached definition via
745
+ * the pointer (no upload); `true` → the pointer was cold/stale so the full
746
+ * definition was re-uploaded. `undefined` when the server-definition-cache
747
+ * fast path didn't run (reuse disabled, or a non-reusable definition such
748
+ * as a remote URL).
749
+ */
750
+ definitionReuploaded?: boolean;
752
751
  } | {
753
- status: 'error';
754
- error: ComputeError;
755
- durationMs: number;
752
+ status: 'error';
753
+ error: ComputeError;
754
+ durationMs: number;
756
755
  } | {
757
- status: 'superseded';
756
+ status: 'superseded';
758
757
  };
759
758
  type SolveExecutor = (definition: SolveDefinition, dataTree: DataTree[], config: GrasshopperComputeConfig) => Promise<GrasshopperComputeResponse>;
760
759
  /**
@@ -769,11 +768,12 @@ type SolveExecutor = (definition: SolveDefinition, dataTree: DataTree[], config:
769
768
  * decoupled from the transport.
770
769
  */
771
770
  type CacheKeyExecutor = (definition: SolveDefinition, dataTree: DataTree[], cacheKey: string | null, config: GrasshopperComputeConfig) => Promise<{
772
- response: GrasshopperComputeResponse;
773
- cacheKey: string | null;
774
- missed: boolean;
771
+ response: GrasshopperComputeResponse;
772
+ cacheKey: string | null;
773
+ missed: boolean;
775
774
  }>;
776
-
775
+ //#endregion
776
+ //#region src/grasshopper/scheduler/solve-scheduler.d.ts
777
777
  /**
778
778
  * Robust scheduler for Grasshopper solves.
779
779
  *
@@ -807,197 +807,198 @@ type CacheKeyExecutor = (definition: SolveDefinition, dataTree: DataTree[], cach
807
807
  * ```
808
808
  */
809
809
  declare class SolveScheduler {
810
- private readonly executor;
811
- private readonly baseConfig;
812
- private readonly mode;
813
- /**
814
- * Mutable via {@link setMaxConcurrent}: the compute server's worker pool can grow
815
- * or shrink while this scheduler is alive, and the dispatch loops re-read this on
816
- * every pass rather than capturing it.
817
- */
818
- private maxConcurrent;
819
- private readonly maxQueueDepth;
820
- private readonly queueWaitMs;
821
- private readonly timeoutMs;
822
- private readonly retry;
823
- private readonly cacheEnabled;
824
- private readonly cacheMaxBytes;
825
- private readonly cacheTtl;
826
- private readonly cacheErroredSolves;
827
- private readonly cache;
828
- /** Sum of `sizeBytes` across retained cache entries. */
829
- private cacheBytes;
830
- /**
831
- * Cumulative hit/miss/eviction counters for {@link cacheStats}. Deliberately
832
- * NOT reset by `clearCache()` — they measure this scheduler's whole lifetime,
833
- * so a hit rate stays comparable across a session rather than restarting at
834
- * every clear.
835
- */
836
- private cacheHits;
837
- private cacheMisses;
838
- private cacheEvictions;
839
- /** Optional cache-key-aware executor and whether server-def-cache reuse is on. */
840
- private readonly cacheKeyExecutor?;
841
- private readonly reuseServerDefinitionCache;
842
- /** definition identity → server cache key (`pointer`) learned from past solves. */
843
- private readonly serverCacheKeys;
844
- private readonly onStart?;
845
- private readonly onSettle?;
846
- private readonly onSuperseded?;
847
- private readonly subscribers;
848
- private readonly inFlight;
849
- private pendingForLatestWins;
850
- private readonly fifoQueue;
851
- private _lastResult;
852
- private _lastError;
853
- private _lastDurationMs;
854
- /** Ordinal handed to each solve() call. */
855
- private solveSeq;
856
- /** seq of the solve that last wrote _lastResult/_lastError — see writeLastState. */
857
- private lastStateSeq;
858
- private disposed;
859
- constructor(executor: SolveExecutor, baseConfig: GrasshopperComputeConfig, options?: SolveSchedulerOptions, cacheKeyExecutor?: CacheKeyExecutor);
860
- get isSolving(): boolean;
861
- get hasPending(): boolean;
862
- get inFlightCount(): number;
863
- get queueDepth(): number;
864
- get lastResult(): GrasshopperComputeResponse | null;
865
- get lastError(): ComputeError | null;
866
- get lastDurationMs(): number | null;
867
- /**
868
- * Adjust how many solves may run at once, for when the compute server's worker
869
- * pool changes size after this scheduler was built.
870
- *
871
- * Raising it drains queued work immediately. Lowering it never interrupts work
872
- * already in flight — those finish above the new limit, and the cap applies from
873
- * the next dispatch. Values below 1 are clamped, since 0 would wedge the queue.
874
- */
875
- setMaxConcurrent(value: number): void;
876
- /** Current concurrency cap — reflects any {@link setMaxConcurrent} adjustment. */
877
- getMaxConcurrent(): number;
878
- /** Subscribe to state changes. */
879
- subscribe(listener: () => void): () => void;
880
- private notify;
881
- /**
882
- * Schedule a solve. Returns a promise that:
883
- * - Resolves with the compute response on success.
884
- * - Rejects with `ComputeError` on failure.
885
- * - Rejects with `code: ErrorCodes.SUPERSEDED` when the call was canceled because
886
- * newer values arrived (latest-wins mode).
887
- * - Rejects with `code: ErrorCodes.ABORTED` when the call was canceled via
888
- * caller-supplied signal or `cancelAll()`.
889
- * - Rejects with `code: ErrorCodes.QUEUE_FULL` when `maxQueueDepth` is set and
890
- * the queue was already full (backpressure; `statusCode: 503`).
891
- * - Rejects with `code: ErrorCodes.QUEUE_TIMEOUT` when `queueWaitMs` is set and
892
- * the call sat queued longer than that before starting (`statusCode: 503`).
893
- *
894
- * Caller-supplied `signal` cancels just this call (rejects with `ABORTED`) —
895
- * including while the call is still queued, before execution starts.
896
- *
897
- * A {@link DefinitionRef} definition is keyed by its `key` (result cache and
898
- * server-pointer map alike) without materializing bytes — `load()` runs only
899
- * when an upload is unavoidable. Its immutability contract is trusted here:
900
- * a reused key serves the other content's cached solve.
901
- *
902
- * Responses served from the cache (and via `lastResult`) are shared objects,
903
- * not copies — treat them as immutable. Mutating one poisons every later
904
- * cache hit for that key.
905
- */
906
- solve(definition: SolveDefinition, dataTree: DataTree[], options?: {
907
- signal?: AbortSignal;
908
- }): Promise<GrasshopperComputeResponse>;
909
- /**
910
- * Record last-result state, but only if no newer solve has written since —
911
- * a slow solve settling late must not overwrite the state a newer solve
912
- * (or cache hit) already published.
913
- */
914
- private writeLastState;
915
- /** latest-wins: supersede the pending item and abort everything in flight. */
916
- private supersedeCurrent;
917
- /** Settle a still-queued item as ABORTED and remove it from its queue. */
918
- private abortQueuedItem;
919
- /**
920
- * Backpressure: settle an incoming item as QUEUE_FULL without ever enqueuing
921
- * it. The structured context lets an HTTP layer map it to 503 + Retry-After.
922
- */
923
- private shedAsQueueFull;
924
- /**
925
- * Arm the queue-wait deadline for an item about to be queued. If the item is
926
- * still waiting when it fires, it's rejected as QUEUE_TIMEOUT and removed from
927
- * the queue. Cleared on execute/settle via {@link clearQueueWaitTimer}.
928
- */
929
- private armQueueWaitTimer;
930
- /** Clear a queued item's wait-deadline timer, if one is pending. */
931
- private clearQueueWaitTimer;
932
- private enqueue;
933
- private execute;
934
- /**
935
- * Run the solve, using the server-definition-cache fast path when it's
936
- * enabled and the definition is reusable. Learns/updates the definition's
937
- * server cache key from the result so later solves can reference it.
938
- *
939
- * `definitionHash` is the {@link hashDefinition} result already computed at
940
- * `solve()` entry — threaded through rather than recomputed, so each solve
941
- * pays exactly one linear pass over the definition (issue 57).
942
- */
943
- private runExecutor;
944
- private drainNext;
945
- private supersede;
946
- private makeAbortError;
947
- /**
948
- * Settle a pending/in-flight item exactly once with an error.
949
- *
950
- * A solve promise can be settled from four concurrent sources — the executor
951
- * resolving, the executor rejecting, `supersede`, and `cancelAll` — and a JS
952
- * promise silently ignores a second settle. This guard is the single place the
953
- * settle-once invariant lives: it makes the *first* settle win and reports
954
- * whether this call was that winner, so callers fire their own hook only when
955
- * they actually settled. Any new settle path must go through here (or
956
- * {@link settleSuccess}) so the guard can't be forgotten.
957
- *
958
- * @returns `true` if this call settled the item; `false` if it was already settled.
959
- */
960
- private settleError;
961
- /**
962
- * Settle a pending/in-flight item exactly once with a successful response.
963
- * The success counterpart to {@link settleError}; see it for the invariant.
964
- *
965
- * @returns `true` if this call settled the item; `false` if it was already settled.
966
- */
967
- private settleSuccess;
968
- /** Detach the queued-phase abort listener, if one is still attached. */
969
- private removeQueuedAbortHandler;
970
- private isAbortLikeError;
971
- private normalizeExecutionError;
972
- /** Cancel everything — in-flight and pending. */
973
- cancelAll(): void;
974
- private rejectAsAborted;
975
- private readCache;
976
- private writeCache;
977
- /** Remove one cache entry and release its bytes from the running total. */
978
- private dropCacheEntry;
979
- clearCache(): void;
980
- /**
981
- * Observability snapshot of the solve cache: current size plus lifetime
982
- * hit/miss/eviction counters.
983
- *
984
- * `hits`/`misses` count cache CONSULTATIONS, so `hits / (hits + misses)` is the
985
- * hit rate. A TTL-expired entry counts as a miss (the solve runs either way).
986
- * `evictions` counts only entries dropped under size/byte pressure — not
987
- * replace-in-place writes or TTL expiry, which are not capacity signals.
988
- * Counters are cumulative and survive `clearCache()`.
989
- */
990
- cacheStats(): {
991
- entries: number;
992
- bytes: number;
993
- hits: number;
994
- misses: number;
995
- evictions: number;
996
- };
997
- dispose(): void;
998
- private runHook;
810
+ private readonly executor;
811
+ private readonly baseConfig;
812
+ private readonly mode;
813
+ /**
814
+ * Mutable via {@link setMaxConcurrent}: the compute server's worker pool can grow
815
+ * or shrink while this scheduler is alive, and the dispatch loops re-read this on
816
+ * every pass rather than capturing it.
817
+ */
818
+ private maxConcurrent;
819
+ private readonly maxQueueDepth;
820
+ private readonly queueWaitMs;
821
+ private readonly timeoutMs;
822
+ private readonly retry;
823
+ private readonly cacheEnabled;
824
+ private readonly cacheMaxBytes;
825
+ private readonly cacheTtl;
826
+ private readonly cacheErroredSolves;
827
+ private readonly cache;
828
+ /** Sum of `sizeBytes` across retained cache entries. */
829
+ private cacheBytes;
830
+ /**
831
+ * Cumulative hit/miss/eviction counters for {@link cacheStats}. Deliberately
832
+ * NOT reset by `clearCache()` — they measure this scheduler's whole lifetime,
833
+ * so a hit rate stays comparable across a session rather than restarting at
834
+ * every clear.
835
+ */
836
+ private cacheHits;
837
+ private cacheMisses;
838
+ private cacheEvictions;
839
+ /** Optional cache-key-aware executor and whether server-def-cache reuse is on. */
840
+ private readonly cacheKeyExecutor?;
841
+ private readonly reuseServerDefinitionCache;
842
+ /** definition identity → server cache key (`pointer`) learned from past solves. */
843
+ private readonly serverCacheKeys;
844
+ private readonly onStart?;
845
+ private readonly onSettle?;
846
+ private readonly onSuperseded?;
847
+ private readonly subscribers;
848
+ private readonly inFlight;
849
+ private pendingForLatestWins;
850
+ private readonly fifoQueue;
851
+ private _lastResult;
852
+ private _lastError;
853
+ private _lastDurationMs;
854
+ /** Ordinal handed to each solve() call. */
855
+ private solveSeq;
856
+ /** seq of the solve that last wrote _lastResult/_lastError — see writeLastState. */
857
+ private lastStateSeq;
858
+ private disposed;
859
+ constructor(executor: SolveExecutor, baseConfig: GrasshopperComputeConfig, options?: SolveSchedulerOptions, cacheKeyExecutor?: CacheKeyExecutor);
860
+ get isSolving(): boolean;
861
+ get hasPending(): boolean;
862
+ get inFlightCount(): number;
863
+ get queueDepth(): number;
864
+ get lastResult(): GrasshopperComputeResponse | null;
865
+ get lastError(): ComputeError | null;
866
+ get lastDurationMs(): number | null;
867
+ /**
868
+ * Adjust how many solves may run at once, for when the compute server's worker
869
+ * pool changes size after this scheduler was built.
870
+ *
871
+ * Raising it drains queued work immediately. Lowering it never interrupts work
872
+ * already in flight — those finish above the new limit, and the cap applies from
873
+ * the next dispatch. Values below 1 are clamped, since 0 would wedge the queue.
874
+ */
875
+ setMaxConcurrent(value: number): void;
876
+ /** Current concurrency cap — reflects any {@link setMaxConcurrent} adjustment. */
877
+ getMaxConcurrent(): number;
878
+ /** Subscribe to state changes. */
879
+ subscribe(listener: () => void): () => void;
880
+ private notify;
881
+ /**
882
+ * Schedule a solve. Returns a promise that:
883
+ * - Resolves with the compute response on success.
884
+ * - Rejects with `ComputeError` on failure.
885
+ * - Rejects with `code: ErrorCodes.SUPERSEDED` when the call was canceled because
886
+ * newer values arrived (latest-wins mode).
887
+ * - Rejects with `code: ErrorCodes.ABORTED` when the call was canceled via
888
+ * caller-supplied signal or `cancelAll()`.
889
+ * - Rejects with `code: ErrorCodes.QUEUE_FULL` when `maxQueueDepth` is set and
890
+ * the queue was already full (backpressure; `statusCode: 503`).
891
+ * - Rejects with `code: ErrorCodes.QUEUE_TIMEOUT` when `queueWaitMs` is set and
892
+ * the call sat queued longer than that before starting (`statusCode: 503`).
893
+ *
894
+ * Caller-supplied `signal` cancels just this call (rejects with `ABORTED`) —
895
+ * including while the call is still queued, before execution starts.
896
+ *
897
+ * A {@link DefinitionRef} definition is keyed by its `key` (result cache and
898
+ * server-pointer map alike) without materializing bytes — `load()` runs only
899
+ * when an upload is unavoidable. Its immutability contract is trusted here:
900
+ * a reused key serves the other content's cached solve.
901
+ *
902
+ * Responses served from the cache (and via `lastResult`) are shared objects,
903
+ * not copies — treat them as immutable. Mutating one poisons every later
904
+ * cache hit for that key.
905
+ */
906
+ solve(definition: SolveDefinition, dataTree: DataTree[], options?: {
907
+ signal?: AbortSignal;
908
+ }): Promise<GrasshopperComputeResponse>;
909
+ /**
910
+ * Record last-result state, but only if no newer solve has written since —
911
+ * a slow solve settling late must not overwrite the state a newer solve
912
+ * (or cache hit) already published.
913
+ */
914
+ private writeLastState;
915
+ /** latest-wins: supersede the pending item and abort everything in flight. */
916
+ private supersedeCurrent;
917
+ /** Settle a still-queued item as ABORTED and remove it from its queue. */
918
+ private abortQueuedItem;
919
+ /**
920
+ * Backpressure: settle an incoming item as QUEUE_FULL without ever enqueuing
921
+ * it. The structured context lets an HTTP layer map it to 503 + Retry-After.
922
+ */
923
+ private shedAsQueueFull;
924
+ /**
925
+ * Arm the queue-wait deadline for an item about to be queued. If the item is
926
+ * still waiting when it fires, it's rejected as QUEUE_TIMEOUT and removed from
927
+ * the queue. Cleared on execute/settle via {@link clearQueueWaitTimer}.
928
+ */
929
+ private armQueueWaitTimer;
930
+ /** Clear a queued item's wait-deadline timer, if one is pending. */
931
+ private clearQueueWaitTimer;
932
+ private enqueue;
933
+ private execute;
934
+ /**
935
+ * Run the solve, using the server-definition-cache fast path when it's
936
+ * enabled and the definition is reusable. Learns/updates the definition's
937
+ * server cache key from the result so later solves can reference it.
938
+ *
939
+ * `definitionHash` is the {@link hashDefinition} result already computed at
940
+ * `solve()` entry — threaded through rather than recomputed, so each solve
941
+ * pays exactly one linear pass over the definition (issue 57).
942
+ */
943
+ private runExecutor;
944
+ private drainNext;
945
+ private supersede;
946
+ private makeAbortError;
947
+ /**
948
+ * Settle a pending/in-flight item exactly once with an error.
949
+ *
950
+ * A solve promise can be settled from four concurrent sources — the executor
951
+ * resolving, the executor rejecting, `supersede`, and `cancelAll` — and a JS
952
+ * promise silently ignores a second settle. This guard is the single place the
953
+ * settle-once invariant lives: it makes the *first* settle win and reports
954
+ * whether this call was that winner, so callers fire their own hook only when
955
+ * they actually settled. Any new settle path must go through here (or
956
+ * {@link settleSuccess}) so the guard can't be forgotten.
957
+ *
958
+ * @returns `true` if this call settled the item; `false` if it was already settled.
959
+ */
960
+ private settleError;
961
+ /**
962
+ * Settle a pending/in-flight item exactly once with a successful response.
963
+ * The success counterpart to {@link settleError}; see it for the invariant.
964
+ *
965
+ * @returns `true` if this call settled the item; `false` if it was already settled.
966
+ */
967
+ private settleSuccess;
968
+ /** Detach the queued-phase abort listener, if one is still attached. */
969
+ private removeQueuedAbortHandler;
970
+ private isAbortLikeError;
971
+ private normalizeExecutionError;
972
+ /** Cancel everything — in-flight and pending. */
973
+ cancelAll(): void;
974
+ private rejectAsAborted;
975
+ private readCache;
976
+ private writeCache;
977
+ /** Remove one cache entry and release its bytes from the running total. */
978
+ private dropCacheEntry;
979
+ clearCache(): void;
980
+ /**
981
+ * Observability snapshot of the solve cache: current size plus lifetime
982
+ * hit/miss/eviction counters.
983
+ *
984
+ * `hits`/`misses` count cache CONSULTATIONS, so `hits / (hits + misses)` is the
985
+ * hit rate. A TTL-expired entry counts as a miss (the solve runs either way).
986
+ * `evictions` counts only entries dropped under size/byte pressure — not
987
+ * replace-in-place writes or TTL expiry, which are not capacity signals.
988
+ * Counters are cumulative and survive `clearCache()`.
989
+ */
990
+ cacheStats(): {
991
+ entries: number;
992
+ bytes: number;
993
+ hits: number;
994
+ misses: number;
995
+ evictions: number;
996
+ };
997
+ dispose(): void;
998
+ private runHook;
999
999
  }
1000
-
1000
+ //#endregion
1001
+ //#region src/grasshopper/client/grasshopper-client.d.ts
1001
1002
  /**
1002
1003
  * Per-call options that override the client's default ComputeConfig values.
1003
1004
  *
@@ -1007,9 +1008,9 @@ declare class SolveScheduler {
1007
1008
  * - `retry` — override retry policy for this call only
1008
1009
  */
1009
1010
  interface SolveOptions {
1010
- signal?: AbortSignal;
1011
- timeoutMs?: number;
1012
- retry?: RetryPolicy;
1011
+ signal?: AbortSignal;
1012
+ timeoutMs?: number;
1013
+ retry?: RetryPolicy;
1013
1014
  }
1014
1015
  /**
1015
1016
  * GrasshopperClient provides a simple API for interacting with a Rhino Compute server and grasshopper.
@@ -1035,104 +1036,106 @@ interface SolveOptions {
1035
1036
  * ```
1036
1037
  */
1037
1038
  declare class GrasshopperClient {
1038
- private readonly config;
1039
- readonly serverStats: ComputeServerStats;
1040
- private disposed;
1041
- private constructor();
1042
- /**
1043
- * Creates and initializes a GrasshopperClient with server validation.
1044
- *
1045
- * The pre-flight liveness probe (a GET on the proxy root `/`) is a
1046
- * single-sample boolean gate that reads a cold or briefly-busy-but-up server
1047
- * as offline. To avoid failing
1048
- * construction on that transient class, the probe is retried with a short
1049
- * exponential backoff before giving up. Each probe is also bounded by a
1050
- * timeout so a hung connection can't stall construction.
1051
- *
1052
- * @throws {ComputeError} with code NETWORK_ERROR if the server stays
1053
- * unreachable across all attempts
1054
- * @throws {ComputeError} with code INVALID_CONFIG if configuration is invalid
1055
- */
1056
- static create(config: GrasshopperComputeConfig): Promise<GrasshopperClient>;
1057
- /**
1058
- * Gets the client's configuration.
1059
- * Useful for passing to lower-level functions.
1060
- */
1061
- getConfig(): GrasshopperComputeConfig;
1062
- /**
1063
- * Get input/output parameters of a Grasshopper definition.
1064
- */
1065
- getIO(definition: string | Uint8Array): Promise<GrasshopperParsedIO>;
1066
- getRawIO(definition: string | Uint8Array): Promise<GrasshopperParsedIORaw>;
1067
- /**
1068
- * Run a compute job with a Grasshopper definition.
1069
- *
1070
- * @throws {ComputeError} with code INVALID_INPUT if definition is empty
1071
- * @throws {ComputeError} with code NETWORK_ERROR if server is offline
1072
- * @throws {ComputeError} with code COMPUTATION_ERROR if computation fails.
1073
- * On a partial-success response (some outputs computed, some errored) the
1074
- * error's `context.values` carries the outputs that did compute — pass
1075
- * `{ values } as GrasshopperComputeResponse` to the response processors to
1076
- * render them. `context.inputSummary` describes the inputs (param names,
1077
- * item counts, byte sizes) without pinning the full data tree.
1078
- */
1079
- solve(definition: SolveDefinition, dataTree: DataTree[], options?: SolveOptions): Promise<GrasshopperComputeResponse>;
1080
- /**
1081
- * Create a scheduler bound to this client. Use a scheduler for any UI surface
1082
- * that fires solves frequently (sliders, live editors) or that needs cancel
1083
- * semantics, response caching, or state observability.
1084
- *
1085
- * Multiple schedulers can be created from a single client — typically one per
1086
- * UI surface so their queues stay independent.
1087
- *
1088
- * @example
1089
- * ```ts
1090
- * const sliderScheduler = client.createScheduler({ mode: 'latest-wins' });
1091
- * const submitScheduler = client.createScheduler({ mode: 'queue', timeoutMs: 0, retry: { attempts: 1 } });
1092
- * ```
1093
- */
1094
- createScheduler(options?: SolveSchedulerOptions): SolveScheduler;
1095
- /**
1096
- * Disposes of client resources.
1097
- * Call this when you're done using the client.
1098
- */
1099
- dispose(): Promise<void>;
1100
- /**
1101
- * Ensures the client hasn't been disposed.
1102
- */
1103
- private ensureNotDisposed;
1104
- /**
1105
- * Validates and normalizes a compute configuration.
1106
- *
1107
- * @throws {ComputeError} with code INVALID_CONFIG if configuration is invalid
1108
- */
1109
- private normalizeComputeConfig;
1039
+ private readonly config;
1040
+ readonly serverStats: ComputeServerStats;
1041
+ private disposed;
1042
+ private constructor();
1043
+ /**
1044
+ * Creates and initializes a GrasshopperClient with server validation.
1045
+ *
1046
+ * The pre-flight liveness probe (a GET on the proxy root `/`) is a
1047
+ * single-sample boolean gate that reads a cold or briefly-busy-but-up server
1048
+ * as offline. To avoid failing
1049
+ * construction on that transient class, the probe is retried with a short
1050
+ * exponential backoff before giving up. Each probe is also bounded by a
1051
+ * timeout so a hung connection can't stall construction.
1052
+ *
1053
+ * @throws {ComputeError} with code NETWORK_ERROR if the server stays
1054
+ * unreachable across all attempts
1055
+ * @throws {ComputeError} with code INVALID_CONFIG if configuration is invalid
1056
+ */
1057
+ static create(config: GrasshopperComputeConfig): Promise<GrasshopperClient>;
1058
+ /**
1059
+ * Gets the client's configuration.
1060
+ * Useful for passing to lower-level functions.
1061
+ */
1062
+ getConfig(): GrasshopperComputeConfig;
1063
+ /**
1064
+ * Get input/output parameters of a Grasshopper definition.
1065
+ */
1066
+ getIO(definition: string | Uint8Array): Promise<GrasshopperParsedIO>;
1067
+ getRawIO(definition: string | Uint8Array): Promise<GrasshopperParsedIORaw>;
1068
+ /**
1069
+ * Run a compute job with a Grasshopper definition.
1070
+ *
1071
+ * @throws {ComputeError} with code INVALID_INPUT if definition is empty
1072
+ * @throws {ComputeError} with code NETWORK_ERROR if server is offline
1073
+ * @throws {ComputeError} with code COMPUTATION_ERROR if computation fails.
1074
+ * On a partial-success response (some outputs computed, some errored) the
1075
+ * error's `context.values` carries the outputs that did compute — pass
1076
+ * `{ values } as GrasshopperComputeResponse` to the response processors to
1077
+ * render them. `context.inputSummary` describes the inputs (param names,
1078
+ * item counts, byte sizes) without pinning the full data tree.
1079
+ */
1080
+ solve(definition: SolveDefinition, dataTree: DataTree[], options?: SolveOptions): Promise<GrasshopperComputeResponse>;
1081
+ /**
1082
+ * Create a scheduler bound to this client. Use a scheduler for any UI surface
1083
+ * that fires solves frequently (sliders, live editors) or that needs cancel
1084
+ * semantics, response caching, or state observability.
1085
+ *
1086
+ * Multiple schedulers can be created from a single client — typically one per
1087
+ * UI surface so their queues stay independent.
1088
+ *
1089
+ * @example
1090
+ * ```ts
1091
+ * const sliderScheduler = client.createScheduler({ mode: 'latest-wins' });
1092
+ * const submitScheduler = client.createScheduler({ mode: 'queue', timeoutMs: 0, retry: { attempts: 1 } });
1093
+ * ```
1094
+ */
1095
+ createScheduler(options?: SolveSchedulerOptions): SolveScheduler;
1096
+ /**
1097
+ * Disposes of client resources.
1098
+ * Call this when you're done using the client.
1099
+ */
1100
+ dispose(): Promise<void>;
1101
+ /**
1102
+ * Ensures the client hasn't been disposed.
1103
+ */
1104
+ private ensureNotDisposed;
1105
+ /**
1106
+ * Validates and normalizes a compute configuration.
1107
+ *
1108
+ * @throws {ComputeError} with code INVALID_CONFIG if configuration is invalid
1109
+ */
1110
+ private normalizeComputeConfig;
1110
1111
  }
1111
-
1112
+ //#endregion
1113
+ //#region src/grasshopper/io/output/response-processors.d.ts
1112
1114
  interface ParsedContext {
1113
- [key: string]: any;
1115
+ [key: string]: any;
1114
1116
  }
1115
1117
  interface GetValuesOptions {
1116
- parseValues?: boolean;
1117
- rhino?: any;
1118
- /**
1119
- * If true, only include values of type System.String in the result.
1120
- * Non-string types are filtered out.
1121
- */
1122
- stringOnly?: boolean;
1118
+ parseValues?: boolean;
1119
+ rhino?: any;
1120
+ /**
1121
+ * If true, only include values of type System.String in the result.
1122
+ * Non-string types are filtered out.
1123
+ */
1124
+ stringOnly?: boolean;
1123
1125
  }
1124
1126
  interface GetValuesResult<T = ParsedContext> {
1125
- values: T;
1126
- /**
1127
- * Free every rhino3dm WASM object decoded into `values`. When a `rhino`
1128
- * instance was passed, decoded geometry lives on the WASM heap and is never
1129
- * garbage-collected — call this once the values are consumed, or the heap
1130
- * grows monotonically across solves. Idempotent; a no-op when nothing was
1131
- * decoded. `values` must not be used after disposal.
1132
- */
1133
- dispose: () => void;
1127
+ values: T;
1128
+ /**
1129
+ * Free every rhino3dm WASM object decoded into `values`. When a `rhino`
1130
+ * instance was passed, decoded geometry lives on the WASM heap and is never
1131
+ * garbage-collected — call this once the values are consumed, or the heap
1132
+ * grows monotonically across solves. Idempotent; a no-op when nothing was
1133
+ * decoded. `values` must not be used after disposal.
1134
+ */
1135
+ dispose: () => void;
1134
1136
  }
1135
-
1137
+ //#endregion
1138
+ //#region src/grasshopper/client/grasshopper-response-processor.d.ts
1136
1139
  /**
1137
1140
  * High-level wrapper for interacting with Grasshopper Compute responses.
1138
1141
  *
@@ -1141,117 +1144,104 @@ interface GetValuesResult<T = ParsedContext> {
1141
1144
  * when working with Grasshopper results in client applications.
1142
1145
  */
1143
1146
  declare class GrasshopperResponseProcessor {
1144
- /**
1145
- * The raw compute response. Public so callers can hand it to a renderer-side parser — e.g.
1146
- * `getThreeMeshesFromComputeResponse` in `@selvajs/visualization/parse`, which replaced this
1147
- * class's former `extractMeshesFromResponse()`.
1148
- */
1149
- readonly response: GrasshopperComputeResponse;
1150
- /**
1151
- * Retained for call-signature compatibility. Its only consumer was the removed
1152
- * `extractMeshesFromResponse()`, which merged it into the parse options; pass `debug`
1153
- * directly to the parser in `@selvajs/visualization/parse` instead.
1154
- */
1155
- readonly debug: boolean;
1156
- constructor(
1157
- /**
1158
- * The raw compute response. Public so callers can hand it to a renderer-side parser — e.g.
1159
- * `getThreeMeshesFromComputeResponse` in `@selvajs/visualization/parse`, which replaced this
1160
- * class's former `extractMeshesFromResponse()`.
1161
- */
1162
- response: GrasshopperComputeResponse,
1163
- /**
1164
- * Retained for call-signature compatibility. Its only consumer was the removed
1165
- * `extractMeshesFromResponse()`, which merged it into the parse options; pass `debug`
1166
- * directly to the parser in `@selvajs/visualization/parse` instead.
1167
- */
1168
- debug?: boolean);
1169
- /**
1170
- * Extract all values in the response.
1171
- *
1172
- * @typeParam T - Expected structure of the return value. Defaults to a simple key/value map. (later cast as needed)
1173
- * @param byId - Key by parameter ID instead of name. Requires the VektorNode rhino.compute branch.
1174
- * @param options - Controls parsing behavior such as Rhino geometry decoding.
1175
- * @returns Parsed Grasshopper output values.
1176
- *
1177
- * @example
1178
- * ```ts
1179
- * const processor = new GrasshopperResponseProcessor(response);
1180
- * const { values } = processor.getValues();
1181
- * ```
1182
- *
1183
- */
1184
- getValues<T = ParsedContext>(byId?: boolean, options?: GetValuesOptions): GetValuesResult<T>;
1185
- /**
1186
- * Retrieve a specific value by parameter name or ID.
1187
- *
1188
- * @param selector - `{ byName }` for the human-readable name, `{ byId }` for the parameter GUID.
1189
- * @param options - Parsing configuration (e.g. disable parsing or enable Rhino).
1190
- * @returns Single parsed value, array of values, or undefined if the parameter is absent.
1191
- *
1192
- * `byId` requires the VektorNode rhino.compute branch.
1193
- *
1194
- * @example
1195
- * ```ts
1196
- * const schema = processor.getValue({ byName: 'Schema' });
1197
- * const output = processor.getValue({ byId: 'a4be1c1e-23f9-4c27-b942-7f3bb2c45c6f' });
1198
- * ```
1199
- */
1200
- getValue(selector: {
1201
- byName: string;
1202
- } | {
1203
- byId: string;
1204
- }, options?: GetValuesOptions): any;
1205
- /**
1206
- * REMOVED: `extractMeshesFromResponse()`. Mesh decoding lives in `@selvajs/visualization` now, so
1207
- * this package stays pure solve/data and carries no `three` dependency. Call the parser directly
1208
- * with the raw response:
1209
- *
1210
- * ```ts
1211
- * import { getThreeMeshesFromComputeResponse } from '@selvajs/visualization/parse';
1212
- *
1213
- * const meshes = await getThreeMeshesFromComputeResponse(processor.response, { rhino });
1214
- * scene.add(...meshes);
1215
- * ```
1216
- */
1217
- private getFileData;
1218
- /**
1219
- * Download all files generated by Grasshopper, optionally including
1220
- * additional user-provided files.
1221
- *
1222
- * Files are grouped under the specified folder name when downloaded.
1223
- *
1224
- * @param folderName - Name for the download directory.
1225
- * @param additionalFiles - Extra files to package (single file, array, or null).
1226
- *
1227
- * @example
1228
- * ```ts
1229
- * await processor.getAndDownloadFiles('gh-output');
1230
- * ```
1231
- *
1232
- * @example
1233
- * ```ts
1234
- * const extra = { name: 'notes.txt', data: 'Example' };
1235
- * await processor.getAndDownloadFiles('project', extra);
1236
- * ```
1237
- */
1238
- getAndDownloadFiles(folderName: string, additionalFiles?: FileBaseInfo[] | FileBaseInfo | null): Promise<void>;
1147
+ /**
1148
+ * The raw compute response. Public so callers can hand it to a renderer-side parser — e.g.
1149
+ * `getThreeMeshesFromComputeResponse` in `@selvajs/visualization/parse`, which replaced this
1150
+ * class's former `extractMeshesFromResponse()`.
1151
+ */
1152
+ readonly response: GrasshopperComputeResponse;
1153
+ /**
1154
+ * Retained for call-signature compatibility. Its only consumer was the removed
1155
+ * `extractMeshesFromResponse()`, which merged it into the parse options; pass `debug`
1156
+ * directly to the parser in `@selvajs/visualization/parse` instead.
1157
+ */
1158
+ readonly debug: boolean;
1159
+ constructor(
1160
+ /**
1161
+ * The raw compute response. Public so callers can hand it to a renderer-side parser — e.g.
1162
+ * `getThreeMeshesFromComputeResponse` in `@selvajs/visualization/parse`, which replaced this
1163
+ * class's former `extractMeshesFromResponse()`.
1164
+ */
1165
+ response: GrasshopperComputeResponse,
1166
+ /**
1167
+ * Retained for call-signature compatibility. Its only consumer was the removed
1168
+ * `extractMeshesFromResponse()`, which merged it into the parse options; pass `debug`
1169
+ * directly to the parser in `@selvajs/visualization/parse` instead.
1170
+ */
1171
+ debug?: boolean);
1172
+ /**
1173
+ * Extract all values in the response.
1174
+ *
1175
+ * @typeParam T - Expected structure of the return value. Defaults to a simple key/value map. (later cast as needed)
1176
+ * @param byId - Key by parameter ID instead of name. Requires the VektorNode rhino.compute branch.
1177
+ * @param options - Controls parsing behavior such as Rhino geometry decoding.
1178
+ * @returns Parsed Grasshopper output values.
1179
+ *
1180
+ * @example
1181
+ * ```ts
1182
+ * const processor = new GrasshopperResponseProcessor(response);
1183
+ * const { values } = processor.getValues();
1184
+ * ```
1185
+ *
1186
+ */
1187
+ getValues<T = ParsedContext>(byId?: boolean, options?: GetValuesOptions): GetValuesResult<T>;
1188
+ /**
1189
+ * Retrieve a specific value by parameter name or ID.
1190
+ *
1191
+ * @param selector - `{ byName }` for the human-readable name, `{ byId }` for the parameter GUID.
1192
+ * @param options - Parsing configuration (e.g. disable parsing or enable Rhino).
1193
+ * @returns Single parsed value, array of values, or undefined if the parameter is absent.
1194
+ *
1195
+ * `byId` requires the VektorNode rhino.compute branch.
1196
+ *
1197
+ * @example
1198
+ * ```ts
1199
+ * const schema = processor.getValue({ byName: 'Schema' });
1200
+ * const output = processor.getValue({ byId: 'a4be1c1e-23f9-4c27-b942-7f3bb2c45c6f' });
1201
+ * ```
1202
+ */
1203
+ getValue(selector: {
1204
+ byName: string;
1205
+ } | {
1206
+ byId: string;
1207
+ }, options?: GetValuesOptions): any;
1208
+ /**
1209
+ * REMOVED: `extractMeshesFromResponse()`. Mesh decoding lives in `@selvajs/visualization` now, so
1210
+ * this package stays pure solve/data and carries no `three` dependency. Call the parser directly
1211
+ * with the raw response:
1212
+ *
1213
+ * ```ts
1214
+ * import { getThreeMeshesFromComputeResponse } from '@selvajs/visualization/parse';
1215
+ *
1216
+ * const meshes = await getThreeMeshesFromComputeResponse(processor.response, { rhino });
1217
+ * scene.add(...meshes);
1218
+ * ```
1219
+ */
1220
+ private getFileData;
1221
+ /**
1222
+ * Download all files generated by Grasshopper, optionally including
1223
+ * additional user-provided files.
1224
+ *
1225
+ * Files are grouped under the specified folder name when downloaded.
1226
+ *
1227
+ * @param folderName - Name for the download directory.
1228
+ * @param additionalFiles - Extra files to package (single file, array, or null).
1229
+ *
1230
+ * @example
1231
+ * ```ts
1232
+ * await processor.getAndDownloadFiles('gh-output');
1233
+ * ```
1234
+ *
1235
+ * @example
1236
+ * ```ts
1237
+ * const extra = { name: 'notes.txt', data: 'Example' };
1238
+ * await processor.getAndDownloadFiles('project', extra);
1239
+ * ```
1240
+ */
1241
+ getAndDownloadFiles(folderName: string, additionalFiles?: FileBaseInfo[] | FileBaseInfo | null): Promise<void>;
1239
1242
  }
1240
-
1241
- /**
1242
- * Stable hashing for solve deduplication and caching.
1243
- *
1244
- * The public keying surface here — {@link stableStringify}, {@link hashDefinition},
1245
- * {@link hashSolveInput} — is exported from the package barrel so a durable
1246
- * (app-layer) cache or a pre-solved bundle viewer can reproduce the *exact*
1247
- * canonicalize-and-hash path the scheduler's in-process cache uses. Key parity
1248
- * across the two layers is a correctness requirement: a durable-cache key that
1249
- * canonicalizes even slightly differently would miss every entry the scheduler
1250
- * wrote (harmless) or, worse, collide differently (not). The FNV plumbing
1251
- * (`fnv1a`/`fnv1aBytes`) and the pre-hashed-definition fast path
1252
- * (`hashSolveInputForDefinition`) stay internal.
1253
- */
1254
-
1243
+ //#endregion
1244
+ //#region src/grasshopper/scheduler/stable-hash.d.ts
1255
1245
  /**
1256
1246
  * Deterministic stringify with sorted keys. {a:1,b:2} and {b:2,a:1} produce
1257
1247
  * the same string. Safely handles circular references and non-finite numbers.
@@ -1288,7 +1278,8 @@ declare function hashSolveInput(definition: SolveDefinition, dataTree: unknown):
1288
1278
  * its immutability contract makes the key equivalent to a content hash.
1289
1279
  */
1290
1280
  declare function hashDefinition(definition: SolveDefinition): string;
1291
-
1281
+ //#endregion
1282
+ //#region src/grasshopper/solve.d.ts
1292
1283
  /**
1293
1284
  * Runs a Rhino Compute job using the provided tree prototypes and Grasshopper definition.
1294
1285
  *
@@ -1316,7 +1307,8 @@ declare function hashDefinition(definition: SolveDefinition): string;
1316
1307
  * await solveGrasshopperDefinition(trees, fileData, config);
1317
1308
  */
1318
1309
  declare function solveGrasshopperDefinition(dataTree: DataTree[], definition: SolveDefinition, config: GrasshopperComputeConfig): Promise<GrasshopperComputeResponse>;
1319
-
1310
+ //#endregion
1311
+ //#region src/grasshopper/io/definition-io.d.ts
1320
1312
  /**
1321
1313
  * Fetches raw input/output schemas from a Grasshopper definition.
1322
1314
  *
@@ -1364,13 +1356,14 @@ declare function fetchDefinitionIO(definition: string | Uint8Array, config: Comp
1364
1356
  * ```
1365
1357
  */
1366
1358
  declare function fetchParsedDefinitionIO(definition: string | Uint8Array, config: ComputeConfig): Promise<GrasshopperParsedIO>;
1367
-
1359
+ //#endregion
1360
+ //#region src/grasshopper/io/schema-endpoint.d.ts
1368
1361
  /** One entry of the schema endpoint's response, after wrapper-key normalization. */
1369
1362
  interface SchemaEndpointResult<TSchema = unknown> {
1370
- /** Schemas embedded in that file. Absent when the file yielded none. */
1371
- schemas?: TSchema[];
1372
- /** Per-file diagnosis. Compute reports this and still answers 200. */
1373
- error?: string;
1363
+ /** Schemas embedded in that file. Absent when the file yielded none. */
1364
+ schemas?: TSchema[];
1365
+ /** Per-file diagnosis. Compute reports this and still answers 200. */
1366
+ error?: string;
1374
1367
  }
1375
1368
  /**
1376
1369
  * Read compute's `/grasshopper/schema` body: `[{ FileName, Schemas }]` per
@@ -1393,14 +1386,16 @@ interface SchemaEndpointResult<TSchema = unknown> {
1393
1386
  * @param raw - The parsed JSON body.
1394
1387
  */
1395
1388
  declare function readSchemaResults<TSchema = unknown>(raw: unknown): SchemaEndpointResult<TSchema>[];
1396
-
1389
+ //#endregion
1390
+ //#region src/grasshopper/io/input/input-processors.d.ts
1397
1391
  /**
1398
1392
  * Parse one raw Grasshopper input schema into a typed {@link InputParam}.
1399
1393
  * Validation failures are swallowed and replaced with a safe default; use
1400
1394
  * {@link processInputWithError} to receive them.
1401
1395
  */
1402
1396
  declare function processInput(rawInput: InputParamSchema): InputParam;
1403
-
1397
+ //#endregion
1398
+ //#region src/grasshopper/data-tree/data-tree.d.ts
1404
1399
  /**
1405
1400
  * Value types that can be stored in a DataTree
1406
1401
  */
@@ -1410,14 +1405,12 @@ type DataTreeValue = string | number | boolean | object | null;
1410
1405
  * Note: While TypeScript defines this as string, Rhino Compute accepts boolean/number primitives in JSON.
1411
1406
  */
1412
1407
  interface ComputeDataItem {
1413
- data: string | boolean | number;
1408
+ data: string | boolean | number;
1414
1409
  }
1415
1410
  /**
1416
1411
  * InnerTree data structure for compute requests.
1417
1412
  */
1418
- type ComputeInnerTreeData = {
1419
- [path in DataTreePath]: ComputeDataItem[];
1420
- };
1413
+ type ComputeInnerTreeData = { [path in DataTreePath]: ComputeDataItem[]; };
1421
1414
  /**
1422
1415
  * Standalone TreeBuilder class for constructing Grasshopper TreeBuilder structures.
1423
1416
  * Does not depend on RhinoCompute library.
@@ -1431,248 +1424,249 @@ type ComputeInnerTreeData = {
1431
1424
  * ```
1432
1425
  */
1433
1426
  declare class TreeBuilder {
1434
- private innerTree;
1435
- private paramName;
1436
- constructor(paramName: string);
1437
- /**
1438
- * Append values to a specific path in the tree.
1439
- *
1440
- * @param path - Array of integers representing the branch path (e.g., [0], [0, 1])
1441
- * @param items - Values to append at this path
1442
- * @returns this for method chaining
1443
- */
1444
- append(path: number[], items: DataTreeValue[]): this;
1445
- /**
1446
- * Append a single value to a path.
1447
- *
1448
- * @param path - Branch path
1449
- * @param item - Single value to append
1450
- * @returns this for method chaining
1451
- */
1452
- appendSingle(path: number[], item: DataTreeValue): this;
1453
- /**
1454
- * Set values from a DataTreeDefault structure.
1455
- * Replaces any existing tree data.
1456
- *
1457
- * @param treeData - TreeBuilder structure with path keys like "{0;1}"
1458
- * @returns this for method chaining
1459
- */
1460
- fromDataTreeDefault(treeData: DataTreeDefault): this;
1461
- /**
1462
- * Append flattened values to path [0].
1463
- * Useful for simple flat inputs.
1464
- *
1465
- * @param values - Single value or array of values
1466
- * @returns this for method chaining
1467
- */
1468
- appendFlat(values: DataTreeValue | DataTreeValue[]): this;
1469
- /**
1470
- * Get the flattened list of all values in the tree.
1471
- *
1472
- * @returns Array of all values across all branches
1473
- */
1474
- flatten(): DataTreeValue[];
1475
- /**
1476
- * Get all paths in the tree.
1477
- *
1478
- * @returns Array of path strings
1479
- */
1480
- getPaths(): DataTreePath[];
1481
- /**
1482
- * Get values at a specific path.
1483
- *
1484
- * @param path - Path to retrieve values from
1485
- * @returns Array of values or undefined if path doesn't exist
1486
- */
1487
- getPath(path: number[]): DataTreeValue[] | undefined;
1488
- /**
1489
- * Convert to format compatible with Grasshopper Compute API.
1490
- *
1491
- * @returns InnerTree object ready for compute
1492
- */
1493
- toComputeFormat(): DataTree;
1494
- /**
1495
- * Get the raw InnerTree data structure.
1496
- *
1497
- * @returns InnerTree data
1498
- */
1499
- getInnerTree(): ComputeInnerTreeData;
1500
- /**
1501
- * Get the parameter name.
1502
- *
1503
- * @returns Parameter name
1504
- */
1505
- getParamName(): string;
1506
- /**
1507
- * Create DataTrees from an array of InputParam definitions.
1508
- * Handles tree access, numeric constraints, and value parsing.
1509
- *
1510
- * @param inputs - Array of input parameter definitions
1511
- * @returns Array of InnerTree instances ready for compute
1512
- *
1513
- * @example
1514
- * ```ts
1515
- * const trees = TreeBuilder.fromInputParams(inputs);
1516
- * ```
1517
- */
1518
- static fromInputParams(inputs: InputParam[]): DataTree[];
1519
- /**
1520
- * Create a TreeBuilder from a single InputParam.
1521
- *
1522
- * @param input - Input parameter definition
1523
- * @returns InnerTree ready for compute or undefined if value is invalid
1524
- */
1525
- static fromInputParam(input: InputParam): DataTree | undefined;
1526
- /**
1527
- * Set or replace a parameter value within a TreeBuilder or InnerTree array.
1528
- *
1529
- * Supports both high-level `DataTree[]` instances and low-level `InnerTree[]` format.
1530
- *
1531
- * **Architecture Note:**
1532
- * - Use with `DataTree[]` when building/modifying before computation
1533
- * - Use with `InnerTree[]` when modifying compute API results
1534
- * - `DataTree` is the high-level builder; `InnerTree` is the Rhino Compute format
1535
- *
1536
- * Copy-on-write: returns a new array; the caller's array is never mutated.
1537
- *
1538
- * @overload For TreeBuilder instances (high-level builder)
1539
- * @param trees - Array of TreeBuilder instances to read from (not mutated)
1540
- * @param paramName - The parameter name to set or replace
1541
- * @param newValue - The new value (scalar, array, or TreeBuilder structure)
1542
- * @returns A new TreeBuilder array with the updated parameter
1543
- *
1544
- * @overload For compiled InnerTree (low-level API format)
1545
- * @param trees - The compiled InnerTree array (typically from `client.solve()`; not mutated)
1546
- * @param paramName - The parameter name to set or replace
1547
- * @param newValue - The new value (scalar, array, or TreeBuilder structure)
1548
- * @returns A new InnerTree array with the updated parameter
1549
- *
1550
- * @example
1551
- * ```ts
1552
- * // With TreeBuilder instances (high-level)
1553
- * let trees = [new TreeBuilder('X'), new TreeBuilder('Y')];
1554
- * trees = TreeBuilder.replaceTreeValue(trees, 'X', 42);
1555
- * const result = await client.solve(definitionUrl,
1556
- * trees.map(t => t.toComputeFormat())
1557
- * );
1558
- * ```
1559
- *
1560
- * @example
1561
- * ```ts
1562
- * // With InnerTree format (low-level, from API)
1563
- * let trees = await client.solve(definitionUrl, initialInputs);
1564
- * trees = TreeBuilder.replaceTreeValue(trees, 'X', 42);
1565
- * trees = TreeBuilder.replaceTreeValue(trees, 'Y', [1, 2, 3]);
1566
- * ```
1567
- */
1568
- static replaceTreeValue(trees: TreeBuilder[], paramName: string, newValue: DataTreeValue): TreeBuilder[];
1569
- static replaceTreeValue(trees: DataTree[], paramName: string, newValue: DataTreeValue): DataTree[];
1570
- /**
1571
- * Build a TreeBuilder from a single value, dispatching on shape:
1572
- * DataTreeDefault structure, array, or scalar.
1573
- */
1574
- private static buildFromValue;
1575
- /**
1576
- * Extract a value from a TreeBuilder or InnerTree array by parameter name.
1577
- *
1578
- * Automatically unwraps single values for convenience.
1579
- * Works with both high-level `DataTree[]` instances and low-level `InnerTree[]` format.
1580
- *
1581
- * **Architecture Note:**
1582
- * - Use with `DataTree[]` to read builder instances
1583
- * - Use with `InnerTree[]` to read compute API responses
1584
- * - Return behavior is consistent across both formats
1585
- *
1586
- * **Return Value Behavior:**
1587
- * - Single value → unwrapped (returns `5` not `[5]`)
1588
- * - Multiple values → array of values
1589
- * - Not found → `null`
1590
- *
1591
- * @overload For TreeBuilder instances
1592
- * @param trees - Array of TreeBuilder instances to read from
1593
- * @param paramName - The parameter name to retrieve
1594
- * @returns The unwrapped value, array of values, or null if parameter not found
1595
- *
1596
- * @overload For compiled InnerTree
1597
- * @param trees - The compiled InnerTree array (typically from `client.solve()`)
1598
- * @param paramName - The parameter name to retrieve
1599
- * @returns The unwrapped value, array of values, or null if parameter not found
1600
- *
1601
- * @example
1602
- * ```ts
1603
- * // With TreeBuilder instances
1604
- * const trees = [new TreeBuilder('X'), new TreeBuilder('Y')];
1605
- * trees[0].appendFlat(42);
1606
- * const x = TreeBuilder.getTreeValue(trees, 'X'); // Returns 42
1607
- * ```
1608
- *
1609
- * @example
1610
- * ```ts
1611
- * // With InnerTree from compute results
1612
- * const result = await client.solve(definitionUrl, inputs);
1613
- * const x = TreeBuilder.getTreeValue(result, 'X'); // Returns 42 (not [42])
1614
- * const points = TreeBuilder.getTreeValue(result, 'Points'); // Returns [point1, point2, ...]
1615
- * ```
1616
- */
1617
- static getTreeValue(trees: TreeBuilder[], paramName: string): DataTreeValue | null;
1618
- static getTreeValue(trees: DataTree[], paramName: string): DataTreeValue | null;
1619
- /**
1620
- * Read all values for `paramName` across every branch of the matching builder.
1621
- * Returns null when the builder isn't found.
1622
- */
1623
- private static readFromBuilders;
1624
- /**
1625
- * Read values from the first branch of the matching compiled InnerTree
1626
- * (multi-branch responses are not flattened — current semantics, pinned by
1627
- * the "reads from the first branch path only" test).
1628
- */
1629
- private static readFromDataTrees;
1630
- /**
1631
- * Parse a TreeBuilder path string like "{0;1;2}" into [0, 1, 2].
1632
- * Negative indices ("{-1;2}") and the root path "{}" are valid.
1633
- *
1634
- * @param pathStr - Path string
1635
- * @returns Array of path indices
1636
- * @throws Error when the path string is not a valid Grasshopper branch path.
1637
- * Malformed keys must never silently collapse to a default branch —
1638
- * two distinct unparseable keys would merge their items into one branch.
1639
- */
1640
- static parsePathString(pathStr: string): number[];
1641
- /**
1642
- * Format a path array into TreeBuilder path string format.
1643
- *
1644
- * @param path - Path as number array
1645
- * @returns Formatted path string like "{0;1;2}"
1646
- */
1647
- static formatPathString(path: number[]): DataTreePath;
1648
- /** Apply numeric constraints to all tree values. */
1649
- private applyNumericConstraints;
1650
- /**
1651
- * Serialize a value for compute requests.
1652
- * Preserves booleans and numbers as primitives for proper Grasshopper parameter handling.
1653
- */
1654
- private static serializeValue;
1655
- /**
1656
- * Deserialize a value back to its original type.
1657
- * Handles both string-encoded values and primitive values.
1658
- */
1659
- private static deserializeValue;
1660
- /**
1661
- * Check if a value is valid for inclusion in a DataTree.
1662
- */
1663
- private static hasValidValue;
1664
- /**
1665
- * Check if input is numeric type.
1666
- */
1667
- private static isNumericInput;
1668
- /**
1669
- * Process array of values based on input type.
1670
- */
1671
- private static processValues;
1672
- /**
1673
- * Clamp numeric value to constraints.
1674
- */
1675
- private static clampValue;
1427
+ private innerTree;
1428
+ private paramName;
1429
+ constructor(paramName: string);
1430
+ /**
1431
+ * Append values to a specific path in the tree.
1432
+ *
1433
+ * @param path - Array of integers representing the branch path (e.g., [0], [0, 1])
1434
+ * @param items - Values to append at this path
1435
+ * @returns this for method chaining
1436
+ */
1437
+ append(path: number[], items: DataTreeValue[]): this;
1438
+ /**
1439
+ * Append a single value to a path.
1440
+ *
1441
+ * @param path - Branch path
1442
+ * @param item - Single value to append
1443
+ * @returns this for method chaining
1444
+ */
1445
+ appendSingle(path: number[], item: DataTreeValue): this;
1446
+ /**
1447
+ * Set values from a DataTreeDefault structure.
1448
+ * Replaces any existing tree data.
1449
+ *
1450
+ * @param treeData - TreeBuilder structure with path keys like "{0;1}"
1451
+ * @returns this for method chaining
1452
+ */
1453
+ fromDataTreeDefault(treeData: DataTreeDefault): this;
1454
+ /**
1455
+ * Append flattened values to path [0].
1456
+ * Useful for simple flat inputs.
1457
+ *
1458
+ * @param values - Single value or array of values
1459
+ * @returns this for method chaining
1460
+ */
1461
+ appendFlat(values: DataTreeValue | DataTreeValue[]): this;
1462
+ /**
1463
+ * Get the flattened list of all values in the tree.
1464
+ *
1465
+ * @returns Array of all values across all branches
1466
+ */
1467
+ flatten(): DataTreeValue[];
1468
+ /**
1469
+ * Get all paths in the tree.
1470
+ *
1471
+ * @returns Array of path strings
1472
+ */
1473
+ getPaths(): DataTreePath[];
1474
+ /**
1475
+ * Get values at a specific path.
1476
+ *
1477
+ * @param path - Path to retrieve values from
1478
+ * @returns Array of values or undefined if path doesn't exist
1479
+ */
1480
+ getPath(path: number[]): DataTreeValue[] | undefined;
1481
+ /**
1482
+ * Convert to format compatible with Grasshopper Compute API.
1483
+ *
1484
+ * @returns InnerTree object ready for compute
1485
+ */
1486
+ toComputeFormat(): DataTree;
1487
+ /**
1488
+ * Get the raw InnerTree data structure.
1489
+ *
1490
+ * @returns InnerTree data
1491
+ */
1492
+ getInnerTree(): ComputeInnerTreeData;
1493
+ /**
1494
+ * Get the parameter name.
1495
+ *
1496
+ * @returns Parameter name
1497
+ */
1498
+ getParamName(): string;
1499
+ /**
1500
+ * Create DataTrees from an array of InputParam definitions.
1501
+ * Handles tree access, numeric constraints, and value parsing.
1502
+ *
1503
+ * @param inputs - Array of input parameter definitions
1504
+ * @returns Array of InnerTree instances ready for compute
1505
+ *
1506
+ * @example
1507
+ * ```ts
1508
+ * const trees = TreeBuilder.fromInputParams(inputs);
1509
+ * ```
1510
+ */
1511
+ static fromInputParams(inputs: InputParam[]): DataTree[];
1512
+ /**
1513
+ * Create a TreeBuilder from a single InputParam.
1514
+ *
1515
+ * @param input - Input parameter definition
1516
+ * @returns InnerTree ready for compute or undefined if value is invalid
1517
+ */
1518
+ static fromInputParam(input: InputParam): DataTree | undefined;
1519
+ /**
1520
+ * Set or replace a parameter value within a TreeBuilder or InnerTree array.
1521
+ *
1522
+ * Supports both high-level `DataTree[]` instances and low-level `InnerTree[]` format.
1523
+ *
1524
+ * **Architecture Note:**
1525
+ * - Use with `DataTree[]` when building/modifying before computation
1526
+ * - Use with `InnerTree[]` when modifying compute API results
1527
+ * - `DataTree` is the high-level builder; `InnerTree` is the Rhino Compute format
1528
+ *
1529
+ * Copy-on-write: returns a new array; the caller's array is never mutated.
1530
+ *
1531
+ * @overload For TreeBuilder instances (high-level builder)
1532
+ * @param trees - Array of TreeBuilder instances to read from (not mutated)
1533
+ * @param paramName - The parameter name to set or replace
1534
+ * @param newValue - The new value (scalar, array, or TreeBuilder structure)
1535
+ * @returns A new TreeBuilder array with the updated parameter
1536
+ *
1537
+ * @overload For compiled InnerTree (low-level API format)
1538
+ * @param trees - The compiled InnerTree array (typically from `client.solve()`; not mutated)
1539
+ * @param paramName - The parameter name to set or replace
1540
+ * @param newValue - The new value (scalar, array, or TreeBuilder structure)
1541
+ * @returns A new InnerTree array with the updated parameter
1542
+ *
1543
+ * @example
1544
+ * ```ts
1545
+ * // With TreeBuilder instances (high-level)
1546
+ * let trees = [new TreeBuilder('X'), new TreeBuilder('Y')];
1547
+ * trees = TreeBuilder.replaceTreeValue(trees, 'X', 42);
1548
+ * const result = await client.solve(definitionUrl,
1549
+ * trees.map(t => t.toComputeFormat())
1550
+ * );
1551
+ * ```
1552
+ *
1553
+ * @example
1554
+ * ```ts
1555
+ * // With InnerTree format (low-level, from API)
1556
+ * let trees = await client.solve(definitionUrl, initialInputs);
1557
+ * trees = TreeBuilder.replaceTreeValue(trees, 'X', 42);
1558
+ * trees = TreeBuilder.replaceTreeValue(trees, 'Y', [1, 2, 3]);
1559
+ * ```
1560
+ */
1561
+ static replaceTreeValue(trees: TreeBuilder[], paramName: string, newValue: DataTreeValue): TreeBuilder[];
1562
+ static replaceTreeValue(trees: DataTree[], paramName: string, newValue: DataTreeValue): DataTree[];
1563
+ /**
1564
+ * Build a TreeBuilder from a single value, dispatching on shape:
1565
+ * DataTreeDefault structure, array, or scalar.
1566
+ */
1567
+ private static buildFromValue;
1568
+ /**
1569
+ * Extract a value from a TreeBuilder or InnerTree array by parameter name.
1570
+ *
1571
+ * Automatically unwraps single values for convenience.
1572
+ * Works with both high-level `DataTree[]` instances and low-level `InnerTree[]` format.
1573
+ *
1574
+ * **Architecture Note:**
1575
+ * - Use with `DataTree[]` to read builder instances
1576
+ * - Use with `InnerTree[]` to read compute API responses
1577
+ * - Return behavior is consistent across both formats
1578
+ *
1579
+ * **Return Value Behavior:**
1580
+ * - Single value → unwrapped (returns `5` not `[5]`)
1581
+ * - Multiple values → array of values
1582
+ * - Not found → `null`
1583
+ *
1584
+ * @overload For TreeBuilder instances
1585
+ * @param trees - Array of TreeBuilder instances to read from
1586
+ * @param paramName - The parameter name to retrieve
1587
+ * @returns The unwrapped value, array of values, or null if parameter not found
1588
+ *
1589
+ * @overload For compiled InnerTree
1590
+ * @param trees - The compiled InnerTree array (typically from `client.solve()`)
1591
+ * @param paramName - The parameter name to retrieve
1592
+ * @returns The unwrapped value, array of values, or null if parameter not found
1593
+ *
1594
+ * @example
1595
+ * ```ts
1596
+ * // With TreeBuilder instances
1597
+ * const trees = [new TreeBuilder('X'), new TreeBuilder('Y')];
1598
+ * trees[0].appendFlat(42);
1599
+ * const x = TreeBuilder.getTreeValue(trees, 'X'); // Returns 42
1600
+ * ```
1601
+ *
1602
+ * @example
1603
+ * ```ts
1604
+ * // With InnerTree from compute results
1605
+ * const result = await client.solve(definitionUrl, inputs);
1606
+ * const x = TreeBuilder.getTreeValue(result, 'X'); // Returns 42 (not [42])
1607
+ * const points = TreeBuilder.getTreeValue(result, 'Points'); // Returns [point1, point2, ...]
1608
+ * ```
1609
+ */
1610
+ static getTreeValue(trees: TreeBuilder[], paramName: string): DataTreeValue | null;
1611
+ static getTreeValue(trees: DataTree[], paramName: string): DataTreeValue | null;
1612
+ /**
1613
+ * Read all values for `paramName` across every branch of the matching builder.
1614
+ * Returns null when the builder isn't found.
1615
+ */
1616
+ private static readFromBuilders;
1617
+ /**
1618
+ * Read values from the first branch of the matching compiled InnerTree
1619
+ * (multi-branch responses are not flattened — current semantics, pinned by
1620
+ * the "reads from the first branch path only" test).
1621
+ */
1622
+ private static readFromDataTrees;
1623
+ /**
1624
+ * Parse a TreeBuilder path string like "{0;1;2}" into [0, 1, 2].
1625
+ * Negative indices ("{-1;2}") and the root path "{}" are valid.
1626
+ *
1627
+ * @param pathStr - Path string
1628
+ * @returns Array of path indices
1629
+ * @throws Error when the path string is not a valid Grasshopper branch path.
1630
+ * Malformed keys must never silently collapse to a default branch —
1631
+ * two distinct unparseable keys would merge their items into one branch.
1632
+ */
1633
+ static parsePathString(pathStr: string): number[];
1634
+ /**
1635
+ * Format a path array into TreeBuilder path string format.
1636
+ *
1637
+ * @param path - Path as number array
1638
+ * @returns Formatted path string like "{0;1;2}"
1639
+ */
1640
+ static formatPathString(path: number[]): DataTreePath;
1641
+ /** Apply numeric constraints to all tree values. */
1642
+ private applyNumericConstraints;
1643
+ /**
1644
+ * Serialize a value for compute requests.
1645
+ * Preserves booleans and numbers as primitives for proper Grasshopper parameter handling.
1646
+ */
1647
+ private static serializeValue;
1648
+ /**
1649
+ * Deserialize a value back to its original type.
1650
+ * Handles both string-encoded values and primitive values.
1651
+ */
1652
+ private static deserializeValue;
1653
+ /**
1654
+ * Check if a value is valid for inclusion in a DataTree.
1655
+ */
1656
+ private static hasValidValue;
1657
+ /**
1658
+ * Check if input is numeric type.
1659
+ */
1660
+ private static isNumericInput;
1661
+ /**
1662
+ * Process array of values based on input type.
1663
+ */
1664
+ private static processValues;
1665
+ /**
1666
+ * Clamp numeric value to constraints.
1667
+ */
1668
+ private static clampValue;
1676
1669
  }
1677
-
1678
- export { type BooleanInputType, type CacheOptions, ComputeServerStats, type DataItem, type DataTree, type DataTreeDefault, type DataTreePath, type DataTreeValue, type DefaultValue, FileBaseInfo, type FileInputType, type GeometryInputType, type GetValuesOptions, type GetValuesResult, GrasshopperClient, type GrasshopperComputeConfig, type GrasshopperComputeResponse, type GrasshopperParsedIO, type GrasshopperParsedIORaw, type GrasshopperRequestSchema, GrasshopperResponseProcessor, type InnerTreeData, type InputParam, type InputParamSchema, type NumericInputType, type OutputParamSchema, type OutputType, type ParsedContext, type RhinoModelUnit, type SchedulerMode, type SchemaEndpointResult, type SolveContext, SolveDefinition, type SolveOptions, type SolveResult, SolveScheduler, type SolveSchedulerOptions, type TextInputType, TreeBuilder, type ValueListInputType, fetchDefinitionIO, fetchParsedDefinitionIO, hashDefinition, hashSolveInput, processInput, readSchemaResults, solveGrasshopperDefinition, stableStringify };
1670
+ //#endregion
1671
+ export { type BooleanInputType, type CacheOptions, ComputeServerStats, type DataItem, type DataTree, type DataTreeDefault, type DataTreePath, type DataTreeValue, type DefaultValue, type DefinitionRef, type FileBaseInfo, type FileData, type FileInputType, type GeometryInputType, type GetValuesOptions, type GetValuesResult, GrasshopperClient, type GrasshopperComputeConfig, type GrasshopperComputeResponse, type GrasshopperParsedIO, type GrasshopperParsedIORaw, type GrasshopperRequestSchema, GrasshopperResponseProcessor, type InnerTreeData, type InputParam, type InputParamSchema, type NumericInputType, type OutputParamSchema, type OutputType, type ParsedContext, type ProcessedFile, type RhinoModelUnit, type SchedulerMode, type SchemaEndpointResult, type SolveContext, type SolveDefinition, type SolveOptions, type SolveResult, SolveScheduler, type SolveSchedulerOptions, type TextInputType, TreeBuilder, type ValueListInputType, downloadFileData, extractFilesFromComputeResponse, fetchDefinitionIO, fetchParsedDefinitionIO, hashDefinition, hashSolveInput, isDefinitionRef, processInput, readSchemaResults, solveGrasshopperDefinition, stableStringify };
1672
+ //# sourceMappingURL=grasshopper.d.ts.map