@selvajs/compute 5.0.0-beta.8 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -11
- package/dist/core.d.ts +190 -2
- package/dist/core.js +1 -1
- package/dist/grasshopper.d.ts +71 -81
- package/dist/grasshopper.js +3081 -1
- package/dist/grasshopper.js.map +1 -1
- package/dist/handle-files-D-4X4Uh_.js +1379 -0
- package/dist/handle-files-D-4X4Uh_.js.map +1 -0
- package/dist/types-Dm9QFVvA.d.ts +247 -0
- package/package.json +14 -40
- package/dist/core-BPYXUtfq.js +0 -3
- package/dist/core-BPYXUtfq.js.map +0 -1
- package/dist/core-DF0VYOve.cjs +0 -3
- package/dist/core-DF0VYOve.cjs.map +0 -1
- package/dist/core.cjs +0 -1
- package/dist/core.d.cts +0 -2
- package/dist/grasshopper.cjs +0 -2
- package/dist/grasshopper.cjs.map +0 -1
- package/dist/grasshopper.d.cts +0 -1345
- package/dist/index-DDmprCht.d.cts +0 -421
- package/dist/index-DDmprCht.d.ts +0 -421
package/dist/grasshopper.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grasshopper.js","names":["readField","ComputeError","ErrorCodes","hasField","getLogger","detectBase64Payload","isDefinitionUrl","isRedirect","getResponseWireSize","base64ByteArray","isBase64Flag","fetchCompute","readBodyText","isDefinitionRef","downloadFileData","classifyProbeFailure","encodeStringToBase64","validateServerUrl","buildAuthHeaders","ComputeServerStats","serverUrl","auth","requestInit","init","timeoutMs","url","label","path","response","truncated","text","err","options","body","initialize","json","tryParseJson","kind","version","activeChildren","purged","children","calls","totalPurged","i","date","seconds","port","intervalMs","active","currentTimeoutId","check","_stats","callback","stopMonitoring","stopMonitor","warnedFunctions","isRealBrowser","nav","userAgent","warnIfClientSide","suppress","functionName","GRASSHOPPER_SERVER_ERROR_CODES","withGrasshopperErrorCodes","config","isDefinitionLoadMiss","error","warnOnEmptyInnerTrees","debug","values","empty","param","innerTree","scope","solveGrasshopperDefinition","uploadAndSolve","dataTree","definition","solveWithServerCacheKey","cacheKey","runSolve","prepareGrasshopperArgs","materializeDefinition","applyOptionalComputeSettings","args","result","maxResponseBytes","_algo","rest","wireSize","algoLength","setResponseWireSize","pointer","setOwnField","key","obj","value","TREE_PATH_RE","isDataTreeDefault","entries","val","coerceDefault","processed","transform","v","transformed","dropInvalidScalar","numericTransformer","trimmed","parsed","booleanTransformer","lower","textTransformer","colorTransformer","cleaned","objectTransformer","inputName","NUMERIC_ITEM_TYPES","INTEGER_ITEM_TYPES","dropDefault","message","input","normalizeDefault","tree","invalidItems","branch","items","item","data","type","num","bool","allValues","applyRounding","rounded","decimalPlaces","decimalsOf","x","clean","mantissa","exponent","decimals","getInputStepSize","serverStepSize","schema","listOr","defaultStep","mapNumbers","fn","computeNumeric","firstValue","stepSource","stepSize","inferred","n","numericParser","def","base","safeValue","booleanParser","textParser","valueListParser","defaultValue","defaultLower","match","warn","geometryFallback","INPUT_TYPE_PARSERS","unknownTypeFallback","CANONICAL_PARAM_TYPES","canonicalizeParamType","paramType","processInput","processInputWithError","rawInput","errors","report","baseInput","normalized","warning","parser","processInputsWithErrors","inputs","parseErrors","raw","rawInputs","normalizeInputSchema","normalizeOutputSchema","fetchDefinitionIO","algo","loadWarnings","nonEmptyStrings","loadErrors","rawOutputs","coerceDiagnostic","fetchParsedDefinitionIO","nodeCrypto","sha256Hex","hash","sha256HexPortable","K","state","Sha256","bytes","offset","take","w","whole","s","c0","c1","j","code","bitLength","tail","view","hex","word","y","s0","s1","h","a","b","c","d","e","f","g","k","S1","ch","t1","t2","stableStringify","stringify","out","parts","hashSolveInput","hashSolveInputForDefinition","hashDefinition","definitionHash","u8DefinitionHashes","hashDefinitionOnce","SolveInputHashes","SolveQueue","dead","estimateResponseSize","SolveScheduler","executor","baseConfig","cacheOpt","cacheConfig","next","raised","listener","detail","hashes","seq","ctx","cached","inflight","waitMs","controller","externalAbortHandler","startTime","callTiming","baseTiming","timing","requestId","durationMs","knownKey","entry","solveErrors","outcome","sizeBytes","oldest","hook","describeDefinition","summarizeDataTree","GrasshopperClient","client","attempts","baseDelayMs","maxDelayMs","lastProbe","failure","used","attempt","delay","resolve","status","isRhinoDecodeError","makeDecodeError","rhinoType","disposeRhinoObjects","seen","walk","del","isDeleted","proto","decoders","rhino","findDecoder","dec","isDecodableEnvelope","parsedData","decodeRhinoGeometry","decoder","decoderThrew","decoded","SYSTEM_TYPES","FILE_DATA_TYPE","skipItem","stringOnly","tryDecodeJSON","first","decodeBySystemType","extractItemValue","parseValues","asFileData","fileName","fileType","subFolder","flag","metadata","getResponseParams","itemType","forEachTreeItem","list","handler","getValues","aggregated","paramName","byId","extractFileData","output","file","getValue","parseOptions","matched","collected","GrasshopperResponseProcessor","selector","folderName","additionalFiles","readSchemaResults","USER_AUTHORED_MAPS","USER_KEYED_OBJECT_MAPS","isUpper","toCamelCase","run","normalizeUISchemaCasing","normalizeValue","child","canonical","normalizeEntries","TreeBuilder","pathStr","treeData","trees","coerce","kept","coerced","minimum","maximum","compiled","newValue","idx","t","branches","inner","min","max"],"sources":["../src/grasshopper/server/compute-server-stats.ts","../src/core/utils/warnings.ts","../src/grasshopper/solve.ts","../src/core/utils/set-own-field.ts","../src/grasshopper/data-tree/tree-path.ts","../src/grasshopper/io/input/transformers.ts","../src/grasshopper/io/input/normalize-default.ts","../src/grasshopper/io/input/numeric-rounding.ts","../src/grasshopper/io/input/input-type-parsers.ts","../src/grasshopper/io/input/input-processors.ts","../src/grasshopper/io/normalize-schema.ts","../src/grasshopper/io/definition-io.ts","../src/core/utils/sha256.ts","../src/grasshopper/scheduler/stable-hash.ts","../src/grasshopper/scheduler/solve-scheduler.ts","../src/grasshopper/client/grasshopper-client.ts","../src/grasshopper/io/output/rhino-objects.ts","../src/grasshopper/io/output/rhino-decoder.ts","../src/grasshopper/io/output/response-processors.ts","../src/grasshopper/client/grasshopper-response-processor.ts","../src/grasshopper/io/schema-endpoint.ts","../src/grasshopper/io/normalize-ui-schema.ts","../src/grasshopper/data-tree/data-tree.ts"],"sourcesContent":["import { ComputeError, ErrorCodes } from '@/core/errors';\nimport { getLogger } from '@/core/utils/logger';\nimport { validateServerUrl } from '@/core/server/validate-server-url';\nimport { buildAuthHeaders } from '@/core/compute-fetch/request';\nimport { readBodyText } from '@/core/compute-fetch/read-body';\nimport { isRedirect } from '@/core/compute-fetch/response';\nimport type { ComputeAuth } from '@/core/types';\n\nexport interface ServerVersion {\n\trhino: string;\n\tcompute: string;\n\tgit_sha: string | null;\n}\n\nexport interface ServerStats {\n\tisOnline: boolean;\n\tversion?: ServerVersion;\n\tactiveChildren?: number;\n}\n\n/**\n * ComputeServerStats provides methods to query Rhino Compute server statistics.\n *\n * @public Use this for server health monitoring and statistics.\n *\n * @example\n * ```typescript\n * const stats = new ComputeServerStats('http://localhost:6500', 'your-api-key');\n *\n * try {\n * const isOnline = await stats.isServerOnline();\n * const children = await stats.getActiveChildren();\n * const version = await stats.getVersion();\n *\n * // Or get everything at once\n * const allStats = await stats.getServerStats();\n * } finally {\n * await stats.dispose(); // Clean up resources\n * }\n * ```\n */\nexport default class ComputeServerStats {\n\tprivate readonly serverUrl: string;\n\tprivate readonly headers: Record<string, string>;\n\tprivate disposed = false;\n\tprivate activeMonitors: Set<() => void> = new Set();\n\n\t/** Timeout (ms) for the fast read/monitoring endpoints. */\n\tprivate static readonly DEFAULT_TIMEOUT_MS = 5000;\n\n\t/** Timeout (ms) for child-lifecycle POSTs — a cold Windows child can take ~30s to spawn. */\n\tprivate static readonly LIFECYCLE_TIMEOUT_MS = 60_000;\n\n\t/** Floor for `monitor()`'s `intervalMs` — anything lower hot-loops the server. */\n\tprivate static readonly MIN_MONITOR_INTERVAL_MS = 100;\n\n\t/** Every control-plane answer is a count, a version or a short map. */\n\tprivate static readonly MAX_BODY_BYTES = 1 << 20;\n\n\t/**\n\t * @param serverUrl - Base URL of the Rhino Compute server with http:// or https:// scheme (e.g., 'http://localhost:6500')\n\t * @param auth - An API key, or the same auth fields a solve's `ComputeConfig` carries\n\t * (`apiKey`, `apiKeyHeader`, `authToken`, `headers`).\n\t */\n\tconstructor(serverUrl: string, auth?: string | ComputeAuth) {\n\t\tthis.serverUrl = validateServerUrl(serverUrl);\n\t\tthis.headers = {\n\t\t\t...buildAuthHeaders(typeof auth === 'string' ? { apiKey: auth } : (auth ?? {})),\n\t\t\t'Content-Type': 'application/json'\n\t\t};\n\t}\n\n\t/**\n\t * Aborts after `timeoutMs` so a hung connection can't stall a probe or `monitor()`; `0` disables\n\t * it. Redirects are not followed: fetch would carry the API key header to the target.\n\t */\n\tprivate fetchWithTimeout(\n\t\turl: string,\n\t\tinit: RequestInit = {},\n\t\ttimeoutMs: number = ComputeServerStats.DEFAULT_TIMEOUT_MS\n\t): Promise<Response> {\n\t\tconst requestInit: RequestInit = { ...init, headers: this.headers, redirect: 'manual' };\n\t\tif (timeoutMs > 0 && !requestInit.signal) {\n\t\t\trequestInit.signal = AbortSignal.timeout(timeoutMs);\n\t\t}\n\t\treturn fetch(url, requestInit);\n\t}\n\n\t/**\n\t * The response body of `path`, or `null` (logged) on a non-2xx or a network\n\t * failure. Throws only when disposed.\n\t */\n\tprivate async getText(\n\t\tpath: string,\n\t\tinit: RequestInit = {},\n\t\ttimeoutMs: number = ComputeServerStats.DEFAULT_TIMEOUT_MS\n\t): Promise<string | null> {\n\t\tthis.ensureNotDisposed();\n\t\tconst label = `${init.method ?? 'GET'} ${path}`;\n\t\ttry {\n\t\t\tconst response = await this.fetchWithTimeout(`${this.serverUrl}${path}`, init, timeoutMs);\n\t\t\tif (!response.ok) {\n\t\t\t\tgetLogger().warn(`[ComputeServerStats] ${label} failed:`, response.status);\n\t\t\t\tawait response.body?.cancel().catch(() => {});\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst { text, truncated } = await readBodyText(response, ComputeServerStats.MAX_BODY_BYTES);\n\t\t\tif (truncated) {\n\t\t\t\tgetLogger().warn(`[ComputeServerStats] ${label} answered with an oversized body`);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn text;\n\t\t} catch (err) {\n\t\t\tgetLogger().warn(`[ComputeServerStats] ${label} errored:`, err);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Check if the server is online.\n\t *\n\t * This is a single-sample probe: it returns `true` only on a 2xx or a redirect\n\t * from the proxy liveness root `/`, and `false` for every other outcome (4xx/5xx,\n\t * network error, or timeout). A cold or briefly-busy-but-up server can therefore\n\t * read as offline — callers that gate on this (e.g. client construction)\n\t * should retry rather than treat a single `false` as authoritative.\n\t *\n\t * @param timeoutMs - Abort the probe after this many ms (default: 5000).\n\t * Pass `0` to disable the timeout. Prevents a hung connection from\n\t * stalling the caller indefinitely.\n\t */\n\tpublic async isServerOnline(\n\t\ttimeoutMs: number = ComputeServerStats.DEFAULT_TIMEOUT_MS\n\t): Promise<boolean> {\n\t\treturn (await this.probeServer(timeoutMs)).online;\n\t}\n\n\t/**\n\t * Detailed liveness probe backing {@link isServerOnline}.\n\t *\n\t * Reports what the probe actually saw so callers can distinguish \"connection\n\t * failed\" (`error` set) from \"server answered non-2xx\" (`status` set) — e.g.\n\t * a 401 from a proxy that requires an API key is a misconfiguration, not an\n\t * offline server. Same single-sample caveat as {@link isServerOnline}.\n\t *\n\t * @param timeoutMs - Abort the probe after this many ms (default: 5000).\n\t * Pass `0` to disable the timeout.\n\t * @returns `online` plus either the HTTP `status` the server answered with,\n\t * or the `error` the connection attempt failed with.\n\t */\n\tpublic async probeServer(\n\t\ttimeoutMs: number = ComputeServerStats.DEFAULT_TIMEOUT_MS\n\t): Promise<{ online: boolean; status?: number; error?: string }> {\n\t\tthis.ensureNotDisposed();\n\n\t\t// The rhino.compute proxy has no `/healthcheck` route; its real liveness\n\t\t// signal is `GET /`, which returns \"compute.rhino3d running\". Probing an\n\t\t// unknown path would instead be forwarded to a child and only tells us the\n\t\t// proxy can reach one. Some builds redirect `/` to rhino3d.com instead:\n\t\t// answering with a redirect is alive.\n\t\tconst url = `${this.serverUrl}/`;\n\n\t\ttry {\n\t\t\tconst response = await this.fetchWithTimeout(url, { method: 'GET' }, timeoutMs);\n\t\t\t// Unread, the body would hold its connection until garbage collection.\n\t\t\tawait response.body?.cancel().catch(() => {});\n\t\t\treturn { online: response.ok || isRedirect(response), status: response.status };\n\t\t} catch (err) {\n\t\t\tgetLogger().debug('[ComputeServerStats] Fetch error:', err);\n\t\t\treturn {\n\t\t\t\tonline: false,\n\t\t\t\terror: err instanceof Error ? `${err.name}: ${err.message}` : String(err)\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Get the number of active child processes on the server.\n\t *\n\t * By default the proxy's `/activechildren` endpoint will *spawn* children up\n\t * to the configured count if none are running, then report the count — which\n\t * wakes (and bills) an idle server. Pass `{ initialize: false }` for a passive\n\t * read that reports the current count without spawning; use this for\n\t * monitoring or before a purge/probe where you must not wake the server.\n\t *\n\t * @param options.initialize - When `false`, append `?initialize=false` so the\n\t * server reports without spawning. Defaults to `true` (the server's default).\n\t * @returns Number of active children, or null if unavailable\n\t */\n\tpublic async getActiveChildren(options: { initialize?: boolean } = {}): Promise<number | null> {\n\t\tconst { initialize = true } = options;\n\t\t// `initialize` may spawn children before answering: give it the lifecycle budget.\n\t\tconst body = initialize\n\t\t\t? await this.getText('/activechildren', {}, ComputeServerStats.LIFECYCLE_TIMEOUT_MS)\n\t\t\t: await this.getText('/activechildren?initialize=false');\n\t\tif (body === null) return null;\n\n\t\t// Strict: `parseInt` would read an error page starting \"302 Found\" as 302 children.\n\t\tconst text = body.trim();\n\t\tif (!/^\\d+$/.test(text)) {\n\t\t\tgetLogger().warn('[ComputeServerStats] /activechildren answered with a non-numeric body');\n\t\t\treturn null;\n\t\t}\n\t\treturn parseInt(text, 10);\n\t}\n\n\t/**\n\t * Get the server version information.\n\t *\n\t * @returns Version object with rhino, compute, and git_sha, or null if unavailable\n\t */\n\tpublic async getVersion(): Promise<ServerVersion | null> {\n\t\tconst text = await this.getText('/version');\n\t\tif (text === null) return null;\n\n\t\t// A non-JSON body is a bare version string.\n\t\tconst json = tryParseJson(text) as {\n\t\t\trhino?: string;\n\t\t\tcompute?: string;\n\t\t\tgit_sha?: string | null;\n\t\t} | null;\n\t\tif (json == null) return { rhino: text, compute: '', git_sha: null };\n\t\treturn { rhino: json.rhino ?? '', compute: json.compute ?? '', git_sha: json.git_sha ?? null };\n\t}\n\n\t/**\n\t * Get the plugins installed on the server.\n\t *\n\t * Returns a `name → version` map of non-core plugins the server has loaded,\n\t * or `null` if the request failed. Pass `kind` to choose which inventory:\n\t * `'gh'` (default) lists Grasshopper add-on assemblies via\n\t * `/plugins/gh/installed`; `'rhino'` lists Rhino plugins via\n\t * `/plugins/rhino/installed`. Plugins that ship with Rhino / are core\n\t * libraries are excluded by the server.\n\t *\n\t * @param kind - `'gh'` for Grasshopper add-ons (default) or `'rhino'` for Rhino plugins.\n\t * @returns Map of plugin name to version, or `null` on failure.\n\t *\n\t * @example\n\t * ```ts\n\t * const gh = await stats.getInstalledPlugins(); // Grasshopper add-ons\n\t * const selvaVersion = gh?.['Selva'] ?? null;\n\t * ```\n\t */\n\tpublic async getInstalledPlugins(\n\t\tkind: 'gh' | 'rhino' = 'gh'\n\t): Promise<Record<string, string> | null> {\n\t\tconst text = await this.getText(`/plugins/${kind}/installed`);\n\t\tconst json = text === null ? undefined : tryParseJson(text);\n\t\treturn json && typeof json === 'object' ? (json as Record<string, string>) : null;\n\t}\n\n\t/**\n\t * Get comprehensive server statistics.\n\t * Fetches all available server information in parallel.\n\t *\n\t * @returns Object containing server status and available stats\n\t */\n\tpublic async getServerStats(): Promise<ServerStats> {\n\t\tthis.ensureNotDisposed();\n\n\t\tconst isOnline = await this.isServerOnline();\n\n\t\tif (!isOnline) {\n\t\t\treturn { isOnline: false };\n\t\t}\n\n\t\t// Passive child count — never spawn from a stats read, or merely viewing\n\t\t// server health would wake (and bill) an idle server.\n\t\tconst [version, activeChildren] = await Promise.all([\n\t\t\tthis.getVersion(),\n\t\t\tthis.getActiveChildren({ initialize: false })\n\t\t]);\n\n\t\treturn {\n\t\t\tisOnline: true,\n\t\t\t...(version && { version }),\n\t\t\t...(activeChildren !== null && { activeChildren })\n\t\t};\n\t}\n\n\t/**\n\t * Purge the server's solve-results / URL-data cache.\n\t *\n\t * POSTs to `cache/purge` and returns the number of entries removed, or `null`\n\t * if the request failed. This clears cached solve responses and fetched\n\t * definition-URL data; it does NOT evict the definition cache (active\n\t * `pointer` references stay valid).\n\t *\n\t * **Caveat:** `cache/purge` is forwarded by the rhino.compute proxy to a\n\t * single round-robin-selected child, so in a multi-child deployment one call\n\t * purges one child's cache. Call repeatedly (or size the pool to 1) if you\n\t * need a fleet-wide purge.\n\t *\n\t * @returns Number of entries removed, or `null` on failure.\n\t *\n\t * @example\n\t * ```ts\n\t * const removed = await stats.purgeCache();\n\t * if (removed !== null) console.log(`Purged ${removed} cached solves`);\n\t * ```\n\t */\n\tpublic async purgeCache(): Promise<number | null> {\n\t\tconst text = await this.getText('/cache/purge', { method: 'POST' });\n\t\tconst json = text === null ? undefined : tryParseJson(text);\n\t\tconst purged = (json as { purged?: unknown } | null | undefined)?.purged;\n\t\treturn typeof purged === 'number' ? purged : null;\n\t}\n\n\t/**\n\t * Best-effort fleet-wide cache purge across a multi-child deployment.\n\t *\n\t * A single {@link purgeCache} POST is forwarded by the rhino.compute proxy to\n\t * just ONE round-robin-selected child, so the other children keep serving\n\t * stale cached solves. There is no proxy endpoint that addresses children\n\t * individually, so this method reads the active child count (passively, never\n\t * spawning) and fires `2 × count` sequential purges, relying on the proxy's\n\t * round-robin to spread the hits across the pool.\n\t *\n\t * **This is best-effort, not a guarantee.** Round-robin can revisit one child\n\t * and skip another; under concurrent traffic the rotation drifts. The result's\n\t * `confident` flag is `true` only when the server reports a single child (where\n\t * one purge is exact) — surface it so callers don't over-promise. For a hard\n\t * fleet-wide guarantee, run the deployment at `--childcount 1` or add a\n\t * server-side fan-out endpoint.\n\t *\n\t * @returns `{ totalPurged, calls, children, confident }`, or `null` if the\n\t * child count couldn't be read (server unreachable). `totalPurged` sums the\n\t * per-call counts; `calls` is how many purges were issued; `children` is the\n\t * reported pool size; `confident` is `true` only at a single-child pool.\n\t *\n\t * @example\n\t * ```ts\n\t * const r = await stats.purgeAllChildren();\n\t * if (r && !r.confident) {\n\t * console.warn(`Purged ~${r.totalPurged} across ${r.children} children (best-effort)`);\n\t * }\n\t * ```\n\t */\n\tpublic async purgeAllChildren(): Promise<{\n\t\ttotalPurged: number;\n\t\tcalls: number;\n\t\tchildren: number;\n\t\tconfident: boolean;\n\t} | null> {\n\t\tthis.ensureNotDisposed();\n\n\t\t// Passive read — must not spawn children just to purge them.\n\t\tconst children = await this.getActiveChildren({ initialize: false });\n\t\tif (children === null) {\n\t\t\tgetLogger().warn('[ComputeServerStats] purgeAllChildren: could not read child count');\n\t\t\treturn null;\n\t\t}\n\n\t\tif (children === 0) {\n\t\t\t// No live children means nothing is cached on the compute side.\n\t\t\treturn { totalPurged: 0, calls: 0, children: 0, confident: true };\n\t\t}\n\n\t\t// 2× the pool size gives round-robin a strong chance of reaching every\n\t\t// child at least once. Sequential (not parallel) so the proxy advances its\n\t\t// round-robin cursor one child per call rather than racing them onto one.\n\t\tconst calls = children * 2;\n\t\tlet totalPurged = 0;\n\t\tfor (let i = 0; i < calls; i++) {\n\t\t\tconst purged = await this.purgeCache();\n\t\t\tif (purged !== null) totalPurged += purged;\n\t\t}\n\n\t\treturn { totalPurged, calls, children, confident: children === 1 };\n\t}\n\n\t/**\n\t * Get the server's current UTC clock.\n\t *\n\t * GETs `/servertime`, which the server emits as a JSON-encoded ISO-8601\n\t * timestamp (e.g. `\"2026-06-18T08:30:00Z\"`). Useful for detecting clock skew\n\t * between caller and server. Returns `null` if the request failed or the body\n\t * isn't a parseable date.\n\t *\n\t * @returns A `Date` for the server's UTC time, or `null` on failure.\n\t */\n\tpublic async getServerTime(): Promise<Date | null> {\n\t\tconst text = await this.getText('/servertime');\n\t\tif (text === null) return null;\n\t\t// The body is a JSON string: strip its quotes.\n\t\tconst date = new Date(text.trim().replace(/^\"|\"$/g, ''));\n\t\treturn isNaN(date.getTime()) ? null : date;\n\t}\n\n\t/**\n\t * Get how long the rhino.compute proxy has been idle.\n\t *\n\t * GETs `/idlespan` on the proxy, which returns the seconds elapsed since the\n\t * last request was forwarded to a compute child. This is a proxy-level metric\n\t * (not proxied to a child) used by autoscalers to decide when a node can be\n\t * drained. Returns `null` if unavailable.\n\t *\n\t * @returns Idle time in seconds, or `null` on failure.\n\t */\n\tpublic async getIdleSpan(): Promise<number | null> {\n\t\tconst text = await this.getText('/idlespan');\n\t\tif (text === null) return null;\n\t\tconst seconds = parseFloat(text.trim());\n\t\treturn isNaN(seconds) ? null : seconds;\n\t}\n\n\t/**\n\t * Fill the compute child pool up to the server's configured baseline.\n\t *\n\t * POSTs `/launch-children`. No-op when the pool is already at or above the\n\t * configured `--childcount`. Returns `{ spawned, active }` — how many children\n\t * were started and the resulting child count — or `null` on failure.\n\t *\n\t * To raise capacity above the baseline use {@link launchChild}; the baseline\n\t * itself can only be changed by restarting rhino.compute.\n\t *\n\t * @returns `{ spawned, active }`, or `null` on failure.\n\t */\n\tpublic async launchChildren(): Promise<{ spawned: number[]; active: number } | null> {\n\t\treturn this.postJson('/launch-children');\n\t}\n\n\t/**\n\t * Add a single compute child to the pool, optionally on a specific port.\n\t *\n\t * POSTs `/launch-child` (with `?port=N` when `port` is given). Unlike\n\t * {@link launchChildren}, this can push the pool above the baseline, up to the\n\t * server's `MaxChildren` cap. Returns `{ spawned: [port] }` on success, or\n\t * `null` on failure (server replies 400 for a bad port, 409 if the port is in\n\t * use, 503 at the max-children cap).\n\t *\n\t * @param port - Optional specific port to launch on; otherwise the next free one.\n\t * @returns `{ spawned }` listing the launched port, or `null` on failure.\n\t */\n\tpublic async launchChild(port?: number): Promise<{ spawned: number[] } | null> {\n\t\tconst path = port === undefined ? '/launch-child' : `/launch-child?port=${port}`;\n\t\treturn this.postJson(path);\n\t}\n\n\t/**\n\t * Gracefully shut down compute children without respawning them.\n\t *\n\t * POSTs `/shutdown-children`. With no `port` it shuts down every child; with\n\t * `port` it targets just that one. Children do not respawn, but the next\n\t * `/grasshopper` request auto-spawns the pool back to the baseline. Returns\n\t * `{ shutdown, active }` — how many were stopped and the remaining count — or\n\t * `null` on failure.\n\t *\n\t * @param port - Optional port to target; omit to shut down all children.\n\t * @returns `{ shutdown, active }`, or `null` on failure.\n\t */\n\tpublic async shutdownChildren(\n\t\tport?: number\n\t): Promise<{ shutdown: number; active: number } | null> {\n\t\tconst path = port === undefined ? '/shutdown-children' : `/shutdown-children?port=${port}`;\n\t\treturn this.postJson(path);\n\t}\n\n\t/**\n\t * Shut down compute children and respawn replacements (rolling restart).\n\t *\n\t * POSTs `/recycle-children`. With no `port` it recycles every child; with\n\t * `port` it recycles just that one. The server recycles sequentially (each\n\t * replacement is serving before the next child is stopped) so the pool never\n\t * drops to zero mid-recycle. Returns `{ shutdown, spawned, active }`, or\n\t * `null` on failure.\n\t *\n\t * @param port - Optional port to target; omit to recycle all children.\n\t * @returns `{ shutdown, spawned, active }`, or `null` on failure.\n\t */\n\tpublic async recycleChildren(\n\t\tport?: number\n\t): Promise<{ shutdown: number; spawned: number[]; active: number } | null> {\n\t\tconst path = port === undefined ? '/recycle-children' : `/recycle-children?port=${port}`;\n\t\treturn this.postJson(path);\n\t}\n\n\t/** A child-lifecycle POST: a spawn or recycle can take ~30 s. */\n\tprivate async postJson<T>(path: string): Promise<T | null> {\n\t\tconst text = await this.getText(\n\t\t\tpath,\n\t\t\t{ method: 'POST' },\n\t\t\tComputeServerStats.LIFECYCLE_TIMEOUT_MS\n\t\t);\n\t\treturn text === null ? null : ((tryParseJson(text) ?? null) as T | null);\n\t}\n\n\t/**\n\t * Continuously monitor server stats at specified interval.\n\t *\n\t * @param callback - Function called with stats on each interval\n\t * @param intervalMs - Milliseconds between checks (default: 5000). Must be a\n\t * finite number of at least 100 ms — lower values would hot-loop the server.\n\t * @returns Function to stop monitoring\n\t * @throws {ComputeError} `INVALID_CONFIG` if `intervalMs` is not a finite number >= 100.\n\t *\n\t * @example\n\t * ```typescript\n\t * const stopMonitoring = stats.monitor((data) => {\n\t * console.log('Server stats:', data);\n\t * }, 3000);\n\t *\n\t * // Later...\n\t * stopMonitoring();\n\t * ```\n\t */\n\tpublic monitor(callback: (stats: ServerStats) => void, intervalMs: number = 5000): () => void {\n\t\tthis.ensureNotDisposed();\n\n\t\tif (!Number.isFinite(intervalMs) || intervalMs < ComputeServerStats.MIN_MONITOR_INTERVAL_MS) {\n\t\t\t// ComputeError (not RangeError) so `err instanceof ComputeError` holds\n\t\t\t// across the package's whole public surface.\n\t\t\tthrow new ComputeError(\n\t\t\t\t`monitor() intervalMs must be a finite number >= ${ComputeServerStats.MIN_MONITOR_INTERVAL_MS}ms, ` +\n\t\t\t\t\t`got ${intervalMs}. Sub-${ComputeServerStats.MIN_MONITOR_INTERVAL_MS}ms polling would hot-loop the server.`,\n\t\t\t\tErrorCodes.INVALID_CONFIG,\n\t\t\t\t{ context: { intervalMs } }\n\t\t\t);\n\t\t}\n\n\t\tlet active = true;\n\t\tlet currentTimeoutId: ReturnType<typeof setTimeout> | null = null;\n\n\t\tgetLogger().info(`🔄 Starting server stats monitoring every ${intervalMs}ms`);\n\n\t\tconst check = async () => {\n\t\t\tcurrentTimeoutId = null;\n\t\t\tif (!active || this.disposed) return;\n\n\t\t\ttry {\n\t\t\t\tconst _stats = await this.getServerStats();\n\n\t\t\t\tif (!active || this.disposed) return;\n\n\t\t\t\ttry {\n\t\t\t\t\tcallback(_stats);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tgetLogger().error('[ComputeServerStats] Monitor callback threw:', err);\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tif (!active || this.disposed) {\n\t\t\t\t\t// dispose()/stop racing an in-flight poll is normal shutdown, not an\n\t\t\t\t\t// error — the nested ensureNotDisposed() throw is expected here.\n\t\t\t\t\tgetLogger().debug('[ComputeServerStats] Monitor poll cancelled during shutdown:', err);\n\t\t\t\t} else {\n\t\t\t\t\tgetLogger().error('[ComputeServerStats] Failed to fetch stats during monitor:', err);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (active && !this.disposed) {\n\t\t\t\tcurrentTimeoutId = setTimeout(() => void check(), intervalMs);\n\t\t\t}\n\t\t};\n\n\t\tconst stopMonitoring = () => {\n\t\t\tactive = false;\n\t\t\tif (currentTimeoutId !== null) {\n\t\t\t\tclearTimeout(currentTimeoutId);\n\t\t\t\tcurrentTimeoutId = null;\n\t\t\t}\n\n\t\t\tthis.activeMonitors.delete(stopMonitoring);\n\t\t};\n\n\t\tthis.activeMonitors.add(stopMonitoring);\n\n\t\tvoid check();\n\n\t\treturn stopMonitoring;\n\t}\n\n\t/**\n\t * Disposes of all resources and stops all active monitors.\n\t * Call this when you're done using the stats instance.\n\t */\n\tpublic async dispose(): Promise<void> {\n\t\tif (this.disposed) return;\n\n\t\tthis.disposed = true;\n\t\tfor (const stopMonitor of this.activeMonitors) {\n\t\t\tstopMonitor();\n\t\t}\n\t\tthis.activeMonitors.clear();\n\t}\n\n\tprivate ensureNotDisposed(): void {\n\t\tif (this.disposed) {\n\t\t\tthrow new ComputeError(\n\t\t\t\t'ComputeServerStats 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\n/** The parsed body, or `undefined` when it isn't JSON. */\nfunction tryParseJson(text: string): unknown {\n\ttry {\n\t\treturn JSON.parse(text);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n","import { getLogger } from './logger';\n\n/** Functions that already warned — the exposure risk doesn't change per call, so once is enough. */\nconst warnedFunctions = new Set<string>();\n\n/**\n * True only in a real browser, where an API key in the config is exposed to users. jsdom defines\n * `window` too, so Node is ruled out by `process.versions.node`, and by jsdom's user-agent marker\n * for setups that hide `process`.\n */\nfunction isRealBrowser(): boolean {\n\tif (typeof window === 'undefined') return false;\n\n\tconst proc = (globalThis as { process?: { versions?: { node?: unknown } } }).process;\n\tif (proc?.versions?.node != null) return false;\n\n\tconst nav = (globalThis as { navigator?: { userAgent?: unknown } }).navigator;\n\tconst userAgent = typeof nav?.userAgent === 'string' ? nav.userAgent : '';\n\tif (/jsdom/i.test(userAgent)) return false;\n\n\treturn true;\n}\n\nexport function warnIfClientSide(functionName: string, suppress?: boolean): void {\n\tif (suppress) {\n\t\treturn;\n\t}\n\n\tif (isRealBrowser() && !warnedFunctions.has(functionName)) {\n\t\twarnedFunctions.add(functionName);\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\n/** Reset the once-per-function dedupe. For tests. */\nexport function resetClientSideWarnings(): void {\n\twarnedFunctions.clear();\n}\n","import { fetchCompute, ComputeError, ErrorCodes } from '@/core';\nimport type { ComputeConfig, ServerErrorCodeMap } from '@/core/types';\nimport { getResponseWireSize, setResponseWireSize } from '@/core/compute-fetch/wire-size';\nimport { base64ByteArray, detectBase64Payload, encodeStringToBase64 } from '@/core/utils/encoding';\nimport { getLogger } from '@/core/utils/logger';\nimport { readField } from '@/core/utils/read-field';\nimport { warnIfClientSide } from '@/core/utils/warnings';\nimport { validateServerUrl } from '@/core/server/validate-server-url';\n\nimport {\n\tGrasshopperRequestSchema,\n\tGrasshopperComputeConfig,\n\tGrasshopperComputeResponse,\n\tDataTree\n} from './types';\nimport { isDefinitionRef, isDefinitionUrl, type SolveDefinition } from '@/core/definition-ref';\n\n/**\n * Server message for a `pointer` it can't resolve (evicted, or a different child in the pool).\n * Production servers scrub it, so the `definition_not_cached` code is the reliable signal; this\n * substring covers debug-mode servers and forks that emit no code.\n */\nconst DEFINITION_LOAD_FAILED = 'Unable to load grasshopper definition';\n\n/** `core/` knows no backend's wire codes, so every Grasshopper request passes these in. */\nconst GRASSHOPPER_SERVER_ERROR_CODES: ServerErrorCodeMap = {\n\tdefinition_not_cached: ErrorCodes.DEFINITION_NOT_CACHED\n};\n\n/** Attach the Grasshopper wire-code table without clobbering a caller's own entries. */\nexport function withGrasshopperErrorCodes<T extends ComputeConfig>(config: T): T {\n\treturn {\n\t\t...config,\n\t\tserverErrorCodes: { ...GRASSHOPPER_SERVER_ERROR_CODES, ...config.serverErrorCodes }\n\t};\n}\n\nfunction isDefinitionLoadMiss(error: unknown): boolean {\n\tif (!(error instanceof ComputeError)) return false;\n\treturn (\n\t\terror.code === ErrorCodes.DEFINITION_NOT_CACHED ||\n\t\terror.message.includes(DEFINITION_LOAD_FAILED)\n\t);\n}\n\n/**\n * Debug-only: names the outputs that produced nothing, usually a definition that didn't compute\n * (missing inputs, a guarded branch). An empty output can be legitimate, so this only warns.\n *\n * @internal Exported for testing.\n */\nexport function warnOnEmptyInnerTrees(response: GrasshopperComputeResponse, debug?: boolean): void {\n\tif (!debug) return;\n\n\tconst values = readField<unknown[]>(response, 'values');\n\tif (!Array.isArray(values) || values.length === 0) return;\n\n\tconst empty: string[] = [];\n\tfor (const param of values) {\n\t\tconst innerTree = readField<Record<string, unknown>>(param, 'innerTree');\n\t\tif (!innerTree || Object.keys(innerTree).length === 0) {\n\t\t\tempty.push(readField<string>(param, 'paramName') ?? '<unnamed>');\n\t\t}\n\t}\n\n\tif (empty.length === 0) return;\n\n\tconst scope = empty.length === values.length ? 'all' : `${empty.length}/${values.length}`;\n\tgetLogger().warn(\n\t\t`Solve returned empty output(s) (${scope}): ${empty.join(', ')}. ` +\n\t\t\t`These parameters produced no data — check the definition's inputs and the branch feeding each.`\n\t);\n}\n\n/**\n * `cacheKey` is the server's `pointer`: its `md5_…` key for an uploaded definition, or the URL\n * echoed back for a URL definition. `null` means only that the response carried no pointer.\n */\nexport interface SolveWithCacheKey {\n\tresponse: GrasshopperComputeResponse;\n\tcacheKey: string | null;\n}\n\n/**\n * Run one solve. `GrasshopperClient.solve()` adds input validation and throws on solver errors.\n *\n * @param definition - A URL (sent as a pointer), base64 `.gh` content, raw `.gh` bytes, or a\n * `DefinitionRef` (loaded for the upload). Any other string is encoded as the definition text.\n * @returns The compute response, without the server's `pointer` and echoed `algo`.\n *\n * @example\n * await solveGrasshopperDefinition(trees, 'https://example.com/definition.gh', config);\n * await solveGrasshopperDefinition(trees, new Uint8Array(await readFile('model.gh')), config);\n */\nexport async function solveGrasshopperDefinition(\n\tdataTree: DataTree[],\n\tdefinition: SolveDefinition,\n\tconfig: GrasshopperComputeConfig\n): Promise<GrasshopperComputeResponse> {\n\twarnIfClientSide('solveGrasshopperDefinition', config.suppressBrowserWarning);\n\treturn (await uploadAndSolve(dataTree, definition, config)).response;\n}\n\n/**\n * Solve by `pointer: cacheKey` when a key is known, skipping the upload. With no key, or when the\n * server has evicted it, upload the full definition and report `missed: true`. A `DefinitionRef`\n * is loaded only on that upload path.\n */\nexport async function solveWithServerCacheKey(\n\tdataTree: DataTree[],\n\tcacheKey: string | null,\n\tdefinition: SolveDefinition,\n\tconfig: GrasshopperComputeConfig\n): Promise<SolveWithCacheKey & { missed: boolean }> {\n\twarnIfClientSide('solveWithServerCacheKey', config.suppressBrowserWarning);\n\n\tif (cacheKey !== null) {\n\t\ttry {\n\t\t\tconst hit = await runSolve({ algo: null, pointer: cacheKey, values: dataTree }, config);\n\t\t\treturn { ...hit, missed: false };\n\t\t} catch (error) {\n\t\t\tif (!isDefinitionLoadMiss(error)) throw error;\n\t\t}\n\t}\n\treturn { ...(await uploadAndSolve(dataTree, definition, config)), missed: true };\n}\n\nasync function uploadAndSolve(\n\tdataTree: DataTree[],\n\tdefinition: SolveDefinition,\n\tconfig: GrasshopperComputeConfig\n): Promise<SolveWithCacheKey> {\n\tconst bytes = await materializeDefinition(definition);\n\treturn runSolve(prepareGrasshopperArgs(bytes, dataTree), config);\n}\n\nasync function materializeDefinition(definition: SolveDefinition): Promise<string | Uint8Array> {\n\tif (!isDefinitionRef(definition)) return definition;\n\ttry {\n\t\treturn await definition.load();\n\t} catch (error) {\n\t\tthrow new ComputeError(\n\t\t\t`Failed to load definition bytes for ref '${definition.key}'`,\n\t\t\tErrorCodes.INVALID_INPUT,\n\t\t\t{\n\t\t\t\tcontext: { definitionKey: definition.key },\n\t\t\t\toriginalError: error\n\t\t\t}\n\t\t);\n\t}\n}\n\n/**\n * POST a solve and split the server's `pointer` off the response. The server echoes the request's\n * base64 definition back as `algo`; it is dropped so a cached response doesn't pin a multi-MB copy\n * of the definition against the scheduler's byte budget.\n */\nasync function runSolve(\n\targs: GrasshopperRequestSchema,\n\tconfig: GrasshopperComputeConfig\n): Promise<SolveWithCacheKey> {\n\tvalidateServerUrl(config.serverUrl);\n\tapplyOptionalComputeSettings(args, config);\n\n\t// The server echoes an uploaded definition back as `algo`: the limit is for the rest.\n\tconst { maxResponseBytes } = config;\n\tconst result = await fetchCompute<GrasshopperComputeResponse>(\n\t\t'grasshopper',\n\t\targs,\n\t\twithGrasshopperErrorCodes({\n\t\t\t...config,\n\t\t\tmaxResponseBytes:\n\t\t\t\tmaxResponseBytes !== undefined && args.algo\n\t\t\t\t\t? maxResponseBytes + args.algo.length\n\t\t\t\t\t: maxResponseBytes\n\t\t})\n\t);\n\n\tconst {\n\t\tpointer,\n\t\talgo: _algo,\n\t\t...rest\n\t} = result as GrasshopperComputeResponse & { pointer?: unknown; algo?: unknown };\n\tconst response = rest as GrasshopperComputeResponse;\n\t// The wire-size hint is keyed by object identity, so the copy re-registers it, minus `algo`.\n\tconst wireSize = getResponseWireSize(result);\n\tif (wireSize !== undefined) {\n\t\tconst algoLength = typeof _algo === 'string' ? _algo.length : 0;\n\t\tsetResponseWireSize(response, Math.max(0, wireSize - algoLength));\n\t}\n\twarnOnEmptyInnerTrees(response, config.debug);\n\treturn {\n\t\tresponse,\n\t\tcacheKey: typeof pointer === 'string' ? pointer : null\n\t};\n}\n\n// ============================================================================\n// Grasshopper Arguments\n// ============================================================================\n\n/** @internal */\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\targs.algo = base64ByteArray(definition);\n\t} else if (isDefinitionUrl(definition)) {\n\t\targs.pointer = definition;\n\t} else {\n\t\t// Only long base64 passes through (see detectBase64Payload); a short base64-shaped string\n\t\t// like \"test\" is encoded as text. A Uint8Array skips the sniffing.\n\t\targs.algo = detectBase64Payload(definition) ?? 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\tif (options.selvaevents != null) arglist.selvaevents = options.selvaevents;\n}\n","/**\n * `obj[key] = value` for a key taken from a wire payload. Assigning `__proto__` would call the\n * prototype setter and swap the object's prototype instead of storing the value.\n */\nexport function setOwnField(obj: object, key: string, value: unknown): void {\n\tif (key === '__proto__') {\n\t\tObject.defineProperty(obj, key, {\n\t\t\tvalue,\n\t\t\twritable: true,\n\t\t\tenumerable: true,\n\t\t\tconfigurable: true\n\t\t});\n\t} else {\n\t\t(obj as Record<string, unknown>)[key] = value;\n\t}\n}\n","import type { DataTreeDefault } from '../types';\n\n/**\n * Canonical matcher for a Grasshopper branch path key like `{0}`, `{0;1}`, or\n * the root path `{}`. This is the single source of truth for the branch-path\n * shape (`DataTreePath`); anything testing \"does this string name a branch?\"\n * should use this rather than re-inlining the regex.\n *\n * Matches `{}`, `{0}`, `{0;1;2}`, and negative indices like `{-1;2}` (GH_Path\n * allows them) — but NOT empty segments like `{0;}` / `{;}` / `{0;;1}`, which\n * would otherwise split to phantom 0s (`Number('') === 0`).\n */\nexport const TREE_PATH_RE = /^\\{(-?\\d+(?:;-?\\d+)*)?\\}$/;\n\n/**\n * Membership test for a {@link DataTreeDefault}: an object keyed entirely by\n * branch paths, each mapping to an array of values. This is the one predicate\n * both the input-type parsers (to pass a tree-access default through untouched)\n * and `TreeBuilder` (to dispatch it to `fromDataTreeDefault`) ask — so the two\n * agree by construction on exactly which values are trees.\n */\nexport function isDataTreeDefault(value: unknown): value is DataTreeDefault {\n\tif (typeof value !== 'object' || value === null || Array.isArray(value)) return false;\n\tconst entries = Object.entries(value);\n\treturn (\n\t\tentries.length > 0 &&\n\t\tentries.every(([key, val]) => TREE_PATH_RE.test(key) && Array.isArray(val))\n\t);\n}\n","import { getLogger } from '@/core';\n\n/**\n * Coerce one raw default value to `T`, or return `null` when it can't be\n * coerced. Transformers never throw — the caller decides whether a `null`\n * is filtered (array items) or surfaced as a parse error (scalars).\n */\nexport type ValueTransformer<T> = (value: unknown) => T | null;\n\n/**\n * Coerce a schema's `default`: array items are transformed and the rejected ones\n * filtered (none left → `undefined`); a rejected scalar becomes `undefined` when\n * `dropInvalidScalar`, else is kept verbatim for the parser to report.\n */\nexport function coerceDefault<T>(\n\tvalue: unknown,\n\ttransform: ValueTransformer<T>,\n\tdropInvalidScalar: boolean\n): unknown {\n\tif (value === undefined || value === null) {\n\t\treturn value;\n\t}\n\n\tif (Array.isArray(value)) {\n\t\tconst processed = value.map(transform).filter((v): v is T => v !== null);\n\t\treturn processed.length > 0 ? processed : undefined;\n\t}\n\n\tconst transformed = transform(value);\n\tif (transformed !== null) {\n\t\treturn transformed;\n\t}\n\treturn dropInvalidScalar ? undefined : value;\n}\n\nexport const numericTransformer: ValueTransformer<number> = (value) => {\n\tif (typeof value === 'number') return Number.isFinite(value) ? value : null;\n\tif (typeof value === 'string') {\n\t\tconst trimmed = value.trim();\n\t\t// `Number('')` is 0, so reject empty/whitespace before coercing — an empty\n\t\t// default should drop to null, not silently become 0.\n\t\tif (trimmed === '') return null;\n\t\t// `Number()` also accepts '0x10' and 'Infinity'; a Grasshopper numeric\n\t\t// default holds neither.\n\t\tif (/^[+-]?0[xbo]/i.test(trimmed)) return null;\n\t\tconst parsed = Number(trimmed);\n\t\treturn Number.isFinite(parsed) ? parsed : null;\n\t}\n\treturn null;\n};\n\nexport const booleanTransformer: ValueTransformer<boolean> = (value) => {\n\tif (typeof value === 'boolean') return value;\n\tif (typeof value === 'string') {\n\t\tconst lower = value.trim().toLowerCase();\n\t\tif (lower === 'true') return true;\n\t\tif (lower === 'false') return false;\n\t}\n\treturn null;\n};\n\nexport const textTransformer: ValueTransformer<string> = (value) => {\n\tif (typeof value === 'string') {\n\t\tif (value.length >= 2 && value.startsWith('\"') && value.endsWith('\"'))\n\t\t\treturn value.slice(1, -1);\n\t\t// Unbalanced leading quote: strip only the quote, not the last character.\n\t\tif (value.startsWith('\"')) return value.slice(1);\n\t\treturn value;\n\t}\n\treturn null;\n};\n\nexport const colorTransformer: ValueTransformer<string> = (value) => {\n\tif (typeof value === 'string') {\n\t\tlet cleaned = value.trim();\n\t\tif (cleaned.startsWith('\"') && cleaned.endsWith('\"')) {\n\t\t\tcleaned = cleaned.slice(1, -1).trim();\n\t\t}\n\t\treturn cleaned;\n\t}\n\treturn null;\n};\n\nexport function objectTransformer(inputName: string): ValueTransformer<object> {\n\treturn (value) => {\n\t\tif (typeof value === 'object' && value !== null) return value;\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) return parsed;\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","import { ErrorCodes, getLogger } from '@/core';\nimport { readField, hasField } from '@/core/utils/read-field';\nimport { setOwnField } from '@/core/utils/set-own-field';\nimport { isDataTreeDefault } from '../../data-tree/tree-path';\nimport { booleanTransformer, numericTransformer } from './transformers';\nimport type { ParserWarning } from './input-type-parsers';\nimport type { InputParamSchema } from '../../types';\n\n/** Every numeric CLR type Grasshopper serializes, so no numeric item stays a string. */\nconst NUMERIC_ITEM_TYPES = new Set([\n\t'System.Double',\n\t'System.Single',\n\t'System.Decimal',\n\t'System.Int32',\n\t'System.Int64'\n]);\n\n/** Integral wire item types — rounded like the scalar Integer path. */\nconst INTEGER_ITEM_TYPES = new Set(['System.Int32', 'System.Int64']);\n\ninterface NormalizedDefault {\n\tschema: InputParamSchema;\n\twarning?: ParserWarning;\n}\n\nfunction dropDefault(input: InputParamSchema, message: string): NormalizedDefault {\n\tgetLogger().warn(message);\n\treturn {\n\t\tschema: { ...input, default: null },\n\t\twarning: { code: ErrorCodes.MALFORMED_DEFAULT, message }\n\t};\n}\n\n/**\n * Flatten a raw input's `default` DataTree into the shape the type parsers take.\n * Never mutates `input`. A default it can't interpret is dropped with a `warning`.\n */\nexport function normalizeDefault(input: InputParamSchema, label: string): NormalizedDefault {\n\tif (typeof input.default !== 'object' || input.default === null) {\n\t\treturn { schema: input };\n\t}\n\n\t// Arrays and branch-path maps are already flattened: the per-type parsers take\n\t// both as-is, so neither may fall into the no-innerTree branch below.\n\tif (Array.isArray(input.default) || isDataTreeDefault(input.default)) {\n\t\treturn { schema: input };\n\t}\n\n\tif (!hasField(input.default, 'innerTree')) {\n\t\treturn dropDefault(\n\t\t\tinput,\n\t\t\t`Input \"${label}\" default had an unrecognized shape (no innerTree key); the default was dropped.`\n\t\t);\n\t}\n\n\tconst innerTree = readField<Record<string, unknown>>(input.default, 'innerTree') ?? {};\n\tif (Object.keys(innerTree).length === 0) {\n\t\treturn { schema: { ...input, default: undefined } };\n\t}\n\n\tif (input.treeAccess || (input.atMost && input.atMost > 1)) {\n\t\t// Items go through the same transformers as scalar defaults.\n\t\tconst tree: Record<string, unknown[]> = {};\n\t\tconst invalidItems: string[] = [];\n\t\tfor (const [branch, items] of Object.entries(innerTree)) {\n\t\t\tif (!Array.isArray(items)) {\n\t\t\t\treturn dropDefault(\n\t\t\t\t\tinput,\n\t\t\t\t\t`Input \"${label}\" default had a non-array innerTree branch (\"${branch}\"); the default was dropped.`\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst parsed: unknown[] = [];\n\t\t\tfor (const item of items) {\n\t\t\t\tconst data = readField(item, 'data');\n\t\t\t\tconst type = readField<string>(item, 'type');\n\t\t\t\tif (type && NUMERIC_ITEM_TYPES.has(type)) {\n\t\t\t\t\t// Blank means \"no value\", as for a scalar default: dropped, never 0.\n\t\t\t\t\tif (typeof data === 'string' && data.trim() === '') continue;\n\t\t\t\t\tconst num = numericTransformer(data);\n\t\t\t\t\tif (num === null) {\n\t\t\t\t\t\tinvalidItems.push(`\"${String(data)}\" (${type})`);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tparsed.push(INTEGER_ITEM_TYPES.has(type) ? Math.round(num) : num);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (type === 'System.Boolean') {\n\t\t\t\t\tconst bool = booleanTransformer(data);\n\t\t\t\t\tif (bool === null) {\n\t\t\t\t\t\tinvalidItems.push(`\"${String(data)}\" (${type})`);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tparsed.push(bool);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// Only geometry is JSON-encoded on the wire. A `System.String` label like\n\t\t\t\t// `[1,2,3]` must stay a string: the fork's Newtonsoft rejects a non-string leaf.\n\t\t\t\tif (typeof data === 'string' && type?.startsWith('Rhino.Geometry')) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tparsed.push(JSON.parse(data));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tparsed.push(data);\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tparsed.push(data);\n\t\t\t}\n\t\t\tsetOwnField(tree, branch, parsed);\n\t\t}\n\n\t\t// The good values survive; the dropped ones are reported.\n\t\tif (invalidItems.length > 0) {\n\t\t\tconst message = `Input \"${label}\" default contained ${invalidItems.length} tree value(s) that could not be parsed and were dropped: ${invalidItems.join(', ')}.`;\n\t\t\tgetLogger().warn(message);\n\t\t\treturn {\n\t\t\t\tschema: { ...input, default: tree },\n\t\t\t\twarning: { code: ErrorCodes.MALFORMED_DEFAULT, message }\n\t\t\t};\n\t\t}\n\t\treturn { schema: { ...input, default: tree } };\n\t}\n\n\tconst allValues: unknown[] = [];\n\tfor (const items of Object.values(innerTree)) {\n\t\tif (!Array.isArray(items)) continue;\n\t\tfor (const item of items) {\n\t\t\tif (item && typeof item === 'object' && hasField(item, 'data')) {\n\t\t\t\tallValues.push(readField(item, 'data'));\n\t\t\t}\n\t\t}\n\t}\n\treturn { schema: { ...input, default: allValues.length > 1 ? allValues : allValues[0] } };\n}\n","import type { InputParamSchema } from '../../types';\n\n/** Snap float noise (`0.30000000000000004`) to `decimalPlaces`; a real difference is kept. */\nexport function applyRounding(value: number, decimalPlaces: number): number {\n\tconst rounded = Number(value.toFixed(decimalPlaces));\n\treturn Math.abs(value - rounded) < 1e-8 ? rounded : value;\n}\n\nconst MAX_DECIMALS = 12;\n// A double holds 15 significant digits exactly. Fixed places would not do: from 1e4 up,\n// `toFixed(12)` prints the float's own noise (`16384.200000000001`).\nconst SIGNIFICANT_DIGITS = 15;\n\n/**\n * Decimal places `x` is written with, ignoring float noise, at most 12:\n * `0.30000000000000004` → 1, `16384.2` → 1, `1e-7` → 7, `1.5e21` → 0.\n */\nexport function decimalsOf(x: number): number {\n\tif (!Number.isFinite(x)) return 0;\n\tconst clean = Number(Math.abs(x).toPrecision(SIGNIFICANT_DIGITS));\n\tconst [mantissa, exponent = '0'] = String(clean).split('e');\n\tconst decimals = (mantissa.split('.')[1]?.length ?? 0) - Number(exponent);\n\treturn Math.min(Math.max(decimals, 0), MAX_DECIMALS);\n}\n\nexport function getInputStepSize(value: number): number {\n\tif (!Number.isFinite(value) || value === 0) return 0.1;\n\tconst decimals = decimalsOf(value);\n\tif (decimals === 0) return Math.abs(value) >= 1 ? 1 : 0.1;\n\treturn Number((10 ** -decimals).toFixed(decimals));\n}\n\n/** A server-authored `stepSize`, or `undefined` when absent or unusable. */\nexport function serverStepSize(schema: InputParamSchema): number | undefined {\n\treturn typeof schema.stepSize === 'number' &&\n\t\tNumber.isFinite(schema.stepSize) &&\n\t\tschema.stepSize > 0\n\t\t? schema.stepSize\n\t\t: undefined;\n}\n","import { ComputeError, ErrorCodes, type ErrorCode } from '@/core/errors';\nimport { getLogger } from '@/core';\nimport { isDataTreeDefault } from '../../data-tree/tree-path';\nimport { applyRounding, decimalsOf, getInputStepSize, serverStepSize } from './numeric-rounding';\nimport {\n\tbooleanTransformer,\n\tcoerceDefault,\n\tcolorTransformer,\n\tnumericTransformer,\n\tobjectTransformer,\n\ttextTransformer\n} from './transformers';\nimport type {\n\tBaseInputType,\n\tBooleanInputType,\n\tColorInputType,\n\tFileInputType,\n\tGeometryInputType,\n\tInputParam,\n\tInputParamSchema,\n\tNumericInputType,\n\tTextInputType,\n\tValueListInputType\n} from '../../types';\n\n/**\n * A recoverable oddity a parser found while still succeeding (e.g. a ValueList\n * default not in its values map). Reaches clients through `parseErrors`.\n */\nexport interface ParserWarning {\n\tcode: ErrorCode;\n\tmessage: string;\n}\n\n/**\n * One adapter per Grasshopper param type: value coercion, type-specific fields\n * and the typed param. Pure: reads a `normalizeDefault`'d schema, never mutates it.\n */\nexport interface InputTypeParser<T extends InputParam = InputParam> {\n\t/** Throws a {@link ComputeError} on bad input; calls `warn` for oddities it recovered from. */\n\tparse(schema: InputParamSchema, base: BaseInputType, warn?: (warning: ParserWarning) => void): T;\n\t/** The safe param when {@link parse} throws. Omit it when `parse` can't throw. */\n\tfallback?(schema: InputParamSchema, base: BaseInputType): T;\n}\n\nconst listOr = <T>(schema: InputParamSchema, value: T): T | T[] =>\n\t(schema.atMost ?? 1) > 1 ? [value] : value;\n\nconst defaultStep = (schema: InputParamSchema): number =>\n\tserverStepSize(schema) ?? (schema.paramType === 'Integer' ? 1 : 0.1);\n\nconst mapNumbers = (value: unknown, fn: (n: number) => number): unknown =>\n\tArray.isArray(value)\n\t\t? value.map((v) => (typeof v === 'number' ? fn(v) : v))\n\t\t: typeof value === 'number'\n\t\t\t? fn(value)\n\t\t\t: value;\n\nfunction computeNumeric(schema: InputParamSchema): {\n\tdefault: NumericInputType['default'];\n\tstepSize: number;\n} {\n\t// A tree default passes through: TreeBuilder applies the numeric constraints.\n\tif (isDataTreeDefault(schema.default)) {\n\t\treturn {\n\t\t\tdefault: schema.default as NumericInputType['default'],\n\t\t\tstepSize: defaultStep(schema)\n\t\t};\n\t}\n\n\t// A non-blank string that doesn't parse ('1,5', 'Infinity', hex) is an error,\n\t// not a silent `undefined`. Blank still means \"no default\".\n\tif (\n\t\ttypeof schema.default === 'string' &&\n\t\tschema.default.trim() !== '' &&\n\t\tnumericTransformer(schema.default) === null\n\t) {\n\t\tthrow new ComputeError(\n\t\t\t`Invalid numeric default \"${schema.default}\" for input \"${schema.name || 'unknown'}\"`,\n\t\t\tErrorCodes.VALIDATION_ERROR,\n\t\t\t{ context: { inputName: schema.name, default: schema.default } }\n\t\t);\n\t}\n\n\tconst value = coerceDefault(schema.default, numericTransformer, true);\n\n\tif (schema.paramType === 'Integer') {\n\t\treturn {\n\t\t\tdefault: mapNumbers(value, Math.round) as NumericInputType['default'],\n\t\t\tstepSize: defaultStep(schema)\n\t\t};\n\t}\n\n\tconst firstValue = Array.isArray(value) ? value[0] : value;\n\tconst stepSource = [firstValue, schema.minimum, schema.maximum].find(\n\t\t(v): v is number => typeof v === 'number' && Number.isFinite(v) && v !== 0\n\t);\n\tconst stepSize =\n\t\tserverStepSize(schema) ?? (stepSource !== undefined ? getInputStepSize(stepSource) : 0.1);\n\n\tlet decimalPlaces = decimalsOf(stepSize);\n\tif (\n\t\tdecimalPlaces === 0 &&\n\t\ttypeof firstValue === 'number' &&\n\t\tfirstValue !== 0 &&\n\t\tMath.abs(firstValue) < 1\n\t) {\n\t\tconst inferred = Math.ceil(-Math.log10(Math.abs(firstValue)));\n\t\tif (Number.isFinite(inferred) && inferred > 0) {\n\t\t\tdecimalPlaces = inferred;\n\t\t}\n\t}\n\tdecimalPlaces = Math.min(Math.max(decimalPlaces, 0), 12);\n\n\treturn {\n\t\tdefault: mapNumbers(value, (n) =>\n\t\t\tapplyRounding(n, decimalPlaces)\n\t\t) as NumericInputType['default'],\n\t\tstepSize\n\t};\n}\n\nconst numericParser: InputTypeParser<NumericInputType> = {\n\tparse(schema, base) {\n\t\tconst { default: def, stepSize } = computeNumeric(schema);\n\t\treturn {\n\t\t\t...base,\n\t\t\tparamType: schema.paramType as 'Number' | 'Integer',\n\t\t\tminimum: schema.minimum,\n\t\t\tmaximum: schema.maximum,\n\t\t\tatLeast: schema.atLeast,\n\t\t\tatMost: schema.atMost,\n\t\t\tstepSize,\n\t\t\tdefault: def\n\t\t};\n\t},\n\tfallback(schema, base) {\n\t\t// With minimum > 0, a plain 0 would sit below the slider's own range.\n\t\tconst safeValue =\n\t\t\ttypeof schema.minimum === 'number' && Number.isFinite(schema.minimum) && schema.minimum > 0\n\t\t\t\t? schema.minimum\n\t\t\t\t: 0;\n\t\treturn {\n\t\t\t...base,\n\t\t\tparamType: schema.paramType as 'Number' | 'Integer',\n\t\t\tminimum: schema.minimum,\n\t\t\tmaximum: schema.maximum,\n\t\t\tatLeast: schema.atLeast,\n\t\t\tatMost: schema.atMost,\n\t\t\tstepSize: defaultStep(schema),\n\t\t\tdefault: listOr(schema, safeValue)\n\t\t};\n\t}\n};\n\nconst booleanParser: InputTypeParser<BooleanInputType> = {\n\tparse(schema, base) {\n\t\tif (isDataTreeDefault(schema.default)) {\n\t\t\treturn {\n\t\t\t\t...base,\n\t\t\t\tparamType: 'Boolean',\n\t\t\t\tdefault: schema.default as BooleanInputType['default']\n\t\t\t};\n\t\t}\n\t\tconst value = coerceDefault(schema.default, booleanTransformer, false);\n\t\t// A bad array item is filtered; a bad scalar survives coercion verbatim\n\t\t// (dropInvalidScalar=false) and is reported here instead.\n\t\tif (\n\t\t\tvalue !== undefined &&\n\t\t\tvalue !== null &&\n\t\t\ttypeof value !== 'boolean' &&\n\t\t\t!Array.isArray(value)\n\t\t) {\n\t\t\tthrow new ComputeError(\n\t\t\t\t`Invalid boolean default \"${String(value)}\" for input \"${schema.name || 'unknown'}\"`,\n\t\t\t\tErrorCodes.VALIDATION_ERROR,\n\t\t\t\t{ context: { inputName: schema.name, default: schema.default } }\n\t\t\t);\n\t\t}\n\t\treturn { ...base, paramType: 'Boolean', default: value as BooleanInputType['default'] };\n\t},\n\tfallback(schema, base) {\n\t\treturn { ...base, paramType: 'Boolean', default: listOr(schema, false) };\n\t}\n};\n\nconst textParser: InputTypeParser<TextInputType> = {\n\tparse(schema, base) {\n\t\tconst value = coerceDefault(schema.default, textTransformer, false);\n\t\treturn { ...base, paramType: 'Text', default: value as TextInputType['default'] };\n\t}\n};\n\nconst valueListParser: InputTypeParser<ValueListInputType> = {\n\tparse(schema, base, warn) {\n\t\tif (\n\t\t\t!schema.values ||\n\t\t\ttypeof schema.values !== 'object' ||\n\t\t\tObject.keys(schema.values).length === 0\n\t\t) {\n\t\t\tconst inputName = schema.nickname || 'unnamed';\n\t\t\tthrow new ComputeError(\n\t\t\t\t`Input \"${inputName}\" has no values defined (expected ValueList)`,\n\t\t\t\tErrorCodes.INVALID_INPUT,\n\t\t\t\t{ context: { inputName, expectedType: 'ValueList' } }\n\t\t\t);\n\t\t}\n\n\t\tlet defaultValue = schema.default as string | undefined;\n\t\tif (schema.default !== undefined && schema.default !== null) {\n\t\t\t// A tree/array-shaped default can't index the values map — `String()`\n\t\t\t// would silently turn it into '[object Object]'. Reject it properly.\n\t\t\tif (typeof schema.default === 'object') {\n\t\t\t\tthrow new ComputeError(\n\t\t\t\t\t`ValueList input \"${schema.nickname || 'unnamed'}\" default is not a string-able value`,\n\t\t\t\t\tErrorCodes.VALIDATION_ERROR,\n\t\t\t\t\t{ context: { inputName: schema.name, default: schema.default } }\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst defaultLower = String(schema.default).toLowerCase();\n\t\t\t// Membership is case-insensitive, but downstream lookups (`values[default]`)\n\t\t\t// are not — return the canonical-cased key on a match.\n\t\t\tconst match = Object.keys(schema.values).find((key) => key.toLowerCase() === defaultLower);\n\t\t\tif (match !== undefined) {\n\t\t\t\tdefaultValue = match;\n\t\t\t} else {\n\t\t\t\t// Out-of-range only warns: the parse still succeeds.\n\t\t\t\tconst message = `ValueList input \"${schema.nickname || 'unnamed'}\" default value \"${schema.default}\" is not in available values`;\n\t\t\t\tgetLogger().warn(message);\n\t\t\t\twarn?.({ code: ErrorCodes.VALIDATION_ERROR, message });\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\t...base,\n\t\t\tparamType: 'ValueList',\n\t\t\tvalues: schema.values as Record<string, string>,\n\t\t\tdefault: defaultValue\n\t\t};\n\t},\n\tfallback(schema, base) {\n\t\t// The default can't be validated against the map here, so drop it.\n\t\treturn {\n\t\t\t...base,\n\t\t\tparamType: 'ValueList',\n\t\t\tvalues: schema.values && typeof schema.values === 'object' ? schema.values : {},\n\t\t\tdefault: undefined\n\t\t};\n\t}\n};\n\nfunction geometryFallback(schema: InputParamSchema, base: BaseInputType): GeometryInputType {\n\treturn {\n\t\t...base,\n\t\tparamType: 'Geometry',\n\t\tdefault: listOr(schema, null) as GeometryInputType['default']\n\t};\n}\n\nconst geometryParser: InputTypeParser<GeometryInputType> = {\n\tparse(schema, base) {\n\t\tconst value = coerceDefault(\n\t\t\tschema.default,\n\t\t\tobjectTransformer(schema.nickname || 'unnamed'),\n\t\t\ttrue\n\t\t);\n\t\treturn {\n\t\t\t...base,\n\t\t\tparamType: 'Geometry',\n\t\t\tdefault: value as GeometryInputType['default']\n\t\t};\n\t},\n\tfallback: geometryFallback\n};\n\nconst fileParser: InputTypeParser<FileInputType> = {\n\tparse(schema, base) {\n\t\tconst value = coerceDefault(\n\t\t\tschema.default,\n\t\t\tobjectTransformer(schema.nickname || 'unnamed'),\n\t\t\ttrue\n\t\t);\n\t\treturn {\n\t\t\t...base,\n\t\t\tparamType: 'File',\n\t\t\tacceptedFormats: schema.acceptedFormats,\n\t\t\tdefault: value as FileInputType['default']\n\t\t};\n\t}\n};\n\nconst colorParser: InputTypeParser<ColorInputType> = {\n\tparse(schema, base) {\n\t\tconst value = coerceDefault(schema.default, colorTransformer, false);\n\t\treturn { ...base, paramType: 'Color', default: value as ColorInputType['default'] };\n\t}\n};\n\n/** Keyed by canonical paramType. */\nexport const INPUT_TYPE_PARSERS: ReadonlyMap<string, InputTypeParser> = new Map<\n\tstring,\n\tInputTypeParser\n>([\n\t['Number', numericParser],\n\t['Integer', numericParser],\n\t['Boolean', booleanParser],\n\t['Text', textParser],\n\t['ValueList', valueListParser],\n\t['Geometry', geometryParser],\n\t['File', fileParser],\n\t['Color', colorParser]\n]);\n\nexport const unknownTypeFallback = geometryFallback;\n","import { ComputeError, ErrorCodes } from '@/core/errors';\nimport { getLogger } from '@/core/utils/logger';\n\nimport { normalizeDefault } from './normalize-default';\nimport { INPUT_TYPE_PARSERS, unknownTypeFallback } from './input-type-parsers';\n\nimport type { BaseInputType, InputParam, InputParamSchema, InputParseError } from '../../types';\n\nconst CANONICAL_PARAM_TYPES = new Map(\n\t[...INPUT_TYPE_PARSERS.keys()].map((key) => [key.toLowerCase(), key])\n);\n\n/** An unknown paramType comes back unchanged, so its unknown-type error still surfaces. */\nfunction canonicalizeParamType(paramType: string): string {\n\treturn CANONICAL_PARAM_TYPES.get(paramType?.toLowerCase()) ?? paramType;\n}\n\n/**\n * Parse one raw Grasshopper input schema into a typed {@link InputParam}.\n * A validation failure is swallowed and replaced with a safe default.\n */\nexport function processInput(rawInput: InputParamSchema): InputParam {\n\treturn processInputWithError(rawInput).input;\n}\n\n/**\n * Like {@link processInput}, but also returns every problem found on the way, in\n * order: a dropped default, warnings the parser recovered from, and the parser\n * error that forced the safe default. Entries carry the RAW declared `paramType`\n * so clients match on the casing they sent.\n *\n * A non-`ComputeError` failure still throws: it is a bug, not bad input.\n */\nexport function processInputWithError(rawInput: InputParamSchema): {\n\tinput: InputParam;\n\terrors: InputParseError[];\n} {\n\tconst label = rawInput.name || 'unknown';\n\tconst errors: InputParseError[] = [];\n\tconst report = ({ code, message }: { code: string; message: string }) => {\n\t\terrors.push({ inputName: label, paramType: rawInput.paramType, message, code });\n\t};\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\t// Absent, not '': \"no group\" must stay distinct from an empty group name.\n\t\tgroupName: rawInput.groupName ?? undefined,\n\t\tid: rawInput.id\n\t};\n\n\t// Selva schemas send \"valueList\", the plugin \"ValueList\".\n\tconst paramType = canonicalizeParamType(rawInput.paramType);\n\n\tlet schema: InputParamSchema = { ...rawInput, paramType };\n\ttry {\n\t\tconst { schema: normalized, warning } = normalizeDefault(schema, label);\n\t\tschema = normalized;\n\t\tif (warning) report(warning);\n\t} catch (error) {\n\t\t// A default too malformed to walk is bad data, not a bug: drop it for this\n\t\t// input rather than failing the whole definition-IO fetch.\n\t\tschema = { ...schema, default: null };\n\t\treport({\n\t\t\tcode: ErrorCodes.MALFORMED_DEFAULT,\n\t\t\tmessage: `Input \"${label}\" default could not be normalized and was dropped: ${\n\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t}`\n\t\t});\n\t}\n\n\tconst parser = INPUT_TYPE_PARSERS.get(paramType);\n\ttry {\n\t\tif (!parser) {\n\t\t\tthrow new ComputeError(`Unknown paramType: ${paramType}`, ErrorCodes.VALIDATION_ERROR, {\n\t\t\t\tcontext: { receivedParamType: paramType, paramName: rawInput.name }\n\t\t\t});\n\t\t}\n\t\treturn { input: parser.parse(schema, baseInput, report), errors };\n\t} catch (error) {\n\t\tif (!(error instanceof ComputeError)) {\n\t\t\tthrow new ComputeError(\n\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\tErrorCodes.VALIDATION_ERROR,\n\t\t\t\t{\n\t\t\t\t\tcontext: { paramName: rawInput.name, paramType },\n\t\t\t\t\toriginalError: error\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t\tgetLogger().error(`Validation error for input ${label}:`, error.message);\n\t\treport(error);\n\t\tconst fallback = parser?.fallback ?? unknownTypeFallback;\n\t\treturn { input: fallback(schema, baseInput), errors };\n\t}\n}\n\n/** {@link processInputWithError} over a list, with every input's errors collected. */\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, errors } = processInputWithError(raw);\n\t\tinputs.push(input);\n\t\tparseErrors.push(...errors);\n\t}\n\treturn { inputs, parseErrors };\n}\n","import { readField } from '@/core/utils/read-field';\nimport type { InputParamSchema, OutputParamSchema } from '../types';\n\n/**\n * Canonicalize a raw `/io` param record's field keys to camelCase, so the\n * parsers can read `schema.paramType` whichever casing the server sent. Values,\n * including `default` and the user-authored `values` map, pass through verbatim.\n *\n * A missing required field gets a fallback that keeps the type true: `paramType`\n * `''` surfaces as an unknown-type parse error, and `groupName` stays `null`\n * (not `''`) so \"no group\" differs from an empty group name.\n */\nexport function normalizeInputSchema(raw: unknown): InputParamSchema {\n\tconst stepSize = readField<unknown>(raw, 'stepSize');\n\treturn {\n\t\tid: readField<string>(raw, 'id') ?? '',\n\t\tname: readField<string>(raw, 'name') ?? '',\n\t\tnickname: readField<string | null>(raw, 'nickname') ?? null,\n\t\tdescription: readField<string>(raw, 'description') ?? '',\n\t\tparamType: readField<string>(raw, 'paramType') ?? '',\n\t\ttreeAccess: readField<boolean>(raw, 'treeAccess') ?? false,\n\t\tminimum: readField<number | null>(raw, 'minimum') ?? null,\n\t\tmaximum: readField<number | null>(raw, 'maximum') ?? null,\n\t\tatLeast: readField<number>(raw, 'atLeast') ?? 1,\n\t\tatMost: readField<number>(raw, 'atMost') ?? 1,\n\t\tstepSize: typeof stepSize === 'number' && Number.isFinite(stepSize) ? stepSize : undefined,\n\t\tdefault: readField(raw, 'default'),\n\t\tvalues: readField<Record<string, string>>(raw, 'values'),\n\t\tacceptedFormats: readField<string[]>(raw, 'acceptedFormats'),\n\t\tgroupName: readField<string | null>(raw, 'groupName') ?? null\n\t};\n}\n\nexport function normalizeOutputSchema(raw: unknown): OutputParamSchema {\n\treturn {\n\t\tname: readField<string>(raw, 'name') ?? '',\n\t\tnickname: readField<string | null>(raw, 'nickname') ?? null,\n\t\tparamType: readField<string>(raw, 'paramType') ?? '',\n\t\tid: readField<string>(raw, 'id') ?? ''\n\t};\n}\n","import { ComputeConfig, ComputeError, ErrorCodes } from '@/core';\nimport { fetchCompute } from '@/core/compute-fetch/compute-fetch';\nimport { readField } from '@/core/utils/read-field';\nimport { warnIfClientSide } from '@/core/utils/warnings';\nimport { validateServerUrl } from '@/core/server/validate-server-url';\nimport { prepareGrasshopperArgs, withGrasshopperErrorCodes } from '../solve';\n\nimport { GrasshopperParsedIO, GrasshopperParsedIORaw } from '../types';\n\nimport { processInputsWithErrors } from './input/input-processors';\nimport { normalizeInputSchema, normalizeOutputSchema } from './normalize-schema';\n\n/**\n * Fetches a definition's input/output schemas without per-type parsing.\n *\n * Field KEYS are canonicalized to camelCase and missing required fields get a\n * fallback ({@link normalizeInputSchema}); a missing or non-array `inputs` /\n * `outputs` becomes `[]`. Field VALUES, notably `default` and the `values`\n * label map, pass through verbatim.\n *\n * @throws {ComputeError} 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\twarnIfClientSide('fetchDefinitionIO', config.suppressBrowserWarning);\n\tvalidateServerUrl(config.serverUrl);\n\n\tconst { algo, pointer } = prepareGrasshopperArgs(definition, []);\n\tif (!algo && !pointer) {\n\t\tthrow new ComputeError(\n\t\t\t'Definition must resolve to either a URL pointer or base64 algo',\n\t\t\tErrorCodes.INVALID_INPUT\n\t\t);\n\t}\n\n\tconst response = await fetchCompute<unknown>(\n\t\t'io',\n\t\talgo ? { algo } : { pointer },\n\t\twithGrasshopperErrorCodes(config)\n\t);\n\n\tif (!response || typeof response !== 'object') {\n\t\tthrow new ComputeError('Invalid IO response structure', ErrorCodes.INVALID_INPUT, {\n\t\t\tcontext: { response }\n\t\t});\n\t}\n\n\tconst loadWarnings = nonEmptyStrings(readField(response, 'warnings'));\n\tconst loadErrors = nonEmptyStrings(readField(response, 'errors'));\n\n\t// Array.isArray, not `?? []`: a load failure can surface as a 200 whose\n\t// `inputs` is `{}` or a string, and that must become `[]` too.\n\tconst rawInputs = readField(response, 'inputs');\n\tconst rawOutputs = readField(response, 'outputs');\n\treturn {\n\t\tinputs: Array.isArray(rawInputs) ? rawInputs.map(normalizeInputSchema) : [],\n\t\toutputs: Array.isArray(rawOutputs) ? rawOutputs.map(normalizeOutputSchema) : [],\n\t\t...(loadWarnings && { loadWarnings }),\n\t\t...(loadErrors && { loadErrors })\n\t};\n}\n\n/**\n * Server diagnostics as non-blank strings, or `undefined` when there are none.\n * Non-string entries are kept, not dropped: a fork reporting `{ message }`\n * objects must not yield an empty input list with no explanation.\n */\nfunction nonEmptyStrings(value: unknown): string[] | undefined {\n\tif (!Array.isArray(value)) return undefined;\n\tconst cleaned = value.map(coerceDiagnostic).filter((v): v is string => v !== undefined);\n\treturn cleaned.length > 0 ? cleaned : undefined;\n}\n\nfunction coerceDiagnostic(value: unknown): string | undefined {\n\tif (value === null || value === undefined) return undefined;\n\tlet text: string;\n\tif (typeof value === 'string') {\n\t\ttext = value;\n\t} else if (typeof value === 'object') {\n\t\tconst message = readField<unknown>(value, 'message');\n\t\tif (typeof message === 'string' && message.trim().length > 0) {\n\t\t\ttext = message;\n\t\t} else {\n\t\t\ttry {\n\t\t\t\ttext = JSON.stringify(value) ?? String(value);\n\t\t\t} catch {\n\t\t\t\ttext = String(value);\n\t\t\t}\n\t\t}\n\t} else {\n\t\ttext = String(value);\n\t}\n\treturn text.trim().length > 0 ? text : undefined;\n}\n\n/**\n * Fetches a definition's input/output schemas and parses each input into its\n * typed `InputParam`. An input that fails to parse gets a safe default\n * and an entry in `parseErrors`.\n *\n * @throws {ComputeError} 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: 'http://localhost:6500', 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\tconst {\n\t\tinputs: rawInputs,\n\t\toutputs,\n\t\tloadWarnings,\n\t\tloadErrors\n\t} = await fetchDefinitionIO(definition, config);\n\tconst { inputs, parseErrors } = processInputsWithErrors(rawInputs);\n\n\treturn {\n\t\tinputs,\n\t\toutputs,\n\t\t...(parseErrors.length > 0 && { parseErrors }),\n\t\t...(loadWarnings && { loadWarnings }),\n\t\t...(loadErrors && { loadErrors })\n\t};\n}\n","/**\n * SHA-256 for cache keys that callers may try to forge: a key shared across users must not be\n * collidable on purpose, which rules out a fast non-cryptographic hash.\n *\n * Strings hash as their UTF-16LE code units. UTF-8 would fold every lone surrogate into U+FFFD,\n * so two different strings would hash alike.\n */\n\ntype NodeCrypto = typeof import('node:crypto');\n\n// `getBuiltinModule` is synchronous, so Node gets native hashing without a top-level import that\n// would break browser bundles. Everywhere else takes the portable path.\nconst nodeCrypto = (\n\tglobalThis as { process?: { getBuiltinModule?: (id: string) => unknown } }\n).process?.getBuiltinModule?.('node:crypto') as NodeCrypto | undefined;\n\n/** Lowercase hex SHA-256 of `input`. */\nexport function sha256Hex(input: string | Uint8Array): string {\n\tif (nodeCrypto) {\n\t\tconst hash = nodeCrypto.createHash('sha256');\n\t\tif (typeof input === 'string') hash.update(input, 'utf16le');\n\t\telse hash.update(input);\n\t\treturn hash.digest('hex');\n\t}\n\treturn sha256HexPortable(input);\n}\n\n// ============================================================================\n// Portable implementation (FIPS 180-4)\n// ============================================================================\n\nconst K = new Int32Array([\n\t0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n\t0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n\t0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n\t0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n\t0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n\t0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n\t0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n\t0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2\n]);\n\n/** @internal Exported so tests can check it against Node's native hash. */\nexport function sha256HexPortable(input: string | Uint8Array): string {\n\tconst state = new Sha256();\n\tif (typeof input === 'string') state.updateString(input);\n\telse state.update(input);\n\treturn state.digestHex();\n}\n\nclass Sha256 {\n\tprivate readonly h = new Int32Array([\n\t\t0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19\n\t]);\n\tprivate readonly w = new Int32Array(64);\n\tprivate readonly pending = new Uint8Array(64);\n\tprivate pendingLength = 0;\n\tprivate totalBytes = 0;\n\n\tupdate(bytes: Uint8Array): void {\n\t\tthis.totalBytes += bytes.length;\n\t\tlet offset = 0;\n\t\tif (this.pendingLength > 0) {\n\t\t\tconst take = Math.min(64 - this.pendingLength, bytes.length);\n\t\t\tthis.pending.set(bytes.subarray(0, take), this.pendingLength);\n\t\t\tthis.pendingLength += take;\n\t\t\toffset = take;\n\t\t\tif (this.pendingLength < 64) return;\n\t\t\tthis.compressBytes(this.pending, 0);\n\t\t\tthis.pendingLength = 0;\n\t\t}\n\t\tfor (; offset + 64 <= bytes.length; offset += 64) this.compressBytes(bytes, offset);\n\t\tthis.pending.set(bytes.subarray(offset), 0);\n\t\tthis.pendingLength = bytes.length - offset;\n\t}\n\n\t/**\n\t * Feed a string's UTF-16LE bytes straight into the message words, skipping a byte buffer.\n\t * Only on a fresh state: 32 code units fill a block exactly, so nothing may be pending.\n\t */\n\tupdateString(s: string): void {\n\t\tconst w = this.w;\n\t\tconst whole = s.length - (s.length % 32);\n\t\tfor (let offset = 0; offset < whole; offset += 32) {\n\t\t\tfor (let i = 0; i < 16; i++) {\n\t\t\t\tconst c0 = s.charCodeAt(offset + 2 * i);\n\t\t\t\tconst c1 = s.charCodeAt(offset + 2 * i + 1);\n\t\t\t\tw[i] = ((c0 & 0xff) << 24) | ((c0 >>> 8) << 16) | ((c1 & 0xff) << 8) | (c1 >>> 8);\n\t\t\t}\n\t\t\tthis.compress();\n\t\t}\n\t\tthis.totalBytes += whole * 2;\n\n\t\tconst rest = new Uint8Array((s.length - whole) * 2);\n\t\tfor (let i = whole, j = 0; i < s.length; i++) {\n\t\t\tconst code = s.charCodeAt(i);\n\t\t\trest[j++] = code & 0xff;\n\t\t\trest[j++] = code >>> 8;\n\t\t}\n\t\tthis.update(rest);\n\t}\n\n\tdigestHex(): string {\n\t\tconst bitLength = this.totalBytes * 8;\n\t\tconst tail = new Uint8Array(this.pendingLength < 56 ? 64 : 128);\n\t\ttail.set(this.pending.subarray(0, this.pendingLength));\n\t\ttail[this.pendingLength] = 0x80;\n\t\tconst view = new DataView(tail.buffer);\n\t\tview.setUint32(tail.length - 8, Math.floor(bitLength / 0x100000000));\n\t\tview.setUint32(tail.length - 4, bitLength >>> 0);\n\t\tfor (let offset = 0; offset < tail.length; offset += 64) this.compressBytes(tail, offset);\n\n\t\tlet hex = '';\n\t\tfor (const word of this.h) hex += (word >>> 0).toString(16).padStart(8, '0');\n\t\treturn hex;\n\t}\n\n\tprivate compressBytes(bytes: Uint8Array, offset: number): void {\n\t\tconst w = this.w;\n\t\tfor (let i = 0; i < 16; i++) {\n\t\t\tconst j = offset + i * 4;\n\t\t\tw[i] = (bytes[j] << 24) | (bytes[j + 1] << 16) | (bytes[j + 2] << 8) | bytes[j + 3];\n\t\t}\n\t\tthis.compress();\n\t}\n\n\t/** One block, from the 16 message words already in `w`. */\n\tprivate compress(): void {\n\t\tconst w = this.w;\n\t\tfor (let i = 16; i < 64; i++) {\n\t\t\tconst x = w[i - 15];\n\t\t\tconst y = w[i - 2];\n\t\t\tconst s0 = ((x >>> 7) | (x << 25)) ^ ((x >>> 18) | (x << 14)) ^ (x >>> 3);\n\t\t\tconst s1 = ((y >>> 17) | (y << 15)) ^ ((y >>> 19) | (y << 13)) ^ (y >>> 10);\n\t\t\tw[i] = (w[i - 16] + s0 + w[i - 7] + s1) | 0;\n\t\t}\n\n\t\tconst h = this.h;\n\t\tlet a = h[0];\n\t\tlet b = h[1];\n\t\tlet c = h[2];\n\t\tlet d = h[3];\n\t\tlet e = h[4];\n\t\tlet f = h[5];\n\t\tlet g = h[6];\n\t\tlet k = h[7];\n\t\tfor (let i = 0; i < 64; i++) {\n\t\t\tconst S1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7));\n\t\t\tconst ch = (e & f) ^ (~e & g);\n\t\t\tconst t1 = (k + S1 + ch + K[i] + w[i]) | 0;\n\t\t\tconst S0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10));\n\t\t\tconst maj = (a & b) ^ (a & c) ^ (b & c);\n\t\t\tconst t2 = (S0 + maj) | 0;\n\t\t\tk = g;\n\t\t\tg = f;\n\t\t\tf = e;\n\t\t\te = (d + t1) | 0;\n\t\t\td = c;\n\t\t\tc = b;\n\t\t\tb = a;\n\t\t\ta = (t1 + t2) | 0;\n\t\t}\n\t\th[0] = (h[0] + a) | 0;\n\t\th[1] = (h[1] + b) | 0;\n\t\th[2] = (h[2] + c) | 0;\n\t\th[3] = (h[3] + d) | 0;\n\t\th[4] = (h[4] + e) | 0;\n\t\th[5] = (h[5] + f) | 0;\n\t\th[6] = (h[6] + g) | 0;\n\t\th[7] = (h[7] + k) | 0;\n\t}\n}\n","/**\n * Solve-key hashing, public so an app-layer durable cache or a pre-solved bundle viewer can\n * reproduce the scheduler's exact key. A key that canonicalizes even slightly differently\n * misses every entry the scheduler wrote, or worse, collides differently.\n *\n * Keys are SHA-256: one scheduler's cache serves every user of a server, so a key a user can\n * collide on purpose lets them choose what another user's solve returns.\n */\n\nimport { isDefinitionRef, type SolveDefinition } from '@/core/definition-ref';\nimport { sha256Hex } from '@/core/utils/sha256';\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 *\n * The output is a cache key, so the invariant that matters is: two payloads\n * that serialize differently on the wire must stringify differently here\n * (false misses are harmless; false hits serve the wrong cached solve).\n * Non-JSON values get unquoted markers (`{u8:…}`, `[Circular]`) that no JSON\n * value stringifies to, so a plain object can't pose as one.\n */\nexport function stableStringify(value: unknown): string {\n\t// Tracks the current recursion path only (entries are removed on the way\n\t// out), so genuine cycles read \"[Circular]\" while shared non-circular\n\t// references stringify by content each time they appear.\n\tconst path = new WeakSet<object>();\n\n\tconst stringify = (v: unknown): string => {\n\t\tif (v === null) return 'null';\n\t\tif (typeof v === 'number') {\n\t\t\treturn Number.isFinite(v) ? String(v) : 'null';\n\t\t}\n\t\tif (typeof v === 'string' || typeof v === 'boolean') return JSON.stringify(v);\n\t\t// Unquoted `n` suffix keeps 1n distinct from the string \"1\".\n\t\tif (typeof v === 'bigint') return `${v}n`;\n\t\t// JSON drops a function or symbol exactly like `undefined`, so they share its marker.\n\t\tif (typeof v !== 'object') return 'undefined';\n\t\tif (v instanceof Uint8Array) return `{u8:${sha256Hex(v)}}`;\n\t\tif (path.has(v)) return '[Circular]';\n\t\tpath.add(v);\n\t\tlet out: string;\n\t\tif (Array.isArray(v)) {\n\t\t\tconst parts: string[] = [];\n\t\t\t// Indexed access (not .map) so holes stringify like undefined instead\n\t\t\t// of vanishing from the joined output.\n\t\t\tfor (let i = 0; i < v.length; i++) parts.push(stringify(v[i]));\n\t\t\tout = `[${parts.join(',')}]`;\n\t\t} else if (typeof (v as { toJSON?: unknown }).toJSON === 'function') {\n\t\t\t// Matches wire behavior: JSON.stringify calls toJSON (Date → ISO string).\n\t\t\tout = stringify((v as { toJSON: () => unknown }).toJSON());\n\t\t} else if (v instanceof Map) {\n\t\t\tconst entries = [...v.entries()].map(([k, val]) => `[${stringify(k)},${stringify(val)}]`);\n\t\t\tout = `{map:[${entries.sort().join(',')}]}`;\n\t\t} else if (v instanceof Set) {\n\t\t\tconst items = [...v.values()].map(stringify);\n\t\t\tout = `{set:[${items.sort().join(',')}]}`;\n\t\t} else {\n\t\t\tconst keys = Object.keys(v).sort();\n\t\t\tconst parts = keys.map(\n\t\t\t\t(k) => `${JSON.stringify(k)}:${stringify((v as Record<string, unknown>)[k])}`\n\t\t\t);\n\t\t\tout = `{${parts.join(',')}}`;\n\t\t}\n\t\tpath.delete(v);\n\t\treturn out;\n\t};\n\n\treturn stringify(value);\n}\n\n/** The scheduler's solve-cache key for a definition and its input tree. */\nexport function hashSolveInput(definition: SolveDefinition, dataTree: unknown): string {\n\treturn hashSolveInputForDefinition(hashDefinition(definition), dataTree);\n}\n\n/**\n * `hashSolveInput` from a precomputed {@link hashDefinition}, so the scheduler, which also keys its\n * server-cache-key map by that hash, passes over a multi-MB definition once per solve.\n *\n * @internal\n */\nexport function hashSolveInputForDefinition(definitionHash: string, dataTree: unknown): string {\n\treturn `${definitionHash}|t:${sha256Hex(stableStringify(dataTree))}`;\n}\n\n/**\n * A definition's identity, without its inputs. Strings and bytes are hashed over their full\n * content; a `DefinitionRef` uses its `key`.\n */\nexport function hashDefinition(definition: SolveDefinition): string {\n\tif (isDefinitionRef(definition)) return `r:${definition.key}`;\n\treturn typeof definition === 'string'\n\t\t? `s:${sha256Hex(definition)}`\n\t\t: `u8:${sha256Hex(definition)}`;\n}\n","import { ComputeError, ErrorCodes } from '@/core/errors';\nimport type { RetryPolicy } from '@/core/types';\nimport { getLogger } from '@/core/utils/logger';\nimport { readField } from '@/core/utils/read-field';\nimport { getResponseWireSize } from '@/core/compute-fetch/wire-size';\n\nimport type {\n\tDataTree,\n\tGrasshopperComputeResponse,\n\tGrasshopperComputeConfig,\n\tSelvaEventTarget\n} from '../types';\nimport { isDefinitionUrl, type SolveDefinition } from '@/core/definition-ref';\nimport { hashSolveInputForDefinition, hashDefinition } from './stable-hash';\n\nimport type {\n\tSchedulerMode,\n\tSchedulerSolveOptions,\n\tSolveContext,\n\tSolveDetail,\n\tSolveExecutor,\n\tSolveResult,\n\tSolveSchedulerOptions\n} from './types';\n\ninterface CacheEntry {\n\tresponse: GrasshopperComputeResponse;\n\tinsertedAt: number;\n\t/** Wire size (or stringify-fallback estimate) counted against `maxBytes`. */\n\tsizeBytes: number;\n}\n\nconst SERVER_CACHE_KEYS_MAX = 100;\n\ninterface PendingItem {\n\tdefinition: SolveDefinition;\n\tdataTree: DataTree[];\n\thashes: SolveInputHashes;\n\tctx: SolveContext;\n\t/** Monotonic solve() ordinal, so an older item's late settle can't overwrite newer state. */\n\tseq: number;\n\tresolve: (detail: SolveDetail) => void;\n\treject: (error: ComputeError) => void;\n\texternalSignal?: AbortSignal;\n\tselvaevents?: SelvaEventTarget;\n\tonServerTiming?: SchedulerSolveOptions['onServerTiming'];\n\t/** Settles the item as ABORTED if its signal fires while it waits in a queue. */\n\tqueuedAbortHandler?: () => void;\n\tqueueWaitTimer?: ReturnType<typeof setTimeout>;\n\tsettled?: { error: ComputeError } | { ok: true };\n}\n\ninterface InFlightItem extends PendingItem {\n\tcontroller: AbortController;\n}\n\nconst u8DefinitionHashes = new WeakMap<Uint8Array, string>();\n\n/**\n * Remembers a buffer's hash by identity: a buffer mutated after its first solve keeps its old\n * hash and is served the old content's cached solves.\n */\nfunction hashDefinitionOnce(definition: SolveDefinition): string {\n\tif (!(definition instanceof Uint8Array)) return hashDefinition(definition);\n\tlet hash = u8DefinitionHashes.get(definition);\n\tif (hash === undefined) {\n\t\thash = hashDefinition(definition);\n\t\tu8DefinitionHashes.set(definition, hash);\n\t}\n\treturn hash;\n}\n\n/** One solve's input hashes, each taken on first use. */\nclass SolveInputHashes {\n\tprivate definitionMemo: string | undefined;\n\tprivate keyMemo: string | undefined;\n\n\tconstructor(\n\t\tprivate readonly hashDefinition: () => string,\n\t\tprivate readonly dataTree: DataTree[]\n\t) {}\n\n\tget definitionHash(): string {\n\t\treturn (this.definitionMemo ??= this.hashDefinition());\n\t}\n\n\tget key(): string {\n\t\treturn (this.keyMemo ??= hashSolveInputForDefinition(this.definitionHash, this.dataTree));\n\t}\n}\n\n/**\n * The FIFO behind `queue` mode. Removing an item only drops it from `live`; the array is swept once\n * the dead entries outnumber the live ones, so aborting n queued solves costs O(n), not O(n²).\n */\nclass SolveQueue {\n\tprivate items: (PendingItem | undefined)[] = [];\n\tprivate head = 0;\n\tprivate readonly live = new Set<PendingItem>();\n\n\tget size(): number {\n\t\treturn this.live.size;\n\t}\n\n\tpush(item: PendingItem): void {\n\t\tthis.items.push(item);\n\t\tthis.live.add(item);\n\t}\n\n\tremove(item: PendingItem): void {\n\t\tif (this.live.delete(item)) this.compact();\n\t}\n\n\tshift(): PendingItem | undefined {\n\t\twhile (this.head < this.items.length) {\n\t\t\tconst item = this.items[this.head];\n\t\t\t// A taken slot is cleared so it doesn't hold the solve's definition and tree.\n\t\t\tthis.items[this.head++] = undefined;\n\t\t\tif (item && this.live.delete(item)) {\n\t\t\t\tthis.compact();\n\t\t\t\treturn item;\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate compact(): void {\n\t\tif (this.live.size === 0) {\n\t\t\tthis.items = [];\n\t\t\tthis.head = 0;\n\t\t\treturn;\n\t\t}\n\t\tconst dead = this.items.length - this.live.size;\n\t\tif (dead <= 64 || dead <= this.live.size) return;\n\t\tthis.items = this.items.filter((item) => item !== undefined && this.live.has(item));\n\t\tthis.head = 0;\n\t}\n}\n\n/**\n * For a response that never crossed the fetch boundary (custom executor), so carries no\n * wire-size hint. An unserializable response is sized 0 rather than failing the solve.\n */\nfunction estimateResponseSize(response: GrasshopperComputeResponse): number {\n\ttry {\n\t\treturn JSON.stringify(response)?.length ?? 0;\n\t} catch {\n\t\treturn 0;\n\t}\n}\n\n/**\n * Adds to a plain solve call:\n * - Scheduling (latest-wins for sliders, queue for jobs)\n * - Backpressure (bounded queue depth + queue-wait deadline)\n * - Cancellation (per-call signal + cancelAll)\n * - Optional response caching for repeated inputs\n * - Lifecycle hooks (start / settle / superseded) and state via subscribe()\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 maxConcurrent: number;\n\tprivate readonly maxQueueDepth: number | undefined;\n\tprivate readonly queueWaitMs: number | undefined;\n\tprivate readonly timeoutMs: number | undefined;\n\tprivate readonly retry: RetryPolicy | undefined;\n\n\tprivate readonly cacheEnabled: boolean;\n\tprivate readonly cacheMaxBytes: number;\n\tprivate readonly cacheTtl: number;\n\tprivate readonly cacheErroredSolves: boolean;\n\tprivate readonly cache = new Map<string, CacheEntry>();\n\tprivate cacheBytes = 0;\n\tprivate cacheHits = 0;\n\tprivate cacheMisses = 0;\n\tprivate cacheEvictions = 0;\n\n\tprivate readonly reuseServerDefinitionCache: boolean;\n\t/** Definition hash → the server's cache key (`pointer`) for it. */\n\tprivate readonly serverCacheKeys = new Map<string, string>();\n\t/** A string definition re-sent unchanged is compared with this, far cheaper than hashing it. */\n\tprivate lastStringDefinition: { value: string; hash: string } | null = null;\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 fifo = new SolveQueue();\n\n\tprivate _lastResult: GrasshopperComputeResponse | null = null;\n\tprivate _lastError: ComputeError | null = null;\n\tprivate _lastDurationMs: number | null = null;\n\n\tprivate solveSeq = 0;\n\t/** seq of the solve that last wrote `_lastResult`/`_lastError`. */\n\tprivate lastStateSeq = 0;\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\tif (this.mode !== 'latest-wins' && this.mode !== 'queue') {\n\t\t\tthrow new ComputeError(\n\t\t\t\t`Unknown scheduler mode '${String(this.mode)}'. For concurrent solves use { mode: 'queue', maxConcurrent: 4 }.`,\n\t\t\t\tErrorCodes.INVALID_CONFIG,\n\t\t\t\t{ context: { mode: this.mode } }\n\t\t\t);\n\t\t}\n\t\tthis.maxConcurrent = Math.max(1, options.maxConcurrent ?? 1);\n\t\t// A non-positive maxQueueDepth means unbounded, not \"reject everything\".\n\t\tthis.maxQueueDepth =\n\t\t\toptions.maxQueueDepth !== undefined && options.maxQueueDepth > 0\n\t\t\t\t? Math.floor(options.maxQueueDepth)\n\t\t\t\t: undefined;\n\t\tthis.queueWaitMs =\n\t\t\toptions.queueWaitMs !== undefined && options.queueWaitMs > 0\n\t\t\t\t? options.queueWaitMs\n\t\t\t\t: undefined;\n\t\tthis.timeoutMs = options.timeoutMs;\n\t\tthis.retry = options.retry;\n\n\t\tconst cacheOpt = options.cache;\n\t\tconst cacheConfig = typeof cacheOpt === 'object' ? cacheOpt : null;\n\t\tthis.cacheMaxBytes = Math.max(0, cacheConfig?.maxBytes ?? 0);\n\t\t// A zero budget retains nothing, so treat it as off rather than running a\n\t\t// cache that can only ever miss.\n\t\tthis.cacheEnabled = this.cacheMaxBytes > 0;\n\t\tthis.cacheTtl = cacheConfig?.ttlMs ?? 0;\n\t\tthis.cacheErroredSolves = cacheConfig?.cacheErroredSolves ?? true;\n\n\t\tthis.reuseServerDefinitionCache = options.reuseServerDefinitionCache ?? true;\n\n\t\tthis.onStart = options.onStart;\n\t\tthis.onSettle = options.onSettle;\n\t\tthis.onSuperseded = options.onSuperseded;\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.fifo.size > 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.fifo.size + (this.pendingForLatestWins ? 1 : 0);\n\t}\n\n\tget lastResult(): GrasshopperComputeResponse | null {\n\t\treturn this._lastResult;\n\t}\n\n\tget lastError(): ComputeError | 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 * Adjust how many solves may run at once, for when the compute server's worker\n\t * pool changes size after this scheduler was built.\n\t *\n\t * Raising it drains queued work immediately. Lowering it never interrupts work\n\t * already in flight — those finish above the new limit, and the cap applies from\n\t * the next dispatch. Values below 1 are clamped, since 0 would wedge the queue.\n\t */\n\tsetMaxConcurrent(value: number): void {\n\t\tconst next = Math.max(1, Math.floor(value));\n\t\tif (next === this.maxConcurrent) return;\n\t\tconst raised = next > this.maxConcurrent;\n\t\tthis.maxConcurrent = next;\n\t\tif (raised) this.drainNext();\n\t}\n\n\t/** Current concurrency cap — reflects any {@link setMaxConcurrent} adjustment. */\n\tgetMaxConcurrent(): number {\n\t\treturn this.maxConcurrent;\n\t}\n\n\t/** Subscribe to state changes. */\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 * Schedule a solve. Returns a promise that:\n\t * - Resolves with the compute response on success.\n\t * - Rejects with `ComputeError` 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 * its own `signal` (queued or running) or `cancelAll()`.\n\t * - Rejects with `code: ErrorCodes.QUEUE_FULL` when `maxQueueDepth` is set and\n\t * the queue was already full (backpressure; `statusCode: 503`).\n\t * - Rejects with `code: ErrorCodes.QUEUE_TIMEOUT` when `queueWaitMs` is set and\n\t * the call sat queued longer than that before starting (`statusCode: 503`).\n\t *\n\t * Don't mutate a `Uint8Array` definition after solving it. Responses served\n\t * from the cache (and via `lastResult`) are shared objects, not copies —\n\t * treat them as immutable. Mutating one poisons every later cache hit for\n\t * that key.\n\t *\n\t * Partial-success contract: unlike `GrasshopperClient.solve()`, which throws\n\t * `COMPUTATION_ERROR` when the response carries solver errors, this RESOLVES\n\t * with the response as-is — check `response.errors` yourself if a partial\n\t * success must not be treated as a result (see `cacheErroredSolves` for the\n\t * caching side of the same distinction).\n\t */\n\tsolve(\n\t\tdefinition: SolveDefinition,\n\t\tdataTree: DataTree[],\n\t\toptions?: SchedulerSolveOptions\n\t): Promise<GrasshopperComputeResponse> {\n\t\treturn this.solveDetailed(definition, dataTree, options).then((detail) => detail.response);\n\t}\n\n\t/** {@link solve}, also resolving with whether this call hit the cache and how long it took. */\n\tsolveDetailed(\n\t\tdefinition: SolveDefinition,\n\t\tdataTree: DataTree[],\n\t\toptions?: SchedulerSolveOptions\n\t): Promise<SolveDetail> {\n\t\tif (this.disposed) {\n\t\t\treturn Promise.reject(\n\t\t\t\tnew ComputeError(\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 hashes = new SolveInputHashes(() => this.hashDefinitionMemo(definition), dataTree);\n\t\t// Only the cache and the hooks read the key. Without them, hashing a multi-MB tree would be\n\t\t// the costliest step of the call.\n\t\tlet key = '';\n\t\tif (this.cacheEnabled || this.onStart || this.onSettle || this.onSuperseded) {\n\t\t\ttry {\n\t\t\t\tkey = hashes.key;\n\t\t\t} catch (error) {\n\t\t\t\t// A tree too deep or too large to stringify can't be sent either.\n\t\t\t\treturn Promise.reject(\n\t\t\t\t\tnew ComputeError('Solve input could not be hashed', ErrorCodes.INVALID_INPUT, {\n\t\t\t\t\t\toriginalError: error\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tconst seq = ++this.solveSeq;\n\t\tconst ctx: SolveContext = { key, enqueuedAt: Date.now(), startedAt: null };\n\n\t\t// An already-aborted signal rejects before anything else — including the\n\t\t// cache: the documented contract is ABORTED, not a result.\n\t\tif (options?.signal?.aborted) {\n\t\t\treturn Promise.reject(this.makeAbortError(ctx));\n\t\t}\n\n\t\tconst cached = this.readCache(key);\n\t\tif (cached) {\n\t\t\t// In latest-wins an older in-flight solve is now stale: left running, its\n\t\t\t// completion would overwrite this hit and snap the UI back.\n\t\t\tif (this.mode === 'latest-wins') {\n\t\t\t\tthis.supersedeCurrent();\n\t\t\t}\n\t\t\tconst result: SolveResult = {\n\t\t\t\tstatus: 'success',\n\t\t\t\tresponse: cached,\n\t\t\t\tdurationMs: 0,\n\t\t\t\tfromCache: true\n\t\t\t};\n\t\t\tthis.writeLastState(seq, { result: cached, durationMs: 0 });\n\t\t\tthis.runHook(this.onStart, ctx);\n\t\t\tthis.runHook(this.onSettle, ctx, result);\n\t\t\tthis.notify();\n\t\t\treturn Promise.resolve({ response: cached, fromCache: true, durationMs: 0 });\n\t\t}\n\n\t\treturn new Promise<SolveDetail>((resolve, reject) => {\n\t\t\tconst item: PendingItem = {\n\t\t\t\tdefinition,\n\t\t\t\tdataTree,\n\t\t\t\thashes,\n\t\t\t\tctx,\n\t\t\t\tseq,\n\t\t\t\tresolve,\n\t\t\t\treject,\n\t\t\t\texternalSignal: options?.signal,\n\t\t\t\tselvaevents: options?.selvaevents,\n\t\t\t\tonServerTiming: options?.onServerTiming\n\t\t\t};\n\n\t\t\tif (item.externalSignal) {\n\t\t\t\titem.queuedAbortHandler = () => this.abortQueuedItem(item);\n\t\t\t\titem.externalSignal.addEventListener('abort', item.queuedAbortHandler, { once: true });\n\t\t\t}\n\n\t\t\tthis.enqueue(item);\n\t\t});\n\t}\n\n\t/** A slow solve settling late must not overwrite state a newer solve or cache hit published. */\n\tprivate writeLastState(\n\t\tseq: number,\n\t\tstate:\n\t\t\t| { result: GrasshopperComputeResponse; durationMs: number }\n\t\t\t| { error: ComputeError; durationMs: number }\n\t): void {\n\t\tif (seq < this.lastStateSeq) return;\n\t\tthis.lastStateSeq = seq;\n\t\tif ('result' in state) {\n\t\t\tthis._lastResult = state.result;\n\t\t\tthis._lastError = null;\n\t\t} else {\n\t\t\tthis._lastError = state.error;\n\t\t}\n\t\tthis._lastDurationMs = state.durationMs;\n\t}\n\n\t/** latest-wins: supersede the pending item and abort everything in flight. */\n\tprivate supersedeCurrent(): void {\n\t\tif (this.pendingForLatestWins) {\n\t\t\tthis.supersede(this.pendingForLatestWins);\n\t\t\tthis.pendingForLatestWins = null;\n\t\t}\n\t\tfor (const inflight of this.inFlight) {\n\t\t\tthis.supersede(inflight);\n\t\t\tinflight.controller.abort();\n\t\t}\n\t}\n\n\tprivate dequeue(item: PendingItem): void {\n\t\tif (this.pendingForLatestWins === item) this.pendingForLatestWins = null;\n\t\tthis.fifo.remove(item);\n\t}\n\n\tprivate abortQueuedItem(item: PendingItem): void {\n\t\tthis.dequeue(item);\n\t\tthis.fail(item, this.makeAbortError(item.ctx));\n\t\tthis.notify();\n\t}\n\n\t/** The structured context lets an HTTP layer map QUEUE_FULL to 503 + Retry-After. */\n\tprivate shedAsQueueFull(item: PendingItem): void {\n\t\tconst err = new ComputeError(\n\t\t\t'Solve queue is full; request rejected (backpressure)',\n\t\t\tErrorCodes.QUEUE_FULL,\n\t\t\t{\n\t\t\t\tstatusCode: 503,\n\t\t\t\tcontext: {\n\t\t\t\t\tqueueDepth: this.fifo.size,\n\t\t\t\t\tmaxQueueDepth: this.maxQueueDepth\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\t\tthis.fail(item, err);\n\t\tthis.notify();\n\t}\n\n\tprivate armQueueWaitTimer(item: PendingItem): void {\n\t\tif (this.queueWaitMs === undefined) return;\n\t\tconst waitMs = this.queueWaitMs;\n\t\titem.queueWaitTimer = setTimeout(() => {\n\t\t\tif (item.settled) return;\n\t\t\tthis.dequeue(item);\n\n\t\t\tconst err = new ComputeError(\n\t\t\t\t`Solve waited longer than ${waitMs}ms in queue; rejected (backpressure)`,\n\t\t\t\tErrorCodes.QUEUE_TIMEOUT,\n\t\t\t\t{\n\t\t\t\t\tstatusCode: 503,\n\t\t\t\t\tcontext: {\n\t\t\t\t\t\twaitedMs: Date.now() - item.ctx.enqueuedAt,\n\t\t\t\t\t\tqueueWaitMs: waitMs\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t);\n\t\t\tthis.fail(item, err);\n\t\t\tthis.notify();\n\t\t}, waitMs);\n\t}\n\n\tprivate clearQueueWaitTimer(item: PendingItem): void {\n\t\tif (item.queueWaitTimer !== undefined) {\n\t\t\tclearTimeout(item.queueWaitTimer);\n\t\t\titem.queueWaitTimer = undefined;\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\tthis.supersedeCurrent();\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\t\tif (this.inFlight.size < this.maxConcurrent) {\n\t\t\t\t\tthis.execute(item);\n\t\t\t\t} else if (this.maxQueueDepth !== undefined && this.fifo.size >= this.maxQueueDepth) {\n\t\t\t\t\t// Shed the newest call, so already-accepted work keeps its place.\n\t\t\t\t\tthis.shedAsQueueFull(item);\n\t\t\t\t} else {\n\t\t\t\t\tthis.armQueueWaitTimer(item);\n\t\t\t\t\tthis.fifo.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\t// Mutate, don't copy: every settle path must share the one `settled` slot.\n\t\tconst inflight = item as InFlightItem;\n\t\tinflight.controller = controller;\n\t\tthis.inFlight.add(inflight);\n\t\titem.ctx.startedAt = Date.now();\n\n\t\tthis.clearQueueWaitTimer(item);\n\t\tthis.removeQueuedAbortHandler(item);\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\t...(item.selvaevents !== undefined && { selvaevents: item.selvaevents })\n\t\t\t};\n\t\t\tconst callTiming = item.onServerTiming;\n\t\t\tif (callTiming) {\n\t\t\t\tconst baseTiming = this.baseConfig.onServerTiming;\n\t\t\t\tconfig.onServerTiming = (timing, requestId) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tbaseTiming?.(timing, requestId);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tcallTiming(timing, requestId);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst { response, definitionReuploaded } = await this.runExecutor(item, config);\n\t\t\tconst durationMs = performance.now() - startTime;\n\n\t\t\tthis.writeCache(item.ctx.key, response);\n\n\t\t\tif (\n\t\t\t\t!this.settleSuccess(item, { response, fromCache: false, definitionReuploaded, durationMs })\n\t\t\t) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.writeLastState(item.seq, { result: response, durationMs });\n\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\tdefinitionReuploaded\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\tthis.writeLastState(item.seq, { error: err, durationMs });\n\t\t\tthis.fail(item, err, durationMs);\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 async runExecutor(\n\t\titem: PendingItem,\n\t\tconfig: GrasshopperComputeConfig\n\t): Promise<{ response: GrasshopperComputeResponse; definitionReuploaded?: boolean }> {\n\t\tconst { definition, dataTree } = item;\n\t\tif (!this.reuseServerDefinitionCache || isDefinitionUrl(definition)) {\n\t\t\tconst { response } = await this.executor(definition, dataTree, null, config);\n\t\t\treturn { response };\n\t\t}\n\n\t\tconst definitionHash = item.hashes.definitionHash;\n\t\tconst knownKey = this.serverCacheKeys.get(definitionHash) ?? null;\n\t\tconst result = await this.executor(definition, dataTree, knownKey, config);\n\n\t\t// Delete before set so a hit moves to the back of the eviction order.\n\t\tthis.serverCacheKeys.delete(definitionHash);\n\t\tif (result.cacheKey) {\n\t\t\tthis.serverCacheKeys.set(definitionHash, result.cacheKey);\n\t\t\tif (this.serverCacheKeys.size > SERVER_CACHE_KEYS_MAX) {\n\t\t\t\tthis.serverCacheKeys.delete(this.serverCacheKeys.keys().next().value!);\n\t\t\t}\n\t\t}\n\n\t\treturn { response: result.response, definitionReuploaded: result.missed };\n\t}\n\n\tprivate hashDefinitionMemo(definition: SolveDefinition): string {\n\t\tif (typeof definition !== 'string') return hashDefinitionOnce(definition);\n\t\tif (this.lastStringDefinition?.value !== definition) {\n\t\t\tthis.lastStringDefinition = { value: definition, hash: hashDefinition(definition) };\n\t\t}\n\t\treturn this.lastStringDefinition.hash;\n\t}\n\n\tprivate drainNext(): void {\n\t\tif (this.disposed) return;\n\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\twhile (this.fifo.size > 0 && this.inFlight.size < this.maxConcurrent) {\n\t\t\tthis.execute(this.fifo.shift()!);\n\t\t}\n\t}\n\n\tprivate supersede(item: PendingItem): void {\n\t\tconst err = new ComputeError('Superseded by newer solve', ErrorCodes.SUPERSEDED, {\n\t\t\tcontext: { enqueuedAt: item.ctx.enqueuedAt }\n\t\t});\n\t\tif (this.settleError(item, err)) {\n\t\t\tthis.runHook(this.onSuperseded, item.ctx);\n\t\t}\n\t}\n\n\tprivate makeAbortError(ctx: SolveContext): ComputeError {\n\t\treturn new ComputeError('Request aborted by caller', ErrorCodes.ABORTED, {\n\t\t\tcontext: { enqueuedAt: ctx.enqueuedAt }\n\t\t});\n\t}\n\n\t/**\n\t * The executor, `supersede`, `cancelAll`, abort and the queue limits can all race to settle one\n\t * item, and a promise ignores a second settle silently. The first settle wins; the return value\n\t * says whether this call won, so only the winner fires its hook. Every settle path goes through\n\t * here or {@link settleSuccess}.\n\t */\n\tprivate settleError(item: PendingItem, err: ComputeError): boolean {\n\t\tif (item.settled) return false;\n\t\titem.settled = { error: err };\n\t\tthis.clearQueueWaitTimer(item);\n\t\tthis.removeQueuedAbortHandler(item);\n\t\titem.reject(err);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Fires `onSettle` for queued items too, so a consumer counting solves in progress doesn't\n\t * leak one. A supersede settles through {@link settleError} and fires `onSuperseded` instead.\n\t */\n\tprivate fail(item: PendingItem, err: ComputeError, durationMs = 0): void {\n\t\tif (this.settleError(item, err)) {\n\t\t\tthis.runHook(this.onSettle, item.ctx, { status: 'error', error: err, durationMs });\n\t\t}\n\t}\n\n\tprivate settleSuccess(item: PendingItem, detail: SolveDetail): boolean {\n\t\tif (item.settled) return false;\n\t\titem.settled = { ok: true };\n\t\tthis.clearQueueWaitTimer(item);\n\t\tthis.removeQueuedAbortHandler(item);\n\t\titem.resolve(detail);\n\t\treturn true;\n\t}\n\n\tprivate removeQueuedAbortHandler(item: PendingItem): void {\n\t\tif (item.queuedAbortHandler && item.externalSignal) {\n\t\t\titem.externalSignal.removeEventListener('abort', item.queuedAbortHandler);\n\t\t}\n\t\titem.queuedAbortHandler = undefined;\n\t}\n\n\tprivate normalizeExecutionError(error: unknown, item: InFlightItem): ComputeError {\n\t\t// Already settled (superseded, cancelled): keep the original cause, not the abort it caused.\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 ComputeError) return error;\n\n\t\t// Not `isAbortError`: a raw `TimeoutError` is a timeout, and must not read as a caller abort.\n\t\tif (error instanceof Error && error.name === 'AbortError') {\n\t\t\treturn this.makeAbortError(item.ctx);\n\t\t}\n\n\t\treturn new ComputeError(\n\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\tErrorCodes.UNKNOWN_ERROR,\n\t\t\t{ originalError: 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\tif (this.pendingForLatestWins) {\n\t\t\tthis.fail(this.pendingForLatestWins, this.makeAbortError(this.pendingForLatestWins.ctx));\n\t\t\tthis.pendingForLatestWins = null;\n\t\t}\n\t\tfor (let item = this.fifo.shift(); item; item = this.fifo.shift()) {\n\t\t\tthis.fail(item, this.makeAbortError(item.ctx));\n\t\t}\n\t\tfor (const inflight of this.inFlight) {\n\t\t\t// startedAt is a Date.now() timestamp, so measure with the same clock.\n\t\t\tconst durationMs = inflight.ctx.startedAt ? Date.now() - inflight.ctx.startedAt : 0;\n\t\t\tthis.fail(inflight, this.makeAbortError(inflight.ctx), durationMs);\n\t\t\tinflight.controller.abort();\n\t\t}\n\t\tthis.notify();\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) {\n\t\t\tthis.cacheMisses += 1;\n\t\t\treturn null;\n\t\t}\n\t\tif (this.cacheTtl > 0 && Date.now() - entry.insertedAt > this.cacheTtl) {\n\t\t\tthis.dropCacheEntry(key);\n\t\t\tthis.cacheMisses += 1;\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\tthis.cacheHits += 1;\n\t\treturn entry.response;\n\t}\n\n\tprivate writeCache(key: string, response: GrasshopperComputeResponse): void {\n\t\tif (!this.cacheEnabled) return;\n\t\tconst solveErrors = readField<unknown[]>(response, 'errors');\n\t\tif (!this.cacheErroredSolves && Array.isArray(solveErrors) && solveErrors.length > 0) return;\n\t\t// An aborted solve stopped partway, so the same inputs must solve again. A blocked one is deterministic.\n\t\tconst outcome = readField(readField(response, 'selva'), 'outcome');\n\t\tif (readField(outcome, 'aborted') === true) return;\n\t\tconst sizeBytes = getResponseWireSize(response) ?? estimateResponseSize(response);\n\t\t// Retaining it would evict everything else and then itself.\n\t\tif (sizeBytes > this.cacheMaxBytes) return;\n\t\tthis.dropCacheEntry(key); // replace-in-place: release the old copy's bytes\n\t\tthis.cache.set(key, { response, insertedAt: Date.now(), sizeBytes });\n\t\tthis.cacheBytes += sizeBytes;\n\t\twhile (this.cacheBytes > this.cacheMaxBytes && this.cache.size > 0) {\n\t\t\tconst oldest = this.cache.keys().next().value;\n\t\t\tif (oldest === undefined) break;\n\t\t\tthis.dropCacheEntry(oldest);\n\t\t\tthis.cacheEvictions += 1;\n\t\t}\n\t}\n\n\tprivate dropCacheEntry(key: string): void {\n\t\tconst entry = this.cache.get(key);\n\t\tif (!entry) return;\n\t\tthis.cache.delete(key);\n\t\tthis.cacheBytes -= entry.sizeBytes;\n\t}\n\n\tclearCache(): void {\n\t\tthis.cache.clear();\n\t\tthis.cacheBytes = 0;\n\t}\n\n\t/**\n\t * Observability snapshot of the solve cache: current size plus lifetime\n\t * hit/miss/eviction counters.\n\t *\n\t * `hits`/`misses` count cache CONSULTATIONS, so `hits / (hits + misses)` is the\n\t * hit rate. A TTL-expired entry counts as a miss (the solve runs either way).\n\t * `evictions` counts only entries dropped under size/byte pressure — not\n\t * replace-in-place writes or TTL expiry, which are not capacity signals.\n\t * Counters are cumulative and survive `clearCache()`.\n\t */\n\tcacheStats(): {\n\t\tentries: number;\n\t\tbytes: number;\n\t\thits: number;\n\t\tmisses: number;\n\t\tevictions: number;\n\t} {\n\t\treturn {\n\t\t\tentries: this.cache.size,\n\t\t\tbytes: this.cacheBytes,\n\t\t\thits: this.cacheHits,\n\t\t\tmisses: this.cacheMisses,\n\t\t\tevictions: this.cacheEvictions\n\t\t};\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.clearCache();\n\t}\n\n\tprivate runHook<H extends (...args: never[]) => 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, ComputeError } from '@/core/errors';\nimport { getLogger } from '@/core/utils/logger';\nimport { readField } from '@/core/utils/read-field';\nimport ComputeServerStats from '../server/compute-server-stats';\nimport { validateServerUrl } from '@/core/server/validate-server-url';\nimport { classifyProbeFailure, type ProbeFailure } from '@/core/server/classify-probe-failure';\nimport { RetryPolicy } from '@/core/types';\n\nimport { fetchDefinitionIO, fetchParsedDefinitionIO } from '../io/definition-io';\nimport { solveGrasshopperDefinition, solveWithServerCacheKey } from '../solve';\nimport {\n\tGrasshopperComputeConfig,\n\tGrasshopperComputeResponse,\n\tDataTree,\n\tSelvaEventTarget\n} from '../types';\nimport { isDefinitionRef, type SolveDefinition } from '@/core/definition-ref';\nimport { SolveScheduler } from '../scheduler/solve-scheduler';\nimport type { SolveSchedulerOptions } from '../scheduler/types';\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\t/** Per-solve live-event callback, forwarded onto the request body. */\n\tselvaevents?: SelvaEventTarget;\n}\n\n/** Options for {@link GrasshopperClient.create} that shape construction, not the solves. */\nexport interface CreateClientOptions {\n\t/**\n\t * Retry ladder for the liveness probe: `attempts` retries after the first probe (default 2),\n\t * with exponential backoff from `baseDelayMs` (default 250) capped at `maxDelayMs` (default 1000).\n\t * Independent of `config.retry`, which governs solves only.\n\t */\n\tprobe?: { attempts?: number; baseDelayMs?: number; maxDelayMs?: number };\n}\n\nfunction describeDefinition(definition: SolveDefinition): string {\n\tif (isDefinitionRef(definition)) return `ref:${definition.key}`;\n\tif (typeof definition === 'string' && definition.length < 200) return definition;\n\treturn '...content...';\n}\n\ninterface DataTreeSummaryEntry {\n\tparam: string;\n\titems: number;\n\t/** Summed `data` string lengths. */\n\tbytes: number;\n}\n\n/**\n * Stands in for the input tree on errors: the tree can hold multi-MB geometry, and every logger\n * or telemetry sink that keeps the error would pin it. Never throws on a malformed tree.\n */\nfunction summarizeDataTree(dataTree: DataTree[]): DataTreeSummaryEntry[] {\n\tif (!Array.isArray(dataTree)) return [];\n\n\treturn dataTree.map((tree) => {\n\t\tlet items = 0;\n\t\tlet bytes = 0;\n\n\t\tconst innerTree = readField<Record<string, unknown>>(tree, 'innerTree');\n\t\tif (innerTree && typeof innerTree === 'object') {\n\t\t\tfor (const branch of Object.values(innerTree)) {\n\t\t\t\tif (!Array.isArray(branch)) continue;\n\t\t\t\titems += branch.length;\n\t\t\t\tfor (const item of branch) {\n\t\t\t\t\tconst data = (item as { data?: unknown } | null)?.data;\n\t\t\t\t\tif (typeof data === 'string') bytes += data.length;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn { param: readField<string>(tree, 'paramName') ?? '<unnamed>', items, bytes };\n\t});\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 io = await client.getIO(definitionUrl);\n * const result = await client.solve(definitionUrl, TreeBuilder.fromInputParams(io.inputs));\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\t/**\n\t * Per-probe timeout for the `create()` liveness gate. A healthy `GET /` on the\n\t * proxy answers in milliseconds; this bound only ever applies to a server that\n\t * is not answering, where every extra second is multiplied by the retry count\n\t * and paid by whoever is waiting on the solve.\n\t */\n\tprivate static readonly CREATE_PROBE_TIMEOUT_MS = 2000;\n\n\tprivate constructor(config: GrasshopperComputeConfig) {\n\t\tthis.config = {\n\t\t\t...config,\n\t\t\tserverUrl: validateServerUrl(config.serverUrl),\n\t\t\tdebug: config.debug ?? false\n\t\t};\n\t\tthis.serverStats = new ComputeServerStats(this.config.serverUrl, this.config);\n\t}\n\n\t/**\n\t * Creates and initializes a GrasshopperClient with server validation.\n\t *\n\t * The pre-flight liveness probe (a GET on the proxy root `/`) is a\n\t * single-sample boolean gate that reads a cold or briefly-busy-but-up server\n\t * as offline. To avoid failing construction on that transient class, the probe\n\t * is retried with a short exponential backoff before giving up.\n\t *\n\t * Retries stop early when {@link classifyProbeFailure} says waiting cannot\n\t * change the answer — connection refused, or a 401/403. This runs in front of\n\t * a user who clicked Solve, so the retry ladder must not spend its whole\n\t * budget confirming that a machine is switched off.\n\t *\n\t * @throws {ComputeError} with code NETWORK_ERROR if the server stays\n\t * unreachable across all attempts. `context.probeVerdict` carries the\n\t * {@link ProbeVerdict} so callers can render a specific cause.\n\t * @throws {ComputeError} with code INVALID_CONFIG if configuration is invalid\n\t */\n\tstatic async create(\n\t\tconfig: GrasshopperComputeConfig,\n\t\toptions: CreateClientOptions = {}\n\t): Promise<GrasshopperClient> {\n\t\tconst client = new GrasshopperClient(config);\n\n\t\tconst attempts = Math.max(1, (options.probe?.attempts ?? 2) + 1);\n\t\tconst baseDelayMs = options.probe?.baseDelayMs ?? 250;\n\t\tconst maxDelayMs = options.probe?.maxDelayMs ?? 1000;\n\n\t\tlet lastProbe: Awaited<ReturnType<ComputeServerStats['probeServer']>> | undefined;\n\t\tlet failure: ProbeFailure | null = null;\n\t\tlet used = 0;\n\t\tfor (let attempt = 0; attempt < attempts; attempt++) {\n\t\t\tused = attempt + 1;\n\t\t\tlastProbe = await client.serverStats.probeServer(GrasshopperClient.CREATE_PROBE_TIMEOUT_MS);\n\t\t\tif (lastProbe.online) {\n\t\t\t\treturn client;\n\t\t\t}\n\n\t\t\tfailure = classifyProbeFailure(lastProbe);\n\t\t\t// Nothing about a refused connection or a rejected key improves by\n\t\t\t// asking again — fail now and let the caller say why.\n\t\t\tif (failure && !failure.retryable) break;\n\n\t\t\tif (attempt < attempts - 1) {\n\t\t\t\tconst delay = Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay));\n\t\t\t}\n\t\t}\n\n\t\tawait client.dispose();\n\t\tconst status = lastProbe?.status;\n\t\tconst message = failure\n\t\t\t? `Rhino Compute server is not available. ${failure.summary}`\n\t\t\t: 'Rhino Compute server is not online';\n\t\tthrow new ComputeError(message, ErrorCodes.NETWORK_ERROR, {\n\t\t\t...(status !== undefined && { statusCode: status }),\n\t\t\tcontext: {\n\t\t\t\tserverUrl: client.config.serverUrl,\n\t\t\t\t// What we actually spent, not the ceiling — an early break makes\n\t\t\t\t// these differ, and the gap is the useful part when reading a log.\n\t\t\t\tattempts: used,\n\t\t\t\tmaxAttempts: attempts,\n\t\t\t\t...(failure && { probeVerdict: failure.verdict, probeRetryable: failure.retryable }),\n\t\t\t\t...(status !== undefined && { lastProbeStatus: status }),\n\t\t\t\t...(lastProbe?.error !== undefined && { lastProbeError: lastProbe.error })\n\t\t\t}\n\t\t});\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 {ComputeError} with code INVALID_INPUT if definition is empty\n\t * @throws {ComputeError} with code NETWORK_ERROR if server is offline\n\t * @throws {ComputeError} with code COMPUTATION_ERROR if computation fails.\n\t * On a partial-success response (some outputs computed, some errored) the\n\t * error's `context.values` carries the outputs that did compute — pass\n\t * `{ values } as GrasshopperComputeResponse` to the response processors to\n\t * render them. `context.inputSummary` describes the inputs (param names,\n\t * item counts, byte sizes) without pinning the full data tree.\n\t */\n\tpublic async solve(\n\t\tdefinition: SolveDefinition,\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 ComputeError('Definition URL/content is required', ErrorCodes.INVALID_INPUT, {\n\t\t\t\t\tcontext: { receivedUrl: definition }\n\t\t\t\t});\n\t\t\t} else if (definition instanceof Uint8Array && definition.length === 0) {\n\t\t\t\tthrow new ComputeError('Definition content is empty', ErrorCodes.INVALID_INPUT);\n\t\t\t} else if (isDefinitionRef(definition) && !definition.key.trim()) {\n\t\t\t\tthrow new ComputeError('DefinitionRef key is empty', ErrorCodes.INVALID_INPUT);\n\t\t\t}\n\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\t...(options?.selvaevents !== undefined && { selvaevents: options.selvaevents })\n\t\t\t};\n\n\t\t\tconst result = await solveGrasshopperDefinition(dataTree, definition, effectiveConfig);\n\n\t\t\t// A partial success (HTTP 500 carrying both `values` and `errors`) must not pass as a result.\n\t\t\tconst solveErrors = readField<unknown[]>(result, 'errors');\n\t\t\tif (Array.isArray(solveErrors) && solveErrors.length > 0) {\n\t\t\t\tthrow new ComputeError(\n\t\t\t\t\tsolveErrors.map(String).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: describeDefinition(definition),\n\t\t\t\t\t\t\tinputSummary: summarizeDataTree(dataTree),\n\t\t\t\t\t\t\terrors: solveErrors,\n\t\t\t\t\t\t\twarnings: readField<unknown[]>(result, 'warnings'),\n\t\t\t\t\t\t\tvalues: result.values\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 ComputeError) {\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tthrow new ComputeError(\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: describeDefinition(definition),\n\t\t\t\t\t\tinputSummary: summarizeDataTree(dataTree)\n\t\t\t\t\t},\n\t\t\t\t\toriginalError: 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\treturn new SolveScheduler(\n\t\t\t(definition, dataTree, cacheKey, config) =>\n\t\t\t\tsolveWithServerCacheKey(dataTree, cacheKey, definition, config),\n\t\t\tthis.config,\n\t\t\toptions\n\t\t);\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\tprivate ensureNotDisposed(): void {\n\t\tif (this.disposed) {\n\t\t\tthrow new ComputeError(\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","// Apart from rhino-decoder.ts so the public types never import `rhino3dm`: consumers install it only to decode geometry.\n\n/**\n * Sentinel a Rhino geometry decode returns when a decode was attempted\n * and failed hard (a registered decoder or `CommonObject.decode` threw). Carries\n * the original payload so callers can log or retry. Use\n * {@link isRhinoDecodeError} to detect it.\n */\nexport interface RhinoDecodeError {\n\t__decodeError: true;\n\t/** The Rhino type name the failed decode was attempted for. */\n\ttype: string;\n\t/** The original (undecoded) payload. */\n\traw: unknown;\n}\n\n/** Type guard for the {@link RhinoDecodeError} sentinel. */\nexport function isRhinoDecodeError(value: unknown): value is RhinoDecodeError {\n\treturn (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\t(value as RhinoDecodeError).__decodeError === true\n\t);\n}\n\nexport function makeDecodeError(rhinoType: string, raw: unknown): RhinoDecodeError {\n\treturn { __decodeError: true, type: rhinoType, raw };\n}\n\n/**\n * Free every rhino3dm WASM object reachable from `value`.\n *\n * rhino3dm objects are emscripten bindings — JS GC never reclaims their WASM\n * heap allocation, so everything decoded from a solve response (`getValues`,\n * `getValue`) must be deleted explicitly or the heap\n * grows monotonically across solves (e.g. a UI decoding per slider tick).\n *\n * Walks arrays and plain objects recursively; anything exposing a `delete()`\n * method is treated as a WASM binding and freed (skipped if already deleted).\n * Safe to call more than once and on values containing no WASM objects.\n */\nexport function disposeRhinoObjects(value: unknown): void {\n\t// Aliased references (the same decoded object aggregated under two keys)\n\t// must only be deleted once.\n\tconst seen = new WeakSet<object>();\n\n\tconst walk = (v: unknown): void => {\n\t\tif (!v || typeof v !== 'object') return;\n\t\tif (seen.has(v)) return;\n\t\tseen.add(v);\n\n\t\tconst del = (v as { delete?: unknown }).delete;\n\t\tif (typeof del === 'function') {\n\t\t\tconst isDeleted = (v as { isDeleted?: () => boolean }).isDeleted;\n\t\t\tif (typeof isDeleted !== 'function' || !isDeleted.call(v)) {\n\t\t\t\t(del as () => void).call(v);\n\t\t\t}\n\t\t\treturn; // a WASM binding's internals are not ours to walk\n\t\t}\n\n\t\tif (Array.isArray(v)) {\n\t\t\tfor (const item of v) walk(item);\n\t\t\treturn;\n\t\t}\n\t\t// Only walk plain containers — class instances other than WASM bindings\n\t\t// (Dates, typed arrays, ...) hold nothing decodable.\n\t\tconst proto = Object.getPrototypeOf(v);\n\t\tif (proto === Object.prototype || proto === null) {\n\t\t\tfor (const item of Object.values(v)) walk(item);\n\t\t}\n\t};\n\n\twalk(value);\n}\n","import type { RhinoModule } from 'rhino3dm';\nimport { getLogger } from '@/core';\nimport { makeDecodeError } from './rhino-objects';\n\ninterface XYZ {\n\tX: number;\n\tY: number;\n\tZ: number;\n}\n\ntype RhinoDecoder = (rhino: RhinoModule, data: unknown) => unknown;\n\nconst decoders = new Map<string, RhinoDecoder>([\n\t[\n\t\t'Rhino.Geometry.Point3d',\n\t\t(rhino, data) => {\n\t\t\tconst d = data as Partial<XYZ> | null;\n\t\t\tif (!d || typeof d.X !== 'number') return null;\n\t\t\treturn new rhino.Point([d.X, d.Y as number, d.Z as number]);\n\t\t}\n\t],\n\t[\n\t\t'Rhino.Geometry.Line',\n\t\t(rhino, data) => {\n\t\t\tconst d = data as { From?: XYZ; To?: XYZ } | null;\n\t\t\tif (!d || !d.From || !d.To) return null;\n\t\t\treturn new rhino.Line([d.From.X, d.From.Y, d.From.Z], [d.To.X, d.To.Y, d.To.Z]);\n\t\t}\n\t]\n]);\n\nfunction findDecoder(rhinoType: string): RhinoDecoder | undefined {\n\tif (decoders.has(rhinoType)) return decoders.get(rhinoType);\n\tfor (const [key, dec] of decoders) {\n\t\tif (rhinoType.startsWith(key)) return dec;\n\t}\n\treturn undefined;\n}\n\n/**\n * Whether the parsed item looks like a rhino3dm serialization envelope\n * (`{version, archive3dm, opennurbs, data: \"<base64>\"}`). `CommonObject.decode` expects the WHOLE\n * envelope — unwrapping `.data` first hands it the bare base64 string, which throws or decodes to\n * garbage (see the correct usage in display-items-parser.ts).\n */\nfunction isDecodableEnvelope(parsedData: unknown): parsedData is object {\n\treturn Boolean(\n\t\tparsedData &&\n\t\ttypeof parsedData === 'object' &&\n\t\ttypeof (parsedData as { data?: unknown }).data === 'string'\n\t);\n}\n\n/**\n * Decode one typed payload from a solve response into a rhino3dm object.\n *\n * Every call yields exactly one of three outcomes:\n *\n * 1. **Decoded value** — a registered decoder recognized the shape, or the\n * payload was a rhino3dm serialization envelope and `CommonObject.decode`\n * succeeded.\n * 2. **Raw passthrough** (`=== parsedData`) — no decode path *applied*: no\n * decoder matched the type (or the matched decoder returned `null`, meaning\n * \"not my shape\") and the payload is not a decodable envelope. Not an error;\n * the caller keeps the parsed JSON as-is.\n * 3. **{@link RhinoDecodeError} sentinel** — a decode was *attempted and threw*\n * (a registered decoder threw and no envelope fallback could recover, or\n * `CommonObject.decode` threw). Detect with {@link isRhinoDecodeError}.\n *\n * A decoder returning `null` is a soft miss, never an error: it falls through\n * to the envelope fallback. This matters for prefix collisions — e.g.\n * `Rhino.Geometry.LineCurve` matches the `Rhino.Geometry.Line` decoder but is\n * a CommonObject envelope, not a `{From, To}` pair, and must reach\n * `CommonObject.decode`.\n */\nexport function decodeRhinoGeometry(\n\tparsedData: unknown,\n\trhinoType: string,\n\trhino: RhinoModule\n): unknown {\n\tconst decoder = findDecoder(rhinoType);\n\t// A throwing decoder is a hard failure (unlike a null return, which is a\n\t// soft \"not my shape\" miss) — remember it so that when no envelope fallback\n\t// can recover we return the error sentinel instead of silently passing the\n\t// raw payload through as if no decode had ever been attempted.\n\tlet decoderThrew = false;\n\tif (decoder) {\n\t\ttry {\n\t\t\tconst decoded = decoder(rhino, parsedData);\n\t\t\tif (decoded != null) return decoded;\n\t\t} catch (error) {\n\t\t\tgetLogger().warn(`Failed to decode Rhino type ${rhinoType}:`, error);\n\t\t\tdecoderThrew = true;\n\t\t}\n\t}\n\n\t// Fallback using CommonObject.decode — fed the full envelope, not the unwrapped payload.\n\ttry {\n\t\tif (isDecodableEnvelope(parsedData)) return rhino.CommonObject.decode(parsedData);\n\t} catch (error) {\n\t\tgetLogger().warn(`Failed to decode ${rhinoType} with CommonObject:`, error);\n\t\treturn makeDecodeError(rhinoType, parsedData);\n\t}\n\n\treturn decoderThrew ? makeDecodeError(rhinoType, parsedData) : parsedData;\n}\n","import { isBase64Flag } from '@/core/files/handle-files';\nimport type { RhinoModule } from 'rhino3dm';\nimport { FileData } from '@/core/files/types';\nimport { hasField, readField } from '@/core/utils/read-field';\nimport { setOwnField } from '@/core/utils/set-own-field';\nimport { GrasshopperComputeResponse, DataItem } from '../../types';\nimport { decodeRhinoGeometry } from './rhino-decoder';\nimport { disposeRhinoObjects } from './rhino-objects';\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\t/**\n\t * Free every rhino3dm WASM object decoded into `values`. When a `rhino`\n\t * instance was passed, decoded geometry lives on the WASM heap and is never\n\t * garbage-collected — call this once the values are consumed, or the heap\n\t * grows monotonically across solves. Idempotent; a no-op when nothing was\n\t * decoded. `values` must not be used after disposal.\n\t */\n\tdispose: () => void;\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\nconst FILE_DATA_TYPE = 'FileData';\n\n/** WebDisplay items are skipped entirely: kept, they would write `null` into the result. */\nfunction skipItem(type: string, stringOnly: boolean): boolean {\n\treturn type.includes('WebDisplay') || (stringOnly && type !== SYSTEM_TYPES.STRING);\n}\n\nfunction tryDecodeJSON(value: string): unknown {\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: unknown, type: string, rhino?: RhinoModule): unknown {\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(String(raw), 10);\n\n\t\tcase SYSTEM_TYPES.DOUBLE:\n\t\t\treturn Number.parseFloat(String(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\nfunction extractItemValue(\n\titem: DataItem,\n\ttype: string,\n\tparseValues: boolean,\n\trhino?: RhinoModule\n): unknown {\n\tif (typeof item.data !== 'string') return item.data;\n\n\tconst raw = parseValues ? tryDecodeJSON(item.data) : item.data;\n\tif (parseValues && type.includes(FILE_DATA_TYPE)) {\n\t\treturn asFileData(raw) ?? raw;\n\t}\n\n\treturn decodeBySystemType(raw, type, rhino);\n}\n\n/**\n * A parsed `FileData` blob in the camelCase shape {@link FileData} documents, or\n * `null` when a required field is missing. Fields are read with `readField`,\n * since their casing varies by server branch.\n */\nfunction asFileData(value: unknown): FileData | null {\n\tif (!value || typeof value !== 'object') return null;\n\n\tconst fileName = readField<unknown>(value, 'fileName');\n\tconst fileType = readField<unknown>(value, 'fileType');\n\tconst subFolder = readField<unknown>(value, 'subFolder');\n\tconst flag = readField<unknown>(value, 'isBase64Encoded');\n\n\tif (typeof fileName !== 'string' || typeof fileType !== 'string') return null;\n\tif (typeof subFolder !== 'string') return null;\n\t// Presence, not type: `data` may legitimately be '' and is validated downstream.\n\tif (!hasField(value, 'data')) return null;\n\t// Reject a flag that is neither a boolean nor a boolean-ish string, rather\n\t// than coercing arbitrary values to `false` and mis-decoding the payload.\n\tif (typeof flag !== 'boolean' && typeof flag !== 'string') return null;\n\n\tconst metadata = readField<Record<string, string>>(value, 'metadata');\n\n\treturn {\n\t\tfileName,\n\t\tfileType,\n\t\tdata: readField<string>(value, 'data') as string,\n\t\tisBase64Encoded: isBase64Flag(flag),\n\t\tsubFolder,\n\t\t...(metadata ? { metadata } : {})\n\t};\n}\n\n/** Partial-success and warnings-only responses can arrive with no `values` at all. */\nfunction getResponseParams(response: GrasshopperComputeResponse): unknown[] {\n\tconst values = readField<unknown[]>(response, 'values');\n\treturn Array.isArray(values) ? values : [];\n}\n\n/** A param's item type, tolerating items whose `type` is missing/non-string. */\nfunction itemType(item: DataItem): string {\n\treturn typeof item.type === 'string' ? item.type : '';\n}\n\n/** Tolerates a missing tree (warnings-only partial successes) and skips non-object items. */\nfunction forEachTreeItem(tree: unknown, handler: (item: DataItem) => void) {\n\tif (!tree || typeof tree !== 'object') return;\n\tfor (const list of Object.values(tree)) {\n\t\tif (Array.isArray(list)) {\n\t\t\tfor (const item of list) {\n\t\t\t\tif (item && typeof item === 'object') handler(item as DataItem);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Read all output values from a Grasshopper Compute response, keyed by parameter\n * name (or ID when `byId`). Duplicate keys aggregate into an array.\n *\n * Geometry decoded via `rhino` lives on the WASM heap: free it with `dispose()`.\n *\n * @param options.parseValues - Parse complex data types into JS objects (default true).\n * @param options.rhino - Rhino3dm instance for geometry decoding.\n * @param options.stringOnly - Keep only string-typed items.\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\t// Keys holding an aggregation array (vs. a single value that happens to BE an array,\n\t// e.g. parsed JSON `[1,2,3]`) — `Array.isArray(result[key])` can't tell those apart.\n\tconst aggregated = new Set<string>();\n\n\tfor (const param of getResponseParams(response)) {\n\t\tconst paramName = readField<string>(param, 'paramName');\n\t\tforEachTreeItem(readField(param, 'innerTree'), (item) => {\n\t\t\tconst type = itemType(item);\n\t\t\tif (skipItem(type, stringOnly)) return;\n\n\t\t\tconst key = byId ? item.id : paramName;\n\t\t\tif (!key) return;\n\n\t\t\tconst value = extractItemValue(item, type, parseValues, rhino);\n\n\t\t\t// Own keys only: an output named `constructor` must not find Object.prototype's.\n\t\t\tif (!Object.prototype.hasOwnProperty.call(result, key)) {\n\t\t\t\tsetOwnField(result, key, value);\n\t\t\t} else if (aggregated.has(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\taggregated.add(key);\n\t\t\t}\n\t\t});\n\t}\n\n\treturn { values: result as T, dispose: () => disposeRhinoObjects(result) };\n}\n\n/** Decode every file-data item in a response into {@link FileData} objects. */\nexport function extractFileData(response: GrasshopperComputeResponse): FileData[] {\n\tconst output: FileData[] = [];\n\n\tfor (const param of getResponseParams(response)) {\n\t\tforEachTreeItem(readField(param, 'innerTree'), (item) => {\n\t\t\tif (!itemType(item).includes(FILE_DATA_TYPE)) return;\n\n\t\t\tconst file = asFileData(tryDecodeJSON(item.data));\n\t\t\tif (file) {\n\t\t\t\toutput.push(file);\n\t\t\t}\n\t\t});\n\t}\n\n\treturn output;\n}\n\n/**\n * Read one parameter's value(s) from a response — `byName` matches a `ParamName`,\n * `byId` matches an item ID. Returns `undefined` if absent, a single value for one\n * match, or an array for several.\n *\n * Geometry decoded via `rhino` lives on the WASM heap: free it with\n * {@link disposeRhinoObjects} once consumed.\n *\n * @param parseOptions.parseValues - Parse raw data into formatted values (default true).\n * @param parseOptions.rhino - Rhino3dm instance for geometry decoding.\n * @param parseOptions.stringOnly - Keep only string-typed items.\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\t// The first param that matches, by name or by holding the target id, is the target.\n\tfor (const param of getResponseParams(response)) {\n\t\tconst tree = readField(param, 'innerTree');\n\n\t\tlet matched = false;\n\t\tif ('byName' in options) {\n\t\t\tif (readField<string>(param, 'paramName') !== options.byName) continue;\n\t\t\tmatched = true;\n\t\t}\n\n\t\tconst collected: unknown[] = [];\n\t\tforEachTreeItem(tree, (item) => {\n\t\t\tif ('byId' in options) {\n\t\t\t\tif (item.id !== options.byId) return;\n\t\t\t\t// Found but filtered out below still ends the scan, returning undefined.\n\t\t\t\tmatched = true;\n\t\t\t}\n\t\t\tconst type = itemType(item);\n\t\t\tif (skipItem(type, stringOnly)) return;\n\t\t\tcollected.push(extractItemValue(item, type, parseValues, rhino));\n\t\t});\n\n\t\tif (matched) {\n\t\t\tif (collected.length === 0) return undefined;\n\t\t\tif (collected.length === 1) return collected[0];\n\t\t\treturn collected;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n","import { downloadFileData } from '@/core/files/handle-files';\nimport { FileBaseInfo } from '@/core/files/types';\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/** Reads parsed values and produced files out of one compute response. */\nexport default class GrasshopperResponseProcessor {\n\tconstructor(\n\t\t/** Hand it to a mesh parser such as `@selvajs/visualization/parse`. */\n\t\tpublic readonly response: GrasshopperComputeResponse\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 - Key by parameter ID instead of name. Requires the VektorNode rhino.compute branch.\n\t * @param options - Controls parsing behavior such as Rhino geometry decoding.\n\t * @returns Parsed Grasshopper output values.\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 */\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 * `byId` requires the VektorNode rhino.compute branch.\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\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 * 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 * await 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 * await processor.getAndDownloadFiles('project', extra);\n\t * ```\n\t */\n\tpublic async getAndDownloadFiles(\n\t\tfolderName: string,\n\t\tadditionalFiles?: FileBaseInfo[] | FileBaseInfo | null\n\t): Promise<void> {\n\t\tawait downloadFileData(extractFileData(this.response), folderName, additionalFiles);\n\t}\n}\n","import { readField } from '@/core/utils/read-field';\n\n/** One entry of the schema endpoint's response, after wrapper-key normalization. */\nexport interface SchemaEndpointResult<TSchema = unknown> {\n\t/** Schemas embedded in that file. Absent when the file yielded none. */\n\tschemas?: TSchema[];\n\t/** Per-file diagnosis. Compute reports this and still answers 200. */\n\terror?: string;\n}\n\n/**\n * Read compute's `/grasshopper/schema` body: `[{ FileName, Schemas }]` per\n * uploaded file, or a bare object for a single file.\n *\n * Use this rather than unwrapping the body by hand: the wrapper keys' casing\n * varies by server branch, and a fixed-key read yields \"no schemas\" with a 200.\n * Only the two wrapper keys are read; schema contents pass through untouched.\n *\n * @typeParam TSchema - Your schema type (e.g. `UISchema`). Not inspected.\n */\nexport function readSchemaResults<TSchema = unknown>(\n\traw: unknown\n): SchemaEndpointResult<TSchema>[] {\n\tconst entries = Array.isArray(raw) ? raw : [raw];\n\treturn entries.map((entry) => ({\n\t\tschemas: readField<TSchema[]>(entry, 'schemas'),\n\t\terror: readField<string>(entry, 'error')\n\t}));\n}\n","/**\n * Canonicalize a `/grasshopper/schema` body's key casing.\n *\n * Compute serializes the plugin's `UISchema`, whose camelCase wire names live in\n * Newtonsoft `[JsonProperty]` attributes. `Selva.gha` ILRepack-merges Newtonsoft,\n * so those attributes are `Selva!Newtonsoft.Json.JsonPropertyAttribute`. When\n * compute's OWN Newtonsoft serializes, it doesn't recognize them, reads no\n * attributes, and emits raw CLR names (`Inputs`, `SchemaVersion`). Nothing throws:\n * `schema.inputs` reads `undefined`, and so does the `schemaVersion` gate.\n *\n * A casing rule, not a key allowlist: the wire names are the CLR names run\n * through Newtonsoft's camelCase strategy, so reproducing it covers every key\n * without drifting each time `UISchema` gains a field.\n */\n\nimport { setOwnField } from '@/core/utils/set-own-field';\n\n/** Maps keyed by the definition's author (dropdown labels): copied verbatim. */\nconst USER_AUTHORED_MAPS = new Set(['options', 'defaultOptions', 'values']);\n\n/** Maps with author or hash keys whose entries are schema objects: keys verbatim, entries normalized. */\nconst USER_KEYED_OBJECT_MAPS = new Set(['optionMedia', 'assets']);\n\nconst isUpper = (ch: string): boolean => ch >= 'A' && ch <= 'Z';\n\n/**\n * Newtonsoft's `CamelCaseNamingStrategy`: a leading run of capitals is lowercased\n * whole, except the last one when a lowercase word follows. `UV` → `uv`,\n * `UVMapping` → `uvMapping` (a first-character rule gives `uVMapping`).\n */\nfunction toCamelCase(key: string): string {\n\tif (key.length === 0 || !isUpper(key[0])) return key;\n\n\tlet run = 0;\n\twhile (run < key.length && isUpper(key[run])) run++;\n\t// `UVMapping`: the `M` opens the next word, so it keeps its capital.\n\tif (run > 1 && run < key.length) run--;\n\n\treturn key.slice(0, run).toLowerCase() + key.slice(run);\n}\n\n/**\n * Returns a schema whose structural keys are camelCase, whichever Newtonsoft\n * serialized it. Always a fresh object the caller may mutate; non-objects are\n * returned as-is.\n */\nexport function normalizeUISchemaCasing<T>(raw: T): T {\n\treturn normalizeValue(raw) as T;\n}\n\nfunction normalizeValue(value: unknown): unknown {\n\tif (Array.isArray(value)) return value.map(normalizeValue);\n\tif (value === null || typeof value !== 'object') return value;\n\n\tconst out: Record<string, unknown> = {};\n\tfor (const [key, child] of Object.entries(value as Record<string, unknown>)) {\n\t\tconst canonical = toCamelCase(key);\n\t\tconst normalized = USER_AUTHORED_MAPS.has(canonical)\n\t\t\t? child\n\t\t\t: USER_KEYED_OBJECT_MAPS.has(canonical)\n\t\t\t\t? normalizeEntries(child)\n\t\t\t\t: normalizeValue(child);\n\t\tsetOwnField(out, canonical, normalized);\n\t}\n\treturn out;\n}\n\nfunction normalizeEntries(value: unknown): unknown {\n\tif (value === null || typeof value !== 'object' || Array.isArray(value)) return value;\n\n\tconst out: Record<string, unknown> = {};\n\tfor (const [key, child] of Object.entries(value as Record<string, unknown>)) {\n\t\tsetOwnField(out, key, normalizeValue(child));\n\t}\n\treturn out;\n}\n","import { DataTreeDefault, DataTreePath, InputParam, DataTree, InnerTreeData } from '../types';\nimport { ComputeError, ErrorCodes, getLogger } from '@/core';\nimport { readField } from '@/core/utils/read-field';\nimport { booleanTransformer, numericTransformer } from '../io/input/transformers';\nimport { isDataTreeDefault, TREE_PATH_RE } from './tree-path';\n\nexport type DataTreeValue = string | number | boolean | object | null;\n\n/** One input's value for {@link TreeBuilder.fromValues}. */\nexport interface TreeValueEntry {\n\tparamName: string;\n\t/** Matched case-insensitively. */\n\tparamType: string;\n\t/** A scalar, an array (branch `{0}`), or a branch-path map like `{ '{0;1}': [1, 2] }`. */\n\tvalue: unknown;\n\tminimum?: number | null;\n\tmaximum?: number | null;\n}\n\n/** A request item. Unlike a response `DataItem`, Rhino Compute takes boolean/number primitives here. */\ninterface ComputeDataItem {\n\tdata: string | boolean | number;\n}\n\ntype ComputeInnerTreeData = {\n\t[path in DataTreePath]: ComputeDataItem[];\n};\n\n/**\n * Builds the Grasshopper data trees a solve takes as input.\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/** Append values to the branch at `path` (`[0, 1]` is `{0;1}`). */\n\tpublic append(path: number[], items: DataTreeValue[]): this {\n\t\tconst branch = (this.innerTree[TreeBuilder.formatPathString(path)] ??= []);\n\t\t// A loop, not push(...items): spreading a few hundred thousand items overflows the stack.\n\t\tfor (const item of items) branch.push({ data: TreeBuilder.serializeValue(item) });\n\t\treturn this;\n\t}\n\n\t/** Replace the tree with a branch-path map like `{ '{0;1}': [1, 2] }`. */\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/** Append a value, or each value of an array, to branch `{0}`. */\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/** Every value across all branches. */\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/** The tree in the shape a solve request sends. */\n\tpublic toComputeFormat(): DataTree {\n\t\treturn {\n\t\t\tParamName: this.paramName,\n\t\t\t// `DataTree` types the response item shape; a request item is looser.\n\t\t\tInnerTree: this.innerTree as unknown as InnerTreeData\n\t\t};\n\t}\n\n\tpublic getParamName(): string {\n\t\treturn this.paramName;\n\t}\n\n\t/**\n\t * One tree per input that has a default, keyed by its nickname. Defaults are coerced and\n\t * clamped by the same rules as {@link TreeBuilder.fromValues}.\n\t */\n\tpublic static fromInputParams(inputs: InputParam[]): DataTree[] {\n\t\treturn TreeBuilder.fromValues(\n\t\t\tinputs.map((input) => ({\n\t\t\t\tparamName: input.nickname || 'unnamed',\n\t\t\t\tparamType: input.paramType,\n\t\t\t\tvalue: input.default,\n\t\t\t\t...('minimum' in input && { minimum: input.minimum, maximum: input.maximum })\n\t\t\t}))\n\t\t);\n\t}\n\n\t/**\n\t * One tree per entry, coercing UI values the way Grasshopper reads them. Number and Integer\n\t * items go through the numeric coercion (Integer rounds) and are clamped to\n\t * `minimum`/`maximum`; Boolean items also accept `'true'`/`'false'`; every other type passes\n\t * through. An item that fails coercion is dropped with a warning. An entry left with no items\n\t * is omitted, so Grasshopper uses its own value; `''` is an item, not an empty value.\n\t */\n\tpublic static fromValues(entries: TreeValueEntry[]): DataTree[] {\n\t\tconst trees: DataTree[] = [];\n\n\t\tfor (const entry of entries) {\n\t\t\tconst tree = new TreeBuilder(entry.paramName);\n\t\t\tconst coerce = TreeBuilder.itemCoercer(entry);\n\n\t\t\tfor (const [path, items] of TreeBuilder.branchesOf(entry.value)) {\n\t\t\t\tconst kept: DataTreeValue[] = [];\n\t\t\t\tfor (const item of items) {\n\t\t\t\t\tif (item === null || item === undefined) continue;\n\t\t\t\t\tconst coerced = coerce(item);\n\t\t\t\t\tif (coerced === null) {\n\t\t\t\t\t\tgetLogger().warn(\n\t\t\t\t\t\t\t`${entry.paramName}: dropped an item that is not a valid ${entry.paramType}`\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tkept.push(coerced);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (kept.length > 0) tree.append(path, kept);\n\t\t\t}\n\n\t\t\tif (Object.keys(tree.innerTree).length > 0) trees.push(tree.toComputeFormat());\n\t\t}\n\n\t\treturn trees;\n\t}\n\n\tprivate static branchesOf(value: unknown): [number[], unknown[]][] {\n\t\tif (!TreeBuilder.hasValidValue(value)) return [];\n\t\tif (isDataTreeDefault(value)) {\n\t\t\treturn Object.entries(value).map(([path, items]) => [\n\t\t\t\tTreeBuilder.parsePathString(path),\n\t\t\t\titems as unknown[]\n\t\t\t]);\n\t\t}\n\t\treturn [[[0], Array.isArray(value) ? value : [value]]];\n\t}\n\n\t/** `null` means the item was rejected. */\n\tprivate static itemCoercer(entry: TreeValueEntry): (item: unknown) => DataTreeValue | null {\n\t\tconst { paramName, minimum, maximum } = entry;\n\t\tconst paramType = entry.paramType.toLowerCase();\n\n\t\tif (paramType === 'number' || paramType === 'integer') {\n\t\t\treturn (item) => {\n\t\t\t\tconst n = numericTransformer(item);\n\t\t\t\tif (n === null) return null;\n\t\t\t\tconst value = paramType === 'integer' ? Math.round(n) : n;\n\t\t\t\treturn TreeBuilder.clampValue(value, minimum, maximum, paramName);\n\t\t\t};\n\t\t}\n\t\tif (paramType === 'boolean') return booleanTransformer;\n\t\treturn (item) => item as DataTreeValue;\n\t}\n\n\t/**\n\t * Set or add `paramName`'s tree to `newValue`: a scalar, an array (branch `{0}`), or a\n\t * branch-path map. Copy-on-write: returns a new array and leaves the caller's untouched, so\n\t * assign the result.\n\t */\n\tpublic static replaceTreeValue(\n\t\ttrees: DataTree[],\n\t\tparamName: string,\n\t\tnewValue: DataTreeValue\n\t): DataTree[] {\n\t\tconst compiled = TreeBuilder.buildFromValue(paramName, newValue).toComputeFormat();\n\t\tconst result = trees.slice();\n\t\tconst idx = result.findIndex((t) => TreeBuilder.paramNameOf(t) === paramName);\n\t\tif (idx === -1) result.push(compiled);\n\t\telse result[idx] = compiled;\n\t\treturn result;\n\t}\n\n\tprivate static buildFromValue(paramName: string, value: DataTreeValue): TreeBuilder {\n\t\tconst tree = new TreeBuilder(paramName);\n\t\tif (isDataTreeDefault(value)) {\n\t\t\ttree.fromDataTreeDefault(value);\n\t\t} else {\n\t\t\ttree.appendFlat(value);\n\t\t}\n\t\treturn tree;\n\t}\n\n\t/**\n\t * `paramName`'s whole tree from a solve result's `values`, keyed by branch path (`'{0;1}'`).\n\t * `null` when no tree has that param, an empty `Map` when it has no branches. An item without\n\t * `data` reads as `null` in place, so indices don't shift.\n\t */\n\tpublic static getTree(\n\t\ttrees: DataTree[],\n\t\tparamName: string\n\t): Map<DataTreePath, DataTreeValue[]> | null {\n\t\tconst tree = trees.find((t) => TreeBuilder.paramNameOf(t) === paramName);\n\t\tif (!tree) return null;\n\n\t\tconst branches = new Map<DataTreePath, DataTreeValue[]>();\n\t\tconst inner = readField(tree, 'InnerTree');\n\t\tif (!inner || typeof inner !== 'object') return branches;\n\n\t\tfor (const [path, items] of Object.entries(inner)) {\n\t\t\tif (!Array.isArray(items)) continue;\n\t\t\tbranches.set(\n\t\t\t\tpath as DataTreePath,\n\t\t\t\titems.map((item) =>\n\t\t\t\t\titem?.data !== undefined ? TreeBuilder.deserializeValue(item.data) : null\n\t\t\t\t)\n\t\t\t);\n\t\t}\n\t\treturn branches;\n\t}\n\n\t/**\n\t * `paramName`'s value when its tree has a single branch: one item unwrapped (`5`, not `[5]`),\n\t * several as an array. `null` for a missing param or an empty tree.\n\t *\n\t * @throws {ComputeError} `INVALID_INPUT` when the tree has more than one branch; read those\n\t * with {@link TreeBuilder.getTree}.\n\t */\n\tpublic static getTreeValue(trees: DataTree[], paramName: string): DataTreeValue | null {\n\t\tconst tree = TreeBuilder.getTree(trees, paramName);\n\t\tif (!tree || tree.size === 0) return null;\n\t\tif (tree.size > 1) {\n\t\t\tthrow new ComputeError(\n\t\t\t\t`\"${paramName}\" has ${tree.size} branches; read it with TreeBuilder.getTree`,\n\t\t\t\tErrorCodes.INVALID_INPUT,\n\t\t\t\t{ context: { paramName, branchCount: tree.size } }\n\t\t\t);\n\t\t}\n\n\t\tconst [items] = tree.values();\n\t\tif (items.length === 0) return null;\n\t\treturn items.length === 1 ? items[0] : items;\n\t}\n\n\tprivate static paramNameOf(tree: DataTree): unknown {\n\t\treturn readField(tree, 'ParamName');\n\t}\n\n\t/**\n\t * Parse a branch path like `\"{0;1;2}\"` into `[0, 1, 2]`.\n\t *\n\t * @throws {ComputeError} `INVALID_INPUT` for a malformed path, rather than\n\t * collapsing it to a default branch where two bad keys would merge.\n\t */\n\tpublic static parsePathString(pathStr: string): number[] {\n\t\tconst match = pathStr.match(TREE_PATH_RE);\n\t\tif (!match) {\n\t\t\tthrow new ComputeError(\n\t\t\t\t`Invalid Grasshopper tree path: \"${pathStr}\". ` +\n\t\t\t\t\t`Expected \"{}\", \"{0}\", or \"{0;1;2}\" (negative indices allowed, no empty segments).`,\n\t\t\t\tErrorCodes.INVALID_INPUT,\n\t\t\t\t{ context: { pathStr } }\n\t\t\t);\n\t\t}\n\t\tif (!match[1]) return [];\n\t\treturn match[1].split(';').map(Number);\n\t}\n\n\t/** `[0, 1, 2]` → `\"{0;1;2}\"`. */\n\tpublic static formatPathString(path: number[]): DataTreePath {\n\t\treturn `{${path.join(';')}}` as DataTreePath;\n\t}\n\n\t/** Booleans and numbers stay primitives: Grasshopper reads them typed. */\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\tprivate static deserializeValue(data: unknown): DataTreeValue {\n\t\tif (typeof data !== 'string') return data as DataTreeValue;\n\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// Coerce to number only when the string is the *canonical* form of a\n\t\t// finite number, i.e. it round-trips exactly (String(Number(s)) === s).\n\t\t// API responses encode numbers as strings ('42', '3.14', '1e+21'), and\n\t\t// those must come back numeric — but non-canonical numeric-looking\n\t\t// strings ('007', '1e5', 'Infinity', '', ' 5') were strings on the\n\t\t// wire and must stay strings.\n\t\tconst num = Number(data);\n\t\tif (Number.isFinite(num) && String(num) === data) {\n\t\t\treturn num;\n\t\t}\n\t\tif (data === 'true') return true;\n\t\tif (data === 'false') return false;\n\t\treturn data;\n\t}\n\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\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":"A,2KAyCA,IAAqBmB,EAArB,MAAqBA,CAAmB,CACvC,UACA,QACA,SAAmB,GACnB,+BAA0C,IAAI,IAG9C,OAAwB,mBAAqB,IAG7C,OAAwB,qBAAuB,IAG/C,OAAwB,wBAA0B,IAGlD,OAAwB,eAAiB,GAAK,GAO9C,YAAY,EAAmB,EAA6B,CAC3D,KAAK,UAAYF,EAAkBG,CAAS,EAC5C,KAAK,QAAU,CACd,GAAGF,GAAiB,OAAOG,GAAS,SAAW,CAAE,OAAQA,CAAK,EAAKA,GAAQ,CAAC,CAAE,EAC9E,eAAgB,kBACjB,CACD,CAMA,iBACC,EACA,EAAoB,CAAC,EACrB,EAAoBF,EAAmB,mBACnB,CACpB,IAAMG,EAA2B,CAAE,GAAGC,EAAM,QAAS,KAAK,QAAS,SAAU,QAAS,EAItF,OAHIC,EAAY,GAAK,CAACF,EAAY,SACjC,EAAY,OAAS,YAAY,QAAQE,CAAS,GAE5C,MAAMC,EAAKH,CAAW,CAC9B,CAMA,MAAc,QACb,EACA,EAAoB,CAAC,EACrB,EAAoBH,EAAmB,mBACd,CACzB,KAAK,kBAAkB,EACvB,IAAMO,EAAQ,GAAGH,EAAK,QAAU,MAAM,GAAGI,IACzC,GAAI,CACH,IAAMC,EAAW,MAAM,KAAK,iBAAiB,GAAG,KAAK,YAAYD,IAAQJ,EAAMC,CAAS,EACxF,GAAI,CAACI,EAAS,GAGb,OAFAxB,EAAU,CAAC,CAAC,KAAK,wBAAwBsB,EAAM,UAAWE,EAAS,MAAM,EACzE,MAAMA,EAAS,MAAM,OAAO,CAAC,CAAC,UAAY,CAAC,CAAC,EACrC,KAER,GAAM,CAAE,OAAM,aAAc,MAAMhB,EAAagB,EAAUT,EAAmB,cAAc,EAK1F,OAJIU,GACHzB,EAAU,CAAC,CAAC,KAAK,wBAAwBsB,EAAM,iCAAiC,EACzE,MAEDI,CACR,OAASC,EAAK,CAEb,OADA3B,EAAU,CAAC,CAAC,KAAK,wBAAwBsB,EAAM,WAAYK,CAAG,EACvD,IACR,CACD,CAeA,MAAa,eACZ,EAAoBZ,EAAmB,mBACpB,CACnB,OAAQ,MAAM,KAAK,YAAYK,CAAS,EAAA,CAAG,MAC5C,CAeA,MAAa,YACZ,EAAoBL,EAAmB,mBACyB,CAChE,KAAK,kBAAkB,EAOvB,IAAMM,EAAM,GAAG,KAAK,UAAU,GAE9B,GAAI,CACH,IAAMG,EAAW,MAAM,KAAK,iBAAiBH,EAAK,CAAE,OAAQ,KAAM,EAAGD,CAAS,EAG9E,OADA,MAAMI,EAAS,MAAM,OAAO,CAAC,CAAC,UAAY,CAAC,CAAC,EACrC,CAAE,OAAQA,EAAS,IAAMrB,EAAWqB,CAAQ,EAAG,OAAQA,EAAS,MAAO,CAC/E,OAASG,EAAK,CAEb,OADA3B,EAAU,CAAC,CAAC,MAAM,oCAAqC2B,CAAG,EACnD,CACN,OAAQ,GACR,MAAOA,aAAe,MAAQ,GAAGA,EAAI,KAAK,IAAIA,EAAI,UAAY,OAAOA,CAAG,CACzE,CACD,CACD,CAeA,MAAa,kBAAkB,EAAoC,CAAC,EAA2B,CAC9F,GAAM,CAAE,aAAa,IAASC,EAExBC,EAAOC,EACV,MAAM,KAAK,QAAQ,kBAAmB,CAAC,EAAGf,EAAmB,oBAAoB,EACjF,MAAM,KAAK,QAAQ,kCAAkC,EACxD,GAAIc,IAAS,KAAM,OAAO,KAG1B,IAAMH,EAAOG,EAAK,KAAK,EAKvB,MAJK,QAAQ,KAAKH,CAAI,EAIf,SAASA,EAAM,EAAE,GAHvB1B,EAAU,CAAC,CAAC,KAAK,uEAAuE,EACjF,KAGT,CAOA,MAAa,YAA4C,CACxD,IAAM0B,EAAO,MAAM,KAAK,QAAQ,UAAU,EAC1C,GAAIA,IAAS,KAAM,OAAO,KAG1B,IAAMK,EAAOC,EAAaN,CAAI,EAM9B,OADIK,GAAQ,KAAa,CAAE,MAAOL,EAAM,QAAS,GAAI,QAAS,IAAK,EAC5D,CAAE,MAAOK,EAAK,OAAS,GAAI,QAASA,EAAK,SAAW,GAAI,QAASA,EAAK,SAAW,IAAK,CAC9F,CAqBA,MAAa,oBACZ,EAAuB,KACkB,CACzC,IAAML,EAAO,MAAM,KAAK,QAAQ,YAAYO,EAAK,WAAW,EACtDF,EAAOL,IAAS,KAAO,IAAA,GAAYM,EAAaN,CAAI,EAC1D,OAAOK,GAAQ,OAAOA,GAAS,SAAYA,EAAkC,IAC9E,CAQA,MAAa,gBAAuC,CAKnD,GAJA,KAAK,kBAAkB,EAInB,CAAC,MAFkB,KAAK,eAAe,EAG1C,MAAO,CAAE,SAAU,EAAM,EAK1B,GAAM,CAACG,EAASC,GAAkB,MAAM,QAAQ,IAAI,CACnD,KAAK,WAAW,EAChB,KAAK,kBAAkB,CAAE,WAAY,EAAM,CAAC,CAC7C,CAAC,EAED,MAAO,CACN,SAAU,GACV,GAAID,GAAW,CAAE,SAAQ,EACzB,GAAIC,IAAmB,MAAQ,CAAE,gBAAe,CACjD,CACD,CAuBA,MAAa,YAAqC,CACjD,IAAMT,EAAO,MAAM,KAAK,QAAQ,eAAgB,CAAE,OAAQ,MAAO,CAAC,EAE5DU,GADOV,IAAS,KAAO,IAAA,GAAYM,EAAaN,CAAI,EAAA,EACQ,OAClE,OAAO,OAAOU,GAAW,SAAWA,EAAS,IAC9C,CAgCA,MAAa,kBAKH,CACT,KAAK,kBAAkB,EAGvB,IAAMC,EAAW,MAAM,KAAK,kBAAkB,CAAE,WAAY,EAAM,CAAC,EACnE,GAAIA,IAAa,KAEhB,OADArC,EAAU,CAAC,CAAC,KAAK,mEAAmE,EAC7E,KAGR,GAAIqC,IAAa,EAEhB,MAAO,CAAE,YAAa,EAAG,MAAO,EAAG,SAAU,EAAG,UAAW,EAAK,EAMjE,IAAMC,EAAQD,EAAW,EACrBE,EAAc,EAClB,IAAK,IAAIC,EAAI,EAAGA,EAAIF,EAAO,IAAK,CAC/B,IAAMF,EAAS,MAAM,KAAK,WAAW,EACjCA,IAAW,OAAM,GAAeA,EACrC,CAEA,MAAO,CAAE,cAAa,QAAO,WAAU,UAAWC,IAAa,CAAE,CAClE,CAYA,MAAa,eAAsC,CAClD,IAAMX,EAAO,MAAM,KAAK,QAAQ,aAAa,EAC7C,GAAIA,IAAS,KAAM,OAAO,KAE1B,IAAMe,EAAO,IAAI,KAAKf,EAAK,KAAK,CAAC,CAAC,QAAQ,SAAU,EAAE,CAAC,EACvD,OAAO,MAAMe,EAAK,QAAQ,CAAC,EAAI,KAAOA,CACvC,CAYA,MAAa,aAAsC,CAClD,IAAMf,EAAO,MAAM,KAAK,QAAQ,WAAW,EAC3C,GAAIA,IAAS,KAAM,OAAO,KAC1B,IAAMgB,EAAU,WAAWhB,EAAK,KAAK,CAAC,EACtC,OAAO,MAAMgB,CAAO,EAAI,KAAOA,CAChC,CAcA,MAAa,gBAAwE,CACpF,OAAO,KAAK,SAAS,kBAAkB,CACxC,CAcA,MAAa,YAAY,EAAsD,CAC9E,IAAMnB,EAAOoB,IAAS,IAAA,GAAY,gBAAkB,sBAAsBA,IAC1E,OAAO,KAAK,SAASpB,CAAI,CAC1B,CAcA,MAAa,iBACZ,EACuD,CACvD,IAAMA,EAAOoB,IAAS,IAAA,GAAY,qBAAuB,2BAA2BA,IACpF,OAAO,KAAK,SAASpB,CAAI,CAC1B,CAcA,MAAa,gBACZ,EAC0E,CAC1E,IAAMA,EAAOoB,IAAS,IAAA,GAAY,oBAAsB,0BAA0BA,IAClF,OAAO,KAAK,SAASpB,CAAI,CAC1B,CAGA,MAAc,SAAY,EAAiC,CAC1D,IAAMG,EAAO,MAAM,KAAK,QACvBH,EACA,CAAE,OAAQ,MAAO,EACjBR,EAAmB,oBACpB,EACA,OAAOW,IAAS,KAAO,KAASM,EAAaN,CAAI,GAAK,IACvD,CAqBA,QAAe,EAAwC,EAAqB,IAAkB,CAG7F,GAFA,KAAK,kBAAkB,EAEnB,CAAC,OAAO,SAASkB,CAAU,GAAKA,EAAa7B,EAAmB,wBAGnE,MAAM,IAAIlB,EACT,mDAAmDkB,EAAmB,wBAAwB,UACtF6B,EAAW,QAAQ7B,EAAmB,wBAAwB,uCACtEjB,EAAW,eACX,CAAE,QAAS,CAAE,YAAW,CAAE,CAC3B,EAGD,IAAI+C,EAAS,GACTC,EAAyD,KAE7D9C,EAAU,CAAC,CAAC,KAAK,6CAA6C4C,EAAW,GAAG,EAE5E,IAAMG,EAAQ,SAAY,CAEzB,GADA,EAAmB,KACdF,GAAU,MAAK,SAEpB,IAAI,CACH,IAAMG,EAAS,MAAM,KAAK,eAAe,EAEzC,GAAI,CAACH,GAAU,KAAK,SAAU,OAE9B,GAAI,CACHI,EAASD,CAAM,CAChB,OAASrB,EAAK,CACb3B,EAAU,CAAC,CAAC,MAAM,+CAAgD2B,CAAG,CACtE,CACD,OAASA,EAAK,CACT,CAACkB,GAAU,KAAK,SAGnB7C,EAAU,CAAC,CAAC,MAAM,+DAAgE2B,CAAG,EAErF3B,EAAU,CAAC,CAAC,MAAM,6DAA8D2B,CAAG,CAErF,CAEIkB,GAAU,CAAC,KAAK,WACnB,EAAmB,eAAiB,KAAKE,EAAM,EAAGH,CAAU,EAH7D,CAKD,EAEMM,MAAuB,CAC5B,EAAS,GACLJ,IAAqB,OACxB,aAAaA,CAAgB,EAC7B,EAAmB,MAGpB,KAAK,eAAe,OAAOI,CAAc,CAC1C,EAMA,OAJA,KAAK,eAAe,IAAIA,CAAc,EAEtCH,EAAW,EAEJG,CACR,CAMA,MAAa,SAAyB,CACrC,GAAI,MAAK,SAET,MAAK,SAAW,GAChB,IAAK,IAAMC,KAAe,KAAK,eAC9BA,EAAY,EAEb,KAAK,eAAe,MAAM,CAJV,CAKjB,CAEA,mBAAkC,CACjC,GAAI,KAAK,SACR,MAAM,IAAItD,EACT,0DACAC,EAAW,aACZ,CAEF,CACD,EAGA,SAASkC,EAAa,EAAuB,CAC5C,GAAI,CACH,OAAO,KAAK,MAAMN,CAAI,CACvB,MAAQ,CACP,MACD,CACD,CCxlBA,MAAM0B,kBAAkB,IAAI,IAO5B,SAASC,IAAyB,CAIjC,GAHI,OAAO,OAAW,KAER,WAA+D,SACnE,UAAU,MAAQ,KAAM,MAAO,GAEzC,IAAMC,EAAO,WAAuD,UAC9DC,EAAY,OAAOD,GAAK,WAAc,SAAWA,EAAI,UAAY,GAGvE,MAFA,CAAI,SAAS,KAAKC,CAAS,CAG5B,CAEA,SAAgBC,EAAiB,EAAsB,EAA0B,CAC5EC,GAIAJ,GAAc,GAAK,CAACD,EAAgB,IAAIM,CAAY,IACvDN,EAAgB,IAAIM,CAAY,EAChC1D,EAAU,CAAC,CAAC,KACX,YAAY0D,EAAa,+GAC1B,EAEF,CCZA,MAGMC,GAAqD,CAC1D,sBAAuB7D,EAAW,qBACnC,EAGA,SAAgB8D,GAAmD,EAAc,CAChF,MAAO,CACN,GAAGC,EACH,iBAAkB,CAAE,GAAGF,GAAgC,GAAGE,EAAO,gBAAiB,CACnF,CACD,CAEA,SAASC,GAAqB,EAAyB,CAEtD,OADMC,aAAiBlE,EAEtBkE,EAAM,OAASjE,EAAW,uBAC1BiE,EAAM,QAAQ,SAAS,uCAAsB,EAHD,EAK9C,CAQA,SAAgBC,GAAsB,EAAsC,EAAuB,CAClG,GAAI,CAACC,EAAO,OAEZ,IAAMC,EAAStE,EAAqB4B,EAAU,QAAQ,EACtD,GAAI,CAAC,MAAM,QAAQ0C,CAAM,GAAKA,EAAO,SAAW,EAAG,OAEnD,IAAMC,EAAkB,CAAC,EACzB,IAAK,IAAMC,KAASF,EAAQ,CAC3B,IAAMG,EAAYzE,EAAmCwE,EAAO,WAAW,EAClEC,GAAa,OAAO,KAAKA,CAAS,CAAC,CAAC,SAAW,GACnDF,EAAM,KAAKvE,EAAkBwE,EAAO,WAAW,GAAK,WAAW,CAEjE,CAEA,GAAID,EAAM,SAAW,EAAG,OAExB,IAAMG,EAAQH,EAAM,SAAWD,EAAO,OAAS,MAAQ,GAAGC,EAAM,OAAO,GAAGD,EAAO,SACjFlE,EAAU,CAAC,CAAC,KACX,mCAAmCsE,EAAM,KAAKH,EAAM,KAAK,IAAI,EAAE,iGAEhE,CACD,CAsBA,eAAsBI,GACrB,EACA,EACA,EACsC,CAEtC,OADAf,EAAiB,6BAA8BK,EAAO,sBAAsB,GACpE,MAAMW,GAAeC,EAAUC,EAAYb,CAAM,EAAA,CAAG,QAC7D,CAOA,eAAsBc,GACrB,EACA,EACA,EACA,EACmD,CAGnD,GAFAnB,EAAiB,0BAA2BK,EAAO,sBAAsB,EAErEe,IAAa,KAChB,GAAI,CAEH,MAAO,CAAE,GAAG,MADMC,GAAS,CAAE,KAAM,KAAM,QAASD,EAAU,OAAQH,CAAS,EAAGZ,CAAM,EACrE,OAAQ,EAAM,CAChC,OAASE,EAAO,CACf,GAAI,CAACD,GAAqBC,CAAK,EAAG,MAAMA,CACzC,CAED,MAAO,CAAE,GAAI,MAAMS,GAAeC,EAAUC,EAAYb,CAAM,EAAI,OAAQ,EAAK,CAChF,CAEA,eAAeW,GACd,EACA,EACA,EAC6B,CAE7B,OAAOK,GAASC,EAAuB,MADnBC,GAAsBL,CAAU,EACND,CAAQ,EAAGZ,CAAM,CAChE,CAEA,eAAekB,GAAsB,EAA2D,CAC/F,GAAI,CAACtE,EAAgBiE,CAAU,EAAG,OAAOA,EACzC,GAAI,CACH,OAAO,MAAMA,EAAW,KAAK,CAC9B,OAASX,EAAO,CACf,MAAM,IAAIlE,EACT,4CAA4C6E,EAAW,IAAI,GAC3D5E,EAAW,cACX,CACC,QAAS,CAAE,cAAe4E,EAAW,GAAI,EACzC,cAAeX,CAChB,CACD,CACD,CACD,CAOA,eAAec,GACd,EACA,EAC6B,CAC7BhE,EAAkBgD,EAAO,SAAS,EAClCmB,GAA6BC,EAAMpB,CAAM,EAGzC,GAAM,CAAE,oBAAqBA,EACvBqB,EAAS,MAAM3E,EACpB,cACA0E,EACArB,GAA0B,CACzB,GAAGC,EACH,iBACCsB,IAAqB,IAAA,IAAaF,EAAK,KACpCE,EAAmBF,EAAK,KAAK,OAC7BE,CACL,CAAC,CACF,EAEM,CACL,UACA,KAAMC,EACN,GAAGC,GACAH,EACE1D,EAAW6D,EAEXC,EAAWlF,EAAoB8E,CAAM,EAC3C,GAAII,IAAa,IAAA,GAAW,CAC3B,IAAMC,EAAa,OAAOH,GAAU,SAAWA,EAAM,OAAS,EAC9DI,EAAoBhE,EAAU,KAAK,IAAI,EAAG8D,EAAWC,CAAU,CAAC,CACjE,CAEA,OADAvB,GAAsBxC,EAAUqC,EAAO,KAAK,EACrC,CACN,WACA,SAAU,OAAO4B,GAAY,SAAWA,EAAU,IACnD,CACD,CAOA,SAAgBX,EACf,EACA,EAC2B,CAC3B,IAAMG,EAAiC,CACtC,KAAM,KACN,QAAS,KACT,OAAQR,CACT,EAYA,OAVIC,aAAsB,WACzB,EAAK,KAAOrE,EAAgBqE,CAAU,EAC5BxE,EAAgBwE,CAAU,EACpC,EAAK,QAAUA,EAIf,EAAK,KAAOzE,EAAoByE,CAAU,GAAK9D,GAAqB8D,CAAU,EAGxEO,CACR,CAKA,SAAgBD,GACf,EACA,EACO,CACHpD,EAAQ,YAAc,OAAM,EAAQ,WAAaA,EAAQ,YACzDA,EAAQ,YAAc,OAAM,EAAQ,WAAaA,EAAQ,YACzDA,EAAQ,gBAAkB,OAAM,EAAQ,eAAiBA,EAAQ,gBACjEA,EAAQ,mBAAqB,OAAM,EAAQ,kBAAoBA,EAAQ,mBACvEA,EAAQ,aAAe,OAAM,EAAQ,YAAcA,EAAQ,aAC3DA,EAAQ,aAAe,OAAM,EAAQ,YAAcA,EAAQ,YAChE,CC1OA,SAAgB8D,EAAY,EAAa,EAAa,EAAsB,CACvEC,IAAQ,YACX,OAAO,eAAeC,EAAKD,EAAK,CAC/B,QACA,SAAU,GACV,WAAY,GACZ,aAAc,EACf,CAAC,EAED,EAAiCA,GAAOE,CAE1C,CCHA,MAAaC,EAAe,4BAS5B,SAAgBC,EAAkB,EAA0C,CAC3E,GAAI,OAAOF,GAAU,WAAYA,GAAkB,MAAM,QAAQA,CAAK,EAAG,MAAO,GAChF,IAAMG,EAAU,OAAO,QAAQH,CAAK,EACpC,OACCG,EAAQ,OAAS,GACjBA,EAAQ,OAAO,CAACL,EAAKM,KAASH,EAAa,KAAKH,CAAG,GAAK,MAAM,QAAQM,CAAG,CAAC,CAE5E,CCdA,SAAgBC,EACf,EACA,EACA,EACU,CACV,GAAIL,GAAiC,KACpC,OAAOA,EAGR,GAAI,MAAM,QAAQA,CAAK,EAAG,CACzB,IAAMM,EAAYN,EAAM,IAAIO,CAAS,CAAC,CAAC,OAAQ,GAAcC,IAAM,IAAI,EACvE,OAAOF,EAAU,OAAS,EAAIA,EAAY,IAAA,EAC3C,CAEA,IAAMG,EAAcF,EAAUP,CAAK,EAInC,OAHIS,IAAgB,KAGbC,EAAoB,IAAA,GAAYV,EAF/BS,CAGT,CAEA,MAAaE,EAAgD,GAAU,CACtE,GAAI,OAAOX,GAAU,SAAU,OAAO,OAAO,SAASA,CAAK,EAAIA,EAAQ,KACvE,GAAI,OAAOA,GAAU,SAAU,CAC9B,IAAMY,EAAUZ,EAAM,KAAK,EAM3B,GAHIY,IAAY,IAGZ,gBAAgB,KAAKA,CAAO,EAAG,OAAO,KAC1C,IAAMC,EAAS,OAAOD,CAAO,EAC7B,OAAO,OAAO,SAASC,CAAM,EAAIA,EAAS,IAC3C,CACA,OAAO,IACR,EAEaC,EAAiD,GAAU,CACvE,GAAI,OAAOd,GAAU,UAAW,OAAOA,EACvC,GAAI,OAAOA,GAAU,SAAU,CAC9B,IAAMe,EAAQf,EAAM,KAAK,CAAC,CAAC,YAAY,EACvC,GAAIe,IAAU,OAAQ,MAAO,GAC7B,GAAIA,IAAU,QAAS,MAAO,EAC/B,CACA,OAAO,IACR,EAEaC,GAA6C,GACrD,OAAOhB,GAAU,SAChBA,EAAM,QAAU,GAAKA,EAAM,WAAW,GAAG,GAAKA,EAAM,SAAS,GAAG,EAC5DA,EAAM,MAAM,EAAG,EAAE,EAErBA,EAAM,WAAW,GAAG,EAAUA,EAAM,MAAM,CAAC,EACxCA,EAED,KAGKiB,GAA8C,GAAU,CACpE,GAAI,OAAOjB,GAAU,SAAU,CAC9B,IAAIkB,EAAUlB,EAAM,KAAK,EAIzB,OAHIkB,EAAQ,WAAW,GAAG,GAAKA,EAAQ,SAAS,GAAG,IAClD,EAAUA,EAAQ,MAAM,EAAG,EAAE,CAAC,CAAC,KAAK,GAE9BA,CACR,CACA,OAAO,IACR,EAEA,SAAgBC,EAAkB,EAA6C,CAC9E,MAAQ,IAAU,CACjB,GAAI,OAAOnB,GAAU,UAAYA,EAAgB,OAAOA,EACxD,GAAI,OAAOA,GAAU,UAAYA,EAAM,KAAK,IAAM,GACjD,GAAI,CACH,IAAMa,EAAS,KAAK,MAAMb,CAAK,EAG/B,OAFI,OAAOa,GAAW,UAAYA,EAAwBA,GAC1D1G,EAAU,CAAC,CAAC,KAAK,0BAA0BiH,EAAU,kBAAkB,EAChE,KACR,OAAStF,EAAK,CAEb,OADA3B,EAAU,CAAC,CAAC,KAAK,iCAAiC6F,EAAM,cAAcoB,IAAatF,CAAG,EAC/E,IACR,CAED,OAAO,IACR,CACD,CC1FA,MAAMuF,mBAAqB,IAAI,IAAI,CAClC,gBACA,gBACA,iBACA,eACA,cACD,CAAC,EAGKC,mBAAqB,IAAI,IAAI,CAAC,eAAgB,cAAc,CAAC,EAOnE,SAASC,EAAY,EAAyB,EAAoC,CAEjF,OADApH,EAAU,CAAC,CAAC,KAAKqH,CAAO,EACjB,CACN,OAAQ,CAAE,GAAGC,EAAO,QAAS,IAAK,EAClC,QAAS,CAAE,KAAMxH,EAAW,kBAAmB,SAAQ,CACxD,CACD,CAMA,SAAgByH,GAAiB,EAAyB,EAAkC,CAO3F,GANI,OAAOD,EAAM,SAAY,UAAYA,EAAM,UAAY,MAMvD,MAAM,QAAQA,EAAM,OAAO,GAAKvB,EAAkBuB,EAAM,OAAO,EAClE,MAAO,CAAE,OAAQA,CAAM,EAGxB,GAAI,CAACvH,EAASuH,EAAM,QAAS,WAAW,EACvC,OAAOF,EACNE,EACA,UAAUhG,EAAM,iFACjB,EAGD,IAAM+C,EAAYzE,EAAmC0H,EAAM,QAAS,WAAW,GAAK,CAAC,EACrF,GAAI,OAAO,KAAKjD,CAAS,CAAC,CAAC,SAAW,EACrC,MAAO,CAAE,OAAQ,CAAE,GAAGiD,EAAO,QAAS,IAAA,EAAU,CAAE,EAGnD,GAAIA,EAAM,YAAeA,EAAM,QAAUA,EAAM,OAAS,EAAI,CAE3D,IAAME,EAAkC,CAAC,EACnCC,EAAyB,CAAC,EAChC,IAAK,GAAM,CAACC,EAAQC,KAAU,OAAO,QAAQtD,CAAS,EAAG,CACxD,GAAI,CAAC,MAAM,QAAQsD,CAAK,EACvB,OAAOP,EACNE,EACA,UAAUhG,EAAM,+CAA+CoG,EAAO,6BACvE,EAED,IAAMhB,EAAoB,CAAC,EAC3B,IAAK,IAAMkB,KAAQD,EAAO,CACzB,IAAME,EAAOjI,EAAUgI,EAAM,MAAM,EAC7BE,EAAOlI,EAAkBgI,EAAM,MAAM,EAC3C,GAAIE,GAAQZ,GAAmB,IAAIY,CAAI,EAAG,CAEzC,GAAI,OAAOD,GAAS,UAAYA,EAAK,KAAK,IAAM,GAAI,SACpD,IAAME,EAAMvB,EAAmBqB,CAAI,EACnC,GAAIE,IAAQ,KAAM,CACjBN,EAAa,KAAK,IAAI,OAAOI,CAAI,EAAE,KAAKC,EAAK,EAAE,EAC/C,QACD,CACApB,EAAO,KAAKS,GAAmB,IAAIW,CAAI,EAAI,KAAK,MAAMC,CAAG,EAAIA,CAAG,CAEjE,MACA,GAAID,IAAS,iBAAkB,CAC9B,IAAME,EAAOrB,EAAmBkB,CAAI,EACpC,GAAIG,IAAS,KAAM,CAClBP,EAAa,KAAK,IAAI,OAAOI,CAAI,EAAE,KAAKC,EAAK,EAAE,EAC/C,QACD,CACApB,EAAO,KAAKsB,CAAI,CAEjB,MAGA,GAAI,OAAOH,GAAS,UAAYC,GAAM,WAAW,gBAAgB,EAChE,GAAI,CACHpB,EAAO,KAAK,KAAK,MAAMmB,CAAI,CAAC,CAC7B,MAAQ,CACPnB,EAAO,KAAKmB,CAAI,CACjB,MAGDnB,EAAO,KAAKmB,CAAI,CACjB,CACAnC,EAAY8B,EAAME,EAAQhB,CAAM,CACjC,CAGA,GAAIe,EAAa,OAAS,EAAG,CAC5B,IAAMJ,EAAU,UAAU/F,EAAM,sBAAsBmG,EAAa,OAAO,4DAA4DA,EAAa,KAAK,IAAI,EAAE,GAE9J,OADAzH,EAAU,CAAC,CAAC,KAAKqH,CAAO,EACjB,CACN,OAAQ,CAAE,GAAGC,EAAO,QAASE,CAAK,EAClC,QAAS,CAAE,KAAM1H,EAAW,kBAAmB,SAAQ,CACxD,CACD,CACA,MAAO,CAAE,OAAQ,CAAE,GAAGwH,EAAO,QAASE,CAAK,CAAE,CAC9C,CAEA,IAAMS,EAAuB,CAAC,EAC9B,IAAK,IAAMN,KAAS,OAAO,OAAOtD,CAAS,EAC1C,GAAK,MAAM,QAAQsD,CAAK,EACxB,IAAK,IAAMC,KAAQD,EACdC,GAAQ,OAAOA,GAAS,UAAY7H,EAAS6H,EAAM,MAAM,GAC5DK,EAAU,KAAKrI,EAAUgI,EAAM,MAAM,CAAC,EAIzC,MAAO,CAAE,OAAQ,CAAE,GAAGN,EAAO,QAASW,EAAU,OAAS,EAAIA,EAAYA,EAAU,EAAG,CAAE,CACzF,CCjIA,SAAgBC,GAAc,EAAe,EAA+B,CAC3E,IAAMC,EAAU,OAAOtC,EAAM,QAAQuC,CAAa,CAAC,EACnD,OAAO,KAAK,IAAIvC,EAAQsC,CAAO,EAAI,KAAOA,EAAUtC,CACrD,CAWA,SAAgBwC,EAAW,EAAmB,CAC7C,GAAI,CAAC,OAAO,SAASC,CAAC,EAAG,MAAO,GAChC,IAAMC,EAAQ,OAAO,KAAK,IAAID,CAAC,CAAC,CAAC,YAAY,EAAkB,CAAC,EAC1D,CAACE,EAAUC,EAAW,KAAO,OAAOF,CAAK,CAAC,CAAC,MAAM,GAAG,EACpDG,GAAYF,EAAS,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,QAAU,GAAK,OAAOC,CAAQ,EACxE,OAAO,KAAK,IAAI,KAAK,IAAIC,EAAU,CAAC,EAAG,EAAY,CACpD,CAEA,SAAgBC,GAAiB,EAAuB,CACvD,GAAI,CAAC,OAAO,SAAS9C,CAAK,GAAKA,IAAU,EAAG,MAAO,IACnD,IAAM6C,EAAWL,EAAWxC,CAAK,EAEjC,OADI6C,IAAa,EAAU,KAAK,IAAI7C,CAAK,GAAK,EAAI,EAAI,GAC/C,QAAQ,IAAM,CAAC6C,EAAA,CAAU,QAAQA,CAAQ,CAAC,CAClD,CAGA,SAAgBE,EAAe,EAA8C,CAC5E,OAAO,OAAOC,EAAO,UAAa,UACjC,OAAO,SAASA,EAAO,QAAQ,GAC/BA,EAAO,SAAW,EAChBA,EAAO,SACP,IAAA,EACJ,CCMA,MAAMC,GAAa,EAA0B,KAC3CD,EAAO,QAAU,GAAK,EAAI,CAAChD,CAAK,EAAIA,EAEhCkD,EAAe,GACpBH,EAAeC,CAAM,IAAMA,EAAO,YAAc,UAAY,EAAI,IAE3DG,GAAc,EAAgB,IACnC,MAAM,QAAQnD,CAAK,EAChBA,EAAM,IAAK,GAAO,OAAOQ,GAAM,SAAW4C,EAAG5C,CAAC,EAAIA,CAAE,EACpD,OAAOR,GAAU,SAChBoD,EAAGpD,CAAK,EACRA,EAEL,SAASqD,GAAe,EAGtB,CAED,GAAInD,EAAkB8C,EAAO,OAAO,EACnC,MAAO,CACN,QAASA,EAAO,QAChB,SAAUE,EAAYF,CAAM,CAC7B,EAKD,GACC,OAAOA,EAAO,SAAY,UAC1BA,EAAO,QAAQ,KAAK,IAAM,IAC1BrC,EAAmBqC,EAAO,OAAO,IAAM,KAEvC,MAAM,IAAIhJ,EACT,4BAA4BgJ,EAAO,QAAQ,eAAeA,EAAO,MAAQ,UAAU,GACnF/I,EAAW,iBACX,CAAE,QAAS,CAAE,UAAW+I,EAAO,KAAM,QAASA,EAAO,OAAQ,CAAE,CAChE,EAGD,IAAMhD,EAAQK,EAAc2C,EAAO,QAASrC,EAAoB,EAAI,EAEpE,GAAIqC,EAAO,YAAc,UACxB,MAAO,CACN,QAASG,EAAWnD,EAAO,KAAK,KAAK,EACrC,SAAUkD,EAAYF,CAAM,CAC7B,EAGD,IAAMM,EAAa,MAAM,QAAQtD,CAAK,EAAIA,EAAM,GAAKA,EAC/CuD,EAAa,CAACD,EAAYN,EAAO,QAASA,EAAO,OAAO,CAAC,CAAC,KAC9D,GAAmB,OAAOxC,GAAM,UAAY,OAAO,SAASA,CAAC,GAAKA,IAAM,CAC1E,EACMgD,EACLT,EAAeC,CAAM,IAAMO,IAAe,IAAA,GAA2C,GAA/BT,GAAiBS,CAAU,GAE9EhB,EAAgBC,EAAWgB,CAAQ,EACvC,GACCjB,IAAkB,GAClB,OAAOe,GAAe,UACtBA,IAAe,GACf,KAAK,IAAIA,CAAU,EAAI,EACtB,CACD,IAAMG,EAAW,KAAK,KAAK,CAAC,KAAK,MAAM,KAAK,IAAIH,CAAU,CAAC,CAAC,EACxD,OAAO,SAASG,CAAQ,GAAKA,EAAW,IAC3C,EAAgBA,EAElB,CAGA,MAFA,GAAgB,KAAK,IAAI,KAAK,IAAIlB,EAAe,CAAC,EAAG,EAAE,EAEhD,CACN,QAASY,EAAWnD,EAAQ,GAC3BqC,GAAcqB,EAAGnB,CAAa,CAC/B,EACA,UACD,CACD,CAEA,MAAMoB,EAAmD,CACxD,MAAM,EAAQ,EAAM,CACnB,GAAM,CAAE,QAASC,EAAK,YAAaP,GAAeL,CAAM,EACxD,MAAO,CACN,GAAGa,EACH,UAAWb,EAAO,UAClB,QAASA,EAAO,QAChB,QAASA,EAAO,QAChB,QAASA,EAAO,QAChB,OAAQA,EAAO,OACf,WACA,QAASY,CACV,CACD,EACA,SAAS,EAAQ,EAAM,CAEtB,IAAME,EACL,OAAOd,EAAO,SAAY,UAAY,OAAO,SAASA,EAAO,OAAO,GAAKA,EAAO,QAAU,EACvFA,EAAO,QACP,EACJ,MAAO,CACN,GAAGa,EACH,UAAWb,EAAO,UAClB,QAASA,EAAO,QAChB,QAASA,EAAO,QAChB,QAASA,EAAO,QAChB,OAAQA,EAAO,OACf,SAAUE,EAAYF,CAAM,EAC5B,QAASC,EAAOD,EAAQc,CAAS,CAClC,CACD,CACD,EAEMC,GAAmD,CACxD,MAAM,EAAQ,EAAM,CACnB,GAAI7D,EAAkB8C,EAAO,OAAO,EACnC,MAAO,CACN,GAAGa,EACH,UAAW,UACX,QAASb,EAAO,OACjB,EAED,IAAMhD,EAAQK,EAAc2C,EAAO,QAASlC,EAAoB,EAAK,EAGrE,GACCd,GACU,MACV,OAAOA,GAAU,WACjB,CAAC,MAAM,QAAQA,CAAK,EAEpB,MAAM,IAAIhG,EACT,4BAA4B,OAAOgG,CAAK,EAAE,eAAegD,EAAO,MAAQ,UAAU,GAClF/I,EAAW,iBACX,CAAE,QAAS,CAAE,UAAW+I,EAAO,KAAM,QAASA,EAAO,OAAQ,CAAE,CAChE,EAED,MAAO,CAAE,GAAGa,EAAM,UAAW,UAAW,QAAS7D,CAAqC,CACvF,EACA,SAAS,EAAQ,EAAM,CACtB,MAAO,CAAE,GAAG6D,EAAM,UAAW,UAAW,QAASZ,EAAOD,EAAQ,EAAK,CAAE,CACxE,CACD,EAEMgB,GAA6C,CAClD,MAAM,EAAQ,EAAM,CACnB,IAAMhE,EAAQK,EAAc2C,EAAO,QAAShC,GAAiB,EAAK,EAClE,MAAO,CAAE,GAAG6C,EAAM,UAAW,OAAQ,QAAS7D,CAAkC,CACjF,CACD,EAEMiE,GAAuD,CAC5D,MAAM,EAAQ,EAAM,EAAM,CACzB,GACC,CAACjB,EAAO,QACR,OAAOA,EAAO,QAAW,UACzB,OAAO,KAAKA,EAAO,MAAM,CAAC,CAAC,SAAW,EACrC,CACD,IAAM5B,EAAY4B,EAAO,UAAY,UACrC,MAAM,IAAIhJ,EACT,UAAUoH,EAAU,8CACpBnH,EAAW,cACX,CAAE,QAAS,CAAE,YAAW,aAAc,WAAY,CAAE,CACrD,CACD,CAEA,IAAIiK,EAAelB,EAAO,QAC1B,GAAIA,EAAO,UAAY,IAAA,IAAaA,EAAO,UAAY,KAAM,CAG5D,GAAI,OAAOA,EAAO,SAAY,SAC7B,MAAM,IAAIhJ,EACT,oBAAoBgJ,EAAO,UAAY,UAAU,sCACjD/I,EAAW,iBACX,CAAE,QAAS,CAAE,UAAW+I,EAAO,KAAM,QAASA,EAAO,OAAQ,CAAE,CAChE,EAED,IAAMmB,EAAe,OAAOnB,EAAO,OAAO,CAAC,CAAC,YAAY,EAGlDoB,EAAQ,OAAO,KAAKpB,EAAO,MAAM,CAAC,CAAC,KAAM,GAAQlD,EAAI,YAAY,IAAMqE,CAAY,EACzF,GAAIC,IAAU,IAAA,GACb,EAAeA,MACT,CAEN,IAAM5C,EAAU,oBAAoBwB,EAAO,UAAY,UAAU,mBAAmBA,EAAO,QAAQ,8BACnG7I,EAAU,CAAC,CAAC,KAAKqH,CAAO,EACxB6C,IAAO,CAAE,KAAMpK,EAAW,iBAAkB,SAAQ,CAAC,CACtD,CACD,CAEA,MAAO,CACN,GAAG4J,EACH,UAAW,YACX,OAAQb,EAAO,OACf,QAASkB,CACV,CACD,EACA,SAAS,EAAQ,EAAM,CAEtB,MAAO,CACN,GAAGL,EACH,UAAW,YACX,OAAQb,EAAO,QAAU,OAAOA,EAAO,QAAW,SAAWA,EAAO,OAAS,CAAC,EAC9E,QAAS,IAAA,EACV,CACD,CACD,EAEA,SAASsB,EAAiB,EAA0B,EAAwC,CAC3F,MAAO,CACN,GAAGT,EACH,UAAW,WACX,QAASZ,EAAOD,EAAQ,IAAI,CAC7B,CACD,CA0CA,MAAauB,kBAA2D,IAAI,IAG1E,CACD,CAAC,SAAUZ,CAAa,EACxB,CAAC,UAAWA,CAAa,EACzB,CAAC,UAAWI,EAAa,EACzB,CAAC,OAAQC,EAAU,EACnB,CAAC,YAAaC,EAAe,EAC7B,CAAC,WAAY,CAhDb,MAAM,EAAQ,EAAM,CACnB,IAAMjE,EAAQK,EACb2C,EAAO,QACP7B,EAAkB6B,EAAO,UAAY,SAAS,EAC9C,EACD,EACA,MAAO,CACN,GAAGa,EACH,UAAW,WACX,QAAS7D,CACV,CACD,EACA,SAAUsE,CAoCG,CAAc,EAC3B,CAAC,OAAQ,CAjCT,MAAM,EAAQ,EAAM,CACnB,IAAMtE,EAAQK,EACb2C,EAAO,QACP7B,EAAkB6B,EAAO,UAAY,SAAS,EAC9C,EACD,EACA,MAAO,CACN,GAAGa,EACH,UAAW,OACX,gBAAiBb,EAAO,gBACxB,QAAShD,CACV,CACD,CAqBS,CAAU,EACnB,CAAC,QAAS,CAlBV,MAAM,EAAQ,EAAM,CACnB,IAAMA,EAAQK,EAAc2C,EAAO,QAAS/B,GAAkB,EAAK,EACnE,MAAO,CAAE,GAAG4C,EAAM,UAAW,QAAS,QAAS7D,CAAmC,CACnF,CAeU,CAAW,CACtB,CAAC,EAEYwE,GAAsBF,ECjT7BG,GAAwB,IAAI,IACjC,CAAC,GAAGF,EAAmB,KAAK,CAAC,CAAC,CAAC,IAAK,GAAQ,CAACzE,EAAI,YAAY,EAAGA,CAAG,CAAC,CACrE,EAGA,SAAS4E,GAAsB,EAA2B,CACzD,OAAOD,GAAsB,IAAIE,GAAW,YAAY,CAAC,GAAKA,CAC/D,CAMA,SAAgBC,GAAa,EAAwC,CACpE,OAAOC,EAAsBC,CAAQ,CAAC,CAAC,KACxC,CAUA,SAAgBD,EAAsB,EAGpC,CACD,IAAMpJ,EAAQqJ,EAAS,MAAQ,UACzBC,EAA4B,CAAC,EAC7BC,GAAU,CAAE,OAAM,aAAiD,CACxED,EAAO,KAAK,CAAE,UAAWtJ,EAAO,UAAWqJ,EAAS,UAAW,UAAS,MAAK,CAAC,CAC/E,EAEMG,EAA2B,CAChC,YAAaH,EAAS,YACtB,KAAMA,EAAS,KACf,SAAUA,EAAS,SACnB,WAAYA,EAAS,WAErB,UAAWA,EAAS,WAAa,IAAA,GACjC,GAAIA,EAAS,EACd,EAGMH,EAAYD,GAAsBI,EAAS,SAAS,EAEtD9B,EAA2B,CAAE,GAAG8B,EAAU,WAAU,EACxD,GAAI,CACH,GAAM,CAAE,OAAQI,EAAY,WAAYxD,GAAiBsB,EAAQvH,CAAK,EACtE,EAASyJ,EACLC,GAASH,EAAOG,CAAO,CAC5B,OAASjH,EAAO,CAGf,EAAS,CAAE,GAAG8E,EAAQ,QAAS,IAAK,EACpCgC,EAAO,CACN,KAAM/K,EAAW,kBACjB,QAAS,UAAUwB,EAAM,qDACxByC,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,GAEvD,CAAC,CACF,CAEA,IAAMkH,EAASb,EAAmB,IAAII,CAAS,EAC/C,GAAI,CACH,GAAI,CAACS,EACJ,MAAM,IAAIpL,EAAa,sBAAsB2K,IAAa1K,EAAW,iBAAkB,CACtF,QAAS,CAAE,kBAAmB0K,EAAW,UAAWG,EAAS,IAAK,CACnE,CAAC,EAEF,MAAO,CAAE,MAAOM,EAAO,MAAMpC,EAAQiC,EAAWD,CAAM,EAAG,QAAO,CACjE,OAAS9G,EAAO,CACf,GAAI,EAAEA,aAAiBlE,GACtB,MAAM,IAAIA,EACTkE,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,EACrDjE,EAAW,iBACX,CACC,QAAS,CAAE,UAAW6K,EAAS,KAAM,WAAU,EAC/C,cAAe5G,CAChB,CACD,EAKD,OAHA/D,EAAU,CAAC,CAAC,MAAM,8BAA8BsB,EAAM,GAAIyC,EAAM,OAAO,EACvE8G,EAAO9G,CAAK,EAEL,CAAE,OADQkH,GAAQ,UAAYZ,GAAA,CACZxB,EAAQiC,CAAS,EAAG,QAAO,CACrD,CACD,CAGA,SAAgBI,GAAwB,EAGtC,CACD,IAAMC,EAAuB,CAAC,EACxBC,EAAiC,CAAC,EACxC,IAAK,IAAMC,KAAOC,EAAW,CAC5B,GAAM,CAAE,QAAO,UAAWZ,EAAsBW,CAAG,EACnDF,EAAO,KAAK7D,CAAK,EACjB8D,EAAY,KAAK,GAAGR,CAAM,CAC3B,CACA,MAAO,CAAE,SAAQ,aAAY,CAC9B,CCpGA,SAAgBW,GAAqB,EAAgC,CACpE,IAAMlC,EAAWzJ,EAAmByL,EAAK,UAAU,EACnD,MAAO,CACN,GAAIzL,EAAkByL,EAAK,IAAI,GAAK,GACpC,KAAMzL,EAAkByL,EAAK,MAAM,GAAK,GACxC,SAAUzL,EAAyByL,EAAK,UAAU,GAAK,KACvD,YAAazL,EAAkByL,EAAK,aAAa,GAAK,GACtD,UAAWzL,EAAkByL,EAAK,WAAW,GAAK,GAClD,WAAYzL,EAAmByL,EAAK,YAAY,GAAK,GACrD,QAASzL,EAAyByL,EAAK,SAAS,GAAK,KACrD,QAASzL,EAAyByL,EAAK,SAAS,GAAK,KACrD,QAASzL,EAAkByL,EAAK,SAAS,GAAK,EAC9C,OAAQzL,EAAkByL,EAAK,QAAQ,GAAK,EAC5C,SAAU,OAAOhC,GAAa,UAAY,OAAO,SAASA,CAAQ,EAAIA,EAAW,IAAA,GACjF,QAASzJ,EAAUyL,EAAK,SAAS,EACjC,OAAQzL,EAAkCyL,EAAK,QAAQ,EACvD,gBAAiBzL,EAAoByL,EAAK,iBAAiB,EAC3D,UAAWzL,EAAyByL,EAAK,WAAW,GAAK,IAC1D,CACD,CAEA,SAAgBG,GAAsB,EAAiC,CACtE,MAAO,CACN,KAAM5L,EAAkByL,EAAK,MAAM,GAAK,GACxC,SAAUzL,EAAyByL,EAAK,UAAU,GAAK,KACvD,UAAWzL,EAAkByL,EAAK,WAAW,GAAK,GAClD,GAAIzL,EAAkByL,EAAK,IAAI,GAAK,EACrC,CACD,CChBA,eAAsBI,EACrB,EACA,EACkC,CAClCjI,EAAiB,oBAAqBK,EAAO,sBAAsB,EACnEhD,EAAkBgD,EAAO,SAAS,EAElC,GAAM,CAAE,OAAM,WAAYiB,EAAuBJ,EAAY,CAAC,CAAC,EAC/D,GAAI,CAACgH,GAAQ,CAACjG,EACb,MAAM,IAAI5F,EACT,iEACAC,EAAW,aACZ,EAGD,IAAM0B,EAAW,MAAMjB,EACtB,KACAmL,EAAO,CAAE,MAAK,EAAI,CAAE,SAAQ,EAC5B9H,GAA0BC,CAAM,CACjC,EAEA,GAAI,CAACrC,GAAY,OAAOA,GAAa,SACpC,MAAM,IAAI3B,EAAa,gCAAiCC,EAAW,cAAe,CACjF,QAAS,CAAE,UAAS,CACrB,CAAC,EAGF,IAAM6L,EAAeC,EAAgBhM,EAAU4B,EAAU,UAAU,CAAC,EAC9DqK,EAAaD,EAAgBhM,EAAU4B,EAAU,QAAQ,CAAC,EAI1D8J,EAAY1L,EAAU4B,EAAU,QAAQ,EACxCsK,EAAalM,EAAU4B,EAAU,SAAS,EAChD,MAAO,CACN,OAAQ,MAAM,QAAQ8J,CAAS,EAAIA,EAAU,IAAIC,EAAoB,EAAI,CAAC,EAC1E,QAAS,MAAM,QAAQO,CAAU,EAAIA,EAAW,IAAIN,EAAqB,EAAI,CAAC,EAC9E,GAAIG,GAAgB,CAAE,cAAa,EACnC,GAAIE,GAAc,CAAE,YAAW,CAChC,CACD,CAOA,SAASD,EAAgB,EAAsC,CAC9D,GAAI,CAAC,MAAM,QAAQ/F,CAAK,EAAG,OAC3B,IAAMkB,EAAUlB,EAAM,IAAIkG,EAAgB,CAAC,CAAC,OAAQ,GAAmB1F,IAAM,IAAA,EAAS,EACtF,OAAOU,EAAQ,OAAS,EAAIA,EAAU,IAAA,EACvC,CAEA,SAASgF,GAAiB,EAAoC,CAC7D,GAAIlG,GAAU,KAA6B,OAC3C,IAAInE,EACJ,GAAI,OAAOmE,GAAU,SACpB,EAAOA,OACD,GAAI,OAAOA,GAAU,SAAU,CACrC,IAAMwB,EAAUzH,EAAmBiG,EAAO,SAAS,EACnD,GAAI,OAAOwB,GAAY,UAAYA,EAAQ,KAAK,CAAC,CAAC,OAAS,EAC1D,EAAOA,OAEP,GAAI,CACH,EAAO,KAAK,UAAUxB,CAAK,GAAK,OAAOA,CAAK,CAC7C,MAAQ,CACP,EAAO,OAAOA,CAAK,CACpB,CAEF,KACC,GAAO,OAAOA,CAAK,EAEpB,OAAOnE,EAAK,KAAK,CAAC,CAAC,OAAS,EAAIA,EAAO,IAAA,EACxC,CAwBA,eAAsBsK,EACrB,EACA,EAC+B,CAC/B,GAAM,CACL,OAAQV,EACR,UACA,eACA,cACG,MAAMG,EAAkB/G,EAAYb,CAAM,EACxC,CAAE,SAAQ,eAAgBqH,GAAwBI,CAAS,EAEjE,MAAO,CACN,SACA,UACA,GAAIF,EAAY,OAAS,GAAK,CAAE,aAAY,EAC5C,GAAIO,GAAgB,CAAE,cAAa,EACnC,GAAIE,GAAc,CAAE,YAAW,CAChC,CACD,CChIA,MAAMI,EACL,WACC,SAAS,mBAAmB,aAAa,EAG3C,SAAgBC,EAAU,EAAoC,CAC7D,GAAID,EAAY,CACf,IAAME,EAAOF,EAAW,WAAW,QAAQ,EAG3C,OAFI,OAAO3E,GAAU,SAAU6E,EAAK,OAAO7E,EAAO,SAAS,EACtD6E,EAAK,OAAO7E,CAAK,EACf6E,EAAK,OAAO,KAAK,CACzB,CACA,OAAOC,GAAkB9E,CAAK,CAC/B,CAMA,MAAM+E,GAAI,IAAI,WAAW,CACxB,WAAY,WAAY,WAAY,WAAY,UAAY,WAAY,WAAY,WACpF,WAAY,UAAY,UAAY,WAAY,WAAY,WAAY,WAAY,WACpF,WAAY,WAAY,UAAY,UAAY,UAAY,WAAY,WAAY,WACpF,WAAY,WAAY,WAAY,WAAY,WAAY,WAAY,UAAY,UACpF,UAAY,UAAY,WAAY,WAAY,WAAY,WAAY,WAAY,WACpF,WAAY,WAAY,WAAY,WAAY,WAAY,WAAY,WAAY,UACpF,UAAY,UAAY,UAAY,UAAY,UAAY,WAAY,WAAY,WACpF,WAAY,WAAY,WAAY,WAAY,WAAY,WAAY,WAAY,UACrF,CAAC,EAGD,SAAgBD,GAAkB,EAAoC,CACrE,IAAME,EAAQ,IAAIC,GAGlB,OAFI,OAAOjF,GAAU,SAAUgF,EAAM,aAAahF,CAAK,EAClDgF,EAAM,OAAOhF,CAAK,EAChBgF,EAAM,UAAU,CACxB,CAEA,IAAMC,GAAN,KAAa,CACZ,EAAqB,IAAI,WAAW,CACnC,WAAY,WAAY,WAAY,WAAY,WAAY,WAAY,UAAY,UACrF,CAAC,EACD,kBAAqB,IAAI,WAAW,EAAE,EACtC,wBAA2B,IAAI,WAAW,EAAE,EAC5C,cAAwB,EACxB,WAAqB,EAErB,OAAO,EAAyB,CAC/B,KAAK,YAAcC,EAAM,OACzB,IAAIC,EAAS,EACb,GAAI,KAAK,cAAgB,EAAG,CAC3B,IAAMC,EAAO,KAAK,IAAI,GAAK,KAAK,cAAeF,EAAM,MAAM,EAI3D,GAHA,KAAK,QAAQ,IAAIA,EAAM,SAAS,EAAGE,CAAI,EAAG,KAAK,aAAa,EAC5D,KAAK,eAAiBA,EACtB,EAASA,EACL,KAAK,cAAgB,GAAI,OAC7B,KAAK,cAAc,KAAK,QAAS,CAAC,EAClC,KAAK,cAAgB,CACtB,CACA,KAAOD,EAAS,IAAMD,EAAM,OAAQ,GAAU,GAAI,KAAK,cAAcA,EAAOC,CAAM,EAClF,KAAK,QAAQ,IAAID,EAAM,SAASC,CAAM,EAAG,CAAC,EAC1C,KAAK,cAAgBD,EAAM,OAASC,CACrC,CAMA,aAAa,EAAiB,CAC7B,IAAME,EAAI,KAAK,EACTC,EAAQC,EAAE,OAAUA,EAAE,OAAS,GACrC,IAAK,IAAIJ,EAAS,EAAGA,EAASG,EAAO,GAAU,GAAI,CAClD,IAAK,IAAIpK,EAAI,EAAGA,EAAI,GAAI,IAAK,CAC5B,IAAMsK,EAAKD,EAAE,WAAWJ,EAAS,EAAIjK,CAAC,EAChCuK,EAAKF,EAAE,WAAWJ,EAAS,EAAIjK,EAAI,CAAC,EAC1C,EAAEA,IAAOsK,EAAK,MAAS,GAAQA,IAAO,GAAM,IAAQC,EAAK,MAAS,EAAMA,IAAO,CAChF,CACA,KAAK,SAAS,CACf,CACA,KAAK,YAAcH,EAAQ,EAE3B,IAAMvH,EAAO,IAAI,YAAYwH,EAAE,OAASD,GAAS,CAAC,EAClD,IAAK,IAAIpK,EAAIoK,EAAOI,EAAI,EAAGxK,EAAIqK,EAAE,OAAQ,IAAK,CAC7C,IAAMI,EAAOJ,EAAE,WAAWrK,CAAC,EAC3B,EAAK,KAAOyK,EAAO,IACnB,EAAK,KAAOA,IAAS,CACtB,CACA,KAAK,OAAO5H,CAAI,CACjB,CAEA,WAAoB,CACnB,IAAM6H,EAAY,KAAK,WAAa,EAC9BC,EAAO,IAAI,WAAW,KAAK,cAAgB,GAAK,GAAK,GAAG,EAC9DA,EAAK,IAAI,KAAK,QAAQ,SAAS,EAAG,KAAK,aAAa,CAAC,EACrD,EAAK,KAAK,eAAiB,IAC3B,IAAMC,EAAO,IAAI,SAASD,EAAK,MAAM,EACrCC,EAAK,UAAUD,EAAK,OAAS,EAAG,KAAK,MAAMD,EAAY,UAAW,CAAC,EACnEE,EAAK,UAAUD,EAAK,OAAS,EAAGD,IAAc,CAAC,EAC/C,IAAK,IAAIT,EAAS,EAAGA,EAASU,EAAK,OAAQ,GAAU,GAAI,KAAK,cAAcA,EAAMV,CAAM,EAExF,IAAIY,EAAM,GACV,IAAK,IAAMC,KAAQ,KAAK,EAAG,IAAQA,IAAS,EAAA,CAAG,SAAS,EAAE,CAAC,CAAC,SAAS,EAAG,GAAG,EAC3E,OAAOD,CACR,CAEA,cAAsB,EAAmB,EAAsB,CAC9D,IAAMV,EAAI,KAAK,EACf,IAAK,IAAInK,EAAI,EAAGA,EAAI,GAAI,IAAK,CAC5B,IAAMwK,EAAIP,EAASjK,EAAI,EACvB,EAAEA,GAAMgK,EAAMQ,IAAM,GAAOR,EAAMQ,EAAI,IAAM,GAAOR,EAAMQ,EAAI,IAAM,EAAKR,EAAMQ,EAAI,EAClF,CACA,KAAK,SAAS,CACf,CAGA,UAAyB,CACxB,IAAML,EAAI,KAAK,EACf,IAAK,IAAInK,EAAI,GAAIA,EAAI,GAAI,IAAK,CAC7B,IAAM8F,EAAIqE,EAAEnK,EAAI,IACV+K,EAAIZ,EAAEnK,EAAI,GACVgL,GAAOlF,IAAM,EAAMA,GAAK,KAASA,IAAM,GAAOA,GAAK,IAAQA,IAAM,EACjEmF,GAAOF,IAAM,GAAOA,GAAK,KAASA,IAAM,GAAOA,GAAK,IAAQA,IAAM,GACxE,EAAE/K,GAAMmK,EAAEnK,EAAI,IAAMgL,EAAKb,EAAEnK,EAAI,GAAKiL,EAAM,CAC3C,CAEA,IAAMC,EAAI,KAAK,EACXC,EAAID,EAAE,GACNE,EAAIF,EAAE,GACNG,EAAIH,EAAE,GACNI,EAAIJ,EAAE,GACNK,EAAIL,EAAE,GACNM,EAAIN,EAAE,GACNO,EAAIP,EAAE,GACNQ,EAAIR,EAAE,GACV,IAAK,IAAIlL,EAAI,EAAGA,EAAI,GAAI,IAAK,CAC5B,IAAM2L,GAAOJ,IAAM,EAAMA,GAAK,KAASA,IAAM,GAAOA,GAAK,KAASA,IAAM,GAAOA,GAAK,GAC9EK,EAAML,EAAIC,EAAM,CAACD,EAAIE,EACrBI,EAAMH,EAAIC,EAAKC,EAAK/B,GAAE7J,GAAKmK,EAAEnK,GAAM,EAGnC8L,IAFOX,IAAM,EAAMA,GAAK,KAASA,IAAM,GAAOA,GAAK,KAASA,IAAM,GAAOA,GAAK,MACvEA,EAAIC,EAAMD,EAAIE,EAAMD,EAAIC,GACb,EACxB,EAAII,EACJ,EAAID,EACJ,EAAID,EACJ,EAAKD,EAAIO,EAAM,EACf,EAAIR,EACJ,EAAID,EACJ,EAAID,EACJ,EAAKU,EAAKC,EAAM,CACjB,CACA,EAAE,GAAMZ,EAAE,GAAKC,EAAK,EACpB,EAAE,GAAMD,EAAE,GAAKE,EAAK,EACpB,EAAE,GAAMF,EAAE,GAAKG,EAAK,EACpB,EAAE,GAAMH,EAAE,GAAKI,EAAK,EACpB,EAAE,GAAMJ,EAAE,GAAKK,EAAK,EACpB,EAAE,GAAML,EAAE,GAAKM,EAAK,EACpB,EAAE,GAAMN,EAAE,GAAKO,EAAK,EACpB,EAAE,GAAMP,EAAE,GAAKQ,EAAK,CACrB,CACD,ECrJA,SAAgBK,EAAgB,EAAwB,CAIvD,IAAMhN,kBAAO,IAAI,QAEXiN,EAAa,GAAuB,CACzC,GAAInI,IAAM,KAAM,MAAO,OACvB,GAAI,OAAOA,GAAM,SAChB,OAAO,OAAO,SAASA,CAAC,EAAI,OAAOA,CAAC,EAAI,OAEzC,GAAI,OAAOA,GAAM,UAAY,OAAOA,GAAM,UAAW,OAAO,KAAK,UAAUA,CAAC,EAE5E,GAAI,OAAOA,GAAM,SAAU,MAAO,GAAGA,EAAE,GAEvC,GAAI,OAAOA,GAAM,SAAU,MAAO,YAClC,GAAIA,aAAa,WAAY,MAAO,OAAO6F,EAAU7F,CAAC,EAAE,GACxD,GAAI9E,EAAK,IAAI8E,CAAC,EAAG,MAAO,aACxB9E,EAAK,IAAI8E,CAAC,EACV,IAAIoI,EACJ,GAAI,MAAM,QAAQpI,CAAC,EAAG,CACrB,IAAMqI,EAAkB,CAAC,EAGzB,IAAK,IAAIlM,EAAI,EAAGA,EAAI6D,EAAE,OAAQ,IAAKqI,EAAM,KAAKF,EAAUnI,EAAE7D,EAAE,CAAC,EAC7D,EAAM,IAAIkM,EAAM,KAAK,GAAG,EAAE,EAC3B,KAAO,CAcND,EAdU,OAAQpI,EAA2B,QAAW,WAElDmI,EAAWnI,EAAgC,OAAO,CAAC,EAC/CA,aAAa,IAEjB,SADU,CAAC,GAAGA,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC6H,EAAGjI,KAAS,IAAIuI,EAAUN,CAAC,EAAE,GAAGM,EAAUvI,CAAG,EAAE,EACjE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,EAAE,IAC9BI,aAAa,IAEjB,SADQ,CAAC,GAAGA,EAAE,OAAO,CAAC,CAAC,CAAC,IAAImI,CACf,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,EAAE,IAMhC,IAJO,OAAO,KAAKnI,CAAC,CAAC,CAAC,KACX,CAAC,CAAC,IACjB,GAAM,GAAG,KAAK,UAAU6H,CAAC,EAAE,GAAGM,EAAWnI,EAA8B6H,EAAE,GAE7D,CAAC,CAAC,KAAK,GAAG,EAAE,GAG3B,OADA3M,EAAK,OAAO8E,CAAC,EACNoI,CACR,EAEA,OAAOD,EAAU3I,CAAK,CACvB,CAGA,SAAgB8I,GAAe,EAA6B,EAA2B,CACtF,OAAOC,EAA4BC,EAAenK,CAAU,EAAGD,CAAQ,CACxE,CAQA,SAAgBmK,EAA4B,EAAwB,EAA2B,CAC9F,MAAO,GAAGE,EAAe,KAAK5C,EAAUqC,EAAgB9J,CAAQ,CAAC,GAClE,CAMA,SAAgBoK,EAAe,EAAqC,CAEnE,OADIpO,EAAgBiE,CAAU,EAAU,KAAKA,EAAW,MACjD,OAAOA,GAAe,SAC1B,KAAKwH,EAAUxH,CAAU,IACzB,MAAMwH,EAAUxH,CAAU,GAC9B,CC/DA,MAwBMqK,kBAAqB,IAAI,QAM/B,SAASC,GAAmB,EAAqC,CAChE,GAAI,EAAEtK,aAAsB,YAAa,OAAOmK,EAAenK,CAAU,EACzE,IAAIyH,EAAO4C,EAAmB,IAAIrK,CAAU,EAK5C,OAJIyH,IAAS,IAAA,KACZ,EAAO0C,EAAenK,CAAU,EAChCqK,EAAmB,IAAIrK,EAAYyH,CAAI,GAEjCA,CACR,CAGA,IAAM8C,GAAN,KAAuB,CAKJ,eACA,SALlB,eACA,QAEA,YACC,EACA,EACC,CAFgB,KAAA,eAAAJ,EACA,KAAA,SAAApK,CACf,CAEH,IAAI,gBAAyB,CAC5B,MAAQ,MAAK,iBAAmB,KAAK,eAAe,CACrD,CAEA,IAAI,KAAc,CACjB,MAAQ,MAAK,UAAYmK,EAA4B,KAAK,eAAgB,KAAK,QAAQ,CACxF,CACD,EAMMM,GAAN,KAAiB,CAChB,MAA6C,CAAC,EAC9C,KAAe,EACf,qBAAwB,IAAI,IAE5B,IAAI,MAAe,CAClB,OAAO,KAAK,KAAK,IAClB,CAEA,KAAK,EAAyB,CAC7B,KAAK,MAAM,KAAKtH,CAAI,EACpB,KAAK,KAAK,IAAIA,CAAI,CACnB,CAEA,OAAO,EAAyB,CAC3B,KAAK,KAAK,OAAOA,CAAI,GAAG,KAAK,QAAQ,CAC1C,CAEA,OAAiC,CAChC,KAAO,KAAK,KAAO,KAAK,MAAM,QAAQ,CACrC,IAAMA,EAAO,KAAK,MAAM,KAAK,MAG7B,GADA,KAAK,MAAM,KAAK,QAAU,IAAA,GACtBA,GAAQ,KAAK,KAAK,OAAOA,CAAI,EAEhC,OADA,KAAK,QAAQ,EACNA,CAET,CAED,CAEA,SAAwB,CACvB,GAAI,KAAK,KAAK,OAAS,EAAG,CACzB,KAAK,MAAQ,CAAC,EACd,KAAK,KAAO,EACZ,MACD,CACA,IAAMuH,EAAO,KAAK,MAAM,OAAS,KAAK,KAAK,KACvCA,GAAQ,IAAMA,GAAQ,KAAK,KAAK,OACpC,KAAK,MAAQ,KAAK,MAAM,OAAQ,GAASvH,IAAS,IAAA,IAAa,KAAK,KAAK,IAAIA,CAAI,CAAC,EAClF,KAAK,KAAO,EACb,CACD,EAMA,SAASwH,GAAqB,EAA8C,CAC3E,GAAI,CACH,OAAO,KAAK,UAAU5N,CAAQ,CAAC,EAAE,QAAU,CAC5C,MAAQ,CACP,MAAO,EACR,CACD,CA2BA,IAAa6N,GAAb,KAA4B,CAC3B,SACA,WAEA,KACA,cACA,cACA,YACA,UACA,MAEA,aACA,cACA,SACA,mBACA,sBAAyB,IAAI,IAC7B,WAAqB,EACrB,UAAoB,EACpB,YAAsB,EACtB,eAAyB,EAEzB,2BAEA,gCAAmC,IAAI,IAEvC,qBAAuE,KAEvE,QACA,SACA,aAEA,4BAA+B,IAAI,IAEnC,yBAA4B,IAAI,IAChC,qBAAmD,KACnD,KAAwB,IAAIH,GAE5B,YAAyD,KACzD,WAA0C,KAC1C,gBAAyC,KAEzC,SAAmB,EAEnB,aAAuB,EAEvB,SAAmB,GAEnB,YACC,EACA,EACA,EAAiC,CAAC,EACjC,CAID,GAHA,KAAK,SAAWI,EAChB,KAAK,WAAaC,EAClB,KAAK,KAAO3N,EAAQ,MAAQ,cACxB,KAAK,OAAS,eAAiB,KAAK,OAAS,QAChD,MAAM,IAAI/B,EACT,2BAA2B,OAAO,KAAK,IAAI,EAAE,mEAC7CC,EAAW,eACX,CAAE,QAAS,CAAE,KAAM,KAAK,IAAK,CAAE,CAChC,EAED,KAAK,cAAgB,KAAK,IAAI,EAAG8B,EAAQ,eAAiB,CAAC,EAE3D,KAAK,cACJA,EAAQ,gBAAkB,IAAA,IAAaA,EAAQ,cAAgB,EAC5D,KAAK,MAAMA,EAAQ,aAAa,EAChC,IAAA,GACJ,KAAK,YACJA,EAAQ,cAAgB,IAAA,IAAaA,EAAQ,YAAc,EACxDA,EAAQ,YACR,IAAA,GACJ,KAAK,UAAYA,EAAQ,UACzB,KAAK,MAAQA,EAAQ,MAErB,IAAM4N,EAAW5N,EAAQ,MACnB6N,EAAc,OAAOD,GAAa,SAAWA,EAAW,KAC9D,KAAK,cAAgB,KAAK,IAAI,EAAGC,GAAa,UAAY,CAAC,EAG3D,KAAK,aAAe,KAAK,cAAgB,EACzC,KAAK,SAAWA,GAAa,OAAS,EACtC,KAAK,mBAAqBA,GAAa,oBAAsB,GAE7D,KAAK,2BAA6B7N,EAAQ,4BAA8B,GAExE,KAAK,QAAUA,EAAQ,QACvB,KAAK,SAAWA,EAAQ,SACxB,KAAK,aAAeA,EAAQ,YAC7B,CAEA,IAAI,WAAqB,CACxB,OAAO,KAAK,SAAS,KAAO,CAC7B,CAEA,IAAI,YAAsB,CACzB,OAAO,KAAK,uBAAyB,MAAQ,KAAK,KAAK,KAAO,CAC/D,CAEA,IAAI,eAAwB,CAC3B,OAAO,KAAK,SAAS,IACtB,CAEA,IAAI,YAAqB,CACxB,OAAO,KAAK,KAAK,MAAQ,QAAK,oBAC/B,CAEA,IAAI,YAAgD,CACnD,OAAO,KAAK,WACb,CAEA,IAAI,WAAiC,CACpC,OAAO,KAAK,UACb,CAEA,IAAI,gBAAgC,CACnC,OAAO,KAAK,eACb,CAUA,iBAAiB,EAAqB,CACrC,IAAM8N,EAAO,KAAK,IAAI,EAAG,KAAK,MAAM7J,CAAK,CAAC,EAC1C,GAAI6J,IAAS,KAAK,cAAe,OACjC,IAAMC,EAASD,EAAO,KAAK,cAC3B,KAAK,cAAgBA,EACjBC,GAAQ,KAAK,UAAU,CAC5B,CAGA,kBAA2B,CAC1B,OAAO,KAAK,aACb,CAGA,UAAU,EAAkC,CAE3C,OADA,KAAK,YAAY,IAAIC,CAAQ,MAChB,KAAK,YAAY,OAAOA,CAAQ,CAC9C,CAEA,QAAuB,CACtB,IAAK,IAAMA,KAAY,KAAK,YAC3B,GAAI,CACHA,EAAS,CACV,OAASjO,EAAK,CACb3B,EAAU,CAAC,CAAC,MAAM,qCAAsC2B,CAAG,CAC5D,CAEF,CA0BA,MACC,EACA,EACA,EACsC,CACtC,OAAO,KAAK,cAAc+C,EAAYD,EAAU7C,CAAO,CAAC,CAAC,KAAM,GAAWiO,EAAO,QAAQ,CAC1F,CAGA,cACC,EACA,EACA,EACuB,CACvB,GAAI,KAAK,SACR,OAAO,QAAQ,OACd,IAAIhQ,EACH,sDACAC,EAAW,aACZ,CACD,EAGD,IAAMgQ,EAAS,IAAIb,OAAuB,KAAK,mBAAmBvK,CAAU,EAAGD,CAAQ,EAGnFkB,EAAM,GACV,GAAI,KAAK,cAAgB,KAAK,SAAW,KAAK,UAAY,KAAK,aAC9D,GAAI,CACH,EAAMmK,EAAO,GACd,OAAS/L,EAAO,CAEf,OAAO,QAAQ,OACd,IAAIlE,EAAa,kCAAmCC,EAAW,cAAe,CAC7E,cAAeiE,CAChB,CAAC,CACF,CACD,CAED,IAAMgM,EAAM,EAAE,KAAK,SACbC,EAAoB,CAAE,MAAK,WAAY,KAAK,IAAI,EAAG,UAAW,IAAK,EAIzE,GAAIpO,GAAS,QAAQ,QACpB,OAAO,QAAQ,OAAO,KAAK,eAAeoO,CAAG,CAAC,EAG/C,IAAMC,EAAS,KAAK,UAAUtK,CAAG,EACjC,GAAIsK,EAAQ,CAGP,KAAK,OAAS,eACjB,KAAK,iBAAiB,EAEvB,IAAM/K,EAAsB,CAC3B,OAAQ,UACR,SAAU+K,EACV,WAAY,EACZ,UAAW,EACZ,EAKA,OAJA,KAAK,eAAeF,EAAK,CAAE,OAAQE,EAAQ,WAAY,CAAE,CAAC,EAC1D,KAAK,QAAQ,KAAK,QAASD,CAAG,EAC9B,KAAK,QAAQ,KAAK,SAAUA,EAAK9K,CAAM,EACvC,KAAK,OAAO,EACL,QAAQ,QAAQ,CAAE,SAAU+K,EAAQ,UAAW,GAAM,WAAY,CAAE,CAAC,CAC5E,CAEA,OAAO,IAAI,SAAsB,EAAS,IAAW,CACpD,IAAMrI,EAAoB,CACzB,aACA,WACA,SACA,MACA,MACA,UACA,SACA,eAAgBhG,GAAS,OACzB,YAAaA,GAAS,YACtB,eAAgBA,GAAS,cAC1B,EAEIgG,EAAK,iBACR,EAAK,uBAA2B,KAAK,gBAAgBA,CAAI,EACzDA,EAAK,eAAe,iBAAiB,QAASA,EAAK,mBAAoB,CAAE,KAAM,EAAK,CAAC,GAGtF,KAAK,QAAQA,CAAI,CAClB,CAAC,CACF,CAGA,eACC,EACA,EAGO,CACHmI,EAAM,KAAK,eACf,KAAK,aAAeA,EAChB,WAAYzD,GACf,KAAK,YAAcA,EAAM,OACzB,KAAK,WAAa,MAElB,KAAK,WAAaA,EAAM,MAEzB,KAAK,gBAAkBA,EAAM,WAC9B,CAGA,kBAAiC,CAChC,AAEC,KAAK,wBADL,KAAK,UAAU,KAAK,oBAAoB,EACZ,MAE7B,IAAK,IAAM4D,KAAY,KAAK,SAC3B,KAAK,UAAUA,CAAQ,EACvBA,EAAS,WAAW,MAAM,CAE5B,CAEA,QAAgB,EAAyB,CACpC,KAAK,uBAAyBtI,IAAM,KAAK,qBAAuB,MACpE,KAAK,KAAK,OAAOA,CAAI,CACtB,CAEA,gBAAwB,EAAyB,CAChD,KAAK,QAAQA,CAAI,EACjB,KAAK,KAAKA,EAAM,KAAK,eAAeA,EAAK,GAAG,CAAC,EAC7C,KAAK,OAAO,CACb,CAGA,gBAAwB,EAAyB,CAChD,IAAMjG,EAAM,IAAI9B,EACf,uDACAC,EAAW,WACX,CACC,WAAY,IACZ,QAAS,CACR,WAAY,KAAK,KAAK,KACtB,cAAe,KAAK,aACrB,CACD,CACD,EACA,KAAK,KAAK8H,EAAMjG,CAAG,EACnB,KAAK,OAAO,CACb,CAEA,kBAA0B,EAAyB,CAClD,GAAI,KAAK,cAAgB,IAAA,GAAW,OACpC,IAAMwO,EAAS,KAAK,YACpB,EAAK,eAAiB,eAAiB,CACtC,GAAIvI,EAAK,QAAS,OAClB,KAAK,QAAQA,CAAI,EAEjB,IAAMjG,EAAM,IAAI9B,EACf,4BAA4BsQ,EAAO,sCACnCrQ,EAAW,cACX,CACC,WAAY,IACZ,QAAS,CACR,SAAU,KAAK,IAAI,EAAI8H,EAAK,IAAI,WAChC,YAAauI,CACd,CACD,CACD,EACA,KAAK,KAAKvI,EAAMjG,CAAG,EACnB,KAAK,OAAO,CACb,EAAGwO,CAAM,CACV,CAEA,oBAA4B,EAAyB,CAChDvI,EAAK,iBAAmB,IAAA,KAC3B,aAAaA,EAAK,cAAc,EAChC,EAAK,eAAiB,IAAA,GAExB,CAEA,QAAgB,EAAyB,CACxC,OAAQ,KAAK,KAAb,CACC,IAAK,cACJ,KAAK,iBAAiB,EAClB,KAAK,SAAS,OAAS,EAC1B,KAAK,QAAQA,CAAI,EAEjB,KAAK,qBAAuBA,EAE7B,MAGD,IAAK,QACA,KAAK,SAAS,KAAO,KAAK,cAC7B,KAAK,QAAQA,CAAI,EACP,KAAK,gBAAkB,IAAA,IAAa,KAAK,KAAK,MAAQ,KAAK,cAErE,KAAK,gBAAgBA,CAAI,GAEzB,KAAK,kBAAkBA,CAAI,EAC3B,KAAK,KAAK,KAAKA,CAAI,EAItB,CACA,KAAK,OAAO,CACb,CAEA,MAAc,QAAQ,EAAkC,CACvD,IAAMwI,EAAa,IAAI,gBAEjBF,EAAWtI,EACjB,EAAS,WAAawI,EACtB,KAAK,SAAS,IAAIF,CAAQ,EAC1B,EAAK,IAAI,UAAY,KAAK,IAAI,EAE9B,KAAK,oBAAoBtI,CAAI,EAC7B,KAAK,yBAAyBA,CAAI,EAClC,IAAMyI,MAA6BD,EAAW,MAAM,EACpDxI,EAAK,gBAAgB,iBAAiB,QAASyI,EAAsB,CAAE,KAAM,EAAK,CAAC,EAEnF,KAAK,QAAQ,KAAK,QAASzI,EAAK,GAAG,EACnC,KAAK,OAAO,EAEZ,IAAM0I,EAAY,YAAY,IAAI,EAClC,GAAI,CACH,IAAMzM,EAAmC,CACxC,GAAG,KAAK,WACR,OAAQuM,EAAW,OACnB,GAAI,KAAK,YAAc,IAAA,IAAa,CAAE,UAAW,KAAK,SAAU,EAChE,GAAI,KAAK,QAAU,IAAA,IAAa,CAAE,MAAO,KAAK,KAAM,EACpD,GAAIxI,EAAK,cAAgB,IAAA,IAAa,CAAE,YAAaA,EAAK,WAAY,CACvE,EACM2I,EAAa3I,EAAK,eACxB,GAAI2I,EAAY,CACf,IAAMC,EAAa,KAAK,WAAW,eACnC,EAAO,gBAAkB,EAAQ,IAAc,CAC9C,GAAI,CACHA,IAAaC,EAAQC,CAAS,CAC/B,QAAU,CACTH,EAAWE,EAAQC,CAAS,CAC7B,CACD,CACD,CAEA,GAAM,CAAE,WAAU,wBAAyB,MAAM,KAAK,YAAY9I,EAAM/D,CAAM,EACxE8M,EAAa,YAAY,IAAI,EAAIL,EAIvC,GAFA,KAAK,WAAW1I,EAAK,IAAI,IAAKpG,CAAQ,EAGrC,CAAC,KAAK,cAAcoG,EAAM,CAAE,WAAU,UAAW,GAAO,uBAAsB,YAAW,CAAC,EAE1F,OAGD,KAAK,eAAeA,EAAK,IAAK,CAAE,OAAQpG,EAAU,YAAW,CAAC,EAE9D,KAAK,QAAQ,KAAK,SAAUoG,EAAK,IAAK,CACrC,OAAQ,UACR,WACA,aACA,UAAW,GACX,sBACD,CAAC,CACF,OAAS7D,EAAO,CACf,IAAM4M,EAAa,YAAY,IAAI,EAAIL,EACjC3O,EAAM,KAAK,wBAAwBoC,EAAOmM,CAAQ,EACxD,KAAK,eAAetI,EAAK,IAAK,CAAE,MAAOjG,EAAK,YAAW,CAAC,EACxD,KAAK,KAAKiG,EAAMjG,EAAKgP,CAAU,CAChC,QAAU,CACT/I,EAAK,gBAAgB,oBAAoB,QAASyI,CAAoB,EACtE,KAAK,SAAS,OAAOH,CAAQ,EAC7B,KAAK,UAAU,EACf,KAAK,OAAO,CACb,CACD,CAEA,MAAc,YACb,EACA,EACoF,CACpF,GAAM,CAAE,aAAY,YAAatI,EACjC,GAAI,CAAC,KAAK,4BAA8B1H,EAAgBwE,CAAU,EAAG,CACpE,GAAM,CAAE,YAAa,MAAM,KAAK,SAASA,EAAYD,EAAU,KAAMZ,CAAM,EAC3E,MAAO,CAAE,UAAS,CACnB,CAEA,IAAMiL,EAAiBlH,EAAK,OAAO,eAC7BgJ,EAAW,KAAK,gBAAgB,IAAI9B,CAAc,GAAK,KACvD5J,EAAS,MAAM,KAAK,SAASR,EAAYD,EAAUmM,EAAU/M,CAAM,EAWzE,OARA,KAAK,gBAAgB,OAAOiL,CAAc,EACtC5J,EAAO,WACV,KAAK,gBAAgB,IAAI4J,EAAgB5J,EAAO,QAAQ,EACpD,KAAK,gBAAgB,KAAO,KAC/B,KAAK,gBAAgB,OAAO,KAAK,gBAAgB,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,KAAM,GAIhE,CAAE,SAAUA,EAAO,SAAU,qBAAsBA,EAAO,MAAO,CACzE,CAEA,mBAA2B,EAAqC,CAK/D,OAJI,OAAOR,GAAe,UACtB,KAAK,sBAAsB,QAAUA,IACxC,KAAK,qBAAuB,CAAE,MAAOA,EAAY,KAAMmK,EAAenK,CAAU,CAAE,GAE5E,KAAK,qBAAqB,MAJUsK,GAAmBtK,CAAU,CAKzE,CAEA,WAA0B,CACzB,GAAI,MAAK,SAET,IAAI,KAAK,OAAS,cACjB,IAAI,KAAK,sBAAwB,KAAK,SAAS,OAAS,EAAG,CAC1D,IAAMgL,EAAO,KAAK,qBAClB,KAAK,qBAAuB,KAC5B,KAAK,QAAQA,CAAI,CAClB,OAID,KAAO,KAAK,KAAK,KAAO,GAAK,KAAK,SAAS,KAAO,KAAK,eACtD,KAAK,QAAQ,KAAK,KAAK,MAAM,CAAE,CAHhC,CAKD,CAEA,UAAkB,EAAyB,CAC1C,IAAM/N,EAAM,IAAI9B,EAAa,4BAA6BC,EAAW,WAAY,CAChF,QAAS,CAAE,WAAY8H,EAAK,IAAI,UAAW,CAC5C,CAAC,EACG,KAAK,YAAYA,EAAMjG,CAAG,GAC7B,KAAK,QAAQ,KAAK,aAAciG,EAAK,GAAG,CAE1C,CAEA,eAAuB,EAAiC,CACvD,OAAO,IAAI/H,EAAa,4BAA6BC,EAAW,QAAS,CACxE,QAAS,CAAE,WAAYkQ,EAAI,UAAW,CACvC,CAAC,CACF,CAQA,YAAoB,EAAmB,EAA4B,CAMlE,MALA,CAAIpI,EAAK,UACT,EAAK,QAAU,CAAE,MAAOjG,CAAI,EAC5B,KAAK,oBAAoBiG,CAAI,EAC7B,KAAK,yBAAyBA,CAAI,EAClCA,EAAK,OAAOjG,CAAG,EACR,GACR,CAMA,KAAa,EAAmB,EAAmB,EAAa,EAAS,CACpE,KAAK,YAAYiG,EAAMjG,CAAG,GAC7B,KAAK,QAAQ,KAAK,SAAUiG,EAAK,IAAK,CAAE,OAAQ,QAAS,MAAOjG,EAAK,YAAW,CAAC,CAEnF,CAEA,cAAsB,EAAmB,EAA8B,CAMtE,MALA,CAAIiG,EAAK,UACT,EAAK,QAAU,CAAE,GAAI,EAAK,EAC1B,KAAK,oBAAoBA,CAAI,EAC7B,KAAK,yBAAyBA,CAAI,EAClCA,EAAK,QAAQiI,CAAM,EACZ,GACR,CAEA,yBAAiC,EAAyB,CACrDjI,EAAK,oBAAsBA,EAAK,gBACnCA,EAAK,eAAe,oBAAoB,QAASA,EAAK,kBAAkB,EAEzE,EAAK,mBAAqB,IAAA,EAC3B,CAEA,wBAAgC,EAAgB,EAAkC,CAajF,OAXIA,EAAK,SAAW,UAAWA,EAAK,QAC5BA,EAAK,QAAQ,MAGjB7D,aAAiBlE,EAAqBkE,EAGtCA,aAAiB,OAASA,EAAM,OAAS,aACrC,KAAK,eAAe6D,EAAK,GAAG,EAG7B,IAAI/H,EACVkE,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,EACrDjE,EAAW,cACX,CAAE,cAAeiE,CAAM,CACxB,CACD,CAOA,WAAkB,CACjB,AAEC,KAAK,wBADL,KAAK,KAAK,KAAK,qBAAsB,KAAK,eAAe,KAAK,qBAAqB,GAAG,CAAC,EAC3D,MAE7B,IAAK,IAAI6D,EAAO,KAAK,KAAK,MAAM,EAAGA,EAAM,EAAO,KAAK,KAAK,MAAM,EAC/D,KAAK,KAAKA,EAAM,KAAK,eAAeA,EAAK,GAAG,CAAC,EAE9C,IAAK,IAAMsI,KAAY,KAAK,SAAU,CAErC,IAAMS,EAAaT,EAAS,IAAI,UAAY,KAAK,IAAI,EAAIA,EAAS,IAAI,UAAY,EAClF,KAAK,KAAKA,EAAU,KAAK,eAAeA,EAAS,GAAG,EAAGS,CAAU,EACjET,EAAS,WAAW,MAAM,CAC3B,CACA,KAAK,OAAO,CACb,CAMA,UAAkB,EAAgD,CACjE,GAAI,CAAC,KAAK,aAAc,OAAO,KAC/B,IAAMW,EAAQ,KAAK,MAAM,IAAIlL,CAAG,EAchC,OAbKkL,EAID,KAAK,SAAW,GAAK,KAAK,IAAI,EAAIA,EAAM,WAAa,KAAK,UAC7D,KAAK,eAAelL,CAAG,EACvB,KAAK,aAAe,EACb,OAGR,KAAK,MAAM,OAAOA,CAAG,EACrB,KAAK,MAAM,IAAIA,EAAKkL,CAAK,EACzB,KAAK,WAAa,EACXA,EAAM,WAZZ,KAAK,aAAe,EACb,KAYT,CAEA,WAAmB,EAAa,EAA4C,CAC3E,GAAI,CAAC,KAAK,aAAc,OACxB,IAAMC,EAAclR,EAAqB4B,EAAU,QAAQ,EAC3D,GAAI,CAAC,KAAK,oBAAsB,MAAM,QAAQsP,CAAW,GAAKA,EAAY,OAAS,EAAG,OAEtF,IAAMC,EAAUnR,EAAUA,EAAU4B,EAAU,OAAO,EAAG,SAAS,EACjE,GAAI5B,EAAUmR,EAAS,SAAS,IAAM,GAAM,OAC5C,IAAMC,EAAY5Q,EAAoBoB,CAAQ,GAAK4N,GAAqB5N,CAAQ,EAEhF,GAAI,IAAY,KAAK,eAIrB,IAHA,KAAK,eAAemE,CAAG,EACvB,KAAK,MAAM,IAAIA,EAAK,CAAE,WAAU,WAAY,KAAK,IAAI,EAAG,WAAU,CAAC,EACnE,KAAK,YAAcqL,EACZ,KAAK,WAAa,KAAK,eAAiB,KAAK,MAAM,KAAO,GAAG,CACnE,IAAMC,EAAS,KAAK,MAAM,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,MACxC,GAAIA,IAAW,IAAA,GAAW,MAC1B,KAAK,eAAeA,CAAM,EAC1B,KAAK,gBAAkB,CACxB,CACD,CAEA,eAAuB,EAAmB,CACzC,IAAMJ,EAAQ,KAAK,MAAM,IAAIlL,CAAG,EAC3BkL,IACL,KAAK,MAAM,OAAOlL,CAAG,EACrB,KAAK,YAAckL,EAAM,UAC1B,CAEA,YAAmB,CAClB,KAAK,MAAM,MAAM,EACjB,KAAK,WAAa,CACnB,CAYA,YAME,CACD,MAAO,CACN,QAAS,KAAK,MAAM,KACpB,MAAO,KAAK,WACZ,KAAM,KAAK,UACX,OAAQ,KAAK,YACb,UAAW,KAAK,cACjB,CACD,CAMA,SAAgB,CACX,KAAK,WACT,KAAK,SAAW,GAChB,KAAK,UAAU,EACf,KAAK,YAAY,MAAM,EACvB,KAAK,WAAW,EACjB,CAEA,QACC,EACA,GAAG5L,EACI,CACP,GAAKiM,EACL,GAAI,CACHA,EAAK,GAAGjM,CAAI,CACb,OAAStD,EAAK,CACb3B,EAAU,CAAC,CAAC,MAAM,+BAAgC2B,CAAG,CACtD,CACD,CACD,ECz0BA,SAASwP,GAAmB,EAAqC,CAGhE,OAFI1Q,EAAgBiE,CAAU,EAAU,OAAOA,EAAW,MACtD,OAAOA,GAAe,UAAYA,EAAW,OAAS,IAAYA,EAC/D,eACR,CAaA,SAAS0M,GAAkB,EAA8C,CAGxE,OAFK,MAAM,QAAQ3M,CAAQ,EAEpBA,EAAS,IAAK,GAAS,CAC7B,IAAIkD,EAAQ,EACR6E,EAAQ,EAENnI,EAAYzE,EAAmC4H,EAAM,WAAW,EACtE,GAAInD,GAAa,OAAOA,GAAc,SACrC,KAAK,IAAMqD,KAAU,OAAO,OAAOrD,CAAS,EAC3C,GAAK,MAAM,QAAQqD,CAAM,EACzB,IAASA,EAAO,OAChB,IAAK,IAAME,KAAQF,EAAQ,CAC1B,IAAMG,EAAQD,GAAoC,KAC9C,OAAOC,GAAS,WAAU,GAASA,EAAK,OAC7C,CAJgB,CAKjB,CAGD,MAAO,CAAE,MAAOjI,EAAkB4H,EAAM,WAAW,GAAK,YAAa,QAAO,OAAM,CACnF,CAAC,EAnBoC,CAAC,CAoBvC,CA0BA,IAAqB6J,GAArB,MAAqBA,CAAkB,CACtC,OACA,YACA,SAAmB,GAQnB,OAAwB,wBAA0B,IAElD,YAAoB,EAAkC,CACrD,KAAK,OAAS,CACb,GAAGxN,EACH,UAAWhD,EAAkBgD,EAAO,SAAS,EAC7C,MAAOA,EAAO,OAAS,EACxB,EACA,KAAK,YAAc,IAAI9C,EAAmB,KAAK,OAAO,UAAW,KAAK,MAAM,CAC7E,CAoBA,aAAa,OACZ,EACA,EAA+B,CAAC,EACH,CAC7B,IAAMuQ,EAAS,IAAID,EAAkBxN,CAAM,EAErC0N,EAAW,KAAK,IAAI,GAAI3P,EAAQ,OAAO,UAAY,GAAK,CAAC,EACzD4P,EAAc5P,EAAQ,OAAO,aAAe,IAC5C6P,EAAa7P,EAAQ,OAAO,YAAc,IAE5C8P,EACAC,EAA+B,KAC/BC,EAAO,EACX,IAAK,IAAIC,EAAU,EAAGA,EAAUN,EAAU,IAAW,CAGpD,GAFA,EAAOM,EAAU,EACjB,EAAY,MAAMP,EAAO,YAAY,YAAYD,EAAkB,uBAAuB,EACtFK,EAAU,OACb,OAAOJ,EAMR,GAHA,EAAU3Q,GAAqB+Q,CAAS,EAGpCC,GAAW,CAACA,EAAQ,UAAW,MAEnC,GAAIE,EAAUN,EAAW,EAAG,CAC3B,IAAMO,EAAQ,KAAK,IAAIN,EAAc,GAAKK,EAASJ,CAAU,EAC7D,MAAM,IAAI,QAAS,GAAY,WAAWM,EAASD,CAAK,CAAC,CAC1D,CACD,CAEA,MAAMR,EAAO,QAAQ,EACrB,IAAMU,EAASN,GAAW,OACpBrK,EAAUsK,EACb,0CAA0CA,EAAQ,UAClD,qCACH,MAAM,IAAI9R,EAAawH,EAASvH,EAAW,cAAe,CACzD,GAAIkS,IAAW,IAAA,IAAa,CAAE,WAAYA,CAAO,EACjD,QAAS,CACR,UAAWV,EAAO,OAAO,UAGzB,SAAUM,EACV,YAAaL,EACb,GAAII,GAAW,CAAE,aAAcA,EAAQ,QAAS,eAAgBA,EAAQ,SAAU,EAClF,GAAIK,IAAW,IAAA,IAAa,CAAE,gBAAiBA,CAAO,EACtD,GAAIN,GAAW,QAAU,IAAA,IAAa,CAAE,eAAgBA,EAAU,KAAM,CACzE,CACD,CAAC,CACF,CAMA,WAA6C,CAE5C,OADA,KAAK,kBAAkB,EAChB,CAAE,GAAG,KAAK,MAAO,CACzB,CAKA,MAAa,MAAM,EAAiC,CAEnD,OADA,KAAK,kBAAkB,EAChB1F,EAAwBtH,EAAY,KAAK,MAAM,CACvD,CAEA,MAAa,SAAS,EAAiC,CAEtD,OADA,KAAK,kBAAkB,EAChB+G,EAAkB/G,EAAY,KAAK,MAAM,CACjD,CAcA,MAAa,MACZ,EACA,EACA,EACsC,CACtC,KAAK,kBAAkB,EAEvB,GAAI,CAEH,GAAI,OAAOA,GAAe,UAAY,CAACA,EAAW,KAAK,EACtD,MAAM,IAAI7E,EAAa,qCAAsCC,EAAW,cAAe,CACtF,QAAS,CAAE,YAAa4E,CAAW,CACpC,CAAC,EACK,GAAIA,aAAsB,YAAcA,EAAW,SAAW,EACpE,MAAM,IAAI7E,EAAa,8BAA+BC,EAAW,aAAa,EACxE,GAAIW,EAAgBiE,CAAU,GAAK,CAACA,EAAW,IAAI,KAAK,EAC9D,MAAM,IAAI7E,EAAa,6BAA8BC,EAAW,aAAa,EAW9E,IAAMoF,EAAS,MAAMX,GAA2BE,EAAUC,EAAY,CAPrE,GAAG,KAAK,OACR,GAAI9C,GAAS,SAAW,IAAA,IAAa,CAAE,OAAQA,EAAQ,MAAO,EAC9D,GAAIA,GAAS,YAAc,IAAA,IAAa,CAAE,UAAWA,EAAQ,SAAU,EACvE,GAAIA,GAAS,QAAU,IAAA,IAAa,CAAE,MAAOA,EAAQ,KAAM,EAC3D,GAAIA,GAAS,cAAgB,IAAA,IAAa,CAAE,YAAaA,EAAQ,WAAY,CAGR,CAAe,EAG/EkP,EAAclR,EAAqBsF,EAAQ,QAAQ,EACzD,GAAI,MAAM,QAAQ4L,CAAW,GAAKA,EAAY,OAAS,EACtD,MAAM,IAAIjR,EACTiR,EAAY,IAAI,MAAM,CAAC,CAAC,KAAK,IAAI,GAAK,qBACtChR,EAAW,kBACX,CACC,QAAS,CACR,WAAYqR,GAAmBzM,CAAU,EACzC,aAAc0M,GAAkB3M,CAAQ,EACxC,OAAQqM,EACR,SAAUlR,EAAqBsF,EAAQ,UAAU,EACjD,OAAQA,EAAO,MAChB,CACD,CACD,EAGD,OAAOA,CACR,OAASnB,EAAO,CASf,MARI,KAAK,OAAO,OACf/D,EAAU,CAAC,CAAC,MAAM,kBAAmB+D,CAAK,EAGvCA,aAAiBlE,EACdkE,EAGD,IAAIlE,EACTkE,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,EACrDjE,EAAW,kBACX,CACC,QAAS,CACR,WAAYqR,GAAmBzM,CAAU,EACzC,aAAc0M,GAAkB3M,CAAQ,CACzC,EACA,cAAeV,CAChB,CACD,CACD,CACD,CAgBA,gBAAuB,EAAiD,CAEvE,OADA,KAAK,kBAAkB,EAChB,IAAIsL,IACT,EAAY,EAAU,EAAU,IAChC1K,GAAwBF,EAAUG,EAAUF,EAAYb,CAAM,EAC/D,KAAK,OACLjC,CACD,CACD,CAMA,MAAa,SAAyB,CACjC,KAAK,WAET,KAAK,SAAW,GAChB,MAAM,KAAK,YAAY,QAAQ,EAChC,CAEA,mBAAkC,CACjC,GAAI,KAAK,SACR,MAAM,IAAI/B,EACT,yDACAC,EAAW,aACZ,CAEF,CACD,EC5UA,SAAgBmS,GAAmB,EAA2C,CAC7E,OACC,OAAOpM,GAAU,YACjBA,GACCA,EAA2B,gBAAkB,EAEhD,CAEA,SAAgBqM,GAAgB,EAAmB,EAAgC,CAClF,MAAO,CAAE,cAAe,GAAM,KAAMC,EAAW,KAAI,CACpD,CAcA,SAAgBC,GAAoB,EAAsB,CAGzD,IAAMC,kBAAO,IAAI,QAEXC,EAAQ,GAAqB,CAElC,GADI,CAACjM,GAAK,OAAOA,GAAM,UACnBgM,EAAK,IAAIhM,CAAC,EAAG,OACjBgM,EAAK,IAAIhM,CAAC,EAEV,IAAMkM,EAAOlM,EAA2B,OACxC,GAAI,OAAOkM,GAAQ,WAAY,CAC9B,IAAMC,EAAanM,EAAoC,UACnD,OAAOmM,GAAc,YAAeA,EAAU,KAAKnM,CAAC,GACvDkM,EAAoB,KAAKlM,CAAC,EAE3B,MACD,CAEA,GAAI,MAAM,QAAQA,CAAC,EAAG,CACrB,IAAK,IAAMuB,KAAQvB,EAAGiM,EAAK1K,CAAI,EAC/B,MACD,CAGA,IAAM6K,EAAQ,OAAO,eAAepM,CAAC,EACrC,GAAIoM,IAAU,OAAO,WAAaA,IAAU,KAC3C,IAAK,IAAM7K,KAAQ,OAAO,OAAOvB,CAAC,EAAGiM,EAAK1K,CAAI,CAEhD,EAEA0K,EAAKzM,CAAK,CACX,CC7DA,MAAM6M,kBAAW,IAAI,IAA0B,CAC9C,CACC,0BACC,EAAO,IAAS,CAChB,IAAM5E,EAAIjG,EAEV,MADI,CAACiG,GAAK,OAAOA,EAAE,GAAM,SAAiB,KACnC,IAAI6E,EAAM,MAAM,CAAC7E,EAAE,EAAGA,EAAE,EAAaA,EAAE,CAAW,CAAC,CAC3D,CACD,EACA,CACC,uBACC,EAAO,IAAS,CAChB,IAAMA,EAAIjG,EAEV,MADI,CAACiG,GAAK,CAACA,EAAE,MAAQ,CAACA,EAAE,GAAW,KAC5B,IAAI6E,EAAM,KAAK,CAAC7E,EAAE,KAAK,EAAGA,EAAE,KAAK,EAAGA,EAAE,KAAK,CAAC,EAAG,CAACA,EAAE,GAAG,EAAGA,EAAE,GAAG,EAAGA,EAAE,GAAG,CAAC,CAAC,CAC/E,CACD,CACD,CAAC,EAED,SAAS8E,GAAY,EAA6C,CACjE,GAAIF,EAAS,IAAIP,CAAS,EAAG,OAAOO,EAAS,IAAIP,CAAS,EAC1D,IAAK,GAAM,CAACxM,EAAKkN,KAAQH,EACxB,GAAIP,EAAU,WAAWxM,CAAG,EAAG,OAAOkN,CAGxC,CAQA,SAASC,GAAoB,EAA2C,CACvE,MAAO,GACNC,GACA,OAAOA,GAAe,UACtB,OAAQA,EAAkC,MAAS,SAErD,CAwBA,SAAgBC,GACf,EACA,EACA,EACU,CACV,IAAMC,EAAUL,GAAYT,CAAS,EAKjCe,EAAe,GACnB,GAAID,EACH,GAAI,CACH,IAAME,EAAUF,EAAQN,EAAOI,CAAU,EACzC,GAAII,GAAW,KAAM,OAAOA,CAC7B,OAASpP,EAAO,CACf/D,EAAU,CAAC,CAAC,KAAK,+BAA+BmS,EAAU,GAAIpO,CAAK,EACnE,EAAe,EAChB,CAID,GAAI,CACH,GAAI+O,GAAoBC,CAAU,EAAG,OAAOJ,EAAM,aAAa,OAAOI,CAAU,CACjF,OAAShP,EAAO,CAEf,OADA/D,EAAU,CAAC,CAAC,KAAK,oBAAoBmS,EAAU,qBAAsBpO,CAAK,EACnEmO,GAAgBC,EAAWY,CAAU,CAC7C,CAEA,OAAOG,EAAehB,GAAgBC,EAAWY,CAAU,EAAIA,CAChE,CCtEA,MAAMK,EAAe,CACpB,OAAQ,gBACR,IAAK,eACL,OAAQ,gBACR,KAAM,gBACP,EAIMC,GAAiB,WAGvB,SAASC,GAAS,EAAc,EAA8B,CAC7D,OAAOxL,EAAK,SAAS,YAAY,GAAMyL,GAAczL,IAASsL,EAAa,MAC5E,CAEA,SAASI,GAAc,EAAwB,CAC9C,GAAI,OAAO3N,GAAU,SAAU,OAAOA,EAEtC,IAAMY,EAAUZ,EAAM,KAAK,EAE3B,GAAI,EADcY,EAAQ,WAAW,GAAG,GAAKA,EAAQ,WAAW,GAAG,GAAKA,EAAQ,WAAW,GAAG,GAC9E,OAAOZ,EAEvB,GAAI,CACH,IAAM4N,EAAQ,KAAK,MAAMhN,CAAO,EAChC,GAAI,OAAOgN,GAAU,SACpB,GAAI,CACH,OAAO,KAAK,MAAMA,CAAK,CACxB,MAAQ,CACP,OAAOA,CACR,CAED,OAAOA,CACR,MAAQ,CACP,OAAO5N,CACR,CACD,CAEA,SAAS6N,GAAmB,EAAc,EAAc,EAA8B,CACrF,OAAQ5L,EAAR,CACC,KAAKsL,EAAa,OAEjB,OADI,OAAO/H,GAAQ,SACZA,EAAI,QAAQ,WAAY,IAAI,EADCA,EAGrC,KAAK+H,EAAa,IACjB,OAAO,OAAO,SAAS,OAAO/H,CAAG,EAAG,EAAE,EAEvC,KAAK+H,EAAa,OACjB,OAAO,OAAO,WAAW,OAAO/H,CAAG,CAAC,EAErC,KAAK+H,EAAa,KAEjB,OADY,OAAO/H,CAAG,CAAC,CAAC,YACf,IAAM,OAGhB,QAIC,OAHIsH,GAAS7K,EAAK,WAAW,iBAAqB,EAC1CkL,GAAoB3H,EAAKvD,EAAM6K,CAAK,EAErCtH,CACT,CACD,CAEA,SAASsI,EACR,EACA,EACA,EACA,EACU,CACV,GAAI,OAAO/L,EAAK,MAAS,SAAU,OAAOA,EAAK,KAE/C,IAAMyD,EAAMuI,EAAcJ,GAAc5L,EAAK,IAAI,EAAIA,EAAK,KAK1D,OAJIgM,GAAe9L,EAAK,SAASuL,EAAc,EACvCQ,GAAWxI,CAAG,GAAKA,EAGpBqI,GAAmBrI,EAAKvD,EAAM6K,CAAK,CAC3C,CAOA,SAASkB,GAAW,EAAiC,CACpD,GAAI,CAAChO,GAAS,OAAOA,GAAU,SAAU,OAAO,KAEhD,IAAMiO,EAAWlU,EAAmBiG,EAAO,UAAU,EAC/CkO,EAAWnU,EAAmBiG,EAAO,UAAU,EAC/CmO,EAAYpU,EAAmBiG,EAAO,WAAW,EACjDoO,EAAOrU,EAAmBiG,EAAO,iBAAiB,EAQxD,GANI,OAAOiO,GAAa,UAAY,OAAOC,GAAa,UACpD,OAAOC,GAAc,UAErB,CAACjU,EAAS8F,EAAO,MAAM,GAGvB,OAAOoO,GAAS,WAAa,OAAOA,GAAS,SAAU,OAAO,KAElE,IAAMC,EAAWtU,EAAkCiG,EAAO,UAAU,EAEpE,MAAO,CACN,WACA,WACA,KAAMjG,EAAkBiG,EAAO,MAAM,EACrC,gBAAiBvF,EAAa2T,CAAI,EAClC,YACA,GAAIC,EAAW,CAAE,UAAS,EAAI,CAAC,CAChC,CACD,CAGA,SAASC,EAAkB,EAAiD,CAC3E,IAAMjQ,EAAStE,EAAqB4B,EAAU,QAAQ,EACtD,OAAO,MAAM,QAAQ0C,CAAM,EAAIA,EAAS,CAAC,CAC1C,CAGA,SAASkQ,EAAS,EAAwB,CACzC,OAAO,OAAOxM,EAAK,MAAS,SAAWA,EAAK,KAAO,EACpD,CAGA,SAASyM,EAAgB,EAAe,EAAmC,CAC1E,GAAK7M,GAAQ,OAAOA,GAAS,SAC7B,KAAK,IAAM8M,KAAQ,OAAO,OAAO9M,CAAI,EACpC,GAAI,MAAM,QAAQ8M,CAAI,EAChB,IAAA,IAAM1M,KAAQ0M,EACd1M,GAAQ,OAAOA,GAAS,UAAU2M,EAAQ3M,CAAgB,CAC/D,CAGH,CAYA,SAAgB4M,GACf,EACA,EAAgB,GAChB,EAA4B,CAAC,EACR,CACrB,GAAM,CAAE,cAAc,GAAM,QAAO,aAAa,IAAU5S,EACpDsD,EAAwB,CAAC,EAGzBuP,kBAAa,IAAI,IAEvB,IAAK,IAAMrQ,KAAS+P,EAAkB3S,CAAQ,EAAG,CAChD,IAAMkT,EAAY9U,EAAkBwE,EAAO,WAAW,EACtDiQ,EAAgBzU,EAAUwE,EAAO,WAAW,EAAI,GAAS,CACxD,IAAM0D,EAAOsM,EAASxM,CAAI,EAC1B,GAAI0L,GAASxL,EAAMyL,CAAU,EAAG,OAEhC,IAAM5N,EAAMgP,EAAO/M,EAAK,GAAK8M,EAC7B,GAAI,CAAC/O,EAAK,OAEV,IAAME,EAAQ8N,EAAiB/L,EAAME,EAAM8L,EAAajB,CAAK,EAGxD,OAAO,UAAU,eAAe,KAAKzN,EAAQS,CAAG,EAE1C8O,EAAW,IAAI9O,CAAG,EAC5BT,EAAOS,EAAI,CAAC,KAAKE,CAAK,GAEtB,EAAOF,GAAO,CAACT,EAAOS,GAAME,CAAK,EACjC4O,EAAW,IAAI9O,CAAG,GALlBD,EAAYR,EAAQS,EAAKE,CAAK,CAOhC,CAAC,CACF,CAEA,MAAO,CAAE,OAAQX,EAAa,YAAekN,GAAoBlN,CAAM,CAAE,CAC1E,CAGA,SAAgB0P,GAAgB,EAAkD,CACjF,IAAMC,EAAqB,CAAC,EAE5B,IAAK,IAAMzQ,KAAS+P,EAAkB3S,CAAQ,EAC7C6S,EAAgBzU,EAAUwE,EAAO,WAAW,EAAI,GAAS,CACxD,GAAI,CAACgQ,EAASxM,CAAI,CAAC,CAAC,SAASyL,EAAc,EAAG,OAE9C,IAAMyB,EAAOjB,GAAWL,GAAc5L,EAAK,IAAI,CAAC,EAC5CkN,GACHD,EAAO,KAAKC,CAAI,CAElB,CAAC,EAGF,OAAOD,CACR,CAcA,SAAgBE,GACf,EACA,EACA,EAAiC,CAAC,EAC5B,CACN,GAAM,CAAE,cAAc,GAAM,QAAO,aAAa,IAAUC,EAG1D,IAAK,IAAM5Q,KAAS+P,EAAkB3S,CAAQ,EAAG,CAChD,IAAMgG,EAAO5H,EAAUwE,EAAO,WAAW,EAErC6Q,EAAU,GACd,GAAI,WAAYrT,EAAS,CACxB,GAAIhC,EAAkBwE,EAAO,WAAW,IAAMxC,EAAQ,OAAQ,SAC9D,EAAU,EACX,CAEA,IAAMsT,EAAuB,CAAC,EAY9B,GAXAb,EAAgB7M,EAAO,GAAS,CAC/B,GAAI,SAAU5F,EAAS,CACtB,GAAIgG,EAAK,KAAOhG,EAAQ,KAAM,OAE9B,EAAU,EACX,CACA,IAAMkG,EAAOsM,EAASxM,CAAI,EACtB0L,GAASxL,EAAMyL,CAAU,GAC7B2B,EAAU,KAAKvB,EAAiB/L,EAAME,EAAM8L,EAAajB,CAAK,CAAC,CAChE,CAAC,EAEGsC,EAGH,OAFIC,EAAU,SAAW,EAAG,OACxBA,EAAU,SAAW,EAAUA,EAAU,GACtCA,CAET,CAGD,CC7QA,IAAqBC,GAArB,KAAkD,CAGhC,SAFjB,YAEC,EACC,CADe,KAAA,SAAA3T,CACd,CAiBH,UACC,EAAgB,GAChB,EAA4B,CAAC,EACR,CACrB,OAAOgT,GAAa,KAAK,SAAUG,EAAM/S,CAAO,CACjD,CAiBA,SACC,EACA,EACM,CACN,OAAOmT,GAAS,KAAK,SAAUK,EAAUxT,CAAO,CACjD,CAsBA,MAAa,oBACZ,EACA,EACgB,CAChB,MAAMlB,GAAiBkU,GAAgB,KAAK,QAAQ,EAAGS,EAAYC,CAAe,CACnF,CACD,ECvEA,SAAgBC,GACf,EACkC,CAElC,OADgB,MAAM,QAAQlK,CAAG,EAAIA,EAAM,CAACA,CAAG,EAAA,CAChC,IAAK,IAAW,CAC9B,QAASzL,EAAqBiR,EAAO,SAAS,EAC9C,MAAOjR,EAAkBiR,EAAO,OAAO,CACxC,EAAE,CACH,CCVA,MAAM2E,mBAAqB,IAAI,IAAI,CAAC,UAAW,iBAAkB,QAAQ,CAAC,EAGpEC,mBAAyB,IAAI,IAAI,CAAC,cAAe,QAAQ,CAAC,EAE1DC,GAAW,GAAwBtH,GAAM,KAAOA,GAAM,IAO5D,SAASuH,GAAY,EAAqB,CACzC,GAAIhQ,EAAI,SAAW,GAAK,CAAC+P,GAAQ/P,EAAI,EAAE,EAAG,OAAOA,EAEjD,IAAIiQ,EAAM,EACV,KAAOA,EAAMjQ,EAAI,QAAU+P,GAAQ/P,EAAIiQ,EAAI,GAAG,IAI9C,OAFIA,EAAM,GAAKA,EAAMjQ,EAAI,QAAQ,IAE1BA,EAAI,MAAM,EAAGiQ,CAAG,CAAC,CAAC,YAAY,EAAIjQ,EAAI,MAAMiQ,CAAG,CACvD,CAOA,SAAgBC,GAA2B,EAAW,CACrD,OAAOC,EAAezK,CAAG,CAC1B,CAEA,SAASyK,EAAe,EAAyB,CAChD,GAAI,MAAM,QAAQjQ,CAAK,EAAG,OAAOA,EAAM,IAAIiQ,CAAc,EACzD,GAAsB,OAAOjQ,GAAU,WAAnCA,EAA6C,OAAOA,EAExD,IAAM4I,EAA+B,CAAC,EACtC,IAAK,GAAM,CAAC9I,EAAKoQ,KAAU,OAAO,QAAQlQ,CAAgC,EAAG,CAC5E,IAAMmQ,EAAYL,GAAYhQ,CAAG,EAMjCD,EAAY+I,EAAKuH,EALER,GAAmB,IAAIQ,CAAS,EAChDD,EACAN,GAAuB,IAAIO,CAAS,EACnCC,GAAiBF,CAAK,EACtBD,EAAeC,CAAK,CACc,CACvC,CACA,OAAOtH,CACR,CAEA,SAASwH,GAAiB,EAAyB,CAClD,GAAsB,OAAOpQ,GAAU,WAAnCA,GAA+C,MAAM,QAAQA,CAAK,EAAG,OAAOA,EAEhF,IAAM4I,EAA+B,CAAC,EACtC,IAAK,GAAM,CAAC9I,EAAKoQ,KAAU,OAAO,QAAQlQ,CAAgC,EACzEH,EAAY+I,EAAK9I,EAAKmQ,EAAeC,CAAK,CAAC,EAE5C,OAAOtH,CACR,CCpCA,IAAayH,GAAb,MAAaA,CAAY,CACxB,UACA,UAEA,YAAY,EAAmB,CAC9B,KAAK,UAAYxB,EACjB,KAAK,UAAY,CAAC,CACnB,CAGA,OAAc,EAAgB,EAA8B,CAC3D,IAAMhN,EAAU,KAAK,UAAUwO,EAAY,iBAAiB3U,CAAI,KAAO,CAAC,EAExE,IAAK,IAAMqG,KAAQD,EAAOD,EAAO,KAAK,CAAE,KAAMwO,EAAY,eAAetO,CAAI,CAAE,CAAC,EAChF,OAAO,IACR,CAGA,oBAA2B,EAAiC,CAC3D,KAAK,UAAY,CAAC,EAElB,IAAK,GAAM,CAACuO,EAASxO,KAAU,OAAO,QAAQyO,CAAQ,EAAG,CACxD,GAAI,CAAC,MAAM,QAAQzO,CAAK,EAAG,SAC3B,IAAMpG,EAAO2U,EAAY,gBAAgBC,CAAO,EAChD,KAAK,OAAO5U,EAAMoG,CAAK,CACxB,CAEA,OAAO,IACR,CAGA,WAAkB,EAA+C,CAChE,IAAMA,EAAQ,MAAM,QAAQzD,CAAM,EAAIA,EAAS,CAACA,CAAM,EACtD,OAAO,KAAK,OAAO,CAAC,CAAC,EAAGyD,CAAK,CAC9B,CAGA,SAAkC,CACjC,IAAMzC,EAA0B,CAAC,EAEjC,IAAK,IAAMyC,KAAS,OAAO,OAAO,KAAK,SAAS,EAC/C,GAAI,MAAM,QAAQA,CAAK,EACtB,IAAK,IAAMC,KAAQD,EAClBzC,EAAO,KAAKgR,EAAY,iBAAiBtO,EAAK,IAAI,CAAC,EAKtD,OAAO1C,CACR,CAGA,iBAAmC,CAClC,MAAO,CACN,UAAW,KAAK,UAEhB,UAAW,KAAK,SACjB,CACD,CAEA,cAA8B,CAC7B,OAAO,KAAK,SACb,CAMA,OAAc,gBAAgB,EAAkC,CAC/D,OAAOgR,EAAY,WAClB/K,EAAO,IAAK,IAAW,CACtB,UAAW7D,EAAM,UAAY,UAC7B,UAAWA,EAAM,UACjB,MAAOA,EAAM,QACb,GAAI,YAAaA,GAAS,CAAE,QAASA,EAAM,QAAS,QAASA,EAAM,OAAQ,CAC5E,EAAE,CACH,CACD,CASA,OAAc,WAAW,EAAuC,CAC/D,IAAM+O,EAAoB,CAAC,EAE3B,IAAK,IAAMxF,KAAS7K,EAAS,CAC5B,IAAMwB,EAAO,IAAI0O,EAAYrF,EAAM,SAAS,EACtCyF,EAASJ,EAAY,YAAYrF,CAAK,EAE5C,IAAK,GAAM,CAACtP,EAAMoG,KAAUuO,EAAY,WAAWrF,EAAM,KAAK,EAAG,CAChE,IAAM0F,EAAwB,CAAC,EAC/B,IAAK,IAAM3O,KAAQD,EAAO,CACzB,GAAIC,GAAS,KAA4B,SACzC,IAAM4O,EAAUF,EAAO1O,CAAI,EACvB4O,IAAY,KACfxW,EAAU,CAAC,CAAC,KACX,GAAG6Q,EAAM,UAAU,wCAAwCA,EAAM,WAClE,EAEA0F,EAAK,KAAKC,CAAO,CAEnB,CACID,EAAK,OAAS,GAAG/O,EAAK,OAAOjG,EAAMgV,CAAI,CAC5C,CAEI,OAAO,KAAK/O,EAAK,SAAS,CAAC,CAAC,OAAS,GAAG6O,EAAM,KAAK7O,EAAK,gBAAgB,CAAC,CAC9E,CAEA,OAAO6O,CACR,CAEA,OAAe,WAAW,EAAyC,CAQlE,OAPKH,EAAY,cAAcrQ,CAAK,EAChCE,EAAkBF,CAAK,EACnB,OAAO,QAAQA,CAAK,CAAC,CAAC,KAAK,CAACtE,EAAMoG,KAAW,CACnDuO,EAAY,gBAAgB3U,CAAI,EAChCoG,CACD,CAAC,EAEK,CAAC,CAAC,CAAC,CAAC,EAAG,MAAM,QAAQ9B,CAAK,EAAIA,EAAQ,CAACA,CAAK,CAAC,CAAC,EAPP,CAAC,CAQhD,CAGA,OAAe,YAAY,EAAgE,CAC1F,GAAM,CAAE,YAAW,UAAS,WAAYgL,EAClCrG,EAAYqG,EAAM,UAAU,YAAY,EAW9C,OATIrG,IAAc,UAAYA,IAAc,UACnC,GAAS,CAChB,IAAMjB,EAAI/C,EAAmBoB,CAAI,EACjC,GAAI2B,IAAM,KAAM,OAAO,KACvB,IAAM1D,EAAQ2E,IAAc,UAAY,KAAK,MAAMjB,CAAC,EAAIA,EACxD,OAAO2M,EAAY,WAAWrQ,EAAO4Q,EAASC,EAAShC,CAAS,CACjE,EAEGlK,IAAc,UAAkB7D,EAC5B,GAASiB,CAClB,CAOA,OAAc,iBACb,EACA,EACA,EACa,CACb,IAAM+O,EAAWT,EAAY,eAAexB,EAAWkC,CAAQ,CAAC,CAAC,gBAAgB,EAC3E1R,EAASmR,EAAM,MAAM,EACrBQ,EAAM3R,EAAO,UAAW,GAAMgR,EAAY,YAAYY,CAAC,IAAMpC,CAAS,EAG5E,OAFImC,IAAQ,GAAI3R,EAAO,KAAKyR,CAAQ,EAC/B,EAAOE,GAAOF,EACZzR,CACR,CAEA,OAAe,eAAe,EAAmB,EAAmC,CACnF,IAAMsC,EAAO,IAAI0O,EAAYxB,CAAS,EAMtC,OALI3O,EAAkBF,CAAK,EAC1B2B,EAAK,oBAAoB3B,CAAK,EAE9B2B,EAAK,WAAW3B,CAAK,EAEf2B,CACR,CAOA,OAAc,QACb,EACA,EAC4C,CAC5C,IAAMA,EAAO6O,EAAM,KAAM,GAAMH,EAAY,YAAYY,CAAC,IAAMpC,CAAS,EACvE,GAAI,CAAClN,EAAM,OAAO,KAElB,IAAMuP,kBAAW,IAAI,IACfC,EAAQpX,EAAU4H,EAAM,WAAW,EACzC,GAAI,CAACwP,GAAS,OAAOA,GAAU,SAAU,OAAOD,EAEhD,IAAK,GAAM,CAACxV,EAAMoG,KAAU,OAAO,QAAQqP,CAAK,EAC1C,MAAM,QAAQrP,CAAK,GACxBoP,EAAS,IACRxV,EACAoG,EAAM,IAAK,GACVC,GAAM,OAAS,IAAA,GAAsD,KAA1CsO,EAAY,iBAAiBtO,EAAK,IAAI,CAClE,CACD,EAED,OAAOmP,CACR,CASA,OAAc,aAAa,EAAmB,EAAyC,CACtF,IAAMvP,EAAO0O,EAAY,QAAQG,EAAO3B,CAAS,EACjD,GAAI,CAAClN,GAAQA,EAAK,OAAS,EAAG,OAAO,KACrC,GAAIA,EAAK,KAAO,EACf,MAAM,IAAI3H,EACT,IAAI6U,EAAU,QAAQlN,EAAK,KAAK,6CAChC1H,EAAW,cACX,CAAE,QAAS,CAAE,YAAW,YAAa0H,EAAK,IAAK,CAAE,CAClD,EAGD,GAAM,CAACG,GAASH,EAAK,OAAO,EAE5B,OADIG,EAAM,SAAW,EAAU,KACxBA,EAAM,SAAW,EAAIA,EAAM,GAAKA,CACxC,CAEA,OAAe,YAAY,EAAyB,CACnD,OAAO/H,EAAU4H,EAAM,WAAW,CACnC,CAQA,OAAc,gBAAgB,EAA2B,CACxD,IAAMyC,EAAQkM,EAAQ,MAAMrQ,CAAY,EACxC,GAAI,CAACmE,EACJ,MAAM,IAAIpK,EACT,mCAAmCsW,EAAQ,sFAE3CrW,EAAW,cACX,CAAE,QAAS,CAAE,SAAQ,CAAE,CACxB,EAGD,OADKmK,EAAM,GACJA,EAAM,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM,EADf,CAAC,CAExB,CAGA,OAAc,iBAAiB,EAA8B,CAC5D,MAAO,IAAI1I,EAAK,KAAK,GAAG,EAAE,EAC3B,CAGA,OAAe,eAAe,EAAiD,CAO9E,OANI,OAAOsE,GAAU,WACjB,OAAOA,GAAU,UACjB,OAAOA,GAAU,SAAiBA,EAClC,OAAOA,GAAU,UAAYA,EACzB,KAAK,UAAUA,CAAK,EAErB,OAAOA,CAAK,CACpB,CAEA,OAAe,iBAAiB,EAA8B,CAC7D,GAAI,OAAOgC,GAAS,SAAU,OAAOA,EAErC,GAAIA,EAAK,WAAW,GAAG,GAAKA,EAAK,WAAW,GAAG,EAC9C,GAAI,CACH,OAAO,KAAK,MAAMA,CAAI,CACvB,MAAQ,CACP,OAAOA,CACR,CAQD,IAAME,EAAM,OAAOF,CAAI,EAMvB,OALI,OAAO,SAASE,CAAG,GAAK,OAAOA,CAAG,IAAMF,EACpCE,EAEJF,IAAS,QACTA,IAAS,SACNA,CACR,CAEA,OAAe,cAAc,EAAyB,CAMrD,OALIhC,GAAiC,KAAa,GAC9C,OAAOA,GAAU,UAErB,EADI,MAAM,QAAQA,CAAK,GAAKA,EAAM,SAAW,GACzC,OAAOA,GAAU,UAAY,CAAC,MAAM,QAAQA,CAAK,GAAK,OAAO,KAAKA,CAAK,CAAC,CAAC,SAAW,EAGzF,CAEA,OAAe,WACd,EACA,EACA,EACA,EACS,CACT,IAAIX,EAASW,EAWb,OATIoR,GAAQ,MAA6B/R,EAAS+R,IACjDjX,EAAU,CAAC,CAAC,KAAK,GAAGiH,EAAU,IAAIpB,EAAM,aAAaoR,EAAI,WAAW,EACpE,EAASA,GAENC,GAAQ,MAA6BhS,EAASgS,IACjDlX,EAAU,CAAC,CAAC,KAAK,GAAGiH,EAAU,IAAIpB,EAAM,aAAaqR,EAAI,WAAW,EACpE,EAASA,GAGHhS,CACR,CACD,E"}
|
|
1
|
+
{"version":3,"file":"grasshopper.js","names":[],"sources":["../src/grasshopper/server/compute-server-stats.ts","../src/grasshopper/solve.ts","../src/core/utils/set-own-field.ts","../src/grasshopper/data-tree/tree-path.ts","../src/grasshopper/io/wire-item-types.ts","../src/grasshopper/io/input/transformers.ts","../src/grasshopper/io/input/normalize-default.ts","../src/grasshopper/io/input/numeric-rounding.ts","../src/grasshopper/io/input/input-type-parsers.ts","../src/grasshopper/io/input/input-processors.ts","../src/grasshopper/io/normalize-schema.ts","../src/grasshopper/io/definition-io.ts","../src/grasshopper/scheduler/response-cache.ts","../src/core/utils/sha256.ts","../src/grasshopper/scheduler/stable-hash.ts","../src/grasshopper/scheduler/solve-scheduler.ts","../src/grasshopper/client/grasshopper-client.ts","../src/grasshopper/io/output/rhino-objects.ts","../src/grasshopper/io/output/rhino-decoder.ts","../src/grasshopper/io/output/response-processors.ts","../src/grasshopper/client/grasshopper-response-processor.ts","../src/grasshopper/io/schema-endpoint.ts","../src/grasshopper/io/normalize-ui-schema.ts","../src/grasshopper/data-tree/data-tree.ts"],"sourcesContent":["import { ComputeError, ErrorCodes } from '@/core/errors';\nimport { getLogger } from '@/core/utils/logger';\nimport { tryParseJson } from '@/core/utils/try-parse-json';\nimport { validateServerUrl } from '@/core/server/validate-server-url';\nimport { buildAuthHeaders, withJsonContentType } from '@/core/compute-fetch/request';\nimport { readBodyText } from '@/core/compute-fetch/read-body';\nimport { isRedirect } from '@/core/compute-fetch/response';\nimport { composeSignal } from '@/core/compute-fetch/signal';\nimport type { ComputeAuth } from '@/core/types';\n\nexport interface ServerVersion {\n\trhino: string;\n\tcompute: string;\n\tgit_sha: string | null;\n}\n\n/** What `GET /limits` reports. Only the VektorNode fork answers it. */\nexport interface ServerLimits {\n\t/** Longest the server lets one request run (`RHINO_COMPUTE_TIMEOUT`, `--timeout`). */\n\ttimeoutSeconds: number;\n\t/** Largest request body it accepts (`RHINO_COMPUTE_MAX_REQUEST_SIZE`, `--max-request-size`). */\n\tmaxRequestBytes: number;\n\t/** A stale definition pointer answers `definition_not_cached`, so pointer reuse is safe. */\n\tsignalsStaleDefinition: boolean;\n}\n\nexport interface ServerStats {\n\tisOnline: boolean;\n\tversion?: ServerVersion;\n\tactiveChildren?: number;\n}\n\n/**\n * Server health and control: liveness, version, child processes, cache purges.\n *\n * @example\n * ```typescript\n * const stats = new ComputeServerStats('http://localhost:6500', 'your-api-key');\n *\n * try {\n * const isOnline = await stats.isServerOnline();\n * const children = await stats.getActiveChildren();\n * const version = await stats.getVersion();\n *\n * // Or get everything at once\n * const allStats = await stats.getServerStats();\n * } finally {\n * await stats.dispose(); // Clean up resources\n * }\n * ```\n */\nexport default class ComputeServerStats {\n\tprivate readonly serverUrl: string;\n\tprivate readonly headers: Record<string, string>;\n\tprivate disposed = false;\n\tprivate activeMonitors: Set<() => void> = new Set();\n\n\t/** Timeout (ms) for the fast read/monitoring endpoints. */\n\tprivate static readonly DEFAULT_TIMEOUT_MS = 5000;\n\n\t/** Timeout (ms) for child-lifecycle POSTs — a cold Windows child can take ~30s to spawn. */\n\tprivate static readonly LIFECYCLE_TIMEOUT_MS = 60_000;\n\n\t/** Floor for `monitor()`'s `intervalMs` — anything lower hot-loops the server. */\n\tprivate static readonly MIN_MONITOR_INTERVAL_MS = 100;\n\n\t/** Every control-plane answer is a count, a version or a short map. */\n\tprivate static readonly MAX_BODY_BYTES = 1 << 20;\n\n\t/**\n\t * @param serverUrl - Base URL of the Rhino Compute server with http:// or https:// scheme (e.g., 'http://localhost:6500')\n\t * @param auth - An API key, or the same auth fields a solve's `ComputeConfig` carries\n\t * (`apiKey`, `apiKeyHeader`, `authToken`, `headers`).\n\t */\n\tconstructor(serverUrl: string, auth?: string | ComputeAuth) {\n\t\tthis.serverUrl = validateServerUrl(serverUrl);\n\t\tthis.headers = buildAuthHeaders(typeof auth === 'string' ? { apiKey: auth } : (auth ?? {}));\n\t}\n\n\t/**\n\t * Aborts after `timeoutMs` so a hung connection can't stall a probe or `monitor()`; `0` disables\n\t * it. Redirects are not followed: fetch would carry the API key header to the target.\n\t */\n\tprivate async fetchWithTimeout(\n\t\turl: string,\n\t\tinit: RequestInit = {},\n\t\ttimeoutMs: number = ComputeServerStats.DEFAULT_TIMEOUT_MS\n\t): Promise<Response> {\n\t\tconst { signal, cleanup } = composeSignal(init.signal ?? undefined, timeoutMs);\n\t\ttry {\n\t\t\treturn await fetch(url, {\n\t\t\t\t...init,\n\t\t\t\t// Only a POST: on a GET the header forces a CORS preflight and describes no body.\n\t\t\t\theaders: init.method === 'POST' ? withJsonContentType(this.headers) : this.headers,\n\t\t\t\tredirect: 'manual',\n\t\t\t\tsignal\n\t\t\t});\n\t\t} finally {\n\t\t\tcleanup();\n\t\t}\n\t}\n\n\t/**\n\t * The response body of `path`, or `null` (logged) on a non-2xx or a network\n\t * failure. Throws only when disposed.\n\t */\n\tprivate async getText(\n\t\tpath: string,\n\t\tinit: RequestInit = {},\n\t\ttimeoutMs: number = ComputeServerStats.DEFAULT_TIMEOUT_MS\n\t): Promise<string | null> {\n\t\tthis.ensureNotDisposed();\n\t\tconst label = `${init.method ?? 'GET'} ${path}`;\n\t\ttry {\n\t\t\tconst response = await this.fetchWithTimeout(`${this.serverUrl}${path}`, init, timeoutMs);\n\t\t\tif (!response.ok) {\n\t\t\t\tgetLogger().warn(`[ComputeServerStats] ${label} failed:`, response.status);\n\t\t\t\tawait response.body?.cancel().catch(() => {});\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst { text, truncated } = await readBodyText(response, ComputeServerStats.MAX_BODY_BYTES);\n\t\t\tif (truncated) {\n\t\t\t\tgetLogger().warn(`[ComputeServerStats] ${label} answered with an oversized body`);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn text;\n\t\t} catch (err) {\n\t\t\tgetLogger().warn(`[ComputeServerStats] ${label} errored:`, err);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Check if the server is online.\n\t *\n\t * This is a single-sample probe: it returns `true` only on a 2xx or a redirect\n\t * from the proxy liveness root `/`, and `false` for every other outcome (4xx/5xx,\n\t * network error, or timeout). A cold or briefly-busy-but-up server can therefore\n\t * read as offline — callers that gate on this (e.g. client construction)\n\t * should retry rather than treat a single `false` as authoritative.\n\t *\n\t * @param timeoutMs - Abort the probe after this many ms (default: 5000).\n\t * Pass `0` to disable the timeout. Prevents a hung connection from\n\t * stalling the caller indefinitely.\n\t */\n\tpublic async isServerOnline(\n\t\ttimeoutMs: number = ComputeServerStats.DEFAULT_TIMEOUT_MS\n\t): Promise<boolean> {\n\t\treturn (await this.probeServer(timeoutMs)).online;\n\t}\n\n\t/**\n\t * Detailed liveness probe backing {@link isServerOnline}.\n\t *\n\t * Reports what the probe actually saw so callers can distinguish \"connection\n\t * failed\" (`error` set) from \"server answered non-2xx\" (`status` set) — e.g.\n\t * a 401 from a proxy that requires an API key is a misconfiguration, not an\n\t * offline server. Same single-sample caveat as {@link isServerOnline}.\n\t *\n\t * @param timeoutMs - Abort the probe after this many ms (default: 5000).\n\t * Pass `0` to disable the timeout.\n\t * @param signal - Cancels the probe, which then reports the abort as its `error`.\n\t * @returns `online` plus either the HTTP `status` the server answered with,\n\t * or the `error` the connection attempt failed with.\n\t */\n\tpublic async probeServer(\n\t\ttimeoutMs: number = ComputeServerStats.DEFAULT_TIMEOUT_MS,\n\t\tsignal?: AbortSignal\n\t): Promise<{ online: boolean; status?: number; error?: string }> {\n\t\tthis.ensureNotDisposed();\n\n\t\t// The rhino.compute proxy has no `/healthcheck` route; its real liveness\n\t\t// signal is `GET /`, which returns \"compute.rhino3d running\". Probing an\n\t\t// unknown path would instead be forwarded to a child and only tells us the\n\t\t// proxy can reach one. Some builds redirect `/` to rhino3d.com instead:\n\t\t// answering with a redirect is alive.\n\t\tconst url = `${this.serverUrl}/`;\n\n\t\ttry {\n\t\t\tconst response = await this.fetchWithTimeout(url, { method: 'GET', signal }, timeoutMs);\n\t\t\t// Unread, the body would hold its connection until garbage collection.\n\t\t\tawait response.body?.cancel().catch(() => {});\n\t\t\treturn { online: response.ok || isRedirect(response), status: response.status };\n\t\t} catch (err) {\n\t\t\tconst error = describeProbeError(err);\n\t\t\tgetLogger().debug(`[ComputeServerStats] Fetch error: ${error}`);\n\t\t\treturn { online: false, error };\n\t\t}\n\t}\n\n\t/**\n\t * Get the number of active child processes on the server.\n\t *\n\t * By default the proxy's `/activechildren` endpoint will *spawn* children up\n\t * to the configured count if none are running, then report the count — which\n\t * wakes (and bills) an idle server. Pass `{ initialize: false }` for a passive\n\t * read that reports the current count without spawning; use this for\n\t * monitoring or before a purge/probe where you must not wake the server.\n\t *\n\t * @param options.initialize - When `false`, append `?initialize=false` so the\n\t * server reports without spawning. Defaults to `true` (the server's default).\n\t * @returns Number of active children, or null if unavailable\n\t */\n\tpublic async getActiveChildren(options: { initialize?: boolean } = {}): Promise<number | null> {\n\t\tconst { initialize = true } = options;\n\t\t// `initialize` may spawn children before answering: give it the lifecycle budget.\n\t\tconst body = initialize\n\t\t\t? await this.getText('/activechildren', {}, ComputeServerStats.LIFECYCLE_TIMEOUT_MS)\n\t\t\t: await this.getText('/activechildren?initialize=false');\n\t\tif (body === null) return null;\n\n\t\t// Strict: `parseInt` would read an error page starting \"302 Found\" as 302 children.\n\t\tconst text = body.trim();\n\t\tif (!/^\\d+$/.test(text)) {\n\t\t\tgetLogger().warn('[ComputeServerStats] /activechildren answered with a non-numeric body');\n\t\t\treturn null;\n\t\t}\n\t\treturn parseInt(text, 10);\n\t}\n\n\t/**\n\t * The server's request limits, or `null` when it can't report them: a standard rhino.compute\n\t * has no `/limits`, and neither does an older fork. A missing route is expected, so it isn't\n\t * logged as a failure.\n\t */\n\tpublic async getLimits(): Promise<ServerLimits | null> {\n\t\tthis.ensureNotDisposed();\n\t\tlet text: string;\n\t\ttry {\n\t\t\tconst response = await this.fetchWithTimeout(`${this.serverUrl}/limits`);\n\t\t\tif (!response.ok) {\n\t\t\t\tawait response.body?.cancel().catch(() => {});\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst read = await readBodyText(response, ComputeServerStats.MAX_BODY_BYTES);\n\t\t\tif (read.truncated) return null;\n\t\t\ttext = read.text;\n\t\t} catch (err) {\n\t\t\tgetLogger().debug(`[ComputeServerStats] GET /limits errored: ${describeProbeError(err)}`);\n\t\t\treturn null;\n\t\t}\n\t\tconst json = tryParseJson(text) as Partial<ServerLimits> | null;\n\t\tconst positive = (n: unknown): n is number =>\n\t\t\ttypeof n === 'number' && Number.isFinite(n) && n > 0;\n\t\tif (!json || !positive(json.timeoutSeconds) || !positive(json.maxRequestBytes)) return null;\n\t\treturn {\n\t\t\ttimeoutSeconds: json.timeoutSeconds,\n\t\t\tmaxRequestBytes: json.maxRequestBytes,\n\t\t\tsignalsStaleDefinition: json.signalsStaleDefinition === true\n\t\t};\n\t}\n\n\t/**\n\t * Get the server version information.\n\t *\n\t * @returns Version object with rhino, compute, and git_sha, or null if unavailable\n\t */\n\tpublic async getVersion(): Promise<ServerVersion | null> {\n\t\tconst text = await this.getText('/version');\n\t\tif (text === null) return null;\n\n\t\t// A non-JSON body is a bare version string.\n\t\tconst json = tryParseJson(text) as {\n\t\t\trhino?: string;\n\t\t\tcompute?: string;\n\t\t\tgit_sha?: string | null;\n\t\t} | null;\n\t\tif (json == null) return { rhino: text, compute: '', git_sha: null };\n\t\treturn { rhino: json.rhino ?? '', compute: json.compute ?? '', git_sha: json.git_sha ?? null };\n\t}\n\n\t/**\n\t * Get the plugins installed on the server.\n\t *\n\t * Returns a `name → version` map of non-core plugins the server has loaded,\n\t * or `null` if the request failed. Pass `kind` to choose which inventory:\n\t * `'gh'` (default) lists Grasshopper add-on assemblies via\n\t * `/plugins/gh/installed`; `'rhino'` lists Rhino plugins via\n\t * `/plugins/rhino/installed`. Plugins that ship with Rhino / are core\n\t * libraries are excluded by the server.\n\t *\n\t * @param kind - `'gh'` for Grasshopper add-ons (default) or `'rhino'` for Rhino plugins.\n\t * @returns Map of plugin name to version, or `null` on failure.\n\t *\n\t * @example\n\t * ```ts\n\t * const gh = await stats.getInstalledPlugins(); // Grasshopper add-ons\n\t * const selvaVersion = gh?.['Selva'] ?? null;\n\t * ```\n\t */\n\tpublic async getInstalledPlugins(\n\t\tkind: 'gh' | 'rhino' = 'gh'\n\t): Promise<Record<string, string> | null> {\n\t\tconst text = await this.getText(`/plugins/${kind}/installed`);\n\t\tconst json = text === null ? undefined : tryParseJson(text);\n\t\treturn json && typeof json === 'object' ? (json as Record<string, string>) : null;\n\t}\n\n\t/**\n\t * Get comprehensive server statistics.\n\t * Fetches all available server information in parallel.\n\t *\n\t * @returns Object containing server status and available stats\n\t */\n\tpublic async getServerStats(): Promise<ServerStats> {\n\t\tthis.ensureNotDisposed();\n\n\t\tconst isOnline = await this.isServerOnline();\n\n\t\tif (!isOnline) {\n\t\t\treturn { isOnline: false };\n\t\t}\n\n\t\t// Passive child count — never spawn from a stats read, or merely viewing\n\t\t// server health would wake (and bill) an idle server.\n\t\tconst [version, activeChildren] = await Promise.all([\n\t\t\tthis.getVersion(),\n\t\t\tthis.getActiveChildren({ initialize: false })\n\t\t]);\n\n\t\treturn {\n\t\t\tisOnline: true,\n\t\t\t...(version && { version }),\n\t\t\t...(activeChildren !== null && { activeChildren })\n\t\t};\n\t}\n\n\t/**\n\t * Purge the server's solve-results / URL-data cache.\n\t *\n\t * POSTs to `cache/purge` and returns the number of entries removed, or `null`\n\t * if the request failed. This clears cached solve responses and fetched\n\t * definition-URL data; it does NOT evict the definition cache (active\n\t * `pointer` references stay valid).\n\t *\n\t * **Caveat:** `cache/purge` is forwarded by the rhino.compute proxy to a\n\t * single round-robin-selected child, so in a multi-child deployment one call\n\t * purges one child's cache. Call repeatedly (or size the pool to 1) if you\n\t * need a fleet-wide purge.\n\t *\n\t * @returns Number of entries removed, or `null` on failure.\n\t *\n\t * @example\n\t * ```ts\n\t * const removed = await stats.purgeCache();\n\t * if (removed !== null) console.log(`Purged ${removed} cached solves`);\n\t * ```\n\t */\n\tpublic async purgeCache(): Promise<number | null> {\n\t\tconst text = await this.getText('/cache/purge', { method: 'POST' });\n\t\tconst json = text === null ? undefined : tryParseJson(text);\n\t\tconst purged = (json as { purged?: unknown } | null | undefined)?.purged;\n\t\treturn typeof purged === 'number' ? purged : null;\n\t}\n\n\t/**\n\t * Best-effort fleet-wide cache purge across a multi-child deployment.\n\t *\n\t * A single {@link purgeCache} POST is forwarded by the rhino.compute proxy to\n\t * just ONE round-robin-selected child, so the other children keep serving\n\t * stale cached solves. There is no proxy endpoint that addresses children\n\t * individually, so this method reads the active child count (passively, never\n\t * spawning) and fires `2 × count` sequential purges, relying on the proxy's\n\t * round-robin to spread the hits across the pool.\n\t *\n\t * **This is best-effort, not a guarantee.** Round-robin can revisit one child\n\t * and skip another; under concurrent traffic the rotation drifts. The result's\n\t * `confident` flag is `true` only when the server reports a single child (where\n\t * one purge is exact) — surface it so callers don't over-promise. For a hard\n\t * fleet-wide guarantee, run the deployment at `--childcount 1` or add a\n\t * server-side fan-out endpoint.\n\t *\n\t * @returns `{ totalPurged, calls, children, confident }`, or `null` if the\n\t * child count couldn't be read (server unreachable). `totalPurged` sums the\n\t * per-call counts; `calls` is how many purges were issued; `children` is the\n\t * reported pool size; `confident` is `true` only at a single-child pool.\n\t *\n\t * @example\n\t * ```ts\n\t * const r = await stats.purgeAllChildren();\n\t * if (r && !r.confident) {\n\t * console.warn(`Purged ~${r.totalPurged} across ${r.children} children (best-effort)`);\n\t * }\n\t * ```\n\t */\n\tpublic async purgeAllChildren(): Promise<{\n\t\ttotalPurged: number;\n\t\tcalls: number;\n\t\tchildren: number;\n\t\tconfident: boolean;\n\t} | null> {\n\t\tthis.ensureNotDisposed();\n\n\t\t// Passive read — must not spawn children just to purge them.\n\t\tconst children = await this.getActiveChildren({ initialize: false });\n\t\tif (children === null) {\n\t\t\tgetLogger().warn('[ComputeServerStats] purgeAllChildren: could not read child count');\n\t\t\treturn null;\n\t\t}\n\n\t\tif (children === 0) {\n\t\t\t// No live children means nothing is cached on the compute side.\n\t\t\treturn { totalPurged: 0, calls: 0, children: 0, confident: true };\n\t\t}\n\n\t\t// 2× the pool size gives round-robin a strong chance of reaching every\n\t\t// child at least once. Sequential (not parallel) so the proxy advances its\n\t\t// round-robin cursor one child per call rather than racing them onto one.\n\t\tconst calls = children * 2;\n\t\tlet totalPurged = 0;\n\t\tfor (let i = 0; i < calls; i++) {\n\t\t\tconst purged = await this.purgeCache();\n\t\t\tif (purged !== null) totalPurged += purged;\n\t\t}\n\n\t\treturn { totalPurged, calls, children, confident: children === 1 };\n\t}\n\n\t/**\n\t * Get the server's current UTC clock.\n\t *\n\t * GETs `/servertime`, which the server emits as a JSON-encoded ISO-8601\n\t * timestamp (e.g. `\"2026-06-18T08:30:00Z\"`). Useful for detecting clock skew\n\t * between caller and server. Returns `null` if the request failed or the body\n\t * isn't a parseable date.\n\t *\n\t * @returns A `Date` for the server's UTC time, or `null` on failure.\n\t */\n\tpublic async getServerTime(): Promise<Date | null> {\n\t\tconst text = await this.getText('/servertime');\n\t\tif (text === null) return null;\n\t\t// The body is a JSON string: strip its quotes.\n\t\tconst date = new Date(text.trim().replace(/^\"|\"$/g, ''));\n\t\treturn isNaN(date.getTime()) ? null : date;\n\t}\n\n\t/**\n\t * Get how long the rhino.compute proxy has been idle.\n\t *\n\t * GETs `/idlespan` on the proxy, which returns the seconds elapsed since the\n\t * last request was forwarded to a compute child. This is a proxy-level metric\n\t * (not proxied to a child) used by autoscalers to decide when a node can be\n\t * drained. Returns `null` if unavailable.\n\t *\n\t * @returns Idle time in seconds, or `null` on failure.\n\t */\n\tpublic async getIdleSpan(): Promise<number | null> {\n\t\tconst text = await this.getText('/idlespan');\n\t\tif (text === null) return null;\n\t\tconst seconds = parseFloat(text.trim());\n\t\treturn isNaN(seconds) ? null : seconds;\n\t}\n\n\t/**\n\t * Fill the compute child pool up to the server's configured baseline.\n\t *\n\t * POSTs `/launch-children`. No-op when the pool is already at or above the\n\t * configured `--childcount`. Returns `{ spawned, active }` — how many children\n\t * were started and the resulting child count — or `null` on failure.\n\t *\n\t * To raise capacity above the baseline use {@link launchChild}; the baseline\n\t * itself can only be changed by restarting rhino.compute.\n\t *\n\t * @returns `{ spawned, active }`, or `null` on failure.\n\t */\n\tpublic async launchChildren(): Promise<{ spawned: number[]; active: number } | null> {\n\t\treturn this.postJson('/launch-children');\n\t}\n\n\t/**\n\t * Add a single compute child to the pool, optionally on a specific port.\n\t *\n\t * POSTs `/launch-child` (with `?port=N` when `port` is given). Unlike\n\t * {@link launchChildren}, this can push the pool above the baseline, up to the\n\t * server's `MaxChildren` cap. Returns `{ spawned: [port] }` on success, or\n\t * `null` on failure (server replies 400 for a bad port, 409 if the port is in\n\t * use, 503 at the max-children cap).\n\t *\n\t * @param port - Optional specific port to launch on; otherwise the next free one.\n\t * @returns `{ spawned }` listing the launched port, or `null` on failure.\n\t */\n\tpublic async launchChild(port?: number): Promise<{ spawned: number[] } | null> {\n\t\tconst path = port === undefined ? '/launch-child' : `/launch-child?port=${port}`;\n\t\treturn this.postJson(path);\n\t}\n\n\t/**\n\t * Gracefully shut down compute children without respawning them.\n\t *\n\t * POSTs `/shutdown-children`. With no `port` it shuts down every child; with\n\t * `port` it targets just that one. Children do not respawn, but the next\n\t * `/grasshopper` request auto-spawns the pool back to the baseline. Returns\n\t * `{ shutdown, active }` — how many were stopped and the remaining count — or\n\t * `null` on failure.\n\t *\n\t * @param port - Optional port to target; omit to shut down all children.\n\t * @returns `{ shutdown, active }`, or `null` on failure.\n\t */\n\tpublic async shutdownChildren(\n\t\tport?: number\n\t): Promise<{ shutdown: number; active: number } | null> {\n\t\tconst path = port === undefined ? '/shutdown-children' : `/shutdown-children?port=${port}`;\n\t\treturn this.postJson(path);\n\t}\n\n\t/**\n\t * Shut down compute children and respawn replacements (rolling restart).\n\t *\n\t * POSTs `/recycle-children`. With no `port` it recycles every child; with\n\t * `port` it recycles just that one. The server recycles sequentially (each\n\t * replacement is serving before the next child is stopped) so the pool never\n\t * drops to zero mid-recycle. Returns `{ shutdown, spawned, active }`, or\n\t * `null` on failure.\n\t *\n\t * @param port - Optional port to target; omit to recycle all children.\n\t * @returns `{ shutdown, spawned, active }`, or `null` on failure.\n\t */\n\tpublic async recycleChildren(\n\t\tport?: number\n\t): Promise<{ shutdown: number; spawned: number[]; active: number } | null> {\n\t\tconst path = port === undefined ? '/recycle-children' : `/recycle-children?port=${port}`;\n\t\treturn this.postJson(path);\n\t}\n\n\t/** A child-lifecycle POST: a spawn or recycle can take ~30 s. */\n\tprivate async postJson<T>(path: string): Promise<T | null> {\n\t\tconst text = await this.getText(\n\t\t\tpath,\n\t\t\t{ method: 'POST' },\n\t\t\tComputeServerStats.LIFECYCLE_TIMEOUT_MS\n\t\t);\n\t\treturn text === null ? null : ((tryParseJson(text) ?? null) as T | null);\n\t}\n\n\t/**\n\t * Continuously monitor server stats at specified interval.\n\t *\n\t * @param callback - Function called with stats on each interval\n\t * @param intervalMs - Milliseconds between checks (default: 5000). Must be a\n\t * finite number of at least 100 ms — lower values would hot-loop the server.\n\t * @returns Function to stop monitoring\n\t * @throws {ComputeError} `INVALID_CONFIG` if `intervalMs` is not a finite number >= 100.\n\t *\n\t * @example\n\t * ```typescript\n\t * const stopMonitoring = stats.monitor((data) => {\n\t * console.log('Server stats:', data);\n\t * }, 3000);\n\t *\n\t * // Later...\n\t * stopMonitoring();\n\t * ```\n\t */\n\tpublic monitor(callback: (stats: ServerStats) => void, intervalMs: number = 5000): () => void {\n\t\tthis.ensureNotDisposed();\n\n\t\tif (!Number.isFinite(intervalMs) || intervalMs < ComputeServerStats.MIN_MONITOR_INTERVAL_MS) {\n\t\t\t// ComputeError (not RangeError) so `err instanceof ComputeError` holds\n\t\t\t// across the package's whole public surface.\n\t\t\tthrow new ComputeError(\n\t\t\t\t`monitor() intervalMs must be a finite number >= ${ComputeServerStats.MIN_MONITOR_INTERVAL_MS}ms, ` +\n\t\t\t\t\t`got ${intervalMs}. Sub-${ComputeServerStats.MIN_MONITOR_INTERVAL_MS}ms polling would hot-loop the server.`,\n\t\t\t\tErrorCodes.INVALID_CONFIG,\n\t\t\t\t{ context: { intervalMs } }\n\t\t\t);\n\t\t}\n\n\t\tlet active = true;\n\t\tlet currentTimeoutId: ReturnType<typeof setTimeout> | null = null;\n\n\t\tgetLogger().info(`🔄 Starting server stats monitoring every ${intervalMs}ms`);\n\n\t\tconst check = async () => {\n\t\t\tcurrentTimeoutId = null;\n\t\t\tif (!active || this.disposed) return;\n\n\t\t\ttry {\n\t\t\t\tconst _stats = await this.getServerStats();\n\n\t\t\t\tif (!active || this.disposed) return;\n\n\t\t\t\ttry {\n\t\t\t\t\tcallback(_stats);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tgetLogger().error('[ComputeServerStats] Monitor callback threw:', err);\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tif (!active || this.disposed) {\n\t\t\t\t\t// dispose()/stop racing an in-flight poll is normal shutdown, not an\n\t\t\t\t\t// error — the nested ensureNotDisposed() throw is expected here.\n\t\t\t\t\tgetLogger().debug('[ComputeServerStats] Monitor poll cancelled during shutdown:', err);\n\t\t\t\t} else {\n\t\t\t\t\tgetLogger().error('[ComputeServerStats] Failed to fetch stats during monitor:', err);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (active && !this.disposed) {\n\t\t\t\tcurrentTimeoutId = setTimeout(() => void check(), intervalMs);\n\t\t\t}\n\t\t};\n\n\t\tconst stopMonitoring = () => {\n\t\t\tactive = false;\n\t\t\tif (currentTimeoutId !== null) {\n\t\t\t\tclearTimeout(currentTimeoutId);\n\t\t\t\tcurrentTimeoutId = null;\n\t\t\t}\n\n\t\t\tthis.activeMonitors.delete(stopMonitoring);\n\t\t};\n\n\t\tthis.activeMonitors.add(stopMonitoring);\n\n\t\tvoid check();\n\n\t\treturn stopMonitoring;\n\t}\n\n\t/**\n\t * Disposes of all resources and stops all active monitors.\n\t * Call this when you're done using the stats instance.\n\t */\n\tpublic async dispose(): Promise<void> {\n\t\tif (this.disposed) return;\n\n\t\tthis.disposed = true;\n\t\tfor (const stopMonitor of this.activeMonitors) {\n\t\t\tstopMonitor();\n\t\t}\n\t\tthis.activeMonitors.clear();\n\t}\n\n\tprivate ensureNotDisposed(): void {\n\t\tif (this.disposed) {\n\t\t\tthrow new ComputeError(\n\t\t\t\t'ComputeServerStats 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\n/**\n * undici reports every connection failure as `TypeError: fetch failed` and keeps what happened on\n * `cause`, whose message is empty when several addresses failed: its `code` is the one sure field.\n */\nfunction describeProbeError(err: unknown): string {\n\tif (!(err instanceof Error)) return String(err);\n\tconst summary = `${err.name}: ${err.message}`;\n\tif (!(err.cause instanceof Error)) return summary;\n\tconst { code } = err.cause as { code?: unknown };\n\treturn `${summary}: ${typeof code === 'string' ? `${code} ` : ''}${err.cause.message}`.trim();\n}\n","import { fetchCompute, validateNumbers } from '@/core/compute-fetch/compute-fetch';\nimport { composeSignal, raceSignal } from '@/core/compute-fetch/signal';\nimport { abortedByCaller, ComputeError, ErrorCodes } from '@/core/errors';\nimport type { ComputeConfig, ServerErrorCodeMap } from '@/core/types';\nimport { getResponseWireSize, setResponseWireSize } from '@/core/compute-fetch/wire-size';\nimport { base64ByteArray, detectBase64Payload, encodeStringToBase64 } from '@/core/utils/encoding';\nimport { getLogger } from '@/core/utils/logger';\nimport { readField } from '@/core/utils/read-field';\nimport { warnIfClientSide } from '@/core/utils/warnings';\nimport { validateServerUrl } from '@/core/server/validate-server-url';\n\nimport type {\n\tGrasshopperRequestSchema,\n\tGrasshopperComputeConfig,\n\tGrasshopperComputeResponse,\n\tDataTree\n} from './types';\nimport { isDefinitionRef, isDefinitionUrl, type SolveDefinition } from '@/core/definition-ref';\n\n/**\n * Server message for a `pointer` it can't resolve (evicted, or a different child in the pool).\n * Production servers scrub it, so the `definition_not_cached` code is the reliable signal; this\n * substring covers debug-mode servers and forks that emit no code.\n */\nconst DEFINITION_LOAD_FAILED = 'Unable to load grasshopper definition';\n\n/** `core/` knows no backend's wire codes, so every Grasshopper request passes these in. */\nconst GRASSHOPPER_SERVER_ERROR_CODES: ServerErrorCodeMap = {\n\tdefinition_not_cached: ErrorCodes.DEFINITION_NOT_CACHED\n};\n\n/** Attach the Grasshopper wire-code table without clobbering a caller's own entries. */\nexport function withGrasshopperErrorCodes<T extends ComputeConfig>(config: T): T {\n\treturn {\n\t\t...config,\n\t\tserverErrorCodes: { ...GRASSHOPPER_SERVER_ERROR_CODES, ...config.serverErrorCodes }\n\t};\n}\n\nfunction isDefinitionLoadMiss(error: unknown): boolean {\n\tif (!(error instanceof ComputeError)) return false;\n\treturn (\n\t\terror.code === ErrorCodes.DEFINITION_NOT_CACHED ||\n\t\terror.message.includes(DEFINITION_LOAD_FAILED)\n\t);\n}\n\n/**\n * Debug-only: names the outputs that produced nothing, usually a definition that didn't compute\n * (missing inputs, a guarded branch). An empty output can be legitimate, so this only warns.\n */\nexport function warnOnEmptyInnerTrees(response: GrasshopperComputeResponse, debug?: boolean): void {\n\tif (!debug) return;\n\n\tconst values = readField<unknown[]>(response, 'values');\n\tif (!Array.isArray(values) || values.length === 0) return;\n\n\tconst empty: string[] = [];\n\tfor (const param of values) {\n\t\tconst innerTree = readField<Record<string, unknown>>(param, 'innerTree');\n\t\tif (!innerTree || Object.keys(innerTree).length === 0) {\n\t\t\tempty.push(readField<string>(param, 'paramName') ?? '<unnamed>');\n\t\t}\n\t}\n\n\tif (empty.length === 0) return;\n\n\tconst scope = empty.length === values.length ? 'all' : `${empty.length}/${values.length}`;\n\tgetLogger().warn(\n\t\t`Solve returned empty output(s) (${scope}): ${empty.join(', ')}. ` +\n\t\t\t`These parameters produced no data — check the definition's inputs and the branch feeding each.`\n\t);\n}\n\n/**\n * `cacheKey` is the server's `pointer`: its `md5_…` key for an uploaded definition, or the URL\n * echoed back for a URL definition. `null` means only that the response carried no pointer.\n */\nexport interface SolveWithCacheKey {\n\tresponse: GrasshopperComputeResponse;\n\tcacheKey: string | null;\n}\n\n/**\n * Run one solve. `GrasshopperClient.solve()` adds input validation and throws on solver errors.\n *\n * @param definition - A URL (sent as a pointer), base64 `.gh` content, raw `.gh` bytes, or a\n * `DefinitionRef` (loaded for the upload). Any other string is encoded as the definition text.\n * @returns The compute response, without the server's `pointer` and echoed `algo`.\n *\n * @example\n * await solveGrasshopperDefinition(trees, 'https://example.com/definition.gh', config);\n * await solveGrasshopperDefinition(trees, new Uint8Array(await readFile('model.gh')), config);\n */\nexport async function solveGrasshopperDefinition(\n\tdataTree: DataTree[],\n\tdefinition: SolveDefinition,\n\tconfig: GrasshopperComputeConfig\n): Promise<GrasshopperComputeResponse> {\n\twarnIfClientSide('solveGrasshopperDefinition', config.suppressBrowserWarning);\n\t// The transport checks the URL's shape only; which hosts to block is decided here.\n\tvalidateServerUrl(config.serverUrl);\n\treturn (await uploadAndSolve(dataTree, definition, config)).response;\n}\n\n/**\n * Solve by `pointer: cacheKey` when a key is known, skipping the upload. With no key, or when the\n * server has evicted it, upload the full definition and report `missed: true`. A `DefinitionRef`\n * is loaded only on that upload path.\n */\nexport async function solveWithServerCacheKey(\n\tdataTree: DataTree[],\n\tcacheKey: string | null,\n\tdefinition: SolveDefinition,\n\tconfig: GrasshopperComputeConfig\n): Promise<SolveWithCacheKey & { missed: boolean }> {\n\twarnIfClientSide('solveWithServerCacheKey', config.suppressBrowserWarning);\n\n\tif (cacheKey !== null) {\n\t\ttry {\n\t\t\tconst hit = await runSolve({ algo: null, pointer: cacheKey, values: dataTree }, config);\n\t\t\treturn { ...hit, missed: false };\n\t\t} catch (error) {\n\t\t\tif (!isDefinitionLoadMiss(error)) throw error;\n\t\t}\n\t}\n\treturn { ...(await uploadAndSolve(dataTree, definition, config)), missed: true };\n}\n\nasync function uploadAndSolve(\n\tdataTree: DataTree[],\n\tdefinition: SolveDefinition,\n\tconfig: GrasshopperComputeConfig\n): Promise<SolveWithCacheKey> {\n\tconst bytes = await materializeDefinition(definition, config);\n\treturn runSolve(prepareGrasshopperArgs(bytes, dataTree), config);\n}\n\nasync function materializeDefinition(\n\tdefinition: SolveDefinition,\n\tconfig: GrasshopperComputeConfig\n): Promise<string | Uint8Array> {\n\tif (!isDefinitionRef(definition)) return definition;\n\tvalidateNumbers(config);\n\tconst { signal, cleanup } = composeSignal(config.signal, config.timeoutMs);\n\ttry {\n\t\t// Raced, not just passed on: a `load()` that ignores the signal must not pin a scheduler slot.\n\t\treturn await raceSignal(definition.load(signal), signal);\n\t} catch (error) {\n\t\tif (config.signal?.aborted) {\n\t\t\tthrow abortedByCaller({ definitionKey: definition.key }, error);\n\t\t}\n\t\tif (signal?.aborted) {\n\t\t\tthrow new ComputeError(\n\t\t\t\t`Loading definition '${definition.key}' timed out after ${config.timeoutMs}ms`,\n\t\t\t\tErrorCodes.TIMEOUT_ERROR,\n\t\t\t\t{ context: { definitionKey: definition.key, timeoutMs: config.timeoutMs } }\n\t\t\t);\n\t\t}\n\t\tthrow new ComputeError(\n\t\t\t`Failed to load definition bytes for ref '${definition.key}'`,\n\t\t\tErrorCodes.INVALID_INPUT,\n\t\t\t{\n\t\t\t\tcontext: { definitionKey: definition.key },\n\t\t\t\toriginalError: error\n\t\t\t}\n\t\t);\n\t} finally {\n\t\tcleanup();\n\t}\n}\n\n/**\n * POST a solve and split the server's `pointer` off the response. The server echoes the request's\n * base64 definition back as `algo`; it is dropped so a cached response doesn't pin a multi-MB copy\n * of the definition against the scheduler's byte budget.\n */\nasync function runSolve(\n\targs: GrasshopperRequestSchema,\n\tconfig: GrasshopperComputeConfig\n): Promise<SolveWithCacheKey> {\n\tapplyOptionalComputeSettings(args, config);\n\n\t// The server echoes an uploaded definition back as `algo`: the limit is for the rest.\n\tconst { maxResponseBytes } = config;\n\tconst result = await fetchCompute<GrasshopperComputeResponse>(\n\t\t'grasshopper',\n\t\targs,\n\t\twithGrasshopperErrorCodes({\n\t\t\t...config,\n\t\t\tmaxResponseBytes:\n\t\t\t\tmaxResponseBytes !== undefined && args.algo\n\t\t\t\t\t? maxResponseBytes + args.algo.length\n\t\t\t\t\t: maxResponseBytes\n\t\t})\n\t);\n\n\tassertResponseObject(result);\n\tconst {\n\t\tpointer,\n\t\talgo: _algo,\n\t\t...rest\n\t} = result as GrasshopperComputeResponse & { pointer?: unknown; algo?: unknown };\n\t// A warnings-only response can arrive without `values`: every caller gets the array the type\n\t// promises. Read through `readField`, so a differently cased key isn't shadowed by this default.\n\tconst response = (\n\t\treadField(rest, 'values') ? rest : { ...rest, values: [] }\n\t) as GrasshopperComputeResponse;\n\t// The wire-size hint is keyed by object identity, so the copy re-registers it, minus `algo`.\n\tconst wireSize = getResponseWireSize(result);\n\tif (wireSize !== undefined) {\n\t\tconst algoLength = typeof _algo === 'string' ? _algo.length : 0;\n\t\tsetResponseWireSize(response, Math.max(0, wireSize - algoLength));\n\t}\n\twarnOnEmptyInnerTrees(response, config.debug);\n\treturn {\n\t\tresponse,\n\t\tcacheKey: typeof pointer === 'string' ? pointer : null\n\t};\n}\n\n/** A 200 can carry valid JSON that isn't an object: `null` from a proxy, or a bare string. */\nexport function assertResponseObject(response: unknown): asserts response is object {\n\tif (!response || typeof response !== 'object') {\n\t\tthrow new ComputeError('Invalid response structure', ErrorCodes.INVALID_RESPONSE, {\n\t\t\tcontext: { responseType: response === null ? 'null' : typeof response }\n\t\t});\n\t}\n}\n\n// ============================================================================\n// Grasshopper Arguments\n// ============================================================================\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\targs.algo = base64ByteArray(definition);\n\t} else if (isDefinitionUrl(definition)) {\n\t\targs.pointer = definition;\n\t} else {\n\t\t// Only long base64 passes through (see detectBase64Payload); a short base64-shaped string\n\t\t// like \"test\" is encoded as text. A Uint8Array skips the sniffing.\n\t\targs.algo = detectBase64Payload(definition) ?? encodeStringToBase64(definition);\n\t}\n\n\treturn args;\n}\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\tif (options.selvaevents != null) arglist.selvaevents = options.selvaevents;\n}\n","/**\n * `obj[key] = value` for a key taken from a wire payload. Assigning `__proto__` would call the\n * prototype setter and swap the object's prototype instead of storing the value.\n */\nexport function setOwnField(obj: object, key: string, value: unknown): void {\n\tif (key === '__proto__') {\n\t\tObject.defineProperty(obj, key, {\n\t\t\tvalue,\n\t\t\twritable: true,\n\t\t\tenumerable: true,\n\t\t\tconfigurable: true\n\t\t});\n\t} else {\n\t\t(obj as Record<string, unknown>)[key] = value;\n\t}\n}\n","import type { DataTreeDefault } from '../types';\n\n/**\n * Canonical matcher for a Grasshopper branch path key like `{0}`, `{0;1}`, or\n * the root path `{}`. This is the single source of truth for the branch-path\n * shape (`DataTreePath`); anything testing \"does this string name a branch?\"\n * should use this rather than re-inlining the regex.\n *\n * Matches `{}`, `{0}`, `{0;1;2}`, and negative indices like `{-1;2}` (GH_Path\n * allows them) — but NOT empty segments like `{0;}` / `{;}` / `{0;;1}`, which\n * would otherwise split to phantom 0s (`Number('') === 0`).\n */\nexport const TREE_PATH_RE = /^\\{(-?\\d+(?:;-?\\d+)*)?\\}$/;\n\n/**\n * Membership test for a {@link DataTreeDefault}: an object keyed entirely by\n * branch paths, each mapping to an array of values. This is the one predicate\n * both the input-type parsers (to pass a tree-access default through untouched)\n * and `TreeBuilder` (to dispatch it to `fromDataTreeDefault`) ask — so the two\n * agree by construction on exactly which values are trees.\n */\nexport function isDataTreeDefault(value: unknown): value is DataTreeDefault {\n\tif (typeof value !== 'object' || value === null || Array.isArray(value)) return false;\n\tconst entries = Object.entries(value);\n\treturn (\n\t\tentries.length > 0 &&\n\t\tentries.every(([key, val]) => TREE_PATH_RE.test(key) && Array.isArray(val))\n\t);\n}\n","export const STRING_ITEM_TYPE = 'System.String';\nexport const BOOLEAN_ITEM_TYPE = 'System.Boolean';\n\n/** Every numeric CLR type Grasshopper serializes, so no numeric item stays a string. */\nexport const NUMERIC_ITEM_TYPES: ReadonlySet<string> = new Set([\n\t'System.Double',\n\t'System.Single',\n\t'System.Decimal',\n\t'System.Int32',\n\t'System.Int64'\n]);\n\n/** Integral wire item types: rounded like the scalar Integer path. */\nexport const INTEGER_ITEM_TYPES: ReadonlySet<string> = new Set(['System.Int32', 'System.Int64']);\n","import { getLogger } from '@/core/utils/logger';\n\n/**\n * Coerce one raw default value to `T`, or return `null` when it can't be\n * coerced. Transformers never throw — the caller decides whether a `null`\n * is filtered (array items) or surfaced as a parse error (scalars).\n */\nexport type ValueTransformer<T> = (value: unknown) => T | null;\n\n/**\n * Coerce a schema's `default`: array items are transformed and the rejected ones\n * filtered (none left → `undefined`); a rejected scalar becomes `undefined` when\n * `dropInvalidScalar`, else is kept verbatim for the parser to report.\n */\nexport function coerceDefault<T>(\n\tvalue: unknown,\n\ttransform: ValueTransformer<T>,\n\tdropInvalidScalar: boolean\n): unknown {\n\tif (value === undefined || value === null) {\n\t\treturn value;\n\t}\n\n\tif (Array.isArray(value)) {\n\t\tconst processed = value.map(transform).filter((v): v is T => v !== null);\n\t\treturn processed.length > 0 ? processed : undefined;\n\t}\n\n\tconst transformed = transform(value);\n\tif (transformed !== null) {\n\t\treturn transformed;\n\t}\n\treturn dropInvalidScalar ? undefined : value;\n}\n\nexport const numericTransformer: ValueTransformer<number> = (value) => {\n\tif (typeof value === 'number') return Number.isFinite(value) ? value : null;\n\tif (typeof value === 'string') {\n\t\tconst trimmed = value.trim();\n\t\t// `Number('')` is 0, so reject empty/whitespace before coercing — an empty\n\t\t// default should drop to null, not silently become 0.\n\t\tif (trimmed === '') return null;\n\t\t// `Number()` also accepts '0x10' and 'Infinity'; a Grasshopper numeric\n\t\t// default holds neither.\n\t\tif (/^[+-]?0[xbo]/i.test(trimmed)) return null;\n\t\tconst parsed = Number(trimmed);\n\t\treturn Number.isFinite(parsed) ? parsed : null;\n\t}\n\treturn null;\n};\n\nexport const booleanTransformer: ValueTransformer<boolean> = (value) => {\n\tif (typeof value === 'boolean') return value;\n\tif (typeof value === 'string') {\n\t\tconst lower = value.trim().toLowerCase();\n\t\tif (lower === 'true') return true;\n\t\tif (lower === 'false') return false;\n\t}\n\treturn null;\n};\n\nexport const textTransformer: ValueTransformer<string> = (value) => {\n\tif (typeof value === 'string') {\n\t\tif (value.length >= 2 && value.startsWith('\"') && value.endsWith('\"'))\n\t\t\treturn value.slice(1, -1);\n\t\t// Unbalanced leading quote: strip only the quote, not the last character.\n\t\tif (value.startsWith('\"')) return value.slice(1);\n\t\treturn value;\n\t}\n\treturn null;\n};\n\nexport const colorTransformer: ValueTransformer<string> = (value) => {\n\tif (typeof value === 'string') {\n\t\tlet cleaned = value.trim();\n\t\tif (cleaned.startsWith('\"') && cleaned.endsWith('\"')) {\n\t\t\tcleaned = cleaned.slice(1, -1).trim();\n\t\t}\n\t\treturn cleaned;\n\t}\n\treturn null;\n};\n\nexport function objectTransformer(inputName: string): ValueTransformer<object> {\n\treturn (value) => {\n\t\tif (typeof value === 'object' && value !== null) return value;\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) return parsed;\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 {\n\t\t\t\tgetLogger().warn(`Failed to parse the object value for input ${inputName}`);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t};\n}\n","import { ErrorCodes } from '@/core/errors';\nimport { getLogger } from '@/core/utils/logger';\nimport { readField, hasField } from '@/core/utils/read-field';\nimport { setOwnField } from '@/core/utils/set-own-field';\nimport { isDataTreeDefault } from '../../data-tree/tree-path';\nimport {\n\tBOOLEAN_ITEM_TYPE,\n\tINTEGER_ITEM_TYPES,\n\tNUMERIC_ITEM_TYPES,\n\tSTRING_ITEM_TYPE\n} from '../wire-item-types';\nimport { booleanTransformer, numericTransformer, textTransformer } from './transformers';\nimport type { ParserWarning } from './input-type-parsers';\nimport type { InputParamSchema } from '../../types';\n\ninterface NormalizedDefault {\n\tschema: InputParamSchema;\n\twarning?: ParserWarning;\n}\n\nfunction dropDefault(input: InputParamSchema, message: string): NormalizedDefault {\n\tgetLogger().warn(message);\n\treturn {\n\t\tschema: { ...input, default: null },\n\t\twarning: { code: ErrorCodes.MALFORMED_DEFAULT, message }\n\t};\n}\n\n/**\n * Flatten a raw input's `default` DataTree into the shape the type parsers take.\n * Never mutates `input`. A default it can't interpret is dropped with a `warning`.\n */\nexport function normalizeDefault(input: InputParamSchema, label: string): NormalizedDefault {\n\tif (typeof input.default !== 'object' || input.default === null) {\n\t\treturn { schema: input };\n\t}\n\n\t// Arrays and branch-path maps are already flattened: the per-type parsers take\n\t// both as-is, so neither may fall into the no-innerTree branch below.\n\tif (Array.isArray(input.default) || isDataTreeDefault(input.default)) {\n\t\treturn { schema: input };\n\t}\n\n\tif (!hasField(input.default, 'innerTree')) {\n\t\treturn dropDefault(\n\t\t\tinput,\n\t\t\t`Input \"${label}\" default had an unrecognized shape (no innerTree key); the default was dropped.`\n\t\t);\n\t}\n\n\tconst innerTree = readField<Record<string, unknown>>(input.default, 'innerTree') ?? {};\n\tif (Object.keys(innerTree).length === 0) {\n\t\treturn { schema: { ...input, default: undefined } };\n\t}\n\n\tif (input.treeAccess || (input.atMost && input.atMost > 1)) {\n\t\t// Items go through the same transformers as scalar defaults.\n\t\tconst tree: Record<string, unknown[]> = {};\n\t\tconst invalidItems: string[] = [];\n\t\tfor (const [branch, items] of Object.entries(innerTree)) {\n\t\t\tif (!Array.isArray(items)) {\n\t\t\t\treturn dropDefault(\n\t\t\t\t\tinput,\n\t\t\t\t\t`Input \"${label}\" default had a non-array innerTree branch (\"${branch}\"); the default was dropped.`\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst parsed: unknown[] = [];\n\t\t\tfor (const item of items) {\n\t\t\t\tconst data = readField(item, 'data');\n\t\t\t\tconst type = readField<string>(item, 'type');\n\t\t\t\tif (type && NUMERIC_ITEM_TYPES.has(type)) {\n\t\t\t\t\t// Blank means \"no value\", as for a scalar default: dropped, never 0.\n\t\t\t\t\tif (typeof data === 'string' && data.trim() === '') continue;\n\t\t\t\t\tconst num = numericTransformer(data);\n\t\t\t\t\tif (num === null) {\n\t\t\t\t\t\tinvalidItems.push(`\"${String(data)}\" (${type})`);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tparsed.push(INTEGER_ITEM_TYPES.has(type) ? Math.round(num) : num);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (type === BOOLEAN_ITEM_TYPE) {\n\t\t\t\t\tconst bool = booleanTransformer(data);\n\t\t\t\t\tif (bool === null) {\n\t\t\t\t\t\tinvalidItems.push(`\"${String(data)}\" (${type})`);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tparsed.push(bool);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (type === STRING_ITEM_TYPE && typeof data === 'string') {\n\t\t\t\t\tparsed.push(textTransformer(data));\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// Only geometry is JSON-encoded on the wire. A `System.String` label like\n\t\t\t\t// `[1,2,3]` must stay a string: the fork's Newtonsoft rejects a non-string leaf.\n\t\t\t\tif (typeof data === 'string' && type?.startsWith('Rhino.Geometry')) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tparsed.push(JSON.parse(data));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tparsed.push(data);\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tparsed.push(data);\n\t\t\t}\n\t\t\tsetOwnField(tree, branch, parsed);\n\t\t}\n\n\t\t// The good values survive; the dropped ones are reported.\n\t\tif (invalidItems.length > 0) {\n\t\t\tconst message = `Input \"${label}\" default contained ${invalidItems.length} tree value(s) that could not be parsed and were dropped: ${invalidItems.join(', ')}.`;\n\t\t\t// The values go to the caller in `warning`, not to the log.\n\t\t\tgetLogger().warn(`Input \"${label}\" default: dropped ${invalidItems.length} tree value(s)`);\n\t\t\treturn {\n\t\t\t\tschema: { ...input, default: tree },\n\t\t\t\twarning: { code: ErrorCodes.MALFORMED_DEFAULT, message }\n\t\t\t};\n\t\t}\n\t\treturn { schema: { ...input, default: tree } };\n\t}\n\n\tconst allValues: unknown[] = [];\n\tfor (const items of Object.values(innerTree)) {\n\t\tif (!Array.isArray(items)) continue;\n\t\tfor (const item of items) {\n\t\t\tif (item && typeof item === 'object' && hasField(item, 'data')) {\n\t\t\t\tallValues.push(readField(item, 'data'));\n\t\t\t}\n\t\t}\n\t}\n\treturn { schema: { ...input, default: allValues.length > 1 ? allValues : allValues[0] } };\n}\n","import type { InputParamSchema } from '../../types';\n\n/** Snap float noise (`0.30000000000000004`) to `decimalPlaces`; a real difference is kept. */\nexport function applyRounding(value: number, decimalPlaces: number): number {\n\tconst rounded = Number(value.toFixed(decimalPlaces));\n\treturn Math.abs(value - rounded) < 1e-8 ? rounded : value;\n}\n\nconst MAX_DECIMALS = 12;\n// A double holds 15 significant digits exactly. Fixed places would not do: from 1e4 up,\n// `toFixed(12)` prints the float's own noise (`16384.200000000001`).\nconst SIGNIFICANT_DIGITS = 15;\n\n/**\n * Decimal places `x` is written with, ignoring float noise, at most 12:\n * `0.30000000000000004` → 1, `16384.2` → 1, `1e-7` → 7, `1.5e21` → 0.\n */\nexport function decimalsOf(x: number): number {\n\tif (!Number.isFinite(x)) return 0;\n\tconst clean = Number(Math.abs(x).toPrecision(SIGNIFICANT_DIGITS));\n\tconst [mantissa, exponent = '0'] = String(clean).split('e');\n\tconst decimals = (mantissa.split('.')[1]?.length ?? 0) - Number(exponent);\n\treturn Math.min(Math.max(decimals, 0), MAX_DECIMALS);\n}\n\nexport function getInputStepSize(value: number): number {\n\tif (!Number.isFinite(value) || value === 0) return 0.1;\n\tconst decimals = decimalsOf(value);\n\tif (decimals === 0) return Math.abs(value) >= 1 ? 1 : 0.1;\n\treturn Number((10 ** -decimals).toFixed(decimals));\n}\n\n/** A server-authored `stepSize`, or `undefined` when absent or unusable. */\nexport function serverStepSize(schema: InputParamSchema): number | undefined {\n\treturn typeof schema.stepSize === 'number' &&\n\t\tNumber.isFinite(schema.stepSize) &&\n\t\tschema.stepSize > 0\n\t\t? schema.stepSize\n\t\t: undefined;\n}\n","import { ComputeError, ErrorCodes, type ErrorCode } from '@/core/errors';\nimport { getLogger } from '@/core/utils/logger';\nimport { isDataTreeDefault } from '../../data-tree/tree-path';\nimport { applyRounding, decimalsOf, getInputStepSize, serverStepSize } from './numeric-rounding';\nimport {\n\tbooleanTransformer,\n\tcoerceDefault,\n\tcolorTransformer,\n\tnumericTransformer,\n\tobjectTransformer,\n\ttextTransformer\n} from './transformers';\nimport type {\n\tBaseInputType,\n\tBooleanInputType,\n\tColorInputType,\n\tFileInputType,\n\tGeometryInputType,\n\tInputParam,\n\tInputParamSchema,\n\tNumericInputType,\n\tTextInputType,\n\tValueListInputType\n} from '../../types';\n\n/**\n * A recoverable oddity a parser found while still succeeding (e.g. a ValueList\n * default not in its values map). Reaches clients through `parseErrors`.\n */\nexport interface ParserWarning {\n\tcode: ErrorCode;\n\tmessage: string;\n}\n\n/**\n * One adapter per Grasshopper param type: value coercion, type-specific fields\n * and the typed param. Pure: reads a `normalizeDefault`'d schema, never mutates it.\n */\nexport interface InputTypeParser<T extends InputParam = InputParam> {\n\t/** Throws a {@link ComputeError} on bad input; calls `warn` for oddities it recovered from. */\n\tparse(schema: InputParamSchema, base: BaseInputType, warn?: (warning: ParserWarning) => void): T;\n\t/** The safe param when {@link parse} throws. Omit it when `parse` can't throw. */\n\tfallback?(schema: InputParamSchema, base: BaseInputType): T;\n}\n\nconst labelOf = (schema: InputParamSchema): string => schema.nickname || 'unnamed';\n\nconst listOr = <T>(schema: InputParamSchema, value: T): T | T[] =>\n\t(schema.atMost ?? 1) > 1 ? [value] : value;\n\nconst defaultStep = (schema: InputParamSchema): number =>\n\tserverStepSize(schema) ?? (schema.paramType === 'Integer' ? 1 : 0.1);\n\nconst mapNumbers = (value: unknown, fn: (n: number) => number): unknown =>\n\tArray.isArray(value)\n\t\t? value.map((v) => (typeof v === 'number' ? fn(v) : v))\n\t\t: typeof value === 'number'\n\t\t\t? fn(value)\n\t\t\t: value;\n\nfunction computeNumeric(schema: InputParamSchema): {\n\tdefault: NumericInputType['default'];\n\tstepSize: number;\n} {\n\t// A tree default passes through: TreeBuilder applies the numeric constraints.\n\tif (isDataTreeDefault(schema.default)) {\n\t\treturn {\n\t\t\tdefault: schema.default as NumericInputType['default'],\n\t\t\tstepSize: defaultStep(schema)\n\t\t};\n\t}\n\n\t// A non-blank string that doesn't parse ('1,5', 'Infinity', hex) is an error,\n\t// not a silent `undefined`. Blank still means \"no default\".\n\tif (\n\t\ttypeof schema.default === 'string' &&\n\t\tschema.default.trim() !== '' &&\n\t\tnumericTransformer(schema.default) === null\n\t) {\n\t\tthrow new ComputeError(\n\t\t\t`Invalid numeric default \"${schema.default}\" for input \"${schema.name || 'unknown'}\"`,\n\t\t\tErrorCodes.VALIDATION_ERROR,\n\t\t\t{ context: { inputName: schema.name, default: schema.default } }\n\t\t);\n\t}\n\n\tconst value = coerceDefault(schema.default, numericTransformer, true);\n\n\tif (schema.paramType === 'Integer') {\n\t\treturn {\n\t\t\tdefault: mapNumbers(value, Math.round) as NumericInputType['default'],\n\t\t\tstepSize: defaultStep(schema)\n\t\t};\n\t}\n\n\tconst firstValue = Array.isArray(value) ? value[0] : value;\n\tconst stepSource = [firstValue, schema.minimum, schema.maximum].find(\n\t\t(v): v is number => typeof v === 'number' && Number.isFinite(v) && v !== 0\n\t);\n\tconst stepSize =\n\t\tserverStepSize(schema) ?? (stepSource !== undefined ? getInputStepSize(stepSource) : 0.1);\n\n\tlet decimalPlaces = decimalsOf(stepSize);\n\tif (\n\t\tdecimalPlaces === 0 &&\n\t\ttypeof firstValue === 'number' &&\n\t\tfirstValue !== 0 &&\n\t\tMath.abs(firstValue) < 1\n\t) {\n\t\tconst inferred = Math.ceil(-Math.log10(Math.abs(firstValue)));\n\t\tif (Number.isFinite(inferred) && inferred > 0) {\n\t\t\tdecimalPlaces = inferred;\n\t\t}\n\t}\n\tdecimalPlaces = Math.min(Math.max(decimalPlaces, 0), 12);\n\n\treturn {\n\t\tdefault: mapNumbers(value, (n) =>\n\t\t\tapplyRounding(n, decimalPlaces)\n\t\t) as NumericInputType['default'],\n\t\tstepSize\n\t};\n}\n\nconst numericParam = (\n\tschema: InputParamSchema,\n\tbase: BaseInputType,\n\tcomputed: Pick<NumericInputType, 'stepSize' | 'default'>\n): NumericInputType => ({\n\t...base,\n\tparamType: schema.paramType as 'Number' | 'Integer',\n\tminimum: schema.minimum,\n\tmaximum: schema.maximum,\n\tatLeast: schema.atLeast,\n\tatMost: schema.atMost,\n\t...computed\n});\n\nconst numericParser: InputTypeParser<NumericInputType> = {\n\tparse(schema, base) {\n\t\treturn numericParam(schema, base, computeNumeric(schema));\n\t},\n\tfallback(schema, base) {\n\t\t// With minimum > 0, a plain 0 would sit below the slider's own range.\n\t\tconst safeValue =\n\t\t\ttypeof schema.minimum === 'number' && Number.isFinite(schema.minimum) && schema.minimum > 0\n\t\t\t\t? schema.minimum\n\t\t\t\t: 0;\n\t\treturn numericParam(schema, base, {\n\t\t\tstepSize: defaultStep(schema),\n\t\t\tdefault: listOr(schema, safeValue)\n\t\t});\n\t}\n};\n\nconst booleanParser: InputTypeParser<BooleanInputType> = {\n\tparse(schema, base) {\n\t\tif (isDataTreeDefault(schema.default)) {\n\t\t\treturn {\n\t\t\t\t...base,\n\t\t\t\tparamType: 'Boolean',\n\t\t\t\tdefault: schema.default as BooleanInputType['default']\n\t\t\t};\n\t\t}\n\t\tconst value = coerceDefault(schema.default, booleanTransformer, false);\n\t\t// A bad array item is filtered; a bad scalar survives coercion verbatim\n\t\t// (dropInvalidScalar=false) and is reported here instead.\n\t\tif (\n\t\t\tvalue !== undefined &&\n\t\t\tvalue !== null &&\n\t\t\ttypeof value !== 'boolean' &&\n\t\t\t!Array.isArray(value)\n\t\t) {\n\t\t\tthrow new ComputeError(\n\t\t\t\t`Invalid boolean default \"${String(value)}\" for input \"${schema.name || 'unknown'}\"`,\n\t\t\t\tErrorCodes.VALIDATION_ERROR,\n\t\t\t\t{ context: { inputName: schema.name, default: schema.default } }\n\t\t\t);\n\t\t}\n\t\treturn { ...base, paramType: 'Boolean', default: value as BooleanInputType['default'] };\n\t},\n\tfallback(schema, base) {\n\t\treturn { ...base, paramType: 'Boolean', default: listOr(schema, false) };\n\t}\n};\n\nconst textParser: InputTypeParser<TextInputType> = {\n\tparse(schema, base) {\n\t\tconst value = coerceDefault(schema.default, textTransformer, false);\n\t\treturn { ...base, paramType: 'Text', default: value as TextInputType['default'] };\n\t}\n};\n\nconst valueListParser: InputTypeParser<ValueListInputType> = {\n\tparse(schema, base, warn) {\n\t\tif (\n\t\t\t!schema.values ||\n\t\t\ttypeof schema.values !== 'object' ||\n\t\t\tObject.keys(schema.values).length === 0\n\t\t) {\n\t\t\tconst inputName = labelOf(schema);\n\t\t\tthrow new ComputeError(\n\t\t\t\t`Input \"${inputName}\" has no values defined (expected ValueList)`,\n\t\t\t\tErrorCodes.INVALID_INPUT,\n\t\t\t\t{ context: { inputName, expectedType: 'ValueList' } }\n\t\t\t);\n\t\t}\n\n\t\tlet defaultValue: string | undefined;\n\t\tif (schema.default !== undefined && schema.default !== null) {\n\t\t\t// A tree/array-shaped default can't index the values map — `String()`\n\t\t\t// would silently turn it into '[object Object]'. Reject it properly.\n\t\t\tif (typeof schema.default === 'object') {\n\t\t\t\tthrow new ComputeError(\n\t\t\t\t\t`ValueList input \"${labelOf(schema)}\" default is not a string-able value`,\n\t\t\t\t\tErrorCodes.VALIDATION_ERROR,\n\t\t\t\t\t{ context: { inputName: schema.name, default: schema.default } }\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst defaultLower = String(schema.default).toLowerCase();\n\t\t\t// Membership is case-insensitive, but downstream lookups (`values[default]`)\n\t\t\t// are not — return the canonical-cased key on a match.\n\t\t\tconst match = Object.keys(schema.values).find((key) => key.toLowerCase() === defaultLower);\n\t\t\tdefaultValue = match ?? String(schema.default);\n\t\t\tif (match === undefined) {\n\t\t\t\t// Out-of-range only warns: the parse still succeeds.\n\t\t\t\tconst inputName = labelOf(schema);\n\t\t\t\tgetLogger().warn(`ValueList input \"${inputName}\" default is not in available values`);\n\t\t\t\twarn?.({\n\t\t\t\t\tcode: ErrorCodes.VALIDATION_ERROR,\n\t\t\t\t\tmessage: `ValueList input \"${inputName}\" default value \"${schema.default}\" is not in available values`\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\t...base,\n\t\t\tparamType: 'ValueList',\n\t\t\tvalues: schema.values as Record<string, string>,\n\t\t\tdefault: defaultValue\n\t\t};\n\t},\n\tfallback(schema, base) {\n\t\t// The default can't be validated against the map here, so drop it.\n\t\treturn {\n\t\t\t...base,\n\t\t\tparamType: 'ValueList',\n\t\t\tvalues: schema.values && typeof schema.values === 'object' ? schema.values : {},\n\t\t\tdefault: undefined\n\t\t};\n\t}\n};\n\nconst objectDefault = (schema: InputParamSchema): unknown =>\n\tcoerceDefault(schema.default, objectTransformer(labelOf(schema)), true);\n\nfunction geometryFallback(schema: InputParamSchema, base: BaseInputType): GeometryInputType {\n\treturn {\n\t\t...base,\n\t\tparamType: 'Geometry',\n\t\tdefault: listOr(schema, null) as GeometryInputType['default']\n\t};\n}\n\nconst geometryParser: InputTypeParser<GeometryInputType> = {\n\tparse(schema, base) {\n\t\treturn {\n\t\t\t...base,\n\t\t\tparamType: 'Geometry',\n\t\t\tdefault: objectDefault(schema) as GeometryInputType['default']\n\t\t};\n\t},\n\tfallback: geometryFallback\n};\n\nconst fileParser: InputTypeParser<FileInputType> = {\n\tparse(schema, base) {\n\t\treturn {\n\t\t\t...base,\n\t\t\tparamType: 'File',\n\t\t\tacceptedFormats: schema.acceptedFormats,\n\t\t\tdefault: objectDefault(schema) as FileInputType['default']\n\t\t};\n\t}\n};\n\nconst colorParser: InputTypeParser<ColorInputType> = {\n\tparse(schema, base) {\n\t\tconst value = coerceDefault(schema.default, colorTransformer, false);\n\t\treturn { ...base, paramType: 'Color', default: value as ColorInputType['default'] };\n\t}\n};\n\n/** Keyed by canonical paramType. */\nexport const INPUT_TYPE_PARSERS: ReadonlyMap<string, InputTypeParser> = new Map<\n\tstring,\n\tInputTypeParser\n>([\n\t['Number', numericParser],\n\t['Integer', numericParser],\n\t['Boolean', booleanParser],\n\t['Text', textParser],\n\t['ValueList', valueListParser],\n\t['Geometry', geometryParser],\n\t['File', fileParser],\n\t['Color', colorParser]\n]);\n\nexport const unknownTypeFallback = geometryFallback;\n","import { ComputeError, ErrorCodes } from '@/core/errors';\nimport { getLogger } from '@/core/utils/logger';\n\nimport { normalizeDefault } from './normalize-default';\nimport { INPUT_TYPE_PARSERS, unknownTypeFallback } from './input-type-parsers';\n\nimport type { BaseInputType, InputParam, InputParamSchema, InputParseError } from '../../types';\n\nconst CANONICAL_PARAM_TYPES = new Map(\n\t[...INPUT_TYPE_PARSERS.keys()].map((key) => [key.toLowerCase(), key])\n);\n\n/** An unknown paramType comes back unchanged, so its unknown-type error still surfaces. */\nfunction canonicalizeParamType(paramType: string): string {\n\treturn CANONICAL_PARAM_TYPES.get(paramType?.toLowerCase()) ?? paramType;\n}\n\n/**\n * Parse one raw Grasshopper input schema into a typed {@link InputParam}.\n * A validation failure is swallowed and replaced with a safe default.\n */\nexport function processInput(rawInput: InputParamSchema): InputParam {\n\treturn processInputWithError(rawInput).input;\n}\n\n/**\n * Like {@link processInput}, but also returns every problem found on the way, in\n * order: a dropped default, warnings the parser recovered from, and the parser\n * error that forced the safe default. Entries carry the RAW declared `paramType`\n * so clients match on the casing they sent.\n *\n * A non-`ComputeError` failure still throws: it is a bug, not bad input.\n */\nexport function processInputWithError(rawInput: InputParamSchema): {\n\tinput: InputParam;\n\terrors: InputParseError[];\n} {\n\tconst label = rawInput.name || 'unknown';\n\tconst errors: InputParseError[] = [];\n\tconst report = ({ code, message }: { code: string; message: string }) => {\n\t\terrors.push({ inputName: label, paramType: rawInput.paramType, message, code });\n\t};\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\t// Absent, not '': \"no group\" must stay distinct from an empty group name.\n\t\tgroupName: rawInput.groupName ?? undefined,\n\t\tid: rawInput.id\n\t};\n\n\t// Selva schemas send \"valueList\", the plugin \"ValueList\".\n\tconst paramType = canonicalizeParamType(rawInput.paramType);\n\n\tlet schema: InputParamSchema = { ...rawInput, paramType };\n\ttry {\n\t\tconst { schema: normalized, warning } = normalizeDefault(schema, label);\n\t\tschema = normalized;\n\t\tif (warning) report(warning);\n\t} catch (error) {\n\t\t// A default too malformed to walk is bad data, not a bug: drop it for this\n\t\t// input rather than failing the whole definition-IO fetch.\n\t\tschema = { ...schema, default: null };\n\t\treport({\n\t\t\tcode: ErrorCodes.MALFORMED_DEFAULT,\n\t\t\tmessage: `Input \"${label}\" default could not be normalized and was dropped: ${\n\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t}`\n\t\t});\n\t}\n\n\tconst parser = INPUT_TYPE_PARSERS.get(paramType);\n\ttry {\n\t\tif (!parser) {\n\t\t\tthrow new ComputeError(`Unknown paramType: ${paramType}`, ErrorCodes.VALIDATION_ERROR, {\n\t\t\t\tcontext: { receivedParamType: paramType, paramName: rawInput.name }\n\t\t\t});\n\t\t}\n\t\treturn { input: parser.parse(schema, baseInput, report), errors };\n\t} catch (error) {\n\t\tif (!(error instanceof ComputeError)) {\n\t\t\tthrow new ComputeError(\n\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\tErrorCodes.VALIDATION_ERROR,\n\t\t\t\t{\n\t\t\t\t\tcontext: { paramName: rawInput.name, paramType },\n\t\t\t\t\toriginalError: error\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t\t// The code, not the message: messages quote the rejected default.\n\t\tgetLogger().error(`Validation error for input ${label}: ${error.code}`);\n\t\treport(error);\n\t\tconst fallback = parser?.fallback ?? unknownTypeFallback;\n\t\treturn { input: fallback(schema, baseInput), errors };\n\t}\n}\n\n/** {@link processInputWithError} over a list, with every input's errors collected. */\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, errors } = processInputWithError(raw);\n\t\tinputs.push(input);\n\t\tparseErrors.push(...errors);\n\t}\n\treturn { inputs, parseErrors };\n}\n","import { readField } from '@/core/utils/read-field';\nimport type { InputParamSchema, OutputParamSchema } from '../types';\n\n/**\n * Canonicalize a raw `/io` param record's field keys to camelCase, so the\n * parsers can read `schema.paramType` whichever casing the server sent. Values,\n * including `default` and the user-authored `values` map, pass through verbatim.\n *\n * A missing required field gets a fallback that keeps the type true: `paramType`\n * `''` surfaces as an unknown-type parse error, and `groupName` stays `null`\n * (not `''`) so \"no group\" differs from an empty group name.\n */\nexport function normalizeInputSchema(raw: unknown): InputParamSchema {\n\tconst stepSize = readField<unknown>(raw, 'stepSize');\n\treturn {\n\t\tid: readField<string>(raw, 'id') ?? '',\n\t\tname: readField<string>(raw, 'name') ?? '',\n\t\tnickname: readField<string | null>(raw, 'nickname') ?? null,\n\t\tdescription: readField<string>(raw, 'description') ?? '',\n\t\tparamType: readField<string>(raw, 'paramType') ?? '',\n\t\ttreeAccess: readField<boolean>(raw, 'treeAccess') ?? false,\n\t\tminimum: readField<number | null>(raw, 'minimum') ?? null,\n\t\tmaximum: readField<number | null>(raw, 'maximum') ?? null,\n\t\tatLeast: readField<number>(raw, 'atLeast') ?? 1,\n\t\tatMost: readField<number>(raw, 'atMost') ?? 1,\n\t\tstepSize: typeof stepSize === 'number' && Number.isFinite(stepSize) ? stepSize : undefined,\n\t\tdefault: readField(raw, 'default'),\n\t\tvalues: readField<Record<string, string>>(raw, 'values'),\n\t\tacceptedFormats: readField<string[]>(raw, 'acceptedFormats'),\n\t\tgroupName: readField<string | null>(raw, 'groupName') ?? null\n\t};\n}\n\nexport function normalizeOutputSchema(raw: unknown): OutputParamSchema {\n\treturn {\n\t\tname: readField<string>(raw, 'name') ?? '',\n\t\tnickname: readField<string | null>(raw, 'nickname') ?? null,\n\t\tparamType: readField<string>(raw, 'paramType') ?? '',\n\t\tid: readField<string>(raw, 'id') ?? ''\n\t};\n}\n","import { fetchCompute } from '@/core/compute-fetch/compute-fetch';\nimport { ComputeError, ErrorCodes } from '@/core/errors';\nimport type { ComputeConfig } from '@/core/types';\nimport { readField } from '@/core/utils/read-field';\nimport { warnIfClientSide } from '@/core/utils/warnings';\nimport { validateServerUrl } from '@/core/server/validate-server-url';\nimport { assertResponseObject, prepareGrasshopperArgs, withGrasshopperErrorCodes } from '../solve';\n\nimport type { GrasshopperParsedIO, GrasshopperParsedIORaw } from '../types';\n\nimport { processInputsWithErrors } from './input/input-processors';\nimport { normalizeInputSchema, normalizeOutputSchema } from './normalize-schema';\n\n/**\n * Fetches a definition's input/output schemas without per-type parsing.\n *\n * Field KEYS are canonicalized to camelCase and missing required fields get a fallback; a missing\n * or non-array `inputs` / `outputs` becomes `[]`. Field VALUES, notably `default` and the `values`\n * label map, pass through verbatim.\n *\n * @throws {ComputeError} If fetch fails or response is invalid\n */\nexport async function fetchDefinitionIO(\n\tdefinition: string | Uint8Array,\n\tconfig: ComputeConfig\n): Promise<GrasshopperParsedIORaw> {\n\twarnIfClientSide('fetchDefinitionIO', config.suppressBrowserWarning);\n\tvalidateServerUrl(config.serverUrl);\n\n\tconst { algo, pointer } = prepareGrasshopperArgs(definition, []);\n\tif (!algo && !pointer) {\n\t\tthrow new ComputeError(\n\t\t\t'Definition must resolve to either a URL pointer or base64 algo',\n\t\t\tErrorCodes.INVALID_INPUT\n\t\t);\n\t}\n\n\tconst response = await fetchCompute<unknown>(\n\t\t'io',\n\t\talgo ? { algo } : { pointer },\n\t\twithGrasshopperErrorCodes(config)\n\t);\n\n\tassertResponseObject(response);\n\n\tconst loadWarnings = nonEmptyStrings(readField(response, 'warnings'));\n\tconst loadErrors = nonEmptyStrings(readField(response, 'errors'));\n\n\t// Array.isArray, not `?? []`: a load failure can surface as a 200 whose\n\t// `inputs` is `{}` or a string, and that must become `[]` too.\n\tconst rawInputs = readField(response, 'inputs');\n\tconst rawOutputs = readField(response, 'outputs');\n\treturn {\n\t\tinputs: Array.isArray(rawInputs) ? rawInputs.map(normalizeInputSchema) : [],\n\t\toutputs: Array.isArray(rawOutputs) ? rawOutputs.map(normalizeOutputSchema) : [],\n\t\t...(loadWarnings && { loadWarnings }),\n\t\t...(loadErrors && { loadErrors })\n\t};\n}\n\n/**\n * Server diagnostics as non-blank strings, or `undefined` when there are none.\n * Non-string entries are kept, not dropped: a fork reporting `{ message }`\n * objects must not yield an empty input list with no explanation.\n */\nfunction nonEmptyStrings(value: unknown): string[] | undefined {\n\tif (!Array.isArray(value)) return undefined;\n\tconst cleaned = value.map(coerceDiagnostic).filter((v): v is string => v !== undefined);\n\treturn cleaned.length > 0 ? cleaned : undefined;\n}\n\nfunction coerceDiagnostic(value: unknown): string | undefined {\n\tif (value === null || value === undefined) return undefined;\n\tlet text: string;\n\tif (typeof value === 'string') {\n\t\ttext = value;\n\t} else if (typeof value === 'object') {\n\t\tconst message = readField<unknown>(value, 'message');\n\t\tif (typeof message === 'string' && message.trim().length > 0) {\n\t\t\ttext = message;\n\t\t} else {\n\t\t\ttry {\n\t\t\t\ttext = JSON.stringify(value) ?? String(value);\n\t\t\t} catch {\n\t\t\t\ttext = String(value);\n\t\t\t}\n\t\t}\n\t} else {\n\t\ttext = String(value);\n\t}\n\treturn text.trim().length > 0 ? text : undefined;\n}\n\n/**\n * Fetches a definition's input/output schemas and parses each input into its\n * typed `InputParam`. An input that fails to parse gets a safe default\n * and an entry in `parseErrors`.\n *\n * @throws {ComputeError} 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: 'http://localhost:6500', 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\tconst {\n\t\tinputs: rawInputs,\n\t\toutputs,\n\t\tloadWarnings,\n\t\tloadErrors\n\t} = await fetchDefinitionIO(definition, config);\n\tconst { inputs, parseErrors } = processInputsWithErrors(rawInputs);\n\n\treturn {\n\t\tinputs,\n\t\toutputs,\n\t\t...(parseErrors.length > 0 && { parseErrors }),\n\t\t...(loadWarnings && { loadWarnings }),\n\t\t...(loadErrors && { loadErrors })\n\t};\n}\n","import { getResponseWireSize } from '@/core/compute-fetch/wire-size';\nimport { readField } from '@/core/utils/read-field';\n\nimport type { GrasshopperComputeResponse } from '../types';\nimport type { CacheOptions, CacheStats } from './types';\n\ninterface CacheEntry {\n\tresponse: GrasshopperComputeResponse;\n\tinsertedAt: number;\n\t/** Wire size (or stringify-fallback estimate) counted against `maxBytes`. */\n\tsizeBytes: number;\n}\n\n/**\n * For a response that never crossed the fetch boundary (custom executor), so carries no\n * wire-size hint. An unserializable response is sized 0 rather than failing the solve.\n */\nfunction estimateResponseSize(response: GrasshopperComputeResponse): number {\n\ttry {\n\t\treturn JSON.stringify(response)?.length ?? 0;\n\t} catch {\n\t\treturn 0;\n\t}\n}\n\n/** The scheduler's response cache: LRU by bytes, with an optional TTL. */\nexport class ResponseCache {\n\tprivate readonly maxBytes: number;\n\tprivate readonly ttlMs: number;\n\tprivate readonly cacheErroredSolves: boolean;\n\tprivate readonly entries = new Map<string, CacheEntry>();\n\tprivate bytes = 0;\n\tprivate hits = 0;\n\tprivate misses = 0;\n\tprivate evictions = 0;\n\n\tconstructor(options: CacheOptions | false | undefined) {\n\t\tconst config = options || null;\n\t\tthis.maxBytes = Math.max(0, config?.maxBytes ?? 0);\n\t\tthis.ttlMs = config?.ttlMs ?? 0;\n\t\tthis.cacheErroredSolves = config?.cacheErroredSolves ?? true;\n\t}\n\n\t/** A zero budget retains nothing, so it is off rather than a cache that can only ever miss. */\n\tget enabled(): boolean {\n\t\treturn this.maxBytes > 0;\n\t}\n\n\tget(key: string): GrasshopperComputeResponse | null {\n\t\tif (!this.enabled) return null;\n\t\tconst entry = this.entries.get(key);\n\t\tif (!entry) {\n\t\t\tthis.misses += 1;\n\t\t\treturn null;\n\t\t}\n\t\tif (this.ttlMs > 0 && Date.now() - entry.insertedAt > this.ttlMs) {\n\t\t\tthis.drop(key);\n\t\t\tthis.misses += 1;\n\t\t\treturn null;\n\t\t}\n\t\t// LRU touch\n\t\tthis.entries.delete(key);\n\t\tthis.entries.set(key, entry);\n\t\tthis.hits += 1;\n\t\treturn entry.response;\n\t}\n\n\tset(key: string, response: GrasshopperComputeResponse): void {\n\t\tif (!this.enabled) return;\n\t\tconst solveErrors = readField<unknown[]>(response, 'errors');\n\t\tif (!this.cacheErroredSolves && Array.isArray(solveErrors) && solveErrors.length > 0) return;\n\t\t// An aborted solve stopped partway, so the same inputs must solve again. A blocked one is deterministic.\n\t\tconst outcome = readField(readField(response, 'selva'), 'outcome');\n\t\tif (readField(outcome, 'aborted') === true) return;\n\t\tconst sizeBytes = getResponseWireSize(response) ?? estimateResponseSize(response);\n\t\t// Retaining it would evict everything else and then itself.\n\t\tif (sizeBytes > this.maxBytes) return;\n\t\tthis.drop(key); // replace-in-place: release the old copy's bytes\n\t\tthis.entries.set(key, { response, insertedAt: Date.now(), sizeBytes });\n\t\tthis.bytes += sizeBytes;\n\t\twhile (this.bytes > this.maxBytes && this.entries.size > 0) {\n\t\t\tconst oldest = this.entries.keys().next().value;\n\t\t\tif (oldest === undefined) break;\n\t\t\tthis.drop(oldest);\n\t\t\tthis.evictions += 1;\n\t\t}\n\t}\n\n\tclear(): void {\n\t\tthis.entries.clear();\n\t\tthis.bytes = 0;\n\t}\n\n\tstats(): CacheStats {\n\t\treturn {\n\t\t\tentries: this.entries.size,\n\t\t\tbytes: this.bytes,\n\t\t\thits: this.hits,\n\t\t\tmisses: this.misses,\n\t\t\tevictions: this.evictions\n\t\t};\n\t}\n\n\tprivate drop(key: string): void {\n\t\tconst entry = this.entries.get(key);\n\t\tif (!entry) return;\n\t\tthis.entries.delete(key);\n\t\tthis.bytes -= entry.sizeBytes;\n\t}\n}\n","/**\n * SHA-256 for cache keys that callers may try to forge: a key shared across users must not be\n * collidable on purpose, which rules out a fast non-cryptographic hash.\n *\n * Strings hash as their UTF-16LE code units. UTF-8 would fold every lone surrogate into U+FFFD,\n * so two different strings would hash alike.\n */\n\ntype NodeCrypto = typeof import('node:crypto');\n\n// `getBuiltinModule` is synchronous, so Node gets native hashing without a top-level import that\n// would break browser bundles. Everywhere else takes the portable path.\nconst nodeCrypto = (\n\tglobalThis as { process?: { getBuiltinModule?: (id: string) => unknown } }\n).process?.getBuiltinModule?.('node:crypto') as NodeCrypto | undefined;\n\n/** Lowercase hex SHA-256 of `input`. */\nexport function sha256Hex(input: string | Uint8Array): string {\n\tif (nodeCrypto) {\n\t\tconst hash = nodeCrypto.createHash('sha256');\n\t\tif (typeof input === 'string') hash.update(input, 'utf16le');\n\t\telse hash.update(input);\n\t\treturn hash.digest('hex');\n\t}\n\treturn sha256HexPortable(input);\n}\n\n// ============================================================================\n// Portable implementation (FIPS 180-4)\n// ============================================================================\n\nconst K = new Int32Array([\n\t0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n\t0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n\t0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n\t0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n\t0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n\t0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n\t0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n\t0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2\n]);\n\n/** Exported so tests can check it against Node's native hash. */\nexport function sha256HexPortable(input: string | Uint8Array): string {\n\tconst state = new Sha256();\n\tif (typeof input === 'string') state.updateString(input);\n\telse state.update(input);\n\treturn state.digestHex();\n}\n\nclass Sha256 {\n\tprivate readonly h = new Int32Array([\n\t\t0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19\n\t]);\n\tprivate readonly w = new Int32Array(64);\n\tprivate readonly pending = new Uint8Array(64);\n\tprivate pendingLength = 0;\n\tprivate totalBytes = 0;\n\n\tupdate(bytes: Uint8Array): void {\n\t\tthis.totalBytes += bytes.length;\n\t\tlet offset = 0;\n\t\tif (this.pendingLength > 0) {\n\t\t\tconst take = Math.min(64 - this.pendingLength, bytes.length);\n\t\t\tthis.pending.set(bytes.subarray(0, take), this.pendingLength);\n\t\t\tthis.pendingLength += take;\n\t\t\toffset = take;\n\t\t\tif (this.pendingLength < 64) return;\n\t\t\tthis.compressBytes(this.pending, 0);\n\t\t\tthis.pendingLength = 0;\n\t\t}\n\t\tfor (; offset + 64 <= bytes.length; offset += 64) this.compressBytes(bytes, offset);\n\t\tthis.pending.set(bytes.subarray(offset), 0);\n\t\tthis.pendingLength = bytes.length - offset;\n\t}\n\n\t/**\n\t * Feed a string's UTF-16LE bytes straight into the message words, skipping a byte buffer.\n\t * Only on a fresh state: 32 code units fill a block exactly, so nothing may be pending.\n\t */\n\tupdateString(s: string): void {\n\t\tconst w = this.w;\n\t\tconst whole = s.length - (s.length % 32);\n\t\tfor (let offset = 0; offset < whole; offset += 32) {\n\t\t\tfor (let i = 0; i < 16; i++) {\n\t\t\t\tconst c0 = s.charCodeAt(offset + 2 * i);\n\t\t\t\tconst c1 = s.charCodeAt(offset + 2 * i + 1);\n\t\t\t\tw[i] = ((c0 & 0xff) << 24) | ((c0 >>> 8) << 16) | ((c1 & 0xff) << 8) | (c1 >>> 8);\n\t\t\t}\n\t\t\tthis.compress();\n\t\t}\n\t\tthis.totalBytes += whole * 2;\n\n\t\tconst rest = new Uint8Array((s.length - whole) * 2);\n\t\tfor (let i = whole, j = 0; i < s.length; i++) {\n\t\t\tconst code = s.charCodeAt(i);\n\t\t\trest[j++] = code & 0xff;\n\t\t\trest[j++] = code >>> 8;\n\t\t}\n\t\tthis.update(rest);\n\t}\n\n\tdigestHex(): string {\n\t\tconst bitLength = this.totalBytes * 8;\n\t\tconst tail = new Uint8Array(this.pendingLength < 56 ? 64 : 128);\n\t\ttail.set(this.pending.subarray(0, this.pendingLength));\n\t\ttail[this.pendingLength] = 0x80;\n\t\tconst view = new DataView(tail.buffer);\n\t\tview.setUint32(tail.length - 8, Math.floor(bitLength / 0x100000000));\n\t\tview.setUint32(tail.length - 4, bitLength >>> 0);\n\t\tfor (let offset = 0; offset < tail.length; offset += 64) this.compressBytes(tail, offset);\n\n\t\tlet hex = '';\n\t\tfor (const word of this.h) hex += (word >>> 0).toString(16).padStart(8, '0');\n\t\treturn hex;\n\t}\n\n\tprivate compressBytes(bytes: Uint8Array, offset: number): void {\n\t\tconst w = this.w;\n\t\tfor (let i = 0; i < 16; i++) {\n\t\t\tconst j = offset + i * 4;\n\t\t\tw[i] = (bytes[j] << 24) | (bytes[j + 1] << 16) | (bytes[j + 2] << 8) | bytes[j + 3];\n\t\t}\n\t\tthis.compress();\n\t}\n\n\t/** One block, from the 16 message words already in `w`. */\n\tprivate compress(): void {\n\t\tconst w = this.w;\n\t\tfor (let i = 16; i < 64; i++) {\n\t\t\tconst x = w[i - 15];\n\t\t\tconst y = w[i - 2];\n\t\t\tconst s0 = ((x >>> 7) | (x << 25)) ^ ((x >>> 18) | (x << 14)) ^ (x >>> 3);\n\t\t\tconst s1 = ((y >>> 17) | (y << 15)) ^ ((y >>> 19) | (y << 13)) ^ (y >>> 10);\n\t\t\tw[i] = (w[i - 16] + s0 + w[i - 7] + s1) | 0;\n\t\t}\n\n\t\tconst h = this.h;\n\t\tlet a = h[0];\n\t\tlet b = h[1];\n\t\tlet c = h[2];\n\t\tlet d = h[3];\n\t\tlet e = h[4];\n\t\tlet f = h[5];\n\t\tlet g = h[6];\n\t\tlet k = h[7];\n\t\tfor (let i = 0; i < 64; i++) {\n\t\t\tconst S1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7));\n\t\t\tconst ch = (e & f) ^ (~e & g);\n\t\t\tconst t1 = (k + S1 + ch + K[i] + w[i]) | 0;\n\t\t\tconst S0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10));\n\t\t\tconst maj = (a & b) ^ (a & c) ^ (b & c);\n\t\t\tconst t2 = (S0 + maj) | 0;\n\t\t\tk = g;\n\t\t\tg = f;\n\t\t\tf = e;\n\t\t\te = (d + t1) | 0;\n\t\t\td = c;\n\t\t\tc = b;\n\t\t\tb = a;\n\t\t\ta = (t1 + t2) | 0;\n\t\t}\n\t\th[0] = (h[0] + a) | 0;\n\t\th[1] = (h[1] + b) | 0;\n\t\th[2] = (h[2] + c) | 0;\n\t\th[3] = (h[3] + d) | 0;\n\t\th[4] = (h[4] + e) | 0;\n\t\th[5] = (h[5] + f) | 0;\n\t\th[6] = (h[6] + g) | 0;\n\t\th[7] = (h[7] + k) | 0;\n\t}\n}\n","/**\n * Solve-key hashing, public so an app-layer durable cache or a pre-solved bundle viewer can\n * reproduce the scheduler's exact key. A key that canonicalizes even slightly differently\n * misses every entry the scheduler wrote, or worse, collides differently.\n *\n * Keys are SHA-256: one scheduler's cache serves every user of a server, so a key a user can\n * collide on purpose lets them choose what another user's solve returns.\n */\n\nimport { isDefinitionRef, type SolveDefinition } from '@/core/definition-ref';\nimport { sha256Hex } from '@/core/utils/sha256';\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 *\n * The output is a cache key, so the invariant that matters is: two payloads\n * that serialize differently on the wire must stringify differently here\n * (false misses are harmless; false hits serve the wrong cached solve).\n * Non-JSON values get unquoted markers (`{u8:…}`, `[Circular]`) that no JSON\n * value stringifies to, so a plain object can't pose as one.\n */\nexport function stableStringify(value: unknown): string {\n\t// Tracks the current recursion path only (entries are removed on the way\n\t// out), so genuine cycles read \"[Circular]\" while shared non-circular\n\t// references stringify by content each time they appear.\n\tconst path = new WeakSet<object>();\n\n\tconst stringify = (v: unknown): string => {\n\t\tif (v === null) return 'null';\n\t\tif (typeof v === 'number') {\n\t\t\treturn Number.isFinite(v) ? String(v) : 'null';\n\t\t}\n\t\tif (typeof v === 'string' || typeof v === 'boolean') return JSON.stringify(v);\n\t\t// Unquoted `n` suffix keeps 1n distinct from the string \"1\".\n\t\tif (typeof v === 'bigint') return `${v}n`;\n\t\t// JSON drops a function or symbol exactly like `undefined`, so they share its marker.\n\t\tif (typeof v !== 'object') return 'undefined';\n\t\tif (v instanceof Uint8Array) return `{u8:${sha256Hex(v)}}`;\n\t\tif (path.has(v)) return '[Circular]';\n\t\tpath.add(v);\n\t\tlet out: string;\n\t\tif (Array.isArray(v)) {\n\t\t\tconst parts: string[] = [];\n\t\t\t// Indexed access (not .map) so holes stringify like undefined instead\n\t\t\t// of vanishing from the joined output.\n\t\t\tfor (let i = 0; i < v.length; i++) parts.push(stringify(v[i]));\n\t\t\tout = `[${parts.join(',')}]`;\n\t\t} else if (typeof (v as { toJSON?: unknown }).toJSON === 'function') {\n\t\t\t// Matches wire behavior: JSON.stringify calls toJSON (Date → ISO string).\n\t\t\tout = stringify((v as { toJSON: () => unknown }).toJSON());\n\t\t} else if (v instanceof Map) {\n\t\t\tconst entries = [...v.entries()].map(([k, val]) => `[${stringify(k)},${stringify(val)}]`);\n\t\t\tout = `{map:[${entries.sort().join(',')}]}`;\n\t\t} else if (v instanceof Set) {\n\t\t\tconst items = [...v.values()].map(stringify);\n\t\t\tout = `{set:[${items.sort().join(',')}]}`;\n\t\t} else {\n\t\t\tconst keys = Object.keys(v).sort();\n\t\t\tconst parts = keys.map(\n\t\t\t\t(k) => `${JSON.stringify(k)}:${stringify((v as Record<string, unknown>)[k])}`\n\t\t\t);\n\t\t\tout = `{${parts.join(',')}}`;\n\t\t}\n\t\tpath.delete(v);\n\t\treturn out;\n\t};\n\n\treturn stringify(value);\n}\n\n/** The scheduler's solve-cache key for a definition and its input tree. */\nexport function hashSolveInput(definition: SolveDefinition, dataTree: unknown): string {\n\treturn hashSolveInputForDefinition(hashDefinition(definition), dataTree);\n}\n\n/**\n * `hashSolveInput` from a precomputed {@link hashDefinition}, so the scheduler, which also keys its\n * server-cache-key map by that hash, passes over a multi-MB definition once per solve.\n */\nexport function hashSolveInputForDefinition(definitionHash: string, dataTree: unknown): string {\n\treturn `${definitionHash}|t:${sha256Hex(stableStringify(dataTree))}`;\n}\n\n/**\n * A definition's identity, without its inputs. Strings and bytes are hashed over their full\n * content; a `DefinitionRef` uses its `key`.\n */\nexport function hashDefinition(definition: SolveDefinition): string {\n\tif (isDefinitionRef(definition)) return `r:${definition.key}`;\n\treturn typeof definition === 'string'\n\t\t? `s:${sha256Hex(definition)}`\n\t\t: `u8:${sha256Hex(definition)}`;\n}\n","import { abortedByCaller, ComputeError, ErrorCodes } from '@/core/errors';\nimport type { RetryPolicy } from '@/core/types';\nimport { getLogger } from '@/core/utils/logger';\n\nimport type {\n\tDataTree,\n\tGrasshopperComputeResponse,\n\tGrasshopperComputeConfig,\n\tSelvaEventTarget\n} from '../types';\nimport { isDefinitionUrl, type SolveDefinition } from '@/core/definition-ref';\nimport { ResponseCache } from './response-cache';\nimport { hashSolveInputForDefinition, hashDefinition } from './stable-hash';\n\nimport type {\n\tCacheStats,\n\tSchedulerMode,\n\tSchedulerSolveOptions,\n\tSolveContext,\n\tSolveDetail,\n\tSolveExecutor,\n\tSolveResult,\n\tSolveSchedulerOptions\n} from './types';\n\nconst SERVER_CACHE_KEYS_MAX = 100;\n\ninterface PendingItem {\n\tdefinition: SolveDefinition;\n\tdataTree: DataTree[];\n\thashes: SolveInputHashes;\n\tctx: SolveContext;\n\t/** Monotonic solve() ordinal, so an older item's late settle can't overwrite newer state. */\n\tseq: number;\n\tresolve: (detail: SolveDetail) => void;\n\treject: (error: ComputeError) => void;\n\texternalSignal?: AbortSignal;\n\tselvaevents?: SelvaEventTarget;\n\tonServerTiming?: SchedulerSolveOptions['onServerTiming'];\n\t/** On `externalSignal`: aborts a running item, settles a queued one as ABORTED. */\n\tonExternalAbort?: () => void;\n\t/** Set once the item runs. */\n\tcontroller?: AbortController;\n\tqueueWaitTimer?: ReturnType<typeof setTimeout>;\n\tsettled?: { error: ComputeError } | { ok: true };\n}\n\ninterface InFlightItem extends PendingItem {\n\tcontroller: AbortController;\n}\n\n/** `NaN` passes `Math.max` and fails every `inFlight < limit`, so nothing would ever dispatch. */\nfunction concurrencyLimit(value: number): number {\n\tif (Number.isNaN(value)) {\n\t\tthrow new ComputeError('maxConcurrent must be a number', ErrorCodes.INVALID_CONFIG, {\n\t\t\tcontext: { maxConcurrent: value }\n\t\t});\n\t}\n\treturn Math.max(1, Math.floor(value));\n}\n\nconst u8DefinitionHashes = new WeakMap<Uint8Array, string>();\n\n/**\n * Remembers a buffer's hash by identity: a buffer mutated after its first solve keeps its old\n * hash and is served the old content's cached solves.\n */\nfunction hashDefinitionOnce(definition: SolveDefinition): string {\n\tif (!(definition instanceof Uint8Array)) return hashDefinition(definition);\n\tlet hash = u8DefinitionHashes.get(definition);\n\tif (hash === undefined) {\n\t\thash = hashDefinition(definition);\n\t\tu8DefinitionHashes.set(definition, hash);\n\t}\n\treturn hash;\n}\n\n/** One solve's input hashes, each taken on first use. */\nclass SolveInputHashes {\n\tprivate definitionMemo: string | undefined;\n\tprivate keyMemo: string | undefined;\n\n\tconstructor(\n\t\tprivate readonly hashDefinition: () => string,\n\t\tprivate readonly dataTree: DataTree[]\n\t) {}\n\n\tget definitionHash(): string {\n\t\treturn (this.definitionMemo ??= this.hashDefinition());\n\t}\n\n\tget key(): string {\n\t\treturn (this.keyMemo ??= hashSolveInputForDefinition(this.definitionHash, this.dataTree));\n\t}\n}\n\n/**\n * The FIFO behind `queue` mode. Removing an item only drops it from `live`; the array is swept once\n * the dead entries outnumber the live ones, so aborting n queued solves costs O(n), not O(n²).\n */\nclass SolveQueue {\n\tprivate items: (PendingItem | undefined)[] = [];\n\tprivate head = 0;\n\tprivate readonly live = new Set<PendingItem>();\n\n\tget size(): number {\n\t\treturn this.live.size;\n\t}\n\n\tpush(item: PendingItem): void {\n\t\tthis.items.push(item);\n\t\tthis.live.add(item);\n\t}\n\n\tremove(item: PendingItem): void {\n\t\tif (this.live.delete(item)) this.compact();\n\t}\n\n\tshift(): PendingItem | undefined {\n\t\twhile (this.head < this.items.length) {\n\t\t\tconst item = this.items[this.head];\n\t\t\t// A taken slot is cleared so it doesn't hold the solve's definition and tree.\n\t\t\tthis.items[this.head++] = undefined;\n\t\t\tif (item && this.live.delete(item)) {\n\t\t\t\tthis.compact();\n\t\t\t\treturn item;\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate compact(): void {\n\t\tif (this.live.size === 0) {\n\t\t\tthis.items = [];\n\t\t\tthis.head = 0;\n\t\t\treturn;\n\t\t}\n\t\tconst dead = this.items.length - this.live.size;\n\t\tif (dead <= 64 || dead <= this.live.size) return;\n\t\tthis.items = this.items.filter((item) => item !== undefined && this.live.has(item));\n\t\tthis.head = 0;\n\t}\n}\n\n/**\n * Adds to a plain solve call:\n * - Scheduling (latest-wins for sliders, queue for jobs)\n * - Backpressure (bounded queue depth + queue-wait deadline)\n * - Cancellation (per-call signal + cancelAll)\n * - Optional response caching for repeated inputs\n * - Lifecycle hooks (start / settle / superseded) and state via subscribe()\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 maxConcurrent: number;\n\tprivate readonly maxQueueDepth: number | undefined;\n\tprivate readonly queueWaitMs: number | undefined;\n\tprivate readonly timeoutMs: number | undefined;\n\tprivate readonly retry: RetryPolicy | undefined;\n\n\tprivate readonly cache: ResponseCache;\n\n\tprivate readonly reuseServerDefinitionCache: boolean;\n\t/** Definition hash → the server's cache key (`pointer`) for it. */\n\tprivate readonly serverCacheKeys = new Map<string, string>();\n\t/** A string definition re-sent unchanged is compared with this, far cheaper than hashing it. */\n\tprivate lastStringDefinition: { value: string; hash: string } | null = null;\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 fifo = new SolveQueue();\n\n\tprivate _lastResult: GrasshopperComputeResponse | null = null;\n\tprivate _lastError: ComputeError | null = null;\n\tprivate _lastDurationMs: number | null = null;\n\n\tprivate solveSeq = 0;\n\t/** seq of the solve that last wrote `_lastResult`/`_lastError`. */\n\tprivate lastStateSeq = 0;\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\tif (this.mode !== 'latest-wins' && this.mode !== 'queue') {\n\t\t\tthrow new ComputeError(\n\t\t\t\t`Unknown scheduler mode '${String(this.mode)}'. For concurrent solves use { mode: 'queue', maxConcurrent: 4 }.`,\n\t\t\t\tErrorCodes.INVALID_CONFIG,\n\t\t\t\t{ context: { mode: this.mode } }\n\t\t\t);\n\t\t}\n\t\tthis.maxConcurrent = concurrencyLimit(options.maxConcurrent ?? 1);\n\t\t// A non-positive maxQueueDepth means unbounded, not \"reject everything\".\n\t\tthis.maxQueueDepth =\n\t\t\toptions.maxQueueDepth !== undefined && options.maxQueueDepth > 0\n\t\t\t\t? Math.floor(options.maxQueueDepth)\n\t\t\t\t: undefined;\n\t\tthis.queueWaitMs =\n\t\t\toptions.queueWaitMs !== undefined && options.queueWaitMs > 0\n\t\t\t\t? options.queueWaitMs\n\t\t\t\t: undefined;\n\t\tthis.timeoutMs = options.timeoutMs;\n\t\tthis.retry = options.retry;\n\n\t\tthis.cache = new ResponseCache(options.cache);\n\n\t\tthis.reuseServerDefinitionCache = options.reuseServerDefinitionCache ?? true;\n\n\t\tthis.onStart = options.onStart;\n\t\tthis.onSettle = options.onSettle;\n\t\tthis.onSuperseded = options.onSuperseded;\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.fifo.size > 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.fifo.size + (this.pendingForLatestWins ? 1 : 0);\n\t}\n\n\tget lastResult(): GrasshopperComputeResponse | null {\n\t\treturn this._lastResult;\n\t}\n\n\tget lastError(): ComputeError | 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 * Adjust how many solves may run at once, for when the compute server's worker\n\t * pool changes size after this scheduler was built.\n\t *\n\t * Raising it drains queued work immediately. Lowering it never interrupts work\n\t * already in flight — those finish above the new limit, and the cap applies from\n\t * the next dispatch. Values below 1 are clamped, since 0 would wedge the queue.\n\t *\n\t * @throws {ComputeError} `INVALID_CONFIG` for `NaN`, which would wedge it too.\n\t */\n\tsetMaxConcurrent(value: number): void {\n\t\tconst next = concurrencyLimit(value);\n\t\tif (next === this.maxConcurrent) return;\n\t\tconst raised = next > this.maxConcurrent;\n\t\tthis.maxConcurrent = next;\n\t\tif (raised) this.drainNext();\n\t}\n\n\t/** Subscribe to state changes. */\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 * Schedule a solve. Returns a promise that:\n\t * - Resolves with the compute response on success.\n\t * - Rejects with `ComputeError` 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 * its own `signal` (queued or running) or `cancelAll()`.\n\t * - Rejects with `code: ErrorCodes.QUEUE_FULL` when `maxQueueDepth` is set and\n\t * the queue was already full (backpressure; `statusCode: 503`).\n\t * - Rejects with `code: ErrorCodes.QUEUE_TIMEOUT` when `queueWaitMs` is set and\n\t * the call sat queued longer than that before starting (`statusCode: 503`).\n\t *\n\t * Don't mutate a `Uint8Array` definition after solving it. Responses served\n\t * from the cache (and via `lastResult`) are shared objects, not copies —\n\t * treat them as immutable. Mutating one poisons every later cache hit for\n\t * that key.\n\t *\n\t * Partial-success contract: unlike `GrasshopperClient.solve()`, which throws\n\t * `COMPUTATION_ERROR` when the response carries solver errors, this RESOLVES\n\t * with the response as-is — check `response.errors` yourself if a partial\n\t * success must not be treated as a result (see `cacheErroredSolves` for the\n\t * caching side of the same distinction).\n\t */\n\tsolve(\n\t\tdefinition: SolveDefinition,\n\t\tdataTree: DataTree[],\n\t\toptions?: SchedulerSolveOptions\n\t): Promise<GrasshopperComputeResponse> {\n\t\treturn this.solveDetailed(definition, dataTree, options).then((detail) => detail.response);\n\t}\n\n\t/** {@link solve}, also resolving with whether this call hit the cache and how long it took. */\n\tsolveDetailed(\n\t\tdefinition: SolveDefinition,\n\t\tdataTree: DataTree[],\n\t\toptions?: SchedulerSolveOptions\n\t): Promise<SolveDetail> {\n\t\tif (this.disposed) {\n\t\t\treturn Promise.reject(\n\t\t\t\tnew ComputeError(\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 hashes = new SolveInputHashes(() => this.hashDefinitionMemo(definition), dataTree);\n\t\t// Only the cache and the hooks read the key. Without them, hashing a multi-MB tree would be\n\t\t// the costliest step of the call.\n\t\tlet key = '';\n\t\tif (this.cache.enabled || this.onStart || this.onSettle || this.onSuperseded) {\n\t\t\ttry {\n\t\t\t\tkey = hashes.key;\n\t\t\t} catch (error) {\n\t\t\t\t// A tree too deep or too large to stringify can't be sent either.\n\t\t\t\treturn Promise.reject(\n\t\t\t\t\tnew ComputeError('Solve input could not be hashed', ErrorCodes.INVALID_INPUT, {\n\t\t\t\t\t\toriginalError: error\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tconst seq = ++this.solveSeq;\n\t\tconst ctx: SolveContext = { key, enqueuedAt: Date.now(), startedAt: null };\n\n\t\t// An already-aborted signal rejects before anything else — including the\n\t\t// cache: the documented contract is ABORTED, not a result.\n\t\tif (options?.signal?.aborted) {\n\t\t\treturn Promise.reject(this.makeAbortError(ctx));\n\t\t}\n\n\t\tconst cached = this.cache.get(key);\n\t\tif (cached) {\n\t\t\t// In latest-wins an older in-flight solve is now stale: left running, its\n\t\t\t// completion would overwrite this hit and snap the UI back.\n\t\t\tif (this.mode === 'latest-wins') {\n\t\t\t\tthis.supersedeCurrent();\n\t\t\t}\n\t\t\tconst result: SolveResult = {\n\t\t\t\tstatus: 'success',\n\t\t\t\tresponse: cached,\n\t\t\t\tdurationMs: 0,\n\t\t\t\tfromCache: true\n\t\t\t};\n\t\t\tthis.writeLastState(seq, { result: cached, durationMs: 0 });\n\t\t\tthis.runHook(this.onStart, ctx);\n\t\t\tthis.runHook(this.onSettle, ctx, result);\n\t\t\tthis.notify();\n\t\t\treturn Promise.resolve({ response: cached, fromCache: true, durationMs: 0 });\n\t\t}\n\n\t\treturn new Promise<SolveDetail>((resolve, reject) => {\n\t\t\tconst item: PendingItem = {\n\t\t\t\tdefinition,\n\t\t\t\tdataTree,\n\t\t\t\thashes,\n\t\t\t\tctx,\n\t\t\t\tseq,\n\t\t\t\tresolve,\n\t\t\t\treject,\n\t\t\t\texternalSignal: options?.signal,\n\t\t\t\tselvaevents: options?.selvaevents,\n\t\t\t\tonServerTiming: options?.onServerTiming\n\t\t\t};\n\n\t\t\tif (item.externalSignal) {\n\t\t\t\titem.onExternalAbort = () => {\n\t\t\t\t\tif (item.controller) item.controller.abort();\n\t\t\t\t\telse this.abortQueuedItem(item);\n\t\t\t\t};\n\t\t\t\titem.externalSignal.addEventListener('abort', item.onExternalAbort, { once: true });\n\t\t\t}\n\n\t\t\tthis.enqueue(item);\n\t\t});\n\t}\n\n\t/** A slow solve settling late must not overwrite state a newer solve or cache hit published. */\n\tprivate writeLastState(\n\t\tseq: number,\n\t\tstate:\n\t\t\t| { result: GrasshopperComputeResponse; durationMs: number }\n\t\t\t| { error: ComputeError; durationMs: number }\n\t): void {\n\t\tif (seq < this.lastStateSeq) return;\n\t\tthis.lastStateSeq = seq;\n\t\tif ('result' in state) {\n\t\t\tthis._lastResult = state.result;\n\t\t\tthis._lastError = null;\n\t\t} else {\n\t\t\tthis._lastError = state.error;\n\t\t}\n\t\tthis._lastDurationMs = state.durationMs;\n\t}\n\n\t/** latest-wins: supersede the pending item and abort everything in flight. */\n\tprivate supersedeCurrent(): void {\n\t\tif (this.pendingForLatestWins) {\n\t\t\tthis.supersede(this.pendingForLatestWins);\n\t\t\tthis.pendingForLatestWins = null;\n\t\t}\n\t\tfor (const inflight of this.inFlight) {\n\t\t\tthis.supersede(inflight);\n\t\t\tinflight.controller.abort();\n\t\t}\n\t}\n\n\tprivate dequeue(item: PendingItem): void {\n\t\tif (this.pendingForLatestWins === item) this.pendingForLatestWins = null;\n\t\tthis.fifo.remove(item);\n\t}\n\n\tprivate abortQueuedItem(item: PendingItem): void {\n\t\tthis.dequeue(item);\n\t\tthis.fail(item, this.makeAbortError(item.ctx));\n\t\tthis.notify();\n\t}\n\n\t/** The structured context lets an HTTP layer map QUEUE_FULL to 503 + Retry-After. */\n\tprivate shedAsQueueFull(item: PendingItem): void {\n\t\tconst err = new ComputeError(\n\t\t\t'Solve queue is full; request rejected (backpressure)',\n\t\t\tErrorCodes.QUEUE_FULL,\n\t\t\t{\n\t\t\t\tstatusCode: 503,\n\t\t\t\tcontext: {\n\t\t\t\t\tqueueDepth: this.fifo.size,\n\t\t\t\t\tmaxQueueDepth: this.maxQueueDepth\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\t\tthis.fail(item, err);\n\t\tthis.notify();\n\t}\n\n\tprivate armQueueWaitTimer(item: PendingItem): void {\n\t\tif (this.queueWaitMs === undefined) return;\n\t\tconst waitMs = this.queueWaitMs;\n\t\titem.queueWaitTimer = setTimeout(() => {\n\t\t\tif (item.settled) return;\n\t\t\tthis.dequeue(item);\n\n\t\t\tconst err = new ComputeError(\n\t\t\t\t`Solve waited longer than ${waitMs}ms in queue; rejected (backpressure)`,\n\t\t\t\tErrorCodes.QUEUE_TIMEOUT,\n\t\t\t\t{\n\t\t\t\t\tstatusCode: 503,\n\t\t\t\t\tcontext: {\n\t\t\t\t\t\twaitedMs: Date.now() - item.ctx.enqueuedAt,\n\t\t\t\t\t\tqueueWaitMs: waitMs\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t);\n\t\t\tthis.fail(item, err);\n\t\t\tthis.notify();\n\t\t}, waitMs);\n\t}\n\n\tprivate clearQueueWaitTimer(item: PendingItem): void {\n\t\tif (item.queueWaitTimer !== undefined) {\n\t\t\tclearTimeout(item.queueWaitTimer);\n\t\t\titem.queueWaitTimer = undefined;\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\tthis.supersedeCurrent();\n\t\t\t\tif (this.inFlight.size === 0) {\n\t\t\t\t\tvoid this.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\t\tif (this.inFlight.size < this.maxConcurrent) {\n\t\t\t\t\tvoid this.execute(item);\n\t\t\t\t} else if (this.maxQueueDepth !== undefined && this.fifo.size >= this.maxQueueDepth) {\n\t\t\t\t\t// Shed the newest call, so already-accepted work keeps its place.\n\t\t\t\t\tthis.shedAsQueueFull(item);\n\t\t\t\t} else {\n\t\t\t\t\tthis.armQueueWaitTimer(item);\n\t\t\t\t\tthis.fifo.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\t// Mutate, don't copy: every settle path must share the one `settled` slot.\n\t\tconst inflight = item as InFlightItem;\n\t\tinflight.controller = controller;\n\t\tthis.inFlight.add(inflight);\n\t\titem.ctx.startedAt = Date.now();\n\n\t\tthis.clearQueueWaitTimer(item);\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\t...(item.selvaevents !== undefined && { selvaevents: item.selvaevents })\n\t\t\t};\n\t\t\tconst callTiming = item.onServerTiming;\n\t\t\tif (callTiming) {\n\t\t\t\tconst baseTiming = this.baseConfig.onServerTiming;\n\t\t\t\tconfig.onServerTiming = (timing, requestId) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tbaseTiming?.(timing, requestId);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tcallTiming(timing, requestId);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst { response, definitionReuploaded } = await this.runExecutor(item, config);\n\t\t\tconst durationMs = performance.now() - startTime;\n\n\t\t\t// Disposed: a late result must not refill the cache `dispose()` emptied.\n\t\t\tif (!this.disposed) this.cache.set(item.ctx.key, response);\n\n\t\t\tif (\n\t\t\t\t!this.settleSuccess(item, { response, fromCache: false, definitionReuploaded, durationMs })\n\t\t\t) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.writeLastState(item.seq, { result: response, durationMs });\n\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\tdefinitionReuploaded\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\tthis.writeLastState(item.seq, { error: err, durationMs });\n\t\t\tthis.fail(item, err, durationMs);\n\t\t} finally {\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 async runExecutor(\n\t\titem: PendingItem,\n\t\tconfig: GrasshopperComputeConfig\n\t): Promise<{ response: GrasshopperComputeResponse; definitionReuploaded?: boolean }> {\n\t\tconst { definition, dataTree } = item;\n\t\tif (!this.reuseServerDefinitionCache || isDefinitionUrl(definition)) {\n\t\t\tconst { response } = await this.executor(definition, dataTree, null, config);\n\t\t\treturn { response };\n\t\t}\n\n\t\tconst definitionHash = item.hashes.definitionHash;\n\t\tconst knownKey = this.serverCacheKeys.get(definitionHash) ?? null;\n\t\tconst result = await this.executor(definition, dataTree, knownKey, config);\n\n\t\t// Delete before set so a hit moves to the back of the eviction order.\n\t\tthis.serverCacheKeys.delete(definitionHash);\n\t\tif (result.cacheKey) {\n\t\t\tthis.serverCacheKeys.set(definitionHash, result.cacheKey);\n\t\t\tif (this.serverCacheKeys.size > SERVER_CACHE_KEYS_MAX) {\n\t\t\t\tthis.serverCacheKeys.delete(this.serverCacheKeys.keys().next().value!);\n\t\t\t}\n\t\t}\n\n\t\treturn { response: result.response, definitionReuploaded: result.missed };\n\t}\n\n\tprivate hashDefinitionMemo(definition: SolveDefinition): string {\n\t\tif (typeof definition !== 'string') return hashDefinitionOnce(definition);\n\t\tif (this.lastStringDefinition?.value !== definition) {\n\t\t\tthis.lastStringDefinition = { value: definition, hash: hashDefinition(definition) };\n\t\t}\n\t\treturn this.lastStringDefinition.hash;\n\t}\n\n\tprivate drainNext(): void {\n\t\tif (this.disposed) return;\n\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\tvoid this.execute(next);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\twhile (this.fifo.size > 0 && this.inFlight.size < this.maxConcurrent) {\n\t\t\tvoid this.execute(this.fifo.shift()!);\n\t\t}\n\t}\n\n\tprivate supersede(item: PendingItem): void {\n\t\tconst err = new ComputeError('Superseded by newer solve', ErrorCodes.SUPERSEDED, {\n\t\t\tcontext: { enqueuedAt: item.ctx.enqueuedAt }\n\t\t});\n\t\tif (this.settleError(item, err)) {\n\t\t\tthis.runHook(this.onSuperseded, item.ctx);\n\t\t}\n\t}\n\n\tprivate makeAbortError(ctx: SolveContext): ComputeError {\n\t\treturn abortedByCaller({ enqueuedAt: ctx.enqueuedAt });\n\t}\n\n\t/**\n\t * The executor, `supersede`, `cancelAll`, abort and the queue limits can all race to settle one\n\t * item, and a promise ignores a second settle silently. The first settle wins; the return value\n\t * says whether this call won, so only the winner fires its hook. Every settle path goes through\n\t * here or {@link settleSuccess}.\n\t */\n\tprivate settleError(item: PendingItem, err: ComputeError): boolean {\n\t\tif (!this.claimSettle(item, { error: err })) return false;\n\t\titem.reject(err);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Fires `onSettle` for queued items too, so a consumer counting solves in progress doesn't\n\t * leak one. A supersede settles through {@link settleError} and fires `onSuperseded` instead.\n\t */\n\tprivate fail(item: PendingItem, err: ComputeError, durationMs = 0): void {\n\t\tif (this.settleError(item, err)) {\n\t\t\tthis.runHook(this.onSettle, item.ctx, { status: 'error', error: err, durationMs });\n\t\t}\n\t}\n\n\tprivate settleSuccess(item: PendingItem, detail: SolveDetail): boolean {\n\t\tif (!this.claimSettle(item, { ok: true })) return false;\n\t\titem.resolve(detail);\n\t\treturn true;\n\t}\n\n\tprivate claimSettle(item: PendingItem, settled: NonNullable<PendingItem['settled']>): boolean {\n\t\tif (item.settled) return false;\n\t\titem.settled = settled;\n\t\tthis.clearQueueWaitTimer(item);\n\t\tif (item.onExternalAbort) {\n\t\t\titem.externalSignal?.removeEventListener('abort', item.onExternalAbort);\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate normalizeExecutionError(error: unknown, item: InFlightItem): ComputeError {\n\t\t// Already settled (superseded, cancelled): keep the original cause, not the abort it caused.\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 ComputeError) return error;\n\n\t\t// Not `isAbortError`: a raw `TimeoutError` is a timeout, and must not read as a caller abort.\n\t\tif (error instanceof Error && error.name === 'AbortError') {\n\t\t\treturn this.makeAbortError(item.ctx);\n\t\t}\n\n\t\treturn new ComputeError(\n\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\tErrorCodes.UNKNOWN_ERROR,\n\t\t\t{ originalError: 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\tif (this.pendingForLatestWins) {\n\t\t\tthis.fail(this.pendingForLatestWins, this.makeAbortError(this.pendingForLatestWins.ctx));\n\t\t\tthis.pendingForLatestWins = null;\n\t\t}\n\t\tfor (let item = this.fifo.shift(); item; item = this.fifo.shift()) {\n\t\t\tthis.fail(item, this.makeAbortError(item.ctx));\n\t\t}\n\t\tfor (const inflight of this.inFlight) {\n\t\t\t// startedAt is a Date.now() timestamp, so measure with the same clock.\n\t\t\tconst durationMs = inflight.ctx.startedAt ? Date.now() - inflight.ctx.startedAt : 0;\n\t\t\tthis.fail(inflight, this.makeAbortError(inflight.ctx), durationMs);\n\t\t\tinflight.controller.abort();\n\t\t}\n\t\tthis.notify();\n\t}\n\n\t// ============================================================================\n\t// Cache\n\t// ============================================================================\n\n\tclearCache(): void {\n\t\tthis.cache.clear();\n\t}\n\n\t/**\n\t * Observability snapshot of the solve cache: current size plus lifetime\n\t * hit/miss/eviction counters.\n\t *\n\t * `hits`/`misses` count cache CONSULTATIONS, so `hits / (hits + misses)` is the\n\t * hit rate. A TTL-expired entry counts as a miss (the solve runs either way).\n\t * `evictions` counts only entries dropped under size/byte pressure — not\n\t * replace-in-place writes or TTL expiry, which are not capacity signals.\n\t * Counters are cumulative and survive `clearCache()`.\n\t */\n\tcacheStats(): CacheStats {\n\t\treturn this.cache.stats();\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.clearCache();\n\t}\n\n\tprivate runHook<H extends (...args: never[]) => 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 { sleep } from '@/core/compute-fetch/retry';\nimport { abortedByCaller, ErrorCodes, ComputeError } from '@/core/errors';\nimport { getLogger } from '@/core/utils/logger';\nimport { readField } from '@/core/utils/read-field';\nimport { redactUrl } from '@/core/utils/redact-url';\nimport ComputeServerStats from '../server/compute-server-stats';\nimport { validateServerUrl } from '@/core/server/validate-server-url';\nimport { classifyProbeFailure, type ProbeFailure } from '@/core/server/classify-probe-failure';\nimport type { RetryPolicy } from '@/core/types';\n\nimport { fetchDefinitionIO, fetchParsedDefinitionIO } from '../io/definition-io';\nimport { solveGrasshopperDefinition, solveWithServerCacheKey } from '../solve';\nimport type {\n\tGrasshopperComputeConfig,\n\tGrasshopperComputeResponse,\n\tDataTree,\n\tSelvaEventTarget\n} from '../types';\nimport { isDefinitionRef, isDefinitionUrl, type SolveDefinition } from '@/core/definition-ref';\nimport { SolveScheduler } from '../scheduler/solve-scheduler';\nimport type { SolveSchedulerOptions } from '../scheduler/types';\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\t/** Per-solve live-event callback, forwarded onto the request body. */\n\tselvaevents?: SelvaEventTarget;\n}\n\n/** Options for {@link GrasshopperClient.create} that shape construction, not the solves. */\nexport interface CreateClientOptions {\n\t/**\n\t * Retry ladder for the liveness probe: `attempts` retries after the first probe (default 2),\n\t * with exponential backoff from `baseDelayMs` (default 250) capped at `maxDelayMs` (default 1000).\n\t * Independent of `config.retry`, which governs solves only.\n\t */\n\tprobe?: { attempts?: number; baseDelayMs?: number; maxDelayMs?: number };\n}\n\nfunction describeDefinition(definition: SolveDefinition): string {\n\tif (isDefinitionRef(definition)) return `ref:${definition.key}`;\n\tif (typeof definition !== 'string' || definition.length >= 200) return '...content...';\n\treturn isDefinitionUrl(definition) ? redactUrl(definition) : definition;\n}\n\n/** Identifiers only: the error's `context` holds outputs and its message the server's words. */\nfunction describeFailure(error: unknown): string {\n\tif (!(error instanceof ComputeError)) {\n\t\treturn `Compute failed: ${error instanceof Error ? error.name : typeof error}`;\n\t}\n\tconst status = error.statusCode === undefined ? '' : ` HTTP ${error.statusCode}`;\n\tconst requestId = error.context?.requestId;\n\treturn `Compute failed: ${error.code}${status}${requestId ? ` [${requestId}]` : ''}`;\n}\n\ninterface DataTreeSummaryEntry {\n\tparam: string;\n\titems: number;\n\t/** Summed `data` string lengths. */\n\tbytes: number;\n}\n\n/**\n * Stands in for the input tree on errors: the tree can hold multi-MB geometry, and every logger\n * or telemetry sink that keeps the error would pin it. Never throws on a malformed tree.\n */\nfunction summarizeDataTree(dataTree: DataTree[]): DataTreeSummaryEntry[] {\n\tif (!Array.isArray(dataTree)) return [];\n\n\treturn dataTree.map((tree) => {\n\t\tlet items = 0;\n\t\tlet bytes = 0;\n\n\t\tconst innerTree = readField<Record<string, unknown>>(tree, 'innerTree');\n\t\tif (innerTree && typeof innerTree === 'object') {\n\t\t\tfor (const branch of Object.values(innerTree)) {\n\t\t\t\tif (!Array.isArray(branch)) continue;\n\t\t\t\titems += branch.length;\n\t\t\t\tfor (const item of branch) {\n\t\t\t\t\tconst data = (item as { data?: unknown } | null)?.data;\n\t\t\t\t\tif (typeof data === 'string') bytes += data.length;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn { param: readField<string>(tree, 'paramName') ?? '<unnamed>', items, bytes };\n\t});\n}\n\n/**\n * The high-level API for solving Grasshopper definitions on a Rhino Compute server.\n *\n * In a browser, the server URL and API key are exposed to users: run it server-side, or proxy\n * 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 io = await client.getIO(definitionUrl);\n * const result = await client.solve(definitionUrl, TreeBuilder.fromInputParams(io.inputs));\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 readonly schedulers = new Set<SolveScheduler>();\n\tprivate disposed = false;\n\n\t/**\n\t * Per-probe timeout for the `create()` liveness gate: a healthy `GET /` answers in milliseconds,\n\t * so this only bounds a silent server, and every extra second is paid once per retry.\n\t */\n\tprivate static readonly CREATE_PROBE_TIMEOUT_MS = 2000;\n\n\tprivate constructor(config: GrasshopperComputeConfig) {\n\t\tthis.config = {\n\t\t\t...config,\n\t\t\tserverUrl: validateServerUrl(config.serverUrl),\n\t\t\tdebug: config.debug ?? false\n\t\t};\n\t\tthis.serverStats = new ComputeServerStats(this.config.serverUrl, this.config);\n\t}\n\n\t/**\n\t * Creates a client once a liveness probe (`GET /`) says the server is up. A cold server can read\n\t * as offline, so the probe retries with a short backoff, stopping early when\n\t * {@link classifyProbeFailure} says waiting can't change the answer. `config.signal` cancels the\n\t * ladder.\n\t *\n\t * @throws {ComputeError} `NETWORK_ERROR` if the server stays unreachable across all attempts.\n\t * `context.probeVerdict` carries the verdict, so a caller can render a specific cause.\n\t * @throws {ComputeError} `ABORTED` if `config.signal` fires first.\n\t * @throws {ComputeError} `INVALID_CONFIG` if the configuration is invalid.\n\t */\n\tstatic async create(\n\t\tconfig: GrasshopperComputeConfig,\n\t\toptions: CreateClientOptions = {}\n\t): Promise<GrasshopperClient> {\n\t\tconst client = new GrasshopperClient(config);\n\t\tconst { signal } = config;\n\n\t\tconst attempts = Math.max(1, (options.probe?.attempts ?? 2) + 1);\n\t\tconst baseDelayMs = options.probe?.baseDelayMs ?? 250;\n\t\tconst maxDelayMs = options.probe?.maxDelayMs ?? 1000;\n\n\t\tlet lastProbe: Awaited<ReturnType<ComputeServerStats['probeServer']>> | undefined;\n\t\tlet failure: ProbeFailure | null = null;\n\t\tlet used = 0;\n\t\tfor (let attempt = 0; attempt < attempts && !signal?.aborted; attempt++) {\n\t\t\tused = attempt + 1;\n\t\t\tlastProbe = await client.serverStats.probeServer(\n\t\t\t\tGrasshopperClient.CREATE_PROBE_TIMEOUT_MS,\n\t\t\t\tsignal\n\t\t\t);\n\t\t\tif (lastProbe.online) {\n\t\t\t\treturn client;\n\t\t\t}\n\n\t\t\tfailure = classifyProbeFailure(lastProbe);\n\t\t\t// Nothing about a refused connection or a rejected key improves by\n\t\t\t// asking again — fail now and let the caller say why.\n\t\t\tif (failure && !failure.retryable) break;\n\n\t\t\tif (attempt < attempts - 1) {\n\t\t\t\tconst delay = Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);\n\t\t\t\t// An abort rejects the sleep; the loop condition then ends the ladder.\n\t\t\t\tawait sleep(delay, signal).catch(() => {});\n\t\t\t}\n\t\t}\n\n\t\tawait client.dispose();\n\t\tif (signal?.aborted) {\n\t\t\tthrow abortedByCaller({ serverUrl: client.config.serverUrl, attempts: used });\n\t\t}\n\t\tconst status = lastProbe?.status;\n\t\tconst message = failure\n\t\t\t? `Rhino Compute server is not available. ${failure.summary}`\n\t\t\t: 'Rhino Compute server is not online';\n\t\tthrow new ComputeError(message, ErrorCodes.NETWORK_ERROR, {\n\t\t\t...(status !== undefined && { statusCode: status }),\n\t\t\tcontext: {\n\t\t\t\tserverUrl: client.config.serverUrl,\n\t\t\t\t// What we actually spent, not the ceiling — an early break makes\n\t\t\t\t// these differ, and the gap is the useful part when reading a log.\n\t\t\t\tattempts: used,\n\t\t\t\tmaxAttempts: attempts,\n\t\t\t\t...(failure && { probeVerdict: failure.verdict, probeRetryable: failure.retryable }),\n\t\t\t\t...(status !== undefined && { lastProbeStatus: status }),\n\t\t\t\t...(lastProbe?.error !== undefined && { lastProbeError: lastProbe.error })\n\t\t\t}\n\t\t});\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 {ComputeError} with code INVALID_INPUT if definition is empty\n\t * @throws {ComputeError} with code NETWORK_ERROR if server is offline\n\t * @throws {ComputeError} with code COMPUTATION_ERROR if computation fails.\n\t * On a partial-success response (some outputs computed, some errored) the\n\t * error's `context.values` carries the outputs that did compute — pass\n\t * `{ values } as GrasshopperComputeResponse` to the response processors to\n\t * render them. `context.inputSummary` describes the inputs (param names,\n\t * item counts, byte sizes) without pinning the full data tree.\n\t */\n\tpublic async solve(\n\t\tdefinition: SolveDefinition,\n\t\tdataTree: DataTree[],\n\t\toptions?: SolveOptions\n\t): Promise<GrasshopperComputeResponse> {\n\t\tthis.ensureNotDisposed();\n\n\t\tconst describeCall = () => ({\n\t\t\tdefinition: describeDefinition(definition),\n\t\t\tinputSummary: summarizeDataTree(dataTree)\n\t\t});\n\n\t\ttry {\n\t\t\tif (typeof definition === 'string' && !definition.trim()) {\n\t\t\t\tthrow new ComputeError('Definition URL/content is required', ErrorCodes.INVALID_INPUT, {\n\t\t\t\t\tcontext: { receivedUrl: definition }\n\t\t\t\t});\n\t\t\t} else if (definition instanceof Uint8Array && definition.length === 0) {\n\t\t\t\tthrow new ComputeError('Definition content is empty', ErrorCodes.INVALID_INPUT);\n\t\t\t} else if (isDefinitionRef(definition) && !definition.key.trim()) {\n\t\t\t\tthrow new ComputeError('DefinitionRef key is empty', ErrorCodes.INVALID_INPUT);\n\t\t\t}\n\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\t...(options?.selvaevents !== undefined && { selvaevents: options.selvaevents })\n\t\t\t};\n\n\t\t\tconst result = await solveGrasshopperDefinition(dataTree, definition, effectiveConfig);\n\n\t\t\t// A partial success (HTTP 500 carrying both `values` and `errors`) must not pass as a result.\n\t\t\tconst solveErrors = readField<unknown[]>(result, 'errors');\n\t\t\tif (Array.isArray(solveErrors) && solveErrors.length > 0) {\n\t\t\t\tthrow new ComputeError(\n\t\t\t\t\tsolveErrors.map(String).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\t...describeCall(),\n\t\t\t\t\t\t\terrors: solveErrors,\n\t\t\t\t\t\t\twarnings: readField<unknown[]>(result, 'warnings'),\n\t\t\t\t\t\t\tvalues: result.values\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(describeFailure(error));\n\t\t\t}\n\n\t\t\tif (error instanceof ComputeError) {\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tthrow new ComputeError(\n\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\tErrorCodes.COMPUTATION_ERROR,\n\t\t\t\t{ context: describeCall(), originalError: error }\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 });\n\t * ```\n\t */\n\tpublic createScheduler(options?: SolveSchedulerOptions): SolveScheduler {\n\t\tthis.ensureNotDisposed();\n\t\tconst scheduler = new SolveScheduler(\n\t\t\t(definition, dataTree, cacheKey, config) =>\n\t\t\t\tsolveWithServerCacheKey(dataTree, cacheKey, definition, config),\n\t\t\tthis.config,\n\t\t\toptions\n\t\t);\n\t\tthis.schedulers.add(scheduler);\n\t\treturn scheduler;\n\t}\n\n\t/** Disposes of client resources, including every scheduler it created. */\n\tpublic async dispose(): Promise<void> {\n\t\tif (this.disposed) return;\n\n\t\tthis.disposed = true;\n\t\tfor (const scheduler of this.schedulers) scheduler.dispose();\n\t\tthis.schedulers.clear();\n\t\tawait this.serverStats.dispose();\n\t}\n\n\tprivate ensureNotDisposed(): void {\n\t\tif (this.disposed) {\n\t\t\tthrow new ComputeError(\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","// Apart from rhino-decoder.ts so the public types never import `rhino3dm`: consumers install it only to decode geometry.\n\n/**\n * Sentinel a Rhino geometry decode returns when a decode was attempted\n * and failed hard (a registered decoder or `CommonObject.decode` threw). Carries\n * the original payload so callers can log or retry. Use\n * {@link isRhinoDecodeError} to detect it.\n */\nexport interface RhinoDecodeError {\n\t__decodeError: true;\n\t/** The Rhino type name the failed decode was attempted for. */\n\ttype: string;\n\t/** The original (undecoded) payload. */\n\traw: unknown;\n}\n\n/** Type guard for the {@link RhinoDecodeError} sentinel. */\nexport function isRhinoDecodeError(value: unknown): value is RhinoDecodeError {\n\treturn (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\t(value as RhinoDecodeError).__decodeError === true\n\t);\n}\n\nexport function makeDecodeError(rhinoType: string, raw: unknown): RhinoDecodeError {\n\treturn { __decodeError: true, type: rhinoType, raw };\n}\n\n/**\n * Free every rhino3dm WASM object reachable from `value`.\n *\n * rhino3dm objects are emscripten bindings — JS GC never reclaims their WASM\n * heap allocation, so everything decoded from a solve response (`getValues`,\n * `getValue`) must be deleted explicitly or the heap\n * grows monotonically across solves (e.g. a UI decoding per slider tick).\n *\n * Walks arrays and plain objects recursively; anything exposing a `delete()`\n * method is treated as a WASM binding and freed (skipped if already deleted).\n * Safe to call more than once and on values containing no WASM objects.\n */\nexport function disposeRhinoObjects(value: unknown): void {\n\t// Aliased references (the same decoded object aggregated under two keys)\n\t// must only be deleted once.\n\tconst seen = new WeakSet<object>();\n\n\tconst walk = (v: unknown): void => {\n\t\tif (!v || typeof v !== 'object') return;\n\t\tif (seen.has(v)) return;\n\t\tseen.add(v);\n\n\t\tconst del = (v as { delete?: unknown }).delete;\n\t\tif (typeof del === 'function') {\n\t\t\tconst isDeleted = (v as { isDeleted?: () => boolean }).isDeleted;\n\t\t\tif (typeof isDeleted !== 'function' || !isDeleted.call(v)) {\n\t\t\t\t(del as () => void).call(v);\n\t\t\t}\n\t\t\treturn; // a WASM binding's internals are not ours to walk\n\t\t}\n\n\t\tif (Array.isArray(v)) {\n\t\t\tfor (const item of v) walk(item);\n\t\t\treturn;\n\t\t}\n\t\t// Only walk plain containers — class instances other than WASM bindings\n\t\t// (Dates, typed arrays, ...) hold nothing decodable.\n\t\tconst proto = Object.getPrototypeOf(v);\n\t\tif (proto === Object.prototype || proto === null) {\n\t\t\tfor (const item of Object.values(v)) walk(item);\n\t\t}\n\t};\n\n\twalk(value);\n}\n","import { getLogger } from '@/core/utils/logger';\nimport { makeDecodeError } from './rhino-objects';\n\ninterface XYZ {\n\tX: number;\n\tY: number;\n\tZ: number;\n}\n\n/**\n * The part of rhino3dm the decoders call. Structural so the public types don't name `rhino3dm`, a\n * dev dependency here. A loaded `RhinoModule` satisfies it.\n */\nexport interface RhinoRuntime {\n\tPoint: new (location: number[]) => unknown;\n\tLine: new (from: number[], to: number[]) => unknown;\n\tCommonObject: { decode(jsonObject: object): unknown };\n}\n\ntype RhinoDecoder = (rhino: RhinoRuntime, data: unknown) => unknown;\n\nconst decoders = new Map<string, RhinoDecoder>([\n\t[\n\t\t'Rhino.Geometry.Point3d',\n\t\t(rhino, data) => {\n\t\t\tconst d = data as Partial<XYZ> | null;\n\t\t\tif (!d || typeof d.X !== 'number') return null;\n\t\t\treturn new rhino.Point([d.X, d.Y as number, d.Z as number]);\n\t\t}\n\t],\n\t[\n\t\t'Rhino.Geometry.Line',\n\t\t(rhino, data) => {\n\t\t\tconst d = data as { From?: XYZ; To?: XYZ } | null;\n\t\t\tif (!d || !d.From || !d.To) return null;\n\t\t\treturn new rhino.Line([d.From.X, d.From.Y, d.From.Z], [d.To.X, d.To.Y, d.To.Z]);\n\t\t}\n\t]\n]);\n\n/**\n * Whether the parsed item looks like a rhino3dm serialization envelope\n * (`{version, archive3dm, opennurbs, data: \"<base64>\"}`). `CommonObject.decode` expects the WHOLE\n * envelope — unwrapping `.data` first hands it the bare base64 string, which throws or decodes to\n * garbage.\n */\nfunction isDecodableEnvelope(parsedData: unknown): parsedData is object {\n\treturn Boolean(\n\t\tparsedData &&\n\t\ttypeof parsedData === 'object' &&\n\t\ttypeof (parsedData as { data?: unknown }).data === 'string'\n\t);\n}\n\n/**\n * Decode one typed payload from a solve response into a rhino3dm object.\n *\n * Every call yields exactly one of three outcomes:\n *\n * 1. **Decoded value** — a registered decoder recognized the shape, or the\n * payload was a rhino3dm serialization envelope and `CommonObject.decode`\n * succeeded.\n * 2. **Raw passthrough** (`=== parsedData`) — no decode path *applied*: no\n * decoder matched the type (or the matched decoder returned `null`, meaning\n * \"not my shape\") and the payload is not a decodable envelope. Not an error;\n * the caller keeps the parsed JSON as-is.\n * 3. **{@link RhinoDecodeError} sentinel** — a decode was *attempted and threw*\n * (a registered decoder threw and no envelope fallback could recover, or\n * `CommonObject.decode` threw). Detect with {@link isRhinoDecodeError}.\n *\n * A decoder returning `null` is a soft miss, never an error: it falls through\n * to the envelope fallback.\n */\nexport function decodeRhinoGeometry(\n\tparsedData: unknown,\n\trhinoType: string,\n\trhino: RhinoRuntime\n): unknown {\n\tconst decoder = decoders.get(rhinoType);\n\t// A throwing decoder is a hard failure (unlike a null return, which is a\n\t// soft \"not my shape\" miss) — remember it so that when no envelope fallback\n\t// can recover we return the error sentinel instead of silently passing the\n\t// raw payload through as if no decode had ever been attempted.\n\tlet decoderThrew = false;\n\tif (decoder) {\n\t\ttry {\n\t\t\tconst decoded = decoder(rhino, parsedData);\n\t\t\tif (decoded != null) return decoded;\n\t\t} catch (error) {\n\t\t\tgetLogger().warn(`Failed to decode Rhino type ${rhinoType}:`, error);\n\t\t\tdecoderThrew = true;\n\t\t}\n\t}\n\n\t// Fallback using CommonObject.decode — fed the full envelope, not the unwrapped payload.\n\ttry {\n\t\tif (isDecodableEnvelope(parsedData)) return rhino.CommonObject.decode(parsedData);\n\t} catch (error) {\n\t\tgetLogger().warn(`Failed to decode ${rhinoType} with CommonObject:`, error);\n\t\treturn makeDecodeError(rhinoType, parsedData);\n\t}\n\n\treturn decoderThrew ? makeDecodeError(rhinoType, parsedData) : parsedData;\n}\n","import { isBase64Flag } from '@/core/files/handle-files';\nimport type { FileData } from '@/core/files/types';\nimport { hasField, readField } from '@/core/utils/read-field';\nimport { setOwnField } from '@/core/utils/set-own-field';\nimport { tryParseJson } from '@/core/utils/try-parse-json';\nimport type { GrasshopperComputeResponse, DataItem } from '../../types';\nimport {\n\tBOOLEAN_ITEM_TYPE,\n\tINTEGER_ITEM_TYPES,\n\tNUMERIC_ITEM_TYPES,\n\tSTRING_ITEM_TYPE\n} from '../wire-item-types';\nimport { decodeRhinoGeometry, type RhinoRuntime } from './rhino-decoder';\nimport { disposeRhinoObjects } from './rhino-objects';\n\nexport interface ParsedContext {\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any -- output shapes are the definition's; `T` narrows them\n\t[key: string]: any;\n}\n\nexport interface GetValuesOptions {\n\tparseValues?: boolean;\n\trhino?: RhinoRuntime;\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\t/**\n\t * Free every rhino3dm WASM object decoded into `values`. When a `rhino`\n\t * instance was passed, decoded geometry lives on the WASM heap and is never\n\t * garbage-collected — call this once the values are consumed, or the heap\n\t * grows monotonically across solves. Idempotent; a no-op when nothing was\n\t * decoded. `values` must not be used after disposal.\n\t */\n\tdispose: () => void;\n}\n\nconst RHINO_GEOMETRY_PREFIX = 'Rhino.Geometry.';\n\nconst FILE_DATA_TYPE = 'FileData';\n\n/** WebDisplay items are skipped entirely: kept, they would write `null` into the result. */\nfunction skipItem(type: string, stringOnly: boolean): boolean {\n\treturn type.includes('WebDisplay') || (stringOnly && type !== STRING_ITEM_TYPE);\n}\n\nconst looksStructured = (text: string): boolean => text.startsWith('{') || text.startsWith('[');\n\nfunction tryDecodeJSON(value: string): unknown {\n\tif (typeof value !== 'string') return value;\n\n\tconst trimmed = value.trim();\n\tif (!looksStructured(trimmed) && !trimmed.startsWith('\"')) return value;\n\n\tconst first = tryParseJson(trimmed) ?? value;\n\t// Only structure is decoded twice: a text output of `\"123\"` or `\"true\"` stays text, and a long\n\t// digit string keeps its digits.\n\treturn typeof first === 'string' && looksStructured(first.trim())\n\t\t? (tryParseJson(first) ?? first)\n\t\t: first;\n}\n\nfunction decodeBySystemType(raw: unknown, type: string, rhino?: RhinoRuntime): unknown {\n\tif (type === STRING_ITEM_TYPE) {\n\t\treturn typeof raw === 'string' ? raw.replace(/^\"(.*)\"$/, '$1') : raw;\n\t}\n\tif (INTEGER_ITEM_TYPES.has(type)) {\n\t\tconst value = Number.parseInt(String(raw), 10);\n\t\t// A `System.Int64` past 2^53 stays as its digits: as a number it would silently round.\n\t\treturn Number.isNaN(value) || Number.isSafeInteger(value) ? value : raw;\n\t}\n\tif (NUMERIC_ITEM_TYPES.has(type)) return Number.parseFloat(String(raw));\n\tif (type === BOOLEAN_ITEM_TYPE) return String(raw).toLowerCase() === 'true';\n\tif (rhino && type.startsWith(RHINO_GEOMETRY_PREFIX)) {\n\t\treturn decodeRhinoGeometry(raw, type, rhino);\n\t}\n\treturn raw;\n}\n\nfunction extractItemValue(\n\titem: DataItem,\n\ttype: string,\n\tparseValues: boolean,\n\trhino?: RhinoRuntime\n): unknown {\n\tif (typeof item.data !== 'string') return item.data;\n\n\tconst raw = parseValues ? tryDecodeJSON(item.data) : item.data;\n\tif (parseValues && type.includes(FILE_DATA_TYPE)) {\n\t\treturn asFileData(raw) ?? raw;\n\t}\n\n\treturn decodeBySystemType(raw, type, rhino);\n}\n\n/**\n * A parsed `FileData` blob in the camelCase shape {@link FileData} documents, or\n * `null` when a required field is missing. Fields are read with `readField`,\n * since their casing varies by server branch.\n */\nfunction asFileData(value: unknown): FileData | null {\n\tif (!value || typeof value !== 'object') return null;\n\n\tconst fileName = readField<unknown>(value, 'fileName');\n\tconst fileType = readField<unknown>(value, 'fileType');\n\tconst subFolder = readField<unknown>(value, 'subFolder');\n\tconst flag = readField<unknown>(value, 'isBase64Encoded');\n\n\tif (typeof fileName !== 'string' || typeof fileType !== 'string') return null;\n\tif (typeof subFolder !== 'string') return null;\n\t// Presence, not type: `data` may legitimately be '' and is validated downstream.\n\tif (!hasField(value, 'data')) return null;\n\t// Reject a flag that is neither a boolean nor a boolean-ish string, rather\n\t// than coercing arbitrary values to `false` and mis-decoding the payload.\n\tif (typeof flag !== 'boolean' && typeof flag !== 'string') return null;\n\n\tconst metadata = readField<Record<string, string>>(value, 'metadata');\n\n\treturn {\n\t\tfileName,\n\t\tfileType,\n\t\tdata: readField<string>(value, 'data') as string,\n\t\tisBase64Encoded: isBase64Flag(flag),\n\t\tsubFolder,\n\t\t...(metadata ? { metadata } : {})\n\t};\n}\n\n/** Partial-success and warnings-only responses can arrive with no `values` at all. */\nfunction getResponseParams(response: GrasshopperComputeResponse): unknown[] {\n\tconst values = readField<unknown[]>(response, 'values');\n\treturn Array.isArray(values) ? values : [];\n}\n\n/** A param's item type, tolerating items whose `type` is missing/non-string. */\nfunction itemType(item: DataItem): string {\n\treturn typeof item.type === 'string' ? item.type : '';\n}\n\n/** Tolerates a missing tree (warnings-only partial successes) and skips non-object items. */\nfunction forEachTreeItem(tree: unknown, handler: (item: DataItem) => void) {\n\tif (!tree || typeof tree !== 'object') return;\n\tfor (const list of Object.values(tree)) {\n\t\tif (Array.isArray(list)) {\n\t\t\tfor (const item of list) {\n\t\t\t\tif (item && typeof item === 'object') handler(item as DataItem);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Read all output values from a Grasshopper Compute response, keyed by parameter\n * name (or ID when `byId`). Duplicate keys aggregate into an array.\n *\n * Geometry decoded via `rhino` lives on the WASM heap: free it with `dispose()`.\n *\n * @param options.parseValues - Parse complex data types into JS objects (default true).\n * @param options.rhino - Rhino3dm instance for geometry decoding.\n * @param options.stringOnly - Keep only string-typed items.\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\t// Keys holding an aggregation array (vs. a single value that happens to BE an array,\n\t// e.g. parsed JSON `[1,2,3]`) — `Array.isArray(result[key])` can't tell those apart.\n\tconst aggregated = new Set<string>();\n\n\tfor (const param of getResponseParams(response)) {\n\t\tconst paramName = readField<string>(param, 'paramName');\n\t\tforEachTreeItem(readField(param, 'innerTree'), (item) => {\n\t\t\tconst type = itemType(item);\n\t\t\tif (skipItem(type, stringOnly)) return;\n\n\t\t\tconst key = byId ? item.id : paramName;\n\t\t\tif (!key) return;\n\n\t\t\tconst value = extractItemValue(item, type, parseValues, rhino);\n\n\t\t\t// Own keys only: an output named `constructor` must not find Object.prototype's.\n\t\t\tif (!Object.hasOwn(result, key)) {\n\t\t\t\tsetOwnField(result, key, value);\n\t\t\t} else if (aggregated.has(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\taggregated.add(key);\n\t\t\t}\n\t\t});\n\t}\n\n\treturn { values: result as T, dispose: () => disposeRhinoObjects(result) };\n}\n\n/** Decode every file-data item in a response into {@link FileData} objects. */\nexport function extractFileData(response: GrasshopperComputeResponse): FileData[] {\n\tconst output: FileData[] = [];\n\n\tfor (const param of getResponseParams(response)) {\n\t\tforEachTreeItem(readField(param, 'innerTree'), (item) => {\n\t\t\tif (!itemType(item).includes(FILE_DATA_TYPE)) return;\n\n\t\t\tconst file = asFileData(tryDecodeJSON(item.data));\n\t\t\tif (file) {\n\t\t\t\toutput.push(file);\n\t\t\t}\n\t\t});\n\t}\n\n\treturn output;\n}\n\n/**\n * Read one parameter's value(s) from a response — `byName` matches a `ParamName`,\n * `byId` matches an item ID. Returns `undefined` if absent, a single value for one\n * match, or an array for several.\n *\n * Geometry decoded via `rhino` lives on the WASM heap: free it with\n * {@link disposeRhinoObjects} once consumed.\n *\n * @param parseOptions.parseValues - Parse raw data into formatted values (default true).\n * @param parseOptions.rhino - Rhino3dm instance for geometry decoding.\n * @param parseOptions.stringOnly - Keep only string-typed items.\n */\nexport function getValue(\n\tresponse: GrasshopperComputeResponse,\n\toptions: { byName: string } | { byId: string },\n\tparseOptions: GetValuesOptions = {}\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any -- the shape is the definition's\n): any {\n\tconst { parseValues = true, rhino, stringOnly = false } = parseOptions;\n\n\t// The first param that matches, by name or by holding the target id, is the target.\n\tfor (const param of getResponseParams(response)) {\n\t\tconst tree = readField(param, 'innerTree');\n\n\t\tlet matched = false;\n\t\tif ('byName' in options) {\n\t\t\tif (readField<string>(param, 'paramName') !== options.byName) continue;\n\t\t\tmatched = true;\n\t\t}\n\n\t\tconst collected: unknown[] = [];\n\t\tforEachTreeItem(tree, (item) => {\n\t\t\tif ('byId' in options) {\n\t\t\t\tif (item.id !== options.byId) return;\n\t\t\t\t// Found but filtered out below still ends the scan, returning undefined.\n\t\t\t\tmatched = true;\n\t\t\t}\n\t\t\tconst type = itemType(item);\n\t\t\tif (skipItem(type, stringOnly)) return;\n\t\t\tcollected.push(extractItemValue(item, type, parseValues, rhino));\n\t\t});\n\n\t\tif (matched) {\n\t\t\tif (collected.length === 0) return undefined;\n\t\t\tif (collected.length === 1) return collected[0];\n\t\t\treturn collected;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n","import { downloadFileData } from '@/core/files/handle-files';\nimport type { FileBaseInfo } from '@/core/files/types';\n\nimport type { GrasshopperComputeResponse } from '../types';\n\nimport {\n\textractFileData,\n\tgetValue,\n\tgetValues,\n\ttype GetValuesOptions,\n\ttype GetValuesResult,\n\ttype ParsedContext\n} from '../io/output/response-processors';\n\n/** Reads parsed values and produced files out of one compute response. */\nexport default class GrasshopperResponseProcessor {\n\tconstructor(\n\t\t/** Hand it to a mesh parser such as `@selvajs/visualization/parse`. */\n\t\tpublic readonly response: GrasshopperComputeResponse\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 - Key by parameter ID instead of name. Requires the VektorNode rhino.compute branch.\n\t * @param options - Controls parsing behavior such as Rhino geometry decoding.\n\t * @returns Parsed Grasshopper output values.\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 */\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 * `byId` requires the VektorNode rhino.compute branch.\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\tpublic getValue(\n\t\tselector: { byName: string } | { byId: string },\n\t\toptions?: GetValuesOptions\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any -- the shape is the definition's\n\t): any {\n\t\treturn getValue(this.response, selector, options);\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 * @param folderName - Name of the zip archive, without the extension.\n\t * @param additionalFiles - Extra files to package (single file, array, or null).\n\t *\n\t * @example\n\t * ```ts\n\t * await processor.getAndDownloadFiles('gh-output');\n\t * ```\n\t *\n\t * @example\n\t * ```ts\n\t * const extra = { fileName: 'notes.txt', filePath: 'https://example.com/notes.txt' };\n\t * await processor.getAndDownloadFiles('project', extra);\n\t * ```\n\t */\n\tpublic async getAndDownloadFiles(\n\t\tfolderName: string,\n\t\tadditionalFiles?: FileBaseInfo[] | FileBaseInfo | null\n\t): Promise<void> {\n\t\tawait downloadFileData(extractFileData(this.response), folderName, additionalFiles);\n\t}\n}\n","import { readField } from '@/core/utils/read-field';\n\n/** One entry of the schema endpoint's response, after wrapper-key normalization. */\nexport interface SchemaEndpointResult<TSchema = unknown> {\n\t/** Schemas embedded in that file. Absent when the file yielded none. */\n\tschemas?: TSchema[];\n\t/** Per-file diagnosis. Compute reports this and still answers 200. */\n\terror?: string;\n}\n\n/**\n * Read compute's `/grasshopper/schema` body: `[{ FileName, Schemas }]` per\n * uploaded file, or a bare object for a single file.\n *\n * Use this rather than unwrapping the body by hand: the wrapper keys' casing\n * varies by server branch, and a fixed-key read yields \"no schemas\" with a 200.\n * Only the two wrapper keys are read; schema contents pass through untouched.\n *\n * @typeParam TSchema - Your schema type (e.g. `UISchema`). Not inspected.\n */\nexport function readSchemaResults<TSchema = unknown>(\n\traw: unknown\n): SchemaEndpointResult<TSchema>[] {\n\tconst entries = Array.isArray(raw) ? raw : [raw];\n\treturn entries.map((entry) => ({\n\t\tschemas: readField<TSchema[]>(entry, 'schemas'),\n\t\terror: readField<string>(entry, 'error')\n\t}));\n}\n","/**\n * Canonicalize a `/grasshopper/schema` body's key casing.\n *\n * Compute serializes the plugin's `UISchema`, whose camelCase wire names live in\n * Newtonsoft `[JsonProperty]` attributes. `Selva.gha` ILRepack-merges Newtonsoft,\n * so those attributes are `Selva!Newtonsoft.Json.JsonPropertyAttribute`. When\n * compute's OWN Newtonsoft serializes, it doesn't recognize them, reads no\n * attributes, and emits raw CLR names (`Inputs`, `SchemaVersion`). Nothing throws:\n * `schema.inputs` reads `undefined`, and so does the `schemaVersion` gate.\n *\n * A casing rule, not a key allowlist: the wire names are the CLR names run\n * through Newtonsoft's camelCase strategy, so reproducing it covers every key\n * without drifting each time `UISchema` gains a field.\n */\n\nimport { setOwnField } from '@/core/utils/set-own-field';\n\n/** Maps keyed by the definition's author (dropdown labels): copied verbatim. */\nconst USER_AUTHORED_MAPS = new Set(['options', 'defaultOptions', 'values']);\n\n/** Maps with author or hash keys whose entries are schema objects: keys verbatim, entries normalized. */\nconst USER_KEYED_OBJECT_MAPS = new Set(['optionMedia', 'assets']);\n\nconst isUpper = (ch: string): boolean => ch >= 'A' && ch <= 'Z';\n\n/**\n * Newtonsoft's `CamelCaseNamingStrategy`: a leading run of capitals is lowercased\n * whole, except the last one when a lowercase word follows. `UV` → `uv`,\n * `UVMapping` → `uvMapping` (a first-character rule gives `uVMapping`).\n */\nfunction toCamelCase(key: string): string {\n\tif (key.length === 0 || !isUpper(key[0])) return key;\n\n\tlet run = 0;\n\twhile (run < key.length && isUpper(key[run])) run++;\n\t// `UVMapping`: the `M` opens the next word, so it keeps its capital.\n\tif (run > 1 && run < key.length) run--;\n\n\treturn key.slice(0, run).toLowerCase() + key.slice(run);\n}\n\n/**\n * Returns a schema whose structural keys are camelCase, whichever Newtonsoft\n * serialized it. Always a fresh object the caller may mutate; non-objects are\n * returned as-is.\n */\nexport function normalizeUISchemaCasing<T>(raw: T): T {\n\treturn normalizeValue(raw) as T;\n}\n\nfunction normalizeValue(value: unknown): unknown {\n\tif (Array.isArray(value)) return value.map(normalizeValue);\n\tif (value === null || typeof value !== 'object') return value;\n\n\tconst out: Record<string, unknown> = {};\n\tfor (const [key, child] of Object.entries(value as Record<string, unknown>)) {\n\t\tconst canonical = toCamelCase(key);\n\t\tconst normalized = USER_AUTHORED_MAPS.has(canonical)\n\t\t\t? child\n\t\t\t: USER_KEYED_OBJECT_MAPS.has(canonical)\n\t\t\t\t? normalizeEntries(child)\n\t\t\t\t: normalizeValue(child);\n\t\tsetOwnField(out, canonical, normalized);\n\t}\n\treturn out;\n}\n\nfunction normalizeEntries(value: unknown): unknown {\n\tif (value === null || typeof value !== 'object' || Array.isArray(value)) return value;\n\n\tconst out: Record<string, unknown> = {};\n\tfor (const [key, child] of Object.entries(value as Record<string, unknown>)) {\n\t\tsetOwnField(out, key, normalizeValue(child));\n\t}\n\treturn out;\n}\n","import type { DataTreeDefault, DataTreePath, InputParam, DataTree, InnerTreeData } from '../types';\nimport { ComputeError, ErrorCodes } from '@/core/errors';\nimport { getLogger } from '@/core/utils/logger';\nimport { readField } from '@/core/utils/read-field';\nimport { booleanTransformer, numericTransformer } from '../io/input/transformers';\nimport { isDataTreeDefault, TREE_PATH_RE } from './tree-path';\n\nexport type DataTreeValue = string | number | boolean | object | null;\n\n/** One input's value for {@link TreeBuilder.fromValues}. */\nexport interface TreeValueEntry {\n\tparamName: string;\n\t/** Matched case-insensitively. */\n\tparamType: string;\n\t/** A scalar, an array (branch `{0}`), or a branch-path map like `{ '{0;1}': [1, 2] }`. */\n\tvalue: unknown;\n\tminimum?: number | null;\n\tmaximum?: number | null;\n}\n\n/** A request item. Unlike a response `DataItem`, Rhino Compute takes boolean/number primitives here. */\ninterface ComputeDataItem {\n\tdata: string | boolean | number;\n}\n\ntype ComputeInnerTreeData = {\n\t[path in DataTreePath]: ComputeDataItem[];\n};\n\n/**\n * Builds the Grasshopper data trees a solve takes as input.\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/** Append values to the branch at `path` (`[0, 1]` is `{0;1}`). A `null` item is skipped. */\n\tpublic append(path: number[], items: DataTreeValue[]): this {\n\t\tconst branch = (this.innerTree[TreeBuilder.formatPathString(path)] ??= []);\n\t\t// A loop, not push(...items): spreading a few hundred thousand items overflows the stack.\n\t\tfor (const item of items) {\n\t\t\tif (item !== null) branch.push({ data: TreeBuilder.serializeValue(item) });\n\t\t}\n\t\treturn this;\n\t}\n\n\t/** Replace the tree with a branch-path map like `{ '{0;1}': [1, 2] }`. */\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/** Append a value, or each value of an array, to branch `{0}`. */\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/** The tree in the shape a solve request sends. */\n\tpublic toComputeFormat(): DataTree {\n\t\treturn {\n\t\t\tParamName: this.paramName,\n\t\t\t// `DataTree` types the response item shape; a request item is looser.\n\t\t\tInnerTree: this.innerTree as unknown as InnerTreeData\n\t\t};\n\t}\n\n\t/**\n\t * One tree per input that has a default, keyed by its nickname. Defaults are coerced and\n\t * clamped by the same rules as {@link TreeBuilder.fromValues}.\n\t */\n\tpublic static fromInputParams(inputs: InputParam[]): DataTree[] {\n\t\treturn TreeBuilder.fromValues(\n\t\t\tinputs.map((input) => ({\n\t\t\t\tparamName: input.nickname || 'unnamed',\n\t\t\t\tparamType: input.paramType,\n\t\t\t\tvalue: input.default,\n\t\t\t\t...('minimum' in input && { minimum: input.minimum, maximum: input.maximum })\n\t\t\t}))\n\t\t);\n\t}\n\n\t/**\n\t * One tree per entry, coercing UI values the way Grasshopper reads them. Number and Integer\n\t * items go through the numeric coercion (Integer rounds) and are clamped to\n\t * `minimum`/`maximum`; Boolean items also accept `'true'`/`'false'`; every other type passes\n\t * through. An item that fails coercion is dropped with a warning. An entry left with no items\n\t * is omitted, so Grasshopper uses its own value; `''` is an item, not an empty value.\n\t */\n\tpublic static fromValues(entries: TreeValueEntry[]): DataTree[] {\n\t\tconst trees: DataTree[] = [];\n\n\t\tfor (const entry of entries) {\n\t\t\tconst tree = new TreeBuilder(entry.paramName);\n\t\t\tconst coerce = TreeBuilder.itemCoercer(entry);\n\n\t\t\tfor (const [path, items] of TreeBuilder.branchesOf(entry.value)) {\n\t\t\t\tconst kept: DataTreeValue[] = [];\n\t\t\t\tfor (const item of items) {\n\t\t\t\t\tif (item === null || item === undefined) continue;\n\t\t\t\t\tconst coerced = coerce(item);\n\t\t\t\t\tif (coerced === null) {\n\t\t\t\t\t\tgetLogger().warn(\n\t\t\t\t\t\t\t`${entry.paramName}: dropped an item that is not a valid ${entry.paramType}`\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tkept.push(coerced);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (kept.length > 0) tree.append(path, kept);\n\t\t\t}\n\n\t\t\tif (Object.keys(tree.innerTree).length > 0) trees.push(tree.toComputeFormat());\n\t\t}\n\n\t\treturn trees;\n\t}\n\n\tprivate static branchesOf(value: unknown): [number[], unknown[]][] {\n\t\tif (!TreeBuilder.hasValidValue(value)) return [];\n\t\tif (isDataTreeDefault(value)) {\n\t\t\treturn Object.entries(value).map(([path, items]) => [\n\t\t\t\tTreeBuilder.parsePathString(path),\n\t\t\t\titems as unknown[]\n\t\t\t]);\n\t\t}\n\t\treturn [[[0], Array.isArray(value) ? value : [value]]];\n\t}\n\n\t/** `null` means the item was rejected. */\n\tprivate static itemCoercer(entry: TreeValueEntry): (item: unknown) => DataTreeValue | null {\n\t\tconst { paramName, minimum, maximum } = entry;\n\t\tconst paramType = entry.paramType.toLowerCase();\n\n\t\tif (paramType === 'number' || paramType === 'integer') {\n\t\t\treturn (item) => {\n\t\t\t\tconst n = numericTransformer(item);\n\t\t\t\tif (n === null) return null;\n\t\t\t\tconst value = paramType === 'integer' ? Math.round(n) : n;\n\t\t\t\treturn TreeBuilder.clampValue(value, minimum, maximum, paramName);\n\t\t\t};\n\t\t}\n\t\tif (paramType === 'boolean') return booleanTransformer;\n\t\treturn (item) => item as DataTreeValue;\n\t}\n\n\t/**\n\t * Set or add `paramName`'s tree to `newValue`: a scalar, an array (branch `{0}`), or a\n\t * branch-path map. Copy-on-write: returns a new array and leaves the caller's untouched, so\n\t * assign the result.\n\t */\n\tpublic static replaceTreeValue(\n\t\ttrees: DataTree[],\n\t\tparamName: string,\n\t\tnewValue: DataTreeValue\n\t): DataTree[] {\n\t\tconst compiled = TreeBuilder.buildFromValue(paramName, newValue).toComputeFormat();\n\t\tconst result = trees.slice();\n\t\tconst idx = result.findIndex((t) => TreeBuilder.paramNameOf(t) === paramName);\n\t\tif (idx === -1) result.push(compiled);\n\t\telse result[idx] = compiled;\n\t\treturn result;\n\t}\n\n\tprivate static buildFromValue(paramName: string, value: DataTreeValue): TreeBuilder {\n\t\tconst tree = new TreeBuilder(paramName);\n\t\tif (isDataTreeDefault(value)) {\n\t\t\ttree.fromDataTreeDefault(value);\n\t\t} else {\n\t\t\ttree.appendFlat(value);\n\t\t}\n\t\treturn tree;\n\t}\n\n\t/**\n\t * `paramName`'s whole tree from a solve result's `values`, keyed by branch path (`'{0;1}'`).\n\t * `null` when no tree has that param, an empty `Map` when it has no branches. An item without\n\t * `data` reads as `null` in place, so indices don't shift.\n\t */\n\tpublic static getTree(\n\t\ttrees: DataTree[],\n\t\tparamName: string\n\t): Map<DataTreePath, DataTreeValue[]> | null {\n\t\tconst tree = trees.find((t) => TreeBuilder.paramNameOf(t) === paramName);\n\t\tif (!tree) return null;\n\n\t\tconst branches = new Map<DataTreePath, DataTreeValue[]>();\n\t\tconst inner = readField(tree, 'InnerTree');\n\t\tif (!inner || typeof inner !== 'object') return branches;\n\n\t\tfor (const [path, items] of Object.entries(inner)) {\n\t\t\tif (!Array.isArray(items)) continue;\n\t\t\tbranches.set(\n\t\t\t\tpath as DataTreePath,\n\t\t\t\titems.map((item) =>\n\t\t\t\t\titem?.data !== undefined ? TreeBuilder.deserializeValue(item.data) : null\n\t\t\t\t)\n\t\t\t);\n\t\t}\n\t\treturn branches;\n\t}\n\n\t/**\n\t * `paramName`'s value when its tree has a single branch: one item unwrapped (`5`, not `[5]`),\n\t * several as an array. `null` for a missing param or an empty tree.\n\t *\n\t * @throws {ComputeError} `INVALID_INPUT` when the tree has more than one branch; read those\n\t * with {@link TreeBuilder.getTree}.\n\t */\n\tpublic static getTreeValue(trees: DataTree[], paramName: string): DataTreeValue | null {\n\t\tconst tree = TreeBuilder.getTree(trees, paramName);\n\t\tif (!tree || tree.size === 0) return null;\n\t\tif (tree.size > 1) {\n\t\t\tthrow new ComputeError(\n\t\t\t\t`\"${paramName}\" has ${tree.size} branches; read it with TreeBuilder.getTree`,\n\t\t\t\tErrorCodes.INVALID_INPUT,\n\t\t\t\t{ context: { paramName, branchCount: tree.size } }\n\t\t\t);\n\t\t}\n\n\t\tconst [items] = tree.values();\n\t\tif (items.length === 0) return null;\n\t\treturn items.length === 1 ? items[0] : items;\n\t}\n\n\tprivate static paramNameOf(tree: DataTree): unknown {\n\t\treturn readField(tree, 'ParamName');\n\t}\n\n\t/**\n\t * Parse a branch path like `\"{0;1;2}\"` into `[0, 1, 2]`.\n\t *\n\t * @throws {ComputeError} `INVALID_INPUT` for a malformed path, rather than\n\t * collapsing it to a default branch where two bad keys would merge.\n\t */\n\tpublic static parsePathString(pathStr: string): number[] {\n\t\tconst match = pathStr.match(TREE_PATH_RE);\n\t\tif (!match) {\n\t\t\tthrow new ComputeError(\n\t\t\t\t`Invalid Grasshopper tree path: \"${pathStr}\". ` +\n\t\t\t\t\t`Expected \"{}\", \"{0}\", or \"{0;1;2}\" (negative indices allowed, no empty segments).`,\n\t\t\t\tErrorCodes.INVALID_INPUT,\n\t\t\t\t{ context: { pathStr } }\n\t\t\t);\n\t\t}\n\t\tif (!match[1]) return [];\n\t\treturn match[1].split(';').map(Number);\n\t}\n\n\t/** `[0, 1, 2]` → `\"{0;1;2}\"`. */\n\tpublic static formatPathString(path: number[]): DataTreePath {\n\t\treturn `{${path.join(';')}}` as DataTreePath;\n\t}\n\n\t/** Booleans and numbers stay primitives: Grasshopper reads them typed. */\n\tprivate static serializeValue(\n\t\tvalue: string | number | boolean | object\n\t): ComputeDataItem['data'] {\n\t\treturn typeof value === 'object' ? JSON.stringify(value) : value;\n\t}\n\n\tprivate static deserializeValue(data: unknown): DataTreeValue {\n\t\tif (typeof data !== 'string') return data as DataTreeValue;\n\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// Coerce to number only when the string is the *canonical* form of a\n\t\t// finite number, i.e. it round-trips exactly (String(Number(s)) === s).\n\t\t// API responses encode numbers as strings ('42', '3.14', '1e+21'), and\n\t\t// those must come back numeric — but non-canonical numeric-looking\n\t\t// strings ('007', '1e5', 'Infinity', '', ' 5') were strings on the\n\t\t// wire and must stay strings.\n\t\tconst num = Number(data);\n\t\tif (Number.isFinite(num) && String(num) === data) {\n\t\t\treturn num;\n\t\t}\n\t\tif (data === 'true') return true;\n\t\tif (data === 'false') return false;\n\t\treturn data;\n\t}\n\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\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":";;;;;;;;;;;;;;;;;;;;;AAmDA,IAAqB,qBAArB,MAAqB,mBAAmB;CACvC;CACA;CACA,WAAmB;CACnB,iCAA0C,IAAI,IAAI;;CAGlD,OAAwB,qBAAqB;;CAG7C,OAAwB,uBAAuB;;CAG/C,OAAwB,0BAA0B;;CAGlD,OAAwB,iBAAiB,KAAK;;;;;;CAO9C,YAAY,WAAmB,MAA6B;EAC3D,KAAK,YAAY,kBAAkB,SAAS;EAC5C,KAAK,UAAU,iBAAiB,OAAO,SAAS,WAAW,EAAE,QAAQ,KAAK,IAAK,QAAQ,CAAC,CAAE;CAC3F;;;;;CAMA,MAAc,iBACb,KACA,OAAoB,CAAC,GACrB,YAAoB,mBAAmB,oBACnB;EACpB,MAAM,EAAE,QAAQ,YAAY,cAAc,KAAK,UAAU,KAAA,GAAW,SAAS;EAC7E,IAAI;GACH,OAAO,MAAM,MAAM,KAAK;IACvB,GAAG;IAEH,SAAS,KAAK,WAAW,SAAS,oBAAoB,KAAK,OAAO,IAAI,KAAK;IAC3E,UAAU;IACV;GACD,CAAC;EACF,UAAU;GACT,QAAQ;EACT;CACD;;;;;CAMA,MAAc,QACb,MACA,OAAoB,CAAC,GACrB,YAAoB,mBAAmB,oBACd;EACzB,KAAK,kBAAkB;EACvB,MAAM,QAAQ,GAAG,KAAK,UAAU,MAAM,GAAG;EACzC,IAAI;GACH,MAAM,WAAW,MAAM,KAAK,iBAAiB,GAAG,KAAK,YAAY,QAAQ,MAAM,SAAS;GACxF,IAAI,CAAC,SAAS,IAAI;IACjB,UAAU,CAAC,CAAC,KAAK,wBAAwB,MAAM,WAAW,SAAS,MAAM;IACzE,MAAM,SAAS,MAAM,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC;IAC5C,OAAO;GACR;GACA,MAAM,EAAE,MAAM,cAAc,MAAM,aAAa,UAAU,mBAAmB,cAAc;GAC1F,IAAI,WAAW;IACd,UAAU,CAAC,CAAC,KAAK,wBAAwB,MAAM,iCAAiC;IAChF,OAAO;GACR;GACA,OAAO;EACR,SAAS,KAAK;GACb,UAAU,CAAC,CAAC,KAAK,wBAAwB,MAAM,YAAY,GAAG;GAC9D,OAAO;EACR;CACD;;;;;;;;;;;;;;CAeA,MAAa,eACZ,YAAoB,mBAAmB,oBACpB;EACnB,QAAQ,MAAM,KAAK,YAAY,SAAS,EAAA,CAAG;CAC5C;;;;;;;;;;;;;;;CAgBA,MAAa,YACZ,YAAoB,mBAAmB,oBACvC,QACgE;EAChE,KAAK,kBAAkB;EAOvB,MAAM,MAAM,GAAG,KAAK,UAAU;EAE9B,IAAI;GACH,MAAM,WAAW,MAAM,KAAK,iBAAiB,KAAK;IAAE,QAAQ;IAAO;GAAO,GAAG,SAAS;GAEtF,MAAM,SAAS,MAAM,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC;GAC5C,OAAO;IAAE,QAAQ,SAAS,MAAM,WAAW,QAAQ;IAAG,QAAQ,SAAS;GAAO;EAC/E,SAAS,KAAK;GACb,MAAM,QAAQ,mBAAmB,GAAG;GACpC,UAAU,CAAC,CAAC,MAAM,qCAAqC,OAAO;GAC9D,OAAO;IAAE,QAAQ;IAAO;GAAM;EAC/B;CACD;;;;;;;;;;;;;;CAeA,MAAa,kBAAkB,UAAoC,CAAC,GAA2B;EAC9F,MAAM,EAAE,aAAa,SAAS;EAE9B,MAAM,OAAO,aACV,MAAM,KAAK,QAAQ,mBAAmB,CAAC,GAAG,mBAAmB,oBAAoB,IACjF,MAAM,KAAK,QAAQ,kCAAkC;EACxD,IAAI,SAAS,MAAM,OAAO;EAG1B,MAAM,OAAO,KAAK,KAAK;EACvB,IAAI,CAAC,QAAQ,KAAK,IAAI,GAAG;GACxB,UAAU,CAAC,CAAC,KAAK,uEAAuE;GACxF,OAAO;EACR;EACA,OAAO,SAAS,MAAM,EAAE;CACzB;;;;;;CAOA,MAAa,YAA0C;EACtD,KAAK,kBAAkB;EACvB,IAAI;EACJ,IAAI;GACH,MAAM,WAAW,MAAM,KAAK,iBAAiB,GAAG,KAAK,UAAU,QAAQ;GACvE,IAAI,CAAC,SAAS,IAAI;IACjB,MAAM,SAAS,MAAM,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC;IAC5C,OAAO;GACR;GACA,MAAM,OAAO,MAAM,aAAa,UAAU,mBAAmB,cAAc;GAC3E,IAAI,KAAK,WAAW,OAAO;GAC3B,OAAO,KAAK;EACb,SAAS,KAAK;GACb,UAAU,CAAC,CAAC,MAAM,6CAA6C,mBAAmB,GAAG,GAAG;GACxF,OAAO;EACR;EACA,MAAM,OAAO,aAAa,IAAI;EAC9B,MAAM,YAAY,MACjB,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,KAAK,IAAI;EACpD,IAAI,CAAC,QAAQ,CAAC,SAAS,KAAK,cAAc,KAAK,CAAC,SAAS,KAAK,eAAe,GAAG,OAAO;EACvF,OAAO;GACN,gBAAgB,KAAK;GACrB,iBAAiB,KAAK;GACtB,wBAAwB,KAAK,2BAA2B;EACzD;CACD;;;;;;CAOA,MAAa,aAA4C;EACxD,MAAM,OAAO,MAAM,KAAK,QAAQ,UAAU;EAC1C,IAAI,SAAS,MAAM,OAAO;EAG1B,MAAM,OAAO,aAAa,IAAI;EAK9B,IAAI,QAAQ,MAAM,OAAO;GAAE,OAAO;GAAM,SAAS;GAAI,SAAS;EAAK;EACnE,OAAO;GAAE,OAAO,KAAK,SAAS;GAAI,SAAS,KAAK,WAAW;GAAI,SAAS,KAAK,WAAW;EAAK;CAC9F;;;;;;;;;;;;;;;;;;;;CAqBA,MAAa,oBACZ,OAAuB,MACkB;EACzC,MAAM,OAAO,MAAM,KAAK,QAAQ,YAAY,KAAK,WAAW;EAC5D,MAAM,OAAO,SAAS,OAAO,KAAA,IAAY,aAAa,IAAI;EAC1D,OAAO,QAAQ,OAAO,SAAS,WAAY,OAAkC;CAC9E;;;;;;;CAQA,MAAa,iBAAuC;EACnD,KAAK,kBAAkB;EAIvB,IAAI,CAAC,MAFkB,KAAK,eAAe,GAG1C,OAAO,EAAE,UAAU,MAAM;EAK1B,MAAM,CAAC,SAAS,kBAAkB,MAAM,QAAQ,IAAI,CACnD,KAAK,WAAW,GAChB,KAAK,kBAAkB,EAAE,YAAY,MAAM,CAAC,CAC7C,CAAC;EAED,OAAO;GACN,UAAU;GACV,GAAI,WAAW,EAAE,QAAQ;GACzB,GAAI,mBAAmB,QAAQ,EAAE,eAAe;EACjD;CACD;;;;;;;;;;;;;;;;;;;;;;CAuBA,MAAa,aAAqC;EACjD,MAAM,OAAO,MAAM,KAAK,QAAQ,gBAAgB,EAAE,QAAQ,OAAO,CAAC;EAElE,MAAM,UADO,SAAS,OAAO,KAAA,IAAY,aAAa,IAAI,EAAA,EACQ;EAClE,OAAO,OAAO,WAAW,WAAW,SAAS;CAC9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgCA,MAAa,mBAKH;EACT,KAAK,kBAAkB;EAGvB,MAAM,WAAW,MAAM,KAAK,kBAAkB,EAAE,YAAY,MAAM,CAAC;EACnE,IAAI,aAAa,MAAM;GACtB,UAAU,CAAC,CAAC,KAAK,mEAAmE;GACpF,OAAO;EACR;EAEA,IAAI,aAAa,GAEhB,OAAO;GAAE,aAAa;GAAG,OAAO;GAAG,UAAU;GAAG,WAAW;EAAK;EAMjE,MAAM,QAAQ,WAAW;EACzB,IAAI,cAAc;EAClB,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KAAK;GAC/B,MAAM,SAAS,MAAM,KAAK,WAAW;GACrC,IAAI,WAAW,MAAM,eAAe;EACrC;EAEA,OAAO;GAAE;GAAa;GAAO;GAAU,WAAW,aAAa;EAAE;CAClE;;;;;;;;;;;CAYA,MAAa,gBAAsC;EAClD,MAAM,OAAO,MAAM,KAAK,QAAQ,aAAa;EAC7C,IAAI,SAAS,MAAM,OAAO;EAE1B,MAAM,OAAO,IAAI,KAAK,KAAK,KAAK,CAAC,CAAC,QAAQ,UAAU,EAAE,CAAC;EACvD,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,OAAO;CACvC;;;;;;;;;;;CAYA,MAAa,cAAsC;EAClD,MAAM,OAAO,MAAM,KAAK,QAAQ,WAAW;EAC3C,IAAI,SAAS,MAAM,OAAO;EAC1B,MAAM,UAAU,WAAW,KAAK,KAAK,CAAC;EACtC,OAAO,MAAM,OAAO,IAAI,OAAO;CAChC;;;;;;;;;;;;;CAcA,MAAa,iBAAwE;EACpF,OAAO,KAAK,SAAS,kBAAkB;CACxC;;;;;;;;;;;;;CAcA,MAAa,YAAY,MAAsD;EAC9E,MAAM,OAAO,SAAS,KAAA,IAAY,kBAAkB,sBAAsB;EAC1E,OAAO,KAAK,SAAS,IAAI;CAC1B;;;;;;;;;;;;;CAcA,MAAa,iBACZ,MACuD;EACvD,MAAM,OAAO,SAAS,KAAA,IAAY,uBAAuB,2BAA2B;EACpF,OAAO,KAAK,SAAS,IAAI;CAC1B;;;;;;;;;;;;;CAcA,MAAa,gBACZ,MAC0E;EAC1E,MAAM,OAAO,SAAS,KAAA,IAAY,sBAAsB,0BAA0B;EAClF,OAAO,KAAK,SAAS,IAAI;CAC1B;;CAGA,MAAc,SAAY,MAAiC;EAC1D,MAAM,OAAO,MAAM,KAAK,QACvB,MACA,EAAE,QAAQ,OAAO,GACjB,mBAAmB,oBACpB;EACA,OAAO,SAAS,OAAO,OAAS,aAAa,IAAI,KAAK;CACvD;;;;;;;;;;;;;;;;;;;;CAqBA,QAAe,UAAwC,aAAqB,KAAkB;EAC7F,KAAK,kBAAkB;EAEvB,IAAI,CAAC,OAAO,SAAS,UAAU,KAAK,aAAa,mBAAmB,yBAGnE,MAAM,IAAI,aACT,mDAAmD,mBAAmB,wBAAwB,UACtF,WAAW,QAAQ,mBAAmB,wBAAwB,wCACtE,WAAW,gBACX,EAAE,SAAS,EAAE,WAAW,EAAE,CAC3B;EAGD,IAAI,SAAS;EACb,IAAI,mBAAyD;EAE7D,UAAU,CAAC,CAAC,KAAK,6CAA6C,WAAW,GAAG;EAE5E,MAAM,QAAQ,YAAY;GACzB,mBAAmB;GACnB,IAAI,CAAC,UAAU,KAAK,UAAU;GAE9B,IAAI;IACH,MAAM,SAAS,MAAM,KAAK,eAAe;IAEzC,IAAI,CAAC,UAAU,KAAK,UAAU;IAE9B,IAAI;KACH,SAAS,MAAM;IAChB,SAAS,KAAK;KACb,UAAU,CAAC,CAAC,MAAM,gDAAgD,GAAG;IACtE;GACD,SAAS,KAAK;IACb,IAAI,CAAC,UAAU,KAAK,UAGnB,UAAU,CAAC,CAAC,MAAM,gEAAgE,GAAG;SAErF,UAAU,CAAC,CAAC,MAAM,8DAA8D,GAAG;GAErF;GAEA,IAAI,UAAU,CAAC,KAAK,UACnB,mBAAmB,iBAAiB,KAAK,MAAM,GAAG,UAAU;EAE9D;EAEA,MAAM,uBAAuB;GAC5B,SAAS;GACT,IAAI,qBAAqB,MAAM;IAC9B,aAAa,gBAAgB;IAC7B,mBAAmB;GACpB;GAEA,KAAK,eAAe,OAAO,cAAc;EAC1C;EAEA,KAAK,eAAe,IAAI,cAAc;EAEtC,MAAW;EAEX,OAAO;CACR;;;;;CAMA,MAAa,UAAyB;EACrC,IAAI,KAAK,UAAU;EAEnB,KAAK,WAAW;EAChB,KAAK,MAAM,eAAe,KAAK,gBAC9B,YAAY;EAEb,KAAK,eAAe,MAAM;CAC3B;CAEA,oBAAkC;EACjC,IAAI,KAAK,UACR,MAAM,IAAI,aACT,2DACA,WAAW,aACZ;CAEF;AACD;;;;;AAMA,SAAS,mBAAmB,KAAsB;CACjD,IAAI,EAAE,eAAe,QAAQ,OAAO,OAAO,GAAG;CAC9C,MAAM,UAAU,GAAG,IAAI,KAAK,IAAI,IAAI;CACpC,IAAI,EAAE,IAAI,iBAAiB,QAAQ,OAAO;CAC1C,MAAM,EAAE,SAAS,IAAI;CACrB,OAAO,GAAG,QAAQ,IAAI,OAAO,SAAS,WAAW,GAAG,KAAK,KAAK,KAAK,IAAI,MAAM,UAAU,KAAK;AAC7F;;;;;;;ACpnBA,MAAM,yBAAyB;;AAG/B,MAAM,iCAAqD,EAC1D,uBAAuB,WAAW,sBACnC;;AAGA,SAAgB,0BAAmD,QAAc;CAChF,OAAO;EACN,GAAG;EACH,kBAAkB;GAAE,GAAG;GAAgC,GAAG,OAAO;EAAiB;CACnF;AACD;AAEA,SAAS,qBAAqB,OAAyB;CACtD,IAAI,EAAE,iBAAiB,eAAe,OAAO;CAC7C,OACC,MAAM,SAAS,WAAW,yBAC1B,MAAM,QAAQ,SAAS,sBAAsB;AAE/C;;;;;AAMA,SAAgB,sBAAsB,UAAsC,OAAuB;CAClG,IAAI,CAAC,OAAO;CAEZ,MAAM,SAAS,UAAqB,UAAU,QAAQ;CACtD,IAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,GAAG;CAEnD,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,SAAS,QAAQ;EAC3B,MAAM,YAAY,UAAmC,OAAO,WAAW;EACvE,IAAI,CAAC,aAAa,OAAO,KAAK,SAAS,CAAC,CAAC,WAAW,GACnD,MAAM,KAAK,UAAkB,OAAO,WAAW,KAAK,WAAW;CAEjE;CAEA,IAAI,MAAM,WAAW,GAAG;CAExB,MAAM,QAAQ,MAAM,WAAW,OAAO,SAAS,QAAQ,GAAG,MAAM,OAAO,GAAG,OAAO;CACjF,UAAU,CAAC,CAAC,KACX,mCAAmC,MAAM,KAAK,MAAM,KAAK,IAAI,EAAE,iGAEhE;AACD;;;;;;;;;;;;AAsBA,eAAsB,2BACrB,UACA,YACA,QACsC;CACtC,iBAAiB,8BAA8B,OAAO,sBAAsB;CAE5E,kBAAkB,OAAO,SAAS;CAClC,QAAQ,MAAM,eAAe,UAAU,YAAY,MAAM,EAAA,CAAG;AAC7D;;;;;;AAOA,eAAsB,wBACrB,UACA,UACA,YACA,QACmD;CACnD,iBAAiB,2BAA2B,OAAO,sBAAsB;CAEzE,IAAI,aAAa,MAChB,IAAI;EAEH,OAAO;GAAE,GAAG,MADM,SAAS;IAAE,MAAM;IAAM,SAAS;IAAU,QAAQ;GAAS,GAAG,MAAM;GACrE,QAAQ;EAAM;CAChC,SAAS,OAAO;EACf,IAAI,CAAC,qBAAqB,KAAK,GAAG,MAAM;CACzC;CAED,OAAO;EAAE,GAAI,MAAM,eAAe,UAAU,YAAY,MAAM;EAAI,QAAQ;CAAK;AAChF;AAEA,eAAe,eACd,UACA,YACA,QAC6B;CAE7B,OAAO,SAAS,uBAAuB,MADnB,sBAAsB,YAAY,MAAM,GACd,QAAQ,GAAG,MAAM;AAChE;AAEA,eAAe,sBACd,YACA,QAC+B;CAC/B,IAAI,CAAC,gBAAgB,UAAU,GAAG,OAAO;CACzC,gBAAgB,MAAM;CACtB,MAAM,EAAE,QAAQ,YAAY,cAAc,OAAO,QAAQ,OAAO,SAAS;CACzE,IAAI;EAEH,OAAO,MAAM,WAAW,WAAW,KAAK,MAAM,GAAG,MAAM;CACxD,SAAS,OAAO;EACf,IAAI,OAAO,QAAQ,SAClB,MAAM,gBAAgB,EAAE,eAAe,WAAW,IAAI,GAAG,KAAK;EAE/D,IAAI,QAAQ,SACX,MAAM,IAAI,aACT,uBAAuB,WAAW,IAAI,oBAAoB,OAAO,UAAU,KAC3E,WAAW,eACX,EAAE,SAAS;GAAE,eAAe,WAAW;GAAK,WAAW,OAAO;EAAU,EAAE,CAC3E;EAED,MAAM,IAAI,aACT,4CAA4C,WAAW,IAAI,IAC3D,WAAW,eACX;GACC,SAAS,EAAE,eAAe,WAAW,IAAI;GACzC,eAAe;EAChB,CACD;CACD,UAAU;EACT,QAAQ;CACT;AACD;;;;;;AAOA,eAAe,SACd,MACA,QAC6B;CAC7B,6BAA6B,MAAM,MAAM;CAGzC,MAAM,EAAE,qBAAqB;CAC7B,MAAM,SAAS,MAAM,aACpB,eACA,MACA,0BAA0B;EACzB,GAAG;EACH,kBACC,qBAAqB,KAAA,KAAa,KAAK,OACpC,mBAAmB,KAAK,KAAK,SAC7B;CACL,CAAC,CACF;CAEA,qBAAqB,MAAM;CAC3B,MAAM,EACL,SACA,MAAM,OACN,GAAG,SACA;CAGJ,MAAM,WACL,UAAU,MAAM,QAAQ,IAAI,OAAO;EAAE,GAAG;EAAM,QAAQ,CAAC;CAAE;CAG1D,MAAM,WAAW,oBAAoB,MAAM;CAC3C,IAAI,aAAa,KAAA,GAAW;EAC3B,MAAM,aAAa,OAAO,UAAU,WAAW,MAAM,SAAS;EAC9D,oBAAoB,UAAU,KAAK,IAAI,GAAG,WAAW,UAAU,CAAC;CACjE;CACA,sBAAsB,UAAU,OAAO,KAAK;CAC5C,OAAO;EACN;EACA,UAAU,OAAO,YAAY,WAAW,UAAU;CACnD;AACD;;AAGA,SAAgB,qBAAqB,UAA+C;CACnF,IAAI,CAAC,YAAY,OAAO,aAAa,UACpC,MAAM,IAAI,aAAa,8BAA8B,WAAW,kBAAkB,EACjF,SAAS,EAAE,cAAc,aAAa,OAAO,SAAS,OAAO,SAAS,EACvE,CAAC;AAEH;AAMA,SAAgB,uBACf,YACA,UAC2B;CAC3B,MAAM,OAAiC;EACtC,MAAM;EACN,SAAS;EACT,QAAQ;CACT;CAEA,IAAI,sBAAsB,YACzB,KAAK,OAAO,gBAAgB,UAAU;MAChC,IAAI,gBAAgB,UAAU,GACpC,KAAK,UAAU;MAIf,KAAK,OAAO,oBAAoB,UAAU,KAAK,qBAAqB,UAAU;CAG/E,OAAO;AACR;AAEA,SAAgB,6BACf,SACA,SACO;CACP,IAAI,QAAQ,cAAc,MAAM,QAAQ,aAAa,QAAQ;CAC7D,IAAI,QAAQ,cAAc,MAAM,QAAQ,aAAa,QAAQ;CAC7D,IAAI,QAAQ,kBAAkB,MAAM,QAAQ,iBAAiB,QAAQ;CACrE,IAAI,QAAQ,qBAAqB,MAAM,QAAQ,oBAAoB,QAAQ;CAC3E,IAAI,QAAQ,eAAe,MAAM,QAAQ,cAAc,QAAQ;CAC/D,IAAI,QAAQ,eAAe,MAAM,QAAQ,cAAc,QAAQ;AAChE;;;;;;ACvQA,SAAgB,YAAY,KAAa,KAAa,OAAsB;CAC3E,IAAI,QAAQ,aACX,OAAO,eAAe,KAAK,KAAK;EAC/B;EACA,UAAU;EACV,YAAY;EACZ,cAAc;CACf,CAAC;MAED,IAAiC,OAAO;AAE1C;;;;;;;;;;;;ACHA,MAAa,eAAe;;;;;;;;AAS5B,SAAgB,kBAAkB,OAA0C;CAC3E,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GAAG,OAAO;CAChF,MAAM,UAAU,OAAO,QAAQ,KAAK;CACpC,OACC,QAAQ,SAAS,KACjB,QAAQ,OAAO,CAAC,KAAK,SAAS,aAAa,KAAK,GAAG,KAAK,MAAM,QAAQ,GAAG,CAAC;AAE5E;;ACxBA,MAAa,qCAA0C,IAAI,IAAI;CAC9D;CACA;CACA;CACA;CACA;AACD,CAAC;;AAGD,MAAa,qCAA0C,IAAI,IAAI,CAAC,gBAAgB,cAAc,CAAC;;;;;;;ACC/F,SAAgB,cACf,OACA,WACA,mBACU;CACV,IAAI,UAAU,KAAA,KAAa,UAAU,MACpC,OAAO;CAGR,IAAI,MAAM,QAAQ,KAAK,GAAG;EACzB,MAAM,YAAY,MAAM,IAAI,SAAS,CAAC,CAAC,QAAQ,MAAc,MAAM,IAAI;EACvE,OAAO,UAAU,SAAS,IAAI,YAAY,KAAA;CAC3C;CAEA,MAAM,cAAc,UAAU,KAAK;CACnC,IAAI,gBAAgB,MACnB,OAAO;CAER,OAAO,oBAAoB,KAAA,IAAY;AACxC;AAEA,MAAa,sBAAgD,UAAU;CACtE,IAAI,OAAO,UAAU,UAAU,OAAO,OAAO,SAAS,KAAK,IAAI,QAAQ;CACvE,IAAI,OAAO,UAAU,UAAU;EAC9B,MAAM,UAAU,MAAM,KAAK;EAG3B,IAAI,YAAY,IAAI,OAAO;EAG3B,IAAI,gBAAgB,KAAK,OAAO,GAAG,OAAO;EAC1C,MAAM,SAAS,OAAO,OAAO;EAC7B,OAAO,OAAO,SAAS,MAAM,IAAI,SAAS;CAC3C;CACA,OAAO;AACR;AAEA,MAAa,sBAAiD,UAAU;CACvE,IAAI,OAAO,UAAU,WAAW,OAAO;CACvC,IAAI,OAAO,UAAU,UAAU;EAC9B,MAAM,QAAQ,MAAM,KAAK,CAAC,CAAC,YAAY;EACvC,IAAI,UAAU,QAAQ,OAAO;EAC7B,IAAI,UAAU,SAAS,OAAO;CAC/B;CACA,OAAO;AACR;AAEA,MAAa,mBAA6C,UAAU;CACnE,IAAI,OAAO,UAAU,UAAU;EAC9B,IAAI,MAAM,UAAU,KAAK,MAAM,WAAW,IAAG,KAAK,MAAM,SAAS,IAAG,GACnE,OAAO,MAAM,MAAM,GAAG,EAAE;EAEzB,IAAI,MAAM,WAAW,IAAG,GAAG,OAAO,MAAM,MAAM,CAAC;EAC/C,OAAO;CACR;CACA,OAAO;AACR;AAEA,MAAa,oBAA8C,UAAU;CACpE,IAAI,OAAO,UAAU,UAAU;EAC9B,IAAI,UAAU,MAAM,KAAK;EACzB,IAAI,QAAQ,WAAW,IAAG,KAAK,QAAQ,SAAS,IAAG,GAClD,UAAU,QAAQ,MAAM,GAAG,EAAE,CAAC,CAAC,KAAK;EAErC,OAAO;CACR;CACA,OAAO;AACR;AAEA,SAAgB,kBAAkB,WAA6C;CAC9E,QAAQ,UAAU;EACjB,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO;EACxD,IAAI,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM,IACjD,IAAI;GACH,MAAM,SAAS,KAAK,MAAM,KAAK;GAC/B,IAAI,OAAO,WAAW,YAAY,WAAW,MAAM,OAAO;GAC1D,UAAU,CAAC,CAAC,KAAK,0BAA0B,UAAU,kBAAkB;GACvE,OAAO;EACR,QAAQ;GACP,UAAU,CAAC,CAAC,KAAK,8CAA8C,WAAW;GAC1E,OAAO;EACR;EAED,OAAO;CACR;AACD;;AC/EA,SAAS,YAAY,OAAyB,SAAoC;CACjF,UAAU,CAAC,CAAC,KAAK,OAAO;CACxB,OAAO;EACN,QAAQ;GAAE,GAAG;GAAO,SAAS;EAAK;EAClC,SAAS;GAAE,MAAM,WAAW;GAAmB;EAAQ;CACxD;AACD;;;;;AAMA,SAAgB,iBAAiB,OAAyB,OAAkC;CAC3F,IAAI,OAAO,MAAM,YAAY,YAAY,MAAM,YAAY,MAC1D,OAAO,EAAE,QAAQ,MAAM;CAKxB,IAAI,MAAM,QAAQ,MAAM,OAAO,KAAK,kBAAkB,MAAM,OAAO,GAClE,OAAO,EAAE,QAAQ,MAAM;CAGxB,IAAI,CAAC,SAAS,MAAM,SAAS,WAAW,GACvC,OAAO,YACN,OACA,UAAU,MAAM,iFACjB;CAGD,MAAM,YAAY,UAAmC,MAAM,SAAS,WAAW,KAAK,CAAC;CACrF,IAAI,OAAO,KAAK,SAAS,CAAC,CAAC,WAAW,GACrC,OAAO,EAAE,QAAQ;EAAE,GAAG;EAAO,SAAS,KAAA;CAAU,EAAE;CAGnD,IAAI,MAAM,cAAe,MAAM,UAAU,MAAM,SAAS,GAAI;EAE3D,MAAM,OAAkC,CAAC;EACzC,MAAM,eAAyB,CAAC;EAChC,KAAK,MAAM,CAAC,QAAQ,UAAU,OAAO,QAAQ,SAAS,GAAG;GACxD,IAAI,CAAC,MAAM,QAAQ,KAAK,GACvB,OAAO,YACN,OACA,UAAU,MAAM,+CAA+C,OAAO,6BACvE;GAED,MAAM,SAAoB,CAAC;GAC3B,KAAK,MAAM,QAAQ,OAAO;IACzB,MAAM,OAAO,UAAU,MAAM,MAAM;IACnC,MAAM,OAAO,UAAkB,MAAM,MAAM;IAC3C,IAAI,QAAQ,mBAAmB,IAAI,IAAI,GAAG;KAEzC,IAAI,OAAO,SAAS,YAAY,KAAK,KAAK,MAAM,IAAI;KACpD,MAAM,MAAM,mBAAmB,IAAI;KACnC,IAAI,QAAQ,MAAM;MACjB,aAAa,KAAK,IAAI,OAAO,IAAI,EAAE,KAAK,KAAK,EAAE;MAC/C;KACD;KACA,OAAO,KAAK,mBAAmB,IAAI,IAAI,IAAI,KAAK,MAAM,GAAG,IAAI,GAAG;KAChE;IACD;IACA,IAAI,SAAA,kBAA4B;KAC/B,MAAM,OAAO,mBAAmB,IAAI;KACpC,IAAI,SAAS,MAAM;MAClB,aAAa,KAAK,IAAI,OAAO,IAAI,EAAE,KAAK,KAAK,EAAE;MAC/C;KACD;KACA,OAAO,KAAK,IAAI;KAChB;IACD;IACA,IAAI,SAAA,mBAA6B,OAAO,SAAS,UAAU;KAC1D,OAAO,KAAK,gBAAgB,IAAI,CAAC;KACjC;IACD;IAGA,IAAI,OAAO,SAAS,YAAY,MAAM,WAAW,gBAAgB,GAAG;KACnE,IAAI;MACH,OAAO,KAAK,KAAK,MAAM,IAAI,CAAC;KAC7B,QAAQ;MACP,OAAO,KAAK,IAAI;KACjB;KACA;IACD;IACA,OAAO,KAAK,IAAI;GACjB;GACA,YAAY,MAAM,QAAQ,MAAM;EACjC;EAGA,IAAI,aAAa,SAAS,GAAG;GAC5B,MAAM,UAAU,UAAU,MAAM,sBAAsB,aAAa,OAAO,4DAA4D,aAAa,KAAK,IAAI,EAAE;GAE9J,UAAU,CAAC,CAAC,KAAK,UAAU,MAAM,qBAAqB,aAAa,OAAO,eAAe;GACzF,OAAO;IACN,QAAQ;KAAE,GAAG;KAAO,SAAS;IAAK;IAClC,SAAS;KAAE,MAAM,WAAW;KAAmB;IAAQ;GACxD;EACD;EACA,OAAO,EAAE,QAAQ;GAAE,GAAG;GAAO,SAAS;EAAK,EAAE;CAC9C;CAEA,MAAM,YAAuB,CAAC;CAC9B,KAAK,MAAM,SAAS,OAAO,OAAO,SAAS,GAAG;EAC7C,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG;EAC3B,KAAK,MAAM,QAAQ,OAClB,IAAI,QAAQ,OAAO,SAAS,YAAY,SAAS,MAAM,MAAM,GAC5D,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC;CAGzC;CACA,OAAO,EAAE,QAAQ;EAAE,GAAG;EAAO,SAAS,UAAU,SAAS,IAAI,YAAY,UAAU;CAAG,EAAE;AACzF;;;ACjIA,SAAgB,cAAc,OAAe,eAA+B;CAC3E,MAAM,UAAU,OAAO,MAAM,QAAQ,aAAa,CAAC;CACnD,OAAO,KAAK,IAAI,QAAQ,OAAO,IAAI,OAAO,UAAU;AACrD;AAEA,MAAM,eAAe;AAGrB,MAAM,qBAAqB;;;;;AAM3B,SAAgB,WAAW,GAAmB;CAC7C,IAAI,CAAC,OAAO,SAAS,CAAC,GAAG,OAAO;CAChC,MAAM,QAAQ,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,YAAY,kBAAkB,CAAC;CAChE,MAAM,CAAC,UAAU,WAAW,OAAO,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG;CAC1D,MAAM,YAAY,SAAS,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,UAAU,KAAK,OAAO,QAAQ;CACxE,OAAO,KAAK,IAAI,KAAK,IAAI,UAAU,CAAC,GAAG,YAAY;AACpD;AAEA,SAAgB,iBAAiB,OAAuB;CACvD,IAAI,CAAC,OAAO,SAAS,KAAK,KAAK,UAAU,GAAG,OAAO;CACnD,MAAM,WAAW,WAAW,KAAK;CACjC,IAAI,aAAa,GAAG,OAAO,KAAK,IAAI,KAAK,KAAK,IAAI,IAAI;CACtD,OAAO,QAAQ,MAAM,CAAC,SAAA,CAAU,QAAQ,QAAQ,CAAC;AAClD;;AAGA,SAAgB,eAAe,QAA8C;CAC5E,OAAO,OAAO,OAAO,aAAa,YACjC,OAAO,SAAS,OAAO,QAAQ,KAC/B,OAAO,WAAW,IAChB,OAAO,WACP,KAAA;AACJ;;ACMA,MAAM,WAAW,WAAqC,OAAO,YAAY;AAEzE,MAAM,UAAa,QAA0B,WAC3C,OAAO,UAAU,KAAK,IAAI,CAAC,KAAK,IAAI;AAEtC,MAAM,eAAe,WACpB,eAAe,MAAM,MAAM,OAAO,cAAc,YAAY,IAAI;AAEjE,MAAM,cAAc,OAAgB,OACnC,MAAM,QAAQ,KAAK,IAChB,MAAM,KAAK,MAAO,OAAO,MAAM,WAAW,GAAG,CAAC,IAAI,CAAE,IACpD,OAAO,UAAU,WAChB,GAAG,KAAK,IACR;AAEL,SAAS,eAAe,QAGtB;CAED,IAAI,kBAAkB,OAAO,OAAO,GACnC,OAAO;EACN,SAAS,OAAO;EAChB,UAAU,YAAY,MAAM;CAC7B;CAKD,IACC,OAAO,OAAO,YAAY,YAC1B,OAAO,QAAQ,KAAK,MAAM,MAC1B,mBAAmB,OAAO,OAAO,MAAM,MAEvC,MAAM,IAAI,aACT,4BAA4B,OAAO,QAAQ,eAAe,OAAO,QAAQ,UAAU,IACnF,WAAW,kBACX,EAAE,SAAS;EAAE,WAAW,OAAO;EAAM,SAAS,OAAO;CAAQ,EAAE,CAChE;CAGD,MAAM,QAAQ,cAAc,OAAO,SAAS,oBAAoB,IAAI;CAEpE,IAAI,OAAO,cAAc,WACxB,OAAO;EACN,SAAS,WAAW,OAAO,KAAK,KAAK;EACrC,UAAU,YAAY,MAAM;CAC7B;CAGD,MAAM,aAAa,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK;CACrD,MAAM,aAAa;EAAC;EAAY,OAAO;EAAS,OAAO;CAAO,CAAC,CAAC,MAC9D,MAAmB,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,KAAK,MAAM,CAC1E;CACA,MAAM,WACL,eAAe,MAAM,MAAM,eAAe,KAAA,IAAY,iBAAiB,UAAU,IAAI;CAEtF,IAAI,gBAAgB,WAAW,QAAQ;CACvC,IACC,kBAAkB,KAClB,OAAO,eAAe,YACtB,eAAe,KACf,KAAK,IAAI,UAAU,IAAI,GACtB;EACD,MAAM,WAAW,KAAK,KAAK,CAAC,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,CAAC;EAC5D,IAAI,OAAO,SAAS,QAAQ,KAAK,WAAW,GAC3C,gBAAgB;CAElB;CACA,gBAAgB,KAAK,IAAI,KAAK,IAAI,eAAe,CAAC,GAAG,EAAE;CAEvD,OAAO;EACN,SAAS,WAAW,QAAQ,MAC3B,cAAc,GAAG,aAAa,CAC/B;EACA;CACD;AACD;AAEA,MAAM,gBACL,QACA,MACA,cACuB;CACvB,GAAG;CACH,WAAW,OAAO;CAClB,SAAS,OAAO;CAChB,SAAS,OAAO;CAChB,SAAS,OAAO;CAChB,QAAQ,OAAO;CACf,GAAG;AACJ;AAEA,MAAM,gBAAmD;CACxD,MAAM,QAAQ,MAAM;EACnB,OAAO,aAAa,QAAQ,MAAM,eAAe,MAAM,CAAC;CACzD;CACA,SAAS,QAAQ,MAAM;EAEtB,MAAM,YACL,OAAO,OAAO,YAAY,YAAY,OAAO,SAAS,OAAO,OAAO,KAAK,OAAO,UAAU,IACvF,OAAO,UACP;EACJ,OAAO,aAAa,QAAQ,MAAM;GACjC,UAAU,YAAY,MAAM;GAC5B,SAAS,OAAO,QAAQ,SAAS;EAClC,CAAC;CACF;AACD;AAEA,MAAM,gBAAmD;CACxD,MAAM,QAAQ,MAAM;EACnB,IAAI,kBAAkB,OAAO,OAAO,GACnC,OAAO;GACN,GAAG;GACH,WAAW;GACX,SAAS,OAAO;EACjB;EAED,MAAM,QAAQ,cAAc,OAAO,SAAS,oBAAoB,KAAK;EAGrE,IACC,UAAU,KAAA,KACV,UAAU,QACV,OAAO,UAAU,aACjB,CAAC,MAAM,QAAQ,KAAK,GAEpB,MAAM,IAAI,aACT,4BAA4B,OAAO,KAAK,EAAE,eAAe,OAAO,QAAQ,UAAU,IAClF,WAAW,kBACX,EAAE,SAAS;GAAE,WAAW,OAAO;GAAM,SAAS,OAAO;EAAQ,EAAE,CAChE;EAED,OAAO;GAAE,GAAG;GAAM,WAAW;GAAW,SAAS;EAAqC;CACvF;CACA,SAAS,QAAQ,MAAM;EACtB,OAAO;GAAE,GAAG;GAAM,WAAW;GAAW,SAAS,OAAO,QAAQ,KAAK;EAAE;CACxE;AACD;AAEA,MAAM,aAA6C,EAClD,MAAM,QAAQ,MAAM;CACnB,MAAM,QAAQ,cAAc,OAAO,SAAS,iBAAiB,KAAK;CAClE,OAAO;EAAE,GAAG;EAAM,WAAW;EAAQ,SAAS;CAAkC;AACjF,EACD;AAEA,MAAM,kBAAuD;CAC5D,MAAM,QAAQ,MAAM,MAAM;EACzB,IACC,CAAC,OAAO,UACR,OAAO,OAAO,WAAW,YACzB,OAAO,KAAK,OAAO,MAAM,CAAC,CAAC,WAAW,GACrC;GACD,MAAM,YAAY,QAAQ,MAAM;GAChC,MAAM,IAAI,aACT,UAAU,UAAU,+CACpB,WAAW,eACX,EAAE,SAAS;IAAE;IAAW,cAAc;GAAY,EAAE,CACrD;EACD;EAEA,IAAI;EACJ,IAAI,OAAO,YAAY,KAAA,KAAa,OAAO,YAAY,MAAM;GAG5D,IAAI,OAAO,OAAO,YAAY,UAC7B,MAAM,IAAI,aACT,oBAAoB,QAAQ,MAAM,EAAE,uCACpC,WAAW,kBACX,EAAE,SAAS;IAAE,WAAW,OAAO;IAAM,SAAS,OAAO;GAAQ,EAAE,CAChE;GAED,MAAM,eAAe,OAAO,OAAO,OAAO,CAAC,CAAC,YAAY;GAGxD,MAAM,QAAQ,OAAO,KAAK,OAAO,MAAM,CAAC,CAAC,MAAM,QAAQ,IAAI,YAAY,MAAM,YAAY;GACzF,eAAe,SAAS,OAAO,OAAO,OAAO;GAC7C,IAAI,UAAU,KAAA,GAAW;IAExB,MAAM,YAAY,QAAQ,MAAM;IAChC,UAAU,CAAC,CAAC,KAAK,oBAAoB,UAAU,qCAAqC;IACpF,OAAO;KACN,MAAM,WAAW;KACjB,SAAS,oBAAoB,UAAU,mBAAmB,OAAO,QAAQ;IAC1E,CAAC;GACF;EACD;EAEA,OAAO;GACN,GAAG;GACH,WAAW;GACX,QAAQ,OAAO;GACf,SAAS;EACV;CACD;CACA,SAAS,QAAQ,MAAM;EAEtB,OAAO;GACN,GAAG;GACH,WAAW;GACX,QAAQ,OAAO,UAAU,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS,CAAC;GAC9E,SAAS,KAAA;EACV;CACD;AACD;AAEA,MAAM,iBAAiB,WACtB,cAAc,OAAO,SAAS,kBAAkB,QAAQ,MAAM,CAAC,GAAG,IAAI;AAEvE,SAAS,iBAAiB,QAA0B,MAAwC;CAC3F,OAAO;EACN,GAAG;EACH,WAAW;EACX,SAAS,OAAO,QAAQ,IAAI;CAC7B;AACD;;AAgCA,MAAa,qCAA2D,IAAI,IAG1E;CACD,CAAC,UAAU,aAAa;CACxB,CAAC,WAAW,aAAa;CACzB,CAAC,WAAW,aAAa;CACzB,CAAC,QAAQ,UAAU;CACnB,CAAC,aAAa,eAAe;CAC7B,CAAC,YAAY;EAtCb,MAAM,QAAQ,MAAM;GACnB,OAAO;IACN,GAAG;IACH,WAAW;IACX,SAAS,cAAc,MAAM;GAC9B;EACD;EACA,UAAU;CA+BG,CAAc;CAC3B,CAAC,QAAQ,EA5BT,MAAM,QAAQ,MAAM;EACnB,OAAO;GACN,GAAG;GACH,WAAW;GACX,iBAAiB,OAAO;GACxB,SAAS,cAAc,MAAM;EAC9B;CACD,EAqBS,CAAU;CACnB,CAAC,SAAS,EAlBV,MAAM,QAAQ,MAAM;EACnB,MAAM,QAAQ,cAAc,OAAO,SAAS,kBAAkB,KAAK;EACnE,OAAO;GAAE,GAAG;GAAM,WAAW;GAAS,SAAS;EAAmC;CACnF,EAeU,CAAW;AACtB,CAAC;AAED,MAAa,sBAAsB;;AC5SnC,MAAM,wBAAwB,IAAI,IACjC,CAAC,GAAG,mBAAmB,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,IAAI,YAAY,GAAG,GAAG,CAAC,CACrE;;AAGA,SAAS,sBAAsB,WAA2B;CACzD,OAAO,sBAAsB,IAAI,WAAW,YAAY,CAAC,KAAK;AAC/D;;;;;AAMA,SAAgB,aAAa,UAAwC;CACpE,OAAO,sBAAsB,QAAQ,CAAC,CAAC;AACxC;;;;;;;;;AAUA,SAAgB,sBAAsB,UAGpC;CACD,MAAM,QAAQ,SAAS,QAAQ;CAC/B,MAAM,SAA4B,CAAC;CACnC,MAAM,UAAU,EAAE,MAAM,cAAiD;EACxE,OAAO,KAAK;GAAE,WAAW;GAAO,WAAW,SAAS;GAAW;GAAS;EAAK,CAAC;CAC/E;CAEA,MAAM,YAA2B;EAChC,aAAa,SAAS;EACtB,MAAM,SAAS;EACf,UAAU,SAAS;EACnB,YAAY,SAAS;EAErB,WAAW,SAAS,aAAa,KAAA;EACjC,IAAI,SAAS;CACd;CAGA,MAAM,YAAY,sBAAsB,SAAS,SAAS;CAE1D,IAAI,SAA2B;EAAE,GAAG;EAAU;CAAU;CACxD,IAAI;EACH,MAAM,EAAE,QAAQ,YAAY,YAAY,iBAAiB,QAAQ,KAAK;EACtE,SAAS;EACT,IAAI,SAAS,OAAO,OAAO;CAC5B,SAAS,OAAO;EAGf,SAAS;GAAE,GAAG;GAAQ,SAAS;EAAK;EACpC,OAAO;GACN,MAAM,WAAW;GACjB,SAAS,UAAU,MAAM,qDACxB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EAEvD,CAAC;CACF;CAEA,MAAM,SAAS,mBAAmB,IAAI,SAAS;CAC/C,IAAI;EACH,IAAI,CAAC,QACJ,MAAM,IAAI,aAAa,sBAAsB,aAAa,WAAW,kBAAkB,EACtF,SAAS;GAAE,mBAAmB;GAAW,WAAW,SAAS;EAAK,EACnE,CAAC;EAEF,OAAO;GAAE,OAAO,OAAO,MAAM,QAAQ,WAAW,MAAM;GAAG;EAAO;CACjE,SAAS,OAAO;EACf,IAAI,EAAE,iBAAiB,eACtB,MAAM,IAAI,aACT,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GACrD,WAAW,kBACX;GACC,SAAS;IAAE,WAAW,SAAS;IAAM;GAAU;GAC/C,eAAe;EAChB,CACD;EAGD,UAAU,CAAC,CAAC,MAAM,8BAA8B,MAAM,IAAI,MAAM,MAAM;EACtE,OAAO,KAAK;EAEZ,OAAO;GAAE,QADQ,QAAQ,YAAY,oBAAA,CACZ,QAAQ,SAAS;GAAG;EAAO;CACrD;AACD;;AAGA,SAAgB,wBAAwB,WAGtC;CACD,MAAM,SAAuB,CAAC;CAC9B,MAAM,cAAiC,CAAC;CACxC,KAAK,MAAM,OAAO,WAAW;EAC5B,MAAM,EAAE,OAAO,WAAW,sBAAsB,GAAG;EACnD,OAAO,KAAK,KAAK;EACjB,YAAY,KAAK,GAAG,MAAM;CAC3B;CACA,OAAO;EAAE;EAAQ;CAAY;AAC9B;;;;;;;;;;;ACrGA,SAAgB,qBAAqB,KAAgC;CACpE,MAAM,WAAW,UAAmB,KAAK,UAAU;CACnD,OAAO;EACN,IAAI,UAAkB,KAAK,IAAI,KAAK;EACpC,MAAM,UAAkB,KAAK,MAAM,KAAK;EACxC,UAAU,UAAyB,KAAK,UAAU,KAAK;EACvD,aAAa,UAAkB,KAAK,aAAa,KAAK;EACtD,WAAW,UAAkB,KAAK,WAAW,KAAK;EAClD,YAAY,UAAmB,KAAK,YAAY,KAAK;EACrD,SAAS,UAAyB,KAAK,SAAS,KAAK;EACrD,SAAS,UAAyB,KAAK,SAAS,KAAK;EACrD,SAAS,UAAkB,KAAK,SAAS,KAAK;EAC9C,QAAQ,UAAkB,KAAK,QAAQ,KAAK;EAC5C,UAAU,OAAO,aAAa,YAAY,OAAO,SAAS,QAAQ,IAAI,WAAW,KAAA;EACjF,SAAS,UAAU,KAAK,SAAS;EACjC,QAAQ,UAAkC,KAAK,QAAQ;EACvD,iBAAiB,UAAoB,KAAK,iBAAiB;EAC3D,WAAW,UAAyB,KAAK,WAAW,KAAK;CAC1D;AACD;AAEA,SAAgB,sBAAsB,KAAiC;CACtE,OAAO;EACN,MAAM,UAAkB,KAAK,MAAM,KAAK;EACxC,UAAU,UAAyB,KAAK,UAAU,KAAK;EACvD,WAAW,UAAkB,KAAK,WAAW,KAAK;EAClD,IAAI,UAAkB,KAAK,IAAI,KAAK;CACrC;AACD;;;;;;;;;;;AClBA,eAAsB,kBACrB,YACA,QACkC;CAClC,iBAAiB,qBAAqB,OAAO,sBAAsB;CACnE,kBAAkB,OAAO,SAAS;CAElC,MAAM,EAAE,MAAM,YAAY,uBAAuB,YAAY,CAAC,CAAC;CAC/D,IAAI,CAAC,QAAQ,CAAC,SACb,MAAM,IAAI,aACT,kEACA,WAAW,aACZ;CAGD,MAAM,WAAW,MAAM,aACtB,MACA,OAAO,EAAE,KAAK,IAAI,EAAE,QAAQ,GAC5B,0BAA0B,MAAM,CACjC;CAEA,qBAAqB,QAAQ;CAE7B,MAAM,eAAe,gBAAgB,UAAU,UAAU,UAAU,CAAC;CACpE,MAAM,aAAa,gBAAgB,UAAU,UAAU,QAAQ,CAAC;CAIhE,MAAM,YAAY,UAAU,UAAU,QAAQ;CAC9C,MAAM,aAAa,UAAU,UAAU,SAAS;CAChD,OAAO;EACN,QAAQ,MAAM,QAAQ,SAAS,IAAI,UAAU,IAAI,oBAAoB,IAAI,CAAC;EAC1E,SAAS,MAAM,QAAQ,UAAU,IAAI,WAAW,IAAI,qBAAqB,IAAI,CAAC;EAC9E,GAAI,gBAAgB,EAAE,aAAa;EACnC,GAAI,cAAc,EAAE,WAAW;CAChC;AACD;;;;;;AAOA,SAAS,gBAAgB,OAAsC;CAC9D,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG,OAAO,KAAA;CAClC,MAAM,UAAU,MAAM,IAAI,gBAAgB,CAAC,CAAC,QAAQ,MAAmB,MAAM,KAAA,CAAS;CACtF,OAAO,QAAQ,SAAS,IAAI,UAAU,KAAA;AACvC;AAEA,SAAS,iBAAiB,OAAoC;CAC7D,IAAI,UAAU,QAAQ,UAAU,KAAA,GAAW,OAAO,KAAA;CAClD,IAAI;CACJ,IAAI,OAAO,UAAU,UACpB,OAAO;MACD,IAAI,OAAO,UAAU,UAAU;EACrC,MAAM,UAAU,UAAmB,OAAO,SAAS;EACnD,IAAI,OAAO,YAAY,YAAY,QAAQ,KAAK,CAAC,CAAC,SAAS,GAC1D,OAAO;OAEP,IAAI;GACH,OAAO,KAAK,UAAU,KAAK,KAAK,OAAO,KAAK;EAC7C,QAAQ;GACP,OAAO,OAAO,KAAK;EACpB;CAEF,OACC,OAAO,OAAO,KAAK;CAEpB,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,IAAI,OAAO,KAAA;AACxC;;;;;;;;;;;;;;;;;;;;;;;AAwBA,eAAsB,wBACrB,YACA,QAC+B;CAC/B,MAAM,EACL,QAAQ,WACR,SACA,cACA,eACG,MAAM,kBAAkB,YAAY,MAAM;CAC9C,MAAM,EAAE,QAAQ,gBAAgB,wBAAwB,SAAS;CAEjE,OAAO;EACN;EACA;EACA,GAAI,YAAY,SAAS,KAAK,EAAE,YAAY;EAC5C,GAAI,gBAAgB,EAAE,aAAa;EACnC,GAAI,cAAc,EAAE,WAAW;CAChC;AACD;;;;;;ACrHA,SAAS,qBAAqB,UAA8C;CAC3E,IAAI;EACH,OAAO,KAAK,UAAU,QAAQ,CAAC,EAAE,UAAU;CAC5C,QAAQ;EACP,OAAO;CACR;AACD;;AAGA,IAAa,gBAAb,MAA2B;CAC1B;CACA;CACA;CACA,0BAA2B,IAAI,IAAwB;CACvD,QAAgB;CAChB,OAAe;CACf,SAAiB;CACjB,YAAoB;CAEpB,YAAY,SAA2C;EACtD,MAAM,SAAS,WAAW;EAC1B,KAAK,WAAW,KAAK,IAAI,GAAG,QAAQ,YAAY,CAAC;EACjD,KAAK,QAAQ,QAAQ,SAAS;EAC9B,KAAK,qBAAqB,QAAQ,sBAAsB;CACzD;;CAGA,IAAI,UAAmB;EACtB,OAAO,KAAK,WAAW;CACxB;CAEA,IAAI,KAAgD;EACnD,IAAI,CAAC,KAAK,SAAS,OAAO;EAC1B,MAAM,QAAQ,KAAK,QAAQ,IAAI,GAAG;EAClC,IAAI,CAAC,OAAO;GACX,KAAK,UAAU;GACf,OAAO;EACR;EACA,IAAI,KAAK,QAAQ,KAAK,KAAK,IAAI,IAAI,MAAM,aAAa,KAAK,OAAO;GACjE,KAAK,KAAK,GAAG;GACb,KAAK,UAAU;GACf,OAAO;EACR;EAEA,KAAK,QAAQ,OAAO,GAAG;EACvB,KAAK,QAAQ,IAAI,KAAK,KAAK;EAC3B,KAAK,QAAQ;EACb,OAAO,MAAM;CACd;CAEA,IAAI,KAAa,UAA4C;EAC5D,IAAI,CAAC,KAAK,SAAS;EACnB,MAAM,cAAc,UAAqB,UAAU,QAAQ;EAC3D,IAAI,CAAC,KAAK,sBAAsB,MAAM,QAAQ,WAAW,KAAK,YAAY,SAAS,GAAG;EAEtF,MAAM,UAAU,UAAU,UAAU,UAAU,OAAO,GAAG,SAAS;EACjE,IAAI,UAAU,SAAS,SAAS,MAAM,MAAM;EAC5C,MAAM,YAAY,oBAAoB,QAAQ,KAAK,qBAAqB,QAAQ;EAEhF,IAAI,YAAY,KAAK,UAAU;EAC/B,KAAK,KAAK,GAAG;EACb,KAAK,QAAQ,IAAI,KAAK;GAAE;GAAU,YAAY,KAAK,IAAI;GAAG;EAAU,CAAC;EACrE,KAAK,SAAS;EACd,OAAO,KAAK,QAAQ,KAAK,YAAY,KAAK,QAAQ,OAAO,GAAG;GAC3D,MAAM,SAAS,KAAK,QAAQ,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;GAC1C,IAAI,WAAW,KAAA,GAAW;GAC1B,KAAK,KAAK,MAAM;GAChB,KAAK,aAAa;EACnB;CACD;CAEA,QAAc;EACb,KAAK,QAAQ,MAAM;EACnB,KAAK,QAAQ;CACd;CAEA,QAAoB;EACnB,OAAO;GACN,SAAS,KAAK,QAAQ;GACtB,OAAO,KAAK;GACZ,MAAM,KAAK;GACX,QAAQ,KAAK;GACb,WAAW,KAAK;EACjB;CACD;CAEA,KAAa,KAAmB;EAC/B,MAAM,QAAQ,KAAK,QAAQ,IAAI,GAAG;EAClC,IAAI,CAAC,OAAO;EACZ,KAAK,QAAQ,OAAO,GAAG;EACvB,KAAK,SAAS,MAAM;CACrB;AACD;;ACjGA,MAAM,aACL,WACC,SAAS,mBAAmB,aAAa;;AAG3C,SAAgB,UAAU,OAAoC;CAC7D,IAAI,YAAY;EACf,MAAM,OAAO,WAAW,WAAW,QAAQ;EAC3C,IAAI,OAAO,UAAU,UAAU,KAAK,OAAO,OAAO,SAAS;OACtD,KAAK,OAAO,KAAK;EACtB,OAAO,KAAK,OAAO,KAAK;CACzB;CACA,OAAO,kBAAkB,KAAK;AAC/B;AAMA,MAAM,IAAI,IAAI,WAAW;CACxB;CAAY;CAAY;CAAY;CAAY;CAAY;CAAY;CAAY;CACpF;CAAY;CAAY;CAAY;CAAY;CAAY;CAAY;CAAY;CACpF;CAAY;CAAY;CAAY;CAAY;CAAY;CAAY;CAAY;CACpF;CAAY;CAAY;CAAY;CAAY;CAAY;CAAY;CAAY;CACpF;CAAY;CAAY;CAAY;CAAY;CAAY;CAAY;CAAY;CACpF;CAAY;CAAY;CAAY;CAAY;CAAY;CAAY;CAAY;CACpF;CAAY;CAAY;CAAY;CAAY;CAAY;CAAY;CAAY;CACpF;CAAY;CAAY;CAAY;CAAY;CAAY;CAAY;CAAY;AACrF,CAAC;;AAGD,SAAgB,kBAAkB,OAAoC;CACrE,MAAM,QAAQ,IAAI,OAAO;CACzB,IAAI,OAAO,UAAU,UAAU,MAAM,aAAa,KAAK;MAClD,MAAM,OAAO,KAAK;CACvB,OAAO,MAAM,UAAU;AACxB;AAEA,IAAM,SAAN,MAAa;CACZ,IAAqB,IAAI,WAAW;EACnC;EAAY;EAAY;EAAY;EAAY;EAAY;EAAY;EAAY;CACrF,CAAC;CACD,oBAAqB,IAAI,WAAW,EAAE;CACtC,0BAA2B,IAAI,WAAW,EAAE;CAC5C,gBAAwB;CACxB,aAAqB;CAErB,OAAO,OAAyB;EAC/B,KAAK,cAAc,MAAM;EACzB,IAAI,SAAS;EACb,IAAI,KAAK,gBAAgB,GAAG;GAC3B,MAAM,OAAO,KAAK,IAAI,KAAK,KAAK,eAAe,MAAM,MAAM;GAC3D,KAAK,QAAQ,IAAI,MAAM,SAAS,GAAG,IAAI,GAAG,KAAK,aAAa;GAC5D,KAAK,iBAAiB;GACtB,SAAS;GACT,IAAI,KAAK,gBAAgB,IAAI;GAC7B,KAAK,cAAc,KAAK,SAAS,CAAC;GAClC,KAAK,gBAAgB;EACtB;EACA,OAAO,SAAS,MAAM,MAAM,QAAQ,UAAU,IAAI,KAAK,cAAc,OAAO,MAAM;EAClF,KAAK,QAAQ,IAAI,MAAM,SAAS,MAAM,GAAG,CAAC;EAC1C,KAAK,gBAAgB,MAAM,SAAS;CACrC;;;;;CAMA,aAAa,GAAiB;EAC7B,MAAM,IAAI,KAAK;EACf,MAAM,QAAQ,EAAE,SAAU,EAAE,SAAS;EACrC,KAAK,IAAI,SAAS,GAAG,SAAS,OAAO,UAAU,IAAI;GAClD,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;IAC5B,MAAM,KAAK,EAAE,WAAW,SAAS,IAAI,CAAC;IACtC,MAAM,KAAK,EAAE,WAAW,SAAS,IAAI,IAAI,CAAC;IAC1C,EAAE,MAAO,KAAK,QAAS,KAAQ,OAAO,KAAM,MAAQ,KAAK,QAAS,IAAM,OAAO;GAChF;GACA,KAAK,SAAS;EACf;EACA,KAAK,cAAc,QAAQ;EAE3B,MAAM,OAAO,IAAI,YAAY,EAAE,SAAS,SAAS,CAAC;EAClD,KAAK,IAAI,IAAI,OAAO,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;GAC7C,MAAM,OAAO,EAAE,WAAW,CAAC;GAC3B,KAAK,OAAO,OAAO;GACnB,KAAK,OAAO,SAAS;EACtB;EACA,KAAK,OAAO,IAAI;CACjB;CAEA,YAAoB;EACnB,MAAM,YAAY,KAAK,aAAa;EACpC,MAAM,OAAO,IAAI,WAAW,KAAK,gBAAgB,KAAK,KAAK,GAAG;EAC9D,KAAK,IAAI,KAAK,QAAQ,SAAS,GAAG,KAAK,aAAa,CAAC;EACrD,KAAK,KAAK,iBAAiB;EAC3B,MAAM,OAAO,IAAI,SAAS,KAAK,MAAM;EACrC,KAAK,UAAU,KAAK,SAAS,GAAG,KAAK,MAAM,YAAY,UAAW,CAAC;EACnE,KAAK,UAAU,KAAK,SAAS,GAAG,cAAc,CAAC;EAC/C,KAAK,IAAI,SAAS,GAAG,SAAS,KAAK,QAAQ,UAAU,IAAI,KAAK,cAAc,MAAM,MAAM;EAExF,IAAI,MAAM;EACV,KAAK,MAAM,QAAQ,KAAK,GAAG,QAAQ,SAAS,EAAA,CAAG,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG;EAC3E,OAAO;CACR;CAEA,cAAsB,OAAmB,QAAsB;EAC9D,MAAM,IAAI,KAAK;EACf,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;GAC5B,MAAM,IAAI,SAAS,IAAI;GACvB,EAAE,KAAM,MAAM,MAAM,KAAO,MAAM,IAAI,MAAM,KAAO,MAAM,IAAI,MAAM,IAAK,MAAM,IAAI;EAClF;EACA,KAAK,SAAS;CACf;;CAGA,WAAyB;EACxB,MAAM,IAAI,KAAK;EACf,KAAK,IAAI,IAAI,IAAI,IAAI,IAAI,KAAK;GAC7B,MAAM,IAAI,EAAE,IAAI;GAChB,MAAM,IAAI,EAAE,IAAI;GAChB,MAAM,MAAO,MAAM,IAAM,KAAK,OAAS,MAAM,KAAO,KAAK,MAAQ,MAAM;GACvE,MAAM,MAAO,MAAM,KAAO,KAAK,OAAS,MAAM,KAAO,KAAK,MAAQ,MAAM;GACxE,EAAE,KAAM,EAAE,IAAI,MAAM,KAAK,EAAE,IAAI,KAAK,KAAM;EAC3C;EAEA,MAAM,IAAI,KAAK;EACf,IAAI,IAAI,EAAE;EACV,IAAI,IAAI,EAAE;EACV,IAAI,IAAI,EAAE;EACV,IAAI,IAAI,EAAE;EACV,IAAI,IAAI,EAAE;EACV,IAAI,IAAI,EAAE;EACV,IAAI,IAAI,EAAE;EACV,IAAI,IAAI,EAAE;EACV,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;GAC5B,MAAM,MAAO,MAAM,IAAM,KAAK,OAAS,MAAM,KAAO,KAAK,OAAS,MAAM,KAAO,KAAK;GACpF,MAAM,KAAM,IAAI,IAAM,CAAC,IAAI;GAC3B,MAAM,KAAM,IAAI,KAAK,KAAK,EAAE,KAAK,EAAE,KAAM;GAGzC,MAAM,OAFO,MAAM,IAAM,KAAK,OAAS,MAAM,KAAO,KAAK,OAAS,MAAM,KAAO,KAAK,QACvE,IAAI,IAAM,IAAI,IAAM,IAAI,KACb;GACxB,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAK,IAAI,KAAM;GACf,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAK,KAAK,KAAM;EACjB;EACA,EAAE,KAAM,EAAE,KAAK,IAAK;EACpB,EAAE,KAAM,EAAE,KAAK,IAAK;EACpB,EAAE,KAAM,EAAE,KAAK,IAAK;EACpB,EAAE,KAAM,EAAE,KAAK,IAAK;EACpB,EAAE,KAAM,EAAE,KAAK,IAAK;EACpB,EAAE,KAAM,EAAE,KAAK,IAAK;EACpB,EAAE,KAAM,EAAE,KAAK,IAAK;EACpB,EAAE,KAAM,EAAE,KAAK,IAAK;CACrB;AACD;;;;;;;;;;;;;;;;;;;;ACrJA,SAAgB,gBAAgB,OAAwB;CAIvD,MAAM,uBAAO,IAAI,QAAgB;CAEjC,MAAM,aAAa,MAAuB;EACzC,IAAI,MAAM,MAAM,OAAO;EACvB,IAAI,OAAO,MAAM,UAChB,OAAO,OAAO,SAAS,CAAC,IAAI,OAAO,CAAC,IAAI;EAEzC,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,WAAW,OAAO,KAAK,UAAU,CAAC;EAE5E,IAAI,OAAO,MAAM,UAAU,OAAO,GAAG,EAAE;EAEvC,IAAI,OAAO,MAAM,UAAU,OAAO;EAClC,IAAI,aAAa,YAAY,OAAO,OAAO,UAAU,CAAC,EAAE;EACxD,IAAI,KAAK,IAAI,CAAC,GAAG,OAAO;EACxB,KAAK,IAAI,CAAC;EACV,IAAI;EACJ,IAAI,MAAM,QAAQ,CAAC,GAAG;GACrB,MAAM,QAAkB,CAAC;GAGzB,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK,MAAM,KAAK,UAAU,EAAE,EAAE,CAAC;GAC7D,MAAM,IAAI,MAAM,KAAK,GAAG,EAAE;EAC3B,OAAO,IAAI,OAAQ,EAA2B,WAAW,YAExD,MAAM,UAAW,EAAgC,OAAO,CAAC;OACnD,IAAI,aAAa,KAEvB,MAAM,SADU,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,SAAS,IAAI,UAAU,CAAC,EAAE,GAAG,UAAU,GAAG,EAAE,EACjE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,EAAE;OAClC,IAAI,aAAa,KAEvB,MAAM,SADQ,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,SACf,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,EAAE;OAMtC,MAAM,IAJO,OAAO,KAAK,CAAC,CAAC,CAAC,KACX,CAAC,CAAC,KACjB,MAAM,GAAG,KAAK,UAAU,CAAC,EAAE,GAAG,UAAW,EAA8B,EAAE,GAE7D,CAAC,CAAC,KAAK,GAAG,EAAE;EAE3B,KAAK,OAAO,CAAC;EACb,OAAO;CACR;CAEA,OAAO,UAAU,KAAK;AACvB;;AAGA,SAAgB,eAAe,YAA6B,UAA2B;CACtF,OAAO,4BAA4B,eAAe,UAAU,GAAG,QAAQ;AACxE;;;;;AAMA,SAAgB,4BAA4B,gBAAwB,UAA2B;CAC9F,OAAO,GAAG,eAAe,KAAK,UAAU,gBAAgB,QAAQ,CAAC;AAClE;;;;;AAMA,SAAgB,eAAe,YAAqC;CACnE,IAAI,gBAAgB,UAAU,GAAG,OAAO,KAAK,WAAW;CACxD,OAAO,OAAO,eAAe,WAC1B,KAAK,UAAU,UAAU,MACzB,MAAM,UAAU,UAAU;AAC9B;;ACpEA,MAAM,wBAAwB;;AA2B9B,SAAS,iBAAiB,OAAuB;CAChD,IAAI,OAAO,MAAM,KAAK,GACrB,MAAM,IAAI,aAAa,kCAAkC,WAAW,gBAAgB,EACnF,SAAS,EAAE,eAAe,MAAM,EACjC,CAAC;CAEF,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,CAAC;AACrC;AAEA,MAAM,qCAAqB,IAAI,QAA4B;;;;;AAM3D,SAAS,mBAAmB,YAAqC;CAChE,IAAI,EAAE,sBAAsB,aAAa,OAAO,eAAe,UAAU;CACzE,IAAI,OAAO,mBAAmB,IAAI,UAAU;CAC5C,IAAI,SAAS,KAAA,GAAW;EACvB,OAAO,eAAe,UAAU;EAChC,mBAAmB,IAAI,YAAY,IAAI;CACxC;CACA,OAAO;AACR;;AAGA,IAAM,mBAAN,MAAuB;CAKJ;CACA;CALlB;CACA;CAEA,YACC,gBACA,UACC;EAFgB,KAAA,iBAAA;EACA,KAAA,WAAA;CACf;CAEH,IAAI,iBAAyB;EAC5B,OAAQ,KAAK,mBAAmB,KAAK,eAAe;CACrD;CAEA,IAAI,MAAc;EACjB,OAAQ,KAAK,YAAY,4BAA4B,KAAK,gBAAgB,KAAK,QAAQ;CACxF;AACD;;;;;AAMA,IAAM,aAAN,MAAiB;CAChB,QAA6C,CAAC;CAC9C,OAAe;CACf,uBAAwB,IAAI,IAAiB;CAE7C,IAAI,OAAe;EAClB,OAAO,KAAK,KAAK;CAClB;CAEA,KAAK,MAAyB;EAC7B,KAAK,MAAM,KAAK,IAAI;EACpB,KAAK,KAAK,IAAI,IAAI;CACnB;CAEA,OAAO,MAAyB;EAC/B,IAAI,KAAK,KAAK,OAAO,IAAI,GAAG,KAAK,QAAQ;CAC1C;CAEA,QAAiC;EAChC,OAAO,KAAK,OAAO,KAAK,MAAM,QAAQ;GACrC,MAAM,OAAO,KAAK,MAAM,KAAK;GAE7B,KAAK,MAAM,KAAK,UAAU,KAAA;GAC1B,IAAI,QAAQ,KAAK,KAAK,OAAO,IAAI,GAAG;IACnC,KAAK,QAAQ;IACb,OAAO;GACR;EACD;CAED;CAEA,UAAwB;EACvB,IAAI,KAAK,KAAK,SAAS,GAAG;GACzB,KAAK,QAAQ,CAAC;GACd,KAAK,OAAO;GACZ;EACD;EACA,MAAM,OAAO,KAAK,MAAM,SAAS,KAAK,KAAK;EAC3C,IAAI,QAAQ,MAAM,QAAQ,KAAK,KAAK,MAAM;EAC1C,KAAK,QAAQ,KAAK,MAAM,QAAQ,SAAS,SAAS,KAAA,KAAa,KAAK,KAAK,IAAI,IAAI,CAAC;EAClF,KAAK,OAAO;CACb;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,IAAa,iBAAb,MAA4B;CAC3B;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CAEA;CAEA;;CAEA,kCAAmC,IAAI,IAAoB;;CAE3D,uBAAuE;CAEvE;CACA;CACA;CAEA,8BAA+B,IAAI,IAAgB;CAEnD,2BAA4B,IAAI,IAAkB;CAClD,uBAAmD;CACnD,OAAwB,IAAI,WAAW;CAEvC,cAAyD;CACzD,aAA0C;CAC1C,kBAAyC;CAEzC,WAAmB;;CAEnB,eAAuB;CAEvB,WAAmB;CAEnB,YACC,UACA,YACA,UAAiC,CAAC,GACjC;EACD,KAAK,WAAW;EAChB,KAAK,aAAa;EAClB,KAAK,OAAO,QAAQ,QAAQ;EAC5B,IAAI,KAAK,SAAS,iBAAiB,KAAK,SAAS,SAChD,MAAM,IAAI,aACT,2BAA2B,OAAO,KAAK,IAAI,EAAE,oEAC7C,WAAW,gBACX,EAAE,SAAS,EAAE,MAAM,KAAK,KAAK,EAAE,CAChC;EAED,KAAK,gBAAgB,iBAAiB,QAAQ,iBAAiB,CAAC;EAEhE,KAAK,gBACJ,QAAQ,kBAAkB,KAAA,KAAa,QAAQ,gBAAgB,IAC5D,KAAK,MAAM,QAAQ,aAAa,IAChC,KAAA;EACJ,KAAK,cACJ,QAAQ,gBAAgB,KAAA,KAAa,QAAQ,cAAc,IACxD,QAAQ,cACR,KAAA;EACJ,KAAK,YAAY,QAAQ;EACzB,KAAK,QAAQ,QAAQ;EAErB,KAAK,QAAQ,IAAI,cAAc,QAAQ,KAAK;EAE5C,KAAK,6BAA6B,QAAQ,8BAA8B;EAExE,KAAK,UAAU,QAAQ;EACvB,KAAK,WAAW,QAAQ;EACxB,KAAK,eAAe,QAAQ;CAC7B;CAEA,IAAI,YAAqB;EACxB,OAAO,KAAK,SAAS,OAAO;CAC7B;CAEA,IAAI,aAAsB;EACzB,OAAO,KAAK,yBAAyB,QAAQ,KAAK,KAAK,OAAO;CAC/D;CAEA,IAAI,gBAAwB;EAC3B,OAAO,KAAK,SAAS;CACtB;CAEA,IAAI,aAAqB;EACxB,OAAO,KAAK,KAAK,QAAQ,KAAK,uBAAuB,IAAI;CAC1D;CAEA,IAAI,aAAgD;EACnD,OAAO,KAAK;CACb;CAEA,IAAI,YAAiC;EACpC,OAAO,KAAK;CACb;CAEA,IAAI,iBAAgC;EACnC,OAAO,KAAK;CACb;;;;;;;;;;;CAYA,iBAAiB,OAAqB;EACrC,MAAM,OAAO,iBAAiB,KAAK;EACnC,IAAI,SAAS,KAAK,eAAe;EACjC,MAAM,SAAS,OAAO,KAAK;EAC3B,KAAK,gBAAgB;EACrB,IAAI,QAAQ,KAAK,UAAU;CAC5B;;CAGA,UAAU,UAAkC;EAC3C,KAAK,YAAY,IAAI,QAAQ;EAC7B,aAAa,KAAK,YAAY,OAAO,QAAQ;CAC9C;CAEA,SAAuB;EACtB,KAAK,MAAM,YAAY,KAAK,aAC3B,IAAI;GACH,SAAS;EACV,SAAS,KAAK;GACb,UAAU,CAAC,CAAC,MAAM,sCAAsC,GAAG;EAC5D;CAEF;;;;;;;;;;;;;;;;;;;;;;;;;CA0BA,MACC,YACA,UACA,SACsC;EACtC,OAAO,KAAK,cAAc,YAAY,UAAU,OAAO,CAAC,CAAC,MAAM,WAAW,OAAO,QAAQ;CAC1F;;CAGA,cACC,YACA,UACA,SACuB;EACvB,IAAI,KAAK,UACR,OAAO,QAAQ,OACd,IAAI,aACH,uDACA,WAAW,aACZ,CACD;EAGD,MAAM,SAAS,IAAI,uBAAuB,KAAK,mBAAmB,UAAU,GAAG,QAAQ;EAGvF,IAAI,MAAM;EACV,IAAI,KAAK,MAAM,WAAW,KAAK,WAAW,KAAK,YAAY,KAAK,cAC/D,IAAI;GACH,MAAM,OAAO;EACd,SAAS,OAAO;GAEf,OAAO,QAAQ,OACd,IAAI,aAAa,mCAAmC,WAAW,eAAe,EAC7E,eAAe,MAChB,CAAC,CACF;EACD;EAED,MAAM,MAAM,EAAE,KAAK;EACnB,MAAM,MAAoB;GAAE;GAAK,YAAY,KAAK,IAAI;GAAG,WAAW;EAAK;EAIzE,IAAI,SAAS,QAAQ,SACpB,OAAO,QAAQ,OAAO,KAAK,eAAe,GAAG,CAAC;EAG/C,MAAM,SAAS,KAAK,MAAM,IAAI,GAAG;EACjC,IAAI,QAAQ;GAGX,IAAI,KAAK,SAAS,eACjB,KAAK,iBAAiB;GAEvB,MAAM,SAAsB;IAC3B,QAAQ;IACR,UAAU;IACV,YAAY;IACZ,WAAW;GACZ;GACA,KAAK,eAAe,KAAK;IAAE,QAAQ;IAAQ,YAAY;GAAE,CAAC;GAC1D,KAAK,QAAQ,KAAK,SAAS,GAAG;GAC9B,KAAK,QAAQ,KAAK,UAAU,KAAK,MAAM;GACvC,KAAK,OAAO;GACZ,OAAO,QAAQ,QAAQ;IAAE,UAAU;IAAQ,WAAW;IAAM,YAAY;GAAE,CAAC;EAC5E;EAEA,OAAO,IAAI,SAAsB,SAAS,WAAW;GACpD,MAAM,OAAoB;IACzB;IACA;IACA;IACA;IACA;IACA;IACA;IACA,gBAAgB,SAAS;IACzB,aAAa,SAAS;IACtB,gBAAgB,SAAS;GAC1B;GAEA,IAAI,KAAK,gBAAgB;IACxB,KAAK,wBAAwB;KAC5B,IAAI,KAAK,YAAY,KAAK,WAAW,MAAM;UACtC,KAAK,gBAAgB,IAAI;IAC/B;IACA,KAAK,eAAe,iBAAiB,SAAS,KAAK,iBAAiB,EAAE,MAAM,KAAK,CAAC;GACnF;GAEA,KAAK,QAAQ,IAAI;EAClB,CAAC;CACF;;CAGA,eACC,KACA,OAGO;EACP,IAAI,MAAM,KAAK,cAAc;EAC7B,KAAK,eAAe;EACpB,IAAI,YAAY,OAAO;GACtB,KAAK,cAAc,MAAM;GACzB,KAAK,aAAa;EACnB,OACC,KAAK,aAAa,MAAM;EAEzB,KAAK,kBAAkB,MAAM;CAC9B;;CAGA,mBAAiC;EAChC,IAAI,KAAK,sBAAsB;GAC9B,KAAK,UAAU,KAAK,oBAAoB;GACxC,KAAK,uBAAuB;EAC7B;EACA,KAAK,MAAM,YAAY,KAAK,UAAU;GACrC,KAAK,UAAU,QAAQ;GACvB,SAAS,WAAW,MAAM;EAC3B;CACD;CAEA,QAAgB,MAAyB;EACxC,IAAI,KAAK,yBAAyB,MAAM,KAAK,uBAAuB;EACpE,KAAK,KAAK,OAAO,IAAI;CACtB;CAEA,gBAAwB,MAAyB;EAChD,KAAK,QAAQ,IAAI;EACjB,KAAK,KAAK,MAAM,KAAK,eAAe,KAAK,GAAG,CAAC;EAC7C,KAAK,OAAO;CACb;;CAGA,gBAAwB,MAAyB;EAChD,MAAM,MAAM,IAAI,aACf,wDACA,WAAW,YACX;GACC,YAAY;GACZ,SAAS;IACR,YAAY,KAAK,KAAK;IACtB,eAAe,KAAK;GACrB;EACD,CACD;EACA,KAAK,KAAK,MAAM,GAAG;EACnB,KAAK,OAAO;CACb;CAEA,kBAA0B,MAAyB;EAClD,IAAI,KAAK,gBAAgB,KAAA,GAAW;EACpC,MAAM,SAAS,KAAK;EACpB,KAAK,iBAAiB,iBAAiB;GACtC,IAAI,KAAK,SAAS;GAClB,KAAK,QAAQ,IAAI;GAEjB,MAAM,MAAM,IAAI,aACf,4BAA4B,OAAO,uCACnC,WAAW,eACX;IACC,YAAY;IACZ,SAAS;KACR,UAAU,KAAK,IAAI,IAAI,KAAK,IAAI;KAChC,aAAa;IACd;GACD,CACD;GACA,KAAK,KAAK,MAAM,GAAG;GACnB,KAAK,OAAO;EACb,GAAG,MAAM;CACV;CAEA,oBAA4B,MAAyB;EACpD,IAAI,KAAK,mBAAmB,KAAA,GAAW;GACtC,aAAa,KAAK,cAAc;GAChC,KAAK,iBAAiB,KAAA;EACvB;CACD;CAEA,QAAgB,MAAyB;EACxC,QAAQ,KAAK,MAAb;GACC,KAAK;IACJ,KAAK,iBAAiB;IACtB,IAAI,KAAK,SAAS,SAAS,GAC1B,KAAU,QAAQ,IAAI;SAEtB,KAAK,uBAAuB;IAE7B;GAGD,KAAK,SACJ,IAAI,KAAK,SAAS,OAAO,KAAK,eAC7B,KAAU,QAAQ,IAAI;QAChB,IAAI,KAAK,kBAAkB,KAAA,KAAa,KAAK,KAAK,QAAQ,KAAK,eAErE,KAAK,gBAAgB,IAAI;QACnB;IACN,KAAK,kBAAkB,IAAI;IAC3B,KAAK,KAAK,KAAK,IAAI;GACpB;EAGF;EACA,KAAK,OAAO;CACb;CAEA,MAAc,QAAQ,MAAkC;EACvD,MAAM,aAAa,IAAI,gBAAgB;EAEvC,MAAM,WAAW;EACjB,SAAS,aAAa;EACtB,KAAK,SAAS,IAAI,QAAQ;EAC1B,KAAK,IAAI,YAAY,KAAK,IAAI;EAE9B,KAAK,oBAAoB,IAAI;EAE7B,KAAK,QAAQ,KAAK,SAAS,KAAK,GAAG;EACnC,KAAK,OAAO;EAEZ,MAAM,YAAY,YAAY,IAAI;EAClC,IAAI;GACH,MAAM,SAAmC;IACxC,GAAG,KAAK;IACR,QAAQ,WAAW;IACnB,GAAI,KAAK,cAAc,KAAA,KAAa,EAAE,WAAW,KAAK,UAAU;IAChE,GAAI,KAAK,UAAU,KAAA,KAAa,EAAE,OAAO,KAAK,MAAM;IACpD,GAAI,KAAK,gBAAgB,KAAA,KAAa,EAAE,aAAa,KAAK,YAAY;GACvE;GACA,MAAM,aAAa,KAAK;GACxB,IAAI,YAAY;IACf,MAAM,aAAa,KAAK,WAAW;IACnC,OAAO,kBAAkB,QAAQ,cAAc;KAC9C,IAAI;MACH,aAAa,QAAQ,SAAS;KAC/B,UAAU;MACT,WAAW,QAAQ,SAAS;KAC7B;IACD;GACD;GAEA,MAAM,EAAE,UAAU,yBAAyB,MAAM,KAAK,YAAY,MAAM,MAAM;GAC9E,MAAM,aAAa,YAAY,IAAI,IAAI;GAGvC,IAAI,CAAC,KAAK,UAAU,KAAK,MAAM,IAAI,KAAK,IAAI,KAAK,QAAQ;GAEzD,IACC,CAAC,KAAK,cAAc,MAAM;IAAE;IAAU,WAAW;IAAO;IAAsB;GAAW,CAAC,GAE1F;GAGD,KAAK,eAAe,KAAK,KAAK;IAAE,QAAQ;IAAU;GAAW,CAAC;GAE9D,KAAK,QAAQ,KAAK,UAAU,KAAK,KAAK;IACrC,QAAQ;IACR;IACA;IACA,WAAW;IACX;GACD,CAAC;EACF,SAAS,OAAO;GACf,MAAM,aAAa,YAAY,IAAI,IAAI;GACvC,MAAM,MAAM,KAAK,wBAAwB,OAAO,QAAQ;GACxD,KAAK,eAAe,KAAK,KAAK;IAAE,OAAO;IAAK;GAAW,CAAC;GACxD,KAAK,KAAK,MAAM,KAAK,UAAU;EAChC,UAAU;GACT,KAAK,SAAS,OAAO,QAAQ;GAC7B,KAAK,UAAU;GACf,KAAK,OAAO;EACb;CACD;CAEA,MAAc,YACb,MACA,QACoF;EACpF,MAAM,EAAE,YAAY,aAAa;EACjC,IAAI,CAAC,KAAK,8BAA8B,gBAAgB,UAAU,GAAG;GACpE,MAAM,EAAE,aAAa,MAAM,KAAK,SAAS,YAAY,UAAU,MAAM,MAAM;GAC3E,OAAO,EAAE,SAAS;EACnB;EAEA,MAAM,iBAAiB,KAAK,OAAO;EACnC,MAAM,WAAW,KAAK,gBAAgB,IAAI,cAAc,KAAK;EAC7D,MAAM,SAAS,MAAM,KAAK,SAAS,YAAY,UAAU,UAAU,MAAM;EAGzE,KAAK,gBAAgB,OAAO,cAAc;EAC1C,IAAI,OAAO,UAAU;GACpB,KAAK,gBAAgB,IAAI,gBAAgB,OAAO,QAAQ;GACxD,IAAI,KAAK,gBAAgB,OAAO,uBAC/B,KAAK,gBAAgB,OAAO,KAAK,gBAAgB,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,KAAM;EAEvE;EAEA,OAAO;GAAE,UAAU,OAAO;GAAU,sBAAsB,OAAO;EAAO;CACzE;CAEA,mBAA2B,YAAqC;EAC/D,IAAI,OAAO,eAAe,UAAU,OAAO,mBAAmB,UAAU;EACxE,IAAI,KAAK,sBAAsB,UAAU,YACxC,KAAK,uBAAuB;GAAE,OAAO;GAAY,MAAM,eAAe,UAAU;EAAE;EAEnF,OAAO,KAAK,qBAAqB;CAClC;CAEA,YAA0B;EACzB,IAAI,KAAK,UAAU;EAEnB,IAAI,KAAK,SAAS,eAAe;GAChC,IAAI,KAAK,wBAAwB,KAAK,SAAS,SAAS,GAAG;IAC1D,MAAM,OAAO,KAAK;IAClB,KAAK,uBAAuB;IAC5B,KAAU,QAAQ,IAAI;GACvB;GACA;EACD;EAEA,OAAO,KAAK,KAAK,OAAO,KAAK,KAAK,SAAS,OAAO,KAAK,eACtD,KAAU,QAAQ,KAAK,KAAK,MAAM,CAAE;CAEtC;CAEA,UAAkB,MAAyB;EAC1C,MAAM,MAAM,IAAI,aAAa,6BAA6B,WAAW,YAAY,EAChF,SAAS,EAAE,YAAY,KAAK,IAAI,WAAW,EAC5C,CAAC;EACD,IAAI,KAAK,YAAY,MAAM,GAAG,GAC7B,KAAK,QAAQ,KAAK,cAAc,KAAK,GAAG;CAE1C;CAEA,eAAuB,KAAiC;EACvD,OAAO,gBAAgB,EAAE,YAAY,IAAI,WAAW,CAAC;CACtD;;;;;;;CAQA,YAAoB,MAAmB,KAA4B;EAClE,IAAI,CAAC,KAAK,YAAY,MAAM,EAAE,OAAO,IAAI,CAAC,GAAG,OAAO;EACpD,KAAK,OAAO,GAAG;EACf,OAAO;CACR;;;;;CAMA,KAAa,MAAmB,KAAmB,aAAa,GAAS;EACxE,IAAI,KAAK,YAAY,MAAM,GAAG,GAC7B,KAAK,QAAQ,KAAK,UAAU,KAAK,KAAK;GAAE,QAAQ;GAAS,OAAO;GAAK;EAAW,CAAC;CAEnF;CAEA,cAAsB,MAAmB,QAA8B;EACtE,IAAI,CAAC,KAAK,YAAY,MAAM,EAAE,IAAI,KAAK,CAAC,GAAG,OAAO;EAClD,KAAK,QAAQ,MAAM;EACnB,OAAO;CACR;CAEA,YAAoB,MAAmB,SAAuD;EAC7F,IAAI,KAAK,SAAS,OAAO;EACzB,KAAK,UAAU;EACf,KAAK,oBAAoB,IAAI;EAC7B,IAAI,KAAK,iBACR,KAAK,gBAAgB,oBAAoB,SAAS,KAAK,eAAe;EAEvE,OAAO;CACR;CAEA,wBAAgC,OAAgB,MAAkC;EAEjF,IAAI,KAAK,WAAW,WAAW,KAAK,SACnC,OAAO,KAAK,QAAQ;EAGrB,IAAI,iBAAiB,cAAc,OAAO;EAG1C,IAAI,iBAAiB,SAAS,MAAM,SAAS,cAC5C,OAAO,KAAK,eAAe,KAAK,GAAG;EAGpC,OAAO,IAAI,aACV,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GACrD,WAAW,eACX,EAAE,eAAe,MAAM,CACxB;CACD;;CAOA,YAAkB;EACjB,IAAI,KAAK,sBAAsB;GAC9B,KAAK,KAAK,KAAK,sBAAsB,KAAK,eAAe,KAAK,qBAAqB,GAAG,CAAC;GACvF,KAAK,uBAAuB;EAC7B;EACA,KAAK,IAAI,OAAO,KAAK,KAAK,MAAM,GAAG,MAAM,OAAO,KAAK,KAAK,MAAM,GAC/D,KAAK,KAAK,MAAM,KAAK,eAAe,KAAK,GAAG,CAAC;EAE9C,KAAK,MAAM,YAAY,KAAK,UAAU;GAErC,MAAM,aAAa,SAAS,IAAI,YAAY,KAAK,IAAI,IAAI,SAAS,IAAI,YAAY;GAClF,KAAK,KAAK,UAAU,KAAK,eAAe,SAAS,GAAG,GAAG,UAAU;GACjE,SAAS,WAAW,MAAM;EAC3B;EACA,KAAK,OAAO;CACb;CAMA,aAAmB;EAClB,KAAK,MAAM,MAAM;CAClB;;;;;;;;;;;CAYA,aAAyB;EACxB,OAAO,KAAK,MAAM,MAAM;CACzB;CAMA,UAAgB;EACf,IAAI,KAAK,UAAU;EACnB,KAAK,WAAW;EAChB,KAAK,UAAU;EACf,KAAK,YAAY,MAAM;EACvB,KAAK,WAAW;CACjB;CAEA,QACC,MACA,GAAG,MACI;EACP,IAAI,CAAC,MAAM;EACX,IAAI;GACH,KAAK,GAAG,IAAI;EACb,SAAS,KAAK;GACb,UAAU,CAAC,CAAC,MAAM,gCAAgC,GAAG;EACtD;CACD;AACD;;AC7uBA,SAAS,mBAAmB,YAAqC;CAChE,IAAI,gBAAgB,UAAU,GAAG,OAAO,OAAO,WAAW;CAC1D,IAAI,OAAO,eAAe,YAAY,WAAW,UAAU,KAAK,OAAO;CACvE,OAAO,gBAAgB,UAAU,IAAI,UAAU,UAAU,IAAI;AAC9D;;AAGA,SAAS,gBAAgB,OAAwB;CAChD,IAAI,EAAE,iBAAiB,eACtB,OAAO,mBAAmB,iBAAiB,QAAQ,MAAM,OAAO,OAAO;CAExE,MAAM,SAAS,MAAM,eAAe,KAAA,IAAY,KAAK,SAAS,MAAM;CACpE,MAAM,YAAY,MAAM,SAAS;CACjC,OAAO,mBAAmB,MAAM,OAAO,SAAS,YAAY,KAAK,UAAU,KAAK;AACjF;;;;;AAaA,SAAS,kBAAkB,UAA8C;CACxE,IAAI,CAAC,MAAM,QAAQ,QAAQ,GAAG,OAAO,CAAC;CAEtC,OAAO,SAAS,KAAK,SAAS;EAC7B,IAAI,QAAQ;EACZ,IAAI,QAAQ;EAEZ,MAAM,YAAY,UAAmC,MAAM,WAAW;EACtE,IAAI,aAAa,OAAO,cAAc,UACrC,KAAK,MAAM,UAAU,OAAO,OAAO,SAAS,GAAG;GAC9C,IAAI,CAAC,MAAM,QAAQ,MAAM,GAAG;GAC5B,SAAS,OAAO;GAChB,KAAK,MAAM,QAAQ,QAAQ;IAC1B,MAAM,OAAQ,MAAoC;IAClD,IAAI,OAAO,SAAS,UAAU,SAAS,KAAK;GAC7C;EACD;EAGD,OAAO;GAAE,OAAO,UAAkB,MAAM,WAAW,KAAK;GAAa;GAAO;EAAM;CACnF,CAAC;AACF;;;;;;;;;;;;;;;;;;;;;;AAuBA,IAAqB,oBAArB,MAAqB,kBAAkB;CACtC;CACA;CACA,6BAA8B,IAAI,IAAoB;CACtD,WAAmB;;;;;CAMnB,OAAwB,0BAA0B;CAElD,YAAoB,QAAkC;EACrD,KAAK,SAAS;GACb,GAAG;GACH,WAAW,kBAAkB,OAAO,SAAS;GAC7C,OAAO,OAAO,SAAS;EACxB;EACA,KAAK,cAAc,IAAI,mBAAmB,KAAK,OAAO,WAAW,KAAK,MAAM;CAC7E;;;;;;;;;;;;CAaA,aAAa,OACZ,QACA,UAA+B,CAAC,GACH;EAC7B,MAAM,SAAS,IAAI,kBAAkB,MAAM;EAC3C,MAAM,EAAE,WAAW;EAEnB,MAAM,WAAW,KAAK,IAAI,IAAI,QAAQ,OAAO,YAAY,KAAK,CAAC;EAC/D,MAAM,cAAc,QAAQ,OAAO,eAAe;EAClD,MAAM,aAAa,QAAQ,OAAO,cAAc;EAEhD,IAAI;EACJ,IAAI,UAA+B;EACnC,IAAI,OAAO;EACX,KAAK,IAAI,UAAU,GAAG,UAAU,YAAY,CAAC,QAAQ,SAAS,WAAW;GACxE,OAAO,UAAU;GACjB,YAAY,MAAM,OAAO,YAAY,YACpC,kBAAkB,yBAClB,MACD;GACA,IAAI,UAAU,QACb,OAAO;GAGR,UAAU,qBAAqB,SAAS;GAGxC,IAAI,WAAW,CAAC,QAAQ,WAAW;GAEnC,IAAI,UAAU,WAAW,GAAG;IAC3B,MAAM,QAAQ,KAAK,IAAI,cAAc,KAAK,SAAS,UAAU;IAE7D,MAAM,MAAM,OAAO,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;GAC1C;EACD;EAEA,MAAM,OAAO,QAAQ;EACrB,IAAI,QAAQ,SACX,MAAM,gBAAgB;GAAE,WAAW,OAAO,OAAO;GAAW,UAAU;EAAK,CAAC;EAE7E,MAAM,SAAS,WAAW;EAC1B,MAAM,UAAU,UACb,0CAA0C,QAAQ,YAClD;EACH,MAAM,IAAI,aAAa,SAAS,WAAW,eAAe;GACzD,GAAI,WAAW,KAAA,KAAa,EAAE,YAAY,OAAO;GACjD,SAAS;IACR,WAAW,OAAO,OAAO;IAGzB,UAAU;IACV,aAAa;IACb,GAAI,WAAW;KAAE,cAAc,QAAQ;KAAS,gBAAgB,QAAQ;IAAU;IAClF,GAAI,WAAW,KAAA,KAAa,EAAE,iBAAiB,OAAO;IACtD,GAAI,WAAW,UAAU,KAAA,KAAa,EAAE,gBAAgB,UAAU,MAAM;GACzE;EACD,CAAC;CACF;;;;CAKA,MAAa,MAAM,YAAiC;EACnD,KAAK,kBAAkB;EACvB,OAAO,wBAAwB,YAAY,KAAK,MAAM;CACvD;CAEA,MAAa,SAAS,YAAiC;EACtD,KAAK,kBAAkB;EACvB,OAAO,kBAAkB,YAAY,KAAK,MAAM;CACjD;;;;;;;;;;;;;CAcA,MAAa,MACZ,YACA,UACA,SACsC;EACtC,KAAK,kBAAkB;EAEvB,MAAM,sBAAsB;GAC3B,YAAY,mBAAmB,UAAU;GACzC,cAAc,kBAAkB,QAAQ;EACzC;EAEA,IAAI;GACH,IAAI,OAAO,eAAe,YAAY,CAAC,WAAW,KAAK,GACtD,MAAM,IAAI,aAAa,sCAAsC,WAAW,eAAe,EACtF,SAAS,EAAE,aAAa,WAAW,EACpC,CAAC;QACK,IAAI,sBAAsB,cAAc,WAAW,WAAW,GACpE,MAAM,IAAI,aAAa,+BAA+B,WAAW,aAAa;QACxE,IAAI,gBAAgB,UAAU,KAAK,CAAC,WAAW,IAAI,KAAK,GAC9D,MAAM,IAAI,aAAa,8BAA8B,WAAW,aAAa;GAW9E,MAAM,SAAS,MAAM,2BAA2B,UAAU,YAAY;IAPrE,GAAG,KAAK;IACR,GAAI,SAAS,WAAW,KAAA,KAAa,EAAE,QAAQ,QAAQ,OAAO;IAC9D,GAAI,SAAS,cAAc,KAAA,KAAa,EAAE,WAAW,QAAQ,UAAU;IACvE,GAAI,SAAS,UAAU,KAAA,KAAa,EAAE,OAAO,QAAQ,MAAM;IAC3D,GAAI,SAAS,gBAAgB,KAAA,KAAa,EAAE,aAAa,QAAQ,YAAY;GAGR,CAAe;GAGrF,MAAM,cAAc,UAAqB,QAAQ,QAAQ;GACzD,IAAI,MAAM,QAAQ,WAAW,KAAK,YAAY,SAAS,GACtD,MAAM,IAAI,aACT,YAAY,IAAI,MAAM,CAAC,CAAC,KAAK,IAAI,KAAK,sBACtC,WAAW,mBACX,EACC,SAAS;IACR,GAAG,aAAa;IAChB,QAAQ;IACR,UAAU,UAAqB,QAAQ,UAAU;IACjD,QAAQ,OAAO;GAChB,EACD,CACD;GAGD,OAAO;EACR,SAAS,OAAO;GACf,IAAI,KAAK,OAAO,OACf,UAAU,CAAC,CAAC,MAAM,gBAAgB,KAAK,CAAC;GAGzC,IAAI,iBAAiB,cACpB,MAAM;GAGP,MAAM,IAAI,aACT,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GACrD,WAAW,mBACX;IAAE,SAAS,aAAa;IAAG,eAAe;GAAM,CACjD;EACD;CACD;;;;;;;;;;;;;;;CAgBA,gBAAuB,SAAiD;EACvE,KAAK,kBAAkB;EACvB,MAAM,YAAY,IAAI,gBACpB,YAAY,UAAU,UAAU,WAChC,wBAAwB,UAAU,UAAU,YAAY,MAAM,GAC/D,KAAK,QACL,OACD;EACA,KAAK,WAAW,IAAI,SAAS;EAC7B,OAAO;CACR;;CAGA,MAAa,UAAyB;EACrC,IAAI,KAAK,UAAU;EAEnB,KAAK,WAAW;EAChB,KAAK,MAAM,aAAa,KAAK,YAAY,UAAU,QAAQ;EAC3D,KAAK,WAAW,MAAM;EACtB,MAAM,KAAK,YAAY,QAAQ;CAChC;CAEA,oBAAkC;EACjC,IAAI,KAAK,UACR,MAAM,IAAI,aACT,0DACA,WAAW,aACZ;CAEF;AACD;;;AC1UA,SAAgB,mBAAmB,OAA2C;CAC7E,OACC,OAAO,UAAU,YACjB,UAAU,QACT,MAA2B,kBAAkB;AAEhD;AAEA,SAAgB,gBAAgB,WAAmB,KAAgC;CAClF,OAAO;EAAE,eAAe;EAAM,MAAM;EAAW;CAAI;AACpD;;;;;;;;;;;;;AAcA,SAAgB,oBAAoB,OAAsB;CAGzD,MAAM,uBAAO,IAAI,QAAgB;CAEjC,MAAM,QAAQ,MAAqB;EAClC,IAAI,CAAC,KAAK,OAAO,MAAM,UAAU;EACjC,IAAI,KAAK,IAAI,CAAC,GAAG;EACjB,KAAK,IAAI,CAAC;EAEV,MAAM,MAAO,EAA2B;EACxC,IAAI,OAAO,QAAQ,YAAY;GAC9B,MAAM,YAAa,EAAoC;GACvD,IAAI,OAAO,cAAc,cAAc,CAAC,UAAU,KAAK,CAAC,GACvD,IAAoB,KAAK,CAAC;GAE3B;EACD;EAEA,IAAI,MAAM,QAAQ,CAAC,GAAG;GACrB,KAAK,MAAM,QAAQ,GAAG,KAAK,IAAI;GAC/B;EACD;EAGA,MAAM,QAAQ,OAAO,eAAe,CAAC;EACrC,IAAI,UAAU,OAAO,aAAa,UAAU,MAC3C,KAAK,MAAM,QAAQ,OAAO,OAAO,CAAC,GAAG,KAAK,IAAI;CAEhD;CAEA,KAAK,KAAK;AACX;;ACpDA,MAAM,2BAAW,IAAI,IAA0B,CAC9C,CACC,2BACC,OAAO,SAAS;CAChB,MAAM,IAAI;CACV,IAAI,CAAC,KAAK,OAAO,EAAE,MAAM,UAAU,OAAO;CAC1C,OAAO,IAAI,MAAM,MAAM;EAAC,EAAE;EAAG,EAAE;EAAa,EAAE;CAAW,CAAC;AAC3D,CACD,GACA,CACC,wBACC,OAAO,SAAS;CAChB,MAAM,IAAI;CACV,IAAI,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,EAAE,IAAI,OAAO;CACnC,OAAO,IAAI,MAAM,KAAK;EAAC,EAAE,KAAK;EAAG,EAAE,KAAK;EAAG,EAAE,KAAK;CAAC,GAAG;EAAC,EAAE,GAAG;EAAG,EAAE,GAAG;EAAG,EAAE,GAAG;CAAC,CAAC;AAC/E,CACD,CACD,CAAC;;;;;;;AAQD,SAAS,oBAAoB,YAA2C;CACvE,OAAO,QACN,cACA,OAAO,eAAe,YACtB,OAAQ,WAAkC,SAAS,QACpD;AACD;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,oBACf,YACA,WACA,OACU;CACV,MAAM,UAAU,SAAS,IAAI,SAAS;CAKtC,IAAI,eAAe;CACnB,IAAI,SACH,IAAI;EACH,MAAM,UAAU,QAAQ,OAAO,UAAU;EACzC,IAAI,WAAW,MAAM,OAAO;CAC7B,SAAS,OAAO;EACf,UAAU,CAAC,CAAC,KAAK,+BAA+B,UAAU,IAAI,KAAK;EACnE,eAAe;CAChB;CAID,IAAI;EACH,IAAI,oBAAoB,UAAU,GAAG,OAAO,MAAM,aAAa,OAAO,UAAU;CACjF,SAAS,OAAO;EACf,UAAU,CAAC,CAAC,KAAK,oBAAoB,UAAU,sBAAsB,KAAK;EAC1E,OAAO,gBAAgB,WAAW,UAAU;CAC7C;CAEA,OAAO,eAAe,gBAAgB,WAAW,UAAU,IAAI;AAChE;;AC7DA,MAAM,wBAAwB;AAE9B,MAAM,iBAAiB;;AAGvB,SAAS,SAAS,MAAc,YAA8B;CAC7D,OAAO,KAAK,SAAS,YAAY,KAAM,cAAc,SAAA;AACtD;AAEA,MAAM,mBAAmB,SAA0B,KAAK,WAAW,GAAG,KAAK,KAAK,WAAW,GAAG;AAE9F,SAAS,cAAc,OAAwB;CAC9C,IAAI,OAAO,UAAU,UAAU,OAAO;CAEtC,MAAM,UAAU,MAAM,KAAK;CAC3B,IAAI,CAAC,gBAAgB,OAAO,KAAK,CAAC,QAAQ,WAAW,IAAG,GAAG,OAAO;CAElE,MAAM,QAAQ,aAAa,OAAO,KAAK;CAGvC,OAAO,OAAO,UAAU,YAAY,gBAAgB,MAAM,KAAK,CAAC,IAC5D,aAAa,KAAK,KAAK,QACxB;AACJ;AAEA,SAAS,mBAAmB,KAAc,MAAc,OAA+B;CACtF,IAAI,SAAA,iBACH,OAAO,OAAO,QAAQ,WAAW,IAAI,QAAQ,YAAY,IAAI,IAAI;CAElE,IAAI,mBAAmB,IAAI,IAAI,GAAG;EACjC,MAAM,QAAQ,OAAO,SAAS,OAAO,GAAG,GAAG,EAAE;EAE7C,OAAO,OAAO,MAAM,KAAK,KAAK,OAAO,cAAc,KAAK,IAAI,QAAQ;CACrE;CACA,IAAI,mBAAmB,IAAI,IAAI,GAAG,OAAO,OAAO,WAAW,OAAO,GAAG,CAAC;CACtE,IAAI,SAAA,kBAA4B,OAAO,OAAO,GAAG,CAAC,CAAC,YAAY,MAAM;CACrE,IAAI,SAAS,KAAK,WAAW,qBAAqB,GACjD,OAAO,oBAAoB,KAAK,MAAM,KAAK;CAE5C,OAAO;AACR;AAEA,SAAS,iBACR,MACA,MACA,aACA,OACU;CACV,IAAI,OAAO,KAAK,SAAS,UAAU,OAAO,KAAK;CAE/C,MAAM,MAAM,cAAc,cAAc,KAAK,IAAI,IAAI,KAAK;CAC1D,IAAI,eAAe,KAAK,SAAS,cAAc,GAC9C,OAAO,WAAW,GAAG,KAAK;CAG3B,OAAO,mBAAmB,KAAK,MAAM,KAAK;AAC3C;;;;;;AAOA,SAAS,WAAW,OAAiC;CACpD,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU,OAAO;CAEhD,MAAM,WAAW,UAAmB,OAAO,UAAU;CACrD,MAAM,WAAW,UAAmB,OAAO,UAAU;CACrD,MAAM,YAAY,UAAmB,OAAO,WAAW;CACvD,MAAM,OAAO,UAAmB,OAAO,iBAAiB;CAExD,IAAI,OAAO,aAAa,YAAY,OAAO,aAAa,UAAU,OAAO;CACzE,IAAI,OAAO,cAAc,UAAU,OAAO;CAE1C,IAAI,CAAC,SAAS,OAAO,MAAM,GAAG,OAAO;CAGrC,IAAI,OAAO,SAAS,aAAa,OAAO,SAAS,UAAU,OAAO;CAElE,MAAM,WAAW,UAAkC,OAAO,UAAU;CAEpE,OAAO;EACN;EACA;EACA,MAAM,UAAkB,OAAO,MAAM;EACrC,iBAAiB,aAAa,IAAI;EAClC;EACA,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;CAChC;AACD;;AAGA,SAAS,kBAAkB,UAAiD;CAC3E,MAAM,SAAS,UAAqB,UAAU,QAAQ;CACtD,OAAO,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC;AAC1C;;AAGA,SAAS,SAAS,MAAwB;CACzC,OAAO,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;AACpD;;AAGA,SAAS,gBAAgB,MAAe,SAAmC;CAC1E,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;CACvC,KAAK,MAAM,QAAQ,OAAO,OAAO,IAAI,GACpC,IAAI,MAAM,QAAQ,IAAI,GAChB;OAAA,MAAM,QAAQ,MAClB,IAAI,QAAQ,OAAO,SAAS,UAAU,QAAQ,IAAgB;CAAA;AAIlE;;;;;;;;;;;AAYA,SAAgB,UACf,UACA,OAAgB,OAChB,UAA4B,CAAC,GACR;CACrB,MAAM,EAAE,cAAc,MAAM,OAAO,aAAa,UAAU;CAC1D,MAAM,SAAwB,CAAC;CAG/B,MAAM,6BAAa,IAAI,IAAY;CAEnC,KAAK,MAAM,SAAS,kBAAkB,QAAQ,GAAG;EAChD,MAAM,YAAY,UAAkB,OAAO,WAAW;EACtD,gBAAgB,UAAU,OAAO,WAAW,IAAI,SAAS;GACxD,MAAM,OAAO,SAAS,IAAI;GAC1B,IAAI,SAAS,MAAM,UAAU,GAAG;GAEhC,MAAM,MAAM,OAAO,KAAK,KAAK;GAC7B,IAAI,CAAC,KAAK;GAEV,MAAM,QAAQ,iBAAiB,MAAM,MAAM,aAAa,KAAK;GAG7D,IAAI,CAAC,OAAO,OAAO,QAAQ,GAAG,GAC7B,YAAY,QAAQ,KAAK,KAAK;QACxB,IAAI,WAAW,IAAI,GAAG,GAC5B,OAAO,IAAI,CAAC,KAAK,KAAK;QAChB;IACN,OAAO,OAAO,CAAC,OAAO,MAAM,KAAK;IACjC,WAAW,IAAI,GAAG;GACnB;EACD,CAAC;CACF;CAEA,OAAO;EAAE,QAAQ;EAAa,eAAe,oBAAoB,MAAM;CAAE;AAC1E;;AAGA,SAAgB,gBAAgB,UAAkD;CACjF,MAAM,SAAqB,CAAC;CAE5B,KAAK,MAAM,SAAS,kBAAkB,QAAQ,GAC7C,gBAAgB,UAAU,OAAO,WAAW,IAAI,SAAS;EACxD,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,cAAc,GAAG;EAE9C,MAAM,OAAO,WAAW,cAAc,KAAK,IAAI,CAAC;EAChD,IAAI,MACH,OAAO,KAAK,IAAI;CAElB,CAAC;CAGF,OAAO;AACR;;;;;;;;;;;;;AAcA,SAAgB,SACf,UACA,SACA,eAAiC,CAAC,GAE5B;CACN,MAAM,EAAE,cAAc,MAAM,OAAO,aAAa,UAAU;CAG1D,KAAK,MAAM,SAAS,kBAAkB,QAAQ,GAAG;EAChD,MAAM,OAAO,UAAU,OAAO,WAAW;EAEzC,IAAI,UAAU;EACd,IAAI,YAAY,SAAS;GACxB,IAAI,UAAkB,OAAO,WAAW,MAAM,QAAQ,QAAQ;GAC9D,UAAU;EACX;EAEA,MAAM,YAAuB,CAAC;EAC9B,gBAAgB,OAAO,SAAS;GAC/B,IAAI,UAAU,SAAS;IACtB,IAAI,KAAK,OAAO,QAAQ,MAAM;IAE9B,UAAU;GACX;GACA,MAAM,OAAO,SAAS,IAAI;GAC1B,IAAI,SAAS,MAAM,UAAU,GAAG;GAChC,UAAU,KAAK,iBAAiB,MAAM,MAAM,aAAa,KAAK,CAAC;EAChE,CAAC;EAED,IAAI,SAAS;GACZ,IAAI,UAAU,WAAW,GAAG,OAAO,KAAA;GACnC,IAAI,UAAU,WAAW,GAAG,OAAO,UAAU;GAC7C,OAAO;EACR;CACD;AAGD;;;AChQA,IAAqB,+BAArB,MAAkD;CAGhC;CAFjB,YAEC,UACC;EADe,KAAA,WAAA;CACd;;;;;;;;;;;;;;;;CAiBH,UACC,OAAgB,OAChB,UAA4B,CAAC,GACR;EACrB,OAAO,UAAa,KAAK,UAAU,MAAM,OAAO;CACjD;;;;;;;;;;;;;;;;CAiBA,SACC,UACA,SAEM;EACN,OAAO,SAAS,KAAK,UAAU,UAAU,OAAO;CACjD;;;;;;;;;;;;;;;;;;;CAoBA,MAAa,oBACZ,YACA,iBACgB;EAChB,MAAM,iBAAiB,gBAAgB,KAAK,QAAQ,GAAG,YAAY,eAAe;CACnF;AACD;;;;;;;;;;;;ACtEA,SAAgB,kBACf,KACkC;CAElC,QADgB,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,GAAG,EAAA,CAChC,KAAK,WAAW;EAC9B,SAAS,UAAqB,OAAO,SAAS;EAC9C,OAAO,UAAkB,OAAO,OAAO;CACxC,EAAE;AACH;;;;;;;;;;;;;;;;;ACVA,MAAM,qCAAqB,IAAI,IAAI;CAAC;CAAW;CAAkB;AAAQ,CAAC;;AAG1E,MAAM,yCAAyB,IAAI,IAAI,CAAC,eAAe,QAAQ,CAAC;AAEhE,MAAM,WAAW,OAAwB,MAAM,OAAO,MAAM;;;;;;AAO5D,SAAS,YAAY,KAAqB;CACzC,IAAI,IAAI,WAAW,KAAK,CAAC,QAAQ,IAAI,EAAE,GAAG,OAAO;CAEjD,IAAI,MAAM;CACV,OAAO,MAAM,IAAI,UAAU,QAAQ,IAAI,IAAI,GAAG;CAE9C,IAAI,MAAM,KAAK,MAAM,IAAI,QAAQ;CAEjC,OAAO,IAAI,MAAM,GAAG,GAAG,CAAC,CAAC,YAAY,IAAI,IAAI,MAAM,GAAG;AACvD;;;;;;AAOA,SAAgB,wBAA2B,KAAW;CACrD,OAAO,eAAe,GAAG;AAC1B;AAEA,SAAS,eAAe,OAAyB;CAChD,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,MAAM,IAAI,cAAc;CACzD,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU,OAAO;CAExD,MAAM,MAA+B,CAAC;CACtC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAgC,GAAG;EAC5E,MAAM,YAAY,YAAY,GAAG;EAMjC,YAAY,KAAK,WALE,mBAAmB,IAAI,SAAS,IAChD,QACA,uBAAuB,IAAI,SAAS,IACnC,iBAAiB,KAAK,IACtB,eAAe,KAAK,CACc;CACvC;CACA,OAAO;AACR;AAEA,SAAS,iBAAiB,OAAyB;CAClD,IAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG,OAAO;CAEhF,MAAM,MAA+B,CAAC;CACtC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAgC,GACzE,YAAY,KAAK,KAAK,eAAe,KAAK,CAAC;CAE5C,OAAO;AACR;;;;;;;;;;;;;ACnCA,IAAa,cAAb,MAAa,YAAY;CACxB;CACA;CAEA,YAAY,WAAmB;EAC9B,KAAK,YAAY;EACjB,KAAK,YAAY,CAAC;CACnB;;CAGA,OAAc,MAAgB,OAA8B;EAC3D,MAAM,SAAU,KAAK,UAAU,YAAY,iBAAiB,IAAI,OAAO,CAAC;EAExE,KAAK,MAAM,QAAQ,OAClB,IAAI,SAAS,MAAM,OAAO,KAAK,EAAE,MAAM,YAAY,eAAe,IAAI,EAAE,CAAC;EAE1E,OAAO;CACR;;CAGA,oBAA2B,UAAiC;EAC3D,KAAK,YAAY,CAAC;EAElB,KAAK,MAAM,CAAC,SAAS,UAAU,OAAO,QAAQ,QAAQ,GAAG;GACxD,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG;GAC3B,MAAM,OAAO,YAAY,gBAAgB,OAAO;GAChD,KAAK,OAAO,MAAM,KAAK;EACxB;EAEA,OAAO;CACR;;CAGA,WAAkB,QAA+C;EAChE,MAAM,QAAQ,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;EACtD,OAAO,KAAK,OAAO,CAAC,CAAC,GAAG,KAAK;CAC9B;;CAGA,kBAAmC;EAClC,OAAO;GACN,WAAW,KAAK;GAEhB,WAAW,KAAK;EACjB;CACD;;;;;CAMA,OAAc,gBAAgB,QAAkC;EAC/D,OAAO,YAAY,WAClB,OAAO,KAAK,WAAW;GACtB,WAAW,MAAM,YAAY;GAC7B,WAAW,MAAM;GACjB,OAAO,MAAM;GACb,GAAI,aAAa,SAAS;IAAE,SAAS,MAAM;IAAS,SAAS,MAAM;GAAQ;EAC5E,EAAE,CACH;CACD;;;;;;;;CASA,OAAc,WAAW,SAAuC;EAC/D,MAAM,QAAoB,CAAC;EAE3B,KAAK,MAAM,SAAS,SAAS;GAC5B,MAAM,OAAO,IAAI,YAAY,MAAM,SAAS;GAC5C,MAAM,SAAS,YAAY,YAAY,KAAK;GAE5C,KAAK,MAAM,CAAC,MAAM,UAAU,YAAY,WAAW,MAAM,KAAK,GAAG;IAChE,MAAM,OAAwB,CAAC;IAC/B,KAAK,MAAM,QAAQ,OAAO;KACzB,IAAI,SAAS,QAAQ,SAAS,KAAA,GAAW;KACzC,MAAM,UAAU,OAAO,IAAI;KAC3B,IAAI,YAAY,MACf,UAAU,CAAC,CAAC,KACX,GAAG,MAAM,UAAU,wCAAwC,MAAM,WAClE;UAEA,KAAK,KAAK,OAAO;IAEnB;IACA,IAAI,KAAK,SAAS,GAAG,KAAK,OAAO,MAAM,IAAI;GAC5C;GAEA,IAAI,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,SAAS,GAAG,MAAM,KAAK,KAAK,gBAAgB,CAAC;EAC9E;EAEA,OAAO;CACR;CAEA,OAAe,WAAW,OAAyC;EAClE,IAAI,CAAC,YAAY,cAAc,KAAK,GAAG,OAAO,CAAC;EAC/C,IAAI,kBAAkB,KAAK,GAC1B,OAAO,OAAO,QAAQ,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,WAAW,CACnD,YAAY,gBAAgB,IAAI,GAChC,KACD,CAAC;EAEF,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC;CACtD;;CAGA,OAAe,YAAY,OAAgE;EAC1F,MAAM,EAAE,WAAW,SAAS,YAAY;EACxC,MAAM,YAAY,MAAM,UAAU,YAAY;EAE9C,IAAI,cAAc,YAAY,cAAc,WAC3C,QAAQ,SAAS;GAChB,MAAM,IAAI,mBAAmB,IAAI;GACjC,IAAI,MAAM,MAAM,OAAO;GACvB,MAAM,QAAQ,cAAc,YAAY,KAAK,MAAM,CAAC,IAAI;GACxD,OAAO,YAAY,WAAW,OAAO,SAAS,SAAS,SAAS;EACjE;EAED,IAAI,cAAc,WAAW,OAAO;EACpC,QAAQ,SAAS;CAClB;;;;;;CAOA,OAAc,iBACb,OACA,WACA,UACa;EACb,MAAM,WAAW,YAAY,eAAe,WAAW,QAAQ,CAAC,CAAC,gBAAgB;EACjF,MAAM,SAAS,MAAM,MAAM;EAC3B,MAAM,MAAM,OAAO,WAAW,MAAM,YAAY,YAAY,CAAC,MAAM,SAAS;EAC5E,IAAI,QAAQ,IAAI,OAAO,KAAK,QAAQ;OAC/B,OAAO,OAAO;EACnB,OAAO;CACR;CAEA,OAAe,eAAe,WAAmB,OAAmC;EACnF,MAAM,OAAO,IAAI,YAAY,SAAS;EACtC,IAAI,kBAAkB,KAAK,GAC1B,KAAK,oBAAoB,KAAK;OAE9B,KAAK,WAAW,KAAK;EAEtB,OAAO;CACR;;;;;;CAOA,OAAc,QACb,OACA,WAC4C;EAC5C,MAAM,OAAO,MAAM,MAAM,MAAM,YAAY,YAAY,CAAC,MAAM,SAAS;EACvE,IAAI,CAAC,MAAM,OAAO;EAElB,MAAM,2BAAW,IAAI,IAAmC;EACxD,MAAM,QAAQ,UAAU,MAAM,WAAW;EACzC,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU,OAAO;EAEhD,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,KAAK,GAAG;GAClD,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG;GAC3B,SAAS,IACR,MACA,MAAM,KAAK,SACV,MAAM,SAAS,KAAA,IAAY,YAAY,iBAAiB,KAAK,IAAI,IAAI,IACtE,CACD;EACD;EACA,OAAO;CACR;;;;;;;;CASA,OAAc,aAAa,OAAmB,WAAyC;EACtF,MAAM,OAAO,YAAY,QAAQ,OAAO,SAAS;EACjD,IAAI,CAAC,QAAQ,KAAK,SAAS,GAAG,OAAO;EACrC,IAAI,KAAK,OAAO,GACf,MAAM,IAAI,aACT,IAAI,UAAU,QAAQ,KAAK,KAAK,8CAChC,WAAW,eACX,EAAE,SAAS;GAAE;GAAW,aAAa,KAAK;EAAK,EAAE,CAClD;EAGD,MAAM,CAAC,SAAS,KAAK,OAAO;EAC5B,IAAI,MAAM,WAAW,GAAG,OAAO;EAC/B,OAAO,MAAM,WAAW,IAAI,MAAM,KAAK;CACxC;CAEA,OAAe,YAAY,MAAyB;EACnD,OAAO,UAAU,MAAM,WAAW;CACnC;;;;;;;CAQA,OAAc,gBAAgB,SAA2B;EACxD,MAAM,QAAQ,QAAQ,MAAM,YAAY;EACxC,IAAI,CAAC,OACJ,MAAM,IAAI,aACT,mCAAmC,QAAQ,uFAE3C,WAAW,eACX,EAAE,SAAS,EAAE,QAAQ,EAAE,CACxB;EAED,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC;EACvB,OAAO,MAAM,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM;CACtC;;CAGA,OAAc,iBAAiB,MAA8B;EAC5D,OAAO,IAAI,KAAK,KAAK,GAAG,EAAE;CAC3B;;CAGA,OAAe,eACd,OAC0B;EAC1B,OAAO,OAAO,UAAU,WAAW,KAAK,UAAU,KAAK,IAAI;CAC5D;CAEA,OAAe,iBAAiB,MAA8B;EAC7D,IAAI,OAAO,SAAS,UAAU,OAAO;EAErC,IAAI,KAAK,WAAW,GAAG,KAAK,KAAK,WAAW,GAAG,GAC9C,IAAI;GACH,OAAO,KAAK,MAAM,IAAI;EACvB,QAAQ;GACP,OAAO;EACR;EAQD,MAAM,MAAM,OAAO,IAAI;EACvB,IAAI,OAAO,SAAS,GAAG,KAAK,OAAO,GAAG,MAAM,MAC3C,OAAO;EAER,IAAI,SAAS,QAAQ,OAAO;EAC5B,IAAI,SAAS,SAAS,OAAO;EAC7B,OAAO;CACR;CAEA,OAAe,cAAc,OAAyB;EACrD,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM,OAAO;EAClD,IAAI,OAAO,UAAU,UAAU,OAAO;EACtC,IAAI,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAAG,OAAO;EACvD,IAAI,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,KAAK,OAAO,KAAK,KAAK,CAAC,CAAC,WAAW,GACvF,OAAO;EACR,OAAO;CACR;CAEA,OAAe,WACd,OACA,KACA,KACA,WACS;EACT,IAAI,SAAS;EAEb,IAAI,QAAQ,QAAQ,QAAQ,KAAA,KAAa,SAAS,KAAK;GACtD,UAAU,CAAC,CAAC,KAAK,GAAG,UAAU,IAAI,MAAM,aAAa,IAAI,WAAW;GACpE,SAAS;EACV;EACA,IAAI,QAAQ,QAAQ,QAAQ,KAAA,KAAa,SAAS,KAAK;GACtD,UAAU,CAAC,CAAC,KAAK,GAAG,UAAU,IAAI,MAAM,aAAa,IAAI,WAAW;GACpE,SAAS;EACV;EAEA,OAAO;CACR;AACD"}
|