@selvajs/server 0.2.1 → 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +23 -16
  2. package/dist/compute/index.d.ts +0 -8
  3. package/dist/compute/index.d.ts.map +1 -1
  4. package/dist/compute/index.js +12 -9
  5. package/dist/compute/index.js.map +1 -1
  6. package/dist/compute/limits.d.ts +38 -54
  7. package/dist/compute/limits.d.ts.map +1 -1
  8. package/dist/compute/limits.js +39 -16
  9. package/dist/compute/limits.js.map +1 -1
  10. package/package.json +6 -12
  11. package/dist/compute/client-cache.d.ts +0 -135
  12. package/dist/compute/client-cache.d.ts.map +0 -1
  13. package/dist/compute/client-cache.js +0 -176
  14. package/dist/compute/client-cache.js.map +0 -1
  15. package/dist/compute/definition-byte-cache.d.ts +0 -85
  16. package/dist/compute/definition-byte-cache.d.ts.map +0 -1
  17. package/dist/compute/definition-byte-cache.js +0 -110
  18. package/dist/compute/definition-byte-cache.js.map +0 -1
  19. package/dist/compute/memory-solve-cache.d.ts +0 -58
  20. package/dist/compute/memory-solve-cache.d.ts.map +0 -1
  21. package/dist/compute/memory-solve-cache.js +0 -0
  22. package/dist/compute/memory-solve-cache.js.map +0 -1
  23. package/dist/compute/solve-cache-envelope.d.ts +0 -53
  24. package/dist/compute/solve-cache-envelope.d.ts.map +0 -1
  25. package/dist/compute/solve-cache-envelope.js +0 -66
  26. package/dist/compute/solve-cache-envelope.js.map +0 -1
  27. package/dist/compute/solve-cache-key.d.ts +0 -64
  28. package/dist/compute/solve-cache-key.d.ts.map +0 -1
  29. package/dist/compute/solve-cache-key.js +0 -51
  30. package/dist/compute/solve-cache-key.js.map +0 -1
  31. package/dist/compute/solve-cache-single-flight.d.ts +0 -42
  32. package/dist/compute/solve-cache-single-flight.d.ts.map +0 -1
  33. package/dist/compute/solve-cache-single-flight.js +0 -50
  34. package/dist/compute/solve-cache-single-flight.js.map +0 -1
  35. package/dist/compute/solve-pipeline.d.ts +0 -221
  36. package/dist/compute/solve-pipeline.d.ts.map +0 -1
  37. package/dist/compute/solve-pipeline.js +0 -419
  38. package/dist/compute/solve-pipeline.js.map +0 -1
  39. package/dist/compute/transform-input.d.ts +0 -20
  40. package/dist/compute/transform-input.d.ts.map +0 -1
  41. package/dist/compute/transform-input.js +0 -62
  42. package/dist/compute/transform-input.js.map +0 -1
  43. package/dist/index.d.ts +0 -27
  44. package/dist/index.d.ts.map +0 -1
  45. package/dist/index.js +0 -27
  46. package/dist/index.js.map +0 -1
package/README.md CHANGED
@@ -1,9 +1,13 @@
1
1
  # @selvajs/server
2
2
 
3
- Transport-agnostic compute/solve/definitions server building blocks for apps built on the
4
- Selva engine: limits, rate limiting, SSRF guard, input transform, solve pipeline, definition
5
- service, render loader, and structured logging. Consumed by `@selvajs/selva`; not tied to
6
- SvelteKit or any specific HTTP framework.
3
+ Transport-agnostic server building blocks for apps built on the Selva engine: limits, rate
4
+ limiting, SSRF guard, definition service, render loader, and structured logging. Consumed by
5
+ `@selvajs/selva`; not tied to SvelteKit or any specific HTTP framework.
6
+
7
+ **The solve core lives in [`@selvajs/solve/server`](../solve/README.md)** — pipeline, L2 result
8
+ cache, single-flight, client and definition-byte caches. This package does not re-export it and does
9
+ not depend on it. What stays here is HTTP _request policy_, which is a different job from running a
10
+ solve.
7
11
 
8
12
  Logging follows the same rule as the rest of the package: nothing here logs unless you wire
9
13
  it. Modules that emit diagnostics take an optional `logger?: ILogger` and default to
@@ -20,18 +24,21 @@ pnpm add @selvajs/server
20
24
 
21
25
  ## Subpath exports
22
26
 
23
- | Export | Contents |
24
- | ----------------------------- | ------------------------------------------------------- |
25
- | `@selvajs/server` | Root — package entry point |
26
- | `@selvajs/server/compute` | Solve pipeline, SSRF guard, Rhino.Compute client wiring |
27
- | `@selvajs/server/definitions` | Definition service + render loader |
28
- | `@selvajs/server/providers` | Provider composition helpers |
29
- | `@selvajs/server/tokens` | API token (PAT) codec |
30
- | `@selvajs/server/errors` | Shared error types |
31
- | `@selvajs/server/logging` | pino-backed `ILogger` + request-id correlation |
32
- | `@selvajs/server/http` | Rate limiting, security headers, request limits |
33
- | `@selvajs/server/access` | Access/permission checks |
34
- | `@selvajs/server/ops` | Operational utilities |
27
+ **There is no root export.** Import from a subpath — `import … from '@selvajs/server'` fails to
28
+ resolve, on purpose. A root barrel re-exporting all nine subpaths put 41 symbols in one namespace and
29
+ hid which slice a consumer actually depended on; nothing in this repo used it.
30
+
31
+ | Export | Contents |
32
+ | ----------------------------- | ------------------------------------------------------------------ |
33
+ | `@selvajs/server/compute` | Compute limits, rate limiting, SSRF guard, remote-definition fetch |
34
+ | `@selvajs/server/definitions` | Definition service + render loader |
35
+ | `@selvajs/server/providers` | Provider composition helpers |
36
+ | `@selvajs/server/tokens` | API token (PAT) codec |
37
+ | `@selvajs/server/errors` | Shared error types |
38
+ | `@selvajs/server/logging` | pino-backed `ILogger` + request-id correlation |
39
+ | `@selvajs/server/http` | Security headers, request limits, route classification |
40
+ | `@selvajs/server/access` | Access/permission checks |
41
+ | `@selvajs/server/ops` | Operational utilities |
35
42
 
36
43
  Requires a `@selvajs/platform` provider (e.g. `@selvajs/local-provider` or
37
44
  `@selvajs/supabase-provider`) for data/storage/auth.
@@ -1,13 +1,5 @@
1
1
  export { resolveComputeLimits, readPositiveInt, readNonNegativeInt, readBool, type ComputeLimits, type EnvRecord } from './limits.js';
2
- export { createDefinitionByteCache, type DefinitionByteCache, type ByteCacheRef, type ByteCacheStats } from './definition-byte-cache.js';
3
- export { createMemorySolveResultCache, type MemorySolveResultCache, type SolveCacheStats } from './memory-solve-cache.js';
4
- export { deriveSolveCacheInputKey, type SolveCacheConfigSubset, type SolveCacheInputKey } from './solve-cache-key.js';
5
- export { encodeSolveCacheEntry, decodeSolveCacheEntry, gunzipEntryBody, type EnvelopeHeader, type DecodedSolveCacheEntry } from './solve-cache-envelope.js';
6
- export { createSolveCacheSingleFlight, type SolveCacheSingleFlight } from './solve-cache-single-flight.js';
7
2
  export { createComputeRateLimiter, DEFAULT_MAX_KEYS, type ComputeRateLimiter, type ComputeRateLimiterConfig, type RateLimitResult } from './rate-limit.js';
8
3
  export { isSafeRemoteDefinitionUrl, assertSafeRemoteDefinitionUrl } from './safe-url.js';
9
- export { transformInputParameter } from './transform-input.js';
10
- export { createClientCache, serverIdentity, type ClientCache, type ClientCacheConfig, type CachedClient, type ResolvedServer, type ServerIdentity } from './client-cache.js';
11
4
  export { createRemoteDefinitionFetcher, readBodyWithCap, type RemoteDefinitionFetcher, type RemoteDefinitionConfig } from './remote-definition.js';
12
- export { runSolvePipeline, adaptEnvelopeToEncoding, COMPUTE_CONTRACT_VERSION, COMPUTE_VERSION_HEADER, type SolvePipelineArgs, type SolvePipelineCacheHook, type SolveOutcome, type SolveEnvelope, type SolvePhaseMetrics, type PipelineInput } from './solve-pipeline.js';
13
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compute/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EACR,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,yBAAyB,EACzB,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACN,4BAA4B,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACpB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACN,wBAAwB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACN,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EACf,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACN,4BAA4B,EAC5B,KAAK,sBAAsB,EAC3B,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACN,wBAAwB,EACxB,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,eAAe,EACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,yBAAyB,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAC;AAEzF,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,EACN,iBAAiB,EACjB,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACN,6BAA6B,EAC7B,eAAe,EACf,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACN,gBAAgB,EAChB,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compute/index.ts"],"names":[],"mappings":"AAaA,OAAO,EACN,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EACR,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,wBAAwB,EACxB,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,eAAe,EACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,yBAAyB,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAC;AAEzF,OAAO,EACN,6BAA6B,EAC7B,eAAe,EACf,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,MAAM,wBAAwB,CAAC"}
@@ -1,14 +1,17 @@
1
- // Compute/solve server building blocks — transport-agnostic, env-injected.
1
+ // HTTP request policy for the compute path — transport-agnostic, env-injected.
2
+ //
3
+ // Admission control (rate limiting), an SSRF guard on operator-supplied URLs, env-derived limits,
4
+ // and the remote-definition fetcher. This is the policy *around* a solve, which is a different job
5
+ // from running one.
6
+ //
7
+ // **The solve core is not here.** Pipeline, L2 result cache, single-flight, and the client /
8
+ // definition-byte caches live in `@selvajs/solve/server`, which owns the solve flow on both sides of
9
+ // the wire. This sub-path deliberately does NOT re-export them: a compat shim was built during the
10
+ // extraction and removed before release, because it left this barrel at 24 exports of which 14 were
11
+ // borrowed — a surface that no longer described what the package does. `@selvajs/server` does not
12
+ // depend on `@selvajs/solve`.
2
13
  export { resolveComputeLimits, readPositiveInt, readNonNegativeInt, readBool } from './limits.js';
3
- export { createDefinitionByteCache } from './definition-byte-cache.js';
4
- export { createMemorySolveResultCache } from './memory-solve-cache.js';
5
- export { deriveSolveCacheInputKey } from './solve-cache-key.js';
6
- export { encodeSolveCacheEntry, decodeSolveCacheEntry, gunzipEntryBody } from './solve-cache-envelope.js';
7
- export { createSolveCacheSingleFlight } from './solve-cache-single-flight.js';
8
14
  export { createComputeRateLimiter, DEFAULT_MAX_KEYS } from './rate-limit.js';
9
15
  export { isSafeRemoteDefinitionUrl, assertSafeRemoteDefinitionUrl } from './safe-url.js';
10
- export { transformInputParameter } from './transform-input.js';
11
- export { createClientCache, serverIdentity } from './client-cache.js';
12
16
  export { createRemoteDefinitionFetcher, readBodyWithCap } from './remote-definition.js';
13
- export { runSolvePipeline, adaptEnvelopeToEncoding, COMPUTE_CONTRACT_VERSION, COMPUTE_VERSION_HEADER } from './solve-pipeline.js';
14
17
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compute/index.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAE3E,OAAO,EACN,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EAGR,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,yBAAyB,EAIzB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACN,4BAA4B,EAG5B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACN,wBAAwB,EAGxB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACN,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EAGf,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACN,4BAA4B,EAE5B,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACN,wBAAwB,EACxB,gBAAgB,EAIhB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,yBAAyB,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAC;AAEzF,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,EACN,iBAAiB,EACjB,cAAc,EAMd,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACN,6BAA6B,EAC7B,eAAe,EAGf,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACN,gBAAgB,EAChB,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,EAOtB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compute/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,kGAAkG;AAClG,mGAAmG;AACnG,oBAAoB;AACpB,EAAE;AACF,6FAA6F;AAC7F,qGAAqG;AACrG,mGAAmG;AACnG,oGAAoG;AACpG,kGAAkG;AAClG,8BAA8B;AAE9B,OAAO,EACN,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EAGR,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,wBAAwB,EACxB,gBAAgB,EAIhB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,yBAAyB,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAC;AAEzF,OAAO,EACN,6BAA6B,EAC7B,eAAe,EAGf,MAAM,wBAAwB,CAAC"}
@@ -49,7 +49,7 @@ export interface ComputeLimits {
49
49
  * serverless platform cap may shoot the request sooner; bumping past those
50
50
  * caps produces 502s, not longer solves.
51
51
  */
52
- maxSolveDurationMs: number;
52
+ solveDeadlineMs: number;
53
53
  /** Fixed-window cap on /api/compute: window length + max requests per window. */
54
54
  rateLimitWindowMs: number;
55
55
  rateLimitMaxRequests: number;
@@ -86,8 +86,15 @@ export interface ComputeLimits {
86
86
  remoteDefinitionMaxBytes: number;
87
87
  /** Deadline on fetching a remote definition (slow-loris protection). */
88
88
  remoteDefinitionFetchTimeoutMs: number;
89
- /** In-process cache TTL for remote-fetched .gh bytes. */
90
- definitionCacheTtlMs: number;
89
+ /**
90
+ * TTL for the in-process cache of .gh bytes fetched from a REMOTE URL.
91
+ *
92
+ * Only the remote path is TTL'd, and the name says so deliberately: the other
93
+ * definition cache is keyed on an immutable version id, where expiry would be a
94
+ * bug (it can only ever throw away valid work). A URL's owner can swap the file
95
+ * underneath us, so that one needs a freshness bound and this is it.
96
+ */
97
+ remoteDefinitionCacheTtlMs: number;
91
98
  /**
92
99
  * Reuse the server's cached definition via a pointer instead of re-uploading
93
100
  * the binary each solve. SAFE only on a compute server that signals a
@@ -109,72 +116,49 @@ export interface ComputeLimits {
109
116
  * honoring the flag. Conservative default off.
110
117
  */
111
118
  computeCacheErroredSolves: boolean;
112
- /**
113
- * Max in-flight solves this app instance sends to ONE compute server
114
- * (forwarded as the SolveScheduler's `maxConcurrent`; excess solves FIFO-queue).
115
- * Size it to the server's `compute.geometry` child count — Rhino.Compute
116
- * spawns N children per VM precisely to run N solves concurrently, and a
117
- * lower value idles that capacity while users queue (audit B6: the scheduler's
118
- * queue-mode default is 1, which serialized ALL solves per server). Multiple
119
- * app instances each apply their own cap.
120
- */
121
- computeMaxConcurrentSolves: number;
122
119
  /**
123
120
  * Backpressure — max solves allowed to WAIT in the per-server FIFO queue (i.e.
124
- * excluding the `computeMaxConcurrentSolves` already in flight). A solve that
125
- * arrives to a full queue is shed immediately (scheduler `QUEUE_FULL`, mapped
126
- * to HTTP 503 + `Retry-After`) instead of piling up unbounded. `0` = unbounded
127
- * (the pre-shedding default): the queue grows without limit, matching prior
128
- * behavior. Size it to roughly 2–3× `computeMaxConcurrentSolves` once the
129
- * Rhino pool's real capacity is known — shedding fast beats a hung request.
121
+ * excluding the in-flight solves, capped at the scheduler's `maxConcurrent`,
122
+ * itself auto-detected from the compute server's active child count). A solve
123
+ * that arrives to a full queue is rejected immediately (scheduler `QUEUE_FULL`,
124
+ * mapped to HTTP 503 + `Retry-After`) instead of piling up unbounded. `0` =
125
+ * unbounded (the pre-backpressure default): the queue grows without limit,
126
+ * matching prior behavior. Size it to roughly 2–3× the compute server's
127
+ * child count once the Rhino pool's real capacity is known — rejecting fast
128
+ * beats a hung request.
130
129
  */
131
130
  computeMaxQueueDepth: number;
132
131
  /**
133
132
  * Backpressure — longest (ms) a solve may sit QUEUED before it starts
134
- * executing; still-waiting past this it's shed (scheduler `QUEUE_TIMEOUT` →
133
+ * executing; still-waiting past this it's rejected (scheduler `QUEUE_TIMEOUT` →
135
134
  * HTTP 503 + `Retry-After`) rather than burning compute on a stale request.
136
135
  * Bounds tail latency: the scheduler's `timeoutMs` clock starts at execution,
137
136
  * so without this a solve's total wait is unbounded and invisible. `0` = no
138
- * queue deadline (the default). A sensible tuned value is ≈ `maxSolveDurationMs`.
137
+ * queue deadline (the default). A sensible tuned value is ≈ `solveDeadlineMs`.
139
138
  */
140
139
  computeQueueWaitMs: number;
141
140
  /**
142
- * Total-byte budget (bytes) for the in-process definition-byte cache keyed on
143
- * immutable version id. A warm entry lets a solve skip `storage.get` entirely,
144
- * and a pointer-known solve never loads bytes at all. `0` disables the cache
145
- * (every solve re-reads storage). Sized as a total-byte LRU because definitions
146
- * span KB→hundreds of MB; env value is MB, converted here.
141
+ * **Definition cache** — total-byte budget (bytes) for the in-process cache of
142
+ * .gh bytes keyed on immutable version id. A warm entry lets a solve skip
143
+ * `storage.get` entirely, and a pointer-known solve never loads bytes at all.
144
+ * `0` disables it (every solve re-reads storage). A total-byte LRU rather than
145
+ * an entry count because definitions span KB→hundreds of MB. Env value is MB.
147
146
  */
148
- computeDefinitionByteCacheBytes: number;
147
+ computeDefinitionCacheBytes: number;
149
148
  /**
150
- * Total-byte budget (bytes) for EACH warm client's in-process solve-response
151
- * cache (the scheduler L1 — the `fromCache` layer in front of Rhino.Compute).
152
- * Responses range KB→100s of MB (`computeResponseMaxBytes` allows 300 MB), so
153
- * the L1's entry-count cap alone can't bound heap; this adds a byte LRU on
154
- * top. Applies PER warm client — worst-case heap is this × the number of
155
- * distinct compute servers kept warm (`maxCachedClients`, default 16), though
156
- * real deployments run 1–2 servers. `0` disables the L1 response cache
157
- * entirely (every solve goes to compute or the L2). Env value is MB.
158
- */
159
- computeResponseCacheBytes: number;
160
- /**
161
- * Default per-definition entry quota for the durable L2 solve cache (H1), used
162
- * when a definition's `solveCacheLimit` is absent (inherit). A definition may
163
- * override this; `0` on the definition turns caching off for it. This global
164
- * default `0` means the L2 is effectively off until an operator sets a quota —
165
- * safe because the memory backend also ships only under
166
- * `SOLVE_CACHE_PROVIDER=memory`. Counts, not bytes: authors think in "how many
167
- * solves to remember"; the byte backstop below is the operator's memory guard.
168
- */
169
- solveCacheDefaultMaxEntries: number;
170
- /**
171
- * Global byte backstop (bytes) across ALL definitions' L2 entries, evicted
172
- * global-LRU regardless of per-definition counts. Entries range KB→100s of MB,
173
- * so a count quota alone can't bound memory. `0` disables the backstop (rely on
174
- * per-definition counts only — not recommended in the memory backend). Env value
175
- * is MB, converted here. On Redis this moves to `maxmemory allkeys-lru`.
149
+ * **Solve cache** — total-byte budget (bytes) for EACH warm client's in-process
150
+ * cache of solve results (the `fromCache` layer in front of Rhino.Compute).
151
+ * Results range KB→100s of MB (`computeResponseMaxBytes` allows 300 MB), so an
152
+ * entry-count cap alone can't bound heap; this adds a byte LRU on top. `0`
153
+ * disables it (every solve goes to compute). Env value is MB.
154
+ *
155
+ * **This is per warm client, so the worst case multiplies.** Selva keeps up to
156
+ * `maxWarmComputeServers` (default 16) distinct compute servers warm, so the true
157
+ * ceiling is this × 16 — 4 GB at the default. Real deployments run 1–2 servers,
158
+ * which is why the default is comfortable, but a deployment that fans out
159
+ * across many servers on a small VPS is exactly the case this knob exists for.
176
160
  */
177
- solveCacheMaxTotalBytes: number;
161
+ computeSolveCacheBytes: number;
178
162
  }
179
163
  /**
180
164
  * Resolve every server-side compute limit from an env map. Pure: the same env
@@ -1 +1 @@
1
- {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/compute/limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAI7D,gFAAgF;AAChF,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAS3D;;;GAGG;AACH,wBAAgB,eAAe,CAC9B,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,OAAc,GACpB,MAAM,CAcR;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CACjC,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,OAAc,GACpB,MAAM,CAcR;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CACvB,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,OAAO,EACjB,MAAM,GAAE,OAAc,GACpB,OAAO,CAaT;AAED,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC7B;;;;;;;;;OASG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iFAAiF;IACjF,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,MAAM,CAAC;IAC7B;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;;;;OAOG;IACH,sBAAsB,EAAE,MAAM,CAAC;IAC/B;;;;;;;;;OASG;IACH,uBAAuB,EAAE,MAAM,CAAC;IAChC,yEAAyE;IACzE,wBAAwB,EAAE,MAAM,CAAC;IACjC,wEAAwE;IACxE,8BAA8B,EAAE,MAAM,CAAC;IACvC,yDAAyD;IACzD,oBAAoB,EAAE,MAAM,CAAC;IAC7B;;;;;;OAMG;IACH,2BAA2B,EAAE,OAAO,CAAC;IACrC;;;;OAIG;IACH,uBAAuB,EAAE,OAAO,CAAC;IACjC;;;;;OAKG;IACH,yBAAyB,EAAE,OAAO,CAAC;IACnC;;;;;;;;OAQG;IACH,0BAA0B,EAAE,MAAM,CAAC;IACnC;;;;;;;;OAQG;IACH,oBAAoB,EAAE,MAAM,CAAC;IAC7B;;;;;;;OAOG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B;;;;;;OAMG;IACH,+BAA+B,EAAE,MAAM,CAAC;IACxC;;;;;;;;;OASG;IACH,yBAAyB,EAAE,MAAM,CAAC;IAClC;;;;;;;;OAQG;IACH,2BAA2B,EAAE,MAAM,CAAC;IACpC;;;;;;OAMG;IACH,uBAAuB,EAAE,MAAM,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,GAAE,OAAc,GAAG,aAAa,CAmD1F"}
1
+ {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/compute/limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAI7D,gFAAgF;AAChF,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAS3D;;;GAGG;AACH,wBAAgB,eAAe,CAC9B,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,OAAc,GACpB,MAAM,CAcR;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CACjC,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,OAAc,GACpB,MAAM,CAcR;AAwBD;;;GAGG;AACH,wBAAgB,QAAQ,CACvB,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,OAAO,EACjB,MAAM,GAAE,OAAc,GACpB,OAAO,CAaT;AAED,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC7B;;;;;;;;;OASG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB,iFAAiF;IACjF,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,MAAM,CAAC;IAC7B;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;;;;OAOG;IACH,sBAAsB,EAAE,MAAM,CAAC;IAC/B;;;;;;;;;OASG;IACH,uBAAuB,EAAE,MAAM,CAAC;IAChC,yEAAyE;IACzE,wBAAwB,EAAE,MAAM,CAAC;IACjC,wEAAwE;IACxE,8BAA8B,EAAE,MAAM,CAAC;IACvC;;;;;;;OAOG;IACH,0BAA0B,EAAE,MAAM,CAAC;IACnC;;;;;;OAMG;IACH,2BAA2B,EAAE,OAAO,CAAC;IACrC;;;;OAIG;IACH,uBAAuB,EAAE,OAAO,CAAC;IACjC;;;;;OAKG;IACH,yBAAyB,EAAE,OAAO,CAAC;IACnC;;;;;;;;;;OAUG;IACH,oBAAoB,EAAE,MAAM,CAAC;IAC7B;;;;;;;OAOG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B;;;;;;OAMG;IACH,2BAA2B,EAAE,MAAM,CAAC;IACpC;;;;;;;;;;;;OAYG;IACH,sBAAsB,EAAE,MAAM,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,GAAE,OAAc,GAAG,aAAa,CAoD1F"}
@@ -67,6 +67,28 @@ export function readNonNegativeInt(env, name, fallback, logger = noop) {
67
67
  }
68
68
  return Math.floor(parsed);
69
69
  }
70
+ /**
71
+ * Read a renamed variable, honouring its previous name for one minor version.
72
+ *
73
+ * Returns the env map's value under `name`, or — when that is unset and the old
74
+ * name is present — the old value plus a warning naming both. Setting both is not
75
+ * an error: the new name wins silently, which is what an operator mid-migration
76
+ * expects.
77
+ *
78
+ * This exists so a rename is never a silent behaviour change. A var that quietly
79
+ * stops being read looks exactly like a var that is working, and the failure only
80
+ * surfaces as a memory number nobody is watching.
81
+ */
82
+ function readRenamed(env, name, oldName, logger) {
83
+ if (env[name] !== undefined || env[oldName] === undefined)
84
+ return env;
85
+ logger.warn('Deprecated env var: rename it', {
86
+ component: 'selva',
87
+ envVar: oldName,
88
+ renamedTo: name
89
+ });
90
+ return { ...env, [name]: env[oldName] };
91
+ }
70
92
  /**
71
93
  * Parse a boolean env flag. Accepts `true/1/yes/on` (case-insensitive) as true
72
94
  * and `false/0/no/off` as false; any other / absent value falls back.
@@ -94,9 +116,17 @@ export function readBool(env, name, fallback, logger = noop) {
94
116
  * this once at its composition root with `$env/dynamic/private`.
95
117
  */
96
118
  export function resolveComputeLimits(env, logger = noop) {
119
+ // Renamed 2026-07 so each cache var says what it HOLDS. Old names still read,
120
+ // with a boot warning; drop this shim one minor version on.
121
+ env = readRenamed(env, 'COMPUTE_DEFINITION_CACHE_MB', 'COMPUTE_DEFINITION_BYTE_CACHE_MB', logger);
122
+ env = readRenamed(env, 'COMPUTE_SOLVE_CACHE_MB', 'COMPUTE_RESPONSE_CACHE_MB', logger);
123
+ env = readRenamed(env, 'REMOTE_DEFINITION_CACHE_TTL_MS', 'DEFINITION_CACHE_TTL_MS', logger);
124
+ // Renamed 2026-08 into the COMPUTE_* namespace it belongs to, and to say what it
125
+ // bounds — one solve — rather than a vague "duration".
126
+ env = readRenamed(env, 'COMPUTE_SOLVE_DEADLINE_MS', 'MAX_SOLVE_DURATION_MS', logger);
97
127
  const maxGhFileSize = readPositiveInt(env, 'MAX_GH_FILE_SIZE_BYTES', 50 * MB, logger);
98
128
  return {
99
- maxSolveDurationMs: readPositiveInt(env, 'MAX_SOLVE_DURATION_MS', 100_000, logger),
129
+ solveDeadlineMs: readPositiveInt(env, 'COMPUTE_SOLVE_DEADLINE_MS', 100_000, logger),
100
130
  rateLimitWindowMs: readPositiveInt(env, 'COMPUTE_RATE_LIMIT_WINDOW_MS', 100_000, logger),
101
131
  rateLimitMaxRequests: readPositiveInt(env, 'COMPUTE_RATE_LIMIT_MAX', 120, logger),
102
132
  maxGhFileSize,
@@ -106,13 +136,11 @@ export function resolveComputeLimits(env, logger = noop) {
106
136
  // Tracks the upload cap so a remote URL can't smuggle a larger file.
107
137
  remoteDefinitionMaxBytes: maxGhFileSize,
108
138
  remoteDefinitionFetchTimeoutMs: readPositiveInt(env, 'REMOTE_DEFINITION_FETCH_TIMEOUT_MS', 30_000, logger),
109
- definitionCacheTtlMs: readPositiveInt(env, 'DEFINITION_CACHE_TTL_MS', 5 * 60 * 1000, logger),
139
+ remoteDefinitionCacheTtlMs: readPositiveInt(env, 'REMOTE_DEFINITION_CACHE_TTL_MS', 5 * 60 * 1000, logger),
110
140
  computeReuseDefinitionCache: readBool(env, 'COMPUTE_REUSE_DEFINITION_CACHE', true, logger),
111
141
  computeServerCachesolve: readBool(env, 'COMPUTE_SERVER_CACHESOLVE', true, logger),
112
142
  computeCacheErroredSolves: readBool(env, 'COMPUTE_CACHE_ERRORED_SOLVES', false, logger),
113
- // 4 matches rhino.compute's default --childcount; tune to the actual VM.
114
- computeMaxConcurrentSolves: readPositiveInt(env, 'COMPUTE_MAX_CONCURRENT', 4, logger),
115
- // Both 0 (unbounded / no deadline) by default — nothing sheds until an
143
+ // Both 0 (unbounded / no deadline) by default — nothing is rejected until an
116
144
  // operator who's measured their pool opts in. readNonNegativeInt so `0` is a
117
145
  // valid "disabled" value, not treated as invalid.
118
146
  computeMaxQueueDepth: readNonNegativeInt(env, 'COMPUTE_MAX_QUEUE_DEPTH', 0, logger),
@@ -120,17 +148,12 @@ export function resolveComputeLimits(env, logger = noop) {
120
148
  // Env is MB; 0 disables. Default 256 MB holds a handful of typical
121
149
  // definitions warm without pinning gigabytes. readNonNegativeInt so `0`
122
150
  // (disable) is honored rather than treated as invalid.
123
- computeDefinitionByteCacheBytes: readNonNegativeInt(env, 'COMPUTE_DEFINITION_BYTE_CACHE_MB', 256, logger) * MB,
124
- // Per-warm-client L1 response cache budget. Env is MB; 0 disables the L1.
125
- // Default 256 MB: enough to keep a scrub session's recent responses warm
126
- // without letting 20 × 300 MB worst-case entries pin gigabytes (audit C2).
127
- computeResponseCacheBytes: readNonNegativeInt(env, 'COMPUTE_RESPONSE_CACHE_MB', 256, logger) * MB,
128
- // L2 durable solve cache (H1). Default per-definition quota 0 = inherit-to-off
129
- // until an operator opts in; the memory backend also only mounts under
130
- // SOLVE_CACHE_PROVIDER=memory, so nothing caches by accident. Byte backstop
131
- // 512 MB caps total heap use across all definitions (env is MB).
132
- solveCacheDefaultMaxEntries: readNonNegativeInt(env, 'SOLVE_CACHE_DEFAULT_MAX_ENTRIES', 0, logger),
133
- solveCacheMaxTotalBytes: readNonNegativeInt(env, 'SOLVE_CACHE_MAX_TOTAL_MB', 512, logger) * MB
151
+ computeDefinitionCacheBytes: readNonNegativeInt(env, 'COMPUTE_DEFINITION_CACHE_MB', 256, logger) * MB,
152
+ // Per-warm-client solve cache budget. Env is MB; 0 disables it. Default
153
+ // 256 MB: enough to keep a scrub session's recent solves warm without
154
+ // letting worst-case entries pin gigabytes (audit C2). PER warm client —
155
+ // see the field doc for the ×16 worst case.
156
+ computeSolveCacheBytes: readNonNegativeInt(env, 'COMPUTE_SOLVE_CACHE_MB', 256, logger) * MB
134
157
  };
135
158
  }
136
159
  //# sourceMappingURL=limits.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/compute/limits.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/compute/limits.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@selvajs/server",
3
- "version": "0.2.1",
4
- "description": "Transport-agnostic compute/solve/definitions server building blocks for apps built on the Selva engine (limits, rate limiting, SSRF guard, input transform, solve pipeline, definition service + render loader)",
3
+ "version": "1.0.0-beta.1",
4
+ "description": "Transport-agnostic server building blocks for apps built on the Selva engine (limits, rate limiting, SSRF guard, remote-definition fetch, definition service + render loader); the solve core lives in @selvajs/solve/server",
5
5
  "license": "MIT",
6
6
  "author": "VektorNode",
7
7
  "homepage": "https://github.com/VektorNode/selva#readme",
@@ -25,13 +25,7 @@
25
25
  "node": ">=22.0.0"
26
26
  },
27
27
  "type": "module",
28
- "main": "./dist/index.js",
29
- "types": "./dist/index.d.ts",
30
28
  "exports": {
31
- ".": {
32
- "types": "./dist/index.d.ts",
33
- "import": "./dist/index.js"
34
- },
35
29
  "./compute": {
36
30
  "types": "./dist/compute/index.d.ts",
37
31
  "import": "./dist/compute/index.js"
@@ -76,7 +70,7 @@
76
70
  "!**/*.spec.*"
77
71
  ],
78
72
  "dependencies": {
79
- "@selvajs/compute": "^3.1.0",
73
+ "@selvajs/compute": "^4.0.0-beta.1",
80
74
  "@selvajs/platform": "^0.16.0",
81
75
  "@selvajs/schemas": "^4.7.0"
82
76
  },
@@ -93,9 +87,9 @@
93
87
  }
94
88
  },
95
89
  "devDependencies": {
96
- "@types/node": "^22.20.1",
97
- "typescript": "^6.0.3",
98
- "vitest": "^3.2.7"
90
+ "@types/node": "^26.1.2",
91
+ "typescript": "~6.0.3",
92
+ "vitest": "^4.1.10"
99
93
  },
100
94
  "scripts": {
101
95
  "build": "tsc",
@@ -1,135 +0,0 @@
1
- import { GrasshopperClient, type SolveScheduler } from '@selvajs/compute';
2
- /**
3
- * Opaque identity of a compute server. Callers never construct the branded
4
- * string directly — they pass a resolved server config to `serverIdentity()`.
5
- * Keeping this opaque from day one (ADR 0004 D1) means "a server is now a pool
6
- * of URLs behind one id" stays an additive, non-breaking change.
7
- */
8
- export type ServerIdentity = string & {
9
- readonly __brand: 'ServerIdentity';
10
- };
11
- /** Minimal resolved-server shape the cache needs (a subset of the app's `ComputeServerConfig`). */
12
- export interface ResolvedServer {
13
- /** Stable identity — the cache key. */
14
- id: string;
15
- /** Base URL of the Rhino.Compute instance (a resolution detail of `id`). */
16
- serverUrl: string;
17
- /** Sent as `RhinoComputeKey`. */
18
- apiKey?: string;
19
- }
20
- /** Derive the opaque cache identity from a resolved server. Identity is the `id`. */
21
- export declare function serverIdentity(server: Pick<ResolvedServer, 'id'>): ServerIdentity;
22
- /**
23
- * Per-request telemetry holders read by the caller right after a solve resolves
24
- * to build a Server-Timing header. Populated by the client/scheduler callbacks.
25
- */
26
- export interface CachedClient {
27
- client: GrasshopperClient;
28
- scheduler: SolveScheduler;
29
- /**
30
- * Last decode/solve/encode reported by the compute server (Server-Timing),
31
- * written by onServerTiming. `seq` increments on every write. The scheduler
32
- * runs up to `maxConcurrentSolves` solves at once, so `last` alone can't be
33
- * trusted per-request: callers snapshot `seq` before their solve and
34
- * attribute `last` only when exactly one write happened since (necessarily
35
- * theirs — see the guard in `runSolvePipeline`), dropping the segment
36
- * otherwise instead of misattributing another request's timing.
37
- */
38
- rhinoTiming: {
39
- last: {
40
- decode: number;
41
- solve: number;
42
- encode: number;
43
- } | null;
44
- seq: number;
45
- };
46
- /**
47
- * Last solve's cache verdicts from the scheduler's onSettle: whether the
48
- * in-process response cache served it (no compute call) and whether the
49
- * definition had to be re-uploaded. `seq` increments on EVERY settle
50
- * (success or error); `last` is only written on success. Same
51
- * snapshot-and-attribute pattern as rhinoTiming — onSettle always fires
52
- * before the corresponding `scheduler.solve()` promise resolves, so at the
53
- * caller's read point its own settle is included in `seq`.
54
- */
55
- solveMeta: {
56
- last: {
57
- fromCache: boolean;
58
- definitionReuploaded?: boolean;
59
- } | null;
60
- seq: number;
61
- };
62
- }
63
- /**
64
- * Config injected by the consuming app. Everything that used to be read from
65
- * `$env` / `computeLimits` in the app module is passed in, so this module stays
66
- * env-agnostic and testable.
67
- */
68
- export interface ClientCacheConfig {
69
- /** Per-solve timeout forwarded to the scheduler (`ComputeLimits.maxSolveDurationMs`). */
70
- maxSolveDurationMs: number;
71
- /** Ask Rhino.Compute to cache solve results and return them on identical repeats. */
72
- cachesolve: boolean;
73
- /** Also cache solves that reported GH errors (only meaningful with `cachesolve`). */
74
- cacheerroredsolves: boolean;
75
- /** Reference large definitions by server cache key (pointer) instead of re-uploading. */
76
- reuseServerDefinitionCache: boolean;
77
- /**
78
- * Max in-flight solves per compute server (scheduler `maxConcurrent`; excess
79
- * FIFO-queues). Size to the server's `compute.geometry` child count
80
- * (`ComputeLimits.computeMaxConcurrentSolves`).
81
- */
82
- maxConcurrentSolves: number;
83
- /**
84
- * Backpressure — max solves that may WAIT in the FIFO queue (excludes the
85
- * in-flight `maxConcurrentSolves`). `0` = unbounded (`ComputeLimits.
86
- * computeMaxQueueDepth`); a full queue sheds new solves with `QUEUE_FULL`.
87
- */
88
- maxQueueDepth: number;
89
- /**
90
- * Backpressure — max ms a solve may sit queued before executing; `0` = no
91
- * deadline (`ComputeLimits.computeQueueWaitMs`). A too-long wait sheds with
92
- * `QUEUE_TIMEOUT`.
93
- */
94
- queueWaitMs: number;
95
- /**
96
- * Byte budget for this client's in-process solve-response cache (scheduler
97
- * L1), evicted LRU alongside its entry-count cap. Applies per warm client —
98
- * total worst-case heap is this × `maxCachedClients`. `0` disables the L1
99
- * response cache entirely (`ComputeLimits.computeResponseCacheBytes`).
100
- */
101
- responseCacheMaxBytes: number;
102
- /** Concise cache/timing logs. When false, `onDebugLog` is never invoked. */
103
- debug: boolean;
104
- /** VERBOSE lib-level logging (full solve request/response incl. geometry). */
105
- debugVerbose: boolean;
106
- /** Max distinct warm clients before the LRU evicts the oldest. Default 16. */
107
- maxCachedClients?: number;
108
- /** Sink for the concise debug lines (the app wires `console.log`). */
109
- onDebugLog?: (message: string) => void;
110
- }
111
- export interface ClientCache {
112
- /**
113
- * Get (or create) the warm client + scheduler for a resolved compute server,
114
- * keyed by its `id`. `definitionGuid`, when present, is stamped as the
115
- * `X-Selva-Definition` header on this client's outbound solve/IO requests —
116
- * inert routing/telemetry metadata until a pool router exists (ADR 0004 D2).
117
- */
118
- getClient(server: ResolvedServer, opts?: {
119
- definitionGuid?: string;
120
- }): Promise<CachedClient>;
121
- /**
122
- * Dispose and drop the warm client for `id` (or a `ServerIdentity`). Called by
123
- * the config-write path when a server's URL/key rotates so the next request
124
- * rebuilds against fresh connection details (ADR 0004 Consequences).
125
- */
126
- evict(id: string | ServerIdentity): void;
127
- /** Dispose every warm client. Test seam / shutdown hook. */
128
- disposeAll(): void;
129
- }
130
- /**
131
- * Build a per-server warm-client cache. Each cache owns its own LRU `Map` keyed
132
- * on server `id`.
133
- */
134
- export declare function createClientCache(config: ClientCacheConfig): ClientCache;
135
- //# sourceMappingURL=client-cache.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-cache.d.ts","sourceRoot":"","sources":["../../src/compute/client-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,iBAAiB,EAGjB,KAAK,cAAc,EACnB,MAAM,kBAAkB,CAAC;AAyB1B;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,CAAC;AAE7E,mGAAmG;AACnG,MAAM,WAAW,cAAc;IAC9B,uCAAuC;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qFAAqF;AACrF,wBAAgB,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,GAAG,cAAc,CAEjF;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,SAAS,EAAE,cAAc,CAAC;IAC1B;;;;;;;;OAQG;IACH,WAAW,EAAE;QAAE,IAAI,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7F;;;;;;;;OAQG;IACH,SAAS,EAAE;QACV,IAAI,EAAE;YAAE,SAAS,EAAE,OAAO,CAAC;YAAC,oBAAoB,CAAC,EAAE,OAAO,CAAA;SAAE,GAAG,IAAI,CAAC;QACpE,GAAG,EAAE,MAAM,CAAC;KACZ,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IACjC,yFAAyF;IACzF,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qFAAqF;IACrF,UAAU,EAAE,OAAO,CAAC;IACpB,qFAAqF;IACrF,kBAAkB,EAAE,OAAO,CAAC;IAC5B,yFAAyF;IACzF,0BAA0B,EAAE,OAAO,CAAC;IACpC;;;;OAIG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,qBAAqB,EAAE,MAAM,CAAC;IAC9B,4EAA4E;IAC5E,KAAK,EAAE,OAAO,CAAC;IACf,8EAA8E;IAC9E,YAAY,EAAE,OAAO,CAAC;IACtB,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sEAAsE;IACtE,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,WAAW;IAC3B;;;;;OAKG;IACH,SAAS,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7F;;;;OAIG;IACH,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAAC;IACzC,4DAA4D;IAC5D,UAAU,IAAI,IAAI,CAAC;CACnB;AAID;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,WAAW,CAuLxE"}