@selvajs/compute 4.0.0-beta.1 → 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.cjs';
2
- export { c as FileData, P as ProcessedFile, f as downloadFileData, g as extractFilesFromComputeResponse } from './handle-files-DcA24IkS.cjs';
1
+ import { C as ComputeConfig, R as RetryPolicy, a as ComputeError, e as SolveDefinition, F as FileBaseInfo } from './handle-files-DMK9bCOW.cjs';
2
+ export { D as DefinitionRef, c as FileData, P as ProcessedFile, f as downloadFileData, g as extractFilesFromComputeResponse, i as isDefinitionRef } from './handle-files-DMK9bCOW.cjs';
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
  *
@@ -427,7 +688,7 @@ interface SolveSchedulerOptions {
427
688
  /**
428
689
  * Backpressure — cap on how many calls may wait in the FIFO queue (i.e.
429
690
  * excluding the ones already in flight). When the queue is full, a new
430
- * `solve()` is shed immediately with `code: QUEUE_FULL` (retryable, meant to
691
+ * `solve()` is rejected immediately with `code: QUEUE_FULL` (retryable, meant to
431
692
  * map to HTTP 503 + Retry-After) instead of piling up unbounded. Bounds the
432
693
  * miss path under load. Only applies to `queue` / `parallel` modes —
433
694
  * `latest-wins` has an intrinsic depth of 1. Default: unbounded.
@@ -435,7 +696,7 @@ interface SolveSchedulerOptions {
435
696
  maxQueueDepth?: number;
436
697
  /**
437
698
  * Backpressure — max time (ms) a call may sit queued before it starts
438
- * 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
439
700
  * `code: QUEUE_TIMEOUT` rather than burning compute on a stale request.
440
701
  * Bounds tail latency. Only applies to `queue` / `parallel` modes. Default:
441
702
  * no deadline.
@@ -490,7 +751,7 @@ type SolveResult = {
490
751
  definitionReuploaded?: boolean;
491
752
  } | {
492
753
  status: 'error';
493
- error: RhinoComputeError;
754
+ error: ComputeError;
494
755
  durationMs: number;
495
756
  } | {
496
757
  status: 'superseded';
@@ -549,7 +810,12 @@ declare class SolveScheduler {
549
810
  private readonly executor;
550
811
  private readonly baseConfig;
551
812
  private readonly mode;
552
- 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;
553
819
  private readonly maxQueueDepth;
554
820
  private readonly queueWaitMs;
555
821
  private readonly timeoutMs;
@@ -596,15 +862,26 @@ declare class SolveScheduler {
596
862
  get inFlightCount(): number;
597
863
  get queueDepth(): number;
598
864
  get lastResult(): GrasshopperComputeResponse | null;
599
- get lastError(): RhinoComputeError | null;
865
+ get lastError(): ComputeError | null;
600
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;
601
878
  /** Subscribe to state changes. */
602
879
  subscribe(listener: () => void): () => void;
603
880
  private notify;
604
881
  /**
605
882
  * Schedule a solve. Returns a promise that:
606
883
  * - Resolves with the compute response on success.
607
- * - Rejects with `RhinoComputeError` on failure.
884
+ * - Rejects with `ComputeError` on failure.
608
885
  * - Rejects with `code: ErrorCodes.SUPERSEDED` when the call was canceled because
609
886
  * newer values arrived (latest-wins mode).
610
887
  * - Rejects with `code: ErrorCodes.ABORTED` when the call was canceled via
@@ -646,8 +923,8 @@ declare class SolveScheduler {
646
923
  private shedAsQueueFull;
647
924
  /**
648
925
  * Arm the queue-wait deadline for an item about to be queued. If the item is
649
- * still waiting when it fires, it's shed as QUEUE_TIMEOUT and removed from the
650
- * 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}.
651
928
  */
652
929
  private armQueueWaitTimer;
653
930
  /** Clear a queued item's wait-deadline timer, if one is pending. */
@@ -772,9 +1049,9 @@ declare class GrasshopperClient {
772
1049
  * exponential backoff before giving up. Each probe is also bounded by a
773
1050
  * timeout so a hung connection can't stall construction.
774
1051
  *
775
- * @throws {RhinoComputeError} with code NETWORK_ERROR if the server stays
1052
+ * @throws {ComputeError} with code NETWORK_ERROR if the server stays
776
1053
  * unreachable across all attempts
777
- * @throws {RhinoComputeError} with code INVALID_CONFIG if configuration is invalid
1054
+ * @throws {ComputeError} with code INVALID_CONFIG if configuration is invalid
778
1055
  */
779
1056
  static create(config: GrasshopperComputeConfig): Promise<GrasshopperClient>;
780
1057
  /**
@@ -790,9 +1067,9 @@ declare class GrasshopperClient {
790
1067
  /**
791
1068
  * Run a compute job with a Grasshopper definition.
792
1069
  *
793
- * @throws {RhinoComputeError} with code INVALID_INPUT if definition is empty
794
- * @throws {RhinoComputeError} with code NETWORK_ERROR if server is offline
795
- * @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.
796
1073
  * On a partial-success response (some outputs computed, some errored) the
797
1074
  * error's `context.values` carries the outputs that did compute — pass
798
1075
  * `{ values } as GrasshopperComputeResponse` to the response processors to
@@ -827,7 +1104,7 @@ declare class GrasshopperClient {
827
1104
  /**
828
1105
  * Validates and normalizes a compute configuration.
829
1106
  *
830
- * @throws {RhinoComputeError} with code INVALID_CONFIG if configuration is invalid
1107
+ * @throws {ComputeError} with code INVALID_CONFIG if configuration is invalid
831
1108
  */
832
1109
  private normalizeComputeConfig;
833
1110
  }
@@ -1055,7 +1332,7 @@ declare function solveGrasshopperDefinition(dataTree: DataTree[], definition: So
1055
1332
  * @param definition - The Grasshopper definition (URL, base64 string, or Uint8Array)
1056
1333
  * @param config - Compute configuration (server URL, API key, etc.)
1057
1334
  * @returns Key-normalized inputs and outputs with no per-type processing
1058
- * @throws {RhinoComputeError} If fetch fails or response is invalid
1335
+ * @throws {ComputeError} If fetch fails or response is invalid
1059
1336
  *
1060
1337
  * @public Use `fetchParsedDefinitionIO()` for processed, type-safe inputs
1061
1338
  */
@@ -1069,7 +1346,7 @@ declare function fetchDefinitionIO(definition: string | Uint8Array, config: Comp
1069
1346
  * @param definition - The Grasshopper definition (URL, base64 string, or Uint8Array)
1070
1347
  * @param config - Compute configuration (server URL, API key, etc.)
1071
1348
  * @returns Processed inputs with discriminated union types and outputs
1072
- * @throws {RhinoComputeError} If fetch fails or response is invalid
1349
+ * @throws {ComputeError} If fetch fails or response is invalid
1073
1350
  *
1074
1351
  * @example
1075
1352
  * ```typescript
@@ -1088,6 +1365,35 @@ declare function fetchDefinitionIO(definition: string | Uint8Array, config: Comp
1088
1365
  */
1089
1366
  declare function fetchParsedDefinitionIO(definition: string | Uint8Array, config: ComputeConfig): Promise<GrasshopperParsedIO>;
1090
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
+
1091
1397
  /**
1092
1398
  * Parse one raw Grasshopper input schema into a typed {@link InputParam}.
1093
1399
  * Validation failures are swallowed and replaced with a safe default; use
@@ -1369,4 +1675,4 @@ declare class TreeBuilder {
1369
1675
  private static clampValue;
1370
1676
  }
1371
1677
 
1372
- 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 };