@selvajs/compute 5.0.0-beta.7 → 5.0.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -11
- package/dist/core.d.ts +190 -2
- package/dist/core.js +1 -1
- package/dist/grasshopper.d.ts +57 -92
- 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 +14 -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 -1355
- package/dist/index-DDmprCht.d.cts +0 -421
- package/dist/index-DDmprCht.d.ts +0 -421
package/README.md
CHANGED
|
@@ -21,6 +21,8 @@ objects, use [`@selvajs/visualization`](https://www.npmjs.com/package/@selvajs/v
|
|
|
21
21
|
npm install @selvajs/compute
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
The package is ESM only, with two entrypoints: `/core` and `/grasshopper`.
|
|
25
|
+
|
|
24
26
|
Two entrypoints: `@selvajs/compute/grasshopper` (client, scheduler, data trees, IO parsing) and
|
|
25
27
|
`@selvajs/compute/core` (low-level fetch, errors, config). There is no root entry: import a
|
|
26
28
|
subpath.
|
|
@@ -65,6 +67,10 @@ scheduler.subscribe(() => {
|
|
|
65
67
|
});
|
|
66
68
|
```
|
|
67
69
|
|
|
70
|
+
New here? [How the client works](https://selva.dev/docs/packages/build/how-it-works) explains the pieces, and [Common tasks](https://selva.dev/docs/packages/build/common-tasks) has copy-ready examples.
|
|
71
|
+
|
|
72
|
+
> `client.solve` throws on solver errors; `scheduler.solve` resolves with them. See [Errors](#errors).
|
|
73
|
+
|
|
68
74
|
## Configuring the scheduler
|
|
69
75
|
|
|
70
76
|
Pick the row that matches what the user is doing:
|
|
@@ -72,7 +78,7 @@ Pick the row that matches what the user is doing:
|
|
|
72
78
|
| Workload | `mode` | `timeoutMs` | `retry` | Notes |
|
|
73
79
|
| --------------------------------- | --------------- | ---------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
74
80
|
| **Slider scrubs / live previews** | `'latest-wins'` | `30_000` | default | Aborts in-flight solves when newer values arrive. Add `cache: { maxBytes: 64 * 1024 * 1024 }` for instant repeats. |
|
|
75
|
-
| **Submit / long-running jobs** | `'queue'` | `0` (no timeout) |
|
|
81
|
+
| **Submit / long-running jobs** | `'queue'` | `0` (no timeout) | default | Serial queue. Pass a caller `signal` so users can hit Cancel. See [Long jobs behind a proxy](#long-jobs-behind-a-proxy). |
|
|
76
82
|
| **Background / batch parallel** | `'queue'` | `60_000` | `{ attempts: 2 }` | Set `maxConcurrent` (e.g. 4) to run that many solves at once; queue mode defaults to 1. |
|
|
77
83
|
|
|
78
84
|
Create one scheduler per UI surface: their queues, cancel scopes and caches are independent.
|
|
@@ -110,6 +116,20 @@ work around that: raise the proxy's idle timeout before running long solves from
|
|
|
110
116
|
solves longer than a couple of minutes, run this library server-side behind your own job queue
|
|
111
117
|
and expose a status endpoint to the browser.
|
|
112
118
|
|
|
119
|
+
On Node, undici stops waiting for response headers at 300 s, and neither `timeoutMs` nor `0` lifts
|
|
120
|
+
that: the solve fails with `NETWORK_ERROR` while it is still running on the server, and a retry
|
|
121
|
+
would run it a second time. Past 300 s, keep `retry` off and pass a `fetch` with a longer limit:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
import { Agent, fetch as undiciFetch } from 'undici';
|
|
125
|
+
|
|
126
|
+
const dispatcher = new Agent({ headersTimeout: 0, bodyTimeout: 0 });
|
|
127
|
+
const client = await GrasshopperClient.create({
|
|
128
|
+
serverUrl,
|
|
129
|
+
fetch: (url, init) => undiciFetch(url, { ...init, dispatcher })
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
113
133
|
## Errors
|
|
114
134
|
|
|
115
135
|
Every failure is a `ComputeError`. Branch on `err.code`, one of `ErrorCodes`, not on the message.
|
|
@@ -147,17 +167,16 @@ Read outputs through `GrasshopperResponseProcessor`, or a whole tree with
|
|
|
147
167
|
`getTreeValue` is the shortcut for a one-branch param: it unwraps a single item and throws when
|
|
148
168
|
the param has more than one branch.
|
|
149
169
|
|
|
150
|
-
Pass a rhino3dm module as `{ rhino }` in the `getValues` / `getValue` options to decode geometry.
|
|
151
|
-
heap, which garbage collection never frees: release them with
|
|
152
|
-
`rhino3dm` yourself; this package doesn't depend on it.
|
|
170
|
+
Pass a rhino3dm module as `{ rhino }` in the `getValues` / `getValue` options to decode geometry.
|
|
171
|
+
Its objects live on the WASM heap, which garbage collection never frees: release them with
|
|
172
|
+
`disposeRhinoObjects`. Install `rhino3dm` yourself; this package doesn't depend on it.
|
|
153
173
|
|
|
154
174
|
## File outputs
|
|
155
175
|
|
|
156
|
-
File outputs need the
|
|
157
|
-
|
|
158
|
-
drops them. Read one with `getValue` and it comes back as `FileData`.
|
|
176
|
+
File outputs need the Selva plugin and the VektorNode fork (see [Requirements](#requirements));
|
|
177
|
+
standard Rhino Compute drops them. Read one with `getValue` and it comes back as `FileData`.
|
|
159
178
|
|
|
160
|
-
Each file component has a `Sub Folder` input, where `::` nests (as in Rhino layer names). The
|
|
179
|
+
Each file component has a `Sub Folder` input, where `/` or `::` nests (as in Rhino layer names). The
|
|
161
180
|
first segment is the root: `downloadFileDataByRoot` from `/core` turns each root into its own
|
|
162
181
|
archive and puts files without one in the fallback archive. With `'export'` as the fallback name:
|
|
163
182
|
|
|
@@ -174,8 +193,6 @@ archive and puts files without one in the fallback archive. With `'export'` as t
|
|
|
174
193
|
|
|
175
194
|
## Requirements
|
|
176
195
|
|
|
177
|
-
**Node.js >= 24.**
|
|
178
|
-
|
|
179
196
|
The [official McNeel Rhino Compute](https://github.com/mcneel/compute.rhino3d) handles plain
|
|
180
197
|
Grasshopper solving. The [VektorNode compute fork](https://github.com/VektorNode/compute.rhino3d)
|
|
181
198
|
adds input grouping (`groupName`), persistent input ids keyed on Grasshopper object GUIDs, file
|
|
@@ -185,7 +202,9 @@ file-export components those features read.
|
|
|
185
202
|
|
|
186
203
|
## Troubleshooting
|
|
187
204
|
|
|
188
|
-
**`
|
|
205
|
+
**`CORS_ERROR` (`Request failed: Failed to fetch`)** means the browser got no response, which it
|
|
206
|
+
reports the same way for a CORS block and for an unreachable server. Outside a browser the same
|
|
207
|
+
failure is `NETWORK_ERROR`. Check that
|
|
189
208
|
`curl http://localhost:6500/` returns 200; that a server on another origin sends
|
|
190
209
|
`Access-Control-Allow-Origin` (standard Rhino Compute doesn't: put a proxy in front that adds it,
|
|
191
210
|
or use the [VektorNode fork](https://github.com/VektorNode/compute.rhino3d)); that an HTTPS app
|
package/dist/core.d.ts
CHANGED
|
@@ -1,2 +1,190 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { a as DefinitionRef, c as ComputeAuth, d as ServerErrorCodeMap, f as ServerTiming, h as ErrorCodes, i as ProcessedFile, l as ComputeConfig, m as ErrorCode, n as FileBaseInfo, o as SolveDefinition, p as ComputeError, r as FileData, s as isDefinitionRef, t as DownloadOptions, u as RetryPolicy } from "./types-Dm9QFVvA.js";
|
|
2
|
+
/**
|
|
3
|
+
* POST `args` as JSON to `<serverUrl>/<endpoint>` with retry, timeout and abort handling, and
|
|
4
|
+
* return the parsed response. Most callers want a higher-level API.
|
|
5
|
+
*
|
|
6
|
+
* `R` names the expected response shape; nothing checks it, so the default `unknown` forces a
|
|
7
|
+
* narrowing.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const response = await fetchCompute('grasshopper', args, {
|
|
11
|
+
* serverUrl: 'https://my-server.com',
|
|
12
|
+
* timeoutMs: 30_000,
|
|
13
|
+
* retry: { attempts: 2 },
|
|
14
|
+
* signal: controller.signal
|
|
15
|
+
* });
|
|
16
|
+
*/
|
|
17
|
+
export declare function fetchCompute<R = unknown>(endpoint: string, args: Record<string, any>, config: ComputeConfig): Promise<R>;
|
|
18
|
+
interface Logger {
|
|
19
|
+
debug(message: string, ...args: unknown[]): void;
|
|
20
|
+
info(message: string, ...args: unknown[]): void;
|
|
21
|
+
warn(message: string, ...args: unknown[]): void;
|
|
22
|
+
error(message: string, ...args: unknown[]): void;
|
|
23
|
+
}
|
|
24
|
+
export declare function getLogger(): Logger;
|
|
25
|
+
/**
|
|
26
|
+
* Route the package's logs to `logger`; `null` silences them (the default).
|
|
27
|
+
*
|
|
28
|
+
* @throws {ComputeError} `INVALID_CONFIG` if the logger lacks one of the four methods, rather
|
|
29
|
+
* than a "debug is not a function" later at an unrelated call site.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* import { setLogger } from '@selvajs/compute/core';
|
|
34
|
+
*
|
|
35
|
+
* setLogger(console);
|
|
36
|
+
* setLogger({
|
|
37
|
+
* debug: (msg, ...args) => myLogger.debug(msg, ...args),
|
|
38
|
+
* info: (msg, ...args) => myLogger.info(msg, ...args),
|
|
39
|
+
* warn: (msg, ...args) => myLogger.warn(msg, ...args),
|
|
40
|
+
* error: (msg, ...args) => myLogger.error(msg, ...args)
|
|
41
|
+
* });
|
|
42
|
+
* setLogger(null);
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function setLogger(logger: Logger | Console | null): void;
|
|
46
|
+
/** Shorthand for `setLogger(console)`. */
|
|
47
|
+
export declare function enableDebugLogging(): void;
|
|
48
|
+
/**
|
|
49
|
+
* Case-insensitive field reads for wire payloads.
|
|
50
|
+
*
|
|
51
|
+
* Rhino Compute branches serialize the same field in different casings. mcneel 8.x/9.x send the IO
|
|
52
|
+
* schema PascalCase (`ParamType`, `Default`, `InnerTree`): those C# classes carry no
|
|
53
|
+
* `[JsonProperty]`. The VektorNode fork sends camelCase, except inside the `default` DataTree
|
|
54
|
+
* (`ParamName`, `InnerTree`), an external type it can't attribute. So one response can mix casings.
|
|
55
|
+
* Read the fields you need through here and leave the rest verbatim: rewriting every key corrupts
|
|
56
|
+
* user-authored ones such as value-list labels.
|
|
57
|
+
*/
|
|
58
|
+
/**
|
|
59
|
+
* Read `name` in any casing, preferring an exact-case match. `undefined` when no own key matches or
|
|
60
|
+
* `obj` isn't an object.
|
|
61
|
+
*/
|
|
62
|
+
export declare function readField<T = unknown>(obj: unknown, name: string): T | undefined;
|
|
63
|
+
/** Whether `name` is present in any casing, even with a null or undefined value. */
|
|
64
|
+
export declare function hasField(obj: unknown, name: string): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Decode base64 to bytes, normalizing and validating the same way in Node and browsers.
|
|
67
|
+
* `packages/visualization/src/shared/encoding.ts` keeps a copy that throws its own error class;
|
|
68
|
+
* change both together.
|
|
69
|
+
*
|
|
70
|
+
* @throws {ComputeError} `ENCODING_ERROR` if invalid, or `ENVIRONMENT_ERROR` if the runtime has no decoder
|
|
71
|
+
*/
|
|
72
|
+
export declare function decodeBase64ToBinary(base64File: string): Uint8Array;
|
|
73
|
+
/**
|
|
74
|
+
* The public McNeel endpoint, blocked by default: users must point at their own server. Its raw IP
|
|
75
|
+
* isn't blocked: it sits behind a load balancer with no stable address to pin.
|
|
76
|
+
*/
|
|
77
|
+
export declare const DEFAULT_BLOCKED_HOST = "compute.rhino3d.com";
|
|
78
|
+
interface ValidateServerUrlOptions {
|
|
79
|
+
/**
|
|
80
|
+
* Hostnames rejected as a `serverUrl` — a backend's shared public endpoint,
|
|
81
|
+
* which callers must not point at. Defaults to `[DEFAULT_BLOCKED_HOST]`; pass
|
|
82
|
+
* `[]` to block nothing. Compared lowercased with any trailing dot stripped.
|
|
83
|
+
*/
|
|
84
|
+
blockedHosts?: readonly string[];
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Validate a compute `serverUrl` and return it trimmed, without trailing slashes.
|
|
88
|
+
*
|
|
89
|
+
* @throws {ComputeError} `INVALID_CONFIG` when the URL is empty, isn't http(s), doesn't parse,
|
|
90
|
+
* embeds credentials, has a query or fragment, or names a blocked host.
|
|
91
|
+
*/
|
|
92
|
+
export declare function validateServerUrl(raw: string, options?: ValidateServerUrlOptions): string;
|
|
93
|
+
/**
|
|
94
|
+
* Why a liveness probe failed, and whether waiting can change the answer.
|
|
95
|
+
*
|
|
96
|
+
* `retryable` is the whole point: a powered-off VM and a booting one both read
|
|
97
|
+
* as "offline" from a single probe, but only one of them will ever come up. A
|
|
98
|
+
* caller that can't tell them apart has to assume the optimistic case and burn
|
|
99
|
+
* its full retry window on a machine that is simply off.
|
|
100
|
+
*/
|
|
101
|
+
type ProbeVerdict =
|
|
102
|
+
/** Nothing is listening on the port — the host answered, and said no. */
|
|
103
|
+
'refused' |
|
|
104
|
+
/** The hostname does not resolve. */
|
|
105
|
+
'dns' |
|
|
106
|
+
/** No answer within the timeout: a booting host swallows packets rather than refusing them. */
|
|
107
|
+
'timeout' |
|
|
108
|
+
/** The server answered, but rejected the probe's credentials (401/403). */
|
|
109
|
+
'unauthorized' |
|
|
110
|
+
/** The server answered with a non-2xx that isn't an auth rejection. */
|
|
111
|
+
'http_error' |
|
|
112
|
+
/** Connection failed in a way we can't attribute. */
|
|
113
|
+
'unknown';
|
|
114
|
+
interface ProbeFailure {
|
|
115
|
+
verdict: ProbeVerdict;
|
|
116
|
+
/** Whether retrying the same probe could plausibly succeed later. */
|
|
117
|
+
retryable: boolean;
|
|
118
|
+
/** Operator-facing sentence. Never includes the API key. */
|
|
119
|
+
summary: string;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Classify a `ComputeServerStats.probeServer` result.
|
|
123
|
+
*
|
|
124
|
+
* A DNS failure is treated as retryable even though a missing record won't fix
|
|
125
|
+
* itself: `EAI_AGAIN` is a *timeout* talking to the resolver, and the two are
|
|
126
|
+
* not distinguishable from the error alone. Retrying a genuinely-wrong hostname
|
|
127
|
+
* costs one window; giving up on a resolver hiccup breaks a server that is fine.
|
|
128
|
+
*
|
|
129
|
+
* @param probe - The `{ online, status?, error? }` a probe returned. An `online`
|
|
130
|
+
* probe is not a failure and yields `null`.
|
|
131
|
+
*/
|
|
132
|
+
export declare function classifyProbeFailure(probe: {
|
|
133
|
+
online: boolean;
|
|
134
|
+
status?: number;
|
|
135
|
+
error?: string;
|
|
136
|
+
}): ProbeFailure | null;
|
|
137
|
+
/**
|
|
138
|
+
* Decode the files in a compute response, plus any external `additionalFiles`, without
|
|
139
|
+
* downloading them. Never throws: an unnamed or undecodable item and a failed external fetch are
|
|
140
|
+
* logged and dropped, the rest still resolve. Colliding archive paths are renamed
|
|
141
|
+
* (`model.txt` → `model-2.txt`), so a consumer keying by `path` loses nothing.
|
|
142
|
+
*/
|
|
143
|
+
export declare const extractFilesFromComputeResponse: (downloadableFiles: FileData[], additionalFiles?: FileBaseInfo[] | FileBaseInfo | null) => Promise<ProcessedFile[]>;
|
|
144
|
+
/** Downloads files from a compute response as a ZIP archive. */
|
|
145
|
+
export declare const downloadFileData: (downloadableFiles: FileData[], fileFoldername: string, additionalFiles?: FileBaseInfo[] | FileBaseInfo | null, options?: DownloadOptions) => Promise<void>;
|
|
146
|
+
/**
|
|
147
|
+
* Download one archive per `Sub Folder` root (see {@link groupFilesByRoot}). Files with no root go
|
|
148
|
+
* into `fallbackName`, so a definition that never sets `Sub Folder` downloads as one archive.
|
|
149
|
+
*
|
|
150
|
+
* Archives are saved one at a time: browsers discard concurrent downloads issued in the same tick.
|
|
151
|
+
* More than one save per gesture may prompt the user for permission.
|
|
152
|
+
*
|
|
153
|
+
* @param downloadableFiles - `FileData` items from the compute response.
|
|
154
|
+
* @param fallbackName - Archive name for files with no `Sub Folder` root.
|
|
155
|
+
* @param additionalFiles - Extra files to package; they carry no root and join the fallback archive.
|
|
156
|
+
* @param options - `saveBlob` replaces the anchor download for every archive.
|
|
157
|
+
*/
|
|
158
|
+
export declare const downloadFileDataByRoot: (downloadableFiles: FileData[], fallbackName: string, additionalFiles?: FileBaseInfo[] | FileBaseInfo | null, options?: DownloadOptions) => Promise<void>;
|
|
159
|
+
/**
|
|
160
|
+
* The `Sub Folder` convention, as authored on Selva's Grasshopper file components.
|
|
161
|
+
*
|
|
162
|
+
* `::` nests, matching Rhino's layer separator (`ROOT::Panels`), and `/` and `\` are accepted
|
|
163
|
+
* because people type them out of habit. The plugin normalizes to `/` before sending, so these
|
|
164
|
+
* helpers also cover payloads from an older plugin that still emit `::`.
|
|
165
|
+
*
|
|
166
|
+
* The first segment is the **root**, which names the archive rather than becoming a folder inside
|
|
167
|
+
* it: `ROOT::Panels` downloads as `ROOT.zip` containing `Panels/…`. Files sharing a root travel
|
|
168
|
+
* together; distinct roots produce separate archives.
|
|
169
|
+
*/
|
|
170
|
+
/**
|
|
171
|
+
* Split a `Sub Folder` value into folder segments; `/`, `\` and `::` all separate.
|
|
172
|
+
*
|
|
173
|
+
* Exported because anything rendering a folder tree has to agree with the archive on where the
|
|
174
|
+
* boundaries are — splitting on `/` alone leaves `Main::Panels` as one literal segment and shows
|
|
175
|
+
* a folder named after the separator.
|
|
176
|
+
*/
|
|
177
|
+
export declare const subFolderSegments: (subFolder: string | undefined) => string[];
|
|
178
|
+
/**
|
|
179
|
+
* Group files by their `Sub Folder` root, preserving encounter order.
|
|
180
|
+
*
|
|
181
|
+
* Rootless files group under `''` — a caller naming archives supplies its own fallback for that
|
|
182
|
+
* bucket. Useful beyond downloading: a consumer writing to disk gets the same grouping without
|
|
183
|
+
* touching the DOM.
|
|
184
|
+
*/
|
|
185
|
+
export declare const groupFilesByRoot: <T extends Pick<FileData, "subFolder">>(files: readonly T[]) => Array<{
|
|
186
|
+
root: string;
|
|
187
|
+
files: T[];
|
|
188
|
+
}>;
|
|
189
|
+
export { type ComputeAuth, type ComputeConfig, ComputeError, type DefinitionRef, type DownloadOptions, type ErrorCode, ErrorCodes, type FileBaseInfo, type FileData, type Logger, type ProbeFailure, type ProbeVerdict, type ProcessedFile, type RetryPolicy, type ServerErrorCodeMap, type ServerTiming, type SolveDefinition, type ValidateServerUrlOptions, isDefinitionRef };
|
|
190
|
+
//# sourceMappingURL=core.d.ts.map
|
package/dist/core.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{
|
|
1
|
+
export { P as ComputeError, T as DEFAULT_BLOCKED_HOST, F as ErrorCodes, d as classifyProbeFailure, y as decodeBase64ToBinary, t as downloadFileData, n as downloadFileDataByRoot, j as enableDebugLogging, r as extractFilesFromComputeResponse, l as fetchCompute, M as getLogger, a as groupFilesByRoot, k as hasField, s as isDefinitionRef, A as readField, N as setLogger, o as subFolderSegments, E as validateServerUrl } from "./handle-files-fKtSlWT2.js";
|
package/dist/grasshopper.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as ComputeAuth, f as ServerTiming, l as ComputeConfig, n as FileBaseInfo, o as SolveDefinition, p as ComputeError, u as RetryPolicy } from "./types-Dm9QFVvA.js";
|
|
2
2
|
interface ServerVersion {
|
|
3
3
|
rhino: string;
|
|
4
4
|
compute: string;
|
|
@@ -10,9 +10,7 @@ interface ServerStats {
|
|
|
10
10
|
activeChildren?: number;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* @public Use this for server health monitoring and statistics.
|
|
13
|
+
* Server health and control: liveness, version, child processes, cache purges.
|
|
16
14
|
*
|
|
17
15
|
* @example
|
|
18
16
|
* ```typescript
|
|
@@ -83,10 +81,11 @@ declare class ComputeServerStats {
|
|
|
83
81
|
*
|
|
84
82
|
* @param timeoutMs - Abort the probe after this many ms (default: 5000).
|
|
85
83
|
* Pass `0` to disable the timeout.
|
|
84
|
+
* @param signal - Cancels the probe, which then reports the abort as its `error`.
|
|
86
85
|
* @returns `online` plus either the HTTP `status` the server answered with,
|
|
87
86
|
* or the `error` the connection attempt failed with.
|
|
88
87
|
*/
|
|
89
|
-
probeServer(timeoutMs?: number): Promise<{
|
|
88
|
+
probeServer(timeoutMs?: number, signal?: AbortSignal): Promise<{
|
|
90
89
|
online: boolean;
|
|
91
90
|
status?: number;
|
|
92
91
|
error?: string;
|
|
@@ -317,7 +316,8 @@ type DataTreePath = `{${string}}`;
|
|
|
317
316
|
interface DataItem {
|
|
318
317
|
type: string;
|
|
319
318
|
data: string;
|
|
320
|
-
|
|
319
|
+
/** **Requires:** the VektorNode compute fork. A stock server sends none. */
|
|
320
|
+
id?: string;
|
|
321
321
|
}
|
|
322
322
|
type DataTreeDefault<T = any> = { [K in DataTreePath]?: T[]; };
|
|
323
323
|
type InnerTreeData = { [path in DataTreePath]: DataItem[]; };
|
|
@@ -353,12 +353,14 @@ interface BaseInputType {
|
|
|
353
353
|
treeAccess: boolean;
|
|
354
354
|
/**
|
|
355
355
|
* Name of the group this parameter belongs to.
|
|
356
|
-
*
|
|
356
|
+
*
|
|
357
|
+
* **Requires:** the VektorNode compute fork.
|
|
357
358
|
*/
|
|
358
359
|
groupName?: string;
|
|
359
360
|
/**
|
|
360
361
|
* Unique identifier for the parameter.
|
|
361
|
-
*
|
|
362
|
+
*
|
|
363
|
+
* **Requires:** the VektorNode compute fork.
|
|
362
364
|
*/
|
|
363
365
|
id?: string;
|
|
364
366
|
}
|
|
@@ -437,15 +439,6 @@ interface GrasshopperBaseSchema {
|
|
|
437
439
|
dataversion?: 7 | 8 | null;
|
|
438
440
|
/** Whether to use cached solution */
|
|
439
441
|
cachesolve?: boolean | null;
|
|
440
|
-
/**
|
|
441
|
-
* Opt-in: cache a solve even when the definition reported GH errors. Default
|
|
442
|
-
* (false/unset) means errored solves are never cached server-side. Some
|
|
443
|
-
* definitions throw GH errors by design (a guarded Python component, a
|
|
444
|
-
* filtered branch) while still producing correct geometry — set this so those
|
|
445
|
-
* still benefit from the server's solve cache. Only meaningful with
|
|
446
|
-
* `cachesolve`. Requires a server that honors it (VektorNode fork).
|
|
447
|
-
*/
|
|
448
|
-
cacheerroredsolves?: boolean | null;
|
|
449
442
|
}
|
|
450
443
|
/**
|
|
451
444
|
* Where a Selva-family plugin running inside the solve may POST live events, and
|
|
@@ -636,12 +629,19 @@ interface CacheOptions {
|
|
|
636
629
|
* Cache responses that carry Grasshopper `errors`. Default `true`: an
|
|
637
630
|
* errored solve is a valid, deterministic result — definitions raise GH
|
|
638
631
|
* errors by design (guarded components, validation branches), so replaying
|
|
639
|
-
* one from cache is correct. Set `false`
|
|
640
|
-
* `cacheerroredsolves` server flag, e.g. when a definition's errors are
|
|
632
|
+
* one from cache is correct. Set `false` when a definition's errors are
|
|
641
633
|
* transient (external data sources) rather than functions of the inputs.
|
|
642
634
|
*/
|
|
643
635
|
cacheErroredSolves?: boolean;
|
|
644
636
|
}
|
|
637
|
+
/** What {@link SolveScheduler.cacheStats} reports. */
|
|
638
|
+
interface CacheStats {
|
|
639
|
+
entries: number;
|
|
640
|
+
bytes: number;
|
|
641
|
+
hits: number;
|
|
642
|
+
misses: number;
|
|
643
|
+
evictions: number;
|
|
644
|
+
}
|
|
645
645
|
interface SolveSchedulerOptions {
|
|
646
646
|
mode?: SchedulerMode;
|
|
647
647
|
maxConcurrent?: number;
|
|
@@ -706,8 +706,6 @@ type SolveResult = {
|
|
|
706
706
|
status: 'error';
|
|
707
707
|
error: ComputeError;
|
|
708
708
|
durationMs: number;
|
|
709
|
-
} | {
|
|
710
|
-
status: 'superseded';
|
|
711
709
|
};
|
|
712
710
|
/**
|
|
713
711
|
* Solves by `cacheKey` when given one, otherwise uploads. Reports the server's
|
|
@@ -773,15 +771,7 @@ export declare class SolveScheduler {
|
|
|
773
771
|
private readonly queueWaitMs;
|
|
774
772
|
private readonly timeoutMs;
|
|
775
773
|
private readonly retry;
|
|
776
|
-
private readonly cacheEnabled;
|
|
777
|
-
private readonly cacheMaxBytes;
|
|
778
|
-
private readonly cacheTtl;
|
|
779
|
-
private readonly cacheErroredSolves;
|
|
780
774
|
private readonly cache;
|
|
781
|
-
private cacheBytes;
|
|
782
|
-
private cacheHits;
|
|
783
|
-
private cacheMisses;
|
|
784
|
-
private cacheEvictions;
|
|
785
775
|
private readonly reuseServerDefinitionCache;
|
|
786
776
|
/** Definition hash → the server's cache key (`pointer`) for it. */
|
|
787
777
|
private readonly serverCacheKeys;
|
|
@@ -816,10 +806,10 @@ export declare class SolveScheduler {
|
|
|
816
806
|
* Raising it drains queued work immediately. Lowering it never interrupts work
|
|
817
807
|
* already in flight — those finish above the new limit, and the cap applies from
|
|
818
808
|
* the next dispatch. Values below 1 are clamped, since 0 would wedge the queue.
|
|
809
|
+
*
|
|
810
|
+
* @throws {ComputeError} `INVALID_CONFIG` for `NaN`, which would wedge it too.
|
|
819
811
|
*/
|
|
820
812
|
setMaxConcurrent(value: number): void;
|
|
821
|
-
/** Current concurrency cap — reflects any {@link setMaxConcurrent} adjustment. */
|
|
822
|
-
getMaxConcurrent(): number;
|
|
823
813
|
/** Subscribe to state changes. */
|
|
824
814
|
subscribe(listener: () => void): () => void;
|
|
825
815
|
private notify;
|
|
@@ -880,13 +870,10 @@ export declare class SolveScheduler {
|
|
|
880
870
|
*/
|
|
881
871
|
private fail;
|
|
882
872
|
private settleSuccess;
|
|
883
|
-
private
|
|
873
|
+
private claimSettle;
|
|
884
874
|
private normalizeExecutionError;
|
|
885
875
|
/** Cancel everything — in-flight and pending. */
|
|
886
876
|
cancelAll(): void;
|
|
887
|
-
private readCache;
|
|
888
|
-
private writeCache;
|
|
889
|
-
private dropCacheEntry;
|
|
890
877
|
clearCache(): void;
|
|
891
878
|
/**
|
|
892
879
|
* Observability snapshot of the solve cache: current size plus lifetime
|
|
@@ -898,13 +885,7 @@ export declare class SolveScheduler {
|
|
|
898
885
|
* replace-in-place writes or TTL expiry, which are not capacity signals.
|
|
899
886
|
* Counters are cumulative and survive `clearCache()`.
|
|
900
887
|
*/
|
|
901
|
-
cacheStats():
|
|
902
|
-
entries: number;
|
|
903
|
-
bytes: number;
|
|
904
|
-
hits: number;
|
|
905
|
-
misses: number;
|
|
906
|
-
evictions: number;
|
|
907
|
-
};
|
|
888
|
+
cacheStats(): CacheStats;
|
|
908
889
|
dispose(): void;
|
|
909
890
|
private runHook;
|
|
910
891
|
}
|
|
@@ -937,13 +918,10 @@ interface CreateClientOptions {
|
|
|
937
918
|
};
|
|
938
919
|
}
|
|
939
920
|
/**
|
|
940
|
-
*
|
|
941
|
-
*
|
|
942
|
-
* @public This is the recommended high-level API for Rhino Compute operations.
|
|
921
|
+
* The high-level API for solving Grasshopper definitions on a Rhino Compute server.
|
|
943
922
|
*
|
|
944
|
-
*
|
|
945
|
-
*
|
|
946
|
-
* For production, use this library server-side or proxy requests through your own backend.
|
|
923
|
+
* In a browser, the server URL and API key are exposed to users: run it server-side, or proxy
|
|
924
|
+
* requests through your own backend.
|
|
947
925
|
*
|
|
948
926
|
* @example
|
|
949
927
|
* ```typescript
|
|
@@ -963,39 +941,26 @@ interface CreateClientOptions {
|
|
|
963
941
|
declare class GrasshopperClient {
|
|
964
942
|
private readonly config;
|
|
965
943
|
readonly serverStats: ComputeServerStats;
|
|
944
|
+
private readonly schedulers;
|
|
966
945
|
private disposed;
|
|
967
946
|
/**
|
|
968
|
-
* Per-probe timeout for the `create()` liveness gate
|
|
969
|
-
*
|
|
970
|
-
* is not answering, where every extra second is multiplied by the retry count
|
|
971
|
-
* and paid by whoever is waiting on the solve.
|
|
947
|
+
* Per-probe timeout for the `create()` liveness gate: a healthy `GET /` answers in milliseconds,
|
|
948
|
+
* so this only bounds a silent server, and every extra second is paid once per retry.
|
|
972
949
|
*/
|
|
973
950
|
private static readonly CREATE_PROBE_TIMEOUT_MS;
|
|
974
951
|
private constructor();
|
|
975
952
|
/**
|
|
976
|
-
* Creates
|
|
953
|
+
* Creates a client once a liveness probe (`GET /`) says the server is up. A cold server can read
|
|
954
|
+
* as offline, so the probe retries with a short backoff, stopping early when
|
|
955
|
+
* {@link classifyProbeFailure} says waiting can't change the answer. `config.signal` cancels the
|
|
956
|
+
* ladder.
|
|
977
957
|
*
|
|
978
|
-
*
|
|
979
|
-
*
|
|
980
|
-
*
|
|
981
|
-
*
|
|
982
|
-
*
|
|
983
|
-
* Retries stop early when {@link classifyProbeFailure} says waiting cannot
|
|
984
|
-
* change the answer — connection refused, or a 401/403. This runs in front of
|
|
985
|
-
* a user who clicked Solve, so the retry ladder must not spend its whole
|
|
986
|
-
* budget confirming that a machine is switched off.
|
|
987
|
-
*
|
|
988
|
-
* @throws {ComputeError} with code NETWORK_ERROR if the server stays
|
|
989
|
-
* unreachable across all attempts. `context.probeVerdict` carries the
|
|
990
|
-
* {@link ProbeVerdict} so callers can render a specific cause.
|
|
991
|
-
* @throws {ComputeError} with code INVALID_CONFIG if configuration is invalid
|
|
958
|
+
* @throws {ComputeError} `NETWORK_ERROR` if the server stays unreachable across all attempts.
|
|
959
|
+
* `context.probeVerdict` carries the verdict, so a caller can render a specific cause.
|
|
960
|
+
* @throws {ComputeError} `ABORTED` if `config.signal` fires first.
|
|
961
|
+
* @throws {ComputeError} `INVALID_CONFIG` if the configuration is invalid.
|
|
992
962
|
*/
|
|
993
963
|
static create(config: GrasshopperComputeConfig, options?: CreateClientOptions): Promise<GrasshopperClient>;
|
|
994
|
-
/**
|
|
995
|
-
* Gets the client's configuration.
|
|
996
|
-
* Useful for passing to lower-level functions.
|
|
997
|
-
*/
|
|
998
|
-
getConfig(): GrasshopperComputeConfig;
|
|
999
964
|
/**
|
|
1000
965
|
* Get input/output parameters of a Grasshopper definition.
|
|
1001
966
|
*/
|
|
@@ -1025,23 +990,31 @@ declare class GrasshopperClient {
|
|
|
1025
990
|
* @example
|
|
1026
991
|
* ```ts
|
|
1027
992
|
* const sliderScheduler = client.createScheduler({ mode: 'latest-wins' });
|
|
1028
|
-
* const submitScheduler = client.createScheduler({ mode: 'queue', timeoutMs: 0
|
|
993
|
+
* const submitScheduler = client.createScheduler({ mode: 'queue', timeoutMs: 0 });
|
|
1029
994
|
* ```
|
|
1030
995
|
*/
|
|
1031
996
|
createScheduler(options?: SolveSchedulerOptions): SolveScheduler;
|
|
1032
|
-
/**
|
|
1033
|
-
* Disposes of client resources.
|
|
1034
|
-
* Call this when you're done using the client.
|
|
1035
|
-
*/
|
|
997
|
+
/** Disposes of client resources, including every scheduler it created. */
|
|
1036
998
|
dispose(): Promise<void>;
|
|
1037
999
|
private ensureNotDisposed;
|
|
1038
1000
|
}
|
|
1001
|
+
/**
|
|
1002
|
+
* The part of rhino3dm the decoders call. Structural so the public types don't name `rhino3dm`, a
|
|
1003
|
+
* dev dependency here. A loaded `RhinoModule` satisfies it.
|
|
1004
|
+
*/
|
|
1005
|
+
interface RhinoRuntime {
|
|
1006
|
+
Point: new (location: number[]) => unknown;
|
|
1007
|
+
Line: new (from: number[], to: number[]) => unknown;
|
|
1008
|
+
CommonObject: {
|
|
1009
|
+
decode(jsonObject: object): unknown;
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1039
1012
|
interface ParsedContext {
|
|
1040
1013
|
[key: string]: any;
|
|
1041
1014
|
}
|
|
1042
1015
|
interface GetValuesOptions {
|
|
1043
1016
|
parseValues?: boolean;
|
|
1044
|
-
rhino?:
|
|
1017
|
+
rhino?: RhinoRuntime;
|
|
1045
1018
|
/**
|
|
1046
1019
|
* If true, only include values of type System.String in the result.
|
|
1047
1020
|
* Non-string types are filtered out.
|
|
@@ -1106,9 +1079,7 @@ declare class GrasshopperResponseProcessor {
|
|
|
1106
1079
|
* Download all files generated by Grasshopper, optionally including
|
|
1107
1080
|
* additional user-provided files.
|
|
1108
1081
|
*
|
|
1109
|
-
*
|
|
1110
|
-
*
|
|
1111
|
-
* @param folderName - Name for the download directory.
|
|
1082
|
+
* @param folderName - Name of the zip archive, without the extension.
|
|
1112
1083
|
* @param additionalFiles - Extra files to package (single file, array, or null).
|
|
1113
1084
|
*
|
|
1114
1085
|
* @example
|
|
@@ -1118,7 +1089,7 @@ declare class GrasshopperResponseProcessor {
|
|
|
1118
1089
|
*
|
|
1119
1090
|
* @example
|
|
1120
1091
|
* ```ts
|
|
1121
|
-
* const extra = {
|
|
1092
|
+
* const extra = { fileName: 'notes.txt', filePath: 'https://example.com/notes.txt' };
|
|
1122
1093
|
* await processor.getAndDownloadFiles('project', extra);
|
|
1123
1094
|
* ```
|
|
1124
1095
|
*/
|
|
@@ -1157,14 +1128,11 @@ export declare function solveGrasshopperDefinition(dataTree: DataTree[], definit
|
|
|
1157
1128
|
/**
|
|
1158
1129
|
* Fetches a definition's input/output schemas without per-type parsing.
|
|
1159
1130
|
*
|
|
1160
|
-
* Field KEYS are canonicalized to camelCase and missing required fields get a
|
|
1161
|
-
*
|
|
1162
|
-
* `outputs` becomes `[]`. Field VALUES, notably `default` and the `values`
|
|
1131
|
+
* Field KEYS are canonicalized to camelCase and missing required fields get a fallback; a missing
|
|
1132
|
+
* or non-array `inputs` / `outputs` becomes `[]`. Field VALUES, notably `default` and the `values`
|
|
1163
1133
|
* label map, pass through verbatim.
|
|
1164
1134
|
*
|
|
1165
1135
|
* @throws {ComputeError} If fetch fails or response is invalid
|
|
1166
|
-
*
|
|
1167
|
-
* @public Use `fetchParsedDefinitionIO()` for processed, type-safe inputs
|
|
1168
1136
|
*/
|
|
1169
1137
|
export declare function fetchDefinitionIO(definition: string | Uint8Array, config: ComputeConfig): Promise<GrasshopperParsedIORaw>;
|
|
1170
1138
|
/**
|
|
@@ -1287,17 +1255,14 @@ export declare class TreeBuilder {
|
|
|
1287
1255
|
private innerTree;
|
|
1288
1256
|
private paramName;
|
|
1289
1257
|
constructor(paramName: string);
|
|
1290
|
-
/** Append values to the branch at `path` (`[0, 1]` is `{0;1}`). */
|
|
1258
|
+
/** Append values to the branch at `path` (`[0, 1]` is `{0;1}`). A `null` item is skipped. */
|
|
1291
1259
|
append(path: number[], items: DataTreeValue[]): this;
|
|
1292
1260
|
/** Replace the tree with a branch-path map like `{ '{0;1}': [1, 2] }`. */
|
|
1293
1261
|
fromDataTreeDefault(treeData: DataTreeDefault): this;
|
|
1294
1262
|
/** Append a value, or each value of an array, to branch `{0}`. */
|
|
1295
1263
|
appendFlat(values: DataTreeValue | DataTreeValue[]): this;
|
|
1296
|
-
/** Every value across all branches. */
|
|
1297
|
-
flatten(): DataTreeValue[];
|
|
1298
1264
|
/** The tree in the shape a solve request sends. */
|
|
1299
1265
|
toComputeFormat(): DataTree;
|
|
1300
|
-
getParamName(): string;
|
|
1301
1266
|
/**
|
|
1302
1267
|
* One tree per input that has a default, keyed by its nickname. Defaults are coerced and
|
|
1303
1268
|
* clamped by the same rules as {@link TreeBuilder.fromValues}.
|
|
@@ -1351,5 +1316,5 @@ export declare class TreeBuilder {
|
|
|
1351
1316
|
private static hasValidValue;
|
|
1352
1317
|
private static clampValue;
|
|
1353
1318
|
}
|
|
1354
|
-
export { type BooleanInputType, type CacheOptions, type ColorInputType, ComputeServerStats, type CreateClientOptions, type DataItem, type DataTree, type DataTreeDefault, type DataTreePath, type DataTreeValue, type DefaultValue, type FileInputType, type GeometryInputType, type GetValuesOptions, type GetValuesResult, GrasshopperClient, type GrasshopperComputeConfig, type GrasshopperComputeResponse, type GrasshopperParsedIO, type GrasshopperParsedIORaw, GrasshopperResponseProcessor, type InnerTreeData, type InputParam, type InputParamSchema, type InputParseError, type NumericInputType, type OutputParamSchema, type ParsedContext, type RhinoDecodeError, type RhinoModelUnit, type SchedulerMode, type SchedulerSolveOptions, type SchemaEndpointResult, type SelvaEventTarget, type SelvaResponseBlock, type ServerStats, type ServerVersion, type SolveContext, type SolveDetail, type SolveExecutor, type SolveOptions, type SolveResult, type SolveSchedulerOptions, type TextInputType, type TreeValueEntry, type ValueListInputType };
|
|
1319
|
+
export { type BooleanInputType, type CacheOptions, type CacheStats, 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 RhinoRuntime, type SchedulerMode, type SchedulerSolveOptions, type SchemaEndpointResult, type SelvaEventTarget, type SelvaResponseBlock, type ServerStats, type ServerVersion, type SolveContext, type SolveDetail, type SolveExecutor, type SolveOptions, type SolveResult, type SolveSchedulerOptions, type TextInputType, type TreeValueEntry, type ValueListInputType };
|
|
1355
1320
|
//# sourceMappingURL=grasshopper.d.ts.map
|