@selvajs/solve 0.2.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Selva VektorNode AG
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # `@selvajs/solve`
2
+
3
+ One owner for the solve flow — **from an input change to a solve result, on both sides of the wire,
4
+ with no transport and no UI.**
5
+
6
+ Before this package the solve flow was scattered across four packages and owned by none of them, so
7
+ both apps hand-wrote a solve coordinator — one of them paid for it with a poisoned-cache bug.
8
+
9
+ ## Layout
10
+
11
+ ```
12
+ shared/ the vocabulary both halves speak — SolveResult, SolveFn, SolveInput, input keying
13
+ client/ form state machine, auto/manual decision, throttle, result memo, driver seam
14
+ server/ solve pipeline (tree build → solve → serialize → envelope), caches, single-flight
15
+ ```
16
+
17
+ `client/` and `server/` both depend on `shared/`, and **never on each other** — enforced three ways:
18
+ no root barrel, `no-restricted-imports` in this package's eslint config, and a bundle test that
19
+ checks the shipped `dist/client.js` rather than the source
20
+ ([`src/__tests__/client-bundle-boundary.test.ts`](./src/__tests__/client-bundle-boundary.test.ts)).
21
+
22
+ See [`src/client/README.md`](./src/client/README.md) for the session, the driver seam, and how to
23
+ write a transport; [`src/server/README.md`](./src/server/README.md) for the pipeline and the cache
24
+ tiers.
25
+
26
+ ## What it must never know
27
+
28
+ - **No UI framework.** No Svelte, no runes, no DOM. `client/` is a state machine, not a component.
29
+ - **No renderer.** No `three`. `SolveResult<TMesh>` keeps meshes opaque — the app that parses a
30
+ response into `THREE.Object3D[]` is the only place that knows the concrete type. This is what lets
31
+ a headless CLI solve without dragging in a parse layer. Where the result memo genuinely needs to
32
+ clone and dispose meshes, the rules are injected as a `MeshPolicy` (three.js implementation:
33
+ `meshPolicy` in `@selvajs/visualization/parse`).
34
+ - **No authorization, orgs, projects, or share links.** App policy.
35
+ - **No HTTP.** `client/` stops at a `SolveFn`; `server/` stops at a `SolveOutcome`. Mapping either to
36
+ a status code is the app's job.
37
+
38
+ ## No root barrel — on purpose
39
+
40
+ `@selvajs/solve` exports `./shared`, `./client` and `./server`. There is deliberately **no `.`
41
+ export**: a root barrel re-exporting both halves would let a browser bundle reach server code — and
42
+ server credentials — through one innocent-looking import, defeating every other guard. Adding one is
43
+ a boundary change, not a convenience.
44
+
45
+ ```ts
46
+ import type { SolveResult } from '@selvajs/solve/shared';
47
+ ```
48
+
49
+ ```ts
50
+ import { createSolveSession, createRequestResponseDriver } from '@selvajs/solve/client';
51
+ import { runSolvePipeline } from '@selvajs/solve/server';
52
+ ```
53
+
54
+ `@selvajs/server/compute` keeps only the HTTP request policy it owns (limits, rate limiting, the
55
+ SSRF guard, remote-definition fetch) and does not re-export any of this — the two packages are
56
+ independent.
57
+
58
+ In a Svelte app use `useSolveSession` from `@selvajs/ui` rather than `createSolveSession` directly —
59
+ the raw factory returns correct values that never re-render.
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _schemas = require('@selvajs/schemas');var V="external";function g(n,r){return`${V}:${n}:${r}`}function P(n){let{scopeKey:r,inputId:s,value:e}=n;!r||!s||typeof sessionStorage>"u"||sessionStorage.setItem(g(r,s),JSON.stringify(e))}function f(n){let{scopeKey:r,inputId:s}=n;if(!r||!s||typeof sessionStorage>"u")return;let e=sessionStorage.getItem(g(r,s));if(e!==null)try{return JSON.parse(e)}catch (e2){return}}function C(n){let{scopeKey:r,inputId:s}=n;!r||!s||typeof sessionStorage>"u"||sessionStorage.removeItem(g(r,s))}function h(n){return _schemas.getInputItems.call(void 0, n).filter(r=>_optionalChain([r, 'access', _ => _.source, 'optionalAccess', _2 => _2.kind])==="client").map(r=>({paramId:r.paramId,displayName:_nullishCoalesce(r.displayName, () => (r.paramId))}))}function v(n,r,s){let e=new Set(h(n).map(t=>t.paramId)),a={};for(let t of n.inputs){if(e.has(t.id)){let i=s({scopeKey:r,inputId:t.id});i!==void 0&&(a[t.id]=i);continue}a[t.id]=_nullishCoalesce(t.default, () => (_schemas.getDefaultValue.call(void 0, t.paramType)))}for(let t of n.outputs)a[t.id]=null;return a}function m(n){let r=n===!1;return{hasPendingChanges:r,hasNeverSolved:r}}function S(n,r,s,e){return n.values[r]=s,e===!1?(n.pendingValues[r]=s,n.hasPendingChanges=!0,{state:n,shouldSolve:!1}):{state:n,shouldSolve:!0}}function y(n,r){if(!_optionalChain([n, 'optionalAccess', _3 => _3.inputs]))return r;let s={};for(let e of n.inputs)e.id in r&&(s[e.id]=r[e.id]);return s}function x(n,r){return n.error="",n.computeErrors=_nullishCoalesce(r.errors, () => ([])),n.computeWarnings=_nullishCoalesce(r.warnings, () => ([])),n.meshes=_nullishCoalesce(r.meshes, () => ([])),Object.assign(n.values,r.outputs),n.pendingValues={},n.hasPendingChanges=!1,n.hasNeverSolved=!1,n}function A(n){let r=n.schema,s=m(_optionalChain([r, 'optionalAccess', _4 => _4.instanceSolve])),e={values:v(r,n.scopeKey,f),error:"",computeErrors:[],computeWarnings:[],meshes:[],pendingValues:{},hasPendingChanges:s.hasPendingChanges,hasNeverSolved:s.hasNeverSolved},a=new Set,t=()=>a.forEach(o=>o());function i(){n.driver.solve({...y(r,e.values)}),t()}return{get values(){return e.values},get error(){return e.error},get computeErrors(){return e.computeErrors},get computeWarnings(){return e.computeWarnings},get meshes(){return e.meshes},get hasPendingChanges(){return e.hasPendingChanges},get hasNeverSolved(){return e.hasNeverSolved},get isSolving(){return n.driver.isSolving},setValue(o,l,u=!1){let{shouldSolve:c}=S(e,o,l,_optionalChain([r, 'optionalAccess', _5 => _5.instanceSolve]));c||u?(u&&!c&&(e.pendingValues={},e.hasPendingChanges=!1),i()):t()},solve(){i()},loadValues(o){Object.assign(e.values,o),_optionalChain([r, 'optionalAccess', _6 => _6.instanceSolve])!==!1?i():(e.hasPendingChanges=!0,t())},rebuild(o,l){r=o,_optionalChain([n, 'access', _7 => _7.driver, 'access', _8 => _8.clearCache, 'optionalCall', _9 => _9()]),e.meshes=[],e.error="",e.computeErrors=[],e.computeWarnings=[],e.pendingValues={},e.values=v(o,l,f);let u=m(_optionalChain([o, 'optionalAccess', _10 => _10.instanceSolve]));e.hasPendingChanges=u.hasPendingChanges,e.hasNeverSolved=u.hasNeverSolved,o&&Object.keys(e.values).length>0&&o.instanceSolve!==!1?i():t()},report(o){x(e,o),t()},reportError(o){e.error=o,t()},subscribe(o){return a.add(o),()=>a.delete(o)},notify:t}}function R(n,r={}){let{timeout:s=6e4,onChange:e}=r,a=!1,t=null,i=null;function o(d){a!==d&&(a=d,_optionalChain([e, 'optionalCall', _11 => _11()]))}function l(){_optionalChain([i, 'optionalAccess', _12 => _12.abort, 'call', _13 => _13()]),i=null}async function u(d){l(),i=new AbortController;let{signal:M}=i,k=setTimeout(()=>{console.warn(`[Solve/throttle] run exceeded ${s}ms \u2014 aborting`),_optionalChain([i, 'optionalAccess', _14 => _14.abort, 'call', _15 => _15()])},s);o(!0);try{await n(d,M)}catch(p){(!(p instanceof Error)||p.name!=="AbortError"&&p.name!=="TimeoutError")&&console.error("[Solve/throttle] unhandled error in run:",p)}finally{if(clearTimeout(k),o(!1),i=null,t!==null){let p=t;t=null,u(p)}}}function c(d){a?(t!==null&&console.debug("[Solve/throttle] superseded pending run (latest-wins)"),t=d):u(d)}function I(){t=null,l()}return{trigger:c,get isRunning(){return a},get hasPending(){return t!==null},cancel:I}}function b(n){return T(n)}function T(n){if(n===null||typeof n!="object")return _nullishCoalesce(JSON.stringify(n), () => ("null"));if(Array.isArray(n))return`[${n.map(T).join(",")}]`;let r=n;return`{${Object.keys(r).sort().map(e=>`${JSON.stringify(e)}:${T(r[e])}`).join(",")}}`}function E(n={}){let{max:r=16,meshPolicy:s}=n,e=new Map;function a(o){_optionalChain([o, 'access', _16 => _16.meshes, 'optionalAccess', _17 => _17.length])&&_optionalChain([s, 'optionalAccess', _18 => _18.release, 'call', _19 => _19(o.meshes)])}function t(o){return!s||!_optionalChain([o, 'access', _20 => _20.meshes, 'optionalAccess', _21 => _21.length])?o:{...o,meshes:s.clone(o.meshes)}}function i(o){let l=e.get(o);l!==void 0&&(e.delete(o),a(l))}return{get(o){let l=b(o),u=e.get(l);if(u!==void 0)return e.delete(l),e.set(l,u),console.info(`[Solve/memo] HIT \u2014 served from client memo (${e.size}/${r})`),t(u)},set(o,l){let u=b(o);for(i(u),e.set(u,t(l));e.size>r;){let c=e.keys().next().value;if(c===void 0)break;i(c),console.info(`[Solve/memo] evicted LRU entry (cap ${r})`)}},clear(){e.size>0&&console.info(`[Solve/memo] cleared ${e.size} entries (definition changed)`),e.forEach(a),e.clear()}}}function O(n,r,s={}){let{meshPolicy:e,...a}=s,t=E({meshPolicy:e}),i=R(async(o,l)=>{let u=t.get(o);if(u!==void 0){if(l.aborted)return;r().report(u);return}try{let c=await n(o,l);if(l.aborted){console.debug("[Solve/driver] solve completed after abort \u2014 result discarded");return}t.set(o,c),r().report(c)}catch(c){if(l.aborted){console.debug("[Solve/driver] solve aborted (superseded, cancelled, or timed out)");return}console.warn("[Solve/driver] solve failed:",c),r().reportError(c instanceof Error?c.message:String(c))}},a);return{solve(o){i.trigger(o)},cancel(){i.cancel()},get isSolving(){return i.isRunning},clearCache(){t.clear()}}}exports.applySolveResult = x; exports.applyValueChange = S; exports.buildInitialValues = v; exports.clearExternalValue = C; exports.createAsyncThrottle = R; exports.createRequestResponseDriver = O; exports.createSolveMemo = E; exports.createSolveSession = A; exports.getExternalInputs = h; exports.makeInitialFlags = m; exports.pickInputValues = y; exports.readExternalValue = f; exports.stableInputKey = b; exports.writeExternalValue = P;
2
+ //# sourceMappingURL=client.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["c:\\Users\\felix\\coding\\selva\\packages\\solve\\dist\\client.cjs"],"names":[],"mappings":"AAAA,4tBAAgD,IAAK,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAA","file":"C:\\Users\\felix\\coding\\selva\\packages\\solve\\dist\\client.cjs","sourcesContent":[null]}
@@ -0,0 +1,182 @@
1
+ import { UISchema } from '@selvajs/schemas';
2
+ import { a as SolveResult, S as SolveFn } from './solve-fn-DGzPCsDu.cjs';
3
+
4
+ /**
5
+ * The transport behind a Solve Session. Never returns outputs directly — results come
6
+ * back via the session's `report()`, which lets push transports (a WebSocket streaming
7
+ * mesh frames on its own schedule) satisfy the same interface as request/response HTTP.
8
+ */
9
+ interface SolveDriver {
10
+ solve(values: Record<string, unknown>): void;
11
+ cancel(): void;
12
+ readonly isSolving: boolean;
13
+ /**
14
+ * Drops any cached solve results the driver holds. Optional — only drivers with a
15
+ * client-side memo (the request/response driver) implement it. Called on rebuild so a
16
+ * definition swap can't serve a stale result from a prior definition's input space.
17
+ */
18
+ clearCache?(): void;
19
+ }
20
+ /**
21
+ * The slice of a SolveSession a driver feeds completed/failed solves back into.
22
+ *
23
+ * `TMesh` defaults to `unknown` because nothing in this package inspects meshes; a host that
24
+ * knows its concrete mesh type (a three.js viewer) narrows it at its own seam.
25
+ */
26
+ interface SolveReporter<TMesh = unknown> {
27
+ report(result: SolveResult<TMesh>): void;
28
+ reportError(message: string): void;
29
+ }
30
+
31
+ interface SolveSession {
32
+ readonly values: Record<string, unknown>;
33
+ readonly error: string;
34
+ readonly computeErrors: string[];
35
+ readonly computeWarnings: string[];
36
+ readonly meshes: unknown[];
37
+ readonly hasPendingChanges: boolean;
38
+ readonly hasNeverSolved: boolean;
39
+ readonly isSolving: boolean;
40
+ /** `forceSolve` overrides manual mode for system-initiated reconciliation (e.g. a dynamic value list pruning a vanished selection), so the output can't lag behind the new value. */
41
+ setValue(id: string, value: unknown, forceSolve?: boolean): void;
42
+ solve(): void;
43
+ /** Merges incoming values, then solves (auto) or marks dirty (manual). */
44
+ loadValues(incoming: Record<string, unknown>): void;
45
+ rebuild(schema: UISchema, scopeKey: string): void;
46
+ report(result: SolveResult): void;
47
+ reportError(message: string): void;
48
+ /** Fires after any mutation of the getters above, including driver-reported `isSolving` transitions. */
49
+ subscribe(listener: () => void): () => void;
50
+ /** For driver-owned state the session only forwards: `isSolving` lives on the driver, so wire this to its `onChange`. */
51
+ notify(): void;
52
+ }
53
+ interface SolveSessionArgs {
54
+ schema: UISchema;
55
+ scopeKey: string;
56
+ driver: SolveDriver;
57
+ }
58
+ declare function createSolveSession(args: SolveSessionArgs): SolveSession;
59
+
60
+ /**
61
+ * Deterministic string key for a set of input values. Object keys are sorted at every
62
+ * level so two logically-equal inputs (built in different key order) collide, matching the
63
+ * server's stable-input keying intent. Values are plain JSON — the projected solve inputs
64
+ * never contain functions or cycles.
65
+ */
66
+ declare function stableInputKey(values: Record<string, unknown>): string;
67
+ /**
68
+ * Omit for mesh-free results, or for meshes no consumer takes ownership of: the memo then
69
+ * stores and serves them by reference, which is correct precisely because nothing frees
70
+ * them behind its back.
71
+ *
72
+ * @property clone - Called on the way in AND on every hit, so neither the stored entry nor
73
+ * a previously served one can be disposed by the other's owner.
74
+ * @property release - Must NOT free anything shared with live content — see
75
+ * `releaseSceneObjects` in `@selvajs/visualization/parse`, which skips materials for
76
+ * exactly that reason.
77
+ */
78
+ interface MeshPolicy<TMesh> {
79
+ clone(meshes: TMesh[]): TMesh[];
80
+ release(meshes: TMesh[]): void;
81
+ }
82
+ interface SolveMemoOptions<TMesh> {
83
+ /**
84
+ * Entry cap (entries, not bytes). Solve results can be large, so the default is
85
+ * deliberately small — this targets the tight slider-scrub loop, not a durable cache.
86
+ */
87
+ max?: number;
88
+ meshPolicy?: MeshPolicy<TMesh>;
89
+ }
90
+ interface SolveMemo<TMesh = unknown> {
91
+ get(values: Record<string, unknown>): SolveResult<TMesh> | undefined;
92
+ /** Evicts the LRU tail if `set` pushes the map past its cap. */
93
+ set(values: Record<string, unknown>, result: SolveResult<TMesh>): void;
94
+ clear(): void;
95
+ }
96
+ declare function createSolveMemo<TMesh = unknown>(options?: SolveMemoOptions<TMesh>): SolveMemo<TMesh>;
97
+
98
+ interface RequestResponseDriverOptions<TMesh> {
99
+ timeout?: number;
100
+ onChange?: () => void;
101
+ /** Mesh ownership policy for the result memo — see `MeshPolicy` (solve-memo.ts). */
102
+ meshPolicy?: MeshPolicy<TMesh>;
103
+ }
104
+ /**
105
+ * `getReporter` is lazy (`() => session`) because the session and driver reference each
106
+ * other — the host builds the driver first, then the session, then can hand this back.
107
+ */
108
+ declare function createRequestResponseDriver<TMesh = unknown>(onSolve: SolveFn<TMesh>, getReporter: () => SolveReporter<TMesh>, options?: RequestResponseDriverOptions<TMesh>): SolveDriver;
109
+
110
+ interface ExternalValueRef {
111
+ scopeKey: string;
112
+ inputId: string;
113
+ }
114
+ declare function writeExternalValue(args: ExternalValueRef & {
115
+ value: unknown;
116
+ }): void;
117
+ declare function readExternalValue(ref: ExternalValueRef): unknown | undefined;
118
+ declare function clearExternalValue(ref: ExternalValueRef): void;
119
+ interface ExternalInput {
120
+ paramId: string;
121
+ displayName: string;
122
+ }
123
+ declare function getExternalInputs(schema: UISchema): ExternalInput[];
124
+
125
+ interface SolveSessionState {
126
+ values: Record<string, unknown>;
127
+ error: string;
128
+ computeErrors: string[];
129
+ computeWarnings: string[];
130
+ meshes: unknown[];
131
+ /** Values changed since the last solve, in manual (instanceSolve === false) mode. */
132
+ pendingValues: Record<string, unknown>;
133
+ hasPendingChanges: boolean;
134
+ hasNeverSolved: boolean;
135
+ }
136
+ type ExternalReader = (ref: ExternalValueRef) => unknown | undefined;
137
+ /**
138
+ * Client-sourced inputs are hydrated from `read` and left absent when no stored value
139
+ * exists, so the missing-inputs panel can detect them. Other inputs fall back to their
140
+ * declared default, then the paramType default.
141
+ */
142
+ declare function buildInitialValues(schema: UISchema, scopeKey: string, read: ExternalReader): Record<string, unknown>;
143
+ /** Manual-solve schemas (instanceSolve === false) start dirty; auto-solve schemas start clean. */
144
+ declare function makeInitialFlags(instanceSolve: boolean | undefined): {
145
+ hasPendingChanges: boolean;
146
+ hasNeverSolved: boolean;
147
+ };
148
+ /** Manual mode defers (records the pending value, raises the dirty flag) instead of solving. */
149
+ declare function applyValueChange(state: SolveSessionState, id: string, value: unknown, instanceSolve: boolean | undefined): {
150
+ state: SolveSessionState;
151
+ shouldSolve: boolean;
152
+ };
153
+ /**
154
+ * Solve outputs live in the same `values` map as inputs (so widgets like dynamic value
155
+ * lists can read them) but must not be echoed back to the driver: a measured 6.4 MB
156
+ * options-list output showed this can re-upload a multi-MB payload no backend reads.
157
+ */
158
+ declare function pickInputValues(schema: UISchema | undefined, values: Record<string, unknown>): Record<string, unknown>;
159
+ declare function applySolveResult(state: SolveSessionState, result: SolveResult): SolveSessionState;
160
+
161
+ /**
162
+ * Single-in-flight, latest-wins throttle for any async operation: only one run at a
163
+ * time, a newly triggered value overwrites the single pending slot (no queue), and a
164
+ * stale run can be aborted via `AbortController` or timeout.
165
+ *
166
+ * Framework-free: `isRunning` is a plain getter, not a store. A reactive host must
167
+ * subscribe via `onChange` (see `@selvajs/ui`'s rune adapter) — polling the getter
168
+ * directly reads correct values but won't trigger a re-render.
169
+ */
170
+ interface AsyncThrottleOptions {
171
+ timeout?: number;
172
+ onChange?: () => void;
173
+ }
174
+ interface AsyncThrottle<T> {
175
+ trigger: (values: T) => void;
176
+ readonly isRunning: boolean;
177
+ readonly hasPending: boolean;
178
+ cancel: () => void;
179
+ }
180
+ declare function createAsyncThrottle<T>(run: (values: T, signal: AbortSignal) => Promise<void>, options?: AsyncThrottleOptions): AsyncThrottle<T>;
181
+
182
+ export { type AsyncThrottle, type ExternalInput, type ExternalReader, type ExternalValueRef, type MeshPolicy, type RequestResponseDriverOptions, type SolveDriver, SolveFn, type SolveMemo, type SolveMemoOptions, type SolveReporter, SolveResult, type SolveSession, type SolveSessionArgs, type SolveSessionState, applySolveResult, applyValueChange, buildInitialValues, clearExternalValue, createAsyncThrottle, createRequestResponseDriver, createSolveMemo, createSolveSession, getExternalInputs, makeInitialFlags, pickInputValues, readExternalValue, stableInputKey, writeExternalValue };
@@ -0,0 +1,182 @@
1
+ import { UISchema } from '@selvajs/schemas';
2
+ import { a as SolveResult, S as SolveFn } from './solve-fn-DGzPCsDu.js';
3
+
4
+ /**
5
+ * The transport behind a Solve Session. Never returns outputs directly — results come
6
+ * back via the session's `report()`, which lets push transports (a WebSocket streaming
7
+ * mesh frames on its own schedule) satisfy the same interface as request/response HTTP.
8
+ */
9
+ interface SolveDriver {
10
+ solve(values: Record<string, unknown>): void;
11
+ cancel(): void;
12
+ readonly isSolving: boolean;
13
+ /**
14
+ * Drops any cached solve results the driver holds. Optional — only drivers with a
15
+ * client-side memo (the request/response driver) implement it. Called on rebuild so a
16
+ * definition swap can't serve a stale result from a prior definition's input space.
17
+ */
18
+ clearCache?(): void;
19
+ }
20
+ /**
21
+ * The slice of a SolveSession a driver feeds completed/failed solves back into.
22
+ *
23
+ * `TMesh` defaults to `unknown` because nothing in this package inspects meshes; a host that
24
+ * knows its concrete mesh type (a three.js viewer) narrows it at its own seam.
25
+ */
26
+ interface SolveReporter<TMesh = unknown> {
27
+ report(result: SolveResult<TMesh>): void;
28
+ reportError(message: string): void;
29
+ }
30
+
31
+ interface SolveSession {
32
+ readonly values: Record<string, unknown>;
33
+ readonly error: string;
34
+ readonly computeErrors: string[];
35
+ readonly computeWarnings: string[];
36
+ readonly meshes: unknown[];
37
+ readonly hasPendingChanges: boolean;
38
+ readonly hasNeverSolved: boolean;
39
+ readonly isSolving: boolean;
40
+ /** `forceSolve` overrides manual mode for system-initiated reconciliation (e.g. a dynamic value list pruning a vanished selection), so the output can't lag behind the new value. */
41
+ setValue(id: string, value: unknown, forceSolve?: boolean): void;
42
+ solve(): void;
43
+ /** Merges incoming values, then solves (auto) or marks dirty (manual). */
44
+ loadValues(incoming: Record<string, unknown>): void;
45
+ rebuild(schema: UISchema, scopeKey: string): void;
46
+ report(result: SolveResult): void;
47
+ reportError(message: string): void;
48
+ /** Fires after any mutation of the getters above, including driver-reported `isSolving` transitions. */
49
+ subscribe(listener: () => void): () => void;
50
+ /** For driver-owned state the session only forwards: `isSolving` lives on the driver, so wire this to its `onChange`. */
51
+ notify(): void;
52
+ }
53
+ interface SolveSessionArgs {
54
+ schema: UISchema;
55
+ scopeKey: string;
56
+ driver: SolveDriver;
57
+ }
58
+ declare function createSolveSession(args: SolveSessionArgs): SolveSession;
59
+
60
+ /**
61
+ * Deterministic string key for a set of input values. Object keys are sorted at every
62
+ * level so two logically-equal inputs (built in different key order) collide, matching the
63
+ * server's stable-input keying intent. Values are plain JSON — the projected solve inputs
64
+ * never contain functions or cycles.
65
+ */
66
+ declare function stableInputKey(values: Record<string, unknown>): string;
67
+ /**
68
+ * Omit for mesh-free results, or for meshes no consumer takes ownership of: the memo then
69
+ * stores and serves them by reference, which is correct precisely because nothing frees
70
+ * them behind its back.
71
+ *
72
+ * @property clone - Called on the way in AND on every hit, so neither the stored entry nor
73
+ * a previously served one can be disposed by the other's owner.
74
+ * @property release - Must NOT free anything shared with live content — see
75
+ * `releaseSceneObjects` in `@selvajs/visualization/parse`, which skips materials for
76
+ * exactly that reason.
77
+ */
78
+ interface MeshPolicy<TMesh> {
79
+ clone(meshes: TMesh[]): TMesh[];
80
+ release(meshes: TMesh[]): void;
81
+ }
82
+ interface SolveMemoOptions<TMesh> {
83
+ /**
84
+ * Entry cap (entries, not bytes). Solve results can be large, so the default is
85
+ * deliberately small — this targets the tight slider-scrub loop, not a durable cache.
86
+ */
87
+ max?: number;
88
+ meshPolicy?: MeshPolicy<TMesh>;
89
+ }
90
+ interface SolveMemo<TMesh = unknown> {
91
+ get(values: Record<string, unknown>): SolveResult<TMesh> | undefined;
92
+ /** Evicts the LRU tail if `set` pushes the map past its cap. */
93
+ set(values: Record<string, unknown>, result: SolveResult<TMesh>): void;
94
+ clear(): void;
95
+ }
96
+ declare function createSolveMemo<TMesh = unknown>(options?: SolveMemoOptions<TMesh>): SolveMemo<TMesh>;
97
+
98
+ interface RequestResponseDriverOptions<TMesh> {
99
+ timeout?: number;
100
+ onChange?: () => void;
101
+ /** Mesh ownership policy for the result memo — see `MeshPolicy` (solve-memo.ts). */
102
+ meshPolicy?: MeshPolicy<TMesh>;
103
+ }
104
+ /**
105
+ * `getReporter` is lazy (`() => session`) because the session and driver reference each
106
+ * other — the host builds the driver first, then the session, then can hand this back.
107
+ */
108
+ declare function createRequestResponseDriver<TMesh = unknown>(onSolve: SolveFn<TMesh>, getReporter: () => SolveReporter<TMesh>, options?: RequestResponseDriverOptions<TMesh>): SolveDriver;
109
+
110
+ interface ExternalValueRef {
111
+ scopeKey: string;
112
+ inputId: string;
113
+ }
114
+ declare function writeExternalValue(args: ExternalValueRef & {
115
+ value: unknown;
116
+ }): void;
117
+ declare function readExternalValue(ref: ExternalValueRef): unknown | undefined;
118
+ declare function clearExternalValue(ref: ExternalValueRef): void;
119
+ interface ExternalInput {
120
+ paramId: string;
121
+ displayName: string;
122
+ }
123
+ declare function getExternalInputs(schema: UISchema): ExternalInput[];
124
+
125
+ interface SolveSessionState {
126
+ values: Record<string, unknown>;
127
+ error: string;
128
+ computeErrors: string[];
129
+ computeWarnings: string[];
130
+ meshes: unknown[];
131
+ /** Values changed since the last solve, in manual (instanceSolve === false) mode. */
132
+ pendingValues: Record<string, unknown>;
133
+ hasPendingChanges: boolean;
134
+ hasNeverSolved: boolean;
135
+ }
136
+ type ExternalReader = (ref: ExternalValueRef) => unknown | undefined;
137
+ /**
138
+ * Client-sourced inputs are hydrated from `read` and left absent when no stored value
139
+ * exists, so the missing-inputs panel can detect them. Other inputs fall back to their
140
+ * declared default, then the paramType default.
141
+ */
142
+ declare function buildInitialValues(schema: UISchema, scopeKey: string, read: ExternalReader): Record<string, unknown>;
143
+ /** Manual-solve schemas (instanceSolve === false) start dirty; auto-solve schemas start clean. */
144
+ declare function makeInitialFlags(instanceSolve: boolean | undefined): {
145
+ hasPendingChanges: boolean;
146
+ hasNeverSolved: boolean;
147
+ };
148
+ /** Manual mode defers (records the pending value, raises the dirty flag) instead of solving. */
149
+ declare function applyValueChange(state: SolveSessionState, id: string, value: unknown, instanceSolve: boolean | undefined): {
150
+ state: SolveSessionState;
151
+ shouldSolve: boolean;
152
+ };
153
+ /**
154
+ * Solve outputs live in the same `values` map as inputs (so widgets like dynamic value
155
+ * lists can read them) but must not be echoed back to the driver: a measured 6.4 MB
156
+ * options-list output showed this can re-upload a multi-MB payload no backend reads.
157
+ */
158
+ declare function pickInputValues(schema: UISchema | undefined, values: Record<string, unknown>): Record<string, unknown>;
159
+ declare function applySolveResult(state: SolveSessionState, result: SolveResult): SolveSessionState;
160
+
161
+ /**
162
+ * Single-in-flight, latest-wins throttle for any async operation: only one run at a
163
+ * time, a newly triggered value overwrites the single pending slot (no queue), and a
164
+ * stale run can be aborted via `AbortController` or timeout.
165
+ *
166
+ * Framework-free: `isRunning` is a plain getter, not a store. A reactive host must
167
+ * subscribe via `onChange` (see `@selvajs/ui`'s rune adapter) — polling the getter
168
+ * directly reads correct values but won't trigger a re-render.
169
+ */
170
+ interface AsyncThrottleOptions {
171
+ timeout?: number;
172
+ onChange?: () => void;
173
+ }
174
+ interface AsyncThrottle<T> {
175
+ trigger: (values: T) => void;
176
+ readonly isRunning: boolean;
177
+ readonly hasPending: boolean;
178
+ cancel: () => void;
179
+ }
180
+ declare function createAsyncThrottle<T>(run: (values: T, signal: AbortSignal) => Promise<void>, options?: AsyncThrottleOptions): AsyncThrottle<T>;
181
+
182
+ export { type AsyncThrottle, type ExternalInput, type ExternalReader, type ExternalValueRef, type MeshPolicy, type RequestResponseDriverOptions, type SolveDriver, SolveFn, type SolveMemo, type SolveMemoOptions, type SolveReporter, SolveResult, type SolveSession, type SolveSessionArgs, type SolveSessionState, applySolveResult, applyValueChange, buildInitialValues, clearExternalValue, createAsyncThrottle, createRequestResponseDriver, createSolveMemo, createSolveSession, getExternalInputs, makeInitialFlags, pickInputValues, readExternalValue, stableInputKey, writeExternalValue };
package/dist/client.js ADDED
@@ -0,0 +1,2 @@
1
+ import{getInputItems as w}from"@selvajs/schemas";var V="external";function g(n,r){return`${V}:${n}:${r}`}function P(n){let{scopeKey:r,inputId:s,value:e}=n;!r||!s||typeof sessionStorage>"u"||sessionStorage.setItem(g(r,s),JSON.stringify(e))}function f(n){let{scopeKey:r,inputId:s}=n;if(!r||!s||typeof sessionStorage>"u")return;let e=sessionStorage.getItem(g(r,s));if(e!==null)try{return JSON.parse(e)}catch{return}}function C(n){let{scopeKey:r,inputId:s}=n;!r||!s||typeof sessionStorage>"u"||sessionStorage.removeItem(g(r,s))}function h(n){return w(n).filter(r=>r.source?.kind==="client").map(r=>({paramId:r.paramId,displayName:r.displayName??r.paramId}))}import{getDefaultValue as j}from"@selvajs/schemas";function v(n,r,s){let e=new Set(h(n).map(t=>t.paramId)),a={};for(let t of n.inputs){if(e.has(t.id)){let i=s({scopeKey:r,inputId:t.id});i!==void 0&&(a[t.id]=i);continue}a[t.id]=t.default??j(t.paramType)}for(let t of n.outputs)a[t.id]=null;return a}function m(n){let r=n===!1;return{hasPendingChanges:r,hasNeverSolved:r}}function S(n,r,s,e){return n.values[r]=s,e===!1?(n.pendingValues[r]=s,n.hasPendingChanges=!0,{state:n,shouldSolve:!1}):{state:n,shouldSolve:!0}}function y(n,r){if(!n?.inputs)return r;let s={};for(let e of n.inputs)e.id in r&&(s[e.id]=r[e.id]);return s}function x(n,r){return n.error="",n.computeErrors=r.errors??[],n.computeWarnings=r.warnings??[],n.meshes=r.meshes??[],Object.assign(n.values,r.outputs),n.pendingValues={},n.hasPendingChanges=!1,n.hasNeverSolved=!1,n}function A(n){let r=n.schema,s=m(r?.instanceSolve),e={values:v(r,n.scopeKey,f),error:"",computeErrors:[],computeWarnings:[],meshes:[],pendingValues:{},hasPendingChanges:s.hasPendingChanges,hasNeverSolved:s.hasNeverSolved},a=new Set,t=()=>a.forEach(o=>o());function i(){n.driver.solve({...y(r,e.values)}),t()}return{get values(){return e.values},get error(){return e.error},get computeErrors(){return e.computeErrors},get computeWarnings(){return e.computeWarnings},get meshes(){return e.meshes},get hasPendingChanges(){return e.hasPendingChanges},get hasNeverSolved(){return e.hasNeverSolved},get isSolving(){return n.driver.isSolving},setValue(o,l,u=!1){let{shouldSolve:c}=S(e,o,l,r?.instanceSolve);c||u?(u&&!c&&(e.pendingValues={},e.hasPendingChanges=!1),i()):t()},solve(){i()},loadValues(o){Object.assign(e.values,o),r?.instanceSolve!==!1?i():(e.hasPendingChanges=!0,t())},rebuild(o,l){r=o,n.driver.clearCache?.(),e.meshes=[],e.error="",e.computeErrors=[],e.computeWarnings=[],e.pendingValues={},e.values=v(o,l,f);let u=m(o?.instanceSolve);e.hasPendingChanges=u.hasPendingChanges,e.hasNeverSolved=u.hasNeverSolved,o&&Object.keys(e.values).length>0&&o.instanceSolve!==!1?i():t()},report(o){x(e,o),t()},reportError(o){e.error=o,t()},subscribe(o){return a.add(o),()=>a.delete(o)},notify:t}}function R(n,r={}){let{timeout:s=6e4,onChange:e}=r,a=!1,t=null,i=null;function o(d){a!==d&&(a=d,e?.())}function l(){i?.abort(),i=null}async function u(d){l(),i=new AbortController;let{signal:M}=i,k=setTimeout(()=>{console.warn(`[Solve/throttle] run exceeded ${s}ms \u2014 aborting`),i?.abort()},s);o(!0);try{await n(d,M)}catch(p){(!(p instanceof Error)||p.name!=="AbortError"&&p.name!=="TimeoutError")&&console.error("[Solve/throttle] unhandled error in run:",p)}finally{if(clearTimeout(k),o(!1),i=null,t!==null){let p=t;t=null,u(p)}}}function c(d){a?(t!==null&&console.debug("[Solve/throttle] superseded pending run (latest-wins)"),t=d):u(d)}function I(){t=null,l()}return{trigger:c,get isRunning(){return a},get hasPending(){return t!==null},cancel:I}}function b(n){return T(n)}function T(n){if(n===null||typeof n!="object")return JSON.stringify(n)??"null";if(Array.isArray(n))return`[${n.map(T).join(",")}]`;let r=n;return`{${Object.keys(r).sort().map(e=>`${JSON.stringify(e)}:${T(r[e])}`).join(",")}}`}function E(n={}){let{max:r=16,meshPolicy:s}=n,e=new Map;function a(o){o.meshes?.length&&s?.release(o.meshes)}function t(o){return!s||!o.meshes?.length?o:{...o,meshes:s.clone(o.meshes)}}function i(o){let l=e.get(o);l!==void 0&&(e.delete(o),a(l))}return{get(o){let l=b(o),u=e.get(l);if(u!==void 0)return e.delete(l),e.set(l,u),console.info(`[Solve/memo] HIT \u2014 served from client memo (${e.size}/${r})`),t(u)},set(o,l){let u=b(o);for(i(u),e.set(u,t(l));e.size>r;){let c=e.keys().next().value;if(c===void 0)break;i(c),console.info(`[Solve/memo] evicted LRU entry (cap ${r})`)}},clear(){e.size>0&&console.info(`[Solve/memo] cleared ${e.size} entries (definition changed)`),e.forEach(a),e.clear()}}}function O(n,r,s={}){let{meshPolicy:e,...a}=s,t=E({meshPolicy:e}),i=R(async(o,l)=>{let u=t.get(o);if(u!==void 0){if(l.aborted)return;r().report(u);return}try{let c=await n(o,l);if(l.aborted){console.debug("[Solve/driver] solve completed after abort \u2014 result discarded");return}t.set(o,c),r().report(c)}catch(c){if(l.aborted){console.debug("[Solve/driver] solve aborted (superseded, cancelled, or timed out)");return}console.warn("[Solve/driver] solve failed:",c),r().reportError(c instanceof Error?c.message:String(c))}},a);return{solve(o){i.trigger(o)},cancel(){i.cancel()},get isSolving(){return i.isRunning},clearCache(){t.clear()}}}export{x as applySolveResult,S as applyValueChange,v as buildInitialValues,C as clearExternalValue,R as createAsyncThrottle,O as createRequestResponseDriver,E as createSolveMemo,A as createSolveSession,h as getExternalInputs,m as makeInitialFlags,y as pickInputValues,f as readExternalValue,b as stableInputKey,P as writeExternalValue};
2
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client/external-storage.ts","../src/client/solve-session-core.ts","../src/client/solve-session.ts","../src/client/async-throttle.ts","../src/client/solve-memo.ts","../src/client/drivers/request-response.ts"],"sourcesContent":["// Transit storage for inputs with source.kind === 'client': a producer route writes the\n// value here, the solver route reads it back. Keyed by (scopeKey, inputId) so values\n// don't bleed across solver contexts — scopeKey is whatever uniquely identifies one\n// (sessionId in plugin-ui/preview, definition guid in selva/library).\n\nimport type { UISchema } from '@selvajs/schemas';\nimport { getInputItems } from '@selvajs/schemas';\n\nconst STORAGE_PREFIX = 'external';\n\nfunction makeKey(scopeKey: string, inputId: string): string {\n\treturn `${STORAGE_PREFIX}:${scopeKey}:${inputId}`;\n}\n\nexport interface ExternalValueRef {\n\tscopeKey: string;\n\tinputId: string;\n}\n\nexport function writeExternalValue(args: ExternalValueRef & { value: unknown }): void {\n\tconst { scopeKey, inputId, value } = args;\n\tif (!scopeKey || !inputId) return;\n\tif (typeof sessionStorage === 'undefined') return;\n\tsessionStorage.setItem(makeKey(scopeKey, inputId), JSON.stringify(value));\n}\n\nexport function readExternalValue(ref: ExternalValueRef): unknown | undefined {\n\tconst { scopeKey, inputId } = ref;\n\tif (!scopeKey || !inputId) return undefined;\n\tif (typeof sessionStorage === 'undefined') return undefined;\n\tconst raw = sessionStorage.getItem(makeKey(scopeKey, inputId));\n\tif (raw === null) return undefined;\n\ttry {\n\t\treturn JSON.parse(raw);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function clearExternalValue(ref: ExternalValueRef): void {\n\tconst { scopeKey, inputId } = ref;\n\tif (!scopeKey || !inputId) return;\n\tif (typeof sessionStorage === 'undefined') return;\n\tsessionStorage.removeItem(makeKey(scopeKey, inputId));\n}\n\nexport interface ExternalInput {\n\tparamId: string;\n\tdisplayName: string;\n}\n\nexport function getExternalInputs(schema: UISchema): ExternalInput[] {\n\treturn getInputItems(schema)\n\t\t.filter((item) => item.source?.kind === 'client')\n\t\t.map((item) => ({\n\t\t\tparamId: item.paramId,\n\t\t\tdisplayName: item.displayName ?? item.paramId\n\t\t}));\n}\n","// Pure transition logic for a Solve Session.\n//\n// This module holds the value/lifecycle state machine with no transport and no state\n// ownership: how values are seeded (including client-sourced hydration), when a value\n// change should dispatch a solve vs. defer it, and what a reported solve result does to\n// the flags. `solve-session.ts` is a thin shell that owns the state object and the\n// subscriber set; every decision lives here, which is what makes it directly testable.\n\nimport type { UISchema } from '@selvajs/schemas';\nimport { getDefaultValue } from '@selvajs/schemas';\nimport { getExternalInputs, type ExternalValueRef } from './external-storage.js';\nimport type { SolveResult } from '../shared/solve-fn.js';\n\nexport interface SolveSessionState {\n\tvalues: Record<string, unknown>;\n\terror: string;\n\tcomputeErrors: string[];\n\tcomputeWarnings: string[];\n\tmeshes: unknown[];\n\t/** Values changed since the last solve, in manual (instanceSolve === false) mode. */\n\tpendingValues: Record<string, unknown>;\n\thasPendingChanges: boolean;\n\thasNeverSolved: boolean;\n}\n\nexport type ExternalReader = (ref: ExternalValueRef) => unknown | undefined;\n\n/**\n * Client-sourced inputs are hydrated from `read` and left absent when no stored value\n * exists, so the missing-inputs panel can detect them. Other inputs fall back to their\n * declared default, then the paramType default.\n */\nexport function buildInitialValues(\n\tschema: UISchema,\n\tscopeKey: string,\n\tread: ExternalReader\n): Record<string, unknown> {\n\tconst clientSet = new Set(getExternalInputs(schema).map((e) => e.paramId));\n\tconst values: Record<string, unknown> = {};\n\tfor (const input of schema.inputs) {\n\t\tif (clientSet.has(input.id)) {\n\t\t\tconst stored = read({ scopeKey, inputId: input.id });\n\t\t\tif (stored !== undefined) values[input.id] = stored;\n\t\t\tcontinue;\n\t\t}\n\t\tvalues[input.id] = input.default ?? getDefaultValue(input.paramType);\n\t}\n\tfor (const output of schema.outputs) {\n\t\tvalues[output.id] = null;\n\t}\n\treturn values;\n}\n\n/** Manual-solve schemas (instanceSolve === false) start dirty; auto-solve schemas start clean. */\nexport function makeInitialFlags(instanceSolve: boolean | undefined): {\n\thasPendingChanges: boolean;\n\thasNeverSolved: boolean;\n} {\n\tconst manual = instanceSolve === false;\n\treturn { hasPendingChanges: manual, hasNeverSolved: manual };\n}\n\n/** Manual mode defers (records the pending value, raises the dirty flag) instead of solving. */\nexport function applyValueChange(\n\tstate: SolveSessionState,\n\tid: string,\n\tvalue: unknown,\n\tinstanceSolve: boolean | undefined\n): { state: SolveSessionState; shouldSolve: boolean } {\n\tstate.values[id] = value;\n\tif (instanceSolve === false) {\n\t\tstate.pendingValues[id] = value;\n\t\tstate.hasPendingChanges = true;\n\t\treturn { state, shouldSolve: false };\n\t}\n\treturn { state, shouldSolve: true };\n}\n\n/**\n * Solve outputs live in the same `values` map as inputs (so widgets like dynamic value\n * lists can read them) but must not be echoed back to the driver: a measured 6.4 MB\n * options-list output showed this can re-upload a multi-MB payload no backend reads.\n */\nexport function pickInputValues(\n\tschema: UISchema | undefined,\n\tvalues: Record<string, unknown>\n): Record<string, unknown> {\n\tif (!schema?.inputs) return values;\n\tconst picked: Record<string, unknown> = {};\n\tfor (const input of schema.inputs) {\n\t\tif (input.id in values) picked[input.id] = values[input.id];\n\t}\n\treturn picked;\n}\n\nexport function applySolveResult(state: SolveSessionState, result: SolveResult): SolveSessionState {\n\tstate.error = '';\n\tstate.computeErrors = result.errors ?? [];\n\tstate.computeWarnings = result.warnings ?? [];\n\tstate.meshes = result.meshes ?? [];\n\tObject.assign(state.values, result.outputs);\n\tstate.pendingValues = {};\n\tstate.hasPendingChanges = false;\n\tstate.hasNeverSolved = false;\n\treturn state;\n}\n","// A framework-free shell over the pure transition logic in solve-session-core.ts: owns the\n// live values/flags, delegates every transition to the core, and drives solves through a\n// transport-agnostic SolveDriver.\n//\n// State is exposed as plain getters; it carries no framework reactivity of its own, so a\n// Svelte host must wrap it — see `useSolveSession.svelte.ts` in `@selvajs/ui`, which\n// republishes these getters as $state. Reading a getter without subscribing gives a\n// correct value but will not re-render.\n\nimport type { UISchema } from '@selvajs/schemas';\nimport { readExternalValue } from './external-storage.js';\nimport type { SolveResult } from '../shared/solve-fn.js';\nimport type { SolveDriver } from './drivers/driver.js';\nimport {\n\tbuildInitialValues,\n\tmakeInitialFlags,\n\tapplyValueChange,\n\tapplySolveResult,\n\tpickInputValues,\n\ttype SolveSessionState\n} from './solve-session-core.js';\n\nexport interface SolveSession {\n\treadonly values: Record<string, unknown>;\n\treadonly error: string;\n\treadonly computeErrors: string[];\n\treadonly computeWarnings: string[];\n\treadonly meshes: unknown[];\n\treadonly hasPendingChanges: boolean;\n\treadonly hasNeverSolved: boolean;\n\treadonly isSolving: boolean;\n\t/** `forceSolve` overrides manual mode for system-initiated reconciliation (e.g. a dynamic value list pruning a vanished selection), so the output can't lag behind the new value. */\n\tsetValue(id: string, value: unknown, forceSolve?: boolean): void;\n\tsolve(): void;\n\t/** Merges incoming values, then solves (auto) or marks dirty (manual). */\n\tloadValues(incoming: Record<string, unknown>): void;\n\trebuild(schema: UISchema, scopeKey: string): void;\n\treport(result: SolveResult): void;\n\treportError(message: string): void;\n\t/** Fires after any mutation of the getters above, including driver-reported `isSolving` transitions. */\n\tsubscribe(listener: () => void): () => void;\n\t/** For driver-owned state the session only forwards: `isSolving` lives on the driver, so wire this to its `onChange`. */\n\tnotify(): void;\n}\n\nexport interface SolveSessionArgs {\n\tschema: UISchema;\n\tscopeKey: string;\n\tdriver: SolveDriver;\n}\n\nexport function createSolveSession(args: SolveSessionArgs): SolveSession {\n\tlet currentSchema = args.schema;\n\n\tconst flags = makeInitialFlags(currentSchema?.instanceSolve);\n\tconst state: SolveSessionState = {\n\t\tvalues: buildInitialValues(currentSchema, args.scopeKey, readExternalValue),\n\t\terror: '',\n\t\tcomputeErrors: [],\n\t\tcomputeWarnings: [],\n\t\tmeshes: [],\n\t\tpendingValues: {},\n\t\thasPendingChanges: flags.hasPendingChanges,\n\t\thasNeverSolved: flags.hasNeverSolved\n\t};\n\n\tconst listeners = new Set<() => void>();\n\tconst emit = () => listeners.forEach((l) => l());\n\n\tfunction dispatch() {\n\t\t// Copy: state.values is the live map, and a driver may hold the payload across an\n\t\t// async solve — a later setValue must not mutate what's already in flight.\n\t\targs.driver.solve({ ...pickInputValues(currentSchema, state.values) });\n\t\temit();\n\t}\n\n\treturn {\n\t\tget values() {\n\t\t\treturn state.values;\n\t\t},\n\t\tget error() {\n\t\t\treturn state.error;\n\t\t},\n\t\tget computeErrors() {\n\t\t\treturn state.computeErrors;\n\t\t},\n\t\tget computeWarnings() {\n\t\t\treturn state.computeWarnings;\n\t\t},\n\t\tget meshes() {\n\t\t\treturn state.meshes;\n\t\t},\n\t\tget hasPendingChanges() {\n\t\t\treturn state.hasPendingChanges;\n\t\t},\n\t\tget hasNeverSolved() {\n\t\t\treturn state.hasNeverSolved;\n\t\t},\n\t\tget isSolving() {\n\t\t\treturn args.driver.isSolving;\n\t\t},\n\n\t\tsetValue(id, value, forceSolve = false) {\n\t\t\tconst { shouldSolve } = applyValueChange(state, id, value, currentSchema?.instanceSolve);\n\t\t\tif (shouldSolve || forceSolve) {\n\t\t\t\tif (forceSolve && !shouldSolve) {\n\t\t\t\t\tstate.pendingValues = {};\n\t\t\t\t\tstate.hasPendingChanges = false;\n\t\t\t\t}\n\t\t\t\tdispatch();\n\t\t\t} else {\n\t\t\t\temit();\n\t\t\t}\n\t\t},\n\n\t\tsolve() {\n\t\t\tdispatch();\n\t\t},\n\n\t\tloadValues(incoming) {\n\t\t\tObject.assign(state.values, incoming);\n\t\t\tif (currentSchema?.instanceSolve !== false) {\n\t\t\t\tdispatch();\n\t\t\t} else {\n\t\t\t\tstate.hasPendingChanges = true;\n\t\t\t\temit();\n\t\t\t}\n\t\t},\n\n\t\trebuild(schema, scopeKey) {\n\t\t\tcurrentSchema = schema;\n\t\t\t// The new definition has its own input space; a matching input key from the prior\n\t\t\t// one must not serve that definition's cached result.\n\t\t\targs.driver.clearCache?.();\n\t\t\tstate.meshes = [];\n\t\t\tstate.error = '';\n\t\t\tstate.computeErrors = [];\n\t\t\tstate.computeWarnings = [];\n\t\t\tstate.pendingValues = {};\n\t\t\tstate.values = buildInitialValues(schema, scopeKey, readExternalValue);\n\t\t\tconst f = makeInitialFlags(schema?.instanceSolve);\n\t\t\tstate.hasPendingChanges = f.hasPendingChanges;\n\t\t\tstate.hasNeverSolved = f.hasNeverSolved;\n\t\t\tif (schema && Object.keys(state.values).length > 0 && schema.instanceSolve !== false) {\n\t\t\t\tdispatch();\n\t\t\t} else {\n\t\t\t\temit();\n\t\t\t}\n\t\t},\n\n\t\treport(result) {\n\t\t\tapplySolveResult(state, result);\n\t\t\temit();\n\t\t},\n\n\t\treportError(message) {\n\t\t\tstate.error = message;\n\t\t\temit();\n\t\t},\n\n\t\tsubscribe(listener) {\n\t\t\tlisteners.add(listener);\n\t\t\treturn () => listeners.delete(listener);\n\t\t},\n\n\t\tnotify: emit\n\t};\n}\n","/**\n * Single-in-flight, latest-wins throttle for any async operation: only one run at a\n * time, a newly triggered value overwrites the single pending slot (no queue), and a\n * stale run can be aborted via `AbortController` or timeout.\n *\n * Framework-free: `isRunning` is a plain getter, not a store. A reactive host must\n * subscribe via `onChange` (see `@selvajs/ui`'s rune adapter) — polling the getter\n * directly reads correct values but won't trigger a re-render.\n */\ninterface AsyncThrottleOptions {\n\ttimeout?: number;\n\tonChange?: () => void;\n}\n\nexport interface AsyncThrottle<T> {\n\ttrigger: (values: T) => void;\n\treadonly isRunning: boolean;\n\treadonly hasPending: boolean;\n\tcancel: () => void;\n}\n\n/** Fallback per-run abort timeout (ms) when the caller doesn't pass one. */\nconst DEFAULT_TIMEOUT_MS = 60_000;\n\nexport function createAsyncThrottle<T>(\n\trun: (values: T, signal: AbortSignal) => Promise<void>,\n\toptions: AsyncThrottleOptions = {}\n): AsyncThrottle<T> {\n\tconst { timeout = DEFAULT_TIMEOUT_MS, onChange } = options;\n\n\tlet isRunning = false;\n\tlet pendingValues: T | null = null;\n\tlet currentAbortController: AbortController | null = null;\n\n\tfunction setRunning(next: boolean) {\n\t\tif (isRunning === next) return;\n\t\tisRunning = next;\n\t\tonChange?.();\n\t}\n\n\tfunction abortCurrent() {\n\t\tcurrentAbortController?.abort();\n\t\tcurrentAbortController = null;\n\t}\n\n\tasync function execute(values: T) {\n\t\tabortCurrent();\n\n\t\tcurrentAbortController = new AbortController();\n\t\tconst { signal } = currentAbortController;\n\t\tconst timeoutId = setTimeout(() => {\n\t\t\t// Cleared in `finally` on every other path, so firing means a genuine\n\t\t\t// timeout — the only signal for it (the abort itself is swallowed below).\n\t\t\tconsole.warn(`[Solve/throttle] run exceeded ${timeout}ms — aborting`);\n\t\t\tcurrentAbortController?.abort();\n\t\t}, timeout);\n\n\t\tsetRunning(true);\n\t\ttry {\n\t\t\tawait run(values, signal);\n\t\t} catch (err) {\n\t\t\t// AbortError is expected (timeout or cancel). Non-abort errors must be\n\t\t\t// handled inside `run` — re-throwing here would produce an unhandled\n\t\t\t// rejection because `execute` is always called fire-and-forget.\n\t\t\tif (!(err instanceof Error) || (err.name !== 'AbortError' && err.name !== 'TimeoutError')) {\n\t\t\t\tconsole.error('[Solve/throttle] unhandled error in run:', err);\n\t\t\t}\n\t\t} finally {\n\t\t\tclearTimeout(timeoutId);\n\t\t\tsetRunning(false);\n\t\t\tcurrentAbortController = null;\n\n\t\t\tif (pendingValues !== null) {\n\t\t\t\tconst next = pendingValues;\n\t\t\t\tpendingValues = null;\n\t\t\t\texecute(next);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction trigger(values: T) {\n\t\tif (isRunning) {\n\t\t\tif (pendingValues !== null) {\n\t\t\t\t// Latest-wins: the previously-queued values are dropped, not run.\n\t\t\t\t// `debug`, not `info`: a slider scrub fires this per frame, and it is a trace of\n\t\t\t\t// normal operation. Promoting it would spam a host's console on every drag.\n\t\t\t\t// eslint-disable-next-line no-console -- see above\n\t\t\t\tconsole.debug('[Solve/throttle] superseded pending run (latest-wins)');\n\t\t\t}\n\t\t\tpendingValues = values;\n\t\t} else {\n\t\t\texecute(values);\n\t\t}\n\t}\n\n\tfunction cancel() {\n\t\tpendingValues = null;\n\t\tabortCurrent();\n\t}\n\n\treturn {\n\t\ttrigger,\n\t\tget isRunning() {\n\t\t\treturn isRunning;\n\t\t},\n\t\tget hasPending() {\n\t\t\treturn pendingValues !== null;\n\t\t},\n\t\tcancel\n\t};\n}\n","// A small LRU keyed on a stable serialization of the solve inputs, sitting in front of the\n// request/response driver: re-solving inputs already seen this session (e.g. dragging a\n// slider back) returns instantly instead of round-tripping. A hit is always a complete\n// result, since only fully-solved values are ever stored.\n//\n// A SolveResult can carry live renderer objects, and a viewer typically takes ownership of\n// every mesh array it renders — disposing the previous content on the next update. So the\n// memo can neither hand out its own instances (they'd be disposed under it, then re-added\n// dead on the next hit) nor drop entries silently (their GPU buffers would leak). It\n// therefore keeps private copies, serves a fresh copy per hit, and releases an entry\n// whenever it leaves the map — but it does not know what a mesh is: `TMesh` is opaque and\n// the clone/release policy is injected, which keeps `three` out of this package entirely.\n\nimport type { SolveResult } from '../shared/solve-fn.js';\n\n/**\n * Deterministic string key for a set of input values. Object keys are sorted at every\n * level so two logically-equal inputs (built in different key order) collide, matching the\n * server's stable-input keying intent. Values are plain JSON — the projected solve inputs\n * never contain functions or cycles.\n */\nexport function stableInputKey(values: Record<string, unknown>): string {\n\treturn serialize(values);\n}\n\nfunction serialize(value: unknown): string {\n\tif (value === null || typeof value !== 'object') return JSON.stringify(value) ?? 'null';\n\tif (Array.isArray(value)) return `[${value.map(serialize).join(',')}]`;\n\tconst obj = value as Record<string, unknown>;\n\tconst keys = Object.keys(obj).sort();\n\treturn `{${keys.map((k) => `${JSON.stringify(k)}:${serialize(obj[k])}`).join(',')}}`;\n}\n\n/**\n * Omit for mesh-free results, or for meshes no consumer takes ownership of: the memo then\n * stores and serves them by reference, which is correct precisely because nothing frees\n * them behind its back.\n *\n * @property clone - Called on the way in AND on every hit, so neither the stored entry nor\n * a previously served one can be disposed by the other's owner.\n * @property release - Must NOT free anything shared with live content — see\n * `releaseSceneObjects` in `@selvajs/visualization/parse`, which skips materials for\n * exactly that reason.\n */\nexport interface MeshPolicy<TMesh> {\n\tclone(meshes: TMesh[]): TMesh[];\n\trelease(meshes: TMesh[]): void;\n}\n\nexport interface SolveMemoOptions<TMesh> {\n\t/**\n\t * Entry cap (entries, not bytes). Solve results can be large, so the default is\n\t * deliberately small — this targets the tight slider-scrub loop, not a durable cache.\n\t */\n\tmax?: number;\n\tmeshPolicy?: MeshPolicy<TMesh>;\n}\n\nexport interface SolveMemo<TMesh = unknown> {\n\tget(values: Record<string, unknown>): SolveResult<TMesh> | undefined;\n\t/** Evicts the LRU tail if `set` pushes the map past its cap. */\n\tset(values: Record<string, unknown>, result: SolveResult<TMesh>): void;\n\tclear(): void;\n}\n\nexport function createSolveMemo<TMesh = unknown>(\n\toptions: SolveMemoOptions<TMesh> = {}\n): SolveMemo<TMesh> {\n\tconst { max = 16, meshPolicy } = options;\n\tconst entries = new Map<string, SolveResult<TMesh>>();\n\n\tfunction release(result: SolveResult<TMesh>): void {\n\t\tif (result.meshes?.length) meshPolicy?.release(result.meshes);\n\t}\n\n\tfunction copy(result: SolveResult<TMesh>): SolveResult<TMesh> {\n\t\tif (!meshPolicy || !result.meshes?.length) return result;\n\t\treturn { ...result, meshes: meshPolicy.clone(result.meshes) };\n\t}\n\n\tfunction evict(key: string): void {\n\t\tconst entry = entries.get(key);\n\t\tif (entry === undefined) return;\n\t\tentries.delete(key);\n\t\trelease(entry);\n\t}\n\n\treturn {\n\t\tget(values) {\n\t\t\tconst key = stableInputKey(values);\n\t\t\tconst hit = entries.get(key);\n\t\t\tif (hit === undefined) return undefined;\n\t\t\t// Refresh recency: re-insert at the tail.\n\t\t\tentries.delete(key);\n\t\t\tentries.set(key, hit);\n\t\t\t// A memo hit skips the transport entirely, so no other log line fires —\n\t\t\t// this line is the only trace it wasn't a fresh solve.\n\t\t\tconsole.info(`[Solve/memo] HIT — served from client memo (${entries.size}/${max})`);\n\t\t\t// The consumer disposes what it renders, so the retained entry must never be\n\t\t\t// the same instance handed out twice.\n\t\t\treturn copy(hit);\n\t\t},\n\t\tset(values, result) {\n\t\t\tconst key = stableInputKey(values);\n\t\t\t// Overwriting a key strands the old value's resources unless it's released first.\n\t\t\tevict(key);\n\t\t\tentries.set(key, copy(result));\n\t\t\twhile (entries.size > max) {\n\t\t\t\tconst oldest = entries.keys().next().value;\n\t\t\t\tif (oldest === undefined) break;\n\t\t\t\tevict(oldest);\n\t\t\t\tconsole.info(`[Solve/memo] evicted LRU entry (cap ${max})`);\n\t\t\t}\n\t\t},\n\t\tclear() {\n\t\t\tif (entries.size > 0) {\n\t\t\t\tconsole.info(`[Solve/memo] cleared ${entries.size} entries (definition changed)`);\n\t\t\t}\n\t\t\tentries.forEach(release);\n\t\t\tentries.clear();\n\t\t}\n\t};\n}\n","import { createAsyncThrottle } from '../async-throttle.js';\nimport { createSolveMemo, type MeshPolicy } from '../solve-memo.js';\nimport type { SolveFn } from '../../shared/solve-fn.js';\nimport type { SolveDriver, SolveReporter } from './driver.js';\n\nexport interface RequestResponseDriverOptions<TMesh> {\n\ttimeout?: number;\n\tonChange?: () => void;\n\t/** Mesh ownership policy for the result memo — see `MeshPolicy` (solve-memo.ts). */\n\tmeshPolicy?: MeshPolicy<TMesh>;\n}\n\n/**\n * `getReporter` is lazy (`() => session`) because the session and driver reference each\n * other — the host builds the driver first, then the session, then can hand this back.\n */\nexport function createRequestResponseDriver<TMesh = unknown>(\n\tonSolve: SolveFn<TMesh>,\n\tgetReporter: () => SolveReporter<TMesh>,\n\toptions: RequestResponseDriverOptions<TMesh> = {}\n): SolveDriver {\n\tconst { meshPolicy, ...throttleOptions } = options;\n\n\t// Checked inside the throttled run, not before triggering it, so a hit only serves\n\t// after the throttle's latest-wins ordering has picked these values to run.\n\tconst memo = createSolveMemo<TMesh>({ meshPolicy });\n\n\tconst throttle = createAsyncThrottle<Record<string, unknown>>(async (values, signal) => {\n\t\tconst cached = memo.get(values);\n\t\tif (cached !== undefined) {\n\t\t\tif (signal.aborted) return;\n\t\t\tgetReporter().report(cached);\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tconst result = await onSolve(values, signal);\n\t\t\tif (signal.aborted) {\n\t\t\t\t// eslint-disable-next-line no-console -- normal during a slider scrub; not a warning\n\t\t\t\tconsole.debug('[Solve/driver] solve completed after abort — result discarded');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tmemo.set(values, result);\n\t\t\tgetReporter().report(result);\n\t\t} catch (err) {\n\t\t\tif (signal.aborted) {\n\t\t\t\t// eslint-disable-next-line no-console -- normal during a slider scrub; not a warning\n\t\t\t\tconsole.debug('[Solve/driver] solve aborted (superseded, cancelled, or timed out)');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconsole.warn('[Solve/driver] solve failed:', err);\n\t\t\tgetReporter().reportError(err instanceof Error ? err.message : String(err));\n\t\t}\n\t}, throttleOptions);\n\n\treturn {\n\t\tsolve(values) {\n\t\t\tthrottle.trigger(values);\n\t\t},\n\t\tcancel() {\n\t\t\tthrottle.cancel();\n\t\t},\n\t\tget isSolving() {\n\t\t\treturn throttle.isRunning;\n\t\t},\n\t\tclearCache() {\n\t\t\tmemo.clear();\n\t\t}\n\t};\n}\n"],"mappings":"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","names":["getInputItems","STORAGE_PREFIX","makeKey","scopeKey","inputId","writeExternalValue","args","value","readExternalValue","ref","raw","clearExternalValue","getExternalInputs","schema","item","getDefaultValue","buildInitialValues","schema","scopeKey","read","clientSet","getExternalInputs","e","values","input","stored","getDefaultValue","output","makeInitialFlags","instanceSolve","manual","applyValueChange","state","id","value","pickInputValues","picked","applySolveResult","result","createSolveSession","args","currentSchema","flags","makeInitialFlags","state","buildInitialValues","readExternalValue","listeners","emit","l","dispatch","pickInputValues","id","value","forceSolve","shouldSolve","applyValueChange","incoming","schema","scopeKey","f","result","applySolveResult","message","listener","createAsyncThrottle","run","options","timeout","onChange","isRunning","pendingValues","currentAbortController","setRunning","next","abortCurrent","execute","values","signal","timeoutId","err","trigger","cancel","stableInputKey","values","serialize","value","obj","k","createSolveMemo","options","max","meshPolicy","entries","release","result","copy","evict","key","entry","hit","oldest","createRequestResponseDriver","onSolve","getReporter","options","meshPolicy","throttleOptions","memo","createSolveMemo","throttle","createAsyncThrottle","values","signal","cached","result","err"]}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _compute = require('@selvajs/compute');var _zlib = require('zlib');var _util = require('util');function T(e,r){let t={id:e.id,name:e.nickname,nickname:e.nickname||null,description:e.description||"",paramType:e.paramType==="dynamicValueList"?"valueList":e.paramType,treeAccess:e.inputStructure==="tree",minimum:_nullishCoalesce(e.minimum, () => (null)),maximum:_nullishCoalesce(e.maximum, () => (null)),atLeast:1,atMost:1,stepSize:e.stepSize,default:_nullishCoalesce(r, () => (e.default))},s=_compute.processInput.call(void 0, t),o=_nullishCoalesce(r, () => (e.default));return s.default==null&&o!=null&&(Array.isArray(o)||s.paramType==="ValueList"&&typeof o!="object")&&(s.default=o),s}var $=_util.promisify.call(void 0, _zlib.gzip),E= exports.COMPUTE_CONTRACT_VERSION =1,P= exports.COMPUTE_VERSION_HEADER ="X-Selva-Compute-Version";function B(e,r){return _compute.TreeBuilder.fromInputParams(e.filter(t=>t.paramType).map(t=>T(t,r[t.id])))}async function D(e){let{client:r,signal:t}=e,s=performance.now(),o=_nullishCoalesce(e.inputTree, () => (B(e.inputs,e.values))),u=performance.now()-s,i=r.solveMeta.seq,l=r.rhinoTiming.seq,n,a=performance.now();try{n=await r.scheduler.solve(e.definitionSource,o,{signal:t})}catch(v){let b=performance.now()-a;if(v instanceof Error&&v.name==="AbortError")return t.aborted?{kind:"client_abort",durationMs:b}:{kind:"timeout",durationMs:b,message:`Solve exceeded the ${Math.round(e.maxSolveDurationMs/1e3)}s deadline.`};if(v instanceof _compute.RhinoComputeError){if(v.code===_compute.ErrorCodes.QUEUE_FULL)return{kind:"shed",durationMs:b,reason:"queue_full",retryAfterSeconds:1,message:"Compute server is at capacity. Retry shortly."};if(v.code===_compute.ErrorCodes.QUEUE_TIMEOUT)return{kind:"shed",durationMs:b,reason:"queue_timeout",retryAfterSeconds:1,message:"Compute request waited too long in the queue. Retry shortly."}}return{kind:"compute_error",durationMs:b,error:v}}let c=performance.now()-a,h=performance.now(),d;try{d=JSON.stringify(n)}catch(v){if(v instanceof RangeError)return{kind:"too_large"};throw v}if(d.length>e.responseMaxBytes)return{kind:"too_large"};let p=performance.now()-h,y=/\bgzip\b/i.test(e.acceptEncoding)&&d.length>1024,f=null,g=0;if(y){let v=performance.now();f=await $(Buffer.from(d)),g=performance.now()-v}let S=performance.now()-e.loadStartMs,M=r.solveMeta.seq===i+1?r.solveMeta.last:null,k=M&&!M.fromCache&&r.rhinoTiming.seq===l+1?r.rhinoTiming.last:null,C={"Content-Type":"application/json","Server-Timing":U({defLoadMs:e.defLoadMs,treeBuildMs:u,solveMs:c,serializeMs:p,gzipMs:g,serverTotalMs:S,rhino:k,settle:M,byteRefOutcome:e.byteRefOutcome,prepMarks:_nullishCoalesce(e.prepMarks, () => ([]))}),[P]:String(E),Vary:"Accept-Encoding"},R={treeBuildMs:u,solveMs:c,serializeMs:p,gzipMs:g,serverTotalMs:S,serializedBytes:d.length,compressedBytes:_nullishCoalesce(_optionalChain([f, 'optionalAccess', _2 => _2.byteLength]), () => (null))},x;return f?(C["Content-Encoding"]="gzip",C["Content-Length"]=String(f.byteLength),x={body:new Uint8Array(f),encoding:"gzip",headers:C,result:n,metrics:R}):(C["Content-Length"]=String(Buffer.byteLength(d)),x={body:d,headers:C,result:n,metrics:R}),{kind:"ok",envelope:x,solveMs:c,errorCount:_nullishCoalesce(_optionalChain([n, 'access', _3 => _3.errors, 'optionalAccess', _4 => _4.length]), () => (0)),warningCount:_nullishCoalesce(_optionalChain([n, 'access', _5 => _5.warnings, 'optionalAccess', _6 => _6.length]), () => (0))}}function F(e,r){let t=/\bgzip\b/i.test(r);if(e.encoding==="gzip"&&!t){let s=_zlib.gunzipSync.call(void 0, e.body instanceof Uint8Array?e.body:Buffer.from(e.body)).toString("utf8"),o={...e.headers};return delete o["Content-Encoding"],o["Content-Length"]=String(Buffer.byteLength(s)),{body:s,headers:o}}return{body:e.body,headers:e.headers}}function U(e){let r=`load;dur=${e.defLoadMs.toFixed(1)}, tree;dur=${e.treeBuildMs.toFixed(1)}, solve;dur=${e.solveMs.toFixed(1)}, serialize;dur=${e.serializeMs.toFixed(1)}, gzip;dur=${e.gzipMs.toFixed(1)}, total;dur=${e.serverTotalMs.toFixed(1)}`,t=e.rhino;if(t){let u=t.decode+t.solve+t.encode,i=Math.max(0,e.solveMs-u);r+=`, rhino_decode;dur=${t.decode.toFixed(1)}, rhino_solve;dur=${t.solve.toFixed(1)}, rhino_encode;dur=${t.encode.toFixed(1)}, compute_link;dur=${i.toFixed(1)}`}for(let[u,i]of e.prepMarks)r+=`, p_${u};dur=${i.toFixed(1)}`;let s=e.settle;s&&(r+=`, selva_cache;dur=${s.fromCache?1:0}`,s.definitionReuploaded!==void 0&&(r+=`, def_reupload;dur=${s.definitionReuploaded?1:0}`));let o=e.byteRefOutcome;if(o){let u=o.loaded?o.fromCache?"hit":"miss":"skipped";r+=`, def_bytes;desc=${u}`}return r}function V(e){return e.id}var N=16;function j(e){let r=_nullishCoalesce(e.maxCachedClients, () => (N)),t=new Map,s=new Map;e.debugVerbose&&_compute.enableDebugLogging.call(void 0, );let o=i=>{e.debug&&_optionalChain([e, 'access', _7 => _7.onDebugLog, 'optionalCall', _8 => _8(i)])};async function u(i,l){let n={last:null,seq:0},a={last:null,seq:0},c={serverUrl:i.serverUrl,apiKey:i.apiKey,debug:e.debugVerbose,cachesolve:e.cachesolve,cacheerroredsolves:e.cacheerroredsolves,onServerTiming:p=>{let m=_nullishCoalesce(p.decode, () => (0)),y=_nullishCoalesce(p.solve, () => (0)),f=_nullishCoalesce(p.encode, () => (0));if(n.seq+=1,n.last={decode:m,solve:y,encode:f},!e.debug)return;let g=m<=1?"HIT (definition reused, no re-upload/parse)":"miss (full parse)",S=e.cachesolve&&y<=1?"HIT (cached result, solve skipped)":e.cachesolve?"miss (solved fresh)":"off";o(`[Compute/rhino-cache] decode=${m}ms solve=${y}ms encode=${f}ms | definition-cache: ${g} | solve-cache (cachesolve): ${S}`)}};l&&(c.headers={"X-Selva-Definition":l});let h=await _compute.GrasshopperClient.create(c),d=h.createScheduler({mode:"queue",maxConcurrent:e.maxConcurrentSolves,maxQueueDepth:e.maxQueueDepth||void 0,queueWaitMs:e.queueWaitMs||void 0,timeoutMs:e.maxSolveDurationMs,cache:e.responseCacheMaxBytes>0?{maxEntries:20,ttlMs:5*6e4,maxBytes:e.responseCacheMaxBytes}:!1,reuseServerDefinitionCache:e.reuseServerDefinitionCache,onSettle:(p,m)=>{if(a.seq+=1,m.status==="success"&&(a.last={fromCache:m.fromCache===!0,definitionReuploaded:m.definitionReuploaded},!!e.debug&&(o(`[Compute/selva-cache] ${m.fromCache?"HIT \u2014 served from cache (no compute call)":"miss \u2014 went to Rhino.Compute"} (${Math.round(m.durationMs)}ms)`),m.fromCache||o(m.definitionReuploaded===!0?"[Compute/def-cache] miss \u2014 definition RE-UPLOADED (pointer was cold/stale)":m.definitionReuploaded===!1?"[Compute/def-cache] HIT \u2014 definition reused from server cache (no upload)":"[Compute/def-cache] n/a \u2014 reuse disabled or non-reusable definition"),!m.fromCache))){let y=_optionalChain([m, 'access', _9 => _9.response, 'optionalAccess', _10 => _10.errors]),f=Array.isArray(y)?y.length:0;if(f>0){let g=e.cacheerroredsolves?"cached (cacheerroredsolves on)":"not cached (cacheerroredsolves off) \u2014 re-run until fixed";o(`[Compute/rhino-cache] ${f} GH error(s); ${g}. First: ${JSON.stringify(y[0])}`)}}}});return{client:h,scheduler:d,rhinoTiming:n,solveMeta:a}}return{async getClient(i,l){let n=i.id,a=t.get(n);if(a)return t.delete(n),t.set(n,a),a;let c=s.get(n);if(c)return c;o(`[Compute/client-cache] miss \u2014 building warm client for server ${n}`);let h=(async()=>{let d=await u(i,_optionalChain([l, 'optionalAccess', _11 => _11.definitionGuid]));if(t.size>=r){let p=t.keys().next().value;p!==void 0&&(o(`[Compute/client-cache] LRU evicted warm client for server ${p} (cap ${r})`),_optionalChain([t, 'access', _12 => _12.get, 'call', _13 => _13(p), 'optionalAccess', _14 => _14.scheduler, 'access', _15 => _15.dispose, 'call', _16 => _16()]),t.delete(p))}return t.set(n,d),d})().finally(()=>s.delete(n));return s.set(n,h),h},evict(i){let l=i,n=t.get(l);n&&(o(`[Compute/client-cache] evicted warm client for server ${l} (config change)`),n.scheduler.dispose(),t.delete(l))},solveCacheStats(){let i={warmClients:t.size,entries:0,bytes:0,hits:0,misses:0,evictions:0};for(let l of t.values()){let n=l.scheduler.cacheStats();i.entries+=n.entries,i.bytes+=n.bytes,i.hits+=n.hits,i.misses+=n.misses,i.evictions+=n.evictions}return i},disposeAll(){o(`[Compute/client-cache] disposing all ${t.size} warm client(s)`);for(let i of t.values())i.scheduler.dispose();t.clear()}}}function H(e){let r=Math.max(0,Math.floor(e)),t=new Map,s=0,o={hits:0,misses:0,evictions:0};function u(n,a){t.delete(n),t.set(n,a)}function i(){for(;s>r&&t.size>0;){let n=t.keys().next().value,a=t.get(n);t.delete(n),s-=a.bytes.byteLength,o.evictions+=1}}function l(n,a){if(r===0||a.byteLength>r)return;let c=t.get(n);c&&(s-=c.bytes.byteLength),t.set(n,{bytes:a}),s+=a.byteLength,i()}return{getOrLoad(n,a){let c={loaded:!1,fromCache:!1};return{key:n,outcome:c,load:async()=>{let h=t.get(n);if(h)return u(n,h),o.hits+=1,c.loaded=!0,c.fromCache=!0,h.bytes;o.misses+=1,c.loaded=!0,c.fromCache=!1;let d=await a();return l(n,d),d}}},stats(){return{hits:o.hits,misses:o.misses,evictions:o.evictions,entries:t.size,bytes:s}},clear(){t.clear(),s=0}}}function W(e={}){let r=new Map;return{run(t,s,o){let u=r.get(t);if(u)return _optionalChain([e, 'access', _17 => _17.onJoin, 'optionalCall', _18 => _18(t)]),_optionalChain([u, 'access', _19 => _19.notifyOwner, 'optionalCall', _20 => _20()]),u.promise;let i={notifyOwner:o,promise:void 0},l=(async()=>s())().finally(()=>{i.notifyOwner=void 0,r.get(t)===i&&r.delete(t)});return i.promise=l,r.set(t,i),l},inFlight(){return r.size}}}exports.COMPUTE_CONTRACT_VERSION = E; exports.COMPUTE_VERSION_HEADER = P; exports.adaptEnvelopeToEncoding = F; exports.buildSolveInputTree = B; exports.createClientCache = j; exports.createDefinitionByteCache = H; exports.createSolveCacheSingleFlight = W; exports.runSolvePipeline = D; exports.serverIdentity = V; exports.transformInputParameter = T;
2
+ //# sourceMappingURL=server.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["c:\\Users\\felix\\coding\\selva\\packages\\solve\\dist\\server.cjs"],"names":[],"mappings":"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","file":"C:\\Users\\felix\\coding\\selva\\packages\\solve\\dist\\server.cjs","sourcesContent":[null]}