@selvajs/compute 1.5.0 → 1.5.2-beta.0
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 +156 -27
- package/dist/{chunk-LNIUUPA5.cjs → chunk-2NFN5ERT.cjs} +2 -2
- package/dist/chunk-2NFN5ERT.cjs.map +1 -0
- package/dist/chunk-BKGFHI2J.js +2 -0
- package/dist/chunk-BKGFHI2J.js.map +1 -0
- package/dist/{chunk-VK2TSW7S.js → chunk-CZCPL35F.js} +2 -2
- package/dist/{chunk-VK2TSW7S.js.map → chunk-CZCPL35F.js.map} +1 -1
- package/dist/chunk-JSS6OQML.cjs +2 -0
- package/dist/chunk-JSS6OQML.cjs.map +1 -0
- package/dist/{chunk-IJZNCO5X.cjs → chunk-XULONXVP.cjs} +2 -2
- package/dist/chunk-XULONXVP.cjs.map +1 -0
- package/dist/{chunk-PZ4HZLFJ.js → chunk-XZUTU46G.js} +2 -2
- package/dist/core.cjs +1 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.js +1 -1
- package/dist/grasshopper.cjs +1 -1
- package/dist/grasshopper.cjs.map +1 -1
- package/dist/grasshopper.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/visualization.cjs +1 -1
- package/dist/visualization.cjs.map +1 -1
- package/dist/visualization.js +1 -1
- package/dist/visualization.js.map +1 -1
- package/package.json +119 -121
- package/LICENSE.md +0 -21
- package/dist/chunk-FRSLCR7G.cjs +0 -2
- package/dist/chunk-FRSLCR7G.cjs.map +0 -1
- package/dist/chunk-IJZNCO5X.cjs.map +0 -1
- package/dist/chunk-LNIUUPA5.cjs.map +0 -1
- package/dist/chunk-WXQGTKU6.js +0 -2
- package/dist/chunk-WXQGTKU6.js.map +0 -1
- /package/dist/{chunk-PZ4HZLFJ.js.map → chunk-XZUTU46G.js.map} +0 -0
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
[](https://www.typescriptlang.org/)
|
|
8
8
|
[](https://nodejs.org/)
|
|
9
|
-
[](https://github.com/VektorNode/selva-compute)
|
|
10
10
|
|
|
11
11
|
</div>
|
|
12
12
|
|
|
@@ -28,46 +28,112 @@ _(Note: `three` is a peer dependency if you use the visualization features)_
|
|
|
28
28
|
|
|
29
29
|
`@selvajs/compute` provides a type-safe, production-ready foundation for building with Rhino Compute:
|
|
30
30
|
|
|
31
|
-
- **Type-safe API**
|
|
32
|
-
- **High-level
|
|
33
|
-
- **
|
|
31
|
+
- **Type-safe API** — Full TypeScript with structured error codes and rich error context.
|
|
32
|
+
- **High-level client** — `GrasshopperClient` for one-off solves, `client.createScheduler()` for any UI that fires solves frequently.
|
|
33
|
+
- **Robust transport** — Configurable timeout, caller-supplied `AbortSignal`, exponential-backoff retries on transient errors, and `Retry-After` honored on 429.
|
|
34
|
+
- **Slider-friendly** — `latest-wins` scheduling aborts stale solves when newer values arrive. Optional response cache makes repeated inputs instant.
|
|
35
|
+
- **Ready-to-use visualization** — Integrated Three.js setup with `initThree()` and configurable rendering options.
|
|
34
36
|
|
|
35
|
-
Whether you're building a simple solver or a
|
|
37
|
+
Whether you're building a simple solver, a slider-driven configurator, or a long-running job submission flow, `@selvajs/compute` handles the plumbing so you can focus on your Grasshopper definitions.
|
|
38
|
+
|
|
39
|
+
> **What this is not:** a job queue. For solves longer than a couple of
|
|
40
|
+
> minutes, run this library server-side behind your own queue
|
|
41
|
+
> (BullMQ / SQS / Cloud Tasks) and expose a status endpoint to the browser.
|
|
36
42
|
|
|
37
43
|
> **Note:** The library currently focuses on the Grasshopper endpoint but is designed to support other Rhino Compute endpoints in future releases.
|
|
38
44
|
|
|
39
|
-
|
|
45
|
+
## Quickstart
|
|
46
|
+
|
|
47
|
+
Every solve in `@selvajs/compute` goes through a **scheduler**. The scheduler
|
|
48
|
+
handles cancellation, retries, loading state, and (optionally) a response cache
|
|
49
|
+
— things every real app needs and shouldn't have to rebuild.
|
|
40
50
|
|
|
41
51
|
```ts
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
52
|
+
import { GrasshopperClient, TreeBuilder, GrasshopperResponseProcessor } from '@selvajs/compute';
|
|
53
|
+
|
|
54
|
+
const client = await GrasshopperClient.create({
|
|
55
|
+
serverUrl: 'http://localhost:6500',
|
|
56
|
+
apiKey: 'your-api-key'
|
|
57
|
+
});
|
|
46
58
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
apiKey: API_KEY
|
|
50
|
-
} as GrasshopperComputeConfig;
|
|
59
|
+
// Configure the scheduler for your workload (see "Configuring the scheduler" below).
|
|
60
|
+
const scheduler = client.createScheduler({ mode: 'latest-wins', timeoutMs: 30_000 });
|
|
51
61
|
|
|
52
|
-
|
|
62
|
+
// Inspect the definition's inputs once, build a data tree.
|
|
63
|
+
const io = await client.getIO('my-definition.gh');
|
|
64
|
+
const inputTree = TreeBuilder.fromInputParams(io.inputs);
|
|
53
65
|
|
|
54
|
-
//
|
|
55
|
-
|
|
66
|
+
// Solve. Returns a Promise — call it as often as you like.
|
|
67
|
+
const result = await scheduler.solve('my-definition.gh', inputTree);
|
|
68
|
+
const { values } = new GrasshopperResponseProcessor(result).getValues();
|
|
69
|
+
```
|
|
56
70
|
|
|
57
|
-
|
|
58
|
-
const io = await client.getIO(DEFINITION_FILE);
|
|
71
|
+
Wire the scheduler's state into your UI for spinners and disabled buttons:
|
|
59
72
|
|
|
60
|
-
|
|
61
|
-
|
|
73
|
+
```ts
|
|
74
|
+
scheduler.subscribe(() => {
|
|
75
|
+
showSpinner = scheduler.isSolving;
|
|
76
|
+
disableSubmit = scheduler.hasPending;
|
|
77
|
+
});
|
|
78
|
+
```
|
|
62
79
|
|
|
63
|
-
|
|
64
|
-
|
|
80
|
+
And handle expected cancellations gracefully — when newer values supersede an
|
|
81
|
+
in-flight solve, or when the user aborts:
|
|
65
82
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
83
|
+
```ts
|
|
84
|
+
scheduler.solve(definition, inputTree).catch((err) => {
|
|
85
|
+
if (/superseded|aborted/i.test(err.message)) return; // expected, not an error
|
|
86
|
+
showError(err);
|
|
87
|
+
});
|
|
69
88
|
```
|
|
70
89
|
|
|
90
|
+
## Configuring the scheduler
|
|
91
|
+
|
|
92
|
+
The scheduler is one API with two knobs that matter — `mode` and `timeoutMs` —
|
|
93
|
+
plus a couple of optional ones. Pick the row that matches what the user is
|
|
94
|
+
doing in your UI:
|
|
95
|
+
|
|
96
|
+
| Workload | `mode` | `timeoutMs` | `retry` | Notes |
|
|
97
|
+
|---|---|---|---|---|
|
|
98
|
+
| **Slider scrubs / live previews** | `'latest-wins'` | `30_000` | default | Aborts in-flight solves when newer values arrive. Add `cache: { ttlMs: 60_000 }` for instant repeats. |
|
|
99
|
+
| **Submit / long-running jobs** | `'queue'` | `0` (no timeout) | `{ attempts: 1 }` | Serial queue. Pass a caller `signal` so users can hit Cancel. Bump proxy idle timeouts (see below). |
|
|
100
|
+
| **Background / batch parallel** | `'parallel'` | `60_000` | `{ attempts: 2 }` | Fires solves concurrently up to `maxConcurrent` (default 4). |
|
|
101
|
+
|
|
102
|
+
You can create multiple schedulers from one client — typically one per UI
|
|
103
|
+
surface. They share the connection pool but their queues, cancel scopes, and
|
|
104
|
+
caches are independent:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
const previewScheduler = client.createScheduler({ mode: 'latest-wins', timeoutMs: 30_000 });
|
|
108
|
+
const submitScheduler = client.createScheduler({ mode: 'queue', timeoutMs: 0, retry: { attempts: 1 } });
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Cancellation
|
|
112
|
+
|
|
113
|
+
Pass a per-call `signal` to cancel just that solve, or call `cancelAll()` to
|
|
114
|
+
cancel everything (e.g. on route change or component unmount):
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
const ctrl = new AbortController();
|
|
118
|
+
scheduler.solve(definition, tree, { signal: ctrl.signal });
|
|
119
|
+
|
|
120
|
+
// Later:
|
|
121
|
+
ctrl.abort(); // cancel just this call
|
|
122
|
+
scheduler.cancelAll(); // cancel everything in flight + pending
|
|
123
|
+
scheduler.dispose(); // cancel everything and tear down the scheduler
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Long jobs behind a proxy
|
|
127
|
+
|
|
128
|
+
Cloudflare's default idle timeout is 100s; AWS ALB's is 60s; nginx is 60s.
|
|
129
|
+
If your Compute server is behind any of them, those values must be bumped
|
|
130
|
+
before you can run long solves through the browser — the library cannot work
|
|
131
|
+
around proxy timeouts.
|
|
132
|
+
|
|
133
|
+
For solves longer than ~2 minutes, the safer architecture is to run this
|
|
134
|
+
library **server-side** behind your own job queue (BullMQ / SQS / Cloud Tasks)
|
|
135
|
+
and expose a status endpoint to the browser.
|
|
136
|
+
|
|
71
137
|
## Requirements
|
|
72
138
|
|
|
73
139
|
### Core Requirements
|
|
@@ -77,7 +143,7 @@ const { values } = processor.getValues();
|
|
|
77
143
|
|
|
78
144
|
### Rhino Compute Compatibility
|
|
79
145
|
|
|
80
|
-
|
|
146
|
+
`@selvajs/compute` works with both standard Rhino Compute and enhanced versions:
|
|
81
147
|
|
|
82
148
|
**Standard Rhino Compute** – The [official McNeel repository](https://github.com/mcneel/compute.rhino3d) works for basic Grasshopper solving with core features.
|
|
83
149
|
|
|
@@ -90,6 +156,69 @@ const { values } = processor.getValues();
|
|
|
90
156
|
|
|
91
157
|
> Features requiring the enhanced setup will be clearly marked in the documentation.
|
|
92
158
|
|
|
159
|
+
## Troubleshooting
|
|
160
|
+
|
|
161
|
+
### `Network error: Failed to fetch`
|
|
162
|
+
|
|
163
|
+
The browser couldn't reach the server. Check, in order:
|
|
164
|
+
|
|
165
|
+
1. **Server is running** — `curl http://localhost:6500/healthcheck` should return
|
|
166
|
+
a 200.
|
|
167
|
+
2. **CORS** — if your Compute server is on a different origin than your app,
|
|
168
|
+
the server must send `Access-Control-Allow-Origin`. Standard Rhino Compute
|
|
169
|
+
does **not** ship with CORS enabled; you'll need to put it behind a proxy
|
|
170
|
+
that adds the headers, or use the [VektorNode custom branch](https://github.com/VektorNode/compute.rhino3d).
|
|
171
|
+
3. **Mixed content** — an HTTPS app can't fetch from an HTTP server. Either
|
|
172
|
+
serve Compute over HTTPS or develop locally on HTTP.
|
|
173
|
+
4. **API key** — you'll see the same error if your `apiKey` is missing for a
|
|
174
|
+
server that requires one (the server typically returns 401 with no CORS
|
|
175
|
+
headers, which the browser surfaces as a network error).
|
|
176
|
+
|
|
177
|
+
### Solves timing out before the server finishes (502 / 504 / aborted)
|
|
178
|
+
|
|
179
|
+
The bottleneck is almost always a proxy in front of Compute, not the library.
|
|
180
|
+
Common culprits:
|
|
181
|
+
|
|
182
|
+
- **Cloudflare** — 100s idle timeout on free/pro plans (525s on enterprise).
|
|
183
|
+
- **AWS ALB** — 60s default; raise via the `idle_timeout` attribute.
|
|
184
|
+
- **nginx** — 60s default; set `proxy_read_timeout` and `proxy_send_timeout`.
|
|
185
|
+
|
|
186
|
+
For solves longer than ~2 minutes, prefer running this library **server-side**
|
|
187
|
+
and exposing your own job-status endpoint to the browser. Direct
|
|
188
|
+
browser → Compute is fine for short solves but fragile for long ones.
|
|
189
|
+
|
|
190
|
+
### `Definition URL/content is required`
|
|
191
|
+
|
|
192
|
+
You called `client.solve('', tree)` or passed a `Uint8Array` of length 0.
|
|
193
|
+
Validate your input before calling.
|
|
194
|
+
|
|
195
|
+
### 401 vs 403
|
|
196
|
+
|
|
197
|
+
- **401 Unauthorized** — `apiKey` (`RhinoComputeKey` header) is missing or
|
|
198
|
+
invalid. Standard Rhino Compute uses this scheme.
|
|
199
|
+
- **403 Forbidden** — your `authToken` (Bearer) was rejected by an upstream
|
|
200
|
+
proxy/API gateway. The Compute server itself almost never returns 403.
|
|
201
|
+
|
|
202
|
+
The error message includes the response body excerpt so you usually get a hint
|
|
203
|
+
from the server itself.
|
|
204
|
+
|
|
205
|
+
### "Superseded by newer solve" errors flooding my console
|
|
206
|
+
|
|
207
|
+
That's the scheduler doing its job in `latest-wins` mode — every aborted slider
|
|
208
|
+
solve rejects with this message. Filter it out:
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
scheduler.solve(def, tree).catch((err) => {
|
|
212
|
+
if (/superseded|aborted/i.test(err.message)) return; // expected, not an error
|
|
213
|
+
showError(err);
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### "Failed to load three.js visualization module"
|
|
218
|
+
|
|
219
|
+
The dynamic import of the visualization layer threw. Make sure `three` is
|
|
220
|
+
installed (`npm install three`) — it's a peer dependency, not a direct one.
|
|
221
|
+
|
|
93
222
|
## Acknowledgement
|
|
94
223
|
|
|
95
224
|
This library is built on production experience and draws from several official McNeel repositories. Where code has been adapted, it is clearly marked in the relevant files.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }var
|
|
2
|
-
//# sourceMappingURL=chunk-
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }var _chunkXULONXVPcjs = require('./chunk-XULONXVP.cjs');function v(r){return Buffer.from(r,"utf-8").toString("base64")}function T(r){return!r||r.length<2||r.length%4!==0?!1:/^[A-Za-z0-9+/]+={0,2}$/.test(r)}function S(r){if(typeof globalThis.Buffer=="function"){let e=globalThis.Buffer.from(r,"base64");return new Uint8Array(e.buffer,e.byteOffset,e.byteLength)}if(typeof globalThis.atob=="function"){let e=globalThis.atob(r),n=new Uint8Array(e.length);for(let a=0;a<e.length;a++)n[a]=e.charCodeAt(a)&255;return n}throw new (0, _chunkXULONXVPcjs.d)("Base64 decoding not supported in this environment.",_chunkXULONXVPcjs.c.INVALID_STATE,{context:{environmentInfo:"atob or Buffer not available"}})}function C(r){if(r==null)throw new (0, _chunkXULONXVPcjs.d)("Input bytes must not be null or undefined",_chunkXULONXVPcjs.c.INVALID_INPUT,{context:{receivedValue:r}});let e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",n=r;n.length>=3&&n[0]===239&&n[1]===187&&n[2]===191&&(n=n.slice(3));let a=n.byteLength,y=a%3,u=a-y,b="",o,i,c,l,d;for(let t=0;t<u;t+=3){let f=n[t]!==void 0?n[t]:0,h=n[t+1]!==void 0?n[t+1]:0,g=n[t+2]!==void 0?n[t+2]:0,s=f<<16|h<<8|g;if(o=(s&16515072)>>18,i=(s&258048)>>12,c=(s&4032)>>6,l=s&63,typeof e!="string")throw new Error("encodings must be a string");if(typeof o!="number"||o<0||o>=e.length)throw new Error("Invalid index a");if(typeof i!="number"||i<0||i>=e.length)throw new Error("Invalid index b");if(typeof c!="number"||c<0||c>=e.length)throw new Error("Invalid index c");if(typeof l!="number"||l<0||l>=e.length)throw new Error("Invalid index d");let w=e[o],B=e[i],x=e[c],I=e[l];if(w===void 0||B===void 0||x===void 0||I===void 0)throw new Error("Invalid encoding index");b+=w+B+x+I}if(y===1){if(d=n[u],d===void 0)throw new Error("'chunk' must not be undefined");o=(d&252)>>2,i=(d&3)<<4;let t=e[o],f=e[i];if(t===void 0||f===void 0)throw new Error("Invalid encoding index");b+=`${t+f}==`}else if(y===2){let t=_nullishCoalesce(n[u], () => (0)),f=n[u+1]!==void 0?n[u+1]:0;if(typeof t!="number"||t<0||t>255||typeof f!="number"||f<0||f>255)throw new Error("Invalid byte1");d=t<<8|f,o=(d&64512)>>10,i=(d&1008)>>4,c=(d&15)<<2;let h=e[o],g=e[i],s=e[c];if(h===void 0||g===void 0||s===void 0)throw new Error("Invalid encoding index");b+=`${h+g+s}=`}return b}exports.a = v; exports.b = T; exports.c = S; exports.d = C;
|
|
2
|
+
//# sourceMappingURL=chunk-2NFN5ERT.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/selva-compute/selva-compute/dist/chunk-2NFN5ERT.cjs","../src/core/utils/encoding.ts"],"names":["encodeStringToBase64","str","isBase64","decodeBase64ToBinary","base64File","buf","binary","bytes","i","RhinoComputeError","ErrorCodes","base64ByteArray","encodings","inputBytes","byteLength","byteRemainder","mainLength","base64","a","b","d","chunk","byte1","byte2","byte3","innerChunk","charA","charB","charC","charD"],"mappings":"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","file":"/home/runner/work/selva-compute/selva-compute/dist/chunk-2NFN5ERT.cjs","sourcesContent":[null,"import { RhinoComputeError } from '../errors/base';\nimport { ErrorCodes } from '../errors/error-codes';\nimport { getLogger } from './logger';\n\n/**\n * Encodes a string to base64 (Node 20+ safe)\n *\n * @internal Internal encoding helper — kept internal to `@selvajs/compute`.\n *\n * @param str - String to encode\n * @returns Base64 encoded string\n */\nexport function encodeStringToBase64(str: string): string {\n\treturn Buffer.from(str, 'utf-8').toString('base64');\n}\n\n/**\n * Decodes a base64 string to a UTF-8 string (Node 20+ safe)\n *\n * @internal Internal encoding helper — kept internal to `@selvajs/compute`.\n *\n * @param base64Str - Base64 encoded string\n * @returns Decoded UTF-8 string\n */\nexport function decodeBase64ToString(base64Str: string): string {\n\treturn Buffer.from(base64Str, 'base64').toString('utf-8');\n}\n\n/**\n * Checks if a string is valid base64\n *\n * @internal Internal encoding helper — kept internal to `@selvajs/compute`.\n *\n * @param str - String to check\n * @returns True if the string is valid base64\n */\nexport function isBase64(str: string): boolean {\n\tif (!str || str.length < 2) return false;\n\t// Length must be a multiple of 4, only alphabet chars + at most 2 trailing '='\n\tif (str.length % 4 !== 0) return false;\n\treturn /^[A-Za-z0-9+/]+={0,2}$/.test(str);\n}\n\n/**\n * Decodes a base64 string to binary data (Uint8Array)\n *\n * @internal Internal encoding helper — kept internal to `@selvajs/compute`.\n *\n * @param base64File - Base64 encoded string\n * @returns Decoded binary data as Uint8Array\n * @throws {RhinoComputeError} If base64 decoding is not supported in this environment.\n */\nexport function decodeBase64ToBinary(base64File: string): Uint8Array {\n\t// Prefer Buffer in Node — it's faster and avoids the latin-1 string detour\n\t// that atob + charCodeAt requires.\n\tif (typeof (globalThis as any).Buffer === 'function') {\n\t\tconst buf = (globalThis as any).Buffer.from(base64File, 'base64');\n\t\treturn new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);\n\t}\n\tif (typeof globalThis.atob === 'function') {\n\t\tconst binary = globalThis.atob(base64File);\n\t\tconst bytes = new Uint8Array(binary.length);\n\t\tfor (let i = 0; i < binary.length; i++) {\n\t\t\tbytes[i] = binary.charCodeAt(i) & 0xff;\n\t\t}\n\t\treturn bytes;\n\t}\n\n\tthrow new RhinoComputeError(\n\t\t'Base64 decoding not supported in this environment.',\n\t\tErrorCodes.INVALID_STATE,\n\t\t{ context: { environmentInfo: 'atob or Buffer not available' } }\n\t);\n}\n\n/**\n * Encodes binary data (Uint8Array) to base64 string\n *\n * @internal Internal encoding helper — kept internal to `@selvajs/compute`.\n *\n * Source: https://github.com/mcneel/compute.rhino3d.appserver/blob/92c95a3b1d076a4d4a5360214ffd27c46425ff03/src/examples/convert/scriptjs\n * https://gist.github.com/jonleighton/958841\n *\n * MIT LICENSE\n * Copyright 2011 Jon Leighton\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\nexport function base64ByteArray(bytes: Uint8Array | null | undefined): string {\n\tif (bytes === null || bytes === undefined) {\n\t\tthrow new RhinoComputeError(\n\t\t\t'Input bytes must not be null or undefined',\n\t\t\tErrorCodes.INVALID_INPUT,\n\t\t\t{ context: { receivedValue: bytes } }\n\t\t);\n\t}\n\n\tconst encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\n\tlet inputBytes = bytes;\n\n\t// strip bom (Byte Order Mark)\n\tif (\n\t\tinputBytes.length >= 3 &&\n\t\tinputBytes[0] === 239 &&\n\t\tinputBytes[1] === 187 &&\n\t\tinputBytes[2] === 191\n\t) {\n\t\tinputBytes = inputBytes.slice(3);\n\t}\n\n\tconst byteLength = inputBytes.byteLength;\n\tconst byteRemainder = byteLength % 3;\n\tconst mainLength = byteLength - byteRemainder;\n\n\tlet base64 = '';\n\tlet a, b, c, d;\n\tlet chunk;\n\n\t// Main loop deals with bytes in chunks of 3\n\tfor (let i = 0; i < mainLength; i += 3) {\n\t\t// Combine the three bytes into a single integer\n\n\t\tconst byte1 = inputBytes[i] !== undefined ? inputBytes[i] : 0;\n\t\tconst byte2 = inputBytes[i + 1] !== undefined ? inputBytes[i + 1] : 0;\n\t\tconst byte3 = inputBytes[i + 2] !== undefined ? inputBytes[i + 2] : 0;\n\n\t\tconst innerChunk = (byte1 << 16) | (byte2 << 8) | byte3;\n\n\t\t// Use bitmasks to extract 6-bit segments from the triplet\n\t\ta = (innerChunk & 16515072) >> 18;\n\t\tb = (innerChunk & 258048) >> 12;\n\t\tc = (innerChunk & 4032) >> 6;\n\t\td = innerChunk & 63;\n\n\t\t// Convert the raw binary segments to the appropriate ASCII encoding\n\t\tif (typeof encodings !== 'string') {\n\t\t\tthrow new Error('encodings must be a string');\n\t\t}\n\n\t\tif (typeof a !== 'number' || a < 0 || a >= encodings.length) {\n\t\t\tthrow new Error('Invalid index a');\n\t\t}\n\n\t\tif (typeof b !== 'number' || b < 0 || b >= encodings.length) {\n\t\t\tthrow new Error('Invalid index b');\n\t\t}\n\n\t\tif (typeof c !== 'number' || c < 0 || c >= encodings.length) {\n\t\t\tthrow new Error('Invalid index c');\n\t\t}\n\n\t\tif (typeof d !== 'number' || d < 0 || d >= encodings.length) {\n\t\t\tthrow new Error('Invalid index d');\n\t\t}\n\n\t\tconst charA = encodings[a];\n\t\tconst charB = encodings[b];\n\t\tconst charC = encodings[c];\n\t\tconst charD = encodings[d];\n\n\t\tif (charA === undefined || charB === undefined || charC === undefined || charD === undefined) {\n\t\t\tthrow new Error('Invalid encoding index');\n\t\t}\n\n\t\tbase64 += charA + charB + charC + charD;\n\t}\n\n\t// Deal with the remaining bytes and padding\n\tif (byteRemainder === 1) {\n\t\tchunk = inputBytes[mainLength];\n\n\t\tif (chunk === undefined) {\n\t\t\tthrow new Error(\"'chunk' must not be undefined\");\n\t\t}\n\n\t\ta = (chunk & 252) >> 2;\n\t\tb = (chunk & 3) << 4;\n\n\t\tconst charA = encodings[a];\n\t\tconst charB = encodings[b];\n\n\t\tif (charA === undefined || charB === undefined) {\n\t\t\tthrow new Error('Invalid encoding index');\n\t\t}\n\n\t\tbase64 += `${charA + charB}==`;\n\t} else if (byteRemainder === 2) {\n\t\tconst byte1 = inputBytes[mainLength] ?? 0;\n\t\tconst byte2 = inputBytes[mainLength + 1] !== undefined ? inputBytes[mainLength + 1] : 0;\n\n\t\tif (\n\t\t\ttypeof byte1 !== 'number' ||\n\t\t\tbyte1 < 0 ||\n\t\t\tbyte1 > 255 ||\n\t\t\ttypeof byte2 !== 'number' ||\n\t\t\tbyte2 < 0 ||\n\t\t\tbyte2 > 255\n\t\t) {\n\t\t\tthrow new Error('Invalid byte1');\n\t\t}\n\n\t\tchunk = (byte1 << 8) | byte2;\n\n\t\ta = (chunk & 64512) >> 10;\n\t\tb = (chunk & 1008) >> 4;\n\t\tc = (chunk & 15) << 2;\n\n\t\tconst charA = encodings[a];\n\t\tconst charB = encodings[b];\n\t\tconst charC = encodings[c];\n\n\t\tif (charA === undefined || charB === undefined || charC === undefined) {\n\t\t\tthrow new Error('Invalid encoding index');\n\t\t}\n\n\t\tbase64 += `${charA + charB + charC}=`;\n\t}\n\n\treturn base64;\n}\n\n/**\n * Convert base64 string to rhino object\n *\n * @internal Internal helper for decoding Rhino objects — not public API.\n *\n * Source: https://github.com/mcneel/compute.rhino3d.appserver/blob/92c95a3b1d076a4d4a5360214ffd27c46425ff03/src/examples/convert/scriptjs\n * @param rhino is the rhino module form rhino3dm. Since not properly typed its not used here.\n * @param item\n * @returns\n */\nexport function base64ToRhinoObject(\n\trhino: any,\n\titem: {\n\t\ttype: string;\n\t\tdata: string;\n\t}\n) {\n\t//Make a type definition for this?\n\tlet decodata: null | object = null;\n\ttry {\n\t\tdecodata = JSON.parse(item.data);\n\t} catch (error) {\n\t\tdecodata = item;\n\t\tgetLogger().warn('Failed to parse JSON, returning original data:', error, item);\n\t}\n\tif (item.type === 'System.String') {\n\t\ttry {\n\t\t\treturn rhino.DracoCompression.decompressBase64String(decodata);\n\t\t} catch (error) {\n\t\t\tgetLogger().error('Failed to decompress Draco base64 string:', error);\n\t\t}\n\t} else if (\n\t\ttypeof decodata === 'object' &&\n\t\tObject.prototype.hasOwnProperty.call(decodata, 'opennurbs')\n\t) {\n\t\treturn rhino.CommonObject.decode(decodata);\n\t} else if (typeof decodata === 'object') {\n\t\ttry {\n\t\t\treturn rhino.CommonObject.decode(decodata);\n\t\t} catch (error) {\n\t\t\tgetLogger().error('Failed to decode Rhino object:', error);\n\t\t}\n\t}\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{a as z,b as W,c as q,d as _}from"./chunk-XZUTU46G.js";import{b as p,c as f,d as l,e as m,h as N,i as j,k as $}from"./chunk-CZCPL35F.js";function L(t){let e=new WeakSet,r=n=>{if(n==null)return JSON.stringify(n);if(typeof n=="number")return Number.isFinite(n)?String(n):JSON.stringify(null);if(typeof n=="string"||typeof n=="boolean")return JSON.stringify(n);if(typeof n=="bigint")return JSON.stringify(n.toString());if(n instanceof Uint8Array){let o=n.length>64?Array.from(n.slice(0,32)).concat(Array.from(n.slice(-32))):Array.from(n);return JSON.stringify({__u8:!0,len:n.length,sample:o})}return Array.isArray(n)?`[${n.map(r).join(",")}]`:typeof n=="object"?e.has(n)?JSON.stringify("[Circular]"):(e.add(n),`{${Object.keys(n).sort().map(a=>`${JSON.stringify(a)}:${r(n[a])}`).join(",")}}`):JSON.stringify(null)};return r(t)}function J(t){let e=2166136261;for(let r=0;r<t.length;r++)e^=t.charCodeAt(r),e=e+((e<<1)+(e<<4)+(e<<7)+(e<<8)+(e<<24))>>>0;return e.toString(16).padStart(8,"0")}function v(t,e){let r=typeof t=="string"?t:L({__u8:!0,len:t.length});return J(`${r}|${L(e)}`)}var h=class{constructor(e,r,n={}){p(this,"executor");p(this,"baseConfig");p(this,"mode");p(this,"maxConcurrent");p(this,"timeoutMs");p(this,"retry");p(this,"cacheEnabled");p(this,"cacheMax");p(this,"cacheTtl");p(this,"cache",new Map);p(this,"onStart");p(this,"onSettle");p(this,"onSuperseded");p(this,"subscribers",new Set);p(this,"inFlight",new Set);p(this,"pendingForLatestWins",null);p(this,"fifoQueue",[]);p(this,"_lastResult",null);p(this,"_lastError",null);p(this,"_lastDurationMs",null);p(this,"disposed",!1);this.executor=e,this.baseConfig=r,this.mode=n.mode??"latest-wins",this.maxConcurrent=Math.max(1,n.maxConcurrent??(this.mode==="parallel"?4:1)),this.timeoutMs=n.timeoutMs,this.retry=n.retry;let o=n.cache;this.cacheEnabled=o!==void 0&&o!==!1;let s=typeof o=="object"?o:{};this.cacheMax=s.maxEntries??50,this.cacheTtl=s.ttlMs??0,this.onStart=n.onStart,this.onSettle=n.onSettle,this.onSuperseded=n.onSuperseded}get isSolving(){return this.inFlight.size>0}get hasPending(){return this.pendingForLatestWins!==null||this.fifoQueue.length>0}get inFlightCount(){return this.inFlight.size}get queueDepth(){return this.fifoQueue.length+(this.pendingForLatestWins?1:0)}get lastResult(){return this._lastResult}get lastError(){return this._lastError}get lastDurationMs(){return this._lastDurationMs}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}notify(){for(let e of this.subscribers)try{e()}catch(r){m().error("[SolveScheduler] subscriber threw:",r)}}solve(e,r,n){if(this.disposed)return Promise.reject(new l("SolveScheduler has been disposed and cannot be used",f.INVALID_STATE));let o=v(e,r),s={key:o,enqueuedAt:Date.now(),startedAt:null};if(this.cacheEnabled){let a=this.readCache(o);if(a){let i={status:"success",response:a,durationMs:0,fromCache:!0};return this._lastResult=a,this._lastError=null,this._lastDurationMs=0,this.runHook(this.onStart,s),this.runHook(this.onSettle,s,i),this.notify(),Promise.resolve(a)}}return new Promise((a,i)=>{let u={definition:e,dataTree:r,ctx:s,resolve:a,reject:i,externalSignal:n?.signal};if(u.externalSignal?.aborted){i(this.makeAbortError(s));return}this.enqueue(u)})}enqueue(e){switch(this.mode){case"latest-wins":{this.pendingForLatestWins&&(this.supersede(this.pendingForLatestWins),this.pendingForLatestWins=null);for(let r of this.inFlight)this.supersede(r),r.controller.abort();this.inFlight.size===0?this.execute(e):this.pendingForLatestWins=e;break}case"queue":{this.inFlight.size<this.maxConcurrent?this.execute(e):this.fifoQueue.push(e);break}case"parallel":{this.inFlight.size<this.maxConcurrent?this.execute(e):this.fifoQueue.push(e);break}}this.notify()}async execute(e){let r=new AbortController,n={...e,controller:r};this.inFlight.add(n),e.ctx.startedAt=Date.now();let o=()=>r.abort();e.externalSignal?.addEventListener("abort",o,{once:!0}),this.runHook(this.onStart,e.ctx),this.notify();let s=performance.now();try{let a={...this.baseConfig,signal:r.signal,...this.timeoutMs!==void 0&&{timeoutMs:this.timeoutMs},...this.retry!==void 0&&{retry:this.retry}},i=await this.executor(e.definition,e.dataTree,a),u=performance.now()-s;this.cacheEnabled&&this.writeCache(e.ctx.key,i),this._lastResult=i,this._lastError=null,this._lastDurationMs=u,e.resolve(i),this.runHook(this.onSettle,e.ctx,{status:"success",response:i,durationMs:u,fromCache:!1})}catch(a){let i=performance.now()-s,u=a instanceof l?a:new l(a instanceof Error?a.message:String(a),f.UNKNOWN_ERROR,{originalError:a instanceof Error?a:new Error(String(a))});this._lastError=u,this._lastDurationMs=i,e.reject(u),this.runHook(this.onSettle,e.ctx,{status:"error",error:u,durationMs:i})}finally{e.externalSignal?.removeEventListener("abort",o),this.inFlight.delete(n),this.drainNext(),this.notify()}}drainNext(){if(!this.disposed){if(this.mode==="latest-wins"){if(this.pendingForLatestWins&&this.inFlight.size===0){let e=this.pendingForLatestWins;this.pendingForLatestWins=null,this.execute(e)}return}for(;this.fifoQueue.length>0&&this.inFlight.size<this.maxConcurrent;){let e=this.fifoQueue.shift();this.execute(e)}}}supersede(e){let r=new l("Superseded by newer solve",f.UNKNOWN_ERROR,{context:{key:e.ctx.key,enqueuedAt:e.ctx.enqueuedAt}});e.reject(r),this.runHook(this.onSuperseded,e.ctx)}makeAbortError(e){return new l("Request aborted by caller",f.UNKNOWN_ERROR,{context:{key:e.key,enqueuedAt:e.enqueuedAt}})}cancelAll(){for(this.pendingForLatestWins&&(this.pendingForLatestWins.reject(this.makeAbortError(this.pendingForLatestWins.ctx)),this.pendingForLatestWins=null);this.fifoQueue.length>0;){let e=this.fifoQueue.shift();e.reject(this.makeAbortError(e.ctx))}for(let e of this.inFlight)e.controller.abort();this.notify()}readCache(e){if(!this.cacheEnabled)return null;let r=this.cache.get(e);return r?this.cacheTtl>0&&Date.now()-r.insertedAt>this.cacheTtl?(this.cache.delete(e),null):(this.cache.delete(e),this.cache.set(e,r),r.response):null}writeCache(e,r){if(this.cacheEnabled)for(this.cache.set(e,{response:r,insertedAt:Date.now()});this.cache.size>this.cacheMax;){let n=this.cache.keys().next().value;if(n===void 0)break;this.cache.delete(n)}}clearCache(){this.cache.clear()}dispose(){this.disposed||(this.disposed=!0,this.cancelAll(),this.subscribers.clear(),this.cache.clear())}runHook(e,...r){if(e)try{e(...r)}catch(n){m().error("[SolveScheduler] hook threw:",n)}}};var T=class t{constructor(e){p(this,"config");p(this,"serverStats");p(this,"disposed",!1);this.config=this.normalizeComputeConfig(e),this.serverStats=new j(this.config.serverUrl,this.config.apiKey)}static async create(e){let r=new t(e);if(!await r.serverStats.isServerOnline())throw new l("Rhino Compute server is not online",f.NETWORK_ERROR,{context:{serverUrl:r.config.serverUrl}});return r}getConfig(){return this.ensureNotDisposed(),{...this.config}}async getIO(e){return this.ensureNotDisposed(),R(e,this.config)}async getRawIO(e){return this.ensureNotDisposed(),S(e,this.config)}async solve(e,r,n){this.ensureNotDisposed();try{if(typeof e=="string"&&!e?.trim())throw new l("Definition URL/content is required",f.INVALID_INPUT,{context:{receivedUrl:e}});if(e instanceof Uint8Array&&e.length===0)throw new l("Definition content is empty",f.INVALID_INPUT);let o={...this.config,...n?.signal!==void 0&&{signal:n.signal},...n?.timeoutMs!==void 0&&{timeoutMs:n.timeoutMs},...n?.retry!==void 0&&{retry:n.retry}},s=await x(r,e,o);if(s&&typeof s=="object"&&"message"in s&&!("fileData"in s))throw new l(s.message||"Computation failed",f.COMPUTATION_ERROR,{context:{definition:typeof e=="string"&&e.length<200?e:"...content...",inputs:r}});return s}catch(o){throw this.config.debug&&m().error("Compute failed:",o),o instanceof l?o:new l(o instanceof Error?o.message:String(o),f.COMPUTATION_ERROR,{context:{definition:typeof e=="string"&&e.length<200?e:"...content...",inputs:r},originalError:o instanceof Error?o:new Error(String(o))})}}createScheduler(e){this.ensureNotDisposed();let r=(n,o,s)=>x(o,n,s);return new h(r,this.config,e)}async dispose(){this.disposed||(this.disposed=!0,"dispose"in this.serverStats&&typeof this.serverStats.dispose=="function"&&await this.serverStats.dispose())}ensureNotDisposed(){if(this.disposed)throw new l("GrasshopperClient has been disposed and cannot be used",f.INVALID_STATE)}normalizeComputeConfig(e){if(!e.serverUrl?.trim())throw new l("serverUrl is required",f.INVALID_CONFIG,{context:{receivedServerUrl:e.serverUrl}});try{new URL(e.serverUrl)}catch{throw new l("serverUrl must be a valid URL",f.INVALID_CONFIG,{context:{receivedServerUrl:e.serverUrl}})}if(e.serverUrl===""||e.serverUrl==="https://compute.rhino3d.com/")throw new l("serverUrl must be set to your Compute server URL. The default public endpoint is not allowed.",f.INVALID_CONFIG,{context:{receivedServerUrl:e.serverUrl}});return{...e,serverUrl:e.serverUrl.replace(/\/+$/,""),apiKey:e.apiKey,authToken:e.authToken,debug:e.debug??!1,suppressClientSideWarning:e.suppressClientSideWarning}}};var M=async(t,e=null)=>{try{return await K(t,e)}catch(r){throw new l("Failed to extract files from compute response",f.INVALID_STATE,{context:{originalError:r instanceof Error?r.message:String(r)},originalError:r instanceof Error?r:void 0})}},P=async(t,e,r=null)=>{if(typeof document>"u"||typeof Blob>"u")throw new l("File download functionality is only available in browser environments. This function requires the DOM API (document, Blob).",f.BROWSER_ONLY,{context:{environment:typeof window<"u"?"browser (SSR)":"Node.js",documentAvailable:typeof document<"u",blobAvailable:typeof Blob<"u"}});try{let n=await K(t,r);await ie(n,e)}catch(n){throw n instanceof l?n:new l("Failed to download files from compute response",f.INVALID_STATE,{context:{originalError:n instanceof Error?n.message:String(n)},originalError:n instanceof Error?n:void 0})}},K=async(t,e)=>{let r=[];if(t.forEach(n=>{let o=`${n.fileName}${n.fileType}`;if(n.subFolder&&n.subFolder.trim()!==""&&(o=`${n.subFolder}/${o}`),n.isBase64Encoded===!0&&n.data){let s=q(n.data);r.push({fileName:`${n.fileName}${n.fileType}`,content:new Uint8Array(s.buffer),path:o})}else n.isBase64Encoded===!1&&n.data&&r.push({fileName:`${n.fileName}${n.fileType}`,content:n.data,path:o})}),e){let n=Array.isArray(e)?e:[e],o=await Promise.all(n.map(async s=>{try{let a=await fetch(s.filePath);if(!a.ok)return m().warn(`Failed to fetch additional file from URL: ${s.filePath}`),null;let u=await(await a.blob()).arrayBuffer();return{fileName:s.fileName,content:new Uint8Array(u),path:s.fileName}}catch(a){return m().error(`Error fetching additional file from URL: ${s.filePath}`,a),null}}));r.push(...o.filter(s=>s!==null))}return r};async function ie(t,e){let{zipSync:r,strToU8:n}=await import("fflate"),o={};t.forEach(i=>{o[i.path]=typeof i.content=="string"?n(i.content):i.content});let s=r(o,{level:6}),a=new Blob([s],{type:"application/zip"});ue(a,`${e}.zip`)}function ue(t,e){if(typeof document>"u")throw new l("saveFile requires a browser environment with DOM API access.",f.BROWSER_ONLY,{context:{function:"saveFile",requiredAPI:"document"}});let r=document.createElement("a");r.href=URL.createObjectURL(t),r.download=e,r.click(),URL.revokeObjectURL(r.href)}var A=new Map;function H(t,e){A.set(t,e)}H("Rhino.Geometry.Point3d",(t,e)=>{let r=e;return!r||typeof r.X!="number"?null:new t.Point([r.X,r.Y,r.Z])});H("Rhino.Geometry.Line",(t,e)=>{let r=e;return!r||!r.From||!r.To?null:new t.Line([r.From.X,r.From.Y,r.From.Z],[r.To.X,r.To.Y,r.To.Z])});function le(t){if(A.has(t))return A.get(t);for(let[e,r]of A)if(t.startsWith(e))return r}function pe(t){return!t||typeof t!="object"?null:t.data??t.value??null}function Y(t,e,r){let n=le(e);if(n)try{return n(r,t)}catch(o){m().warn(`Failed to decode Rhino type ${e}:`,o)}try{let o=pe(t);if(o)return r.CommonObject.decode(o)}catch(o){return m().warn(`Failed to decode ${e} with CommonObject:`,o),{__decodeError:!0,type:e,raw:t}}return t}var C={STRING:"System.String",INT:"System.Int32",DOUBLE:"System.Double",BOOL:"System.Boolean"},ce="Rhino.Geometry.",fe=["WebDisplay"],me="FileData";function Q(t){return fe.some(e=>t.includes(e))}function X(t){if(typeof t!="string")return t;let e=t.trim();if(!(e.startsWith("{")||e.startsWith("[")||e.startsWith('"')))return t;try{let n=JSON.parse(e);if(typeof n=="string")try{return JSON.parse(n)}catch{return n}return n}catch{return t}}function de(t,e,r){switch(e){case C.STRING:return typeof t!="string"?t:t.replace(/^"(.*)"$/,"$1");case C.INT:return Number.parseInt(t,10);case C.DOUBLE:return Number.parseFloat(t);case C.BOOL:return String(t).toLowerCase()==="true";default:return r&&e.startsWith(ce)?Y(t,e,r):t}}function Z(t,e,r,n){if(typeof t!="string")return t;let o=r?X(t):t;return de(o,e,n)}function E(t,e){for(let r of Object.values(t))if(Array.isArray(r))for(let n of r)e(n)}function ee(t,e=!1,r={}){let{parseValues:n=!0,rhino:o,stringOnly:s=!1}=r,a={};for(let i of t.values)E(i.InnerTree,u=>{if(Q(u.type)||s&&u.type!==C.STRING)return;let c=e?u.id:i.ParamName;if(!c)return;let d=Z(u.data,u.type,n,o);a[c]===void 0?a[c]=d:Array.isArray(a[c])?a[c].push(d):a[c]=[a[c],d]});return{values:a}}function te(t){let e=[];for(let r of t.values)E(r.InnerTree,n=>{if(!n.type.includes(me))return;let o=X(n.data);o&&o.fileName&&o.fileType&&"data"in o&&typeof o.isBase64Encoded=="boolean"&&typeof o.subFolder=="string"&&e.push(o)});return e}function k(t,e,r={}){let{parseValues:n=!0,rhino:o,stringOnly:s=!1}=r,a;if("byName"in e?a=t.values.find(u=>u.ParamName===e.byName):a=t.values.find(u=>{let c=!1;return E(u.InnerTree,d=>{d.id===e.byId&&(c=!0)}),c}),!a)return;let i=[];if(E(a.InnerTree,u=>{if("byId"in e&&u.id!==e.byId||Q(u.type)||s&&u.type!==C.STRING)return;let c=Z(u.data,u.type,n,o);i.push(c)}),i.length!==0)return i.length===1?i[0]:i}var I=class{constructor(e,r=!1){this.response=e;this.debug=r}getValues(e=!1,r={}){return ee(this.response,e,r)}getValueByParamName(e,r){return k(this.response,{byName:e},r)}getValueByParamId(e,r){return k(this.response,{byId:e},r)}async extractMeshesFromResponse(e){let r={debug:this.debug,...e},n;try{({getThreeMeshesFromComputeResponse:n}=await import("./visualization.js"))}catch(o){throw new l("Failed to load three.js visualization module. Ensure three.js is installed as a peer dependency.",f.INVALID_STATE,{context:{originalError:o instanceof Error?o.message:String(o)}})}return n(this.response,r)}getFileData(){return te(this.response)}getAndDownloadFiles(e,r){let n=this.getFileData();P(n,e,r)}};function V(t,e){e||typeof window<"u"&&m().warn(`Warning: ${t} is running on the client side. For better performance and security, consider running this on the server side.`)}async function x(t,e,r){r.debug&&V("solveGrasshopperDefinition",r.suppressClientSideWarning);let n=B(e,t);he(n,r);let o=await N("grasshopper",n,r);if("pointer"in o){let{pointer:s,...a}=o;return a}return o}function B(t,e){let r={algo:null,pointer:null,values:e};return t instanceof Uint8Array?r.algo=_(t):/^https?:\/\//i.test(t)?r.pointer=t:W(t)?r.algo=t:r.algo=z(t),r}function he(t,e){e.cachesolve!=null&&(t.cachesolve=e.cachesolve),e.modelunits!=null&&(t.modelunits=e.modelunits),e.angletolerance!=null&&(t.angletolerance=e.angletolerance),e.absolutetolerance!=null&&(t.absolutetolerance=e.absolutetolerance),e.dataversion!=null&&(t.dataversion=e.dataversion)}function re(t){if(typeof t.default!="object"||t.default===null)return;if(!("innerTree"in t.default)){m().warn("Unexpected structure in input.default:",t.default),t.default=null;return}let e=t.default.innerTree;if(Object.keys(e).length===0){t.default=void 0;return}if(t.treeAccess||t.atMost&&t.atMost>1){let n={};for(let[o,s]of Object.entries(e))n[o]=s.map(a=>{if(typeof a.data=="string"){if(a.type==="System.Double"||a.type==="System.Int32"){let i=Number(a.data);return Number.isNaN(i)?a.data:i}if(a.type==="System.Boolean")return a.data.toLowerCase()==="true";if(a.type.startsWith("Rhino.Geometry")||a.type==="System.String")try{return JSON.parse(a.data)}catch{return a.data}}return a.data});t.default=n;return}let r=[];for(let n of Object.values(e))Array.isArray(n)&&n.forEach(o=>{o&&typeof o=="object"&&"data"in o&&r.push(o.data)});r.length===0?t.default=void 0:r.length===1?t.default=r[0]:t.default=r}function O(t,e){let{transform:r,setUndefinedOnEmpty:n=!0}=e;if(!(t.default===void 0||t.default===null))if(Array.isArray(t.default)){let o=t.default.map(r).filter(s=>s!==null);t.default=o.length>0?o:void 0}else{let o=r(t.default);o!==null?t.default=o:n&&(t.default=void 0)}}function ye(){return t=>{if(typeof t=="number")return t;if(typeof t=="string"){let e=Number(t.trim());return Number.isNaN(e)?null:e}return null}}function ge(){return t=>{if(typeof t=="boolean")return t;if(typeof t=="string"){let e=t.toLowerCase();if(e==="true")return!0;if(e==="false")return!1;throw new Error(`Invalid boolean string: "${t}"`)}return null}}function be(){return t=>typeof t=="string"?t.startsWith('"')&&t.endsWith('"')||t.startsWith('"')?t.slice(1,-1):t:null}function Te(){return t=>{if(typeof t=="string"){let e=t.trim();return e.startsWith('"')&&e.endsWith('"')&&(e=e.slice(1,-1).trim()),e}return null}}function xe(t){O(t,{transform:Te(),setUndefinedOnEmpty:!1})}function Se(t="unknown"){return e=>{if(typeof e=="object"&&e!==null)return e;if(typeof e=="string"&&e.trim()!=="")try{let r=JSON.parse(e);return typeof r=="object"&&r!==null?r:(m().warn(`Parsed value for input ${t} is not an object`),null)}catch(r){return m().warn(`Failed to parse object value "${e}" for input ${t}`,r),null}return null}}function ne(t,e,r){let n=Number(t.toFixed(e));return Math.abs(t-n)<r?n:t}function Ce(t,e=1e-8){if(!Number.isFinite(t)||t===0)return .1;let r=Math.abs(t);if(r>=1){let y=String(t).split(".")[1];if(y&&y.length>0){let g=Math.min(y.length,12),b=Math.pow(10,-g),U=Number(b.toFixed(g));return Math.abs(U-b)<e?U:b}return 1}let n=String(t),o=n.toLowerCase().match(/e(-?\d+)/);if(o){let G=Number(o[1]);if(G<0||n.toLowerCase().includes("e-")){let y=Math.abs(G),g=Math.pow(10,-y),b=Number(g.toFixed(y));return Math.abs(b-g)<e?b:g}return .1}let s=12,i=r.toFixed(s).replace(/0+$/,""),u=Math.min((i.split(".")[1]||"").length,s);if(u===0)return .1;let c=Math.pow(10,-u),d=Number(c.toFixed(u));return Math.abs(d-c)<e?d:c}function oe(t,e=1e-8){let r=t.paramType==="Integer";if(O(t,{transform:ye()}),r){Array.isArray(t.default)?t.default=t.default.map(s=>typeof s=="number"?Math.round(s):s):typeof t.default=="number"&&(t.default=Math.round(t.default)),t.stepSize=1;return}let n=Array.isArray(t.default)?t.default[0]:t.default,o;if(typeof n=="number"&&Number.isFinite(n)&&n!==0?o=n:typeof t.minimum=="number"&&Number.isFinite(t.minimum)&&t.minimum!==0?o=t.minimum:typeof t.maximum=="number"&&Number.isFinite(t.maximum)&&t.maximum!==0&&(o=t.maximum),o!==void 0?t.stepSize=Ce(o,e):t.stepSize=.1,typeof t.stepSize=="number"){let s=0,a=String(t.stepSize),i=a.toLowerCase().match(/e(-?\d+)/);if(i?s=Math.abs(Number(i[1])):s=a.split(".")[1]?.length??0,s===0&&typeof n=="number"&&n!==0&&Math.abs(n)<1){let u=Math.ceil(-Math.log10(Math.abs(n)));Number.isFinite(u)&&u>0&&(s=u)}s=Math.min(Math.max(s,0),12),Array.isArray(t.default)?t.default=t.default.map(u=>typeof u=="number"?ne(u,s,e):u):typeof t.default=="number"&&(t.default=ne(t.default,s,e))}}function Ie(t){try{O(t,{transform:ge(),setUndefinedOnEmpty:!1})}catch(e){throw e instanceof Error?new l(e.message):e}}function De(t){O(t,{transform:be(),setUndefinedOnEmpty:!1})}function se(t){O(t,{transform:Se(t.nickname||"unnamed"),setUndefinedOnEmpty:!0})}function ve(t){if(!t.values||typeof t.values!="object"||Object.keys(t.values).length===0)throw l.missingValues(t.nickname||"unnamed","ValueList");if(t.default!==void 0&&t.default!==null){let e=String(t.default).toLowerCase();Object.keys(t.values).some(n=>n.toLowerCase()===e)||m().warn(`ValueList input "${t.nickname||"unnamed"}" default value "${t.default}" is not in available values`)}}var ae={Number:oe,Integer:oe,Boolean:Ie,Text:De,ValueList:ve,Geometry:se,File:se,Color:xe};function Re(t,e){let r=(t.atMost??1)>1;switch(t.paramType){case"Number":case"Integer":return{...e,paramType:t.paramType,minimum:t.minimum,maximum:t.maximum,atLeast:t.atLeast,atMost:t.atMost,default:r?[0]:0};case"Boolean":return{...e,paramType:"Boolean",default:r?[!1]:!1};case"Text":return{...e,paramType:"Text",default:r?[""]:""};case"ValueList":return{...e,paramType:"ValueList",values:t.values??{},default:r?[t.default]:t.default};case"File":return{...e,paramType:"File",default:r?[null]:null};case"Color":return{...e,paramType:"Color",default:r?["0, 0, 0"]:"0, 0, 0"};default:return{...e,paramType:"Geometry",default:r?[null]:null}}}function F(t){let e={description:t.description,name:t.name,nickname:t.nickname,treeAccess:t.treeAccess,groupName:t.groupName??"",id:t.id};try{re(t);let r=ae[t.paramType];if(!r)throw l.unknownParamType(t.paramType,t.name);switch(r(t),t.paramType){case"Number":case"Integer":return{...e,paramType:t.paramType,minimum:t.minimum,maximum:t.maximum,atLeast:t.atLeast,atMost:t.atMost,stepSize:t.stepSize,default:t.default};case"Boolean":return{...e,paramType:"Boolean",default:t.default};case"Text":return{...e,paramType:"Text",default:t.default};case"ValueList":return{...e,paramType:"ValueList",values:t.values,default:t.default};case"Geometry":return{...e,paramType:t.paramType,default:t.default};case"File":return{...e,paramType:t.paramType,acceptedFormats:t.acceptedFormats,default:t.default};case"Color":return{...e,paramType:"Color",default:t.default};default:throw l.unknownParamType(t.paramType,t.name)}}catch(r){if(r instanceof l)return m().error(`Validation error for input ${t.name||"unknown"}:`,r.message),Re(t,e);throw new l(r instanceof Error?r.message:String(r),"VALIDATION_ERROR",{context:{paramName:t.name,paramType:t.paramType},originalError:r instanceof Error?r:new Error(String(r))})}}function D(t){return t.map(e=>F(e))}async function S(t,e){let r=B(t,[]),n={};if(r.algo&&(n.algo=r.algo),r.pointer&&(n.pointer=r.pointer),!n.algo&&!n.pointer)throw new l("Definition must resolve to either a URL pointer or base64 algo",f.INVALID_INPUT,{context:{definition:t}});let o=await N("io",n,e);if(!o||typeof o!="object")throw new l("Invalid IO response structure",f.INVALID_INPUT,{context:{response:o,definition:t}});let s=$(o,{deep:!0});return{inputs:s.inputs,outputs:s.outputs}}async function R(t,e){V("fetchParsedDefinitionIO",e.suppressClientSideWarning);let{inputs:r,outputs:n}=await S(t,e);return{inputs:D(r),outputs:n}}var w=class t{constructor(e){p(this,"innerTree");p(this,"paramName");this.paramName=e,this.innerTree={}}append(e,r){let n=t.formatPathString(e);this.innerTree[n]||(this.innerTree[n]=[]);let o=r.map(s=>({data:t.serializeValue(s)}));return this.innerTree[n].push(...o),this}appendSingle(e,r){return this.append(e,[r])}fromDataTreeDefault(e){this.innerTree={};for(let[r,n]of Object.entries(e)){if(!Array.isArray(n))continue;let o=t.parsePathString(r);this.append(o,n)}return this}appendFlat(e){let r=Array.isArray(e)?e:[e];return this.append([0],r)}flatten(){let e=[];for(let r of Object.values(this.innerTree))if(Array.isArray(r))for(let n of r)e.push(t.deserializeValue(n.data));return e}getPaths(){return Object.keys(this.innerTree)}getPath(e){let r=t.formatPathString(e),n=this.innerTree[r];if(n)return n.map(o=>t.deserializeValue(o.data))}toComputeFormat(){return{ParamName:this.paramName,InnerTree:this.innerTree}}getInnerTree(){return this.innerTree}getParamName(){return this.paramName}static fromInputParams(e){return e.filter(r=>t.hasValidValue(r.default)).map(r=>{let n=new t(r.nickname||"unnamed"),o=r.default;if(r.treeAccess&&t.isDataTreeStructure(o))n.fromDataTreeDefault(o),t.isNumericInput(r)&&n.applyNumericConstraints(r.minimum,r.maximum,r.nickname||"unnamed");else{let s=Array.isArray(o)?o:[o],a=t.processValues(s,r);n.appendFlat(a)}return n.toComputeFormat()})}static fromInputParam(e){return t.hasValidValue(e.default)?t.fromInputParams([e])[0]:void 0}static replaceTreeValue(e,r,n){if(e.length>0&&e[0]instanceof t){let s=e,a=s.findIndex(u=>u.getParamName()===r),i=new t(r);return typeof n=="object"&&n!==null&&!Array.isArray(n)&&t.isDataTreeStructure(n)?i.fromDataTreeDefault(n):(Array.isArray(n),i.appendFlat(n)),a!==-1?s[a]=i:s.push(i),s}else{let s=e,a=s.findIndex(c=>c.ParamName===r),i=new t(r);typeof n=="object"&&n!==null&&!Array.isArray(n)&&t.isDataTreeStructure(n)?i.fromDataTreeDefault(n):(Array.isArray(n),i.appendFlat(n));let u=i.toComputeFormat();return a!==-1?s[a]=u:s.push(u),s}}static getTreeValue(e,r){if(e.length>0&&e[0]instanceof t){let s=e.find(i=>i.getParamName()===r);if(!s)return null;let a=s.flatten();return a.length===0?null:a.length===1?a[0]:a}else{let s=e.find(c=>c.ParamName===r);if(!s)return null;let a=s.InnerTree;if(!a)return null;let i=Object.keys(a)[0];if(!i)return null;let u=a[i];if(Array.isArray(u)){if(u.length===1){let c=u[0]?.data;return c!==void 0?t.deserializeValue(c):null}return u.map(c=>c?.data!==void 0?t.deserializeValue(c.data):null).filter(c=>c!==null)}return u?.data!==void 0?t.deserializeValue(u.data):u}}static parsePathString(e){let r=e.match(/^\{([\d;]*)\}$/);return r?r[1]===""?[]:r[1].split(";").map(Number):(m().warn(`Invalid TreeBuilder path format: ${e}, using [0]`),[0])}static formatPathString(e){return`{${e.join(";")}}`}applyNumericConstraints(e,r,n){for(let o of Object.values(this.innerTree))if(Array.isArray(o))for(let s of o){let a=t.deserializeValue(s.data);if(typeof a=="number"){let i=t.clampValue(a,e,r,n);s.data=t.serializeValue(i)}}}static serializeValue(e){return typeof e=="boolean"||typeof e=="number"||typeof e=="string"?e:typeof e=="object"&&e!==null?JSON.stringify(e):String(e)}static deserializeValue(e){if(typeof e=="boolean"||typeof e=="number"||typeof e!="string")return e;if(e.startsWith("{")||e.startsWith("["))try{return JSON.parse(e)}catch{return e}return isNaN(Number(e))?e==="true"?!0:e==="false"?!1:e:Number(e)}static hasValidValue(e){return e==null?!1:typeof e=="string"?!0:!(Array.isArray(e)&&e.length===0||typeof e=="object"&&!Array.isArray(e)&&Object.keys(e).length===0)}static isDataTreeStructure(e){return typeof e!="object"||e===null||Array.isArray(e)?!1:Object.entries(e).every(([r,n])=>typeof r=="string"&&/^\{[\d;]+\}$/.test(r)&&Array.isArray(n))}static isNumericInput(e){return e.paramType==="Number"||e.paramType==="Integer"}static processValues(e,r){return e.map(n=>t.isNumericInput(r)&&typeof n=="number"?t.clampValue(n,r.minimum,r.maximum,r.nickname||"unnamed"):n).filter(n=>n!=null)}static clampValue(e,r,n,o){let s=e;return r!=null&&s<r&&(m().warn(`${o}: ${e} below min ${r}, clamping`),s=r),n!=null&&s>n&&(m().warn(`${o}: ${e} above max ${n}, clamping`),s=n),s}};export{v as a,h as b,T as c,M as d,P as e,I as f,x as g,F as h,D as i,S as j,R as k,w as l};
|
|
2
|
+
//# sourceMappingURL=chunk-BKGFHI2J.js.map
|