@selvajs/solve 0.2.0-beta.4 → 1.0.0-beta.10
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 +3 -2
- package/dist/client.cjs +1 -1
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +196 -164
- package/dist/client.d.ts +196 -164
- package/dist/client.js +1 -1
- package/dist/client.js.map +1 -1
- package/dist/server.cjs +1 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +291 -307
- package/dist/server.d.ts +291 -307
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/shared.cjs +0 -1
- package/dist/shared.d.cts +3 -3
- package/dist/shared.d.ts +3 -3
- package/dist/shared.js +1 -1
- package/dist/solve-fn-lsWj61m5.d.cts +48 -0
- package/dist/solve-fn-lsWj61m5.d.ts +48 -0
- package/dist/{solve-input-CqYtetTA.d.ts → solve-input-BDv_JdvS.d.cts} +8 -7
- package/dist/{solve-input-CqYtetTA.d.cts → solve-input-BDv_JdvS.d.ts} +8 -7
- package/package.json +17 -11
- package/dist/shared.cjs.map +0 -1
- package/dist/shared.js.map +0 -1
- package/dist/solve-fn-0hPOtZVD.d.cts +0 -37
- package/dist/solve-fn-0hPOtZVD.d.ts +0 -37
package/dist/server.d.cts
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { t as SolveInput } from "./solve-input-BDv_JdvS.cjs";
|
|
2
|
+
import { SchemaInput } from "@selvajs/schemas";
|
|
3
|
+
import { DataTree, DefinitionRef as DefinitionRef$1, GrasshopperClient, GrasshopperComputeResponse, InputParam, SolveScheduler } from "@selvajs/compute/grasshopper";
|
|
4
|
+
import { DefinitionRef, SolveDefinition, SolveDefinition as SolveDefinition$1, isDefinitionRef } from "@selvajs/compute/core";
|
|
5
|
+
import { ILogger } from "@selvajs/platform";
|
|
6
|
+
//#region src/server/client-cache.d.ts
|
|
7
7
|
/**
|
|
8
8
|
* Opaque identity of a compute server — construct only via `serverIdentity()`.
|
|
9
9
|
* Kept opaque so "a server is now a pool of URLs behind one id" stays additive.
|
|
10
10
|
*/
|
|
11
11
|
type ServerIdentity = string & {
|
|
12
|
-
|
|
12
|
+
readonly __brand: 'ServerIdentity';
|
|
13
13
|
};
|
|
14
14
|
/** Minimal resolved-server shape the cache needs (a subset of the app's `ComputeServerConfig`). */
|
|
15
15
|
interface ResolvedServer {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
id: string;
|
|
17
|
+
serverUrl: string;
|
|
18
|
+
/** Sent as the `RhinoComputeKey` header. */
|
|
19
|
+
apiKey?: string;
|
|
20
20
|
}
|
|
21
21
|
declare function serverIdentity(server: Pick<ResolvedServer, 'id'>): ServerIdentity;
|
|
22
22
|
interface CachedClient {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
23
|
+
client: GrasshopperClient;
|
|
24
|
+
scheduler: SolveScheduler;
|
|
25
|
+
/**
|
|
26
|
+
* Last Server-Timing decode/solve/encode, written by `onServerTiming`. The
|
|
27
|
+
* scheduler runs up to `maxConcurrent` solves at once, so `last` alone can't
|
|
28
|
+
* be trusted per-request: callers snapshot `seq` before their solve and only
|
|
29
|
+
* attribute `last` to themselves if exactly one write happened since (see
|
|
30
|
+
* the guard in `runSolvePipeline`), dropping it otherwise rather than risk
|
|
31
|
+
* misattributing another request's timing.
|
|
32
|
+
*/
|
|
33
|
+
rhinoTiming: {
|
|
34
|
+
last: {
|
|
35
|
+
decode: number;
|
|
36
|
+
solve: number;
|
|
37
|
+
encode: number;
|
|
38
|
+
} | null;
|
|
39
|
+
seq: number;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Same snapshot-and-attribute pattern as `rhinoTiming`, for the scheduler's
|
|
43
|
+
* `onSettle` cache verdict. `seq` increments on every settle (success or
|
|
44
|
+
* error); `last` is written only on success.
|
|
45
|
+
*/
|
|
46
|
+
solveMeta: {
|
|
47
|
+
last: {
|
|
48
|
+
fromCache: boolean;
|
|
49
|
+
definitionReuploaded?: boolean;
|
|
50
|
+
} | null;
|
|
51
|
+
seq: number;
|
|
52
|
+
};
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
55
|
* Debug verbosity: `false` is silent, `true` gives concise cache/timing logs
|
|
@@ -59,74 +59,75 @@ interface CachedClient {
|
|
|
59
59
|
type ClientCacheDebug = boolean | 'verbose';
|
|
60
60
|
/** Config injected by the consuming app, so this module stays env-agnostic and testable. */
|
|
61
61
|
interface ClientCacheConfig {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
62
|
+
/** Per-solve timeout forwarded to the scheduler (`ComputeLimits.solveDeadlineMs`). */
|
|
63
|
+
solveDeadlineMs: number;
|
|
64
|
+
cachesolve: boolean;
|
|
65
|
+
/** Only meaningful with `cachesolve`. */
|
|
66
|
+
cacheerroredsolves: boolean;
|
|
67
|
+
/** Reference large definitions by server cache key instead of re-uploading. */
|
|
68
|
+
reuseServerDefinitionCache: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Max solves that may wait in the FIFO queue, excluding in-flight solves
|
|
71
|
+
* (capped at `maxConcurrent`, itself driven by the server's probed child
|
|
72
|
+
* count). `0` = unbounded (`ComputeLimits.computeMaxQueueDepth`); a full
|
|
73
|
+
* queue rejects new solves with `QUEUE_FULL`.
|
|
74
|
+
*/
|
|
75
|
+
maxQueueDepth: number;
|
|
76
|
+
/**
|
|
77
|
+
* Max ms a solve may sit queued before executing; `0` = no deadline
|
|
78
|
+
* (`ComputeLimits.computeQueueWaitMs`). Too long a wait rejects with
|
|
79
|
+
* `QUEUE_TIMEOUT`.
|
|
80
|
+
*/
|
|
81
|
+
queueWaitMs: number;
|
|
82
|
+
/**
|
|
83
|
+
* Byte budget for this client's in-process solve cache. Applies per warm
|
|
84
|
+
* client — worst-case heap is this × `maxWarmComputeServers`. `0` disables
|
|
85
|
+
* the cache (`ComputeLimits.computeSolveCacheBytes`, env `COMPUTE_SOLVE_CACHE_MB`).
|
|
86
|
+
*/
|
|
87
|
+
responseCacheMaxBytes: number;
|
|
88
|
+
debug: ClientCacheDebug;
|
|
89
|
+
/** Max distinct warm compute servers before the LRU evicts the oldest. Default 16. */
|
|
90
|
+
maxWarmComputeServers?: number;
|
|
91
|
+
/** Sink for the debug lines. `onDebugLog` only fires when `debug` is not `false`. */
|
|
92
|
+
onDebugLog?: (message: string) => void;
|
|
93
93
|
}
|
|
94
94
|
interface ClientCache {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Get (or create) the warm client + scheduler for a resolved compute server,
|
|
97
|
+
* keyed by its `id`. `definitionGuid`, when present, is stamped as the
|
|
98
|
+
* `X-Selva-Definition` header on this client's outbound solve/IO requests —
|
|
99
|
+
* inert routing/telemetry metadata until a pool router exists.
|
|
100
|
+
*/
|
|
101
|
+
getClient(server: ResolvedServer, opts?: {
|
|
102
|
+
definitionGuid?: string;
|
|
103
|
+
}): Promise<CachedClient>;
|
|
104
|
+
/** Dispose and drop the warm client for `id`, so the next request rebuilds against fresh connection details. */
|
|
105
|
+
evict(id: string | ServerIdentity): void;
|
|
106
|
+
/** Solve-cache counters summed across every warm client. Counters die with the client that owns them, so totals can fall over time. */
|
|
107
|
+
solveCacheStats(): SolveCacheStats;
|
|
108
|
+
/**
|
|
109
|
+
* Drop every retained solve result, keeping the warm clients (and their
|
|
110
|
+
* connections and server-side definition pointers) intact. Nothing expires
|
|
111
|
+
* these on its own — the byte budget is the only other pressure — so this is
|
|
112
|
+
* the operator's release valve when a definition's inputs no longer describe
|
|
113
|
+
* its output, e.g. it reads an external source that has since changed.
|
|
114
|
+
*/
|
|
115
|
+
clearSolveCaches(): void;
|
|
116
|
+
/** Dispose every warm client. Test seam / shutdown hook. */
|
|
117
|
+
disposeAll(): void;
|
|
118
118
|
}
|
|
119
119
|
interface SolveCacheStats {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
120
|
+
warmClients: number;
|
|
121
|
+
entries: number;
|
|
122
|
+
bytes: number;
|
|
123
|
+
hits: number;
|
|
124
|
+
misses: number;
|
|
125
|
+
/** Entries dropped under size/byte pressure (not replacement or manual clears). */
|
|
126
|
+
evictions: number;
|
|
127
127
|
}
|
|
128
128
|
declare function createClientCache(config: ClientCacheConfig): ClientCache;
|
|
129
|
-
|
|
129
|
+
//#endregion
|
|
130
|
+
//#region src/server/definition-byte-cache.d.ts
|
|
130
131
|
/**
|
|
131
132
|
* In-process cache of `.gh` definition bytes, keyed by immutable **version id**.
|
|
132
133
|
*
|
|
@@ -153,109 +154,102 @@ declare function createClientCache(config: ClientCacheConfig): ClientCache;
|
|
|
153
154
|
* verdict (`skipped` / `hit` / `miss`).
|
|
154
155
|
*/
|
|
155
156
|
interface ByteRefOutcome {
|
|
156
|
-
|
|
157
|
-
|
|
157
|
+
loaded: boolean;
|
|
158
|
+
fromCache: boolean;
|
|
158
159
|
}
|
|
159
160
|
/** A definition reference shaped for `@selvajs/compute`'s `DefinitionRef`. */
|
|
160
161
|
interface ByteCacheRef {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
162
|
+
key: string;
|
|
163
|
+
load: () => Promise<Uint8Array>;
|
|
164
|
+
outcome: ByteRefOutcome;
|
|
164
165
|
}
|
|
165
166
|
interface ByteCacheStats {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
167
|
+
hits: number;
|
|
168
|
+
misses: number;
|
|
169
|
+
evictions: number;
|
|
170
|
+
entries: number;
|
|
171
|
+
bytes: number;
|
|
171
172
|
}
|
|
172
173
|
interface DefinitionByteCache {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
getOrLoad(versionId: string, load: () => Promise<Uint8Array>): ByteCacheRef;
|
|
175
|
+
stats(): ByteCacheStats;
|
|
176
|
+
clear(): void;
|
|
176
177
|
}
|
|
177
178
|
/**
|
|
178
179
|
* @param maxBytes total retained-byte budget; `0` (or negative) disables caching
|
|
179
180
|
* entirely — every `load()` calls the loader and nothing is retained.
|
|
180
181
|
*/
|
|
181
182
|
declare function createDefinitionByteCache(maxBytes: number): DefinitionByteCache;
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
* Transport-agnostic solve pipeline: given an already-resolved solve context
|
|
185
|
-
* (`.gh` bytes, input params + user values, a warm `SolveScheduler`), runs the
|
|
186
|
-
* solve and returns a discriminated {@link SolveOutcome} instead of throwing
|
|
187
|
-
* for expected failures. Auth, the database, share tokens, rate limits, and
|
|
188
|
-
* metric sinks are the calling route's job, not this file's.
|
|
189
|
-
*/
|
|
190
|
-
|
|
183
|
+
//#endregion
|
|
184
|
+
//#region src/server/solve-pipeline.d.ts
|
|
191
185
|
/** Compute-response contract version. Bump (and document the change) whenever the envelope's shape changes in a way a consumer could observe. */
|
|
192
186
|
declare const COMPUTE_CONTRACT_VERSION: 1;
|
|
193
187
|
declare const COMPUTE_VERSION_HEADER = "X-Selva-Compute-Version";
|
|
194
188
|
type PipelineInput = SchemaInput & {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
189
|
+
minimum?: number;
|
|
190
|
+
maximum?: number;
|
|
191
|
+
stepSize?: number;
|
|
198
192
|
};
|
|
199
193
|
interface SolvePipelineArgs {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
194
|
+
/**
|
|
195
|
+
* Raw `.gh` bytes, or a byte-cache `DefinitionRef` whose bytes the scheduler
|
|
196
|
+
* materializes only when an upload is unavoidable — a pointer-known solve
|
|
197
|
+
* moves zero bytes.
|
|
198
|
+
*/
|
|
199
|
+
definitionSource: SolveDefinition$1;
|
|
200
|
+
/** Mutable load outcome for a `DefinitionRef` source, so the `def_bytes` Server-Timing verdict can be emitted. Omit for raw-bytes solves. */
|
|
201
|
+
byteRefOutcome?: ByteRefOutcome;
|
|
202
|
+
/** Persisted input params; only those with a `paramType` are sent to the solve. */
|
|
203
|
+
inputs: PipelineInput[];
|
|
204
|
+
/**
|
|
205
|
+
* A tree the caller already built with {@link buildSolveInputTree}; skips the
|
|
206
|
+
* pipeline's own build and solves this object directly.
|
|
207
|
+
*
|
|
208
|
+
* A caller coalescing concurrent solves needs this: a single-flight key
|
|
209
|
+
* derived from raw `{inputs, values}` would split two requests that transform
|
|
210
|
+
* to the same tree, which is the identity the scheduler actually caches on.
|
|
211
|
+
*/
|
|
212
|
+
inputTree?: DataTree[];
|
|
213
|
+
/** User-chosen values keyed by input id; missing keys fall back to the schema default. */
|
|
214
|
+
values: Record<string, unknown>;
|
|
215
|
+
client: CachedClient;
|
|
216
|
+
responseMaxBytes: number;
|
|
217
|
+
/** Only phrases the timeout message — the scheduler enforces the deadline itself. */
|
|
218
|
+
solveDeadlineMs: number;
|
|
219
|
+
/** Client's `Accept-Encoding`; gzip is applied only when it advertises `gzip`. */
|
|
220
|
+
acceptEncoding: string;
|
|
221
|
+
/**
|
|
222
|
+
* Forwarded to the scheduler so a client disconnect cancels the upstream
|
|
223
|
+
* compute call. Its `aborted` flag also distinguishes a client disconnect
|
|
224
|
+
* from the scheduler's own deadline firing.
|
|
225
|
+
*/
|
|
226
|
+
signal: AbortSignal;
|
|
227
|
+
/** Wall-clock origin (`performance.now()` at the top of the request) so `load`/`total` phase timings line up with the caller's pre-solve prep. */
|
|
228
|
+
loadStartMs: number;
|
|
229
|
+
/** Pre-solve "load" phase duration the caller already measured (auth + DB + fetch). */
|
|
230
|
+
defLoadMs: number;
|
|
231
|
+
/** Pre-solve prep sub-phase marks (`[label, ms]`), surfaced verbatim as `p_*` Server-Timing entries. */
|
|
232
|
+
prepMarks?: [string, number][];
|
|
239
233
|
}
|
|
240
234
|
/** Phase timings the pipeline measured; the caller uses them for debug logging. */
|
|
241
235
|
interface SolvePhaseMetrics {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
236
|
+
treeBuildMs: number;
|
|
237
|
+
solveMs: number;
|
|
238
|
+
serializeMs: number;
|
|
239
|
+
gzipMs: number;
|
|
240
|
+
serverTotalMs: number;
|
|
241
|
+
serializedBytes: number;
|
|
242
|
+
/** Null when compression was skipped. */
|
|
243
|
+
compressedBytes: number | null;
|
|
250
244
|
}
|
|
251
245
|
/** A ready-to-send response: body + headers + the solve result + phase metrics. */
|
|
252
246
|
interface SolveEnvelope {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
247
|
+
/** A gzip `Uint8Array` when `encoding === 'gzip'`, else the JSON string. */
|
|
248
|
+
body: string | Uint8Array;
|
|
249
|
+
encoding?: 'gzip';
|
|
250
|
+
headers: Record<string, string>;
|
|
251
|
+
result: GrasshopperComputeResponse;
|
|
252
|
+
metrics: SolvePhaseMetrics;
|
|
259
253
|
}
|
|
260
254
|
/**
|
|
261
255
|
* `ok` carries the envelope; every other variant names an expected failure the
|
|
@@ -265,32 +259,32 @@ interface SolveEnvelope {
|
|
|
265
259
|
* the metric record.
|
|
266
260
|
*/
|
|
267
261
|
type SolveOutcome = {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
262
|
+
kind: 'ok';
|
|
263
|
+
envelope: SolveEnvelope;
|
|
264
|
+
solveMs: number;
|
|
265
|
+
errorCount: number;
|
|
266
|
+
warningCount: number;
|
|
273
267
|
} | {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
268
|
+
kind: 'timeout';
|
|
269
|
+
durationMs: number;
|
|
270
|
+
message: string;
|
|
277
271
|
} | {
|
|
278
|
-
|
|
279
|
-
|
|
272
|
+
kind: 'client_abort';
|
|
273
|
+
durationMs: number;
|
|
280
274
|
} | {
|
|
281
|
-
|
|
282
|
-
}
|
|
275
|
+
kind: 'too_large';
|
|
276
|
+
} |
|
|
283
277
|
/** Scheduler backpressure rejected the solve before it ran: queue was full (`queue_full`) or the wait exceeded the deadline (`queue_timeout`). Retryable — `retryAfterSeconds` is a backoff hint. */
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
278
|
+
{
|
|
279
|
+
kind: 'shed';
|
|
280
|
+
durationMs: number;
|
|
281
|
+
reason: 'queue_full' | 'queue_timeout';
|
|
282
|
+
retryAfterSeconds: number;
|
|
283
|
+
message: string;
|
|
290
284
|
} | {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
285
|
+
kind: 'compute_error';
|
|
286
|
+
durationMs: number;
|
|
287
|
+
error: unknown;
|
|
294
288
|
};
|
|
295
289
|
/** The transformed input tree exactly as handed to the scheduler; `runSolvePipeline` calls this itself unless the caller supplies {@link SolvePipelineArgs.inputTree}. */
|
|
296
290
|
declare function buildSolveInputTree(inputs: PipelineInput[], values: Record<string, unknown>): DataTree[];
|
|
@@ -311,20 +305,22 @@ declare function runSolvePipeline(args: SolvePipelineArgs): Promise<SolveOutcome
|
|
|
311
305
|
* gzip-capable waiter) is left uncompressed — correct, just not maximally small.
|
|
312
306
|
*/
|
|
313
307
|
declare function adaptEnvelopeToEncoding(envelope: SolveEnvelope, acceptEncoding: string): {
|
|
314
|
-
|
|
315
|
-
|
|
308
|
+
body: string | Uint8Array;
|
|
309
|
+
headers: Record<string, string>;
|
|
316
310
|
};
|
|
317
|
-
|
|
311
|
+
//#endregion
|
|
312
|
+
//#region src/server/transform-input.d.ts
|
|
318
313
|
/**
|
|
319
314
|
* `SchemaInput` has no `values`/`acceptedFormats` (those live on the layout item's config), so
|
|
320
315
|
* valueList/file inputs here carry just the selected value with no option list.
|
|
321
316
|
*/
|
|
322
317
|
declare function transformInputParameter(input: SchemaInput & {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
318
|
+
minimum?: number;
|
|
319
|
+
maximum?: number;
|
|
320
|
+
stepSize?: number;
|
|
326
321
|
}, value: unknown): InputParam;
|
|
327
|
-
|
|
322
|
+
//#endregion
|
|
323
|
+
//#region src/server/solve-cache-single-flight.d.ts
|
|
328
324
|
/**
|
|
329
325
|
* In-process single-flight — dogpile protection for every solve.
|
|
330
326
|
*
|
|
@@ -343,63 +339,50 @@ declare function transformInputParameter(input: SchemaInput & {
|
|
|
343
339
|
* because the app serializes it per response.
|
|
344
340
|
*/
|
|
345
341
|
interface SolveCacheSingleFlight {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
342
|
+
/**
|
|
343
|
+
* Coalesces concurrent calls under the same key: the first caller runs `work`,
|
|
344
|
+
* later callers for that key await the same promise, and the key frees as soon
|
|
345
|
+
* as it settles.
|
|
346
|
+
*
|
|
347
|
+
* `onWaiterJoined` fires only on the owner's call, only while `work` is still
|
|
348
|
+
* running, each time another caller joins. Ownership changes what the owner's
|
|
349
|
+
* abort signal should mean: a solo run can cancel on its own client's
|
|
350
|
+
* disconnect, but a shared run can't without 499-ing every waiter.
|
|
351
|
+
*/
|
|
352
|
+
run<T>(key: string, work: () => Promise<T>, onWaiterJoined?: () => void): Promise<T>;
|
|
353
|
+
inFlight(): number;
|
|
358
354
|
}
|
|
359
355
|
interface SolveCacheSingleFlightOptions {
|
|
360
|
-
|
|
356
|
+
onJoin?: (key: string) => void;
|
|
361
357
|
}
|
|
362
358
|
declare function createSolveCacheSingleFlight(options?: SolveCacheSingleFlightOptions): SolveCacheSingleFlight;
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
* Facade over the warm-client cache, definition-byte cache, single-flight
|
|
366
|
-
* coalescing, and the solve pipeline — the primitives an app route would
|
|
367
|
-
* otherwise wire up by hand.
|
|
368
|
-
*
|
|
369
|
-
* Doesn't read env itself: `@selvajs/server` owns `resolveComputeLimits` and
|
|
370
|
-
* depends on `@selvajs/solve`, so calling it from here would be a circular
|
|
371
|
-
* import. Callers resolve their own limits and pass in the fields below.
|
|
372
|
-
*
|
|
373
|
-
* Stays out of auth, DB reads, share tokens, rate limiting, metrics, and
|
|
374
|
-
* compute-server selection — all app policy, supplied per-call via
|
|
375
|
-
* `SolveEngineSolveArgs.server`.
|
|
376
|
-
*/
|
|
377
|
-
|
|
359
|
+
//#endregion
|
|
360
|
+
//#region src/server/solve-engine.d.ts
|
|
378
361
|
/**
|
|
379
362
|
* Subset of `ComputeLimits` (`@selvajs/server/compute`) the engine needs.
|
|
380
363
|
* Pass the resolved object through as-is — extra fields are ignored.
|
|
381
364
|
*/
|
|
382
365
|
interface SolveEngineLimits {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
366
|
+
solveDeadlineMs: number;
|
|
367
|
+
computeResponseMaxBytes: number;
|
|
368
|
+
computeReuseDefinitionCache: boolean;
|
|
369
|
+
computeServerCachesolve: boolean;
|
|
370
|
+
computeCacheErroredSolves: boolean;
|
|
371
|
+
computeMaxQueueDepth: number;
|
|
372
|
+
computeQueueWaitMs: number;
|
|
373
|
+
computeDefinitionCacheBytes: number;
|
|
374
|
+
computeSolveCacheBytes: number;
|
|
392
375
|
}
|
|
393
376
|
interface SolveEngineOptions {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
377
|
+
limits: SolveEngineLimits;
|
|
378
|
+
logger?: ILogger;
|
|
379
|
+
/** Max distinct warm compute servers before the LRU evicts the oldest. Default 16. */
|
|
380
|
+
maxWarmComputeServers?: number;
|
|
381
|
+
/** Concise cache/timing logs when truthy; `'verbose'` also dumps full lib-level requests/responses. Default off. */
|
|
382
|
+
debug?: ClientCacheDebug;
|
|
383
|
+
onDebugLog?: (message: string) => void;
|
|
384
|
+
/** Fired when a caller joins an already-in-flight solve instead of running its own. */
|
|
385
|
+
onSolveCoalesced?: (key: string) => void;
|
|
403
386
|
}
|
|
404
387
|
/**
|
|
405
388
|
* Accepts every form `runSolvePipeline` does, plus `{versionId, load}` sugar
|
|
@@ -408,64 +391,65 @@ interface SolveEngineOptions {
|
|
|
408
391
|
* schema extraction before solving) is passed through as-is — detected by its
|
|
409
392
|
* `outcome` field, which a plain `DefinitionRef` never has.
|
|
410
393
|
*/
|
|
411
|
-
type SolveEngineDefinitionSource = Uint8Array | string | DefinitionRef | ByteCacheRef | {
|
|
412
|
-
|
|
413
|
-
|
|
394
|
+
type SolveEngineDefinitionSource = Uint8Array | string | DefinitionRef$1 | ByteCacheRef | {
|
|
395
|
+
versionId: string;
|
|
396
|
+
load: () => Promise<Uint8Array>;
|
|
414
397
|
};
|
|
415
398
|
interface SolveEngineSolveArgs {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
399
|
+
server: ResolvedServer;
|
|
400
|
+
definitionSource: SolveEngineDefinitionSource;
|
|
401
|
+
/**
|
|
402
|
+
* Coalesce-key identity for a raw `Uint8Array`/`string` source, which has
|
|
403
|
+
* no natural identity the way a `DefinitionRef`'s `.key` does. Required in
|
|
404
|
+
* that case — `solve()` throws rather than silently hashing the bytes.
|
|
405
|
+
* Ignored for the other source forms.
|
|
406
|
+
*/
|
|
407
|
+
definitionKey?: string;
|
|
408
|
+
inputs: PipelineInput[];
|
|
409
|
+
values: Record<string, unknown>;
|
|
410
|
+
signal: AbortSignal;
|
|
411
|
+
acceptEncoding?: string;
|
|
412
|
+
/** Stamped as `X-Selva-Definition` on this solve's client. */
|
|
413
|
+
definitionGuid?: string;
|
|
414
|
+
loadStartMs?: number;
|
|
415
|
+
defLoadMs?: number;
|
|
416
|
+
prepMarks?: [string, number][];
|
|
434
417
|
}
|
|
435
418
|
interface FrameworkAgnosticResponse {
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
419
|
+
status: number;
|
|
420
|
+
headers: Record<string, string>;
|
|
421
|
+
body: string | Uint8Array;
|
|
439
422
|
}
|
|
440
423
|
interface SolveEngineStats {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
424
|
+
client: SolveCacheStats;
|
|
425
|
+
definitionBytes: ByteCacheStats;
|
|
426
|
+
coalescing: {
|
|
427
|
+
inFlight: number;
|
|
428
|
+
};
|
|
446
429
|
}
|
|
447
430
|
declare class SolveEngine {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
431
|
+
private readonly limits;
|
|
432
|
+
private readonly clientCache;
|
|
433
|
+
private readonly byteCache;
|
|
434
|
+
private readonly singleFlight;
|
|
435
|
+
constructor(options: SolveEngineOptions);
|
|
436
|
+
getClient(server: ResolvedServer, opts?: {
|
|
437
|
+
definitionGuid?: string;
|
|
438
|
+
}): Promise<CachedClient>;
|
|
439
|
+
definitionRef(versionId: string, load: () => Promise<Uint8Array>): ByteCacheRef;
|
|
440
|
+
evictServer(id: string): void;
|
|
441
|
+
clearSolveCaches(): void;
|
|
442
|
+
stats(): SolveEngineStats;
|
|
443
|
+
solve(args: SolveEngineSolveArgs): Promise<SolveOutcome>;
|
|
444
|
+
toResponse(outcome: SolveOutcome, opts?: {
|
|
445
|
+
onError?: (status: number, body: {
|
|
446
|
+
message: string;
|
|
447
|
+
retryAfter?: number;
|
|
448
|
+
}) => never;
|
|
449
|
+
}): FrameworkAgnosticResponse;
|
|
450
|
+
toWebResponse(outcome: SolveOutcome): Response;
|
|
451
|
+
private resolveDefinitionSource;
|
|
469
452
|
}
|
|
470
|
-
|
|
471
|
-
export { type ByteCacheRef, type ByteCacheStats, type ByteRefOutcome, COMPUTE_CONTRACT_VERSION, COMPUTE_VERSION_HEADER, type CachedClient, type ClientCache, type ClientCacheConfig, type DefinitionByteCache, type FrameworkAgnosticResponse, type PipelineInput, type ResolvedServer, type ServerIdentity, type SolveCacheSingleFlight, type SolveCacheSingleFlightOptions, type SolveCacheStats, SolveEngine, type SolveEngineDefinitionSource, type SolveEngineLimits, type SolveEngineOptions, type SolveEngineSolveArgs, type SolveEngineStats, type SolveEnvelope, type SolveOutcome, type SolvePhaseMetrics, type SolvePipelineArgs, adaptEnvelopeToEncoding, buildSolveInputTree, createClientCache, createDefinitionByteCache, createSolveCacheSingleFlight, runSolvePipeline, serverIdentity, transformInputParameter };
|
|
453
|
+
//#endregion
|
|
454
|
+
export { type ByteCacheRef, type ByteCacheStats, type ByteRefOutcome, COMPUTE_CONTRACT_VERSION, COMPUTE_VERSION_HEADER, type CachedClient, type ClientCache, type ClientCacheConfig, type DefinitionByteCache, type DefinitionRef, type FrameworkAgnosticResponse, type PipelineInput, type ResolvedServer, type ServerIdentity, type SolveCacheSingleFlight, type SolveCacheSingleFlightOptions, type SolveCacheStats, type SolveDefinition, SolveEngine, type SolveEngineDefinitionSource, type SolveEngineLimits, type SolveEngineOptions, type SolveEngineSolveArgs, type SolveEngineStats, type SolveEnvelope, type SolveInput, type SolveOutcome, type SolvePhaseMetrics, type SolvePipelineArgs, adaptEnvelopeToEncoding, buildSolveInputTree, createClientCache, createDefinitionByteCache, createSolveCacheSingleFlight, isDefinitionRef, runSolvePipeline, serverIdentity, transformInputParameter };
|
|
455
|
+
//# sourceMappingURL=server.d.cts.map
|