@defjs/core 0.4.0-beta.0 → 0.4.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 (5) hide show
  1. package/README.md +64 -72
  2. package/index.d.ts +2280 -731
  3. package/index.js +5830 -4562
  4. package/index.min.js +38 -2
  5. package/package.json +3 -1
package/index.d.ts CHANGED
@@ -1,12 +1,29 @@
1
- //#region src/internal/http_request.d.ts
2
1
  /**
3
- * How the HTTP response body should be parsed before struct validation.
4
- * Defaults to `'json'` when an output shape is declared.
2
+ * Representation used to read an HTTP body before struct validation.
3
+ *
4
+ * Chosen from the declared struct kind: `string` is `text`, `blob` and `file`
5
+ * are `blob`, `arrayBuffer` is `arraybuffer` (bytes), and every other kind is
6
+ * `json`. Not a field on `defineRequest`. It appears on `HttpRequest` as
7
+ * `responseType` and `errorResponseType`.
8
+ *
9
+ * @group http
5
10
  */
6
- type HttpResponseType = 'arraybuffer' | 'blob' | 'json' | 'text';
11
+ type HttpResponseType = "arraybuffer" | "blob" | "json" | "text";
7
12
  /**
8
- * Progress snapshot for upload or download callbacks.
9
- * Mirrors the familiar `ProgressEvent` shape (`loaded` / `total` / `lengthComputable`).
13
+ * Progress snapshot passed to upload and download callbacks.
14
+ *
15
+ * Mirrors the `ProgressEvent` fields `loaded`, `total`, and `lengthComputable`.
16
+ * `loaded` counts bytes read from the upload or download stream, not bytes
17
+ * acknowledged by the server. Download bytes may already be decompressed.
18
+ * Unknown lengths use `total: 0` and `lengthComputable: false`. A download total
19
+ * is computable only with a positive `Content-Length` and an absent or
20
+ * `identity` `Content-Encoding`; compressed downloads, other encoded responses,
21
+ * and bytes exceeding `Content-Length` have an unknown decoded length. A
22
+ * streamed FormData upload also has an unknown total unless a valid explicit
23
+ * `Content-Length` supplies it. Other unknown-length streams use the same
24
+ * `0` / `false` fields.
25
+ *
26
+ * @group http
10
27
  */
11
28
  interface HttpProgressEvent {
12
29
  readonly lengthComputable: boolean;
@@ -15,31 +32,85 @@ interface HttpProgressEvent {
15
32
  }
16
33
  /**
17
34
  * Callback invoked as request bytes are uploaded or response bytes are downloaded.
35
+ *
36
+ * Passed as `onDownloadProgress` or `onUploadProgress` in `HttpExecuteOptions`.
37
+ * Callbacks may be async and are awaited before the next chunk is processed. A
38
+ * callback that throws or rejects fails the call with `EXT_OBSERVER_FAILED`,
39
+ * with the thrown value as `cause`, for downloads and uploads alike. Caller
40
+ * cancellation and `timeout` still take precedence and report `NET_ABORTED` or
41
+ * `NET_TIMEOUT`.
42
+ *
43
+ * Upload progress for a `Blob`, `File`, `ArrayBuffer`, or `FormData` body is
44
+ * reported only where the runtime can stream request bodies (Bun, Node). In
45
+ * browsers that body is sent unchanged and `onUploadProgress` is not called. A
46
+ * caller-supplied `ReadableStream` still reports progress wherever the runtime
47
+ * accepts it. Download progress is unaffected.
48
+ *
49
+ * On server runtimes, FormData with `onUploadProgress` reads its serialized
50
+ * stream directly, keeps its multipart boundary, and is sent without a
51
+ * library-generated `Content-Length` (chunked over HTTP/1.1), so progress,
52
+ * timeout, and abort work without buffering the whole body. It reports an
53
+ * unknown total unless a valid explicit `Content-Length` supplies it. For an
54
+ * endpoint that requires `Content-Length`, such as some presigned upload
55
+ * targets, omit `onUploadProgress`; platform Fetch then sends FormData with its
56
+ * length. Non-identity encoded downloads also report `total: 0` and
57
+ * `lengthComputable: false`, since `loaded` counts decoded stream bytes.
58
+ * Cancellation releases the wrapped upload stream.
59
+ *
60
+ * @group http
18
61
  */
19
62
  type HttpProgressFn = (event: HttpProgressEvent) => Promise<void> | void;
20
63
  /**
21
- * Normalized outgoing HTTP request used by handlers, interceptors, and `fetchHandler`.
64
+ * Normalized outgoing HTTP request seen by transports, handlers, interceptors, and `fetchHandler` after the builder runs.
65
+ *
66
+ * `body` is already the wire value. For JSON that is a **JSON text string**;
67
+ * sign HMAC or digests against that string (or other final bytes), not the
68
+ * original struct input.
22
69
  *
23
- * After `struct.json(...)` (or another body codec) runs during command build, `body` is
24
- * already the wire value — for JSON that means a **JSON text string**. Sign HMAC / digests
25
- * against that string (or other final bytes), not the original Struct input object.
70
+ * - `bodyContentType` holds the inferred type of the builder's body. `null` suppresses that header. FormData owns its multipart boundary. HTTP inference follows a direct write to `body` and is not copied by object spread; an explicit `bodyContentType` assignment is copied. A replacement body is detected again, so declare its type with a `content-type` header. For the unchanged body, `null` suppresses library inference when there is no explicit header, but native Fetch may add its own string type.
71
+ * - HTTP never writes inferred Content-Type into `headers`; interceptors see only declared headers. An explicit `content-type` header always wins over `bodyContentType` and detection, whether set by the builder, client defaults, an interceptor, or a raw `fetchHandler` request. `body: undefined` has no library-inferred Content-Type. Signers should read `headers.get('content-type') ?? request.bodyContentType`; do not configure internal fields.
72
+ * - `endpoint` is the path with path parameters filled in; `baseEndpoint` is the client endpoint.
73
+ * - Interceptors see `queryParams` as `new URLSearchParams(queryString)`. A builder that returns `URLSearchParams` passes that instance through.
74
+ * - `operation` is a static, low-cardinality endpoint identity for telemetry and diagnostics.
75
+ * - `downloadProgress` and `uploadProgress` are the `onDownloadProgress` and `onUploadProgress` hooks of `HttpExecuteOptions`.
76
+ * - `responseType` and `errorResponseType` are readonly: they are derived from the endpoint's `output` and `error` struct kinds and must not be changed. Each is set only when its struct is declared. An interceptor that short-circuits with `makeResponse` reads them to match the transport.
77
+ *
78
+ * @group http
26
79
  */
27
80
  interface HttpRequest {
28
81
  abort?: AbortSignal;
82
+ /** Trusted absolute base; its origin and directory bound the resolved endpoint path. */
29
83
  baseEndpoint?: string;
30
84
  body?: Blob | ArrayBuffer | FormData | URLSearchParams | ReadableStream<Uint8Array> | object | string | number | boolean | null;
85
+ /**
86
+ * Current body media type; null suppresses the header. FormData owns its boundary.
87
+ * HTTP inference follows direct body mutations and is not copied by object spread; explicit assignments are copied.
88
+ */
31
89
  bodyContentType?: string | null;
32
90
  /** @internal Tracks which body value produced bodyContentType so interceptors cannot leave stale metadata behind. */
33
91
  bodyContentTypeSource?: unknown;
34
92
  downloadProgress?: HttpProgressFn;
93
+ /** Relative path validated after building and again when an interceptor calls the transport. */
35
94
  endpoint: string;
95
+ /** HTTP interceptors see declared headers; detected Content-Type is applied at the Fetch boundary. */
36
96
  headers?: Headers;
37
97
  method: string;
38
98
  /** Static, low-cardinality endpoint identity for telemetry and diagnostics. */
39
99
  operation?: string;
40
100
  queryParams?: URLSearchParams;
41
101
  queryString?: string;
42
- responseType?: HttpResponseType;
102
+ /**
103
+ * Representation for a 2xx body, derived from the endpoint `output` struct kind.
104
+ * Set only when `output` is declared. Interceptors that short-circuit with `makeResponse`
105
+ * read this to match the transport. Must not be changed. `undefined` leaves the 2xx body unread.
106
+ */
107
+ readonly responseType?: HttpResponseType;
108
+ /**
109
+ * Representation for a non-2xx body, derived from the endpoint `error` struct kind.
110
+ * Set only when `error` is declared. Must not be changed. `undefined` leaves the non-2xx body
111
+ * unread and does not fall back to `responseType`.
112
+ */
113
+ readonly errorResponseType?: HttpResponseType;
43
114
  timeout?: number;
44
115
  uploadProgress?: HttpProgressFn;
45
116
  withCredentials?: boolean;
@@ -51,12 +122,19 @@ interface HttpRequest {
51
122
  }) => string | null | undefined;
52
123
  };
53
124
  }
54
- //#endregion
55
- //#region src/internal/http_response.d.ts
56
125
  declare const HTTP_RESPONSE: unique symbol;
57
126
  /**
58
- * Parsed HTTP response wrapper returned by handlers and `executeHttpCommand`.
59
- * `ok` is true for status codes in the 2xx range; `error` may hold transport or parse failures.
127
+ * Wire-shape HTTP response that transports produce and interceptors accept.
128
+ *
129
+ * `body` is parsed JSON, text, a `Blob`, or an `ArrayBuffer`, according to
130
+ * `responseType`, not a decoded struct value. `body` is `undefined` when no body
131
+ * was present; a JSON `null` stays `null`. For a JSON representation, a string
132
+ * is a JSON string root and is not parsed again; bytes and blobs are raw and
133
+ * are parsed once. `ok` is true for status codes in the 2xx range. A transport
134
+ * failure is a fault, never a response. `DecodedResponse` is what reaches the
135
+ * caller. The symbol key is an internal brand. Build values with `makeResponse`.
136
+ *
137
+ * @group http
60
138
  */
61
139
  type HttpResponse<R> = {
62
140
  readonly [HTTP_RESPONSE]: true;
@@ -64,12 +142,46 @@ type HttpResponse<R> = {
64
142
  readonly status: number;
65
143
  readonly statusText: string;
66
144
  readonly headers: Headers;
67
- readonly body: R | null;
68
- readonly error?: unknown;
145
+ /** Absent when no body was present. JSON `null` stays `null`. */
146
+ readonly body: R | null | undefined;
147
+ readonly ok: boolean;
148
+ };
149
+ /**
150
+ * Response metadata, available whenever a response reached the client.
151
+ *
152
+ * It carries no `body`: a body only exists once a declared struct decoded it.
153
+ *
154
+ * @group http
155
+ */
156
+ type HttpMeta = {
157
+ readonly headers: Headers;
69
158
  readonly ok: boolean;
159
+ readonly status: number;
160
+ readonly statusText: string;
161
+ readonly url: string;
162
+ };
163
+ /**
164
+ * Response metadata plus a body that decoded against a declared struct.
165
+ *
166
+ * Holding this type is the proof that decoding happened; a failed decode is
167
+ * reported as a fault carrying `HttpMeta` alone.
168
+ *
169
+ * @group http
170
+ */
171
+ type DecodedResponse<TBody> = HttpMeta & {
172
+ readonly body: TBody;
70
173
  };
71
174
  /**
72
- * Fields accepted by `makeResponse` when building a synthetic `HttpResponse` (for example in interceptors).
175
+ * Fields accepted by `makeResponse` when building a synthetic `HttpResponse`, for example in interceptors.
176
+ *
177
+ * `status` defaults to `0` and `statusText` to `''`. `url` and `headers` default
178
+ * to values taken from `request`: its resolved URL (base endpoint, path and
179
+ * query) and a copy of its headers. A request without `baseEndpoint` gives its
180
+ * `endpoint` as written. Without a request, `url` is `''` and `headers` is
181
+ * empty. An omitted `body` stays `undefined`; only an explicit `null` is a JSON
182
+ * `null`.
183
+ *
184
+ * @group http
73
185
  */
74
186
  type MakeResponseOptions<R> = {
75
187
  status?: number;
@@ -77,32 +189,127 @@ type MakeResponseOptions<R> = {
77
189
  url?: string;
78
190
  headers?: Headers;
79
191
  body?: R | null;
80
- error?: unknown;
81
192
  request?: HttpRequest;
82
193
  };
83
194
  /**
84
- * Build an `HttpResponse` value without performing a network call.
85
- * Useful in interceptors that short-circuit `next`, and in tests.
195
+ * Builds an `HttpResponse` without a network call, for interceptors that short-circuit `next` and for tests.
86
196
  *
87
- * @param options - Status, headers, body, optional request to copy headers/url from, and optional error; defaults yield status `0`.
197
+ * Default status is `0`, so pass a 2xx `status` for a success. `ok` is true for
198
+ * 2xx. The value it returns goes through the same media-type check and the same
199
+ * declared struct as one off the wire; there is no trusted short-circuit. For a
200
+ * JSON body, set a JSON `content-type` header.
201
+ *
202
+ * @param options - Status, headers, body, and an optional request to copy headers/url from; defaults yield status `0`.
88
203
  * @returns An `HttpResponse` with `ok` derived from the status code.
204
+ * @group http
89
205
  */
90
206
  export declare function makeResponse<R>(options?: MakeResponseOptions<R>): HttpResponse<R>;
91
- //#endregion
92
- //#region src/struct/errors.d.ts
93
207
  /**
94
- * Optional callback that rewrites a `StructIssue` message before it is stored.
208
+ * Shared Error sentinel that marks a request as aborted.
209
+ *
210
+ * Its `message` is `Request was aborted`. Shared `cause` and message value for caller cancellation. `createNetworkFault`
211
+ * maps it, and a `DOMException` named `AbortError`, to `NET_ABORTED`. An HTTP
212
+ * interceptor that throws it makes the call resolve with a `NET_ABORTED` fault.
213
+ * SSE and WebSocket interceptors that throw it are reported as
214
+ * `EXT_INTERCEPTOR_FAILED`.
215
+ *
216
+ * The sentinel carries a non-enumerable `Symbol.for('defjs.cause.aborted')`
217
+ * brand. {@link isAbortCause} recognizes this sentinel from any Core copy
218
+ * without comparing object identity. Classification is independent of unrelated
219
+ * earlier executions.
220
+ *
221
+ * @group errors
222
+ */
223
+ export declare const ERR_ABORTED: Error;
224
+ /**
225
+ * Shared Error sentinel that marks a request as timed out.
226
+ *
227
+ * Its `message` is `Request timed out`. Shared `cause` and message value for timeouts. `createNetworkFault` maps it,
228
+ * and any error named `TimeoutError`, to `NET_TIMEOUT`. An HTTP interceptor
229
+ * that throws it makes the call resolve with a `NET_TIMEOUT` fault. SSE and
230
+ * WebSocket interceptors that throw it are reported as `EXT_INTERCEPTOR_FAILED`.
231
+ *
232
+ * The sentinel carries a non-enumerable `Symbol.for('defjs.cause.timeout')`
233
+ * brand. {@link isTimeoutCause} recognizes this sentinel from any Core copy
234
+ * without comparing object identity. Classification is independent of unrelated
235
+ * earlier executions.
236
+ *
237
+ * @group errors
238
+ */
239
+ export declare const ERR_TIMEOUT: Error;
240
+ /**
241
+ * Whether `cause` is caller cancellation, across Core copies and platform abort errors.
242
+ *
243
+ * Returns `true` for an `Error` carrying the non-enumerable
244
+ * `Symbol.for('defjs.cause.aborted')` brand, including `ERR_ABORTED` from
245
+ * another Core copy, or a `DOMException` named `AbortError`. An ordinary
246
+ * unbranded `Error` with that name does not match. Other values return `false`.
247
+ *
248
+ * Pass the underlying `fault.cause`; this function does not unwrap faults.
249
+ * `createNetworkFault` uses this check to select `NET_ABORTED`. OpenTelemetry
250
+ * uses it to suppress caller-cancellation logs while preserving timeout logs.
251
+ *
252
+ * @group errors
253
+ * @see {@link ERR_ABORTED}
254
+ * @see {@link isTimeoutCause}
255
+ */
256
+ export declare function isAbortCause(cause: unknown): boolean;
257
+ /**
258
+ * Whether `cause` is a timeout, across Core copies and platform timeout errors.
259
+ *
260
+ * Returns `true` for an `Error` carrying the non-enumerable
261
+ * `Symbol.for('defjs.cause.timeout')` brand, including `ERR_TIMEOUT` from
262
+ * another Core copy, or a `DOMException` or `Error` named `TimeoutError`.
263
+ * Other values return `false`.
264
+ *
265
+ * Pass the underlying `fault.cause`; this function does not unwrap faults.
266
+ * `createNetworkFault` uses this check to select `NET_TIMEOUT`. OpenTelemetry
267
+ * keeps logging timeouts even when the abort signal is also set.
268
+ *
269
+ * @group errors
270
+ * @see {@link ERR_TIMEOUT}
271
+ * @see {@link isAbortCause}
272
+ */
273
+ export declare function isTimeoutCause(cause: unknown): boolean;
274
+ /**
275
+ * Type-only export of the internal enumerable tag key on objects produced while parsing endpoint input, used by the outbound encoder.
276
+ *
277
+ * `STRUCT` is exported as a **type** only (`export type { STRUCT }`). `@defjs/core` has no runtime `STRUCT` export, so `import { STRUCT } from '@defjs/core'` fails to compile and gives no value.
278
+ *
279
+ * It marks those objects so the outbound encoder knows which struct produced them. Response decoding and `struct.parse` leave it unset. The symbol is internal to the outbound encoder; application code should not depend on it.
280
+ *
281
+ * @internal
282
+ * @group struct
283
+ */
284
+ declare const STRUCT: unique symbol;
285
+ /**
286
+ * Optional callback that rewrites a {@link StructIssue} message before it is stored.
95
287
  *
96
- * Return a string to replace the default message, or `undefined` to keep it.
288
+ * Passed as the `errorMap` option of `struct.parse`. It is called for each issue (`code`, `expected`, `message`, `path`, `received`) before the issue is recorded. Any string, including `''`, replaces the default message; only `undefined` keeps it. The map belongs to this parse call and also handles bigint/date conversion failures from a struct created by another Core copy. Default messages omit received values; custom text is used verbatim, so avoid including sensitive `received` values.
97
289
  *
98
290
  * @param issue - Issue about to be recorded.
99
291
  * @returns Replacement message, or `undefined` to keep the default.
292
+ * @group struct
100
293
  */
101
294
  type ErrorMap = (issue: StructIssue) => string | undefined;
102
295
  /**
103
- * Aggregate of one or more `StructIssue`s from a failed parse.
296
+ * Error returned by a failed struct parse, aggregating one or more {@link StructIssue}s and their formatters.
297
+ *
298
+ * `name` is `'StructError'`. Construct it with `constructor(issues: StructIssue[])`. `issues` is the `readonly` list of those issues. `message` is the issue's message for a single issue, `N struct issues: <first message>` for several, and `Struct parse failed` for none.
299
+ *
300
+ * Default issue messages name the received kind and omit the value, so `message` and `prettify()` can be logged. For example, `Expected number at id, received string`. `message` and the formatters use that text. The raw value stays on `StructIssue.received`; custom `errorMap` text is used verbatim and can reintroduce sensitive values.
104
301
  *
105
302
  * Use `format()`, `flatten()`, or `prettify()` to present issues to callers.
303
+ *
304
+ * - `struct.parse` stops at the first failure, so `issues` holds one {@link StructIssue} there.
305
+ *
306
+ * - `format()` returns a {@link FormattedStructError} tree. Each node has an `_errors` array, and nested keys follow `issue.path`.
307
+ *
308
+ * - `flatten()` returns a {@link FlattenedStructError}: messages with an empty path go to `formErrors`, the others are grouped by the first path segment in `fieldErrors`.
309
+ *
310
+ * - `prettify()` returns one `× path: message` line per issue. An empty path is shown as `<root>`.
311
+ *
312
+ * @group struct
106
313
  */
107
314
  export declare class StructError extends Error {
108
315
  /** Issues collected for this failure, in encounter order. */
@@ -126,18 +333,19 @@ export declare class StructError extends Error {
126
333
  /**
127
334
  * Render a multi-line, human-readable summary of all issues.
128
335
  *
336
+ * Each line uses the issue message, which names the received kind and omits the value.
337
+ *
129
338
  * @returns One `× path: message` line per issue.
130
339
  */
131
340
  prettify(): string;
132
341
  }
133
- //#endregion
134
- //#region src/struct/types.d.ts
135
342
  type Path = Array<number | string>;
136
343
  /**
137
- * Error-first parse tuple from `struct.parse`.
344
+ * Error-first tuple returned by `struct.parse`.
138
345
  *
139
- * On success the error is `null` and `value` is the parsed output. On failure
140
- * the error is a `StructError` and `value` is always `undefined`.
346
+ * On success the error is `null` and `value` is the parsed output `O`. On failure the error is a `StructError` and `value` is always `undefined`.
347
+ *
348
+ * @group struct
141
349
  */
142
350
  type ParseResult<O> = [error: null, value: O] | [error: StructError, value: undefined];
143
351
  type LiteralValue = boolean | null | number | string;
@@ -149,9 +357,14 @@ interface StructTypes<Input = unknown, Output = unknown, OptionalOut extends boo
149
357
  /**
150
358
  * Minimal structural brand carried by every struct instance.
151
359
  *
152
- * `_struct` holds input/output phantom types used by `Infer` and `StructInput`.
360
+ * Anything with a `_struct` type payload. `_struct` holds input and output phantom types: {@link StructInput} and {@link Infer} read `input` and `output` from it, and object structs use `optionalOut` to decide which output fields may be absent.
361
+ *
362
+ * `_struct` exists only in the type system. Structs have no `_struct` property at runtime, and a plain object with that type is not a valid struct. `input` and `output` are the phantom types. `optionalOut` is `true` when the third type argument is `true`, and `undefined` otherwise.
363
+ *
364
+ * @group struct
153
365
  */
154
366
  interface StructLike<I = unknown, O = unknown, OO extends boolean = boolean> {
367
+ /** Phantom payload. `optionalOut` is `true` only when this struct's output fields may be absent. */
155
368
  readonly _struct: StructTypes<I, O, OO>;
156
369
  }
157
370
  type OptionalOutputStruct = {
@@ -161,13 +374,25 @@ type OptionalOutputStruct = {
161
374
  };
162
375
  /**
163
376
  * A single validation failure produced while parsing a struct.
377
+ *
378
+ * - `code` is the machine-readable category. The parser produces `invalid_type`, `invalid_literal`, `invalid_enum`, `invalid_union`, `invalid_intersection`, and `missing_key`; `custom` is part of the type, but the built-in structs never produce it.
379
+ *
380
+ * - `expected` describes the expected type or value.
381
+ *
382
+ * - `message` is the final text, after an optional {@link ErrorMap} rewrite.
383
+ *
384
+ * - `path` runs from the parse root to the failing location: field names and array indices. It is empty for a root-level failure.
385
+ *
386
+ * - `received` is the value that failed at `path`.
387
+ *
388
+ * @group struct
164
389
  */
165
390
  interface StructIssue {
166
391
  /** Machine-readable failure category. */
167
- code: 'custom' | 'invalid_enum' | 'invalid_literal' | 'invalid_type' | 'invalid_union' | 'missing_key';
392
+ code: "custom" | "invalid_enum" | "invalid_intersection" | "invalid_literal" | "invalid_type" | "invalid_union" | "missing_key";
168
393
  /** Human-readable description of the expected type or value. */
169
394
  expected: string;
170
- /** Final message after optional `ErrorMap` rewriting. */
395
+ /** Final message after optional `ErrorMap` rewriting. The default names the received kind, not the value. */
171
396
  message: string;
172
397
  /** Path from the parse root to the failing location. */
173
398
  path: Path;
@@ -177,14 +402,20 @@ interface StructIssue {
177
402
  /**
178
403
  * Nested error tree returned by `StructError.format()`.
179
404
  *
180
- * Leaf messages live under `_errors`; nested keys mirror the failing path.
405
+ * Leaf messages live under `_errors`; nested keys mirror the failing path. A path segment literally named `_errors` is stored as `\_errors`.
406
+ *
407
+ * @group struct
181
408
  */
182
409
  interface FormattedStructError {
183
410
  _errors: string[];
184
411
  [key: string]: FormattedStructError | string[];
185
412
  }
186
413
  /**
187
- * Flat field/form error bags returned by `StructError.flatten()`.
414
+ * Flat field and form error bags returned by `StructError.flatten()`.
415
+ *
416
+ * `formErrors` holds messages of issues with an empty path (root-level issues). `fieldErrors` holds messages keyed by the first path segment, so nested issues are grouped under their top-level field.
417
+ *
418
+ * @group struct
188
419
  */
189
420
  interface FlattenedStructError {
190
421
  /** Messages whose path is empty (root-level issues). */
@@ -196,6 +427,18 @@ interface FlattenedStructError {
196
427
  }
197
428
  /**
198
429
  * Chainable modifiers available on every `Struct` instance.
430
+ *
431
+ * Each method returns a new struct and leaves the original unchanged.
432
+ *
433
+ * - `alias(name)` sets the wire name of an object field. `struct.parse` reads logical names unless `aliases: true` is passed; JSON and flat request codecs write the alias. A non-string `name` throws `TypeError`.
434
+ *
435
+ * - `null()` and `nullable()` are the same: they also accept `null`.
436
+ *
437
+ * - `optional()` also accepts `undefined`. In an object, the key may be missing, and an absent field stays absent in the output.
438
+ *
439
+ * - `nullish()` accepts both `null` and `undefined`.
440
+ *
441
+ * @group struct
199
442
  */
200
443
  interface StructMethods<I, O, OO extends boolean> {
201
444
  /**
@@ -204,7 +447,7 @@ interface StructMethods<I, O, OO extends boolean> {
204
447
  * @param name - Wire / protocol field name.
205
448
  * @returns The same struct branded with the alias.
206
449
  */
207
- alias(name: string): Struct<I, O, OO>;
450
+ alias(name: string): this;
208
451
  /**
209
452
  * Allow `null` in addition to the current input and output types.
210
453
  *
@@ -231,46 +474,86 @@ interface StructMethods<I, O, OO extends boolean> {
231
474
  optional(): Struct<I | undefined, O | undefined, true>;
232
475
  }
233
476
  /**
234
- * Public struct contract: modifiers plus the `_struct` type brand.
477
+ * Public struct contract: input and output types, modifier methods, and the `_struct` type brand.
478
+ *
479
+ * A parseable shape. `Input` is what it accepts ({@link StructInput}), `Output` is what a successful parse returns ({@link Infer}), and `OptionalOut` marks a field that may be absent from object output.
480
+ *
481
+ * Build structs with the {@link struct} facade (`struct.string()`, `struct.object()`, …). Every modifier in {@link StructMethods} returns a new struct and leaves the original unchanged.
235
482
  *
236
- * Built via the `struct` facade (`struct.string()`, `struct.object()`, …).
483
+ * @group struct
237
484
  */
238
485
  type Struct<Input = unknown, Output = Input, OptionalOut extends boolean = false> = StructMethods<Input, Output, OptionalOut> & StructLike<Input, Output, OptionalOut>;
239
486
  type PresentValue = NonNullable<unknown>;
487
+ declare const carriedStruct: unique symbol;
488
+ /** Keeps the struct that a nullish modifier wrapped, so builders still see its fields. */
489
+ type CarriedStruct<T> = {
490
+ readonly [carriedStruct]: T;
491
+ };
492
+ declare const unionStructOptions: unique symbol;
493
+ declare const intersectionStructOptions: unique symbol;
494
+ declare const discriminatedUnionOptions: unique symbol;
495
+ interface PreservedStruct<T, I, O, OO extends boolean> extends Struct<I, O, OO>, CarriedStruct<T> {
496
+ null(): PreservedStruct<T, I | null, O | null, OO>;
497
+ nullable(): PreservedStruct<T, I | null, O | null, OO>;
498
+ nullish(): PreservedStruct<T, I | null | undefined, O | null | undefined, true>;
499
+ optional(): PreservedStruct<T, I | undefined, O | undefined, true>;
500
+ }
240
501
  /**
241
- * Broad struct constraint for endpoint and client generics.
502
+ * Broad struct constraint that accepts any struct, for endpoint and client generics.
242
503
  *
243
- * Prefer a concrete `Struct<…>` when you need precise input/output inference.
504
+ * Used by generics such as `defineRequest`, `Fault`, and `HttpCommand`. Prefer a concrete `Struct<…>` when you need precise input and output inference.
505
+ *
506
+ * @group struct
244
507
  */
245
508
  type AnyStruct = Struct<any, any, boolean>;
246
509
  /**
247
- * Infer the accepted input type of a struct.
510
+ * Type helper that extracts the accepted input type of a struct.
511
+ *
512
+ * The type a struct accepts, as opposed to {@link Infer}, which is what a successful parse returns. `T` is a struct, or any {@link StructLike}. Anything else gives `never`.
513
+ *
514
+ * The two differ when a struct converts its input:
515
+ *
516
+ * ```ts
517
+ * const Created = struct.date();
518
+ *
519
+ * type In = StructInput<typeof Created>; // Date | number | string
520
+ * type Out = Infer<typeof Created>; // Date
521
+ * ```
248
522
  *
249
523
  * @typeParam T - A struct-like value with a `_struct.input` brand.
524
+ * @group struct
250
525
  */
251
526
  type StructInput<T> = T extends {
252
527
  readonly _struct: {
253
528
  readonly input: unknown;
254
529
  };
255
- } ? T['_struct']['input'] : never;
530
+ } ? T["_struct"]["input"] : never;
256
531
  type StructOutput<T> = T extends {
257
532
  readonly _struct: {
258
533
  readonly output: unknown;
259
534
  };
260
- } ? T['_struct']['output'] : never;
535
+ } ? T["_struct"]["output"] : never;
261
536
  /**
262
- * Infer the parsed output type of a struct (alias of the internal output brand).
537
+ * Infers the parsed output type of a struct, as an alias of the internal output brand.
538
+ *
539
+ * The type a successful parse produces. Use `Infer<typeof User>` for the decoded value of a struct, and `StructInput` for the accepted input type. A type that is not a struct yields `never`.
540
+ *
541
+ * ```ts
542
+ * const User = struct.object({ id: struct.number(), name: struct.string() });
543
+ * type User = Infer<typeof User>; // { id: number; name: string }
544
+ * ```
263
545
  *
264
546
  * @typeParam T - A struct-like value with a `_struct.output` brand.
547
+ * @group struct
265
548
  */
266
549
  type Infer<T> = StructOutput<T>;
267
- type FieldOutput<S> = S extends StructLike<unknown, unknown, boolean> ? S extends OptionalOutputStruct ? Exclude<S['_struct']['output'], undefined> : S['_struct']['output'] : never;
550
+ type FieldOutput<S> = S extends StructLike<unknown, unknown, boolean> ? S extends OptionalOutputStruct ? Exclude<S["_struct"]["output"], undefined> : S["_struct"]["output"] : never;
268
551
  type Simplify<T> = { [K in keyof T]: T[K]; } & {};
269
552
  type ObjectShape = {
270
553
  [key: string]: any;
271
554
  };
272
- type ObjectInput<T extends ObjectShape> = Simplify<{ -readonly [K in keyof T as T[K] extends OptionalOutputStruct ? never : K]: T[K]['_struct']['input']; } & { -readonly [K in keyof T as T[K] extends OptionalOutputStruct ? K : never]?: Exclude<T[K]['_struct']['input'], undefined>; }>;
273
- type ObjectOutput<T extends ObjectShape> = Simplify<{ -readonly [K in keyof T as T[K] extends OptionalOutputStruct ? never : K]: FieldOutput<T[K]>; } & { -readonly [K in keyof T as T[K] extends OptionalOutputStruct ? K : never]?: FieldOutput<T[K]>; }>;
555
+ type ObjectInput<T extends ObjectShape> = Simplify<{ -readonly [K in keyof T as true extends T[K]["_struct"]["optionalOut"] ? never : K]: T[K]["_struct"]["input"]; } & { -readonly [K in keyof T as true extends T[K]["_struct"]["optionalOut"] ? K : never]?: Exclude<T[K]["_struct"]["input"], undefined>; }>;
556
+ type ObjectOutput<T extends ObjectShape> = Simplify<{ -readonly [K in keyof T as true extends T[K]["_struct"]["optionalOut"] ? never : K]: FieldOutput<T[K]>; } & { -readonly [K in keyof T as true extends T[K]["_struct"]["optionalOut"] ? K : never]?: FieldOutput<T[K]>; }>;
274
557
  type TupleInput<T extends readonly StructLike<unknown, unknown, boolean>[]> = { -readonly [K in keyof T]: StructInput<T[K]>; };
275
558
  type TupleOutput<T extends readonly StructLike<unknown, unknown, boolean>[]> = { -readonly [K in keyof T]: StructOutput<T[K]>; };
276
559
  type UnionOutput<T extends readonly StructLike<unknown, unknown, boolean>[]> = Exclude<StructOutput<T[number]>, undefined>;
@@ -291,6 +574,10 @@ interface ArrayStructTypes<S extends StructLike<unknown, unknown, boolean>> exte
291
574
  }
292
575
  interface ArrayStruct<S extends StructLike<unknown, unknown, boolean>> extends StructMethods<ArrayInput<S>, ArrayOutput<S>, false>, StructLike<ArrayInput<S>, ArrayOutput<S>, false> {
293
576
  readonly _struct: ArrayStructTypes<S>;
577
+ null(): PreservedStruct<ArrayStruct<S>, ArrayInput<S> | null, ArrayOutput<S> | null, false>;
578
+ nullable(): PreservedStruct<ArrayStruct<S>, ArrayInput<S> | null, ArrayOutput<S> | null, false>;
579
+ nullish(): PreservedStruct<ArrayStruct<S>, ArrayInput<S> | null | undefined, ArrayOutput<S> | null | undefined, true>;
580
+ optional(): PreservedStruct<ArrayStruct<S>, ArrayInput<S> | undefined, ArrayOutput<S> | undefined, true>;
294
581
  }
295
582
  interface ObjectStructTypes<T extends ObjectShape> extends StructTypes<ObjectInput<T>, ObjectOutput<T>, false> {
296
583
  input: ObjectInput<T>;
@@ -298,515 +585,1217 @@ interface ObjectStructTypes<T extends ObjectShape> extends StructTypes<ObjectInp
298
585
  output: ObjectOutput<T>;
299
586
  }
300
587
  /**
301
- * Object-shaped struct produced by `struct.object(shape)`.
588
+ * Object-shaped struct type produced by `struct.object(shape)`.
589
+ *
590
+ * Fields are required by default. A field marked `optional()` or `nullish()` becomes an optional key, so field presence follows those modifiers. Unknown keys are stripped when parsing. Wire names can differ from TypeScript keys via `.alias()`. A failed parse returns `[StructError, undefined]`. `ObjectInput`, `ObjectOutput`, `ObjectStructTypes`, and `PreservedStruct` are internal helper types.
302
591
  *
303
- * Field presence follows each field's optional/nullish modifiers; wire names
304
- * can differ from TypeScript keys via `.alias()`.
592
+ * @group struct
305
593
  */
306
594
  interface ObjectStruct<T extends ObjectShape> extends StructMethods<ObjectInput<T>, ObjectOutput<T>, false>, StructLike<ObjectInput<T>, ObjectOutput<T>, false> {
307
595
  readonly _struct: ObjectStructTypes<T>;
596
+ null(): PreservedStruct<ObjectStruct<T>, ObjectInput<T> | null, ObjectOutput<T> | null, false>;
597
+ nullable(): PreservedStruct<ObjectStruct<T>, ObjectInput<T> | null, ObjectOutput<T> | null, false>;
598
+ nullish(): PreservedStruct<ObjectStruct<T>, ObjectInput<T> | null | undefined, ObjectOutput<T> | null | undefined, true>;
599
+ optional(): PreservedStruct<ObjectStruct<T>, ObjectInput<T> | undefined, ObjectOutput<T> | undefined, true>;
308
600
  }
309
- type RequestBodyCodec = 'arrayBuffer' | 'blob' | 'formData' | 'json' | 'text' | 'urlencoded';
310
- interface RequestBodyStructTypes<C extends RequestBodyCodec, S extends StructLike<unknown, unknown, boolean>> extends StructTypes<Exclude<StructInput<S>, undefined>, Exclude<StructOutput<S>, undefined>, false> {
311
- codec: C;
312
- input: Exclude<StructInput<S>, undefined>;
313
- optionalOut: undefined;
314
- output: Exclude<StructOutput<S>, undefined>;
315
- }
316
- interface RequestBodyStruct<C extends RequestBodyCodec, S extends StructLike<unknown, unknown, boolean>> extends StructMethods<Exclude<StructInput<S>, undefined>, Exclude<StructOutput<S>, undefined>, false>, StructLike<Exclude<StructInput<S>, undefined>, Exclude<StructOutput<S>, undefined>, false> {
317
- readonly _struct: RequestBodyStructTypes<C, S>;
318
- }
319
- type RequestBinaryBodyStruct = Struct<ArrayBuffer, ArrayBuffer> | Struct<Blob, Blob>;
320
- type RequestBodyShapeStruct = RequestBinaryBodyStruct | RequestBodyStruct<RequestBodyCodec, StructLike<unknown, unknown, boolean>>;
321
- type RequestShape = {
322
- body?: RequestBodyShapeStruct;
323
- headers?: ObjectStruct<ObjectShape>;
324
- path?: ObjectStruct<ObjectShape>;
325
- query?: ObjectStruct<ObjectShape>;
601
+ declare const recordValueStruct: unique symbol;
602
+ declare const tupleItemStructs: unique symbol;
603
+ type RecordInput<S extends StructLike<unknown, unknown, boolean>> = {
604
+ [key: string]: StructInput<S>;
605
+ };
606
+ type RecordOutput<S extends StructLike<unknown, unknown, boolean>> = {
607
+ [key: string]: FieldOutput<S>;
326
608
  };
327
- type RequestObjectSectionInput<TKey extends 'headers' | 'path' | 'query', TSection> = TSection extends ObjectStruct<ObjectShape> ? {} extends StructInput<TSection> ? { [K in TKey]?: StructInput<TSection>; } : { [K in TKey]: StructInput<TSection>; } : {};
328
- type RequestInput<T extends RequestShape> = Simplify<RequestObjectSectionInput<'path', T['path']> & RequestObjectSectionInput<'query', T['query']> & RequestObjectSectionInput<'headers', T['headers']> & (T['body'] extends StructLike<unknown, unknown, boolean> ? {
329
- body: StructInput<T['body']>;
330
- } : {})>;
331
- type RequestOutput<T extends RequestShape> = Simplify<(T['path'] extends ObjectStruct<ObjectShape> ? {
332
- path: StructOutput<T['path']>;
333
- } : {}) & (T['query'] extends ObjectStruct<ObjectShape> ? {
334
- query: StructOutput<T['query']>;
335
- } : {}) & (T['headers'] extends ObjectStruct<ObjectShape> ? {
336
- headers: StructOutput<T['headers']>;
337
- } : {}) & (T['body'] extends StructLike<unknown, unknown, boolean> ? {
338
- body: StructOutput<T['body']>;
339
- } : {})>;
340
- interface RequestStructTypes<T extends RequestShape> extends StructTypes<RequestInput<T>, RequestOutput<T>, false> {
341
- input: RequestInput<T>;
342
- optionalOut: undefined;
343
- output: RequestOutput<T>;
344
- }
345
609
  /**
346
- * Request-section struct produced by `struct.request({ path, query, headers, body })`.
347
- *
348
- * Groups path, query, headers, and body contracts used by HTTP/SSE/WebSocket builders.
610
+ * Value struct carried by `struct.record`.
611
+ * Tagged parsing stores this struct on the record object itself. `null` and `undefined` are not tagged.
349
612
  */
350
- interface RequestStruct<T extends RequestShape> extends StructMethods<RequestInput<T>, RequestOutput<T>, false>, StructLike<RequestInput<T>, RequestOutput<T>, false> {
351
- readonly _struct: RequestStructTypes<T>;
613
+ interface RecordStruct<S extends StructLike<unknown, unknown, boolean>> extends Struct<RecordInput<S>, RecordOutput<S>> {
614
+ readonly [recordValueStruct]: S;
615
+ null(): PreservedStruct<RecordStruct<S>, RecordInput<S> | null, RecordOutput<S> | null, false>;
616
+ nullable(): PreservedStruct<RecordStruct<S>, RecordInput<S> | null, RecordOutput<S> | null, false>;
617
+ nullish(): PreservedStruct<RecordStruct<S>, RecordInput<S> | null | undefined, RecordOutput<S> | null | undefined, true>;
618
+ optional(): PreservedStruct<RecordStruct<S>, RecordInput<S> | undefined, RecordOutput<S> | undefined, true>;
352
619
  }
353
- type RecordStruct<S extends StructLike<unknown, unknown, boolean>> = Struct<{
354
- [key: string]: StructInput<S>;
355
- }, {
356
- [key: string]: FieldOutput<S>;
357
- }>;
358
- type TupleStruct<T extends readonly StructLike<unknown, unknown, boolean>[]> = Struct<TupleInput<T>, TupleOutput<T>>;
359
- type UnionStruct<T extends readonly StructLike<unknown, unknown, boolean>[]> = Struct<UnionInput<T>, UnionOutput<T>>;
360
- type DiscriminatedUnionStruct<TOptions extends readonly ObjectStruct<ObjectShape>[]> = Struct<StructInput<TOptions[number]>, StructOutput<TOptions[number]>>;
361
- //#endregion
362
- //#region src/internal/request_values.d.ts
363
- interface RequestBodyOptions {
364
- readonly contentType?: string | null;
620
+ /** Item structs carried by `struct.tuple`, so builders tag each position. The tuple itself is not tagged. */
621
+ interface TupleStruct<T extends readonly StructLike<unknown, unknown, boolean>[]> extends Struct<TupleInput<T>, TupleOutput<T>> {
622
+ readonly [tupleItemStructs]: T;
623
+ null(): PreservedStruct<TupleStruct<T>, TupleInput<T> | null, TupleOutput<T> | null, false>;
624
+ nullable(): PreservedStruct<TupleStruct<T>, TupleInput<T> | null, TupleOutput<T> | null, false>;
625
+ nullish(): PreservedStruct<TupleStruct<T>, TupleInput<T> | null | undefined, TupleOutput<T> | null | undefined, true>;
626
+ optional(): PreservedStruct<TupleStruct<T>, TupleInput<T> | undefined, TupleOutput<T> | undefined, true>;
365
627
  }
366
- //#endregion
367
- //#region src/internal/request_builder.d.ts
368
- type RequestTransport = 'http' | 'sse' | 'webSocket';
369
- declare const BOUND_SOURCE: unique symbol;
370
- declare const ARRAY_PROJECTION: unique symbol;
371
- type BuildBoundRef<TOutput = unknown> = {
372
- readonly [BOUND_SOURCE]: {
373
- readonly output: TOutput;
374
- };
375
- };
376
- type BuildArrayProjection<TItem> = {
377
- readonly [ARRAY_PROJECTION]: never;
378
- readonly item: TItem;
379
- };
380
- type BuildArray<TItem, TOutput extends readonly unknown[]> = BuildBoundRef<TOutput> & {
381
- map<TProjection>(callback: (item: TItem) => TProjection): BuildArrayProjection<TProjection>;
382
- };
383
- type BuildInput<T> = T extends {
384
- readonly _struct: {
385
- readonly output: infer TOutput;
386
- };
387
- } ? BuildOutput<TOutput> : BuildBoundRef;
388
- type BuildOutput<TOutput> = TOutput extends readonly (infer TItem)[] ? BuildArray<BuildOutput<TItem>, TOutput> : TOutput extends {
389
- [key: string]: unknown;
390
- } ? BuildBoundRef & { readonly [K in keyof TOutput]: BuildOutput<TOutput[K]>; } : BuildBoundRef<TOutput>;
391
- type BuildFlatScalar = bigint | boolean | Date | null | number | string | undefined;
392
- type BuildFlatValue = BuildFlatScalar | readonly BuildFlatScalar[];
393
- type BuildFormDataValue = BuildFlatValue | Blob | File | readonly (Blob | BuildFlatScalar | File)[];
394
- type BuildJsonProjection = BuildArrayProjection<unknown> | BuildBoundRef | undefined | readonly BuildJsonProjection[] | {
395
- readonly [key: string]: BuildJsonProjection;
396
- };
397
- type BuildRecordProjection = {
398
- readonly [key: string]: BuildBoundRef<BuildFlatValue> | undefined;
399
- };
400
- type BuildFormDataProjection = {
401
- readonly [key: string]: BuildBoundRef<BuildFormDataValue> | undefined;
402
- };
403
- interface RequestBuilder {
404
- addFormData(projection: BuildFormDataProjection): void;
405
- addFormUrlEncoded(projection: BuildRecordProjection, options?: RequestBodyOptions): void;
406
- addHeaders(projection: BuildRecordProjection): void;
407
- setArrayBuffer(value: BuildBoundRef<ArrayBuffer>, options?: RequestBodyOptions): void;
408
- setBlob(value: BuildBoundRef<Blob>, options?: RequestBodyOptions): void;
409
- setFormData(projection: BuildFormDataProjection): void;
410
- setFormUrlEncoded(projection: BuildRecordProjection, options?: RequestBodyOptions): void;
411
- setHeaders(projection: BuildRecordProjection): void;
412
- setHtml(value: BuildBoundRef<string>, options?: RequestBodyOptions): void;
413
- setJson(projection: BuildJsonProjection, options?: RequestBodyOptions): void;
414
- setPathParams(projection: BuildRecordProjection): void;
415
- setQueryParams(projection: BuildRecordProjection): void;
416
- setText(value: BuildBoundRef<string>, options?: RequestBodyOptions): void;
628
+ interface UnionStruct<T extends readonly StructLike<unknown, unknown, boolean>[]> extends Struct<UnionInput<T>, UnionOutput<T>> {
629
+ readonly [unionStructOptions]: T;
630
+ null(): PreservedStruct<UnionStruct<T>, UnionInput<T> | null, UnionOutput<T> | null, false>;
631
+ nullable(): PreservedStruct<UnionStruct<T>, UnionInput<T> | null, UnionOutput<T> | null, false>;
632
+ nullish(): PreservedStruct<UnionStruct<T>, UnionInput<T> | null | undefined, UnionOutput<T> | null | undefined, true>;
633
+ optional(): PreservedStruct<UnionStruct<T>, UnionInput<T> | undefined, UnionOutput<T> | undefined, true>;
417
634
  }
418
- interface WebSocketRequestBuildContext {
419
- setPathParams(projection: BuildRecordProjection): void;
420
- setQueryParams(projection: BuildRecordProjection): void;
635
+ interface DiscriminatedUnionStruct<TOptions extends readonly ObjectStruct<ObjectShape>[]> extends Struct<StructInput<TOptions[number]>, StructOutput<TOptions[number]>> {
636
+ readonly [discriminatedUnionOptions]: TOptions;
637
+ null(): PreservedStruct<DiscriminatedUnionStruct<TOptions>, StructInput<TOptions[number]> | null, StructOutput<TOptions[number]> | null, false>;
638
+ nullable(): PreservedStruct<DiscriminatedUnionStruct<TOptions>, StructInput<TOptions[number]> | null, StructOutput<TOptions[number]> | null, false>;
639
+ nullish(): PreservedStruct<DiscriminatedUnionStruct<TOptions>, StructInput<TOptions[number]> | null | undefined, StructOutput<TOptions[number]> | null | undefined, true>;
640
+ optional(): PreservedStruct<DiscriminatedUnionStruct<TOptions>, StructInput<TOptions[number]> | undefined, StructOutput<TOptions[number]> | undefined, true>;
641
+ }
642
+ interface IntersectionStruct<T extends readonly StructLike<unknown, unknown, boolean>[]> extends Struct<IntersectionInput<T>, IntersectionOutput<T>> {
643
+ readonly [intersectionStructOptions]: T;
644
+ null(): PreservedStruct<IntersectionStruct<T>, IntersectionInput<T> | null, IntersectionOutput<T> | null, false>;
645
+ nullable(): PreservedStruct<IntersectionStruct<T>, IntersectionInput<T> | null, IntersectionOutput<T> | null, false>;
646
+ nullish(): PreservedStruct<IntersectionStruct<T>, IntersectionInput<T> | null | undefined, IntersectionOutput<T> | null | undefined, true>;
647
+ optional(): PreservedStruct<IntersectionStruct<T>, IntersectionInput<T> | undefined, IntersectionOutput<T> | undefined, true>;
421
648
  }
422
- type RequestBuildInput<TInput extends AnyStruct | undefined> = [TInput] extends [undefined] ? unknown : TInput extends AnyStruct ? BuildInput<TInput> : never;
423
- type RequestBuildContext<TTransport extends RequestTransport = 'http'> = TTransport extends 'webSocket' ? WebSocketRequestBuildContext : RequestBuilder;
424
- type RequestBuildHandler<TInput extends AnyStruct | undefined, TTransport extends RequestTransport = 'http'> = (request: RequestBuildContext<TTransport>, input: RequestBuildInput<TInput>) => void;
425
- //#endregion
426
- //#region src/struct/constructors.d.ts
427
- declare function createStringStruct(): StringStruct;
428
- declare function createNumberStruct(): NumberStruct;
429
- declare function createBooleanStruct(): Struct<boolean, boolean>;
430
- declare function createNullStruct(): Struct<null, null>;
431
- declare function createAnyStruct(): Struct<PresentValue, any>;
432
- declare function createUnknownStruct(): UnknownStruct;
433
- declare function createLiteralStruct<const T extends LiteralValue>(value: T): Struct<T, T>;
434
- declare function createArrayStruct<S extends StructLike<unknown, unknown, boolean>>(item: S): ArrayStruct<S>;
435
- declare function createObjectStruct<T extends ObjectShape>(shape: T): ObjectStruct<T>;
436
- declare function createRequestStruct<const T extends RequestShape>(shape: T): RequestStruct<T>;
437
- declare function createJsonBodyStruct<S extends StructLike<unknown, unknown, boolean>>(struct: S, options?: {
438
- contentType?: string | null;
439
- }): RequestBodyStruct<'json', S>;
440
- declare function createUrlencodedBodyStruct<T extends ObjectShape>(shape: T): RequestBodyStruct<'urlencoded', ObjectStruct<T>>;
441
- declare function createFormDataBodyStruct<T extends ObjectShape>(shape: T): RequestBodyStruct<'formData', ObjectStruct<T>>;
442
- declare function createTextBodyStruct(): RequestBodyStruct<'text', StringStruct>;
443
- declare function createRecordStruct<S extends StructLike<unknown, unknown, boolean>>(value: S): RecordStruct<S>;
444
- declare function createTupleStruct<const T extends readonly [StructLike<unknown, unknown, boolean>, ...StructLike<unknown, unknown, boolean>[]]>(items: T): TupleStruct<T>;
445
- type RequiredDiscriminatorOption<TDiscriminator extends string, TOption extends ObjectStruct<ObjectShape>> = TOption extends ObjectStruct<infer TShape> ? TDiscriminator extends keyof TShape ? TShape[TDiscriminator] extends StructLike<infer TInput, unknown, false> ? undefined extends TInput ? never : null extends TInput ? [TInput] extends [null] ? TOption : never : TOption : never : never : never;
446
- type RequiredDiscriminatorOptions<TDiscriminator extends string, TOptions extends readonly ObjectStruct<ObjectShape>[]> = { [K in keyof TOptions]: TOptions[K] extends ObjectStruct<ObjectShape> ? RequiredDiscriminatorOption<TDiscriminator, TOptions[K]> : never; };
447
- declare function createDiscriminatedUnionStruct<const TDiscriminator extends string, const TOptions extends readonly [ObjectStruct<ObjectShape>, ...ObjectStruct<ObjectShape>[]]>(discriminator: TDiscriminator, options: TOptions & RequiredDiscriminatorOptions<TDiscriminator, TOptions>): DiscriminatedUnionStruct<TOptions>;
448
- declare function createBlobStruct(): Struct<Blob, Blob>;
449
- declare function createBigIntStruct(): Struct<bigint | string, bigint>;
450
- declare function createDateStruct(): Struct<Date | number | string, Date>;
451
- declare function createIntersectionStruct<const T extends readonly [StructLike<unknown, unknown, boolean>, ...StructLike<unknown, unknown, boolean>[]]>(...structs: T): Struct<IntersectionInput<T>, IntersectionOutput<T>>;
452
- declare function createFileStruct(): Struct<File, File>;
453
- declare function createArrayBufferStruct(): Struct<ArrayBuffer, ArrayBuffer>;
454
- //#endregion
455
- //#region src/struct/introspection.d.ts
456
- declare function parseStructTuple<S extends StructLike<unknown, unknown, boolean>>(struct: S, value: unknown, options?: {
457
- aliases?: boolean;
458
- errorMap?: ErrorMap;
459
- }): ParseResult<S['_struct']['output']>;
460
- //#endregion
461
- //#region src/struct/facade.d.ts
462
- type EnumValue<T> = T extends readonly (infer U extends string)[] ? U : T extends {
463
- [key: string]: infer U extends number | string;
464
- } ? U : never;
465
- declare function structEnum<const T extends readonly [string, ...string[]]>(value: T): Struct<EnumValue<T>, EnumValue<T>>;
466
- declare function structEnum<const T extends {
467
- [key: string]: number | string;
468
- }>(value: T): Struct<EnumValue<T>, EnumValue<T>>;
469
- declare function structOr<const T extends readonly [StructLike, ...StructLike[]]>(...options: T): UnionStruct<T>;
470
649
  /**
471
- * Facade for building protocol-boundary structs and parsing values.
650
+ * Every failure Defjs reports, as one closed set of codes.
472
651
  *
473
- * Prefer this entry point over internal constructors. Compose shapes with
474
- * `struct.object` / `struct.request`, then parse with `struct.parse` or let
475
- * HTTP/SSE/WebSocket endpoints apply the same contracts at the wire boundary.
652
+ * The prefix before the first `_` is the class; there is no separate `kind` field.
653
+ * Read the prefix for coarse triage and the whole code for an exact case:
476
654
  *
477
- * @example
478
- * ```ts
479
- * const User = struct.object({
480
- * id: struct.number(),
481
- * displayName: struct.string().alias('display_name'),
482
- * })
655
+ * | Class | Meaning |
656
+ * | ------ | -------------------------------------------------------------- |
657
+ * | `HTTP` | The peer answered with a non-2xx status. |
658
+ * | `REQ` | The request never left: input, execute options, or build. |
659
+ * | `NET` | No usable response: cancel, timeout, connect, truncated body. |
660
+ * | `RES` | A response arrived but could not be read as declared. |
661
+ * | `EXT` | Code you attached to the pipeline threw. |
662
+ * | `CAP` | A declared buffer or queue limit was exceeded. |
663
+ * | `ENV` | The host runtime lacks a required capability. |
483
664
  *
484
- * const getUserInput = struct.request({
485
- * path: struct.object({ id: struct.number() }),
486
- * })
665
+ * The set is closed on purpose: it keeps `switch (fault.code)` exhaustive. Extensions
666
+ * report their own detail through `cause`, not through a new code.
487
667
  *
488
- * const [error, user] = struct.parse(User, { id: 1, displayName: 'Ada' })
489
- * ```
668
+ * @group errors
490
669
  */
491
- export declare const struct: {
492
- readonly any: typeof createAnyStruct;
493
- readonly array: typeof createArrayStruct;
494
- readonly arrayBuffer: typeof createArrayBufferStruct;
495
- readonly bigint: typeof createBigIntStruct;
496
- readonly blob: typeof createBlobStruct;
497
- readonly boolean: typeof createBooleanStruct;
498
- readonly date: typeof createDateStruct;
499
- readonly discriminatedUnion: typeof createDiscriminatedUnionStruct;
500
- readonly enum: typeof structEnum;
501
- readonly file: typeof createFileStruct;
502
- readonly formData: typeof createFormDataBodyStruct;
503
- readonly intersection: typeof createIntersectionStruct;
504
- readonly json: typeof createJsonBodyStruct;
505
- readonly literal: typeof createLiteralStruct;
506
- readonly null: typeof createNullStruct;
507
- readonly number: typeof createNumberStruct;
508
- readonly object: typeof createObjectStruct;
509
- readonly or: typeof structOr;
510
- readonly parse: typeof parseStructTuple;
511
- readonly record: typeof createRecordStruct;
512
- readonly request: typeof createRequestStruct;
513
- readonly string: typeof createStringStruct;
514
- readonly text: typeof createTextBodyStruct;
515
- readonly tuple: typeof createTupleStruct;
516
- readonly unknown: typeof createUnknownStruct;
517
- readonly urlencoded: typeof createUrlencodedBodyStruct;
518
- };
519
- //#endregion
520
- //#region src/http/request.d.ts
670
+ export type FaultCode = "CAP_BUFFER_EXCEEDED" | "CAP_QUEUE_OVERFLOW" | "ENV_UNSUPPORTED" | "EXT_HOOK_FAILED" | "EXT_INTERCEPTOR_FAILED" | "EXT_OBSERVER_FAILED" | "HTTP_STATUS" | "NET_ABORTED" | "NET_BODY_INCOMPLETE" | "NET_TIMEOUT" | "NET_UNREACHABLE" | "REQ_BUILD_FAILED" | "REQ_INPUT_INVALID" | "REQ_OPTIONS_INVALID" | "RES_DECODE_FAILED" | "RES_MEDIA_TYPE_INVALID" | "RES_STRUCT_MISMATCH";
521
671
  /**
522
- * One status-to-body mapping used in the array form of `RequestOutputShape`.
523
- * `status` may be a single code or a list of codes that share the same body struct.
672
+ * Extracts the class prefix from a fault code: the segment before its first `_`.
673
+ *
674
+ * `FaultClass<FaultCode>` is
675
+ * `'CAP' | 'ENV' | 'EXT' | 'HTTP' | 'NET' | 'REQ' | 'RES'`.
676
+ *
677
+ * @typeParam C - A `FaultCode`, or a union of them.
678
+ * @group errors
524
679
  */
525
- type ResponseGroupItem<S extends number = number, B extends AnyStruct = AnyStruct> = {
526
- body: B;
527
- status: S | readonly S[];
528
- };
680
+ export type FaultClass<C extends string> = C extends `${infer TClass}_${string}` ? TClass : never;
529
681
  /**
530
- * Declared HTTP response shapes keyed by status code, as a map or a list of `ResponseGroupItem`s.
682
+ * Codes whose fault carries response metadata but no body.
683
+ *
684
+ * These are the `code` values of `DecodeFault`. `PreflightFaultCode` is every
685
+ * other `FaultCode` except `HTTP_STATUS`.
686
+ *
687
+ * @group errors
531
688
  */
532
- type RequestOutputShape = {
533
- [key: number]: AnyStruct;
534
- } | readonly ResponseGroupItem[];
535
- //#endregion
536
- //#region src/sse/transport/parser.d.ts
689
+ export type DecodeFaultCode = "RES_DECODE_FAILED" | "RES_MEDIA_TYPE_INVALID" | "RES_STRUCT_MISMATCH";
537
690
  /**
538
- * Portions of this file are adapted from Azure/fetch-event-source.
539
- * See packages/core/THIRD_PARTY_NOTICES.md for the license text.
691
+ * Codes whose fault may never have reached a response.
692
+ *
693
+ * Every `FaultCode` except `HTTP_STATUS` and the decode codes:
694
+ * `CAP_BUFFER_EXCEEDED`, `CAP_QUEUE_OVERFLOW`, `ENV_UNSUPPORTED`,
695
+ * `EXT_HOOK_FAILED`, `EXT_INTERCEPTOR_FAILED`, `EXT_OBSERVER_FAILED`,
696
+ * `NET_ABORTED`, `NET_BODY_INCOMPLETE`, `NET_TIMEOUT`, `NET_UNREACHABLE`,
697
+ * `REQ_BUILD_FAILED`, `REQ_INPUT_INVALID`, and `REQ_OPTIONS_INVALID`. These
698
+ * are the `code` values of `PreflightFault`.
699
+ *
700
+ * @group errors
540
701
  */
541
- interface EventStreamMessage {
542
- id: string;
543
- event: string;
544
- data: string;
545
- }
546
- //#endregion
547
- //#region src/sse/transport/event_stream.d.ts
548
- /** Metadata available after an SSE response opens successfully. */
549
- interface EventStreamOpenInfo {
550
- response: HttpResponse<unknown>;
551
- url: string;
552
- }
553
- /** Fatal or recoverable error codes reported when an SSE stream closes with `code: 'error'`. */
554
- type EventStreamErrorCode = 'INVALID_RESPONSE' | 'MESSAGE_PROCESSING_FAILED' | 'PARSER_LIMIT_EXCEEDED' | 'QUEUE_OVERFLOW' | 'TIMEOUT' | 'TRANSPORT_ERROR';
555
- interface EventStreamCloseInfoBase {
556
- reason?: string;
557
- cause?: unknown;
558
- }
559
- /** How an SSE stream ended: clean EOF, abort, or an error with `errorCode`. */
560
- type EventStreamCloseInfo = (EventStreamCloseInfoBase & {
561
- code: 'eof';
562
- }) | (EventStreamCloseInfoBase & {
563
- code: 'aborted';
564
- }) | (EventStreamCloseInfoBase & {
565
- code: 'error';
566
- errorCode: EventStreamErrorCode;
567
- });
568
- /** Open SSE stream: iterate events, inspect open metadata, and close early. */
569
- interface EventStreamHandle<TEvent = EventStreamMessage> extends AsyncIterable<TEvent>, AsyncDisposable {
570
- readonly open: EventStreamOpenInfo;
571
- readonly closed: Promise<EventStreamCloseInfo>;
572
- close(reason?: unknown): void;
573
- [Symbol.asyncDispose](): PromiseLike<void>;
574
- }
575
- //#endregion
576
- //#region src/error/types.d.ts
702
+ export type PreflightFaultCode = Exclude<FaultCode, DecodeFaultCode | "HTTP_STATUS">;
703
+ type ErrorBody<TErr> = [TErr] extends [undefined] ? undefined : Infer<TErr>;
577
704
  /**
578
- * Error for an HTTP response whose status is treated as failure.
705
+ * Non-2xx fault expressed by its decoded body type instead of the struct that produced it.
706
+ *
707
+ * Factories work in decoded values, so they use this form; endpoint types use
708
+ * `HttpStatusFault` and let `Infer` bridge the two. When `TData` is `undefined`,
709
+ * the body was never read and `response` is metadata alone.
710
+ *
711
+ * @group errors
579
712
  */
580
- export interface HttpStatusError<TErrorData = unknown, TStatus extends number = number> extends Error {
581
- code: 'HTTP_STATUS';
582
- data: TErrorData;
583
- kind: 'http';
584
- message: string;
585
- response: HttpResponse<unknown>;
586
- status: TStatus;
587
- }
713
+ export type HttpStatusFaultOf<TData> = Error & {
714
+ code: "HTTP_STATUS";
715
+ data: TData;
716
+ response: [TData] extends [undefined] ? HttpMeta : DecodedResponse<TData>;
717
+ status: number;
718
+ };
588
719
  /**
589
- * Error for transport-level failures such as abort, timeout, or network issues.
720
+ * Fault for a non-2xx response, typed by the declared `error` struct.
721
+ *
722
+ * `TErr` is the endpoint's declared `error` struct, or `undefined` when it
723
+ * declares none. Any non-2xx status. When the endpoint declared `error` and the
724
+ * body decoded, `data` is that body and `response` carries it. Omitting `error`
725
+ * skips decoding, which leaves `data` as `undefined` and `response` without a
726
+ * `body` field. `ErrorBody<TErr>` is that decoded body: `undefined` when no
727
+ * error struct was declared, otherwise `Infer<TErr>`. {@link HttpStatusFaultOf}
728
+ * is the member shape.
729
+ *
730
+ * @group errors
590
731
  */
591
- export interface TransportError extends Error {
592
- cause?: unknown;
593
- code: 'ABORTED' | 'NETWORK_ERROR' | 'TIMEOUT';
594
- kind: 'transport';
595
- message: string;
596
- }
732
+ export type HttpStatusFault<TErr extends AnyStruct | undefined = undefined> = HttpStatusFaultOf<ErrorBody<TErr>>;
597
733
  /**
598
- * Error for definition-time validation or undeclared response status failures.
734
+ * A response arrived but could not be read as declared.
735
+ *
736
+ * `code` is one of `RES_DECODE_FAILED`, `RES_MEDIA_TYPE_INVALID`, or
737
+ * `RES_STRUCT_MISMATCH`. `response` is metadata only: a body is a decoded value,
738
+ * and decoding is what failed, so there is no `body` field to reach for. The
739
+ * offending detail lives on `cause` (a `StructError` for a struct mismatch, the
740
+ * parser failure for an unreadable representation).
741
+ *
742
+ * @group errors
599
743
  */
600
- export type DefinitionError = (Error & {
601
- cause?: unknown;
602
- code: 'REQUEST_VALIDATION_FAILED' | 'RESPONSE_VALIDATION_FAILED' | 'INTERCEPTOR_FAILED';
603
- kind: 'definition';
604
- response?: HttpResponse<unknown>;
605
- }) | (Error & {
606
- cause?: unknown;
607
- code: 'UNDECLARED_STATUS';
608
- kind: 'definition';
609
- response: HttpResponse<unknown>;
744
+ export type DecodeFault = Error & {
745
+ cause: unknown;
746
+ code: DecodeFaultCode;
747
+ response: HttpMeta;
610
748
  status: number;
611
- });
749
+ };
612
750
  /**
613
- * Union of HTTP status, transport, and definition errors from request execution.
751
+ * A failure that may have happened before any response existed.
752
+ *
753
+ * Covers `REQ_*`, `NET_*`, `EXT_*`, `CAP_*`, and `ENV_UNSUPPORTED`. `response`
754
+ * is present only where the transport already had metadata to report, such as a
755
+ * body that truncated mid-download.
614
756
  *
615
- * @typeParam TErrorData - Declared error body type for `HttpStatusError` variants.
757
+ * @group errors
616
758
  */
617
- export type RequestError<TErrorData = unknown> = HttpStatusError<TErrorData> | TransportError | DefinitionError;
618
- //#endregion
619
- //#region src/error/factory.d.ts
620
- /** Shared cause/message sentinel for aborted requests. */
621
- export declare const ERR_ABORTED: Error;
622
- /** Shared cause/message sentinel for timed-out requests. */
623
- export declare const ERR_TIMEOUT: Error;
759
+ export type PreflightFault = Error & {
760
+ cause?: unknown;
761
+ code: PreflightFaultCode;
762
+ response?: HttpMeta;
763
+ };
624
764
  /**
625
- * Build a `TransportError` from an abort, timeout, or other network cause.
765
+ * Any failure returned by `client.execute`, expressed by its decoded error-body type.
626
766
  *
627
- * @param cause - Underlying abort/timeout/error value.
628
- * @returns A normalized `TransportError`.
767
+ * `Fault<TErr>` is `FaultOf` applied to `Infer<TErr>`, and `HttpAwaitResult` uses
768
+ * `FaultOf<TErrorData>`. Pass a decoded type, not a struct: write
769
+ * `FaultOf<Infer<typeof errorStruct>>`, or use `Fault<typeof errorStruct>`.
770
+ *
771
+ * @typeParam TData - The decoded `error` body, or `undefined` when no error struct was declared.
772
+ * @group errors
629
773
  */
630
- export declare function createTransportError(cause: unknown): TransportError;
774
+ export type FaultOf<TData = undefined> = DecodeFault | HttpStatusFaultOf<TData> | PreflightFault;
631
775
  /**
632
- * Build a `DefinitionError` for request/response validation or undeclared status.
776
+ * Union of every failure `client.execute` can return, discriminated by `code`.
777
+ *
778
+ * `Fault<TErr>` is {@link FaultOf} applied to the decoded body type, so it is the
779
+ * union of {@link DecodeFault}, {@link HttpStatusFault}<TErr>, and
780
+ * {@link PreflightFault}. `ErrorBody<TErr>` is `undefined` when `TErr` is
781
+ * `undefined`, and `Infer<TErr>` otherwise. Switch on `fault.code`.
633
782
  *
634
- * @param code - Definition error code.
635
- * @param cause - Underlying validation or status failure.
636
- * @param response - HTTP response; required for `UNDECLARED_STATUS` (status is taken from it).
637
- * @returns A normalized `DefinitionError`.
783
+ * Every variant is a native `Error` named `DefjsFault`, so `String(fault)` produces
784
+ * `DefjsFault: <message>`. `code` and the variant metadata — `status`, `response`,
785
+ * `data` — are enumerable own properties. The native `cause` chain is non-enumerable.
786
+ *
787
+ * Default Struct messages say `received <kind>`, never the received value. A JSON
788
+ * `SyntaxError` behind `RES_DECODE_FAILED` gives the message
789
+ * `Response body could not be decoded`. Structural paths retain record keys, which
790
+ * may be sensitive. Raw `StructIssue.received` and parser causes remain sensitive;
791
+ * custom `errorMap` and extension messages are used verbatim and need review before
792
+ * logging.
793
+ *
794
+ * Call `format()`, `flatten()`, or `prettify()` only after narrowing `fault.cause`
795
+ * to `StructError`; those helpers are not copied onto the fault.
796
+ *
797
+ * @typeParam TErr - The endpoint's declared `error` struct, or `undefined` when it declares none.
798
+ * @group errors
799
+ * @exampleFile examples/api/fault.ts#example
638
800
  */
639
- export declare function createDefinitionError(code: 'UNDECLARED_STATUS', cause: unknown, response: HttpResponse<unknown>): Extract<DefinitionError, {
640
- code: 'UNDECLARED_STATUS';
641
- }>;
642
- export declare function createDefinitionError(code: Exclude<DefinitionError['code'], 'UNDECLARED_STATUS'>, cause: unknown, response?: HttpResponse<unknown>): Extract<DefinitionError, {
643
- code: Exclude<DefinitionError['code'], 'UNDECLARED_STATUS'>;
644
- }>;
801
+ export type Fault<TErr extends AnyStruct | undefined = undefined> = FaultOf<ErrorBody<TErr>>;
645
802
  /**
646
- * Build an `HttpStatusError` for a non-success HTTP status.
803
+ * Any fault at all, whatever its decoded error-body type.
804
+ *
805
+ * Use it for handlers that classify faults without caring which endpoint produced
806
+ * them. Prefer `Fault<typeof yourErrorStruct>` where the body type matters.
647
807
  *
648
- * @param status - HTTP status code.
649
- * @param message - Human-readable error message.
650
- * @param response - Full HTTP response.
651
- * @param data - Optional parsed error body.
652
- * @returns A normalized `HttpStatusError`.
808
+ * @group errors
653
809
  */
654
- export declare function createHttpStatusError<TErrorData = unknown, TStatus extends number = number>(status: TStatus, message: string, response: HttpResponse<unknown>, data?: TErrorData): HttpStatusError<TErrorData, TStatus>;
655
- //#endregion
656
- //#region src/internal/abort.d.ts
657
- type UseCancellationConfig = {
658
- abort?: AbortSignal;
659
- timeout?: never;
660
- } | {
661
- abort?: never;
662
- timeout?: number;
663
- };
664
- //#endregion
665
- //#region src/internal/endpoint_input.d.ts
666
- type EndpointInput<TInput extends AnyStruct | undefined> = TInput extends AnyStruct ? StructInput<TInput> : unknown;
667
- //#endregion
668
- //#region src/internal/endpoint_command.d.ts
810
+ export type AnyFault = DecodeFault | HttpStatusFaultOf<undefined> | HttpStatusFaultOf<unknown> | PreflightFault;
811
+ /**
812
+ * Builds an `HTTP_STATUS` fault for a non-2xx response whose body decoded against the declared `error` struct.
813
+ *
814
+ * The fault is a native `Error` named `DefjsFault` with `code: 'HTTP_STATUS'`,
815
+ * `data` set to `response.body`, `status` copied from the response, and
816
+ * `response` as passed. The message is `Http failure response: <status>`,
817
+ * followed by ` - <statusText>` when there is one.
818
+ *
819
+ * Use {@link createUndecodedHttpStatusFault} when no body was decoded.
820
+ *
821
+ * @param response - The response, carrying the decoded body.
822
+ * @returns An `HTTP_STATUS` fault whose `data` is that body.
823
+ * @group errors
824
+ */
825
+ export declare function createHttpStatusFault<TData>(response: DecodedResponse<TData>): HttpStatusFaultOf<TData>;
826
+ /**
827
+ * Builds an `HTTP_STATUS` fault for a non-2xx response that was never decoded, because the endpoint declared no `error` struct.
828
+ *
829
+ * The fault is a native `Error` named `DefjsFault` with `code: 'HTTP_STATUS'`,
830
+ * `data: undefined`, `status` copied from the response, and `response` as passed.
831
+ *
832
+ * Use {@link createHttpStatusFault} when the body was decoded.
833
+ *
834
+ * @param response - Metadata of the response; no body was read.
835
+ * @returns An `HTTP_STATUS` fault whose `data` is `undefined`.
836
+ * @group errors
837
+ */
838
+ export declare function createUndecodedHttpStatusFault(response: HttpMeta): HttpStatusFaultOf<undefined>;
839
+ /**
840
+ * Builds a decode fault for a response that arrived but could not be read as declared.
841
+ *
842
+ * `code` is `RES_DECODE_FAILED`, `RES_MEDIA_TYPE_INVALID`, or
843
+ * `RES_STRUCT_MISMATCH`. The fault carries metadata only: a body is a decoded
844
+ * value, and decoding is what failed, so there is no `body`. `status` is copied
845
+ * from `response`. The result is a native `Error` named `DefjsFault`.
846
+ *
847
+ * `cause` is the underlying parser or struct failure and is kept as `cause`.
848
+ * Its message normally becomes the fault message (`String(cause)` when it is
849
+ * not an `Error`). For `RES_DECODE_FAILED` with an `Error` named `SyntaxError`,
850
+ * the message is `Response body could not be decoded`, so `String(fault)` does
851
+ * not include a body fragment. Default Struct messages contain received kinds,
852
+ * not values; raw values and parser text remain on the cause.
853
+ *
854
+ * @param code - Which read step failed.
855
+ * @param cause - Underlying parser or struct failure.
856
+ * @param response - Metadata of the response that could not be read.
857
+ * @returns A decode fault with no body.
858
+ * @group errors
859
+ */
860
+ export declare function createDecodeFault(code: DecodeFaultCode, cause: unknown, response: HttpMeta): DecodeFault;
861
+ /**
862
+ * Builds a fault for a failure that may have happened before any response existed.
863
+ *
864
+ * When `cause` is given, its message becomes the fault message; otherwise the
865
+ * code is the message. The result is a native `Error` named `DefjsFault` with
866
+ * `code`, plus `response` when one was given.
867
+ *
868
+ * @param code - The specific failure.
869
+ * @param cause - Underlying value, when there is one; the code becomes the message otherwise.
870
+ * @param response - Metadata, only where the transport already had some to report.
871
+ * @returns A preflight fault.
872
+ * @group errors
873
+ */
874
+ export declare function createPreflightFault(code: PreflightFaultCode, cause?: unknown, response?: HttpMeta): PreflightFault;
875
+ /**
876
+ * Builds a `NET_*` `PreflightFault`, choosing the code from the shape of `cause`.
877
+ *
878
+ * Optional `response` metadata is attached when the transport already had some
879
+ * to report. The cause is kept as `cause`.
880
+ *
881
+ * - `NET_ABORTED` when {@link isAbortCause} recognizes an abort-branded `Error`
882
+ * (including `ERR_ABORTED` from another Core copy) or a `DOMException` named
883
+ * `AbortError`. The message is `Request was aborted`.
884
+ * - `NET_TIMEOUT` when {@link isTimeoutCause} recognizes a timeout-branded
885
+ * `Error` (including `ERR_TIMEOUT` from another Core copy) or a `DOMException`
886
+ * or `Error` named `TimeoutError`. The message is the cause's message, or
887
+ * `Request timed out` when that is empty.
888
+ * - `NET_UNREACHABLE` for anything else, with the cause's message.
889
+ *
890
+ * @param cause - Underlying cancellation, timeout, or transport failure.
891
+ * @param response - Metadata, only where the transport already had some to report.
892
+ * @returns A `NET_ABORTED`, `NET_TIMEOUT`, or `NET_UNREACHABLE` fault.
893
+ * @group errors
894
+ */
895
+ export declare function createNetworkFault(cause: unknown, response?: HttpMeta): PreflightFault;
896
+ /**
897
+ * Struct that accepts string values.
898
+ *
899
+ * - Accepts `string` values only; anything else fails with `invalid_type`.
900
+ *
901
+ * - As a body root, or as an inbound `output` / `error` struct, the bytes stay text instead of being parsed as JSON.
902
+ *
903
+ * @group struct
904
+ */
905
+ declare function createStringStruct(): StringStruct;
906
+ /**
907
+ * Struct that accepts number values except NaN.
908
+ *
909
+ * - Accepts `number` values, including `Infinity`. `NaN` and non-numbers fail with `invalid_type`.
910
+ *
911
+ * - Strings are not coerced.
912
+ *
913
+ * @group struct
914
+ */
915
+ declare function createNumberStruct(): NumberStruct;
916
+ /**
917
+ * Struct that accepts boolean values.
918
+ *
919
+ * Accepts `true` and `false` only. Other values, including `'true'` and `1`, fail with `invalid_type`.
920
+ *
921
+ * @group struct
922
+ */
923
+ declare function createBooleanStruct(): Struct<boolean, boolean>;
924
+ /**
925
+ * Struct that accepts only null.
926
+ *
927
+ * Accepts `null` only. Every other value fails with `invalid_type`. That includes `undefined` at the root or in an array or tuple; a missing object or record field reports `missing_key` instead.
928
+ *
929
+ * This is not the `.null()` modifier. The modifier adds `null` to the values another struct accepts.
930
+ *
931
+ * @group struct
932
+ */
933
+ declare function createNullStruct(): Struct<null, null>;
934
+ /**
935
+ * Struct that accepts any value except null and undefined, without validation.
936
+ *
937
+ * The value is returned unchanged, and nothing inside it is validated or stripped.
938
+ *
939
+ * - Chain `.null()`, `.optional()`, or `.nullish()` to accept `null` or `undefined`.
940
+ *
941
+ * - The output type is `any`. Prefer {@link struct.unknown}() when the caller should narrow the value.
942
+ *
943
+ * @group struct
944
+ */
945
+ declare function createAnyStruct(): Struct<PresentValue, any>;
946
+ /**
947
+ * Struct that accepts any value except null and undefined, typed as unknown.
948
+ *
949
+ * The value is returned unchanged, and nothing inside it is validated or stripped.
950
+ *
951
+ * - Chain `.null()`, `.optional()`, or `.nullish()` to accept `null` or `undefined`.
952
+ *
953
+ * - The output type is `unknown`, so the caller must narrow it before use.
954
+ *
955
+ * @group struct
956
+ */
957
+ declare function createUnknownStruct(): UnknownStruct;
958
+ /**
959
+ * Struct that accepts exactly one boolean, null, number, or string value.
960
+ *
961
+ * - Accepts only `value`. The comparison uses `Object.is`, so any other value fails with `invalid_literal`.
962
+ *
963
+ * - `value` must be a `boolean`, `null`, `number`, or `string`.
964
+ *
965
+ * - Use it for the tag field of {@link struct.discriminatedUnion}().
966
+ *
967
+ * @group struct
968
+ */
969
+ declare function createLiteralStruct<const T extends LiteralValue>(value: T): Struct<T, T>;
970
+ /**
971
+ * Struct that parses an array with one item struct.
972
+ *
973
+ * - Parses every element with `item` and outputs a new array. A non-array fails with `invalid_type` (expected `array`).
974
+ *
975
+ * - Parsing stops at the first failing element, and `issue.path` holds its index.
976
+ *
977
+ * - Throws `TypeError` when `item` is not a struct.
978
+ *
979
+ * `ArrayStruct<S>` is not exported. It is a `Struct<StructInput<S>[], Infer<S>[]>`.
980
+ *
981
+ * @group struct
982
+ */
983
+ declare function createArrayStruct<S extends StructLike<unknown, unknown, boolean>>(item: S): ArrayStruct<S>;
984
+ /**
985
+ * Struct that parses a plain object with a fixed set of fields.
986
+ *
987
+ * `shape` maps field names to structs.
988
+ *
989
+ * - Every field is **required** by default. A missing key fails with `missing_key`.
990
+ *
991
+ * - `.optional()` and `.nullish()` allow a missing key. An absent optional field stays absent in the output.
992
+ *
993
+ * - `.null()` / `.nullable()` allow `null`.
994
+ *
995
+ * - `.alias(name)` sets the field's wire key. `struct.parse` reads logical keys by default; `{ aliases: true }` and HTTP JSON decoding read wire keys, and HTTP JSON encoding writes them. Parsed output uses logical keys.
996
+ *
997
+ * - Keys that are not in `shape` are removed from the output. The output is a new object with a `null` prototype.
998
+ *
999
+ * - The input must be a plain object. Arrays, `Map`, and class instances fail with `invalid_type` (expected `object`).
1000
+ *
1001
+ * - Parsing stops at the first failing field, and `issue.path` holds its key.
1002
+ *
1003
+ * - Throws `TypeError` when `shape` is not a plain object.
1004
+ *
1005
+ * `ObjectShape` is `{ [key: string]: any }`; each value must be a struct.
1006
+ *
1007
+ * @group struct
1008
+ */
1009
+ declare function createObjectStruct<T extends ObjectShape>(shape: T): ObjectStruct<T>;
1010
+ /**
1011
+ * Object struct that is encoded as an URL-encoded form when it is the request body.
1012
+ *
1013
+ * Parses like {@link struct.object}(), and carries a `urlencoded` codec tag.
1014
+ *
1015
+ * - The tag applies only when this struct's value is the request body root. The body is then sent as `URLSearchParams` with `application/x-www-form-urlencoded;charset=UTF-8`.
1016
+ *
1017
+ * - Arrays repeat the field name and `undefined` fields are skipped. A nested object or a `Blob` fails the request with `REQ_BUILD_FAILED`.
1018
+ *
1019
+ * - `struct.parse` ignores the tag. It does not accept a `URLSearchParams` instance, only a plain object.
1020
+ *
1021
+ * @group struct
1022
+ */
1023
+ declare function createUrlencodedBodyStruct<T extends ObjectShape>(shape: T): ObjectStruct<T>;
1024
+ /**
1025
+ * Object struct that is encoded as multipart FormData when it is the request body.
1026
+ *
1027
+ * Parses like {@link struct.object}(), and carries a `formData` codec tag.
1028
+ *
1029
+ * - The tag applies only when this struct's value is the request body root. The body is then sent as `FormData` with a platform multipart boundary.
1030
+ *
1031
+ * - Arrays repeat the field name, `undefined` fields are skipped, and `Blob` / `File` values are appended as files. A nested object fails the request with `REQ_BUILD_FAILED`.
1032
+ *
1033
+ * - `struct.parse` ignores the tag. It does not accept a `FormData` instance, only a plain object.
1034
+ *
1035
+ * @group struct
1036
+ */
1037
+ declare function createFormDataBodyStruct<T extends ObjectShape>(shape: T): ObjectStruct<T>;
1038
+ /**
1039
+ * Struct that parses an object with string keys and one value struct.
1040
+ *
1041
+ * - Parses every own key of a plain object with `value`. Anything else fails with `invalid_type` (expected `record`).
1042
+ *
1043
+ * - Parsing stops at the first failing value, and `issue.path` holds its key.
1044
+ *
1045
+ * - The output is a new object with a `null` prototype.
1046
+ *
1047
+ * - Throws `TypeError` when `value` is not a struct.
1048
+ *
1049
+ * `RecordStruct<S>` is not exported. It is a `Struct<{ [key: string]: StructInput<S> }, { [key: string]: Output }>`, where `Output` is `Infer<S>` without `undefined`. When `S` is an optional struct, a value that is `undefined` omits its key from the output.
1050
+ *
1051
+ * @group struct
1052
+ */
1053
+ declare function createRecordStruct<S extends StructLike<unknown, unknown, boolean>>(value: S): RecordStruct<S>;
1054
+ /**
1055
+ * Struct that parses a fixed-length array with one struct per position.
1056
+ *
1057
+ * `struct.tuple` takes **one array** of structs, not separate arguments:
1058
+ *
1059
+ * ```ts
1060
+ * const Pair = struct.tuple([struct.string(), struct.number()]);
1061
+ * ```
1062
+ *
1063
+ * - Each position is parsed with the struct at the same index. The input must be an array of exactly the same length, otherwise it fails with `invalid_type` (expected `tuple of length N`).
1064
+ *
1065
+ * - Passing the structs as separate arguments, `struct.tuple(a, b)`, throws a `TypeError`.
1066
+ *
1067
+ * - Throws `TypeError` when an item is not a struct.
1068
+ *
1069
+ * `TupleStruct<T>` is not exported. It is a `Struct` whose input and output are tuples of the item types.
1070
+ *
1071
+ * @group struct
1072
+ */
1073
+ declare function createTupleStruct<const T extends readonly [StructLike<unknown, unknown, boolean>, ...StructLike<unknown, unknown, boolean>[]]>(items: T): TupleStruct<T>;
1074
+ type RequiredDiscriminatorOption<TDiscriminator extends string, TOption extends ObjectStruct<ObjectShape>> = TOption extends ObjectStruct<infer TShape> ? TDiscriminator extends keyof TShape ? TShape[TDiscriminator] extends StructLike<infer TInput, unknown, false> ? undefined extends TInput ? never : null extends TInput ? [TInput] extends [null] ? TOption : never : TOption : never : never : never;
1075
+ type RequiredDiscriminatorOptions<TDiscriminator extends string, TOptions extends readonly ObjectStruct<ObjectShape>[]> = { [K in keyof TOptions]: TOptions[K] extends ObjectStruct<ObjectShape> ? RequiredDiscriminatorOption<TDiscriminator, TOptions[K]> : never; };
1076
+ /**
1077
+ * Struct that picks one object struct by the value of a tag field.
1078
+ *
1079
+ * ```ts
1080
+ * const Event = struct.discriminatedUnion("kind", [
1081
+ * struct.object({ kind: struct.literal("created"), id: struct.number() }),
1082
+ * struct.object({ kind: struct.literal("deleted"), id: struct.number() }),
1083
+ * ]);
1084
+ * ```
1085
+ *
1086
+ * - Every option must be an object struct whose `discriminator` field is a required {@link struct.literal}(). A missing field, a non-literal field, an optional field, or a duplicate tag value throws `TypeError` when the struct is created.
1087
+ *
1088
+ * - The input must be a plain object. The tag value selects the option, and that option parses the whole object.
1089
+ *
1090
+ * - A missing tag fails with `missing_key`, and an unknown tag fails with `invalid_union`. Both report the tag field's path. Other failures come from the selected option.
1091
+ *
1092
+ * - With `struct.parse(..., { aliases: true })` the tag is read from the field's alias.
1093
+ *
1094
+ * `RequiredDiscriminatorOptions` and `DiscriminatedUnionStruct` are not exported. The type check rejects an option whose tag field is missing or may be `undefined`.
1095
+ *
1096
+ * @group struct
1097
+ */
1098
+ declare function createDiscriminatedUnionStruct<const TDiscriminator extends string, const TOptions extends readonly [ObjectStruct<ObjectShape>, ...ObjectStruct<ObjectShape>[]]>(discriminator: TDiscriminator, options: TOptions & RequiredDiscriminatorOptions<TDiscriminator, TOptions>): DiscriminatedUnionStruct<TOptions>;
1099
+ /**
1100
+ * Struct that accepts a Blob.
1101
+ *
1102
+ * - Accepts a `Blob` (a `File` is a `Blob`). Anything else fails with `invalid_type`.
1103
+ *
1104
+ * - As a body root it is sent as is, with the blob's own type or `application/octet-stream`.
1105
+ *
1106
+ * - As an `output` or `error` struct it makes the response body be read as a `Blob`.
1107
+ *
1108
+ * @group struct
1109
+ */
1110
+ declare function createBlobStruct(): Struct<Blob, Blob>;
1111
+ /**
1112
+ * Struct that accepts a bigint or a bigint string and outputs a bigint.
1113
+ *
1114
+ * - Accepts a `bigint`, or a string that `BigInt()` can parse. A `number` is rejected.
1115
+ *
1116
+ * - Output is always a `bigint`; an unparsable string fails with `invalid_type`.
1117
+ *
1118
+ * - When encoded for the wire, the value becomes a decimal string.
1119
+ *
1120
+ * @group struct
1121
+ */
1122
+ declare function createBigIntStruct(): Struct<bigint | string, bigint>;
1123
+ /**
1124
+ * Struct that accepts a Date, timestamp, or date string and outputs a Date.
1125
+ *
1126
+ * - Accepts a `Date`, a millisecond timestamp, or a string. A valid `Date` is returned as the same object. A number or string is converted with `new Date(value)`.
1127
+ *
1128
+ * - Output is a `Date`. A value that produces an invalid date fails with `invalid_type`.
1129
+ *
1130
+ * - When encoded for the wire, the value becomes an ISO 8601 string (`toISOString()`).
1131
+ *
1132
+ * @group struct
1133
+ */
1134
+ declare function createDateStruct(): Struct<Date | number | string, Date>;
1135
+ /**
1136
+ * Struct that requires a value to satisfy every given struct.
1137
+ *
1138
+ * `struct.intersection` is variadic: pass each struct as its own argument.
1139
+ *
1140
+ * - Parses the input with every struct and merges the results.
1141
+ *
1142
+ * - When every struct is an object struct, the output is one object with the fields of all of them. Keys that none of them declares are removed.
1143
+ *
1144
+ * - When two structs produce incompatible values for the same key, it fails with `invalid_intersection`.
1145
+ *
1146
+ * - Accepts `null` only when every struct accepts it, including a nested intersection marked `.null()`.
1147
+ *
1148
+ * - A single struct is returned unchanged, so it keeps its own identity and type, including `.optional()` and `.null()`. Calling it with no structs throws `TypeError`, and so does a non-struct argument.
1149
+ *
1150
+ * - A nonempty variadic tuple must account for that singleton. Spreading a tuple that may contain only one member infers both the singleton and multi-member results. Its object fields may therefore be optional. A tuple known to contain at least two members keeps the required intersection semantics.
1151
+ *
1152
+ * `IntersectionStruct<T>` is not exported. It is a `Struct` whose input and output are the intersections of the member types.
1153
+ *
1154
+ * @group struct
1155
+ */
1156
+ declare function createIntersectionStruct<const T extends readonly [StructLike<unknown, unknown, boolean>, ...StructLike<unknown, unknown, boolean>[]]>(...structs: T): T extends readonly [unknown] ? T[0] : T extends readonly [unknown, unknown, ...unknown[]] ? IntersectionStruct<T> : T[0] | IntersectionStruct<T>;
1157
+ /**
1158
+ * Struct that accepts a File.
1159
+ *
1160
+ * - Accepts a `File` only; a plain `Blob` fails with `invalid_type`.
1161
+ *
1162
+ * - Use it for a field of {@link struct.formData}(), or for a raw file body.
1163
+ *
1164
+ * - As an `output` or `error` struct it makes the response body be read as a blob.
1165
+ *
1166
+ * @group struct
1167
+ */
1168
+ declare function createFileStruct(): Struct<File, File>;
1169
+ /**
1170
+ * Struct that accepts an ArrayBuffer.
1171
+ *
1172
+ * - Accepts an `ArrayBuffer` only. Typed arrays and other views fail with `invalid_type`.
1173
+ *
1174
+ * - As a body root it is sent as bytes with `application/octet-stream`.
1175
+ *
1176
+ * - As an `output` or `error` struct it makes the response body be read as an `ArrayBuffer`.
1177
+ *
1178
+ * @group struct
1179
+ */
1180
+ declare function createArrayBufferStruct(): Struct<ArrayBuffer, ArrayBuffer>;
1181
+ /**
1182
+ * Parses an unknown value with a struct and returns an error-first tuple.
1183
+ *
1184
+ * - **struct** is the struct to parse with. A value that is not a struct throws `TypeError`.
1185
+ *
1186
+ * - **value** is the unknown input.
1187
+ *
1188
+ * - **options.aliases** defaults to `false`: object keys are read by their **logical** names (`displayName`). With `true` they are read by their wire names (`display_name`), the way HTTP/JSON codecs read them. The output always uses logical names.
1189
+ *
1190
+ * - **options.errorMap** is an {@link ErrorMap} that rewrites issue messages for this call only, including bigint/date conversion failures from a struct created by another Core copy. Any string, including `''`, replaces the message; only `undefined` keeps the default. Custom text is used verbatim.
1191
+ *
1192
+ * - Parsing is fail-fast: it stops at the first failure, so the error holds one issue. Unknown object keys are stripped from the output, and the input is not modified.
1193
+ *
1194
+ * - At most **256 nested containers** (`object`, `array`, `record`, or `tuple`) are visited along a path, counting the root container as the first. The next container fails with `invalid_type` at its logical path, with `expected: 'value nested at most 256 levels deep'`. In a union, later candidates may still succeed; if none match, the union reports `invalid_union`. Cycles through declared container fields also reach this limit; shared non-cyclic children are allowed. Union and intersection branches do not add a container level, and `any`/`unknown` values remain opaque rather than being traversed.
1195
+ *
1196
+ * ```ts
1197
+ * import { struct } from "@defjs/core";
1198
+ *
1199
+ * const User = struct.object({ id: struct.number(), displayName: struct.string() });
1200
+ *
1201
+ * function readUser() {
1202
+ * const [error, user] = struct.parse(User, { id: 1, displayName: "Ada" });
1203
+ * if (error) {
1204
+ * console.error(error.prettify());
1205
+ * return;
1206
+ * }
1207
+ * console.log(user.displayName);
1208
+ * }
1209
+ *
1210
+ * readUser();
1211
+ * ```
1212
+ *
1213
+ * `any` and `unknown` do not traverse their values, but an intersection may recursively merge two distinct object or array values returned by those leaves. This merge shares the same 256-container budget with its enclosing logical path. Exceeding it produces `invalid_intersection` at the rejected container's logical path. Identical references are retained without traversal, including a shared cyclic value.
1214
+ *
1215
+ * @returns `[null, output]` or `[StructError, undefined]`. Validation failures do not throw. Exceptions from caller-provided getters and `errorMap` callbacks propagate unchanged.
1216
+ * @group struct
1217
+ * @see {@link Struct}
1218
+ * @see {@link ParseResult}
1219
+ */
1220
+ declare function parseStructTuple<S extends StructLike<unknown, unknown, boolean>>(struct: S, value: unknown, options?: {
1221
+ aliases?: boolean;
1222
+ errorMap?: ErrorMap;
1223
+ }): ParseResult<S["_struct"]["output"]>;
1224
+ type EnumValue<T> = T extends readonly (infer U extends string)[] ? U : T extends {
1225
+ [key: string]: infer U extends number | string;
1226
+ } ? U : never;
1227
+ /**
1228
+ * Struct that accepts one value from a list of strings or from the values of an object.
1229
+ *
1230
+ * - With a non-empty string array, the accepted values are the array items.
1231
+ *
1232
+ * - With an object, the accepted values are the object's string and number **values**, not its keys. `{ A: 'a', B: 2 }` accepts `'a'` and `2`.
1233
+ *
1234
+ * - A TypeScript numeric `enum` object also accepts its member names, because the reverse mapping is part of its values.
1235
+ *
1236
+ * - A value outside the set fails with `invalid_enum`. An object with no string or number value throws `TypeError` when the struct is created.
1237
+ *
1238
+ * @group struct
1239
+ */
1240
+ declare function structEnum<const T extends readonly [string, ...string[]]>(value: T): Struct<EnumValue<T>, EnumValue<T>>;
1241
+ declare function structEnum<const T extends {
1242
+ [key: string]: number | string;
1243
+ }>(value: T): Struct<EnumValue<T>, EnumValue<T>>;
1244
+ /**
1245
+ * Struct that accepts a value matching any one of several structs.
1246
+ *
1247
+ * `struct.or` is variadic: pass each option as its own argument.
1248
+ *
1249
+ * - Tries the options in order and returns the result of the first one that parses.
1250
+ *
1251
+ * - If none matches, it fails with one `invalid_union` issue at the union's own path. The issues of the individual options are not reported.
1252
+ *
1253
+ * - Throws `TypeError` when an option is not a struct.
1254
+ *
1255
+ * `UnionStruct<T>` is not exported. It is a `Struct` whose input and output are the unions of the option types, without `undefined`. For tagged objects prefer {@link struct.discriminatedUnion}(), which reports the issue of the selected option.
1256
+ *
1257
+ * @group struct
1258
+ */
1259
+ declare function structOr<const T extends readonly [StructLike, ...StructLike[]]>(...options: T): UnionStruct<T>;
1260
+ /**
1261
+ * Facade object for building protocol-boundary structs and parsing values with its constructors and `struct.parse`.
1262
+ *
1263
+ * Prefer this object over importing the constructor functions one by one. Compose shapes with
1264
+ * `struct.object`, then parse with `struct.parse` or let HTTP/SSE/WebSocket
1265
+ * endpoints apply the same contracts at the wire boundary. `struct` is a plain object of helpers.
1266
+ *
1267
+ * ```ts
1268
+ * import { struct } from "@defjs/core";
1269
+ *
1270
+ * const User = struct.object({
1271
+ * id: struct.number(),
1272
+ * displayName: struct.string().alias("display_name"),
1273
+ * });
1274
+ *
1275
+ * function readUser() {
1276
+ * const [error, user] = struct.parse(User, { id: 1, displayName: "Ada" });
1277
+ * if (error) {
1278
+ * console.error(error.message);
1279
+ * return;
1280
+ * }
1281
+ * console.log(user.displayName);
1282
+ * }
1283
+ *
1284
+ * readUser();
1285
+ * ```
1286
+ *
1287
+ * ### Primitives and containers
1288
+ *
1289
+ * | Helper | Role |
1290
+ * | -------------------------------------------------------------------------------- | --------------------------------------------------------- |
1291
+ * | {@link struct.string}() | Text |
1292
+ * | {@link struct.number}() | Number, `NaN` rejected |
1293
+ * | {@link struct.boolean}() | Boolean |
1294
+ * | {@link struct.bigint}() | `bigint`, or a string that parses as one |
1295
+ * | {@link struct.date}() | `Date`, from a `Date`, timestamp, or string |
1296
+ * | {@link struct.literal}(value) | Exact literal |
1297
+ * | {@link struct.null}() | Only `null` |
1298
+ * | {@link struct.any}() / unknown() ({@link struct.unknown}) | Any value except `null` and `undefined` |
1299
+ * | {@link struct.array}(item) | Array of one item struct |
1300
+ * | {@link struct.tuple}(items) | Fixed-length array; `items` is one array of structs |
1301
+ * | {@link struct.record}(value) | Plain object with string keys and one value struct |
1302
+ * | {@link struct.object}(shape) | Plain object with fixed fields; unknown keys are stripped |
1303
+ * | {@link struct.or}(...options) | First matching option |
1304
+ * | {@link struct.intersection}(...structs) | Every struct must match; object results are merged |
1305
+ * | {@link struct.discriminatedUnion}(tag, options) | Object option picked by the value of the `tag` field |
1306
+ * | {@link struct.enum}(values) | String array, or the values of a `{ Name: value }` object |
1307
+ *
1308
+ * ### Body codecs and binary values
1309
+ *
1310
+ * | Helper | Role |
1311
+ * | ------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- |
1312
+ * | {@link struct.formData}(shape) / urlencoded(shape) ({@link struct.urlencoded}) | Object struct plus a codec tag. The tag applies only when that value is the request body root |
1313
+ * | {@link struct.arrayBuffer}() / blob() ({@link struct.blob}) / file() ({@link struct.file}) | Binary values. As a body root they are sent as bytes; as `output` / `error` they select that representation |
1314
+ * | {@link struct.string}() | Text. As a body root or an inbound `string` struct, the bytes stay text |
1315
+ *
1316
+ * ### Parsing and modifiers
1317
+ *
1318
+ * {@link struct.parse}(struct, value, options?) parses an unknown value and returns `[null, value]` or `[StructError, undefined]`.
1319
+ *
1320
+ * Every struct has the chainable modifiers `.optional()`, `.null()` / `.nullable()`, `.nullish()`, and `.alias(name)`. See {@link StructMethods}.
1321
+ *
1322
+ * @example
1323
+ * ```ts
1324
+ * const User = struct.object({
1325
+ * id: struct.number(),
1326
+ * displayName: struct.string().alias('display_name'),
1327
+ * })
1328
+ *
1329
+ * const getUser = defineRequest({
1330
+ * method: 'GET',
1331
+ * path: '/users/:id',
1332
+ * input: struct.object({ id: struct.number() }),
1333
+ * builder: (input) => ({ path: input }),
1334
+ * output: User,
1335
+ * })
1336
+ *
1337
+ * function readUser() {
1338
+ * const [error, user] = struct.parse(User, { id: 1, displayName: 'Ada' })
1339
+ * if (error) {
1340
+ * console.error(error.message)
1341
+ * return
1342
+ * }
1343
+ * console.log(user.displayName)
1344
+ * }
1345
+ *
1346
+ * readUser()
1347
+ * ```
1348
+ * @group struct
1349
+ */
1350
+ export declare const struct: {
1351
+ readonly any: typeof createAnyStruct;
1352
+ readonly array: typeof createArrayStruct;
1353
+ readonly arrayBuffer: typeof createArrayBufferStruct;
1354
+ readonly bigint: typeof createBigIntStruct;
1355
+ readonly blob: typeof createBlobStruct;
1356
+ readonly boolean: typeof createBooleanStruct;
1357
+ readonly date: typeof createDateStruct;
1358
+ readonly discriminatedUnion: typeof createDiscriminatedUnionStruct;
1359
+ readonly enum: typeof structEnum;
1360
+ readonly file: typeof createFileStruct;
1361
+ readonly formData: typeof createFormDataBodyStruct;
1362
+ readonly intersection: typeof createIntersectionStruct;
1363
+ readonly literal: typeof createLiteralStruct;
1364
+ readonly null: typeof createNullStruct;
1365
+ readonly number: typeof createNumberStruct;
1366
+ readonly object: typeof createObjectStruct;
1367
+ readonly or: typeof structOr;
1368
+ readonly parse: typeof parseStructTuple;
1369
+ readonly record: typeof createRecordStruct;
1370
+ readonly string: typeof createStringStruct;
1371
+ readonly tuple: typeof createTupleStruct;
1372
+ readonly unknown: typeof createUnknownStruct;
1373
+ readonly urlencoded: typeof createUrlencodedBodyStruct;
1374
+ };
1375
+ interface EventStreamMessage {
1376
+ id: string;
1377
+ event: string;
1378
+ data: string;
1379
+ }
1380
+ /**
1381
+ * Metadata available once an SSE response has opened.
1382
+ *
1383
+ * `response` is the handshake response and `url` is the resolved request URL. It is the third element of a successful `client.execute` result and the value of `EventStreamHandle.open`.
1384
+ *
1385
+ * @group sse
1386
+ */
1387
+ interface EventStreamOpenInfo {
1388
+ response: HttpResponse<unknown>;
1389
+ url: string;
1390
+ }
1391
+ /**
1392
+ * Fault codes an event stream can end with, drawn from the one shared `FaultCode` namespace.
1393
+ *
1394
+ * A subset of the shared `FaultCode` set. It is the `code` of an `EventStreamCloseInfo` with `kind: 'error'`.
1395
+ *
1396
+ * @group sse
1397
+ */
1398
+ type EventStreamFaultCode = Extract<FaultCode, "CAP_BUFFER_EXCEEDED" | "CAP_QUEUE_OVERFLOW" | "EXT_HOOK_FAILED" | "EXT_OBSERVER_FAILED" | "NET_TIMEOUT" | "NET_UNREACHABLE" | "RES_DECODE_FAILED" | "RES_MEDIA_TYPE_INVALID">;
1399
+ interface EventStreamCloseInfoBase {
1400
+ reason?: string;
1401
+ cause?: unknown;
1402
+ }
1403
+ /**
1404
+ * How an SSE stream ended: clean EOF, abort, or an error carrying its fault `code`.
1405
+ *
1406
+ * The discriminant is `kind` so that `code` can mean the same thing it means on a fault.
1407
+ *
1408
+ * The value of `EventStreamHandle.closed`. `eof` means the response ended normally. `aborted` means the stream was closed with `close()` or disposal, or its caller signal aborted with a non-timeout reason; `reason` and `cause` carry the value passed to `close`. If the caller signal aborts after open with a `TimeoutError` or `ERR_TIMEOUT`, the iterator throws `ERR_TIMEOUT` and `closed` is `{ kind: 'error', code: 'NET_TIMEOUT', ... }`. Explicit `close(timeoutError)` still reports `aborted`. `error` carries the fault `code` (see `EventStreamFaultCode`) and the failure as `cause`.
1409
+ *
1410
+ * @group sse
1411
+ */
1412
+ type EventStreamCloseInfo = (EventStreamCloseInfoBase & {
1413
+ kind: "eof";
1414
+ }) | (EventStreamCloseInfoBase & {
1415
+ kind: "aborted";
1416
+ }) | (EventStreamCloseInfoBase & {
1417
+ code: EventStreamFaultCode;
1418
+ kind: "error";
1419
+ });
1420
+ /**
1421
+ * Open SSE stream: iterate events, read open metadata, and close early.
1422
+ *
1423
+ * `EventStreamMessage` is the raw `{ id: string; event: string; data: string }` message. Handles returned by `client.execute` carry parsed {@link EventStreamData}.
1424
+ *
1425
+ * `open` is the startup snapshot ({@link EventStreamOpenInfo}); after a reconnect it can change, so read `stream.open` again. `closed` resolves with an {@link EventStreamCloseInfo} when the stream ends.
1426
+ *
1427
+ * Prefer `await using stream = handle` for owned scope cleanup. The disposer stops Defjs reading and reconnect work and waits for its reader lock to be released; it does not promise that a provider-controlled, stuck `ReadableStream.cancel()` promise will finish. Manual `close()` / `closed` remains available.
1428
+ *
1429
+ * ```ts
1430
+ * async function consume(handle: EventStreamHandle<unknown>): Promise<void> {
1431
+ * await using stream = handle;
1432
+ * for await (const event of stream) console.log(event);
1433
+ * }
1434
+ * ```
1435
+ *
1436
+ * Structural implementations of `EventStreamHandle` must implement `[Symbol.asyncDispose]()` and return the same owned teardown path. This is a compile-time requirement for custom handles; code that only receives handles returned by Defjs has no additional runtime call requirement.
1437
+ *
1438
+ * @group sse
1439
+ */
1440
+ interface EventStreamHandle<TEvent = EventStreamMessage> extends AsyncIterable<TEvent>, AsyncDisposable {
1441
+ readonly open: EventStreamOpenInfo;
1442
+ readonly closed: Promise<EventStreamCloseInfo>;
1443
+ close(reason?: unknown): void;
1444
+ [Symbol.asyncDispose](): PromiseLike<void>;
1445
+ }
1446
+ type UseCancellationConfig = {
1447
+ abort?: AbortSignal;
1448
+ timeout?: never;
1449
+ } | {
1450
+ abort?: never;
1451
+ timeout?: number;
1452
+ };
1453
+ type EndpointInput<TInput extends AnyStruct | undefined> = TInput extends AnyStruct ? StructInput<TInput> : unknown;
669
1454
  type IsEndpointInputOptional<TInput extends AnyStruct | undefined> = [TInput] extends [undefined] ? true : undefined extends EndpointInput<TInput> ? true : false;
670
- type EndpointCommandBuilder<TInput extends AnyStruct | undefined, TCommand> = IsEndpointInputOptional<TInput> extends true ? (input?: EndpointInput<TInput>) => TCommand : (input: EndpointInput<TInput>) => TCommand;
671
- //#endregion
672
- //#region src/http/http.d.ts
1455
+ type EndpointCallable<TInput extends AnyStruct | undefined, TCommand> = [TInput] extends [undefined] ? () => TCommand : IsEndpointInputOptional<TInput> extends true ? (input?: EndpointInput<TInput>) => TCommand : (input: EndpointInput<TInput>) => TCommand;
1456
+ /** Object produced by endpoint input parsing. The symbol points at the struct that produced the node. */
1457
+ type Typed = {
1458
+ readonly [STRUCT]: AnyStruct;
1459
+ };
1460
+ type Scalar = string | number | bigint | boolean | Date;
1461
+ type FlatValue = Scalar | readonly Scalar[] | undefined;
1462
+ type FlatRecord = {
1463
+ readonly [key: string]: FlatValue;
1464
+ };
1465
+ type QueryValue = Scalar | readonly QueryValue[] | {
1466
+ readonly [key: string]: QueryValue;
1467
+ } | undefined;
1468
+ type QueryRecord = {
1469
+ readonly [key: string]: QueryValue;
1470
+ };
1471
+ /** A builder body: a wire value Fetch sends as is, or JSON data. An `interface` value or class instance needs `{ ...value }`. */
1472
+ type BodyValue = Scalar | null | Blob | ArrayBuffer | ArrayBufferView | FormData | URLSearchParams | ReadableStream<Uint8Array> | readonly unknown[] | {
1473
+ readonly [key: string]: unknown;
1474
+ };
1475
+ type PathParams<P extends string> = P extends `${string}:${infer Name}/${infer Rest}` ? Name | PathParams<`/${Rest}`> : P extends `${string}:${infer Name}` ? Name : never;
1476
+ type PathValue<P extends string> = [PathParams<P>] extends [never] ? never : ({ readonly [K in PathParams<P>]: Scalar; } & {
1477
+ readonly [STRUCT]?: never;
1478
+ }) | (Typed & {
1479
+ readonly [key: string]: Scalar;
1480
+ });
1481
+ /**
1482
+ * Request shape an HTTP builder returns: path, query, headers, and body.
1483
+ *
1484
+ * `P` is the endpoint path and `M` its method. `GET` and `HEAD` cannot return a
1485
+ * body. `BodyValue` is a wire value Fetch sends as is (`string`, `Blob`,
1486
+ * `ArrayBuffer`, a typed array or `DataView`, `FormData`, `URLSearchParams`,
1487
+ * `ReadableStream<Uint8Array>`) or JSON data (`number`, `boolean`, `bigint`,
1488
+ * `Date`, `null`, an array, or an object type with string keys); an `interface`
1489
+ * value or class instance needs `{ ...value }`. `path` values must be scalars.
1490
+ * A header value may be a scalar or an array of scalars, and `HeadersInit` is
1491
+ * accepted. `query` may nest, and a nested object needs `withQuerySerializer`
1492
+ * at runtime. `FlatRecord`, `QueryRecord`, `PathValue`, and `BodyValue` are
1493
+ * internal helper types.
1494
+ *
1495
+ * @group http
1496
+ */
1497
+ type HttpRequestShape<P extends string, M extends string> = {
1498
+ /**
1499
+ * Request body. Not allowed for GET or HEAD.
1500
+ * Other methods send a native body or encoded JSON, depending on the value.
1501
+ */
1502
+ body?: Uppercase<M> extends "GET" | "HEAD" ? never : BodyValue;
1503
+ /**
1504
+ * HeadersInit or a flat record of scalars or scalar arrays.
1505
+ * Put cross-request policies on the Client.
1506
+ */
1507
+ headers?: HeadersInit | FlatRecord;
1508
+ /**
1509
+ * Explicit values for path placeholders. Anonymous objects use template keys;
1510
+ * typed objects apply their struct aliases.
1511
+ */
1512
+ path?: PathValue<P>;
1513
+ /**
1514
+ * URLSearchParams passes through unchanged; objects are encoded first.
1515
+ * Configure the Client's query serializer for nested query values.
1516
+ */
1517
+ query?: URLSearchParams | QueryRecord;
1518
+ };
1519
+ /**
1520
+ * Request shape the `builder` of a {@link WebSocketDefinition} may return, where `P` is the endpoint path.
1521
+ *
1522
+ * - `path` supplies the `:name` segments of `P`: one scalar value per name. It is `never` when the path has none.
1523
+ * - `query` is a `URLSearchParams` or a record of scalars, arrays, and nested records.
1524
+ * - `headers` and `body` are `never`. A WebSocket handshake takes neither from the builder; put credentials in `beforeConnect` or an interceptor.
1525
+ *
1526
+ * @group web-socket
1527
+ */
1528
+ type WebSocketRequestShape<P extends string> = {
1529
+ /** WebSocket handshakes cannot have a body. Use session.send after connecting. */
1530
+ body?: never;
1531
+ /** WebSocket builders cannot set handshake headers. */
1532
+ headers?: never;
1533
+ /**
1534
+ * Explicit values for path placeholders. Anonymous objects use template keys;
1535
+ * typed objects apply their struct aliases.
1536
+ */
1537
+ path?: PathValue<P>;
1538
+ /**
1539
+ * URLSearchParams passes through unchanged; objects are encoded first.
1540
+ * Configure the Client's query serializer for nested query values.
1541
+ */
1542
+ query?: URLSearchParams | QueryRecord;
1543
+ };
1544
+ /**
1545
+ * Request shape an SSE builder returns: the same fields as {@link HttpRequestShape} for method `M` and path `P`.
1546
+ *
1547
+ * @group sse
1548
+ */
1549
+ type EventStreamRequestShape<P extends string, M extends string> = HttpRequestShape<P, M>;
1550
+ type NullishBuilderInput<S, Inner> = null extends Infer<S> ? undefined extends Infer<S> ? BuilderInput<Inner> | null | undefined : BuilderInput<Inner> | null : undefined extends Infer<S> ? BuilderInput<Inner> | undefined : BuilderInput<Inner>;
1551
+ type IntersectBuilderInputs<T extends readonly StructLike<unknown, unknown, boolean>[]> = T extends readonly [infer Head extends StructLike<unknown, unknown, boolean>, ...infer Tail extends readonly StructLike<unknown, unknown, boolean>[]] ? BuilderInput<Head> & (Tail["length"] extends 0 ? unknown : IntersectBuilderInputs<Tail>) : unknown;
1552
+ /**
1553
+ * Parsed endpoint input that a request builder receives.
1554
+ *
1555
+ * Object nodes are intersected with the internal `Typed` brand, so they carry the struct that
1556
+ * produced them and alias, `Date`, and `bigint` encoding follow the value into
1557
+ * the request. Record entries and tuple items are branded the same way. At
1558
+ * runtime a record container is also tagged when the parsed value is an object,
1559
+ * whether or not it was merged. Array and tuple containers are not. `null` and
1560
+ * `undefined` are never tagged. Nullish modifiers recover the struct they
1561
+ * wrapped. Union options are distributed; intersection options are combined.
1562
+ * Any other struct falls back to `Infer<S>`.
1563
+ *
1564
+ * `CarriedStruct`, `NullishBuilderInput`, `IntersectBuilderInputs`, and `Typed`
1565
+ * are internal helper types.
1566
+ *
1567
+ * @group http
1568
+ */
1569
+ type BuilderInput<S> = S extends CarriedStruct<infer Inner> ? NullishBuilderInput<S, Inner> : S extends UnionStruct<infer Options> ? BuilderInput<Options[number]> : S extends DiscriminatedUnionStruct<infer Options> ? BuilderInput<Options[number]> : S extends IntersectionStruct<infer Options> ? IntersectBuilderInputs<Options> : S extends RecordStruct<infer Value> ? {
1570
+ [key: string]: BuilderInput<Value>;
1571
+ } : S extends TupleStruct<infer Items> ? { [K in keyof Items]: Items[K] extends StructLike<unknown, unknown, boolean> ? BuilderInput<Items[K]> : Items[K]; } : S extends ArrayStruct<infer Item> ? BuilderInput<Item>[] : S extends ObjectStruct<infer Shape> ? { [K in keyof Shape]: BuilderInput<Shape[K]>; } & Typed : Infer<S>;
1572
+ type BuilderField<TInput, TShape> = [TInput] extends [undefined] ? {
1573
+ /** Pure, synchronous request builder, evaluated when the command runs. Optional without input. */
1574
+ builder?: () => TShape;
1575
+ } : {
1576
+ /** Pure, synchronous function that explicitly places parsed input on the request when the command runs. */
1577
+ builder: (input: BuilderInput<TInput>) => TShape;
1578
+ };
1579
+ /** Accepts any concrete builder. `never` keeps the parameter contravariant. */
1580
+ type OutboundBuilder = (input: never) => unknown;
673
1581
  /**
674
1582
  * Per-request options for HTTP execution: progress callbacks and cancellation.
675
- * Cancellation is either `abort` or `timeout` (not both).
1583
+ *
1584
+ * The HTTP-specific part of {@link HttpExecuteOptions}, which adds `signal`.
1585
+ * `onDownloadProgress` and `onUploadProgress` are {@link HttpProgressFn}
1586
+ * callbacks. They receive an {@link HttpProgressEvent} with `loaded`, `total`,
1587
+ * and `lengthComputable`.
1588
+ *
1589
+ * Cancellation is either `abort` or `timeout`, never both. The type rejects
1590
+ * both, and at runtime passing both fails with a `REQ_OPTIONS_INVALID` fault.
1591
+ * `timeout` must be a positive safe integer no greater than `2_147_483_647`.
1592
+ * It replaces the default set by {@link withTimeout} for that call. `abort`
1593
+ * combines with that default instead.
1594
+ *
1595
+ * @group http
676
1596
  */
677
1597
  export type UseRequestConfig = {
678
1598
  onDownloadProgress?: HttpProgressFn;
679
1599
  onUploadProgress?: HttpProgressFn;
680
1600
  } & UseCancellationConfig;
681
1601
  /**
682
- * Executable HTTP command produced by a `defineRequest` builder.
683
- * Carries the request definition and optional typed input for `client.execute`.
1602
+ * Opaque executable HTTP command produced by a `defineRequest` endpoint.
1603
+ *
1604
+ * Created by calling a `RequestEndpoint` with input. It carries the request
1605
+ * definition and optional typed input for `client.execute`. Treat it as opaque
1606
+ * and pass it to `client.execute`.
1607
+ *
1608
+ * @group http
684
1609
  */
685
- export interface HttpCommand<TInput extends AnyStruct | undefined, TOutput extends RequestOutputShape | undefined> extends BaseCommand<typeof HTTP_COMMAND> {
686
- readonly definition: RequestDefinition<TInput, TOutput>;
1610
+ export interface HttpCommand<TInput extends AnyStruct | undefined, TOutput extends AnyStruct | undefined, TError extends AnyStruct | undefined = undefined> extends BaseCommand<typeof HTTP_COMMAND> {
1611
+ readonly definition: RequestDefinition<TInput, TOutput, TError>;
687
1612
  readonly input: EndpointInput<TInput> | undefined;
688
1613
  }
689
1614
  /**
690
- * Options passed to `client.execute` / `executeHttpCommand` for a single HTTP call.
691
- * `signal` combines with `abort` or `timeout`; it is not an alias for `abort`.
692
- * `abort` and `timeout` are mutually exclusive (`abort` XOR `timeout`).
1615
+ * Options passed to `client.execute` or `executeHttpCommand` for one HTTP call: progress, `abort` or `timeout`, and `signal`.
1616
+ *
1617
+ * Cancellation is `abort` or `timeout`, not both (`abort` XOR `timeout`).
1618
+ * `signal` combines with either; it is not an alias for `abort`. Valid shapes:
1619
+ * `{ timeout }`, `{ abort }`, `{ signal, timeout }`, `{ signal, abort }`.
1620
+ * `{ abort, timeout }` is invalid and makes the call resolve with a
1621
+ * `REQ_OPTIONS_INVALID` fault. `timeout` must be a positive safe integer in
1622
+ * `1..2_147_483_647`.
1623
+ *
1624
+ * When `timeout` is omitted, the client's `withTimeout` default applies;
1625
+ * `abort` combines with that default.
1626
+ *
1627
+ * @group http
693
1628
  */
694
1629
  export type HttpExecuteOptions = UseRequestConfig & {
695
1630
  signal?: AbortSignal;
696
1631
  };
697
1632
  /**
698
- * Builder returned by `defineRequest`; call with input to create an `HttpCommand`.
1633
+ * Callable endpoint returned by `defineRequest`.
1634
+ *
1635
+ * Call it to get an `HttpCommand`: with no arguments when the endpoint declares
1636
+ * no `input`, with an optional argument when the input struct accepts
1637
+ * `undefined`, and otherwise with the struct's input value.
1638
+ *
1639
+ * @group http
699
1640
  */
700
- export type RequestCommandBuilder<TInput extends AnyStruct | undefined, TOutput extends RequestOutputShape | undefined> = EndpointCommandBuilder<TInput, HttpCommand<TInput, TOutput>>;
701
- type ResponsePairForStatus<TBody extends AnyStruct, TStatus> = TStatus extends readonly (infer U extends number)[] ? U extends number ? {
702
- body: TBody;
703
- status: U;
704
- } : never : TStatus extends number ? {
705
- body: TBody;
706
- status: TStatus;
707
- } : never;
708
- type ResponsePair<TOutput extends RequestOutputShape | undefined> = NonNullable<TOutput> extends readonly (infer TItem)[] ? TItem extends {
709
- body: infer TBody extends AnyStruct;
710
- status: infer TStatus;
711
- } ? ResponsePairForStatus<TBody, TStatus> : never : { [K in keyof NonNullable<TOutput>]: NonNullable<TOutput>[K] extends (infer TBody extends AnyStruct) ? K extends number ? {
712
- body: TBody;
713
- status: K;
714
- } : K extends `${infer TStatus extends number}` ? {
715
- body: TBody;
716
- status: TStatus;
717
- } : never : never; }[keyof NonNullable<TOutput>];
718
- type HttpStatusErrorByOutput<TOutput extends RequestOutputShape | undefined> = [TOutput] extends [undefined] ? HttpStatusError<undefined> : ResponsePair<TOutput> extends (infer TPair) ? TPair extends {
719
- body: infer TBody extends AnyStruct;
720
- status: infer TStatus extends number;
721
- } ? `${TStatus}` extends `2${string}` ? never : HttpStatusError<Infer<TBody>, TStatus> : never : never;
722
- type RequestErrorByOutput<TOutput extends RequestOutputShape | undefined> = HttpStatusErrorByOutput<TOutput> | TransportError | DefinitionError;
723
- type ResponseBodyByStatus<TOutput extends RequestOutputShape | undefined, TOk extends boolean> = ResponsePair<TOutput> extends (infer TPair) ? TPair extends {
724
- body: infer TBody extends AnyStruct;
725
- status: infer TStatus extends number;
726
- } ? `${TStatus}` extends `2${string}` ? TOk extends true ? TBody : never : TOk extends true ? never : TBody : never : never;
727
- type InferResponseBodyByStatus<TOutput extends RequestOutputShape | undefined, TOk extends boolean> = [TOutput] extends [undefined] ? undefined : [ResponseBodyByStatus<TOutput, TOk>] extends [never] ? unknown : Infer<ResponseBodyByStatus<TOutput, TOk>>;
728
- /** Inferred success body type for 2xx statuses declared in `TOutput`. */
729
- export type RequestSuccessData<TOutput extends RequestOutputShape | undefined> = InferResponseBodyByStatus<TOutput, true>;
730
- /** Inferred error-body type for non-2xx statuses declared in `TOutput`. */
731
- export type RequestErrorData<TOutput extends RequestOutputShape | undefined> = InferResponseBodyByStatus<TOutput, false>;
732
- type ResponseDeclaration<TOutput extends RequestOutputShape | undefined> = [TOutput] extends [undefined] ? {
1641
+ export type RequestEndpoint<TInput extends AnyStruct | undefined, TOutput extends AnyStruct | undefined, TError extends AnyStruct | undefined = undefined> = EndpointCallable<TInput, HttpCommand<TInput, TOutput, TError>>;
1642
+ type DeclaredBody<TStruct> = [TStruct] extends [undefined] ? undefined : Infer<TStruct>;
1643
+ type DeclaredResponse<TStruct> = [TStruct] extends [undefined] ? HttpMeta : DecodedResponse<Infer<TStruct>>;
1644
+ /**
1645
+ * Decoded success body for the declared `output` struct, or `undefined` when none is declared.
1646
+ *
1647
+ * @group http
1648
+ */
1649
+ export type RequestSuccessData<TOutput extends AnyStruct | undefined> = DeclaredBody<TOutput>;
1650
+ /**
1651
+ * Decoded error body for the declared `error` struct, or `undefined` when none is declared.
1652
+ *
1653
+ * @group http
1654
+ */
1655
+ export type RequestErrorData<TError extends AnyStruct | undefined> = DeclaredBody<TError>;
1656
+ type ResponseDeclaration<TOutput extends AnyStruct | undefined, TError extends AnyStruct | undefined> = ([TOutput] extends [undefined] ? {
733
1657
  output?: never;
734
- responseType?: never;
735
1658
  } : {
736
1659
  output: TOutput;
737
- responseType?: HttpResponseType;
738
- };
1660
+ }) & ([TError] extends [undefined] ? {
1661
+ error?: never;
1662
+ } : {
1663
+ error: TError;
1664
+ });
739
1665
  /**
740
- * Contract for an HTTP endpoint: method, path, optional input struct, and status-keyed outputs.
741
- * Pass to `defineRequest` to get a typed command builder.
1666
+ * Contract for an HTTP endpoint: method, path, optional input, and the structs that decode its bodies.
1667
+ *
1668
+ * Declaring `input` requires an explicit `builder` to place the parsed value on
1669
+ * the request. `input` only defines the parameters; the builder alone decides
1670
+ * where each value goes. Only definitions without `input` may omit `builder`.
1671
+ * `output` declares the body for a 2xx response, `error` the body for anything
1672
+ * else. Only one of the two ever runs, and omitting one means that body is never
1673
+ * read. `ResponseDeclaration` is an internal helper type: `output` and `error`
1674
+ * are required only when the matching type parameter is a struct. There is no
1675
+ * `responseType`: the struct kind selects how a declared body is read. Pass to
1676
+ * `defineRequest` to get a typed endpoint.
1677
+ *
1678
+ * @group http
742
1679
  */
743
- export type RequestDefinition<TInput extends AnyStruct | undefined = undefined, TOutput extends RequestOutputShape | undefined = undefined> = ResponseDeclaration<TOutput> & {
744
- operation?: string;
745
- } & ({
746
- method: string;
747
- path: string;
748
- build?: never;
1680
+ export type RequestDefinition<TInput extends AnyStruct | undefined = undefined, TOutput extends AnyStruct | undefined = undefined, TError extends AnyStruct | undefined = undefined> = ResponseDeclaration<TOutput, TError> & {
1681
+ builder?: OutboundBuilder;
749
1682
  input?: TInput;
750
- } | (TInput extends AnyStruct ? {
751
1683
  method: string;
1684
+ operation?: string;
752
1685
  path: string;
753
- build: RequestBuildHandler<TInput>;
754
- input: TInput;
755
- } : never));
1686
+ };
756
1687
  /**
757
- * Await-result tuple for an HTTP call: success `[null, body, response]` or failure `[error, undefined, response?]`.
1688
+ * Await-result tuple for an HTTP call: success is `[null, data, response]`, and failure is `[fault, undefined, undefined]`.
1689
+ *
1690
+ * `TData` and `TErrorData` are decoded body types. On success the third slot
1691
+ * carries `body` only when `output` was declared. On failure it is `undefined`,
1692
+ * because the fault already holds whatever response metadata existed.
1693
+ *
1694
+ * @group http
758
1695
  */
759
- export type HttpAwaitResult<TSuccess = unknown, TErrorData = unknown> = [error: null, result: TSuccess, response: HttpResponse<TSuccess>] | [error: RequestError<TErrorData>, result: undefined, response: HttpResponse<unknown> | undefined];
760
- type HttpExecuteAwaitResult<TOutput extends RequestOutputShape | undefined> = [error: null, result: RequestSuccessData<TOutput>, response: HttpResponse<RequestSuccessData<TOutput>>] | [error: RequestErrorByOutput<TOutput>, result: undefined, response: HttpResponse<unknown> | undefined];
1696
+ export type HttpAwaitResult<TData = undefined, TErrorData = undefined> = [error: null, result: TData, response: [TData] extends [undefined] ? HttpMeta : DecodedResponse<TData>] | [error: FaultOf<TErrorData>, result: undefined, response: undefined];
1697
+ type HttpExecuteAwaitResult<TOutput extends AnyStruct | undefined, TError extends AnyStruct | undefined> = [error: null, result: RequestSuccessData<TOutput>, response: DeclaredResponse<TOutput>] | [error: Fault<TError>, result: undefined, response: undefined];
761
1698
  /**
762
- * Declare a typed HTTP request command builder.
1699
+ * Declares a typed HTTP request endpoint.
1700
+ *
1701
+ * Pass method, path, an optional input struct, and the single `output` and
1702
+ * `error` structs. Call the returned endpoint with input to get an `HttpCommand`
1703
+ * for `client.execute`.
763
1704
  *
764
- * Pass method, path, optional input struct, and status-keyed output structs.
765
- * Call the returned builder with input to get an `HttpCommand` for `client.execute`.
1705
+ * The contract is `method`, `path`, an optional `input` struct, optional
1706
+ * `output` and `error` structs, and an optional `operation`. A declared `input`
1707
+ * always requires an explicit `builder`. `input` only defines the parameters;
1708
+ * the builder alone decides where each value goes. Only definitions without
1709
+ * `input` may omit `builder`. `ResponseDeclaration` and `BuilderField` are
1710
+ * internal helper types: `output` and `error` are required only when the
1711
+ * matching type parameter is a struct, and `builder` is
1712
+ * `(input: BuilderInput<TInput>) => HttpRequestShape` when `input` is declared.
766
1713
  *
767
- * @param definition - Request contract (method, path, input, output).
768
- * @returns A builder that creates `HttpCommand` values from input.
1714
+ * `output` is one struct for the 2xx body; `error` is one struct for every
1715
+ * non-2xx body. Neither is keyed by status. A declaration is an assertion:
1716
+ * omitting one means that body is never read.
1717
+ *
1718
+ * @param definition - Request contract (method, path, input, builder, output).
1719
+ * @returns An endpoint that creates `HttpCommand` values from input.
1720
+ *
1721
+ * The checked example file passes the whole input object as `path`. This snippet maps individual fields into `path` and `query` instead:
769
1722
  *
770
1723
  * @example
771
1724
  * ```ts
772
1725
  * const getUser = defineRequest({
773
1726
  * method: 'GET',
774
1727
  * path: '/users/:id',
775
- * input: struct.request({ path: struct.object({ id: struct.number() }) }),
776
- * output: [{ status: 200, body: struct.object({ id: struct.number(), name: struct.string() }) }],
1728
+ * input: struct.object({ id: struct.number(), verbose: struct.boolean() }),
1729
+ * builder: (input) => ({ path: { id: input.id }, query: { verbose: input.verbose } }),
1730
+ * output: struct.object({ id: struct.number(), name: struct.string() }),
777
1731
  * })
778
1732
  * ```
1733
+ * @group http
1734
+ * @exampleFile examples/api/define-request.ts#example
779
1735
  */
780
- export declare function defineRequest<TInput extends AnyStruct, const TOutput extends RequestOutputShape | undefined = undefined>(definition: RequestDefinition<TInput, TOutput>): RequestCommandBuilder<TInput, TOutput>;
781
- export declare function defineRequest<TInput extends AnyStruct | undefined = undefined, const TOutput extends RequestOutputShape | undefined = undefined>(definition: RequestDefinition<TInput, TOutput>): RequestCommandBuilder<TInput, TOutput>;
782
- /**
783
- * Run an `HttpCommand` against client config: validate input, send the request, parse the response.
784
- * Prefer `client.execute(command)` in application code; this is the underlying implementation.
1736
+ export declare function defineRequest<const M extends string, const P extends string, TInput extends AnyStruct | undefined = undefined, TOutput extends AnyStruct | undefined = undefined, TError extends AnyStruct | undefined = undefined>(definition: ResponseDeclaration<TOutput, TError> & {
1737
+ input?: TInput;
1738
+ method: M;
1739
+ operation?: string;
1740
+ path: P;
1741
+ } & BuilderField<NoInfer<TInput>, HttpRequestShape<NoInfer<P>, NoInfer<M>>>): RequestEndpoint<TInput, TOutput, TError>;
1742
+ /**
1743
+ * Runs an `HttpCommand` against client config: validate input, send the request, and parse the response.
1744
+ *
1745
+ * Low-level entry used by `client.execute`. Prefer `client.execute(command, options)`
1746
+ * in application code. `HttpClientConfig` is the HTTP part of `ClientConfig`, so
1747
+ * a `ClientConfig` is accepted. `HttpExecuteAwaitResult` is the internal result
1748
+ * tuple.
1749
+ *
1750
+ * The result is `[null, data, response]` or `[fault, undefined, undefined]`.
1751
+ * `data` is the decoded `output` body, or `undefined` when none is declared.
1752
+ * `response` is `DecodedResponse` when `output` is declared and `HttpMeta`
1753
+ * otherwise. The fault is `Fault<TError>`. A non-2xx status is `HTTP_STATUS`
1754
+ * when no `error` struct is declared (its `data` is `undefined`) or when the
1755
+ * declared body decodes (its `data` is that body). When a declared `error` body
1756
+ * cannot be read, the fault is a `RES_*` decode fault instead. On failure the
1757
+ * tuple's third slot is `undefined`; the fault carries the response metadata.
1758
+ * `options.timeout` falls back to the client's `withTimeout` default when omitted.
785
1759
  *
786
1760
  * @param clientConfig - Resolved HTTP client configuration (endpoint, interceptors, handler).
787
- * @param command - Command from a `defineRequest` builder.
1761
+ * @param command - Command from a `defineRequest` endpoint.
788
1762
  * @param options - Per-request progress and cancellation options.
789
1763
  * @returns An await-result tuple of success body or typed request error.
1764
+ * @group http
1765
+ */
1766
+ export declare function executeHttpCommand<TInput extends AnyStruct | undefined, TOutput extends AnyStruct | undefined, TError extends AnyStruct | undefined>(clientConfig: HttpClientConfig, command: HttpCommand<TInput, TOutput, TError>, options?: HttpExecuteOptions): Promise<HttpExecuteAwaitResult<TOutput, TError>>;
1767
+ /**
1768
+ * Map from SSE event names to the structs that decode their data.
1769
+ *
1770
+ * An event with no `event:` field is named `message`. An event whose name has no own entry uses the `default` struct; with neither, the event is skipped and `onInvalidEvent` is called. Declare the event name `__proto__` with a computed own key.
1771
+ *
1772
+ * A `string` event struct keeps the raw `data` text. Every other kind is JSON text: `JSON.parse`, then the struct. A bare `struct.literal` therefore needs JSON text, not an unquoted token.
1773
+ *
1774
+ * Reconnect lives on the client (`withSSEReconnect`). SSE `jitter` is a **0-1 multiplicative factor**, same as WebSocket.
1775
+ *
1776
+ * @group sse
790
1777
  */
791
- export declare function executeHttpCommand<TInput extends AnyStruct | undefined, TOutput extends RequestOutputShape | undefined>(clientConfig: HttpClientConfig, command: HttpCommand<TInput, TOutput>, options?: HttpExecuteOptions): Promise<HttpExecuteAwaitResult<TOutput>>;
792
- //#endregion
793
- //#region src/sse/sse.d.ts
794
- /** Map of SSE event names to payload structs. Declare `__proto__` with a computed own key. */
795
1778
  export type EventStructs = {
796
1779
  [key: string]: AnyStruct;
797
1780
  };
798
1781
  type EventName<TKey extends string | number> = `${TKey}`;
799
- type KnownEventUnion<TEvents extends EventStructs> = { [K in keyof TEvents & (number | string)]: EventName<K> extends 'default' ? never : {
1782
+ type KnownEventUnion<TEvents extends EventStructs> = { [K in keyof TEvents & (number | string)]: EventName<K> extends "default" ? never : {
800
1783
  data: Infer<TEvents[K]>;
801
1784
  event: EventName<K>;
802
1785
  id?: string;
803
1786
  }; }[keyof TEvents & (number | string)];
804
- type DefaultEventUnion<TEvents extends EventStructs> = 'default' extends keyof TEvents ? {
805
- data: Infer<TEvents['default']>;
1787
+ type DefaultEventUnion<TEvents extends EventStructs> = "default" extends keyof TEvents ? {
1788
+ data: Infer<TEvents["default"]>;
806
1789
  event: string;
807
1790
  id?: string;
808
1791
  } : never;
809
- /** Parsed SSE event payload inferred from an `EventStructs` map. */
1792
+ /**
1793
+ * Union of parsed SSE events inferred from an EventStructs map.
1794
+ *
1795
+ * For each declared event name except `default`, the union has `{ event: <name>; data: Infer<TEvents[name]>; id?: string }`. When a `default` struct is declared, it adds `{ event: string; data: Infer<TEvents['default']>; id?: string }` for every event name that has no struct of its own. `KnownEventUnion` and `DefaultEventUnion` are internal helper types.
1796
+ *
1797
+ * @group sse
1798
+ */
810
1799
  export type EventStreamData<TEvents extends EventStructs> = TEvents extends EventStructs ? KnownEventUnion<TEvents> | DefaultEventUnion<TEvents> : never;
811
1800
  interface EventStreamDefinitionBase<TEvents extends EventStructs = EventStructs> {
812
1801
  events: TEvents;
@@ -814,178 +1803,283 @@ interface EventStreamDefinitionBase<TEvents extends EventStructs = EventStructs>
814
1803
  maxQueueSize: number;
815
1804
  method?: string;
816
1805
  operation?: string;
817
- /** Optional non-2xx handshake bodies; fills `error.data` when the open status is declared. */
818
- output?: RequestOutputShape;
819
1806
  path: string;
820
1807
  }
821
- type EventStreamDefinitionWithoutBuild<TInput extends AnyStruct | undefined = undefined, TEvents extends EventStructs = EventStructs> = EventStreamDefinitionBase<TEvents> & {
822
- build?: never;
823
- input?: TInput;
1808
+ /** Makes `error` required only when an error struct is declared, so omission stays `Fault<undefined>`. */
1809
+ type EventStreamErrorField<TError extends AnyStruct | undefined> = [TError] extends [undefined] ? {
1810
+ error?: never;
1811
+ } : {
1812
+ error: TError;
824
1813
  };
825
- type EventStreamDefinitionWithBuild<TInput extends AnyStruct, TEvents extends EventStructs = EventStructs> = EventStreamDefinitionBase<TEvents> & {
826
- build: RequestBuildHandler<TInput, 'sse'>;
827
- input: TInput;
1814
+ /**
1815
+ * Contract that describes an SSE endpoint for defineEventStream.
1816
+ *
1817
+ * The composed members are:
1818
+ *
1819
+ * - `events`, `maxBufferSize`, and `maxQueueSize` (both limits are required positive safe integers), optional `method`, `operation`, and a required `path`.
1820
+ *
1821
+ * - `error`, required only when `TError` is a struct. It decodes the body of a failed handshake (a non-2xx response). The kind rule is the same as for HTTP: `string` is text, `blob` and `file` are blobs, `arrayBuffer` is bytes, and everything else is JSON.
1822
+ *
1823
+ * - `input` and `builder`. The request shape matches HTTP, including `body`; `GET` and `HEAD` cannot return a body. A declared `input` always requires an explicit `builder`. `input` only defines the parameters; the builder alone decides where each value goes. Only definitions without `input` may omit `builder`.
1824
+ *
1825
+ * `EventStreamDefinitionBase` and `EventStreamErrorField` are internal helper types.
1826
+ *
1827
+ * @group sse
1828
+ */
1829
+ export type EventStreamDefinition<TInput extends AnyStruct | undefined = undefined, TEvents extends EventStructs = EventStructs, TError extends AnyStruct | undefined = undefined> = EventStreamDefinitionBase<TEvents> & EventStreamErrorField<TError> & {
1830
+ builder?: OutboundBuilder;
1831
+ input?: TInput;
828
1832
  };
829
- /** Contract describing an SSE endpoint: path, events, and buffer/queue limits. */
830
- export type EventStreamDefinition<TInput extends AnyStruct | undefined = undefined, TEvents extends EventStructs = EventStructs> = EventStreamDefinitionWithoutBuild<TInput, TEvents> | (TInput extends AnyStruct ? EventStreamDefinitionWithBuild<TInput, TEvents> : never);
831
- /** Await-result tuple from opening an SSE stream via `client.execute`. */
832
- export type StreamAwaitResult<TEvent> = [error: null, stream: EventStreamHandle<TEvent>, open: EventStreamOpenInfo] | [error: RequestError<unknown>, stream: undefined, open: EventStreamOpenInfo | undefined];
833
- /** Executable SSE command produced by an `EventStreamCommandBuilder`. */
834
- export interface EventStreamCommand<TInput extends AnyStruct | undefined, TEvents extends EventStructs> extends BaseCommand<typeof EVENT_STREAM_COMMAND> {
835
- readonly endpoint: EventStreamEndpoint<TInput, TEvents>;
1833
+ /**
1834
+ * Await-result tuple from opening an SSE stream via `client.execute`.
1835
+ *
1836
+ * - On success: `[null, stream, open]`. `open` is the returned connection's first successful handshake snapshot for native Defjs handles or wrappers forwarding the same live `open` object; custom handles or wrappers overriding `open` use their current public `open` even after reconnect, while `stream.open` stays live.
1837
+ *
1838
+ * - On failure: `[fault, undefined, open]`. `open` carries the response snapshot when the failure happened after a response arrived, for example a non-2xx handshake, and is `undefined` otherwise.
1839
+ *
1840
+ * - `TError` is the declared `error` struct. It types the decoded error body on the fault.
1841
+ *
1842
+ * @group sse
1843
+ */
1844
+ export type StreamAwaitResult<TEvent, TError extends AnyStruct | undefined = undefined> = [error: null, stream: EventStreamHandle<TEvent>, open: EventStreamOpenInfo] | [error: Fault<TError>, stream: undefined, open: EventStreamOpenInfo | undefined];
1845
+ /**
1846
+ * Opaque executable SSE command produced by an `EventStreamEndpoint`.
1847
+ *
1848
+ * Created by calling an `EventStreamEndpoint` with input. It also has internal symbol-keyed members that identify and run the command. Treat it as opaque and pass it to `client.execute`. `endpoint` is the definition plus `method`, which is always a string (it defaults to `'GET'` when the definition omits it). `ResolvedEventStreamDefinition` names that shape and is not exported.
1849
+ *
1850
+ * @group sse
1851
+ */
1852
+ export interface EventStreamCommand<TInput extends AnyStruct | undefined, TEvents extends EventStructs, TError extends AnyStruct | undefined = undefined> extends BaseCommand<typeof EVENT_STREAM_COMMAND> {
1853
+ readonly endpoint: ResolvedEventStreamDefinition<TInput, TEvents, TError>;
836
1854
  readonly input: EndpointInput<TInput> | undefined;
837
1855
  }
838
- /** Per-execute options for SSE commands (abort, timeout). */
1856
+ /**
1857
+ * Per-call options for opening an SSE stream: abort or timeout, plus an optional signal.
1858
+ *
1859
+ * Cancellation is `abort` **or** `timeout`, not both; passing both makes the call resolve with a `REQ_OPTIONS_INVALID` fault. `signal` combines with either and is not an alias for `abort`. `timeout` must be a positive safe integer up to 2147483647.
1860
+ *
1861
+ * For SSE, `timeout` is a startup deadline. It covers input parsing, the builder, the interceptors, and opening the connection, and stops applying once the stream is open. The client-level `withTimeout` default does not apply to SSE.
1862
+ *
1863
+ * @group sse
1864
+ */
839
1865
  export type EventStreamExecuteOptions = UseCancellationConfig & {
840
1866
  signal?: AbortSignal;
841
1867
  };
842
- /** Builder function that creates `EventStreamCommand` values from endpoint input. */
843
- export type EventStreamCommandBuilder<TInput extends AnyStruct | undefined, TEvents extends EventStructs> = EndpointCommandBuilder<TInput, EventStreamCommand<TInput, TEvents>>;
844
- type EventStreamEndpoint<TInput extends AnyStruct | undefined = undefined, TEvents extends EventStructs = EventStructs> = EventStreamDefinition<TInput, TEvents> & {
1868
+ /**
1869
+ * Callable endpoint returned by defineEventStream.
1870
+ *
1871
+ * Returned by `defineEventStream`. Call it to get an `EventStreamCommand`: with no arguments when the endpoint declares no `input`, with an optional argument when the input struct accepts `undefined`, and otherwise with the struct's input value.
1872
+ *
1873
+ * @group sse
1874
+ */
1875
+ export type EventStreamEndpoint<TInput extends AnyStruct | undefined, TEvents extends EventStructs, TError extends AnyStruct | undefined = undefined> = EndpointCallable<TInput, EventStreamCommand<TInput, TEvents, TError>>;
1876
+ type ResolvedEventStreamDefinition<TInput extends AnyStruct | undefined = undefined, TEvents extends EventStructs = EventStructs, TError extends AnyStruct | undefined = undefined> = EventStreamDefinition<TInput, TEvents, TError> & {
845
1877
  readonly method: string;
846
1878
  };
847
1879
  /**
848
- * Declare a typed Server-Sent Events command builder.
1880
+ * Declare a typed Server-Sent Events endpoint.
849
1881
  *
850
- * Pass path, event structs, and buffer/queue limits. Call the returned builder
1882
+ * Pass path, event structs, and buffer/queue limits. Call the returned endpoint
851
1883
  * with input to get an `EventStreamCommand` for `client.execute`.
852
1884
  *
1885
+ * - **definition** — `path`, an `events` map, and the required `maxBufferSize` and `maxQueueSize` limits (positive safe integers). Optional `method` (default `'GET'`), `operation`, `input`, and `error`. A declared `input` always requires an explicit `builder`. `input` only defines the parameters; the builder alone decides where each value goes. Only definitions without `input` may omit `builder`.
1886
+ *
1887
+ * `EventStreamDefinitionBase`, `EventStreamErrorField`, and `BuilderField` are internal helper types: `error` is required only when `TError` is a struct, and `builder` is `(input: BuilderInput<TInput>) => EventStreamRequestShape` when `input` is declared.
1888
+ *
853
1889
  * @param definition - SSE contract (path, events, buffer limits, optional input).
854
- * @returns A builder that creates `EventStreamCommand` values from input.
1890
+ * @returns An endpoint that creates `EventStreamCommand` values from input.
855
1891
  *
856
- * @example
857
- * ```ts
858
- * const useEvents = defineEventStream({
859
- * maxBufferSize: 1024,
860
- * maxQueueSize: 16,
861
- * path: '/events',
862
- * events: { message: struct.object({ text: struct.string() }) },
863
- * })
864
- * ```
1892
+ * @group sse
1893
+ * @exampleFile examples/api/define-event-stream.ts#example
865
1894
  */
866
- export declare function defineEventStream<TInput extends AnyStruct, TEvents extends EventStructs = EventStructs>(definition: EventStreamDefinitionWithBuild<TInput, TEvents>): EventStreamCommandBuilder<TInput, TEvents>;
867
- export declare function defineEventStream<TInput extends AnyStruct | undefined = undefined, TEvents extends EventStructs = EventStructs>(definition: EventStreamDefinitionWithoutBuild<TInput, TEvents>): EventStreamCommandBuilder<TInput, TEvents>;
1895
+ export declare function defineEventStream<const P extends string, const M extends string = "GET", TInput extends AnyStruct | undefined = undefined, TEvents extends EventStructs = EventStructs, TError extends AnyStruct | undefined = undefined>(definition: Omit<EventStreamDefinitionBase<TEvents>, "method" | "path"> & EventStreamErrorField<TError> & {
1896
+ input?: TInput;
1897
+ method?: M;
1898
+ path: P;
1899
+ } & BuilderField<NoInfer<TInput>, EventStreamRequestShape<NoInfer<P>, NoInfer<M>>>): EventStreamEndpoint<TInput, TEvents, TError>;
868
1900
  /**
869
1901
  * Open an SSE command against the given client config.
870
1902
  *
871
1903
  * Prefer `client.execute(command)` in application code; this is the low-level entry used by the client.
872
1904
  *
1905
+ * The result is `[null, stream, open]` or `[fault, undefined, open | undefined]`. The fault is `Fault<TError>`: the decoded handshake body is on `data` when `error` is declared, and `Fault<undefined>` otherwise.
1906
+ *
873
1907
  * @param clientConfig - Resolved client configuration.
874
- * @param command - SSE command from an `EventStreamCommandBuilder`.
1908
+ * @param command - SSE command from an `EventStreamEndpoint`.
875
1909
  * @param options - Optional execute options (abort, timeout).
876
1910
  * @returns Await-result tuple of `[error, stream, open]`.
1911
+ * @group sse
877
1912
  */
878
- export declare function executeEventStreamCommand<TInput extends AnyStruct | undefined, TEvents extends EventStructs>(clientConfig: ClientConfig, command: EventStreamCommand<TInput, TEvents>, options?: EventStreamExecuteOptions): Promise<StreamAwaitResult<EventStreamData<TEvents>>>;
879
- //#endregion
880
- //#region src/client/command.d.ts
1913
+ export declare function executeEventStreamCommand<TInput extends AnyStruct | undefined, TEvents extends EventStructs, TError extends AnyStruct | undefined = undefined>(clientConfig: ClientConfig, command: EventStreamCommand<TInput, TEvents, TError>, options?: EventStreamExecuteOptions): Promise<StreamAwaitResult<EventStreamData<TEvents>, TError>>;
881
1914
  declare const COMMAND_TYPE: unique symbol;
882
1915
  declare const HTTP_COMMAND: unique symbol;
883
1916
  declare const EVENT_STREAM_COMMAND: unique symbol;
884
1917
  declare const WEB_SOCKET_COMMAND: unique symbol;
1918
+ /** The command's own runner, so `client.execute` never imports a transport the app does not use. */
1919
+ declare const COMMAND_EXECUTE: unique symbol;
1920
+ type CommandExecutor = (config: ClientConfig, command: never, options?: never) => Promise<unknown>;
885
1921
  type CommandType = typeof HTTP_COMMAND | typeof EVENT_STREAM_COMMAND | typeof WEB_SOCKET_COMMAND;
886
1922
  interface BaseCommand<TCommandType extends CommandType> {
1923
+ readonly [COMMAND_EXECUTE]: CommandExecutor;
887
1924
  readonly [COMMAND_TYPE]: TCommandType;
888
1925
  }
889
- //#endregion
890
- //#region src/web_socket/codec.d.ts
891
1926
  interface WebSocketCloseSnapshot {
892
1927
  cause?: unknown;
893
1928
  code?: number;
894
1929
  reason?: string;
895
1930
  wasClean?: boolean;
896
- }
897
- //#endregion
898
- //#region src/web_socket/web_socket.d.ts
899
- /** Lifecycle state of a managed WebSocket session. */
900
- export type WebSocketState = 'aborted' | 'closed' | 'closing' | 'connecting' | 'error' | 'idle' | 'open' | 'reconnecting';
901
- /** Map of WebSocket message type names to payload structs. */
902
- export type SocketStructs = {
903
- [key: string]: AnyStruct;
904
- };
905
- export interface NormalizedWebSocketIncoming {
906
- data: unknown;
907
- type: string;
908
- }
909
- export type NormalizedWebSocketOutgoing = boolean | null | number | string | readonly unknown[] | {
910
- readonly [key: string]: unknown;
911
- };
912
- export type WebSocketIncomingNormalizer = (decoded: unknown) => NormalizedWebSocketIncoming | undefined;
913
- export type WebSocketOutgoingNormalizer = (type: string, encodedPayload: unknown) => NormalizedWebSocketOutgoing;
914
- type SimplifySocket<T> = { [K in keyof T]: T[K]; } & {};
915
- type NormalizeSocketMessage<TKey extends string, TPayload> = TPayload extends {
916
- [key: string]: unknown;
917
- } ? SimplifySocket<{
918
- type: TKey;
919
- } & TPayload> : {
920
- data: TPayload;
921
- type: TKey;
922
- };
923
- type SocketSendMessage<TKey extends string, TPayload> = TPayload extends {
924
- [key: string]: unknown;
925
- } ? SimplifySocket<{
926
- type: TKey;
927
- } & TPayload> | {
928
- data: TPayload;
929
- type: TKey;
930
- } : {
931
- data: TPayload;
932
- type: TKey;
933
- };
934
- type KnownIncomingSocketUnion<TIncoming extends SocketStructs> = { [K in keyof TIncoming & string as K extends 'default' ? never : K]: NormalizeSocketMessage<K, Infer<TIncoming[K]>>; } extends (infer O) ? O[keyof O] : never;
935
- type KnownOutgoingSocketUnion<TOutgoing extends SocketStructs> = { [K in keyof TOutgoing & string as K extends 'default' ? never : K]: SocketSendMessage<K, EndpointInput<TOutgoing[K]>>; } extends (infer O) ? O[keyof O] : never;
936
- type DefaultIncomingSocketUnion<TIncoming extends SocketStructs> = 'default' extends keyof TIncoming ? NormalizeSocketMessage<string, Infer<TIncoming['default']>> : never;
937
- /** Incoming message shape inferred from an incoming `SocketStructs` map. */
938
- export type WebSocketIncomingData<TIncoming extends SocketStructs> = [KnownIncomingSocketUnion<TIncoming> | DefaultIncomingSocketUnion<TIncoming>] extends [never] ? never : KnownIncomingSocketUnion<TIncoming> | DefaultIncomingSocketUnion<TIncoming>;
939
- /** Outgoing message shape inferred from an outgoing `SocketStructs` map. */
940
- export type WebSocketOutgoingData<TOutgoing extends SocketStructs | undefined> = TOutgoing extends SocketStructs ? [KnownOutgoingSocketUnion<TOutgoing>] extends [never] ? never : KnownOutgoingSocketUnion<TOutgoing> : never;
941
- interface WebSocketDefinitionBase<TIncoming extends SocketStructs = SocketStructs, TOutgoing extends SocketStructs | undefined = undefined> {
1931
+ }
1932
+ /**
1933
+ * Lifecycle state of a managed WebSocket session.
1934
+ *
1935
+ * | State | Meaning |
1936
+ * | -------------- | ---------------------------------------------------------------------- |
1937
+ * | `idle` | Before the first connect attempt |
1938
+ * | `connecting` | Preparing and opening the first connection |
1939
+ * | `open` | The physical socket is open |
1940
+ * | `reconnecting` | Preparing or waiting for a replacement socket after a close |
1941
+ * | `closing` | Closure was requested with `session.close()` or disposal |
1942
+ * | `closed` | Terminal: the session ended without an error or cancellation |
1943
+ * | `aborted` | Terminal: the session was cancelled through `abort` or `signal` |
1944
+ * | `error` | Terminal: the session failed, including a startup failure or `timeout` |
1945
+ *
1946
+ * Observe it with `session.state` and `session.onStateChange`.
1947
+ *
1948
+ * @group web-socket
1949
+ */
1950
+ export type WebSocketState = "aborted" | "closed" | "closing" | "connecting" | "error" | "idle" | "open" | "reconnecting";
1951
+ /**
1952
+ * Decoded type of a received WebSocket message.
1953
+ *
1954
+ * The type of each message yielded by `session.receive`: the {@link Infer} output of the endpoint's `incoming` struct. Aliased fields use their logical names.
1955
+ *
1956
+ * @group web-socket
1957
+ */
1958
+ export type WebSocketIncomingData<TIncoming extends AnyStruct> = Infer<TIncoming>;
1959
+ /**
1960
+ * Type accepted by WebSocket session send.
1961
+ *
1962
+ * The type `session.send` accepts: the {@link StructInput} of the endpoint's `outgoing` struct, or `never` when the endpoint declares none, so `send` cannot be called with a value.
1963
+ *
1964
+ * @group web-socket
1965
+ */
1966
+ export type WebSocketOutgoingData<TOutgoing extends AnyStruct | undefined> = TOutgoing extends AnyStruct ? StructInput<TOutgoing> : never;
1967
+ interface WebSocketDefinitionBase<TIncoming extends AnyStruct = AnyStruct, TOutgoing extends AnyStruct | undefined = undefined> {
942
1968
  incoming: TIncoming;
943
1969
  maxIncomingQueueSize: number;
944
1970
  maxOutgoingQueueSize?: number;
945
- normalizeIncoming?: WebSocketIncomingNormalizer;
946
- normalizeOutgoing?: WebSocketOutgoingNormalizer;
947
1971
  operation?: string;
948
1972
  outgoing?: TOutgoing;
949
1973
  path: string;
950
1974
  protocols?: readonly string[];
951
1975
  }
952
- type WebSocketDefinitionWithoutBuild<TInput extends AnyStruct | undefined = undefined, TIncoming extends SocketStructs = SocketStructs, TOutgoing extends SocketStructs | undefined = undefined> = WebSocketDefinitionBase<TIncoming, TOutgoing> & {
953
- build?: never;
1976
+ /**
1977
+ * Contract object passed to defineWebSocket.
1978
+ *
1979
+ * - `incoming` is the struct every received frame is parsed with. Frames are JSON text.
1980
+ *
1981
+ * - `outgoing` is the struct `session.send` validates and encodes with. Without it, `send` throws.
1982
+ *
1983
+ * - `maxIncomingQueueSize` is required and must be a positive safe integer. `maxOutgoingQueueSize` must be a non-negative safe integer; it defaults to `0`, which disables queueing of messages sent while the session is `reconnecting`.
1984
+ *
1985
+ * - `protocols` is the static default subprotocol list. The `protocols` execute option and {@link withWebSocketProtocols} override it.
1986
+ *
1987
+ * - `operation` is a static, low-cardinality name for telemetry.
1988
+ *
1989
+ * - `input` is the struct for the command input. A declared `input` always requires an explicit `builder`. `input` only defines the parameters; the builder alone decides where each value goes. Only definitions without `input` may omit `builder`. The builder may return `path` and `query` only; see {@link WebSocketRequestShape}.
1990
+ *
1991
+ * Invalid queue limits are reported when the command executes, as a `REQ_OPTIONS_INVALID` fault.
1992
+ *
1993
+ * @group web-socket
1994
+ */
1995
+ export type WebSocketDefinition<TInput extends AnyStruct | undefined = undefined, TIncoming extends AnyStruct = AnyStruct, TOutgoing extends AnyStruct | undefined = undefined> = WebSocketDefinitionBase<TIncoming, TOutgoing> & {
1996
+ builder?: OutboundBuilder;
954
1997
  input?: TInput;
955
1998
  };
956
- type WebSocketDefinitionWithBuild<TInput extends AnyStruct, TIncoming extends SocketStructs = SocketStructs, TOutgoing extends SocketStructs | undefined = undefined> = WebSocketDefinitionBase<TIncoming, TOutgoing> & {
957
- build: RequestBuildHandler<TInput, 'webSocket'>;
958
- input: TInput;
959
- };
960
- /** Contract describing a WebSocket endpoint: path, message structs, and queue limits. */
961
- export type WebSocketDefinition<TInput extends AnyStruct | undefined = undefined, TIncoming extends SocketStructs = SocketStructs, TOutgoing extends SocketStructs | undefined = undefined> = WebSocketDefinitionWithoutBuild<TInput, TIncoming, TOutgoing> | (TInput extends AnyStruct ? WebSocketDefinitionWithBuild<TInput, TIncoming, TOutgoing> : never);
962
- /** Connection metadata for the current WebSocket generation. */
1999
+ /**
2000
+ * Connection metadata for the current WebSocket generation.
2001
+ *
2002
+ * - `url` is the URL the socket reports.
2003
+ *
2004
+ * - `protocol` is the negotiated subprotocol and `extensions` the negotiated extensions. Both are `undefined` when the socket reports an empty string.
2005
+ *
2006
+ * - `generation` counts the physical sockets that reached `open`: `1` after the first open, one more after each reconnect. A snapshot taken before any open has `0`.
2007
+ *
2008
+ * `session.connection` exposes the session's current public connection info. The `connection` in the execute result preserves the first successful snapshot only for a native Defjs session or a wrapper forwarding the same live `connection` object. Custom sessions and wrappers that transform `connection` instead return their current public `connection`. Sharing `closed` cannot bypass URL sanitization or metadata filtering, and Defjs does not reconstruct first-connection metadata from arbitrary transformations.
2009
+ *
2010
+ * @group web-socket
2011
+ */
963
2012
  export interface WebSocketConnectionInfo {
964
2013
  extensions?: string;
965
2014
  generation: number;
966
2015
  protocol?: string;
967
2016
  url?: string;
968
2017
  }
969
- /** How a WebSocket session ended: closed, aborted, or error. */
2018
+ /**
2019
+ * Logical terminal outcome of a WebSocket session.
2020
+ *
2021
+ * What `session.closed` resolves with, once the session has ended for good.
2022
+ *
2023
+ * - `closed`: the session ended normally, including a manual `session.close()`.
2024
+ *
2025
+ * - `aborted`: the session was cancelled; `cause` is the abort reason.
2026
+ *
2027
+ * - `error`: the session failed; `cause` is required.
2028
+ *
2029
+ * - `code`, `reason`, and `wasClean` come from the native close event when one was observed. A manual `session.close(code, reason)` fills `code` and `reason` when the close event does not.
2030
+ *
2031
+ * @group web-socket
2032
+ */
970
2033
  export type WebSocketCloseInfo = {
971
- kind: 'closed';
2034
+ kind: "closed";
972
2035
  code?: number;
973
2036
  reason?: string;
974
2037
  wasClean?: boolean;
975
2038
  } | {
976
- kind: 'aborted';
2039
+ kind: "aborted";
977
2040
  cause?: unknown;
978
2041
  code?: number;
979
2042
  reason?: string;
980
2043
  wasClean?: boolean;
981
2044
  } | {
982
- kind: 'error';
2045
+ kind: "error";
983
2046
  cause: unknown;
984
2047
  code?: number;
985
2048
  reason?: string;
986
2049
  wasClean?: boolean;
987
2050
  };
988
- /** Typed WebSocket session: send/receive messages, observe state, and close. */
2051
+ /**
2052
+ * Typed WebSocket session: send and receive messages, observe state, and close.
2053
+ *
2054
+ * - `state` is the logical {@link WebSocketState}; `connection` is the latest {@link WebSocketConnectionInfo}. `bufferedAmount` is the native unsent byte count, or `0` with no physical socket.
2055
+ *
2056
+ * - `receive` yields incoming messages already parsed with the `incoming` struct. It is one-consumer: asking for a second iterator throws a `TypeError`. A frame that cannot be parsed is reported to `onRuntimeError` listeners and skipped.
2057
+ *
2058
+ * - `send(message)` validates and encodes the message with the `outgoing` struct, then sends JSON text. It sends immediately while `open`, queues while `reconnecting` when `maxOutgoingQueueSize` allows, and throws a `DOMException` named `InvalidStateError` when the session is not writable. It also throws when no `outgoing` struct is declared, the message fails the struct, or the queue is disabled or full.
2059
+ *
2060
+ * - After encoding, a value JSON cannot faithfully represent throws `TypeError` before sending or entering the queue. This includes non-finite numbers, functions, symbols, `Map`, `Set`, streams, and class instances without a JSON representation, at the root or nested. The same guard protects heartbeat messages; a heartbeat serialization failure ends the session. Struct validation may reject a value first.
2061
+ *
2062
+ * - `close(code?, reason?)` requests closure and stops the heartbeat. `code` must be `1000` or in `3000..4999`, and `reason` at most 123 UTF-8 bytes; invalid arguments throw before the state changes.
2063
+ *
2064
+ * - `closed` resolves with the logical terminal {@link WebSocketCloseInfo}.
2065
+ *
2066
+ * - `onStateChange` and `onRuntimeError` return an unsubscribe function.
2067
+ *
2068
+ * Prefer `await using ownedSession = session` for owned scope cleanup. Manual `session.close()` and `await session.closed` remain available.
2069
+ *
2070
+ * ```ts
2071
+ * async function consume(
2072
+ * session: WebSocketSession<unknown, never>,
2073
+ * ): Promise<void> {
2074
+ * await using ownedSession = session;
2075
+ * for await (const message of ownedSession.receive) console.log(message);
2076
+ * }
2077
+ * ```
2078
+ *
2079
+ * The async disposer requests native close and waits for Defjs-owned teardown, but it bounds a missing close event at one second and may reject with a `DOMException` named `TimeoutError`. It cannot prove that the physical TCP connection closed.
2080
+ *
2081
+ * @group web-socket
2082
+ */
989
2083
  export interface WebSocketSession<TIncoming = unknown, TOutgoing = never> extends AsyncDisposable {
990
2084
  readonly bufferedAmount: number;
991
2085
  readonly connection: WebSocketConnectionInfo;
@@ -998,136 +2092,326 @@ export interface WebSocketSession<TIncoming = unknown, TOutgoing = never> extend
998
2092
  onStateChange(listener: (state: WebSocketState) => void): () => void;
999
2093
  send(message: TOutgoing): void;
1000
2094
  }
1001
- /** Await-result tuple from opening a WebSocket via `client.execute`. */
1002
- export type SocketAwaitResult<TIncoming, TOutgoing = never> = [error: null, socket: WebSocketSession<TIncoming, TOutgoing>, connection: WebSocketConnectionInfo] | [error: RequestError<unknown>, socket: undefined, connection: WebSocketConnectionInfo | undefined];
2095
+ /**
2096
+ * Await-result tuple from opening a WebSocket via `client.execute`.
2097
+ *
2098
+ * - On success: `[null, session, connection]`. `session.connection` moves forward on reconnect. The tuple's `connection` preserves the first successful snapshot only for a native Defjs session or a wrapper forwarding the same live `connection` object. Custom sessions and wrappers that transform `connection` instead return their current public `connection`. Sharing `closed` cannot bypass URL sanitization or metadata filtering, and Defjs does not reconstruct first-connection metadata from arbitrary transformations.
2099
+ *
2100
+ * - On failure: `[fault, undefined, connection]`. `connection` may be `undefined`, for example when the constructor failed before the socket opened.
2101
+ *
2102
+ * - Only startup failures arrive here. Once the session is returned, later errors surface through `onRuntimeError`, `receive`, and `closed`.
2103
+ *
2104
+ * @group web-socket
2105
+ */
2106
+ export type SocketAwaitResult<TIncoming, TOutgoing = never> = [error: null, socket: WebSocketSession<TIncoming, TOutgoing>, connection: WebSocketConnectionInfo] | [error: Fault<undefined>, socket: undefined, connection: WebSocketConnectionInfo | undefined];
1003
2107
  interface UseWebSocketBaseConfig<TIncoming = unknown, TOutgoing = unknown> {
1004
- beforeConnect?: ClientWebSocketOptions['beforeConnect'];
2108
+ beforeConnect?: ClientWebSocketOptions["beforeConnect"];
1005
2109
  heartbeat?: WebSocketHeartbeatConfig<TIncoming, TOutgoing>;
1006
2110
  protocols?: readonly string[];
1007
- reconnect?: ClientWebSocketOptions['reconnect'];
2111
+ reconnect?: ClientWebSocketOptions["reconnect"];
1008
2112
  }
1009
- /** Client configuration for WebSocket execute (heartbeat, reconnect, abort). */
2113
+ /**
2114
+ * WebSocket execute options without signal.
2115
+ *
2116
+ * The WebSocket-specific part of {@link WebSocketExecuteOptions}, which adds `signal`.
2117
+ *
2118
+ * - `beforeConnect`, `heartbeat`, `protocols`, and `reconnect` override the client-level options for one call.
2119
+ *
2120
+ * - Cancellation is either `abort` or `timeout`, never both. The type rejects both, and at runtime passing both fails with a `REQ_OPTIONS_INVALID` fault.
2121
+ *
2122
+ * @group web-socket
2123
+ */
1010
2124
  export type UseWebSocketConfig<TIncoming = unknown, TOutgoing = unknown> = UseWebSocketBaseConfig<TIncoming, TOutgoing> & UseCancellationConfig;
1011
- /** Per-execute options for WebSocket commands, including an optional `AbortSignal`. */
2125
+ /**
2126
+ * Per-call options for executing a WebSocket command.
2127
+ *
2128
+ * {@link UseWebSocketConfig} plus `signal`. Expanded:
2129
+ *
2130
+ * ```ts
2131
+ * type WebSocketExecuteOptions<TIncoming = unknown, TOutgoing = unknown> = {
2132
+ * beforeConnect?: (context: {
2133
+ * attempt: number;
2134
+ * signal: AbortSignal;
2135
+ * }) => void | Promise<void>;
2136
+ * heartbeat?: WebSocketHeartbeatConfig<TIncoming, TOutgoing>;
2137
+ * protocols?: readonly string[];
2138
+ * reconnect?: ClientWebSocketOptions["reconnect"];
2139
+ * signal?: AbortSignal;
2140
+ * } & (
2141
+ * { abort?: AbortSignal; timeout?: never } | { abort?: never; timeout?: number }
2142
+ * );
2143
+ * ```
2144
+ *
2145
+ * - `abort` and `timeout` are mutually exclusive. The type rejects both, and at runtime passing both fails with a `REQ_OPTIONS_INVALID` fault.
2146
+ *
2147
+ * - `signal` combines with either one; it is not an alias for `abort`.
2148
+ *
2149
+ * - `timeout` must be a positive safe integer no greater than `2_147_483_647`. It limits how long opening the session may take and is not applied again on reconnect.
2150
+ *
2151
+ * - `beforeConnect`, `heartbeat`, `protocols`, and `reconnect` override the client-level options for this call.
2152
+ *
2153
+ * - When `reconnect` is set, `attempts` defaults to `3` per outage; `0` disables. Successful `open` resets the budget and backoff exponent. Omitting `attempts` does not give unlimited retries within one outage. See {@link withWebSocketReconnect} for validation and final delay caps.
2154
+ *
2155
+ * See {@link WebSocketHeartbeatConfig}.
2156
+ *
2157
+ * @group web-socket
2158
+ */
1012
2159
  export type WebSocketExecuteOptions<TIncoming = unknown, TOutgoing = unknown> = UseWebSocketConfig<TIncoming, TOutgoing> & {
1013
2160
  signal?: AbortSignal;
1014
2161
  };
1015
- /** Executable WebSocket command produced by a `WebSocketCommandBuilder`. */
1016
- export interface WebSocketCommand<TInput extends AnyStruct | undefined, TIncoming extends SocketStructs, TOutgoing extends SocketStructs | undefined> extends BaseCommand<typeof WEB_SOCKET_COMMAND> {
1017
- readonly endpoint: WebSocketEndpoint<TInput, TIncoming, TOutgoing>;
2162
+ /**
2163
+ * Executable command created by calling a WebSocketEndpoint.
2164
+ *
2165
+ * Created by calling a {@link WebSocketEndpoint}. It holds the endpoint definition and the unparsed input. Pass it to `client.execute` to open a session; nothing connects until then.
2166
+ *
2167
+ * `BaseCommand` carries symbol-keyed members that route the command to the right transport. They are internal, so treat the command as opaque.
2168
+ *
2169
+ * @group web-socket
2170
+ */
2171
+ export interface WebSocketCommand<TInput extends AnyStruct | undefined, TIncoming extends AnyStruct, TOutgoing extends AnyStruct | undefined> extends BaseCommand<typeof WEB_SOCKET_COMMAND> {
2172
+ readonly endpoint: WebSocketDefinition<TInput, TIncoming, TOutgoing>;
1018
2173
  readonly input: EndpointInput<TInput> | undefined;
1019
2174
  }
1020
- /** Builder function that creates `WebSocketCommand` values from endpoint input. */
1021
- export type WebSocketCommandBuilder<TInput extends AnyStruct | undefined, TIncoming extends SocketStructs, TOutgoing extends SocketStructs | undefined> = EndpointCommandBuilder<TInput, WebSocketCommand<TInput, TIncoming, TOutgoing>>;
1022
- type WebSocketEndpoint<TInput extends AnyStruct | undefined = undefined, TIncoming extends SocketStructs = SocketStructs, TOutgoing extends SocketStructs | undefined = undefined> = WebSocketDefinition<TInput, TIncoming, TOutgoing>;
1023
- /** Reason recorded when the session is closed via `session.close()`. */
2175
+ /**
2176
+ * Callable returned by defineWebSocket.
2177
+ *
2178
+ * Returned by {@link defineWebSocket}. Calling it creates a {@link WebSocketCommand} that you pass to `client.execute`. The call signature depends on `input`:
2179
+ *
2180
+ * - No `input` declared: `() => WebSocketCommand`.
2181
+ *
2182
+ * - `input` accepts `undefined`: `(input?: StructInput<TInput>) => WebSocketCommand`.
2183
+ *
2184
+ * - Otherwise: `(input: StructInput<TInput>) => WebSocketCommand`.
2185
+ *
2186
+ * Calling the endpoint does not connect. The input is parsed when the command is executed.
2187
+ *
2188
+ * @group web-socket
2189
+ */
2190
+ export type WebSocketEndpoint<TInput extends AnyStruct | undefined, TIncoming extends AnyStruct, TOutgoing extends AnyStruct | undefined> = EndpointCallable<TInput, WebSocketCommand<TInput, TIncoming, TOutgoing>>;
2191
+ /**
2192
+ * Close code and reason recorded when a session is closed with session.close.
2193
+ *
2194
+ * Records the `code` and `reason` passed to `session.close(code?, reason?)`. They fill `code` and `reason` of the final `{ kind: 'closed' }` close info when the peer's close event carries none. `close` throws a `DOMException` when a provided `code` is not the integer `1000` or an integer from `3000` through `4999`, and when `reason` exceeds 123 UTF-8 bytes. Omitting `code` is allowed.
2195
+ *
2196
+ * @group web-socket
2197
+ */
1024
2198
  export type ManualSocketCloseReason = {
1025
2199
  code?: number;
1026
- kind: 'manual-web-socket-close';
2200
+ kind: "manual-web-socket-close";
1027
2201
  reason?: string;
1028
2202
  };
1029
- /** Heartbeat ping/ack configuration for a live WebSocket session. */
2203
+ /**
2204
+ * Heartbeat ping and acknowledgement settings for a live WebSocket session.
2205
+ *
2206
+ * - `intervalMs` is the delay between heartbeat messages.
2207
+ *
2208
+ * - `message()` builds the payload. It is sent through the `outgoing` struct. Nothing is sent when it is omitted or returns `undefined`.
2209
+ *
2210
+ * - After struct encoding, non-JSON values fail with `TypeError` through the same serializer as `session.send`; that heartbeat failure ends the logical session without reconnecting.
2211
+ *
2212
+ * - `isAck(message)` receives each decoded incoming message. When it returns `true`, the message is treated as the acknowledgement and is not delivered to `receive`.
2213
+ *
2214
+ * - `timeoutMs` is how long to wait for an acknowledgement after a heartbeat. Missing it fails the session.
2215
+ *
2216
+ * - `intervalMs` and `timeoutMs` must be positive, finite, and at most `2_147_483_647`.
2217
+ *
2218
+ * Set it with {@link withWebSocketHeartbeat} or the `heartbeat` execute option.
2219
+ *
2220
+ * @group web-socket
2221
+ */
1030
2222
  export interface WebSocketHeartbeatConfig<TIncoming = unknown, TOutgoing = unknown> {
1031
2223
  intervalMs: number;
1032
2224
  isAck?: (message: TIncoming) => boolean;
1033
- message?: <T = TOutgoing>() => T | unknown;
2225
+ message?: () => TOutgoing;
1034
2226
  timeoutMs?: number;
1035
2227
  }
1036
2228
  /**
1037
- * Declare a typed WebSocket command builder.
2229
+ * Declare a typed WebSocket endpoint.
1038
2230
  *
1039
2231
  * Pass path, incoming (and optional outgoing) message structs, and queue limits.
1040
- * Call the returned builder with input to get a `WebSocketCommand` for `client.execute`.
2232
+ * Call the returned endpoint with input to get a `WebSocketCommand` for `client.execute`.
2233
+ *
2234
+ * - **definition** — `path`, one `incoming` struct, and the required `maxIncomingQueueSize` (a positive safe integer). Optional `outgoing` and `input` structs, static `protocols`, `maxOutgoingQueueSize` (a non-negative safe integer), and `operation`. A declared `input` always requires an explicit `builder`. `input` only defines the parameters; the builder alone decides where each value goes. Only definitions without `input` may omit `builder`.
2235
+ *
2236
+ * A WebSocket `builder` may return `path` and `query` only; `headers` and `body` are rejected. `WebSocketDefinitionBase` and `BuilderField` are internal helper types.
1041
2237
  *
1042
2238
  * @param definition - WebSocket contract (path, structs, queue limits, optional input).
1043
- * @returns A builder that creates `WebSocketCommand` values from input.
2239
+ * @returns An endpoint that creates `WebSocketCommand` values from input.
1044
2240
  *
1045
- * @example
1046
- * ```ts
1047
- * const useChat = defineWebSocket({
1048
- * maxIncomingQueueSize: 16,
1049
- * path: '/ws/chat',
1050
- * incoming: { message: struct.object({ text: struct.string() }) },
1051
- * outgoing: { message: struct.object({ text: struct.string() }) },
1052
- * })
1053
- * ```
2241
+ * @group web-socket
2242
+ * @exampleFile examples/api/define-web-socket.ts#example
1054
2243
  */
1055
- export declare function defineWebSocket<TInput extends AnyStruct, TIncoming extends SocketStructs = SocketStructs, TOutgoing extends SocketStructs | undefined = undefined>(definition: WebSocketDefinitionWithBuild<TInput, TIncoming, TOutgoing>): WebSocketCommandBuilder<TInput, TIncoming, TOutgoing>;
1056
- export declare function defineWebSocket<TInput extends AnyStruct | undefined = undefined, TIncoming extends SocketStructs = SocketStructs, TOutgoing extends SocketStructs | undefined = undefined>(definition: WebSocketDefinitionWithoutBuild<TInput, TIncoming, TOutgoing>): WebSocketCommandBuilder<TInput, TIncoming, TOutgoing>;
2244
+ export declare function defineWebSocket<const P extends string, TInput extends AnyStruct | undefined = undefined, TIncoming extends AnyStruct = AnyStruct, TOutgoing extends AnyStruct | undefined = undefined>(definition: Omit<WebSocketDefinitionBase<TIncoming, TOutgoing>, "path"> & {
2245
+ input?: TInput;
2246
+ path: P;
2247
+ } & BuilderField<NoInfer<TInput>, WebSocketRequestShape<NoInfer<P>>>): WebSocketEndpoint<TInput, TIncoming, TOutgoing>;
1057
2248
  /**
1058
2249
  * Open a WebSocket command against the given client config.
1059
2250
  *
1060
2251
  * Prefer `client.execute(command)` in application code; this is the low-level entry used by the client.
1061
2252
  *
2253
+ * The result is `[null, session, connection]` or `[fault, undefined, connection | undefined]`, where the fault is `Fault<undefined>`.
2254
+ *
2255
+ * WebSocket execute options can override the client's `beforeConnect`, `heartbeat`, `protocols`, and `reconnect`.
2256
+ *
1062
2257
  * @param clientConfig - Resolved client configuration.
1063
- * @param command - WebSocket command from a `WebSocketCommandBuilder`.
2258
+ * @param command - WebSocket command from a `WebSocketEndpoint`.
1064
2259
  * @param options - Optional execute options (heartbeat, reconnect, abort).
1065
2260
  * @returns Await-result tuple of `[error, socket, connection]`.
2261
+ * @group web-socket
2262
+ */
2263
+ export declare function executeWebSocketCommand<TInput extends AnyStruct | undefined, TIncoming extends AnyStruct, TOutgoing extends AnyStruct | undefined>(clientConfig: ClientConfig, command: WebSocketCommand<TInput, TIncoming, TOutgoing>, options?: WebSocketExecuteOptions<WebSocketIncomingData<TIncoming>, WebSocketOutgoingData<TOutgoing>>): Promise<SocketAwaitResult<WebSocketIncomingData<TIncoming>, WebSocketOutgoingData<TOutgoing>>>;
2264
+ /**
2265
+ * The outcome of a **single connect attempt**, not the final result of the session.
2266
+ *
2267
+ * The reconnect logic uses it to decide whether to try again.
2268
+ *
2269
+ * - `closeInfo` holds the close details of that attempt: `{ cause?, code?, reason?, wasClean? }`.
2270
+ *
2271
+ * - `connection` is the connection snapshot, when the attempt got one.
2272
+ *
2273
+ * - `cause` is the error that ended the attempt, if any.
2274
+ *
2275
+ * - `opened` tells whether the socket reached `open` before it ended.
2276
+ *
2277
+ * The `shouldReconnect` option does not receive this object. It gets `attempt`, `cause`, `code`, `reason`, and `wasClean`, taken from it. The final result of the session is {@link WebSocketCloseInfo}.
2278
+ *
2279
+ * @group web-socket
1066
2280
  */
1067
- export declare function executeWebSocketCommand<TInput extends AnyStruct | undefined, TIncoming extends SocketStructs, TOutgoing extends SocketStructs | undefined>(clientConfig: ClientConfig, command: WebSocketCommand<TInput, TIncoming, TOutgoing>, options?: WebSocketExecuteOptions<WebSocketIncomingData<TIncoming>, WebSocketOutgoingData<TOutgoing>>): Promise<SocketAwaitResult<WebSocketIncomingData<TIncoming>, WebSocketOutgoingData<TOutgoing>>>;
1068
- /** Outcome of a single WebSocket connect attempt used by reconnect logic. */
1069
2281
  export type SocketLifecycleOutcome = {
1070
2282
  closeInfo: WebSocketCloseSnapshot;
1071
2283
  connection?: WebSocketConnectionInfo;
1072
2284
  cause?: unknown;
1073
2285
  opened: boolean;
1074
2286
  };
1075
- //#endregion
1076
- //#region src/interceptor/interceptor.d.ts
1077
2287
  /**
1078
- * Continuation that runs the rest of the HTTP interceptor chain (or the handler).
2288
+ * Continuation that runs the rest of the HTTP interceptor chain, or the handler.
2289
+ *
2290
+ * `next(req)` only goes inward: the interceptors registered after this one, then
2291
+ * the Fetch handler. It is not a re-dispatch of `client.execute` from the
2292
+ * outside of the chain. Call `next` again for a safe
2293
+ * replay (for example one 401 refresh) from the same interceptor; outer layers
2294
+ * do not run a second time. Once the chain has settled, calling `next` rejects
2295
+ * with `HTTP interceptor next() cannot be called after the chain has settled`.
1079
2296
  *
1080
- * `next(req)` only goes **inward** — remaining interceptors registered after this one,
1081
- * then the Fetch handler. It is not a re-dispatch of `client.execute` / `axios(config)`
1082
- * from the outside of the onion. Call `next` again for a safe replay (for example one
1083
- * 401 refresh) from the same interceptor; outer layers do not run a second time.
2297
+ * @group interceptors
1084
2298
  */
1085
2299
  type HttpInterceptorNext = (req: HttpRequest) => Promise<HttpResponse<unknown>>;
1086
2300
  /**
1087
- * HTTP interceptor function: may inspect/modify the request, then call `next`.
2301
+ * HTTP interceptor function: may inspect or modify the request, then call `next`.
2302
+ *
2303
+ * Call `next(req)` or short-circuit with `makeResponse`. Wrap it with
2304
+ * `createHttpInterceptor` to register it.
2305
+ *
2306
+ * @group interceptors
1088
2307
  */
1089
2308
  type InterceptorFn = (req: HttpRequest, next: HttpInterceptorNext) => Promise<HttpResponse<unknown>>;
1090
2309
  /**
1091
- * Tagged HTTP interceptor for client option lists.
2310
+ * Tagged HTTP interceptor accepted by client interceptor options.
2311
+ *
2312
+ * Created by `createHttpInterceptor`. `kind` is the discriminant that lets
2313
+ * `withInterceptors` accept HTTP, SSE, and WebSocket interceptors in one list.
2314
+ *
2315
+ * @group interceptors
1092
2316
  */
1093
2317
  interface HttpInterceptor {
1094
- kind: 'http';
2318
+ kind: "http";
1095
2319
  fn: InterceptorFn;
1096
2320
  }
1097
2321
  /**
1098
- * Wrap an `InterceptorFn` as a tagged `HttpInterceptor`.
2322
+ * Wraps an `InterceptorFn` as a tagged `HttpInterceptor`.
2323
+ *
2324
+ * Returns `{ kind: 'http', fn }`. Call `next(req)` (optionally with a modified
2325
+ * request) or short-circuit with `makeResponse`. `next` runs only the
2326
+ * interceptors registered after this one and then the fetch handler. HTTP
2327
+ * interceptors do not run for SSE or WebSocket commands.
2328
+ *
2329
+ * A deterministic URL error from `next(req)` rejects the returned promise, so
2330
+ * `.then` or `.catch` recovery runs; if it is not recovered, `client.execute`
2331
+ * still reports `REQ_BUILD_FAILED`.
1099
2332
  *
1100
2333
  * @param fn - HTTP interceptor function.
1101
2334
  * @returns An `HttpInterceptor` with `kind: 'http'`.
2335
+ * @group interceptors
1102
2336
  */
1103
2337
  export declare function createHttpInterceptor(fn: InterceptorFn): HttpInterceptor;
1104
2338
  /**
1105
- * Terminal SSE handler that opens an event stream for the given request.
2339
+ * Terminal `next` function an SSE interceptor receives; it opens an event stream for the request.
2340
+ *
2341
+ * `next(req)` goes inward only: through the interceptors registered after this
2342
+ * one, then the transport that opens the stream. Calling it after the chain has
2343
+ * settled rejects.
2344
+ *
2345
+ * `EventStreamHandle` extends `AsyncDisposable`. A short-circuit implementation
2346
+ * that builds its own handle must provide
2347
+ * `[Symbol.asyncDispose](): PromiseLike<void>` and connect it to the same close
2348
+ * lifecycle.
2349
+ *
2350
+ * @group interceptors
1106
2351
  */
1107
2352
  type SSEHandler = (req: HttpRequest) => Promise<EventStreamHandle<unknown>>;
1108
2353
  /**
1109
- * SSE interceptor function: may inspect/modify the request, then call `next`.
2354
+ * SSE interceptor function: may inspect, modify, or replace the request, then call `next`.
2355
+ *
2356
+ * Call `next` (an {@link SSEHandler}) to continue the chain, or return your own
2357
+ * `EventStreamHandle` to short-circuit. Wrap it with `createSSEInterceptor` to
2358
+ * register it through `withInterceptors`.
2359
+ *
2360
+ * A short-circuit handle must provide
2361
+ * `[Symbol.asyncDispose](): PromiseLike<void>` and connect it to the same close
2362
+ * lifecycle.
2363
+ *
2364
+ * @group interceptors
1110
2365
  */
1111
2366
  type SSEInterceptorFn = (req: HttpRequest, next: SSEHandler) => Promise<EventStreamHandle<unknown>>;
1112
2367
  /**
1113
- * Tagged SSE interceptor for client option lists.
2368
+ * Tagged SSE interceptor for client option lists, accepted by `withInterceptors`.
2369
+ *
2370
+ * Created by `createSSEInterceptor`. The `kind` tag lets `withInterceptors`
2371
+ * accept HTTP, SSE, and WebSocket interceptors in one list; only `kind: 'sse'`
2372
+ * interceptors run for SSE commands.
2373
+ *
2374
+ * @group interceptors
1114
2375
  */
1115
2376
  interface SSEInterceptor {
1116
- kind: 'sse';
2377
+ kind: "sse";
1117
2378
  fn: SSEInterceptorFn;
1118
2379
  }
1119
2380
  /**
1120
- * Wrap an `SSEInterceptorFn` as a tagged `SSEInterceptor`.
2381
+ * Wraps an `SSEInterceptorFn` as a tagged `SSEInterceptor`.
2382
+ *
2383
+ * Returns `{ kind: 'sse', fn }`. SSE interceptors run only for SSE commands, in
2384
+ * registration order; `next(req)` opens the stream through the remaining
2385
+ * interceptors.
2386
+ *
2387
+ * A deterministic URL error from `next(req)` rejects the returned promise, so
2388
+ * `.then` or `.catch` recovery runs; if it is not recovered, `client.execute`
2389
+ * still reports `REQ_BUILD_FAILED`.
2390
+ *
2391
+ * `EventStreamHandle` extends `AsyncDisposable`. A structural short-circuit
2392
+ * implementation must provide `[Symbol.asyncDispose](): PromiseLike<void>` and
2393
+ * connect it to the same close lifecycle.
1121
2394
  *
1122
2395
  * @param fn - SSE interceptor function.
1123
2396
  * @returns An `SSEInterceptor` with `kind: 'sse'`.
2397
+ * @group interceptors
1124
2398
  */
1125
2399
  export declare function createSSEInterceptor(fn: SSEInterceptorFn): SSEInterceptor;
1126
2400
  /**
1127
- * Minimal WebSocket session surface used by interceptors.
2401
+ * Minimal WebSocket session surface that interceptors see.
2402
+ *
2403
+ * Structurally compatible with {@link WebSocketSession} without importing that
2404
+ * type, which avoids a circular dependency between interceptors and WebSocket.
2405
+ * Messages are typed `unknown`.
1128
2406
  *
1129
- * Structurally compatible with `WebSocketSession` without importing that type
1130
- * (avoids a circular dependency between interceptors and WebSocket).
2407
+ * A wrapper or short-circuit session must forward the same
2408
+ * `[Symbol.asyncDispose]()` teardown as its `close` / `closed` lifecycle;
2409
+ * returning an unrelated resolved promise is not sufficient. Requiring this
2410
+ * member is a compile-time breaking change for structural implementations. Code
2411
+ * that only accepts sessions returned by Defjs has no additional runtime call
2412
+ * requirement.
2413
+ *
2414
+ * @group interceptors
1131
2415
  */
1132
2416
  interface WebSocketSessionLike extends AsyncDisposable {
1133
2417
  readonly bufferedAmount: number;
@@ -1147,64 +2431,128 @@ interface WebSocketSessionLike extends AsyncDisposable {
1147
2431
  send(message: unknown): void;
1148
2432
  }
1149
2433
  /**
1150
- * Terminal WebSocket handler that opens a session for the given request.
2434
+ * Terminal `next` function a WebSocket interceptor receives; it opens a session for the request.
2435
+ *
2436
+ * `next(req)` goes inward only: through the interceptors registered after this
2437
+ * one, then the transport that opens the session. It resolves to a
2438
+ * {@link WebSocketSessionLike}. It may be called once per execute; a second
2439
+ * call, or a call after the chain has settled, rejects.
2440
+ *
2441
+ * @group interceptors
1151
2442
  */
1152
2443
  type WebSocketHandler = (req: HttpRequest) => Promise<WebSocketSessionLike>;
1153
2444
  /**
1154
- * WebSocket interceptor function: may inspect/modify the request, then call `next`.
2445
+ * WebSocket interceptor function: may inspect, modify, or replace the request, then call `next`.
2446
+ *
2447
+ * Call `next` (a {@link WebSocketHandler}) to continue the chain, or return your own
2448
+ * {@link WebSocketSessionLike} to short-circuit. Wrap it with
2449
+ * `createWebSocketInterceptor` to register it through `withInterceptors`.
2450
+ *
2451
+ * The chain runs once per `execute`. Reconnects happen inside the session it
2452
+ * returns and do not run it again.
2453
+ *
2454
+ * @group interceptors
1155
2455
  */
1156
2456
  type WebSocketInterceptorFn = (req: HttpRequest, next: WebSocketHandler) => Promise<WebSocketSessionLike>;
1157
2457
  /**
1158
- * Tagged WebSocket interceptor for client option lists.
2458
+ * Tagged WebSocket interceptor for client option lists, accepted by `withInterceptors`.
2459
+ *
2460
+ * Created by `createWebSocketInterceptor`. The `kind` tag lets
2461
+ * `withInterceptors` accept HTTP, SSE, and WebSocket interceptors in one list;
2462
+ * only `kind: 'web-socket'` interceptors run for WebSocket commands.
2463
+ *
2464
+ * @group interceptors
1159
2465
  */
1160
2466
  interface WebSocketInterceptor {
1161
- kind: 'web-socket';
2467
+ kind: "web-socket";
1162
2468
  fn: WebSocketInterceptorFn;
1163
2469
  }
1164
2470
  /**
1165
- * Wrap a `WebSocketInterceptorFn` as a tagged `WebSocketInterceptor`.
2471
+ * Wraps a `WebSocketInterceptorFn` as a tagged `WebSocketInterceptor`.
2472
+ *
2473
+ * Returns `{ kind: 'web-socket', fn }`. WebSocket interceptors run only for
2474
+ * WebSocket commands. `next` and the interceptor return a
2475
+ * {@link WebSocketSessionLike}, the interceptor-facing session. A wrapper or
2476
+ * short-circuit session must forward its `[Symbol.asyncDispose]()` teardown.
1166
2477
  *
1167
2478
  * @param fn - WebSocket interceptor function.
1168
2479
  * @returns A `WebSocketInterceptor` with `kind: 'web-socket'`.
2480
+ * @group interceptors
1169
2481
  */
1170
2482
  export declare function createWebSocketInterceptor(fn: WebSocketInterceptorFn): WebSocketInterceptor;
1171
2483
  /**
1172
2484
  * Any tagged interceptor accepted by client interceptor options.
2485
+ *
2486
+ * `kind` (`'http'`, `'sse'`, or `'web-socket'`) selects which commands an
2487
+ * interceptor runs for. `withInterceptors` accepts any mix; each transport runs
2488
+ * only its own kind, in registration order.
2489
+ *
2490
+ * @group interceptors
1173
2491
  */
1174
2492
  type Interceptor = HttpInterceptor | SSEInterceptor | WebSocketInterceptor;
1175
- //#endregion
1176
- //#region src/client/config.d.ts
1177
2493
  /**
1178
- * Serializes query parameters for the request URL.
2494
+ * Serializes an encoded query object into a query string.
2495
+ *
2496
+ * `query` is the wire object: aliases already applied, `Date` values already ISO strings, `bigint` values already strings. Nested objects and arrays are preserved. The string is used verbatim and is not passed through `URLSearchParams`. Interceptors see `queryParams` as `new URLSearchParams(queryString)`.
1179
2497
  *
1180
- * @param params - Normalized `URLSearchParams` built from the request.
1181
- * @param rawParams - Optional original param object before normalization.
1182
- * @returns The query string (without a leading `?`).
2498
+ * Set it with `withQuerySerializer`. Without one, queries use `URLSearchParams`: arrays repeat keys and a nested object fails. A builder that returns `URLSearchParams` skips the serializer.
2499
+ *
2500
+ * @param query - Encoded query object.
2501
+ * @returns The query string without a leading `?`.
2502
+ * @group client
1183
2503
  */
1184
- type QueryParamsSerializer = (params: URLSearchParams, rawParams?: {
2504
+ type QuerySerializer = (query: {
1185
2505
  [key: string]: unknown;
1186
2506
  }) => string;
1187
2507
  /**
1188
2508
  * Fetch-compatible function used as an HTTP or SSE handle.
1189
2509
  *
1190
- * This is the `(input, init?) => Promise<Response>` call signature Defjs invokes,
1191
- * not `typeof fetch`, so mocks do not need host extras such as `preconnect`.
2510
+ * This is the `(input, init?) => Promise<Response>` call signature Defjs invokes, deliberately not `typeof fetch`, so a mock does not need host extras such as `preconnect`. HTTP calls the untouched platform Fetch as `(url, init)`; a replaced global Fetch or custom handle receives a `Request`. The default resolves the global on each call. SSE always passes a `Request`, including to its default handle.
2511
+ *
2512
+ * Accepted by `withHTTPHandle`, `withSSEHandle`, and `fetchHandler`.
2513
+ *
2514
+ * @group client
1192
2515
  */
1193
2516
  type FetchHandle = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
1194
2517
  /**
1195
- * Minimal WebSocket-like handle used by the client transport.
2518
+ * Minimal WebSocket instance surface used by the client transport.
2519
+ *
2520
+ * It is the DOM `WebSocket` without its event-handler properties, `binaryType`, `dispatchEvent`, and the state constants. A custom implementation therefore needs `url`, `protocol`, `extensions`, `bufferedAmount`, `send`, `close`, `addEventListener`, `removeEventListener`, and a numeric `readyState`.
1196
2521
  *
1197
- * Omits browser event-handler properties so custom implementations only need
1198
- * the send/close/state surface Defjs relies on.
2522
+ * The transport sets `binaryType = 'arraybuffer'` when the property exists and ignores its absence. If the setter throws, it requests best-effort closure of the constructed handle before failing that attempt with `NET_UNREACHABLE`; the socket is not left outside cleanup. This property is an optimization, and the decoder also accepts Blob frames. This is the instance type produced by a {@link WebSocketHandleConstructor}.
2523
+ *
2524
+ * @group client
1199
2525
  */
1200
- type WebSocketHandle = Omit<WebSocket, 'binaryType' | 'CLOSED' | 'CLOSING' | 'CONNECTING' | 'dispatchEvent' | 'onclose' | 'onerror' | 'onmessage' | 'onopen' | 'OPEN' | 'readyState'> & {
2526
+ type WebSocketHandle = Omit<WebSocket, "binaryType" | "CLOSED" | "CLOSING" | "CONNECTING" | "dispatchEvent" | "onclose" | "onerror" | "onmessage" | "onopen" | "OPEN" | "readyState"> & {
1201
2527
  readonly readyState: number;
1202
2528
  };
1203
- /** Constructor for a `WebSocketHandle`, including the `OPEN` ready-state constant. */
2529
+ /**
2530
+ * Constructor type accepted by `withWebSocketHandle`.
2531
+ *
2532
+ * A class or function that builds a {@link WebSocketHandle} and exposes the numeric `OPEN` ready-state constant. The transport compares `readyState` with `OPEN`.
2533
+ *
2534
+ * It is called as `new WebSocketImpl(url)`, or `new WebSocketImpl(url, protocols)` when subprotocols are set. The default is `globalThis.WebSocket`.
2535
+ *
2536
+ * @group client
2537
+ */
1204
2538
  type WebSocketHandleConstructor = {
1205
2539
  readonly OPEN: number;
1206
2540
  } & (new (...args: never[]) => WebSocketHandle);
1207
- /** Optional WebSocket settings applied when creating a client. */
2541
+ /**
2542
+ * Optional WebSocket settings applied when creating a client: constructor, `beforeConnect` hook, heartbeat, protocols, and reconnect policy.
2543
+ *
2544
+ * - **handle** — WebSocket constructor; defaults to `globalThis.WebSocket`.
2545
+ * - **beforeConnect** — Runs before each connect and reconnect attempt.
2546
+ * - **heartbeat** — Sends `message()` every `intervalMs`. With `timeoutMs` and `isAck`, a missing acknowledgement marks the connection unhealthy. `intervalMs` and `timeoutMs` must be positive and at most 2147483647.
2547
+ * - **protocols** — Subprotocols passed to the constructor.
2548
+ * - **reconnect** — Opt-in. `attempts` defaults to **3**; `0` disables retry, and omitting `attempts` is not unlimited. Other defaults: `delayMs` 1000, `factor` 2, `jitter` 0 (a 0-1 multiplicative factor), `maxDelayMs` 30000. `shouldReconnect` receives the close `code`, `reason`, and `wasClean` and can veto a retry.
2549
+ *
2550
+ * The retry budget applies per outage. Successful `open` resets the budget, callback attempt, and backoff exponent; the next outage starts at retry 1. `maxDelayMs` caps the final delay after jitter and rounding, also bounded by `2_147_483_647` ms. See {@link withWebSocketReconnect} for validation rules and overflow behavior.
2551
+ *
2552
+ * Set these with `withWebSocketHandle`, `withWebSocketBeforeConnect`, `withWebSocketHeartbeat`, `withWebSocketProtocols`, and `withWebSocketReconnect`. `WebSocketExecuteOptions` can override `beforeConnect`, `heartbeat`, `protocols`, and `reconnect` per call.
2553
+ *
2554
+ * @group client
2555
+ */
1208
2556
  interface ClientWebSocketOptions {
1209
2557
  /** WebSocket constructor; defaults to `globalThis.WebSocket`. */
1210
2558
  handle?: WebSocketHandleConstructor;
@@ -1228,15 +2576,15 @@ interface ClientWebSocketOptions {
1228
2576
  protocols?: readonly string[];
1229
2577
  /** Automatic reconnect policy after unexpected closes. */
1230
2578
  reconnect?: {
1231
- /** Retry budget after the first connect. Defaults to 3 when `reconnect` is set. `0` disables. Omitting this field is not unlimited. */
2579
+ /** Retries per outage; a successful open resets the budget. Non-negative safe integer; defaults to 3. `0` disables. */
1232
2580
  attempts?: number;
1233
2581
  /** Initial backoff delay in milliseconds. */
1234
2582
  delayMs?: number;
1235
- /** Multiplier applied to the delay after each failed attempt. */
2583
+ /** Multiplier applied after each failed attempt of the current outage. A successful `open` resets the exponent. */
1236
2584
  factor?: number;
1237
2585
  /** Random jitter factor applied to the delay (0–1). */
1238
2586
  jitter?: number;
1239
- /** Upper bound for computed backoff delay. */
2587
+ /** Non-negative cap on the final backoff delay, including jitter; `Infinity` removes the configured cap. */
1240
2588
  maxDelayMs?: number;
1241
2589
  /** Return `false` to skip reconnecting for this close. */
1242
2590
  shouldReconnect?: (context: {
@@ -1247,22 +2595,26 @@ interface ClientWebSocketOptions {
1247
2595
  wasClean?: boolean;
1248
2596
  }) => boolean;
1249
2597
  };
1250
- /** Called when an incoming message has no matching struct (unknown `type`). Struct/JSON failures still use `onRuntimeError`. */
1251
- onInvalidEvent?: (context: {
1252
- reason: 'missing-struct';
1253
- message: {
1254
- type: string;
1255
- data: unknown;
1256
- };
1257
- }) => unknown;
1258
2598
  }
1259
- /** Optional SSE settings applied when creating a client. */
2599
+ /**
2600
+ * Optional SSE settings applied when creating a client: fetch handle, invalid-event hook, and reconnect policy.
2601
+ *
2602
+ * - **handle** — Fetch implementation that opens the stream. The default resolves `globalThis.fetch` on each attempt, including replacements installed after import. Every SSE handle receives a `Request`. Missing global Fetch fails default execution with `ENV_UNSUPPORTED`; a custom handle remains usable.
2603
+ * - **onInvalidEvent** — Called when an event has no matching struct and no `default` struct (`'missing-struct'`), or its data fails validation (`'validation-failed'`). The event is skipped. An error thrown by the hook does not close the stream.
2604
+ * - **reconnect** — Opt-in. Without it the stream never reconnects. `attempts` defaults to **3** per outage; `attempts: 0` disables retry. A successful handshake resets the budget, callback `attempt`, and backoff exponent. `delayMs` is the first base delay, 1000 by default, and a server-sent `retry:` value replaces it. `factor` defaults to 1. `jitter` is a 0–1 multiplicative factor and defaults to 0. `maxDelayMs` caps the final delay after jitter and rounding; without a configured cap, the platform limit of `2_147_483_647` ms still applies. A zero base stays zero; valid-number overflow saturates at the caps. Without `shouldReconnect` only `GET`, `HEAD`, `OPTIONS`, `PUT`, and `DELETE` reconnect; with it, its result decides for every method and its context carries `method`. A `ReadableStream` request body never reconnects. Reconnect sends the latest ID as UTF-8 bytes in `Last-Event-ID`; an empty ID clears it and an ID containing NUL is ignored.
2605
+ *
2606
+ * Reconnect options are validated before interceptors or Fetch: `attempts` is a non-negative safe integer; `delayMs` must be finite and non-negative; `maxDelayMs` must be a non-negative finite number or positive `Infinity`, which removes the configured cap but not the platform limit of `2_147_483_647` ms; `factor` is finite and positive; `jitter` is finite and within 0–1. Invalid values return `REQ_OPTIONS_INVALID` at execute time.
2607
+ *
2608
+ * Set these with `withSSEHandle`, `withSSEOnInvalidEvent`, and `withSSEReconnect`. Omitting `withSSEReconnect` disables retry.
2609
+ *
2610
+ * @group client
2611
+ */
1260
2612
  interface ClientSSEOptions {
1261
- /** Fetch implementation used to open the event stream. */
2613
+ /** Fetch implementation used to open the event stream; defaults to resolving global `fetch` on each call. */
1262
2614
  handle?: FetchHandle;
1263
2615
  /** Called when an event has no matching struct or fails validation. */
1264
2616
  onInvalidEvent?: (context: {
1265
- reason: 'missing-struct' | 'validation-failed';
2617
+ reason: "missing-struct" | "validation-failed";
1266
2618
  message: {
1267
2619
  id: string;
1268
2620
  event: string;
@@ -1273,21 +2625,25 @@ interface ClientSSEOptions {
1273
2625
  }) => unknown;
1274
2626
  /** Automatic reconnect policy after stream failures. */
1275
2627
  reconnect?: {
1276
- /** Retry budget after the first attempt. Defaults to 3 when `reconnect` is set. `0` disables. Omitting this field is not unlimited. */
2628
+ /** Retries per outage; a successful handshake resets the budget. Non-negative safe integer; defaults to 3. `0` disables. */
1277
2629
  attempts?: number;
1278
- /** Initial backoff delay in milliseconds. */
2630
+ /** Initial backoff delay in milliseconds, finite and non-negative. Defaults to 1000. */
1279
2631
  delayMs?: number;
1280
- /** Multiplier applied to the delay after each failed attempt. */
2632
+ /** Finite positive multiplier per failed retry, reset after a successful handshake. Defaults to 1. */
1281
2633
  factor?: number;
1282
- /** Random jitter factor applied to the delay (0–1). */
2634
+ /** Finite random jitter factor applied to the delay (0–1). Defaults to 0. */
1283
2635
  jitter?: number;
1284
- /** Upper bound for computed backoff delay. */
2636
+ /** Non-negative cap on the final delay, including jitter; no configured cap when omitted or `Infinity`. */
1285
2637
  maxDelayMs?: number;
1286
- /** Return `false` to skip reconnecting for this failure. */
2638
+ /**
2639
+ * Without this callback only `GET`, `HEAD`, `OPTIONS`, `PUT` and `DELETE` reconnect. With it, the result decides
2640
+ * for every method. It is never called for a `ReadableStream` body.
2641
+ */
1287
2642
  shouldReconnect?: (context: {
1288
2643
  attempt: number;
1289
2644
  cause?: unknown;
1290
2645
  lastEventId: string;
2646
+ method: string;
1291
2647
  open?: {
1292
2648
  response: {
1293
2649
  status: number;
@@ -1299,7 +2655,13 @@ interface ClientSSEOptions {
1299
2655
  }) => boolean | Promise<boolean>;
1300
2656
  };
1301
2657
  }
1302
- /** Resolved SSE config with a required `handle`. */
2658
+ /**
2659
+ * Resolved SSE configuration with a required fetch handle.
2660
+ *
2661
+ * The type of `ClientConfig.sse`. It is {@link ClientSSEOptions} with `handle` always present; the default wrapper resolves the current global `fetch` on each attempt, rather than capturing it at import time.
2662
+ *
2663
+ * @group client
2664
+ */
1303
2665
  interface ClientSSEConfig extends ClientSSEOptions {
1304
2666
  /** Fetch implementation used to open the event stream. */
1305
2667
  handle: FetchHandle;
@@ -1310,14 +2672,14 @@ interface HttpClientOptions {
1310
2672
  endpoint: string;
1311
2673
  /** Static default headers merged under command headers for HTTP/SSE. */
1312
2674
  headers?: Headers;
1313
- /** HTTP transport overrides. */
2675
+ /** HTTP transport overrides; the default resolves global `fetch` on each call. */
1314
2676
  http?: {
1315
2677
  handle?: FetchHandle;
1316
2678
  };
1317
2679
  /** Interceptors applied in registration order. */
1318
2680
  interceptors?: Interceptor[];
1319
- /** Custom query-string serializer. */
1320
- queryParamsSerializer?: QueryParamsSerializer;
2681
+ /** Serializes encoded query objects. Omit to use `URLSearchParams`. */
2682
+ querySerializer?: QuerySerializer;
1321
2683
  /** Default HTTP execute timeout in milliseconds (SSE/WebSocket ignore this). */
1322
2684
  timeout?: number;
1323
2685
  /** XSRF cookie/header mapping and optional token provider. */
@@ -1334,7 +2696,13 @@ interface HttpClientOptions {
1334
2696
  /** When `true`, credentials are included with requests. */
1335
2697
  withCredentials?: boolean;
1336
2698
  }
1337
- /** User-facing options for `createClient`, including SSE and WebSocket. */
2699
+ /**
2700
+ * Input-shaped client options, including SSE and WebSocket, that mirror what the option helpers configure.
2701
+ *
2702
+ * The user-facing shape of the settings before defaults are applied. `createClient` does not accept this object; configure a client with option helpers such as `withEndpoint` and `withTimeout`. {@link ClientConfig} is the resolved counterpart. The HTTP members come from the non-exported `HttpClientOptions` base and are shown inline.
2703
+ *
2704
+ * @group client
2705
+ */
1338
2706
  interface ClientOptions extends HttpClientOptions {
1339
2707
  /** SSE transport and reconnect options. */
1340
2708
  sse?: ClientSSEOptions;
@@ -1353,8 +2721,8 @@ interface HttpClientConfig {
1353
2721
  };
1354
2722
  /** Interceptors applied in registration order. */
1355
2723
  interceptors: Interceptor[];
1356
- /** Query-string serializer used for requests. */
1357
- queryParamsSerializer: QueryParamsSerializer;
2724
+ /** Serializes encoded query objects. Omit to use `URLSearchParams`. */
2725
+ querySerializer?: QuerySerializer;
1358
2726
  /** Default HTTP execute timeout in milliseconds (SSE/WebSocket ignore this). */
1359
2727
  timeout?: number;
1360
2728
  /** Resolved XSRF settings when enabled. */
@@ -1371,209 +2739,376 @@ interface HttpClientConfig {
1371
2739
  /** When `true`, credentials are included with requests. */
1372
2740
  withCredentials?: boolean;
1373
2741
  }
1374
- /** Resolved client config held by instances from `createClient`. */
2742
+ /**
2743
+ * Resolved client configuration after option helpers have been applied.
2744
+ *
2745
+ * The snapshot that `createClient` builds, that every option helper mutates, and that instances from `createClient` hold. The HTTP members come from a non-exported `HttpClientConfig` base and are shown inline.
2746
+ *
2747
+ * Before any option applies, `endpoint` is `''`, `interceptors` is empty, and `http.handle` and `sse.handle` share a default wrapper that resolves `globalThis.fetch` on each call. Replacing Fetch after import affects both defaults. Core can be imported without Fetch; a default HTTP/SSE execution fails with `ENV_UNSUPPORTED` if it is still missing. Custom handles do not require a global Fetch. `webSocket.handle` is `globalThis.WebSocket`, and the other `webSocket` settings are unset. `withXSRF` fills `xsrf.cookieName` with `XSRF-TOKEN` and `xsrf.headerName` with `X-XSRF-TOKEN` when they are omitted.
2748
+ *
2749
+ * @group client
2750
+ */
1375
2751
  interface ClientConfig extends HttpClientConfig {
1376
2752
  /** Resolved SSE configuration. */
1377
2753
  sse: ClientSSEConfig;
1378
2754
  /** Resolved WebSocket configuration. */
1379
2755
  webSocket: ClientWebSocketOptions;
1380
2756
  }
1381
- //#endregion
1382
- //#region src/client/option.d.ts
1383
- /** Mutates `ClientConfig` when applied by `createClient`. */
2757
+ /**
2758
+ * Function type of the option helpers accepted by `createClient`.
2759
+ *
2760
+ * Mutates the {@link ClientConfig} that `createClient` is building. Every `with*` helper returns one. `createClient` applies the options in order, so a later option overwrites an earlier one for the same setting, except `withInterceptors`, which appends.
2761
+ *
2762
+ * @group client
2763
+ * @see {@link ClientConfig}
2764
+ */
1384
2765
  type ClientOption = (config: ClientConfig) => void;
1385
2766
  /**
1386
- * Sets the base URL prepended to relative request paths.
2767
+ * Sets the valid absolute base URL prepended to relative request paths.
2768
+ *
2769
+ * The default is an empty string, so execution requires an explicit endpoint. Its pathname is kept as a directory; its query and hash are discarded. For WebSocket commands, an `http:` base becomes `ws:` and `https:` becomes `wss:`.
2770
+ *
2771
+ * A missing, relative, or invalid endpoint, or an illegal request path, fails with `REQ_BUILD_FAILED` during building, before any interceptor or transport runs, even when an interceptor would short-circuit. HTTP, SSE, and WebSocket share this rule. The resolved request must keep the base origin and stay under its directory; C0 control characters (including tab, CR, and LF), backslashes, `?`, `#`, and directory escapes in a path are rejected. Scheme-like and protocol-relative forms are rejected even with leading spaces; a plain inner space is allowed and sent as `%20`. An interceptor's final `req.endpoint` is checked against its final `req.baseEndpoint` with the same rule, but it may deliberately replace the base with another valid absolute URL. Deterministic URL errors are never retried.
1387
2772
  *
1388
- * @param endpoint - Absolute or relative base endpoint.
2773
+ * @param endpoint - Absolute base endpoint. Missing or invalid endpoints fail request building.
1389
2774
  * @returns An option that writes `config.endpoint`.
2775
+ * @group client
1390
2776
  */
1391
2777
  export declare function withEndpoint(endpoint: string): ClientOption;
1392
2778
  /**
1393
- * Sets static default headers for HTTP/SSE requests built by this client.
2779
+ * Sets static default headers for HTTP and SSE requests built by this client.
1394
2780
  *
1395
- * Command `struct.request({ headers })` values override the same names. HMAC and
1396
- * dynamic Bearer tokens still belong in interceptors (they need the serialized body
1397
- * or a refreshed credential).
2781
+ * WebSocket connections do not use them. The `HeadersInit` is copied into a `Headers` object when the option is applied. A later call replaces the earlier headers instead of merging.
2782
+ *
2783
+ * Headers returned by a request builder replace a default of the same name. The first value for that name replaces the default; later values in the same array append only to that replacement. A scalar or a `Headers` instance also replaces. Other default names stay.
2784
+ *
2785
+ * Per-request or computed credentials, such as a refreshed Bearer token or an HMAC signature, belong in an interceptor, because they need the serialized body or a refreshed credential.
1398
2786
  *
1399
2787
  * @param headers - `HeadersInit` bag cloned onto the client config.
1400
2788
  * @returns An option that writes `config.headers`.
2789
+ * @group client
1401
2790
  */
1402
2791
  export declare function withHeaders(headers: HeadersInit): ClientOption;
1403
2792
  /**
1404
- * Sets the default timeout for HTTP `execute` only.
2793
+ * Sets the default timeout, in milliseconds, for HTTP `execute` only.
1405
2794
  *
1406
- * Timeout is applied before the interceptor chain (`mergeAbortSignals`). Execute
1407
- * `{ timeout }` overrides this default. Execute `{ abort }` combines with the
1408
- * default (abort → `ABORTED`, timer → `TIMEOUT`). SSE and WebSocket ignore this
1409
- * option. XOR still rejects execute options that pass both `abort` and `timeout`.
2795
+ * SSE and WebSocket ignore it. `ms` must be a positive safe integer no greater than `2_147_483_647`. Anything else throws `RangeError` immediately, when `withTimeout` is called, not when the client executes.
2796
+ *
2797
+ * The timer starts before the interceptor chain runs. When it fires, the call returns a `NET_TIMEOUT` fault. Execute `{ timeout }` replaces this default for that call. Execute `{ abort }` combines with the default: an abort gives `NET_ABORTED`, the timer gives `NET_TIMEOUT`. Passing both `abort` and `timeout` to the same `execute` call is still rejected.
1410
2798
  *
1411
2799
  * @param ms - Positive safe integer timeout in milliseconds.
1412
2800
  * @returns An option that writes `config.timeout`.
2801
+ * @group client
1413
2802
  */
1414
2803
  export declare function withTimeout(ms: number): ClientOption;
1415
2804
  /**
1416
2805
  * Appends interceptors to the client interceptor chain.
1417
2806
  *
2807
+ * Calling it again appends more; it never replaces earlier ones. The first registered interceptor is the outermost layer. HTTP, SSE, and WebSocket interceptors can be mixed in one call. At execute time only the interceptors of the command's transport run, in their registration order.
2808
+ *
1418
2809
  * @param interceptors - Interceptors applied in registration order.
1419
2810
  * @returns An option that pushes onto `config.interceptors`.
2811
+ * @group client
1420
2812
  */
1421
2813
  export declare function withInterceptors(...interceptors: Interceptor[]): ClientOption;
1422
2814
  /**
1423
- * Replaces the serializer used when encoding query parameters.
2815
+ * Replaces query encoding for HTTP, SSE, and WebSocket requests.
1424
2816
  *
1425
- * @param serializer - Function that turns params into a query string.
1426
- * @returns An option that sets `config.queryParamsSerializer`.
2817
+ * `serializer(query)` receives the encoded wire object: aliases applied, `Date` as an ISO string, `bigint` as a string. Nested objects and arrays are kept as they are. It must return the query string without a leading `?`. The string is used verbatim.
2818
+ *
2819
+ * It is not called when the request has no query. A builder that returns `URLSearchParams` skips it. Without this option, queries use `URLSearchParams`: arrays repeat keys and a nested object fails.
2820
+ *
2821
+ * @param serializer - Function that turns an encoded query object into a string.
2822
+ * @returns An option that sets `config.querySerializer`.
2823
+ * @group client
1427
2824
  */
1428
- export declare function withQueryParamsSerializer(serializer: QueryParamsSerializer): ClientOption;
2825
+ export declare function withQuerySerializer(serializer: QuerySerializer): ClientOption;
1429
2826
  /**
1430
- * Overrides the Fetch-compatible function used for HTTP requests.
2827
+ * Replaces the Fetch-compatible function used for HTTP requests.
2828
+ *
2829
+ * The default is the platform `fetch`. `fetchImpl` is a {@link FetchHandle}, so a test double does not need host extras such as `fetch.preconnect`. It only affects HTTP. SSE uses {@link withSSEHandle}.
1431
2830
  *
1432
2831
  * @param fetchImpl - `(input, init?) => Promise<Response>`. Not `typeof fetch`, so
1433
2832
  * a mock does not need host extras such as `preconnect`.
1434
2833
  * @returns An option that sets `config.http.handle`.
2834
+ * @group client
1435
2835
  */
1436
2836
  export declare function withHTTPHandle(fetchImpl: FetchHandle): ClientOption;
1437
2837
  /**
1438
- * Overrides the Fetch-compatible function used for SSE connections.
2838
+ * Replaces the Fetch-compatible function used to open SSE event streams.
2839
+ *
2840
+ * The default is the platform `fetch`. `fetchImpl` is a {@link FetchHandle}. It only affects SSE. HTTP uses {@link withHTTPHandle}.
1439
2841
  *
1440
2842
  * @param fetchImpl - `(input, init?) => Promise<Response>`. Not `typeof fetch`, so
1441
2843
  * a mock does not need host extras such as `preconnect`.
1442
2844
  * @returns An option that sets `config.sse.handle`.
2845
+ * @group client
1443
2846
  */
1444
2847
  export declare function withSSEHandle(fetchImpl: FetchHandle): ClientOption;
1445
2848
  /**
1446
- * Overrides the WebSocket constructor used for socket connections.
2849
+ * Replaces the WebSocket constructor used for socket connections.
2850
+ *
2851
+ * The default is `globalThis.WebSocket`. `WebSocketImpl` is a {@link WebSocketHandleConstructor} that returns a {@link WebSocketHandle}. If no constructor is available, executing a WebSocket command fails with an `ENV_UNSUPPORTED` fault.
1447
2852
  *
1448
2853
  * @param WebSocketImpl - Constructor that returns a `WebSocketHandle`.
1449
2854
  * @returns An option that sets `config.webSocket.handle`.
2855
+ * @group client
1450
2856
  */
1451
2857
  export declare function withWebSocketHandle(WebSocketImpl: WebSocketHandleConstructor): ClientOption;
1452
2858
  /**
1453
- * Runs logic before each WebSocket connect or reconnect attempt.
2859
+ * Runs a hook before the initial WebSocket connect and before every reconnect.
2860
+ *
2861
+ * The hook type is:
2862
+ *
2863
+ * ```ts
2864
+ * (context: { attempt: number; signal: AbortSignal }) => void | Promise<void>
2865
+ * ```
2866
+ *
2867
+ * - `attempt` is `0` for the first connect and grows by one for each reconnect.
2868
+ * - Pass `signal` into token refreshes and other async work so cancelling the session stops them.
2869
+ * - If the hook throws, the attempt fails with an `EXT_HOOK_FAILED` fault.
2870
+ * - A `beforeConnect` in the execute options replaces this one for that call.
1454
2871
  *
1455
2872
  * @param beforeConnect - Hook receiving attempt index and abort signal.
1456
2873
  * @returns An option that sets `config.webSocket.beforeConnect`.
2874
+ * @group client
1457
2875
  */
1458
- export declare function withWebSocketBeforeConnect(beforeConnect: ClientWebSocketOptions['beforeConnect']): ClientOption;
2876
+ export declare function withWebSocketBeforeConnect(beforeConnect: ClientWebSocketOptions["beforeConnect"]): ClientOption;
1459
2877
  /**
1460
- * Configure WebSocket subprotocols for the connection handshake.
2878
+ * Sets the WebSocket subprotocols offered during the handshake.
1461
2879
  *
1462
- * `protocols` are passed as the second argument to the `WebSocket` constructor
1463
- * and participate in the RFC 6455 subprotocol negotiation. They are only
1464
- * meaningful when the server requires a specific protocol (for example,
1465
- * `['json']` or `['v1']` to select a message encoding version).
2880
+ * They are passed as the second argument to the `WebSocket` constructor only when the list is not empty, so the platform can run the RFC 6455 subprotocol negotiation. They matter only when the server requires a specific subprotocol, for example `['json']` or `['v1']`.
2881
+ *
2882
+ * Precedence, highest first: the `protocols` execute option, this client option, then `protocols` on the endpoint definition.
1466
2883
  *
1467
2884
  * @param protocols - Subprotocol list for the WebSocket constructor.
1468
2885
  * @returns An option that sets `config.webSocket.protocols`.
2886
+ * @group client
1469
2887
  */
1470
2888
  export declare function withWebSocketProtocols(protocols: readonly string[]): ClientOption;
1471
2889
  /**
1472
- * Enables WebSocket heartbeat pings and optional ACK detection.
2890
+ * Enables WebSocket heartbeat messages with optional acknowledgement detection.
2891
+ *
2892
+ * Passing `undefined` clears an earlier setting. The option type is:
2893
+ *
2894
+ * ```ts
2895
+ * {
2896
+ * intervalMs: number
2897
+ * isAck?: (message: unknown) => boolean
2898
+ * message?: <T = unknown>() => T | unknown
2899
+ * timeoutMs?: number
2900
+ * }
2901
+ * ```
2902
+ *
2903
+ * - Every `intervalMs`, `message()` is called and its result is sent through the endpoint's `outgoing` struct. No message is sent when `message` is omitted or returns `undefined`.
2904
+ * - With `timeoutMs`, an acknowledgement is expected after each heartbeat. `isAck` receives the decoded incoming message; when it returns `true`, the timer is cleared and that message is **not** delivered to `receive`.
2905
+ * - If no ack arrives within `timeoutMs`, or a heartbeat cannot be sent, the session fails. This does not trigger a reconnect.
2906
+ * - Heartbeat uses the same JSON guard as `session.send`: a value JSON cannot represent after struct encoding causes `TypeError` and ends the logical session.
2907
+ * - `intervalMs` and `timeoutMs` must be positive, finite, and at most `2_147_483_647`; otherwise execute fails with `REQ_OPTIONS_INVALID`.
2908
+ * - A `heartbeat` in the execute options replaces this one for that call.
1473
2909
  *
1474
2910
  * @param options - Heartbeat interval, payload, timeout, and ACK predicate.
1475
2911
  * @returns An option that sets `config.webSocket.heartbeat`.
2912
+ * @group client
1476
2913
  */
1477
- export declare function withWebSocketHeartbeat(options: ClientWebSocketOptions['heartbeat']): ClientOption;
2914
+ export declare function withWebSocketHeartbeat(options: ClientWebSocketOptions["heartbeat"]): ClientOption;
1478
2915
  /**
1479
- * Configures automatic WebSocket reconnection after unexpected closes.
2916
+ * Configures automatic reconnection when the socket closes for any reason other than a manual `session.close()`.
2917
+ *
2918
+ * Reconnection is opt-in: without this option a close ends the session. Passing `undefined` clears an earlier setting. The option type is:
2919
+ *
2920
+ * ```ts
2921
+ * {
2922
+ * attempts?: number
2923
+ * delayMs?: number
2924
+ * factor?: number
2925
+ * jitter?: number
2926
+ * maxDelayMs?: number
2927
+ * shouldReconnect?: (context: { attempt: number; cause?: unknown; code?: number; reason?: string; wasClean?: boolean }) => boolean
2928
+ * }
2929
+ * ```
2930
+ *
2931
+ * - `attempts` is the retry budget for each outage. It defaults to `3`, and `0` disables reconnection. Successful `open` resets the budget and backoff exponent; the next outage starts at retry 1. Omitting it does not give unlimited retries within one outage. A socket that successfully opens and is immediately closed again by the server can keep reconnecting at the base delay (subject to jitter and caps), because every open resets the budget; use `shouldReconnect` to stop it.
2932
+ * - `delayMs` defaults to `1000`, `factor` to `2`, `maxDelayMs` to `30000`, and `jitter` (0 to 1) to `0`. Retry _n_ starts with `delayMs * factor^(n-1)`, capped at `maxDelayMs`; jitter and rounding follow, then the final delay is capped again at `maxDelayMs` and `2_147_483_647` ms. A zero base stays zero; valid-number overflow saturates at those caps.
2933
+ * - `shouldReconnect` can return `false` to end the session instead. Without it every close is retried while the budget lasts. It is not called for a manual `session.close()`.
2934
+ * - Reconnect options are validated before connecting: `attempts` must be a non-negative safe integer; `delayMs` must be finite and non-negative; `maxDelayMs` must be a non-negative finite number or positive `Infinity`, which removes the configured cap but not the platform limit of `2_147_483_647` ms; `factor` must be finite and positive; `jitter` must be finite and within 0–1. Invalid values make execute fail with `REQ_OPTIONS_INVALID` before connecting. `attempts: 0` disables subsequent retries, but all explicitly supplied reconnect fields are still validated before the initial connection.
2935
+ * - A `reconnect` in the execute options replaces this one for that call.
1480
2936
  *
1481
2937
  * @param options - Retry budget, backoff, and `shouldReconnect` gate.
1482
2938
  * @returns An option that sets `config.webSocket.reconnect`.
2939
+ * @group client
1483
2940
  */
1484
- export declare function withWebSocketReconnect(options: ClientWebSocketOptions['reconnect']): ClientOption;
2941
+ export declare function withWebSocketReconnect(options: ClientWebSocketOptions["reconnect"]): ClientOption;
1485
2942
  /**
1486
- * Handles WebSocket messages whose `type` has no matching incoming struct.
2943
+ * Observes SSE events that have no matching event struct or fail validation.
1487
2944
  *
1488
- * JSON/Struct decode failures still go to `session.onRuntimeError`. Unknown types
1489
- * do not tear down the session.
2945
+ * The handler may be async and is awaited. The callback receives:
1490
2946
  *
1491
- * @param handler - Observer callback; failures are isolated.
1492
- * @returns An option that sets `config.webSocket.onInvalidEvent`.
1493
- */
1494
- export declare function withWebSocketOnInvalidEvent(handler: ClientWebSocketOptions['onInvalidEvent']): ClientOption;
1495
- /**
1496
- * Handles SSE events that lack a matching struct or fail validation.
2947
+ * ```ts
2948
+ * {
2949
+ * reason: 'missing-struct' | 'validation-failed'
2950
+ * message: { id: string; event: string; data: string }
2951
+ * cause?: unknown
2952
+ * signal: AbortSignal
2953
+ * }
2954
+ * ```
2955
+ *
2956
+ * - The invalid event is **always dropped**; it is never delivered to the stream. The callback only observes it.
2957
+ * - The return value is ignored. An error thrown by the callback is swallowed and does not close the stream, unless `signal` has already aborted.
2958
+ * - `cause` is set for `validation-failed`. Without a handler the invalid event is dropped silently.
1497
2959
  *
1498
- * @param handler - Callback for invalid events; may throw or return a value.
2960
+ * @param handler - Observer callback for invalid events.
1499
2961
  * @returns An option that sets `config.sse.onInvalidEvent`.
2962
+ * @group client
1500
2963
  */
1501
- export declare function withSSEOnInvalidEvent(handler: ClientSSEOptions['onInvalidEvent']): ClientOption;
2964
+ export declare function withSSEOnInvalidEvent(handler: ClientSSEOptions["onInvalidEvent"]): ClientOption;
1502
2965
  /**
1503
- * Configures automatic SSE reconnection after stream failures.
2966
+ * Configures automatic reconnection after an SSE stream fails.
2967
+ *
2968
+ * Reconnection is opt-in: without this option a failed stream is not retried. Passing `undefined` clears an earlier setting.
2969
+ *
2970
+ * ```ts
2971
+ * type SSEReconnect = {
2972
+ * attempts?: number;
2973
+ * delayMs?: number;
2974
+ * factor?: number;
2975
+ * jitter?: number;
2976
+ * maxDelayMs?: number;
2977
+ * shouldReconnect?: (context: {
2978
+ * attempt: number;
2979
+ * cause?: unknown;
2980
+ * lastEventId: string;
2981
+ * method: string;
2982
+ * open?: {
2983
+ * response: { status: number; statusText: string; url: string };
2984
+ * url: string;
2985
+ * };
2986
+ * }) => boolean | Promise<boolean>;
2987
+ * };
2988
+ * ```
2989
+ *
2990
+ * - `attempts` is the retry budget for each outage. It defaults to `3` when `reconnect` is set, and `0` disables retries. A successful handshake resets the budget, callback `attempt`, and backoff exponent. Omitting it does not give unlimited retries within one outage. Repeated successful opens followed immediately by retryable stream failures can keep reconnecting at the current base delay (subject to jitter and caps), because each open resets the budget; use `shouldReconnect` to stop them. Normal EOF remains terminal.
2991
+ * - Without `shouldReconnect`, only `GET`, `HEAD`, `OPTIONS`, `PUT`, and `DELETE` reconnect. With it, its result decides for every method, `POST` included; `method` in the context tells endpoints apart. Return `false` to stop for this failure. It runs before the attempts limit is checked.
2992
+ * - A request with a `ReadableStream` body never reconnects, and `shouldReconnect` is not called for it.
2993
+ * - `delayMs`, `factor`, `jitter` (0 to 1), and `maxDelayMs` shape the backoff. `factor` defaults to `1` and `jitter` to `0`. `maxDelayMs` caps the final delay after jitter and rounding. Without a configured cap, the platform timer limit of `2_147_483_647` ms still applies. A zero base stays zero; valid-number overflow saturates at the caps.
2994
+ * - `delayMs` is only the starting base delay (`1000` by default). A server `retry:` field (digits only, in milliseconds) replaces it for later attempts.
2995
+ * - The last event id is sent again as UTF-8 bytes in `Last-Event-ID` on the next attempt. An empty `id:` clears it; an ID containing NUL is ignored.
2996
+ * - Reconnect options are validated before interceptors or Fetch: `attempts` must be a non-negative safe integer; `delayMs` must be finite and non-negative; `maxDelayMs` must be a non-negative finite number or positive `Infinity`, which removes the configured cap but not the platform limit of `2_147_483_647` ms; `factor` must be finite and positive; `jitter` must be finite and within 0–1. Invalid values return `REQ_OPTIONS_INVALID` at execute time.
1504
2997
  *
1505
2998
  * @param options - Retry budget, backoff, and `shouldReconnect` gate.
1506
2999
  * @returns An option that sets `config.sse.reconnect`.
3000
+ * @group client
1507
3001
  */
1508
- export declare function withSSEReconnect(options: ClientSSEOptions['reconnect']): ClientOption;
3002
+ export declare function withSSEReconnect(options: ClientSSEOptions["reconnect"]): ClientOption;
1509
3003
  /**
1510
3004
  * Enables XSRF cookie-to-header token forwarding for HTTP requests.
1511
3005
  *
1512
- * Defaults to cookie `XSRF-TOKEN` and header `X-XSRF-TOKEN` when names are omitted.
3006
+ * SSE and WebSocket requests do not get the header. `cookieName` defaults to `XSRF-TOKEN` and `headerName` to `X-XSRF-TOKEN` when names are omitted.
3007
+ *
3008
+ * - The header is set only when all of these hold: the method is not `GET`, `HEAD`, `OPTIONS`, or `TRACE`; the request does not already have that header; the client has an endpoint; and, whenever `location` exists (Window or Worker), the resolved request URL has the same origin as `location.origin`. A cross-origin request does not call the provider.
3009
+ * - Without `tokenProvider`, the token is read from `document.cookie` and URL-decoded only when `document` exists. Workers and servers need a provider to supply a token.
3010
+ * - With `tokenProvider`, the cookie is never read. The provider receives the request, and its result is the token. An empty string, `null`, or `undefined` means no header.
1513
3011
  *
1514
3012
  * @param options - Cookie/header names and optional token provider.
1515
3013
  * @returns An option that sets `config.xsrf`.
3014
+ * @group client
1516
3015
  */
1517
- export declare function withXSRF(options?: HttpClientOptions['xsrf']): ClientOption;
3016
+ export declare function withXSRF(options?: HttpClientOptions["xsrf"]): ClientOption;
1518
3017
  /**
1519
- * Sets whether HTTP/SSE requests should include credentials (cookies).
3018
+ * Sets whether HTTP and SSE requests include credentials.
3019
+ *
3020
+ * When `value` is `true`, Fetch uses `credentials: 'include'`, so cookies and other credentials are sent with cross-origin requests. When it is `false` or never set, `credentials` is left unset and Fetch uses its default. The WebSocket constructor does not receive this setting.
1520
3021
  *
1521
3022
  * @param value - When `true`, credentials are sent with cross-origin requests.
1522
3023
  * @returns An option that sets `config.withCredentials`.
3024
+ * @group client
1523
3025
  */
1524
3026
  export declare function withCredentials(value: boolean): ClientOption;
1525
- //#endregion
1526
- //#region src/client/client.d.ts
1527
3027
  declare const CLIENT: unique symbol;
1528
3028
  /**
1529
- * Defjs client: holds config and `execute`s HTTP, SSE, and WebSocket commands.
3029
+ * A client returned by `createClient` holds the resolved {@link ClientConfig} and executes HTTP, SSE, and WebSocket commands.
3030
+ *
3031
+ * The command type selects the `execute` overload. Failures are returned as faults in the result tuple rather than thrown; the promise rejects only for a value that is not a Defjs command (`Error: Unsupported command`). `[CLIENT]` is an internal symbol key that holds the resolved {@link ClientConfig}; it is not part of the call surface.
3032
+ *
3033
+ * @group client
3034
+ * @see {@link HttpAwaitResult}
3035
+ * @see {@link StreamAwaitResult}
3036
+ * @see {@link SocketAwaitResult}
1530
3037
  */
1531
3038
  type Client = {
3039
+ /** Internal symbol key for the resolved client configuration. */
1532
3040
  readonly [CLIENT]: ClientConfig;
1533
3041
  /** Open an SSE command and return stream open/await results. */
1534
- execute<TInput extends AnyStruct | undefined, TEvents extends EventStructs>(command: EventStreamCommand<TInput, TEvents>, options?: EventStreamExecuteOptions): Promise<StreamAwaitResult<EventStreamData<TEvents>>>;
3042
+ execute<TInput extends AnyStruct | undefined, TEvents extends EventStructs, TError extends AnyStruct | undefined = undefined>(command: EventStreamCommand<TInput, TEvents, TError>, options?: EventStreamExecuteOptions): Promise<StreamAwaitResult<EventStreamData<TEvents>, TError>>;
1535
3043
  /** Open a WebSocket command and return session await results. */
1536
- execute<TInput extends AnyStruct | undefined, TIncoming extends SocketStructs, TOutgoing extends SocketStructs | undefined>(command: WebSocketCommand<TInput, TIncoming, TOutgoing>, options?: WebSocketExecuteOptions<WebSocketIncomingData<TIncoming>, WebSocketOutgoingData<TOutgoing>>): Promise<SocketAwaitResult<WebSocketIncomingData<TIncoming>, WebSocketOutgoingData<TOutgoing>>>;
3044
+ execute<TInput extends AnyStruct | undefined, TIncoming extends AnyStruct, TOutgoing extends AnyStruct | undefined>(command: WebSocketCommand<TInput, TIncoming, TOutgoing>, options?: WebSocketExecuteOptions<WebSocketIncomingData<TIncoming>, WebSocketOutgoingData<TOutgoing>>): Promise<SocketAwaitResult<WebSocketIncomingData<TIncoming>, WebSocketOutgoingData<TOutgoing>>>;
1537
3045
  /** Run an HTTP command and return an await-result tuple. */
1538
- execute<TInput extends AnyStruct | undefined, TOutput extends RequestOutputShape | undefined>(command: HttpCommand<TInput, TOutput>, options?: HttpExecuteOptions): ReturnType<typeof executeHttpCommand<TInput, TOutput>>;
3046
+ execute<TInput extends AnyStruct | undefined, TOutput extends AnyStruct | undefined, TError extends AnyStruct | undefined>(command: HttpCommand<TInput, TOutput, TError>, options?: HttpExecuteOptions): ReturnType<typeof executeHttpCommand<TInput, TOutput, TError>>;
1539
3047
  };
1540
3048
  /**
1541
- * Create a Defjs client from option helpers.
3049
+ * Creates a Defjs client from option helpers.
1542
3050
  *
1543
- * Options are applied in order. Prefer creating the client inside the
1544
- * request boundary when interceptors close over auth or tenants.
3051
+ * Options apply in order: a later option overwrites an earlier one for the same setting, and interceptors accumulate. Prefer creating the client inside the request boundary when interceptors close over auth or tenants. Default HTTP/SSE fetch availability is checked when the interceptor chain invokes transport.
1545
3052
  *
1546
3053
  * @param options - Client option helpers such as `withEndpoint`.
1547
3054
  * @returns A client that can `execute` HTTP, SSE, and WebSocket commands.
1548
3055
  *
1549
- * @example
1550
- * ```ts
1551
- * const client = createClient(withEndpoint('https://api.example.com'))
1552
- * ```
3056
+ * @group client
3057
+ * @see {@link Client}
3058
+ * @see {@link ClientOption}
3059
+ * @exampleFile examples/api/create-client.ts#example
1553
3060
  */
1554
3061
  export declare function createClient(...options: ClientOption[]): Client;
1555
- //#endregion
1556
- //#region src/http/transport/fetch.d.ts
1557
3062
  /**
1558
3063
  * Default HTTP transport: perform `httpRequest` with `fetch` and return an `HttpResponse`.
1559
- * Used by the client unless `withHTTPHandle` replaces it; also usable in tests and custom handlers.
3064
+ *
3065
+ * The client always uses it; `withHTTPHandle` only supplies the `fetchImpl` it
3066
+ * calls. Also usable in tests and custom handlers. `fetchImpl` defaults to the
3067
+ * global `fetch` resolved at send time. It rejects, rather than resolving with
3068
+ * a synthetic response, when no response reached the client.
1560
3069
  *
1561
3070
  * @param httpRequest - Normalized request (URL, headers, body, abort, progress hooks).
1562
- * @param fetchImpl - Fetch implementation; defaults to global `fetch`.
1563
- * @returns Parsed `HttpResponse`, or a status-0 response when the network/abort fails.
3071
+ * @param fetchImpl - Fetch implementation; defaults to global `fetch`. The library default handle is called as
3072
+ * `(url, init)` while the global is untouched, and its body is read in one piece unless download progress is observed. Any other handle receives a
3073
+ * `Request` and its body is read chunk by chunk.
3074
+ * @returns Parsed `HttpResponse`. Rejects when no response reached the client, when reading the body
3075
+ * failed, or when an upload observer threw (`EXT_OBSERVER_FAILED`). Cancellation still wins.
3076
+ * @group http
1564
3077
  */
1565
3078
  export declare function fetchHandler(httpRequest: HttpRequest, fetchImpl?: FetchHandle): Promise<HttpResponse<unknown>>;
1566
- //#endregion
1567
- //#region src/internal/url.d.ts
3079
+ /**
3080
+ * Resolves a relative definition path under the base endpoint's origin and directory into a URL.
3081
+ *
3082
+ * During `client.execute`, a missing, relative, or invalid base or an illegal
3083
+ * path becomes `REQ_BUILD_FAILED` before any interceptor or transport runs,
3084
+ * including a short-circuiting interceptor, for HTTP, SSE, and WebSocket alike.
3085
+ * An interceptor's final `req.endpoint` is also validated against its final
3086
+ * `req.baseEndpoint`; deliberately replacing the base with another valid
3087
+ * absolute URL is allowed. Deterministic URL errors are never retried.
3088
+ *
3089
+ * @throws TypeError when the endpoint or path is invalid.
3090
+ * @param baseEndpoint - A valid absolute client endpoint. Its query and hash are dropped, and a trailing slash is added to its pathname so that the base path is kept: `https://api.example.com/v1` with `/users` gives `https://api.example.com/v1/users`.
3091
+ * @param path - A relative endpoint path. A single leading slash is removed. After resolution, the URL must keep the base origin and stay under its directory. Absolute or protocol-relative paths, C0 control characters (including tab, CR, and LF), backslashes, `?`, `#`, and directory escapes are rejected. Scheme-like and protocol-relative forms are rejected even with leading spaces; a plain inner space is allowed and sent as `%20`.
3092
+ * @param queryString - Query without a leading `?`. When non-empty it is set as the URL's search verbatim.
3093
+ * @returns the resolved `URL`.
3094
+ * @group http
3095
+ */
1568
3096
  export declare function createResolvedRequestUrl(baseEndpoint: string, path: string, queryString?: string): URL;
1569
- //#endregion
1570
- //#region src/interceptor/basic_auth.d.ts
1571
3097
  type BasicCredential = {
1572
3098
  username: string;
1573
3099
  password: string;
1574
3100
  };
1575
3101
  /**
1576
3102
  * Options for basic-auth interceptors, including a custom credential encoder.
3103
+ *
3104
+ * `encode` replaces only the credential encoder. The default is
3105
+ * ``btoa(`${username}:${password}`)``. The interceptor always prefixes the
3106
+ * returned string with `Basic `, so return the credential part alone: returning
3107
+ * `'Basic abc'` produces the header `Basic Basic abc`.
3108
+ *
3109
+ * Accepted by {@link basicAuthHttpInterceptor} and {@link basicAuthSSEInterceptor}.
3110
+ *
3111
+ * @group interceptors
1577
3112
  */
1578
3113
  type BasicAuthInterceptorOptions = {
1579
3114
  encode?: (credential: BasicCredential) => string;
@@ -1581,18 +3116,32 @@ type BasicAuthInterceptorOptions = {
1581
3116
  /**
1582
3117
  * HTTP interceptor that sets an `Authorization: Basic …` header from credentials.
1583
3118
  *
3119
+ * It sets `Authorization: Basic <encoded>` on a copy of the request headers,
3120
+ * replacing any existing `Authorization` header. The default encoder is
3121
+ * ``btoa(`${username}:${password}`)``, so credentials must be Latin-1. Without
3122
+ * `encode`, calling `basicAuthHttpInterceptor` throws
3123
+ * `Error: BasicAuthInterceptor is not supported in this environment` when `btoa`
3124
+ * is not available in the runtime. Use {@link basicAuthSSEInterceptor} for SSE
3125
+ * commands.
3126
+ *
1584
3127
  * @param fn - Factory returning username/password for each request.
1585
3128
  * @param options - Optional custom encoder (defaults to `btoa`).
1586
3129
  * @returns An `HttpInterceptor` that injects basic auth.
3130
+ * @group interceptors
1587
3131
  */
1588
3132
  export declare function basicAuthHttpInterceptor(fn: () => BasicCredential, options?: BasicAuthInterceptorOptions): HttpInterceptor;
1589
3133
  /**
1590
3134
  * SSE interceptor that sets an `Authorization: Basic …` header from credentials.
1591
3135
  *
3136
+ * It sets `Authorization: Basic <encoded>` on a copy of the request headers,
3137
+ * replacing any existing `Authorization` header. Encoding and the `btoa`
3138
+ * requirement are the same as for {@link basicAuthHttpInterceptor}, including
3139
+ * the synchronous throw when `btoa` is missing.
3140
+ *
1592
3141
  * @param fn - Factory returning username/password for each request.
1593
3142
  * @param options - Optional custom encoder (defaults to `btoa`).
1594
3143
  * @returns An `SSEInterceptor` that injects basic auth.
3144
+ * @group interceptors
1595
3145
  */
1596
3146
  export declare function basicAuthSSEInterceptor(fn: () => BasicCredential, options?: BasicAuthInterceptorOptions): SSEInterceptor;
1597
- //#endregion
1598
- export type { AnyStruct, BasicAuthInterceptorOptions, Client, ClientConfig, ClientOption, ClientOptions, ClientSSEConfig, ClientSSEOptions, ClientWebSocketOptions, ErrorMap, EventStreamCloseInfo, EventStreamErrorCode, EventStreamHandle, EventStreamOpenInfo, FetchHandle, FlattenedStructError, FormattedStructError, HttpInterceptor, HttpInterceptorNext, HttpProgressEvent, HttpProgressFn, HttpRequest, HttpResponse, HttpResponseType, Infer, Interceptor, InterceptorFn, MakeResponseOptions, ObjectStruct, ParseResult, QueryParamsSerializer, RequestOutputShape, RequestStruct, ResponseGroupItem, SSEHandler, SSEInterceptor, SSEInterceptorFn, Struct, StructInput, StructIssue, StructLike, StructMethods, WebSocketHandle, WebSocketHandleConstructor, WebSocketHandler, WebSocketInterceptor, WebSocketInterceptorFn, WebSocketSessionLike };
3147
+ export type { AnyStruct, BasicAuthInterceptorOptions, BuilderInput, Client, ClientConfig, ClientOption, ClientOptions, ClientSSEConfig, ClientSSEOptions, ClientWebSocketOptions, DecodedResponse, ErrorMap, EventStreamCloseInfo, EventStreamFaultCode, EventStreamHandle, EventStreamOpenInfo, EventStreamRequestShape, FetchHandle, FlattenedStructError, FormattedStructError, HttpInterceptor, HttpInterceptorNext, HttpMeta, HttpProgressEvent, HttpProgressFn, HttpRequest, HttpRequestShape, HttpResponse, HttpResponseType, Infer, Interceptor, InterceptorFn, MakeResponseOptions, ObjectStruct, ParseResult, QuerySerializer, SSEHandler, SSEInterceptor, SSEInterceptorFn, STRUCT, Struct, StructInput, StructIssue, StructLike, StructMethods, WebSocketHandle, WebSocketHandleConstructor, WebSocketHandler, WebSocketInterceptor, WebSocketInterceptorFn, WebSocketRequestShape, WebSocketSessionLike };