@selvajs/compute 5.0.0-beta.7 → 5.0.0-beta.9

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,1355 +0,0 @@
1
- import { D as ComputeAuth, M as ComputeError, O as ComputeConfig, _ as SolveDefinition, j as ServerTiming, k as RetryPolicy, s as FileBaseInfo } from "./index-DDmprCht.cjs";
2
- interface ServerVersion {
3
- rhino: string;
4
- compute: string;
5
- git_sha: string | null;
6
- }
7
- interface ServerStats {
8
- isOnline: boolean;
9
- version?: ServerVersion;
10
- activeChildren?: number;
11
- }
12
- /**
13
- * ComputeServerStats provides methods to query Rhino Compute server statistics.
14
- *
15
- * @public Use this for server health monitoring and statistics.
16
- *
17
- * @example
18
- * ```typescript
19
- * const stats = new ComputeServerStats('http://localhost:6500', 'your-api-key');
20
- *
21
- * try {
22
- * const isOnline = await stats.isServerOnline();
23
- * const children = await stats.getActiveChildren();
24
- * const version = await stats.getVersion();
25
- *
26
- * // Or get everything at once
27
- * const allStats = await stats.getServerStats();
28
- * } finally {
29
- * await stats.dispose(); // Clean up resources
30
- * }
31
- * ```
32
- */
33
- declare class ComputeServerStats {
34
- private readonly serverUrl;
35
- private readonly headers;
36
- private disposed;
37
- private activeMonitors;
38
- /** Timeout (ms) for the fast read/monitoring endpoints. */
39
- private static readonly DEFAULT_TIMEOUT_MS;
40
- /** Timeout (ms) for child-lifecycle POSTs — a cold Windows child can take ~30s to spawn. */
41
- private static readonly LIFECYCLE_TIMEOUT_MS;
42
- /** Floor for `monitor()`'s `intervalMs` — anything lower hot-loops the server. */
43
- private static readonly MIN_MONITOR_INTERVAL_MS;
44
- /** Every control-plane answer is a count, a version or a short map. */
45
- private static readonly MAX_BODY_BYTES;
46
- /**
47
- * @param serverUrl - Base URL of the Rhino Compute server with http:// or https:// scheme (e.g., 'http://localhost:6500')
48
- * @param auth - An API key, or the same auth fields a solve's `ComputeConfig` carries
49
- * (`apiKey`, `apiKeyHeader`, `authToken`, `headers`).
50
- */
51
- constructor(serverUrl: string, auth?: string | ComputeAuth);
52
- /**
53
- * Aborts after `timeoutMs` so a hung connection can't stall a probe or `monitor()`; `0` disables
54
- * it. Redirects are not followed: fetch would carry the API key header to the target.
55
- */
56
- private fetchWithTimeout;
57
- /**
58
- * The response body of `path`, or `null` (logged) on a non-2xx or a network
59
- * failure. Throws only when disposed.
60
- */
61
- private getText;
62
- /**
63
- * Check if the server is online.
64
- *
65
- * This is a single-sample probe: it returns `true` only on a 2xx or a redirect
66
- * from the proxy liveness root `/`, and `false` for every other outcome (4xx/5xx,
67
- * network error, or timeout). A cold or briefly-busy-but-up server can therefore
68
- * read as offline — callers that gate on this (e.g. client construction)
69
- * should retry rather than treat a single `false` as authoritative.
70
- *
71
- * @param timeoutMs - Abort the probe after this many ms (default: 5000).
72
- * Pass `0` to disable the timeout. Prevents a hung connection from
73
- * stalling the caller indefinitely.
74
- */
75
- isServerOnline(timeoutMs?: number): Promise<boolean>;
76
- /**
77
- * Detailed liveness probe backing {@link isServerOnline}.
78
- *
79
- * Reports what the probe actually saw so callers can distinguish "connection
80
- * failed" (`error` set) from "server answered non-2xx" (`status` set) — e.g.
81
- * a 401 from a proxy that requires an API key is a misconfiguration, not an
82
- * offline server. Same single-sample caveat as {@link isServerOnline}.
83
- *
84
- * @param timeoutMs - Abort the probe after this many ms (default: 5000).
85
- * Pass `0` to disable the timeout.
86
- * @returns `online` plus either the HTTP `status` the server answered with,
87
- * or the `error` the connection attempt failed with.
88
- */
89
- probeServer(timeoutMs?: number): Promise<{
90
- online: boolean;
91
- status?: number;
92
- error?: string;
93
- }>;
94
- /**
95
- * Get the number of active child processes on the server.
96
- *
97
- * By default the proxy's `/activechildren` endpoint will *spawn* children up
98
- * to the configured count if none are running, then report the count — which
99
- * wakes (and bills) an idle server. Pass `{ initialize: false }` for a passive
100
- * read that reports the current count without spawning; use this for
101
- * monitoring or before a purge/probe where you must not wake the server.
102
- *
103
- * @param options.initialize - When `false`, append `?initialize=false` so the
104
- * server reports without spawning. Defaults to `true` (the server's default).
105
- * @returns Number of active children, or null if unavailable
106
- */
107
- getActiveChildren(options?: {
108
- initialize?: boolean;
109
- }): Promise<number | null>;
110
- /**
111
- * Get the server version information.
112
- *
113
- * @returns Version object with rhino, compute, and git_sha, or null if unavailable
114
- */
115
- getVersion(): Promise<ServerVersion | null>;
116
- /**
117
- * Get the plugins installed on the server.
118
- *
119
- * Returns a `name → version` map of non-core plugins the server has loaded,
120
- * or `null` if the request failed. Pass `kind` to choose which inventory:
121
- * `'gh'` (default) lists Grasshopper add-on assemblies via
122
- * `/plugins/gh/installed`; `'rhino'` lists Rhino plugins via
123
- * `/plugins/rhino/installed`. Plugins that ship with Rhino / are core
124
- * libraries are excluded by the server.
125
- *
126
- * @param kind - `'gh'` for Grasshopper add-ons (default) or `'rhino'` for Rhino plugins.
127
- * @returns Map of plugin name to version, or `null` on failure.
128
- *
129
- * @example
130
- * ```ts
131
- * const gh = await stats.getInstalledPlugins(); // Grasshopper add-ons
132
- * const selvaVersion = gh?.['Selva'] ?? null;
133
- * ```
134
- */
135
- getInstalledPlugins(kind?: 'gh' | 'rhino'): Promise<Record<string, string> | null>;
136
- /**
137
- * Get comprehensive server statistics.
138
- * Fetches all available server information in parallel.
139
- *
140
- * @returns Object containing server status and available stats
141
- */
142
- getServerStats(): Promise<ServerStats>;
143
- /**
144
- * Purge the server's solve-results / URL-data cache.
145
- *
146
- * POSTs to `cache/purge` and returns the number of entries removed, or `null`
147
- * if the request failed. This clears cached solve responses and fetched
148
- * definition-URL data; it does NOT evict the definition cache (active
149
- * `pointer` references stay valid).
150
- *
151
- * **Caveat:** `cache/purge` is forwarded by the rhino.compute proxy to a
152
- * single round-robin-selected child, so in a multi-child deployment one call
153
- * purges one child's cache. Call repeatedly (or size the pool to 1) if you
154
- * need a fleet-wide purge.
155
- *
156
- * @returns Number of entries removed, or `null` on failure.
157
- *
158
- * @example
159
- * ```ts
160
- * const removed = await stats.purgeCache();
161
- * if (removed !== null) console.log(`Purged ${removed} cached solves`);
162
- * ```
163
- */
164
- purgeCache(): Promise<number | null>;
165
- /**
166
- * Best-effort fleet-wide cache purge across a multi-child deployment.
167
- *
168
- * A single {@link purgeCache} POST is forwarded by the rhino.compute proxy to
169
- * just ONE round-robin-selected child, so the other children keep serving
170
- * stale cached solves. There is no proxy endpoint that addresses children
171
- * individually, so this method reads the active child count (passively, never
172
- * spawning) and fires `2 × count` sequential purges, relying on the proxy's
173
- * round-robin to spread the hits across the pool.
174
- *
175
- * **This is best-effort, not a guarantee.** Round-robin can revisit one child
176
- * and skip another; under concurrent traffic the rotation drifts. The result's
177
- * `confident` flag is `true` only when the server reports a single child (where
178
- * one purge is exact) — surface it so callers don't over-promise. For a hard
179
- * fleet-wide guarantee, run the deployment at `--childcount 1` or add a
180
- * server-side fan-out endpoint.
181
- *
182
- * @returns `{ totalPurged, calls, children, confident }`, or `null` if the
183
- * child count couldn't be read (server unreachable). `totalPurged` sums the
184
- * per-call counts; `calls` is how many purges were issued; `children` is the
185
- * reported pool size; `confident` is `true` only at a single-child pool.
186
- *
187
- * @example
188
- * ```ts
189
- * const r = await stats.purgeAllChildren();
190
- * if (r && !r.confident) {
191
- * console.warn(`Purged ~${r.totalPurged} across ${r.children} children (best-effort)`);
192
- * }
193
- * ```
194
- */
195
- purgeAllChildren(): Promise<{
196
- totalPurged: number;
197
- calls: number;
198
- children: number;
199
- confident: boolean;
200
- } | null>;
201
- /**
202
- * Get the server's current UTC clock.
203
- *
204
- * GETs `/servertime`, which the server emits as a JSON-encoded ISO-8601
205
- * timestamp (e.g. `"2026-06-18T08:30:00Z"`). Useful for detecting clock skew
206
- * between caller and server. Returns `null` if the request failed or the body
207
- * isn't a parseable date.
208
- *
209
- * @returns A `Date` for the server's UTC time, or `null` on failure.
210
- */
211
- getServerTime(): Promise<Date | null>;
212
- /**
213
- * Get how long the rhino.compute proxy has been idle.
214
- *
215
- * GETs `/idlespan` on the proxy, which returns the seconds elapsed since the
216
- * last request was forwarded to a compute child. This is a proxy-level metric
217
- * (not proxied to a child) used by autoscalers to decide when a node can be
218
- * drained. Returns `null` if unavailable.
219
- *
220
- * @returns Idle time in seconds, or `null` on failure.
221
- */
222
- getIdleSpan(): Promise<number | null>;
223
- /**
224
- * Fill the compute child pool up to the server's configured baseline.
225
- *
226
- * POSTs `/launch-children`. No-op when the pool is already at or above the
227
- * configured `--childcount`. Returns `{ spawned, active }` — how many children
228
- * were started and the resulting child count — or `null` on failure.
229
- *
230
- * To raise capacity above the baseline use {@link launchChild}; the baseline
231
- * itself can only be changed by restarting rhino.compute.
232
- *
233
- * @returns `{ spawned, active }`, or `null` on failure.
234
- */
235
- launchChildren(): Promise<{
236
- spawned: number[];
237
- active: number;
238
- } | null>;
239
- /**
240
- * Add a single compute child to the pool, optionally on a specific port.
241
- *
242
- * POSTs `/launch-child` (with `?port=N` when `port` is given). Unlike
243
- * {@link launchChildren}, this can push the pool above the baseline, up to the
244
- * server's `MaxChildren` cap. Returns `{ spawned: [port] }` on success, or
245
- * `null` on failure (server replies 400 for a bad port, 409 if the port is in
246
- * use, 503 at the max-children cap).
247
- *
248
- * @param port - Optional specific port to launch on; otherwise the next free one.
249
- * @returns `{ spawned }` listing the launched port, or `null` on failure.
250
- */
251
- launchChild(port?: number): Promise<{
252
- spawned: number[];
253
- } | null>;
254
- /**
255
- * Gracefully shut down compute children without respawning them.
256
- *
257
- * POSTs `/shutdown-children`. With no `port` it shuts down every child; with
258
- * `port` it targets just that one. Children do not respawn, but the next
259
- * `/grasshopper` request auto-spawns the pool back to the baseline. Returns
260
- * `{ shutdown, active }` — how many were stopped and the remaining count — or
261
- * `null` on failure.
262
- *
263
- * @param port - Optional port to target; omit to shut down all children.
264
- * @returns `{ shutdown, active }`, or `null` on failure.
265
- */
266
- shutdownChildren(port?: number): Promise<{
267
- shutdown: number;
268
- active: number;
269
- } | null>;
270
- /**
271
- * Shut down compute children and respawn replacements (rolling restart).
272
- *
273
- * POSTs `/recycle-children`. With no `port` it recycles every child; with
274
- * `port` it recycles just that one. The server recycles sequentially (each
275
- * replacement is serving before the next child is stopped) so the pool never
276
- * drops to zero mid-recycle. Returns `{ shutdown, spawned, active }`, or
277
- * `null` on failure.
278
- *
279
- * @param port - Optional port to target; omit to recycle all children.
280
- * @returns `{ shutdown, spawned, active }`, or `null` on failure.
281
- */
282
- recycleChildren(port?: number): Promise<{
283
- shutdown: number;
284
- spawned: number[];
285
- active: number;
286
- } | null>;
287
- /** A child-lifecycle POST: a spawn or recycle can take ~30 s. */
288
- private postJson;
289
- /**
290
- * Continuously monitor server stats at specified interval.
291
- *
292
- * @param callback - Function called with stats on each interval
293
- * @param intervalMs - Milliseconds between checks (default: 5000). Must be a
294
- * finite number of at least 100 ms — lower values would hot-loop the server.
295
- * @returns Function to stop monitoring
296
- * @throws {ComputeError} `INVALID_CONFIG` if `intervalMs` is not a finite number >= 100.
297
- *
298
- * @example
299
- * ```typescript
300
- * const stopMonitoring = stats.monitor((data) => {
301
- * console.log('Server stats:', data);
302
- * }, 3000);
303
- *
304
- * // Later...
305
- * stopMonitoring();
306
- * ```
307
- */
308
- monitor(callback: (stats: ServerStats) => void, intervalMs?: number): () => void;
309
- /**
310
- * Disposes of all resources and stops all active monitors.
311
- * Call this when you're done using the stats instance.
312
- */
313
- dispose(): Promise<void>;
314
- private ensureNotDisposed;
315
- }
316
- type DataTreePath = `{${string}}`;
317
- interface DataItem {
318
- type: string;
319
- data: string;
320
- id: string;
321
- }
322
- type DataTreeDefault<T = any> = { [K in DataTreePath]?: T[]; };
323
- type InnerTreeData = { [path in DataTreePath]: DataItem[]; };
324
- /**
325
- * Tree with parameter metadata (used in compute requests and responses).
326
- *
327
- * This client sends PascalCase (`TreeBuilder.toComputeFormat()`), so those
328
- * fields are required; a response's casing varies by server branch, so read
329
- * one with `readField` rather than by property.
330
- */
331
- interface DataTree {
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;
340
- }
341
- /**
342
- * Union type for all possible default value types
343
- */
344
- type DefaultValue<T> = T | T[] | DataTreeDefault<T> | undefined | null;
345
- /**
346
- * Base properties common to all processed input types.
347
- * Note: `groupName` and `id` require the custom Rhino Compute branch.
348
- */
349
- interface BaseInputType {
350
- description: string;
351
- name: string;
352
- nickname: string | null;
353
- treeAccess: boolean;
354
- /**
355
- * Name of the group this parameter belongs to.
356
- * @requires Custom branch of compute.rhino3d
357
- */
358
- groupName?: string;
359
- /**
360
- * Unique identifier for the parameter.
361
- * @requires Custom branch of compute.rhino3d
362
- */
363
- id?: string;
364
- }
365
- /**
366
- * Numeric input type (Number or Integer)
367
- */
368
- interface NumericInputType extends BaseInputType {
369
- paramType: 'Number' | 'Integer';
370
- minimum?: number | null;
371
- maximum?: number | null;
372
- atLeast?: number | null;
373
- atMost?: number | null;
374
- stepSize?: number | null;
375
- default: DefaultValue<number>;
376
- }
377
- /**
378
- * Text input type
379
- */
380
- interface TextInputType extends BaseInputType {
381
- paramType: 'Text';
382
- default: DefaultValue<string>;
383
- }
384
- /**
385
- * Boolean input type
386
- */
387
- interface BooleanInputType extends BaseInputType {
388
- paramType: 'Boolean';
389
- default: DefaultValue<boolean>;
390
- }
391
- /**
392
- * Geometry input type (generic geometry)
393
- */
394
- interface GeometryInputType extends BaseInputType {
395
- paramType: 'Geometry';
396
- default: DefaultValue<object | string>;
397
- }
398
- /**
399
- * ValueList input type (dropdown/select)
400
- */
401
- interface ValueListInputType extends BaseInputType {
402
- paramType: 'ValueList';
403
- values: Record<string, string>;
404
- default?: string;
405
- }
406
- /**
407
- * File input type
408
- */
409
- interface FileInputType extends BaseInputType {
410
- paramType: 'File';
411
- acceptedFormats?: string[];
412
- default: DefaultValue<object | string>;
413
- }
414
- /**
415
- * Color input type. Values are `"R, G, B"` strings (`"255, 0, 0"`), not hex.
416
- */
417
- interface ColorInputType extends BaseInputType {
418
- paramType: 'Color';
419
- default: DefaultValue<string>;
420
- }
421
- /**
422
- * Discriminated union of all input parameter types
423
- */
424
- type InputParam = NumericInputType | BooleanInputType | TextInputType | ValueListInputType | GeometryInputType | FileInputType | ColorInputType;
425
- type RhinoModelUnit = 'None' | 'Microns' | 'Millimeters' | 'Centimeters' | 'Decimeters' | 'Meters' | 'Kilometers' | 'Mils' | 'Inches' | 'Feet' | 'Yards' | 'Miles' | 'CustomUnits' | 'Unset';
426
- /**
427
- * Base Grasshopper schema properties shared by config, args, and response
428
- */
429
- interface GrasshopperBaseSchema {
430
- /** Absolute tolerance used in computation */
431
- absolutetolerance?: number | null;
432
- /** Angular tolerance used in computation */
433
- angletolerance?: number | null;
434
- /** Model units used */
435
- modelunits?: RhinoModelUnit | null;
436
- /** Data version (7 or 8) */
437
- dataversion?: 7 | 8 | null;
438
- /** Whether to use cached solution */
439
- cachesolve?: boolean | null;
440
- /**
441
- * Opt-in: cache a solve even when the definition reported GH errors. Default
442
- * (false/unset) means errored solves are never cached server-side. Some
443
- * definitions throw GH errors by design (a guarded Python component, a
444
- * filtered branch) while still producing correct geometry — set this so those
445
- * still benefit from the server's solve cache. Only meaningful with
446
- * `cachesolve`. Requires a server that honors it (VektorNode fork).
447
- */
448
- cacheerroredsolves?: boolean | null;
449
- }
450
- /**
451
- * Where a Selva-family plugin running inside the solve may POST live events, and
452
- * the bearer it presents. The VektorNode fork hands these to the document as
453
- * constants; a stock server ignores the block. Never echoed in the response.
454
- */
455
- interface SelvaEventTarget {
456
- url: string;
457
- solveId: string;
458
- token: string;
459
- }
460
- /**
461
- * Definition source (used in args and response)
462
- */
463
- interface GrasshopperDefinitionSource {
464
- /** Base64 encoded algorithm (if embedded) */
465
- algo?: string | null;
466
- /** URL pointer to definition file */
467
- pointer?: string | null;
468
- /** Filename of the definition */
469
- filename?: string | null;
470
- }
471
- /**
472
- * Server config plus the Grasshopper solve settings. The definition is not part
473
- * of it: pass that to `solve()` and friends.
474
- */
475
- interface GrasshopperComputeConfig extends ComputeConfig, GrasshopperBaseSchema {
476
- /** Per-solve live-event callback; see {@link SelvaEventTarget}. */
477
- selvaevents?: SelvaEventTarget | null;
478
- }
479
- /**
480
- * Response from Grasshopper compute server.
481
- *
482
- * No `pointer`: the solve returns the server's echoed pointer as its `cacheKey`
483
- * instead. The echoed settings (`cachesolve`, `dataversion`) stay optional:
484
- * nothing client-side enforces them.
485
- */
486
- interface GrasshopperComputeResponse extends GrasshopperBaseSchema, Omit<GrasshopperDefinitionSource, 'pointer'> {
487
- /**
488
- * Model units the definition was solved in. Every conforming server response
489
- * carries this (it drives downstream scaling, e.g. the webdisplay parser),
490
- * but note it is not validated client-side — a non-conforming server or
491
- * hand-built mock may omit it at runtime.
492
- */
493
- modelunits: RhinoModelUnit;
494
- /** Always `undefined` on a client-returned response: the solve strips it. */
495
- algo?: string | null;
496
- /** Filename of the definition (always present in response) */
497
- filename: string | null;
498
- /** Recursion level used */
499
- recursionlevel?: number;
500
- /** Output values organized by parameter */
501
- values: DataTree[];
502
- /** Computation errors */
503
- errors?: string[];
504
- /** Computation warnings */
505
- warnings?: string[];
506
- /** Set by the VektorNode fork when a Selva plugin judged the solve; see {@link SelvaResponseBlock}. */
507
- selva?: SelvaResponseBlock | null;
508
- }
509
- /**
510
- * The Selva plugin's verdict on a solve, which the VektorNode fork copies verbatim into the
511
- * response. `outcome` is `SolveOutcome` from `@selvajs/schemas`, typed loosely here because
512
- * this package does not depend on it. A blocked or aborted outcome comes with empty `values`.
513
- * Absent from a stock server, an older plugin, or a definition without a Selva component.
514
- */
515
- interface SelvaResponseBlock {
516
- outcome?: unknown;
517
- }
518
- /**
519
- * Output parameter
520
- */
521
- interface OutputParamSchema {
522
- name: string;
523
- nickname: string | null;
524
- paramType: string;
525
- /**
526
- * Grasshopper parameter instance GUID
527
- */
528
- id: string;
529
- }
530
- /**
531
- * Input parameter
532
- */
533
- interface InputParamSchema {
534
- /**
535
- * Grasshopper parameter instance GUID
536
- */
537
- id: string;
538
- name: string;
539
- nickname: string | null;
540
- description: string;
541
- paramType: string;
542
- treeAccess: boolean;
543
- minimum: number | null;
544
- maximum: number | null;
545
- atLeast: number;
546
- atMost: number;
547
- stepSize?: number;
548
- default: any;
549
- /**
550
- * Key-value pairs for dropdown options
551
- */
552
- values?: Record<string, string>;
553
- /**
554
- * Accepted file formats for File input
555
- */
556
- acceptedFormats?: string[];
557
- groupName?: string | null;
558
- }
559
- /** Input/output schemas as the `/io` endpoint reports them, keys canonicalized. */
560
- interface GrasshopperParsedIORaw {
561
- inputs: InputParamSchema[];
562
- outputs: OutputParamSchema[];
563
- /**
564
- * Warnings the server raised loading the definition (e.g. an obsolete
565
- * component). Distinct from `parseErrors`, the client failing to type an input.
566
- */
567
- loadWarnings?: string[];
568
- /**
569
- * Errors the server raised loading the definition (e.g. a missing plugin).
570
- * When present, `inputs`/`outputs` may be incomplete.
571
- */
572
- loadErrors?: string[];
573
- }
574
- /**
575
- * Per-input parse failure. The corresponding entry in `inputs` was filled
576
- * with a safe default so the rest of the pipeline can keep going — but the
577
- * caller should surface this so the user knows their definition has a
578
- * misconfigured parameter.
579
- */
580
- interface InputParseError {
581
- /** The input's `name` (or `'unknown'` if the schema didn't have one). */
582
- inputName: string;
583
- /** The declared paramType from the raw schema. */
584
- paramType: string;
585
- /** Human-readable reason from the underlying ComputeError. */
586
- message: string;
587
- /** Error code from the underlying ComputeError, if available. */
588
- code?: string;
589
- }
590
- /** {@link GrasshopperParsedIORaw} with each input parsed into its typed {@link InputParam}. */
591
- interface GrasshopperParsedIO extends Omit<GrasshopperParsedIORaw, 'inputs'> {
592
- inputs: InputParam[];
593
- /**
594
- * Inputs that failed validation and got a safe default. The result is still
595
- * usable, but surface these so the user can fix their definition.
596
- */
597
- parseErrors?: InputParseError[];
598
- }
599
- /**
600
- * How concurrent `solve()` calls interact.
601
- *
602
- * - `latest-wins`: one in flight at a time. A new call supersedes the pending
603
- * one and aborts the one in flight. For slider scrubs and live UIs.
604
- * - `queue`: FIFO, every call runs to completion, up to `maxConcurrent` at
605
- * once (default 1). For "submit job" flows where every request matters.
606
- */
607
- type SchedulerMode = 'latest-wins' | 'queue';
608
- interface CacheOptions {
609
- /**
610
- * Total byte budget for retained responses, evicted LRU. The only size bound
611
- * there is: responses range KB→100s of MB, so memory is the constraint that
612
- * matters and an entry count would only obscure it.
613
- *
614
- * Sizing uses the response's wire size (JSON text length, recorded at the
615
- * fetch boundary — no re-serialization); a response without that hint
616
- * (custom executor) is sized by a one-off `JSON.stringify`. A single
617
- * response larger than the whole budget is served but never retained.
618
- * Parsed, a response of many small items holds more heap than its wire size,
619
- * so leave headroom.
620
- *
621
- * Required and must be > 0 — to disable caching, pass `cache: false`.
622
- */
623
- maxBytes: number;
624
- /**
625
- * Time-to-live in ms. Set to `0` for no expiry (default, and the right
626
- * choice for a solve keyed by immutable definition+inputs — expiry there
627
- * only buys a paid re-solve of an identical answer). Meaningful only when a
628
- * definition reaches outside its inputs (external data source, clock), where
629
- * a stale result is genuinely wrong rather than merely old.
630
- *
631
- * Expiry is evaluated lazily on read: an expired entry keeps its bytes
632
- * counted against `maxBytes` until that exact key is next consulted.
633
- */
634
- ttlMs?: number;
635
- /**
636
- * Cache responses that carry Grasshopper `errors`. Default `true`: an
637
- * errored solve is a valid, deterministic result — definitions raise GH
638
- * errors by design (guarded components, validation branches), so replaying
639
- * one from cache is correct. Set `false` for parity with Rhino's opt-in
640
- * `cacheerroredsolves` server flag, e.g. when a definition's errors are
641
- * transient (external data sources) rather than functions of the inputs.
642
- */
643
- cacheErroredSolves?: boolean;
644
- }
645
- interface SolveSchedulerOptions {
646
- mode?: SchedulerMode;
647
- maxConcurrent?: number;
648
- /**
649
- * Backpressure — cap on how many calls may wait in the FIFO queue (i.e.
650
- * excluding the ones already in flight). When the queue is full, a new
651
- * `solve()` is rejected immediately with `code: QUEUE_FULL` (retryable, meant to
652
- * map to HTTP 503 + Retry-After) instead of piling up unbounded. Bounds the
653
- * miss path under load. Only applies to `queue` mode —
654
- * `latest-wins` has an intrinsic depth of 1. Default: unbounded.
655
- */
656
- maxQueueDepth?: number;
657
- /**
658
- * Backpressure — max time (ms) a call may sit queued before it starts
659
- * executing. If it's still waiting after this long it's rejected with
660
- * `code: QUEUE_TIMEOUT` rather than burning compute on a stale request.
661
- * Bounds tail latency. Only applies to `queue` mode. Default: no deadline.
662
- */
663
- queueWaitMs?: number;
664
- timeoutMs?: number;
665
- retry?: RetryPolicy;
666
- /**
667
- * Response caching keyed by hash of (definition, dataTree). Omit or pass
668
- * `false` to disable; otherwise a byte budget is required, so an unbounded
669
- * cache can't be enabled by accident.
670
- */
671
- cache?: false | CacheOptions;
672
- /**
673
- * Upload a definition once, then solve it by the server's cache key (`pointer`)
674
- * instead of re-sending the bytes; a `DefinitionRef` also skips its `load()`.
675
- * When the server has evicted the key, the solve falls back to a full upload,
676
- * so this is safe to leave on. No effect for URL definitions, which are
677
- * already a reference. Default `true`.
678
- */
679
- reuseServerDefinitionCache?: boolean;
680
- /** Lifecycle hooks — fired in order. Errors thrown by hooks are logged, not rethrown. */
681
- onStart?: (ctx: SolveContext) => void;
682
- onSettle?: (ctx: SolveContext, result: SolveResult) => void;
683
- onSuperseded?: (ctx: SolveContext) => void;
684
- }
685
- interface SolveContext {
686
- /** Stable hash of (definition, dataTree). */
687
- key: string;
688
- /** Timestamp when scheduler.solve() was called. */
689
- enqueuedAt: number;
690
- /** Timestamp when execution actually started (after queueing). */
691
- startedAt: number | null;
692
- }
693
- type SolveResult = {
694
- status: 'success';
695
- response: GrasshopperComputeResponse;
696
- durationMs: number;
697
- fromCache: boolean;
698
- /**
699
- * Whether this solve uploaded the full definition: `true` on its first
700
- * solve or after the server evicted the cache key, `false` when the server
701
- * reused its cached copy via the pointer. `undefined` for a `fromCache`
702
- * hit, or when reuse is off or the definition is a URL.
703
- */
704
- definitionReuploaded?: boolean;
705
- } | {
706
- status: 'error';
707
- error: ComputeError;
708
- durationMs: number;
709
- } | {
710
- status: 'superseded';
711
- };
712
- /**
713
- * Solves by `cacheKey` when given one, otherwise uploads. Reports the server's
714
- * (possibly new) key and whether the full definition was uploaded (`missed`).
715
- * The scheduler passes `null` whenever server-definition reuse is off.
716
- */
717
- type SolveExecutor = (definition: SolveDefinition, dataTree: DataTree[], cacheKey: string | null, config: GrasshopperComputeConfig) => Promise<{
718
- response: GrasshopperComputeResponse;
719
- cacheKey: string | null;
720
- missed: boolean;
721
- }>;
722
- interface SchedulerSolveOptions {
723
- signal?: AbortSignal;
724
- /** Per-solve live-event callback. Not part of the cache key: it names where events go, not what is solved. */
725
- selvaevents?: SelvaEventTarget;
726
- /**
727
- * This call's `Server-Timing` breakdown, after the client config's own `onServerTiming`.
728
- * Never fires for a `fromCache` hit, which sends no request.
729
- */
730
- onServerTiming?: (timing: ServerTiming, requestId: string) => void;
731
- }
732
- /** What `solveDetailed()` resolves with: the response plus how this call got it. */
733
- interface SolveDetail {
734
- response: GrasshopperComputeResponse;
735
- /** Served from the scheduler's response cache without a request. */
736
- fromCache: boolean;
737
- /** Same meaning as on {@link SolveResult}. */
738
- definitionReuploaded?: boolean;
739
- /** Executor wall time; `0` for a `fromCache` hit. */
740
- durationMs: number;
741
- }
742
- /**
743
- * Adds to a plain solve call:
744
- * - Scheduling (latest-wins for sliders, queue for jobs)
745
- * - Backpressure (bounded queue depth + queue-wait deadline)
746
- * - Cancellation (per-call signal + cancelAll)
747
- * - Optional response caching for repeated inputs
748
- * - Lifecycle hooks (start / settle / superseded) and state via subscribe()
749
- *
750
- * @example
751
- * ```ts
752
- * const scheduler = client.createScheduler({ mode: 'latest-wins', timeoutMs: 30_000 });
753
- *
754
- * // From a slider handler:
755
- * scheduler.solve(definition, tree).then((result) => {
756
- * updateMeshes(result);
757
- * }).catch((err) => {
758
- * if (err.code !== 'SUPERSEDED') showError(err);
759
- * });
760
- *
761
- * // From a UI binding:
762
- * scheduler.subscribe(() => {
763
- * showSpinner = scheduler.isSolving;
764
- * });
765
- * ```
766
- */
767
- export declare class SolveScheduler {
768
- private readonly executor;
769
- private readonly baseConfig;
770
- private readonly mode;
771
- private maxConcurrent;
772
- private readonly maxQueueDepth;
773
- private readonly queueWaitMs;
774
- private readonly timeoutMs;
775
- private readonly retry;
776
- private readonly cacheEnabled;
777
- private readonly cacheMaxBytes;
778
- private readonly cacheTtl;
779
- private readonly cacheErroredSolves;
780
- private readonly cache;
781
- private cacheBytes;
782
- private cacheHits;
783
- private cacheMisses;
784
- private cacheEvictions;
785
- private readonly reuseServerDefinitionCache;
786
- /** Definition hash → the server's cache key (`pointer`) for it. */
787
- private readonly serverCacheKeys;
788
- /** A string definition re-sent unchanged is compared with this, far cheaper than hashing it. */
789
- private lastStringDefinition;
790
- private readonly onStart?;
791
- private readonly onSettle?;
792
- private readonly onSuperseded?;
793
- private readonly subscribers;
794
- private readonly inFlight;
795
- private pendingForLatestWins;
796
- private readonly fifo;
797
- private _lastResult;
798
- private _lastError;
799
- private _lastDurationMs;
800
- private solveSeq;
801
- /** seq of the solve that last wrote `_lastResult`/`_lastError`. */
802
- private lastStateSeq;
803
- private disposed;
804
- constructor(executor: SolveExecutor, baseConfig: GrasshopperComputeConfig, options?: SolveSchedulerOptions);
805
- get isSolving(): boolean;
806
- get hasPending(): boolean;
807
- get inFlightCount(): number;
808
- get queueDepth(): number;
809
- get lastResult(): GrasshopperComputeResponse | null;
810
- get lastError(): ComputeError | null;
811
- get lastDurationMs(): number | null;
812
- /**
813
- * Adjust how many solves may run at once, for when the compute server's worker
814
- * pool changes size after this scheduler was built.
815
- *
816
- * Raising it drains queued work immediately. Lowering it never interrupts work
817
- * already in flight — those finish above the new limit, and the cap applies from
818
- * the next dispatch. Values below 1 are clamped, since 0 would wedge the queue.
819
- */
820
- setMaxConcurrent(value: number): void;
821
- /** Current concurrency cap — reflects any {@link setMaxConcurrent} adjustment. */
822
- getMaxConcurrent(): number;
823
- /** Subscribe to state changes. */
824
- subscribe(listener: () => void): () => void;
825
- private notify;
826
- /**
827
- * Schedule a solve. Returns a promise that:
828
- * - Resolves with the compute response on success.
829
- * - Rejects with `ComputeError` on failure.
830
- * - Rejects with `code: ErrorCodes.SUPERSEDED` when the call was canceled because
831
- * newer values arrived (latest-wins mode).
832
- * - Rejects with `code: ErrorCodes.ABORTED` when the call was canceled via
833
- * its own `signal` (queued or running) or `cancelAll()`.
834
- * - Rejects with `code: ErrorCodes.QUEUE_FULL` when `maxQueueDepth` is set and
835
- * the queue was already full (backpressure; `statusCode: 503`).
836
- * - Rejects with `code: ErrorCodes.QUEUE_TIMEOUT` when `queueWaitMs` is set and
837
- * the call sat queued longer than that before starting (`statusCode: 503`).
838
- *
839
- * Don't mutate a `Uint8Array` definition after solving it. Responses served
840
- * from the cache (and via `lastResult`) are shared objects, not copies —
841
- * treat them as immutable. Mutating one poisons every later cache hit for
842
- * that key.
843
- *
844
- * Partial-success contract: unlike `GrasshopperClient.solve()`, which throws
845
- * `COMPUTATION_ERROR` when the response carries solver errors, this RESOLVES
846
- * with the response as-is — check `response.errors` yourself if a partial
847
- * success must not be treated as a result (see `cacheErroredSolves` for the
848
- * caching side of the same distinction).
849
- */
850
- solve(definition: SolveDefinition, dataTree: DataTree[], options?: SchedulerSolveOptions): Promise<GrasshopperComputeResponse>;
851
- /** {@link solve}, also resolving with whether this call hit the cache and how long it took. */
852
- solveDetailed(definition: SolveDefinition, dataTree: DataTree[], options?: SchedulerSolveOptions): Promise<SolveDetail>;
853
- /** A slow solve settling late must not overwrite state a newer solve or cache hit published. */
854
- private writeLastState;
855
- /** latest-wins: supersede the pending item and abort everything in flight. */
856
- private supersedeCurrent;
857
- private dequeue;
858
- private abortQueuedItem;
859
- /** The structured context lets an HTTP layer map QUEUE_FULL to 503 + Retry-After. */
860
- private shedAsQueueFull;
861
- private armQueueWaitTimer;
862
- private clearQueueWaitTimer;
863
- private enqueue;
864
- private execute;
865
- private runExecutor;
866
- private hashDefinitionMemo;
867
- private drainNext;
868
- private supersede;
869
- private makeAbortError;
870
- /**
871
- * The executor, `supersede`, `cancelAll`, abort and the queue limits can all race to settle one
872
- * item, and a promise ignores a second settle silently. The first settle wins; the return value
873
- * says whether this call won, so only the winner fires its hook. Every settle path goes through
874
- * here or {@link settleSuccess}.
875
- */
876
- private settleError;
877
- /**
878
- * Fires `onSettle` for queued items too, so a consumer counting solves in progress doesn't
879
- * leak one. A supersede settles through {@link settleError} and fires `onSuperseded` instead.
880
- */
881
- private fail;
882
- private settleSuccess;
883
- private removeQueuedAbortHandler;
884
- private normalizeExecutionError;
885
- /** Cancel everything — in-flight and pending. */
886
- cancelAll(): void;
887
- private readCache;
888
- private writeCache;
889
- private dropCacheEntry;
890
- clearCache(): void;
891
- /**
892
- * Observability snapshot of the solve cache: current size plus lifetime
893
- * hit/miss/eviction counters.
894
- *
895
- * `hits`/`misses` count cache CONSULTATIONS, so `hits / (hits + misses)` is the
896
- * hit rate. A TTL-expired entry counts as a miss (the solve runs either way).
897
- * `evictions` counts only entries dropped under size/byte pressure — not
898
- * replace-in-place writes or TTL expiry, which are not capacity signals.
899
- * Counters are cumulative and survive `clearCache()`.
900
- */
901
- cacheStats(): {
902
- entries: number;
903
- bytes: number;
904
- hits: number;
905
- misses: number;
906
- evictions: number;
907
- };
908
- dispose(): void;
909
- private runHook;
910
- }
911
- /**
912
- * Per-call options that override the client's default ComputeConfig values.
913
- *
914
- * Use these for per-request control without mutating the client config:
915
- * - `signal` — cancel a specific solve (e.g. when a slider value is superseded)
916
- * - `timeoutMs` — extend timeout for a long-running solve, or pass `0` to disable
917
- * - `retry` — override retry policy for this call only
918
- */
919
- interface SolveOptions {
920
- signal?: AbortSignal;
921
- timeoutMs?: number;
922
- retry?: RetryPolicy;
923
- /** Per-solve live-event callback, forwarded onto the request body. */
924
- selvaevents?: SelvaEventTarget;
925
- }
926
- /** Options for {@link GrasshopperClient.create} that shape construction, not the solves. */
927
- interface CreateClientOptions {
928
- /**
929
- * Retry ladder for the liveness probe: `attempts` retries after the first probe (default 2),
930
- * with exponential backoff from `baseDelayMs` (default 250) capped at `maxDelayMs` (default 1000).
931
- * Independent of `config.retry`, which governs solves only.
932
- */
933
- probe?: {
934
- attempts?: number;
935
- baseDelayMs?: number;
936
- maxDelayMs?: number;
937
- };
938
- }
939
- /**
940
- * GrasshopperClient provides a simple API for interacting with a Rhino Compute server and grasshopper.
941
- *
942
- * @public This is the recommended high-level API for Rhino Compute operations.
943
- *
944
- * **Security Warning:**
945
- * Using this client in a browser environment exposes your server URL and API key to users.
946
- * For production, use this library server-side or proxy requests through your own backend.
947
- *
948
- * @example
949
- * ```typescript
950
- * const client = await GrasshopperClient.create({
951
- * serverUrl: 'http://localhost:6500',
952
- * apiKey: 'your-api-key'
953
- * });
954
- *
955
- * try {
956
- * const io = await client.getIO(definitionUrl);
957
- * const result = await client.solve(definitionUrl, TreeBuilder.fromInputParams(io.inputs));
958
- * } finally {
959
- * await client.dispose(); // Clean up resources
960
- * }
961
- * ```
962
- */
963
- declare class GrasshopperClient {
964
- private readonly config;
965
- readonly serverStats: ComputeServerStats;
966
- private disposed;
967
- /**
968
- * Per-probe timeout for the `create()` liveness gate. A healthy `GET /` on the
969
- * proxy answers in milliseconds; this bound only ever applies to a server that
970
- * is not answering, where every extra second is multiplied by the retry count
971
- * and paid by whoever is waiting on the solve.
972
- */
973
- private static readonly CREATE_PROBE_TIMEOUT_MS;
974
- private constructor();
975
- /**
976
- * Creates and initializes a GrasshopperClient with server validation.
977
- *
978
- * The pre-flight liveness probe (a GET on the proxy root `/`) is a
979
- * single-sample boolean gate that reads a cold or briefly-busy-but-up server
980
- * as offline. To avoid failing construction on that transient class, the probe
981
- * is retried with a short exponential backoff before giving up.
982
- *
983
- * Retries stop early when {@link classifyProbeFailure} says waiting cannot
984
- * change the answer — connection refused, or a 401/403. This runs in front of
985
- * a user who clicked Solve, so the retry ladder must not spend its whole
986
- * budget confirming that a machine is switched off.
987
- *
988
- * @throws {ComputeError} with code NETWORK_ERROR if the server stays
989
- * unreachable across all attempts. `context.probeVerdict` carries the
990
- * {@link ProbeVerdict} so callers can render a specific cause.
991
- * @throws {ComputeError} with code INVALID_CONFIG if configuration is invalid
992
- */
993
- static create(config: GrasshopperComputeConfig, options?: CreateClientOptions): Promise<GrasshopperClient>;
994
- /**
995
- * Gets the client's configuration.
996
- * Useful for passing to lower-level functions.
997
- */
998
- getConfig(): GrasshopperComputeConfig;
999
- /**
1000
- * Get input/output parameters of a Grasshopper definition.
1001
- */
1002
- getIO(definition: string | Uint8Array): Promise<GrasshopperParsedIO>;
1003
- getRawIO(definition: string | Uint8Array): Promise<GrasshopperParsedIORaw>;
1004
- /**
1005
- * Run a compute job with a Grasshopper definition.
1006
- *
1007
- * @throws {ComputeError} with code INVALID_INPUT if definition is empty
1008
- * @throws {ComputeError} with code NETWORK_ERROR if server is offline
1009
- * @throws {ComputeError} with code COMPUTATION_ERROR if computation fails.
1010
- * On a partial-success response (some outputs computed, some errored) the
1011
- * error's `context.values` carries the outputs that did compute — pass
1012
- * `{ values } as GrasshopperComputeResponse` to the response processors to
1013
- * render them. `context.inputSummary` describes the inputs (param names,
1014
- * item counts, byte sizes) without pinning the full data tree.
1015
- */
1016
- solve(definition: SolveDefinition, dataTree: DataTree[], options?: SolveOptions): Promise<GrasshopperComputeResponse>;
1017
- /**
1018
- * Create a scheduler bound to this client. Use a scheduler for any UI surface
1019
- * that fires solves frequently (sliders, live editors) or that needs cancel
1020
- * semantics, response caching, or state observability.
1021
- *
1022
- * Multiple schedulers can be created from a single client — typically one per
1023
- * UI surface so their queues stay independent.
1024
- *
1025
- * @example
1026
- * ```ts
1027
- * const sliderScheduler = client.createScheduler({ mode: 'latest-wins' });
1028
- * const submitScheduler = client.createScheduler({ mode: 'queue', timeoutMs: 0, retry: { attempts: 1 } });
1029
- * ```
1030
- */
1031
- createScheduler(options?: SolveSchedulerOptions): SolveScheduler;
1032
- /**
1033
- * Disposes of client resources.
1034
- * Call this when you're done using the client.
1035
- */
1036
- dispose(): Promise<void>;
1037
- private ensureNotDisposed;
1038
- }
1039
- interface ParsedContext {
1040
- [key: string]: any;
1041
- }
1042
- interface GetValuesOptions {
1043
- parseValues?: boolean;
1044
- rhino?: any;
1045
- /**
1046
- * If true, only include values of type System.String in the result.
1047
- * Non-string types are filtered out.
1048
- */
1049
- stringOnly?: boolean;
1050
- }
1051
- interface GetValuesResult<T = ParsedContext> {
1052
- values: T;
1053
- /**
1054
- * Free every rhino3dm WASM object decoded into `values`. When a `rhino`
1055
- * instance was passed, decoded geometry lives on the WASM heap and is never
1056
- * garbage-collected — call this once the values are consumed, or the heap
1057
- * grows monotonically across solves. Idempotent; a no-op when nothing was
1058
- * decoded. `values` must not be used after disposal.
1059
- */
1060
- dispose: () => void;
1061
- }
1062
- /** Reads parsed values and produced files out of one compute response. */
1063
- declare class GrasshopperResponseProcessor {
1064
- /** Hand it to a mesh parser such as `@selvajs/visualization/parse`. */
1065
- readonly response: GrasshopperComputeResponse;
1066
- constructor(
1067
- /** Hand it to a mesh parser such as `@selvajs/visualization/parse`. */
1068
- response: GrasshopperComputeResponse);
1069
- /**
1070
- * Extract all values in the response.
1071
- *
1072
- * @typeParam T - Expected structure of the return value. Defaults to a simple key/value map. (later cast as needed)
1073
- * @param byId - Key by parameter ID instead of name. Requires the VektorNode rhino.compute branch.
1074
- * @param options - Controls parsing behavior such as Rhino geometry decoding.
1075
- * @returns Parsed Grasshopper output values.
1076
- *
1077
- * @example
1078
- * ```ts
1079
- * const processor = new GrasshopperResponseProcessor(response);
1080
- * const { values } = processor.getValues();
1081
- * ```
1082
- *
1083
- */
1084
- getValues<T = ParsedContext>(byId?: boolean, options?: GetValuesOptions): GetValuesResult<T>;
1085
- /**
1086
- * Retrieve a specific value by parameter name or ID.
1087
- *
1088
- * @param selector - `{ byName }` for the human-readable name, `{ byId }` for the parameter GUID.
1089
- * @param options - Parsing configuration (e.g. disable parsing or enable Rhino).
1090
- * @returns Single parsed value, array of values, or undefined if the parameter is absent.
1091
- *
1092
- * `byId` requires the VektorNode rhino.compute branch.
1093
- *
1094
- * @example
1095
- * ```ts
1096
- * const schema = processor.getValue({ byName: 'Schema' });
1097
- * const output = processor.getValue({ byId: 'a4be1c1e-23f9-4c27-b942-7f3bb2c45c6f' });
1098
- * ```
1099
- */
1100
- getValue(selector: {
1101
- byName: string;
1102
- } | {
1103
- byId: string;
1104
- }, options?: GetValuesOptions): any;
1105
- /**
1106
- * Download all files generated by Grasshopper, optionally including
1107
- * additional user-provided files.
1108
- *
1109
- * Files are grouped under the specified folder name when downloaded.
1110
- *
1111
- * @param folderName - Name for the download directory.
1112
- * @param additionalFiles - Extra files to package (single file, array, or null).
1113
- *
1114
- * @example
1115
- * ```ts
1116
- * await processor.getAndDownloadFiles('gh-output');
1117
- * ```
1118
- *
1119
- * @example
1120
- * ```ts
1121
- * const extra = { name: 'notes.txt', data: 'Example' };
1122
- * await processor.getAndDownloadFiles('project', extra);
1123
- * ```
1124
- */
1125
- getAndDownloadFiles(folderName: string, additionalFiles?: FileBaseInfo[] | FileBaseInfo | null): Promise<void>;
1126
- }
1127
- /**
1128
- * Deterministic stringify with sorted keys. {a:1,b:2} and {b:2,a:1} produce
1129
- * the same string. Safely handles circular references and non-finite numbers.
1130
- *
1131
- * The output is a cache key, so the invariant that matters is: two payloads
1132
- * that serialize differently on the wire must stringify differently here
1133
- * (false misses are harmless; false hits serve the wrong cached solve).
1134
- * Non-JSON values get unquoted markers (`{u8:…}`, `[Circular]`) that no JSON
1135
- * value stringifies to, so a plain object can't pose as one.
1136
- */
1137
- export declare function stableStringify(value: unknown): string;
1138
- /** The scheduler's solve-cache key for a definition and its input tree. */
1139
- export declare function hashSolveInput(definition: SolveDefinition, dataTree: unknown): string;
1140
- /**
1141
- * A definition's identity, without its inputs. Strings and bytes are hashed over their full
1142
- * content; a `DefinitionRef` uses its `key`.
1143
- */
1144
- export declare function hashDefinition(definition: SolveDefinition): string;
1145
- /**
1146
- * Run one solve. `GrasshopperClient.solve()` adds input validation and throws on solver errors.
1147
- *
1148
- * @param definition - A URL (sent as a pointer), base64 `.gh` content, raw `.gh` bytes, or a
1149
- * `DefinitionRef` (loaded for the upload). Any other string is encoded as the definition text.
1150
- * @returns The compute response, without the server's `pointer` and echoed `algo`.
1151
- *
1152
- * @example
1153
- * await solveGrasshopperDefinition(trees, 'https://example.com/definition.gh', config);
1154
- * await solveGrasshopperDefinition(trees, new Uint8Array(await readFile('model.gh')), config);
1155
- */
1156
- export declare function solveGrasshopperDefinition(dataTree: DataTree[], definition: SolveDefinition, config: GrasshopperComputeConfig): Promise<GrasshopperComputeResponse>;
1157
- /**
1158
- * Fetches a definition's input/output schemas without per-type parsing.
1159
- *
1160
- * Field KEYS are canonicalized to camelCase and missing required fields get a
1161
- * fallback ({@link normalizeInputSchema}); a missing or non-array `inputs` /
1162
- * `outputs` becomes `[]`. Field VALUES, notably `default` and the `values`
1163
- * label map, pass through verbatim.
1164
- *
1165
- * @throws {ComputeError} If fetch fails or response is invalid
1166
- *
1167
- * @public Use `fetchParsedDefinitionIO()` for processed, type-safe inputs
1168
- */
1169
- export declare function fetchDefinitionIO(definition: string | Uint8Array, config: ComputeConfig): Promise<GrasshopperParsedIORaw>;
1170
- /**
1171
- * Fetches a definition's input/output schemas and parses each input into its
1172
- * typed `InputParam`. An input that fails to parse gets a safe default
1173
- * and an entry in `parseErrors`.
1174
- *
1175
- * @throws {ComputeError} If fetch fails or response is invalid
1176
- *
1177
- * @example
1178
- * ```typescript
1179
- * const { inputs, outputs } = await fetchParsedDefinitionIO(
1180
- * 'https://example.com/definition.gh',
1181
- * { serverUrl: 'http://localhost:6500', apiKey: 'YOUR_KEY' }
1182
- * );
1183
- *
1184
- * // Inputs are now strongly typed
1185
- * inputs.forEach(input => {
1186
- * if (input.paramType === 'Number') {
1187
- * console.log(input.minimum, input.maximum); // TypeScript knows these exist
1188
- * }
1189
- * });
1190
- * ```
1191
- */
1192
- export declare function fetchParsedDefinitionIO(definition: string | Uint8Array, config: ComputeConfig): Promise<GrasshopperParsedIO>;
1193
- /** One entry of the schema endpoint's response, after wrapper-key normalization. */
1194
- interface SchemaEndpointResult<TSchema = unknown> {
1195
- /** Schemas embedded in that file. Absent when the file yielded none. */
1196
- schemas?: TSchema[];
1197
- /** Per-file diagnosis. Compute reports this and still answers 200. */
1198
- error?: string;
1199
- }
1200
- /**
1201
- * Read compute's `/grasshopper/schema` body: `[{ FileName, Schemas }]` per
1202
- * uploaded file, or a bare object for a single file.
1203
- *
1204
- * Use this rather than unwrapping the body by hand: the wrapper keys' casing
1205
- * varies by server branch, and a fixed-key read yields "no schemas" with a 200.
1206
- * Only the two wrapper keys are read; schema contents pass through untouched.
1207
- *
1208
- * @typeParam TSchema - Your schema type (e.g. `UISchema`). Not inspected.
1209
- */
1210
- export declare function readSchemaResults<TSchema = unknown>(raw: unknown): SchemaEndpointResult<TSchema>[];
1211
- /**
1212
- * Canonicalize a `/grasshopper/schema` body's key casing.
1213
- *
1214
- * Compute serializes the plugin's `UISchema`, whose camelCase wire names live in
1215
- * Newtonsoft `[JsonProperty]` attributes. `Selva.gha` ILRepack-merges Newtonsoft,
1216
- * so those attributes are `Selva!Newtonsoft.Json.JsonPropertyAttribute`. When
1217
- * compute's OWN Newtonsoft serializes, it doesn't recognize them, reads no
1218
- * attributes, and emits raw CLR names (`Inputs`, `SchemaVersion`). Nothing throws:
1219
- * `schema.inputs` reads `undefined`, and so does the `schemaVersion` gate.
1220
- *
1221
- * A casing rule, not a key allowlist: the wire names are the CLR names run
1222
- * through Newtonsoft's camelCase strategy, so reproducing it covers every key
1223
- * without drifting each time `UISchema` gains a field.
1224
- */
1225
- /**
1226
- * Returns a schema whose structural keys are camelCase, whichever Newtonsoft
1227
- * serialized it. Always a fresh object the caller may mutate; non-objects are
1228
- * returned as-is.
1229
- */
1230
- export declare function normalizeUISchemaCasing<T>(raw: T): T;
1231
- /**
1232
- * Parse one raw Grasshopper input schema into a typed {@link InputParam}.
1233
- * A validation failure is swallowed and replaced with a safe default.
1234
- */
1235
- export declare function processInput(rawInput: InputParamSchema): InputParam;
1236
- /**
1237
- * Sentinel a Rhino geometry decode returns when a decode was attempted
1238
- * and failed hard (a registered decoder or `CommonObject.decode` threw). Carries
1239
- * the original payload so callers can log or retry. Use
1240
- * {@link isRhinoDecodeError} to detect it.
1241
- */
1242
- interface RhinoDecodeError {
1243
- __decodeError: true;
1244
- /** The Rhino type name the failed decode was attempted for. */
1245
- type: string;
1246
- /** The original (undecoded) payload. */
1247
- raw: unknown;
1248
- }
1249
- /** Type guard for the {@link RhinoDecodeError} sentinel. */
1250
- export declare function isRhinoDecodeError(value: unknown): value is RhinoDecodeError;
1251
- /**
1252
- * Free every rhino3dm WASM object reachable from `value`.
1253
- *
1254
- * rhino3dm objects are emscripten bindings — JS GC never reclaims their WASM
1255
- * heap allocation, so everything decoded from a solve response (`getValues`,
1256
- * `getValue`) must be deleted explicitly or the heap
1257
- * grows monotonically across solves (e.g. a UI decoding per slider tick).
1258
- *
1259
- * Walks arrays and plain objects recursively; anything exposing a `delete()`
1260
- * method is treated as a WASM binding and freed (skipped if already deleted).
1261
- * Safe to call more than once and on values containing no WASM objects.
1262
- */
1263
- export declare function disposeRhinoObjects(value: unknown): void;
1264
- type DataTreeValue = string | number | boolean | object | null;
1265
- /** One input's value for {@link TreeBuilder.fromValues}. */
1266
- interface TreeValueEntry {
1267
- paramName: string;
1268
- /** Matched case-insensitively. */
1269
- paramType: string;
1270
- /** A scalar, an array (branch `{0}`), or a branch-path map like `{ '{0;1}': [1, 2] }`. */
1271
- value: unknown;
1272
- minimum?: number | null;
1273
- maximum?: number | null;
1274
- }
1275
- /**
1276
- * Builds the Grasshopper data trees a solve takes as input.
1277
- *
1278
- * @example
1279
- * ```ts
1280
- * const tree = new TreeBuilder('MyParam')
1281
- * .append([0], [1, 2, 3])
1282
- * .append([1], [4, 5])
1283
- * .toComputeFormat();
1284
- * ```
1285
- */
1286
- export declare class TreeBuilder {
1287
- private innerTree;
1288
- private paramName;
1289
- constructor(paramName: string);
1290
- /** Append values to the branch at `path` (`[0, 1]` is `{0;1}`). */
1291
- append(path: number[], items: DataTreeValue[]): this;
1292
- /** Replace the tree with a branch-path map like `{ '{0;1}': [1, 2] }`. */
1293
- fromDataTreeDefault(treeData: DataTreeDefault): this;
1294
- /** Append a value, or each value of an array, to branch `{0}`. */
1295
- appendFlat(values: DataTreeValue | DataTreeValue[]): this;
1296
- /** Every value across all branches. */
1297
- flatten(): DataTreeValue[];
1298
- /** The tree in the shape a solve request sends. */
1299
- toComputeFormat(): DataTree;
1300
- getParamName(): string;
1301
- /**
1302
- * One tree per input that has a default, keyed by its nickname. Defaults are coerced and
1303
- * clamped by the same rules as {@link TreeBuilder.fromValues}.
1304
- */
1305
- static fromInputParams(inputs: InputParam[]): DataTree[];
1306
- /**
1307
- * One tree per entry, coercing UI values the way Grasshopper reads them. Number and Integer
1308
- * items go through the numeric coercion (Integer rounds) and are clamped to
1309
- * `minimum`/`maximum`; Boolean items also accept `'true'`/`'false'`; every other type passes
1310
- * through. An item that fails coercion is dropped with a warning. An entry left with no items
1311
- * is omitted, so Grasshopper uses its own value; `''` is an item, not an empty value.
1312
- */
1313
- static fromValues(entries: TreeValueEntry[]): DataTree[];
1314
- private static branchesOf;
1315
- /** `null` means the item was rejected. */
1316
- private static itemCoercer;
1317
- /**
1318
- * Set or add `paramName`'s tree to `newValue`: a scalar, an array (branch `{0}`), or a
1319
- * branch-path map. Copy-on-write: returns a new array and leaves the caller's untouched, so
1320
- * assign the result.
1321
- */
1322
- static replaceTreeValue(trees: DataTree[], paramName: string, newValue: DataTreeValue): DataTree[];
1323
- private static buildFromValue;
1324
- /**
1325
- * `paramName`'s whole tree from a solve result's `values`, keyed by branch path (`'{0;1}'`).
1326
- * `null` when no tree has that param, an empty `Map` when it has no branches. An item without
1327
- * `data` reads as `null` in place, so indices don't shift.
1328
- */
1329
- static getTree(trees: DataTree[], paramName: string): Map<DataTreePath, DataTreeValue[]> | null;
1330
- /**
1331
- * `paramName`'s value when its tree has a single branch: one item unwrapped (`5`, not `[5]`),
1332
- * several as an array. `null` for a missing param or an empty tree.
1333
- *
1334
- * @throws {ComputeError} `INVALID_INPUT` when the tree has more than one branch; read those
1335
- * with {@link TreeBuilder.getTree}.
1336
- */
1337
- static getTreeValue(trees: DataTree[], paramName: string): DataTreeValue | null;
1338
- private static paramNameOf;
1339
- /**
1340
- * Parse a branch path like `"{0;1;2}"` into `[0, 1, 2]`.
1341
- *
1342
- * @throws {ComputeError} `INVALID_INPUT` for a malformed path, rather than
1343
- * collapsing it to a default branch where two bad keys would merge.
1344
- */
1345
- static parsePathString(pathStr: string): number[];
1346
- /** `[0, 1, 2]` → `"{0;1;2}"`. */
1347
- static formatPathString(path: number[]): DataTreePath;
1348
- /** Booleans and numbers stay primitives: Grasshopper reads them typed. */
1349
- private static serializeValue;
1350
- private static deserializeValue;
1351
- private static hasValidValue;
1352
- private static clampValue;
1353
- }
1354
- export { type BooleanInputType, type CacheOptions, type ColorInputType, ComputeServerStats, type CreateClientOptions, type DataItem, type DataTree, type DataTreeDefault, type DataTreePath, type DataTreeValue, type DefaultValue, type FileInputType, type GeometryInputType, type GetValuesOptions, type GetValuesResult, GrasshopperClient, type GrasshopperComputeConfig, type GrasshopperComputeResponse, type GrasshopperParsedIO, type GrasshopperParsedIORaw, GrasshopperResponseProcessor, type InnerTreeData, type InputParam, type InputParamSchema, type InputParseError, type NumericInputType, type OutputParamSchema, type ParsedContext, type RhinoDecodeError, type RhinoModelUnit, type SchedulerMode, type SchedulerSolveOptions, type SchemaEndpointResult, type SelvaEventTarget, type SelvaResponseBlock, type ServerStats, type ServerVersion, type SolveContext, type SolveDetail, type SolveExecutor, type SolveOptions, type SolveResult, type SolveSchedulerOptions, type TextInputType, type TreeValueEntry, type ValueListInputType };
1355
- //# sourceMappingURL=grasshopper.d.cts.map