@selvajs/compute 5.0.0-beta.8 → 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.
- package/README.md +30 -11
- package/dist/core.d.ts +190 -2
- package/dist/core.js +1 -1
- package/dist/grasshopper.d.ts +56 -81
- package/dist/grasshopper.js +3051 -1
- package/dist/grasshopper.js.map +1 -1
- package/dist/handle-files-fKtSlWT2.js +1351 -0
- package/dist/handle-files-fKtSlWT2.js.map +1 -0
- package/dist/types-Dm9QFVvA.d.ts +247 -0
- package/package.json +13 -39
- package/dist/core-BPYXUtfq.js +0 -3
- package/dist/core-BPYXUtfq.js.map +0 -1
- package/dist/core-DF0VYOve.cjs +0 -3
- package/dist/core-DF0VYOve.cjs.map +0 -1
- package/dist/core.cjs +0 -1
- package/dist/core.d.cts +0 -2
- package/dist/grasshopper.cjs +0 -2
- package/dist/grasshopper.cjs.map +0 -1
- package/dist/grasshopper.d.cts +0 -1345
- package/dist/index-DDmprCht.d.cts +0 -421
- package/dist/index-DDmprCht.d.ts +0 -421
package/dist/grasshopper.d.cts
DELETED
|
@@ -1,1345 +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
|
-
/**
|
|
442
|
-
* Where a Selva-family plugin running inside the solve may POST live events, and
|
|
443
|
-
* the bearer it presents. The VektorNode fork hands these to the document as
|
|
444
|
-
* constants; a stock server ignores the block. Never echoed in the response.
|
|
445
|
-
*/
|
|
446
|
-
interface SelvaEventTarget {
|
|
447
|
-
url: string;
|
|
448
|
-
solveId: string;
|
|
449
|
-
token: string;
|
|
450
|
-
}
|
|
451
|
-
/**
|
|
452
|
-
* Definition source (used in args and response)
|
|
453
|
-
*/
|
|
454
|
-
interface GrasshopperDefinitionSource {
|
|
455
|
-
/** Base64 encoded algorithm (if embedded) */
|
|
456
|
-
algo?: string | null;
|
|
457
|
-
/** URL pointer to definition file */
|
|
458
|
-
pointer?: string | null;
|
|
459
|
-
/** Filename of the definition */
|
|
460
|
-
filename?: string | null;
|
|
461
|
-
}
|
|
462
|
-
/**
|
|
463
|
-
* Server config plus the Grasshopper solve settings. The definition is not part
|
|
464
|
-
* of it: pass that to `solve()` and friends.
|
|
465
|
-
*/
|
|
466
|
-
interface GrasshopperComputeConfig extends ComputeConfig, GrasshopperBaseSchema {
|
|
467
|
-
/** Per-solve live-event callback; see {@link SelvaEventTarget}. */
|
|
468
|
-
selvaevents?: SelvaEventTarget | null;
|
|
469
|
-
}
|
|
470
|
-
/**
|
|
471
|
-
* Response from Grasshopper compute server.
|
|
472
|
-
*
|
|
473
|
-
* No `pointer`: the solve returns the server's echoed pointer as its `cacheKey`
|
|
474
|
-
* instead. The echoed settings (`cachesolve`, `dataversion`) stay optional:
|
|
475
|
-
* nothing client-side enforces them.
|
|
476
|
-
*/
|
|
477
|
-
interface GrasshopperComputeResponse extends GrasshopperBaseSchema, Omit<GrasshopperDefinitionSource, 'pointer'> {
|
|
478
|
-
/**
|
|
479
|
-
* Model units the definition was solved in. Every conforming server response
|
|
480
|
-
* carries this (it drives downstream scaling, e.g. the webdisplay parser),
|
|
481
|
-
* but note it is not validated client-side — a non-conforming server or
|
|
482
|
-
* hand-built mock may omit it at runtime.
|
|
483
|
-
*/
|
|
484
|
-
modelunits: RhinoModelUnit;
|
|
485
|
-
/** Always `undefined` on a client-returned response: the solve strips it. */
|
|
486
|
-
algo?: string | null;
|
|
487
|
-
/** Filename of the definition (always present in response) */
|
|
488
|
-
filename: string | null;
|
|
489
|
-
/** Recursion level used */
|
|
490
|
-
recursionlevel?: number;
|
|
491
|
-
/** Output values organized by parameter */
|
|
492
|
-
values: DataTree[];
|
|
493
|
-
/** Computation errors */
|
|
494
|
-
errors?: string[];
|
|
495
|
-
/** Computation warnings */
|
|
496
|
-
warnings?: string[];
|
|
497
|
-
/** Set by the VektorNode fork when a Selva plugin judged the solve; see {@link SelvaResponseBlock}. */
|
|
498
|
-
selva?: SelvaResponseBlock | null;
|
|
499
|
-
}
|
|
500
|
-
/**
|
|
501
|
-
* The Selva plugin's verdict on a solve, which the VektorNode fork copies verbatim into the
|
|
502
|
-
* response. `outcome` is `SolveOutcome` from `@selvajs/schemas`, typed loosely here because
|
|
503
|
-
* this package does not depend on it. A blocked or aborted outcome comes with empty `values`.
|
|
504
|
-
* Absent from a stock server, an older plugin, or a definition without a Selva component.
|
|
505
|
-
*/
|
|
506
|
-
interface SelvaResponseBlock {
|
|
507
|
-
outcome?: unknown;
|
|
508
|
-
}
|
|
509
|
-
/**
|
|
510
|
-
* Output parameter
|
|
511
|
-
*/
|
|
512
|
-
interface OutputParamSchema {
|
|
513
|
-
name: string;
|
|
514
|
-
nickname: string | null;
|
|
515
|
-
paramType: string;
|
|
516
|
-
/**
|
|
517
|
-
* Grasshopper parameter instance GUID
|
|
518
|
-
*/
|
|
519
|
-
id: string;
|
|
520
|
-
}
|
|
521
|
-
/**
|
|
522
|
-
* Input parameter
|
|
523
|
-
*/
|
|
524
|
-
interface InputParamSchema {
|
|
525
|
-
/**
|
|
526
|
-
* Grasshopper parameter instance GUID
|
|
527
|
-
*/
|
|
528
|
-
id: string;
|
|
529
|
-
name: string;
|
|
530
|
-
nickname: string | null;
|
|
531
|
-
description: string;
|
|
532
|
-
paramType: string;
|
|
533
|
-
treeAccess: boolean;
|
|
534
|
-
minimum: number | null;
|
|
535
|
-
maximum: number | null;
|
|
536
|
-
atLeast: number;
|
|
537
|
-
atMost: number;
|
|
538
|
-
stepSize?: number;
|
|
539
|
-
default: any;
|
|
540
|
-
/**
|
|
541
|
-
* Key-value pairs for dropdown options
|
|
542
|
-
*/
|
|
543
|
-
values?: Record<string, string>;
|
|
544
|
-
/**
|
|
545
|
-
* Accepted file formats for File input
|
|
546
|
-
*/
|
|
547
|
-
acceptedFormats?: string[];
|
|
548
|
-
groupName?: string | null;
|
|
549
|
-
}
|
|
550
|
-
/** Input/output schemas as the `/io` endpoint reports them, keys canonicalized. */
|
|
551
|
-
interface GrasshopperParsedIORaw {
|
|
552
|
-
inputs: InputParamSchema[];
|
|
553
|
-
outputs: OutputParamSchema[];
|
|
554
|
-
/**
|
|
555
|
-
* Warnings the server raised loading the definition (e.g. an obsolete
|
|
556
|
-
* component). Distinct from `parseErrors`, the client failing to type an input.
|
|
557
|
-
*/
|
|
558
|
-
loadWarnings?: string[];
|
|
559
|
-
/**
|
|
560
|
-
* Errors the server raised loading the definition (e.g. a missing plugin).
|
|
561
|
-
* When present, `inputs`/`outputs` may be incomplete.
|
|
562
|
-
*/
|
|
563
|
-
loadErrors?: string[];
|
|
564
|
-
}
|
|
565
|
-
/**
|
|
566
|
-
* Per-input parse failure. The corresponding entry in `inputs` was filled
|
|
567
|
-
* with a safe default so the rest of the pipeline can keep going — but the
|
|
568
|
-
* caller should surface this so the user knows their definition has a
|
|
569
|
-
* misconfigured parameter.
|
|
570
|
-
*/
|
|
571
|
-
interface InputParseError {
|
|
572
|
-
/** The input's `name` (or `'unknown'` if the schema didn't have one). */
|
|
573
|
-
inputName: string;
|
|
574
|
-
/** The declared paramType from the raw schema. */
|
|
575
|
-
paramType: string;
|
|
576
|
-
/** Human-readable reason from the underlying ComputeError. */
|
|
577
|
-
message: string;
|
|
578
|
-
/** Error code from the underlying ComputeError, if available. */
|
|
579
|
-
code?: string;
|
|
580
|
-
}
|
|
581
|
-
/** {@link GrasshopperParsedIORaw} with each input parsed into its typed {@link InputParam}. */
|
|
582
|
-
interface GrasshopperParsedIO extends Omit<GrasshopperParsedIORaw, 'inputs'> {
|
|
583
|
-
inputs: InputParam[];
|
|
584
|
-
/**
|
|
585
|
-
* Inputs that failed validation and got a safe default. The result is still
|
|
586
|
-
* usable, but surface these so the user can fix their definition.
|
|
587
|
-
*/
|
|
588
|
-
parseErrors?: InputParseError[];
|
|
589
|
-
}
|
|
590
|
-
/**
|
|
591
|
-
* How concurrent `solve()` calls interact.
|
|
592
|
-
*
|
|
593
|
-
* - `latest-wins`: one in flight at a time. A new call supersedes the pending
|
|
594
|
-
* one and aborts the one in flight. For slider scrubs and live UIs.
|
|
595
|
-
* - `queue`: FIFO, every call runs to completion, up to `maxConcurrent` at
|
|
596
|
-
* once (default 1). For "submit job" flows where every request matters.
|
|
597
|
-
*/
|
|
598
|
-
type SchedulerMode = 'latest-wins' | 'queue';
|
|
599
|
-
interface CacheOptions {
|
|
600
|
-
/**
|
|
601
|
-
* Total byte budget for retained responses, evicted LRU. The only size bound
|
|
602
|
-
* there is: responses range KB→100s of MB, so memory is the constraint that
|
|
603
|
-
* matters and an entry count would only obscure it.
|
|
604
|
-
*
|
|
605
|
-
* Sizing uses the response's wire size (JSON text length, recorded at the
|
|
606
|
-
* fetch boundary — no re-serialization); a response without that hint
|
|
607
|
-
* (custom executor) is sized by a one-off `JSON.stringify`. A single
|
|
608
|
-
* response larger than the whole budget is served but never retained.
|
|
609
|
-
* Parsed, a response of many small items holds more heap than its wire size,
|
|
610
|
-
* so leave headroom.
|
|
611
|
-
*
|
|
612
|
-
* Required and must be > 0 — to disable caching, pass `cache: false`.
|
|
613
|
-
*/
|
|
614
|
-
maxBytes: number;
|
|
615
|
-
/**
|
|
616
|
-
* Time-to-live in ms. Set to `0` for no expiry (default, and the right
|
|
617
|
-
* choice for a solve keyed by immutable definition+inputs — expiry there
|
|
618
|
-
* only buys a paid re-solve of an identical answer). Meaningful only when a
|
|
619
|
-
* definition reaches outside its inputs (external data source, clock), where
|
|
620
|
-
* a stale result is genuinely wrong rather than merely old.
|
|
621
|
-
*
|
|
622
|
-
* Expiry is evaluated lazily on read: an expired entry keeps its bytes
|
|
623
|
-
* counted against `maxBytes` until that exact key is next consulted.
|
|
624
|
-
*/
|
|
625
|
-
ttlMs?: number;
|
|
626
|
-
/**
|
|
627
|
-
* Cache responses that carry Grasshopper `errors`. Default `true`: an
|
|
628
|
-
* errored solve is a valid, deterministic result — definitions raise GH
|
|
629
|
-
* errors by design (guarded components, validation branches), so replaying
|
|
630
|
-
* one from cache is correct. Set `false` when a definition's errors are
|
|
631
|
-
* transient (external data sources) rather than functions of the inputs.
|
|
632
|
-
*/
|
|
633
|
-
cacheErroredSolves?: boolean;
|
|
634
|
-
}
|
|
635
|
-
interface SolveSchedulerOptions {
|
|
636
|
-
mode?: SchedulerMode;
|
|
637
|
-
maxConcurrent?: number;
|
|
638
|
-
/**
|
|
639
|
-
* Backpressure — cap on how many calls may wait in the FIFO queue (i.e.
|
|
640
|
-
* excluding the ones already in flight). When the queue is full, a new
|
|
641
|
-
* `solve()` is rejected immediately with `code: QUEUE_FULL` (retryable, meant to
|
|
642
|
-
* map to HTTP 503 + Retry-After) instead of piling up unbounded. Bounds the
|
|
643
|
-
* miss path under load. Only applies to `queue` mode —
|
|
644
|
-
* `latest-wins` has an intrinsic depth of 1. Default: unbounded.
|
|
645
|
-
*/
|
|
646
|
-
maxQueueDepth?: number;
|
|
647
|
-
/**
|
|
648
|
-
* Backpressure — max time (ms) a call may sit queued before it starts
|
|
649
|
-
* executing. If it's still waiting after this long it's rejected with
|
|
650
|
-
* `code: QUEUE_TIMEOUT` rather than burning compute on a stale request.
|
|
651
|
-
* Bounds tail latency. Only applies to `queue` mode. Default: no deadline.
|
|
652
|
-
*/
|
|
653
|
-
queueWaitMs?: number;
|
|
654
|
-
timeoutMs?: number;
|
|
655
|
-
retry?: RetryPolicy;
|
|
656
|
-
/**
|
|
657
|
-
* Response caching keyed by hash of (definition, dataTree). Omit or pass
|
|
658
|
-
* `false` to disable; otherwise a byte budget is required, so an unbounded
|
|
659
|
-
* cache can't be enabled by accident.
|
|
660
|
-
*/
|
|
661
|
-
cache?: false | CacheOptions;
|
|
662
|
-
/**
|
|
663
|
-
* Upload a definition once, then solve it by the server's cache key (`pointer`)
|
|
664
|
-
* instead of re-sending the bytes; a `DefinitionRef` also skips its `load()`.
|
|
665
|
-
* When the server has evicted the key, the solve falls back to a full upload,
|
|
666
|
-
* so this is safe to leave on. No effect for URL definitions, which are
|
|
667
|
-
* already a reference. Default `true`.
|
|
668
|
-
*/
|
|
669
|
-
reuseServerDefinitionCache?: boolean;
|
|
670
|
-
/** Lifecycle hooks — fired in order. Errors thrown by hooks are logged, not rethrown. */
|
|
671
|
-
onStart?: (ctx: SolveContext) => void;
|
|
672
|
-
onSettle?: (ctx: SolveContext, result: SolveResult) => void;
|
|
673
|
-
onSuperseded?: (ctx: SolveContext) => void;
|
|
674
|
-
}
|
|
675
|
-
interface SolveContext {
|
|
676
|
-
/** Stable hash of (definition, dataTree). */
|
|
677
|
-
key: string;
|
|
678
|
-
/** Timestamp when scheduler.solve() was called. */
|
|
679
|
-
enqueuedAt: number;
|
|
680
|
-
/** Timestamp when execution actually started (after queueing). */
|
|
681
|
-
startedAt: number | null;
|
|
682
|
-
}
|
|
683
|
-
type SolveResult = {
|
|
684
|
-
status: 'success';
|
|
685
|
-
response: GrasshopperComputeResponse;
|
|
686
|
-
durationMs: number;
|
|
687
|
-
fromCache: boolean;
|
|
688
|
-
/**
|
|
689
|
-
* Whether this solve uploaded the full definition: `true` on its first
|
|
690
|
-
* solve or after the server evicted the cache key, `false` when the server
|
|
691
|
-
* reused its cached copy via the pointer. `undefined` for a `fromCache`
|
|
692
|
-
* hit, or when reuse is off or the definition is a URL.
|
|
693
|
-
*/
|
|
694
|
-
definitionReuploaded?: boolean;
|
|
695
|
-
} | {
|
|
696
|
-
status: 'error';
|
|
697
|
-
error: ComputeError;
|
|
698
|
-
durationMs: number;
|
|
699
|
-
} | {
|
|
700
|
-
status: 'superseded';
|
|
701
|
-
};
|
|
702
|
-
/**
|
|
703
|
-
* Solves by `cacheKey` when given one, otherwise uploads. Reports the server's
|
|
704
|
-
* (possibly new) key and whether the full definition was uploaded (`missed`).
|
|
705
|
-
* The scheduler passes `null` whenever server-definition reuse is off.
|
|
706
|
-
*/
|
|
707
|
-
type SolveExecutor = (definition: SolveDefinition, dataTree: DataTree[], cacheKey: string | null, config: GrasshopperComputeConfig) => Promise<{
|
|
708
|
-
response: GrasshopperComputeResponse;
|
|
709
|
-
cacheKey: string | null;
|
|
710
|
-
missed: boolean;
|
|
711
|
-
}>;
|
|
712
|
-
interface SchedulerSolveOptions {
|
|
713
|
-
signal?: AbortSignal;
|
|
714
|
-
/** Per-solve live-event callback. Not part of the cache key: it names where events go, not what is solved. */
|
|
715
|
-
selvaevents?: SelvaEventTarget;
|
|
716
|
-
/**
|
|
717
|
-
* This call's `Server-Timing` breakdown, after the client config's own `onServerTiming`.
|
|
718
|
-
* Never fires for a `fromCache` hit, which sends no request.
|
|
719
|
-
*/
|
|
720
|
-
onServerTiming?: (timing: ServerTiming, requestId: string) => void;
|
|
721
|
-
}
|
|
722
|
-
/** What `solveDetailed()` resolves with: the response plus how this call got it. */
|
|
723
|
-
interface SolveDetail {
|
|
724
|
-
response: GrasshopperComputeResponse;
|
|
725
|
-
/** Served from the scheduler's response cache without a request. */
|
|
726
|
-
fromCache: boolean;
|
|
727
|
-
/** Same meaning as on {@link SolveResult}. */
|
|
728
|
-
definitionReuploaded?: boolean;
|
|
729
|
-
/** Executor wall time; `0` for a `fromCache` hit. */
|
|
730
|
-
durationMs: number;
|
|
731
|
-
}
|
|
732
|
-
/**
|
|
733
|
-
* Adds to a plain solve call:
|
|
734
|
-
* - Scheduling (latest-wins for sliders, queue for jobs)
|
|
735
|
-
* - Backpressure (bounded queue depth + queue-wait deadline)
|
|
736
|
-
* - Cancellation (per-call signal + cancelAll)
|
|
737
|
-
* - Optional response caching for repeated inputs
|
|
738
|
-
* - Lifecycle hooks (start / settle / superseded) and state via subscribe()
|
|
739
|
-
*
|
|
740
|
-
* @example
|
|
741
|
-
* ```ts
|
|
742
|
-
* const scheduler = client.createScheduler({ mode: 'latest-wins', timeoutMs: 30_000 });
|
|
743
|
-
*
|
|
744
|
-
* // From a slider handler:
|
|
745
|
-
* scheduler.solve(definition, tree).then((result) => {
|
|
746
|
-
* updateMeshes(result);
|
|
747
|
-
* }).catch((err) => {
|
|
748
|
-
* if (err.code !== 'SUPERSEDED') showError(err);
|
|
749
|
-
* });
|
|
750
|
-
*
|
|
751
|
-
* // From a UI binding:
|
|
752
|
-
* scheduler.subscribe(() => {
|
|
753
|
-
* showSpinner = scheduler.isSolving;
|
|
754
|
-
* });
|
|
755
|
-
* ```
|
|
756
|
-
*/
|
|
757
|
-
export declare class SolveScheduler {
|
|
758
|
-
private readonly executor;
|
|
759
|
-
private readonly baseConfig;
|
|
760
|
-
private readonly mode;
|
|
761
|
-
private maxConcurrent;
|
|
762
|
-
private readonly maxQueueDepth;
|
|
763
|
-
private readonly queueWaitMs;
|
|
764
|
-
private readonly timeoutMs;
|
|
765
|
-
private readonly retry;
|
|
766
|
-
private readonly cacheEnabled;
|
|
767
|
-
private readonly cacheMaxBytes;
|
|
768
|
-
private readonly cacheTtl;
|
|
769
|
-
private readonly cacheErroredSolves;
|
|
770
|
-
private readonly cache;
|
|
771
|
-
private cacheBytes;
|
|
772
|
-
private cacheHits;
|
|
773
|
-
private cacheMisses;
|
|
774
|
-
private cacheEvictions;
|
|
775
|
-
private readonly reuseServerDefinitionCache;
|
|
776
|
-
/** Definition hash → the server's cache key (`pointer`) for it. */
|
|
777
|
-
private readonly serverCacheKeys;
|
|
778
|
-
/** A string definition re-sent unchanged is compared with this, far cheaper than hashing it. */
|
|
779
|
-
private lastStringDefinition;
|
|
780
|
-
private readonly onStart?;
|
|
781
|
-
private readonly onSettle?;
|
|
782
|
-
private readonly onSuperseded?;
|
|
783
|
-
private readonly subscribers;
|
|
784
|
-
private readonly inFlight;
|
|
785
|
-
private pendingForLatestWins;
|
|
786
|
-
private readonly fifo;
|
|
787
|
-
private _lastResult;
|
|
788
|
-
private _lastError;
|
|
789
|
-
private _lastDurationMs;
|
|
790
|
-
private solveSeq;
|
|
791
|
-
/** seq of the solve that last wrote `_lastResult`/`_lastError`. */
|
|
792
|
-
private lastStateSeq;
|
|
793
|
-
private disposed;
|
|
794
|
-
constructor(executor: SolveExecutor, baseConfig: GrasshopperComputeConfig, options?: SolveSchedulerOptions);
|
|
795
|
-
get isSolving(): boolean;
|
|
796
|
-
get hasPending(): boolean;
|
|
797
|
-
get inFlightCount(): number;
|
|
798
|
-
get queueDepth(): number;
|
|
799
|
-
get lastResult(): GrasshopperComputeResponse | null;
|
|
800
|
-
get lastError(): ComputeError | null;
|
|
801
|
-
get lastDurationMs(): number | null;
|
|
802
|
-
/**
|
|
803
|
-
* Adjust how many solves may run at once, for when the compute server's worker
|
|
804
|
-
* pool changes size after this scheduler was built.
|
|
805
|
-
*
|
|
806
|
-
* Raising it drains queued work immediately. Lowering it never interrupts work
|
|
807
|
-
* already in flight — those finish above the new limit, and the cap applies from
|
|
808
|
-
* the next dispatch. Values below 1 are clamped, since 0 would wedge the queue.
|
|
809
|
-
*/
|
|
810
|
-
setMaxConcurrent(value: number): void;
|
|
811
|
-
/** Current concurrency cap — reflects any {@link setMaxConcurrent} adjustment. */
|
|
812
|
-
getMaxConcurrent(): number;
|
|
813
|
-
/** Subscribe to state changes. */
|
|
814
|
-
subscribe(listener: () => void): () => void;
|
|
815
|
-
private notify;
|
|
816
|
-
/**
|
|
817
|
-
* Schedule a solve. Returns a promise that:
|
|
818
|
-
* - Resolves with the compute response on success.
|
|
819
|
-
* - Rejects with `ComputeError` on failure.
|
|
820
|
-
* - Rejects with `code: ErrorCodes.SUPERSEDED` when the call was canceled because
|
|
821
|
-
* newer values arrived (latest-wins mode).
|
|
822
|
-
* - Rejects with `code: ErrorCodes.ABORTED` when the call was canceled via
|
|
823
|
-
* its own `signal` (queued or running) or `cancelAll()`.
|
|
824
|
-
* - Rejects with `code: ErrorCodes.QUEUE_FULL` when `maxQueueDepth` is set and
|
|
825
|
-
* the queue was already full (backpressure; `statusCode: 503`).
|
|
826
|
-
* - Rejects with `code: ErrorCodes.QUEUE_TIMEOUT` when `queueWaitMs` is set and
|
|
827
|
-
* the call sat queued longer than that before starting (`statusCode: 503`).
|
|
828
|
-
*
|
|
829
|
-
* Don't mutate a `Uint8Array` definition after solving it. Responses served
|
|
830
|
-
* from the cache (and via `lastResult`) are shared objects, not copies —
|
|
831
|
-
* treat them as immutable. Mutating one poisons every later cache hit for
|
|
832
|
-
* that key.
|
|
833
|
-
*
|
|
834
|
-
* Partial-success contract: unlike `GrasshopperClient.solve()`, which throws
|
|
835
|
-
* `COMPUTATION_ERROR` when the response carries solver errors, this RESOLVES
|
|
836
|
-
* with the response as-is — check `response.errors` yourself if a partial
|
|
837
|
-
* success must not be treated as a result (see `cacheErroredSolves` for the
|
|
838
|
-
* caching side of the same distinction).
|
|
839
|
-
*/
|
|
840
|
-
solve(definition: SolveDefinition, dataTree: DataTree[], options?: SchedulerSolveOptions): Promise<GrasshopperComputeResponse>;
|
|
841
|
-
/** {@link solve}, also resolving with whether this call hit the cache and how long it took. */
|
|
842
|
-
solveDetailed(definition: SolveDefinition, dataTree: DataTree[], options?: SchedulerSolveOptions): Promise<SolveDetail>;
|
|
843
|
-
/** A slow solve settling late must not overwrite state a newer solve or cache hit published. */
|
|
844
|
-
private writeLastState;
|
|
845
|
-
/** latest-wins: supersede the pending item and abort everything in flight. */
|
|
846
|
-
private supersedeCurrent;
|
|
847
|
-
private dequeue;
|
|
848
|
-
private abortQueuedItem;
|
|
849
|
-
/** The structured context lets an HTTP layer map QUEUE_FULL to 503 + Retry-After. */
|
|
850
|
-
private shedAsQueueFull;
|
|
851
|
-
private armQueueWaitTimer;
|
|
852
|
-
private clearQueueWaitTimer;
|
|
853
|
-
private enqueue;
|
|
854
|
-
private execute;
|
|
855
|
-
private runExecutor;
|
|
856
|
-
private hashDefinitionMemo;
|
|
857
|
-
private drainNext;
|
|
858
|
-
private supersede;
|
|
859
|
-
private makeAbortError;
|
|
860
|
-
/**
|
|
861
|
-
* The executor, `supersede`, `cancelAll`, abort and the queue limits can all race to settle one
|
|
862
|
-
* item, and a promise ignores a second settle silently. The first settle wins; the return value
|
|
863
|
-
* says whether this call won, so only the winner fires its hook. Every settle path goes through
|
|
864
|
-
* here or {@link settleSuccess}.
|
|
865
|
-
*/
|
|
866
|
-
private settleError;
|
|
867
|
-
/**
|
|
868
|
-
* Fires `onSettle` for queued items too, so a consumer counting solves in progress doesn't
|
|
869
|
-
* leak one. A supersede settles through {@link settleError} and fires `onSuperseded` instead.
|
|
870
|
-
*/
|
|
871
|
-
private fail;
|
|
872
|
-
private settleSuccess;
|
|
873
|
-
private removeQueuedAbortHandler;
|
|
874
|
-
private normalizeExecutionError;
|
|
875
|
-
/** Cancel everything — in-flight and pending. */
|
|
876
|
-
cancelAll(): void;
|
|
877
|
-
private readCache;
|
|
878
|
-
private writeCache;
|
|
879
|
-
private dropCacheEntry;
|
|
880
|
-
clearCache(): void;
|
|
881
|
-
/**
|
|
882
|
-
* Observability snapshot of the solve cache: current size plus lifetime
|
|
883
|
-
* hit/miss/eviction counters.
|
|
884
|
-
*
|
|
885
|
-
* `hits`/`misses` count cache CONSULTATIONS, so `hits / (hits + misses)` is the
|
|
886
|
-
* hit rate. A TTL-expired entry counts as a miss (the solve runs either way).
|
|
887
|
-
* `evictions` counts only entries dropped under size/byte pressure — not
|
|
888
|
-
* replace-in-place writes or TTL expiry, which are not capacity signals.
|
|
889
|
-
* Counters are cumulative and survive `clearCache()`.
|
|
890
|
-
*/
|
|
891
|
-
cacheStats(): {
|
|
892
|
-
entries: number;
|
|
893
|
-
bytes: number;
|
|
894
|
-
hits: number;
|
|
895
|
-
misses: number;
|
|
896
|
-
evictions: number;
|
|
897
|
-
};
|
|
898
|
-
dispose(): void;
|
|
899
|
-
private runHook;
|
|
900
|
-
}
|
|
901
|
-
/**
|
|
902
|
-
* Per-call options that override the client's default ComputeConfig values.
|
|
903
|
-
*
|
|
904
|
-
* Use these for per-request control without mutating the client config:
|
|
905
|
-
* - `signal` — cancel a specific solve (e.g. when a slider value is superseded)
|
|
906
|
-
* - `timeoutMs` — extend timeout for a long-running solve, or pass `0` to disable
|
|
907
|
-
* - `retry` — override retry policy for this call only
|
|
908
|
-
*/
|
|
909
|
-
interface SolveOptions {
|
|
910
|
-
signal?: AbortSignal;
|
|
911
|
-
timeoutMs?: number;
|
|
912
|
-
retry?: RetryPolicy;
|
|
913
|
-
/** Per-solve live-event callback, forwarded onto the request body. */
|
|
914
|
-
selvaevents?: SelvaEventTarget;
|
|
915
|
-
}
|
|
916
|
-
/** Options for {@link GrasshopperClient.create} that shape construction, not the solves. */
|
|
917
|
-
interface CreateClientOptions {
|
|
918
|
-
/**
|
|
919
|
-
* Retry ladder for the liveness probe: `attempts` retries after the first probe (default 2),
|
|
920
|
-
* with exponential backoff from `baseDelayMs` (default 250) capped at `maxDelayMs` (default 1000).
|
|
921
|
-
* Independent of `config.retry`, which governs solves only.
|
|
922
|
-
*/
|
|
923
|
-
probe?: {
|
|
924
|
-
attempts?: number;
|
|
925
|
-
baseDelayMs?: number;
|
|
926
|
-
maxDelayMs?: number;
|
|
927
|
-
};
|
|
928
|
-
}
|
|
929
|
-
/**
|
|
930
|
-
* GrasshopperClient provides a simple API for interacting with a Rhino Compute server and grasshopper.
|
|
931
|
-
*
|
|
932
|
-
* @public This is the recommended high-level API for Rhino Compute operations.
|
|
933
|
-
*
|
|
934
|
-
* **Security Warning:**
|
|
935
|
-
* Using this client in a browser environment exposes your server URL and API key to users.
|
|
936
|
-
* For production, use this library server-side or proxy requests through your own backend.
|
|
937
|
-
*
|
|
938
|
-
* @example
|
|
939
|
-
* ```typescript
|
|
940
|
-
* const client = await GrasshopperClient.create({
|
|
941
|
-
* serverUrl: 'http://localhost:6500',
|
|
942
|
-
* apiKey: 'your-api-key'
|
|
943
|
-
* });
|
|
944
|
-
*
|
|
945
|
-
* try {
|
|
946
|
-
* const io = await client.getIO(definitionUrl);
|
|
947
|
-
* const result = await client.solve(definitionUrl, TreeBuilder.fromInputParams(io.inputs));
|
|
948
|
-
* } finally {
|
|
949
|
-
* await client.dispose(); // Clean up resources
|
|
950
|
-
* }
|
|
951
|
-
* ```
|
|
952
|
-
*/
|
|
953
|
-
declare class GrasshopperClient {
|
|
954
|
-
private readonly config;
|
|
955
|
-
readonly serverStats: ComputeServerStats;
|
|
956
|
-
private disposed;
|
|
957
|
-
/**
|
|
958
|
-
* Per-probe timeout for the `create()` liveness gate. A healthy `GET /` on the
|
|
959
|
-
* proxy answers in milliseconds; this bound only ever applies to a server that
|
|
960
|
-
* is not answering, where every extra second is multiplied by the retry count
|
|
961
|
-
* and paid by whoever is waiting on the solve.
|
|
962
|
-
*/
|
|
963
|
-
private static readonly CREATE_PROBE_TIMEOUT_MS;
|
|
964
|
-
private constructor();
|
|
965
|
-
/**
|
|
966
|
-
* Creates and initializes a GrasshopperClient with server validation.
|
|
967
|
-
*
|
|
968
|
-
* The pre-flight liveness probe (a GET on the proxy root `/`) is a
|
|
969
|
-
* single-sample boolean gate that reads a cold or briefly-busy-but-up server
|
|
970
|
-
* as offline. To avoid failing construction on that transient class, the probe
|
|
971
|
-
* is retried with a short exponential backoff before giving up.
|
|
972
|
-
*
|
|
973
|
-
* Retries stop early when {@link classifyProbeFailure} says waiting cannot
|
|
974
|
-
* change the answer — connection refused, or a 401/403. This runs in front of
|
|
975
|
-
* a user who clicked Solve, so the retry ladder must not spend its whole
|
|
976
|
-
* budget confirming that a machine is switched off.
|
|
977
|
-
*
|
|
978
|
-
* @throws {ComputeError} with code NETWORK_ERROR if the server stays
|
|
979
|
-
* unreachable across all attempts. `context.probeVerdict` carries the
|
|
980
|
-
* {@link ProbeVerdict} so callers can render a specific cause.
|
|
981
|
-
* @throws {ComputeError} with code INVALID_CONFIG if configuration is invalid
|
|
982
|
-
*/
|
|
983
|
-
static create(config: GrasshopperComputeConfig, options?: CreateClientOptions): Promise<GrasshopperClient>;
|
|
984
|
-
/**
|
|
985
|
-
* Gets the client's configuration.
|
|
986
|
-
* Useful for passing to lower-level functions.
|
|
987
|
-
*/
|
|
988
|
-
getConfig(): GrasshopperComputeConfig;
|
|
989
|
-
/**
|
|
990
|
-
* Get input/output parameters of a Grasshopper definition.
|
|
991
|
-
*/
|
|
992
|
-
getIO(definition: string | Uint8Array): Promise<GrasshopperParsedIO>;
|
|
993
|
-
getRawIO(definition: string | Uint8Array): Promise<GrasshopperParsedIORaw>;
|
|
994
|
-
/**
|
|
995
|
-
* Run a compute job with a Grasshopper definition.
|
|
996
|
-
*
|
|
997
|
-
* @throws {ComputeError} with code INVALID_INPUT if definition is empty
|
|
998
|
-
* @throws {ComputeError} with code NETWORK_ERROR if server is offline
|
|
999
|
-
* @throws {ComputeError} with code COMPUTATION_ERROR if computation fails.
|
|
1000
|
-
* On a partial-success response (some outputs computed, some errored) the
|
|
1001
|
-
* error's `context.values` carries the outputs that did compute — pass
|
|
1002
|
-
* `{ values } as GrasshopperComputeResponse` to the response processors to
|
|
1003
|
-
* render them. `context.inputSummary` describes the inputs (param names,
|
|
1004
|
-
* item counts, byte sizes) without pinning the full data tree.
|
|
1005
|
-
*/
|
|
1006
|
-
solve(definition: SolveDefinition, dataTree: DataTree[], options?: SolveOptions): Promise<GrasshopperComputeResponse>;
|
|
1007
|
-
/**
|
|
1008
|
-
* Create a scheduler bound to this client. Use a scheduler for any UI surface
|
|
1009
|
-
* that fires solves frequently (sliders, live editors) or that needs cancel
|
|
1010
|
-
* semantics, response caching, or state observability.
|
|
1011
|
-
*
|
|
1012
|
-
* Multiple schedulers can be created from a single client — typically one per
|
|
1013
|
-
* UI surface so their queues stay independent.
|
|
1014
|
-
*
|
|
1015
|
-
* @example
|
|
1016
|
-
* ```ts
|
|
1017
|
-
* const sliderScheduler = client.createScheduler({ mode: 'latest-wins' });
|
|
1018
|
-
* const submitScheduler = client.createScheduler({ mode: 'queue', timeoutMs: 0, retry: { attempts: 1 } });
|
|
1019
|
-
* ```
|
|
1020
|
-
*/
|
|
1021
|
-
createScheduler(options?: SolveSchedulerOptions): SolveScheduler;
|
|
1022
|
-
/**
|
|
1023
|
-
* Disposes of client resources.
|
|
1024
|
-
* Call this when you're done using the client.
|
|
1025
|
-
*/
|
|
1026
|
-
dispose(): Promise<void>;
|
|
1027
|
-
private ensureNotDisposed;
|
|
1028
|
-
}
|
|
1029
|
-
interface ParsedContext {
|
|
1030
|
-
[key: string]: any;
|
|
1031
|
-
}
|
|
1032
|
-
interface GetValuesOptions {
|
|
1033
|
-
parseValues?: boolean;
|
|
1034
|
-
rhino?: any;
|
|
1035
|
-
/**
|
|
1036
|
-
* If true, only include values of type System.String in the result.
|
|
1037
|
-
* Non-string types are filtered out.
|
|
1038
|
-
*/
|
|
1039
|
-
stringOnly?: boolean;
|
|
1040
|
-
}
|
|
1041
|
-
interface GetValuesResult<T = ParsedContext> {
|
|
1042
|
-
values: T;
|
|
1043
|
-
/**
|
|
1044
|
-
* Free every rhino3dm WASM object decoded into `values`. When a `rhino`
|
|
1045
|
-
* instance was passed, decoded geometry lives on the WASM heap and is never
|
|
1046
|
-
* garbage-collected — call this once the values are consumed, or the heap
|
|
1047
|
-
* grows monotonically across solves. Idempotent; a no-op when nothing was
|
|
1048
|
-
* decoded. `values` must not be used after disposal.
|
|
1049
|
-
*/
|
|
1050
|
-
dispose: () => void;
|
|
1051
|
-
}
|
|
1052
|
-
/** Reads parsed values and produced files out of one compute response. */
|
|
1053
|
-
declare class GrasshopperResponseProcessor {
|
|
1054
|
-
/** Hand it to a mesh parser such as `@selvajs/visualization/parse`. */
|
|
1055
|
-
readonly response: GrasshopperComputeResponse;
|
|
1056
|
-
constructor(
|
|
1057
|
-
/** Hand it to a mesh parser such as `@selvajs/visualization/parse`. */
|
|
1058
|
-
response: GrasshopperComputeResponse);
|
|
1059
|
-
/**
|
|
1060
|
-
* Extract all values in the response.
|
|
1061
|
-
*
|
|
1062
|
-
* @typeParam T - Expected structure of the return value. Defaults to a simple key/value map. (later cast as needed)
|
|
1063
|
-
* @param byId - Key by parameter ID instead of name. Requires the VektorNode rhino.compute branch.
|
|
1064
|
-
* @param options - Controls parsing behavior such as Rhino geometry decoding.
|
|
1065
|
-
* @returns Parsed Grasshopper output values.
|
|
1066
|
-
*
|
|
1067
|
-
* @example
|
|
1068
|
-
* ```ts
|
|
1069
|
-
* const processor = new GrasshopperResponseProcessor(response);
|
|
1070
|
-
* const { values } = processor.getValues();
|
|
1071
|
-
* ```
|
|
1072
|
-
*
|
|
1073
|
-
*/
|
|
1074
|
-
getValues<T = ParsedContext>(byId?: boolean, options?: GetValuesOptions): GetValuesResult<T>;
|
|
1075
|
-
/**
|
|
1076
|
-
* Retrieve a specific value by parameter name or ID.
|
|
1077
|
-
*
|
|
1078
|
-
* @param selector - `{ byName }` for the human-readable name, `{ byId }` for the parameter GUID.
|
|
1079
|
-
* @param options - Parsing configuration (e.g. disable parsing or enable Rhino).
|
|
1080
|
-
* @returns Single parsed value, array of values, or undefined if the parameter is absent.
|
|
1081
|
-
*
|
|
1082
|
-
* `byId` requires the VektorNode rhino.compute branch.
|
|
1083
|
-
*
|
|
1084
|
-
* @example
|
|
1085
|
-
* ```ts
|
|
1086
|
-
* const schema = processor.getValue({ byName: 'Schema' });
|
|
1087
|
-
* const output = processor.getValue({ byId: 'a4be1c1e-23f9-4c27-b942-7f3bb2c45c6f' });
|
|
1088
|
-
* ```
|
|
1089
|
-
*/
|
|
1090
|
-
getValue(selector: {
|
|
1091
|
-
byName: string;
|
|
1092
|
-
} | {
|
|
1093
|
-
byId: string;
|
|
1094
|
-
}, options?: GetValuesOptions): any;
|
|
1095
|
-
/**
|
|
1096
|
-
* Download all files generated by Grasshopper, optionally including
|
|
1097
|
-
* additional user-provided files.
|
|
1098
|
-
*
|
|
1099
|
-
* Files are grouped under the specified folder name when downloaded.
|
|
1100
|
-
*
|
|
1101
|
-
* @param folderName - Name for the download directory.
|
|
1102
|
-
* @param additionalFiles - Extra files to package (single file, array, or null).
|
|
1103
|
-
*
|
|
1104
|
-
* @example
|
|
1105
|
-
* ```ts
|
|
1106
|
-
* await processor.getAndDownloadFiles('gh-output');
|
|
1107
|
-
* ```
|
|
1108
|
-
*
|
|
1109
|
-
* @example
|
|
1110
|
-
* ```ts
|
|
1111
|
-
* const extra = { name: 'notes.txt', data: 'Example' };
|
|
1112
|
-
* await processor.getAndDownloadFiles('project', extra);
|
|
1113
|
-
* ```
|
|
1114
|
-
*/
|
|
1115
|
-
getAndDownloadFiles(folderName: string, additionalFiles?: FileBaseInfo[] | FileBaseInfo | null): Promise<void>;
|
|
1116
|
-
}
|
|
1117
|
-
/**
|
|
1118
|
-
* Deterministic stringify with sorted keys. {a:1,b:2} and {b:2,a:1} produce
|
|
1119
|
-
* the same string. Safely handles circular references and non-finite numbers.
|
|
1120
|
-
*
|
|
1121
|
-
* The output is a cache key, so the invariant that matters is: two payloads
|
|
1122
|
-
* that serialize differently on the wire must stringify differently here
|
|
1123
|
-
* (false misses are harmless; false hits serve the wrong cached solve).
|
|
1124
|
-
* Non-JSON values get unquoted markers (`{u8:…}`, `[Circular]`) that no JSON
|
|
1125
|
-
* value stringifies to, so a plain object can't pose as one.
|
|
1126
|
-
*/
|
|
1127
|
-
export declare function stableStringify(value: unknown): string;
|
|
1128
|
-
/** The scheduler's solve-cache key for a definition and its input tree. */
|
|
1129
|
-
export declare function hashSolveInput(definition: SolveDefinition, dataTree: unknown): string;
|
|
1130
|
-
/**
|
|
1131
|
-
* A definition's identity, without its inputs. Strings and bytes are hashed over their full
|
|
1132
|
-
* content; a `DefinitionRef` uses its `key`.
|
|
1133
|
-
*/
|
|
1134
|
-
export declare function hashDefinition(definition: SolveDefinition): string;
|
|
1135
|
-
/**
|
|
1136
|
-
* Run one solve. `GrasshopperClient.solve()` adds input validation and throws on solver errors.
|
|
1137
|
-
*
|
|
1138
|
-
* @param definition - A URL (sent as a pointer), base64 `.gh` content, raw `.gh` bytes, or a
|
|
1139
|
-
* `DefinitionRef` (loaded for the upload). Any other string is encoded as the definition text.
|
|
1140
|
-
* @returns The compute response, without the server's `pointer` and echoed `algo`.
|
|
1141
|
-
*
|
|
1142
|
-
* @example
|
|
1143
|
-
* await solveGrasshopperDefinition(trees, 'https://example.com/definition.gh', config);
|
|
1144
|
-
* await solveGrasshopperDefinition(trees, new Uint8Array(await readFile('model.gh')), config);
|
|
1145
|
-
*/
|
|
1146
|
-
export declare function solveGrasshopperDefinition(dataTree: DataTree[], definition: SolveDefinition, config: GrasshopperComputeConfig): Promise<GrasshopperComputeResponse>;
|
|
1147
|
-
/**
|
|
1148
|
-
* Fetches a definition's input/output schemas without per-type parsing.
|
|
1149
|
-
*
|
|
1150
|
-
* Field KEYS are canonicalized to camelCase and missing required fields get a
|
|
1151
|
-
* fallback ({@link normalizeInputSchema}); a missing or non-array `inputs` /
|
|
1152
|
-
* `outputs` becomes `[]`. Field VALUES, notably `default` and the `values`
|
|
1153
|
-
* label map, pass through verbatim.
|
|
1154
|
-
*
|
|
1155
|
-
* @throws {ComputeError} If fetch fails or response is invalid
|
|
1156
|
-
*
|
|
1157
|
-
* @public Use `fetchParsedDefinitionIO()` for processed, type-safe inputs
|
|
1158
|
-
*/
|
|
1159
|
-
export declare function fetchDefinitionIO(definition: string | Uint8Array, config: ComputeConfig): Promise<GrasshopperParsedIORaw>;
|
|
1160
|
-
/**
|
|
1161
|
-
* Fetches a definition's input/output schemas and parses each input into its
|
|
1162
|
-
* typed `InputParam`. An input that fails to parse gets a safe default
|
|
1163
|
-
* and an entry in `parseErrors`.
|
|
1164
|
-
*
|
|
1165
|
-
* @throws {ComputeError} If fetch fails or response is invalid
|
|
1166
|
-
*
|
|
1167
|
-
* @example
|
|
1168
|
-
* ```typescript
|
|
1169
|
-
* const { inputs, outputs } = await fetchParsedDefinitionIO(
|
|
1170
|
-
* 'https://example.com/definition.gh',
|
|
1171
|
-
* { serverUrl: 'http://localhost:6500', apiKey: 'YOUR_KEY' }
|
|
1172
|
-
* );
|
|
1173
|
-
*
|
|
1174
|
-
* // Inputs are now strongly typed
|
|
1175
|
-
* inputs.forEach(input => {
|
|
1176
|
-
* if (input.paramType === 'Number') {
|
|
1177
|
-
* console.log(input.minimum, input.maximum); // TypeScript knows these exist
|
|
1178
|
-
* }
|
|
1179
|
-
* });
|
|
1180
|
-
* ```
|
|
1181
|
-
*/
|
|
1182
|
-
export declare function fetchParsedDefinitionIO(definition: string | Uint8Array, config: ComputeConfig): Promise<GrasshopperParsedIO>;
|
|
1183
|
-
/** One entry of the schema endpoint's response, after wrapper-key normalization. */
|
|
1184
|
-
interface SchemaEndpointResult<TSchema = unknown> {
|
|
1185
|
-
/** Schemas embedded in that file. Absent when the file yielded none. */
|
|
1186
|
-
schemas?: TSchema[];
|
|
1187
|
-
/** Per-file diagnosis. Compute reports this and still answers 200. */
|
|
1188
|
-
error?: string;
|
|
1189
|
-
}
|
|
1190
|
-
/**
|
|
1191
|
-
* Read compute's `/grasshopper/schema` body: `[{ FileName, Schemas }]` per
|
|
1192
|
-
* uploaded file, or a bare object for a single file.
|
|
1193
|
-
*
|
|
1194
|
-
* Use this rather than unwrapping the body by hand: the wrapper keys' casing
|
|
1195
|
-
* varies by server branch, and a fixed-key read yields "no schemas" with a 200.
|
|
1196
|
-
* Only the two wrapper keys are read; schema contents pass through untouched.
|
|
1197
|
-
*
|
|
1198
|
-
* @typeParam TSchema - Your schema type (e.g. `UISchema`). Not inspected.
|
|
1199
|
-
*/
|
|
1200
|
-
export declare function readSchemaResults<TSchema = unknown>(raw: unknown): SchemaEndpointResult<TSchema>[];
|
|
1201
|
-
/**
|
|
1202
|
-
* Canonicalize a `/grasshopper/schema` body's key casing.
|
|
1203
|
-
*
|
|
1204
|
-
* Compute serializes the plugin's `UISchema`, whose camelCase wire names live in
|
|
1205
|
-
* Newtonsoft `[JsonProperty]` attributes. `Selva.gha` ILRepack-merges Newtonsoft,
|
|
1206
|
-
* so those attributes are `Selva!Newtonsoft.Json.JsonPropertyAttribute`. When
|
|
1207
|
-
* compute's OWN Newtonsoft serializes, it doesn't recognize them, reads no
|
|
1208
|
-
* attributes, and emits raw CLR names (`Inputs`, `SchemaVersion`). Nothing throws:
|
|
1209
|
-
* `schema.inputs` reads `undefined`, and so does the `schemaVersion` gate.
|
|
1210
|
-
*
|
|
1211
|
-
* A casing rule, not a key allowlist: the wire names are the CLR names run
|
|
1212
|
-
* through Newtonsoft's camelCase strategy, so reproducing it covers every key
|
|
1213
|
-
* without drifting each time `UISchema` gains a field.
|
|
1214
|
-
*/
|
|
1215
|
-
/**
|
|
1216
|
-
* Returns a schema whose structural keys are camelCase, whichever Newtonsoft
|
|
1217
|
-
* serialized it. Always a fresh object the caller may mutate; non-objects are
|
|
1218
|
-
* returned as-is.
|
|
1219
|
-
*/
|
|
1220
|
-
export declare function normalizeUISchemaCasing<T>(raw: T): T;
|
|
1221
|
-
/**
|
|
1222
|
-
* Parse one raw Grasshopper input schema into a typed {@link InputParam}.
|
|
1223
|
-
* A validation failure is swallowed and replaced with a safe default.
|
|
1224
|
-
*/
|
|
1225
|
-
export declare function processInput(rawInput: InputParamSchema): InputParam;
|
|
1226
|
-
/**
|
|
1227
|
-
* Sentinel a Rhino geometry decode returns when a decode was attempted
|
|
1228
|
-
* and failed hard (a registered decoder or `CommonObject.decode` threw). Carries
|
|
1229
|
-
* the original payload so callers can log or retry. Use
|
|
1230
|
-
* {@link isRhinoDecodeError} to detect it.
|
|
1231
|
-
*/
|
|
1232
|
-
interface RhinoDecodeError {
|
|
1233
|
-
__decodeError: true;
|
|
1234
|
-
/** The Rhino type name the failed decode was attempted for. */
|
|
1235
|
-
type: string;
|
|
1236
|
-
/** The original (undecoded) payload. */
|
|
1237
|
-
raw: unknown;
|
|
1238
|
-
}
|
|
1239
|
-
/** Type guard for the {@link RhinoDecodeError} sentinel. */
|
|
1240
|
-
export declare function isRhinoDecodeError(value: unknown): value is RhinoDecodeError;
|
|
1241
|
-
/**
|
|
1242
|
-
* Free every rhino3dm WASM object reachable from `value`.
|
|
1243
|
-
*
|
|
1244
|
-
* rhino3dm objects are emscripten bindings — JS GC never reclaims their WASM
|
|
1245
|
-
* heap allocation, so everything decoded from a solve response (`getValues`,
|
|
1246
|
-
* `getValue`) must be deleted explicitly or the heap
|
|
1247
|
-
* grows monotonically across solves (e.g. a UI decoding per slider tick).
|
|
1248
|
-
*
|
|
1249
|
-
* Walks arrays and plain objects recursively; anything exposing a `delete()`
|
|
1250
|
-
* method is treated as a WASM binding and freed (skipped if already deleted).
|
|
1251
|
-
* Safe to call more than once and on values containing no WASM objects.
|
|
1252
|
-
*/
|
|
1253
|
-
export declare function disposeRhinoObjects(value: unknown): void;
|
|
1254
|
-
type DataTreeValue = string | number | boolean | object | null;
|
|
1255
|
-
/** One input's value for {@link TreeBuilder.fromValues}. */
|
|
1256
|
-
interface TreeValueEntry {
|
|
1257
|
-
paramName: string;
|
|
1258
|
-
/** Matched case-insensitively. */
|
|
1259
|
-
paramType: string;
|
|
1260
|
-
/** A scalar, an array (branch `{0}`), or a branch-path map like `{ '{0;1}': [1, 2] }`. */
|
|
1261
|
-
value: unknown;
|
|
1262
|
-
minimum?: number | null;
|
|
1263
|
-
maximum?: number | null;
|
|
1264
|
-
}
|
|
1265
|
-
/**
|
|
1266
|
-
* Builds the Grasshopper data trees a solve takes as input.
|
|
1267
|
-
*
|
|
1268
|
-
* @example
|
|
1269
|
-
* ```ts
|
|
1270
|
-
* const tree = new TreeBuilder('MyParam')
|
|
1271
|
-
* .append([0], [1, 2, 3])
|
|
1272
|
-
* .append([1], [4, 5])
|
|
1273
|
-
* .toComputeFormat();
|
|
1274
|
-
* ```
|
|
1275
|
-
*/
|
|
1276
|
-
export declare class TreeBuilder {
|
|
1277
|
-
private innerTree;
|
|
1278
|
-
private paramName;
|
|
1279
|
-
constructor(paramName: string);
|
|
1280
|
-
/** Append values to the branch at `path` (`[0, 1]` is `{0;1}`). */
|
|
1281
|
-
append(path: number[], items: DataTreeValue[]): this;
|
|
1282
|
-
/** Replace the tree with a branch-path map like `{ '{0;1}': [1, 2] }`. */
|
|
1283
|
-
fromDataTreeDefault(treeData: DataTreeDefault): this;
|
|
1284
|
-
/** Append a value, or each value of an array, to branch `{0}`. */
|
|
1285
|
-
appendFlat(values: DataTreeValue | DataTreeValue[]): this;
|
|
1286
|
-
/** Every value across all branches. */
|
|
1287
|
-
flatten(): DataTreeValue[];
|
|
1288
|
-
/** The tree in the shape a solve request sends. */
|
|
1289
|
-
toComputeFormat(): DataTree;
|
|
1290
|
-
getParamName(): string;
|
|
1291
|
-
/**
|
|
1292
|
-
* One tree per input that has a default, keyed by its nickname. Defaults are coerced and
|
|
1293
|
-
* clamped by the same rules as {@link TreeBuilder.fromValues}.
|
|
1294
|
-
*/
|
|
1295
|
-
static fromInputParams(inputs: InputParam[]): DataTree[];
|
|
1296
|
-
/**
|
|
1297
|
-
* One tree per entry, coercing UI values the way Grasshopper reads them. Number and Integer
|
|
1298
|
-
* items go through the numeric coercion (Integer rounds) and are clamped to
|
|
1299
|
-
* `minimum`/`maximum`; Boolean items also accept `'true'`/`'false'`; every other type passes
|
|
1300
|
-
* through. An item that fails coercion is dropped with a warning. An entry left with no items
|
|
1301
|
-
* is omitted, so Grasshopper uses its own value; `''` is an item, not an empty value.
|
|
1302
|
-
*/
|
|
1303
|
-
static fromValues(entries: TreeValueEntry[]): DataTree[];
|
|
1304
|
-
private static branchesOf;
|
|
1305
|
-
/** `null` means the item was rejected. */
|
|
1306
|
-
private static itemCoercer;
|
|
1307
|
-
/**
|
|
1308
|
-
* Set or add `paramName`'s tree to `newValue`: a scalar, an array (branch `{0}`), or a
|
|
1309
|
-
* branch-path map. Copy-on-write: returns a new array and leaves the caller's untouched, so
|
|
1310
|
-
* assign the result.
|
|
1311
|
-
*/
|
|
1312
|
-
static replaceTreeValue(trees: DataTree[], paramName: string, newValue: DataTreeValue): DataTree[];
|
|
1313
|
-
private static buildFromValue;
|
|
1314
|
-
/**
|
|
1315
|
-
* `paramName`'s whole tree from a solve result's `values`, keyed by branch path (`'{0;1}'`).
|
|
1316
|
-
* `null` when no tree has that param, an empty `Map` when it has no branches. An item without
|
|
1317
|
-
* `data` reads as `null` in place, so indices don't shift.
|
|
1318
|
-
*/
|
|
1319
|
-
static getTree(trees: DataTree[], paramName: string): Map<DataTreePath, DataTreeValue[]> | null;
|
|
1320
|
-
/**
|
|
1321
|
-
* `paramName`'s value when its tree has a single branch: one item unwrapped (`5`, not `[5]`),
|
|
1322
|
-
* several as an array. `null` for a missing param or an empty tree.
|
|
1323
|
-
*
|
|
1324
|
-
* @throws {ComputeError} `INVALID_INPUT` when the tree has more than one branch; read those
|
|
1325
|
-
* with {@link TreeBuilder.getTree}.
|
|
1326
|
-
*/
|
|
1327
|
-
static getTreeValue(trees: DataTree[], paramName: string): DataTreeValue | null;
|
|
1328
|
-
private static paramNameOf;
|
|
1329
|
-
/**
|
|
1330
|
-
* Parse a branch path like `"{0;1;2}"` into `[0, 1, 2]`.
|
|
1331
|
-
*
|
|
1332
|
-
* @throws {ComputeError} `INVALID_INPUT` for a malformed path, rather than
|
|
1333
|
-
* collapsing it to a default branch where two bad keys would merge.
|
|
1334
|
-
*/
|
|
1335
|
-
static parsePathString(pathStr: string): number[];
|
|
1336
|
-
/** `[0, 1, 2]` → `"{0;1;2}"`. */
|
|
1337
|
-
static formatPathString(path: number[]): DataTreePath;
|
|
1338
|
-
/** Booleans and numbers stay primitives: Grasshopper reads them typed. */
|
|
1339
|
-
private static serializeValue;
|
|
1340
|
-
private static deserializeValue;
|
|
1341
|
-
private static hasValidValue;
|
|
1342
|
-
private static clampValue;
|
|
1343
|
-
}
|
|
1344
|
-
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 };
|
|
1345
|
-
//# sourceMappingURL=grasshopper.d.cts.map
|