brass-runtime 1.14.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +410 -135
  2. package/dist/agent/cli/main.cjs +49 -43
  3. package/dist/agent/cli/main.js +11 -5
  4. package/dist/agent/cli/main.mjs +11 -5
  5. package/dist/agent/index.cjs +8 -3
  6. package/dist/agent/index.d.ts +1 -1
  7. package/dist/agent/index.js +7 -2
  8. package/dist/agent/index.mjs +7 -2
  9. package/dist/{chunk-BMRF4FN6.js → chunk-2WC63LJK.mjs} +68 -242
  10. package/dist/chunk-3RG5ZIWI.js +10 -0
  11. package/dist/chunk-45F7OKGT.cjs +104 -0
  12. package/dist/chunk-5YOQOXEQ.cjs +2491 -0
  13. package/dist/chunk-7HUOJA4W.cjs +493 -0
  14. package/dist/{chunk-4N2JEK4H.mjs → chunk-7LVI2GIN.js} +252 -495
  15. package/dist/chunk-7TL2LHQJ.js +2491 -0
  16. package/dist/chunk-7V4KY4RL.mjs +104 -0
  17. package/dist/chunk-7XOPAB5Q.js +2143 -0
  18. package/dist/chunk-CCKHV5BT.mjs +193 -0
  19. package/dist/chunk-CY33PGEX.mjs +1110 -0
  20. package/dist/chunk-DJQ7OMMB.cjs +144 -0
  21. package/dist/chunk-F5EUMJL7.mjs +2143 -0
  22. package/dist/chunk-FM4W4QPL.js +193 -0
  23. package/dist/chunk-G3XGCZDQ.js +131 -0
  24. package/dist/{chunk-JT7D6M5H.js → chunk-G6IQOE4P.mjs} +252 -495
  25. package/dist/chunk-GOV47PPB.mjs +552 -0
  26. package/dist/chunk-H55LI6WY.js +93 -0
  27. package/dist/chunk-IJT6RRQ5.cjs +93 -0
  28. package/dist/chunk-J3H54ZRV.mjs +131 -0
  29. package/dist/chunk-JF4XXPZ5.cjs +552 -0
  30. package/dist/chunk-JNFRRJYH.cjs +2143 -0
  31. package/dist/chunk-JX3LZQJH.cjs +354 -0
  32. package/dist/chunk-K2T3DV26.mjs +93 -0
  33. package/dist/chunk-KCPT2D6G.js +552 -0
  34. package/dist/chunk-MWXMNYJS.cjs +1110 -0
  35. package/dist/{chunk-XTMZTVIT.cjs → chunk-N6VHMOWB.cjs} +140 -134
  36. package/dist/{chunk-WJESVBWN.js → chunk-NC5SDRYE.js} +16 -10
  37. package/dist/chunk-NOYZIMUJ.mjs +144 -0
  38. package/dist/chunk-NYL4D7SK.cjs +131 -0
  39. package/dist/chunk-OBGZSXTJ.cjs +10 -0
  40. package/dist/{chunk-UWMMYKVK.mjs → chunk-OOGJ73B6.js} +68 -242
  41. package/dist/chunk-PNVFW245.js +144 -0
  42. package/dist/chunk-PRWCB3QL.mjs +2491 -0
  43. package/dist/chunk-QY5FKYEQ.js +1110 -0
  44. package/dist/chunk-ROJC3NBJ.js +104 -0
  45. package/dist/chunk-SPUEME2B.cjs +343 -0
  46. package/dist/chunk-TDVMADDN.js +343 -0
  47. package/dist/chunk-TVN5I4U6.cjs +193 -0
  48. package/dist/chunk-U5KWK3PX.mjs +343 -0
  49. package/dist/chunk-VFIUZG7J.mjs +354 -0
  50. package/dist/{chunk-BKBFSOGT.cjs → chunk-WQ5QNU5R.cjs} +460 -703
  51. package/dist/chunk-XDZOO4L5.js +354 -0
  52. package/dist/chunk-Y6FXYEAI.mjs +10 -0
  53. package/dist/{chunk-MQF7HZ7Y.mjs → chunk-ZGLD4TVZ.mjs} +16 -10
  54. package/dist/client-CtFmoDvM.d.ts +645 -0
  55. package/dist/core/index.cjs +284 -0
  56. package/dist/core/index.d.ts +567 -0
  57. package/dist/core/index.js +284 -0
  58. package/dist/core/index.mjs +284 -0
  59. package/dist/{effect-DM56H743.d.ts → effect-CGNl5Rqp.d.ts} +118 -11
  60. package/dist/effectRunner-3ZHAD3LE.cjs +8 -0
  61. package/dist/effectRunner-A4CHJXJI.js +8 -0
  62. package/dist/effectRunner-OPUF6QRN.mjs +8 -0
  63. package/dist/http/index.cjs +4130 -890
  64. package/dist/http/index.d.ts +2289 -219
  65. package/dist/http/index.js +4116 -876
  66. package/dist/http/index.mjs +4116 -876
  67. package/dist/http/testing.cjs +159 -0
  68. package/dist/http/testing.d.ts +42 -0
  69. package/dist/http/testing.js +159 -0
  70. package/dist/http/testing.mjs +159 -0
  71. package/dist/index.cjs +305 -1168
  72. package/dist/index.d.ts +9 -701
  73. package/dist/index.js +176 -1039
  74. package/dist/index.mjs +176 -1039
  75. package/dist/observability/index.cjs +677 -0
  76. package/dist/observability/index.d.ts +79 -0
  77. package/dist/observability/index.js +677 -0
  78. package/dist/observability/index.mjs +677 -0
  79. package/dist/schedule-Fque9Abz.d.ts +70 -0
  80. package/dist/schema/index.cjs +25 -0
  81. package/dist/schema/index.d.ts +177 -0
  82. package/dist/schema/index.js +25 -0
  83. package/dist/schema/index.mjs +25 -0
  84. package/dist/server-C8hDXA74.d.ts +674 -0
  85. package/dist/stream-dvSs0QS5.d.ts +74 -0
  86. package/dist/tracer-B5tRH9H7.d.ts +230 -0
  87. package/dist/tracing-Dt9S_6V8.d.ts +148 -0
  88. package/package.json +37 -3
  89. package/dist/chunk-SKVY72E5.cjs +0 -667
  90. package/dist/stream-Oqe6WeLE.d.ts +0 -173
@@ -1,188 +1,178 @@
1
- import { A as Async, a as AsyncWithPromise } from '../effect-DM56H743.js';
2
- import { Z as ZStream, C as CircuitBreakerConfig, T as Tracer } from '../stream-Oqe6WeLE.js';
3
-
4
- type RetryPolicy = {
5
- maxRetries: number;
6
- baseDelayMs: number;
7
- maxDelayMs: number;
8
- /** Optional total retry budget, including request attempts and sleeps. */
9
- maxElapsedMs?: number;
10
- /** Defaults to true. When true, Retry-After is honored but capped by maxDelayMs/budget. */
11
- respectRetryAfter?: boolean;
12
- retryOnMethods?: HttpMethod[];
13
- retryOnStatus?: (status: number) => boolean;
14
- retryOnError?: (e: HttpError) => boolean;
15
- /** Strict engine selector for retry planning. Defaults to ts. */
16
- engine?: "ts" | "wasm";
17
- /** Back-compat knob: wasm=true maps to engine="wasm", wasm=false maps to engine="ts". */
18
- wasm?: boolean;
19
- };
20
-
21
- type HttpPoolKeyResolver = "global" | "origin" | "host" | ((req: HttpRequest, url: URL) => string);
22
- type HttpPoolConfig = {
23
- /** Max concurrent downstream calls per resolved key. */
24
- readonly concurrency?: number;
25
- /** Max queued waiters per key. `0` means fail fast when the pool is full. */
26
- readonly maxQueue?: number;
27
- /** Max time a request may wait for a pool slot before failing fast. */
28
- readonly queueTimeoutMs?: number;
29
- /** How to isolate pools. Default: `origin`; useful values: `global`, `host`, `origin`. */
30
- readonly key?: HttpPoolKeyResolver;
31
- /**
32
- * Strict engine selector for permit governance. Defaults to ts.
33
- * - ts: TypeScript permit pool.
34
- * - wasm: require BrassWasmHttpPermitPool from wasm/pkg; never falls back.
35
- */
36
- readonly engine?: "ts" | "wasm";
37
- /** Back-compat knob: wasm=true maps to engine="wasm", wasm=false maps to engine="ts". */
38
- readonly wasm?: boolean;
39
- };
40
- type HttpPoolKeyStats = {
41
- readonly key: string;
42
- readonly running: number;
43
- readonly queued: number;
44
- readonly concurrency: number;
45
- readonly maxQueue: number;
46
- readonly acquired: number;
47
- readonly released: number;
48
- readonly rejected: number;
49
- readonly queueTimeouts: number;
50
- readonly abortedWhileQueued: number;
51
- };
52
- type HttpPoolStats = {
53
- readonly running: number;
54
- readonly queued: number;
55
- readonly acquired: number;
56
- readonly released: number;
57
- readonly rejected: number;
58
- readonly queueTimeouts: number;
59
- readonly abortedWhileQueued: number;
60
- readonly wasm?: unknown;
61
- readonly keys: HttpPoolKeyStats[];
62
- };
63
- type HttpPoolLease = {
64
- readonly key: string;
65
- release: () => void;
66
- };
67
- declare function resolveHttpPoolKey(resolver: HttpPoolKeyResolver | undefined, req: HttpRequest, url: URL): string;
68
- declare class HttpConcurrencyPool {
69
- private readonly states;
70
- private readonly concurrency;
71
- private readonly maxQueue;
72
- private readonly queueTimeoutMs;
73
- readonly keyResolver: HttpPoolKeyResolver | undefined;
74
- private readonly wasm;
75
- private readonly wasmWaiters;
76
- private wasmTimer;
77
- private nextSubjectId;
78
- constructor(config?: HttpPoolConfig);
79
- acquire(key: string, signal: AbortSignal): Promise<HttpPoolLease>;
80
- stats(): HttpPoolStats;
81
- private acquireJs;
82
- private acquireWasm;
83
- private getState;
84
- private makeLease;
85
- private drain;
86
- private handleWasmGrants;
87
- private handleWasmTimeouts;
88
- private scheduleWasmTimeoutPump;
89
- private cleanupWaiter;
90
- private removeWaiter;
91
- private allocateSubjectId;
1
+ import { e as HttpClientFn, c as HttpError, b as HttpWireResponse, a as HttpRequest, d as HttpMiddleware, R as RetryPolicy, H as HttpClient, M as MakeHttpConfig, f as HttpWireResponseStream, g as HttpClientStream, h as HttpClientStats, i as AdaptiveLimiter, j as AdaptiveLimiterConfig, k as HttpMethod, l as AdaptiveLimiterPreset, m as HttpPoolConfig } from '../client-CtFmoDvM.js';
2
+ export { n as AdaptiveAcquireOptions, o as AdaptiveBaselineStrategy, p as AdaptiveHeadroomContext, q as AdaptiveHeadroomMode, r as AdaptiveHeadroomStrategy, s as AdaptiveLease, t as AdaptiveLimiterDiagnostics, u as AdaptiveLimiterKeySnapshot, A as AdaptiveLimiterStats, v as AdaptiveQueueLoadShedding, w as AdaptiveQueueStrategy, x as AdaptiveReleaseInfo, y as HttpBody, z as HttpClientStreamFn, B as HttpConcurrencyPool, C as HttpInit, D as HttpPoolKeyResolver, E as HttpPoolKeyStats, F as HttpPoolLease, G as HttpPoolStats, L as LimitChangeEvent, P as PerRequestRetryOverride, I as ResolvedConfig, J as RetryEvent, K as RetryScheduleInput, N as adaptiveLimiterPresets, O as backoffDelayMs, Q as decorate, S as defaultRetryOnError, T as defaultRetryOnStatus, U as defaultRetryableMethods, V as makeAdaptiveLimiterConfig, W as makeHttp, X as makeHttpStream, Y as normalizeHeadersInit, Z as normalizeRetryBudget, _ as resolveConfig, $ as resolveHttpPoolKey, a0 as retryAfterMs, a1 as validateConfig, a2 as withMiddleware, a3 as withRetry, a4 as withRetryStream } from '../client-CtFmoDvM.js';
3
+ import { A as Async, C as AsyncWithPromise, f as Runtime, h as RuntimeOptions } from '../effect-CGNl5Rqp.js';
4
+ import { SchemaIssue, JsonSchemaLike, AnyJsonSchemaLike, InferJsonSchema } from '../schema/index.js';
5
+ export { AnySchema, ConfigValidationError, InferObject, InferSchema, JsonValidator, JsonValidatorResult, NumberSchemaOptions, ObjectSchemaOptions, Schema, SchemaPathPart, SchemaResult, SchemaShape, SchemaValidationException, StringSchemaOptions, formatIssues, isSchema, makeSchemaIssue, parseConfig, s, schema, validateValue } from '../schema/index.js';
6
+ import { g as CircuitBreakerConfig, h as CircuitBreakerError } from '../tracer-B5tRH9H7.js';
7
+ import { T as Tracer, R as Resource } from '../tracing-Dt9S_6V8.js';
8
+ import { Server } from 'node:http';
9
+ import { AddressInfo } from 'node:net';
10
+ import { S as Schedule } from '../schedule-Fque9Abz.js';
11
+ import { O as Observability, p as HttpServerObservabilityOptions, Z as RuntimeHealthOptions } from '../server-C8hDXA74.js';
12
+ import '../stream-dvSs0QS5.js';
13
+
14
+ /**
15
+ * A fixed-size circular buffer that stores latency samples and supports
16
+ * efficient min and percentile computation using the nearest-rank method.
17
+ *
18
+ * The ring preserves eviction order while `sorted` keeps an exact sorted view.
19
+ * Recording is O(windowSize) because it removes/inserts by binary search +
20
+ * splice, but percentile reads are O(1) and avoid sorting on every call.
21
+ */
22
+ declare class LatencyWindow {
23
+ private readonly buffer;
24
+ private readonly sorted;
25
+ private readonly size;
26
+ private head;
27
+ private count;
28
+ constructor(size: number);
29
+ /**
30
+ * Record a latency sample. Discards non-positive, NaN, and Infinity values.
31
+ * Evicts the oldest sample when the buffer is full.
32
+ */
33
+ record(latencyMs: number): void;
34
+ /**
35
+ * Returns the minimum latency in the current window, or undefined if empty.
36
+ */
37
+ min(): number | undefined;
38
+ /**
39
+ * Computes the percentile using the nearest-rank method.
40
+ * Returns undefined if fewer than 2 samples are present.
41
+ * @param p - Percentile value in [0, 100]
42
+ */
43
+ percentile(p: number): number | undefined;
44
+ /**
45
+ * Computes a percentile where newer samples receive exponentially higher
46
+ * weight. A decay of 1 is identical to `percentile`; lower values adapt
47
+ * faster to recent latency shifts.
48
+ */
49
+ weightedPercentile(p: number, decay: number): number | undefined;
50
+ /** Number of samples currently in the window. */
51
+ get length(): number;
52
+ /** Maximum capacity of the window. */
53
+ get capacity(): number;
54
+ /** Returns a copy of the current samples (oldest to newest). */
55
+ samples(): number[];
56
+ private insertSorted;
57
+ private removeSorted;
58
+ private lowerBound;
92
59
  }
93
60
 
94
- type HttpError = {
95
- _tag: "Abort";
96
- } | {
97
- _tag: "BadUrl";
98
- message: string;
99
- } | {
100
- _tag: "FetchError";
101
- message: string;
102
- } | {
103
- _tag: "Timeout";
104
- timeoutMs: number;
105
- message: string;
106
- phase?: "request" | "queue" | "retry";
107
- } | {
108
- _tag: "PoolRejected";
109
- key: string;
110
- limit: number;
111
- message: string;
61
+ /**
62
+ * Exponential Moving Average computer.
63
+ * Smooths latency samples using the formula: ema = α * sample + (1 - α) * previous_ema
64
+ */
65
+ declare class EmaComputer {
66
+ private readonly alpha;
67
+ private current;
68
+ /**
69
+ * @param alpha - Smoothing factor in (0, 1]. Higher values weight recent samples more.
70
+ */
71
+ constructor(alpha: number);
72
+ /**
73
+ * Update the EMA with a new sample and return the new EMA value.
74
+ * On the first sample, the EMA is initialized to that sample.
75
+ */
76
+ update(sample: number): number;
77
+ /** Returns the current EMA value, or undefined if no samples have been recorded. */
78
+ get value(): number | undefined;
79
+ /** Resets the EMA state. */
80
+ reset(): void;
81
+ }
82
+
83
+ /**
84
+ * Computes the gradient as the ratio of minimum latency to current (smoothed) latency.
85
+ * A gradient < 1.0 indicates latency is increasing (saturation).
86
+ * A gradient >= 1.0 indicates latency is stable or decreasing.
87
+ */
88
+ declare function computeGradient(minLatency: number, currentLatency: number): number;
89
+ /**
90
+ * Computes the new concurrency limit based on the gradient.
91
+ *
92
+ * - If gradient < decreaseThreshold: decrease toward `currentLimit * gradient`
93
+ * - If gradient >= increaseThreshold: newLimit = currentLimit + headroom
94
+ * - Otherwise: hold the current limit
95
+ *
96
+ * Decreases are capped by `maxDecreaseRatio` so a single noisy latency sample
97
+ * cannot collapse concurrency.
98
+ *
99
+ * The result is clamped to [minBound, maxBound].
100
+ */
101
+ declare function computeNewLimit(currentLimit: number, gradient: number, headroom: number, minBound: number, maxBound: number, options?: {
102
+ readonly decreaseThreshold?: number;
103
+ readonly increaseThreshold?: number;
104
+ readonly maxDecreaseRatio?: number;
105
+ }): number;
106
+
107
+ type ValidationError = {
108
+ readonly _tag: "ValidationError";
109
+ readonly message: string;
110
+ readonly body: string;
111
+ readonly issues: readonly SchemaIssue[];
112
+ readonly phase?: "request" | "response";
113
+ readonly schema?: string;
114
+ };
115
+ type JsonDecodeResult<A> = {
116
+ readonly success: true;
117
+ readonly data: A;
112
118
  } | {
113
- _tag: "PoolTimeout";
114
- key: string;
115
- timeoutMs: number;
116
- message: string;
119
+ readonly success: false;
120
+ readonly error: ValidationError;
117
121
  };
118
- type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
119
- type HttpInit = Omit<RequestInit, "method" | "body" | "headers">;
120
- type HttpRequest = {
121
- method: HttpMethod;
122
- url: string;
123
- headers?: Record<string, string>;
124
- body?: string;
125
- init?: HttpInit;
126
- /** Per-request override for `MakeHttpConfig.timeoutMs`. */
122
+ declare function decodeJsonBody<A = unknown>(bodyText: string, validator?: JsonSchemaLike<A>, options?: {
123
+ readonly schemaName?: string;
124
+ }): JsonDecodeResult<A>;
125
+ declare function encodeJsonBodyEffect(bodyObj: unknown, validator?: JsonSchemaLike<any>, options?: {
126
+ readonly schemaName?: string;
127
+ }): Async<unknown, ValidationError, string>;
128
+ declare function decodeJsonBodyEffect<A = unknown>(bodyText: string, validator?: JsonSchemaLike<A>, options?: {
129
+ readonly schemaName?: string;
130
+ }): Async<unknown, ValidationError, A>;
131
+ declare function validatedJson<A>(client: HttpClientFn, validator: JsonSchemaLike<A>): (req: Parameters<HttpClientFn>[0]) => Async<unknown, HttpError | ValidationError, A>;
132
+ declare function validatedJsonResponse<A>(client: HttpClientFn, validator: JsonSchemaLike<A>): (req: Parameters<HttpClientFn>[0]) => Async<unknown, HttpError | ValidationError, HttpWireResponse & {
133
+ readonly body: A;
134
+ }>;
135
+
136
+ type InitNoMethodBody$1 = Omit<RequestInit, "method" | "body"> & {
127
137
  timeoutMs?: number;
128
- /** Optional stable key for downstream isolation. When omitted, the pool uses origin/host/global config. */
129
138
  poolKey?: string;
139
+ headers?: any;
130
140
  };
131
- type HttpWireResponse = {
132
- status: number;
133
- statusText: string;
134
- headers: Record<string, string>;
135
- bodyText: string;
136
- ms: number;
141
+ type JsonInitNoSchema$1 = InitNoMethodBody$1 & {
142
+ schema?: undefined;
143
+ schemaName?: string;
137
144
  };
138
- type HttpClientStats = {
139
- readonly inFlight: number;
140
- readonly started: number;
141
- readonly succeeded: number;
142
- readonly failed: number;
143
- readonly aborted: number;
144
- readonly timedOut: number;
145
- readonly poolRejected: number;
146
- readonly poolTimeouts: number;
147
- readonly lastDurationMs?: number;
148
- readonly pool?: HttpPoolStats;
149
- };
150
- type MakeHttpConfig = {
151
- baseUrl?: string;
152
- headers?: Record<string, string>;
153
- /** Request budget covering pool wait + fetch + body read. Disabled when omitted. */
154
- timeoutMs?: number;
155
- /** Downstream pool/concurrency limiter. Disabled by default to preserve existing behavior. */
156
- pool?: false | HttpPoolConfig;
145
+ type JsonInitWithSchema<Validator extends AnyJsonSchemaLike> = InitNoMethodBody$1 & {
146
+ schema: Validator;
147
+ schemaName?: string;
157
148
  };
158
- type HttpWireResponseStream = {
159
- status: number;
160
- statusText: string;
161
- headers: Record<string, string>;
162
- body: ZStream<unknown, HttpError, Uint8Array>;
163
- ms: number;
149
+ type PostJsonInitNoSchema$1 = AnyInitWithHeaders & {
150
+ schema?: undefined;
151
+ schemaName?: string;
152
+ bodySchema?: undefined;
153
+ bodySchemaName?: string;
164
154
  };
165
- type HttpClientStreamFn = (req: HttpRequest) => Async<unknown, HttpError, HttpWireResponseStream>;
166
- type HttpClientStream = HttpClientStreamFn & {
167
- stats: () => HttpClientStats;
155
+ type PostJsonInitWithBodySchema<BodyValidator extends AnyJsonSchemaLike> = AnyInitWithHeaders & {
156
+ schema?: undefined;
157
+ schemaName?: string;
158
+ bodySchema: BodyValidator;
159
+ bodySchemaName?: string;
168
160
  };
169
- type HttpClientFn = (req: HttpRequest) => Async<unknown, HttpError, HttpWireResponse>;
170
- type HttpMiddleware = (next: HttpClientFn) => HttpClientFn;
171
- type HttpClient = HttpClientFn & {
172
- with: (mw: HttpMiddleware) => HttpClient;
173
- stats: () => HttpClientStats;
161
+ type PostJsonInitWithSchema<Validator extends AnyJsonSchemaLike> = AnyInitWithHeaders & {
162
+ schema: Validator;
163
+ schemaName?: string;
164
+ bodySchema?: undefined;
165
+ bodySchemaName?: string;
174
166
  };
175
- declare const decorate: (run: HttpClientFn, stats?: () => HttpClientStats) => HttpClient;
176
- declare const withMiddleware: (mw: HttpMiddleware) => (c: HttpClient) => HttpClient;
177
- declare const normalizeHeadersInit: (h: any) => Record<string, string> | undefined;
178
- declare function makeHttpStream(cfg?: MakeHttpConfig): HttpClientStream;
179
- declare function makeHttp(cfg?: MakeHttpConfig): HttpClient;
180
- declare const withRetryStream: (p: RetryPolicy) => (next: HttpClientStream) => HttpClientStream;
181
-
182
- type InitNoMethodBody = Omit<RequestInit, "method" | "body"> & {
183
- timeoutMs?: number;
184
- poolKey?: string;
185
- headers?: any;
167
+ type PostJsonInitWithSchemaAndBody<Validator extends AnyJsonSchemaLike, BodyValidator extends AnyJsonSchemaLike> = AnyInitWithHeaders & {
168
+ schema: Validator;
169
+ schemaName?: string;
170
+ bodySchema: BodyValidator;
171
+ bodySchemaName?: string;
172
+ };
173
+ type AnyJsonInit = InitNoMethodBody$1 & {
174
+ schema?: AnyJsonSchemaLike;
175
+ schemaName?: string;
186
176
  };
187
177
  type HttpMeta = {
188
178
  request: HttpRequest;
@@ -214,9 +204,17 @@ type Dx = {
214
204
  request: (req: HttpRequest) => AsyncWithPromise<unknown, HttpError, HttpWireResponse>;
215
205
  get: (url: string, init?: AnyInitWithHeaders) => AsyncWithPromise<unknown, HttpError, HttpWireResponse>;
216
206
  post: (url: string, body?: string, init?: AnyInitWithHeaders) => AsyncWithPromise<unknown, HttpError, HttpWireResponse>;
217
- getText: (url: string, init?: InitNoMethodBody) => AsyncWithPromise<unknown, HttpError, HttpResponse<string>>;
218
- getJson: <A>(url: string, init?: InitNoMethodBody) => AsyncWithPromise<unknown, HttpError, HttpResponse<A>>;
219
- postJson: <A>(url: string, bodyObj: unknown, init?: AnyInitWithHeaders) => AsyncWithPromise<unknown, HttpError, HttpResponse<A>>;
207
+ getText: (url: string, init?: InitNoMethodBody$1) => AsyncWithPromise<unknown, HttpError, HttpResponse<string>>;
208
+ getJson: {
209
+ <Validator extends AnyJsonSchemaLike>(url: string, init: JsonInitWithSchema<Validator>): AsyncWithPromise<unknown, HttpError | ValidationError, HttpResponse<InferJsonSchema<Validator>>>;
210
+ <A = unknown>(url: string, init?: JsonInitNoSchema$1): AsyncWithPromise<unknown, HttpError | ValidationError, HttpResponse<A>>;
211
+ };
212
+ postJson: {
213
+ <Validator extends AnyJsonSchemaLike, BodyValidator extends AnyJsonSchemaLike>(url: string, bodyObj: InferJsonSchema<BodyValidator>, init: PostJsonInitWithSchemaAndBody<Validator, BodyValidator>): AsyncWithPromise<unknown, HttpError | ValidationError, HttpResponse<InferJsonSchema<Validator>>>;
214
+ <BodyValidator extends AnyJsonSchemaLike, A = unknown>(url: string, bodyObj: InferJsonSchema<BodyValidator>, init: PostJsonInitWithBodySchema<BodyValidator>): AsyncWithPromise<unknown, HttpError | ValidationError, HttpResponse<A>>;
215
+ <Validator extends AnyJsonSchemaLike>(url: string, bodyObj: unknown, init: PostJsonInitWithSchema<Validator>): AsyncWithPromise<unknown, HttpError | ValidationError, HttpResponse<InferJsonSchema<Validator>>>;
216
+ <A = unknown>(url: string, bodyObj: unknown, init?: PostJsonInitNoSchema$1): AsyncWithPromise<unknown, HttpError | ValidationError, HttpResponse<A>>;
217
+ };
220
218
  with: (mw: HttpMiddleware) => Dx;
221
219
  withRetry: (p: RetryPolicy) => Dx;
222
220
  wire: HttpClient;
@@ -228,11 +226,11 @@ declare function httpClientWithMeta(cfg?: MakeHttpConfig): {
228
226
  wire: HttpWireResponse;
229
227
  meta: HttpMeta;
230
228
  }>;
231
- get: (url: string, init?: InitNoMethodBody) => AsyncWithPromise<unknown, HttpError, {
229
+ get: (url: string, init?: InitNoMethodBody$1) => AsyncWithPromise<unknown, HttpError, {
232
230
  wire: HttpWireResponse;
233
231
  meta: HttpMeta;
234
232
  }>;
235
- getText: (url: string, init?: InitNoMethodBody) => AsyncWithPromise<unknown, HttpError, {
233
+ getText: (url: string, init?: InitNoMethodBody$1) => AsyncWithPromise<unknown, HttpError, {
236
234
  wire: HttpWireResponse;
237
235
  response: {
238
236
  status: number;
@@ -242,37 +240,30 @@ declare function httpClientWithMeta(cfg?: MakeHttpConfig): {
242
240
  };
243
241
  meta: HttpMeta;
244
242
  }>;
245
- getJson: <A>(url: string, init?: InitNoMethodBody) => AsyncWithPromise<unknown, HttpError, {
243
+ getJson: <A>(url: string, init?: AnyJsonInit) => AsyncWithPromise<unknown, HttpError | ValidationError, {
246
244
  wire: HttpWireResponse;
247
- response: {
248
- status: number;
249
- statusText: string;
250
- headers: Record<string, string>;
251
- body: A;
252
- };
245
+ response: HttpResponse<A>;
253
246
  meta: HttpMeta;
254
247
  }>;
255
- post: (url: string, body?: string, init?: InitNoMethodBody) => AsyncWithPromise<unknown, HttpError, {
248
+ post: (url: string, body?: string, init?: InitNoMethodBody$1) => AsyncWithPromise<unknown, HttpError, {
256
249
  wire: HttpWireResponse;
257
250
  meta: HttpMeta;
258
251
  }>;
259
- postJson: <A>(url: string, bodyObj: unknown, init?: HttpInit & {
260
- headers?: Record<string, string>;
261
- }) => AsyncWithPromise<unknown, HttpError, {
252
+ postJson: <A>(url: string, bodyObj: unknown, init?: AnyInitWithHeaders & {
253
+ schema?: AnyJsonSchemaLike;
254
+ schemaName?: string;
255
+ bodySchema?: AnyJsonSchemaLike;
256
+ bodySchemaName?: string;
257
+ }) => AsyncWithPromise<unknown, HttpError | ValidationError, {
262
258
  wire: HttpWireResponse;
263
- response: {
264
- status: number;
265
- statusText: string;
266
- headers: Record<string, string>;
267
- body: A;
268
- };
259
+ response: HttpResponse<A>;
269
260
  meta: HttpMeta;
270
261
  }>;
271
262
  };
272
263
  declare function httpClientStream(cfg?: MakeHttpConfig): {
273
264
  request: (req: HttpRequest) => AsyncWithPromise<unknown, HttpError, HttpWireResponseStream>;
274
- getStream: (url: string, init?: InitNoMethodBody) => AsyncWithPromise<unknown, HttpError, HttpWireResponseStream>;
275
- get: (url: string, init?: InitNoMethodBody) => AsyncWithPromise<unknown, HttpError, HttpWireResponseStream>;
265
+ getStream: (url: string, init?: InitNoMethodBody$1) => AsyncWithPromise<unknown, HttpError, HttpWireResponseStream>;
266
+ get: (url: string, init?: InitNoMethodBody$1) => AsyncWithPromise<unknown, HttpError, HttpWireResponseStream>;
276
267
  with: (mw: (n: HttpClientStream) => HttpClientStream) => /*elided*/ any;
277
268
  withRetry: (p: RetryPolicy) => /*elided*/ any;
278
269
  wire: HttpClientStream;
@@ -282,6 +273,10 @@ declare function httpClientStream(cfg?: MakeHttpConfig): {
282
273
  type HttpCircuitBreakerConfig = CircuitBreakerConfig & {
283
274
  /** Key resolver for per-origin circuit breakers. Default: per-origin. */
284
275
  perOrigin?: boolean;
276
+ /** Optional limiter to notify when this breaker is already/opened. Defaults to `next.adaptiveLimiter` when available. */
277
+ adaptiveLimiter?: Pick<AdaptiveLimiter, "keyResolver" | "markCircuitOpen">;
278
+ /** Optional resolver when the limiter key differs from the request URL origin/global fallback. */
279
+ adaptiveLimiterKey?: (req: HttpRequest) => string;
285
280
  };
286
281
  /**
287
282
  * HTTP middleware that wraps requests in a circuit breaker.
@@ -294,34 +289,2109 @@ declare function withCircuitBreaker(config?: HttpCircuitBreakerConfig): HttpMidd
294
289
  */
295
290
  declare function withTracing(tracer: Tracer): HttpMiddleware;
296
291
 
297
- type ValidationError = {
298
- _tag: "ValidationError";
299
- message: string;
292
+ type KnownHttpError = HttpError | ValidationError | CircuitBreakerError;
293
+ type KnownHttpErrorTag = KnownHttpError["_tag"];
294
+ type HttpErrorHandlers<R> = {
295
+ readonly [K in KnownHttpErrorTag]?: (error: Extract<KnownHttpError, {
296
+ readonly _tag: K;
297
+ }>) => R;
298
+ } & {
299
+ readonly default?: (error: unknown) => R;
300
+ };
301
+ declare function isHttpError(error: unknown): error is HttpError;
302
+ declare function isValidationError(error: unknown): error is ValidationError;
303
+ declare function isCircuitBreakerOpen(error: unknown): error is CircuitBreakerError;
304
+ declare function isKnownHttpError(error: unknown): error is KnownHttpError;
305
+ declare function matchHttpError<R>(error: unknown, handlers: HttpErrorHandlers<R>): R | undefined;
306
+ declare function formatHttpError(error: unknown): string;
307
+
308
+ /**
309
+ * Developer-provided function that defines how to combine multiple requests
310
+ * into one and split the response back.
311
+ *
312
+ * @typeParam K - The item type extracted from individual requests, ensuring
313
+ * type consistency between coalesce and split.
314
+ */
315
+ type BatchFunction<K> = {
316
+ /**
317
+ * Combines multiple HttpRequest objects into a single batched HttpRequest.
318
+ * The items array corresponds 1:1 with the requests array.
319
+ */
320
+ coalesce: (requests: readonly HttpRequest[]) => HttpRequest;
321
+ /**
322
+ * Splits the batched response back into individual responses.
323
+ * Must return an array of the same length as the original requests array.
324
+ */
325
+ split: (response: HttpWireResponse, requests: readonly HttpRequest[]) => HttpWireResponse[];
326
+ };
327
+ /**
328
+ * Configuration for the Batch_Middleware.
329
+ *
330
+ * @typeParam K - The item type linking coalesce and split operations.
331
+ */
332
+ type BatchConfig<K = unknown> = {
333
+ /** The batch function defining coalesce/split logic. Required. */
334
+ batch: BatchFunction<K>;
335
+ /**
336
+ * Time window in milliseconds to collect requests before dispatching.
337
+ * Valid range: [1, 5000]. Required.
338
+ */
339
+ windowMs: number;
340
+ /**
341
+ * Maximum number of requests in a single batch.
342
+ * When reached, the batch dispatches immediately.
343
+ * Valid range: [2, 10000]. Required.
344
+ */
345
+ maxBatchSize: number;
346
+ /**
347
+ * Computes the Batch_Key from a request.
348
+ * Requests with the same key are batched together.
349
+ * Return empty string to bypass batching for a request.
350
+ * Throwing bypasses batching for that request.
351
+ */
352
+ batchKey: (req: HttpRequest) => string;
353
+ };
354
+ /**
355
+ * Optional observer callback for batch lifecycle events.
356
+ */
357
+ type BatchEventCallback = (event: {
358
+ type: "batch-hit" | "batch-dispatch";
359
+ batchKey: string;
360
+ batchSize?: number;
361
+ }) => void;
362
+ /**
363
+ * Creates a Batch_Middleware that collects requests by Batch_Key during
364
+ * a time window and dispatches them as a single batched request.
365
+ *
366
+ * @typeParam K - Inferred from the provided BatchFunction.
367
+ * @param config - Batch configuration.
368
+ * @param onEvent - Optional lifecycle event observer.
369
+ * @returns An HttpMiddleware conforming to (next: HttpClientFn) => HttpClientFn.
370
+ */
371
+ declare function withBatch<K>(config: BatchConfig<K>, onEvent?: BatchEventCallback): HttpMiddleware;
372
+
373
+ /**
374
+ * Status of a pre-warm probe result.
375
+ */
376
+ type PrewarmResultStatus = "warmed" | "already-warm" | "failed" | "cancelled";
377
+ /**
378
+ * Result of a single pre-warm probe operation.
379
+ */
380
+ type PrewarmResult = {
381
+ /** The origin that was probed. */
382
+ origin: string;
383
+ /** Outcome of the probe operation. */
384
+ status: PrewarmResultStatus;
385
+ /** Duration of the probe in milliseconds (0 for "already-warm"). */
386
+ durationMs: number;
387
+ /** Error information if status is "failed". */
388
+ error?: string;
389
+ };
390
+ /**
391
+ * Event types emitted by the PrewarmManager during connection state changes.
392
+ */
393
+ type PrewarmEventType = "connection-warmed" | "connection-expired" | "connection-failed" | "connection-cancelled";
394
+ /**
395
+ * An event emitted by the PrewarmManager to notify observers of connection state changes.
396
+ */
397
+ type PrewarmEvent = {
398
+ /** The type of prewarm event. */
399
+ type: PrewarmEventType;
400
+ /** The origin associated with the event. */
401
+ origin: string;
402
+ /** Timestamp (ms) when the event occurred. */
403
+ timestamp: number;
404
+ /** Duration of the probe in milliseconds, if applicable. */
405
+ durationMs?: number;
406
+ /** Error information, if applicable. */
407
+ error?: string;
408
+ };
409
+ /**
410
+ * Per-origin connection state in the state machine.
411
+ */
412
+ type PrewarmOriginStatus = "idle" | "probing" | "warm" | "expired";
413
+ /**
414
+ * State information for a single managed origin.
415
+ */
416
+ type PrewarmOriginState = {
417
+ /** The origin string. */
418
+ origin: string;
419
+ /** Current state of the origin. */
420
+ status: PrewarmOriginStatus;
421
+ /** Timestamp of the last successful probe, if any. */
422
+ lastProbeAt?: number;
423
+ /** Timestamp until which the connection is considered warm. */
424
+ warmUntil?: number;
425
+ };
426
+ /**
427
+ * Snapshot of all managed origins and their current states.
428
+ */
429
+ type PrewarmStatusSnapshot = {
430
+ /** State of each managed origin. */
431
+ origins: PrewarmOriginState[];
432
+ };
433
+ /**
434
+ * Configuration for creating a PrewarmManager.
435
+ */
436
+ type PrewarmConfig = {
437
+ /** Origins to pre-warm. Each must be a valid URL origin (scheme + host + optional port). */
438
+ origins: string[];
439
+ /** Keep-alive duration in ms. Default: 55000. */
440
+ keepAliveDurationMs?: number;
441
+ /** Max concurrent in-flight probes. Default: 4. */
442
+ budget?: number;
443
+ /** Probe timeout in ms. Default: 5000. */
444
+ probeTimeoutMs?: number;
445
+ /** Auto-refresh expired connections. Default: false. */
446
+ autoRefresh?: boolean;
447
+ /** Route probes through the Wire_Client pool. Default: false. */
448
+ useClientPool?: boolean;
449
+ /** Optional Wire_Client to use when useClientPool is true. */
450
+ client?: HttpClientFn;
451
+ /** Event observer callback. */
452
+ onEvent?: (event: PrewarmEvent) => void;
453
+ };
454
+
455
+ /**
456
+ * Configuration for the prewarm layer within the lifecycle client.
457
+ *
458
+ * When provided to `makeLifecycleClient`, the lifecycle client creates and manages
459
+ * a PrewarmManager internally, triggering pre-warming based on the `afterResponse` hook.
460
+ */
461
+ type PrewarmLifecycleConfig = {
462
+ /** Origins to pre-warm at client creation. */
463
+ origins?: string[];
464
+ /** Hook called after each successful response to determine origins to pre-warm. */
465
+ afterResponse?: (response: HttpWireResponse, request: HttpRequest) => string[];
466
+ /** Keep-alive duration in ms. Default: 55000. */
467
+ keepAliveDurationMs?: number;
468
+ /** Max concurrent in-flight probes. Default: 4. */
469
+ budget?: number;
470
+ /** Probe timeout in ms. Default: 5000. */
471
+ probeTimeoutMs?: number;
472
+ /** Auto-refresh expired connections. Default: false. */
473
+ autoRefresh?: boolean;
474
+ /** Route probes through the Wire_Client pool. Default: false. */
475
+ useClientPool?: boolean;
476
+ /** Event observer for prewarm events. */
477
+ onEvent?: (event: PrewarmEvent) => void;
478
+ };
479
+ /**
480
+ * Configuration for the deduplication layer.
481
+ *
482
+ * When enabled, the dedup layer collapses concurrent identical requests into a single
483
+ * in-flight Async_Effect, sharing the response across all callers with the same Cache_Key.
484
+ *
485
+ * @property {function} [dedupKey] - Custom key function that computes a dedup key from an HttpRequest.
486
+ * When provided, overrides the default Cache_Key computation. Default: undefined (uses default key derivation).
487
+ */
488
+ type DedupConfig$1 = {
489
+ /** Custom key function. When provided, overrides default Cache_Key computation. */
490
+ dedupKey?: (req: HttpRequest) => string;
491
+ /** Internal lifecycle observer. Public callers should prefer LifecycleClientConfig.onEvent. */
492
+ onEvent?: (event: {
493
+ type: "dedup-hit" | "dedup-miss" | "dedup-active";
494
+ cacheKey?: string;
495
+ active?: number;
496
+ }) => void;
497
+ };
498
+ /**
499
+ * Configuration for the response cache layer.
500
+ *
501
+ * Controls how responses are stored and retrieved from the in-memory LRU cache.
502
+ * Each cached entry is keyed by its deterministic Cache_Key.
503
+ *
504
+ * @property {number} [ttlSeconds] - Time-to-live in seconds for cached entries. Default: 60. Valid range: [1, 86400].
505
+ * @property {number} [maxEntries] - Maximum number of cached entries before LRU eviction. Default: 1024. Valid range: >= 1.
506
+ * @property {boolean} [staleWhileRevalidate] - When true, serves stale cache entries while revalidating in the background. Default: false.
507
+ * @property {function} [cachePolicy] - Custom cache policy function that determines cacheability and optional TTL override for a given request/response pair. Default: undefined (uses built-in policy).
508
+ * @property {string[]} [cacheRelevantHeaders] - Additional HTTP headers to include in Cache_Key computation beyond the defaults. Default: undefined (uses DEFAULT_CACHE_RELEVANT_HEADERS).
509
+ */
510
+ type CacheConfig$1 = {
511
+ /** Time-to-live in seconds. Default: 60. Range: [1, 86400]. */
512
+ ttlSeconds?: number;
513
+ /** Maximum number of cached entries. Default: 1024. Minimum: 1. */
514
+ maxEntries?: number;
515
+ /** Enable stale-while-revalidate. Default: false. */
516
+ staleWhileRevalidate?: boolean;
517
+ /** Custom cache policy function. */
518
+ cachePolicy?: (req: HttpRequest, res: HttpWireResponse) => CachePolicyResult$1;
519
+ /** Additional headers to include in Cache_Key computation. */
520
+ cacheRelevantHeaders?: string[];
521
+ /** Cache-specific observer for stale revalidation failures. */
522
+ onEvent?: (event: {
523
+ type: string;
524
+ cacheKey?: string;
525
+ error?: any;
526
+ }) => void;
527
+ /** Internal lifecycle observer. Public callers should prefer LifecycleClientConfig.onEvent. */
528
+ onLifecycleEvent?: (event: {
529
+ type: "cache-hit" | "cache-miss" | "cache-eviction";
530
+ cacheKey?: string;
531
+ count?: number;
532
+ }) => void;
533
+ };
534
+ /**
535
+ * Result of a custom cache policy evaluation.
536
+ *
537
+ * Returned by the `cachePolicy` function in {@link CacheConfig} to control
538
+ * whether a response should be stored in the cache and for how long.
539
+ *
540
+ * @property {boolean} cacheable - Whether the response should be cached. Required.
541
+ * @property {number} [ttlSeconds] - Optional TTL override in seconds. When provided, takes precedence over the global CacheConfig ttlSeconds. Valid range: [1, 86400].
542
+ */
543
+ type CachePolicyResult$1 = {
544
+ /** Whether the response should be cached. */
545
+ cacheable: boolean;
546
+ /** Optional TTL override in seconds. */
547
+ ttlSeconds?: number;
548
+ };
549
+ /**
550
+ * Configuration for the priority scheduler layer.
551
+ *
552
+ * The priority scheduler orders outgoing requests by priority level and limits
553
+ * concurrency to prevent overwhelming the downstream Wire_Client.
554
+ *
555
+ * @property {number} [concurrency] - Maximum concurrent requests dispatched by the priority scheduler. Default: 32. Valid range: >= 1.
556
+ * @property {number} [queueTimeoutMs] - Queue timeout in milliseconds for priority-queued requests. When a request waits longer than this value, it is rejected. Default: undefined (no timeout).
557
+ */
558
+ type PriorityConfig$1 = {
559
+ /** Maximum concurrent requests dispatched by the priority scheduler. Default: 32. Valid range: >= 1. */
560
+ concurrency?: number;
561
+ /** Queue timeout in ms for priority-queued requests. Default: no timeout. */
562
+ queueTimeoutMs?: number;
563
+ /** Internal lifecycle observer. Public callers should prefer LifecycleClientConfig.onEvent. */
564
+ onEvent?: (event: {
565
+ type: "queue-enqueue" | "queue-dispatch";
566
+ priority: number;
567
+ }) => void;
568
+ };
569
+ /**
570
+ * Configuration for creating a lifecycle client.
571
+ *
572
+ * Extends MakeHttpConfig with optional lifecycle layer configurations.
573
+ * Each layer (dedup, cache, priority) can be configured with an options object
574
+ * or explicitly disabled by setting it to `false`. When omitted, the layer is disabled
575
+ * (zero-cost when disabled).
576
+ *
577
+ * @property {DedupConfig | false} [dedup] - Dedup layer configuration. Set to an object to enable with options, or `false` to explicitly disable. Default: undefined (disabled).
578
+ * @property {CacheConfig | false} [cache] - Cache layer configuration. Set to an object to enable with options, or `false` to explicitly disable. Default: undefined (disabled).
579
+ * @property {PriorityConfig | false} [priority] - Priority scheduler configuration. Set to an object to enable with options, or `false` to explicitly disable. Default: undefined (disabled).
580
+ * @property {function} [onEvent] - Optional event observer callback invoked for each {@link LifecycleEvent} during request processing. Default: undefined.
581
+ */
582
+ type LifecycleClientConfig = MakeHttpConfig & {
583
+ /** Dedup layer config. Set to `false` to explicitly disable. Default: undefined (disabled). */
584
+ dedup?: DedupConfig$1 | false;
585
+ /** Batch layer config. Set to `false` to explicitly disable. Default: undefined (disabled). */
586
+ batch?: BatchConfig | false;
587
+ /** Cache layer config. Set to `false` to explicitly disable. Default: undefined (disabled). */
588
+ cache?: CacheConfig$1 | false;
589
+ /** Priority scheduler config. Set to `false` to explicitly disable. Default: undefined (disabled). */
590
+ priority?: PriorityConfig$1 | false;
591
+ /** Retry policy. Set to `false` to explicitly disable. Default: undefined (disabled). */
592
+ retry?: RetryPolicy | false;
593
+ /** Prewarm layer config. Set to `false` to explicitly disable. Default: undefined (disabled). */
594
+ prewarm?: PrewarmLifecycleConfig | false;
595
+ /** Adaptive concurrency limiter config. Set to `false` to explicitly disable. Default: undefined (disabled). */
596
+ adaptiveLimiter?: AdaptiveLimiterConfig | false;
597
+ /** Optional event observer for lifecycle events. */
598
+ onEvent?: (event: LifecycleEvent) => void;
599
+ };
600
+ /**
601
+ * The lifecycle client interface.
602
+ *
603
+ * A callable HTTP client function (Wire_Client wrapper) with additional lifecycle
604
+ * management methods. Supports middleware composition via `.with()`, statistics
605
+ * via `.stats()`, and bulk cancellation via `.cancelAll()`.
606
+ *
607
+ * @property {function} with - Apply middleware, returning a new LifecycleClient with the middleware applied.
608
+ * @property {function} stats - Return a frozen snapshot of {@link LifecycleStats}.
609
+ * @property {function} cancelAll - Cancel all in-flight and queued requests, returning an Async_Effect that resolves when cancellation is complete.
610
+ * @property {object} cache - Cache management methods for manual invalidation.
611
+ * @property {function} cache.invalidate - Invalidate a specific cache entry by its Cache_Key.
612
+ * @property {function} cache.clear - Clear all cache entries.
613
+ */
614
+ type LifecycleClient = HttpClientFn & {
615
+ /** Apply middleware, returning a new LifecycleClient with the middleware applied. */
616
+ with: (mw: HttpMiddleware) => LifecycleClient;
617
+ /** Return a frozen snapshot of lifecycle statistics. */
618
+ stats: () => LifecycleStats;
619
+ /** Cancel all in-flight and queued requests. Returns an Async_Effect that resolves when complete. */
620
+ cancelAll: () => Async<unknown, never, void>;
621
+ /** Cancel active work and release owned resources such as adaptive limiter timers. */
622
+ shutdown: () => Async<unknown, never, void>;
623
+ /** Adaptive limiter owned by the underlying wire client, when enabled. */
624
+ adaptiveLimiter?: AdaptiveLimiter;
625
+ /** Cache management methods. */
626
+ cache: {
627
+ /** Invalidate a specific cache entry by Cache_Key. */
628
+ invalidate: (key: string) => void;
629
+ /** Clear all cache entries. */
630
+ clear: () => void;
631
+ };
632
+ };
633
+ /**
634
+ * Lifecycle event types emitted during request processing.
635
+ *
636
+ * Each value represents a distinct point in the lifecycle pipeline:
637
+ * - `"request-start"` — Emitted when a request enters the lifecycle pipeline.
638
+ * - `"request-end"` — Emitted when a request completes (success or failure).
639
+ * - `"cache-hit"` — Emitted when a response is served from the cache.
640
+ * - `"cache-miss"` — Emitted when a request misses the cache and proceeds to the Wire_Client.
641
+ * - `"dedup-hit"` — Emitted when a request is collapsed into an existing in-flight Async_Effect.
642
+ * - `"dedup-miss"` — Emitted when a request initiates a new in-flight Async_Effect (no existing match).
643
+ * - `"queue-enqueue"` — Emitted when a request is enqueued in the priority scheduler.
644
+ * - `"queue-dispatch"` — Emitted when a queued request is dispatched to the Wire_Client.
645
+ * - `"retry"` — Emitted when the retry middleware schedules another attempt.
646
+ */
647
+ type LifecycleEventType = "request-start" | "request-end" | "cache-hit" | "cache-miss" | "dedup-hit" | "dedup-miss" | "queue-enqueue" | "queue-dispatch" | "retry" | "batch-hit" | "batch-dispatch" | "limit-change";
648
+ /**
649
+ * A lifecycle event emitted to the onEvent observer.
650
+ *
651
+ * Provides observability into the lifecycle pipeline by reporting events
652
+ * as they occur during request processing.
653
+ *
654
+ * @property {LifecycleEventType} type - The type of lifecycle event. Required.
655
+ * @property {number} timestamp - Timestamp in milliseconds (from `Date.now()`) when the event occurred. Required.
656
+ * @property {string} [cacheKey] - The Cache_Key associated with the event, if applicable (present for cache and dedup events).
657
+ * @property {number} [priority] - Priority level associated with the event, if applicable (present for queue events). Valid range: 0-9.
658
+ * @property {number} [attempt] - Zero-based retry attempt, if applicable.
659
+ * @property {number} [delayMs] - Retry delay in milliseconds, if applicable.
660
+ * @property {number} [status] - HTTP status that triggered retry, if applicable.
661
+ * @property {string} [errorTag] - HttpError tag that triggered retry, if applicable.
662
+ */
663
+ type LifecycleEvent = {
664
+ /** The type of lifecycle event. */
665
+ type: LifecycleEventType;
666
+ /** Timestamp (ms) when the event occurred. */
667
+ timestamp: number;
668
+ /** Cache_Key associated with the event, if applicable. */
669
+ cacheKey?: string;
670
+ /** Priority level associated with the event, if applicable. Valid range: 0-9. */
671
+ priority?: number;
672
+ /** Batch_Key associated with the event, if applicable. Present for batch events. */
673
+ batchKey?: string;
674
+ /** Number of requests in the batch, if applicable. Present for batch-dispatch events. */
675
+ batchSize?: number;
676
+ /** Zero-based retry attempt, if applicable. */
677
+ attempt?: number;
678
+ /** Retry delay in milliseconds, if applicable. */
679
+ delayMs?: number;
680
+ /** HTTP status that triggered retry, if applicable. */
681
+ status?: number;
682
+ /** HttpError tag that triggered retry, if applicable. */
683
+ errorTag?: string;
684
+ /** Previous concurrency limit, present for limit-change events. */
685
+ previousLimit?: number;
686
+ /** New concurrency limit, present for limit-change events. */
687
+ newLimit?: number;
688
+ /** Gradient value, present for limit-change events. */
689
+ gradient?: number;
690
+ /** Smoothed latency value, present for limit-change events. */
691
+ smoothedLatency?: number;
692
+ };
693
+ /**
694
+ * Lifecycle statistics snapshot.
695
+ *
696
+ * All counters start at zero and increase monotonically. Returned as a frozen
697
+ * object by {@link LifecycleClient.stats}.
698
+ *
699
+ * @property {number} cacheHits - Number of cache hits (responses served from cache). Default: 0.
700
+ * @property {number} cacheMisses - Number of cache misses (requests forwarded to Wire_Client). Default: 0.
701
+ * @property {number} cacheEvictions - Number of cache evictions triggered by LRU policy. Default: 0.
702
+ * @property {number} dedupHits - Number of dedup hits (requests collapsed into an existing in-flight Async_Effect). Default: 0.
703
+ * @property {number} dedupActive - Number of currently active dedup groups (in-flight unique Cache_Keys). Default: 0.
704
+ * @property {number} queueDepth - Current depth of the priority queue (requests waiting to be dispatched). Default: 0.
705
+ * @property {number} requestsStarted - Total number of requests that entered the lifecycle pipeline. Default: 0.
706
+ * @property {number} requestsCompleted - Total number of requests that completed successfully. Default: 0.
707
+ * @property {number} requestsFailed - Total number of requests that failed with an error. Default: 0.
708
+ * @property {number} retries - Total number of retry attempts scheduled. Default: 0.
709
+ * @property {HttpClientStats} wire - Underlying Wire_Client statistics snapshot.
710
+ */
711
+ type LifecycleStats = {
712
+ /** Number of cache hits. */
713
+ cacheHits: number;
714
+ /** Number of cache misses. */
715
+ cacheMisses: number;
716
+ /** Number of cache evictions (LRU). */
717
+ cacheEvictions: number;
718
+ /** Number of dedup hits (requests collapsed into existing in-flight Async_Effect). */
719
+ dedupHits: number;
720
+ /** Number of currently active dedup groups. */
721
+ dedupActive: number;
722
+ /** Current depth of the priority queue. */
723
+ queueDepth: number;
724
+ /** Total number of requests started. */
725
+ requestsStarted: number;
726
+ /** Total number of requests completed successfully. */
727
+ requestsCompleted: number;
728
+ /** Total number of requests that failed. */
729
+ requestsFailed: number;
730
+ /** Total number of retry attempts scheduled. */
731
+ retries: number;
732
+ /** Total number of batch dispatches. */
733
+ batchDispatches: number;
734
+ /** Total number of individual requests that were coalesced into batches. */
735
+ batchedRequests: number;
736
+ /** Underlying Wire_Client stats. */
737
+ wire: HttpClientStats;
738
+ };
739
+ /**
740
+ * Per-request lifecycle options that can be passed alongside a request.
741
+ *
742
+ * Allows fine-grained control over lifecycle behavior on a per-request basis,
743
+ * overriding the client-level configuration for individual requests.
744
+ *
745
+ * @property {number} [priority] - Priority level for this request. Valid range: 0-9 (0 = highest priority). Default: 5.
746
+ * @property {string} [dedupKey] - Custom dedup key override for this request. When provided, overrides the computed Cache_Key for dedup purposes. Default: undefined.
747
+ * @property {boolean} [noCache] - When true, bypasses the cache layer for this request (neither reads from nor writes to cache). Default: false.
748
+ * @property {boolean} [noDedup] - When true, bypasses the dedup layer for this request (always creates a new in-flight Async_Effect). Default: false.
749
+ */
750
+ type LifecycleRequestOptions = {
751
+ /** Priority level 0-9 (0 = highest). Default: 5. Valid range: 0-9. */
752
+ priority?: number;
753
+ /** Custom dedup key override for this request. */
754
+ dedupKey?: string;
755
+ /** Skip cache for this request. Default: false. */
756
+ noCache?: boolean;
757
+ /** Skip dedup for this request. Default: false. */
758
+ noDedup?: boolean;
759
+ };
760
+
761
+ /**
762
+ * Creates a lifecycle-aware HTTP client that composes deduplication, caching,
763
+ * and priority scheduling layers on top of the Wire_Client.
764
+ *
765
+ * When no layers are configured, the client delegates directly to the underlying
766
+ * Wire_Client with zero additional overhead (zero-cost when disabled). Each layer
767
+ * is independently optional and can be set to `false` to explicitly disable.
768
+ *
769
+ * Layer composition order (outermost to innermost):
770
+ * - User middleware (applied via `.with()`)
771
+ * - Dedup layer (if enabled)
772
+ * - Cache layer (if enabled)
773
+ * - Priority layer (if enabled)
774
+ * - Wire_Client (`makeHttp`)
775
+ *
776
+ * @param config - Lifecycle client configuration extending `MakeHttpConfig` with optional layer settings.
777
+ * - `config.baseUrl` — Base URL prepended to relative request paths.
778
+ * - `config.headers` — Default headers merged into every request.
779
+ * - `config.timeoutMs` — Request budget in milliseconds covering pool wait + fetch + body read.
780
+ * - `config.dedup` — Deduplication layer config or `false` to disable.
781
+ * - `config.dedup.dedupKey` — Custom key function overriding default key computation.
782
+ * - `config.cache` — Response cache layer config or `false` to disable.
783
+ * - `config.cache.ttlSeconds` — Time-to-live in seconds; integer between 1 and 86400 (default: 60).
784
+ * - `config.cache.maxEntries` — Maximum cached entries; integer >= 1 (default: 1024).
785
+ * - `config.cache.staleWhileRevalidate` — Enable stale-while-revalidate (default: false).
786
+ * - `config.cache.cachePolicy` — Custom cache policy function.
787
+ * - `config.cache.cacheRelevantHeaders` — Additional headers included in Cache_Key computation.
788
+ * - `config.priority` — Priority scheduler layer config or `false` to disable.
789
+ * - `config.priority.concurrency` — Maximum concurrent dispatched requests; integer >= 1 (default: 32).
790
+ * - `config.priority.queueTimeoutMs` — Queue timeout in milliseconds for priority-queued requests.
791
+ * - `config.onEvent` — Optional observer callback invoked on each lifecycle event.
792
+ *
793
+ * @returns A {@link LifecycleClient} instance that is callable as an `HttpClientFn` and exposes
794
+ * `.with()` for middleware composition, `.stats()` for observability, `.cancelAll()` for
795
+ * bulk cancellation, and `.cache` for cache management.
796
+ *
797
+ * @example
798
+ * ```typescript
799
+ * import { makeLifecycleClient } from "./index";
800
+ * import type { LifecycleClientConfig } from "./index";
801
+ *
802
+ * const config: LifecycleClientConfig = {
803
+ * baseUrl: "https://api.example.com",
804
+ * cache: { ttlSeconds: 120, maxEntries: 512 },
805
+ * priority: { concurrency: 8 },
806
+ * dedup: {},
807
+ * };
808
+ *
809
+ * const client = makeLifecycleClient(config);
810
+ *
811
+ * // Execute a GET request through all lifecycle layers
812
+ * const response = client({ method: "GET", url: "/users" });
813
+ * ```
814
+ */
815
+ declare function makeLifecycleClient(config?: LifecycleClientConfig): LifecycleClient;
816
+ /**
817
+ * Canonical production HTTP client factory.
818
+ *
819
+ * Alias of {@link makeLifecycleClient}; kept as the recommended public name
820
+ * for callers that want the stable wire -> priority -> retry -> cache -> dedup
821
+ * lifecycle pipeline without importing lower-level building blocks.
822
+ *
823
+ * @param config - Lifecycle client configuration with optional wire, retry, cache, dedup, and priority settings.
824
+ * @returns A lifecycle-aware HTTP client with stats, cache controls, middleware composition, and `cancelAll`.
825
+ */
826
+ declare function makeHttpClient(config?: LifecycleClientConfig): LifecycleClient;
827
+
828
+ /**
829
+ * Components of a parsed Cache_Key, representing the individual parts
830
+ * that make up a deterministic cache key string.
831
+ *
832
+ * @property method - The HTTP method (uppercase), e.g. "GET", "POST"
833
+ * @property resolvedUrl - The fully resolved URL including base URL resolution
834
+ * @property headers - Cache-relevant headers as key-value pairs (lowercase keys)
835
+ * @property body - The request body string, or empty string if no body was present
836
+ */
837
+ type CacheKeyComponents = {
838
+ method: string;
839
+ resolvedUrl: string;
840
+ headers: Record<string, string>;
300
841
  body: string;
301
- schema?: string;
302
842
  };
303
- type JsonValidator<A> = (data: unknown) => {
304
- success: true;
305
- data: A;
306
- } | {
307
- success: false;
308
- error: string;
843
+ /**
844
+ * Null character (`\u0000`) used as a separator between Cache_Key components.
845
+ *
846
+ * This non-printable character is chosen because it cannot appear in valid HTTP
847
+ * method names, URLs, or header values, ensuring unambiguous key parsing via
848
+ * `parseCacheKey`.
849
+ */
850
+ declare const SEPARATOR = "\0";
851
+ /**
852
+ * Default set of HTTP headers included in Cache_Key computation.
853
+ *
854
+ * Value: `["accept", "authorization", "content-type"]`
855
+ *
856
+ * These headers are always factored into the cache key to ensure that requests
857
+ * with different content negotiation, authentication, or body encoding are
858
+ * cached separately. Additional headers can be included via the `extraHeaders`
859
+ * parameter of `computeCacheKey` or the `cacheRelevantHeaders` option in `CacheConfig`.
860
+ */
861
+ declare const DEFAULT_CACHE_RELEVANT_HEADERS: string[];
862
+ /**
863
+ * Computes a deterministic Cache_Key string from an HTTP request.
864
+ *
865
+ * The key is composed of: method (uppercase), resolved URL, sorted filtered headers,
866
+ * and body — concatenated with null character separators. The resulting string
867
+ * uniquely identifies a cacheable request and can be round-tripped via `parseCacheKey`.
868
+ *
869
+ * @param req - The HTTP request to compute a Cache_Key for
870
+ * @param baseUrl - Base URL for resolving relative request URLs
871
+ * @param extraHeaders - Additional header names to include in the Cache_Key beyond
872
+ * the defaults in `DEFAULT_CACHE_RELEVANT_HEADERS`
873
+ * @returns A deterministic Cache_Key string suitable for use as a cache lookup key
874
+ *
875
+ * @example
876
+ * ```typescript
877
+ * import { computeCacheKey } from "./cacheKey";
878
+ *
879
+ * const key = computeCacheKey(
880
+ * { method: "GET", url: "/users", headers: { accept: "application/json" } },
881
+ * "https://api.example.com"
882
+ * );
883
+ * // key is a deterministic string encoding method, URL, headers, and body
884
+ * ```
885
+ */
886
+ declare function computeCacheKey(req: HttpRequest, baseUrl: string, extraHeaders?: string[]): string;
887
+ /**
888
+ * Parses a Cache_Key string back into its component parts.
889
+ *
890
+ * Splits on the null character separator and reconstructs the `CacheKeyComponents` object.
891
+ * The body may contain separator characters, so all parts after the third separator
892
+ * are joined back together as the body. This enables round-trip fidelity with
893
+ * `computeCacheKey`.
894
+ *
895
+ * @param key - A Cache_Key string produced by `computeCacheKey`
896
+ * @returns The parsed `CacheKeyComponents` with method, resolvedUrl, headers, and body
897
+ *
898
+ * @example
899
+ * ```typescript
900
+ * import { computeCacheKey, parseCacheKey } from "./cacheKey";
901
+ *
902
+ * const key = computeCacheKey(
903
+ * { method: "POST", url: "/data", headers: { "content-type": "application/json" }, body: '{"id":1}' },
904
+ * "https://api.example.com"
905
+ * );
906
+ * const parts = parseCacheKey(key);
907
+ * // parts.method === "POST"
908
+ * // parts.resolvedUrl === "https://api.example.com/data"
909
+ * // parts.headers === { "content-type": "application/json" }
910
+ * // parts.body === '{"id":1}'
911
+ * ```
912
+ */
913
+ declare function parseCacheKey(key: string): CacheKeyComponents;
914
+
915
+ /**
916
+ * Event object passed to the `withLogging` middleware's logger callback on each
917
+ * request lifecycle phase (request, response, or error).
918
+ *
919
+ * @property phase - The lifecycle phase that triggered this event: `"request"` before
920
+ * the request is sent, `"response"` on success, or `"error"` on failure.
921
+ * @property req - The original HttpRequest being executed.
922
+ * @property res - The HttpWireResponse received from the server. Present only when
923
+ * `phase` is `"response"`.
924
+ * @property error - The HttpError that occurred. Present only when `phase` is `"error"`.
925
+ * @property durationMs - Elapsed time in milliseconds since the request was initiated.
926
+ * Present only when `phase` is `"response"` or `"error"`.
927
+ */
928
+ type LogEvent = {
929
+ phase: "request" | "response" | "error";
930
+ req: HttpRequest;
931
+ res?: HttpWireResponse;
932
+ error?: HttpError;
933
+ durationMs?: number;
309
934
  };
310
935
  /**
311
- * Creates a validated JSON getter that checks the response body against a schema.
936
+ * Creates a middleware that injects a Bearer token into the Authorization header.
937
+ * The token is obtained asynchronously via the provided `tokenProvider` Async_Effect.
938
+ * If the token provider fails, the error propagates to the caller unchanged.
312
939
  *
313
- * Usage:
314
- * ```ts
315
- * const getUser = validatedJson<User>(client, (data) => {
316
- * if (typeof data === "object" && data !== null && "id" in data) {
317
- * return { success: true, data: data as User };
318
- * }
319
- * return { success: false, error: "Invalid user shape" };
320
- * });
940
+ * @param tokenProvider - A function returning an Async_Effect that resolves to the
941
+ * Bearer token string. Called on every request to support token rotation.
942
+ * @returns An HttpMiddleware that prepends `Authorization: Bearer <token>` to outgoing requests.
943
+ *
944
+ * @example
945
+ * ```typescript
946
+ * import { makeLifecycleClient, withAuth } from "./index";
947
+ * import { asyncSucceed } from "../../core/types/asyncEffect";
948
+ *
949
+ * const client = makeLifecycleClient({ baseUrl: "https://api.example.com" })
950
+ * .with(withAuth(() => asyncSucceed("my-secret-token")));
321
951
  *
322
- * const user = await run(getUser({ method: "GET", url: "/users/1" }));
952
+ * // All requests now include Authorization: Bearer my-secret-token
953
+ * const result = client({ method: "GET", url: "/users" });
323
954
  * ```
324
955
  */
325
- declare function validatedJson<A>(client: HttpClientFn, validator: JsonValidator<A>): (req: Parameters<HttpClientFn>[0]) => Async<unknown, HttpError | ValidationError, A>;
956
+ declare function withAuth(tokenProvider: () => Async<unknown, HttpError, string>): HttpMiddleware;
957
+ /**
958
+ * Creates a middleware that logs request, response, and error events through a
959
+ * user-supplied logger callback. The logger is invoked synchronously at each phase;
960
+ * if it throws, the error is swallowed to avoid disrupting the request pipeline.
961
+ *
962
+ * @param logger - A synchronous callback invoked with a {@link LogEvent} for each
963
+ * lifecycle phase (`"request"`, `"response"`, `"error"`). Exceptions thrown by
964
+ * the logger are silently caught.
965
+ * @returns An HttpMiddleware that instruments requests with logging side-effects.
966
+ *
967
+ * @example
968
+ * ```typescript
969
+ * import { makeLifecycleClient, withLogging } from "./index";
970
+ * import type { LogEvent } from "./index";
971
+ *
972
+ * const client = makeLifecycleClient({ baseUrl: "https://api.example.com" })
973
+ * .with(withLogging((event: LogEvent) => {
974
+ * console.log(`[${event.phase}] ${event.req.method} ${event.req.url} ${event.durationMs ?? ""}ms`);
975
+ * }));
976
+ *
977
+ * const result = client({ method: "GET", url: "/health" });
978
+ * ```
979
+ */
980
+ declare function withLogging(logger: (event: LogEvent) => void): HttpMiddleware;
981
+ /**
982
+ * Creates a middleware that transforms HTTP responses after retrieval. The
983
+ * transformation is applied to both cached and network responses. Cached
984
+ * responses are stored in their original (untransformed) form, so the transform
985
+ * runs on every access.
986
+ *
987
+ * If the transform function throws, the error is propagated as a `FetchError`.
988
+ *
989
+ * @param fn - A synchronous function that receives the response and the original
990
+ * request, and returns a modified HttpWireResponse. Must not return `undefined`.
991
+ * @returns An HttpMiddleware that applies the transform to every successful response.
992
+ *
993
+ * @example
994
+ * ```typescript
995
+ * import { makeLifecycleClient, withResponseTransform } from "./index";
996
+ *
997
+ * const client = makeLifecycleClient({ baseUrl: "https://api.example.com" })
998
+ * .with(withResponseTransform((res, req) => ({
999
+ * ...res,
1000
+ * headers: { ...res.headers, "x-request-url": req.url },
1001
+ * })));
1002
+ *
1003
+ * // Responses now include the x-request-url header
1004
+ * const result = client({ method: "GET", url: "/data" });
1005
+ * ```
1006
+ */
1007
+ declare function withResponseTransform(fn: (res: HttpWireResponse, req: HttpRequest) => HttpWireResponse): HttpMiddleware;
1008
+
1009
+ /**
1010
+ * Configuration for the LRU cache.
1011
+ *
1012
+ * @property maxEntries - Maximum number of entries the cache can hold.
1013
+ * Must be >= 1. Values less than 1 are clamped to 1. Fractional values are floored.
1014
+ * Default: 1024.
1015
+ * @property onEvict - Optional callback invoked when entries are evicted from the cache.
1016
+ * Receives the number of entries evicted in that operation (currently always 1).
1017
+ *
1018
+ * @example
1019
+ * ```typescript
1020
+ * import { LRUCache } from "./lruCache";
1021
+ *
1022
+ * const cache = new LRUCache<string>({ maxEntries: 100, onEvict: (n) => console.log(`Evicted ${n}`) });
1023
+ * ```
1024
+ */
1025
+ type LRUCacheConfig = {
1026
+ /** Maximum number of entries. Must be >= 1. Default: 1024. */
1027
+ maxEntries?: number;
1028
+ /** Optional callback invoked with the number of entries evicted on each eviction. */
1029
+ onEvict?: (count: number) => void;
1030
+ };
1031
+ /**
1032
+ * A generic LRU (Least Recently Used) cache with per-entry TTL support.
1033
+ *
1034
+ * Uses a doubly-linked list combined with a Map for O(1) get, set, and eviction
1035
+ * operations. The head of the list is the most recently used entry; the tail is
1036
+ * the least recently used.
1037
+ *
1038
+ * When the cache exceeds `maxEntries`, the least recently used entry is evicted.
1039
+ * Expired entries are lazily removed on access (get).
1040
+ *
1041
+ * @example
1042
+ * ```typescript
1043
+ * import { LRUCache } from "./lruCache";
1044
+ *
1045
+ * const cache = new LRUCache<string>({ maxEntries: 256 });
1046
+ * cache.set("user:1", "Alice", 60_000); // TTL of 60 seconds
1047
+ * const value = cache.get("user:1"); // "Alice" (moves to head)
1048
+ * cache.delete("user:1"); // true
1049
+ * ```
1050
+ */
1051
+ declare class LRUCache<V> {
1052
+ private readonly map;
1053
+ private head;
1054
+ private tail;
1055
+ private readonly maxEntries;
1056
+ private readonly onEvict;
1057
+ /**
1058
+ * Creates a new LRU cache instance.
1059
+ *
1060
+ * @param config - Cache configuration options.
1061
+ * @param config.maxEntries - Maximum number of entries. Must be >= 1. Default: 1024.
1062
+ * @param config.onEvict - Optional eviction callback.
1063
+ *
1064
+ * @example
1065
+ * ```typescript
1066
+ * import { LRUCache } from "./lruCache";
1067
+ *
1068
+ * const cache = new LRUCache<number>({ maxEntries: 50 });
1069
+ * ```
1070
+ */
1071
+ constructor(config?: LRUCacheConfig);
1072
+ /**
1073
+ * Returns the number of entries currently in the cache.
1074
+ *
1075
+ * @returns The current entry count.
1076
+ *
1077
+ * @example
1078
+ * ```typescript
1079
+ * import { LRUCache } from "./lruCache";
1080
+ *
1081
+ * const cache = new LRUCache<string>();
1082
+ * cache.set("a", "1", 10_000);
1083
+ * console.log(cache.size); // 1
1084
+ * ```
1085
+ */
1086
+ get size(): number;
1087
+ /**
1088
+ * Retrieves a value by key.
1089
+ *
1090
+ * Returns `undefined` if the key is not found or the entry has expired.
1091
+ * On a hit (non-expired), the entry is moved to the head (most recently used).
1092
+ * Expired entries are lazily removed on access.
1093
+ *
1094
+ * @param key - The cache key to look up.
1095
+ * @returns The cached value, or `undefined` if not found or expired.
1096
+ *
1097
+ * @example
1098
+ * ```typescript
1099
+ * import { LRUCache } from "./lruCache";
1100
+ *
1101
+ * const cache = new LRUCache<string>();
1102
+ * cache.set("greeting", "hello", 30_000);
1103
+ * const val = cache.get("greeting"); // "hello"
1104
+ * const miss = cache.get("unknown"); // undefined
1105
+ * ```
1106
+ */
1107
+ get(key: string): V | undefined;
1108
+ /**
1109
+ * Inserts or updates an entry in the cache.
1110
+ *
1111
+ * If the key already exists, the value and TTL are updated and the entry is
1112
+ * moved to the head. If inserting a new entry causes the cache to exceed
1113
+ * `maxEntries` (must be >= 1), the least recently used entry is evicted.
1114
+ *
1115
+ * @param key - The cache key.
1116
+ * @param value - The value to store.
1117
+ * @param ttlMs - Time-to-live in milliseconds. The entry expires after this duration.
1118
+ *
1119
+ * @example
1120
+ * ```typescript
1121
+ * import { LRUCache } from "./lruCache";
1122
+ *
1123
+ * const cache = new LRUCache<string>({ maxEntries: 2 });
1124
+ * cache.set("a", "alpha", 60_000);
1125
+ * cache.set("b", "beta", 60_000);
1126
+ * cache.set("c", "gamma", 60_000); // evicts "a" (LRU)
1127
+ * ```
1128
+ */
1129
+ set(key: string, value: V, ttlMs: number): void;
1130
+ /**
1131
+ * Removes an entry by key.
1132
+ *
1133
+ * @param key - The cache key to remove.
1134
+ * @returns `true` if the entry was found and removed, `false` otherwise.
1135
+ *
1136
+ * @example
1137
+ * ```typescript
1138
+ * import { LRUCache } from "./lruCache";
1139
+ *
1140
+ * const cache = new LRUCache<string>();
1141
+ * cache.set("x", "value", 10_000);
1142
+ * cache.delete("x"); // true
1143
+ * cache.delete("x"); // false (already removed)
1144
+ * ```
1145
+ */
1146
+ delete(key: string): boolean;
1147
+ /**
1148
+ * Removes all entries from the cache, resetting it to an empty state.
1149
+ *
1150
+ * @example
1151
+ * ```typescript
1152
+ * import { LRUCache } from "./lruCache";
1153
+ *
1154
+ * const cache = new LRUCache<string>();
1155
+ * cache.set("a", "1", 10_000);
1156
+ * cache.clear();
1157
+ * console.log(cache.size); // 0
1158
+ * ```
1159
+ */
1160
+ clear(): void;
1161
+ /** Adds a node to the head of the list (most recently used position). */
1162
+ private addToHead;
1163
+ /** Removes a node from its current position in the list. */
1164
+ private removeNode;
1165
+ /** Moves an existing node to the head of the list. */
1166
+ private moveToHead;
1167
+ /** Evicts the tail node (least recently used) and notifies via callback. */
1168
+ private evictTail;
1169
+ }
1170
+
1171
+ /**
1172
+ * Clamps a priority value to the valid range [0, 9].
1173
+ *
1174
+ * - Truncates toward zero (removes fractional part)
1175
+ * - Clamps the result to the integer range 0 through 9
1176
+ * - Returns a default of 5 for `undefined`, `NaN`, or non-finite values
1177
+ *
1178
+ * @param value - The priority value to clamp. Must be an integer from 0 to 9.
1179
+ * Values outside this range are clamped. Undefined or non-finite values default to 5.
1180
+ * @returns An integer in the range [0, 9] representing the clamped priority.
1181
+ *
1182
+ * @example
1183
+ * ```typescript
1184
+ * import { clampPriority } from "./priorityQueue";
1185
+ *
1186
+ * clampPriority(3); // 3
1187
+ * clampPriority(15); // 9 (clamped to max)
1188
+ * clampPriority(-2); // 0 (clamped to min)
1189
+ * clampPriority(undefined); // 5 (default)
1190
+ * clampPriority(2.7); // 2 (truncated)
1191
+ * ```
1192
+ */
1193
+ declare function clampPriority(value: number | undefined): number;
1194
+ /**
1195
+ * An entry stored in the priority queue.
1196
+ *
1197
+ * @property priority - Priority level from 0 to 9, where 0 is the highest priority.
1198
+ * Clamped on enqueue via `clampPriority`.
1199
+ * @property arrivalOrder - Monotonic counter used for FIFO tiebreak within the same
1200
+ * priority level. Lower values are dispatched first.
1201
+ * @property value - The stored value associated with this entry.
1202
+ * @property cancelled - When `true`, the entry is logically removed (lazy deletion).
1203
+ * Cancelled entries are skipped during dequeue and peek operations.
1204
+ */
1205
+ type PriorityQueueEntry<T> = {
1206
+ /** Priority level 0-9 (0 = highest priority). Clamped on enqueue. */
1207
+ priority: number;
1208
+ /** Monotonic counter for FIFO tiebreak within the same priority level. */
1209
+ arrivalOrder: number;
1210
+ /** The stored value. */
1211
+ value: T;
1212
+ /** When true, the entry is logically removed (lazy deletion). */
1213
+ cancelled: boolean;
1214
+ };
1215
+ /**
1216
+ * A generic binary min-heap priority queue.
1217
+ *
1218
+ * Entries are ordered by priority ascending (lower value = higher priority),
1219
+ * with FIFO tiebreak via a monotonic arrivalOrder counter. Priority values
1220
+ * are integers from 0 to 9, where 0 is the highest priority.
1221
+ *
1222
+ * Supports lazy removal: entries can be marked as cancelled and are
1223
+ * skipped during dequeue and peek operations.
1224
+ *
1225
+ * @example
1226
+ * ```typescript
1227
+ * import { PriorityQueue } from "./priorityQueue";
1228
+ *
1229
+ * const queue = new PriorityQueue<string>();
1230
+ * queue.enqueue("low", 9);
1231
+ * queue.enqueue("high", 0);
1232
+ * const entry = queue.dequeue(); // { value: "high", priority: 0, ... }
1233
+ * ```
1234
+ */
1235
+ declare class PriorityQueue<T> {
1236
+ private heap;
1237
+ private counter;
1238
+ /**
1239
+ * Returns the number of entries in the queue (including cancelled entries).
1240
+ *
1241
+ * @returns The total number of entries in the internal heap.
1242
+ *
1243
+ * @example
1244
+ * ```typescript
1245
+ * import { PriorityQueue } from "./priorityQueue";
1246
+ *
1247
+ * const queue = new PriorityQueue<string>();
1248
+ * queue.enqueue("task", 5);
1249
+ * console.log(queue.size); // 1
1250
+ * ```
1251
+ */
1252
+ get size(): number;
1253
+ /** Returns the number of entries that have not been cancelled. */
1254
+ get activeSize(): number;
1255
+ /**
1256
+ * Adds a value to the queue with the given priority.
1257
+ *
1258
+ * Priority is clamped to the valid range [0, 9] via `clampPriority`.
1259
+ * Returns the created entry, which can be used for later cancellation
1260
+ * by setting `entry.cancelled = true`.
1261
+ *
1262
+ * @param value - The value to enqueue.
1263
+ * @param priority - Priority level, integer from 0 (highest) to 9 (lowest).
1264
+ * Clamped to [0, 9]. Defaults to 5 if undefined.
1265
+ * @returns The created queue entry.
1266
+ *
1267
+ * @example
1268
+ * ```typescript
1269
+ * import { PriorityQueue } from "./priorityQueue";
1270
+ *
1271
+ * const queue = new PriorityQueue<string>();
1272
+ * const entry = queue.enqueue("urgent-task", 0);
1273
+ * entry.cancelled = true; // cancel later if needed
1274
+ * ```
1275
+ */
1276
+ enqueue(value: T, priority?: number): PriorityQueueEntry<T>;
1277
+ /**
1278
+ * Removes and returns the highest-priority non-cancelled entry.
1279
+ *
1280
+ * Skips (and discards) any cancelled entries at the top of the heap.
1281
+ * Returns `undefined` if the queue is empty or all entries are cancelled.
1282
+ *
1283
+ * @returns The highest-priority non-cancelled entry, or `undefined` if none available.
1284
+ *
1285
+ * @example
1286
+ * ```typescript
1287
+ * import { PriorityQueue } from "./priorityQueue";
1288
+ *
1289
+ * const queue = new PriorityQueue<string>();
1290
+ * queue.enqueue("first", 1);
1291
+ * queue.enqueue("second", 2);
1292
+ * const entry = queue.dequeue(); // { value: "first", priority: 1, ... }
1293
+ * ```
1294
+ */
1295
+ dequeue(): PriorityQueueEntry<T> | undefined;
1296
+ /**
1297
+ * Returns the highest-priority non-cancelled entry without removing it.
1298
+ *
1299
+ * Discards cancelled entries at the top of the heap as a side effect.
1300
+ * Returns `undefined` if the queue is empty or all entries are cancelled.
1301
+ *
1302
+ * @returns The highest-priority non-cancelled entry, or `undefined` if none available.
1303
+ *
1304
+ * @example
1305
+ * ```typescript
1306
+ * import { PriorityQueue } from "./priorityQueue";
1307
+ *
1308
+ * const queue = new PriorityQueue<string>();
1309
+ * queue.enqueue("task", 3);
1310
+ * const top = queue.peek(); // { value: "task", priority: 3, ... }
1311
+ * console.log(queue.size); // 1 (not removed)
1312
+ * ```
1313
+ */
1314
+ peek(): PriorityQueueEntry<T> | undefined;
1315
+ /**
1316
+ * Marks all entries matching the predicate as cancelled (lazy removal).
1317
+ *
1318
+ * Cancelled entries are skipped on subsequent dequeue/peek calls.
1319
+ * This does not immediately remove entries from the heap; they are
1320
+ * discarded lazily when encountered at the top during dequeue or peek.
1321
+ *
1322
+ * @param predicate - A function that returns `true` for entries to cancel.
1323
+ * @returns The number of entries marked as cancelled.
1324
+ *
1325
+ * @example
1326
+ * ```typescript
1327
+ * import { PriorityQueue } from "./priorityQueue";
1328
+ *
1329
+ * const queue = new PriorityQueue<string>();
1330
+ * queue.enqueue("a", 1);
1331
+ * queue.enqueue("b", 2);
1332
+ * const removed = queue.remove((e) => e.value === "a"); // 1
1333
+ * ```
1334
+ */
1335
+ remove(predicate: (entry: PriorityQueueEntry<T>) => boolean): number;
1336
+ /** Removes the top element from the heap and restores heap property. */
1337
+ private removeTop;
1338
+ /** Moves an element up the heap until the heap property is restored. */
1339
+ private bubbleUp;
1340
+ /** Moves an element down the heap until the heap property is restored. */
1341
+ private sinkDown;
1342
+ }
1343
+
1344
+ /**
1345
+ * Tracks lifecycle statistics for the HTTP Lifecycle Client.
1346
+ *
1347
+ * All counters start at zero and increase monotonically. The tracker also
1348
+ * provides event emission for observability, wrapping the user-supplied
1349
+ * `onEvent` callback in a try-catch so that callback errors never disrupt
1350
+ * request processing.
1351
+ *
1352
+ * Use the `snapshot()` method to obtain a frozen point-in-time view of all
1353
+ * statistics, including wire-level stats from the underlying HTTP client.
1354
+ *
1355
+ * @example
1356
+ * ```typescript
1357
+ * import { LifecycleStatsTracker } from "./stats";
1358
+ *
1359
+ * const tracker = new LifecycleStatsTracker({
1360
+ * onEvent: (event) => console.log(event.type),
1361
+ * wireStats: () => ({ requestCount: 0, errorCount: 0 }),
1362
+ * });
1363
+ * tracker.cacheHit();
1364
+ * const stats = tracker.snapshot();
1365
+ * console.log(stats.cacheHits); // 1
1366
+ * ```
1367
+ */
1368
+ declare class LifecycleStatsTracker {
1369
+ private _cacheHits;
1370
+ private _cacheMisses;
1371
+ private _cacheEvictions;
1372
+ private _dedupHits;
1373
+ private _dedupActive;
1374
+ private _queueDepth;
1375
+ private _requestsStarted;
1376
+ private _requestsCompleted;
1377
+ private _requestsFailed;
1378
+ private _retries;
1379
+ private _batchDispatches;
1380
+ private _batchedRequests;
1381
+ private readonly _onEvent;
1382
+ private readonly _wireStats;
1383
+ /**
1384
+ * Creates a new lifecycle stats tracker.
1385
+ *
1386
+ * @param opts - Configuration options for the tracker.
1387
+ * @param opts.onEvent - Optional callback invoked on each lifecycle event.
1388
+ * Errors thrown by this callback are silently discarded.
1389
+ * @param opts.wireStats - A function returning the current wire-level HTTP client stats.
1390
+ *
1391
+ * @example
1392
+ * ```typescript
1393
+ * import { LifecycleStatsTracker } from "./stats";
1394
+ *
1395
+ * const tracker = new LifecycleStatsTracker({
1396
+ * wireStats: () => ({ requestCount: 0, errorCount: 0 }),
1397
+ * });
1398
+ * ```
1399
+ */
1400
+ constructor(opts: {
1401
+ onEvent?: (event: LifecycleEvent) => void;
1402
+ wireStats: () => HttpClientStats;
1403
+ });
1404
+ /**
1405
+ * Records a cache hit. Increments the cache hit counter by 1.
1406
+ *
1407
+ * @example
1408
+ * ```typescript
1409
+ * import { LifecycleStatsTracker } from "./stats";
1410
+ *
1411
+ * const tracker = new LifecycleStatsTracker({ wireStats: () => ({ requestCount: 0, errorCount: 0 }) });
1412
+ * tracker.cacheHit();
1413
+ * ```
1414
+ */
1415
+ cacheHit(): void;
1416
+ /**
1417
+ * Records a cache miss. Increments the cache miss counter by 1.
1418
+ *
1419
+ * @example
1420
+ * ```typescript
1421
+ * import { LifecycleStatsTracker } from "./stats";
1422
+ *
1423
+ * const tracker = new LifecycleStatsTracker({ wireStats: () => ({ requestCount: 0, errorCount: 0 }) });
1424
+ * tracker.cacheMiss();
1425
+ * ```
1426
+ */
1427
+ cacheMiss(): void;
1428
+ /**
1429
+ * Records a cache eviction. Increments the cache eviction counter by 1.
1430
+ *
1431
+ * @example
1432
+ * ```typescript
1433
+ * import { LifecycleStatsTracker } from "./stats";
1434
+ *
1435
+ * const tracker = new LifecycleStatsTracker({ wireStats: () => ({ requestCount: 0, errorCount: 0 }) });
1436
+ * tracker.cacheEviction();
1437
+ * ```
1438
+ */
1439
+ cacheEviction(): void;
1440
+ /**
1441
+ * Records a dedup hit (a request that joined an in-flight duplicate).
1442
+ * Increments the dedup hit counter by 1.
1443
+ *
1444
+ * @example
1445
+ * ```typescript
1446
+ * import { LifecycleStatsTracker } from "./stats";
1447
+ *
1448
+ * const tracker = new LifecycleStatsTracker({ wireStats: () => ({ requestCount: 0, errorCount: 0 }) });
1449
+ * tracker.dedupHit();
1450
+ * ```
1451
+ */
1452
+ dedupHit(): void;
1453
+ /**
1454
+ * Sets the current number of active dedup groups.
1455
+ *
1456
+ * @param n - The current count of active dedup groups. Must be >= 0.
1457
+ *
1458
+ * @example
1459
+ * ```typescript
1460
+ * import { LifecycleStatsTracker } from "./stats";
1461
+ *
1462
+ * const tracker = new LifecycleStatsTracker({ wireStats: () => ({ requestCount: 0, errorCount: 0 }) });
1463
+ * tracker.setDedupActive(3);
1464
+ * ```
1465
+ */
1466
+ setDedupActive(n: number): void;
1467
+ /**
1468
+ * Sets the current priority queue depth.
1469
+ *
1470
+ * @param n - The current number of entries in the priority queue. Must be >= 0.
1471
+ *
1472
+ * @example
1473
+ * ```typescript
1474
+ * import { LifecycleStatsTracker } from "./stats";
1475
+ *
1476
+ * const tracker = new LifecycleStatsTracker({ wireStats: () => ({ requestCount: 0, errorCount: 0 }) });
1477
+ * tracker.setQueueDepth(5);
1478
+ * ```
1479
+ */
1480
+ setQueueDepth(n: number): void;
1481
+ /**
1482
+ * Records that a request has started. Increments the requests started counter by 1.
1483
+ *
1484
+ * @example
1485
+ * ```typescript
1486
+ * import { LifecycleStatsTracker } from "./stats";
1487
+ *
1488
+ * const tracker = new LifecycleStatsTracker({ wireStats: () => ({ requestCount: 0, errorCount: 0 }) });
1489
+ * tracker.requestStarted();
1490
+ * ```
1491
+ */
1492
+ requestStarted(): void;
1493
+ /**
1494
+ * Records that a request has completed successfully.
1495
+ * Increments the requests completed counter by 1.
1496
+ *
1497
+ * @example
1498
+ * ```typescript
1499
+ * import { LifecycleStatsTracker } from "./stats";
1500
+ *
1501
+ * const tracker = new LifecycleStatsTracker({ wireStats: () => ({ requestCount: 0, errorCount: 0 }) });
1502
+ * tracker.requestCompleted();
1503
+ * ```
1504
+ */
1505
+ requestCompleted(): void;
1506
+ /**
1507
+ * Records that a request has failed.
1508
+ * Increments the requests failed counter by 1.
1509
+ *
1510
+ * @example
1511
+ * ```typescript
1512
+ * import { LifecycleStatsTracker } from "./stats";
1513
+ *
1514
+ * const tracker = new LifecycleStatsTracker({ wireStats: () => ({ requestCount: 0, errorCount: 0 }) });
1515
+ * tracker.requestFailed();
1516
+ * ```
1517
+ */
1518
+ requestFailed(): void;
1519
+ retry(): void;
1520
+ /**
1521
+ * Records a batch dispatch. Increments the batch dispatches counter by 1.
1522
+ */
1523
+ batchDispatch(): void;
1524
+ /**
1525
+ * Records requests that were coalesced into a batch.
1526
+ * @param count - The number of individual requests in the batch.
1527
+ */
1528
+ batchedRequests(count: number): void;
1529
+ /**
1530
+ * Emits a lifecycle event to the registered `onEvent` callback.
1531
+ *
1532
+ * The callback is wrapped in a try-catch so that any exception thrown by
1533
+ * the callback is silently discarded and request processing continues
1534
+ * unaffected. If no `onEvent` callback was provided, this is a no-op.
1535
+ *
1536
+ * @param type - The lifecycle event type to emit (e.g., `"cache-hit"`, `"request-start"`).
1537
+ * @param extra - Optional additional event data.
1538
+ *
1539
+ * @example
1540
+ * ```typescript
1541
+ * import { LifecycleStatsTracker } from "./stats";
1542
+ *
1543
+ * const tracker = new LifecycleStatsTracker({
1544
+ * onEvent: (event) => console.log(event.type, event.timestamp),
1545
+ * wireStats: () => ({ requestCount: 0, errorCount: 0 }),
1546
+ * });
1547
+ * tracker.emit("cache-hit", { cacheKey: "GET|/api/users" });
1548
+ * ```
1549
+ */
1550
+ emit(type: LifecycleEventType, extra?: {
1551
+ cacheKey?: string;
1552
+ priority?: number;
1553
+ batchKey?: string;
1554
+ batchSize?: number;
1555
+ attempt?: number;
1556
+ delayMs?: number;
1557
+ status?: number;
1558
+ errorTag?: string;
1559
+ }): void;
1560
+ /**
1561
+ * Returns a frozen snapshot of all lifecycle statistics including wire stats.
1562
+ *
1563
+ * The returned object is frozen (immutable) and represents a point-in-time
1564
+ * view of all counters and gauges.
1565
+ *
1566
+ * @returns A frozen `LifecycleStats` object containing all current statistics.
1567
+ *
1568
+ * @example
1569
+ * ```typescript
1570
+ * import { LifecycleStatsTracker } from "./stats";
1571
+ *
1572
+ * const tracker = new LifecycleStatsTracker({
1573
+ * wireStats: () => ({ requestCount: 10, errorCount: 1 }),
1574
+ * });
1575
+ * tracker.cacheHit();
1576
+ * tracker.cacheHit();
1577
+ * const stats = tracker.snapshot();
1578
+ * console.log(stats.cacheHits); // 2
1579
+ * ```
1580
+ */
1581
+ snapshot(): LifecycleStats;
1582
+ }
1583
+
1584
+ /**
1585
+ * Configuration for the deduplication middleware.
1586
+ */
1587
+ type DedupConfig = {
1588
+ /** Custom key function. When provided, overrides default key computation. */
1589
+ dedupKey?: (req: HttpRequest) => string;
1590
+ /** Optional lifecycle observer for dedup hits/misses. */
1591
+ onEvent?: (event: {
1592
+ type: "dedup-hit" | "dedup-miss" | "dedup-active";
1593
+ cacheKey?: string;
1594
+ active?: number;
1595
+ }) => void;
1596
+ };
1597
+ /**
1598
+ * Creates a deduplication middleware that collapses identical in-flight requests
1599
+ * into a single network call.
1600
+ *
1601
+ * For safe HTTP methods (GET, HEAD, OPTIONS), concurrent requests with the same
1602
+ * dedup key share a single underlying network call. All callers receive the same
1603
+ * response or error.
1604
+ *
1605
+ * Non-safe methods (POST, PUT, PATCH, DELETE) pass through without deduplication.
1606
+ *
1607
+ * Supports ref-counted cancellation: when a caller cancels, the refCount is decremented.
1608
+ * When refCount reaches 0, the underlying request is aborted via AbortController.
1609
+ *
1610
+ * @param config - Optional dedup configuration. Provide a `dedupKey` function to override
1611
+ * the default Cache_Key computation. Return an empty string from `dedupKey` to bypass
1612
+ * deduplication for a specific request.
1613
+ * @returns An HttpMiddleware that wraps the next Wire_Client with deduplication logic.
1614
+ * Concurrent safe-method requests sharing the same key resolve to a single network call.
1615
+ *
1616
+ * @example
1617
+ * ```typescript
1618
+ * import { withDedup } from "./dedup";
1619
+ *
1620
+ * // Basic usage with default key computation
1621
+ * const dedupMiddleware = withDedup();
1622
+ *
1623
+ * // With custom key function
1624
+ * const customDedup = withDedup({
1625
+ * dedupKey: (req) => `${req.method}:${req.url}`,
1626
+ * });
1627
+ * ```
1628
+ */
1629
+ declare function withDedup(config?: DedupConfig): HttpMiddleware;
1630
+
1631
+ /**
1632
+ * Result of a custom cache policy function.
1633
+ */
1634
+ type CachePolicyResult = {
1635
+ cacheable: boolean;
1636
+ ttlSeconds?: number;
1637
+ };
1638
+ /**
1639
+ * Configuration for the response cache middleware.
1640
+ */
1641
+ type CacheConfig = {
1642
+ /** Time-to-live in seconds. Default: 60. Range: [1, 86400]. */
1643
+ ttlSeconds?: number;
1644
+ /** Maximum number of cached entries. Default: 1024. Minimum: 1. */
1645
+ maxEntries?: number;
1646
+ /** Enable stale-while-revalidate. Default: false. */
1647
+ staleWhileRevalidate?: boolean;
1648
+ /** Custom cache policy function. */
1649
+ cachePolicy?: (req: HttpRequest, res: HttpWireResponse) => CachePolicyResult;
1650
+ /** Additional headers to include in cache key computation. */
1651
+ cacheRelevantHeaders?: string[];
1652
+ /** Base URL needed for cache key computation. */
1653
+ baseUrl?: string;
1654
+ /** Optional event callback for structured cache failure events. */
1655
+ onEvent?: (event: {
1656
+ type: string;
1657
+ cacheKey?: string;
1658
+ error?: any;
1659
+ }) => void;
1660
+ /** Optional internal lifecycle callback for hit/miss/eviction stats. */
1661
+ onLifecycleEvent?: (event: {
1662
+ type: "cache-hit" | "cache-miss" | "cache-eviction";
1663
+ cacheKey?: string;
1664
+ count?: number;
1665
+ }) => void;
1666
+ };
1667
+ /**
1668
+ * Creates a response cache middleware that stores and serves previously fetched
1669
+ * responses based on configurable cache policies.
1670
+ *
1671
+ * Features:
1672
+ * - LRU eviction when maxEntries is exceeded
1673
+ * - Per-entry TTL with configurable default
1674
+ * - Stale-while-revalidate support
1675
+ * - Custom cache policy function for cacheability and TTL override
1676
+ * - Only caches safe methods (GET, HEAD, OPTIONS) by default
1677
+ * - Exposes `invalidate(key)` and `clear()` for manual cache control
1678
+ *
1679
+ * @param config - Optional cache configuration object.
1680
+ * - `ttlSeconds`: Time-to-live per entry in seconds, clamped to [1, 86400]. Default: 60.
1681
+ * - `maxEntries`: Maximum cached entries, minimum 1. Default: 1024.
1682
+ * - `staleWhileRevalidate`: When true, serves stale entries while refreshing in background. Default: false.
1683
+ * - `cachePolicy`: Custom function to determine cacheability and per-entry TTL override.
1684
+ * - `cacheRelevantHeaders`: Additional headers included in Cache_Key computation.
1685
+ * - `baseUrl`: Base URL for Cache_Key computation.
1686
+ * - `onEvent`: Callback for structured cache events (e.g., revalidation failures).
1687
+ * @returns An object containing:
1688
+ * - `middleware`: An HttpMiddleware that wraps the next Wire_Client with caching logic.
1689
+ * - `invalidate(key)`: Removes a specific entry from the cache by its Cache_Key.
1690
+ * - `clear()`: Removes all entries from the cache.
1691
+ *
1692
+ * @example
1693
+ * ```typescript
1694
+ * import { withCache } from "./responseCache";
1695
+ *
1696
+ * // Basic usage with defaults (60s TTL, 1024 max entries)
1697
+ * const { middleware, invalidate, clear } = withCache();
1698
+ *
1699
+ * // Custom TTL and max entries
1700
+ * const cache = withCache({
1701
+ * ttlSeconds: 300,
1702
+ * maxEntries: 512,
1703
+ * staleWhileRevalidate: true,
1704
+ * });
1705
+ *
1706
+ * // Manually invalidate a cached entry
1707
+ * cache.invalidate("GET|https://api.example.com/users");
1708
+ * ```
1709
+ */
1710
+ declare function withCache(config?: CacheConfig): {
1711
+ middleware: HttpMiddleware;
1712
+ invalidate: (key: string) => void;
1713
+ clear: () => void;
1714
+ };
1715
+
1716
+ /**
1717
+ * Configuration for the priority scheduler middleware.
1718
+ */
1719
+ type PriorityConfig = {
1720
+ /** Maximum concurrent requests dispatched to the wire client. Default: 32. */
1721
+ concurrency?: number;
1722
+ /** Queue timeout in ms for priority-queued requests. Default: no timeout. */
1723
+ queueTimeoutMs?: number;
1724
+ /** Optional lifecycle observer for queue events. */
1725
+ onEvent?: (event: {
1726
+ type: "queue-enqueue" | "queue-dispatch";
1727
+ priority: number;
1728
+ }) => void;
1729
+ };
1730
+ /**
1731
+ * Creates a priority scheduler middleware that reorders queued requests
1732
+ * by priority before dispatching them to the downstream wire client.
1733
+ *
1734
+ * When the concurrency limit is not reached, requests are dispatched immediately.
1735
+ * When at capacity, requests are held in a priority queue (lower numeric priority = higher urgency)
1736
+ * and dispatched in priority order as slots become available.
1737
+ *
1738
+ * Supports:
1739
+ * - Priority extraction from request options (default 5, clamped to 0-9)
1740
+ * - Queue timeout via `queueTimeoutMs` config (produces PoolTimeout error)
1741
+ * - Cancellation: removes from queue on abort signal
1742
+ * - Stats tracking via `queueDepth` getter
1743
+ *
1744
+ * @param config - Optional priority scheduler configuration.
1745
+ * - `concurrency`: Maximum concurrent requests dispatched to the Wire_Client.
1746
+ * Must be a positive integer (>= 1). Default: 32.
1747
+ * - `queueTimeoutMs`: Maximum time in milliseconds a request may wait in the queue
1748
+ * before receiving a PoolTimeout error. Must be a positive integer (>= 1) or undefined
1749
+ * for no timeout. Default: undefined (no timeout).
1750
+ * @returns An HttpMiddleware (with an additional `queueDepth()` method) that wraps the
1751
+ * next Wire_Client with priority-based scheduling. Requests carry a priority level
1752
+ * (integer from 0 to 9, where 0 is highest urgency). Default priority is 5.
1753
+ *
1754
+ * @example
1755
+ * ```typescript
1756
+ * import { withPriority } from "./priorityScheduler";
1757
+ *
1758
+ * // Basic usage with default concurrency (32)
1759
+ * const priorityMiddleware = withPriority();
1760
+ *
1761
+ * // Limit concurrency and set queue timeout
1762
+ * const scheduler = withPriority({
1763
+ * concurrency: 4,
1764
+ * queueTimeoutMs: 5000,
1765
+ * });
1766
+ *
1767
+ * // Check current queue depth
1768
+ * const depth = scheduler.queueDepth();
1769
+ * ```
1770
+ */
1771
+ declare function withPriority(config?: PriorityConfig): HttpMiddleware & {
1772
+ queueDepth: () => number;
1773
+ };
1774
+
1775
+ /**
1776
+ * Supported content encoding algorithms.
1777
+ */
1778
+ type SupportedEncoding = "gzip" | "br" | "deflate";
1779
+ /**
1780
+ * All supported encodings in default preference order (Brotli first).
1781
+ */
1782
+ declare const SUPPORTED_ENCODINGS: readonly SupportedEncoding[];
1783
+ /**
1784
+ * Configuration for the compression middleware.
1785
+ */
1786
+ type CompressionConfig = {
1787
+ /**
1788
+ * Enabled encodings in preference order.
1789
+ * Default: ["br", "gzip", "deflate"]
1790
+ */
1791
+ encodings?: SupportedEncoding[];
1792
+ };
1793
+ type RequestCompressionConfig = {
1794
+ /**
1795
+ * Encoding to apply to outbound request bodies.
1796
+ * Default: "gzip".
1797
+ */
1798
+ encoding?: SupportedEncoding;
1799
+ /**
1800
+ * Minimum uncompressed body size in bytes before compression is attempted.
1801
+ * Default: 1024.
1802
+ */
1803
+ minBytes?: number;
1804
+ /**
1805
+ * HTTP methods eligible for request compression.
1806
+ * Default: ["POST", "PUT", "PATCH"].
1807
+ */
1808
+ methods?: readonly string[];
1809
+ };
1810
+ /**
1811
+ * Frozen snapshot of compression statistics.
1812
+ */
1813
+ type CompressionStats = {
1814
+ /** Responses decompressed per encoding type */
1815
+ readonly decompressed: Readonly<Record<SupportedEncoding, number>>;
1816
+ /** Total compressed bytes received */
1817
+ readonly compressedBytes: number;
1818
+ /** Total decompressed bytes produced */
1819
+ readonly decompressedBytes: number;
1820
+ /** Responses that bypassed decompression */
1821
+ readonly passthroughCount: number;
1822
+ /** Decompression errors encountered */
1823
+ readonly errorCount: number;
1824
+ /** Unsupported encoding warnings */
1825
+ readonly unsupportedEncodingCount: number;
1826
+ };
1827
+ type RequestCompressionStats = {
1828
+ readonly compressedCount: number;
1829
+ readonly skippedCount: number;
1830
+ readonly errorCount: number;
1831
+ readonly originalBytes: number;
1832
+ readonly compressedBytes: number;
1833
+ };
1834
+ /**
1835
+ * Result of a decompression attempt.
1836
+ */
1837
+ type DecompressResult = {
1838
+ ok: true;
1839
+ data: Buffer;
1840
+ } | {
1841
+ ok: false;
1842
+ error: string;
1843
+ };
1844
+ /**
1845
+ * Abstraction over zlib / noop decompression.
1846
+ */
1847
+ interface Decompressor {
1848
+ readonly isPassthrough: boolean;
1849
+ decompress(data: Buffer | Uint8Array, encoding: SupportedEncoding): DecompressResult;
1850
+ }
1851
+ /**
1852
+ * Result of makeCompressionMiddleware — the middleware plus a stats accessor.
1853
+ */
1854
+ type CompressionMiddlewareResult = {
1855
+ middleware: HttpMiddleware;
1856
+ stats: () => CompressionStats;
1857
+ };
1858
+ type RequestCompressionMiddlewareResult = {
1859
+ middleware: HttpMiddleware;
1860
+ stats: () => RequestCompressionStats;
1861
+ };
1862
+
1863
+ /**
1864
+ * Creates the compression middleware with optional configuration.
1865
+ *
1866
+ * The middleware:
1867
+ * 1. Injects Accept-Encoding header on outgoing requests (if missing)
1868
+ * 2. Decompresses response bodies based on Content-Encoding header
1869
+ * 3. Tracks compression statistics
1870
+ */
1871
+ declare function makeCompressionMiddleware(config?: CompressionConfig): CompressionMiddlewareResult;
1872
+ declare const makeResponseCompressionMiddleware: typeof makeCompressionMiddleware;
1873
+ declare function makeRequestCompressionMiddleware(config?: RequestCompressionConfig): RequestCompressionMiddlewareResult;
1874
+
1875
+ type RequestBatchingEvent = {
1876
+ type: "batch-enqueue";
1877
+ key: string;
1878
+ size: number;
1879
+ request: HttpRequest;
1880
+ } | {
1881
+ type: "batch-flush";
1882
+ key: string;
1883
+ size: number;
1884
+ reason: "size" | "timer" | "manual";
1885
+ } | {
1886
+ type: "batch-cancel";
1887
+ key: string;
1888
+ remaining: number;
1889
+ } | {
1890
+ type: "batch-error";
1891
+ key: string;
1892
+ size: number;
1893
+ error: HttpError;
1894
+ };
1895
+ type RequestBatchingConfig = {
1896
+ /**
1897
+ * Groups requests into independent batches. Return undefined/null/empty string
1898
+ * to bypass batching for a request.
1899
+ *
1900
+ * Default: `${method}:${url}`.
1901
+ */
1902
+ key?: (req: HttpRequest) => string | undefined | null;
1903
+ /** Extra predicate for per-request opt-in/out. Default: batch all keyed requests. */
1904
+ shouldBatch?: (req: HttpRequest) => boolean;
1905
+ /** Maximum requests per batch. Default: 16. */
1906
+ maxBatchSize?: number;
1907
+ /** Maximum time to wait before flushing a non-full batch. Default: 5ms. */
1908
+ maxWaitMs?: number;
1909
+ /** Builds the actual wire request sent to the batch endpoint. */
1910
+ encode: (requests: readonly HttpRequest[]) => HttpRequest;
1911
+ /**
1912
+ * Splits the batch endpoint response back into one response per original
1913
+ * request. The returned array must have the same length and order.
1914
+ */
1915
+ decode: (response: HttpWireResponse, requests: readonly HttpRequest[]) => readonly HttpWireResponse[];
1916
+ /** Optional observability hook. Exceptions are swallowed. */
1917
+ onEvent?: (event: RequestBatchingEvent) => void;
1918
+ };
1919
+ declare function withRequestBatching(config: RequestBatchingConfig): HttpMiddleware;
1920
+
1921
+ /**
1922
+ * The PrewarmManager interface for managing connection pre-warming.
1923
+ */
1924
+ type PrewarmManager = {
1925
+ /** Warm a single origin. Skips if already warm. */
1926
+ warm: (origin: string) => Promise<PrewarmResult>;
1927
+ /** Warm all configured origins. Skips already-warm origins. */
1928
+ warmAll: () => Promise<PrewarmResult[]>;
1929
+ /** Check if an origin has an active warm connection. */
1930
+ isWarm: (origin: string) => boolean;
1931
+ /** Cancel in-flight probe for a specific origin. */
1932
+ cancel: (origin: string) => void;
1933
+ /** Cancel all in-flight and queued probes. */
1934
+ cancelAll: () => void;
1935
+ /** Get a snapshot of all managed origins and their states. */
1936
+ status: () => PrewarmStatusSnapshot;
1937
+ /** Dispose the manager: cancel all, stop timers, release resources. */
1938
+ dispose: () => void;
1939
+ };
1940
+ /**
1941
+ * Creates a PrewarmManager that proactively establishes TCP+TLS connections
1942
+ * to known origins using lightweight HEAD probe requests.
1943
+ *
1944
+ * @param config - Configuration for the prewarm manager.
1945
+ * @returns A PrewarmManager instance.
1946
+ * @throws Error if fetch/AbortController is unavailable or origins are invalid.
1947
+ */
1948
+ declare function makePrewarmManager(config: PrewarmConfig): PrewarmManager;
1949
+
1950
+ /**
1951
+ * Validates and normalizes an origin string.
1952
+ *
1953
+ * A valid origin is: scheme + host + optional port (e.g., "https://api.example.com" or "http://localhost:3000").
1954
+ * Trailing slashes are stripped. Paths, query strings, and fragments are rejected.
1955
+ *
1956
+ * @param origin - The origin string to validate.
1957
+ * @returns The normalized origin string.
1958
+ * @throws Error if the origin is invalid.
1959
+ */
1960
+ declare function validateOrigin(origin: string): string;
1961
+
1962
+ /**
1963
+ * Detects the current runtime platform.
1964
+ *
1965
+ * @returns "browser" if running in a browser environment, "node" otherwise.
1966
+ */
1967
+ declare function detectPlatform(): "browser" | "node";
1968
+ /**
1969
+ * Validates that the global `fetch` API is available.
1970
+ *
1971
+ * @throws Error if `fetch` or `AbortController` is not available.
1972
+ */
1973
+ declare function validateFetchAvailable(): void;
1974
+
1975
+ /**
1976
+ * Outcome of a probe request.
1977
+ */
1978
+ type ProbeOutcome = {
1979
+ /** Whether the probe succeeded (connection established). */
1980
+ ok: boolean;
1981
+ /** Duration of the probe in milliseconds. */
1982
+ durationMs: number;
1983
+ /** Error message if the probe failed. */
1984
+ error?: string;
1985
+ };
1986
+ /**
1987
+ * Options for executing a probe request.
1988
+ */
1989
+ type ProbeOptions = {
1990
+ /** Probe timeout in milliseconds. */
1991
+ timeoutMs: number;
1992
+ /** AbortSignal for external cancellation. */
1993
+ signal: AbortSignal;
1994
+ /** Runtime platform. */
1995
+ platform: "browser" | "node";
1996
+ /** Optional Wire_Client to route through (when useClientPool is true). */
1997
+ client?: HttpClientFn;
1998
+ };
1999
+ /**
2000
+ * Executes a HEAD probe request to the root path of the given origin.
2001
+ *
2002
+ * The probe is a lightweight HEAD request to `${origin}/` designed to trigger
2003
+ * TCP+TLS connection establishment in the platform's connection pool.
2004
+ *
2005
+ * @param origin - The validated origin to probe (e.g., "https://api.example.com").
2006
+ * @param options - Probe configuration options.
2007
+ * @returns A ProbeOutcome indicating success or failure.
2008
+ */
2009
+ declare function executeProbe(origin: string, options: ProbeOptions): Promise<ProbeOutcome>;
2010
+
2011
+ /**
2012
+ * Interface for the connection state map.
2013
+ */
2014
+ type ConnectionStateMap = {
2015
+ /** Mark an origin as warm with the current timestamp. */
2016
+ markWarm: (origin: string, now?: number) => void;
2017
+ /** Mark an origin as expired. */
2018
+ markExpired: (origin: string) => void;
2019
+ /** Mark an origin as idle (reset state). */
2020
+ markIdle: (origin: string) => void;
2021
+ /** Mark an origin as probing. */
2022
+ markProbing: (origin: string) => void;
2023
+ /** Check if an origin is currently warm (not expired). */
2024
+ isWarm: (origin: string, now?: number) => boolean;
2025
+ /** Get the current state of an origin. */
2026
+ getState: (origin: string) => PrewarmOriginState | undefined;
2027
+ /** Get a snapshot of all managed origins. */
2028
+ snapshot: () => PrewarmStatusSnapshot;
2029
+ };
2030
+ /**
2031
+ * Creates a connection state map for tracking per-origin warm/expired/idle states.
2032
+ *
2033
+ * @param origins - Array of origin strings to manage.
2034
+ * @param keepAliveDurationMs - Duration in ms after which a warm connection expires.
2035
+ * @returns A ConnectionStateMap instance.
2036
+ */
2037
+ declare function makeConnectionStateMap(origins: string[], keepAliveDurationMs: number): ConnectionStateMap;
2038
+
2039
+ /**
2040
+ * A lightweight counting semaphore that limits concurrent in-flight operations.
2041
+ */
2042
+ type BudgetSemaphore = {
2043
+ /** Acquire a slot. Resolves with a release handle when a slot is available. */
2044
+ acquire: () => Promise<{
2045
+ release: () => void;
2046
+ }>;
2047
+ /** Try to acquire a slot synchronously. Returns undefined if no slot is available. */
2048
+ tryAcquire: () => {
2049
+ release: () => void;
2050
+ } | undefined;
2051
+ /** Number of currently available slots. */
2052
+ available: () => number;
2053
+ /** Number of waiters currently queued. */
2054
+ queued: () => number;
2055
+ };
2056
+ /**
2057
+ * Creates a budget semaphore with the given capacity.
2058
+ *
2059
+ * @param capacity - Maximum number of concurrent slots. Must be >= 1.
2060
+ * @returns A BudgetSemaphore instance.
2061
+ */
2062
+ declare function makeBudgetSemaphore(capacity: number): BudgetSemaphore;
2063
+
2064
+ type ConnectionPrewarmAttempt = {
2065
+ url: string;
2066
+ origin: string;
2067
+ ok: boolean;
2068
+ status?: number;
2069
+ ms: number;
2070
+ error?: HttpError;
2071
+ };
2072
+ type ConnectionPrewarmResult = {
2073
+ attempted: number;
2074
+ warmed: number;
2075
+ failed: number;
2076
+ skipped: number;
2077
+ attempts: readonly ConnectionPrewarmAttempt[];
2078
+ };
2079
+ type ConnectionPrewarmEvent = {
2080
+ type: "prewarm-start";
2081
+ url: string;
2082
+ origin: string;
2083
+ } | {
2084
+ type: "prewarm-success";
2085
+ url: string;
2086
+ origin: string;
2087
+ status: number;
2088
+ ms: number;
2089
+ } | {
2090
+ type: "prewarm-failure";
2091
+ url: string;
2092
+ origin: string;
2093
+ error: HttpError;
2094
+ ms: number;
2095
+ };
2096
+ type ConnectionPrewarmConfig = {
2097
+ baseUrl?: string;
2098
+ urls?: readonly string[];
2099
+ origins?: readonly string[];
2100
+ path?: string;
2101
+ method?: Extract<HttpMethod, "HEAD" | "GET" | "OPTIONS">;
2102
+ headers?: Record<string, string>;
2103
+ timeoutMs?: number;
2104
+ failFast?: boolean;
2105
+ fetchImpl?: typeof fetch;
2106
+ onEvent?: (event: ConnectionPrewarmEvent) => void;
2107
+ };
2108
+ type ConnectionPrewarmingMiddlewareConfig = ConnectionPrewarmConfig & {
2109
+ once?: boolean;
2110
+ shouldPrewarm?: (req: HttpRequest) => boolean;
2111
+ target?: (req: HttpRequest) => string | undefined | null;
2112
+ };
2113
+ declare function prewarmConnections(config?: ConnectionPrewarmConfig): Async<unknown, HttpError, ConnectionPrewarmResult>;
2114
+ declare const prewarmHttpConnections: typeof prewarmConnections;
2115
+ declare function withConnectionPrewarming(config?: ConnectionPrewarmingMiddlewareConfig): HttpMiddleware;
2116
+
2117
+ type InitNoMethodBody = Omit<RequestInit, "method" | "body"> & {
2118
+ timeoutMs?: number;
2119
+ poolKey?: string;
2120
+ headers?: unknown;
2121
+ };
2122
+ type InitWithHeaders = {
2123
+ headers?: unknown;
2124
+ timeoutMs?: number;
2125
+ poolKey?: string;
2126
+ } & Record<string, unknown>;
2127
+ type HttpJsonInit<Validator extends AnyJsonSchemaLike> = InitNoMethodBody & {
2128
+ readonly schema: Validator;
2129
+ readonly schemaName?: string;
2130
+ };
2131
+ type HttpPostJsonInit<Validator extends AnyJsonSchemaLike> = InitWithHeaders & {
2132
+ readonly schema: Validator;
2133
+ readonly schemaName?: string;
2134
+ readonly bodySchema?: undefined;
2135
+ readonly bodySchemaName?: string;
2136
+ };
2137
+ type HttpPostJsonSchemaBodyInit<Validator extends AnyJsonSchemaLike, BodyValidator extends AnyJsonSchemaLike> = InitWithHeaders & {
2138
+ readonly schema: Validator;
2139
+ readonly schemaName?: string;
2140
+ readonly bodySchema: BodyValidator;
2141
+ readonly bodySchemaName?: string;
2142
+ };
2143
+ type HttpPostJsonBodyInit<BodyValidator extends AnyJsonSchemaLike> = InitWithHeaders & {
2144
+ readonly schema?: undefined;
2145
+ readonly schemaName?: string;
2146
+ readonly bodySchema: BodyValidator;
2147
+ readonly bodySchemaName?: string;
2148
+ };
2149
+ type JsonInitNoSchema = InitNoMethodBody & {
2150
+ readonly schema?: undefined;
2151
+ readonly schemaName?: string;
2152
+ };
2153
+ type PostJsonInitNoSchema = InitWithHeaders & {
2154
+ readonly schema?: undefined;
2155
+ readonly schemaName?: string;
2156
+ readonly bodySchema?: undefined;
2157
+ readonly bodySchemaName?: string;
2158
+ };
2159
+ type DefaultGetJson = {
2160
+ <Validator extends AnyJsonSchemaLike>(url: string, init: HttpJsonInit<Validator>): AsyncWithPromise<unknown, HttpError | ValidationError, HttpResponse<InferJsonSchema<Validator>>>;
2161
+ <A = unknown>(url: string, init?: JsonInitNoSchema): AsyncWithPromise<unknown, HttpError | ValidationError, HttpResponse<A>>;
2162
+ };
2163
+ type DefaultPostJson = {
2164
+ <Validator extends AnyJsonSchemaLike, BodyValidator extends AnyJsonSchemaLike>(url: string, bodyObj: InferJsonSchema<BodyValidator>, init: HttpPostJsonSchemaBodyInit<Validator, BodyValidator>): AsyncWithPromise<unknown, HttpError | ValidationError, HttpResponse<InferJsonSchema<Validator>>>;
2165
+ <BodyValidator extends AnyJsonSchemaLike, A = unknown>(url: string, bodyObj: InferJsonSchema<BodyValidator>, init: HttpPostJsonBodyInit<BodyValidator>): AsyncWithPromise<unknown, HttpError | ValidationError, HttpResponse<A>>;
2166
+ <Validator extends AnyJsonSchemaLike>(url: string, bodyObj: unknown, init: HttpPostJsonInit<Validator>): AsyncWithPromise<unknown, HttpError | ValidationError, HttpResponse<InferJsonSchema<Validator>>>;
2167
+ <A = unknown>(url: string, bodyObj: unknown, init?: PostJsonInitNoSchema): AsyncWithPromise<unknown, HttpError | ValidationError, HttpResponse<A>>;
2168
+ };
2169
+ type DefaultHttpClientPreset = "minimal" | "balanced" | "default";
2170
+ type DefaultHttpClientFeatures = {
2171
+ readonly dedup: boolean;
2172
+ readonly batch: boolean;
2173
+ readonly cache: boolean;
2174
+ readonly priority: boolean;
2175
+ readonly retry: boolean;
2176
+ readonly prewarm: boolean;
2177
+ readonly adaptiveLimiter: boolean;
2178
+ readonly compression: boolean;
2179
+ readonly middleware: number;
2180
+ };
2181
+ type DefaultHttpClientConfig = LifecycleClientConfig & {
2182
+ /**
2183
+ * Preset used as the baseline before caller overrides are applied.
2184
+ * - minimal: wire client + timeout only.
2185
+ * - balanced: retry, priority, dedup, adaptive limiter, response compression.
2186
+ * - default: balanced + short safe-method response cache.
2187
+ */
2188
+ readonly preset?: DefaultHttpClientPreset;
2189
+ /** Response decompression. Enabled by balanced/default presets; set false to disable. */
2190
+ readonly compression?: CompressionConfig | false;
2191
+ /** Extra middleware applied outermost after the preset stack, e.g. withHttpObservability(obs). */
2192
+ readonly middleware?: readonly HttpMiddleware[];
2193
+ };
2194
+ type DefaultHttpClient = {
2195
+ readonly request: (req: HttpRequest) => AsyncWithPromise<unknown, HttpError, HttpWireResponse>;
2196
+ readonly get: (url: string, init?: InitNoMethodBody) => AsyncWithPromise<unknown, HttpError, HttpWireResponse>;
2197
+ readonly post: (url: string, body?: string, init?: InitWithHeaders) => AsyncWithPromise<unknown, HttpError, HttpWireResponse>;
2198
+ readonly getText: (url: string, init?: InitNoMethodBody) => AsyncWithPromise<unknown, HttpError, HttpResponse<string>>;
2199
+ readonly getJson: DefaultGetJson;
2200
+ readonly postJson: DefaultPostJson;
2201
+ readonly with: (mw: HttpMiddleware) => DefaultHttpClient;
2202
+ readonly wire: LifecycleClient;
2203
+ readonly stats: () => LifecycleStats;
2204
+ readonly cache: LifecycleClient["cache"];
2205
+ readonly cancelAll: LifecycleClient["cancelAll"];
2206
+ readonly shutdown: LifecycleClient["shutdown"];
2207
+ readonly preset: DefaultHttpClientPreset;
2208
+ readonly features: DefaultHttpClientFeatures;
2209
+ readonly compression?: {
2210
+ readonly stats: () => CompressionStats;
2211
+ };
2212
+ };
2213
+ declare const defaultHttpClientPreset: DefaultHttpClientPreset;
2214
+ /**
2215
+ * Creates the recommended default HTTP client.
2216
+ *
2217
+ * The returned client has the easy JSON/text helpers from `httpClient`, but its
2218
+ * wire path is the full lifecycle stack: priority, retry, cache, batch, dedup,
2219
+ * adaptive concurrency, optional prewarm, compression, stats, and cancelAll.
2220
+ */
2221
+ declare function makeDefaultHttpClient(config?: DefaultHttpClientConfig): DefaultHttpClient;
2222
+
2223
+ type HttpClientBuilder = {
2224
+ readonly config: () => DefaultHttpClientConfig;
2225
+ readonly baseUrl: (baseUrl: string) => HttpClientBuilder;
2226
+ readonly header: (name: string, value: string) => HttpClientBuilder;
2227
+ readonly headers: (headers: Record<string, string>) => HttpClientBuilder;
2228
+ readonly timeoutMs: (timeoutMs: number) => HttpClientBuilder;
2229
+ readonly timeout: (timeoutMs: number) => HttpClientBuilder;
2230
+ readonly preset: (preset: DefaultHttpClientPreset) => HttpClientBuilder;
2231
+ readonly minimal: () => HttpClientBuilder;
2232
+ readonly balanced: () => HttpClientBuilder;
2233
+ readonly defaultPreset: () => HttpClientBuilder;
2234
+ readonly dedup: (config?: DedupConfig$1 | false) => HttpClientBuilder;
2235
+ readonly noDedup: () => HttpClientBuilder;
2236
+ readonly batch: (config: BatchConfig | false) => HttpClientBuilder;
2237
+ readonly noBatch: () => HttpClientBuilder;
2238
+ readonly cache: (config?: CacheConfig$1 | false) => HttpClientBuilder;
2239
+ readonly noCache: () => HttpClientBuilder;
2240
+ readonly priority: (config?: PriorityConfig$1 | false) => HttpClientBuilder;
2241
+ readonly noPriority: () => HttpClientBuilder;
2242
+ readonly retry: (config?: RetryPolicy | false) => HttpClientBuilder;
2243
+ readonly noRetry: () => HttpClientBuilder;
2244
+ readonly prewarm: (config?: PrewarmLifecycleConfig | false) => HttpClientBuilder;
2245
+ readonly noPrewarm: () => HttpClientBuilder;
2246
+ readonly adaptiveLimiter: (config?: AdaptiveLimiterConfig | false) => HttpClientBuilder;
2247
+ readonly adaptiveLimiterPreset: (preset: AdaptiveLimiterPreset, overrides?: AdaptiveLimiterConfig) => HttpClientBuilder;
2248
+ readonly conservativeLimiter: (overrides?: AdaptiveLimiterConfig) => HttpClientBuilder;
2249
+ readonly balancedLimiter: (overrides?: AdaptiveLimiterConfig) => HttpClientBuilder;
2250
+ readonly aggressiveLimiter: (overrides?: AdaptiveLimiterConfig) => HttpClientBuilder;
2251
+ readonly noAdaptiveLimiter: () => HttpClientBuilder;
2252
+ readonly pool: (config?: HttpPoolConfig | false) => HttpClientBuilder;
2253
+ readonly noPool: () => HttpClientBuilder;
2254
+ readonly compression: (config?: CompressionConfig | false) => HttpClientBuilder;
2255
+ readonly noCompression: () => HttpClientBuilder;
2256
+ readonly middleware: (mw: HttpMiddleware) => HttpClientBuilder;
2257
+ readonly use: (mw: HttpMiddleware) => HttpClientBuilder;
2258
+ readonly configure: (config: DefaultHttpClientConfig) => HttpClientBuilder;
2259
+ readonly build: () => DefaultHttpClient;
2260
+ readonly buildWire: () => LifecycleClient;
2261
+ };
2262
+ declare function httpClientBuilder(config?: DefaultHttpClientConfig): HttpClientBuilder;
2263
+ declare const makeHttpClientBuilder: typeof httpClientBuilder;
2264
+ declare const httpBuilder: typeof httpClientBuilder;
2265
+
2266
+ type HttpServerMethod = HttpMethod | "ALL";
2267
+ type HttpServerHeaders = Record<string, string>;
2268
+ type HttpServerQuery = Record<string, string | readonly string[]>;
2269
+ type HttpServerParams = Record<string, string>;
2270
+ type HttpServerBody = string | Uint8Array | ArrayBuffer | unknown;
2271
+ type HttpServerRequest = {
2272
+ readonly method: string;
2273
+ readonly url: string;
2274
+ readonly path: string;
2275
+ readonly target: string;
2276
+ readonly headers: HttpServerHeaders;
2277
+ readonly query: HttpServerQuery;
2278
+ readonly params: HttpServerParams;
2279
+ readonly bodyText: string;
2280
+ readonly raw?: unknown;
2281
+ };
2282
+ type HttpServerContext<Params = HttpServerParams, Query = HttpServerQuery, Body = unknown> = Omit<HttpServerRequest, "params" | "query"> & {
2283
+ readonly route: string;
2284
+ readonly params: Params;
2285
+ readonly query: Query;
2286
+ readonly body: Body;
2287
+ };
2288
+ type HttpServerResponse<Body = unknown> = {
2289
+ readonly status?: number;
2290
+ readonly headers?: HttpServerHeaders;
2291
+ readonly body?: Body;
2292
+ };
2293
+ type HttpServerHandler<R = unknown, E = unknown, Params = HttpServerParams, Query = HttpServerQuery, Body = unknown, ResponseBody = unknown> = (ctx: HttpServerContext<Params, Query, Body>) => Async<R, E, HttpServerResponse<ResponseBody>>;
2294
+ type HttpServerMiddleware = (next: HttpServerHandler<any, any, any, any, any, any>) => HttpServerHandler<any, any, any, any, any, any>;
2295
+ type HttpRouteSchemas<ParamsSchema extends AnyJsonSchemaLike | undefined = undefined, QuerySchema extends AnyJsonSchemaLike | undefined = undefined, BodySchema extends AnyJsonSchemaLike | undefined = undefined, ResponseSchema extends AnyJsonSchemaLike | undefined = undefined> = {
2296
+ readonly params?: ParamsSchema;
2297
+ readonly paramsSchemaName?: string;
2298
+ readonly query?: QuerySchema;
2299
+ readonly querySchemaName?: string;
2300
+ readonly body?: BodySchema;
2301
+ readonly bodySchemaName?: string;
2302
+ readonly response?: ResponseSchema;
2303
+ readonly responseSchemaName?: string;
2304
+ };
2305
+ type HttpRouteOptions<ParamsSchema extends AnyJsonSchemaLike | undefined = undefined, QuerySchema extends AnyJsonSchemaLike | undefined = undefined, BodySchema extends AnyJsonSchemaLike | undefined = undefined, ResponseSchema extends AnyJsonSchemaLike | undefined = undefined> = HttpRouteSchemas<ParamsSchema, QuerySchema, BodySchema, ResponseSchema> & {
2306
+ readonly middleware?: readonly HttpServerMiddleware[];
2307
+ };
2308
+ type HttpRuntimeHealthRouteOptions = RuntimeHealthOptions & {
2309
+ readonly path?: string;
2310
+ };
2311
+ type InferServerPart<Schema, Fallback> = Schema extends AnyJsonSchemaLike ? InferJsonSchema<Schema> : Fallback;
2312
+ type RoutePathParamNames<Path extends string> = Path extends `${string}:${infer Param}/${infer Rest}` ? StripRouteParamModifier<Param> | RoutePathParamNames<`/${Rest}`> : Path extends `${string}:${infer Param}` ? StripRouteParamModifier<Param> : never;
2313
+ type RoutePathParams<Path extends string> = [
2314
+ RoutePathParamNames<Path>
2315
+ ] extends [never] ? {} : {
2316
+ readonly [Key in RoutePathParamNames<Path>]: string;
2317
+ };
2318
+ type StripRouteParamModifier<Param extends string> = Param extends `${infer Name}?` ? Name : Param extends `${infer Name}+` ? Name : Param extends `${infer Name}*` ? Name : Param;
2319
+ type HttpServerRoute<R = unknown, E = unknown, Path extends string = string, ParamsSchema extends AnyJsonSchemaLike | undefined = AnyJsonSchemaLike | undefined, QuerySchema extends AnyJsonSchemaLike | undefined = AnyJsonSchemaLike | undefined, BodySchema extends AnyJsonSchemaLike | undefined = AnyJsonSchemaLike | undefined, ResponseSchema extends AnyJsonSchemaLike | undefined = AnyJsonSchemaLike | undefined> = {
2320
+ readonly method: HttpServerMethod;
2321
+ readonly path: Path;
2322
+ readonly options: HttpRouteOptions<ParamsSchema, QuerySchema, BodySchema, ResponseSchema>;
2323
+ readonly handler: HttpServerHandler<R, E, InferServerPart<ParamsSchema, RoutePathParams<Path>>, InferServerPart<QuerySchema, HttpServerQuery>, InferServerPart<BodySchema, unknown>, InferServerPart<ResponseSchema, unknown>>;
2324
+ readonly match: (path: string) => HttpServerParams | undefined;
2325
+ };
2326
+ type HttpRouterOptions = {
2327
+ readonly middleware?: readonly HttpServerMiddleware[];
2328
+ readonly includeErrorDetails?: boolean;
2329
+ };
2330
+ type HttpRouteMatch = {
2331
+ readonly _tag: "Match";
2332
+ readonly route: HttpServerRoute<any, any, any, any, any, any, any>;
2333
+ readonly params: HttpServerParams;
2334
+ } | {
2335
+ readonly _tag: "MethodNotAllowed";
2336
+ readonly route: HttpServerRoute<any, any, any, any, any, any, any>;
2337
+ readonly allowed: readonly HttpServerMethod[];
2338
+ } | {
2339
+ readonly _tag: "NotFound";
2340
+ };
2341
+ type HttpRouter = {
2342
+ readonly routes: readonly HttpServerRoute<any, any, any, any, any, any, any>[];
2343
+ readonly match: (method: string, path: string) => HttpRouteMatch;
2344
+ readonly handle: (request: HttpServerRequest, match?: HttpRouteMatch) => Async<unknown, never, HttpServerResponse>;
2345
+ readonly listen: <R extends object = {}>(options?: Omit<NodeHttpServerOptions<R>, "router">) => Resource<unknown, NodeHttpServerError, NodeHttpServerHandle>;
2346
+ };
2347
+ type NodeHttpServerError = {
2348
+ readonly _tag: "ListenError";
2349
+ readonly error: unknown;
2350
+ readonly message: string;
2351
+ } | {
2352
+ readonly _tag: "ServerClosed";
2353
+ readonly message: string;
2354
+ };
2355
+ type NodeHttpServerHandle = {
2356
+ readonly server: Server;
2357
+ readonly router: HttpRouter;
2358
+ readonly address: () => AddressInfo | string | null;
2359
+ readonly url: () => string | undefined;
2360
+ readonly close: () => Promise<void>;
2361
+ };
2362
+ type NodeHttpServerOptions<R extends object = {}> = {
2363
+ readonly router: HttpRouter | readonly HttpServerRoute<any, any, any, any, any, any, any>[];
2364
+ readonly host?: string;
2365
+ readonly port?: number;
2366
+ readonly env?: R;
2367
+ readonly runtime?: Runtime<R>;
2368
+ readonly runtimeOptions?: Omit<RuntimeOptions<R>, "env" | "hooks">;
2369
+ readonly observability?: Observability;
2370
+ readonly observabilityOptions?: HttpServerObservabilityOptions<HttpServerResponse>;
2371
+ readonly maxBodyBytes?: number;
2372
+ readonly gracefulShutdownMs?: number;
2373
+ readonly shutdownPollSchedule?: Schedule<NodeHttpServerShutdownState, unknown>;
2374
+ readonly onError?: (error: unknown) => void;
2375
+ };
2376
+ type NodeHttpServerShutdownState = {
2377
+ readonly listening: boolean;
2378
+ readonly elapsedMs: number;
2379
+ };
2380
+ declare function route<Path extends string, ParamsSchema extends AnyJsonSchemaLike | undefined = undefined, QuerySchema extends AnyJsonSchemaLike | undefined = undefined, BodySchema extends AnyJsonSchemaLike | undefined = undefined, ResponseSchema extends AnyJsonSchemaLike | undefined = undefined, R = unknown, E = unknown>(method: HttpServerMethod, path: Path, options: HttpRouteOptions<ParamsSchema, QuerySchema, BodySchema, ResponseSchema>, handler: HttpServerHandler<R, E, InferServerPart<ParamsSchema, RoutePathParams<Path>>, InferServerPart<QuerySchema, HttpServerQuery>, InferServerPart<BodySchema, unknown>, InferServerPart<ResponseSchema, unknown>>): HttpServerRoute<R, E, Path, ParamsSchema, QuerySchema, BodySchema, ResponseSchema>;
2381
+ declare function route<Path extends string, R = unknown, E = unknown>(method: HttpServerMethod, path: Path, handler: HttpServerHandler<R, E, RoutePathParams<Path>>): HttpServerRoute<R, E, Path, undefined, undefined, undefined, undefined>;
2382
+ declare const httpRoute: typeof route;
2383
+ declare function makeHttpRouter(routes: readonly HttpServerRoute<any, any, any, any, any, any, any>[], options?: HttpRouterOptions): HttpRouter;
2384
+ declare function json<Body>(body: Body, init?: Omit<HttpServerResponse<Body>, "body">): HttpServerResponse<Body>;
2385
+ declare function text(body: string, init?: Omit<HttpServerResponse<string>, "body">): HttpServerResponse<string>;
2386
+ declare function empty(status?: number, headers?: HttpServerHeaders): HttpServerResponse<void>;
2387
+ declare function makeRuntimeHealthRoute(options?: HttpRuntimeHealthRouteOptions): HttpServerRoute<unknown, never, string, undefined, undefined, undefined, undefined>;
2388
+ declare function makeRuntimeReadinessRoute(options?: HttpRuntimeHealthRouteOptions): HttpServerRoute<unknown, never, string, undefined, undefined, undefined, undefined>;
2389
+ declare const runtimeHealthRoute: typeof makeRuntimeHealthRoute;
2390
+ declare const runtimeReadinessRoute: typeof makeRuntimeReadinessRoute;
2391
+ declare function withResponseHeader(name: string, value: string): HttpServerMiddleware;
2392
+ declare function makeNodeHttpServer<R extends object = {}>(options: NodeHttpServerOptions<R>): Async<unknown, NodeHttpServerError, NodeHttpServerHandle>;
2393
+ declare function nodeHttpServerResource<R extends object = {}>(options: NodeHttpServerOptions<R>): Resource<unknown, NodeHttpServerError, NodeHttpServerHandle>;
2394
+ declare const makeNodeHttpServerResource: typeof nodeHttpServerResource;
2395
+ declare const makeHttpServerResource: typeof nodeHttpServerResource;
326
2396
 
327
- export { type Dx, type HttpCircuitBreakerConfig, type HttpClient, type HttpClientFn, type HttpClientStats, type HttpClientStream, type HttpClientStreamFn, HttpConcurrencyPool, type HttpError, type HttpInit, type HttpMeta, type HttpMethod, type HttpMiddleware, type HttpPoolConfig, type HttpPoolKeyResolver, type HttpPoolKeyStats, type HttpPoolLease, type HttpPoolStats, type HttpRequest, type HttpResponse, type HttpResponseWithMeta, type HttpWireResponse, type HttpWireResponseStream, type HttpWireWithMeta, type JsonValidator, type MakeHttpConfig, type ValidationError, decorate, httpClient, httpClientStream, httpClientWithMeta, makeHttp, makeHttpStream, normalizeHeadersInit, resolveHttpPoolKey, validatedJson, withCircuitBreaker, withMiddleware, withRetryStream, withTracing };
2397
+ export { AdaptiveLimiter, AdaptiveLimiterConfig, AdaptiveLimiterPreset, AnyJsonSchemaLike, type BatchConfig, type BatchFunction, type BudgetSemaphore, type CacheConfig$1 as CacheConfig, type CacheKeyComponents, type CachePolicyResult$1 as CachePolicyResult, type CompressionConfig, type CompressionMiddlewareResult, type CompressionStats, type ConnectionPrewarmAttempt, type ConnectionPrewarmConfig, type ConnectionPrewarmEvent, type ConnectionPrewarmResult, type ConnectionPrewarmingMiddlewareConfig, type ConnectionStateMap, DEFAULT_CACHE_RELEVANT_HEADERS, type DecompressResult, type Decompressor, type DedupConfig$1 as DedupConfig, type DefaultGetJson, type DefaultHttpClient, type DefaultHttpClientConfig, type DefaultHttpClientFeatures, type DefaultHttpClientPreset, type DefaultPostJson, type Dx, EmaComputer, type HttpCircuitBreakerConfig, HttpClient, type HttpClientBuilder, HttpClientFn, HttpClientStats, HttpClientStream, HttpError, type HttpErrorHandlers, type HttpJsonInit, type HttpMeta, HttpMethod, HttpMiddleware, HttpPoolConfig, type HttpPostJsonBodyInit, type HttpPostJsonInit, type HttpPostJsonSchemaBodyInit, HttpRequest, type HttpResponse, type HttpResponseWithMeta, type HttpRouteMatch, type HttpRouteOptions, type HttpRouteSchemas, type HttpRouter, type HttpRouterOptions, type HttpRuntimeHealthRouteOptions, type HttpServerBody, type HttpServerContext, type HttpServerHandler, type HttpServerHeaders, type HttpServerMethod, type HttpServerMiddleware, type HttpServerParams, type HttpServerQuery, type HttpServerRequest, type HttpServerResponse, type HttpServerRoute, HttpWireResponse, HttpWireResponseStream, type HttpWireWithMeta, InferJsonSchema, type InferServerPart, type JsonDecodeResult, JsonSchemaLike, type KnownHttpError, type KnownHttpErrorTag, LRUCache, type LRUCacheConfig, LatencyWindow, type LifecycleClient, type LifecycleClientConfig, type LifecycleEvent, type LifecycleEventType, type LifecycleRequestOptions, type LifecycleStats, LifecycleStatsTracker, type LogEvent, MakeHttpConfig, type NodeHttpServerError, type NodeHttpServerHandle, type NodeHttpServerOptions, type NodeHttpServerShutdownState, type PrewarmConfig, type PrewarmEvent, type PrewarmEventType, type PrewarmLifecycleConfig, type PrewarmManager, type PrewarmOriginState, type PrewarmOriginStatus, type PrewarmResult, type PrewarmResultStatus, type PrewarmStatusSnapshot, type PriorityConfig$1 as PriorityConfig, PriorityQueue, type PriorityQueueEntry, type ProbeOutcome, type RequestBatchingConfig, type RequestBatchingEvent, type RequestCompressionConfig, type RequestCompressionMiddlewareResult, type RequestCompressionStats, RetryPolicy, type RoutePathParamNames, type RoutePathParams, SEPARATOR, SUPPORTED_ENCODINGS, SchemaIssue, type SupportedEncoding, type ValidationError, clampPriority, computeCacheKey, computeGradient, computeNewLimit, decodeJsonBody, decodeJsonBodyEffect, defaultHttpClientPreset, detectPlatform, empty, encodeJsonBodyEffect, executeProbe, formatHttpError, httpBuilder, httpClient, httpClientBuilder, httpClientStream, httpClientWithMeta, httpRoute, isCircuitBreakerOpen, isHttpError, isKnownHttpError, isValidationError, json, makeBudgetSemaphore, makeCompressionMiddleware, makeConnectionStateMap, makeDefaultHttpClient, makeHttpClient, makeHttpClientBuilder, makeHttpRouter, makeHttpServerResource, makeLifecycleClient, makeNodeHttpServer, makeNodeHttpServerResource, makePrewarmManager, makeRequestCompressionMiddleware, makeResponseCompressionMiddleware, makeRuntimeHealthRoute, makeRuntimeReadinessRoute, matchHttpError, nodeHttpServerResource, parseCacheKey, prewarmConnections, prewarmHttpConnections, route, runtimeHealthRoute, runtimeReadinessRoute, text, validateFetchAvailable, validateOrigin, validatedJson, validatedJsonResponse, withAuth, withBatch, withCache, withCircuitBreaker, withConnectionPrewarming, withDedup, withLogging, withPriority, withRequestBatching, withResponseHeader, withResponseTransform, withTracing };