@selvajs/compute 4.3.0-beta.1 → 5.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,574 +0,0 @@
1
- //#region src/core/errors.d.ts
2
- declare const ErrorCodes: {
3
- readonly NETWORK_ERROR: "NETWORK_ERROR";
4
- readonly AUTH_ERROR: "AUTH_ERROR";
5
- readonly VALIDATION_ERROR: "VALIDATION_ERROR";
6
- readonly COMPUTATION_ERROR: "COMPUTATION_ERROR";
7
- readonly TIMEOUT_ERROR: "TIMEOUT_ERROR";
8
- readonly CORS_ERROR: "CORS_ERROR";
9
- /** HTTP 404 — the endpoint path does not exist on the server (usually a typo'd route or wrong server). Never retried. */
10
- readonly NOT_FOUND: "NOT_FOUND";
11
- /** HTTP 429 — the server is rate-limiting requests. Retried (honoring `Retry-After`) once a retry policy sets `attempts > 0`; opt out via `RetryPolicy.retryOn429`. */
12
- readonly RATE_LIMIT: "RATE_LIMIT";
13
- /**
14
- * The server answered 2xx but the body is deterministically not JSON (the
15
- * response declared a non-JSON `Content-Type`, e.g. an HTML captive-portal or
16
- * proxy login page, or a misconfigured endpoint). Never retried — refetching
17
- * returns the same page.
18
- */
19
- readonly INVALID_RESPONSE: "INVALID_RESPONSE";
20
- readonly UNKNOWN_ERROR: "UNKNOWN_ERROR";
21
- readonly INVALID_STATE: "INVALID_STATE";
22
- readonly INVALID_INPUT: "INVALID_INPUT";
23
- readonly INVALID_CONFIG: "INVALID_CONFIG";
24
- readonly BROWSER_ONLY: "BROWSER_ONLY";
25
- /** The runtime lacks a required capability (e.g. no base64 codec — neither `Buffer` nor `atob`/`btoa`). */
26
- readonly ENVIRONMENT_ERROR: "ENVIRONMENT_ERROR";
27
- readonly ENCODING_ERROR: "ENCODING_ERROR";
28
- /** An input's `default` had a shape the normalizer didn't recognize (no innerTree key). */
29
- readonly MALFORMED_DEFAULT: "MALFORMED_DEFAULT";
30
- /** Scheduler latest-wins: this call was replaced by a newer one. */
31
- readonly SUPERSEDED: "SUPERSEDED";
32
- /** Scheduler / caller-supplied AbortSignal: this call was aborted. */
33
- readonly ABORTED: "ABORTED";
34
- /**
35
- * Scheduler backpressure: the queue was already at `maxQueueDepth` when this
36
- * call arrived, so it was rejected immediately rather than queued. Retryable — the
37
- * caller (or an HTTP layer, as 503 + Retry-After) should back off and retry.
38
- * The error `context` carries `{ queueDepth, maxQueueDepth }`.
39
- */
40
- readonly QUEUE_FULL: "QUEUE_FULL";
41
- /**
42
- * Scheduler backpressure: this call sat queued longer than `queueWaitMs`
43
- * without starting, so it was rejected before wasting compute on a stale request.
44
- * Retryable. The error `context` carries `{ waitedMs, queueWaitMs }`.
45
- */
46
- readonly QUEUE_TIMEOUT: "QUEUE_TIMEOUT";
47
- /**
48
- * A solve referenced a definition by `pointer` (server-side cache key), but the
49
- * server no longer holds that definition (evicted / GC'd / a different child in
50
- * the pool / server restarted). The caller should retry with the full
51
- * definition. Surfaced when the server tags its error body with
52
- * `code: "definition_not_cached"`, so it survives production message-scrubbing.
53
- */
54
- readonly DEFINITION_NOT_CACHED: "DEFINITION_NOT_CACHED";
55
- };
56
- type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];
57
- /**
58
- * Simplified error for Rhino Compute operations
59
- *
60
- * @public Use this for error handling with error codes and context.
61
- */
62
- declare class ComputeError extends Error {
63
- readonly code: ErrorCode;
64
- readonly statusCode?: number;
65
- readonly context?: Record<string, unknown>;
66
- readonly originalError?: Error;
67
- constructor(message: string, code?: ErrorCode, options?: {
68
- statusCode?: number;
69
- context?: Record<string, unknown>;
70
- originalError?: Error;
71
- });
72
- /**
73
- * Create an error for missing/empty values
74
- */
75
- static missingValues(inputName: string, expectedType?: string, context?: Record<string, unknown>): ComputeError;
76
- /**
77
- * Create an error for unknown parameter type
78
- */
79
- static unknownParamType(paramType: string, paramName?: string, context?: Record<string, unknown>): ComputeError;
80
- }
81
- //#endregion
82
- //#region src/core/types.d.ts
83
- /**
84
- * Backend wire code (the JSON error body's `code` field) → one of our
85
- * {@link ErrorCodes}. An unlisted code falls back to the status-based mapping.
86
- */
87
- type ServerErrorCodeMap = Record<string, ErrorCode>;
88
- interface RetryPolicy {
89
- /** Maximum number of retry attempts after the initial request (default: 0). */
90
- attempts?: number;
91
- /** Base delay in milliseconds for exponential backoff (default: 500). */
92
- baseDelayMs?: number;
93
- /** Upper bound for backoff delay (default: 30_000). */
94
- maxDelayMs?: number;
95
- /** Whether to retry on 429 responses (default: true — honors Retry-After). */
96
- retryOn429?: boolean;
97
- }
98
- interface ComputeConfig {
99
- serverUrl: string;
100
- /** Optional API key for authenticating with the server. Sent as {@link apiKeyHeader}. */
101
- apiKey?: string;
102
- /**
103
- * Header name carrying `apiKey`. Defaults to `'RhinoComputeKey'` — the name
104
- * rhino.compute expects. A different backend sets its own.
105
- */
106
- apiKeyHeader?: string;
107
- /** Optional Bearer token for authentication (e.g., when behind a proxy or API gateway) */
108
- authToken?: string;
109
- headers?: Record<string, string>;
110
- /** Enable debug logging to the console */
111
- debug?: boolean;
112
- /** Suppress browser security warnings in the console */
113
- suppressBrowserWarning?: boolean;
114
- timeoutMs?: number;
115
- /**
116
- * Retry policy for transient errors. Default: no retries.
117
- */
118
- retry?: RetryPolicy;
119
- /**
120
- * Optional caller-supplied AbortSignal. Composes with the internal timeout —
121
- * whichever fires first wins. Lets callers cancel in-flight requests
122
- * (e.g. on component unmount or when superseding a stale solve).
123
- */
124
- signal?: AbortSignal;
125
- /**
126
- * Machine-readable error codes this backend tags onto its error bodies, mapped
127
- * to our {@link ErrorCodes}. A code here outranks the status-based mapping.
128
- * Core knows no backend's codes — the client supplies its own table.
129
- */
130
- serverErrorCodes?: ServerErrorCodeMap;
131
- onServerTiming?: (timing: ServerTiming, requestId: string) => void;
132
- }
133
- /** The config fields that decide how a request authenticates. */
134
- type ComputeAuth = Pick<ComputeConfig, 'apiKey' | 'apiKeyHeader' | 'authToken' | 'headers'>;
135
- interface ServerTiming {
136
- decode?: number;
137
- solve?: number;
138
- encode?: number;
139
- raw: string;
140
- }
141
- //#endregion
142
- //#region src/core/compute-fetch/compute-fetch.d.ts
143
- /**
144
- * Generic Rhino Compute fetch function.
145
- * Sends a POST request to any Compute endpoint with pre-prepared arguments.
146
- *
147
- * Use this for advanced, low-level control over compute requests. For most use cases, prefer higher-level APIs.
148
- *
149
- * The transport is response-type-agnostic: it does not know which response a
150
- * given endpoint returns. Callers supply the response type via `R` (defaulting
151
- * to `unknown`, which forces an explicit narrowing before use).
152
- *
153
- * Timeout semantics: `config.timeoutMs` is a PER-ATTEMPT timeout, re-armed for
154
- * every retry. With `retry: { attempts: N }` the worst-case wall clock is
155
- * `(N + 1) × timeoutMs` plus backoff sleeps (and a server `Retry-After` can
156
- * stretch a single sleep up to 60s). For a hard overall deadline, pass
157
- * `config.signal` (e.g. `AbortSignal.timeout(totalMs)`) — a caller abort wins
158
- * immediately, including during backoff.
159
- *
160
- * Retry caveats: requests are POSTs, so a retry after a mid-flight connection
161
- * loss may re-execute a request the server already ran (see {@link RetryPolicy}).
162
- * A 2xx response that declares a non-JSON `Content-Type` (captive portal,
163
- * proxy login page) fails immediately with `INVALID_RESPONSE` and is never
164
- * retried; a body that fails to parse under a JSON content-type is treated as
165
- * a truncated stream and is retried.
166
- *
167
- * @typeParam R - The expected response shape. The caller names it at the call site.
168
- * @param endpoint - The Compute API endpoint (e.g., 'grasshopper', 'io', 'mesh').
169
- * @param args - Pre-prepared arguments for the request body.
170
- * @param config - Compute configuration (server URL, API key, timeout, debug, retry, signal).
171
- * @returns The parsed JSON response from the server, typed as `R`.
172
- *
173
- * @example
174
- * // Basic usage for the Grasshopper endpoint:
175
- * const response = await fetchCompute(
176
- * 'grasshopper',
177
- * { ... },
178
- * {
179
- * serverUrl: 'https://my-server.com',
180
- * debug: true,
181
- * timeoutMs: 30_000,
182
- * retry: { attempts: 2 },
183
- * signal: controller.signal,
184
- * }
185
- * );
186
- */
187
- declare function fetchCompute<R = unknown>(endpoint: string, args: Record<string, any>, config: ComputeConfig): Promise<R>;
188
- //#endregion
189
- //#region src/core/compute-fetch/wire-size.d.ts
190
- /**
191
- * Side-channel carrying a response's wire size (its JSON text length) alongside
192
- * the parsed object, so downstream caches can budget by bytes without
193
- * re-serializing (audit C2/C3 — the response tree can be hundreds of MB, and
194
- * every extra `JSON.stringify` pass over it is a real cost).
195
- *
196
- * A `WeakMap` rather than a property on the response: the response type is the
197
- * server's schema, and an extra enumerable field would leak into every
198
- * `JSON.stringify(response)` on the way back out to clients. Derived copies
199
- * (e.g. the `algo`-stripped shallow copy in `runSolve`) must re-register — the
200
- * hint follows object identity, not content.
201
- *
202
- * The size is `text.length` (UTF-16 code units), not strict UTF-8 bytes —
203
- * compute responses are ASCII-dominated JSON (base64 + numerals), so the two
204
- * are interchangeable for budgeting purposes and `.length` is free.
205
- */
206
- /** Record `response`'s wire size. No-op for non-object/null values. */
207
- declare function setResponseWireSize(response: unknown, size: number): void;
208
- /** The wire size recorded for `response`, or undefined when never registered. */
209
- declare function getResponseWireSize(response: unknown): number | undefined;
210
- //#endregion
211
- //#region src/core/utils/logger.d.ts
212
- /**
213
- * Logger interface for structured logging
214
- *
215
- * @public Implement this interface to provide custom logging behavior.
216
- */
217
- interface Logger {
218
- debug(message: string, ...args: unknown[]): void;
219
- info(message: string, ...args: unknown[]): void;
220
- warn(message: string, ...args: unknown[]): void;
221
- error(message: string, ...args: unknown[]): void;
222
- }
223
- /**
224
- * Get the current logger instance
225
- *
226
- * @returns The current logger instance
227
- */
228
- declare function getLogger(): Logger;
229
- /**
230
- * Set a custom logger instance
231
- *
232
- * @public Use this to configure custom logging behavior.
233
- *
234
- * @param logger - Custom logger implementation or null to disable logging
235
- * @throws {ComputeError} `INVALID_CONFIG` if the logger is missing any of
236
- * the four required methods — failing here beats a confusing
237
- * "getLogger().debug is not a function" at some later, unrelated call site.
238
- *
239
- * @example
240
- * ```typescript
241
- * import { setLogger } from '@selvajs/compute/core';
242
- *
243
- * // Enable console logging
244
- * setLogger(console);
245
- *
246
- * // Use a custom logger
247
- * setLogger({
248
- * debug: (msg, ...args) => myLogger.debug(msg, ...args),
249
- * info: (msg, ...args) => myLogger.info(msg, ...args),
250
- * warn: (msg, ...args) => myLogger.warn(msg, ...args),
251
- * error: (msg, ...args) => myLogger.error(msg, ...args)
252
- * });
253
- *
254
- * // Disable logging
255
- * setLogger(null);
256
- * ```
257
- */
258
- declare function setLogger(logger: Logger | Console | null): void;
259
- /**
260
- * Enable debug logging to console
261
- *
262
- * @public Convenience method to enable console logging.
263
- *
264
- * @example
265
- * ```typescript
266
- * import { enableDebugLogging } from '@selvajs/compute/core';
267
- *
268
- * enableDebugLogging();
269
- * ```
270
- */
271
- declare function enableDebugLogging(): void;
272
- //#endregion
273
- //#region src/core/utils/read-field.d.ts
274
- /**
275
- * Case-insensitive single-key reader for wire payloads.
276
- *
277
- * The Rhino Compute family serializes the same logical field with different
278
- * casing depending on the server branch:
279
- *
280
- * - mcneel 8.x / 9.x: the IO schema is PascalCase (`ParamType`, `Default`,
281
- * `InnerTree`, …) because those C# classes carry no `[JsonProperty]`.
282
- * - VektorNode Compute8: the IO schema is camelCase (`paramType`, `default`,
283
- * …) because the fork added `[JsonProperty("camelCase")]`, BUT the nested
284
- * `default` DataTree wrapper stays PascalCase (`ParamName` / `InnerTree`)
285
- * since `Resthopper.IO.DataTree` is an external type the fork can't attribute.
286
- *
287
- * So a single response can mix casings, and which casing a given field uses
288
- * depends on the server branch. Rather than deep-camelCasing the whole payload
289
- * (the old `camelcaseKeys` approach — which corrupted user-authored value-list
290
- * label keys and item `data` JSON), read the specific fields we care about
291
- * case-insensitively and leave everything else verbatim.
292
- *
293
- * Prefers an exact-case match when present, then falls back to the first
294
- * case-insensitive match. Returns `undefined` when no key matches.
295
- *
296
- * @param obj - The source object (any non-object input yields `undefined`).
297
- * @param name - The logical field name, in any casing.
298
- */
299
- declare function readField<T = unknown>(obj: unknown, name: string): T | undefined;
300
- /**
301
- * True when `obj` has a key matching `name` (case-insensitively). Distinguishes
302
- * "field present but value is null/undefined" from "field absent" — needed where
303
- * presence itself carries meaning (e.g. an `innerTree` that exists but is empty).
304
- */
305
- declare function hasField(obj: unknown, name: string): boolean;
306
- //#endregion
307
- //#region src/core/utils/encoding.d.ts
308
- /**
309
- * Decodes a base64 string to binary data (Uint8Array).
310
- * Normalizes and validates input per WHATWG forgiving-base64 so both runtimes fail consistently.
311
- *
312
- * @param base64File - Base64 encoded string
313
- * @returns Decoded binary data as Uint8Array
314
- * @throws {ComputeError} `ENCODING_ERROR` if invalid, or `ENVIRONMENT_ERROR` if the runtime has no decoder
315
- */
316
- declare function decodeBase64ToBinary(base64File: string): Uint8Array;
317
- //#endregion
318
- //#region src/core/definition-ref.d.ts
319
- /**
320
- * By-reference definition form for solves.
321
- *
322
- * Lets a caller that already knows a definition's identity (e.g. a stored
323
- * version's UUID) schedule solves without materializing the multi-MB bytes:
324
- * cache keys and the server-pointer map are derived from `key` alone, and
325
- * `load()` is only called when an upload is genuinely unavoidable (first solve
326
- * of a definition, or a server-side pointer miss).
327
- */
328
- /**
329
- * A definition identified by a stable key, with bytes materialized on demand.
330
- *
331
- * **Immutability contract — read this before constructing one.** `key` must
332
- * identify IMMUTABLE bytes: every `load()` for a given `key` must return the
333
- * same content, forever. All caching (the scheduler's result cache, the
334
- * server-pointer map, and any durable cache built on these keys) trusts the
335
- * key as the definition's identity WITHOUT looking at the bytes. If two
336
- * different byte contents ever share a key, cached solves from one are served
337
- * for the other — silent cache poisoning with no diagnostic. Use an identity
338
- * that can never be reused for different content (e.g. a version UUID), never
339
- * a mutable name or path.
340
- */
341
- interface DefinitionRef {
342
- /**
343
- * Identity of immutable bytes (e.g. a version UUID). Two different byte
344
- * contents must never share a key — cache poisoning otherwise.
345
- */
346
- key: string;
347
- /**
348
- * Materialize the bytes. Called ONLY when an upload is unavoidable — inside
349
- * the solve execution, so it counts toward the solve's abort semantics.
350
- */
351
- load: () => Promise<Uint8Array>;
352
- }
353
- /**
354
- * Every definition form accepted by the solve entry points:
355
- * - a URL, base64 string, or plain string (content-hashed for caching)
356
- * - raw `.gh` bytes (content-hashed)
357
- * - a {@link DefinitionRef} (identity-keyed; bytes loaded lazily)
358
- */
359
- type SolveDefinition = string | Uint8Array | DefinitionRef;
360
- /** Narrow a {@link SolveDefinition} to the by-reference form. */
361
- declare function isDefinitionRef(definition: SolveDefinition): definition is DefinitionRef;
362
- //#endregion
363
- //#region src/core/server/validate-server-url.d.ts
364
- /**
365
- * The public McNeel endpoint's host — the default blocked host; users must point at
366
- * their own server. Compared against the parsed hostname lowercased and with any
367
- * trailing dot stripped, so the FQDN form (`compute.rhino3d.com.`) can't bypass it.
368
- * Known limitation: the endpoint's raw IP is not blocked — it sits behind a load
369
- * balancer with no single stable, verifiable address to pin.
370
- */
371
- declare const DEFAULT_BLOCKED_HOST = "compute.rhino3d.com";
372
- interface ValidateServerUrlOptions {
373
- /**
374
- * Hostnames rejected as a `serverUrl` — a backend's shared public endpoint,
375
- * which callers must not point at. Defaults to `[DEFAULT_BLOCKED_HOST]`; pass
376
- * `[]` to block nothing. Compared lowercased with any trailing dot stripped.
377
- */
378
- blockedHosts?: readonly string[];
379
- }
380
- /**
381
- * Validate and normalize a compute `serverUrl`.
382
- *
383
- * This is the single source of truth for "is this a usable server URL?" — both
384
- * `GrasshopperClient` (via `normalizeComputeConfig`) and the standalone-exported
385
- * `ComputeServerStats` constructor delegate here, so a given URL is accepted or
386
- * rejected identically no matter which entry point a caller uses.
387
- *
388
- * Rules (all enforced, on the *trimmed* input — the trimmed form is what's
389
- * returned, so no stray whitespace survives into later `fetch` calls):
390
- * - non-empty (after trim)
391
- * - `http://` or `https://` scheme (case-insensitive, per RFC 3986)
392
- * - parseable by `new URL()`
393
- * - no embedded credentials (`http://user:pass@host`) — `fetch`/`new Request`
394
- * reject credentialed URLs at runtime, so they must fail here instead
395
- * - no query string or fragment — endpoint paths are appended to this URL
396
- * (`${serverUrl}/version`), which a `?…` or `#…` suffix would corrupt
397
- * - not a blocked host (by default the public McNeel endpoint) — compared by
398
- * parsed hostname (lowercased, trailing dot stripped), so scheme, casing, port,
399
- * path, trailing-slash, or FQDN-dot variants can't slip past the block
400
- *
401
- * @param raw - The candidate server URL.
402
- * @param options - Override the blocked-host list for a non-Rhino backend.
403
- * @returns The trimmed, normalized URL with any trailing slashes removed.
404
- * @throws {ComputeError} `INVALID_CONFIG` if any rule fails.
405
- */
406
- declare function validateServerUrl(raw: string, options?: ValidateServerUrlOptions): string;
407
- //#endregion
408
- //#region src/core/server/classify-probe-failure.d.ts
409
- /**
410
- * Why a liveness probe failed, and whether waiting can change the answer.
411
- *
412
- * `retryable` is the whole point: a powered-off VM and a booting one both read
413
- * as "offline" from a single probe, but only one of them will ever come up. A
414
- * caller that can't tell them apart has to assume the optimistic case and burn
415
- * its full retry window on a machine that is simply off.
416
- */
417
- type ProbeVerdict =
418
- /** Nothing is listening on the port — the host answered, and said no. */
419
- 'refused' |
420
- /** The hostname does not resolve. */
421
- 'dns' |
422
- /** No answer within the timeout: a booting host swallows packets rather than refusing them. */
423
- 'timeout' |
424
- /** The server answered, but rejected the probe's credentials (401/403). */
425
- 'unauthorized' |
426
- /** The server answered with a non-2xx that isn't an auth rejection. */
427
- 'http_error' |
428
- /** Connection failed in a way we can't attribute. */
429
- 'unknown';
430
- interface ProbeFailure {
431
- verdict: ProbeVerdict;
432
- /** Whether retrying the same probe could plausibly succeed later. */
433
- retryable: boolean;
434
- /** Operator-facing sentence. Never includes the API key. */
435
- summary: string;
436
- }
437
- /**
438
- * Classify a {@link ComputeServerStats.probeServer} result.
439
- *
440
- * A DNS failure is treated as retryable even though a missing record won't fix
441
- * itself: `EAI_AGAIN` is a *timeout* talking to the resolver, and the two are
442
- * not distinguishable from the error alone. Retrying a genuinely-wrong hostname
443
- * costs one window; giving up on a resolver hiccup breaks a server that is fine.
444
- *
445
- * @param probe - The `{ online, status?, error? }` a probe returned. An `online`
446
- * probe is not a failure and yields `null`.
447
- */
448
- declare function classifyProbeFailure(probe: {
449
- online: boolean;
450
- status?: number;
451
- error?: string;
452
- }): ProbeFailure | null;
453
- //#endregion
454
- //#region src/core/files/types.d.ts
455
- /**
456
- * Raw file data from Grasshopper/Rhino Compute response, with metadata for processing.
457
- * Files are typically combined with additional files and packaged into a ZIP archive.
458
- * @see {@link extractFilesFromComputeResponse} for extraction from compute responses
459
- */
460
- type FileData = {
461
- /** Base filename without extension (e.g., "model") */
462
- fileName: string;
463
- /** File content, base64-encoded or plain string depending on isBase64Encoded flag */
464
- data: string;
465
- /** File extension including the dot (e.g., ".3dm", ".json"). Appended to fileName to create the full filename */
466
- fileType: string;
467
- /** Whether data is base64-encoded. If true, must be decoded to binary before use */
468
- isBase64Encoded: boolean;
469
- /** Directory path for archive organization (e.g., "subfolder/nested"). Empty string for root-level files */
470
- subFolder: string;
471
- /** Arbitrary metadata attached in Grasshopper. Not interpreted by compute; passed through for downstream consumers. May be absent on older payloads */
472
- metadata?: Record<string, string>;
473
- };
474
- /**
475
- * Normalized file ready for consumption or archival.
476
- * Unified intermediate format from FileData or FileBaseInfo, ready to be packaged into archives or returned to callers.
477
- */
478
- type ProcessedFile = {
479
- /** Full filename including extension (e.g., "model.3dm") */
480
- fileName: string;
481
- /** File content as binary data (Uint8Array for decoded base64 or fetched binary) or text */
482
- content: Uint8Array | string;
483
- /** File path for archive organization (e.g., "subfolder/model.3dm") */
484
- path: string;
485
- /**
486
- * The sanitized `subFolder` this file came from, separately from `path`.
487
- *
488
- * `path` fuses folder and name for the archive, and a consumer that stores files
489
- * itself (rather than zipping them) otherwise has to re-split it — which is
490
- * ambiguous once a duplicate path has been renamed. `''` means archive root;
491
- * absent means the producer did not record one (hand-built `ProcessedFile`s).
492
- */
493
- subFolder?: string;
494
- /**
495
- * Grasshopper-authored metadata carried through from {@link FileData}. Absent when
496
- * the source item had none, or for files fetched via {@link FileBaseInfo} (an
497
- * external URL has no GH metadata).
498
- */
499
- metadata?: Record<string, string>;
500
- };
501
- /**
502
- * Reference to an external file to be fetched and included in file operations.
503
- * Specifies additional files (beyond compute response files) to fetch and process as ProcessedFile.
504
- * @see {@link fetchRemoteFiles} for how FileBaseInfo is fetched and converted
505
- */
506
- type FileBaseInfo = {
507
- /** Destination filename for the file in the archive or result set (e.g., "additional-data.json") */
508
- fileName: string;
509
- /** URL to fetch the file from. Must be accessible from the runtime environment */
510
- filePath: string;
511
- /** Optional directory path for archive organization (e.g., "extras/docs"). When omitted, file lands at archive root */
512
- subFolder?: string;
513
- };
514
- //#endregion
515
- //#region src/core/files/handle-files.d.ts
516
- /**
517
- * Extracts and processes files from compute response data without downloading
518
- * them. Never throws: undecodable/unnamed items and failed external fetches are
519
- * logged and dropped per-file (see {@link fetchRemoteFiles}).
520
- */
521
- declare const extractFilesFromComputeResponse: (downloadableFiles: FileData[], additionalFiles?: FileBaseInfo[] | FileBaseInfo | null) => Promise<ProcessedFile[]>;
522
- /** Downloads files from a compute response as a ZIP archive. */
523
- declare const downloadFileData: (downloadableFiles: FileData[], fileFoldername: string, additionalFiles?: FileBaseInfo[] | FileBaseInfo | null) => Promise<void>;
524
- /**
525
- * Download files as one archive per `Sub Folder` root.
526
- *
527
- * `ROOT::Panels` and `OTHERROOT::Panels` produce `ROOT.zip` and `OTHERROOT.zip`, each containing
528
- * `Panels/…` — the root names the archive instead of nesting inside it. Files with no root fall
529
- * back to `fallbackName`, so a definition that never sets `Sub Folder` downloads exactly as before.
530
- *
531
- * Archives are saved one at a time: browsers discard concurrent downloads issued in the same tick,
532
- * and these are user-initiated saves rather than a throughput-bound batch. Note that saving more
533
- * than one file per gesture may prompt for permission.
534
- *
535
- * @param downloadableFiles - `FileData` items from the compute response.
536
- * @param fallbackName - Archive name for files with no `Sub Folder` root.
537
- * @param additionalFiles - Extra files to package; they carry no root and join the fallback archive.
538
- */
539
- declare const downloadFileDataByRoot: (downloadableFiles: FileData[], fallbackName: string, additionalFiles?: FileBaseInfo[] | FileBaseInfo | null) => Promise<void>;
540
- //#endregion
541
- //#region src/core/files/sub-folder.d.ts
542
- /**
543
- * The `Sub Folder` convention, as authored on Selva's Grasshopper file components.
544
- *
545
- * `::` nests, matching Rhino's layer separator (`ROOT::Panels`), and `/` and `\` are accepted
546
- * because people type them out of habit. The plugin normalizes to `/` before sending, so these
547
- * helpers also cover payloads from an older plugin that still emit `::`.
548
- *
549
- * The first segment is the **root**, which names the archive rather than becoming a folder inside
550
- * it: `ROOT::Panels` downloads as `ROOT.zip` containing `Panels/…`. Files sharing a root travel
551
- * together; distinct roots produce separate archives.
552
- */
553
- /**
554
- * Split a `Sub Folder` value into folder segments; `/`, `\` and `::` all separate.
555
- *
556
- * Exported because anything rendering a folder tree has to agree with the archive on where the
557
- * boundaries are — splitting on `/` alone leaves `Main::Panels` as one literal segment and shows
558
- * a folder named after the separator.
559
- */
560
- declare const subFolderSegments: (subFolder: string | undefined) => string[];
561
- /**
562
- * Group files by their `Sub Folder` root, preserving encounter order.
563
- *
564
- * Rootless files group under `''` — a caller naming archives supplies its own fallback for that
565
- * bucket. Useful beyond downloading: a consumer writing to disk gets the same grouping without
566
- * touching the DOM.
567
- */
568
- declare const groupFilesByRoot: <T extends Pick<FileData, "subFolder">>(files: readonly T[]) => Array<{
569
- root: string;
570
- files: T[];
571
- }>;
572
- //#endregion
573
- export { RetryPolicy as A, getLogger as C, fetchCompute as D, setResponseWireSize as E, ErrorCodes as F, ServerTiming as M, ComputeError as N, ComputeAuth as O, ErrorCode as P, enableDebugLogging as S, getResponseWireSize as T, isDefinitionRef as _, extractFilesFromComputeResponse as a, readField as b, ProcessedFile as c, classifyProbeFailure as d, DEFAULT_BLOCKED_HOST as f, SolveDefinition as g, DefinitionRef as h, downloadFileDataByRoot as i, ServerErrorCodeMap as j, ComputeConfig as k, ProbeFailure as l, validateServerUrl as m, subFolderSegments as n, FileBaseInfo as o, ValidateServerUrlOptions as p, downloadFileData as r, FileData as s, groupFilesByRoot as t, ProbeVerdict as u, decodeBase64ToBinary as v, setLogger as w, Logger as x, hasField as y };
574
- //# sourceMappingURL=index-BR_XZvjj.d.cts.map