@selvajs/compute 4.0.0-beta.0 → 4.0.0-beta.3

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,5 +1,309 @@
1
- import { C as ComputeConfig, e as RhinoModelUnit, R as RetryPolicy, d as RhinoComputeError, a as ComputeServerStats, F as FileBaseInfo } from './handle-files-DcA24IkS.js';
2
- export { c as FileData, P as ProcessedFile, f as downloadFileData, g as extractFilesFromComputeResponse } from './handle-files-DcA24IkS.js';
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
+
4
+ /**
5
+ * ComputeServerStats provides methods to query Rhino Compute server statistics.
6
+ *
7
+ * @public Use this for server health monitoring and statistics.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const stats = new ComputeServerStats('http://localhost:6500', 'your-api-key');
12
+ *
13
+ * try {
14
+ * const isOnline = await stats.isServerOnline();
15
+ * const children = await stats.getActiveChildren();
16
+ * const version = await stats.getVersion();
17
+ *
18
+ * // Or get everything at once
19
+ * const allStats = await stats.getServerStats();
20
+ * } finally {
21
+ * await stats.dispose(); // Clean up resources
22
+ * }
23
+ * ```
24
+ */
25
+ 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;
306
+ }
3
307
 
4
308
  type DataTreePath = `{${string}}`;
5
309
  interface DataItem {
@@ -124,6 +428,7 @@ interface ColorInputType extends BaseInputType {
124
428
  */
125
429
  type InputParam = NumericInputType | BooleanInputType | TextInputType | ValueListInputType | GeometryInputType | FileInputType | ColorInputType;
126
430
 
431
+ type RhinoModelUnit = 'None' | 'Microns' | 'Millimeters' | 'Centimeters' | 'Decimeters' | 'Meters' | 'Kilometers' | 'Mils' | 'Inches' | 'Feet' | 'Yards' | 'Miles' | 'CustomUnits' | 'Unset';
127
432
  /**
128
433
  * Base Grasshopper schema properties shared by config, args, and response
129
434
  */
@@ -301,9 +606,9 @@ interface InputParseError {
301
606
  inputName: string;
302
607
  /** The declared paramType from the raw schema. */
303
608
  paramType: string;
304
- /** Human-readable reason from the underlying RhinoComputeError. */
609
+ /** Human-readable reason from the underlying ComputeError. */
305
610
  message: string;
306
- /** Error code from the underlying RhinoComputeError, if available. */
611
+ /** Error code from the underlying ComputeError, if available. */
307
612
  code?: string;
308
613
  }
309
614
  /**
@@ -331,50 +636,6 @@ interface GrasshopperParsedIO {
331
636
  loadErrors?: string[];
332
637
  }
333
638
 
334
- /**
335
- * By-reference definition form for solves.
336
- *
337
- * Lets a caller that already knows a definition's identity (e.g. a stored
338
- * version's UUID) schedule solves without materializing the multi-MB bytes:
339
- * cache keys and the server-pointer map are derived from `key` alone, and
340
- * `load()` is only called when an upload is genuinely unavoidable (first solve
341
- * of a definition, or a server-side pointer miss).
342
- */
343
- /**
344
- * A definition identified by a stable key, with bytes materialized on demand.
345
- *
346
- * **Immutability contract — read this before constructing one.** `key` must
347
- * identify IMMUTABLE bytes: every `load()` for a given `key` must return the
348
- * same content, forever. All caching (the scheduler's result cache, the
349
- * server-pointer map, and any durable cache built on these keys) trusts the
350
- * key as the definition's identity WITHOUT looking at the bytes. If two
351
- * different byte contents ever share a key, cached solves from one are served
352
- * for the other — silent cache poisoning with no diagnostic. Use an identity
353
- * that can never be reused for different content (e.g. a version UUID), never
354
- * a mutable name or path.
355
- */
356
- interface DefinitionRef {
357
- /**
358
- * Identity of immutable bytes (e.g. a version UUID). Two different byte
359
- * contents must never share a key — cache poisoning otherwise.
360
- */
361
- key: string;
362
- /**
363
- * Materialize the bytes. Called ONLY when an upload is unavoidable — inside
364
- * the solve execution, so it counts toward the solve's abort semantics.
365
- */
366
- load: () => Promise<Uint8Array>;
367
- }
368
- /**
369
- * Every definition form accepted by the solve entry points:
370
- * - a URL, base64 string, or plain string (content-hashed for caching)
371
- * - raw `.gh` bytes (content-hashed)
372
- * - a {@link DefinitionRef} (identity-keyed; bytes loaded lazily)
373
- */
374
- type SolveDefinition = string | Uint8Array | DefinitionRef;
375
- /** Narrow a {@link SolveDefinition} to the by-reference form. */
376
- declare function isDefinitionRef(definition: SolveDefinition): definition is DefinitionRef;
377
-
378
639
  /**
379
640
  * Scheduling mode — controls how concurrent `solve()` calls interact.
380
641
  *
@@ -387,19 +648,29 @@ declare function isDefinitionRef(definition: SolveDefinition): definition is Def
387
648
  */
388
649
  type SchedulerMode = 'latest-wins' | 'queue' | 'parallel';
389
650
  interface CacheOptions {
390
- /** Maximum entries kept in the LRU. Default: 50. */
391
- maxEntries?: number;
392
- /**
393
- * Total byte budget for retained responses, evicted LRU alongside
394
- * `maxEntries` (both bounds apply). Responses range KB→100s of MB, so an
395
- * entry count alone can't bound heap. Sizing uses the response's wire size
396
- * (JSON text length, recorded at the fetch boundary — no re-serialization);
397
- * a response without that hint (custom executor) is sized by a one-off
398
- * `JSON.stringify`. A single response larger than the whole budget is
399
- * served but never retained. `0` = no byte bound (count-only, default).
400
- */
401
- maxBytes?: number;
402
- /** Time-to-live in ms. Set to `0` for no expiry (default). */
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
+ */
403
674
  ttlMs?: number;
404
675
  /**
405
676
  * Cache responses that carry Grasshopper `errors`. Default `true`: an
@@ -417,7 +688,7 @@ interface SolveSchedulerOptions {
417
688
  /**
418
689
  * Backpressure — cap on how many calls may wait in the FIFO queue (i.e.
419
690
  * excluding the ones already in flight). When the queue is full, a new
420
- * `solve()` is shed immediately with `code: QUEUE_FULL` (retryable, meant to
691
+ * `solve()` is rejected immediately with `code: QUEUE_FULL` (retryable, meant to
421
692
  * map to HTTP 503 + Retry-After) instead of piling up unbounded. Bounds the
422
693
  * miss path under load. Only applies to `queue` / `parallel` modes —
423
694
  * `latest-wins` has an intrinsic depth of 1. Default: unbounded.
@@ -425,7 +696,7 @@ interface SolveSchedulerOptions {
425
696
  maxQueueDepth?: number;
426
697
  /**
427
698
  * Backpressure — max time (ms) a call may sit queued before it starts
428
- * executing. If it's still waiting after this long it's shed with
699
+ * executing. If it's still waiting after this long it's rejected with
429
700
  * `code: QUEUE_TIMEOUT` rather than burning compute on a stale request.
430
701
  * Bounds tail latency. Only applies to `queue` / `parallel` modes. Default:
431
702
  * no deadline.
@@ -433,8 +704,12 @@ interface SolveSchedulerOptions {
433
704
  queueWaitMs?: number;
434
705
  timeoutMs?: number;
435
706
  retry?: RetryPolicy;
436
- /** Enable response caching keyed by hash of (definition, dataTree). */
437
- cache?: boolean | CacheOptions;
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;
438
713
  /**
439
714
  * Reuse the server's definition cache key so a large (base64/binary)
440
715
  * definition is uploaded once and subsequent solves reference it by
@@ -476,7 +751,7 @@ type SolveResult = {
476
751
  definitionReuploaded?: boolean;
477
752
  } | {
478
753
  status: 'error';
479
- error: RhinoComputeError;
754
+ error: ComputeError;
480
755
  durationMs: number;
481
756
  } | {
482
757
  status: 'superseded';
@@ -535,13 +810,17 @@ declare class SolveScheduler {
535
810
  private readonly executor;
536
811
  private readonly baseConfig;
537
812
  private readonly mode;
538
- private readonly maxConcurrent;
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;
539
819
  private readonly maxQueueDepth;
540
820
  private readonly queueWaitMs;
541
821
  private readonly timeoutMs;
542
822
  private readonly retry;
543
823
  private readonly cacheEnabled;
544
- private readonly cacheMax;
545
824
  private readonly cacheMaxBytes;
546
825
  private readonly cacheTtl;
547
826
  private readonly cacheErroredSolves;
@@ -583,15 +862,26 @@ declare class SolveScheduler {
583
862
  get inFlightCount(): number;
584
863
  get queueDepth(): number;
585
864
  get lastResult(): GrasshopperComputeResponse | null;
586
- get lastError(): RhinoComputeError | null;
865
+ get lastError(): ComputeError | null;
587
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;
588
878
  /** Subscribe to state changes. */
589
879
  subscribe(listener: () => void): () => void;
590
880
  private notify;
591
881
  /**
592
882
  * Schedule a solve. Returns a promise that:
593
883
  * - Resolves with the compute response on success.
594
- * - Rejects with `RhinoComputeError` on failure.
884
+ * - Rejects with `ComputeError` on failure.
595
885
  * - Rejects with `code: ErrorCodes.SUPERSEDED` when the call was canceled because
596
886
  * newer values arrived (latest-wins mode).
597
887
  * - Rejects with `code: ErrorCodes.ABORTED` when the call was canceled via
@@ -633,8 +923,8 @@ declare class SolveScheduler {
633
923
  private shedAsQueueFull;
634
924
  /**
635
925
  * Arm the queue-wait deadline for an item about to be queued. If the item is
636
- * still waiting when it fires, it's shed as QUEUE_TIMEOUT and removed from the
637
- * queue. Cleared on execute/settle via {@link clearQueueWaitTimer}.
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}.
638
928
  */
639
929
  private armQueueWaitTimer;
640
930
  /** Clear a queued item's wait-deadline timer, if one is pending. */
@@ -759,9 +1049,9 @@ declare class GrasshopperClient {
759
1049
  * exponential backoff before giving up. Each probe is also bounded by a
760
1050
  * timeout so a hung connection can't stall construction.
761
1051
  *
762
- * @throws {RhinoComputeError} with code NETWORK_ERROR if the server stays
1052
+ * @throws {ComputeError} with code NETWORK_ERROR if the server stays
763
1053
  * unreachable across all attempts
764
- * @throws {RhinoComputeError} with code INVALID_CONFIG if configuration is invalid
1054
+ * @throws {ComputeError} with code INVALID_CONFIG if configuration is invalid
765
1055
  */
766
1056
  static create(config: GrasshopperComputeConfig): Promise<GrasshopperClient>;
767
1057
  /**
@@ -777,9 +1067,9 @@ declare class GrasshopperClient {
777
1067
  /**
778
1068
  * Run a compute job with a Grasshopper definition.
779
1069
  *
780
- * @throws {RhinoComputeError} with code INVALID_INPUT if definition is empty
781
- * @throws {RhinoComputeError} with code NETWORK_ERROR if server is offline
782
- * @throws {RhinoComputeError} with code COMPUTATION_ERROR if computation fails.
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.
783
1073
  * On a partial-success response (some outputs computed, some errored) the
784
1074
  * error's `context.values` carries the outputs that did compute — pass
785
1075
  * `{ values } as GrasshopperComputeResponse` to the response processors to
@@ -814,7 +1104,7 @@ declare class GrasshopperClient {
814
1104
  /**
815
1105
  * Validates and normalizes a compute configuration.
816
1106
  *
817
- * @throws {RhinoComputeError} with code INVALID_CONFIG if configuration is invalid
1107
+ * @throws {ComputeError} with code INVALID_CONFIG if configuration is invalid
818
1108
  */
819
1109
  private normalizeComputeConfig;
820
1110
  }
@@ -1042,7 +1332,7 @@ declare function solveGrasshopperDefinition(dataTree: DataTree[], definition: So
1042
1332
  * @param definition - The Grasshopper definition (URL, base64 string, or Uint8Array)
1043
1333
  * @param config - Compute configuration (server URL, API key, etc.)
1044
1334
  * @returns Key-normalized inputs and outputs with no per-type processing
1045
- * @throws {RhinoComputeError} If fetch fails or response is invalid
1335
+ * @throws {ComputeError} If fetch fails or response is invalid
1046
1336
  *
1047
1337
  * @public Use `fetchParsedDefinitionIO()` for processed, type-safe inputs
1048
1338
  */
@@ -1056,7 +1346,7 @@ declare function fetchDefinitionIO(definition: string | Uint8Array, config: Comp
1056
1346
  * @param definition - The Grasshopper definition (URL, base64 string, or Uint8Array)
1057
1347
  * @param config - Compute configuration (server URL, API key, etc.)
1058
1348
  * @returns Processed inputs with discriminated union types and outputs
1059
- * @throws {RhinoComputeError} If fetch fails or response is invalid
1349
+ * @throws {ComputeError} If fetch fails or response is invalid
1060
1350
  *
1061
1351
  * @example
1062
1352
  * ```typescript
@@ -1075,6 +1365,35 @@ declare function fetchDefinitionIO(definition: string | Uint8Array, config: Comp
1075
1365
  */
1076
1366
  declare function fetchParsedDefinitionIO(definition: string | Uint8Array, config: ComputeConfig): Promise<GrasshopperParsedIO>;
1077
1367
 
1368
+ /** One entry of the schema endpoint's response, after wrapper-key normalization. */
1369
+ 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;
1374
+ }
1375
+ /**
1376
+ * Read compute's `/grasshopper/schema` body: `[{ FileName, Schemas }]` per
1377
+ * uploaded file, or a bare object for a single file.
1378
+ *
1379
+ * Use this rather than unwrapping the body by hand. The wrapper's casing varies
1380
+ * by server branch — mcneel serializes `FileName`/`Schemas`, the VektorNode fork
1381
+ * `fileName`/`schemas` — so a fixed-key read silently yields `undefined` against
1382
+ * half the servers, and the endpoint answers 200 either way. The failure looks
1383
+ * like "this definition has no schemas", which sends you debugging the wrong
1384
+ * thing entirely.
1385
+ *
1386
+ * A blanket key-rewrite (the old `camelcaseKeys` approach) is NOT the fix: it
1387
+ * reaches inside the schemas and mangles user-authored names — `"Display3d"` →
1388
+ * `"display3d"`, value-list labels like `"Option A"` → `"optionA"`. Only the two
1389
+ * wrapper keys are read here; schema CONTENTS pass through untouched, which is
1390
+ * why `TSchema` is a pass-through type parameter this module never inspects.
1391
+ *
1392
+ * @typeParam TSchema - Your schema type (e.g. `UISchema`). Not inspected.
1393
+ * @param raw - The parsed JSON body.
1394
+ */
1395
+ declare function readSchemaResults<TSchema = unknown>(raw: unknown): SchemaEndpointResult<TSchema>[];
1396
+
1078
1397
  /**
1079
1398
  * Parse one raw Grasshopper input schema into a typed {@link InputParam}.
1080
1399
  * Validation failures are swallowed and replaced with a safe default; use
@@ -1356,4 +1675,4 @@ declare class TreeBuilder {
1356
1675
  private static clampValue;
1357
1676
  }
1358
1677
 
1359
- export { type BooleanInputType, type CacheOptions, type DataItem, type DataTree, type DataTreeDefault, type DataTreePath, type DataTreeValue, type DefaultValue, type DefinitionRef, 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 SchedulerMode, type SolveContext, type SolveDefinition, type SolveOptions, type SolveResult, SolveScheduler, type SolveSchedulerOptions, type TextInputType, TreeBuilder, type ValueListInputType, fetchDefinitionIO, fetchParsedDefinitionIO, hashDefinition, hashSolveInput, isDefinitionRef, processInput, solveGrasshopperDefinition, stableStringify };
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 };