@selvajs/server 0.2.0 → 1.0.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/README.md +23 -16
- package/dist/compute/index.d.ts +0 -8
- package/dist/compute/index.d.ts.map +1 -1
- package/dist/compute/index.js +12 -9
- package/dist/compute/index.js.map +1 -1
- package/dist/compute/limits.d.ts +27 -35
- package/dist/compute/limits.d.ts.map +1 -1
- package/dist/compute/limits.js +34 -12
- package/dist/compute/limits.js.map +1 -1
- package/package.json +7 -13
- package/dist/compute/client-cache.d.ts +0 -135
- package/dist/compute/client-cache.d.ts.map +0 -1
- package/dist/compute/client-cache.js +0 -176
- package/dist/compute/client-cache.js.map +0 -1
- package/dist/compute/definition-byte-cache.d.ts +0 -85
- package/dist/compute/definition-byte-cache.d.ts.map +0 -1
- package/dist/compute/definition-byte-cache.js +0 -110
- package/dist/compute/definition-byte-cache.js.map +0 -1
- package/dist/compute/memory-solve-cache.d.ts +0 -58
- package/dist/compute/memory-solve-cache.d.ts.map +0 -1
- package/dist/compute/memory-solve-cache.js +0 -0
- package/dist/compute/memory-solve-cache.js.map +0 -1
- package/dist/compute/solve-cache-envelope.d.ts +0 -53
- package/dist/compute/solve-cache-envelope.d.ts.map +0 -1
- package/dist/compute/solve-cache-envelope.js +0 -66
- package/dist/compute/solve-cache-envelope.js.map +0 -1
- package/dist/compute/solve-cache-key.d.ts +0 -64
- package/dist/compute/solve-cache-key.d.ts.map +0 -1
- package/dist/compute/solve-cache-key.js +0 -51
- package/dist/compute/solve-cache-key.js.map +0 -1
- package/dist/compute/solve-cache-single-flight.d.ts +0 -42
- package/dist/compute/solve-cache-single-flight.d.ts.map +0 -1
- package/dist/compute/solve-cache-single-flight.js +0 -50
- package/dist/compute/solve-cache-single-flight.js.map +0 -1
- package/dist/compute/solve-pipeline.d.ts +0 -221
- package/dist/compute/solve-pipeline.d.ts.map +0 -1
- package/dist/compute/solve-pipeline.js +0 -419
- package/dist/compute/solve-pipeline.js.map +0 -1
- package/dist/compute/transform-input.d.ts +0 -20
- package/dist/compute/transform-input.d.ts.map +0 -1
- package/dist/compute/transform-input.js +0 -62
- package/dist/compute/transform-input.js.map +0 -1
- package/dist/index.d.ts +0 -27
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -27
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
# @selvajs/server
|
|
2
2
|
|
|
3
|
-
Transport-agnostic
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
| `@selvajs/server/
|
|
30
|
-
| `@selvajs/server/
|
|
31
|
-
| `@selvajs/server/
|
|
32
|
-
| `@selvajs/server/
|
|
33
|
-
| `@selvajs/server/
|
|
34
|
-
| `@selvajs/server/
|
|
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.
|
package/dist/compute/index.d.ts
CHANGED
|
@@ -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":"
|
|
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"}
|
package/dist/compute/index.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
//
|
|
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,
|
|
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"}
|
package/dist/compute/limits.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
90
|
-
|
|
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
|
|
@@ -139,42 +146,27 @@ export interface ComputeLimits {
|
|
|
139
146
|
*/
|
|
140
147
|
computeQueueWaitMs: number;
|
|
141
148
|
/**
|
|
142
|
-
*
|
|
143
|
-
* immutable version id. A warm entry lets a solve skip
|
|
144
|
-
* and a pointer-known solve never loads bytes at all.
|
|
145
|
-
* (every solve re-reads storage).
|
|
146
|
-
* span KB→hundreds of MB
|
|
147
|
-
*/
|
|
148
|
-
computeDefinitionByteCacheBytes: number;
|
|
149
|
-
/**
|
|
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.
|
|
149
|
+
* **Definition cache** — total-byte budget (bytes) for the in-process cache of
|
|
150
|
+
* .gh bytes keyed on immutable version id. A warm entry lets a solve skip
|
|
151
|
+
* `storage.get` entirely, and a pointer-known solve never loads bytes at all.
|
|
152
|
+
* `0` disables it (every solve re-reads storage). A total-byte LRU rather than
|
|
153
|
+
* an entry count because definitions span KB→hundreds of MB. Env value is MB.
|
|
168
154
|
*/
|
|
169
|
-
|
|
155
|
+
computeDefinitionCacheBytes: number;
|
|
170
156
|
/**
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
157
|
+
* **Solve cache** — total-byte budget (bytes) for EACH warm client's in-process
|
|
158
|
+
* cache of solve results (the `fromCache` layer in front of Rhino.Compute).
|
|
159
|
+
* Results range KB→100s of MB (`computeResponseMaxBytes` allows 300 MB), so an
|
|
160
|
+
* entry-count cap alone can't bound heap; this adds a byte LRU on top. `0`
|
|
161
|
+
* disables it (every solve goes to compute). Env value is MB.
|
|
162
|
+
*
|
|
163
|
+
* **This is per warm client, so the worst case multiplies.** Selva keeps up to
|
|
164
|
+
* `maxCachedClients` (default 16) distinct compute servers warm, so the true
|
|
165
|
+
* ceiling is this × 16 — 4 GB at the default. Real deployments run 1–2 servers,
|
|
166
|
+
* which is why the default is comfortable, but a deployment that fans out
|
|
167
|
+
* across many servers on a small VPS is exactly the case this knob exists for.
|
|
176
168
|
*/
|
|
177
|
-
|
|
169
|
+
computeSolveCacheBytes: number;
|
|
178
170
|
}
|
|
179
171
|
/**
|
|
180
172
|
* 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;
|
|
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,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;;;;;;;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;;;;;;;;OAQG;IACH,0BAA0B,EAAE,MAAM,CAAC;IACnC;;;;;;;;OAQG;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,CAmD1F"}
|
package/dist/compute/limits.js
CHANGED
|
@@ -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,6 +116,11 @@ 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);
|
|
97
124
|
const maxGhFileSize = readPositiveInt(env, 'MAX_GH_FILE_SIZE_BYTES', 50 * MB, logger);
|
|
98
125
|
return {
|
|
99
126
|
maxSolveDurationMs: readPositiveInt(env, 'MAX_SOLVE_DURATION_MS', 100_000, logger),
|
|
@@ -106,7 +133,7 @@ export function resolveComputeLimits(env, logger = noop) {
|
|
|
106
133
|
// Tracks the upload cap so a remote URL can't smuggle a larger file.
|
|
107
134
|
remoteDefinitionMaxBytes: maxGhFileSize,
|
|
108
135
|
remoteDefinitionFetchTimeoutMs: readPositiveInt(env, 'REMOTE_DEFINITION_FETCH_TIMEOUT_MS', 30_000, logger),
|
|
109
|
-
|
|
136
|
+
remoteDefinitionCacheTtlMs: readPositiveInt(env, 'REMOTE_DEFINITION_CACHE_TTL_MS', 5 * 60 * 1000, logger),
|
|
110
137
|
computeReuseDefinitionCache: readBool(env, 'COMPUTE_REUSE_DEFINITION_CACHE', true, logger),
|
|
111
138
|
computeServerCachesolve: readBool(env, 'COMPUTE_SERVER_CACHESOLVE', true, logger),
|
|
112
139
|
computeCacheErroredSolves: readBool(env, 'COMPUTE_CACHE_ERRORED_SOLVES', false, logger),
|
|
@@ -120,17 +147,12 @@ export function resolveComputeLimits(env, logger = noop) {
|
|
|
120
147
|
// Env is MB; 0 disables. Default 256 MB holds a handful of typical
|
|
121
148
|
// definitions warm without pinning gigabytes. readNonNegativeInt so `0`
|
|
122
149
|
// (disable) is honored rather than treated as invalid.
|
|
123
|
-
|
|
124
|
-
// Per-warm-client
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
|
|
128
|
-
|
|
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
|
|
150
|
+
computeDefinitionCacheBytes: readNonNegativeInt(env, 'COMPUTE_DEFINITION_CACHE_MB', 256, logger) * MB,
|
|
151
|
+
// Per-warm-client solve cache budget. Env is MB; 0 disables it. Default
|
|
152
|
+
// 256 MB: enough to keep a scrub session's recent solves warm without
|
|
153
|
+
// letting worst-case entries pin gigabytes (audit C2). PER warm client —
|
|
154
|
+
// see the field doc for the ×16 worst case.
|
|
155
|
+
computeSolveCacheBytes: readNonNegativeInt(env, 'COMPUTE_SOLVE_CACHE_MB', 256, logger) * MB
|
|
134
156
|
};
|
|
135
157
|
}
|
|
136
158
|
//# sourceMappingURL=limits.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/compute/limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,UAAU,EAAgB,MAAM,mBAAmB,CAAC;AAE7D,MAAM,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAKvB;;;;GAIG;AACH,MAAM,IAAI,GAAG,IAAI,UAAU,EAAE,CAAC;AAE9B;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC9B,GAAc,EACd,IAAY,EACZ,QAAgB,EAChB,SAAkB,IAAI;IAEtB,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IACtB,IAAI,CAAC,GAAG;QAAE,OAAO,QAAQ,CAAC;IAC1B,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,4CAA4C,EAAE;YACzD,SAAS,EAAE,OAAO;YAClB,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,GAAG;YACV,QAAQ;SACR,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC;IACjB,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CACjC,GAAc,EACd,IAAY,EACZ,QAAgB,EAChB,SAAkB,IAAI;IAEtB,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IACtB,IAAI,CAAC,GAAG;QAAE,OAAO,QAAQ,CAAC;IAC1B,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,4CAA4C,EAAE;YACzD,SAAS,EAAE,OAAO;YAClB,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,GAAG;YACV,QAAQ;SACR,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC;IACjB,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CACvB,GAAc,EACd,IAAY,EACZ,QAAiB,EACjB,SAAkB,IAAI;IAEtB,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IACtB,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,QAAQ,CAAC;IAC/C,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACnC,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACxE,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IAC1E,MAAM,CAAC,IAAI,CAAC,4CAA4C,EAAE;QACzD,SAAS,EAAE,OAAO;QAClB,MAAM,EAAE,IAAI;QACZ,KAAK,EAAE,GAAG;QACV,QAAQ;KACR,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC;AACjB,CAAC;
|
|
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.
|
|
4
|
-
"description": "Transport-agnostic
|
|
3
|
+
"version": "1.0.0-beta.0",
|
|
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,8 +70,8 @@
|
|
|
76
70
|
"!**/*.spec.*"
|
|
77
71
|
],
|
|
78
72
|
"dependencies": {
|
|
79
|
-
"@selvajs/compute": "^
|
|
80
|
-
"@selvajs/platform": "^0.
|
|
73
|
+
"@selvajs/compute": "^4.0.0-beta.0",
|
|
74
|
+
"@selvajs/platform": "^0.16.0",
|
|
81
75
|
"@selvajs/schemas": "^4.7.0"
|
|
82
76
|
},
|
|
83
77
|
"peerDependencies": {
|
|
@@ -93,9 +87,9 @@
|
|
|
93
87
|
}
|
|
94
88
|
},
|
|
95
89
|
"devDependencies": {
|
|
96
|
-
"@types/node": "^
|
|
97
|
-
"typescript": "
|
|
98
|
-
"vitest": "^
|
|
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"}
|