@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.
- 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 +38 -54
- package/dist/compute/limits.d.ts.map +1 -1
- package/dist/compute/limits.js +39 -16
- package/dist/compute/limits.js.map +1 -1
- package/package.json +6 -12
- 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
|
@@ -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
|
-
|
|
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
|
-
/**
|
|
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
|
|
@@ -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
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
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
|
|
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 ≈ `
|
|
137
|
+
* queue deadline (the default). A sensible tuned value is ≈ `solveDeadlineMs`.
|
|
139
138
|
*/
|
|
140
139
|
computeQueueWaitMs: number;
|
|
141
140
|
/**
|
|
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
|
|
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
|
-
|
|
147
|
+
computeDefinitionCacheBytes: number;
|
|
149
148
|
/**
|
|
150
|
-
*
|
|
151
|
-
* cache
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
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;
|
|
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"}
|
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,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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
|
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":"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.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": "^
|
|
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": "^
|
|
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"}
|