@selvajs/solve 1.3.0-beta.3 → 2.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/dist/client.cjs +1 -1
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +1 -21
- package/dist/client.d.ts +1 -21
- package/dist/client.js +1 -1
- package/dist/client.js.map +1 -1
- package/dist/outcome-BOrAeRAQ.js +2 -0
- package/dist/outcome-BOrAeRAQ.js.map +1 -0
- package/dist/outcome-DUHQoZqq.cjs +2 -0
- package/dist/outcome-DUHQoZqq.cjs.map +1 -0
- package/dist/{outcome-ePNabW7y.d.ts → outcome-DsNmx27P.d.cts} +1 -5
- package/dist/{outcome-ePNabW7y.d.cts → outcome-DsNmx27P.d.ts} +1 -5
- package/dist/server.cjs +1 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +11 -63
- package/dist/server.d.ts +11 -63
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/shared.cjs +1 -1
- package/dist/shared.d.cts +2 -2
- package/dist/shared.d.ts +2 -2
- package/dist/shared.js +1 -1
- package/dist/solve-input-CUjVGYPC.d.cts +11 -0
- package/dist/solve-input-CUjVGYPC.d.ts +11 -0
- package/package.json +10 -10
- package/dist/outcome-DdbgqiIG.cjs +0 -2
- package/dist/outcome-DdbgqiIG.cjs.map +0 -1
- package/dist/outcome-H5XjAroQ.js +0 -2
- package/dist/outcome-H5XjAroQ.js.map +0 -1
- package/dist/solve-input-BDv_JdvS.d.cts +0 -15
- package/dist/solve-input-BDv_JdvS.d.ts +0 -15
package/dist/server.d.cts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { t as SolveInput } from "./solve-input-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { DefinitionRef, SolveDefinition, SolveDefinition as SolveDefinition$1, isDefinitionRef } from "@selvajs/compute/core";
|
|
1
|
+
import { t as SolveInput } from "./solve-input-CUjVGYPC.cjs";
|
|
2
|
+
import { DataTree, GrasshopperClient, GrasshopperComputeResponse, SelvaEventTarget, SolveScheduler } from "@selvajs/compute/grasshopper";
|
|
3
|
+
import { DefinitionRef, DefinitionRef as DefinitionRef$1, SolveDefinition, SolveDefinition as SolveDefinition$1, isDefinitionRef } from "@selvajs/compute/core";
|
|
5
4
|
import { ILogger } from "@selvajs/platform";
|
|
6
|
-
//#region src/server/client-cache.d.ts
|
|
7
5
|
/**
|
|
8
6
|
* Opaque identity of a compute server — construct only via `serverIdentity()`.
|
|
9
7
|
* Kept opaque so "a server is now a pool of URLs behind one id" stays additive.
|
|
@@ -22,34 +20,6 @@ export declare function serverIdentity(server: Pick<ResolvedServer, 'id'>): Serv
|
|
|
22
20
|
interface CachedClient {
|
|
23
21
|
client: GrasshopperClient;
|
|
24
22
|
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
23
|
}
|
|
54
24
|
/**
|
|
55
25
|
* Debug verbosity: `false` is silent, `true` gives concise cache/timing logs
|
|
@@ -62,8 +32,6 @@ interface ClientCacheConfig {
|
|
|
62
32
|
/** Per-solve timeout forwarded to the scheduler (`ComputeLimits.solveDeadlineMs`). */
|
|
63
33
|
solveDeadlineMs: number;
|
|
64
34
|
cachesolve: boolean;
|
|
65
|
-
/** Only meaningful with `cachesolve`. */
|
|
66
|
-
cacheerroredsolves: boolean;
|
|
67
35
|
/** Reference large definitions by server cache key instead of re-uploading. */
|
|
68
36
|
reuseServerDefinitionCache: boolean;
|
|
69
37
|
/**
|
|
@@ -85,6 +53,11 @@ interface ClientCacheConfig {
|
|
|
85
53
|
* the cache (`ComputeLimits.computeSolveCacheBytes`, env `COMPUTE_SOLVE_CACHE_MB`).
|
|
86
54
|
*/
|
|
87
55
|
responseCacheMaxBytes: number;
|
|
56
|
+
/**
|
|
57
|
+
* Largest compute response body read; past it the download is cancelled (`too_large`).
|
|
58
|
+
* Default: no limit.
|
|
59
|
+
*/
|
|
60
|
+
maxResponseBytes?: number;
|
|
88
61
|
debug: ClientCacheDebug;
|
|
89
62
|
/** Max distinct warm compute servers before the LRU evicts the oldest. Default 16. */
|
|
90
63
|
maxWarmComputeServers?: number;
|
|
@@ -126,8 +99,6 @@ interface SolveCacheStats {
|
|
|
126
99
|
evictions: number;
|
|
127
100
|
}
|
|
128
101
|
export declare function createClientCache(config: ClientCacheConfig): ClientCache;
|
|
129
|
-
//#endregion
|
|
130
|
-
//#region src/server/definition-byte-cache.d.ts
|
|
131
102
|
/**
|
|
132
103
|
* In-process cache of `.gh` definition bytes, keyed by immutable **version id**.
|
|
133
104
|
*
|
|
@@ -180,16 +151,10 @@ interface DefinitionByteCache {
|
|
|
180
151
|
* entirely — every `load()` calls the loader and nothing is retained.
|
|
181
152
|
*/
|
|
182
153
|
export declare function createDefinitionByteCache(maxBytes: number): DefinitionByteCache;
|
|
183
|
-
//#endregion
|
|
184
|
-
//#region src/server/solve-pipeline.d.ts
|
|
185
154
|
/** Compute-response contract version. Bump (and document the change) whenever the envelope's shape changes in a way a consumer could observe. */
|
|
186
155
|
export declare const COMPUTE_CONTRACT_VERSION: 1;
|
|
187
156
|
export declare const COMPUTE_VERSION_HEADER = "X-Selva-Compute-Version";
|
|
188
|
-
type PipelineInput =
|
|
189
|
-
minimum?: number;
|
|
190
|
-
maximum?: number;
|
|
191
|
-
stepSize?: number;
|
|
192
|
-
};
|
|
157
|
+
type PipelineInput = SolveInput;
|
|
193
158
|
interface SolvePipelineArgs {
|
|
194
159
|
/**
|
|
195
160
|
* Raw `.gh` bytes, or a byte-cache `DefinitionRef` whose bytes the scheduler
|
|
@@ -288,8 +253,6 @@ type SolveOutcome = {
|
|
|
288
253
|
durationMs: number;
|
|
289
254
|
error: unknown;
|
|
290
255
|
};
|
|
291
|
-
/** The transformed input tree exactly as handed to the scheduler; `runSolvePipeline` calls this itself unless the caller supplies {@link SolvePipelineArgs.inputTree}. */
|
|
292
|
-
export declare function buildSolveInputTree(inputs: PipelineInput[], values: Record<string, unknown>): DataTree[];
|
|
293
256
|
export declare function runSolvePipeline(args: SolvePipelineArgs): Promise<SolveOutcome>;
|
|
294
257
|
/**
|
|
295
258
|
* Re-key a coalesced envelope to a single waiter's `Accept-Encoding`.
|
|
@@ -310,19 +273,8 @@ export declare function adaptEnvelopeToEncoding(envelope: SolveEnvelope, acceptE
|
|
|
310
273
|
body: string | Uint8Array;
|
|
311
274
|
headers: Record<string, string>;
|
|
312
275
|
};
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* `SchemaInput` has no `values`/`acceptedFormats` (those live on the layout item's config), so
|
|
317
|
-
* valueList/file inputs here carry just the selected value with no option list.
|
|
318
|
-
*/
|
|
319
|
-
export declare function transformInputParameter(input: SchemaInput & {
|
|
320
|
-
minimum?: number;
|
|
321
|
-
maximum?: number;
|
|
322
|
-
stepSize?: number;
|
|
323
|
-
}, value: unknown): InputParam;
|
|
324
|
-
//#endregion
|
|
325
|
-
//#region src/server/solve-cache-single-flight.d.ts
|
|
276
|
+
/** The input tree a solve sends: each input's value, or its schema default when it has none. */
|
|
277
|
+
export declare function buildSolveInputTree(inputs: SolveInput[], values: Record<string, unknown>): DataTree[];
|
|
326
278
|
/**
|
|
327
279
|
* In-process single-flight — dogpile protection for every solve.
|
|
328
280
|
*
|
|
@@ -358,8 +310,6 @@ interface SolveCacheSingleFlightOptions {
|
|
|
358
310
|
onJoin?: (key: string) => void;
|
|
359
311
|
}
|
|
360
312
|
export declare function createSolveCacheSingleFlight(options?: SolveCacheSingleFlightOptions): SolveCacheSingleFlight;
|
|
361
|
-
//#endregion
|
|
362
|
-
//#region src/server/solve-engine.d.ts
|
|
363
313
|
/**
|
|
364
314
|
* Subset of `ComputeLimits` (`@selvajs/server/compute`) the engine needs.
|
|
365
315
|
* Pass the resolved object through as-is — extra fields are ignored.
|
|
@@ -369,7 +319,6 @@ interface SolveEngineLimits {
|
|
|
369
319
|
computeResponseMaxBytes: number;
|
|
370
320
|
computeReuseDefinitionCache: boolean;
|
|
371
321
|
computeServerCachesolve: boolean;
|
|
372
|
-
computeCacheErroredSolves: boolean;
|
|
373
322
|
computeMaxQueueDepth: number;
|
|
374
323
|
computeQueueWaitMs: number;
|
|
375
324
|
computeDefinitionCacheBytes: number;
|
|
@@ -463,6 +412,5 @@ export declare class SolveEngine {
|
|
|
463
412
|
toWebResponse(outcome: SolveOutcome): Response;
|
|
464
413
|
private resolveDefinitionSource;
|
|
465
414
|
}
|
|
466
|
-
//#endregion
|
|
467
415
|
export { type ByteCacheRef, type ByteCacheStats, type ByteRefOutcome, 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, type SolveEngineDefinitionSource, type SolveEngineLimits, type SolveEngineOptions, type SolveEngineSolveArgs, type SolveEngineStats, type SolveEnvelope, type SolveInput, type SolveOutcome, type SolvePhaseMetrics, type SolvePipelineArgs, isDefinitionRef };
|
|
468
416
|
//# sourceMappingURL=server.d.cts.map
|
package/dist/server.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { t as SolveInput } from "./solve-input-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { DefinitionRef, SolveDefinition, SolveDefinition as SolveDefinition$1, isDefinitionRef } from "@selvajs/compute/core";
|
|
1
|
+
import { t as SolveInput } from "./solve-input-CUjVGYPC.js";
|
|
2
|
+
import { DataTree, GrasshopperClient, GrasshopperComputeResponse, SelvaEventTarget, SolveScheduler } from "@selvajs/compute/grasshopper";
|
|
3
|
+
import { DefinitionRef, DefinitionRef as DefinitionRef$1, SolveDefinition, SolveDefinition as SolveDefinition$1, isDefinitionRef } from "@selvajs/compute/core";
|
|
5
4
|
import { ILogger } from "@selvajs/platform";
|
|
6
|
-
//#region src/server/client-cache.d.ts
|
|
7
5
|
/**
|
|
8
6
|
* Opaque identity of a compute server — construct only via `serverIdentity()`.
|
|
9
7
|
* Kept opaque so "a server is now a pool of URLs behind one id" stays additive.
|
|
@@ -22,34 +20,6 @@ export declare function serverIdentity(server: Pick<ResolvedServer, 'id'>): Serv
|
|
|
22
20
|
interface CachedClient {
|
|
23
21
|
client: GrasshopperClient;
|
|
24
22
|
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
23
|
}
|
|
54
24
|
/**
|
|
55
25
|
* Debug verbosity: `false` is silent, `true` gives concise cache/timing logs
|
|
@@ -62,8 +32,6 @@ interface ClientCacheConfig {
|
|
|
62
32
|
/** Per-solve timeout forwarded to the scheduler (`ComputeLimits.solveDeadlineMs`). */
|
|
63
33
|
solveDeadlineMs: number;
|
|
64
34
|
cachesolve: boolean;
|
|
65
|
-
/** Only meaningful with `cachesolve`. */
|
|
66
|
-
cacheerroredsolves: boolean;
|
|
67
35
|
/** Reference large definitions by server cache key instead of re-uploading. */
|
|
68
36
|
reuseServerDefinitionCache: boolean;
|
|
69
37
|
/**
|
|
@@ -85,6 +53,11 @@ interface ClientCacheConfig {
|
|
|
85
53
|
* the cache (`ComputeLimits.computeSolveCacheBytes`, env `COMPUTE_SOLVE_CACHE_MB`).
|
|
86
54
|
*/
|
|
87
55
|
responseCacheMaxBytes: number;
|
|
56
|
+
/**
|
|
57
|
+
* Largest compute response body read; past it the download is cancelled (`too_large`).
|
|
58
|
+
* Default: no limit.
|
|
59
|
+
*/
|
|
60
|
+
maxResponseBytes?: number;
|
|
88
61
|
debug: ClientCacheDebug;
|
|
89
62
|
/** Max distinct warm compute servers before the LRU evicts the oldest. Default 16. */
|
|
90
63
|
maxWarmComputeServers?: number;
|
|
@@ -126,8 +99,6 @@ interface SolveCacheStats {
|
|
|
126
99
|
evictions: number;
|
|
127
100
|
}
|
|
128
101
|
export declare function createClientCache(config: ClientCacheConfig): ClientCache;
|
|
129
|
-
//#endregion
|
|
130
|
-
//#region src/server/definition-byte-cache.d.ts
|
|
131
102
|
/**
|
|
132
103
|
* In-process cache of `.gh` definition bytes, keyed by immutable **version id**.
|
|
133
104
|
*
|
|
@@ -180,16 +151,10 @@ interface DefinitionByteCache {
|
|
|
180
151
|
* entirely — every `load()` calls the loader and nothing is retained.
|
|
181
152
|
*/
|
|
182
153
|
export declare function createDefinitionByteCache(maxBytes: number): DefinitionByteCache;
|
|
183
|
-
//#endregion
|
|
184
|
-
//#region src/server/solve-pipeline.d.ts
|
|
185
154
|
/** Compute-response contract version. Bump (and document the change) whenever the envelope's shape changes in a way a consumer could observe. */
|
|
186
155
|
export declare const COMPUTE_CONTRACT_VERSION: 1;
|
|
187
156
|
export declare const COMPUTE_VERSION_HEADER = "X-Selva-Compute-Version";
|
|
188
|
-
type PipelineInput =
|
|
189
|
-
minimum?: number;
|
|
190
|
-
maximum?: number;
|
|
191
|
-
stepSize?: number;
|
|
192
|
-
};
|
|
157
|
+
type PipelineInput = SolveInput;
|
|
193
158
|
interface SolvePipelineArgs {
|
|
194
159
|
/**
|
|
195
160
|
* Raw `.gh` bytes, or a byte-cache `DefinitionRef` whose bytes the scheduler
|
|
@@ -288,8 +253,6 @@ type SolveOutcome = {
|
|
|
288
253
|
durationMs: number;
|
|
289
254
|
error: unknown;
|
|
290
255
|
};
|
|
291
|
-
/** The transformed input tree exactly as handed to the scheduler; `runSolvePipeline` calls this itself unless the caller supplies {@link SolvePipelineArgs.inputTree}. */
|
|
292
|
-
export declare function buildSolveInputTree(inputs: PipelineInput[], values: Record<string, unknown>): DataTree[];
|
|
293
256
|
export declare function runSolvePipeline(args: SolvePipelineArgs): Promise<SolveOutcome>;
|
|
294
257
|
/**
|
|
295
258
|
* Re-key a coalesced envelope to a single waiter's `Accept-Encoding`.
|
|
@@ -310,19 +273,8 @@ export declare function adaptEnvelopeToEncoding(envelope: SolveEnvelope, acceptE
|
|
|
310
273
|
body: string | Uint8Array;
|
|
311
274
|
headers: Record<string, string>;
|
|
312
275
|
};
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* `SchemaInput` has no `values`/`acceptedFormats` (those live on the layout item's config), so
|
|
317
|
-
* valueList/file inputs here carry just the selected value with no option list.
|
|
318
|
-
*/
|
|
319
|
-
export declare function transformInputParameter(input: SchemaInput & {
|
|
320
|
-
minimum?: number;
|
|
321
|
-
maximum?: number;
|
|
322
|
-
stepSize?: number;
|
|
323
|
-
}, value: unknown): InputParam;
|
|
324
|
-
//#endregion
|
|
325
|
-
//#region src/server/solve-cache-single-flight.d.ts
|
|
276
|
+
/** The input tree a solve sends: each input's value, or its schema default when it has none. */
|
|
277
|
+
export declare function buildSolveInputTree(inputs: SolveInput[], values: Record<string, unknown>): DataTree[];
|
|
326
278
|
/**
|
|
327
279
|
* In-process single-flight — dogpile protection for every solve.
|
|
328
280
|
*
|
|
@@ -358,8 +310,6 @@ interface SolveCacheSingleFlightOptions {
|
|
|
358
310
|
onJoin?: (key: string) => void;
|
|
359
311
|
}
|
|
360
312
|
export declare function createSolveCacheSingleFlight(options?: SolveCacheSingleFlightOptions): SolveCacheSingleFlight;
|
|
361
|
-
//#endregion
|
|
362
|
-
//#region src/server/solve-engine.d.ts
|
|
363
313
|
/**
|
|
364
314
|
* Subset of `ComputeLimits` (`@selvajs/server/compute`) the engine needs.
|
|
365
315
|
* Pass the resolved object through as-is — extra fields are ignored.
|
|
@@ -369,7 +319,6 @@ interface SolveEngineLimits {
|
|
|
369
319
|
computeResponseMaxBytes: number;
|
|
370
320
|
computeReuseDefinitionCache: boolean;
|
|
371
321
|
computeServerCachesolve: boolean;
|
|
372
|
-
computeCacheErroredSolves: boolean;
|
|
373
322
|
computeMaxQueueDepth: number;
|
|
374
323
|
computeQueueWaitMs: number;
|
|
375
324
|
computeDefinitionCacheBytes: number;
|
|
@@ -463,6 +412,5 @@ export declare class SolveEngine {
|
|
|
463
412
|
toWebResponse(outcome: SolveOutcome): Response;
|
|
464
413
|
private resolveDefinitionSource;
|
|
465
414
|
}
|
|
466
|
-
//#endregion
|
|
467
415
|
export { type ByteCacheRef, type ByteCacheStats, type ByteRefOutcome, 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, type SolveEngineDefinitionSource, type SolveEngineLimits, type SolveEngineOptions, type SolveEngineSolveArgs, type SolveEngineStats, type SolveEnvelope, type SolveInput, type SolveOutcome, type SolvePhaseMetrics, type SolvePipelineArgs, isDefinitionRef };
|
|
468
416
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{GrasshopperClient as e,TreeBuilder as t,isDefinitionRef as n,processInput as r,stableStringify as i}from"@selvajs/compute/grasshopper";import{ComputeError as a,ErrorCodes as o,enableDebugLogging as s,isDefinitionRef as c}from"@selvajs/compute/core";import{gunzipSync as l,gzip as u}from"node:zlib";import{promisify as d}from"node:util";import{NoopLogger as f}from"@selvajs/platform";function p(e,t){let n={id:e.id,name:e.nickname,nickname:e.nickname||null,description:e.description||``,paramType:e.paramType===`dynamicValueList`?`valueList`:e.paramType,treeAccess:e.inputStructure===`tree`,minimum:e.minimum??null,maximum:e.maximum??null,atLeast:1,atMost:1,stepSize:e.stepSize,default:t??e.default},i=r(n),a=t??e.default;return i.default==null&&a!=null&&(Array.isArray(a)||i.paramType===`ValueList`&&typeof a!=`object`)&&(i.default=a),i}const m=d(u),h=1,g=`X-Selva-Compute-Version`;function _(e,n){return t.fromInputParams(e.filter(e=>e.paramType).map(e=>p(e,n[e.id])))}async function v(e){let{client:t,signal:n}=e,r=performance.now(),i=e.inputTree??_(e.inputs,e.values),s=performance.now()-r,c=t.solveMeta.seq,l=t.rhinoTiming.seq,u,d=performance.now();try{u=await t.scheduler.solve(e.definitionSource,i,{signal:n,selvaevents:e.selvaEvents})}catch(t){let r=performance.now()-d,i=t instanceof Error&&t.name===`AbortError`||t instanceof a&&t.code===o.ABORTED,s=t instanceof a&&t.code===o.TIMEOUT_ERROR;if(i||s)return i&&n.aborted?{kind:`client_abort`,durationMs:r}:{kind:`timeout`,durationMs:r,message:`Solve exceeded the ${Math.round(e.solveDeadlineMs/1e3)}s deadline.`};if(t instanceof a){if(t.code===o.QUEUE_FULL)return{kind:`shed`,durationMs:r,reason:`queue_full`,retryAfterSeconds:1,message:`Compute server is at capacity. Retry shortly.`};if(t.code===o.QUEUE_TIMEOUT)return{kind:`shed`,durationMs:r,reason:`queue_timeout`,retryAfterSeconds:1,message:`Compute request waited too long in the queue. Retry shortly.`}}return{kind:`compute_error`,durationMs:r,error:t}}let f=performance.now()-d,p=performance.now(),h;try{h=JSON.stringify(u)}catch(e){if(e instanceof RangeError)return{kind:`too_large`};throw e}if(h.length>e.responseMaxBytes)return{kind:`too_large`};let v=performance.now()-p,y=/\bgzip\b/i.test(e.acceptEncoding)&&h.length>1024,x=null,S=0;if(y){let e=performance.now();x=await m(Buffer.from(h)),S=performance.now()-e}let C=performance.now()-e.loadStartMs,w=t.solveMeta.seq===c+1?t.solveMeta.last:null,T=w&&!w.fromCache&&t.rhinoTiming.seq===l+1?t.rhinoTiming.last:null,E={"Content-Type":`application/json`,"Server-Timing":b({defLoadMs:e.defLoadMs,treeBuildMs:s,solveMs:f,serializeMs:v,gzipMs:S,serverTotalMs:C,rhino:T,settle:w,byteRefOutcome:e.byteRefOutcome,prepMarks:e.prepMarks??[]}),[g]:`1`,Vary:`Accept-Encoding`},D={treeBuildMs:s,solveMs:f,serializeMs:v,gzipMs:S,serverTotalMs:C,serializedBytes:h.length,compressedBytes:x?.byteLength??null},O;return x?(E[`Content-Encoding`]=`gzip`,E[`Content-Length`]=String(x.byteLength),O={body:new Uint8Array(x),encoding:`gzip`,headers:E,result:u,metrics:D}):(E[`Content-Length`]=String(Buffer.byteLength(h)),O={body:h,headers:E,result:u,metrics:D}),{kind:`ok`,envelope:O,solveMs:f,errorCount:u.errors?.length??0,warningCount:u.warnings?.length??0}}function y(e,t){let n=/\bgzip\b/i.test(t);if(e.encoding===`gzip`&&!n){let t=l(e.body instanceof Uint8Array?e.body:Buffer.from(e.body)).toString(`utf8`),n={...e.headers};return delete n[`Content-Encoding`],n[`Content-Length`]=String(Buffer.byteLength(t)),{body:t,headers:n}}return{body:e.body,headers:e.headers}}function b(e){let t=`load;dur=${e.defLoadMs.toFixed(1)}, tree;dur=${e.treeBuildMs.toFixed(1)}, solve;dur=${e.solveMs.toFixed(1)}, serialize;dur=${e.serializeMs.toFixed(1)}, gzip;dur=${e.gzipMs.toFixed(1)}, total;dur=${e.serverTotalMs.toFixed(1)}`,n=e.rhino;if(n){let r=n.decode+n.solve+n.encode,i=Math.max(0,e.solveMs-r);t+=`, rhino_decode;dur=${n.decode.toFixed(1)}, rhino_solve;dur=${n.solve.toFixed(1)}, rhino_encode;dur=${n.encode.toFixed(1)}, compute_link;dur=${i.toFixed(1)}`}for(let[n,r]of e.prepMarks)t+=`, p_${n};dur=${r.toFixed(1)}`;let r=e.settle;r&&(t+=`, selva_cache;dur=${+!!r.fromCache}`,r.definitionReuploaded!==void 0&&(t+=`, def_reupload;dur=${+!!r.definitionReuploaded}`));let i=e.byteRefOutcome;if(i){let e=i.loaded?i.fromCache?`hit`:`miss`:`skipped`;t+=`, def_bytes;desc=${e}`}return t}function x(e){return e.id}const S=5e3;function C(t){let n=t.maxWarmComputeServers??16,r=new Map,i=new Map,a=new Map;t.debug===`verbose`&&s();let o=e=>{t.debug&&t.onDebugLog?.(e)};async function c(n,r){let i={last:null,seq:0},a={last:null,seq:0},s=t.debug===`verbose`,c={serverUrl:n.serverUrl,apiKey:n.apiKey,debug:s,cachesolve:t.cachesolve,cacheerroredsolves:t.cacheerroredsolves,onServerTiming:e=>{let n=e.decode??0,r=e.solve??0,a=e.encode??0;if(i.seq+=1,i.last={decode:n,solve:r,encode:a},!t.debug)return;let s=n<=1?`HIT (definition reused, no re-upload/parse)`:`miss (full parse)`,c=t.cachesolve&&r<=1?`HIT (cached result, solve skipped)`:t.cachesolve?`miss (solved fresh)`:`off`;o(`[Compute/rhino-cache] decode=${n}ms solve=${r}ms encode=${a}ms | definition-cache: ${s} | solve-cache (cachesolve): ${c}`)}};r&&(c.headers={"X-Selva-Definition":r});let l=await e.create(c),u=1,d=0,f=!1;async function p(e){if(!f){f=!0;try{let t=await Promise.resolve(l.serverStats?.getActiveChildren({initialize:!1})).catch(()=>null);d=Date.now();let n=!t||t<=0?1:t;if(n===u)return;o(`[Compute/client-cache] concurrency ${u} → ${n} (server child count)`),u=n,e?.setMaxConcurrent(n)}finally{f=!1}}}await p();let m=l.createScheduler({mode:`queue`,maxConcurrent:u,maxQueueDepth:t.maxQueueDepth||void 0,queueWaitMs:t.queueWaitMs||void 0,timeoutMs:t.solveDeadlineMs,cache:t.responseCacheMaxBytes>0&&{maxBytes:t.responseCacheMaxBytes},reuseServerDefinitionCache:t.reuseServerDefinitionCache,onSettle:(e,n)=>{if(a.seq+=1,n.status===`success`&&(n.fromCache!==!0&&Date.now()-d>=3e5&&p(m),a.last={fromCache:n.fromCache===!0,definitionReuploaded:n.definitionReuploaded},t.debug&&(o(`[Compute/selva-cache] ${n.fromCache?`HIT — served from cache (no compute call)`:`miss — went to Rhino.Compute`} (${Math.round(n.durationMs)}ms)`),n.fromCache||o(n.definitionReuploaded===!0?`[Compute/def-cache] miss — definition RE-UPLOADED (pointer was cold/stale)`:n.definitionReuploaded===!1?`[Compute/def-cache] HIT — definition reused from server cache (no upload)`:`[Compute/def-cache] n/a — reuse disabled or non-reusable definition`),!n.fromCache))){let e=n.response?.errors,r=Array.isArray(e)?e.length:0;if(r>0){let n=t.cacheerroredsolves?`cached (cacheerroredsolves on)`:`not cached (cacheerroredsolves off) — re-run until fixed`;o(`[Compute/rhino-cache] ${r} GH error(s); ${n}. First: ${JSON.stringify(e[0])}`)}}}});return{client:l,scheduler:m,rhinoTiming:i,solveMeta:a}}return{async getClient(e,t){let s=e.id,l=r.get(s);if(l)return r.delete(s),r.set(s,l),l;let u=i.get(s);if(u)return u;let d=a.get(s);if(d){if(Date.now()-d.at<S)throw o(`[Compute/client-cache] server ${s} failed to build <${S}ms ago — replaying error`),d.error;a.delete(s)}o(`[Compute/client-cache] miss — building warm client for server ${s}`);let f=(async()=>{let i;try{i=await c(e,t?.definitionGuid)}catch(e){throw a.set(s,{error:e,at:Date.now()}),e}if(r.size>=n){let e=r.keys().next().value;e!==void 0&&(o(`[Compute/client-cache] LRU evicted warm client for server ${e} (cap ${n})`),r.get(e)?.scheduler.dispose(),r.delete(e))}return r.set(s,i),i})().finally(()=>i.delete(s));return i.set(s,f),f},evict(e){let t=e;a.delete(t);let n=r.get(t);n&&(o(`[Compute/client-cache] evicted warm client for server ${t} (config change)`),n.scheduler.dispose(),r.delete(t))},solveCacheStats(){let e={warmClients:r.size,entries:0,bytes:0,hits:0,misses:0,evictions:0};for(let t of r.values()){let n=t.scheduler.cacheStats();e.entries+=n.entries,e.bytes+=n.bytes,e.hits+=n.hits,e.misses+=n.misses,e.evictions+=n.evictions}return e},clearSolveCaches(){o(`[Compute/client-cache] cleared solve caches on ${r.size} warm client(s)`);for(let e of r.values())e.scheduler.clearCache()},disposeAll(){o(`[Compute/client-cache] disposing all ${r.size} warm client(s)`);for(let e of r.values())e.scheduler.dispose();r.clear(),a.clear()}}}function w(e){let t=Math.max(0,Math.floor(e)),n=new Map,r=0,i={hits:0,misses:0,evictions:0};function a(e,t){n.delete(e),n.set(e,t)}function o(){for(;r>t&&n.size>0;){let e=n.keys().next().value,t=n.get(e);n.delete(e),r-=t.bytes.byteLength,i.evictions+=1}}function s(e,i){if(t===0||i.byteLength>t)return;let a=n.get(e);a&&(r-=a.bytes.byteLength),n.set(e,{bytes:i}),r+=i.byteLength,o()}return{getOrLoad(e,t){let r={loaded:!1,fromCache:!1};return{key:e,outcome:r,load:async()=>{let o=n.get(e);if(o)return a(e,o),i.hits+=1,r.loaded=!0,r.fromCache=!0,o.bytes;i.misses+=1,r.loaded=!0,r.fromCache=!1;let c=await t();return s(e,c),c}}},stats(){return{hits:i.hits,misses:i.misses,evictions:i.evictions,entries:n.size,bytes:r}},clear(){n.clear(),r=0}}}function T(e={}){let t=new Map;return{run(n,r,i){let a=t.get(n);if(a)return e.onJoin?.(n),a.notifyOwner?.(),a.promise;let o={notifyOwner:i,promise:void 0},s=(async()=>r())().finally(()=>{o.notifyOwner=void 0,t.get(n)===o&&t.delete(n)});return o.promise=s,t.set(n,o),s},inFlight(){return t.size}}}function E(e){return typeof e==`object`&&!!e&&!(e instanceof Uint8Array)&&`outcome`in e}function D(e){return typeof e==`object`&&!!e&&!(e instanceof Uint8Array)&&!(`outcome`in e)&&!(`key`in e)&&`versionId`in e}var O=class{limits;clientCache;byteCache;singleFlight;constructor(e){this.limits=e.limits;let t=e.logger??new f;this.clientCache=C({solveDeadlineMs:this.limits.solveDeadlineMs,cachesolve:this.limits.computeServerCachesolve,cacheerroredsolves:this.limits.computeCacheErroredSolves,reuseServerDefinitionCache:this.limits.computeReuseDefinitionCache,maxQueueDepth:this.limits.computeMaxQueueDepth,queueWaitMs:this.limits.computeQueueWaitMs,responseCacheMaxBytes:this.limits.computeSolveCacheBytes,debug:e.debug??!1,maxWarmComputeServers:e.maxWarmComputeServers,onDebugLog:e.onDebugLog??(e=>t.debug(e,{component:`Compute/client-cache`}))}),this.byteCache=w(this.limits.computeDefinitionCacheBytes),this.singleFlight=T({onJoin:e.onSolveCoalesced})}getClient(e,t){return this.clientCache.getClient(e,t)}definitionRef(e,t){return this.byteCache.getOrLoad(e,t)}evictServer(e){this.clientCache.evict(e)}clearSolveCaches(){this.clientCache.clearSolveCaches()}clearDefinitionCache(){this.byteCache.clear()}stats(){return{client:this.clientCache.solveCacheStats(),definitionBytes:this.byteCache.stats(),coalescing:{inFlight:this.singleFlight.inFlight()}}}async solve(e){let{definitionSource:t,byteRefOutcome:n,identity:r}=this.resolveDefinitionSource(e),a=await this.clientCache.getClient(e.server,{definitionGuid:e.definitionGuid}),o=_(e.inputs,e.values),s=`${r}:${e.server.id}:${i(o)}`,c=new AbortController,l=!1,u=()=>{l||c.abort()};e.signal.addEventListener(`abort`,u,{once:!0});let d=e.acceptEncoding??``,f;try{f=await this.singleFlight.run(s,()=>v({definitionSource:t,byteRefOutcome:n,inputs:e.inputs,values:e.values,inputTree:o,client:a,responseMaxBytes:this.limits.computeResponseMaxBytes,solveDeadlineMs:this.limits.solveDeadlineMs,acceptEncoding:d,selvaEvents:e.selvaEvents,signal:c.signal,loadStartMs:e.loadStartMs??performance.now(),defLoadMs:e.defLoadMs??0,prepMarks:e.prepMarks}),()=>{l=!0})}finally{e.signal.removeEventListener(`abort`,u)}if(f.kind!==`ok`)return f;let p=y(f.envelope,d);return{...f,envelope:{...f.envelope,body:p.body,headers:p.headers}}}toResponse(e,t){let n=(e,n)=>{t?.onError&&t.onError(e,n);let r={"Content-Type":`application/json`};return n.retryAfter!==void 0&&(r[`Retry-After`]=String(n.retryAfter)),{status:e,headers:r,body:JSON.stringify(n)}};switch(e.kind){case`ok`:return{status:200,headers:e.envelope.headers,body:e.envelope.body};case`timeout`:return n(504,{message:e.message});case`client_abort`:return n(499,{message:`Client closed request`});case`too_large`:return n(413,{message:`Solve result is too large to return. This usually means a file output exceeds the supported size.`});case`shed`:return n(503,{message:e.message,retryAfter:e.retryAfterSeconds});case`compute_error`:throw e.error}}toWebResponse(e){let t=this.toResponse(e);return new Response(typeof t.body==`string`?t.body:new Uint8Array(t.body),{status:t.status,headers:t.headers})}resolveDefinitionSource(e){let t=e.definitionSource;if(D(t)){let e=this.byteCache.getOrLoad(t.versionId,t.load);return{definitionSource:e,byteRefOutcome:e.outcome,identity:e.key}}if(E(t))return{definitionSource:t,byteRefOutcome:t.outcome,identity:t.key};if(n(t))return{definitionSource:t,identity:t.key};if(!e.definitionKey)throw Error(`SolveEngine.solve(): definitionKey is required when definitionSource is raw bytes or a string.`);return{definitionSource:t,identity:e.definitionKey}}};export{h as COMPUTE_CONTRACT_VERSION,g as COMPUTE_VERSION_HEADER,O as SolveEngine,y as adaptEnvelopeToEncoding,_ as buildSolveInputTree,C as createClientCache,w as createDefinitionByteCache,T as createSolveCacheSingleFlight,c as isDefinitionRef,v as runSolvePipeline,x as serverIdentity,p as transformInputParameter};
|
|
1
|
+
import{GrasshopperClient as e,TreeBuilder as t,stableStringify as n}from"@selvajs/compute/grasshopper";import{ComputeError as r,ErrorCodes as i,enableDebugLogging as a,isDefinitionRef as o,isDefinitionRef as s}from"@selvajs/compute/core";import{gunzipSync as c,gzip as l}from"node:zlib";import{promisify as u}from"node:util";import{NoopLogger as d}from"@selvajs/platform";function f(e,n){return t.fromValues(e.filter(e=>e.paramType).map(e=>({paramName:e.nickname,paramType:e.paramType,value:n[e.id]??e.default,minimum:e.minimum,maximum:e.maximum})))}const p=u(l),m=1,h=`X-Selva-Compute-Version`;async function g(e){let{client:t,signal:n}=e,a=performance.now(),o=e.inputTree??f(e.inputs,e.values),s=performance.now()-a,c=null,l,u,d=performance.now();try{let r=await t.scheduler.solveDetailed(e.definitionSource,o,{signal:n,selvaevents:e.selvaEvents,onServerTiming:e=>{c=e}});l=r.response,u={fromCache:r.fromCache,definitionReuploaded:r.definitionReuploaded}}catch(t){let a=performance.now()-d,o=t instanceof Error&&t.name===`AbortError`||t instanceof r&&t.code===i.ABORTED,s=t instanceof r&&t.code===i.TIMEOUT_ERROR;if(o||s)return o&&n.aborted?{kind:`client_abort`,durationMs:a}:{kind:`timeout`,durationMs:a,message:`Solve exceeded the ${Math.round(e.solveDeadlineMs/1e3)}s deadline.`};if(t instanceof r){if(t.code===i.RESPONSE_TOO_LARGE)return{kind:`too_large`};if(t.code===i.QUEUE_FULL)return{kind:`shed`,durationMs:a,reason:`queue_full`,retryAfterSeconds:1,message:`Compute server is at capacity. Retry shortly.`};if(t.code===i.QUEUE_TIMEOUT)return{kind:`shed`,durationMs:a,reason:`queue_timeout`,retryAfterSeconds:1,message:`Compute request waited too long in the queue. Retry shortly.`}}return{kind:`compute_error`,durationMs:a,error:t}}let m=performance.now()-d,g=performance.now(),_;try{_=JSON.stringify(l)}catch(e){if(e instanceof RangeError)return{kind:`too_large`};throw e}if(_.length>e.responseMaxBytes)return{kind:`too_large`};let y=performance.now()-g,b=/\bgzip\b/i.test(e.acceptEncoding)&&_.length>1024,x=null,S=0;if(b){let e=performance.now();x=await p(Buffer.from(_)),S=performance.now()-e}let C=performance.now()-e.loadStartMs,w={"Content-Type":`application/json`,"Server-Timing":v({defLoadMs:e.defLoadMs,treeBuildMs:s,solveMs:m,serializeMs:y,gzipMs:S,serverTotalMs:C,rhino:c,settle:u,byteRefOutcome:e.byteRefOutcome,prepMarks:e.prepMarks??[]}),[h]:`1`,Vary:`Accept-Encoding`},T={treeBuildMs:s,solveMs:m,serializeMs:y,gzipMs:S,serverTotalMs:C,serializedBytes:_.length,compressedBytes:x?.byteLength??null},E;return x?(w[`Content-Encoding`]=`gzip`,w[`Content-Length`]=String(x.byteLength),E={body:new Uint8Array(x),encoding:`gzip`,headers:w,result:l,metrics:T}):(w[`Content-Length`]=String(Buffer.byteLength(_)),E={body:_,headers:w,result:l,metrics:T}),{kind:`ok`,envelope:E,solveMs:m,errorCount:l.errors?.length??0,warningCount:l.warnings?.length??0}}function _(e,t){let n=/\bgzip\b/i.test(t);if(e.encoding===`gzip`&&!n){let t=c(e.body instanceof Uint8Array?e.body:Buffer.from(e.body)).toString(`utf8`),n={...e.headers};return delete n[`Content-Encoding`],n[`Content-Length`]=String(Buffer.byteLength(t)),{body:t,headers:n}}return{body:e.body,headers:e.headers}}function v(e){let t=`load;dur=${e.defLoadMs.toFixed(1)}, tree;dur=${e.treeBuildMs.toFixed(1)}, solve;dur=${e.solveMs.toFixed(1)}, serialize;dur=${e.serializeMs.toFixed(1)}, gzip;dur=${e.gzipMs.toFixed(1)}, total;dur=${e.serverTotalMs.toFixed(1)}`,n=e.rhino;if(n){let r=n.decode??0,i=n.solve??0,a=n.encode??0,o=Math.max(0,e.solveMs-r-i-a);t+=`, rhino_decode;dur=${r.toFixed(1)}, rhino_solve;dur=${i.toFixed(1)}, rhino_encode;dur=${a.toFixed(1)}, compute_link;dur=${o.toFixed(1)}, rhino_cache;dur=${+!!n.cacheHit}`,n.gh!==void 0&&(t+=`, rhino_gh;dur=${n.gh.toFixed(1)}`),n.recomputed&&(t+=`, rhino_recomputed;desc="${n.recomputed.ran}/${n.recomputed.total}"`)}for(let[n,r]of e.prepMarks)t+=`, p_${n};dur=${r.toFixed(1)}`;let r=e.settle;t+=`, selva_cache;dur=${+!!r.fromCache}`,r.definitionReuploaded!==void 0&&(t+=`, def_reupload;dur=${+!!r.definitionReuploaded}`);let i=e.byteRefOutcome;if(i){let e=i.loaded?i.fromCache?`hit`:`miss`:`skipped`;t+=`, def_bytes;desc=${e}`}return t}function y(e){return e.id}const b=5e3;function x(t){let n=t.maxWarmComputeServers??16,r=/* @__PURE__ */ new Map,i=/* @__PURE__ */ new Map,o=/* @__PURE__ */ new Map;t.debug===`verbose`&&a();let s=e=>{t.debug&&t.onDebugLog?.(e)};async function c(n,r){let i=t.debug===`verbose`,a={serverUrl:n.serverUrl,apiKey:n.apiKey,debug:i,cachesolve:t.cachesolve,maxResponseBytes:t.maxResponseBytes};t.debug&&(a.onServerTiming=e=>{if(e.cacheHit){s(`[Compute/rhino-cache] solve-cache (cachesolve): HIT (cached result, solve skipped)`);return}let n=e.decode??0,r=e.solve??0,i=e.encode??0,a=n<=1?`HIT (definition reused, no re-upload/parse)`:`miss (full parse)`,o=t.cachesolve?`miss (solved fresh)`:`off`,c=e.recomputed?` | ran ${e.recomputed.ran}/${e.recomputed.total} objects`:``;s(`[Compute/rhino-cache] decode=${n}ms solve=${r}ms encode=${i}ms | definition-cache: ${a} | solve-cache (cachesolve): ${o}${c}`)}),r&&(a.headers={"X-Selva-Definition":r});let o=await e.create(a),c=1,l=0,u=!1;async function d(e){if(!u){u=!0;try{let t=await Promise.resolve(o.serverStats?.getActiveChildren({initialize:!1})).catch(()=>null);l=Date.now();let n=!t||t<=0?1:t;if(n===c)return;s(`[Compute/client-cache] concurrency ${c} → ${n} (server child count)`),c=n,e?.setMaxConcurrent(n)}finally{u=!1}}}await d();let f=o.createScheduler({mode:`queue`,maxConcurrent:c,maxQueueDepth:t.maxQueueDepth||void 0,queueWaitMs:t.queueWaitMs||void 0,timeoutMs:t.solveDeadlineMs,cache:t.responseCacheMaxBytes>0&&{maxBytes:t.responseCacheMaxBytes},reuseServerDefinitionCache:t.reuseServerDefinitionCache,onSettle:(e,n)=>{if(n.status===`success`&&(n.fromCache!==!0&&Date.now()-l>=3e5&&d(f),t.debug&&(s(`[Compute/selva-cache] ${n.fromCache?`HIT — served from cache (no compute call)`:`miss — went to Rhino.Compute`} (${Math.round(n.durationMs)}ms)`),n.fromCache||s(n.definitionReuploaded===!0?`[Compute/def-cache] miss — definition RE-UPLOADED (pointer was cold/stale)`:n.definitionReuploaded===!1?`[Compute/def-cache] HIT — definition reused from server cache (no upload)`:`[Compute/def-cache] n/a — reuse disabled or non-reusable definition`),!n.fromCache))){let e=n.response?.errors,t=Array.isArray(e)?e.length:0;t>0&&s(`[Compute/rhino-cache] ${t} GH error(s); not cached by Rhino.Compute. First: ${JSON.stringify(e[0])}`)}}});return{client:o,scheduler:f}}return{async getClient(e,t){let a=e.id,l=r.get(a);if(l)return r.delete(a),r.set(a,l),l;let u=i.get(a);if(u)return u;let d=o.get(a);if(d){if(Date.now()-d.at<b)throw s(`[Compute/client-cache] server ${a} failed to build <${b}ms ago — replaying error`),d.error;o.delete(a)}s(`[Compute/client-cache] miss — building warm client for server ${a}`);let f=(async()=>{let i;try{i=await c(e,t?.definitionGuid)}catch(e){throw o.set(a,{error:e,at:Date.now()}),e}if(r.size>=n){let e=r.keys().next().value;e!==void 0&&(s(`[Compute/client-cache] LRU evicted warm client for server ${e} (cap ${n})`),r.get(e)?.scheduler.dispose(),r.delete(e))}return r.set(a,i),i})().finally(()=>i.delete(a));return i.set(a,f),f},evict(e){let t=e;o.delete(t);let n=r.get(t);n&&(s(`[Compute/client-cache] evicted warm client for server ${t} (config change)`),n.scheduler.dispose(),r.delete(t))},solveCacheStats(){let e={warmClients:r.size,entries:0,bytes:0,hits:0,misses:0,evictions:0};for(let t of r.values()){let n=t.scheduler.cacheStats();e.entries+=n.entries,e.bytes+=n.bytes,e.hits+=n.hits,e.misses+=n.misses,e.evictions+=n.evictions}return e},clearSolveCaches(){s(`[Compute/client-cache] cleared solve caches on ${r.size} warm client(s)`);for(let e of r.values())e.scheduler.clearCache()},disposeAll(){s(`[Compute/client-cache] disposing all ${r.size} warm client(s)`);for(let e of r.values())e.scheduler.dispose();r.clear(),o.clear()}}}function S(e){let t=Math.max(0,Math.floor(e)),n=/* @__PURE__ */ new Map,r=0,i={hits:0,misses:0,evictions:0};function a(e,t){n.delete(e),n.set(e,t)}function o(){for(;r>t&&n.size>0;){let e=n.keys().next().value,t=n.get(e);n.delete(e),r-=t.bytes.byteLength,i.evictions+=1}}function s(e,i){if(t===0||i.byteLength>t)return;let a=n.get(e);a&&(r-=a.bytes.byteLength),n.set(e,{bytes:i}),r+=i.byteLength,o()}return{getOrLoad(e,t){let r={loaded:!1,fromCache:!1};return{key:e,outcome:r,load:async()=>{let o=n.get(e);if(o)return a(e,o),i.hits+=1,r.loaded=!0,r.fromCache=!0,o.bytes;i.misses+=1,r.loaded=!0,r.fromCache=!1;let c=await t();return s(e,c),c}}},stats(){return{hits:i.hits,misses:i.misses,evictions:i.evictions,entries:n.size,bytes:r}},clear(){n.clear(),r=0}}}function C(e={}){let t=/* @__PURE__ */ new Map;return{run(n,r,i){let a=t.get(n);if(a)return e.onJoin?.(n),a.notifyOwner?.(),a.promise;let o={notifyOwner:i,promise:void 0},s=(async()=>r())().finally(()=>{o.notifyOwner=void 0,t.get(n)===o&&t.delete(n)});return o.promise=s,t.set(n,o),s},inFlight(){return t.size}}}function w(e){return typeof e==`object`&&!!e&&!(e instanceof Uint8Array)&&`outcome`in e}function T(e){return typeof e==`object`&&!!e&&!(e instanceof Uint8Array)&&!(`outcome`in e)&&!(`key`in e)&&`versionId`in e}var E=class{limits;clientCache;byteCache;singleFlight;constructor(e){this.limits=e.limits;let t=e.logger??new d;this.clientCache=x({solveDeadlineMs:this.limits.solveDeadlineMs,cachesolve:this.limits.computeServerCachesolve,reuseServerDefinitionCache:this.limits.computeReuseDefinitionCache,maxQueueDepth:this.limits.computeMaxQueueDepth,queueWaitMs:this.limits.computeQueueWaitMs,responseCacheMaxBytes:this.limits.computeSolveCacheBytes,maxResponseBytes:this.limits.computeResponseMaxBytes,debug:e.debug??!1,maxWarmComputeServers:e.maxWarmComputeServers,onDebugLog:e.onDebugLog??(e=>t.debug(e,{component:`Compute/client-cache`}))}),this.byteCache=S(this.limits.computeDefinitionCacheBytes),this.singleFlight=C({onJoin:e.onSolveCoalesced})}getClient(e,t){return this.clientCache.getClient(e,t)}definitionRef(e,t){return this.byteCache.getOrLoad(e,t)}evictServer(e){this.clientCache.evict(e)}clearSolveCaches(){this.clientCache.clearSolveCaches()}clearDefinitionCache(){this.byteCache.clear()}stats(){return{client:this.clientCache.solveCacheStats(),definitionBytes:this.byteCache.stats(),coalescing:{inFlight:this.singleFlight.inFlight()}}}async solve(e){let{definitionSource:t,byteRefOutcome:r,identity:i}=this.resolveDefinitionSource(e),a=await this.clientCache.getClient(e.server,{definitionGuid:e.definitionGuid}),o=f(e.inputs,e.values),s=`${i}:${e.server.id}:${n(o)}`,c=new AbortController,l=!1,u=()=>{l||c.abort()};e.signal.addEventListener(`abort`,u,{once:!0});let d=e.acceptEncoding??``,p;try{p=await this.singleFlight.run(s,()=>g({definitionSource:t,byteRefOutcome:r,inputs:e.inputs,values:e.values,inputTree:o,client:a,responseMaxBytes:this.limits.computeResponseMaxBytes,solveDeadlineMs:this.limits.solveDeadlineMs,acceptEncoding:d,selvaEvents:e.selvaEvents,signal:c.signal,loadStartMs:e.loadStartMs??performance.now(),defLoadMs:e.defLoadMs??0,prepMarks:e.prepMarks}),()=>{l=!0})}finally{e.signal.removeEventListener(`abort`,u)}if(p.kind!==`ok`)return p;let m=_(p.envelope,d);return{...p,envelope:{...p.envelope,body:m.body,headers:m.headers}}}toResponse(e,t){let n=(e,n)=>{t?.onError&&t.onError(e,n);let r={"Content-Type":`application/json`};return n.retryAfter!==void 0&&(r[`Retry-After`]=String(n.retryAfter)),{status:e,headers:r,body:JSON.stringify(n)}};switch(e.kind){case`ok`:return{status:200,headers:e.envelope.headers,body:e.envelope.body};case`timeout`:return n(504,{message:e.message});case`client_abort`:return n(499,{message:`Client closed request`});case`too_large`:return n(413,{message:`Solve result is too large to return. This usually means a file output exceeds the supported size.`});case`shed`:return n(503,{message:e.message,retryAfter:e.retryAfterSeconds});case`compute_error`:throw e.error}}toWebResponse(e){let t=this.toResponse(e);return new Response(typeof t.body==`string`?t.body:new Uint8Array(t.body),{status:t.status,headers:t.headers})}resolveDefinitionSource(e){let t=e.definitionSource;if(T(t)){let e=this.byteCache.getOrLoad(t.versionId,t.load);return{definitionSource:e,byteRefOutcome:e.outcome,identity:e.key}}if(w(t))return{definitionSource:t,byteRefOutcome:t.outcome,identity:t.key};if(s(t))return{definitionSource:t,identity:t.key};if(!e.definitionKey)throw Error(`SolveEngine.solve(): definitionKey is required when definitionSource is raw bytes or a string.`);return{definitionSource:t,identity:e.definitionKey}}};export{m as COMPUTE_CONTRACT_VERSION,h as COMPUTE_VERSION_HEADER,E as SolveEngine,_ as adaptEnvelopeToEncoding,f as buildSolveInputTree,x as createClientCache,S as createDefinitionByteCache,C as createSolveCacheSingleFlight,o as isDefinitionRef,g as runSolvePipeline,y as serverIdentity};
|
|
2
2
|
//# sourceMappingURL=server.js.map
|