@selvajs/compute 4.2.2-beta.0 → 5.0.0-beta.2

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,15 @@
1
- import { M as ComputeError, O as ComputeConfig, _ as isDefinitionRef, a as extractFilesFromComputeResponse, c as ProcessedFile, g as SolveDefinition, h as DefinitionRef, k as RetryPolicy, o as FileBaseInfo, r as downloadFileData, s as FileData } from "./index-zlzEAi12.cjs";
1
+ import { A as ServerTiming, D as ComputeConfig, E as ComputeAuth, O as RetryPolicy, g as SolveDefinition, j as ComputeError, o as FileBaseInfo } from "./index-C5T6o4Fr.cjs";
2
2
  //#region src/grasshopper/server/compute-server-stats.d.ts
3
+ interface ServerVersion {
4
+ rhino: string;
5
+ compute: string;
6
+ git_sha: string | null;
7
+ }
8
+ interface ServerStats {
9
+ isOnline: boolean;
10
+ version?: ServerVersion;
11
+ activeChildren?: number;
12
+ }
3
13
  /**
4
14
  * ComputeServerStats provides methods to query Rhino Compute server statistics.
5
15
  *
@@ -23,10 +33,9 @@ import { M as ComputeError, O as ComputeConfig, _ as isDefinitionRef, a as extra
23
33
  */
24
34
  declare class ComputeServerStats {
25
35
  private readonly serverUrl;
26
- private readonly apiKey?;
36
+ private readonly headers;
27
37
  private disposed;
28
38
  private activeMonitors;
29
- private activeTimeouts;
30
39
  /** Timeout (ms) for the fast read/monitoring endpoints. */
31
40
  private static readonly DEFAULT_TIMEOUT_MS;
32
41
  /** Timeout (ms) for child-lifecycle POSTs — a cold Windows child can take ~30s to spawn. */
@@ -35,18 +44,17 @@ declare class ComputeServerStats {
35
44
  private static readonly MIN_MONITOR_INTERVAL_MS;
36
45
  /**
37
46
  * @param serverUrl - Base URL of the Rhino Compute server with http:// or https:// scheme (e.g., 'http://localhost:6500')
38
- * @param apiKey - Optional API key for authentication
39
- */
40
- constructor(serverUrl: string, apiKey?: string);
41
- /**
42
- * Build request headers with optional API key.
47
+ * @param auth - An API key, or the same auth fields a solve's `ComputeConfig` carries
48
+ * (`apiKey`, `apiKeyHeader`, `authToken`, `headers`).
43
49
  */
44
- private buildHeaders;
50
+ constructor(serverUrl: string, auth?: string | ComputeAuth);
51
+ /** Aborts after `timeoutMs` so a hung connection can't stall a probe or `monitor()`; `0` disables it. */
52
+ private fetchWithTimeout;
45
53
  /**
46
- * `fetch` wrapper that aborts after `timeoutMs` so a hung connection can't stall
47
- * a probe (or the `monitor()` loop) forever. Pass `0` to disable the timeout.
54
+ * The response body of `path`, or `null` (logged) on a non-2xx or a network
55
+ * failure. Throws only when disposed.
48
56
  */
49
- private fetchWithTimeout;
57
+ private getText;
50
58
  /**
51
59
  * Check if the server is online.
52
60
  *
@@ -100,11 +108,7 @@ declare class ComputeServerStats {
100
108
  *
101
109
  * @returns Version object with rhino, compute, and git_sha, or null if unavailable
102
110
  */
103
- getVersion(): Promise<{
104
- rhino: string;
105
- compute: string;
106
- git_sha: string | null;
107
- } | null>;
111
+ getVersion(): Promise<ServerVersion | null>;
108
112
  /**
109
113
  * Get the plugins installed on the server.
110
114
  *
@@ -131,15 +135,7 @@ declare class ComputeServerStats {
131
135
  *
132
136
  * @returns Object containing server status and available stats
133
137
  */
134
- getServerStats(): Promise<{
135
- isOnline: boolean;
136
- version?: {
137
- rhino: string;
138
- compute: string;
139
- git_sha: string | null;
140
- };
141
- activeChildren?: number;
142
- }>;
138
+ getServerStats(): Promise<ServerStats>;
143
139
  /**
144
140
  * Purge the server's solve-results / URL-data cache.
145
141
  *
@@ -284,12 +280,7 @@ declare class ComputeServerStats {
284
280
  spawned: number[];
285
281
  active: number;
286
282
  } | null>;
287
- /**
288
- * POST a control endpoint that replies with a JSON object and return it,
289
- * degrading to `null` on any non-2xx, non-JSON, or network failure. Shared by
290
- * the child-lifecycle methods so their failure semantics stay identical. Uses
291
- * the longer lifecycle timeout since a spawn/recycle can take ~30s.
292
- */
283
+ /** A child-lifecycle POST: a spawn or recycle can take ~30 s. */
293
284
  private postJson;
294
285
  /**
295
286
  * Continuously monitor server stats at specified interval.
@@ -310,15 +301,12 @@ declare class ComputeServerStats {
310
301
  * stopMonitoring();
311
302
  * ```
312
303
  */
313
- monitor(callback: (stats: Awaited<ReturnType<typeof this.getServerStats>>) => void, intervalMs?: number): () => void;
304
+ monitor(callback: (stats: ServerStats) => void, intervalMs?: number): () => void;
314
305
  /**
315
306
  * Disposes of all resources and stops all active monitors.
316
307
  * Call this when you're done using the stats instance.
317
308
  */
318
309
  dispose(): Promise<void>;
319
- /**
320
- * Ensures the instance hasn't been disposed.
321
- */
322
310
  private ensureNotDisposed;
323
311
  }
324
312
  //#endregion
@@ -334,17 +322,9 @@ type InnerTreeData = { [path in DataTreePath]: DataItem[]; };
334
322
  /**
335
323
  * Tree with parameter metadata (used in compute requests and responses).
336
324
  *
337
- * Field casing varies by server branch: stock mcneel compute (8.x/9.x)
338
- * serializes `Resthopper.IO.DataTree` in PascalCase (`ParamName`/`InnerTree`
339
- * — the C# class carries no `[JsonProperty]` attributes), while
340
- * camelCase-serializing forks return `paramName`/`innerTree` instead. This
341
- * client always *sends* PascalCase (see `DataTree.toCompute()` in
342
- * `data-tree.ts`), so the PascalCase fields remain the canonical/required
343
- * shape; the optional camelCase fields exist so responses from camelCase
344
- * forks are representable without casts. Code that must read trees across
345
- * both server families should use `readField`/`hasField`
346
- * (`@/core/utils/read-field`, case-insensitive) rather than direct property
347
- * access — `warnOnEmptyInnerTrees` in `solve.ts` is the reference example.
325
+ * This client sends PascalCase (`TreeBuilder.toComputeFormat()`), so those
326
+ * fields are required; a response's casing varies by server branch, so read
327
+ * one with `readField` rather than by property.
348
328
  */
349
329
  interface DataTree {
350
330
  /** PascalCase — stock mcneel servers, and the request shape this client sends. */
@@ -356,7 +336,6 @@ interface DataTree {
356
336
  /** camelCase — sent instead of `ParamName` by camelCase server forks. */
357
337
  paramName?: string;
358
338
  }
359
- type OutputType = 'System.String' | 'System.Double' | 'System.Int32' | 'System.Boolean' | 'Rhino.Geometry.Point3d' | 'Rhino.Geometry.Line' | 'Rhino.Geometry.Circle' | 'Rhino.Geometry.Arc' | 'Rhino.Geometry.NurbsCurve' | 'Rhino.Geometry.Brep' | 'Rhino.Geometry.Mesh' | 'Rhino.Geometry.Vector3d' | 'Rhino.Geometry.Plane' | 'Rhino.Geometry.Box' | (string & {});
360
339
  /**
361
340
  * Union type for all possible default value types
362
341
  */
@@ -431,7 +410,7 @@ interface FileInputType extends BaseInputType {
431
410
  default: DefaultValue<object | string>;
432
411
  }
433
412
  /**
434
- * Color input type (stored as hex string)
413
+ * Color input type. Values are `"R, G, B"` strings (`"255, 0, 0"`), not hex.
435
414
  */
436
415
  interface ColorInputType extends BaseInputType {
437
416
  paramType: 'Color';
@@ -490,55 +469,19 @@ interface GrasshopperDefinitionSource {
490
469
  filename?: string | null;
491
470
  }
492
471
  /**
493
- * Configuration for Grasshopper compute operations
494
- * Combines server config with optional Grasshopper-specific computation settings
495
- *
496
- * Note: The definition source (pointer/algo) is NOT part of config.
497
- * Instead, pass the definition directly to methods like solve(), getIO(), etc.
498
- */
499
- interface GrasshopperComputeConfig extends ComputeConfig {
500
- /** Absolute tolerance used in computation */
501
- absolutetolerance?: number | null;
502
- /** Angular tolerance used in computation */
503
- angletolerance?: number | null;
504
- /** Model units used */
505
- modelunits?: RhinoModelUnit | null;
506
- /** Data version (7 or 8) */
507
- dataversion?: 7 | 8 | null;
508
- /** Whether to use cached solution */
509
- cachesolve?: boolean | null;
510
- /**
511
- * Opt-in: cache a solve even when the definition reported GH errors. See
512
- * {@link GrasshopperBaseSchema.cacheerroredsolves}. Only meaningful with
513
- * `cachesolve`.
514
- */
515
- cacheerroredsolves?: boolean | null;
516
- /** Per-solve live-event callback; see {@link SelvaEventTarget}. */
517
- selvaevents?: SelvaEventTarget | null;
518
- }
519
- /**
520
- * Arguments sent to Grasshopper compute endpoint
521
- * Includes config options + definition source + input values
472
+ * Server config plus the Grasshopper solve settings. The definition is not part
473
+ * of it: pass that to `solve()` and friends.
522
474
  */
523
- interface GrasshopperRequestSchema extends GrasshopperBaseSchema, GrasshopperDefinitionSource {
524
- /** Input values organized by parameter */
525
- values?: DataTree[];
475
+ interface GrasshopperComputeConfig extends ComputeConfig, GrasshopperBaseSchema {
526
476
  /** Per-solve live-event callback; see {@link SelvaEventTarget}. */
527
477
  selvaevents?: SelvaEventTarget | null;
528
478
  }
529
479
  /**
530
- * Response from Grasshopper compute server
531
- * Includes all schema fields + computed results
532
- *
533
- * `pointer` is deliberately excluded (`Omit`): the solve layer (`runSolve` in
534
- * `solve.ts`) splits the server's echoed `pointer` off as the solve's
535
- * `cacheKey` and strips it from the returned response, so client-returned
536
- * responses never carry it.
480
+ * Response from Grasshopper compute server.
537
481
  *
538
- * The schema-echo fields (`cachesolve`/`modelunits`/`dataversion`) stay
539
- * optional as inherited from {@link GrasshopperBaseSchema}: the server echoes
540
- * them back when set, but nothing client-side enforces their presence — don't
541
- * rely on them without a fallback.
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.
542
485
  */
543
486
  interface GrasshopperComputeResponse extends GrasshopperBaseSchema, Omit<GrasshopperDefinitionSource, 'pointer'> {
544
487
  /**
@@ -548,12 +491,7 @@ interface GrasshopperComputeResponse extends GrasshopperBaseSchema, Omit<Grassho
548
491
  * hand-built mock may omit it at runtime.
549
492
  */
550
493
  modelunits: RhinoModelUnit;
551
- /**
552
- * The server echoes the request's full base64 definition back as `algo`, but the client strips
553
- * it before returning — retaining it would pin a multi-MB copy of the definition per response,
554
- * multiplied by every cache that holds responses. Always `undefined` on client-returned
555
- * responses (inherited optional field from {@link GrasshopperDefinitionSource}).
556
- */
494
+ /** Always `undefined` on a client-returned response: the solve strips it. */
557
495
  algo?: string | null;
558
496
  /** Filename of the definition (always present in response) */
559
497
  filename: string | null;
@@ -620,20 +558,19 @@ interface InputParamSchema {
620
558
  }
621
559
  //#endregion
622
560
  //#region src/grasshopper/types/outputs.d.ts
623
- /**
624
- * Parsed input/output structure with raw schemas.
625
- *
626
- * `loadWarnings` / `loadErrors` carry the server's definition-load diagnostics
627
- * (missing plugin, broken component, etc.) from the `/io` response. They are
628
- * distinct from per-input parse failures (`InputParseError`): these come from
629
- * the server loading the definition, those from the client typing an input.
630
- */
561
+ /** Input/output schemas as the `/io` endpoint reports them, keys canonicalized. */
631
562
  interface GrasshopperParsedIORaw {
632
563
  inputs: InputParamSchema[];
633
564
  outputs: OutputParamSchema[];
634
- /** Server-side definition-load warnings, if any. */
565
+ /**
566
+ * Warnings the server raised loading the definition (e.g. an obsolete
567
+ * component). Distinct from `parseErrors`, the client failing to type an input.
568
+ */
635
569
  loadWarnings?: string[];
636
- /** Server-side definition-load errors, if any (e.g. missing plugin). */
570
+ /**
571
+ * Errors the server raised loading the definition (e.g. a missing plugin).
572
+ * When present, `inputs`/`outputs` may be incomplete.
573
+ */
637
574
  loadErrors?: string[];
638
575
  }
639
576
  /**
@@ -652,43 +589,26 @@ interface InputParseError {
652
589
  /** Error code from the underlying ComputeError, if available. */
653
590
  code?: string;
654
591
  }
655
- /**
656
- * Parsed input/output structure with processed types.
657
- *
658
- * `parseErrors` is populated when one or more inputs failed validation and
659
- * fell back to a safe default. The result is still usable, but the UI should
660
- * surface these so the user can fix their definition.
661
- */
662
- interface GrasshopperParsedIO {
592
+ /** {@link GrasshopperParsedIORaw} with each input parsed into its typed {@link InputParam}. */
593
+ interface GrasshopperParsedIO extends Omit<GrasshopperParsedIORaw, 'inputs'> {
663
594
  inputs: InputParam[];
664
- outputs: OutputParamSchema[];
665
- parseErrors?: InputParseError[];
666
- /**
667
- * Server-side definition-load warnings from the `/io` response (e.g. an
668
- * obsolete component). Surface these so the user understands a degraded IO
669
- * list. Distinct from `parseErrors` (client-side input typing failures).
670
- */
671
- loadWarnings?: string[];
672
595
  /**
673
- * Server-side definition-load errors from the `/io` response (e.g. a missing
674
- * plugin that left inputs unresolved). When present, the inputs/outputs may
675
- * be incomplete — the user needs to fix their server/definition.
596
+ * Inputs that failed validation and got a safe default. The result is still
597
+ * usable, but surface these so the user can fix their definition.
676
598
  */
677
- loadErrors?: string[];
599
+ parseErrors?: InputParseError[];
678
600
  }
679
601
  //#endregion
680
602
  //#region src/grasshopper/scheduler/types.d.ts
681
603
  /**
682
- * Scheduling mode — controls how concurrent `solve()` calls interact.
604
+ * How concurrent `solve()` calls interact.
683
605
  *
684
- * - `latest-wins`: One in flight at a time. New calls supersede any pending
685
- * call (in-flight one is aborted). Optimal for slider scrubs / live UIs.
686
- * - `queue`: FIFO queue. Each solve runs to completion. Concurrency capped
687
- * by `maxConcurrent`. Use for "submit job" flows where every request matters.
688
- * - `parallel`: No scheduling — calls run concurrently up to `maxConcurrent`.
689
- * Closest to plain `client.solve()` but with shared cancel/state.
606
+ * - `latest-wins`: one in flight at a time. A new call supersedes the pending
607
+ * one and aborts the one in flight. For slider scrubs and live UIs.
608
+ * - `queue`: FIFO, every call runs to completion, up to `maxConcurrent` at
609
+ * once (default 1). For "submit job" flows where every request matters.
690
610
  */
691
- type SchedulerMode = 'latest-wins' | 'queue' | 'parallel';
611
+ type SchedulerMode = 'latest-wins' | 'queue';
692
612
  interface CacheOptions {
693
613
  /**
694
614
  * Total byte budget for retained responses, evicted LRU. The only size bound
@@ -732,7 +652,7 @@ interface SolveSchedulerOptions {
732
652
  * excluding the ones already in flight). When the queue is full, a new
733
653
  * `solve()` is rejected immediately with `code: QUEUE_FULL` (retryable, meant to
734
654
  * map to HTTP 503 + Retry-After) instead of piling up unbounded. Bounds the
735
- * miss path under load. Only applies to `queue` / `parallel` modes —
655
+ * miss path under load. Only applies to `queue` mode —
736
656
  * `latest-wins` has an intrinsic depth of 1. Default: unbounded.
737
657
  */
738
658
  maxQueueDepth?: number;
@@ -740,8 +660,7 @@ interface SolveSchedulerOptions {
740
660
  * Backpressure — max time (ms) a call may sit queued before it starts
741
661
  * executing. If it's still waiting after this long it's rejected with
742
662
  * `code: QUEUE_TIMEOUT` rather than burning compute on a stale request.
743
- * Bounds tail latency. Only applies to `queue` / `parallel` modes. Default:
744
- * no deadline.
663
+ * Bounds tail latency. Only applies to `queue` mode. Default: no deadline.
745
664
  */
746
665
  queueWaitMs?: number;
747
666
  timeoutMs?: number;
@@ -753,15 +672,11 @@ interface SolveSchedulerOptions {
753
672
  */
754
673
  cache?: false | CacheOptions;
755
674
  /**
756
- * Reuse the server's definition cache key so a large (base64/binary)
757
- * definition is uploaded once and subsequent solves reference it by
758
- * `pointer` instead of re-sending the full payload. Hugely cheaper for
759
- * multi-MB definitions on a live UI (slider scrubs, etc.).
760
- *
761
- * Requires a `cacheKeyExecutor` to be supplied (the client wires one). Has no
762
- * effect for URL-pointer definitions (already a reference). On a server-side
763
- * cache miss the executor transparently falls back to a full upload, so this
764
- * is safe to leave on. Default: `true` when a `cacheKeyExecutor` is present.
675
+ * Upload a definition once, then solve it by the server's cache key (`pointer`)
676
+ * instead of re-sending the bytes; a `DefinitionRef` also skips its `load()`.
677
+ * When the server has evicted the key, the solve falls back to a full upload,
678
+ * so this is safe to leave on. No effect for URL definitions, which are
679
+ * already a reference. Default `true`.
765
680
  */
766
681
  reuseServerDefinitionCache?: boolean;
767
682
  /** Lifecycle hooks — fired in order. Errors thrown by hooks are logged, not rethrown. */
@@ -783,12 +698,10 @@ type SolveResult = {
783
698
  durationMs: number;
784
699
  fromCache: boolean;
785
700
  /**
786
- * Definition-cache telemetry for a real compute call (not a Selva-cache
787
- * `fromCache` hit). `false` → the server reused its cached definition via
788
- * the pointer (no upload); `true` → the pointer was cold/stale so the full
789
- * definition was re-uploaded. `undefined` when the server-definition-cache
790
- * fast path didn't run (reuse disabled, or a non-reusable definition such
791
- * as a remote URL).
701
+ * Whether this solve uploaded the full definition: `true` on its first
702
+ * solve or after the server evicted the cache key, `false` when the server
703
+ * reused its cached copy via the pointer. `undefined` for a `fromCache`
704
+ * hit, or when reuse is off or the definition is a URL.
792
705
  */
793
706
  definitionReuploaded?: boolean;
794
707
  } | {
@@ -798,39 +711,45 @@ type SolveResult = {
798
711
  } | {
799
712
  status: 'superseded';
800
713
  };
801
- type SolveExecutor = (definition: SolveDefinition, dataTree: DataTree[], config: GrasshopperComputeConfig) => Promise<GrasshopperComputeResponse>;
802
714
  /**
803
- * Cache-key-aware executor. When `cacheKey` is provided, the executor solves by
804
- * reference (`pointer: cacheKey`) and falls back to a full upload on a server
805
- * cache miss. Always reports the (possibly refreshed) `cacheKey` so the
806
- * scheduler can update its definition→key map, plus whether the fast path
807
- * `missed` (for telemetry). When `cacheKey` is null it's a first solve — upload
808
- * fully and capture the key the server assigns.
809
- *
810
- * Supplied by the client (which owns the solve primitives); the scheduler stays
811
- * decoupled from the transport.
715
+ * Solves by `cacheKey` when given one, otherwise uploads. Reports the server's
716
+ * (possibly new) key and whether the full definition was uploaded (`missed`).
717
+ * The scheduler passes `null` whenever server-definition reuse is off.
812
718
  */
813
- type CacheKeyExecutor = (definition: SolveDefinition, dataTree: DataTree[], cacheKey: string | null, config: GrasshopperComputeConfig) => Promise<{
719
+ type SolveExecutor = (definition: SolveDefinition, dataTree: DataTree[], cacheKey: string | null, config: GrasshopperComputeConfig) => Promise<{
814
720
  response: GrasshopperComputeResponse;
815
721
  cacheKey: string | null;
816
722
  missed: boolean;
817
723
  }>;
724
+ interface SchedulerSolveOptions {
725
+ signal?: AbortSignal;
726
+ /** Per-solve live-event callback. Not part of the cache key: it names where events go, not what is solved. */
727
+ selvaevents?: SelvaEventTarget;
728
+ /**
729
+ * This call's `Server-Timing` breakdown, after the client config's own `onServerTiming`.
730
+ * Never fires for a `fromCache` hit, which sends no request.
731
+ */
732
+ onServerTiming?: (timing: ServerTiming, requestId: string) => void;
733
+ }
734
+ /** What `solveDetailed()` resolves with: the response plus how this call got it. */
735
+ interface SolveDetail {
736
+ response: GrasshopperComputeResponse;
737
+ /** Served from the scheduler's response cache without a request. */
738
+ fromCache: boolean;
739
+ /** Same meaning as on {@link SolveResult}. */
740
+ definitionReuploaded?: boolean;
741
+ /** Executor wall time; `0` for a `fromCache` hit. */
742
+ durationMs: number;
743
+ }
818
744
  //#endregion
819
745
  //#region src/grasshopper/scheduler/solve-scheduler.d.ts
820
746
  /**
821
- * Robust scheduler for Grasshopper solves.
822
- *
823
- * Sits between your application code and the underlying compute call,
824
- * adding:
825
- * - Configurable scheduling (latest-wins for sliders, queue for jobs)
826
- * - Backpressure (bounded queue depth + queue-wait deadline) for the miss path
827
- * - In-flight cancellation (per-call signal + cancelAll)
747
+ * Adds to a plain solve call:
748
+ * - Scheduling (latest-wins for sliders, queue for jobs)
749
+ * - Backpressure (bounded queue depth + queue-wait deadline)
750
+ * - Cancellation (per-call signal + cancelAll)
828
751
  * - Optional response caching for repeated inputs
829
- * - Lifecycle hooks for UI indicators (start / settle / superseded)
830
- * - State observability via subscribe()
831
- *
832
- * Multiple schedulers can share a single GrasshopperClient — typically one
833
- * per UI surface (e.g. one for slider scrubs, one for long-running submits).
752
+ * - Lifecycle hooks (start / settle / superseded) and state via subscribe()
834
753
  *
835
754
  * @example
836
755
  * ```ts
@@ -853,11 +772,6 @@ export declare class SolveScheduler {
853
772
  private readonly executor;
854
773
  private readonly baseConfig;
855
774
  private readonly mode;
856
- /**
857
- * Mutable via {@link setMaxConcurrent}: the compute server's worker pool can grow
858
- * or shrink while this scheduler is alive, and the dispatch loops re-read this on
859
- * every pass rather than capturing it.
860
- */
861
775
  private maxConcurrent;
862
776
  private readonly maxQueueDepth;
863
777
  private readonly queueWaitMs;
@@ -868,21 +782,12 @@ export declare class SolveScheduler {
868
782
  private readonly cacheTtl;
869
783
  private readonly cacheErroredSolves;
870
784
  private readonly cache;
871
- /** Sum of `sizeBytes` across retained cache entries. */
872
785
  private cacheBytes;
873
- /**
874
- * Cumulative hit/miss/eviction counters for {@link cacheStats}. Deliberately
875
- * NOT reset by `clearCache()` — they measure this scheduler's whole lifetime,
876
- * so a hit rate stays comparable across a session rather than restarting at
877
- * every clear.
878
- */
879
786
  private cacheHits;
880
787
  private cacheMisses;
881
788
  private cacheEvictions;
882
- /** Optional cache-key-aware executor and whether server-def-cache reuse is on. */
883
- private readonly cacheKeyExecutor?;
884
789
  private readonly reuseServerDefinitionCache;
885
- /** definition identity → server cache key (`pointer`) learned from past solves. */
790
+ /** Definition hash → the server's cache key (`pointer`) for it. */
886
791
  private readonly serverCacheKeys;
887
792
  private readonly onStart?;
888
793
  private readonly onSettle?;
@@ -894,12 +799,11 @@ export declare class SolveScheduler {
894
799
  private _lastResult;
895
800
  private _lastError;
896
801
  private _lastDurationMs;
897
- /** Ordinal handed to each solve() call. */
898
802
  private solveSeq;
899
- /** seq of the solve that last wrote _lastResult/_lastError — see writeLastState. */
803
+ /** seq of the solve that last wrote `_lastResult`/`_lastError`. */
900
804
  private lastStateSeq;
901
805
  private disposed;
902
- constructor(executor: SolveExecutor, baseConfig: GrasshopperComputeConfig, options?: SolveSchedulerOptions, cacheKeyExecutor?: CacheKeyExecutor);
806
+ constructor(executor: SolveExecutor, baseConfig: GrasshopperComputeConfig, options?: SolveSchedulerOptions);
903
807
  get isSolving(): boolean;
904
808
  get hasPending(): boolean;
905
809
  get inFlightCount(): number;
@@ -928,23 +832,16 @@ export declare class SolveScheduler {
928
832
  * - Rejects with `code: ErrorCodes.SUPERSEDED` when the call was canceled because
929
833
  * newer values arrived (latest-wins mode).
930
834
  * - Rejects with `code: ErrorCodes.ABORTED` when the call was canceled via
931
- * caller-supplied signal or `cancelAll()`.
835
+ * its own `signal` (queued or running) or `cancelAll()`.
932
836
  * - Rejects with `code: ErrorCodes.QUEUE_FULL` when `maxQueueDepth` is set and
933
837
  * the queue was already full (backpressure; `statusCode: 503`).
934
838
  * - Rejects with `code: ErrorCodes.QUEUE_TIMEOUT` when `queueWaitMs` is set and
935
839
  * the call sat queued longer than that before starting (`statusCode: 503`).
936
840
  *
937
- * Caller-supplied `signal` cancels just this call (rejects with `ABORTED`) —
938
- * including while the call is still queued, before execution starts.
939
- *
940
- * A {@link DefinitionRef} definition is keyed by its `key` (result cache and
941
- * server-pointer map alike) without materializing bytes — `load()` runs only
942
- * when an upload is unavoidable. Its immutability contract is trusted here:
943
- * a reused key serves the other content's cached solve.
944
- *
945
- * Responses served from the cache (and via `lastResult`) are shared objects,
946
- * not copies — treat them as immutable. Mutating one poisons every later
947
- * cache hit for that key.
841
+ * Don't mutate a `Uint8Array` definition after solving it. Responses served
842
+ * from the cache (and via `lastResult`) are shared objects, not copies —
843
+ * treat them as immutable. Mutating one poisons every later cache hit for
844
+ * that key.
948
845
  *
949
846
  * Partial-success contract: unlike `GrasshopperClient.solve()`, which throws
950
847
  * `COMPUTATION_ERROR` when the response carries solver errors, this RESOLVES
@@ -952,79 +849,44 @@ export declare class SolveScheduler {
952
849
  * success must not be treated as a result (see `cacheErroredSolves` for the
953
850
  * caching side of the same distinction).
954
851
  */
955
- solve(definition: SolveDefinition, dataTree: DataTree[], options?: {
956
- signal?: AbortSignal;
957
- selvaevents?: SelvaEventTarget;
958
- }): Promise<GrasshopperComputeResponse>;
959
- /**
960
- * Record last-result state, but only if no newer solve has written since —
961
- * a slow solve settling late must not overwrite the state a newer solve
962
- * (or cache hit) already published.
963
- */
852
+ solve(definition: SolveDefinition, dataTree: DataTree[], options?: SchedulerSolveOptions): Promise<GrasshopperComputeResponse>;
853
+ /** {@link solve}, also resolving with whether this call hit the cache and how long it took. */
854
+ solveDetailed(definition: SolveDefinition, dataTree: DataTree[], options?: SchedulerSolveOptions): Promise<SolveDetail>;
855
+ /** A slow solve settling late must not overwrite state a newer solve or cache hit published. */
964
856
  private writeLastState;
965
857
  /** latest-wins: supersede the pending item and abort everything in flight. */
966
858
  private supersedeCurrent;
967
- /** Settle a still-queued item as ABORTED and remove it from its queue. */
859
+ private dequeue;
968
860
  private abortQueuedItem;
969
- /**
970
- * Backpressure: settle an incoming item as QUEUE_FULL without ever enqueuing
971
- * it. The structured context lets an HTTP layer map it to 503 + Retry-After.
972
- */
861
+ /** The structured context lets an HTTP layer map QUEUE_FULL to 503 + Retry-After. */
973
862
  private shedAsQueueFull;
974
- /**
975
- * Arm the queue-wait deadline for an item about to be queued. If the item is
976
- * still waiting when it fires, it's rejected as QUEUE_TIMEOUT and removed from
977
- * the queue. Cleared on execute/settle via {@link clearQueueWaitTimer}.
978
- */
979
863
  private armQueueWaitTimer;
980
- /** Clear a queued item's wait-deadline timer, if one is pending. */
981
864
  private clearQueueWaitTimer;
982
865
  private enqueue;
983
866
  private execute;
984
- /**
985
- * Run the solve, using the server-definition-cache fast path when it's
986
- * enabled and the definition is reusable. Learns/updates the definition's
987
- * server cache key from the result so later solves can reference it.
988
- *
989
- * `definitionHash` is the {@link hashDefinition} result already computed at
990
- * `solve()` entry — threaded through rather than recomputed, so each solve
991
- * pays exactly one linear pass over the definition (issue 57).
992
- */
993
867
  private runExecutor;
994
868
  private drainNext;
995
869
  private supersede;
996
870
  private makeAbortError;
997
871
  /**
998
- * Settle a pending/in-flight item exactly once with an error.
999
- *
1000
- * A solve promise can be settled from four concurrent sources — the executor
1001
- * resolving, the executor rejecting, `supersede`, and `cancelAll` — and a JS
1002
- * promise silently ignores a second settle. This guard is the single place the
1003
- * settle-once invariant lives: it makes the *first* settle win and reports
1004
- * whether this call was that winner, so callers fire their own hook only when
1005
- * they actually settled. Any new settle path must go through here (or
1006
- * {@link settleSuccess}) so the guard can't be forgotten.
1007
- *
1008
- * @returns `true` if this call settled the item; `false` if it was already settled.
872
+ * The executor, `supersede`, `cancelAll`, abort and the queue limits can all race to settle one
873
+ * item, and a promise ignores a second settle silently. The first settle wins; the return value
874
+ * says whether this call won, so only the winner fires its hook. Every settle path goes through
875
+ * here or {@link settleSuccess}.
1009
876
  */
1010
877
  private settleError;
1011
878
  /**
1012
- * Settle a pending/in-flight item exactly once with a successful response.
1013
- * The success counterpart to {@link settleError}; see it for the invariant.
1014
- *
1015
- * @returns `true` if this call settled the item; `false` if it was already settled.
879
+ * Fires `onSettle` for queued items too, so a consumer counting solves in progress doesn't
880
+ * leak one. A supersede settles through {@link settleError} and fires `onSuperseded` instead.
1016
881
  */
882
+ private fail;
1017
883
  private settleSuccess;
1018
- /** Detach the queued-phase abort listener, if one is still attached. */
1019
884
  private removeQueuedAbortHandler;
1020
- private isAbortLikeError;
1021
885
  private normalizeExecutionError;
1022
886
  /** Cancel everything — in-flight and pending. */
1023
887
  cancelAll(): void;
1024
- private rejectAsAborted;
1025
888
  private readCache;
1026
889
  private writeCache;
1027
- /** Remove one cache entry and release its bytes from the running total. */
1028
890
  private dropCacheEntry;
1029
891
  clearCache(): void;
1030
892
  /**
@@ -1064,6 +926,19 @@ interface SolveOptions {
1064
926
  /** Per-solve live-event callback, forwarded onto the request body. */
1065
927
  selvaevents?: SelvaEventTarget;
1066
928
  }
929
+ /** Options for {@link GrasshopperClient.create} that shape construction, not the solves. */
930
+ interface CreateClientOptions {
931
+ /**
932
+ * Retry ladder for the liveness probe: `attempts` retries after the first probe (default 2),
933
+ * with exponential backoff from `baseDelayMs` (default 250) capped at `maxDelayMs` (default 1000).
934
+ * Independent of `config.retry`, which governs solves only.
935
+ */
936
+ probe?: {
937
+ attempts?: number;
938
+ baseDelayMs?: number;
939
+ maxDelayMs?: number;
940
+ };
941
+ }
1067
942
  /**
1068
943
  * GrasshopperClient provides a simple API for interacting with a Rhino Compute server and grasshopper.
1069
944
  *
@@ -1081,7 +956,8 @@ interface SolveOptions {
1081
956
  * });
1082
957
  *
1083
958
  * try {
1084
- * const result = await client.solve(definitionUrl, { x: 1, y: 2 });
959
+ * const io = await client.getIO(definitionUrl);
960
+ * const result = await client.solve(definitionUrl, TreeBuilder.fromInputParams(io.inputs));
1085
961
  * } finally {
1086
962
  * await client.dispose(); // Clean up resources
1087
963
  * }
@@ -1112,16 +988,12 @@ declare class GrasshopperClient {
1112
988
  * a user who clicked Solve, so the retry ladder must not spend its whole
1113
989
  * budget confirming that a machine is switched off.
1114
990
  *
1115
- * Each probe is bounded by {@link CREATE_PROBE_TIMEOUT_MS} rather than the
1116
- * stats default: a healthy `GET /` answers in milliseconds, so a longer
1117
- * per-probe timeout only multiplies the wait when the server is unreachable.
1118
- *
1119
991
  * @throws {ComputeError} with code NETWORK_ERROR if the server stays
1120
992
  * unreachable across all attempts. `context.probeVerdict` carries the
1121
993
  * {@link ProbeVerdict} so callers can render a specific cause.
1122
994
  * @throws {ComputeError} with code INVALID_CONFIG if configuration is invalid
1123
995
  */
1124
- static create(config: GrasshopperComputeConfig): Promise<GrasshopperClient>;
996
+ static create(config: GrasshopperComputeConfig, options?: CreateClientOptions): Promise<GrasshopperClient>;
1125
997
  /**
1126
998
  * Gets the client's configuration.
1127
999
  * Useful for passing to lower-level functions.
@@ -1165,16 +1037,7 @@ declare class GrasshopperClient {
1165
1037
  * Call this when you're done using the client.
1166
1038
  */
1167
1039
  dispose(): Promise<void>;
1168
- /**
1169
- * Ensures the client hasn't been disposed.
1170
- */
1171
1040
  private ensureNotDisposed;
1172
- /**
1173
- * Validates and normalizes a compute configuration.
1174
- *
1175
- * @throws {ComputeError} with code INVALID_CONFIG if configuration is invalid
1176
- */
1177
- private normalizeComputeConfig;
1178
1041
  }
1179
1042
  //#endregion
1180
1043
  //#region src/grasshopper/io/output/response-processors.d.ts
@@ -1203,39 +1066,13 @@ interface GetValuesResult<T = ParsedContext> {
1203
1066
  }
1204
1067
  //#endregion
1205
1068
  //#region src/grasshopper/client/grasshopper-response-processor.d.ts
1206
- /**
1207
- * High-level wrapper for interacting with Grasshopper Compute responses.
1208
- *
1209
- * This class exposes a clean, consistent API for accessing parsed values,
1210
- * geometry, and produced files. It is designed to be the primary interface
1211
- * when working with Grasshopper results in client applications.
1212
- */
1069
+ /** Reads parsed values and produced files out of one compute response. */
1213
1070
  declare class GrasshopperResponseProcessor {
1214
- /**
1215
- * The raw compute response. Public so callers can hand it to a renderer-side parser — e.g.
1216
- * `getThreeMeshesFromComputeResponse` in `@selvajs/visualization/parse`, which replaced this
1217
- * class's former `extractMeshesFromResponse()`.
1218
- */
1071
+ /** Hand it to a mesh parser such as `@selvajs/visualization/parse`. */
1219
1072
  readonly response: GrasshopperComputeResponse;
1220
- /**
1221
- * Retained for call-signature compatibility. Its only consumer was the removed
1222
- * `extractMeshesFromResponse()`, which merged it into the parse options; pass `debug`
1223
- * directly to the parser in `@selvajs/visualization/parse` instead.
1224
- */
1225
- readonly debug: boolean;
1226
1073
  constructor(
1227
- /**
1228
- * The raw compute response. Public so callers can hand it to a renderer-side parser — e.g.
1229
- * `getThreeMeshesFromComputeResponse` in `@selvajs/visualization/parse`, which replaced this
1230
- * class's former `extractMeshesFromResponse()`.
1231
- */
1232
- response: GrasshopperComputeResponse,
1233
- /**
1234
- * Retained for call-signature compatibility. Its only consumer was the removed
1235
- * `extractMeshesFromResponse()`, which merged it into the parse options; pass `debug`
1236
- * directly to the parser in `@selvajs/visualization/parse` instead.
1237
- */
1238
- debug?: boolean);
1074
+ /** Hand it to a mesh parser such as `@selvajs/visualization/parse`. */
1075
+ response: GrasshopperComputeResponse);
1239
1076
  /**
1240
1077
  * Extract all values in the response.
1241
1078
  *
@@ -1272,19 +1109,6 @@ declare class GrasshopperResponseProcessor {
1272
1109
  } | {
1273
1110
  byId: string;
1274
1111
  }, options?: GetValuesOptions): any;
1275
- /**
1276
- * REMOVED: `extractMeshesFromResponse()`. Mesh decoding lives in `@selvajs/visualization` now, so
1277
- * this package stays pure solve/data and carries no `three` dependency. Call the parser directly
1278
- * with the raw response:
1279
- *
1280
- * ```ts
1281
- * import { getThreeMeshesFromComputeResponse } from '@selvajs/visualization/parse';
1282
- *
1283
- * const meshes = await getThreeMeshesFromComputeResponse(processor.response, { rhino });
1284
- * scene.add(...meshes);
1285
- * ```
1286
- */
1287
- private getFileData;
1288
1112
  /**
1289
1113
  * Download all files generated by Grasshopper, optionally including
1290
1114
  * additional user-provided files.
@@ -1318,100 +1142,54 @@ declare class GrasshopperResponseProcessor {
1318
1142
  * (false misses are harmless; false hits serve the wrong cached solve).
1319
1143
  */
1320
1144
  export declare function stableStringify(value: unknown): string;
1321
- /**
1322
- * Hash definition and data tree into a stable cache key.
1323
- *
1324
- * The definition is the *identity* of what we solve, so a binary definition is
1325
- * hashed over its full content (`fnv1aBytes`) — a length-only or sampled key
1326
- * would let two different `.gh` files collide and serve one's cached solve for
1327
- * the other. `.gh` files are small enough that a single linear pass is
1328
- * negligible. A {@link DefinitionRef} is keyed by its `key` alone — the
1329
- * caller-declared identity of immutable bytes — so no bytes are materialized
1330
- * or hashed at all.
1331
- *
1332
- * The key keeps the definition and tree hashes as separate parts rather than
1333
- * collapsing them into one 32-bit hash: a single FNV pass over the pair would
1334
- * birthday-collide quadratically in cache size, while requiring both 32-bit
1335
- * parts (plus lengths) to collide at once makes that negligible.
1336
- */
1145
+ /** The scheduler's solve-cache key for a definition and its input tree. */
1337
1146
  export declare function hashSolveInput(definition: SolveDefinition, dataTree: unknown): string;
1338
1147
  /**
1339
- * Stable identity of a definition alone (no inputs) — used to key the
1340
- * server-cache-key map so the same definition reuses its `pointer` across solves
1341
- * with different inputs. Same full-content hashing as {@link hashSolveInput}: a
1342
- * binary definition is hashed over all its bytes so two distinct `.gh` files of
1343
- * equal length can't share a cache key. A {@link DefinitionRef} is keyed by its
1344
- * `key` verbatim (refs are short identities like UUIDs, safe as Map keys) —
1345
- * its immutability contract makes the key equivalent to a content hash.
1148
+ * A definition's identity, without its inputs. Strings and bytes are hashed over their full
1149
+ * content, so two files of equal length never share a key; a `DefinitionRef` uses its `key`.
1346
1150
  */
1347
1151
  export declare function hashDefinition(definition: SolveDefinition): string;
1348
1152
  //#endregion
1349
1153
  //#region src/grasshopper/solve.d.ts
1350
1154
  /**
1351
- * Runs a Rhino Compute job using the provided tree prototypes and Grasshopper definition.
1352
- *
1353
- * @public Use this for direct compute control. For high-level API, use `GrasshopperClient.solve()`.
1155
+ * Run one solve. `GrasshopperClient.solve()` adds input validation and throws on solver errors.
1354
1156
  *
1355
- * @param dataTree - An array of `DataTree` objects representing the input data for the compute job.
1356
- * @param definition - The Grasshopper definition, which can be:
1357
- * - A URL string (e.g., 'https://example.com/definition.gh')
1358
- * - A base64-encoded string of the .gh file
1359
- * - A plain string (will be base64-encoded)
1360
- * - A Uint8Array of the .gh file (will be base64-encoded)
1361
- * - A `DefinitionRef` (bytes are loaded via `ref.load()` for the upload)
1362
- * @param config - Compute configuration (server URL, API key, etc. along with optional timeout, units, etc.)
1363
- * @returns An object containing the compute result and extracted file data.
1157
+ * @param definition - A URL (sent as a pointer), base64 `.gh` content, raw `.gh` bytes, or a
1158
+ * `DefinitionRef` (loaded for the upload). Any other string is encoded as the definition text.
1159
+ * @returns The compute response, without the server's `pointer` and echoed `algo`.
1364
1160
  *
1365
1161
  * @example
1366
- * // Using a URL
1367
1162
  * await solveGrasshopperDefinition(trees, 'https://example.com/definition.gh', config);
1368
- *
1369
- * // Using a base64 string
1370
- * await solveGrasshopperDefinition(trees, 'UEsDBBQAAAAIAL...', config);
1371
- *
1372
- * // Using binary data
1373
- * const fileData = new Uint8Array([...]);
1374
- * await solveGrasshopperDefinition(trees, fileData, config);
1163
+ * await solveGrasshopperDefinition(trees, new Uint8Array(await readFile('model.gh')), config);
1375
1164
  */
1376
1165
  export declare function solveGrasshopperDefinition(dataTree: DataTree[], definition: SolveDefinition, config: GrasshopperComputeConfig): Promise<GrasshopperComputeResponse>;
1377
1166
  //#endregion
1378
1167
  //#region src/grasshopper/io/definition-io.d.ts
1379
1168
  /**
1380
- * Fetches raw input/output schemas from a Grasshopper definition.
1169
+ * Fetches a definition's input/output schemas without per-type parsing.
1381
1170
  *
1382
- * "Raw" means no per-type parsing (no default coercion, no discriminated-union
1383
- * typing) — but NOT byte-for-byte wire data. The response IS normalized:
1384
- * per-param field KEYS are canonicalized to camelCase across server branches
1385
- * ({@link normalizeInputSchema} / {@link normalizeOutputSchema}, with honest
1386
- * fallbacks for missing required fields), missing/non-array `inputs`/`outputs`
1387
- * coerce to `[]`, and server load diagnostics surface as
1388
- * `loadWarnings`/`loadErrors` (coerced to strings). Field VALUES — notably
1389
- * `default` and the `values` dropdown-label map — pass through verbatim.
1171
+ * Field KEYS are canonicalized to camelCase and missing required fields get a
1172
+ * fallback ({@link normalizeInputSchema}); a missing or non-array `inputs` /
1173
+ * `outputs` becomes `[]`. Field VALUES, notably `default` and the `values`
1174
+ * label map, pass through verbatim.
1390
1175
  *
1391
- * @param definition - The Grasshopper definition (URL, base64 string, or Uint8Array)
1392
- * @param config - Compute configuration (server URL, API key, etc.)
1393
- * @returns Key-normalized inputs and outputs with no per-type processing
1394
1176
  * @throws {ComputeError} If fetch fails or response is invalid
1395
1177
  *
1396
1178
  * @public Use `fetchParsedDefinitionIO()` for processed, type-safe inputs
1397
1179
  */
1398
1180
  export declare function fetchDefinitionIO(definition: string | Uint8Array, config: ComputeConfig): Promise<GrasshopperParsedIORaw>;
1399
1181
  /**
1400
- * Fetches and processes input/output schemas from a Grasshopper definition.
1401
- * Returns strongly-typed, validated input parameters ready for use.
1402
- *
1403
- * @public This is the recommended way to fetch definition I/O schemas.
1182
+ * Fetches a definition's input/output schemas and parses each input into its
1183
+ * typed `InputParam`. An input that fails to parse gets a safe default
1184
+ * and an entry in `parseErrors`.
1404
1185
  *
1405
- * @param definition - The Grasshopper definition (URL, base64 string, or Uint8Array)
1406
- * @param config - Compute configuration (server URL, API key, etc.)
1407
- * @returns Processed inputs with discriminated union types and outputs
1408
1186
  * @throws {ComputeError} If fetch fails or response is invalid
1409
1187
  *
1410
1188
  * @example
1411
1189
  * ```typescript
1412
1190
  * const { inputs, outputs } = await fetchParsedDefinitionIO(
1413
1191
  * 'https://example.com/definition.gh',
1414
- * { serverUrl: 'https://compute.rhino3d.com', apiKey: 'YOUR_KEY' }
1192
+ * { serverUrl: 'http://localhost:6500', apiKey: 'YOUR_KEY' }
1415
1193
  * );
1416
1194
  *
1417
1195
  * // Inputs are now strongly typed
@@ -1436,101 +1214,87 @@ interface SchemaEndpointResult<TSchema = unknown> {
1436
1214
  * Read compute's `/grasshopper/schema` body: `[{ FileName, Schemas }]` per
1437
1215
  * uploaded file, or a bare object for a single file.
1438
1216
  *
1439
- * Use this rather than unwrapping the body by hand. The wrapper's casing varies
1440
- * by server branch — mcneel serializes `FileName`/`Schemas`, the VektorNode fork
1441
- * `fileName`/`schemas` — so a fixed-key read silently yields `undefined` against
1442
- * half the servers, and the endpoint answers 200 either way. The failure looks
1443
- * like "this definition has no schemas", which sends you debugging the wrong
1444
- * thing entirely.
1445
- *
1446
- * A blanket key-rewrite (the old `camelcaseKeys` approach) is NOT the fix: it
1447
- * reaches inside the schemas and mangles user-authored names — `"Display3d"` →
1448
- * `"display3d"`, value-list labels like `"Option A"` → `"optionA"`. Only the two
1449
- * wrapper keys are read here; schema CONTENTS pass through untouched, which is
1450
- * why `TSchema` is a pass-through type parameter this module never inspects.
1217
+ * Use this rather than unwrapping the body by hand: the wrapper keys' casing
1218
+ * varies by server branch, and a fixed-key read yields "no schemas" with a 200.
1219
+ * Only the two wrapper keys are read; schema contents pass through untouched.
1451
1220
  *
1452
1221
  * @typeParam TSchema - Your schema type (e.g. `UISchema`). Not inspected.
1453
- * @param raw - The parsed JSON body.
1454
1222
  */
1455
1223
  export declare function readSchemaResults<TSchema = unknown>(raw: unknown): SchemaEndpointResult<TSchema>[];
1456
1224
  //#endregion
1457
1225
  //#region src/grasshopper/io/normalize-ui-schema.d.ts
1458
1226
  /**
1459
- * Canonicalize a `/grasshopper/schema` body's key CASING.
1460
- *
1461
- * ## Why this exists
1462
- *
1463
- * Compute serializes the plugin's `UISchema` POCO, whose camelCase wire names
1464
- * live in Newtonsoft `[JsonProperty]` attributes. `Selva.gha` ILRepack-merges
1465
- * Newtonsoft into itself, so those attributes have type
1466
- * `Selva!Newtonsoft.Json.JsonPropertyAttribute`. When the serializer that runs is
1467
- * compute's OWN Newtonsoft assembly, it does not recognize that type as its own
1468
- * attribute, reads no attributes at all, and falls back to raw CLR member names —
1469
- * emitting `Inputs`/`Layout`/`SchemaVersion`.
1470
- *
1471
- * Nothing throws on the wire. Every consumer reads `schema.inputs` as
1472
- * `undefined`, so the definition renders with no inputs, and `schemaVersion`
1473
- * reads `undefined` — which also silently disables the newer-plugin version gate.
1474
- *
1475
- * This is the schema-body counterpart to {@link normalizeInputSchema}, which
1476
- * solves the same split for the `/io` endpoint's param records.
1477
- *
1478
- * ## Why a casing rule, and not a key allowlist
1479
- *
1480
- * The wire names are the CLR names run through Newtonsoft's camelCase strategy,
1481
- * so reproducing that strategy covers every key. An allowlist would have to
1482
- * enumerate every structural key in `UISchema` and drift out of date each time
1483
- * the schema gains a field, failing silently and in exactly this way again.
1227
+ * Canonicalize a `/grasshopper/schema` body's key casing.
1484
1228
  *
1485
- * ## What it does NOT touch
1229
+ * Compute serializes the plugin's `UISchema`, whose camelCase wire names live in
1230
+ * Newtonsoft `[JsonProperty]` attributes. `Selva.gha` ILRepack-merges Newtonsoft,
1231
+ * so those attributes are `Selva!Newtonsoft.Json.JsonPropertyAttribute`. When
1232
+ * compute's OWN Newtonsoft serializes, it doesn't recognize them, reads no
1233
+ * attributes, and emits raw CLR names (`Inputs`, `SchemaVersion`). Nothing throws:
1234
+ * `schema.inputs` reads `undefined`, and so does the `schemaVersion` gate.
1486
1235
  *
1487
- * `options`, `defaultOptions` and `values` hold USER-AUTHORED keys — dropdown
1488
- * labels like `"Standart Beschichtung"`, `"Use 10 Elements instead"`, `"True"`.
1489
- * Those maps are copied verbatim; only the key naming them is canonicalized.
1490
- * Rewriting their contents is the exact regression that motivated deleting the
1491
- * old global `camelcaseKeys` pass (see `read-field.ts`), which mangled
1492
- * `"Option A"` into `"optionA"` and silently changed what a definition solved
1493
- * with.
1494
- *
1495
- * Values are never inspected — only object keys are rewritten.
1236
+ * A casing rule, not a key allowlist: the wire names are the CLR names run
1237
+ * through Newtonsoft's camelCase strategy, so reproducing it covers every key
1238
+ * without drifting each time `UISchema` gains a field.
1496
1239
  */
1497
1240
  /**
1498
- * Returns a schema whose structural keys are camelCase, regardless of which
1499
- * Newtonsoft serialized it. A body that is already camelCase passes through
1500
- * unchanged in shape (it is still rebuilt, so the result is always a fresh
1501
- * object the caller may mutate).
1502
- *
1503
- * Safe to call on any parsed JSON value; non-objects are returned as-is.
1241
+ * Returns a schema whose structural keys are camelCase, whichever Newtonsoft
1242
+ * serialized it. Always a fresh object the caller may mutate; non-objects are
1243
+ * returned as-is.
1504
1244
  */
1505
1245
  export declare function normalizeUISchemaCasing<T>(raw: T): T;
1506
1246
  //#endregion
1507
1247
  //#region src/grasshopper/io/input/input-processors.d.ts
1508
1248
  /**
1509
1249
  * Parse one raw Grasshopper input schema into a typed {@link InputParam}.
1510
- * Validation failures are swallowed and replaced with a safe default; use
1511
- * {@link processInputWithError} to receive them.
1250
+ * A validation failure is swallowed and replaced with a safe default.
1512
1251
  */
1513
1252
  export declare function processInput(rawInput: InputParamSchema): InputParam;
1514
1253
  //#endregion
1515
- //#region src/grasshopper/data-tree/data-tree.d.ts
1254
+ //#region src/grasshopper/io/output/rhino-objects.d.ts
1516
1255
  /**
1517
- * Value types that can be stored in a DataTree
1256
+ * Sentinel a Rhino geometry decode returns when a decode was attempted
1257
+ * and failed hard (a registered decoder or `CommonObject.decode` threw). Carries
1258
+ * the original payload so callers can log or retry. Use
1259
+ * {@link isRhinoDecodeError} to detect it.
1518
1260
  */
1519
- type DataTreeValue = string | number | boolean | object | null;
1520
- /**
1521
- * Simple data item for compute requests (not to be confused with DataItem interface for responses).
1522
- * Note: While TypeScript defines this as string, Rhino Compute accepts boolean/number primitives in JSON.
1523
- */
1524
- interface ComputeDataItem {
1525
- data: string | boolean | number;
1261
+ interface RhinoDecodeError {
1262
+ __decodeError: true;
1263
+ /** The Rhino type name the failed decode was attempted for. */
1264
+ type: string;
1265
+ /** The original (undecoded) payload. */
1266
+ raw: unknown;
1526
1267
  }
1268
+ /** Type guard for the {@link RhinoDecodeError} sentinel. */
1269
+ export declare function isRhinoDecodeError(value: unknown): value is RhinoDecodeError;
1527
1270
  /**
1528
- * InnerTree data structure for compute requests.
1271
+ * Free every rhino3dm WASM object reachable from `value`.
1272
+ *
1273
+ * rhino3dm objects are emscripten bindings — JS GC never reclaims their WASM
1274
+ * heap allocation, so everything decoded from a solve response (`getValues`,
1275
+ * `getValue`) must be deleted explicitly or the heap
1276
+ * grows monotonically across solves (e.g. a UI decoding per slider tick).
1277
+ *
1278
+ * Walks arrays and plain objects recursively; anything exposing a `delete()`
1279
+ * method is treated as a WASM binding and freed (skipped if already deleted).
1280
+ * Safe to call more than once and on values containing no WASM objects.
1529
1281
  */
1530
- type ComputeInnerTreeData = { [path in DataTreePath]: ComputeDataItem[]; };
1282
+ export declare function disposeRhinoObjects(value: unknown): void;
1283
+ //#endregion
1284
+ //#region src/grasshopper/data-tree/data-tree.d.ts
1285
+ type DataTreeValue = string | number | boolean | object | null;
1286
+ /** One input's value for {@link TreeBuilder.fromValues}. */
1287
+ interface TreeValueEntry {
1288
+ paramName: string;
1289
+ /** Matched case-insensitively. */
1290
+ paramType: string;
1291
+ /** A scalar, an array (branch `{0}`), or a branch-path map like `{ '{0;1}': [1, 2] }`. */
1292
+ value: unknown;
1293
+ minimum?: number | null;
1294
+ maximum?: number | null;
1295
+ }
1531
1296
  /**
1532
- * Standalone TreeBuilder class for constructing Grasshopper TreeBuilder structures.
1533
- * Does not depend on RhinoCompute library.
1297
+ * Builds the Grasshopper data trees a solve takes as input.
1534
1298
  *
1535
1299
  * @example
1536
1300
  * ```ts
@@ -1544,247 +1308,70 @@ export declare class TreeBuilder {
1544
1308
  private innerTree;
1545
1309
  private paramName;
1546
1310
  constructor(paramName: string);
1547
- /**
1548
- * Append values to a specific path in the tree.
1549
- *
1550
- * @param path - Array of integers representing the branch path (e.g., [0], [0, 1])
1551
- * @param items - Values to append at this path
1552
- * @returns this for method chaining
1553
- */
1311
+ /** Append values to the branch at `path` (`[0, 1]` is `{0;1}`). */
1554
1312
  append(path: number[], items: DataTreeValue[]): this;
1555
- /**
1556
- * Append a single value to a path.
1557
- *
1558
- * @param path - Branch path
1559
- * @param item - Single value to append
1560
- * @returns this for method chaining
1561
- */
1562
- appendSingle(path: number[], item: DataTreeValue): this;
1563
- /**
1564
- * Set values from a DataTreeDefault structure.
1565
- * Replaces any existing tree data.
1566
- *
1567
- * @param treeData - TreeBuilder structure with path keys like "{0;1}"
1568
- * @returns this for method chaining
1569
- */
1313
+ /** Replace the tree with a branch-path map like `{ '{0;1}': [1, 2] }`. */
1570
1314
  fromDataTreeDefault(treeData: DataTreeDefault): this;
1571
- /**
1572
- * Append flattened values to path [0].
1573
- * Useful for simple flat inputs.
1574
- *
1575
- * @param values - Single value or array of values
1576
- * @returns this for method chaining
1577
- */
1315
+ /** Append a value, or each value of an array, to branch `{0}`. */
1578
1316
  appendFlat(values: DataTreeValue | DataTreeValue[]): this;
1579
- /**
1580
- * Get the flattened list of all values in the tree.
1581
- *
1582
- * @returns Array of all values across all branches
1583
- */
1317
+ /** Every value across all branches. */
1584
1318
  flatten(): DataTreeValue[];
1585
- /**
1586
- * Get all paths in the tree.
1587
- *
1588
- * @returns Array of path strings
1589
- */
1590
- getPaths(): DataTreePath[];
1591
- /**
1592
- * Get values at a specific path.
1593
- *
1594
- * @param path - Path to retrieve values from
1595
- * @returns Array of values or undefined if path doesn't exist
1596
- */
1597
- getPath(path: number[]): DataTreeValue[] | undefined;
1598
- /**
1599
- * Convert to format compatible with Grasshopper Compute API.
1600
- *
1601
- * @returns InnerTree object ready for compute
1602
- */
1319
+ /** The tree in the shape a solve request sends. */
1603
1320
  toComputeFormat(): DataTree;
1604
- /**
1605
- * Get the raw InnerTree data structure.
1606
- *
1607
- * @returns InnerTree data
1608
- */
1609
- getInnerTree(): ComputeInnerTreeData;
1610
- /**
1611
- * Get the parameter name.
1612
- *
1613
- * @returns Parameter name
1614
- */
1615
1321
  getParamName(): string;
1616
1322
  /**
1617
- * Create DataTrees from an array of InputParam definitions.
1618
- * Handles tree access, numeric constraints, and value parsing.
1619
- *
1620
- * @param inputs - Array of input parameter definitions
1621
- * @returns Array of InnerTree instances ready for compute
1622
- *
1623
- * @example
1624
- * ```ts
1625
- * const trees = TreeBuilder.fromInputParams(inputs);
1626
- * ```
1323
+ * One tree per input that has a default, keyed by its nickname. Defaults are coerced and
1324
+ * clamped by the same rules as {@link TreeBuilder.fromValues}.
1627
1325
  */
1628
1326
  static fromInputParams(inputs: InputParam[]): DataTree[];
1629
1327
  /**
1630
- * Create a TreeBuilder from a single InputParam.
1631
- *
1632
- * @param input - Input parameter definition
1633
- * @returns InnerTree ready for compute or undefined if value is invalid
1328
+ * One tree per entry, coercing UI values the way Grasshopper reads them. Number and Integer
1329
+ * items go through the numeric coercion (Integer rounds) and are clamped to
1330
+ * `minimum`/`maximum`; Boolean items also accept `'true'`/`'false'`; every other type passes
1331
+ * through. An item that fails coercion is dropped with a warning. An entry left with no items
1332
+ * is omitted, so Grasshopper uses its own value; `''` is an item, not an empty value.
1634
1333
  */
1635
- static fromInputParam(input: InputParam): DataTree | undefined;
1334
+ static fromValues(entries: TreeValueEntry[]): DataTree[];
1335
+ private static branchesOf;
1336
+ /** `null` means the item was rejected. */
1337
+ private static itemCoercer;
1636
1338
  /**
1637
- * Set or replace a parameter value within a TreeBuilder or InnerTree array.
1638
- *
1639
- * Supports both high-level `DataTree[]` instances and low-level `InnerTree[]` format.
1640
- *
1641
- * **Architecture Note:**
1642
- * - Use with `DataTree[]` when building/modifying before computation
1643
- * - Use with `InnerTree[]` when modifying compute API results
1644
- * - `DataTree` is the high-level builder; `InnerTree` is the Rhino Compute format
1645
- *
1646
- * Copy-on-write: returns a new array; the caller's array is never mutated.
1647
- *
1648
- * @overload For TreeBuilder instances (high-level builder)
1649
- * @param trees - Array of TreeBuilder instances to read from (not mutated)
1650
- * @param paramName - The parameter name to set or replace
1651
- * @param newValue - The new value (scalar, array, or TreeBuilder structure)
1652
- * @returns A new TreeBuilder array with the updated parameter
1653
- *
1654
- * @overload For compiled InnerTree (low-level API format)
1655
- * @param trees - The compiled InnerTree array (typically from `client.solve()`; not mutated)
1656
- * @param paramName - The parameter name to set or replace
1657
- * @param newValue - The new value (scalar, array, or TreeBuilder structure)
1658
- * @returns A new InnerTree array with the updated parameter
1659
- *
1660
- * @example
1661
- * ```ts
1662
- * // With TreeBuilder instances (high-level)
1663
- * let trees = [new TreeBuilder('X'), new TreeBuilder('Y')];
1664
- * trees = TreeBuilder.replaceTreeValue(trees, 'X', 42);
1665
- * const result = await client.solve(definitionUrl,
1666
- * trees.map(t => t.toComputeFormat())
1667
- * );
1668
- * ```
1669
- *
1670
- * @example
1671
- * ```ts
1672
- * // With InnerTree format (low-level, from API)
1673
- * let trees = await client.solve(definitionUrl, initialInputs);
1674
- * trees = TreeBuilder.replaceTreeValue(trees, 'X', 42);
1675
- * trees = TreeBuilder.replaceTreeValue(trees, 'Y', [1, 2, 3]);
1676
- * ```
1339
+ * Set or add `paramName`'s tree to `newValue`: a scalar, an array (branch `{0}`), or a
1340
+ * branch-path map. Copy-on-write: returns a new array and leaves the caller's untouched, so
1341
+ * assign the result.
1677
1342
  */
1678
- static replaceTreeValue(trees: TreeBuilder[], paramName: string, newValue: DataTreeValue): TreeBuilder[];
1679
1343
  static replaceTreeValue(trees: DataTree[], paramName: string, newValue: DataTreeValue): DataTree[];
1344
+ private static buildFromValue;
1680
1345
  /**
1681
- * Build a TreeBuilder from a single value, dispatching on shape:
1682
- * DataTreeDefault structure, array, or scalar.
1346
+ * `paramName`'s whole tree from a solve result's `values`, keyed by branch path (`'{0;1}'`).
1347
+ * `null` when no tree has that param, an empty `Map` when it has no branches. An item without
1348
+ * `data` reads as `null` in place, so indices don't shift.
1683
1349
  */
1684
- private static buildFromValue;
1350
+ static getTree(trees: DataTree[], paramName: string): Map<DataTreePath, DataTreeValue[]> | null;
1685
1351
  /**
1686
- * Extract a value from a TreeBuilder or InnerTree array by parameter name.
1687
- *
1688
- * Automatically unwraps single values for convenience.
1689
- * Works with both high-level `DataTree[]` instances and low-level `InnerTree[]` format.
1690
- *
1691
- * **Architecture Note:**
1692
- * - Use with `DataTree[]` to read builder instances
1693
- * - Use with `InnerTree[]` to read compute API responses
1694
- * - Return behavior is consistent across both formats
1695
- *
1696
- * **Return Value Behavior:**
1697
- * - Single value → unwrapped (returns `5` not `[5]`)
1698
- * - Multiple values → array of values
1699
- * - Not found → `null`
1352
+ * `paramName`'s value when its tree has a single branch: one item unwrapped (`5`, not `[5]`),
1353
+ * several as an array. `null` for a missing param or an empty tree.
1700
1354
  *
1701
- * @overload For TreeBuilder instances
1702
- * @param trees - Array of TreeBuilder instances to read from
1703
- * @param paramName - The parameter name to retrieve
1704
- * @returns The unwrapped value, array of values, or null if parameter not found
1705
- *
1706
- * @overload For compiled InnerTree
1707
- * @param trees - The compiled InnerTree array (typically from `client.solve()`)
1708
- * @param paramName - The parameter name to retrieve
1709
- * @returns The unwrapped value, array of values, or null if parameter not found
1710
- *
1711
- * @example
1712
- * ```ts
1713
- * // With TreeBuilder instances
1714
- * const trees = [new TreeBuilder('X'), new TreeBuilder('Y')];
1715
- * trees[0].appendFlat(42);
1716
- * const x = TreeBuilder.getTreeValue(trees, 'X'); // Returns 42
1717
- * ```
1718
- *
1719
- * @example
1720
- * ```ts
1721
- * // With InnerTree from compute results
1722
- * const result = await client.solve(definitionUrl, inputs);
1723
- * const x = TreeBuilder.getTreeValue(result, 'X'); // Returns 42 (not [42])
1724
- * const points = TreeBuilder.getTreeValue(result, 'Points'); // Returns [point1, point2, ...]
1725
- * ```
1355
+ * @throws {ComputeError} `INVALID_INPUT` when the tree has more than one branch; read those
1356
+ * with {@link TreeBuilder.getTree}.
1726
1357
  */
1727
- static getTreeValue(trees: TreeBuilder[], paramName: string): DataTreeValue | null;
1728
1358
  static getTreeValue(trees: DataTree[], paramName: string): DataTreeValue | null;
1359
+ private static paramNameOf;
1729
1360
  /**
1730
- * Read all values for `paramName` across every branch of the matching builder.
1731
- * Returns null when the builder isn't found.
1732
- */
1733
- private static readFromBuilders;
1734
- /**
1735
- * Read values from the first branch of the matching compiled InnerTree
1736
- * (multi-branch responses are not flattened — current semantics, pinned by
1737
- * the "reads from the first branch path only" test).
1738
- */
1739
- private static readFromDataTrees;
1740
- /**
1741
- * Parse a TreeBuilder path string like "{0;1;2}" into [0, 1, 2].
1742
- * Negative indices ("{-1;2}") and the root path "{}" are valid.
1361
+ * Parse a branch path like `"{0;1;2}"` into `[0, 1, 2]`.
1743
1362
  *
1744
- * @param pathStr - Path string
1745
- * @returns Array of path indices
1746
- * @throws {ComputeError} `INVALID_INPUT` when the path string is not a valid
1747
- * Grasshopper branch path. Malformed keys must never silently collapse to a
1748
- * default branch — two distinct unparseable keys would merge their items
1749
- * into one branch.
1363
+ * @throws {ComputeError} `INVALID_INPUT` for a malformed path, rather than
1364
+ * collapsing it to a default branch where two bad keys would merge.
1750
1365
  */
1751
1366
  static parsePathString(pathStr: string): number[];
1752
- /**
1753
- * Format a path array into TreeBuilder path string format.
1754
- *
1755
- * @param path - Path as number array
1756
- * @returns Formatted path string like "{0;1;2}"
1757
- */
1367
+ /** `[0, 1, 2]` → `"{0;1;2}"`. */
1758
1368
  static formatPathString(path: number[]): DataTreePath;
1759
- /** Apply numeric constraints to all tree values. */
1760
- private applyNumericConstraints;
1761
- /**
1762
- * Serialize a value for compute requests.
1763
- * Preserves booleans and numbers as primitives for proper Grasshopper parameter handling.
1764
- */
1369
+ /** Booleans and numbers stay primitives: Grasshopper reads them typed. */
1765
1370
  private static serializeValue;
1766
- /**
1767
- * Deserialize a value back to its original type.
1768
- * Handles both string-encoded values and primitive values.
1769
- */
1770
1371
  private static deserializeValue;
1771
- /**
1772
- * Check if a value is valid for inclusion in a DataTree.
1773
- */
1774
1372
  private static hasValidValue;
1775
- /**
1776
- * Check if input is numeric type.
1777
- */
1778
- private static isNumericInput;
1779
- /**
1780
- * Process array of values based on input type.
1781
- */
1782
- private static processValues;
1783
- /**
1784
- * Clamp numeric value to constraints.
1785
- */
1786
1373
  private static clampValue;
1787
1374
  }
1788
1375
  //#endregion
1789
- export { type BooleanInputType, type CacheOptions, ComputeServerStats, type DataItem, type DataTree, type DataTreeDefault, type DataTreePath, type DataTreeValue, type DefaultValue, type DefinitionRef, type FileBaseInfo, type FileData, type FileInputType, type GeometryInputType, type GetValuesOptions, type GetValuesResult, GrasshopperClient, type GrasshopperComputeConfig, type GrasshopperComputeResponse, type GrasshopperParsedIO, type GrasshopperParsedIORaw, type GrasshopperRequestSchema, GrasshopperResponseProcessor, type InnerTreeData, type InputParam, type InputParamSchema, type NumericInputType, type OutputParamSchema, type OutputType, type ParsedContext, type ProcessedFile, type RhinoModelUnit, type SchedulerMode, type SchemaEndpointResult, type SelvaEventTarget, type SelvaResponseBlock, type SolveContext, type SolveDefinition, type SolveOptions, type SolveResult, type SolveSchedulerOptions, type TextInputType, type ValueListInputType, downloadFileData, extractFilesFromComputeResponse, isDefinitionRef };
1376
+ 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 };
1790
1377
  //# sourceMappingURL=grasshopper.d.cts.map