@selvajs/solve 0.2.0-beta.2 → 0.2.0-beta.4
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 +10 -14
- package/dist/client.cjs +1 -1
- package/dist/client.d.cts +19 -32
- package/dist/client.d.ts +19 -32
- 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 +94 -156
- package/dist/server.d.ts +94 -156
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/shared.d.cts +1 -1
- package/dist/shared.d.ts +1 -1
- package/dist/solve-fn-0hPOtZVD.d.cts +37 -0
- package/dist/solve-fn-0hPOtZVD.d.ts +37 -0
- package/package.json +3 -3
- package/dist/solve-fn-DGzPCsDu.d.cts +0 -22
- package/dist/solve-fn-DGzPCsDu.d.ts +0 -22
package/dist/server.d.cts
CHANGED
|
@@ -15,21 +15,20 @@ type ServerIdentity = string & {
|
|
|
15
15
|
interface ResolvedServer {
|
|
16
16
|
id: string;
|
|
17
17
|
serverUrl: string;
|
|
18
|
-
/** Sent as `RhinoComputeKey
|
|
18
|
+
/** Sent as the `RhinoComputeKey` header. */
|
|
19
19
|
apiKey?: string;
|
|
20
20
|
}
|
|
21
|
-
/** Derive the opaque cache identity from a resolved server. Identity is the `id`. */
|
|
22
21
|
declare function serverIdentity(server: Pick<ResolvedServer, 'id'>): ServerIdentity;
|
|
23
22
|
interface CachedClient {
|
|
24
23
|
client: GrasshopperClient;
|
|
25
24
|
scheduler: SolveScheduler;
|
|
26
25
|
/**
|
|
27
|
-
* Last Server-Timing decode/solve/encode, written by onServerTiming
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
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.
|
|
33
32
|
*/
|
|
34
33
|
rhinoTiming: {
|
|
35
34
|
last: {
|
|
@@ -41,7 +40,7 @@ interface CachedClient {
|
|
|
41
40
|
};
|
|
42
41
|
/**
|
|
43
42
|
* Same snapshot-and-attribute pattern as `rhinoTiming`, for the scheduler's
|
|
44
|
-
* onSettle cache verdict. `seq` increments on every settle (success or
|
|
43
|
+
* `onSettle` cache verdict. `seq` increments on every settle (success or
|
|
45
44
|
* error); `last` is written only on success.
|
|
46
45
|
*/
|
|
47
46
|
solveMeta: {
|
|
@@ -62,37 +61,34 @@ type ClientCacheDebug = boolean | 'verbose';
|
|
|
62
61
|
interface ClientCacheConfig {
|
|
63
62
|
/** Per-solve timeout forwarded to the scheduler (`ComputeLimits.maxSolveDurationMs`). */
|
|
64
63
|
maxSolveDurationMs: number;
|
|
65
|
-
/** Ask Rhino.Compute to cache solve results and return them on identical repeats. */
|
|
66
64
|
cachesolve: boolean;
|
|
67
|
-
/**
|
|
65
|
+
/** Only meaningful with `cachesolve`. */
|
|
68
66
|
cacheerroredsolves: boolean;
|
|
69
|
-
/** Reference large definitions by server cache key
|
|
67
|
+
/** Reference large definitions by server cache key instead of re-uploading. */
|
|
70
68
|
reuseServerDefinitionCache: boolean;
|
|
71
69
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
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`.
|
|
76
74
|
*/
|
|
77
75
|
maxQueueDepth: number;
|
|
78
76
|
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
77
|
+
* Max ms a solve may sit queued before executing; `0` = no deadline
|
|
78
|
+
* (`ComputeLimits.computeQueueWaitMs`). Too long a wait rejects with
|
|
81
79
|
* `QUEUE_TIMEOUT`.
|
|
82
80
|
*/
|
|
83
81
|
queueWaitMs: number;
|
|
84
82
|
/**
|
|
85
|
-
* Byte budget for this client's in-process solve cache
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* (`ComputeLimits.computeSolveCacheBytes`, env `COMPUTE_SOLVE_CACHE_MB`).
|
|
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`).
|
|
89
86
|
*/
|
|
90
87
|
responseCacheMaxBytes: number;
|
|
91
|
-
/** Debug verbosity. `onDebugLog` is only ever invoked when this is not `false`. */
|
|
92
88
|
debug: ClientCacheDebug;
|
|
93
89
|
/** Max distinct warm compute servers before the LRU evicts the oldest. Default 16. */
|
|
94
90
|
maxWarmComputeServers?: number;
|
|
95
|
-
/** Sink for the
|
|
91
|
+
/** Sink for the debug lines. `onDebugLog` only fires when `debug` is not `false`. */
|
|
96
92
|
onDebugLog?: (message: string) => void;
|
|
97
93
|
}
|
|
98
94
|
interface ClientCache {
|
|
@@ -107,11 +103,7 @@ interface ClientCache {
|
|
|
107
103
|
}): Promise<CachedClient>;
|
|
108
104
|
/** Dispose and drop the warm client for `id`, so the next request rebuilds against fresh connection details. */
|
|
109
105
|
evict(id: string | ServerIdentity): void;
|
|
110
|
-
/**
|
|
111
|
-
* Solve-cache counters summed across every warm client. `warmClients` is
|
|
112
|
-
* reported alongside since each client owns its own cache. Counters die
|
|
113
|
-
* with the client that owns them, so totals can fall over time.
|
|
114
|
-
*/
|
|
106
|
+
/** Solve-cache counters summed across every warm client. Counters die with the client that owns them, so totals can fall over time. */
|
|
115
107
|
solveCacheStats(): SolveCacheStats;
|
|
116
108
|
/**
|
|
117
109
|
* Drop every retained solve result, keeping the warm clients (and their
|
|
@@ -124,7 +116,6 @@ interface ClientCache {
|
|
|
124
116
|
/** Dispose every warm client. Test seam / shutdown hook. */
|
|
125
117
|
disposeAll(): void;
|
|
126
118
|
}
|
|
127
|
-
/** Aggregate solve-cache counters across the warm clients (see `solveCacheStats`). */
|
|
128
119
|
interface SolveCacheStats {
|
|
129
120
|
warmClients: number;
|
|
130
121
|
entries: number;
|
|
@@ -163,7 +154,6 @@ declare function createClientCache(config: ClientCacheConfig): ClientCache;
|
|
|
163
154
|
*/
|
|
164
155
|
interface ByteRefOutcome {
|
|
165
156
|
loaded: boolean;
|
|
166
|
-
/** When `loaded`, whether the bytes came from a warm cache entry. */
|
|
167
157
|
fromCache: boolean;
|
|
168
158
|
}
|
|
169
159
|
/** A definition reference shaped for `@selvajs/compute`'s `DefinitionRef`. */
|
|
@@ -172,24 +162,16 @@ interface ByteCacheRef {
|
|
|
172
162
|
load: () => Promise<Uint8Array>;
|
|
173
163
|
outcome: ByteRefOutcome;
|
|
174
164
|
}
|
|
175
|
-
/** Hit/miss/eviction counters for observability (Server-Timing, admin debug). */
|
|
176
165
|
interface ByteCacheStats {
|
|
177
166
|
hits: number;
|
|
178
167
|
misses: number;
|
|
179
|
-
/** Entries dropped by the byte-budget LRU. */
|
|
180
168
|
evictions: number;
|
|
181
169
|
entries: number;
|
|
182
170
|
bytes: number;
|
|
183
171
|
}
|
|
184
172
|
interface DefinitionByteCache {
|
|
185
|
-
/**
|
|
186
|
-
* `DefinitionRef` whose `load()` serves `versionId`'s bytes from the cache
|
|
187
|
-
* when warm, otherwise calls `load` and caches the result. Cheap to build
|
|
188
|
-
* and moves no bytes until `load()` runs.
|
|
189
|
-
*/
|
|
190
173
|
getOrLoad(versionId: string, load: () => Promise<Uint8Array>): ByteCacheRef;
|
|
191
174
|
stats(): ByteCacheStats;
|
|
192
|
-
/** Test seam / eviction on definition delete if ever needed. */
|
|
193
175
|
clear(): void;
|
|
194
176
|
}
|
|
195
177
|
/**
|
|
@@ -201,10 +183,9 @@ declare function createDefinitionByteCache(maxBytes: number): DefinitionByteCach
|
|
|
201
183
|
/**
|
|
202
184
|
* Transport-agnostic solve pipeline: given an already-resolved solve context
|
|
203
185
|
* (`.gh` bytes, input params + user values, a warm `SolveScheduler`), runs the
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
* in the route that calls this.
|
|
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.
|
|
208
189
|
*/
|
|
209
190
|
|
|
210
191
|
/** Compute-response contract version. Bump (and document the change) whenever the envelope's shape changes in a way a consumer could observe. */
|
|
@@ -217,25 +198,20 @@ type PipelineInput = SchemaInput & {
|
|
|
217
198
|
};
|
|
218
199
|
interface SolvePipelineArgs {
|
|
219
200
|
/**
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
* zero bytes.
|
|
201
|
+
* Raw `.gh` bytes, or a byte-cache `DefinitionRef` whose bytes the scheduler
|
|
202
|
+
* materializes only when an upload is unavoidable — a pointer-known solve
|
|
203
|
+
* moves zero bytes.
|
|
224
204
|
*/
|
|
225
205
|
definitionSource: SolveDefinition;
|
|
226
|
-
/**
|
|
227
|
-
* When `definitionSource` is a byte-cache `DefinitionRef`, its mutable outcome
|
|
228
|
-
* so the pipeline can emit the `def_bytes` Server-Timing verdict. Omit for
|
|
229
|
-
* raw-bytes solves.
|
|
230
|
-
*/
|
|
206
|
+
/** Mutable load outcome for a `DefinitionRef` source, so the `def_bytes` Server-Timing verdict can be emitted. Omit for raw-bytes solves. */
|
|
231
207
|
byteRefOutcome?: ByteRefOutcome;
|
|
232
208
|
/** Persisted input params; only those with a `paramType` are sent to the solve. */
|
|
233
209
|
inputs: PipelineInput[];
|
|
234
210
|
/**
|
|
235
|
-
* A tree the caller already built with {@link buildSolveInputTree}
|
|
236
|
-
*
|
|
211
|
+
* A tree the caller already built with {@link buildSolveInputTree}; skips the
|
|
212
|
+
* pipeline's own build and solves this object directly.
|
|
237
213
|
*
|
|
238
|
-
*
|
|
214
|
+
* A caller coalescing concurrent solves needs this: a single-flight key
|
|
239
215
|
* derived from raw `{inputs, values}` would split two requests that transform
|
|
240
216
|
* to the same tree, which is the identity the scheduler actually caches on.
|
|
241
217
|
*/
|
|
@@ -244,28 +220,21 @@ interface SolvePipelineArgs {
|
|
|
244
220
|
values: Record<string, unknown>;
|
|
245
221
|
client: CachedClient;
|
|
246
222
|
responseMaxBytes: number;
|
|
247
|
-
/**
|
|
223
|
+
/** Only phrases the timeout message — the scheduler enforces the deadline itself. */
|
|
248
224
|
maxSolveDurationMs: number;
|
|
249
225
|
/** Client's `Accept-Encoding`; gzip is applied only when it advertises `gzip`. */
|
|
250
226
|
acceptEncoding: string;
|
|
251
227
|
/**
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
228
|
+
* Forwarded to the scheduler so a client disconnect cancels the upstream
|
|
229
|
+
* compute call. Its `aborted` flag also distinguishes a client disconnect
|
|
230
|
+
* from the scheduler's own deadline firing.
|
|
255
231
|
*/
|
|
256
232
|
signal: AbortSignal;
|
|
257
|
-
/**
|
|
258
|
-
* Wall-clock origin (`performance.now()` captured at the top of the request)
|
|
259
|
-
* so the pipeline's `load`/`total` phase timings line up with the caller's
|
|
260
|
-
* pre-solve prep.
|
|
261
|
-
*/
|
|
233
|
+
/** 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. */
|
|
262
234
|
loadStartMs: number;
|
|
263
235
|
/** Pre-solve "load" phase duration the caller already measured (auth + DB + fetch). */
|
|
264
236
|
defLoadMs: number;
|
|
265
|
-
/**
|
|
266
|
-
* Pre-solve prep sub-phase marks (`[label, ms]`), surfaced verbatim as `p_*`
|
|
267
|
-
* Server-Timing entries.
|
|
268
|
-
*/
|
|
237
|
+
/** Pre-solve prep sub-phase marks (`[label, ms]`), surfaced verbatim as `p_*` Server-Timing entries. */
|
|
269
238
|
prepMarks?: [string, number][];
|
|
270
239
|
}
|
|
271
240
|
/** Phase timings the pipeline measured; the caller uses them for debug logging. */
|
|
@@ -289,12 +258,11 @@ interface SolveEnvelope {
|
|
|
289
258
|
metrics: SolvePhaseMetrics;
|
|
290
259
|
}
|
|
291
260
|
/**
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
* `
|
|
296
|
-
*
|
|
297
|
-
* for the metric record.
|
|
261
|
+
* `ok` carries the envelope; every other variant names an expected failure the
|
|
262
|
+
* calling route maps to a status code (timeout→504, client_abort→499,
|
|
263
|
+
* too_large→413, shed→503+Retry-After, compute_error→generic 500/503).
|
|
264
|
+
* `durationMs` on error variants is the solve wall time up to the failure, for
|
|
265
|
+
* the metric record.
|
|
298
266
|
*/
|
|
299
267
|
type SolveOutcome = {
|
|
300
268
|
kind: 'ok';
|
|
@@ -312,12 +280,7 @@ type SolveOutcome = {
|
|
|
312
280
|
} | {
|
|
313
281
|
kind: 'too_large';
|
|
314
282
|
}
|
|
315
|
-
/**
|
|
316
|
-
* Scheduler backpressure shed the solve before it executed — the per-server
|
|
317
|
-
* queue was full (`QUEUE_FULL`) or it sat queued past the wait deadline
|
|
318
|
-
* (`QUEUE_TIMEOUT`). Retryable: the route maps this to 503 + `Retry-After`.
|
|
319
|
-
* `retryAfterSeconds` is a suggested backoff hint for the client.
|
|
320
|
-
*/
|
|
283
|
+
/** 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. */
|
|
321
284
|
| {
|
|
322
285
|
kind: 'shed';
|
|
323
286
|
durationMs: number;
|
|
@@ -329,24 +292,19 @@ type SolveOutcome = {
|
|
|
329
292
|
durationMs: number;
|
|
330
293
|
error: unknown;
|
|
331
294
|
};
|
|
332
|
-
/**
|
|
333
|
-
* Build the transformed input tree — the exact object handed to the scheduler.
|
|
334
|
-
* `runSolvePipeline` calls this itself; a caller only needs it directly to see
|
|
335
|
-
* the tree before the pipeline runs (see {@link SolvePipelineArgs.inputTree}).
|
|
336
|
-
*/
|
|
295
|
+
/** The transformed input tree exactly as handed to the scheduler; `runSolvePipeline` calls this itself unless the caller supplies {@link SolvePipelineArgs.inputTree}. */
|
|
337
296
|
declare function buildSolveInputTree(inputs: PipelineInput[], values: Record<string, unknown>): DataTree[];
|
|
338
297
|
declare function runSolvePipeline(args: SolvePipelineArgs): Promise<SolveOutcome>;
|
|
339
298
|
/**
|
|
340
299
|
* Re-key a coalesced envelope to a single waiter's `Accept-Encoding`.
|
|
341
300
|
*
|
|
342
301
|
* The single-flight coalescer runs ONE pipeline execution for N identical
|
|
343
|
-
* concurrent solves and hands every waiter the same {@link SolveEnvelope}
|
|
344
|
-
*
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
* result to each waiter instead.
|
|
302
|
+
* concurrent solves and hands every waiter the same {@link SolveEnvelope},
|
|
303
|
+
* baked from the FIRST caller's `Accept-Encoding`. A later waiter with a
|
|
304
|
+
* different `Accept-Encoding` would otherwise get a body labelled with the
|
|
305
|
+
* wrong encoding — `Vary` can't help here, since it's one shared object, not a
|
|
306
|
+
* cache lookup. Encoding is deliberately left out of the coalesce key so mixed
|
|
307
|
+
* clients still coalesce; this adapts the shared result to each waiter instead.
|
|
350
308
|
*
|
|
351
309
|
* Only the correctness-critical direction is adapted: a gzip envelope served to
|
|
352
310
|
* a non-gzip waiter is gunzipped back to JSON. The reverse (plain JSON to a
|
|
@@ -358,11 +316,8 @@ declare function adaptEnvelopeToEncoding(envelope: SolveEnvelope, acceptEncoding
|
|
|
358
316
|
};
|
|
359
317
|
|
|
360
318
|
/**
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
* are absent on `SchemaInput` (they live on the layout item's config), so
|
|
364
|
-
* valueList/file inputs fall back to carrying the selected value with no option
|
|
365
|
-
* list — the same value that reaches Grasshopper.
|
|
319
|
+
* `SchemaInput` has no `values`/`acceptedFormats` (those live on the layout item's config), so
|
|
320
|
+
* valueList/file inputs here carry just the selected value with no option list.
|
|
366
321
|
*/
|
|
367
322
|
declare function transformInputParameter(input: SchemaInput & {
|
|
368
323
|
minimum?: number;
|
|
@@ -373,70 +328,56 @@ declare function transformInputParameter(input: SchemaInput & {
|
|
|
373
328
|
/**
|
|
374
329
|
* In-process single-flight — dogpile protection for every solve.
|
|
375
330
|
*
|
|
376
|
-
*
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
-
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
331
|
+
* Always on, not gated behind a result cache being configured: the dogpile is worst
|
|
332
|
+
* when nothing else is caching, since N concurrent identical solves each pay a full
|
|
333
|
+
* Rhino round trip (the scheduler doesn't coalesce in-flight requests itself, and
|
|
334
|
+
* Rhino's `cachesolve` still costs a round trip per repeat). An earlier version
|
|
335
|
+
* gated this on cache config and so disabled it in exactly the deployments most
|
|
336
|
+
* exposed to a cold-key stampede — hot public definition plus a deploy.
|
|
382
337
|
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
338
|
+
* Per app instance, above `ISolveResultCache`. No cross-instance lease (Redis
|
|
339
|
+
* `SET NX`) yet.
|
|
385
340
|
*
|
|
386
|
-
*
|
|
387
|
-
*
|
|
388
|
-
*
|
|
389
|
-
* app serializes it per response).
|
|
341
|
+
* The shared promise resolves to one value for every waiter, so `work` must return
|
|
342
|
+
* something safe to share by reference — the solve pipeline's envelope qualifies
|
|
343
|
+
* because the app serializes it per response.
|
|
390
344
|
*/
|
|
391
345
|
interface SolveCacheSingleFlight {
|
|
392
346
|
/**
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
*
|
|
347
|
+
* Coalesces concurrent calls under the same key: the first caller runs `work`,
|
|
348
|
+
* later callers for that key await the same promise, and the key frees as soon
|
|
349
|
+
* as it settles.
|
|
396
350
|
*
|
|
397
|
-
* `onWaiterJoined` fires on the
|
|
398
|
-
*
|
|
399
|
-
*
|
|
400
|
-
*
|
|
401
|
-
* settles.
|
|
351
|
+
* `onWaiterJoined` fires only on the owner's call, only while `work` is still
|
|
352
|
+
* running, each time another caller joins. Ownership changes what the owner's
|
|
353
|
+
* abort signal should mean: a solo run can cancel on its own client's
|
|
354
|
+
* disconnect, but a shared run can't without 499-ing every waiter.
|
|
402
355
|
*/
|
|
403
356
|
run<T>(key: string, work: () => Promise<T>, onWaiterJoined?: () => void): Promise<T>;
|
|
404
|
-
/** Number of keys currently in flight (observability / tests). */
|
|
405
357
|
inFlight(): number;
|
|
406
358
|
}
|
|
407
359
|
interface SolveCacheSingleFlightOptions {
|
|
408
|
-
/**
|
|
409
|
-
* Fired when a caller joins an already-in-flight key instead of running its
|
|
410
|
-
* own work — the coalescing win this module exists for, otherwise invisible.
|
|
411
|
-
*/
|
|
412
360
|
onJoin?: (key: string) => void;
|
|
413
361
|
}
|
|
414
362
|
declare function createSolveCacheSingleFlight(options?: SolveCacheSingleFlightOptions): SolveCacheSingleFlight;
|
|
415
363
|
|
|
416
364
|
/**
|
|
417
|
-
*
|
|
418
|
-
*
|
|
419
|
-
*
|
|
420
|
-
* outcome→HTTP mapping. Composes `createClientCache` + `createDefinitionByteCache`
|
|
421
|
-
* + `createSolveCacheSingleFlight` + `runSolvePipeline` — the same primitives a
|
|
422
|
-
* hand-assembled app route already wires, minus the wiring.
|
|
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.
|
|
423
368
|
*
|
|
424
|
-
*
|
|
425
|
-
* `@selvajs/
|
|
426
|
-
*
|
|
427
|
-
* (`resolveComputeLimits` from `@selvajs/server/compute`, or any equivalent) and
|
|
428
|
-
* passes the handful of fields this engine actually needs.
|
|
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.
|
|
429
372
|
*
|
|
430
|
-
*
|
|
431
|
-
*
|
|
432
|
-
* server
|
|
433
|
-
* per-call via `SolveEngineSolveArgs.server`.
|
|
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`.
|
|
434
376
|
*/
|
|
435
377
|
|
|
436
378
|
/**
|
|
437
|
-
*
|
|
438
|
-
*
|
|
439
|
-
* through, its extra fields are ignored.
|
|
379
|
+
* Subset of `ComputeLimits` (`@selvajs/server/compute`) the engine needs.
|
|
380
|
+
* Pass the resolved object through as-is — extra fields are ignored.
|
|
440
381
|
*/
|
|
441
382
|
interface SolveEngineLimits {
|
|
442
383
|
maxSolveDurationMs: number;
|
|
@@ -450,24 +391,22 @@ interface SolveEngineLimits {
|
|
|
450
391
|
computeSolveCacheBytes: number;
|
|
451
392
|
}
|
|
452
393
|
interface SolveEngineOptions {
|
|
453
|
-
/** Required — the engine does not read env itself (see module doc). */
|
|
454
394
|
limits: SolveEngineLimits;
|
|
455
395
|
logger?: ILogger;
|
|
456
|
-
/** Max distinct warm compute servers before the LRU evicts the oldest. Default 16
|
|
396
|
+
/** Max distinct warm compute servers before the LRU evicts the oldest. Default 16. */
|
|
457
397
|
maxWarmComputeServers?: number;
|
|
458
|
-
/** Concise cache/timing logs when truthy; `'verbose'` also
|
|
398
|
+
/** Concise cache/timing logs when truthy; `'verbose'` also dumps full lib-level requests/responses. Default off. */
|
|
459
399
|
debug?: ClientCacheDebug;
|
|
460
400
|
onDebugLog?: (message: string) => void;
|
|
461
|
-
/** Fired when a caller joins an already-in-flight solve instead of running its own
|
|
401
|
+
/** Fired when a caller joins an already-in-flight solve instead of running its own. */
|
|
462
402
|
onSolveCoalesced?: (key: string) => void;
|
|
463
403
|
}
|
|
464
404
|
/**
|
|
465
|
-
*
|
|
466
|
-
*
|
|
467
|
-
*
|
|
468
|
-
*
|
|
469
|
-
*
|
|
470
|
-
* `DefinitionRef` never has.
|
|
405
|
+
* Accepts every form `runSolvePipeline` does, plus `{versionId, load}` sugar
|
|
406
|
+
* that builds (and caches) a `ByteCacheRef` internally. A `ByteCacheRef`
|
|
407
|
+
* obtained ahead of time from `engine.definitionRef()` (e.g. to read bytes for
|
|
408
|
+
* schema extraction before solving) is passed through as-is — detected by its
|
|
409
|
+
* `outcome` field, which a plain `DefinitionRef` never has.
|
|
471
410
|
*/
|
|
472
411
|
type SolveEngineDefinitionSource = Uint8Array | string | DefinitionRef | ByteCacheRef | {
|
|
473
412
|
versionId: string;
|
|
@@ -477,18 +416,17 @@ interface SolveEngineSolveArgs {
|
|
|
477
416
|
server: ResolvedServer;
|
|
478
417
|
definitionSource: SolveEngineDefinitionSource;
|
|
479
418
|
/**
|
|
480
|
-
* Coalesce-key identity for a raw `Uint8Array`/`string`
|
|
481
|
-
*
|
|
482
|
-
*
|
|
483
|
-
*
|
|
484
|
-
* package, which keys on immutable ids). Ignored for the other source forms.
|
|
419
|
+
* Coalesce-key identity for a raw `Uint8Array`/`string` source, which has
|
|
420
|
+
* no natural identity the way a `DefinitionRef`'s `.key` does. Required in
|
|
421
|
+
* that case — `solve()` throws rather than silently hashing the bytes.
|
|
422
|
+
* Ignored for the other source forms.
|
|
485
423
|
*/
|
|
486
424
|
definitionKey?: string;
|
|
487
425
|
inputs: PipelineInput[];
|
|
488
426
|
values: Record<string, unknown>;
|
|
489
427
|
signal: AbortSignal;
|
|
490
428
|
acceptEncoding?: string;
|
|
491
|
-
/** Stamped as `X-Selva-Definition` on this solve's client
|
|
429
|
+
/** Stamped as `X-Selva-Definition` on this solve's client. */
|
|
492
430
|
definitionGuid?: string;
|
|
493
431
|
loadStartMs?: number;
|
|
494
432
|
defLoadMs?: number;
|