@selvajs/compute 1.5.3 → 2.0.0-beta.1
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/dist/chunk-3FH7FKLG.cjs +2 -0
- package/dist/chunk-3FH7FKLG.cjs.map +1 -0
- package/dist/chunk-DELOBV2Q.js +2 -0
- package/dist/chunk-DELOBV2Q.js.map +1 -0
- package/dist/chunk-GTTKNF4G.js +4 -0
- package/dist/chunk-GTTKNF4G.js.map +1 -0
- package/dist/chunk-JFLD2UCY.cjs +2 -0
- package/dist/chunk-JFLD2UCY.cjs.map +1 -0
- package/dist/chunk-MA6YB3YZ.cjs +4 -0
- package/dist/chunk-MA6YB3YZ.cjs.map +1 -0
- package/dist/chunk-MKW2KTPT.js +2 -0
- package/dist/chunk-MKW2KTPT.js.map +1 -0
- package/dist/core.cjs +1 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +10 -15
- package/dist/core.d.ts +10 -15
- package/dist/core.js +1 -1
- package/dist/grasshopper.cjs +1 -1
- package/dist/grasshopper.cjs.map +1 -1
- package/dist/grasshopper.d.cts +77 -101
- package/dist/grasshopper.d.ts +77 -101
- package/dist/grasshopper.js +1 -1
- package/dist/handle-files-DsrxHKHP.d.cts +262 -0
- package/dist/handle-files-DsrxHKHP.d.ts +262 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/{types-Dfeei0dD.d.cts → types-CJ092lxB.d.cts} +116 -73
- package/dist/types-D1SkNje_.d.cts +87 -0
- package/dist/types-D1SkNje_.d.ts +87 -0
- package/dist/{types-Dfeei0dD.d.ts → types-XCUrJGby.d.ts} +116 -73
- package/dist/visualization-GU7JIB4V.cjs +2 -0
- package/dist/visualization-GU7JIB4V.cjs.map +1 -0
- package/dist/visualization-WIUVT2FZ.js +2 -0
- package/dist/visualization.cjs +1 -1
- package/dist/visualization.cjs.map +1 -1
- package/dist/visualization.d.cts +16 -21
- package/dist/visualization.d.ts +16 -21
- package/dist/visualization.js +1 -1
- package/package.json +18 -7
- package/dist/chunk-GZYSQSLH.cjs +0 -2
- package/dist/chunk-GZYSQSLH.cjs.map +0 -1
- package/dist/chunk-JZYEMZZ5.js +0 -2
- package/dist/chunk-JZYEMZZ5.js.map +0 -1
- package/dist/chunk-OEDLGVIQ.js +0 -2
- package/dist/chunk-OEDLGVIQ.js.map +0 -1
- package/dist/chunk-OW6HV6QP.js +0 -2
- package/dist/chunk-OW6HV6QP.js.map +0 -1
- package/dist/chunk-RBNF6MNH.cjs +0 -3
- package/dist/chunk-RBNF6MNH.cjs.map +0 -1
- package/dist/chunk-SVEXPGHW.cjs +0 -2
- package/dist/chunk-SVEXPGHW.cjs.map +0 -1
- package/dist/chunk-XBIEAJBK.js +0 -3
- package/dist/chunk-XBIEAJBK.js.map +0 -1
- package/dist/chunk-ZRQRYG6F.cjs +0 -2
- package/dist/chunk-ZRQRYG6F.cjs.map +0 -1
- package/dist/errors-CEy4nM1J.d.cts +0 -149
- package/dist/errors-CEy4nM1J.d.ts +0 -149
- package/dist/types-COCuQEMk.d.cts +0 -93
- package/dist/types-COCuQEMk.d.ts +0 -93
- package/dist/visualization-ENMBHWIN.js +0 -2
- package/dist/visualization-TBPFFBFU.cjs +0 -2
- package/dist/visualization-TBPFFBFU.cjs.map +0 -1
- /package/dist/{visualization-ENMBHWIN.js.map → visualization-WIUVT2FZ.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/features/grasshopper/scheduler/stable-hash.ts","../src/features/grasshopper/scheduler/solve-scheduler.ts","../src/features/grasshopper/client/grasshopper-client.ts","../src/features/grasshopper/file-handling/handle-files.ts","../src/features/grasshopper/io/output/rhino-decoder.ts","../src/features/grasshopper/io/output/response-processors.ts","../src/features/grasshopper/client/grasshopper-response-processor.ts","../src/core/utils/warnings.ts","../src/features/grasshopper/compute/solve.ts","../src/features/grasshopper/io/input/input-validators.ts","../src/features/grasshopper/io/input/input-parsers.ts","../src/features/grasshopper/io/input/input-processors.ts","../src/features/grasshopper/io/definition-io.ts","../src/features/grasshopper/data-tree/data-tree.ts"],"sourcesContent":["/**\n * Stable hashing for solve deduplication and caching.\n * @internal\n */\n\n/**\n * Deterministic stringify with sorted keys. {a:1,b:2} and {b:2,a:1} produce\n * the same string. Safely handles circular references and non-finite numbers.\n */\nexport function stableStringify(value: unknown): string {\n\tconst seen = new WeakSet<object>();\n\n\tconst stringify = (v: unknown): string => {\n\t\tif (v === null || v === undefined) return JSON.stringify(v);\n\t\tif (typeof v === 'number') {\n\t\t\treturn Number.isFinite(v) ? String(v) : JSON.stringify(null);\n\t\t}\n\t\tif (typeof v === 'string' || typeof v === 'boolean') return JSON.stringify(v);\n\t\tif (typeof v === 'bigint') return JSON.stringify(v.toString());\n\t\tif (v instanceof Uint8Array) {\n\t\t\t// Use length + sample instead of full buffer to avoid stringifying large data\n\t\t\tconst sample =\n\t\t\t\tv.length > 64 ? Array.from(v.slice(0, 32)).concat(Array.from(v.slice(-32))) : Array.from(v);\n\t\t\treturn JSON.stringify({ __u8: true, len: v.length, sample });\n\t\t}\n\t\tif (Array.isArray(v)) {\n\t\t\treturn `[${v.map(stringify).join(',')}]`;\n\t\t}\n\t\tif (typeof v === 'object') {\n\t\t\tif (seen.has(v as object)) return JSON.stringify('[Circular]');\n\t\t\tseen.add(v as object);\n\t\t\tconst keys = Object.keys(v as object).sort();\n\t\t\tconst parts = keys.map((k) => `${JSON.stringify(k)}:${stringify((v as any)[k])}`);\n\t\t\treturn `{${parts.join(',')}}`;\n\t\t}\n\t\t// Fallback for functions, symbols, etc.\n\t\treturn JSON.stringify(null);\n\t};\n\n\treturn stringify(value);\n}\n\n/**\n * 32-bit FNV-1a— fast, no dependencies. Returns unsigned hex string.\n */\nexport function fnv1a(input: string): string {\n\tlet hash = 0x811c9dc5;\n\tfor (let i = 0; i < input.length; i++) {\n\t\thash ^= input.charCodeAt(i);\n\t\thash = (hash + ((hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24))) >>> 0;\n\t}\n\treturn hash.toString(16).padStart(8, '0');\n}\n\n/**\n * Hash definition and data tree into a stable cache key.\n * For Uint8Array, uses length + samples to keep hashing fast.\n */\nexport function hashSolveInput(definition: string | Uint8Array, dataTree: unknown): string {\n\tconst defKey =\n\t\ttypeof definition === 'string'\n\t\t\t? definition\n\t\t\t: stableStringify({ __u8: true, len: definition.length });\n\treturn fnv1a(`${defKey}|${stableStringify(dataTree)}`);\n}\n","import { RhinoComputeError, ErrorCodes } from '@/core/errors';\nimport type { RetryPolicy } from '@/core/types';\nimport { getLogger } from '@/core/utils/logger';\n\nimport type { DataTree, GrasshopperComputeResponse, GrasshopperComputeConfig } from '../types';\nimport { hashSolveInput } from './stable-hash';\n\n/**\n * Scheduling mode — controls how concurrent `solve()` calls interact.\n *\n * - `latest-wins`: One in flight at a time. New calls supersede any pending\n * call (in-flight one is aborted). Optimal for slider scrubs / live UIs.\n * - `queue`: FIFO queue. Each solve runs to completion. Concurrency capped\n * by `maxConcurrent`. Use for \"submit job\" flows where every request matters.\n * - `parallel`: No scheduling — calls run concurrently up to `maxConcurrent`.\n * Closest to plain `client.solve()` but with shared cancel/state.\n */\nexport type SchedulerMode = 'latest-wins' | 'queue' | 'parallel';\n\nexport interface CacheOptions {\n\t/** Maximum entries kept in the LRU. Default: 50. */\n\tmaxEntries?: number;\n\t/** Time-to-live in ms. Set to `0` for no expiry (default). */\n\tttlMs?: number;\n}\n\nexport interface SolveSchedulerOptions {\n\tmode?: SchedulerMode;\n\tmaxConcurrent?: number;\n\ttimeoutMs?: number;\n\tretry?: RetryPolicy;\n\t/** Enable response caching keyed by hash of (definition, dataTree). */\n\tcache?: boolean | CacheOptions;\n\t/** Lifecycle hooks — fired in order. Errors thrown by hooks are logged, not rethrown. */\n\tonStart?: (ctx: SolveContext) => void;\n\tonSettle?: (ctx: SolveContext, result: SolveResult) => void;\n\tonSuperseded?: (ctx: SolveContext) => void;\n}\n\nexport interface SolveContext {\n\t/** Stable hash of (definition, dataTree). */\n\tkey: string;\n\t/** Timestamp when scheduler.solve() was called. */\n\tenqueuedAt: number;\n\t/** Timestamp when execution actually started (after queueing). */\n\tstartedAt: number | null;\n}\n\nexport type SolveResult =\n\t| {\n\t\t\tstatus: 'success';\n\t\t\tresponse: GrasshopperComputeResponse;\n\t\t\tdurationMs: number;\n\t\t\tfromCache: boolean;\n\t }\n\t| { status: 'error'; error: RhinoComputeError; durationMs: number }\n\t| { status: 'superseded' };\n\ninterface CacheEntry {\n\tresponse: GrasshopperComputeResponse;\n\tinsertedAt: number;\n}\n\ninterface PendingItem {\n\tdefinition: string | Uint8Array;\n\tdataTree: DataTree[];\n\tctx: SolveContext;\n\tresolve: (response: GrasshopperComputeResponse) => void;\n\treject: (error: RhinoComputeError) => void;\n\texternalSignal?: AbortSignal;\n\t/** Set once the promise has been settled, so a late executor rejection becomes a no-op. */\n\tsettled?: { error: RhinoComputeError } | { ok: true };\n}\n\ninterface InFlightItem extends PendingItem {\n\tcontroller: AbortController;\n}\n\n/**\n * Adapter for the underlying solve function. Lets the scheduler be tested\n * without a real Compute server, and decouples it from the client class.\n */\nexport type SolveExecutor = (\n\tdefinition: string | Uint8Array,\n\tdataTree: DataTree[],\n\tconfig: GrasshopperComputeConfig\n) => Promise<GrasshopperComputeResponse>;\n\n/**\n * Robust scheduler for Grasshopper solves.\n *\n * Sits between your application code and the underlying compute call,\n * adding:\n * - Configurable scheduling (latest-wins for sliders, queue for jobs)\n * - In-flight cancellation (per-call signal + cancelAll)\n * - Optional response caching for repeated inputs\n * - Lifecycle hooks for UI indicators (start / settle / superseded)\n * - State observability via subscribe()\n *\n * Multiple schedulers can share a single GrasshopperClient — typically one\n * per UI surface (e.g. one for slider scrubs, one for long-running submits).\n *\n * @example\n * ```ts\n * const scheduler = client.createScheduler({ mode: 'latest-wins', timeoutMs: 30_000 });\n *\n * // From a slider handler:\n * scheduler.solve(definition, tree).then((result) => {\n * updateMeshes(result);\n * }).catch((err) => {\n * if (err.code !== 'SUPERSEDED') showError(err);\n * });\n *\n * // From a UI binding:\n * scheduler.subscribe(() => {\n * showSpinner = scheduler.isSolving;\n * });\n * ```\n */\nexport class SolveScheduler {\n\tprivate readonly executor: SolveExecutor;\n\tprivate readonly baseConfig: GrasshopperComputeConfig;\n\n\tprivate readonly mode: SchedulerMode;\n\tprivate readonly maxConcurrent: number;\n\tprivate readonly timeoutMs: number | undefined;\n\tprivate readonly retry: RetryPolicy | undefined;\n\n\tprivate readonly cacheEnabled: boolean;\n\tprivate readonly cacheMax: number;\n\tprivate readonly cacheTtl: number;\n\tprivate readonly cache = new Map<string, CacheEntry>();\n\n\tprivate readonly onStart?: SolveSchedulerOptions['onStart'];\n\tprivate readonly onSettle?: SolveSchedulerOptions['onSettle'];\n\tprivate readonly onSuperseded?: SolveSchedulerOptions['onSuperseded'];\n\n\tprivate readonly subscribers = new Set<() => void>();\n\n\tprivate readonly inFlight = new Set<InFlightItem>();\n\tprivate pendingForLatestWins: PendingItem | null = null;\n\tprivate readonly fifoQueue: PendingItem[] = [];\n\n\tprivate _lastResult: GrasshopperComputeResponse | null = null;\n\tprivate _lastError: RhinoComputeError | null = null;\n\tprivate _lastDurationMs: number | null = null;\n\n\tprivate disposed = false;\n\n\tconstructor(\n\t\texecutor: SolveExecutor,\n\t\tbaseConfig: GrasshopperComputeConfig,\n\t\toptions: SolveSchedulerOptions = {}\n\t) {\n\t\tthis.executor = executor;\n\t\tthis.baseConfig = baseConfig;\n\t\tthis.mode = options.mode ?? 'latest-wins';\n\t\tthis.maxConcurrent = Math.max(1, options.maxConcurrent ?? (this.mode === 'parallel' ? 4 : 1));\n\t\tthis.timeoutMs = options.timeoutMs;\n\t\tthis.retry = options.retry;\n\n\t\tconst cacheOpt = options.cache;\n\t\tthis.cacheEnabled = cacheOpt !== undefined && cacheOpt !== false;\n\t\tconst cacheConfig = typeof cacheOpt === 'object' ? cacheOpt : {};\n\t\tthis.cacheMax = cacheConfig.maxEntries ?? 50;\n\t\tthis.cacheTtl = cacheConfig.ttlMs ?? 0;\n\n\t\tthis.onStart = options.onStart;\n\t\tthis.onSettle = options.onSettle;\n\t\tthis.onSuperseded = options.onSuperseded;\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// Public state\n\t// --------------------------------------------------------------------------\n\n\tget isSolving(): boolean {\n\t\treturn this.inFlight.size > 0;\n\t}\n\n\tget hasPending(): boolean {\n\t\treturn this.pendingForLatestWins !== null || this.fifoQueue.length > 0;\n\t}\n\n\tget inFlightCount(): number {\n\t\treturn this.inFlight.size;\n\t}\n\n\tget queueDepth(): number {\n\t\treturn this.fifoQueue.length + (this.pendingForLatestWins ? 1 : 0);\n\t}\n\n\tget lastResult(): GrasshopperComputeResponse | null {\n\t\treturn this._lastResult;\n\t}\n\n\tget lastError(): RhinoComputeError | null {\n\t\treturn this._lastError;\n\t}\n\n\tget lastDurationMs(): number | null {\n\t\treturn this._lastDurationMs;\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// Subscribe — minimal observable. Called whenever observable state changes.\n\t// --------------------------------------------------------------------------\n\n\tsubscribe(listener: () => void): () => void {\n\t\tthis.subscribers.add(listener);\n\t\treturn () => this.subscribers.delete(listener);\n\t}\n\n\tprivate notify(): void {\n\t\tfor (const listener of this.subscribers) {\n\t\t\ttry {\n\t\t\t\tlistener();\n\t\t\t} catch (err) {\n\t\t\t\tgetLogger().error('[SolveScheduler] subscriber threw:', err);\n\t\t\t}\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// solve()\n\t// --------------------------------------------------------------------------\n\n\t/**\n\t * Schedule a solve. Returns a promise that:\n\t * - Resolves with the compute response on success.\n\t * - Rejects with `RhinoComputeError` on failure.\n\t * - Rejects with `code: ErrorCodes.SUPERSEDED` when the call was canceled because\n\t * newer values arrived (latest-wins mode).\n\t * - Rejects with `code: ErrorCodes.ABORTED` when the call was canceled via\n\t * caller-supplied signal or `cancelAll()`.\n\t *\n\t * Caller-supplied `signal` cancels just this call (rejects with `ABORTED`).\n\t */\n\tsolve(\n\t\tdefinition: string | Uint8Array,\n\t\tdataTree: DataTree[],\n\t\toptions?: { signal?: AbortSignal }\n\t): Promise<GrasshopperComputeResponse> {\n\t\tif (this.disposed) {\n\t\t\treturn Promise.reject(\n\t\t\t\tnew RhinoComputeError(\n\t\t\t\t\t'SolveScheduler has been disposed and cannot be used',\n\t\t\t\t\tErrorCodes.INVALID_STATE\n\t\t\t\t)\n\t\t\t);\n\t\t}\n\n\t\tconst key = hashSolveInput(definition, dataTree);\n\t\tconst ctx: SolveContext = {\n\t\t\tkey,\n\t\t\tenqueuedAt: Date.now(),\n\t\t\tstartedAt: null\n\t\t};\n\n\t\t// Cache hit — return synchronously-resolved promise\n\t\tif (this.cacheEnabled) {\n\t\t\tconst cached = this.readCache(key);\n\t\t\tif (cached) {\n\t\t\t\tconst result: SolveResult = {\n\t\t\t\t\tstatus: 'success',\n\t\t\t\t\tresponse: cached,\n\t\t\t\t\tdurationMs: 0,\n\t\t\t\t\tfromCache: true\n\t\t\t\t};\n\t\t\t\tthis._lastResult = cached;\n\t\t\t\tthis._lastError = null;\n\t\t\t\tthis._lastDurationMs = 0;\n\t\t\t\tthis.runHook(this.onStart, ctx);\n\t\t\t\tthis.runHook(this.onSettle, ctx, result);\n\t\t\t\tthis.notify();\n\t\t\t\treturn Promise.resolve(cached);\n\t\t\t}\n\t\t}\n\n\t\treturn new Promise<GrasshopperComputeResponse>((resolve, reject) => {\n\t\t\tconst item: PendingItem = {\n\t\t\t\tdefinition,\n\t\t\t\tdataTree,\n\t\t\t\tctx,\n\t\t\t\tresolve,\n\t\t\t\treject,\n\t\t\t\texternalSignal: options?.signal\n\t\t\t};\n\n\t\t\t// External signal cancellation — reject immediately if already aborted\n\t\t\tif (item.externalSignal?.aborted) {\n\t\t\t\tconst abortErr = this.makeAbortError(ctx);\n\t\t\t\titem.settled = { error: abortErr };\n\t\t\t\treject(abortErr);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.enqueue(item);\n\t\t});\n\t}\n\n\tprivate enqueue(item: PendingItem): void {\n\t\tswitch (this.mode) {\n\t\t\tcase 'latest-wins': {\n\t\t\t\t// Reject any pending one as superseded\n\t\t\t\tif (this.pendingForLatestWins) {\n\t\t\t\t\tthis.supersede(this.pendingForLatestWins);\n\t\t\t\t\tthis.pendingForLatestWins = null;\n\t\t\t\t}\n\t\t\t\t// Abort any in-flight one as superseded\n\t\t\t\tfor (const inflight of this.inFlight) {\n\t\t\t\t\tthis.supersede(inflight);\n\t\t\t\t\tinflight.controller.abort();\n\t\t\t\t}\n\t\t\t\t// Run immediately if no slot is taken\n\t\t\t\tif (this.inFlight.size === 0) {\n\t\t\t\t\tthis.execute(item);\n\t\t\t\t} else {\n\t\t\t\t\tthis.pendingForLatestWins = item;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase 'queue':\n\t\t\tcase 'parallel': {\n\t\t\t\t// Same dispatch logic — the modes differ only in `maxConcurrent`'s\n\t\t\t\t// default (1 for queue, 4 for parallel), set in the constructor.\n\t\t\t\tif (this.inFlight.size < this.maxConcurrent) {\n\t\t\t\t\tthis.execute(item);\n\t\t\t\t} else {\n\t\t\t\t\tthis.fifoQueue.push(item);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tthis.notify();\n\t}\n\n\tprivate async execute(item: PendingItem): Promise<void> {\n\t\tconst controller = new AbortController();\n\t\tconst inflight: InFlightItem = { ...item, controller };\n\t\tthis.inFlight.add(inflight);\n\t\titem.ctx.startedAt = Date.now();\n\n\t\tconst externalAbortHandler = () => controller.abort();\n\t\titem.externalSignal?.addEventListener('abort', externalAbortHandler, { once: true });\n\n\t\tthis.runHook(this.onStart, item.ctx);\n\t\tthis.notify();\n\n\t\tconst startTime = performance.now();\n\t\ttry {\n\t\t\tconst config: GrasshopperComputeConfig = {\n\t\t\t\t...this.baseConfig,\n\t\t\t\tsignal: controller.signal,\n\t\t\t\t...(this.timeoutMs !== undefined && { timeoutMs: this.timeoutMs }),\n\t\t\t\t...(this.retry !== undefined && { retry: this.retry })\n\t\t\t};\n\n\t\t\tconst response = await this.executor(item.definition, item.dataTree, config);\n\t\t\tconst durationMs = performance.now() - startTime;\n\n\t\t\tif (this.cacheEnabled) this.writeCache(item.ctx.key, response);\n\n\t\t\tif (item.settled) {\n\t\t\t\t// Already superseded mid-flight — drop the late success silently.\n\t\t\t\treturn;\n\t\t\t}\n\t\t\titem.settled = { ok: true };\n\n\t\t\tthis._lastResult = response;\n\t\t\tthis._lastError = null;\n\t\t\tthis._lastDurationMs = durationMs;\n\n\t\t\titem.resolve(response);\n\t\t\tthis.runHook(this.onSettle, item.ctx, {\n\t\t\t\tstatus: 'success',\n\t\t\t\tresponse,\n\t\t\t\tdurationMs,\n\t\t\t\tfromCache: false\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconst durationMs = performance.now() - startTime;\n\t\t\tconst err = this.normalizeExecutionError(error, inflight);\n\t\t\tconst alreadySettled = !!inflight.settled;\n\n\t\t\tthis._lastError = err;\n\t\t\tthis._lastDurationMs = durationMs;\n\n\t\t\tif (!alreadySettled) {\n\t\t\t\tinflight.settled = { error: err };\n\t\t\t\titem.reject(err);\n\t\t\t\tthis.runHook(this.onSettle, item.ctx, { status: 'error', error: err, durationMs });\n\t\t\t}\n\t\t} finally {\n\t\t\titem.externalSignal?.removeEventListener('abort', externalAbortHandler);\n\t\t\tthis.inFlight.delete(inflight);\n\t\t\tthis.drainNext();\n\t\t\tthis.notify();\n\t\t}\n\t}\n\n\tprivate drainNext(): void {\n\t\tif (this.disposed) return;\n\n\t\t// latest-wins: promote pending if no in-flight\n\t\tif (this.mode === 'latest-wins') {\n\t\t\tif (this.pendingForLatestWins && this.inFlight.size === 0) {\n\t\t\t\tconst next = this.pendingForLatestWins;\n\t\t\t\tthis.pendingForLatestWins = null;\n\t\t\t\tthis.execute(next);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// queue / parallel: pull from FIFO until at capacity\n\t\twhile (this.fifoQueue.length > 0 && this.inFlight.size < this.maxConcurrent) {\n\t\t\tconst next = this.fifoQueue.shift()!;\n\t\t\tthis.execute(next);\n\t\t}\n\t}\n\n\tprivate supersede(item: PendingItem): void {\n\t\tif (item.settled) return;\n\t\tconst err = new RhinoComputeError('Superseded by newer solve', ErrorCodes.SUPERSEDED, {\n\t\t\tcontext: { key: item.ctx.key, enqueuedAt: item.ctx.enqueuedAt }\n\t\t});\n\t\titem.settled = { error: err };\n\t\titem.reject(err);\n\t\tthis.runHook(this.onSuperseded, item.ctx);\n\t}\n\n\tprivate makeAbortError(ctx: SolveContext): RhinoComputeError {\n\t\treturn new RhinoComputeError('Request aborted by caller', ErrorCodes.ABORTED, {\n\t\t\tcontext: { key: ctx.key, enqueuedAt: ctx.enqueuedAt }\n\t\t});\n\t}\n\n\tprivate isAbortLikeError(error: unknown): boolean {\n\t\tif (error instanceof Error) {\n\t\t\tif (error.name === 'AbortError') return true;\n\t\t\tif (typeof DOMException !== 'undefined' && error instanceof DOMException) {\n\t\t\t\treturn error.name === 'AbortError';\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate normalizeExecutionError(error: unknown, item: InFlightItem): RhinoComputeError {\n\t\t// If the item was already settled (e.g. by supersede), return that error so\n\t\t// _lastError reflects the original cause rather than the downstream abort.\n\t\tif (item.settled && 'error' in item.settled) {\n\t\t\treturn item.settled.error;\n\t\t}\n\n\t\tif (error instanceof RhinoComputeError) return error;\n\n\t\tif (this.isAbortLikeError(error)) {\n\t\t\treturn this.makeAbortError(item.ctx);\n\t\t}\n\n\t\treturn new RhinoComputeError(\n\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\tErrorCodes.UNKNOWN_ERROR,\n\t\t\t{ originalError: error instanceof Error ? error : new Error(String(error)) }\n\t\t);\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// Cancellation\n\t// --------------------------------------------------------------------------\n\n\t/** Cancel everything — in-flight and pending. */\n\tcancelAll(): void {\n\t\t// Reject pending\n\t\tif (this.pendingForLatestWins) {\n\t\t\tthis.rejectAsAborted(this.pendingForLatestWins);\n\t\t\tthis.pendingForLatestWins = null;\n\t\t}\n\t\twhile (this.fifoQueue.length > 0) {\n\t\t\tconst item = this.fifoQueue.shift()!;\n\t\t\tthis.rejectAsAborted(item);\n\t\t}\n\t\t// Abort in-flight — their finally blocks will reject their promises\n\t\tfor (const inflight of this.inFlight) {\n\t\t\tif (!inflight.settled) {\n\t\t\t\tconst err = this.makeAbortError(inflight.ctx);\n\t\t\t\tinflight.settled = { error: err };\n\t\t\t\tinflight.reject(err);\n\t\t\t\tthis.runHook(this.onSettle, inflight.ctx, {\n\t\t\t\t\tstatus: 'error',\n\t\t\t\t\terror: err,\n\t\t\t\t\tdurationMs: inflight.ctx.startedAt ? performance.now() - inflight.ctx.startedAt : 0\n\t\t\t\t});\n\t\t\t}\n\t\t\tinflight.controller.abort();\n\t\t}\n\t\tthis.notify();\n\t}\n\n\tprivate rejectAsAborted(item: PendingItem): void {\n\t\tif (item.settled) return;\n\t\tconst err = this.makeAbortError(item.ctx);\n\t\titem.settled = { error: err };\n\t\titem.reject(err);\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// Cache\n\t// --------------------------------------------------------------------------\n\n\tprivate readCache(key: string): GrasshopperComputeResponse | null {\n\t\tif (!this.cacheEnabled) return null;\n\t\tconst entry = this.cache.get(key);\n\t\tif (!entry) return null;\n\t\tif (this.cacheTtl > 0 && Date.now() - entry.insertedAt > this.cacheTtl) {\n\t\t\tthis.cache.delete(key);\n\t\t\treturn null;\n\t\t}\n\t\t// LRU touch\n\t\tthis.cache.delete(key);\n\t\tthis.cache.set(key, entry);\n\t\treturn entry.response;\n\t}\n\n\tprivate writeCache(key: string, response: GrasshopperComputeResponse): void {\n\t\tif (!this.cacheEnabled) return;\n\t\tthis.cache.set(key, { response, insertedAt: Date.now() });\n\t\twhile (this.cache.size > this.cacheMax) {\n\t\t\tconst oldest = this.cache.keys().next().value;\n\t\t\tif (oldest === undefined) break;\n\t\t\tthis.cache.delete(oldest);\n\t\t}\n\t}\n\n\tclearCache(): void {\n\t\tthis.cache.clear();\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// Lifecycle\n\t// --------------------------------------------------------------------------\n\n\tdispose(): void {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\tthis.cancelAll();\n\t\tthis.subscribers.clear();\n\t\tthis.cache.clear();\n\t}\n\n\tprivate runHook<H extends (...args: any[]) => void>(\n\t\thook: H | undefined,\n\t\t...args: Parameters<H>\n\t): void {\n\t\tif (!hook) return;\n\t\ttry {\n\t\t\thook(...args);\n\t\t} catch (err) {\n\t\t\tgetLogger().error('[SolveScheduler] hook threw:', err);\n\t\t}\n\t}\n}\n","import { ErrorCodes, RhinoComputeError } from '@/core/errors';\nimport { getLogger } from '@/core/utils/logger';\nimport ComputeServerStats from '@/core/server/compute-server-stats';\nimport { ComputeConfig, RetryPolicy } from '@/core/types';\n\nimport { fetchDefinitionIO, fetchParsedDefinitionIO, solveGrasshopperDefinition } from '..';\nimport { GrasshopperComputeConfig, GrasshopperComputeResponse, DataTree } from '../types';\nimport { SolveScheduler, SolveSchedulerOptions } from '../scheduler/solve-scheduler';\n\n/**\n * Per-call options that override the client's default ComputeConfig values.\n *\n * Use these for per-request control without mutating the client config:\n * - `signal` — cancel a specific solve (e.g. when a slider value is superseded)\n * - `timeoutMs` — extend timeout for a long-running solve, or pass `0` to disable\n * - `retry` — override retry policy for this call only\n */\nexport interface SolveOptions {\n\tsignal?: AbortSignal;\n\ttimeoutMs?: number;\n\tretry?: RetryPolicy;\n}\n\n/**\n * GrasshopperClient provides a simple API for interacting with a Rhino Compute server and grasshopper.\n *\n * @public This is the recommended high-level API for Rhino Compute operations.\n *\n * **Security Warning:**\n * Using this client in a browser environment exposes your server URL and API key to users.\n * For production, use this library server-side or proxy requests through your own backend.\n *\n * @example\n * ```typescript\n * const client = await GrasshopperClient.create({\n * serverUrl: 'http://localhost:6500',\n * apiKey: 'your-api-key'\n * });\n *\n * try {\n * const result = await client.solve(definitionUrl, { x: 1, y: 2 });\n * } finally {\n * await client.dispose(); // Clean up resources\n * }\n * ```\n */\nexport default class GrasshopperClient {\n\tprivate readonly config: GrasshopperComputeConfig;\n\tpublic readonly serverStats: ComputeServerStats;\n\tprivate disposed = false;\n\n\tprivate constructor(config: GrasshopperComputeConfig) {\n\t\tthis.config = this.normalizeComputeConfig(config);\n\t\tthis.serverStats = new ComputeServerStats(this.config.serverUrl, this.config.apiKey);\n\t}\n\n\t/**\n\t * Creates and initializes a GrasshopperClient with server validation.\n\t *\n\t * @throws {RhinoComputeError} with code NETWORK_ERROR if server is offline\n\t * @throws {RhinoComputeError} with code INVALID_CONFIG if configuration is invalid\n\t */\n\tstatic async create(config: GrasshopperComputeConfig): Promise<GrasshopperClient> {\n\t\tconst client = new GrasshopperClient(config);\n\n\t\t// Check server is online before returning\n\t\tif (!(await client.serverStats.isServerOnline())) {\n\t\t\tthrow new RhinoComputeError('Rhino Compute server is not online', ErrorCodes.NETWORK_ERROR, {\n\t\t\t\tcontext: { serverUrl: client.config.serverUrl }\n\t\t\t});\n\t\t}\n\n\t\treturn client;\n\t}\n\n\t/**\n\t * Gets the client's configuration.\n\t * Useful for passing to lower-level functions.\n\t */\n\tpublic getConfig(): GrasshopperComputeConfig {\n\t\tthis.ensureNotDisposed();\n\t\treturn { ...this.config };\n\t}\n\n\t/**\n\t * Get input/output parameters of a Grasshopper definition.\n\t */\n\tpublic async getIO(definition: string | Uint8Array) {\n\t\tthis.ensureNotDisposed();\n\t\treturn fetchParsedDefinitionIO(definition, this.config);\n\t}\n\n\tpublic async getRawIO(definition: string | Uint8Array) {\n\t\tthis.ensureNotDisposed();\n\t\treturn fetchDefinitionIO(definition, this.config);\n\t}\n\n\t/**\n\t * Run a compute job with a Grasshopper definition.\n\t *\n\t * @throws {RhinoComputeError} with code INVALID_INPUT if definition is empty\n\t * @throws {RhinoComputeError} with code NETWORK_ERROR if server is offline\n\t * @throws {RhinoComputeError} with code COMPUTATION_ERROR if computation fails\n\t */\n\tpublic async solve(\n\t\tdefinition: string | Uint8Array,\n\t\tdataTree: DataTree[],\n\t\toptions?: SolveOptions\n\t): Promise<GrasshopperComputeResponse> {\n\t\tthis.ensureNotDisposed();\n\n\t\ttry {\n\t\t\t// Validate inputs\n\t\t\tif (typeof definition === 'string' && !definition?.trim()) {\n\t\t\t\tthrow new RhinoComputeError(\n\t\t\t\t\t'Definition URL/content is required',\n\t\t\t\t\tErrorCodes.INVALID_INPUT,\n\t\t\t\t\t{\n\t\t\t\t\t\tcontext: { receivedUrl: definition }\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t} else if (definition instanceof Uint8Array && definition.length === 0) {\n\t\t\t\tthrow new RhinoComputeError('Definition content is empty', ErrorCodes.INVALID_INPUT);\n\t\t\t}\n\n\t\t\t// Per-call options override the client's stored config for this request only\n\t\t\tconst effectiveConfig: GrasshopperComputeConfig = {\n\t\t\t\t...this.config,\n\t\t\t\t...(options?.signal !== undefined && { signal: options.signal }),\n\t\t\t\t...(options?.timeoutMs !== undefined && { timeoutMs: options.timeoutMs }),\n\t\t\t\t...(options?.retry !== undefined && { retry: options.retry })\n\t\t\t};\n\n\t\t\t// Skip the redundant pre-flight healthcheck — fetchRhinoCompute already surfaces\n\t\t\t// network failures with a NETWORK_ERROR code, so adding a roundtrip here only\n\t\t\t// doubles latency on every solve.\n\t\t\tconst result = await solveGrasshopperDefinition(dataTree, definition, effectiveConfig);\n\n\t\t\t// Compute may return a partial-success response (HTTP 500 with a body\n\t\t\t// containing both `values` and `errors`/`warnings`). Surface that as a\n\t\t\t// COMPUTATION_ERROR so callers don't silently consume a broken result.\n\t\t\tif (result?.errors && result.errors.length > 0) {\n\t\t\t\tthrow new RhinoComputeError(\n\t\t\t\t\tresult.errors.join('; ') || 'Computation failed',\n\t\t\t\t\tErrorCodes.COMPUTATION_ERROR,\n\t\t\t\t\t{\n\t\t\t\t\t\tcontext: {\n\t\t\t\t\t\t\tdefinition:\n\t\t\t\t\t\t\t\ttypeof definition === 'string' && definition.length < 200\n\t\t\t\t\t\t\t\t\t? definition\n\t\t\t\t\t\t\t\t\t: '...content...',\n\t\t\t\t\t\t\tinputs: dataTree,\n\t\t\t\t\t\t\terrors: result.errors,\n\t\t\t\t\t\t\twarnings: result.warnings\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tif (this.config.debug) {\n\t\t\t\tgetLogger().error('Compute failed:', error);\n\t\t\t}\n\n\t\t\tif (error instanceof RhinoComputeError) {\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tthrow new RhinoComputeError(\n\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\tErrorCodes.COMPUTATION_ERROR,\n\t\t\t\t{\n\t\t\t\t\tcontext: {\n\t\t\t\t\t\tdefinition:\n\t\t\t\t\t\t\ttypeof definition === 'string' && definition.length < 200\n\t\t\t\t\t\t\t\t? definition\n\t\t\t\t\t\t\t\t: '...content...',\n\t\t\t\t\t\tinputs: dataTree\n\t\t\t\t\t},\n\t\t\t\t\toriginalError: error instanceof Error ? error : new Error(String(error))\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Create a scheduler bound to this client. Use a scheduler for any UI surface\n\t * that fires solves frequently (sliders, live editors) or that needs cancel\n\t * semantics, response caching, or state observability.\n\t *\n\t * Multiple schedulers can be created from a single client — typically one per\n\t * UI surface so their queues stay independent.\n\t *\n\t * @example\n\t * ```ts\n\t * const sliderScheduler = client.createScheduler({ mode: 'latest-wins' });\n\t * const submitScheduler = client.createScheduler({ mode: 'queue', timeoutMs: 0, retry: { attempts: 1 } });\n\t * ```\n\t */\n\tpublic createScheduler(options?: SolveSchedulerOptions): SolveScheduler {\n\t\tthis.ensureNotDisposed();\n\t\tconst executor = (\n\t\t\tdefinition: string | Uint8Array,\n\t\t\tdataTree: DataTree[],\n\t\t\tconfig: GrasshopperComputeConfig\n\t\t) => solveGrasshopperDefinition(dataTree, definition, config);\n\t\treturn new SolveScheduler(executor, this.config, options);\n\t}\n\n\t/**\n\t * Disposes of client resources.\n\t * Call this when you're done using the client.\n\t */\n\tpublic async dispose(): Promise<void> {\n\t\tif (this.disposed) return;\n\n\t\tthis.disposed = true;\n\t\tawait this.serverStats.dispose();\n\t}\n\n\t/**\n\t * Ensures the client hasn't been disposed.\n\t */\n\tprivate ensureNotDisposed(): void {\n\t\tif (this.disposed) {\n\t\t\tthrow new RhinoComputeError(\n\t\t\t\t'GrasshopperClient has been disposed and cannot be used',\n\t\t\t\tErrorCodes.INVALID_STATE\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Validates and normalizes a compute configuration.\n\t *\n\t * @throws {RhinoComputeError} with code INVALID_CONFIG if configuration is invalid\n\t */\n\tprivate normalizeComputeConfig<T extends ComputeConfig | GrasshopperComputeConfig>(config: T): T {\n\t\tif (!config.serverUrl?.trim()) {\n\t\t\tthrow new RhinoComputeError('serverUrl is required', ErrorCodes.INVALID_CONFIG, {\n\t\t\t\tcontext: { receivedServerUrl: config.serverUrl }\n\t\t\t});\n\t\t}\n\n\t\t// Validate URL format\n\t\ttry {\n\t\t\tnew URL(config.serverUrl);\n\t\t} catch {\n\t\t\tthrow new RhinoComputeError('serverUrl must be a valid URL', ErrorCodes.INVALID_CONFIG, {\n\t\t\t\tcontext: { receivedServerUrl: config.serverUrl }\n\t\t\t});\n\t\t}\n\n\t\t// Validate that it's not the default public endpoint\n\t\tif (config.serverUrl === '' || config.serverUrl === 'https://compute.rhino3d.com/') {\n\t\t\tthrow new RhinoComputeError(\n\t\t\t\t'serverUrl must be set to your Compute server URL. The default public endpoint is not allowed.',\n\t\t\t\tErrorCodes.INVALID_CONFIG,\n\t\t\t\t{ context: { receivedServerUrl: config.serverUrl } }\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\t...config,\n\t\t\tserverUrl: config.serverUrl.replace(/\\/+$/, ''), // Remove trailing slashes\n\t\t\tapiKey: config.apiKey,\n\t\t\tauthToken: config.authToken,\n\t\t\tdebug: config.debug ?? false,\n\t\t\tsuppressBrowserWarning: config.suppressBrowserWarning ?? config.suppressClientSideWarning\n\t\t} as T;\n\t}\n}\n","import { RhinoComputeError, ErrorCodes, getLogger } from '@/core';\nimport { decodeBase64ToBinary } from '@/core/utils/encoding';\n\nimport { FileBaseInfo, FileData, ProcessedFile } from './types';\n\n/**\n * Extracts and processes files from compute response data without downloading them.\n * Returns an array of ProcessedFile objects that can be used programmatically.\n *\n * @param downloadableFiles - An array of FileData items from the compute response.\n * @param additionalFiles - Optional additional files to include (fetched from URLs).\n * @returns A Promise resolving to an array of ProcessedFile objects.\n * @throws Will throw an error if file processing fails.\n *\n * @example\n * const files = await extractFilesFromComputeResponse(fileData);\n * files.forEach(file => {\n * console.log(`File: ${file.fileName}, Size: ${file.content.length}`);\n * });\n */\nexport const extractFilesFromComputeResponse = async (\n\tdownloadableFiles: FileData[],\n\tadditionalFiles: FileBaseInfo[] | FileBaseInfo | null = null\n): Promise<ProcessedFile[]> => {\n\ttry {\n\t\treturn await processFiles(downloadableFiles, additionalFiles);\n\t} catch (err) {\n\t\tthrow new RhinoComputeError(\n\t\t\t'Failed to extract files from compute response',\n\t\t\tErrorCodes.INVALID_STATE,\n\t\t\t{\n\t\t\t\tcontext: { originalError: err instanceof Error ? err.message : String(err) },\n\t\t\t\toriginalError: err instanceof Error ? err : undefined\n\t\t\t}\n\t\t);\n\t}\n};\n\n/**\n * Downloads files from a compute response as a ZIP archive.\n * Packages multiple files into a single ZIP file and triggers a browser download.\n *\n * @param downloadableFiles - An array of FileData items from the compute response.\n * @param additionalFiles - Optional additional files to include in the ZIP (fetched from URLs).\n * @param fileFoldername - The name of the ZIP file (without extension).\n * @throws Will throw an error if the file handling or download fails.\n *\n * @example\n * await downloadDataFromComputeResponse(fileData, null, 'my-export');\n * // Downloads 'my-export.zip'\n */\nexport const downloadFileData = async (\n\tdownloadableFiles: FileData[],\n\tfileFoldername: string,\n\tadditionalFiles: FileBaseInfo[] | FileBaseInfo | null = null\n): Promise<void> => {\n\t// Check if we're in a browser environment\n\tif (typeof document === 'undefined' || typeof Blob === 'undefined') {\n\t\tthrow new RhinoComputeError(\n\t\t\t'File download functionality is only available in browser environments. This function requires the DOM API (document, Blob).',\n\t\t\tErrorCodes.BROWSER_ONLY,\n\t\t\t{\n\t\t\t\tcontext: {\n\t\t\t\t\tenvironment: typeof window !== 'undefined' ? 'browser (SSR)' : 'Node.js',\n\t\t\t\t\tdocumentAvailable: typeof document !== 'undefined',\n\t\t\t\t\tblobAvailable: typeof Blob !== 'undefined'\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\t}\n\n\ttry {\n\t\tconst processedFiles = await processFiles(downloadableFiles, additionalFiles);\n\t\tawait createAndDownloadZip(processedFiles, fileFoldername);\n\t} catch (err) {\n\t\t// Re-throw if it's already a RhinoComputeError\n\t\tif (err instanceof RhinoComputeError) {\n\t\t\tthrow err;\n\t\t}\n\t\tthrow new RhinoComputeError(\n\t\t\t'Failed to download files from compute response',\n\t\t\tErrorCodes.INVALID_STATE,\n\t\t\t{\n\t\t\t\tcontext: { originalError: err instanceof Error ? err.message : String(err) },\n\t\t\t\toriginalError: err instanceof Error ? err : undefined\n\t\t\t}\n\t\t);\n\t}\n};\n\n/**\n * Processes files from compute response data and additional files.\n * Converts base64-encoded data to binary and fetches additional files from URLs.\n *\n * @param dataItems - An array of FileData items to process.\n * @param additionalFiles - Optional additional files to fetch and include.\n * @returns A Promise resolving to an array of ProcessedFile objects.\n */\nconst processFiles = async (\n\tdataItems: FileData[],\n\tadditionalFiles: FileBaseInfo[] | FileBaseInfo | null\n): Promise<ProcessedFile[]> => {\n\tconst processedFiles: ProcessedFile[] = [];\n\n\t// Process compute response files\n\tdataItems.forEach((item) => {\n\t\tlet filePath = `${item.fileName}${item.fileType}`;\n\n\t\tif (item.subFolder && item.subFolder.trim() !== '') {\n\t\t\tfilePath = `${item.subFolder}/${filePath}`;\n\t\t}\n\n\t\tif (item.isBase64Encoded === true && item.data) {\n\t\t\tconst bites = decodeBase64ToBinary(item.data);\n\t\t\tprocessedFiles.push({\n\t\t\t\tfileName: `${item.fileName}${item.fileType}`,\n\t\t\t\tcontent: new Uint8Array(bites.buffer),\n\t\t\t\tpath: filePath\n\t\t\t});\n\t\t} else if (item.isBase64Encoded === false && item.data) {\n\t\t\tprocessedFiles.push({\n\t\t\t\tfileName: `${item.fileName}${item.fileType}`,\n\t\t\t\tcontent: item.data,\n\t\t\t\tpath: filePath\n\t\t\t});\n\t\t}\n\t});\n\n\tif (additionalFiles) {\n\t\tconst filesArray = Array.isArray(additionalFiles) ? additionalFiles : [additionalFiles];\n\t\tconst additionalProcessed = await Promise.all(\n\t\t\tfilesArray.map(async (file) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetch(file.filePath);\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\tgetLogger().warn(`Failed to fetch additional file from URL: ${file.filePath}`);\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\tconst fileBlob = await response.blob();\n\t\t\t\t\tconst arrayBuffer = await fileBlob.arrayBuffer();\n\t\t\t\t\treturn {\n\t\t\t\t\t\tfileName: file.fileName,\n\t\t\t\t\t\tcontent: new Uint8Array(arrayBuffer),\n\t\t\t\t\t\tpath: file.fileName\n\t\t\t\t\t} as ProcessedFile;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tgetLogger().error(`Error fetching additional file from URL: ${file.filePath}`, error);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t})\n\t\t);\n\n\t\tprocessedFiles.push(...additionalProcessed.filter((f): f is ProcessedFile => f !== null));\n\t}\n\n\treturn processedFiles;\n};\n\n/**\n * Creates a ZIP archive from processed files and triggers a browser download.\n *\n * @param files - An array of ProcessedFile objects to include in the ZIP.\n * @param zipName - The name of the ZIP file (without extension).\n * @returns A Promise that resolves when the ZIP is generated and download is triggered.\n */\nasync function createAndDownloadZip(files: ProcessedFile[], zipName: string): Promise<void> {\n\tconst { zipSync, strToU8 } = await import('fflate');\n\n\t// Convert files to fflate format\n\tconst zipData: Record<string, Uint8Array> = {};\n\tfiles.forEach((file) => {\n\t\tzipData[file.path] = typeof file.content === 'string' ? strToU8(file.content) : file.content;\n\t});\n\n\tconst zipped = zipSync(zipData, { level: 6 });\n\n\tconst blob = new Blob([zipped as BlobPart], { type: 'application/zip' });\n\tsaveFile(blob, `${zipName}.zip`);\n}\n\n/**\n * Saves a Blob object as a file in the user's browser.\n *\n * @param blob - The Blob object representing the file content.\n * @param filename - The name to give the downloaded file (including extension).\n * @throws {RhinoComputeError} If not running in a browser environment.\n */\nfunction saveFile(blob: Blob, filename: string) {\n\tif (typeof document === 'undefined') {\n\t\tthrow new RhinoComputeError(\n\t\t\t'saveFile requires a browser environment with DOM API access.',\n\t\t\tErrorCodes.BROWSER_ONLY,\n\t\t\t{\n\t\t\t\tcontext: { function: 'saveFile', requiredAPI: 'document' }\n\t\t\t}\n\t\t);\n\t}\n\n\tconst a = document.createElement('a');\n\ta.href = URL.createObjectURL(blob);\n\ta.download = filename;\n\ta.click();\n\tURL.revokeObjectURL(a.href);\n}\n","import type { RhinoModule } from 'rhino3dm';\nimport { getLogger } from '@/core';\n\n// -----------------------------------------------------------------------------\n// Decoder Types\n// -----------------------------------------------------------------------------\n\ntype RhinoDecoder = (rhino: RhinoModule, data: unknown) => unknown;\n\nconst decoderRegistry = new Map<string, RhinoDecoder>();\n\n// -----------------------------------------------------------------------------\n// Registration\n// -----------------------------------------------------------------------------\n\nexport function registerDecoder(typeName: string, decoder: RhinoDecoder): void {\n\tdecoderRegistry.set(typeName, decoder);\n}\n\nregisterDecoder('Rhino.Geometry.Point3d', (rhino, data) => {\n\tconst d = data as any;\n\tif (!d || typeof d.X !== 'number') return null;\n\treturn new rhino.Point([d.X, d.Y, d.Z]);\n});\n\nregisterDecoder('Rhino.Geometry.Line', (rhino, data) => {\n\tconst d = data as any;\n\tif (!d || !d.From || !d.To) return null;\n\treturn new rhino.Line([d.From.X, d.From.Y, d.From.Z], [d.To.X, d.To.Y, d.To.Z]);\n});\n\n// -----------------------------------------------------------------------------\n// Utility Functions\n// -----------------------------------------------------------------------------\n\nfunction findDecoder(rhinoType: string): RhinoDecoder | undefined {\n\tif (decoderRegistry.has(rhinoType)) return decoderRegistry.get(rhinoType);\n\tfor (const [key, dec] of decoderRegistry) {\n\t\tif (rhinoType.startsWith(key)) return dec;\n\t}\n\treturn undefined;\n}\n\nfunction extractPayload(parsedData: any): any {\n\tif (!parsedData || typeof parsedData !== 'object') return null;\n\treturn (parsedData as any).data ?? (parsedData as any).value ?? null;\n}\n\n// -----------------------------------------------------------------------------\n// Geometry Decoding\n// -----------------------------------------------------------------------------\n\nexport function decodeRhinoGeometry(\n\tparsedData: unknown,\n\trhinoType: string,\n\trhino: RhinoModule\n): unknown {\n\tconst decoder = findDecoder(rhinoType);\n\tif (decoder) {\n\t\ttry {\n\t\t\treturn decoder(rhino, parsedData);\n\t\t} catch (error) {\n\t\t\tgetLogger().warn(`Failed to decode Rhino type ${rhinoType}:`, error);\n\t\t}\n\t}\n\n\t// Fallback using CommonObject.decode\n\ttry {\n\t\tconst payload = extractPayload(parsedData);\n\t\tif (payload) return rhino.CommonObject.decode(payload);\n\t} catch (error) {\n\t\tgetLogger().warn(`Failed to decode ${rhinoType} with CommonObject:`, error);\n\t\treturn { __decodeError: true, type: rhinoType, raw: parsedData };\n\t}\n\n\treturn parsedData;\n}\n\n// -----------------------------------------------------------------------------\n// Object Decoder\n// -----------------------------------------------------------------------------\n\nexport interface DecodeRhinoOptions {\n\tkeys?: string[];\n\tskipKeys?: string[];\n\tdeep?: boolean;\n}\n\nexport function decodeRhinoObject<T extends Record<string, unknown>>(\n\tobj: T,\n\trhino: RhinoModule,\n\toptions: DecodeRhinoOptions = {}\n): T {\n\tconst { keys, skipKeys, deep } = options;\n\tconst out: Record<string, unknown> = { ...obj };\n\n\tconst shouldProcessKey = (k: string) => {\n\t\tif (skipKeys?.includes(k)) return false;\n\t\tif (keys && !keys.includes(k)) return false;\n\t\treturn true;\n\t};\n\n\tfor (const [key, value] of Object.entries(obj)) {\n\t\tif (!shouldProcessKey(key)) continue;\n\t\tif (!value || typeof value !== 'object') continue;\n\n\t\tconst v: any = value;\n\t\tconst maybeType = typeof v.type === 'string' ? v.type : undefined;\n\n\t\tif (maybeType) {\n\t\t\tout[key] = decodeRhinoGeometry(v, maybeType, rhino);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (deep && typeof v === 'object') {\n\t\t\tout[key] = decodeRhinoObject(v as any, rhino, options);\n\t\t}\n\t}\n\n\treturn out as T;\n}\n","import { FileData } from '../../file-handling/types';\nimport { GrasshopperComputeResponse, DataItem } from '../../types';\nimport { decodeRhinoGeometry } from './rhino-decoder';\n\nexport interface ParsedContext {\n\t[key: string]: any;\n}\n\nexport interface GetValuesOptions {\n\tparseValues?: boolean;\n\trhino?: any;\n\t/**\n\t * If true, only include values of type System.String in the result.\n\t * Non-string types are filtered out.\n\t */\n\tstringOnly?: boolean;\n}\n\nexport interface GetValuesResult<T = ParsedContext> {\n\tvalues: T;\n}\n\n// -----------------------------------------------------------------------------\n// Constants\n// -----------------------------------------------------------------------------\n\nconst SYSTEM_TYPES = {\n\tSTRING: 'System.String',\n\tINT: 'System.Int32',\n\tDOUBLE: 'System.Double',\n\tBOOL: 'System.Boolean'\n};\n\nconst RHINO_GEOMETRY_PREFIX = 'Rhino.Geometry.';\n\n// Only relevant is Selva plugin is used\nconst EXCLUDED_TYPES = ['WebDisplay'];\nconst FILE_DATA_TYPE = 'FileData';\n\n// -----------------------------------------------------------------------------\n// Utilities\n// -----------------------------------------------------------------------------\n\n/**\n * Checks if a given type string should be excluded by verifying if it contains\n * any of the substrings defined in the `EXCLUDED_TYPES` list.\n *\n * @param type - The string representation of the type to check.\n * @returns `true` if the type matches any excluded pattern; otherwise, `false`.\n */\nfunction isExcludedType(type: string): boolean {\n\treturn EXCLUDED_TYPES.some((t) => type.includes(t));\n}\n\nfunction tryDecodeJSON(value: string): any {\n\tif (typeof value !== 'string') return value;\n\n\tconst trimmed = value.trim();\n\tconst looksJson = trimmed.startsWith('{') || trimmed.startsWith('[') || trimmed.startsWith('\"');\n\tif (!looksJson) return value;\n\n\ttry {\n\t\tconst first = JSON.parse(trimmed);\n\t\tif (typeof first === 'string') {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(first);\n\t\t\t} catch {\n\t\t\t\treturn first;\n\t\t\t}\n\t\t}\n\t\treturn first;\n\t} catch {\n\t\treturn value;\n\t}\n}\n\nfunction decodeBySystemType(raw: any, type: string, rhino?: any): any {\n\tswitch (type) {\n\t\tcase SYSTEM_TYPES.STRING:\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\treturn raw.replace(/^\"(.*)\"$/, '$1');\n\n\t\tcase SYSTEM_TYPES.INT:\n\t\t\treturn Number.parseInt(raw, 10);\n\n\t\tcase SYSTEM_TYPES.DOUBLE:\n\t\t\treturn Number.parseFloat(raw);\n\n\t\tcase SYSTEM_TYPES.BOOL: {\n\t\t\tconst str = String(raw).toLowerCase();\n\t\t\treturn str === 'true';\n\t\t}\n\n\t\tdefault:\n\t\t\tif (rhino && type.startsWith(RHINO_GEOMETRY_PREFIX)) {\n\t\t\t\treturn decodeRhinoGeometry(raw, type, rhino);\n\t\t\t}\n\t\t\treturn raw;\n\t}\n}\n\n// Main extractor — assumes type has already been filtered through isExcludedType\n// at the call site. Returning a sentinel from here would pollute the aggregated\n// arrays in getValues / getValue when multiple branches are mixed.\nfunction extractItemValue(data: any, type: string, parseValues: boolean, rhino?: any): any {\n\tif (typeof data !== 'string') return data;\n\n\tconst raw = parseValues ? tryDecodeJSON(data) : data;\n\treturn decodeBySystemType(raw, type, rhino);\n}\n\n/**\n * Type guard for {@link FileData}. The Compute server emits these as JSON\n * blobs inside `FileData`-typed values; this checks that the parsed shape\n * has every required field before we trust it.\n */\nfunction isFileData(value: unknown): value is FileData {\n\tif (!value || typeof value !== 'object') return false;\n\tconst v = value as Record<string, unknown>;\n\treturn (\n\t\ttypeof v.fileName === 'string' &&\n\t\ttypeof v.fileType === 'string' &&\n\t\t'data' in v &&\n\t\ttypeof v.isBase64Encoded === 'boolean' &&\n\t\ttypeof v.subFolder === 'string'\n\t);\n}\n\n// Traversal helper\n/**\n * Iterates over every data item within a Grasshopper tree structure.\n *\n * @param tree - The Grasshopper tree structure containing branches of items.\n * @param handler - A callback function invoked for each {@link DataItem} found within the tree branches.\n */\nfunction forEachTreeItem(\n\ttree: GrasshopperComputeResponse['values'][0]['InnerTree'],\n\thandler: (item: DataItem) => void\n) {\n\tfor (const list of Object.values(tree)) {\n\t\tif (Array.isArray(list)) {\n\t\t\tfor (const item of list) handler(item);\n\t\t}\n\t}\n}\n\n// -----------------------------------------------------------------------------\n// Public API\n// -----------------------------------------------------------------------------\n\n/**\n * Extracts and processes values from a Grasshopper Compute response object.\n *\n * This function iterates through the internal tree structure of the response parameters,\n * extracts individual data items, and aggregates them into a structured result object.\n * Values can be mapped by their parameter names or unique identifiers.\n *\n * @template T - The type of the resulting parsed context values.\n * @param response - The raw response object received from the Grasshopper Compute service.\n * @param byId - Whether to use the parameter's unique ID as the key (true) or its name (false).\n * @param options - Configuration options for value extraction.\n * @param options.parseValues - Whether to attempt parsing complex data types into JavaScript objects.\n * @param options.rhino - An optional Rhino3dm instance used for geometry decoding.\n * @param options.stringOnly - If true, only items identified as strings will be included in the output.\n * @returns A result object containing the mapped values, where duplicate keys are aggregated into arrays.\n */\nexport function getValues<T = ParsedContext>(\n\tresponse: GrasshopperComputeResponse,\n\tbyId: boolean = false,\n\toptions: GetValuesOptions = {}\n): GetValuesResult<T> {\n\tconst { parseValues = true, rhino, stringOnly = false } = options;\n\tconst result: ParsedContext = {};\n\n\tfor (const param of response.values) {\n\t\tforEachTreeItem(param.InnerTree, (item) => {\n\t\t\t// Skip excluded types (e.g. WebDisplay) entirely — leaving them in\n\t\t\t// would write null into the aggregated result.\n\t\t\tif (isExcludedType(item.type)) return;\n\t\t\t// Skip non-string types if stringOnly is enabled\n\t\t\tif (stringOnly && item.type !== SYSTEM_TYPES.STRING) return;\n\n\t\t\tconst key = byId ? item.id : param.ParamName;\n\t\t\tif (!key) return;\n\n\t\t\tconst value = extractItemValue(item.data, item.type, parseValues, rhino);\n\n\t\t\tif (result[key] === undefined) {\n\t\t\t\tresult[key] = value;\n\t\t\t} else if (Array.isArray(result[key])) {\n\t\t\t\tresult[key].push(value);\n\t\t\t} else {\n\t\t\t\tresult[key] = [result[key], value];\n\t\t\t}\n\t\t});\n\t}\n\n\treturn { values: result as T };\n}\n\n/**\n * Extracts and decodes file data from a Grasshopper Compute response.\n *\n * This function iterates through all parameter values in the compute response,\n * identifies items that match the file data type, and attempts to decode their\n * JSON content into {@link FileData} objects.\n *\n * @param response - The response object received from a Grasshopper Compute request.\n * @returns An array of valid {@link FileData} objects extracted from the response trees.\n */\nexport function extractFileData(response: GrasshopperComputeResponse): FileData[] {\n\tconst output: FileData[] = [];\n\n\tfor (const param of response.values) {\n\t\tforEachTreeItem(param.InnerTree, (item) => {\n\t\t\tif (!item.type.includes(FILE_DATA_TYPE)) return;\n\n\t\t\tconst parsed = tryDecodeJSON(item.data);\n\t\t\tif (isFileData(parsed)) {\n\t\t\t\toutput.push(parsed);\n\t\t\t}\n\t\t});\n\t}\n\n\treturn output;\n}\n\n/**\n * Extracts a value or collection of values from a Grasshopper Compute response based on the provided criteria.\n *\n * This function searches through the `InnerTree` structures of the response values. If searching `byName`,\n * it returns all values (or a single value) within that parameter's tree. If searching `byId`, it specifically\n * targets items matching that unique identifier.\n *\n * @param response - The compute response object containing the results of a Grasshopper definition execution.\n * @param options - Search criteria, either a `{ byName: string }` to match a `ParamName`, or `{ byId: string }` to match a specific item ID.\n * @param parseOptions - Optional configuration for how values are extracted and filtered.\n * @param parseOptions.parseValues - Whether to process raw data into formatted values (defaults to `true`).\n * @param parseOptions.rhino - Optional Rhino/OpenNURBS instance used for geometry decoding.\n * @param parseOptions.stringOnly - If `true`, non-string types will be filtered out (defaults to `false`).\n *\n * @returns\n * - `undefined` if no matching parameter or items are found.\n * - A single extracted value if only one matching item exists.\n * - An array of extracted values if multiple matching items are found.\n */\nexport function getValue(\n\tresponse: GrasshopperComputeResponse,\n\toptions: { byName: string } | { byId: string },\n\tparseOptions: GetValuesOptions = {}\n): any {\n\tconst { parseValues = true, rhino, stringOnly = false } = parseOptions;\n\n\tlet targetParam: GrasshopperComputeResponse['values'][0] | undefined;\n\n\tif ('byName' in options) {\n\t\ttargetParam = response.values.find((p) => p.ParamName === options.byName);\n\t} else {\n\t\ttargetParam = response.values.find((p) => {\n\t\t\tlet found = false;\n\t\t\tforEachTreeItem(p.InnerTree, (item) => {\n\t\t\t\tif (item.id === options.byId) found = true;\n\t\t\t});\n\t\t\treturn found;\n\t\t});\n\t}\n\n\tif (!targetParam) return undefined;\n\n\tconst collected: any[] = [];\n\n\tforEachTreeItem(targetParam.InnerTree, (item) => {\n\t\tif ('byId' in options && item.id !== options.byId) return;\n\t\t// Skip excluded types (e.g. WebDisplay) entirely.\n\t\tif (isExcludedType(item.type)) return;\n\t\t// Skip non-string types if stringOnly is enabled\n\t\tif (stringOnly && item.type !== SYSTEM_TYPES.STRING) return;\n\t\tconst v = extractItemValue(item.data, item.type, parseValues, rhino);\n\t\tcollected.push(v);\n\t});\n\n\tif (collected.length === 0) return undefined;\n\tif (collected.length === 1) return collected[0];\n\treturn collected;\n}\n","import { downloadFileData } from '@/features/grasshopper/file-handling/handle-files';\nimport { FileBaseInfo, FileData } from '@/features/grasshopper/file-handling/types';\nimport type { MeshExtractionOptions } from '@/features/visualization/webdisplay/types';\nimport { RhinoComputeError, ErrorCodes } from '@/core/errors';\n\nimport { GrasshopperComputeResponse } from '../types';\n\nimport {\n\textractFileData,\n\tgetValue,\n\tgetValues,\n\tGetValuesOptions,\n\tGetValuesResult,\n\tParsedContext\n} from '../io/output/response-processors';\n\n/**\n * High-level wrapper for interacting with Grasshopper Compute responses.\n *\n * This class exposes a clean, consistent API for accessing parsed values,\n * geometry, and produced files. It is designed to be the primary interface\n * when working with Grasshopper results in client applications.\n */\nexport default class GrasshopperResponseProcessor {\n\t/**\n\t * Store the compute response for reuse.\n\t */\n\tconstructor(\n\t\tprivate readonly response: GrasshopperComputeResponse,\n\t\tprivate readonly debug: boolean = false\n\t) {}\n\n\t/**\n\t * Extract all values in the response.\n\t *\n\t * @typeParam T - Expected structure of the return value. Defaults to a simple key/value map. (later cast as needed)\n\t * @param byId - If true, keys are parameter IDs; if false, keys are parameter names.\n\t * @param options - Controls parsing behavior such as Rhino geometry decoding.\n\t * @returns Parsed Grasshopper output values.\n\t *\n\t * **Note:** Using `byId` only works with the custom VektorNode rhino.compute branch.\n\t *\n\t * @example\n\t * ```ts\n\t * const processor = new GrasshopperResponseProcessor(response);\n\t * const { values } = processor.getValues();\n\t * ```\n\t *\n\t * @example\n\t * ```ts\n\t * const { values } = processor.getValues(true); // keyed by param ID\n\t * ```\n\t */\n\tpublic getValues<T = ParsedContext>(\n\t\tbyId: boolean = false,\n\t\toptions: GetValuesOptions = {}\n\t): GetValuesResult<T> {\n\t\treturn getValues<T>(this.response, byId, options);\n\t}\n\n\t/**\n\t * Retrieve a specific value by parameter name or ID.\n\t *\n\t * @param selector - `{ byName }` for the human-readable name, `{ byId }` for the parameter GUID.\n\t * @param options - Parsing configuration (e.g. disable parsing or enable Rhino).\n\t * @returns Single parsed value, array of values, or undefined if the parameter is absent.\n\t *\n\t * @example\n\t * ```ts\n\t * const schema = processor.getValue({ byName: 'Schema' });\n\t * const output = processor.getValue({ byId: 'a4be1c1e-23f9-4c27-b942-7f3bb2c45c6f' });\n\t * ```\n\t *\n\t * **Note:** `byId` only works with the custom VektorNode rhino.compute branch.\n\t */\n\tpublic getValue(\n\t\tselector: { byName: string } | { byId: string },\n\t\toptions?: GetValuesOptions\n\t): any {\n\t\treturn getValue(this.response, selector, options);\n\t}\n\n\t/**\n\t * @deprecated Use `getValue({ byName })` instead.\n\t */\n\tpublic getValueByParamName(paramName: string, options?: GetValuesOptions): any {\n\t\treturn getValue(this.response, { byName: paramName }, options);\n\t}\n\n\t/**\n\t * @deprecated Use `getValue({ byId })` instead.\n\t */\n\tpublic getValueByParamId(paramId: string, options?: GetValuesOptions): any {\n\t\treturn getValue(this.response, { byId: paramId }, options);\n\t}\n\n\t/**\n\t * Convert all geometry results into Three.js mesh objects.\n\t *\n\t * This uses internal helpers to decode Rhino geometry into Three.js\n\t * primitives such as meshes and lines, making them ready for rendering.\n\t *\n\t * All processing options (scaling, positioning, compression, etc.) can be customized.\n\t * The processor's debug flag is merged with options - explicit options take precedence.\n\t *\n\t * **Note:** This only works when using the **Selva Display** component in Grasshopper, and requires the custom branch of rhino.compute from VektorNode. This method dynamically imports three.js visualization modules. Ensure three.js is installed as a peer dependency if you use this feature.\n\t *\n\t * @param options - Configuration for mesh extraction and parsing. Overrides processor's debug flag if provided.\n\t * @returns Promise resolving to an array of Three.js mesh objects.\n\t * @throws {RhinoComputeError} If three.js visualization module cannot be loaded.\n\t *\n\t * @example\n\t * ```ts\n\t * const meshes = await processor.extractMeshesFromResponse();\n\t * scene.add(...meshes);\n\t * ```\n\t *\n\t * @example\n\t * ```ts\n\t * const meshes = await processor.extractMeshesFromResponse({\n\t * debug: true,\n\t * allowScaling: true,\n\t * allowAutoPosition: false,\n\t * parsing: {\n\t * mergeByMaterial: false,\n\t * applyTransforms: true,\n\t * debug: true,\n\t * },\n\t * });\n\t * ```\n\t */\n\tpublic async extractMeshesFromResponse(options?: MeshExtractionOptions) {\n\t\tconst mergedOptions: MeshExtractionOptions = {\n\t\t\tdebug: this.debug,\n\t\t\t...options\n\t\t};\n\n\t\t// Dynamically import visualization module to avoid coupling three.js at module load time.\n\t\t// Narrow the try/catch to the import only — errors from mesh extraction itself should\n\t\t// propagate so callers can debug them, not get re-wrapped as \"failed to load\".\n\t\tlet getThreeMeshesFromComputeResponse: typeof import('@/features/visualization').getThreeMeshesFromComputeResponse;\n\t\ttry {\n\t\t\t({ getThreeMeshesFromComputeResponse } = await import('@/features/visualization'));\n\t\t} catch (error) {\n\t\t\tthrow new RhinoComputeError(\n\t\t\t\t'Failed to load three.js visualization module. Ensure three.js is installed as a peer dependency.',\n\t\t\t\tErrorCodes.INVALID_STATE,\n\t\t\t\t{\n\t\t\t\t\tcontext: { originalError: error instanceof Error ? error.message : String(error) }\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\n\t\treturn getThreeMeshesFromComputeResponse(this.response, mergedOptions);\n\t}\n\n\t/**\n\t * Extract internal file data structures from the response.\n\t * This includes Grasshopper-generated textures, JSON exports,\n\t * CAD formats, or any file structure packaged in the response.\n\t *\n\t * **Note:** This only works when using the **Block to File** and **Geometry To File** components from the Selva plugin in Grasshopper, and requires the custom branch of rhino.compute from VektorNode.\n\t *\n\t * @returns Raw file data entries.\n\t */\n\tprivate getFileData(): FileData[] {\n\t\treturn extractFileData(this.response);\n\t}\n\n\t/**\n\t * Download all files generated by Grasshopper, optionally including\n\t * additional user-provided files.\n\t *\n\t * Files are grouped under the specified folder name when downloaded.\n\t *\n\t * @param folderName - Name for the download directory.\n\t * @param additionalFiles - Extra files to package (single file, array, or null).\n\t *\n\t * @example\n\t * ```ts\n\t * processor.getAndDownloadFiles('gh-output');\n\t * ```\n\t *\n\t * @example\n\t * ```ts\n\t * const extra = { name: 'notes.txt', data: 'Example' };\n\t * processor.getAndDownloadFiles('project', extra);\n\t * ```\n\t */\n\tpublic getAndDownloadFiles(\n\t\tfolderName: string,\n\t\tadditionalFiles?: FileBaseInfo[] | FileBaseInfo | null\n\t) {\n\t\tconst files = this.getFileData();\n\t\tdownloadFileData(files, folderName, additionalFiles);\n\t}\n}\n","import { getLogger } from './logger';\n\nexport function warnIfClientSide(functionName: string, suppress?: boolean): void {\n\tif (suppress) {\n\t\treturn;\n\t}\n\n\tif (typeof window !== 'undefined') {\n\t\tgetLogger().warn(\n\t\t\t`Warning: ${functionName} is running on the client side. For better performance and security, consider running this on the server side.`\n\t\t);\n\t}\n}\n","import { fetchRhinoCompute } from '@/core';\nimport { base64ByteArray, encodeStringToBase64, isBase64 } from '@/core/utils/encoding';\nimport { warnIfClientSide } from '@/core/utils/warnings';\n\nimport {\n\tGrasshopperRequestSchema,\n\tGrasshopperComputeConfig,\n\tGrasshopperComputeResponse,\n\tDataTree\n} from '../types';\n\n/**\n * Runs a Rhino Compute job using the provided tree prototypes and Grasshopper definition.\n *\n * @public Use this for direct compute control. For high-level API, use `GrasshopperClient.solve()`.\n *\n * @param dataTree - An array of `DataTree` objects representing the input data for the compute job.\n * @param definition - The Grasshopper definition, which can be:\n * - A URL string (e.g., 'https://example.com/definition.gh')\n * - A base64-encoded string of the .gh file\n * - A plain string (will be base64-encoded)\n * - A Uint8Array of the .gh file (will be base64-encoded)\n * @param config - Compute configuration (server URL, API key, etc. along with optional timeout, units, etc.)\n * @returns An object containing the compute result and extracted file data.\n *\n * @example\n * // Using a URL\n * await solveGrasshopperDefinition(trees, 'https://example.com/definition.gh', config);\n *\n * // Using a base64 string\n * await solveGrasshopperDefinition(trees, 'UEsDBBQAAAAIAL...', config);\n *\n * // Using binary data\n * const fileData = new Uint8Array([...]);\n * await solveGrasshopperDefinition(trees, fileData, config);\n */\nexport async function solveGrasshopperDefinition(\n\tdataTree: DataTree[],\n\tdefinition: string | Uint8Array,\n\tconfig: GrasshopperComputeConfig\n): Promise<GrasshopperComputeResponse> {\n\tif (config.debug) {\n\t\twarnIfClientSide(\n\t\t\t'solveGrasshopperDefinition',\n\t\t\tconfig.suppressBrowserWarning ?? config.suppressClientSideWarning\n\t\t);\n\t}\n\n\tconst args = prepareGrasshopperArgs(definition, dataTree);\n\tapplyOptionalComputeSettings(args, config);\n\n\tconst result = await fetchRhinoCompute('grasshopper', args, config);\n\n\tif ('pointer' in result) {\n\t\t// Strip via shallow copy rather than `delete result.pointer` so we don't\n\t\t// mutate an object the scheduler (or any caller holding a reference) may\n\t\t// have already observed.\n\t\tconst { pointer: _pointer, ...rest } = result as GrasshopperComputeResponse & {\n\t\t\tpointer?: unknown;\n\t\t};\n\t\treturn rest as GrasshopperComputeResponse;\n\t}\n\n\treturn result;\n}\n\n// ============================================================================\n// Grasshopper Arguments\n// ============================================================================\n\n/**\n * Prepares Grasshopper arguments from a definition and data tree.\n * Automatically detects the definition format and converts it appropriately.\n *\n * @param definition - Can be a URL, base64 string, plain string, or Uint8Array\n * @param dataTree - Array of DataTree objects for compute inputs\n * @internal\n */\nexport function prepareGrasshopperArgs(\n\tdefinition: string | Uint8Array,\n\tdataTree: DataTree[]\n): GrasshopperRequestSchema {\n\tconst args: GrasshopperRequestSchema = {\n\t\talgo: null,\n\t\tpointer: null,\n\t\tvalues: dataTree\n\t};\n\n\tif (definition instanceof Uint8Array) {\n\t\t// Binary data → convert to base64\n\t\targs.algo = base64ByteArray(definition);\n\t} else if (/^https?:\\/\\//i.test(definition)) {\n\t\t// URL → use as pointer reference\n\t\targs.pointer = definition;\n\t} else if (isBase64(definition)) {\n\t\t// Already base64 → use as-is\n\t\targs.algo = definition;\n\t} else {\n\t\t// Plain string → encode to base64\n\t\targs.algo = encodeStringToBase64(definition);\n\t}\n\n\treturn args;\n}\n\n/**\n * @internal\n */\nexport function applyOptionalComputeSettings(\n\targlist: GrasshopperRequestSchema,\n\toptions: GrasshopperComputeConfig\n): void {\n\tif (options.cachesolve != null) arglist.cachesolve = options.cachesolve;\n\tif (options.modelunits != null) arglist.modelunits = options.modelunits;\n\tif (options.angletolerance != null) arglist.angletolerance = options.angletolerance;\n\tif (options.absolutetolerance != null) arglist.absolutetolerance = options.absolutetolerance;\n\tif (options.dataversion != null) arglist.dataversion = options.dataversion;\n}\n","import { getLogger } from '@/core';\nimport type { InputParamSchema } from '../../types';\n\n/**\n * @internal Pre-processing helpers for raw input parameters.\n */\n\n/**\n * Pre-processes raw input to normalize default values\n * Handles data tree structures, flattening, and type parsing\n *\n * @param input - The input parameter to pre-process\n *\n * @remarks\n * Handles:\n * - Empty data trees → undefined\n * - Tree structure preservation for tree access parameters\n * - Flattening of multiple values\n * - Type-aware parsing (numbers, booleans, JSON)\n */\nexport function preProcessInputDefault(input: InputParamSchema): void {\n\tif (typeof input.default !== 'object' || input.default === null) {\n\t\treturn;\n\t}\n\n\tif (!('innerTree' in input.default)) {\n\t\tgetLogger().warn('Unexpected structure in input.default:', input.default);\n\t\tinput.default = null;\n\t\treturn;\n\t}\n\n\tconst innerTree = (input.default as any).innerTree;\n\n\t// If innerTree is empty, set default to undefined\n\tif (Object.keys(innerTree).length === 0) {\n\t\tinput.default = undefined;\n\t\treturn;\n\t}\n\n\t// If treeAccess is true or atMost > 1, preserve the tree structure\n\tif (input.treeAccess || (input.atMost && input.atMost > 1)) {\n\t\t// Convert each branch to an array of parsed data\n\t\tconst tree: Record<string, any[]> = {};\n\t\tfor (const [branch, items] of Object.entries(innerTree)) {\n\t\t\ttree[branch] = (items as any[]).map((item) => {\n\t\t\t\t// Try to parse numbers, booleans, or JSON if possible\n\t\t\t\tif (typeof item.data === 'string') {\n\t\t\t\t\tif (item.type === 'System.Double' || item.type === 'System.Int32') {\n\t\t\t\t\t\tconst num = Number(item.data);\n\t\t\t\t\t\treturn Number.isNaN(num) ? item.data : num;\n\t\t\t\t\t}\n\t\t\t\t\tif (item.type === 'System.Boolean') {\n\t\t\t\t\t\treturn item.data.toLowerCase() === 'true';\n\t\t\t\t\t}\n\t\t\t\t\tif (item.type.startsWith('Rhino.Geometry') || item.type === 'System.String') {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\treturn JSON.parse(item.data);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\treturn item.data;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn item.data;\n\t\t\t});\n\t\t}\n\t\tinput.default = tree;\n\t\treturn;\n\t}\n\n\t// Otherwise, flatten all values as before\n\tconst allValues: any[] = [];\n\tfor (const items of Object.values(innerTree)) {\n\t\tif (Array.isArray(items)) {\n\t\t\titems.forEach((item) => {\n\t\t\t\tif (item && typeof item === 'object' && 'data' in item) {\n\t\t\t\t\tallValues.push(item.data);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\tif (allValues.length === 0) {\n\t\tinput.default = undefined;\n\t} else if (allValues.length === 1) {\n\t\tinput.default = allValues[0];\n\t} else {\n\t\tinput.default = allValues;\n\t}\n}\n","import { RhinoComputeError } from '@/core/errors';\nimport { getLogger } from '@/core';\nimport type { InputParamSchema } from '../../types';\n\n/**\n * Type for a single value transformer function\n */\nexport type ValueTransformer<T> = (value: unknown) => T | null;\n\n/**\n * Options for processing input values\n */\nexport interface ProcessValueOptions<T> {\n\t/**\n\t * Function to transform a single value\n\t */\n\ttransform: ValueTransformer<T>;\n\t/**\n\t * Whether to set default to undefined if all values fail transformation\n\t * @default true\n\t */\n\tsetUndefinedOnEmpty?: boolean;\n}\n\n/**\n * Generic utility to process input default values (arrays or single values)\n *\n * @internal\n */\nfunction processInputValue<T>(input: InputParamSchema, options: ProcessValueOptions<T>): void {\n\tconst { transform, setUndefinedOnEmpty = true } = options;\n\n\t// Don't process undefined or null - preserve them as is\n\tif (input.default === undefined || input.default === null) {\n\t\treturn;\n\t}\n\n\tif (Array.isArray(input.default)) {\n\t\tconst processedArray = input.default.map(transform).filter((v): v is T => v !== null);\n\n\t\t// For arrays, always set to undefined if empty (regardless of setUndefinedOnEmpty)\n\t\tinput.default = processedArray.length > 0 ? processedArray : undefined;\n\t} else {\n\t\tconst transformed = transform(input.default);\n\t\tif (transformed !== null) {\n\t\t\t// Transformation succeeded\n\t\t\tinput.default = transformed;\n\t\t} else {\n\t\t\t// Transformation failed - set to undefined only if setUndefinedOnEmpty is true\n\t\t\tif (setUndefinedOnEmpty) {\n\t\t\t\tinput.default = undefined;\n\t\t\t}\n\t\t\t// Otherwise preserve original value\n\t\t}\n\t}\n}\n\n/**\n * Creates a numeric value transformer (for Number and Integer types)\n */\nfunction createNumericTransformer(): ValueTransformer<number> {\n\treturn (value: unknown): number | null => {\n\t\tif (typeof value === 'number') {\n\t\t\treturn value;\n\t\t}\n\t\tif (typeof value === 'string') {\n\t\t\tconst parsed = Number(value.trim());\n\t\t\treturn Number.isNaN(parsed) ? null : parsed;\n\t\t}\n\t\treturn null;\n\t};\n}\n\n/**\n * Creates a boolean value transformer\n */\nfunction createBooleanTransformer(): ValueTransformer<boolean> {\n\treturn (value: unknown): boolean | null => {\n\t\tif (typeof value === 'boolean') {\n\t\t\treturn value;\n\t\t}\n\t\tif (typeof value === 'string') {\n\t\t\tconst lowerValue = value.toLowerCase();\n\t\t\tif (lowerValue === 'true') return true;\n\t\t\tif (lowerValue === 'false') return false;\n\t\t\tthrow new Error(`Invalid boolean string: \"${value}\"`);\n\t\t}\n\t\treturn null;\n\t};\n}\n\n/**\n * Creates a text value transformer that removes surrounding quotes\n */\nfunction createTextTransformer(): ValueTransformer<string> {\n\treturn (value: unknown): string | null => {\n\t\tif (typeof value === 'string') {\n\t\t\t// Handle strings with both start and end quotes\n\t\t\tif (value.startsWith('\"') && value.endsWith('\"')) {\n\t\t\t\treturn value.slice(1, -1);\n\t\t\t}\n\t\t\t// Handle strings that start with quote but don't end with one (legacy behavior)\n\t\t\tif (value.startsWith('\"')) {\n\t\t\t\treturn value.slice(1, -1);\n\t\t\t}\n\t\t\treturn value;\n\t\t}\n\t\treturn null;\n\t};\n}\n\n/**\n * Creates a color value transformer that normalizes RGB strings\n */\nfunction createColorTransformer(): ValueTransformer<string> {\n\treturn (value: unknown): string | null => {\n\t\tif (typeof value === 'string') {\n\t\t\t// Remove surrounding quotes if present\n\t\t\tlet cleaned = value.trim();\n\t\t\tif (cleaned.startsWith('\"') && cleaned.endsWith('\"')) {\n\t\t\t\tcleaned = cleaned.slice(1, -1).trim();\n\t\t\t}\n\t\t\t// Return as-is if it's a valid RGB string\n\t\t\treturn cleaned;\n\t\t}\n\t\treturn null;\n\t};\n}\n\n/**\n * Processes color input parameters\n */\nfunction processColorInput(input: InputParamSchema): void {\n\tprocessInputValue(input, {\n\t\ttransform: createColorTransformer(),\n\t\tsetUndefinedOnEmpty: false\n\t});\n}\n\n/**\n * Creates an object value transformer that parses JSON strings\n */\nfunction createObjectTransformer(inputName: string = 'unknown'): ValueTransformer<object> {\n\treturn (value: unknown): object | null => {\n\t\tif (typeof value === 'object' && value !== null) {\n\t\t\treturn value;\n\t\t}\n\t\tif (typeof value === 'string' && value.trim() !== '') {\n\t\t\ttry {\n\t\t\t\tconst parsed = JSON.parse(value);\n\t\t\t\tif (typeof parsed === 'object' && parsed !== null) {\n\t\t\t\t\treturn parsed;\n\t\t\t\t}\n\t\t\t\tgetLogger().warn(`Parsed value for input ${inputName} is not an object`);\n\t\t\t\treturn null;\n\t\t\t} catch (err) {\n\t\t\t\tgetLogger().warn(`Failed to parse object value \"${value}\" for input ${inputName}`, err);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t};\n}\n\n/**\n * Applies rounding with tolerance to avoid floating-point artifacts\n */\nfunction applyRounding(value: number, decimalPlaces: number, tolerance: number): number {\n\tconst rounded = Number(value.toFixed(decimalPlaces));\n\n\t// If the difference is within tolerance, use the rounded value\n\tif (Math.abs(value - rounded) < tolerance) {\n\t\treturn rounded;\n\t}\n\n\treturn value;\n}\n\n/**\n * Calculates the step size for a given numeric input value based on its decimal precision.\n */\nfunction getInputStepSize(value: number, roundingTolerance: number = 1e-8): number {\n\tif (!Number.isFinite(value)) return 0.1;\n\tif (value === 0) return 0.1;\n\n\tconst abs = Math.abs(value);\n\n\tif (abs >= 1) {\n\t\tconst str = String(value);\n\t\tconst decimalPart = str.split('.')[1];\n\t\tif (decimalPart && decimalPart.length > 0) {\n\t\t\tconst decimals = Math.min(decimalPart.length, 12);\n\t\t\tconst step = Math.pow(10, -decimals);\n\t\t\tconst rounded = Number(step.toFixed(decimals));\n\t\t\treturn Math.abs(rounded - step) < roundingTolerance ? rounded : step;\n\t\t}\n\t\treturn 1;\n\t}\n\n\t// Handle exponential notation\n\tconst s = String(value);\n\tconst expMatch = s.toLowerCase().match(/e(-?\\d+)/);\n\tif (expMatch) {\n\t\tconst exp = Number(expMatch[1]);\n\t\tif (exp < 0 || s.toLowerCase().includes('e-')) {\n\t\t\tconst absExp = Math.abs(exp);\n\t\t\tconst step = Math.pow(10, -absExp);\n\t\t\tconst rounded = Number(step.toFixed(absExp));\n\t\t\treturn Math.abs(rounded - step) < roundingTolerance ? rounded : step;\n\t\t}\n\t\treturn 0.1;\n\t}\n\n\t// Handle standard decimal notation\n\tconst MAX_DECIMALS = 12;\n\tconst fixed = abs.toFixed(MAX_DECIMALS);\n\tconst trimmed = fixed.replace(/0+$/, '');\n\tconst decimals = Math.min((trimmed.split('.')[1] || '').length, MAX_DECIMALS);\n\n\tif (decimals === 0) return 0.1;\n\n\tconst step = Math.pow(10, -decimals);\n\tconst rounded = Number(step.toFixed(decimals));\n\treturn Math.abs(rounded - step) < roundingTolerance ? rounded : step;\n}\n\n/**\n * Processes numeric input parameters including step size and decimal places\n */\nfunction processNumericInput(input: InputParamSchema, roundingTolerance: number = 1e-8): void {\n\tconst isIntegerType = input.paramType === 'Integer';\n\n\t// Convert string values to numbers\n\tprocessInputValue(input, {\n\t\ttransform: createNumericTransformer()\n\t});\n\n\t// Round to integer if it's an integer type\n\tif (isIntegerType) {\n\t\tif (Array.isArray(input.default)) {\n\t\t\tinput.default = input.default.map((val) => (typeof val === 'number' ? Math.round(val) : val));\n\t\t} else if (typeof input.default === 'number') {\n\t\t\tinput.default = Math.round(input.default);\n\t\t}\n\n\t\t// Integer inputs always have step size of 1\n\t\tinput.stepSize = 1;\n\t\treturn;\n\t}\n\n\t// Calculate step size from the first numeric value\n\tconst firstValue = Array.isArray(input.default) ? input.default[0] : input.default;\n\n\tlet stepSource: number | undefined;\n\n\tif (typeof firstValue === 'number' && Number.isFinite(firstValue) && firstValue !== 0) {\n\t\tstepSource = firstValue;\n\t} else if (\n\t\ttypeof input.minimum === 'number' &&\n\t\tNumber.isFinite(input.minimum) &&\n\t\tinput.minimum !== 0\n\t) {\n\t\tstepSource = input.minimum;\n\t} else if (\n\t\ttypeof input.maximum === 'number' &&\n\t\tNumber.isFinite(input.maximum) &&\n\t\tinput.maximum !== 0\n\t) {\n\t\tstepSource = input.maximum;\n\t}\n\n\tif (stepSource !== undefined) {\n\t\tinput.stepSize = getInputStepSize(stepSource, roundingTolerance);\n\t} else {\n\t\tinput.stepSize = 0.1;\n\t}\n\n\t// Apply precision to all numeric values\n\tif (typeof input.stepSize === 'number') {\n\t\tlet decimalPlaces = 0;\n\t\tconst stepStr = String(input.stepSize);\n\n\t\tconst expMatch = stepStr.toLowerCase().match(/e(-?\\d+)/);\n\t\tif (expMatch) {\n\t\t\tdecimalPlaces = Math.abs(Number(expMatch[1]));\n\t\t} else {\n\t\t\tdecimalPlaces = stepStr.split('.')[1]?.length ?? 0;\n\t\t}\n\n\t\t// Infer decimal places from small values when step size doesn't provide enough precision\n\t\tif (\n\t\t\tdecimalPlaces === 0 &&\n\t\t\ttypeof firstValue === 'number' &&\n\t\t\tfirstValue !== 0 &&\n\t\t\tMath.abs(firstValue) < 1\n\t\t) {\n\t\t\tconst inferred = Math.ceil(-Math.log10(Math.abs(firstValue)));\n\t\t\tif (Number.isFinite(inferred) && inferred > 0) {\n\t\t\t\tdecimalPlaces = inferred;\n\t\t\t}\n\t\t}\n\n\t\tdecimalPlaces = Math.min(Math.max(decimalPlaces, 0), 12);\n\n\t\t// Apply precision to all values\n\t\tif (Array.isArray(input.default)) {\n\t\t\tinput.default = input.default.map((val) =>\n\t\t\t\ttypeof val === 'number' ? applyRounding(val, decimalPlaces, roundingTolerance) : val\n\t\t\t);\n\t\t} else if (typeof input.default === 'number') {\n\t\t\tinput.default = applyRounding(input.default, decimalPlaces, roundingTolerance);\n\t\t}\n\t}\n}\n\n/**\n * Processes boolean input parameters\n */\nfunction processBooleanInput(input: InputParamSchema): void {\n\ttry {\n\t\tprocessInputValue(input, {\n\t\t\ttransform: createBooleanTransformer(),\n\t\t\tsetUndefinedOnEmpty: false\n\t\t});\n\t} catch (error) {\n\t\t// Re-throw as RhinoComputeError for consistency\n\t\tif (error instanceof Error) {\n\t\t\tthrow new RhinoComputeError(error.message);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Processes text input parameters\n */\nfunction processTextInput(input: InputParamSchema): void {\n\tprocessInputValue(input, {\n\t\ttransform: createTextTransformer(),\n\t\tsetUndefinedOnEmpty: false\n\t});\n}\n\n/**\n * Processes object input parameters by parsing JSON strings\n */\nfunction parseToObject(input: InputParamSchema): void {\n\tprocessInputValue(input, {\n\t\ttransform: createObjectTransformer(input.nickname || 'unnamed'),\n\t\tsetUndefinedOnEmpty: true\n\t});\n}\n\n/**\n * Processes a ValueList input parameter.\n * Validates that the values object exists and contains at least one entry.\n */\nfunction processValueListInput(input: InputParamSchema): void {\n\tif (!input.values || typeof input.values !== 'object' || Object.keys(input.values).length === 0) {\n\t\tthrow RhinoComputeError.missingValues(input.nickname || 'unnamed', 'ValueList');\n\t}\n\n\t// Validate that default is one of the available values (if default exists)\n\tif (input.default !== undefined && input.default !== null) {\n\t\t// Case-insensitive check\n\t\tconst defaultLower = String(input.default).toLowerCase();\n\t\tconst valueExists = Object.keys(input.values).some((key) => key.toLowerCase() === defaultLower);\n\n\t\tif (!valueExists) {\n\t\t\tgetLogger().warn(\n\t\t\t\t`ValueList input \"${input.nickname || 'unnamed'}\" default value \"${input.default}\" is not in available values`\n\t\t\t);\n\t\t}\n\t}\n}\n\n/**\n * Maps parameter types to their parsing functions\n */\nexport const PARSERS: Record<string, (input: InputParamSchema) => void> = {\n\tNumber: processNumericInput,\n\tInteger: processNumericInput,\n\tBoolean: processBooleanInput,\n\tText: processTextInput,\n\tValueList: processValueListInput,\n\tGeometry: parseToObject,\n\tFile: parseToObject,\n\tColor: processColorInput\n};\n\n// Export parser functions for direct use\nexport {\n\tprocessNumericInput,\n\tprocessBooleanInput,\n\tprocessTextInput,\n\tparseToObject,\n\tprocessValueListInput,\n\tprocessColorInput\n};\n","import { RhinoComputeError } from '@/core/errors';\n\nimport { preProcessInputDefault } from './input-validators';\nimport { PARSERS } from './input-parsers';\nimport { getLogger } from '@/core/utils/logger';\n\n/** Canonical paramType for each supported type, keyed by its lowercased form. */\nconst CANONICAL_PARAM_TYPES = new Map(Object.keys(PARSERS).map((key) => [key.toLowerCase(), key]));\n\n/**\n * Returns the canonical casing for a paramType (e.g. \"valuelist\" → \"ValueList\"),\n * or the original value unchanged when it isn't a known type so the\n * unknown-paramType error still surfaces downstream.\n */\nfunction canonicalizeParamType(paramType: string): string {\n\treturn CANONICAL_PARAM_TYPES.get(paramType?.toLowerCase()) ?? paramType;\n}\n\nimport type {\n\tBaseInputType,\n\tBooleanInputType,\n\tGeometryInputType,\n\tInputParam,\n\tNumericInputType,\n\tInputParamSchema,\n\tTextInputType,\n\tValueListInputType,\n\tFileInputType,\n\tColorInputType,\n\tInputParseError\n} from '../../types';\n\n/**\n * Creates a safe default InputType when processing fails\n */\nfunction createSafeDefault(rawInput: InputParamSchema, baseInput: BaseInputType): InputParam {\n\tconst isList = (rawInput.atMost ?? 1) > 1;\n\tswitch (rawInput.paramType) {\n\t\tcase 'Number':\n\t\tcase 'Integer':\n\t\t\treturn {\n\t\t\t\t...baseInput,\n\t\t\t\tparamType: rawInput.paramType,\n\t\t\t\tminimum: rawInput.minimum,\n\t\t\t\tmaximum: rawInput.maximum,\n\t\t\t\tatLeast: rawInput.atLeast,\n\t\t\t\tatMost: rawInput.atMost,\n\t\t\t\tdefault: isList ? [0] : 0\n\t\t\t} as NumericInputType;\n\t\tcase 'Boolean':\n\t\t\treturn {\n\t\t\t\t...baseInput,\n\t\t\t\tparamType: 'Boolean',\n\t\t\t\tdefault: isList ? [false] : false\n\t\t\t} as BooleanInputType;\n\t\tcase 'Text':\n\t\t\treturn {\n\t\t\t\t...baseInput,\n\t\t\t\tparamType: 'Text',\n\t\t\t\tdefault: isList ? [''] : ''\n\t\t\t} as TextInputType;\n\t\tcase 'ValueList':\n\t\t\treturn {\n\t\t\t\t...baseInput,\n\t\t\t\tparamType: 'ValueList',\n\t\t\t\tvalues: rawInput.values ?? {},\n\t\t\t\tdefault: isList ? [rawInput.default] : rawInput.default\n\t\t\t} as ValueListInputType;\n\t\tcase 'File':\n\t\t\treturn {\n\t\t\t\t...baseInput,\n\t\t\t\tparamType: 'File',\n\t\t\t\tdefault: isList ? [null] : null\n\t\t\t} as FileInputType;\n\t\tcase 'Color':\n\t\t\treturn {\n\t\t\t\t...baseInput,\n\t\t\t\tparamType: 'Color',\n\t\t\t\tdefault: isList ? ['0, 0, 0'] : '0, 0, 0'\n\t\t\t} as ColorInputType;\n\t\tdefault:\n\t\t\treturn {\n\t\t\t\t...baseInput,\n\t\t\t\tparamType: 'Geometry',\n\t\t\t\tdefault: isList ? [null] : null\n\t\t\t} as GeometryInputType;\n\t}\n}\n\n/**\n * Processes a raw input parameter schema and converts it into a typed InputParam object.\n *\n * @internal This is an internal processor. Use `fetchParsedDefinitionIO()` to get processed inputs instead.\n *\n * This function handles the transformation of raw input parameter data from Grasshopper into\n * a structured, type-safe format. It performs validation, type-specific processing, and error\n * handling for various parameter types including numeric, boolean, text, geometry, point, and line inputs.\n *\n * @param rawInput - The raw input parameter schema to process\n * @returns A fully processed and typed InputParam object with appropriate type-specific properties\n *\n * @throws {RhinoComputeError} When an unknown paramType is encountered\n * @throws {Error} Re-throws any non-RhinoComputeError exceptions\n *\n * @remarks\n * The function performs the following operations:\n * - Extracts base properties common to all input types\n * - Preprocesses the raw input data\n * - Applies type-specific validation and transformation\n * - Handles errors gracefully by creating safe default values for validation errors\n *\n * Supported parameter types:\n * - `Number` and `Integer`: Numeric inputs with optional min/max constraints\n * - `Boolean`: Boolean flag inputs\n * - `Text`: String inputs\n * - `Geometry`: Generic geometry objects\n * - `Point`: 3D point objects\n * - `Line`: Line objects\n *\n * @example\n * ```typescript\n * const rawInput = {\n * name: 'Length',\n * paramType: 'Number',\n * minimum: 0,\n * maximum: 100,\n * default: 50\n * };\n * const processedInput = processInput(rawInput);\n * ```\n */\nexport function processInput(rawInput: InputParamSchema): InputParam {\n\treturn processInputWithError(rawInput).input;\n}\n\n/**\n * Like {@link processInput}, but reports validation failures back to the caller\n * instead of swallowing them with a logger warning.\n *\n * On success: `{ input, error: undefined }`.\n * On a recoverable validation failure: `{ input: <safe default>, error: {...} }`.\n *\n * Unexpected (non-RhinoComputeError) failures still throw — they indicate a\n * programming bug, not bad user input.\n *\n * @internal Used by {@link processInputsWithErrors} / {@link fetchParsedDefinitionIO}.\n */\nexport function processInputWithError(rawInput: InputParamSchema): {\n\tinput: InputParam;\n\terror?: InputParseError;\n} {\n\tconst baseInput: BaseInputType = {\n\t\tdescription: rawInput.description,\n\t\tname: rawInput.name,\n\t\tnickname: rawInput.nickname,\n\t\ttreeAccess: rawInput.treeAccess,\n\t\tgroupName: rawInput.groupName ?? '',\n\t\tid: rawInput.id\n\t};\n\n\t// Normalize paramType to its canonical casing so callers can send any case\n\t// (e.g. Selva schemas emit lowercase \"valueList\" while the plugin reports\n\t// \"ValueList\"). Both the PARSERS lookup and the switch below match exactly,\n\t// so we canonicalize once up front rather than at each comparison site.\n\trawInput.paramType = canonicalizeParamType(rawInput.paramType);\n\n\ttry {\n\t\tpreProcessInputDefault(rawInput);\n\n\t\tconst parser = PARSERS[rawInput.paramType];\n\t\tif (!parser) {\n\t\t\tthrow RhinoComputeError.unknownParamType(rawInput.paramType, rawInput.name);\n\t\t}\n\n\t\tparser(rawInput);\n\n\t\tswitch (rawInput.paramType) {\n\t\t\tcase 'Number':\n\t\t\tcase 'Integer':\n\t\t\t\treturn {\n\t\t\t\t\tinput: {\n\t\t\t\t\t\t...baseInput,\n\t\t\t\t\t\tparamType: rawInput.paramType,\n\t\t\t\t\t\tminimum: rawInput.minimum,\n\t\t\t\t\t\tmaximum: rawInput.maximum,\n\t\t\t\t\t\tatLeast: rawInput.atLeast,\n\t\t\t\t\t\tatMost: rawInput.atMost,\n\t\t\t\t\t\tstepSize: rawInput.stepSize,\n\t\t\t\t\t\tdefault: rawInput.default as number | undefined\n\t\t\t\t\t} as NumericInputType\n\t\t\t\t};\n\t\t\tcase 'Boolean':\n\t\t\t\treturn {\n\t\t\t\t\tinput: {\n\t\t\t\t\t\t...baseInput,\n\t\t\t\t\t\tparamType: 'Boolean',\n\t\t\t\t\t\tdefault: rawInput.default as boolean | undefined\n\t\t\t\t\t} as BooleanInputType\n\t\t\t\t};\n\t\t\tcase 'Text':\n\t\t\t\treturn {\n\t\t\t\t\tinput: {\n\t\t\t\t\t\t...baseInput,\n\t\t\t\t\t\tparamType: 'Text',\n\t\t\t\t\t\tdefault: rawInput.default as string | undefined\n\t\t\t\t\t} as TextInputType\n\t\t\t\t};\n\t\t\tcase 'ValueList':\n\t\t\t\treturn {\n\t\t\t\t\tinput: {\n\t\t\t\t\t\t...baseInput,\n\t\t\t\t\t\tparamType: 'ValueList',\n\t\t\t\t\t\tvalues: rawInput.values as Record<string, string>,\n\t\t\t\t\t\tdefault: rawInput.default as string | undefined\n\t\t\t\t\t} as ValueListInputType\n\t\t\t\t};\n\t\t\tcase 'Geometry':\n\t\t\t\treturn {\n\t\t\t\t\tinput: {\n\t\t\t\t\t\t...baseInput,\n\t\t\t\t\t\tparamType: rawInput.paramType as 'Geometry',\n\t\t\t\t\t\tdefault: rawInput.default as object | string | undefined\n\t\t\t\t\t} as GeometryInputType\n\t\t\t\t};\n\t\t\tcase 'File':\n\t\t\t\treturn {\n\t\t\t\t\tinput: {\n\t\t\t\t\t\t...baseInput,\n\t\t\t\t\t\tparamType: rawInput.paramType as 'File',\n\t\t\t\t\t\tacceptedFormats: rawInput.acceptedFormats,\n\t\t\t\t\t\tdefault: rawInput.default as object | string | undefined\n\t\t\t\t\t} as FileInputType\n\t\t\t\t};\n\t\t\tcase 'Color':\n\t\t\t\treturn {\n\t\t\t\t\tinput: {\n\t\t\t\t\t\t...baseInput,\n\t\t\t\t\t\tparamType: 'Color',\n\t\t\t\t\t\tdefault: rawInput.default as string | undefined\n\t\t\t\t\t} as ColorInputType\n\t\t\t\t};\n\t\t\tdefault:\n\t\t\t\tthrow RhinoComputeError.unknownParamType(rawInput.paramType, rawInput.name);\n\t\t}\n\t} catch (error) {\n\t\tif (error instanceof RhinoComputeError) {\n\t\t\tgetLogger().error(`Validation error for input ${rawInput.name || 'unknown'}:`, error.message);\n\t\t\treturn {\n\t\t\t\tinput: createSafeDefault(rawInput, baseInput),\n\t\t\t\terror: {\n\t\t\t\t\tinputName: rawInput.name || 'unknown',\n\t\t\t\t\tparamType: rawInput.paramType,\n\t\t\t\t\tmessage: error.message,\n\t\t\t\t\tcode: error.code\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\t// Unexpected failure — surface it.\n\t\tthrow new RhinoComputeError(\n\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t'VALIDATION_ERROR',\n\t\t\t{\n\t\t\t\tcontext: { paramName: rawInput.name, paramType: rawInput.paramType },\n\t\t\t\toriginalError: error instanceof Error ? error : new Error(String(error))\n\t\t\t}\n\t\t);\n\t}\n}\n\n/**\n * Processes raw Grasshopper input schemas into strongly-typed TypeScript interfaces.\n *\n * @internal This is an internal batch processor. Use `fetchParsedDefinitionIO()` to get processed inputs instead.\n *\n * Transforms each raw input parameter by:\n * - Normalizing default values (flattening data trees, parsing primitives)\n * - Applying type-specific parsing (Number, Text, Boolean, Geometry, etc.)\n * - Validating constraints (min/max, required fields)\n * - Converting to discriminated union types for type safety\n *\n * @param rawInputs - Array of raw input schemas from Rhino Compute API\n * @returns Array of processed, strongly-typed input parameters\n *\n * @remarks\n * - Empty data trees are converted to `undefined`\n * - Single values are extracted from arrays when appropriate\n * - Tree structures are preserved for list/tree access parameters\n * - Invalid inputs fall back to safe defaults with console warnings\n *\n * @example\n * ```typescript\n * const rawInputs = [\n * { paramType: 'Number', name: 'radius', minimum: 0, default: 10 },\n * { paramType: 'Text', name: 'label', default: 'Hello' }\n * ];\n *\n * const processed = processInputs(rawInputs);\n * // Result: [\n * // { paramType: 'Number', name: 'radius', minimum: 0, default: 10, ... },\n * // { paramType: 'Text', name: 'label', default: 'Hello', ... }\n * // ]\n *\n * // Now type-safe:\n * if (processed[0].paramType === 'Number') {\n * console.log(processed[0].minimum); // TypeScript knows this exists\n * }\n * ```\n *\n * @see {@link processInput} for individual input processing logic\n */\nexport function processInputs(rawInputs: InputParamSchema[]): InputParam[] {\n\treturn processInputsWithErrors(rawInputs).inputs;\n}\n\n/**\n * Like {@link processInputs}, but additionally returns a list of inputs that\n * failed validation and were filled with a safe default.\n *\n * @internal Used by {@link fetchParsedDefinitionIO}.\n */\nexport function processInputsWithErrors(rawInputs: InputParamSchema[]): {\n\tinputs: InputParam[];\n\tparseErrors: InputParseError[];\n} {\n\tconst inputs: InputParam[] = [];\n\tconst parseErrors: InputParseError[] = [];\n\tfor (const raw of rawInputs) {\n\t\tconst { input, error } = processInputWithError(raw);\n\t\tinputs.push(input);\n\t\tif (error) parseErrors.push(error);\n\t}\n\treturn { inputs, parseErrors };\n}\n","import { ComputeConfig, RhinoComputeError, ErrorCodes } from '@/core';\nimport { fetchRhinoCompute } from '@/core/compute-fetch/compute-fetch';\nimport { camelcaseKeys } from '@/core/utils/camel-case';\nimport { warnIfClientSide } from '@/core/utils/warnings';\nimport { prepareGrasshopperArgs } from '../compute/solve';\n\nimport { GrasshopperParsedIO, GrasshopperParsedIORaw, IoResponseSchema } from '../types';\n\nimport { processInputsWithErrors } from './input/input-processors';\n\n/**\n * Fetches raw input/output schemas from a Grasshopper definition.\n * Returns unprocessed data exactly as received from the Rhino Compute API (camelCased).\n *\n * @param definition - The Grasshopper definition (URL, base64 string, or Uint8Array)\n * @param config - Compute configuration (server URL, API key, etc.)\n * @returns Raw inputs and outputs with no type processing\n * @throws {RhinoComputeError} If fetch fails or response is invalid\n *\n * @public Use `fetchParsedDefinitionIO()` for processed, type-safe inputs\n */\nexport async function fetchDefinitionIO(\n\tdefinition: string | Uint8Array,\n\tconfig: ComputeConfig\n): Promise<GrasshopperParsedIORaw> {\n\tconst args = prepareGrasshopperArgs(definition, []);\n\tconst payload: { algo?: string | null; pointer?: string | null } = {};\n\tif (args.algo) payload.algo = args.algo;\n\tif (args.pointer) payload.pointer = args.pointer;\n\n\tif (!payload.algo && !payload.pointer) {\n\t\tthrow new RhinoComputeError(\n\t\t\t'Definition must resolve to either a URL pointer or base64 algo',\n\t\t\tErrorCodes.INVALID_INPUT,\n\t\t\t{ context: { definition } }\n\t\t);\n\t}\n\n\tconst response = await fetchRhinoCompute<'io'>('io', payload, config);\n\n\tif (!response || typeof response !== 'object') {\n\t\tthrow new RhinoComputeError('Invalid IO response structure', ErrorCodes.INVALID_INPUT, {\n\t\t\tcontext: { response, definition }\n\t\t});\n\t}\n\n\t// Convert PascalCase to camelCase\n\tconst camelCased = camelcaseKeys(response, { deep: true }) as IoResponseSchema;\n\n\treturn {\n\t\tinputs: camelCased.inputs,\n\t\toutputs: camelCased.outputs\n\t};\n}\n\n/**\n * Fetches and processes input/output schemas from a Grasshopper definition.\n * Returns strongly-typed, validated input parameters ready for use.\n *\n * @public This is the recommended way to fetch definition I/O schemas.\n *\n * @param definition - The Grasshopper definition (URL, base64 string, or Uint8Array)\n * @param config - Compute configuration (server URL, API key, etc.)\n * @returns Processed inputs with discriminated union types and outputs\n * @throws {RhinoComputeError} If fetch fails or response is invalid\n *\n * @example\n * ```typescript\n * const { inputs, outputs } = await fetchParsedDefinitionIO(\n * 'https://example.com/definition.gh',\n * { serverUrl: 'https://compute.rhino3d.com', apiKey: 'YOUR_KEY' }\n * );\n *\n * // Inputs are now strongly typed\n * inputs.forEach(input => {\n * if (input.paramType === 'Number') {\n * console.log(input.minimum, input.maximum); // TypeScript knows these exist\n * }\n * });\n * ```\n */\nexport async function fetchParsedDefinitionIO(\n\tdefinition: string | Uint8Array,\n\tconfig: ComputeConfig\n): Promise<GrasshopperParsedIO> {\n\twarnIfClientSide(\n\t\t'fetchParsedDefinitionIO',\n\t\tconfig.suppressBrowserWarning ?? config.suppressClientSideWarning\n\t);\n\n\tconst { inputs: rawInputs, outputs } = await fetchDefinitionIO(definition, config);\n\tconst { inputs, parseErrors } = processInputsWithErrors(rawInputs);\n\n\treturn parseErrors.length > 0 ? { inputs, outputs, parseErrors } : { inputs, outputs };\n}\n","import { DataTreeDefault, DataTreePath, InputParam, DataTree } from '../types';\nimport { getLogger } from '@/core';\n\n/**\n * Value types that can be stored in a DataTree\n */\nexport type DataTreeValue = string | number | boolean | object | null;\n\n/**\n * Simple data item for compute requests (not to be confused with DataItem interface for responses).\n * Note: While TypeScript defines this as string, Rhino Compute accepts boolean/number primitives in JSON.\n */\ninterface ComputeDataItem {\n\tdata: string | boolean | number;\n}\n\n/**\n * InnerTree data structure for compute requests.\n */\ntype ComputeInnerTreeData = {\n\t[path in DataTreePath]: ComputeDataItem[];\n};\n\n/**\n * Standalone TreeBuilder class for constructing Grasshopper TreeBuilder structures.\n * Does not depend on RhinoCompute library.\n *\n * @example\n * ```ts\n * const tree = new TreeBuilder('MyParam')\n * .append([0], [1, 2, 3])\n * .append([1], [4, 5])\n * .toComputeFormat();\n * ```\n */\nexport class TreeBuilder {\n\tprivate innerTree: ComputeInnerTreeData;\n\tprivate paramName: string;\n\n\tconstructor(paramName: string) {\n\t\tthis.paramName = paramName;\n\t\tthis.innerTree = {} as ComputeInnerTreeData;\n\t}\n\n\t/**\n\t * Append values to a specific path in the tree.\n\t *\n\t * @param path - Array of integers representing the branch path (e.g., [0], [0, 1])\n\t * @param items - Values to append at this path\n\t * @returns this for method chaining\n\t */\n\tpublic append(path: number[], items: DataTreeValue[]): this {\n\t\tconst pathKey = TreeBuilder.formatPathString(path);\n\n\t\tif (!this.innerTree[pathKey]) {\n\t\t\tthis.innerTree[pathKey] = [];\n\t\t}\n\n\t\tconst dataItems: ComputeDataItem[] = items.map((item) => ({\n\t\t\tdata: TreeBuilder.serializeValue(item)\n\t\t}));\n\n\t\tthis.innerTree[pathKey].push(...dataItems);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Append a single value to a path.\n\t *\n\t * @param path - Branch path\n\t * @param item - Single value to append\n\t * @returns this for method chaining\n\t */\n\tpublic appendSingle(path: number[], item: DataTreeValue): this {\n\t\treturn this.append(path, [item]);\n\t}\n\n\t/**\n\t * Set values from a DataTreeDefault structure.\n\t * Replaces any existing tree data.\n\t *\n\t * @param treeData - TreeBuilder structure with path keys like \"{0;1}\"\n\t * @returns this for method chaining\n\t */\n\tpublic fromDataTreeDefault(treeData: DataTreeDefault): this {\n\t\tthis.innerTree = {} as ComputeInnerTreeData;\n\n\t\tfor (const [pathStr, items] of Object.entries(treeData)) {\n\t\t\tif (!Array.isArray(items)) continue;\n\t\t\tconst path = TreeBuilder.parsePathString(pathStr);\n\t\t\tthis.append(path, items);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Append flattened values to path [0].\n\t * Useful for simple flat inputs.\n\t *\n\t * @param values - Single value or array of values\n\t * @returns this for method chaining\n\t */\n\tpublic appendFlat(values: DataTreeValue | DataTreeValue[]): this {\n\t\tconst items = Array.isArray(values) ? values : [values];\n\t\treturn this.append([0], items);\n\t}\n\n\t/**\n\t * Get the flattened list of all values in the tree.\n\t *\n\t * @returns Array of all values across all branches\n\t */\n\tpublic flatten(): DataTreeValue[] {\n\t\tconst result: DataTreeValue[] = [];\n\n\t\tfor (const items of Object.values(this.innerTree)) {\n\t\t\tif (Array.isArray(items)) {\n\t\t\t\tfor (const item of items) {\n\t\t\t\t\tresult.push(TreeBuilder.deserializeValue(item.data));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get all paths in the tree.\n\t *\n\t * @returns Array of path strings\n\t */\n\tpublic getPaths(): DataTreePath[] {\n\t\treturn Object.keys(this.innerTree) as DataTreePath[];\n\t}\n\n\t/**\n\t * Get values at a specific path.\n\t *\n\t * @param path - Path to retrieve values from\n\t * @returns Array of values or undefined if path doesn't exist\n\t */\n\tpublic getPath(path: number[]): DataTreeValue[] | undefined {\n\t\tconst pathKey = TreeBuilder.formatPathString(path);\n\t\tconst items = this.innerTree[pathKey];\n\t\tif (!items) return undefined;\n\t\treturn items.map((item: ComputeDataItem) => TreeBuilder.deserializeValue(item.data));\n\t}\n\n\t/**\n\t * Convert to format compatible with Grasshopper Compute API.\n\t *\n\t * @returns InnerTree object ready for compute\n\t */\n\tpublic toComputeFormat(): DataTree {\n\t\treturn {\n\t\t\tParamName: this.paramName,\n\t\t\tInnerTree: this.innerTree as any // Cast to any because request format differs from response type\n\t\t};\n\t}\n\n\t/**\n\t * Get the raw InnerTree data structure.\n\t *\n\t * @returns InnerTree data\n\t */\n\tpublic getInnerTree(): ComputeInnerTreeData {\n\t\treturn this.innerTree;\n\t}\n\n\t/**\n\t * Get the parameter name.\n\t *\n\t * @returns Parameter name\n\t */\n\tpublic getParamName(): string {\n\t\treturn this.paramName;\n\t}\n\n\t// ============================================================================\n\t// Static Factory Methods\n\t// ============================================================================\n\n\t/**\n\t * Create DataTrees from an array of InputParam definitions.\n\t * Handles tree access, numeric constraints, and value parsing.\n\t *\n\t * @param inputs - Array of input parameter definitions\n\t * @returns Array of InnerTree instances ready for compute\n\t *\n\t * @example\n\t * ```ts\n\t * const trees = TreeBuilder.fromInputParams(inputs);\n\t * ```\n\t */\n\tpublic static fromInputParams(inputs: InputParam[]): DataTree[] {\n\t\treturn inputs\n\t\t\t.filter((input) => TreeBuilder.hasValidValue(input.default))\n\t\t\t.map((input) => {\n\t\t\t\tconst tree = new TreeBuilder(input.nickname || 'unnamed');\n\t\t\t\tconst value = input.default;\n\n\t\t\t\t// Handle tree access (complex TreeBuilder structure)\n\t\t\t\tif (input.treeAccess && TreeBuilder.isDataTreeStructure(value)) {\n\t\t\t\t\ttree.fromDataTreeDefault(value as DataTreeDefault);\n\n\t\t\t\t\t// Apply numeric constraints to tree items\n\t\t\t\t\tif (TreeBuilder.isNumericInput(input)) {\n\t\t\t\t\t\ttree.applyNumericConstraints(input.minimum, input.maximum, input.nickname || 'unnamed');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Handle flat inputs\n\t\t\t\telse {\n\t\t\t\t\tconst values = Array.isArray(value) ? value : [value];\n\t\t\t\t\tconst processed = TreeBuilder.processValues(values, input);\n\t\t\t\t\ttree.appendFlat(processed);\n\t\t\t\t}\n\n\t\t\t\treturn tree.toComputeFormat();\n\t\t\t});\n\t}\n\n\t/**\n\t * Create a TreeBuilder from a single InputParam.\n\t *\n\t * @param input - Input parameter definition\n\t * @returns InnerTree ready for compute or undefined if value is invalid\n\t */\n\tpublic static fromInputParam(input: InputParam): DataTree | undefined {\n\t\tif (!TreeBuilder.hasValidValue(input.default)) return undefined;\n\n\t\tconst trees = TreeBuilder.fromInputParams([input]);\n\t\treturn trees[0];\n\t}\n\n\t/**\n\t * Set or replace a parameter value within a TreeBuilder or InnerTree array.\n\t *\n\t * Supports both high-level `DataTree[]` instances and low-level `InnerTree[]` format.\n\t *\n\t * **Architecture Note:**\n\t * - Use with `DataTree[]` when building/modifying before computation\n\t * - Use with `InnerTree[]` when modifying compute API results\n\t * - `DataTree` is the high-level builder; `InnerTree` is the Rhino Compute format\n\t *\n\t * @overload For TreeBuilder instances (high-level builder)\n\t * @param trees - Array of TreeBuilder instances to modify\n\t * @param paramName - The parameter name to set or replace\n\t * @param newValue - The new value (scalar, array, or TreeBuilder structure)\n\t * @returns A new/modified TreeBuilder array with the updated parameter\n\t *\n\t * @overload For compiled InnerTree (low-level API format)\n\t * @param trees - The compiled InnerTree array (typically from `client.solve()`)\n\t * @param paramName - The parameter name to set or replace\n\t * @param newValue - The new value (scalar, array, or TreeBuilder structure)\n\t * @returns A new/modified InnerTree array with the updated parameter\n\t *\n\t * @example\n\t * ```ts\n\t * // With TreeBuilder instances (high-level)\n\t * let trees = [new TreeBuilder('X'), new TreeBuilder('Y')];\n\t * trees = TreeBuilder.replaceTreeValue(trees, 'X', 42);\n\t * const result = await client.solve(definitionUrl,\n\t * trees.map(t => t.toComputeFormat())\n\t * );\n\t * ```\n\t *\n\t * @example\n\t * ```ts\n\t * // With InnerTree format (low-level, from API)\n\t * let trees = await client.solve(definitionUrl, initialInputs);\n\t * trees = TreeBuilder.replaceTreeValue(trees, 'X', 42);\n\t * trees = TreeBuilder.replaceTreeValue(trees, 'Y', [1, 2, 3]);\n\t * ```\n\t */\n\tpublic static replaceTreeValue(\n\t\ttrees: TreeBuilder[],\n\t\tparamName: string,\n\t\tnewValue: DataTreeValue\n\t): TreeBuilder[];\n\tpublic static replaceTreeValue(\n\t\ttrees: DataTree[],\n\t\tparamName: string,\n\t\tnewValue: DataTreeValue\n\t): DataTree[];\n\tpublic static replaceTreeValue(\n\t\ttrees: TreeBuilder[] | DataTree[],\n\t\tparamName: string,\n\t\tnewValue: DataTreeValue\n\t): TreeBuilder[] | DataTree[] {\n\t\tconst isBuilderArray = trees.length > 0 && trees[0] instanceof TreeBuilder;\n\t\tconst builder = TreeBuilder.buildFromValue(paramName, newValue);\n\n\t\tif (isBuilderArray) {\n\t\t\tconst builders = trees as TreeBuilder[];\n\t\t\tconst idx = builders.findIndex((t) => t.getParamName() === paramName);\n\t\t\tif (idx !== -1) builders[idx] = builder;\n\t\t\telse builders.push(builder);\n\t\t\treturn builders;\n\t\t}\n\n\t\t// Empty arrays land here too — see the \"empty array\" characterization\n\t\t// test in data-tree.test.ts: pins the current behavior of returning the\n\t\t// compute-format shape rather than a TreeBuilder.\n\t\tconst dataTrees = trees as DataTree[];\n\t\tconst compiled = builder.toComputeFormat();\n\t\tconst idx = dataTrees.findIndex((t) => t.ParamName === paramName);\n\t\tif (idx !== -1) dataTrees[idx] = compiled;\n\t\telse dataTrees.push(compiled);\n\t\treturn dataTrees;\n\t}\n\n\t/**\n\t * Build a TreeBuilder from a single value, dispatching on shape:\n\t * DataTreeDefault structure, array, or scalar.\n\t */\n\tprivate static buildFromValue(paramName: string, value: DataTreeValue): TreeBuilder {\n\t\tconst tree = new TreeBuilder(paramName);\n\t\tif (\n\t\t\ttypeof value === 'object' &&\n\t\t\tvalue !== null &&\n\t\t\t!Array.isArray(value) &&\n\t\t\tTreeBuilder.isDataTreeStructure(value)\n\t\t) {\n\t\t\ttree.fromDataTreeDefault(value as DataTreeDefault);\n\t\t} else {\n\t\t\ttree.appendFlat(value);\n\t\t}\n\t\treturn tree;\n\t}\n\n\t/**\n\t * Extract a value from a TreeBuilder or InnerTree array by parameter name.\n\t *\n\t * Automatically unwraps single values for convenience.\n\t * Works with both high-level `DataTree[]` instances and low-level `InnerTree[]` format.\n\t *\n\t * **Architecture Note:**\n\t * - Use with `DataTree[]` to read builder instances\n\t * - Use with `InnerTree[]` to read compute API responses\n\t * - Return behavior is consistent across both formats\n\t *\n\t * **Return Value Behavior:**\n\t * - Single value → unwrapped (returns `5` not `[5]`)\n\t * - Multiple values → array of values\n\t * - Not found → `null`\n\t *\n\t * @overload For TreeBuilder instances\n\t * @param trees - Array of TreeBuilder instances to read from\n\t * @param paramName - The parameter name to retrieve\n\t * @returns The unwrapped value, array of values, or null if parameter not found\n\t *\n\t * @overload For compiled InnerTree\n\t * @param trees - The compiled InnerTree array (typically from `client.solve()`)\n\t * @param paramName - The parameter name to retrieve\n\t * @returns The unwrapped value, array of values, or null if parameter not found\n\t *\n\t * @example\n\t * ```ts\n\t * // With TreeBuilder instances\n\t * const trees = [new TreeBuilder('X'), new TreeBuilder('Y')];\n\t * trees[0].appendFlat(42);\n\t * const x = TreeBuilder.getTreeValue(trees, 'X'); // Returns 42\n\t * ```\n\t *\n\t * @example\n\t * ```ts\n\t * // With InnerTree from compute results\n\t * const result = await client.solve(definitionUrl, inputs);\n\t * const x = TreeBuilder.getTreeValue(result, 'X'); // Returns 42 (not [42])\n\t * const points = TreeBuilder.getTreeValue(result, 'Points'); // Returns [point1, point2, ...]\n\t * ```\n\t */\n\tpublic static getTreeValue(trees: TreeBuilder[], paramName: string): DataTreeValue | null;\n\tpublic static getTreeValue(trees: DataTree[], paramName: string): DataTreeValue | null;\n\tpublic static getTreeValue(\n\t\ttrees: TreeBuilder[] | DataTree[],\n\t\tparamName: string\n\t): DataTreeValue | null {\n\t\tconst isBuilderArray = trees.length > 0 && trees[0] instanceof TreeBuilder;\n\n\t\tconst values = isBuilderArray\n\t\t\t? TreeBuilder.readFromBuilders(trees as TreeBuilder[], paramName)\n\t\t\t: TreeBuilder.readFromDataTrees(trees as DataTree[], paramName);\n\n\t\tif (values === null) return null;\n\t\tif (values.length === 0) return null;\n\t\tif (values.length === 1) return values[0];\n\t\treturn values;\n\t}\n\n\t/**\n\t * Read all values for `paramName` across every branch of the matching builder.\n\t * Returns null when the builder isn't found.\n\t */\n\tprivate static readFromBuilders(\n\t\tbuilders: TreeBuilder[],\n\t\tparamName: string\n\t): DataTreeValue[] | null {\n\t\tconst tree = builders.find((t) => t.getParamName() === paramName);\n\t\treturn tree ? tree.flatten() : null;\n\t}\n\n\t/**\n\t * Read values from the first branch of the matching compiled InnerTree\n\t * (multi-branch responses are not flattened — current semantics, pinned by\n\t * the \"reads from the first branch path only\" test).\n\t */\n\tprivate static readFromDataTrees(\n\t\tdataTrees: DataTree[],\n\t\tparamName: string\n\t): DataTreeValue[] | null {\n\t\tconst tree = dataTrees.find((t) => t.ParamName === paramName);\n\t\tif (!tree?.InnerTree) return null;\n\n\t\tconst firstKey = Object.keys(tree.InnerTree)[0];\n\t\tif (!firstKey) return null;\n\n\t\t// @ts-expect-error - Dynamic key access on innerTree\n\t\tconst items = tree.InnerTree[firstKey];\n\n\t\tif (Array.isArray(items)) {\n\t\t\treturn items\n\t\t\t\t.map((item) => (item?.data !== undefined ? TreeBuilder.deserializeValue(item.data) : null))\n\t\t\t\t.filter((v): v is DataTreeValue => v !== null);\n\t\t}\n\n\t\tif (items?.data !== undefined) return [TreeBuilder.deserializeValue(items.data)];\n\t\treturn items !== undefined ? [items as DataTreeValue] : null;\n\t}\n\n\t/**\n\t * Parse a TreeBuilder path string like \"{0;1;2}\" into [0, 1, 2].\n\t *\n\t * @param pathStr - Path string\n\t * @returns Array of path indices\n\t */\n\tpublic static parsePathString(pathStr: string): number[] {\n\t\t// Allow the legitimate root path \"{}\" alongside \"{0;1;2}\"\n\t\tconst match = pathStr.match(/^\\{([\\d;]*)\\}$/);\n\t\tif (!match) {\n\t\t\tgetLogger().warn(`Invalid TreeBuilder path format: ${pathStr}, using [0]`);\n\t\t\treturn [0];\n\t\t}\n\t\tif (match[1] === '') return [];\n\t\treturn match[1].split(';').map(Number);\n\t}\n\n\t/**\n\t * Format a path array into TreeBuilder path string format.\n\t *\n\t * @param path - Path as number array\n\t * @returns Formatted path string like \"{0;1;2}\"\n\t */\n\tpublic static formatPathString(path: number[]): DataTreePath {\n\t\treturn `{${path.join(';')}}` as DataTreePath;\n\t}\n\n\t// ============================================================================\n\t// Private Helper Methods\n\t// ============================================================================\n\n\t/**\n\t * Apply numeric constraints to all tree values.\n\t */\n\tprivate applyNumericConstraints(\n\t\tmin: number | null | undefined,\n\t\tmax: number | null | undefined,\n\t\tinputName: string\n\t): void {\n\t\tfor (const items of Object.values(this.innerTree)) {\n\t\t\tif (!Array.isArray(items)) continue;\n\n\t\t\tfor (const item of items) {\n\t\t\t\tconst value = TreeBuilder.deserializeValue(item.data);\n\t\t\t\tif (typeof value === 'number') {\n\t\t\t\t\tconst clamped = TreeBuilder.clampValue(value, min, max, inputName);\n\t\t\t\t\titem.data = TreeBuilder.serializeValue(clamped);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Serialize a value for compute requests.\n\t * Preserves booleans and numbers as primitives for proper Grasshopper parameter handling.\n\t */\n\tprivate static serializeValue(value: DataTreeValue): string | boolean | number {\n\t\tif (typeof value === 'boolean') return value;\n\t\tif (typeof value === 'number') return value;\n\t\tif (typeof value === 'string') return value;\n\t\tif (typeof value === 'object' && value !== null) {\n\t\t\treturn JSON.stringify(value);\n\t\t}\n\t\treturn String(value);\n\t}\n\n\t/**\n\t * Deserialize a value back to its original type.\n\t * Handles both string-encoded values and primitive values.\n\t */\n\tprivate static deserializeValue(data: string | boolean | number): DataTreeValue {\n\t\t// If already a primitive type, return as-is\n\t\tif (typeof data === 'boolean') return data;\n\t\tif (typeof data === 'number') return data;\n\n\t\t// Handle string values\n\t\tif (typeof data !== 'string') return data;\n\n\t\t// Try to parse as JSON first\n\t\tif (data.startsWith('{') || data.startsWith('[')) {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(data);\n\t\t\t} catch {\n\t\t\t\treturn data;\n\t\t\t}\n\t\t}\n\t\t// Try to parse as number\n\t\tif (!isNaN(Number(data))) {\n\t\t\treturn Number(data);\n\t\t}\n\t\t// Try to parse as boolean\n\t\tif (data === 'true') return true;\n\t\tif (data === 'false') return false;\n\t\treturn data;\n\t}\n\n\t/**\n\t * Check if a value is valid for inclusion in a DataTree.\n\t */\n\tprivate static hasValidValue(value: unknown): boolean {\n\t\tif (value === undefined || value === null) return false;\n\t\tif (typeof value === 'string') return true;\n\t\tif (Array.isArray(value) && value.length === 0) return false;\n\t\tif (typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0)\n\t\t\treturn false;\n\t\treturn true;\n\t}\n\n\t/**\n\t * Check if value is a TreeBuilder structure.\n\t */\n\tprivate static isDataTreeStructure(value: unknown): value is DataTreeDefault {\n\t\tif (typeof value !== 'object' || value === null || Array.isArray(value)) return false;\n\t\treturn Object.entries(value).every(\n\t\t\t([key, val]) => typeof key === 'string' && /^\\{[\\d;]+\\}$/.test(key) && Array.isArray(val)\n\t\t);\n\t}\n\n\t/**\n\t * Check if input is numeric type.\n\t */\n\tprivate static isNumericInput(input: InputParam): input is InputParam & {\n\t\tparamType: 'Number' | 'Integer';\n\t\tminimum?: number | null;\n\t\tmaximum?: number | null;\n\t} {\n\t\treturn input.paramType === 'Number' || input.paramType === 'Integer';\n\t}\n\n\t/**\n\t * Process array of values based on input type.\n\t */\n\tprivate static processValues(values: DataTreeValue[], input: InputParam): DataTreeValue[] {\n\t\treturn values\n\t\t\t.map((val) => {\n\t\t\t\t// Apply numeric constraints\n\t\t\t\tif (TreeBuilder.isNumericInput(input) && typeof val === 'number') {\n\t\t\t\t\treturn TreeBuilder.clampValue(\n\t\t\t\t\t\tval,\n\t\t\t\t\t\tinput.minimum,\n\t\t\t\t\t\tinput.maximum,\n\t\t\t\t\t\tinput.nickname || 'unnamed'\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Keep objects and strings as-is (serialization happens in append)\n\t\t\t\treturn val;\n\t\t\t})\n\t\t\t.filter((v) => v !== null && v !== undefined);\n\t}\n\n\t/**\n\t * Clamp numeric value to constraints.\n\t */\n\tprivate static clampValue(\n\t\tvalue: number,\n\t\tmin: number | null | undefined,\n\t\tmax: number | null | undefined,\n\t\tinputName: string\n\t): number {\n\t\tlet result = value;\n\n\t\tif (min !== null && min !== undefined && result < min) {\n\t\t\tgetLogger().warn(`${inputName}: ${value} below min ${min}, clamping`);\n\t\t\tresult = min;\n\t\t}\n\t\tif (max !== null && max !== undefined && result > max) {\n\t\t\tgetLogger().warn(`${inputName}: ${value} above max ${max}, clamping`);\n\t\t\tresult = max;\n\t\t}\n\n\t\treturn result;\n\t}\n}\n"],"mappings":"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","names":["stableStringify","value","seen","stringify","v","sample","k","fnv1a","input","hash","i","hashSolveInput","definition","dataTree","defKey","SolveScheduler","executor","baseConfig","options","__publicField","cacheOpt","cacheConfig","listener","err","getLogger","definition","dataTree","RhinoComputeError","ErrorCodes","key","hashSolveInput","ctx","cached","result","resolve","reject","item","abortErr","inflight","controller","externalAbortHandler","startTime","config","response","durationMs","error","alreadySettled","next","entry","oldest","hook","args","GrasshopperClient","_GrasshopperClient","config","__publicField","ComputeServerStats","client","RhinoComputeError","ErrorCodes","definition","fetchParsedDefinitionIO","fetchDefinitionIO","dataTree","options","effectiveConfig","result","solveGrasshopperDefinition","error","getLogger","executor","SolveScheduler","extractFilesFromComputeResponse","downloadableFiles","additionalFiles","processFiles","err","RhinoComputeError","ErrorCodes","downloadFileData","fileFoldername","processedFiles","createAndDownloadZip","dataItems","item","filePath","bites","decodeBase64ToBinary","filesArray","additionalProcessed","file","response","getLogger","arrayBuffer","error","f","files","zipName","zipSync","strToU8","zipData","zipped","blob","saveFile","filename","a","decoderRegistry","registerDecoder","typeName","decoder","rhino","data","d","findDecoder","rhinoType","key","dec","extractPayload","parsedData","decodeRhinoGeometry","error","getLogger","payload","SYSTEM_TYPES","RHINO_GEOMETRY_PREFIX","EXCLUDED_TYPES","FILE_DATA_TYPE","isExcludedType","type","t","tryDecodeJSON","value","trimmed","first","decodeBySystemType","raw","rhino","decodeRhinoGeometry","extractItemValue","data","parseValues","isFileData","v","forEachTreeItem","tree","handler","list","item","getValues","response","byId","options","stringOnly","result","param","key","extractFileData","output","parsed","getValue","parseOptions","targetParam","p","found","collected","GrasshopperResponseProcessor","response","debug","byId","options","getValues","selector","getValue","paramName","paramId","mergedOptions","getThreeMeshesFromComputeResponse","error","RhinoComputeError","ErrorCodes","extractFileData","folderName","additionalFiles","files","downloadFileData","warnIfClientSide","functionName","suppress","getLogger","solveGrasshopperDefinition","dataTree","definition","config","warnIfClientSide","args","prepareGrasshopperArgs","applyOptionalComputeSettings","result","fetchRhinoCompute","_pointer","rest","base64ByteArray","isBase64","encodeStringToBase64","arglist","options","preProcessInputDefault","input","getLogger","innerTree","tree","branch","items","item","num","allValues","processInputValue","input","options","transform","setUndefinedOnEmpty","processedArray","v","transformed","createNumericTransformer","value","parsed","createBooleanTransformer","lowerValue","createTextTransformer","createColorTransformer","cleaned","processColorInput","createObjectTransformer","inputName","getLogger","err","applyRounding","decimalPlaces","tolerance","rounded","getInputStepSize","roundingTolerance","abs","decimalPart","decimals","step","s","expMatch","exp","absExp","MAX_DECIMALS","trimmed","processNumericInput","isIntegerType","val","firstValue","stepSource","stepStr","inferred","processBooleanInput","error","RhinoComputeError","processTextInput","parseToObject","processValueListInput","defaultLower","key","PARSERS","CANONICAL_PARAM_TYPES","PARSERS","key","canonicalizeParamType","paramType","createSafeDefault","rawInput","baseInput","isList","processInput","processInputWithError","preProcessInputDefault","parser","RhinoComputeError","error","getLogger","processInputs","rawInputs","processInputsWithErrors","inputs","parseErrors","raw","input","fetchDefinitionIO","definition","config","args","prepareGrasshopperArgs","payload","RhinoComputeError","ErrorCodes","response","fetchRhinoCompute","camelCased","camelcaseKeys","fetchParsedDefinitionIO","warnIfClientSide","rawInputs","outputs","inputs","parseErrors","processInputsWithErrors","TreeBuilder","_TreeBuilder","paramName","__publicField","path","items","pathKey","dataItems","item","treeData","pathStr","values","result","inputs","input","tree","value","processed","trees","newValue","isBuilderArray","builder","builders","idx","t","dataTrees","compiled","firstKey","v","match","getLogger","min","max","inputName","clamped","data","key","val"]}
|
package/dist/chunk-OEDLGVIQ.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{c as _}from"./chunk-OW6HV6QP.js";import{a as te,c as D,d as F,e as y}from"./chunk-XBIEAJBK.js";import*as s from"three";import{OrbitControls as ne}from"three/addons/controls/OrbitControls.js";import{HDRLoader as re}from"three/addons/loaders/HDRLoader.js";var V=new s.Vector3(0,0,1),P=(e,r,n,t)=>{switch(e){case"mm":return r;case"cm":return n;default:return t}},Ze=function(e,r){let n=oe(r||{}),t=ae(n),o=de(n,e),a=me(e,n),i=he(o,e,n);le(t,n),ce(t,n),n.floor?.enabled&&ue(t,n);let c=n.events.enableEventHandlers!==!1?fe(e,t,o,i,n):{dispose:()=>{},fitToView:()=>{},clearSelection:()=>{}},l=e.parentElement,h=()=>l?{width:l.clientWidth,height:l.clientHeight}:{width:window.innerWidth,height:window.innerHeight},{animate:m,dispose:u}=se(a,t,o,i,h,n.events.onFrame);m();let E=n.environment?.sceneUp||V;return t.up.set(E.x,E.y,E.z),{scene:t,camera:o,controls:i,renderer:a,dispose:()=>{u(),c.dispose(),i.dispose(),a.dispose(),t.traverse(d=>{d instanceof s.Mesh&&(d.geometry?.dispose(),Array.isArray(d.material)?d.material.forEach(H=>H.dispose()):d.material?.dispose())})},fitToView:c.fitToView,clearSelection:c.clearSelection}};function oe(e){let r=e.sceneScale||"m",t={mm:{cameraDistance:20,near:.1,far:2e3,floorSize:100,lightDistance:10,lightHeight:20,minDistance:.1,shadowSize:100,scaleFactor:1e3},cm:{cameraDistance:20,near:.1,far:2e3,floorSize:100,lightDistance:25,lightHeight:50,minDistance:.1,shadowSize:100,scaleFactor:100},m:{cameraDistance:10,near:.01,far:2e3,floorSize:50,lightDistance:25,lightHeight:50,minDistance:.001,shadowSize:100,scaleFactor:1},inches:{cameraDistance:15,near:.1,far:2e3,floorSize:80,lightDistance:20,lightHeight:40,minDistance:.1,shadowSize:80,scaleFactor:39.37},feet:{cameraDistance:8,near:.1,far:2e3,floorSize:40,lightDistance:15,lightHeight:30,minDistance:.1,shadowSize:60,scaleFactor:3.28084}}[r];return{sceneScale:r,camera:{position:e.camera?.position||new s.Vector3(-t.cameraDistance,t.cameraDistance,t.cameraDistance),fov:e.camera?.fov||20,near:e.camera?.near||t.near,far:e.camera?.far||t.far,target:e.camera?.target||new s.Vector3(0,0,0)},lighting:{enableSunlight:e.lighting?.enableSunlight??!0,sunlightIntensity:e.lighting?.sunlightIntensity||1,sunlightPosition:e.lighting?.sunlightPosition||new s.Vector3(t.lightDistance,t.lightHeight,t.lightDistance),ambientLightColor:e.lighting?.ambientLightColor||new s.Color(4210752),ambientLightIntensity:e.lighting?.ambientLightIntensity||1,sunlightColor:e.lighting?.sunlightColor||16777215},environment:{hdrPath:e.environment?.hdrPath||"/baseHDR.hdr",backgroundColor:e.environment?.backgroundColor||new s.Color(15790320),enableEnvironmentLighting:e.environment?.enableEnvironmentLighting??!0,sceneUp:e.environment?.sceneUp||V,showEnvironment:e.environment?.showEnvironment??!1},floor:{enabled:e.floor?.enabled??!1,size:e.floor?.size||t.floorSize,color:e.floor?.color||new s.Color(8421504),roughness:e.floor?.roughness||.7,metalness:e.floor?.metalness||0,receiveShadow:e.floor?.receiveShadow??!0},render:{enableShadows:e.render?.enableShadows??!0,shadowMapSize:e.render?.shadowMapSize||2048,antialias:e.render?.antialias??!0,pixelRatio:e.render?.pixelRatio||Math.min(window.devicePixelRatio,2),toneMapping:e.render?.toneMapping||s.NeutralToneMapping,toneMappingExposure:e.render?.toneMappingExposure||1,preserveDrawingBuffer:e.render?.preserveDrawingBuffer??!1},controls:{enableDamping:e.controls?.enableDamping??!1,dampingFactor:e.controls?.dampingFactor||.05,autoRotate:e.controls?.autoRotate??!1,autoRotateSpeed:e.controls?.autoRotateSpeed||.5,enableZoom:e.controls?.enableZoom??!0,enablePan:e.controls?.enablePan??!0,minDistance:e.controls?.minDistance||t.minDistance,maxDistance:e.controls?.maxDistance||1/0},events:{onBackgroundClicked:e.events?.onBackgroundClicked,onObjectSelected:e.events?.onObjectSelected,onMeshMetadataClicked:e.events?.onMeshMetadataClicked,onMeshDoubleClicked:e.events?.onMeshDoubleClicked,selectionColor:e.events?.selectionColor||"#ff0000",enableEventHandlers:e.events?.enableEventHandlers??!0,enableKeyboardControls:e.events?.enableKeyboardControls??!0,enableClickToFocus:e.events?.enableClickToFocus??!0,enableDoubleClickZoom:e.events?.enableDoubleClickZoom??!0,onReady:e.events?.onReady,onFrame:e.events?.onFrame}}}function ae(e){let r=new s.Scene,n=typeof e.environment.backgroundColor=="string"?new s.Color(e.environment.backgroundColor):e.environment.backgroundColor;return r.background=n||null,r}function ie(e,r,n,t,o=200){let a=e.position.clone(),i=r.target.clone(),c=performance.now(),l=m=>1-Math.pow(1-m,3),h=()=>{let m=performance.now()-c,u=l(Math.min(m/o,1));e.position.lerpVectors(a,n,u),r.target.lerpVectors(i,t,u),r.update(),u<1&&requestAnimationFrame(h)};requestAnimationFrame(h)}function se(e,r,n,t,o,a){let i=null,c=performance.now(),l=()=>{let{width:u,height:E}=o();if(u===0||E===0)return;let g=Math.min(window.devicePixelRatio,2),d=Math.round(u*g),H=Math.round(E*g);(e.domElement.width!==d||e.domElement.height!==H)&&(e.setPixelRatio(g),e.setSize(u,E,!1),n.aspect=u/E,n.updateProjectionMatrix())},h=function(){i=requestAnimationFrame(h);let u=performance.now(),E=(u-c)/1e3;c=u,l(),(t.enableDamping||t.autoRotate)&&t.update(),a?.(E),e.render(r,n)};return{animate:h,dispose:()=>{i!==null&&(cancelAnimationFrame(i),i=null)}}}function le(e,r){r.environment.enableEnvironmentLighting?new re().load(r.environment.hdrPath||"/baseHDR.hdr",function(n){n.mapping=s.EquirectangularReflectionMapping,e.environment=n,r.environment.showEnvironment&&(e.background=n),r.events.onReady?.()},void 0,function(n){y().warn("HDR texture could not be loaded, falling back to basic lighting:",n),r.events.onReady?.()}):r.events.onReady?.()}function ce(e,r){let n=new s.AmbientLight(r.lighting.ambientLightColor,r.lighting.ambientLightIntensity);if(e.add(n),r.lighting.enableSunlight){let t=new s.DirectionalLight(r.lighting.sunlightColor??16777215,r.lighting.sunlightIntensity),o=r.lighting.sunlightPosition;if(o&&t.position.set(o.x,o.y,o.z),r.render.enableShadows){t.castShadow=!0;let a=P(r.sceneScale,.1,10,100);t.shadow.camera.left=-a,t.shadow.camera.right=a,t.shadow.camera.top=a,t.shadow.camera.bottom=-a;let i=P(r.sceneScale,.001,.1,.5),c=P(r.sceneScale,1,100,500);t.shadow.camera.near=i,t.shadow.camera.far=c,t.shadow.mapSize.width=r.render.shadowMapSize||2048,t.shadow.mapSize.height=r.render.shadowMapSize||2048,t.shadow.bias=-1e-4,t.shadow.normalBias=.02}e.add(t)}}function ue(e,r){let n=r.floor.size,t=new s.PlaneGeometry(n,n),o=typeof r.floor.color=="string"?new s.Color(r.floor.color):r.floor.color,a=new s.MeshStandardMaterial({color:o,roughness:r.floor.roughness,metalness:r.floor.metalness,side:s.DoubleSide}),i=new s.Mesh(t,a);i.userData.id="floor",i.name="floor",i.rotation.x=-Math.PI/2,i.position.y=0,r.floor.receiveShadow&&r.render.enableShadows&&(i.receiveShadow=!0),e.add(i)}function de(e,r){let n=r.parentElement,t=n?n.clientWidth:window.innerWidth,o=n?n.clientHeight:window.innerHeight,a=new s.PerspectiveCamera(e.camera.fov,t/o,e.camera.near,e.camera.far),i=e.camera.position;return i&&a.position.set(i.x,i.y,i.z),a}function me(e,r){let n=new s.WebGLRenderer({antialias:r.render.antialias,canvas:e,alpha:!0,powerPreference:"high-performance",preserveDrawingBuffer:r.render.preserveDrawingBuffer,logarithmicDepthBuffer:!0}),t=e.parentElement,o=t?t.clientWidth:window.innerWidth,a=t?t.clientHeight:window.innerHeight;return t&&(e.style.width="100%",e.style.height="100%",e.style.display="block"),n.setSize(o,a,!1),n.setPixelRatio(r.render.pixelRatio||Math.min(window.devicePixelRatio,2)),r.render.enableShadows&&(n.shadowMap.enabled=!0,n.shadowMap.type=s.VSMShadowMap),n.toneMapping=r.render.toneMapping,n.toneMappingExposure=r.render.toneMappingExposure||1,n.outputColorSpace=s.SRGBColorSpace,n.sortObjects=!0,n}function fe(e,r,n,t,o){let a=new Set,i=new Map,c=new s.Raycaster,l=new s.Vector2,h=new s.Vector2,m=f=>{let p=f;for(;p;){if(!p.visible)return!1;p=p.parent}return!0},u=()=>{let f=new s.Box3;if(r.traverse(A=>{A.visible&&A.userData.id!=="floor"&&A instanceof s.Mesh&&f.expandByObject(A)}),f.isEmpty()){y().warn("No objects to fit to view");return}let p=f.getCenter(new s.Vector3),T=f.getSize(new s.Vector3),v=Math.max(T.x,T.y,T.z),R=n.fov*(Math.PI/180),S=v/(2*Math.tan(R/2));S*=1.5;let O=n.position.clone().sub(t.target).normalize();n.position.copy(p.clone().add(O.multiplyScalar(S))),t.target.copy(p),t.update()},E=typeof o.events.selectionColor=="string"?new s.Color(o.events.selectionColor):o.events.selectionColor instanceof s.Color?o.events.selectionColor:new s.Color("#ff0000"),g=()=>{a.forEach(f=>{f instanceof s.Mesh&&i.has(f)&&(f.material=i.get(f),i.delete(f))}),a.clear()},d=f=>{h.set(f.clientX,f.clientY)},H=f=>{let p=new s.Vector2(f.clientX,f.clientY);if(h.distanceTo(p)>5)return;let T=e.getBoundingClientRect();l.x=(f.clientX-T.left)/T.width*2-1,l.y=-((f.clientY-T.top)/T.height)*2+1,c.setFromCamera(l,n);let v=c.intersectObjects(r.children,!0).filter(R=>m(R.object));if(v.length>0){let R=v[0].object;if(!a.has(R)){if(g(),a.add(R),R instanceof s.Mesh&&R.material instanceof s.Material){i.set(R,R.material);let S=R.material.clone();S.emissive=E.clone(),R.material=S}o.events?.onObjectSelected?.(R),R instanceof s.Mesh&&Object.keys(R.userData).length>0&&o.events?.onMeshMetadataClicked?.(R.userData)}}else g(),o.events?.onBackgroundClicked?.({x:l.x,y:l.y})},L=f=>{let p=e.getBoundingClientRect();l.x=(f.clientX-p.left)/p.width*2-1,l.y=-((f.clientY-p.top)/p.height)*2+1,c.setFromCamera(l,n);let T=c.intersectObjects(r.children,!0).filter(ee=>m(ee.object));if(T.length===0)return;let v=T[0].object;if(o.events?.onMeshDoubleClicked?.(v),!o.events?.enableDoubleClickZoom)return;let R=new s.Box3().setFromObject(v);if(R.isEmpty())return;let S=R.getCenter(new s.Vector3),O=R.getSize(new s.Vector3),A=Math.max(O.x,O.y,O.z),K=n.fov*(Math.PI/180),X=A/(2*Math.tan(K/2))*1.5,J=n.position.clone().sub(t.target).normalize(),Q=S.clone().add(J.multiplyScalar(X));ie(n,t,Q,S)},x=f=>{if(o.events?.enableKeyboardControls)switch(f.key.toLowerCase()){case"f":f.preventDefault(),u();break;case"escape":f.preventDefault(),g();break;case" ":f.preventDefault(),u();break}};return o.events?.enableClickToFocus&&(e.addEventListener("mousedown",d),e.addEventListener("click",H),e.addEventListener("dblclick",L)),o.events?.enableKeyboardControls&&(e.setAttribute("tabindex","0"),e.addEventListener("keydown",x)),{dispose:()=>{e.removeEventListener("mousedown",d),e.removeEventListener("click",H),e.removeEventListener("dblclick",L),e.removeEventListener("keydown",x),g()},fitToView:u,clearSelection:g}}function he(e,r,n){let t=new ne(e,r),o=n.camera.target;return o&&t.target.set(o.x,o.y,o.z),t.enableDamping=n.controls.enableDamping||!1,t.dampingFactor=n.controls.dampingFactor||.05,t.autoRotate=n.controls.autoRotate||!1,t.autoRotateSpeed=n.controls.autoRotateSpeed||.5,t.enableZoom=n.controls.enableZoom??!0,t.enablePan=n.controls.enablePan??!0,t.minDistance=n.controls.minDistance||.001,t.maxDistance=n.controls.maxDistance||1/0,t.screenSpacePanning=!1,t.maxPolarAngle=Math.PI,t.update(),t}import*as w from"three";var B={HUGE_THRESHOLD:1e4,LARGE_THRESHOLD:1e3,SCALE_RATIO_THRESHOLD:100,NEAR_PLANE_FACTOR:{TINY:1e-4,SMALL:.001,NORMAL:.01},FAR_PLANE_FACTOR:{HUGE:100,LARGE:50,NORMAL:20},INITIAL_DISTANCE_MULTIPLIER:4};function Xe(e,r,n,t,o){if(Ee(e),r.length===0)return;r.forEach(m=>{e.add(m)});let a=z(r),i=a.getCenter(new w.Vector3),c=a.getSize(new w.Vector3),l=Math.max(c.x,c.y,c.z);if(l/Math.min(c.x||1,c.y||1,c.z||1)>B.SCALE_RATIO_THRESHOLD||l>B.HUGE_THRESHOLD?(n.near=l*B.NEAR_PLANE_FACTOR.TINY,n.far=l*B.FAR_PLANE_FACTOR.HUGE):l>B.LARGE_THRESHOLD?(n.near=l*B.NEAR_PLANE_FACTOR.SMALL,n.far=l*B.FAR_PLANE_FACTOR.LARGE):(n.near=Math.max(.01,l*B.NEAR_PLANE_FACTOR.NORMAL),n.far=Math.max(2e3,l*B.FAR_PLANE_FACTOR.NORMAL)),n.updateProjectionMatrix(),o)t.minDistance=n.near*2,t.maxDistance=n.far*.9;else{let m=l*B.INITIAL_DISTANCE_MULTIPLIER;n.position.set(i.x+m*.8,i.y+m,i.z+m*1.2),t.target.copy(i),t.minDistance=n.near*2,t.maxDistance=n.far*.9,t.update()}}function N(e){if(!e||typeof e!="string")return y().warn(`Invalid color input: ${e}, using white`),new w.Color(16777215);let r=e.trim();if(/^#?[0-9A-Fa-f]{6}$/.test(r))try{let n=r.startsWith("#")?r:`#${r}`;return new w.Color(n)}catch{return y().warn(`Invalid hex color: ${e}, using white`),new w.Color(16777215)}if(r.includes(",")){let n=r.split(",").map(t=>parseInt(t.trim(),10));if(n.length===3&&n.every(t=>!isNaN(t)&&t>=0&&t<=255))return new w.Color(n[0]/255,n[1]/255,n[2]/255)}try{return new w.Color(r.toLowerCase())}catch{return y().warn(`Invalid color string: ${e}, using white`),new w.Color(16777215)}}function G(e,r){e.forEach(n=>{n.position.y-=r})}function z(e){let r=new w.Box3;return e.length===0||e.forEach(n=>{n.updateMatrixWorld(!0);let t=new w.Box3().setFromObject(n);r.union(t)}),r}function Ee(e){[...e.children].forEach(n=>{n.userData.id!=="floor"&&(n.traverse(t=>{if(!(t instanceof w.Mesh))return;t.geometry?.dispose(),(Array.isArray(t.material)?t.material:[t.material]).forEach(a=>{for(let i of Object.values(a))i instanceof w.Texture&&i.dispose();a.dispose()})}),n.removeFromParent())})}var Me={};te(Me,{CONCRETE_MATERIAL:()=>ye,EMISSIVE_MATERIAL:()=>pe,GLASS_MATERIAL:()=>Re,METAL_MATERIAL:()=>ge,PLASTIC_MATERIAL:()=>be,RUBBER_MATERIAL:()=>Te,WOOD_MATERIAL:()=>we});import*as b from"three";var pe=new b.MeshPhysicalMaterial({color:0,emissive:new b.Color(16777215),emissiveIntensity:5,metalness:0,roughness:.2,clearcoat:.3,clearcoatRoughness:.2,depthWrite:!0,depthTest:!0,transparent:!1,alphaTest:0,polygonOffset:!0,side:b.FrontSide,dithering:!0}),ge=new b.MeshPhysicalMaterial({color:new b.Color(0),metalness:.9,roughness:.3,envMapIntensity:1.2,clearcoat:.3,clearcoatRoughness:.2,reflectivity:1,ior:2.5,thickness:1,depthWrite:!0,transparent:!1,alphaTest:0,depthTest:!0,polygonOffset:!0,side:b.FrontSide,dithering:!0}),ye=new b.MeshPhysicalMaterial({color:new b.Color(13421772),metalness:0,roughness:.92,envMapIntensity:.15,clearcoat:.05,clearcoatRoughness:.9,reflectivity:.15,transmission:0,ior:1.45,thickness:0,depthWrite:!0,transparent:!1,alphaTest:.5,depthTest:!0,polygonOffset:!0,side:b.FrontSide,dithering:!0}),be=new b.MeshPhysicalMaterial({color:new b.Color(16777215),metalness:0,roughness:.3,envMapIntensity:.5,clearcoat:.5,clearcoatRoughness:.1,reflectivity:.5,ior:1.4,transmission:0,transparent:!1,depthWrite:!0,side:b.FrontSide,dithering:!0,polygonOffset:!0,polygonOffsetFactor:1,polygonOffsetUnits:1}),Re=new b.MeshPhysicalMaterial({color:new b.Color(16777215),metalness:0,roughness:0,transmission:.95,transparent:!0,opacity:.3,envMapIntensity:1,clearcoat:1,clearcoatRoughness:0,ior:1.52,reflectivity:.9,thickness:1,side:b.DoubleSide,polygonOffset:!0,polygonOffsetFactor:1,polygonOffsetUnits:1}),Te=new b.MeshPhysicalMaterial({color:new b.Color(1710618),metalness:0,roughness:.9,envMapIntensity:.2,clearcoat:.1,clearcoatRoughness:.8,reflectivity:.2,ior:1.3,transmission:0,depthWrite:!0,side:b.FrontSide,polygonOffset:!0,polygonOffsetFactor:1,polygonOffsetUnits:1}),we=new b.MeshPhysicalMaterial({color:new b.Color(8934707),metalness:0,roughness:.7,envMapIntensity:.3,clearcoat:.3,clearcoatRoughness:.4,reflectivity:.3,ior:1.3,transmission:0,depthWrite:!0,side:b.FrontSide,dithering:!0,polygonOffset:!0,polygonOffsetFactor:1,polygonOffsetUnits:1});var $=1096174675,W=1,k=1,Y=12,j=56;function U(e){let r=He(e),n=new DataView(r.buffer,r.byteOffset,r.byteLength);if(r.byteLength<Y)throw I("Blob too small to contain SLVA header.",{expectedBytes:Y,availableBytes:r.byteLength});let t=0,o=n.getUint32(t,!0);if(t+=4,o!==$)throw I(`Invalid SLVA magic: 0x${o.toString(16)}`,{expectedMagic:`0x${$.toString(16)}`,actualMagic:`0x${o.toString(16)}`});let a=n.getUint32(t,!0);if(t+=4,a!==W)throw I(`Unsupported SLVA version: ${a}`,{expectedVersion:W,actualVersion:a});let i=n.getUint32(t,!0);if(t+=4,t+i>r.byteLength)throw I("Insufficient data to read metadata JSON.",{expectedBytes:i,availableBytes:r.byteLength-t,offset:t});let c=r.subarray(t,t+i);t+=i;let l;try{l=JSON.parse(xe(c))}catch(A){throw I(`Failed to parse metadata JSON: ${A instanceof Error?A.message:String(A)}`,{metadataLen:i})}if(t+j>r.byteLength)throw I("Insufficient data to read geometry header.",{expectedBytes:j,availableBytes:r.byteLength-t,offset:t});let h=n.getUint32(t,!0);t+=4;let m=n.getFloat64(t,!0);t+=8;let u=n.getFloat64(t,!0);t+=8;let E=n.getFloat64(t,!0);t+=8;let g=n.getFloat64(t,!0);t+=8;let d=n.getFloat64(t,!0);t+=8;let H=n.getFloat64(t,!0);t+=8;let L=n.getUint32(t,!0);t+=4;let x=(h&k)!==0,C=L*3,p=C*(x?4:2);if(t+p>r.byteLength)throw I("Insufficient data to read vertices.",{expectedBytes:p,availableBytes:r.byteLength-t,offset:t,useFloat32:x,vertexCount:L});let T=r.byteOffset+t,v=x?ve(r.buffer,T,C):Ce(r.buffer,T,C);if(t+=p,t+4>r.byteLength)throw I("Insufficient data to read index count.",{expectedBytes:4,availableBytes:r.byteLength-t,offset:t});let R=n.getUint32(t,!0);t+=4;let S=R*4;if(t+S>r.byteLength)throw I("Insufficient data to read indices.",{expectedBytes:S,availableBytes:r.byteLength-t,offset:t,indexCount:R});let O=Se(r.buffer,r.byteOffset+t,R);return{metadata:l,flags:h,vertices:v,indices:O,origin:[m,u,E],scale:[g,d,H]}}function He(e){return typeof e=="string"?_(e):e instanceof Uint8Array?e:new Uint8Array(e)}function xe(e){if(typeof TextDecoder<"u")return new TextDecoder("utf-8").decode(e);if(typeof globalThis.Buffer<"u")return globalThis.Buffer.from(e).toString("utf-8");throw new F("No UTF-8 decoder available in this environment.",D.INVALID_STATE)}function Ce(e,r,n){if(n===0)return new Int16Array(0);if(r%2===0)return new Int16Array(e,r,n);let t=new Uint8Array(n*2);return t.set(new Uint8Array(e,r,n*2)),new Int16Array(t.buffer)}function ve(e,r,n){if(n===0)return new Float32Array(0);if(r%4===0)return new Float32Array(e,r,n);let t=new Uint8Array(n*4);return t.set(new Uint8Array(e,r,n*4)),new Float32Array(t.buffer)}function Se(e,r,n){if(n===0)return new Uint32Array(0);if(r%4===0)return new Uint32Array(e,r,n);let t=new Uint8Array(n*4);return t.set(new Uint8Array(e,r,n*4)),new Uint32Array(t.buffer)}function I(e,r){return new F(e,D.VALIDATION_ERROR,{context:r})}import*as M from"three";async function Z(e,r){let{mergeByMaterial:n=!0,applyTransforms:t=!0,debug:o=!1}=r??{},a=o?performance.now():0,i=0;try{let c=performance.now(),l=JSON.parse(e);return i=performance.now()-c,await Ae(l,{mergeByMaterial:n,applyTransforms:t,debug:o,parseTime:i,perfStart:a})}catch(c){return y().error("Error parsing mesh batch:",c),[]}}async function Ae(e,r){let{mergeByMaterial:n=!0,applyTransforms:t=!0,scaleFactor:o=1,debug:a=!1,parseTime:i=0,perfStart:c=a?performance.now():0}=r??{};try{let l=performance.now(),h=U(e.compressedData),m=performance.now()-l,u=a?Fe(e.compressedData):0;return q(h,{mergeByMaterial:n,applyTransforms:t,scaleFactor:o,debug:a,parseTime:i,decodeTime:m,perfStart:c,blobBytes:u,fallback:{materials:e.materials,groups:e.groups,sourceComponentId:e.sourceComponentId}})}catch(l){return y().error("Error parsing mesh batch object:",l),[]}}async function at(e,r){let{mergeByMaterial:n=!0,applyTransforms:t=!0,scaleFactor:o=1,debug:a=!1}=r??{},i=a?performance.now():0;try{let c=performance.now(),l=U(e),h=performance.now()-c,m=(e instanceof Uint8Array,e.byteLength);return q(l,{mergeByMaterial:n,applyTransforms:t,scaleFactor:o,debug:a,parseTime:0,decodeTime:h,perfStart:i,blobBytes:m})}catch(c){return y().error("Error parsing mesh batch blob:",c),[]}}function q(e,r){let{mergeByMaterial:n,applyTransforms:t,scaleFactor:o,debug:a,parseTime:i,decodeTime:c,perfStart:l,blobBytes:h,fallback:m}=r,u=e.metadata.materials??m?.materials??[],E=e.metadata.groups??m?.groups??[],g=e.metadata.sourceComponentId??m?.sourceComponentId,d=(e.flags&k)!==0,H=d?Le(e.vertices,t):Be(e.vertices,e.origin,e.scale,t);if(a){let p=e.vertices.byteLength+e.indices.byteLength;y().debug("Mesh Batch Stats:"),y().debug(` Materials: ${u.length} | Groups: ${E.length}`),y().debug(` Vertices: ${e.vertices.length/3} | Indices: ${e.indices.length}`),y().debug(` Format: ${d?"float32":"int16 quantized"}`),y().debug(` Blob: ${(h/1024/1024).toFixed(2)} MB | Geometry on wire: ${(p/1024/1024).toFixed(2)} MB`)}let L=performance.now(),x=u.map(Ie),C=[];for(let p of E)if(n&&p.meshes.length>1){let T=Oe(p,H,e.indices,x);T.userData.sourceComponentId=g??null,C.push(T)}else{let T=De(p,H,e.indices,x);for(let v of T)v.userData.sourceComponentId=g??null;C.push(...T)}if(o!==1)for(let p of C)p.scale.set(o,o,o);let f=performance.now()-L;if(a){let p=performance.now()-l;y().debug("Performance:"),i>0&&y().debug(` Parse JSON: ${i.toFixed(2)}ms`),y().debug(` Decode binary: ${c.toFixed(2)}ms`),y().debug(` Create Meshes: ${f.toFixed(2)}ms`),y().debug(` Total: ${p.toFixed(2)}ms`)}return Promise.resolve(C)}function Be(e,r,n,t){let o=new Float32Array(e.length),a=r[0],i=r[1],c=r[2],l=n[0],h=n[1],m=n[2];if(t)for(let u=0;u<e.length;u+=3){let E=a+(e[u]+32767)*l,g=i+(e[u+1]+32767)*h,d=c+(e[u+2]+32767)*m;o[u]=E,o[u+1]=d,o[u+2]=-g}else for(let u=0;u<e.length;u+=3)o[u]=a+(e[u]+32767)*l,o[u+1]=i+(e[u+1]+32767)*h,o[u+2]=c+(e[u+2]+32767)*m;return o}function Le(e,r){if(!r)return e;let n=new Float32Array(e.length);for(let t=0;t<e.length;t+=3){let o=e[t],a=e[t+1],i=e[t+2];n[t]=o,n[t+1]=i,n[t+2]=-a}return n}function Ie(e){let r=N(e.color);return new M.MeshPhysicalMaterial({color:r,metalness:e.metalness,roughness:e.roughness,opacity:e.opacity,transparent:e.transparent,side:M.DoubleSide,polygonOffset:!0,polygonOffsetFactor:.5,polygonOffsetUnits:.5,depthWrite:!0,depthTest:!0})}function Oe(e,r,n,t){let o=0,a=0;for(let d of e.meshes)o+=d.vertexCount,a+=d.indexCount;let i=new Float32Array(o*3),c=new Uint32Array(a),l=0,h=0;for(let d of e.meshes){let H=d.vertexStart*3,L=d.vertexCount*3;i.set(r.subarray(H,H+L),l*3);let x=n.subarray(d.indexStart,d.indexStart+d.indexCount),C=l-d.vertexStart;if(C===0)c.set(x,h);else for(let f=0;f<x.length;f++)c[h+f]=x[f]+C;l+=d.vertexCount,h+=d.indexCount}let m=new M.BufferGeometry;m.setAttribute("position",new M.BufferAttribute(i,3)),m.setIndex(new M.BufferAttribute(c,1)),m.computeVertexNormals();let u=new M.Mesh(m,t[e.materialId]),E=e.meshes[0],g=e.meshes.map(d=>d.name).filter(d=>d&&d.length>0);return u.name=g.length>0?g[0]:`merged_material_${e.materialId}`,u.castShadow=!0,u.receiveShadow=!0,u.userData={name:u.name,layer:E?.layer??"",originalIndex:E?.originalIndex??0,metadata:E?.metadata??{},mergedFrom:e.meshes.slice(1).map(d=>({name:d.name,layer:d.layer,originalIndex:d.originalIndex}))},u}function De(e,r,n,t){let o=[];for(let a of e.meshes){let i=a.vertexStart*3,c=a.vertexCount*3,l=r.slice(i,i+c),h=n.subarray(a.indexStart,a.indexStart+a.indexCount),m=new Uint32Array(h.length),u=a.vertexStart;for(let d=0;d<h.length;d++)m[d]=h[d]-u;let E=new M.BufferGeometry;E.setAttribute("position",new M.BufferAttribute(l,3)),E.setIndex(new M.BufferAttribute(m,1)),E.computeVertexNormals();let g=new M.Mesh(E,t[e.materialId]);g.name=a.name,g.userData={name:a.name,layer:a.layer??"",originalIndex:a.originalIndex,metadata:a.metadata??{}},g.castShadow=!0,g.receiveShadow=!0,o.push(g)}return o}function Fe(e){return Math.floor(e.length*3/4)}var Pe={Millimeters:1/1e3,Centimeters:1/100,Meters:1,Inches:1/39.37,Feet:1/3.28084},ze="Display";async function ut(e,r){let n=performance.now(),t=[],{allowScaling:o=!0,allowAutoPosition:a=!0,debug:i=!1,parsing:c={}}=r??{};try{let l=o?ke(e.modelunits):1;return await Ue(e,t,l,c,i),a&&Ve(t),t}catch(l){throw Ne(l,t),l}finally{i&&$e(n)}}function ke(e){return Pe[e]??1}async function Ue(e,r,n,t,o){for(let a of e.values){let i=a.InnerTree;for(let c in i){let l=i[c];l&&await _e(l,r,n,t,o)}}}async function _e(e,r,n,t,o){for(let a of e)if(a.type.includes(ze)){let i={mergeByMaterial:!0,applyTransforms:!0,debug:!1,...t},c=await Z(a.data,i);if(n!==1)for(let l of c)l.scale.set(n,n,n);r.push(...c),o&&y().debug(`Extracted ${c.length} meshes from batch`)}}function Ve(e){if(e.length===0)return;let n=z(e).min.y;G(e,n)}function Ne(e,r){y().error("An unexpected error occurred:",e),Ge(r)}function Ge(e){for(let r of e)r.geometry&&r.geometry.dispose(),r.material&&(Array.isArray(r.material)?r.material.forEach(n=>n.dispose()):r.material.dispose())}function $e(e){let r=performance.now()-e;y().info("Time to process meshes:",`${r.toFixed(2)}ms`)}export{Ze as a,Xe as b,N as c,G as d,z as e,Me as f,$ as g,W as h,k as i,U as j,Z as k,Ae as l,at as m,Pe as n,ut as o};
|
|
2
|
-
//# sourceMappingURL=chunk-OEDLGVIQ.js.map
|