@defjs/core 0.4.0-beta.1 → 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.
- package/README.md +58 -29
- package/index.d.ts +1839 -342
- package/index.js +1587 -817
- package/index.min.js +21 -27
- package/package.json +2 -1
package/index.d.ts
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
|
-
//#region src/internal/http_request.d.ts
|
|
2
1
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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
|
|
6
10
|
*/
|
|
7
|
-
type HttpResponseType =
|
|
11
|
+
type HttpResponseType = "arraybuffer" | "blob" | "json" | "text";
|
|
8
12
|
/**
|
|
9
|
-
* Progress snapshot
|
|
10
|
-
*
|
|
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
|
|
11
27
|
*/
|
|
12
28
|
interface HttpProgressEvent {
|
|
13
29
|
readonly lengthComputable: boolean;
|
|
@@ -16,24 +32,67 @@ interface HttpProgressEvent {
|
|
|
16
32
|
}
|
|
17
33
|
/**
|
|
18
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
|
|
19
61
|
*/
|
|
20
62
|
type HttpProgressFn = (event: HttpProgressEvent) => Promise<void> | void;
|
|
21
63
|
/**
|
|
22
|
-
* Normalized outgoing HTTP request
|
|
64
|
+
* Normalized outgoing HTTP request seen by transports, handlers, interceptors, and `fetchHandler` after the builder runs.
|
|
23
65
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
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
|
|
26
68
|
* original struct input.
|
|
69
|
+
*
|
|
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
|
|
27
79
|
*/
|
|
28
80
|
interface HttpRequest {
|
|
29
81
|
abort?: AbortSignal;
|
|
82
|
+
/** Trusted absolute base; its origin and directory bound the resolved endpoint path. */
|
|
30
83
|
baseEndpoint?: string;
|
|
31
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
|
+
*/
|
|
32
89
|
bodyContentType?: string | null;
|
|
33
90
|
/** @internal Tracks which body value produced bodyContentType so interceptors cannot leave stale metadata behind. */
|
|
34
91
|
bodyContentTypeSource?: unknown;
|
|
35
92
|
downloadProgress?: HttpProgressFn;
|
|
93
|
+
/** Relative path validated after building and again when an interceptor calls the transport. */
|
|
36
94
|
endpoint: string;
|
|
95
|
+
/** HTTP interceptors see declared headers; detected Content-Type is applied at the Fetch boundary. */
|
|
37
96
|
headers?: Headers;
|
|
38
97
|
method: string;
|
|
39
98
|
/** Static, low-cardinality endpoint identity for telemetry and diagnostics. */
|
|
@@ -63,12 +122,19 @@ interface HttpRequest {
|
|
|
63
122
|
}) => string | null | undefined;
|
|
64
123
|
};
|
|
65
124
|
}
|
|
66
|
-
//#endregion
|
|
67
|
-
//#region src/internal/http_response.d.ts
|
|
68
125
|
declare const HTTP_RESPONSE: unique symbol;
|
|
69
126
|
/**
|
|
70
|
-
* Wire-shape HTTP response
|
|
71
|
-
*
|
|
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
|
|
72
138
|
*/
|
|
73
139
|
type HttpResponse<R> = {
|
|
74
140
|
readonly [HTTP_RESPONSE]: true;
|
|
@@ -83,7 +149,9 @@ type HttpResponse<R> = {
|
|
|
83
149
|
/**
|
|
84
150
|
* Response metadata, available whenever a response reached the client.
|
|
85
151
|
*
|
|
86
|
-
*
|
|
152
|
+
* It carries no `body`: a body only exists once a declared struct decoded it.
|
|
153
|
+
*
|
|
154
|
+
* @group http
|
|
87
155
|
*/
|
|
88
156
|
type HttpMeta = {
|
|
89
157
|
readonly headers: Headers;
|
|
@@ -93,16 +161,27 @@ type HttpMeta = {
|
|
|
93
161
|
readonly url: string;
|
|
94
162
|
};
|
|
95
163
|
/**
|
|
96
|
-
* Response
|
|
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.
|
|
97
168
|
*
|
|
98
|
-
*
|
|
99
|
-
* as a fault carrying `HttpMeta` alone.
|
|
169
|
+
* @group http
|
|
100
170
|
*/
|
|
101
171
|
type DecodedResponse<TBody> = HttpMeta & {
|
|
102
172
|
readonly body: TBody;
|
|
103
173
|
};
|
|
104
174
|
/**
|
|
105
|
-
* Fields accepted by `makeResponse` when building a synthetic `HttpResponse
|
|
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
|
|
106
185
|
*/
|
|
107
186
|
type MakeResponseOptions<R> = {
|
|
108
187
|
status?: number;
|
|
@@ -113,34 +192,124 @@ type MakeResponseOptions<R> = {
|
|
|
113
192
|
request?: HttpRequest;
|
|
114
193
|
};
|
|
115
194
|
/**
|
|
116
|
-
*
|
|
117
|
-
*
|
|
195
|
+
* Builds an `HttpResponse` without a network call, for interceptors that short-circuit `next` and for tests.
|
|
196
|
+
*
|
|
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.
|
|
118
201
|
*
|
|
119
202
|
* @param options - Status, headers, body, and an optional request to copy headers/url from; defaults yield status `0`.
|
|
120
203
|
* @returns An `HttpResponse` with `ok` derived from the status code.
|
|
204
|
+
* @group http
|
|
121
205
|
*/
|
|
122
206
|
export declare function makeResponse<R>(options?: MakeResponseOptions<R>): HttpResponse<R>;
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
207
|
+
/**
|
|
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
|
+
*/
|
|
126
223
|
export declare const ERR_ABORTED: Error;
|
|
127
|
-
/**
|
|
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
|
+
*/
|
|
128
239
|
export declare const ERR_TIMEOUT: Error;
|
|
129
|
-
//#endregion
|
|
130
|
-
//#region src/struct/errors.d.ts
|
|
131
240
|
/**
|
|
132
|
-
*
|
|
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`.
|
|
133
264
|
*
|
|
134
|
-
*
|
|
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.
|
|
287
|
+
*
|
|
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.
|
|
135
289
|
*
|
|
136
290
|
* @param issue - Issue about to be recorded.
|
|
137
291
|
* @returns Replacement message, or `undefined` to keep the default.
|
|
292
|
+
* @group struct
|
|
138
293
|
*/
|
|
139
294
|
type ErrorMap = (issue: StructIssue) => string | undefined;
|
|
140
295
|
/**
|
|
141
|
-
*
|
|
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.
|
|
142
301
|
*
|
|
143
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
|
|
144
313
|
*/
|
|
145
314
|
export declare class StructError extends Error {
|
|
146
315
|
/** Issues collected for this failure, in encounter order. */
|
|
@@ -164,27 +333,19 @@ export declare class StructError extends Error {
|
|
|
164
333
|
/**
|
|
165
334
|
* Render a multi-line, human-readable summary of all issues.
|
|
166
335
|
*
|
|
336
|
+
* Each line uses the issue message, which names the received kind and omits the value.
|
|
337
|
+
*
|
|
167
338
|
* @returns One `× path: message` line per issue.
|
|
168
339
|
*/
|
|
169
340
|
prettify(): string;
|
|
170
341
|
}
|
|
171
|
-
//#endregion
|
|
172
|
-
//#region src/struct/symbols.d.ts
|
|
173
|
-
/**
|
|
174
|
-
* Enumerable tag on objects produced while parsing endpoint input.
|
|
175
|
-
* Response decoding and `struct.parse` leave it unset. Internal to the outbound encoder.
|
|
176
|
-
*
|
|
177
|
-
* @internal
|
|
178
|
-
*/
|
|
179
|
-
declare const STRUCT: unique symbol;
|
|
180
|
-
//#endregion
|
|
181
|
-
//#region src/struct/types.d.ts
|
|
182
342
|
type Path = Array<number | string>;
|
|
183
343
|
/**
|
|
184
|
-
* Error-first
|
|
344
|
+
* Error-first tuple returned by `struct.parse`.
|
|
345
|
+
*
|
|
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`.
|
|
185
347
|
*
|
|
186
|
-
*
|
|
187
|
-
* the error is a `StructError` and `value` is always `undefined`.
|
|
348
|
+
* @group struct
|
|
188
349
|
*/
|
|
189
350
|
type ParseResult<O> = [error: null, value: O] | [error: StructError, value: undefined];
|
|
190
351
|
type LiteralValue = boolean | null | number | string;
|
|
@@ -196,9 +357,14 @@ interface StructTypes<Input = unknown, Output = unknown, OptionalOut extends boo
|
|
|
196
357
|
/**
|
|
197
358
|
* Minimal structural brand carried by every struct instance.
|
|
198
359
|
*
|
|
199
|
-
* `_struct` holds input
|
|
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
|
|
200
365
|
*/
|
|
201
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. */
|
|
202
368
|
readonly _struct: StructTypes<I, O, OO>;
|
|
203
369
|
}
|
|
204
370
|
type OptionalOutputStruct = {
|
|
@@ -208,13 +374,25 @@ type OptionalOutputStruct = {
|
|
|
208
374
|
};
|
|
209
375
|
/**
|
|
210
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
|
|
211
389
|
*/
|
|
212
390
|
interface StructIssue {
|
|
213
391
|
/** Machine-readable failure category. */
|
|
214
|
-
code:
|
|
392
|
+
code: "custom" | "invalid_enum" | "invalid_intersection" | "invalid_literal" | "invalid_type" | "invalid_union" | "missing_key";
|
|
215
393
|
/** Human-readable description of the expected type or value. */
|
|
216
394
|
expected: string;
|
|
217
|
-
/** Final message after optional `ErrorMap` rewriting. */
|
|
395
|
+
/** Final message after optional `ErrorMap` rewriting. The default names the received kind, not the value. */
|
|
218
396
|
message: string;
|
|
219
397
|
/** Path from the parse root to the failing location. */
|
|
220
398
|
path: Path;
|
|
@@ -224,14 +402,20 @@ interface StructIssue {
|
|
|
224
402
|
/**
|
|
225
403
|
* Nested error tree returned by `StructError.format()`.
|
|
226
404
|
*
|
|
227
|
-
* 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
|
|
228
408
|
*/
|
|
229
409
|
interface FormattedStructError {
|
|
230
410
|
_errors: string[];
|
|
231
411
|
[key: string]: FormattedStructError | string[];
|
|
232
412
|
}
|
|
233
413
|
/**
|
|
234
|
-
* Flat field
|
|
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
|
|
235
419
|
*/
|
|
236
420
|
interface FlattenedStructError {
|
|
237
421
|
/** Messages whose path is empty (root-level issues). */
|
|
@@ -243,6 +427,18 @@ interface FlattenedStructError {
|
|
|
243
427
|
}
|
|
244
428
|
/**
|
|
245
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
|
|
246
442
|
*/
|
|
247
443
|
interface StructMethods<I, O, OO extends boolean> {
|
|
248
444
|
/**
|
|
@@ -278,9 +474,13 @@ interface StructMethods<I, O, OO extends boolean> {
|
|
|
278
474
|
optional(): Struct<I | undefined, O | undefined, true>;
|
|
279
475
|
}
|
|
280
476
|
/**
|
|
281
|
-
* Public struct contract:
|
|
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.
|
|
282
480
|
*
|
|
283
|
-
*
|
|
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.
|
|
482
|
+
*
|
|
483
|
+
* @group struct
|
|
284
484
|
*/
|
|
285
485
|
type Struct<Input = unknown, Output = Input, OptionalOut extends boolean = false> = StructMethods<Input, Output, OptionalOut> & StructLike<Input, Output, OptionalOut>;
|
|
286
486
|
type PresentValue = NonNullable<unknown>;
|
|
@@ -299,39 +499,61 @@ interface PreservedStruct<T, I, O, OO extends boolean> extends Struct<I, O, OO>,
|
|
|
299
499
|
optional(): PreservedStruct<T, I | undefined, O | undefined, true>;
|
|
300
500
|
}
|
|
301
501
|
/**
|
|
302
|
-
* Broad struct constraint for endpoint and client generics.
|
|
502
|
+
* Broad struct constraint that accepts any struct, for endpoint and client generics.
|
|
503
|
+
*
|
|
504
|
+
* Used by generics such as `defineRequest`, `Fault`, and `HttpCommand`. Prefer a concrete `Struct<…>` when you need precise input and output inference.
|
|
303
505
|
*
|
|
304
|
-
*
|
|
506
|
+
* @group struct
|
|
305
507
|
*/
|
|
306
508
|
type AnyStruct = Struct<any, any, boolean>;
|
|
307
509
|
/**
|
|
308
|
-
*
|
|
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
|
+
* ```
|
|
309
522
|
*
|
|
310
523
|
* @typeParam T - A struct-like value with a `_struct.input` brand.
|
|
524
|
+
* @group struct
|
|
311
525
|
*/
|
|
312
526
|
type StructInput<T> = T extends {
|
|
313
527
|
readonly _struct: {
|
|
314
528
|
readonly input: unknown;
|
|
315
529
|
};
|
|
316
|
-
} ? T[
|
|
530
|
+
} ? T["_struct"]["input"] : never;
|
|
317
531
|
type StructOutput<T> = T extends {
|
|
318
532
|
readonly _struct: {
|
|
319
533
|
readonly output: unknown;
|
|
320
534
|
};
|
|
321
|
-
} ? T[
|
|
535
|
+
} ? T["_struct"]["output"] : never;
|
|
322
536
|
/**
|
|
323
|
-
*
|
|
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
|
+
* ```
|
|
324
545
|
*
|
|
325
546
|
* @typeParam T - A struct-like value with a `_struct.output` brand.
|
|
547
|
+
* @group struct
|
|
326
548
|
*/
|
|
327
549
|
type Infer<T> = StructOutput<T>;
|
|
328
|
-
type FieldOutput<S> = S extends StructLike<unknown, unknown, boolean> ? S extends OptionalOutputStruct ? Exclude<S[
|
|
550
|
+
type FieldOutput<S> = S extends StructLike<unknown, unknown, boolean> ? S extends OptionalOutputStruct ? Exclude<S["_struct"]["output"], undefined> : S["_struct"]["output"] : never;
|
|
329
551
|
type Simplify<T> = { [K in keyof T]: T[K]; } & {};
|
|
330
552
|
type ObjectShape = {
|
|
331
553
|
[key: string]: any;
|
|
332
554
|
};
|
|
333
|
-
type ObjectInput<T extends ObjectShape> = Simplify<{ -readonly [K in keyof T as T[K]
|
|
334
|
-
type ObjectOutput<T extends ObjectShape> = Simplify<{ -readonly [K in keyof T as 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]>; }>;
|
|
335
557
|
type TupleInput<T extends readonly StructLike<unknown, unknown, boolean>[]> = { -readonly [K in keyof T]: StructInput<T[K]>; };
|
|
336
558
|
type TupleOutput<T extends readonly StructLike<unknown, unknown, boolean>[]> = { -readonly [K in keyof T]: StructOutput<T[K]>; };
|
|
337
559
|
type UnionOutput<T extends readonly StructLike<unknown, unknown, boolean>[]> = Exclude<StructOutput<T[number]>, undefined>;
|
|
@@ -363,10 +585,11 @@ interface ObjectStructTypes<T extends ObjectShape> extends StructTypes<ObjectInp
|
|
|
363
585
|
output: ObjectOutput<T>;
|
|
364
586
|
}
|
|
365
587
|
/**
|
|
366
|
-
* 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.
|
|
367
591
|
*
|
|
368
|
-
*
|
|
369
|
-
* can differ from TypeScript keys via `.alias()`.
|
|
592
|
+
* @group struct
|
|
370
593
|
*/
|
|
371
594
|
interface ObjectStruct<T extends ObjectShape> extends StructMethods<ObjectInput<T>, ObjectOutput<T>, false>, StructLike<ObjectInput<T>, ObjectOutput<T>, false> {
|
|
372
595
|
readonly _struct: ObjectStructTypes<T>;
|
|
@@ -423,8 +646,6 @@ interface IntersectionStruct<T extends readonly StructLike<unknown, unknown, boo
|
|
|
423
646
|
nullish(): PreservedStruct<IntersectionStruct<T>, IntersectionInput<T> | null | undefined, IntersectionOutput<T> | null | undefined, true>;
|
|
424
647
|
optional(): PreservedStruct<IntersectionStruct<T>, IntersectionInput<T> | undefined, IntersectionOutput<T> | undefined, true>;
|
|
425
648
|
}
|
|
426
|
-
//#endregion
|
|
427
|
-
//#region src/error/types.d.ts
|
|
428
649
|
/**
|
|
429
650
|
* Every failure Defjs reports, as one closed set of codes.
|
|
430
651
|
*
|
|
@@ -443,44 +664,82 @@ interface IntersectionStruct<T extends readonly StructLike<unknown, unknown, boo
|
|
|
443
664
|
*
|
|
444
665
|
* The set is closed on purpose: it keeps `switch (fault.code)` exhaustive. Extensions
|
|
445
666
|
* report their own detail through `cause`, not through a new code.
|
|
667
|
+
*
|
|
668
|
+
* @group errors
|
|
446
669
|
*/
|
|
447
|
-
export type FaultCode =
|
|
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";
|
|
448
671
|
/**
|
|
449
|
-
*
|
|
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'`.
|
|
450
676
|
*
|
|
451
677
|
* @typeParam C - A `FaultCode`, or a union of them.
|
|
678
|
+
* @group errors
|
|
452
679
|
*/
|
|
453
680
|
export type FaultClass<C extends string> = C extends `${infer TClass}_${string}` ? TClass : never;
|
|
454
|
-
/**
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
681
|
+
/**
|
|
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
|
|
688
|
+
*/
|
|
689
|
+
export type DecodeFaultCode = "RES_DECODE_FAILED" | "RES_MEDIA_TYPE_INVALID" | "RES_STRUCT_MISMATCH";
|
|
690
|
+
/**
|
|
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
|
|
701
|
+
*/
|
|
702
|
+
export type PreflightFaultCode = Exclude<FaultCode, DecodeFaultCode | "HTTP_STATUS">;
|
|
458
703
|
type ErrorBody<TErr> = [TErr] extends [undefined] ? undefined : Infer<TErr>;
|
|
459
704
|
/**
|
|
460
|
-
*
|
|
705
|
+
* Non-2xx fault expressed by its decoded body type instead of the struct that produced it.
|
|
461
706
|
*
|
|
462
707
|
* Factories work in decoded values, so they use this form; endpoint types use
|
|
463
|
-
* `HttpStatusFault` and let `Infer` bridge the two.
|
|
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
|
|
464
712
|
*/
|
|
465
713
|
export type HttpStatusFaultOf<TData> = Error & {
|
|
466
|
-
code:
|
|
714
|
+
code: "HTTP_STATUS";
|
|
467
715
|
data: TData;
|
|
468
716
|
response: [TData] extends [undefined] ? HttpMeta : DecodedResponse<TData>;
|
|
469
717
|
status: number;
|
|
470
718
|
};
|
|
471
719
|
/**
|
|
472
|
-
*
|
|
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.
|
|
473
729
|
*
|
|
474
|
-
*
|
|
475
|
-
* without a `body` field.
|
|
730
|
+
* @group errors
|
|
476
731
|
*/
|
|
477
732
|
export type HttpStatusFault<TErr extends AnyStruct | undefined = undefined> = HttpStatusFaultOf<ErrorBody<TErr>>;
|
|
478
733
|
/**
|
|
479
734
|
* A response arrived but could not be read as declared.
|
|
480
735
|
*
|
|
481
|
-
* `
|
|
482
|
-
*
|
|
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
|
|
483
740
|
* parser failure for an unreadable representation).
|
|
741
|
+
*
|
|
742
|
+
* @group errors
|
|
484
743
|
*/
|
|
485
744
|
export type DecodeFault = Error & {
|
|
486
745
|
cause: unknown;
|
|
@@ -491,8 +750,11 @@ export type DecodeFault = Error & {
|
|
|
491
750
|
/**
|
|
492
751
|
* A failure that may have happened before any response existed.
|
|
493
752
|
*
|
|
494
|
-
* `
|
|
495
|
-
*
|
|
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.
|
|
756
|
+
*
|
|
757
|
+
* @group errors
|
|
496
758
|
*/
|
|
497
759
|
export type PreflightFault = Error & {
|
|
498
760
|
cause?: unknown;
|
|
@@ -502,116 +764,560 @@ export type PreflightFault = Error & {
|
|
|
502
764
|
/**
|
|
503
765
|
* Any failure returned by `client.execute`, expressed by its decoded error-body type.
|
|
504
766
|
*
|
|
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
|
+
*
|
|
505
771
|
* @typeParam TData - The decoded `error` body, or `undefined` when no error struct was declared.
|
|
772
|
+
* @group errors
|
|
506
773
|
*/
|
|
507
774
|
export type FaultOf<TData = undefined> = DecodeFault | HttpStatusFaultOf<TData> | PreflightFault;
|
|
508
775
|
/**
|
|
509
|
-
*
|
|
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`.
|
|
782
|
+
*
|
|
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.
|
|
510
796
|
*
|
|
511
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
|
|
512
800
|
*/
|
|
513
801
|
export type Fault<TErr extends AnyStruct | undefined = undefined> = FaultOf<ErrorBody<TErr>>;
|
|
514
802
|
/**
|
|
515
803
|
* Any fault at all, whatever its decoded error-body type.
|
|
516
804
|
*
|
|
517
|
-
* Use it for handlers that classify faults without caring which endpoint produced
|
|
518
|
-
* Prefer `Fault<typeof yourErrorStruct>` where the body type matters.
|
|
805
|
+
* Use it for handlers that classify faults without caring which endpoint produced
|
|
806
|
+
* them. Prefer `Fault<typeof yourErrorStruct>` where the body type matters.
|
|
807
|
+
*
|
|
808
|
+
* @group errors
|
|
519
809
|
*/
|
|
520
810
|
export type AnyFault = DecodeFault | HttpStatusFaultOf<undefined> | HttpStatusFaultOf<unknown> | PreflightFault;
|
|
521
|
-
//#endregion
|
|
522
|
-
//#region src/error/factory.d.ts
|
|
523
811
|
/**
|
|
524
|
-
*
|
|
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.
|
|
525
820
|
*
|
|
526
821
|
* @param response - The response, carrying the decoded body.
|
|
527
822
|
* @returns An `HTTP_STATUS` fault whose `data` is that body.
|
|
823
|
+
* @group errors
|
|
528
824
|
*/
|
|
529
825
|
export declare function createHttpStatusFault<TData>(response: DecodedResponse<TData>): HttpStatusFaultOf<TData>;
|
|
530
826
|
/**
|
|
531
|
-
*
|
|
532
|
-
*
|
|
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.
|
|
533
833
|
*
|
|
534
834
|
* @param response - Metadata of the response; no body was read.
|
|
535
835
|
* @returns An `HTTP_STATUS` fault whose `data` is `undefined`.
|
|
836
|
+
* @group errors
|
|
536
837
|
*/
|
|
537
838
|
export declare function createUndecodedHttpStatusFault(response: HttpMeta): HttpStatusFaultOf<undefined>;
|
|
538
839
|
/**
|
|
539
|
-
*
|
|
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`.
|
|
540
846
|
*
|
|
541
|
-
*
|
|
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.
|
|
542
853
|
*
|
|
543
854
|
* @param code - Which read step failed.
|
|
544
855
|
* @param cause - Underlying parser or struct failure.
|
|
545
856
|
* @param response - Metadata of the response that could not be read.
|
|
546
857
|
* @returns A decode fault with no body.
|
|
858
|
+
* @group errors
|
|
547
859
|
*/
|
|
548
860
|
export declare function createDecodeFault(code: DecodeFaultCode, cause: unknown, response: HttpMeta): DecodeFault;
|
|
549
861
|
/**
|
|
550
|
-
*
|
|
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.
|
|
551
867
|
*
|
|
552
868
|
* @param code - The specific failure.
|
|
553
869
|
* @param cause - Underlying value, when there is one; the code becomes the message otherwise.
|
|
554
870
|
* @param response - Metadata, only where the transport already had some to report.
|
|
555
871
|
* @returns A preflight fault.
|
|
872
|
+
* @group errors
|
|
556
873
|
*/
|
|
557
874
|
export declare function createPreflightFault(code: PreflightFaultCode, cause?: unknown, response?: HttpMeta): PreflightFault;
|
|
558
875
|
/**
|
|
559
|
-
*
|
|
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`.
|
|
560
880
|
*
|
|
561
|
-
*
|
|
562
|
-
*
|
|
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.
|
|
563
889
|
*
|
|
564
890
|
* @param cause - Underlying cancellation, timeout, or transport failure.
|
|
565
891
|
* @param response - Metadata, only where the transport already had some to report.
|
|
566
892
|
* @returns A `NET_ABORTED`, `NET_TIMEOUT`, or `NET_UNREACHABLE` fault.
|
|
893
|
+
* @group errors
|
|
567
894
|
*/
|
|
568
895
|
export declare function createNetworkFault(cause: unknown, response?: HttpMeta): PreflightFault;
|
|
569
|
-
|
|
570
|
-
|
|
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
|
+
*/
|
|
571
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
|
+
*/
|
|
572
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
|
+
*/
|
|
573
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
|
+
*/
|
|
574
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
|
+
*/
|
|
575
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
|
+
*/
|
|
576
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
|
+
*/
|
|
577
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
|
+
*/
|
|
578
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
|
+
*/
|
|
579
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
|
+
*/
|
|
580
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
|
+
*/
|
|
581
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
|
+
*/
|
|
582
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
|
+
*/
|
|
583
1073
|
declare function createTupleStruct<const T extends readonly [StructLike<unknown, unknown, boolean>, ...StructLike<unknown, unknown, boolean>[]]>(items: T): TupleStruct<T>;
|
|
584
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;
|
|
585
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
|
+
*/
|
|
586
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
|
+
*/
|
|
587
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
|
+
*/
|
|
588
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
|
+
*/
|
|
589
1134
|
declare function createDateStruct(): Struct<Date | number | string, Date>;
|
|
590
|
-
|
|
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
|
+
*/
|
|
591
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
|
+
*/
|
|
592
1180
|
declare function createArrayBufferStruct(): Struct<ArrayBuffer, ArrayBuffer>;
|
|
593
|
-
|
|
594
|
-
|
|
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
|
+
*/
|
|
595
1220
|
declare function parseStructTuple<S extends StructLike<unknown, unknown, boolean>>(struct: S, value: unknown, options?: {
|
|
596
1221
|
aliases?: boolean;
|
|
597
1222
|
errorMap?: ErrorMap;
|
|
598
|
-
}): ParseResult<S[
|
|
599
|
-
//#endregion
|
|
600
|
-
//#region src/struct/facade.d.ts
|
|
1223
|
+
}): ParseResult<S["_struct"]["output"]>;
|
|
601
1224
|
type EnumValue<T> = T extends readonly (infer U extends string)[] ? U : T extends {
|
|
602
1225
|
[key: string]: infer U extends number | string;
|
|
603
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
|
+
*/
|
|
604
1240
|
declare function structEnum<const T extends readonly [string, ...string[]]>(value: T): Struct<EnumValue<T>, EnumValue<T>>;
|
|
605
1241
|
declare function structEnum<const T extends {
|
|
606
1242
|
[key: string]: number | string;
|
|
607
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
|
+
*/
|
|
608
1259
|
declare function structOr<const T extends readonly [StructLike, ...StructLike[]]>(...options: T): UnionStruct<T>;
|
|
609
1260
|
/**
|
|
610
|
-
* Facade for building protocol-boundary structs and parsing values.
|
|
1261
|
+
* Facade object for building protocol-boundary structs and parsing values with its constructors and `struct.parse`.
|
|
611
1262
|
*
|
|
612
|
-
* Prefer this
|
|
1263
|
+
* Prefer this object over importing the constructor functions one by one. Compose shapes with
|
|
613
1264
|
* `struct.object`, then parse with `struct.parse` or let HTTP/SSE/WebSocket
|
|
614
|
-
* endpoints apply the same contracts at the wire boundary.
|
|
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}.
|
|
615
1321
|
*
|
|
616
1322
|
* @example
|
|
617
1323
|
* ```ts
|
|
@@ -628,8 +1334,18 @@ declare function structOr<const T extends readonly [StructLike, ...StructLike[]]
|
|
|
628
1334
|
* output: User,
|
|
629
1335
|
* })
|
|
630
1336
|
*
|
|
631
|
-
*
|
|
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()
|
|
632
1347
|
* ```
|
|
1348
|
+
* @group struct
|
|
633
1349
|
*/
|
|
634
1350
|
export declare const struct: {
|
|
635
1351
|
readonly any: typeof createAnyStruct;
|
|
@@ -656,20 +1372,18 @@ export declare const struct: {
|
|
|
656
1372
|
readonly unknown: typeof createUnknownStruct;
|
|
657
1373
|
readonly urlencoded: typeof createUrlencodedBodyStruct;
|
|
658
1374
|
};
|
|
659
|
-
//#endregion
|
|
660
|
-
//#region src/sse/transport/parser.d.ts
|
|
661
|
-
/**
|
|
662
|
-
* Portions of this file are adapted from Azure/fetch-event-source.
|
|
663
|
-
* See packages/core/THIRD_PARTY_NOTICES.md for the license text.
|
|
664
|
-
*/
|
|
665
1375
|
interface EventStreamMessage {
|
|
666
1376
|
id: string;
|
|
667
1377
|
event: string;
|
|
668
1378
|
data: string;
|
|
669
1379
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
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
|
+
*/
|
|
673
1387
|
interface EventStreamOpenInfo {
|
|
674
1388
|
response: HttpResponse<unknown>;
|
|
675
1389
|
url: string;
|
|
@@ -677,9 +1391,11 @@ interface EventStreamOpenInfo {
|
|
|
677
1391
|
/**
|
|
678
1392
|
* Fault codes an event stream can end with, drawn from the one shared `FaultCode` namespace.
|
|
679
1393
|
*
|
|
680
|
-
*
|
|
1394
|
+
* A subset of the shared `FaultCode` set. It is the `code` of an `EventStreamCloseInfo` with `kind: 'error'`.
|
|
1395
|
+
*
|
|
1396
|
+
* @group sse
|
|
681
1397
|
*/
|
|
682
|
-
type EventStreamFaultCode = Extract<FaultCode,
|
|
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">;
|
|
683
1399
|
interface EventStreamCloseInfoBase {
|
|
684
1400
|
reason?: string;
|
|
685
1401
|
cause?: unknown;
|
|
@@ -688,24 +1404,45 @@ interface EventStreamCloseInfoBase {
|
|
|
688
1404
|
* How an SSE stream ended: clean EOF, abort, or an error carrying its fault `code`.
|
|
689
1405
|
*
|
|
690
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
|
|
691
1411
|
*/
|
|
692
1412
|
type EventStreamCloseInfo = (EventStreamCloseInfoBase & {
|
|
693
|
-
kind:
|
|
1413
|
+
kind: "eof";
|
|
694
1414
|
}) | (EventStreamCloseInfoBase & {
|
|
695
|
-
kind:
|
|
1415
|
+
kind: "aborted";
|
|
696
1416
|
}) | (EventStreamCloseInfoBase & {
|
|
697
1417
|
code: EventStreamFaultCode;
|
|
698
|
-
kind:
|
|
1418
|
+
kind: "error";
|
|
699
1419
|
});
|
|
700
|
-
/**
|
|
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
|
+
*/
|
|
701
1440
|
interface EventStreamHandle<TEvent = EventStreamMessage> extends AsyncIterable<TEvent>, AsyncDisposable {
|
|
702
1441
|
readonly open: EventStreamOpenInfo;
|
|
703
1442
|
readonly closed: Promise<EventStreamCloseInfo>;
|
|
704
1443
|
close(reason?: unknown): void;
|
|
705
1444
|
[Symbol.asyncDispose](): PromiseLike<void>;
|
|
706
1445
|
}
|
|
707
|
-
//#endregion
|
|
708
|
-
//#region src/internal/abort.d.ts
|
|
709
1446
|
type UseCancellationConfig = {
|
|
710
1447
|
abort?: AbortSignal;
|
|
711
1448
|
timeout?: never;
|
|
@@ -713,15 +1450,9 @@ type UseCancellationConfig = {
|
|
|
713
1450
|
abort?: never;
|
|
714
1451
|
timeout?: number;
|
|
715
1452
|
};
|
|
716
|
-
//#endregion
|
|
717
|
-
//#region src/internal/endpoint_input.d.ts
|
|
718
1453
|
type EndpointInput<TInput extends AnyStruct | undefined> = TInput extends AnyStruct ? StructInput<TInput> : unknown;
|
|
719
|
-
//#endregion
|
|
720
|
-
//#region src/internal/endpoint_command.d.ts
|
|
721
1454
|
type IsEndpointInputOptional<TInput extends AnyStruct | undefined> = [TInput] extends [undefined] ? true : undefined extends EndpointInput<TInput> ? true : false;
|
|
722
1455
|
type EndpointCallable<TInput extends AnyStruct | undefined, TCommand> = [TInput] extends [undefined] ? () => TCommand : IsEndpointInputOptional<TInput> extends true ? (input?: EndpointInput<TInput>) => TCommand : (input: EndpointInput<TInput>) => TCommand;
|
|
723
|
-
//#endregion
|
|
724
|
-
//#region src/internal/builder_types.d.ts
|
|
725
1456
|
/** Object produced by endpoint input parsing. The symbol points at the struct that produced the node. */
|
|
726
1457
|
type Typed = {
|
|
727
1458
|
readonly [STRUCT]: AnyStruct;
|
|
@@ -737,84 +1468,190 @@ type QueryValue = Scalar | readonly QueryValue[] | {
|
|
|
737
1468
|
type QueryRecord = {
|
|
738
1469
|
readonly [key: string]: QueryValue;
|
|
739
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
|
+
};
|
|
740
1475
|
type PathParams<P extends string> = P extends `${string}:${infer Name}/${infer Rest}` ? Name | PathParams<`/${Rest}`> : P extends `${string}:${infer Name}` ? Name : never;
|
|
741
1476
|
type PathValue<P extends string> = [PathParams<P>] extends [never] ? never : ({ readonly [K in PathParams<P>]: Scalar; } & {
|
|
742
1477
|
readonly [STRUCT]?: never;
|
|
743
1478
|
}) | (Typed & {
|
|
744
1479
|
readonly [key: string]: Scalar;
|
|
745
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
|
+
*/
|
|
746
1497
|
type HttpRequestShape<P extends string, M extends string> = {
|
|
747
|
-
|
|
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
|
+
*/
|
|
748
1507
|
headers?: HeadersInit | FlatRecord;
|
|
1508
|
+
/**
|
|
1509
|
+
* Explicit values for path placeholders. Anonymous objects use template keys;
|
|
1510
|
+
* typed objects apply their struct aliases.
|
|
1511
|
+
*/
|
|
749
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
|
+
*/
|
|
750
1517
|
query?: URLSearchParams | QueryRecord;
|
|
751
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
|
+
*/
|
|
752
1528
|
type WebSocketRequestShape<P extends string> = {
|
|
1529
|
+
/** WebSocket handshakes cannot have a body. Use session.send after connecting. */
|
|
753
1530
|
body?: never;
|
|
1531
|
+
/** WebSocket builders cannot set handshake headers. */
|
|
754
1532
|
headers?: never;
|
|
1533
|
+
/**
|
|
1534
|
+
* Explicit values for path placeholders. Anonymous objects use template keys;
|
|
1535
|
+
* typed objects apply their struct aliases.
|
|
1536
|
+
*/
|
|
755
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
|
+
*/
|
|
756
1542
|
query?: URLSearchParams | QueryRecord;
|
|
757
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
|
+
*/
|
|
758
1549
|
type EventStreamRequestShape<P extends string, M extends string> = HttpRequestShape<P, M>;
|
|
759
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>;
|
|
760
|
-
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[
|
|
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;
|
|
761
1552
|
/**
|
|
762
|
-
* Parsed endpoint input
|
|
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.
|
|
763
1566
|
*
|
|
764
|
-
*
|
|
765
|
-
* At runtime a record container is also tagged when the parsed value is an object, whether or not it
|
|
766
|
-
* was merged. Array and tuple containers are not. `null` and `undefined` are never tagged.
|
|
767
|
-
* Modifiers recover the struct they wrapped. Union options are distributed; intersection options are combined.
|
|
1567
|
+
* @group http
|
|
768
1568
|
*/
|
|
769
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> ? {
|
|
770
1570
|
[key: string]: BuilderInput<Value>;
|
|
771
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>;
|
|
772
1572
|
type BuilderField<TInput, TShape> = [TInput] extends [undefined] ? {
|
|
1573
|
+
/** Pure, synchronous request builder, evaluated when the command runs. Optional without input. */
|
|
773
1574
|
builder?: () => TShape;
|
|
774
|
-
} : BuilderInput<TInput> extends TShape ? {
|
|
775
|
-
builder?: (input: BuilderInput<TInput>) => TShape;
|
|
776
1575
|
} : {
|
|
1576
|
+
/** Pure, synchronous function that explicitly places parsed input on the request when the command runs. */
|
|
777
1577
|
builder: (input: BuilderInput<TInput>) => TShape;
|
|
778
1578
|
};
|
|
779
|
-
//#endregion
|
|
780
|
-
//#region src/internal/outbound_request.d.ts
|
|
781
1579
|
/** Accepts any concrete builder. `never` keeps the parameter contravariant. */
|
|
782
1580
|
type OutboundBuilder = (input: never) => unknown;
|
|
783
|
-
//#endregion
|
|
784
|
-
//#region src/http/http.d.ts
|
|
785
1581
|
/**
|
|
786
1582
|
* Per-request options for HTTP execution: progress callbacks and cancellation.
|
|
787
|
-
*
|
|
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
|
|
788
1596
|
*/
|
|
789
1597
|
export type UseRequestConfig = {
|
|
790
1598
|
onDownloadProgress?: HttpProgressFn;
|
|
791
1599
|
onUploadProgress?: HttpProgressFn;
|
|
792
1600
|
} & UseCancellationConfig;
|
|
793
1601
|
/**
|
|
794
|
-
*
|
|
795
|
-
*
|
|
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
|
|
796
1609
|
*/
|
|
797
1610
|
export interface HttpCommand<TInput extends AnyStruct | undefined, TOutput extends AnyStruct | undefined, TError extends AnyStruct | undefined = undefined> extends BaseCommand<typeof HTTP_COMMAND> {
|
|
798
1611
|
readonly definition: RequestDefinition<TInput, TOutput, TError>;
|
|
799
1612
|
readonly input: EndpointInput<TInput> | undefined;
|
|
800
1613
|
}
|
|
801
1614
|
/**
|
|
802
|
-
* Options passed to `client.execute`
|
|
803
|
-
*
|
|
804
|
-
* `abort`
|
|
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
|
|
805
1628
|
*/
|
|
806
1629
|
export type HttpExecuteOptions = UseRequestConfig & {
|
|
807
1630
|
signal?: AbortSignal;
|
|
808
1631
|
};
|
|
809
1632
|
/**
|
|
810
|
-
*
|
|
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
|
|
811
1640
|
*/
|
|
812
1641
|
export type RequestEndpoint<TInput extends AnyStruct | undefined, TOutput extends AnyStruct | undefined, TError extends AnyStruct | undefined = undefined> = EndpointCallable<TInput, HttpCommand<TInput, TOutput, TError>>;
|
|
813
1642
|
type DeclaredBody<TStruct> = [TStruct] extends [undefined] ? undefined : Infer<TStruct>;
|
|
814
1643
|
type DeclaredResponse<TStruct> = [TStruct] extends [undefined] ? HttpMeta : DecodedResponse<Infer<TStruct>>;
|
|
815
|
-
/**
|
|
1644
|
+
/**
|
|
1645
|
+
* Decoded success body for the declared `output` struct, or `undefined` when none is declared.
|
|
1646
|
+
*
|
|
1647
|
+
* @group http
|
|
1648
|
+
*/
|
|
816
1649
|
export type RequestSuccessData<TOutput extends AnyStruct | undefined> = DeclaredBody<TOutput>;
|
|
817
|
-
/**
|
|
1650
|
+
/**
|
|
1651
|
+
* Decoded error body for the declared `error` struct, or `undefined` when none is declared.
|
|
1652
|
+
*
|
|
1653
|
+
* @group http
|
|
1654
|
+
*/
|
|
818
1655
|
export type RequestErrorData<TError extends AnyStruct | undefined> = DeclaredBody<TError>;
|
|
819
1656
|
type ResponseDeclaration<TOutput extends AnyStruct | undefined, TError extends AnyStruct | undefined> = ([TOutput] extends [undefined] ? {
|
|
820
1657
|
output?: never;
|
|
@@ -826,12 +1663,19 @@ type ResponseDeclaration<TOutput extends AnyStruct | undefined, TError extends A
|
|
|
826
1663
|
error: TError;
|
|
827
1664
|
});
|
|
828
1665
|
/**
|
|
829
|
-
* Contract for an HTTP endpoint: method, path, optional input
|
|
830
|
-
* decode its bodies.
|
|
1666
|
+
* Contract for an HTTP endpoint: method, path, optional input, and the structs that decode its bodies.
|
|
831
1667
|
*
|
|
832
|
-
* `
|
|
833
|
-
*
|
|
834
|
-
*
|
|
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
|
|
835
1679
|
*/
|
|
836
1680
|
export type RequestDefinition<TInput extends AnyStruct | undefined = undefined, TOutput extends AnyStruct | undefined = undefined, TError extends AnyStruct | undefined = undefined> = ResponseDeclaration<TOutput, TError> & {
|
|
837
1681
|
builder?: OutboundBuilder;
|
|
@@ -841,33 +1685,53 @@ export type RequestDefinition<TInput extends AnyStruct | undefined = undefined,
|
|
|
841
1685
|
path: string;
|
|
842
1686
|
};
|
|
843
1687
|
/**
|
|
844
|
-
* Await-result tuple for an HTTP call
|
|
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.
|
|
845
1693
|
*
|
|
846
|
-
*
|
|
847
|
-
* fault carries whatever response metadata existed.
|
|
1694
|
+
* @group http
|
|
848
1695
|
*/
|
|
849
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];
|
|
850
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];
|
|
851
1698
|
/**
|
|
852
|
-
*
|
|
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`.
|
|
853
1704
|
*
|
|
854
|
-
*
|
|
855
|
-
*
|
|
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.
|
|
1713
|
+
*
|
|
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.
|
|
856
1717
|
*
|
|
857
1718
|
* @param definition - Request contract (method, path, input, builder, output).
|
|
858
1719
|
* @returns An endpoint that creates `HttpCommand` values from input.
|
|
859
1720
|
*
|
|
1721
|
+
* The checked example file passes the whole input object as `path`. This snippet maps individual fields into `path` and `query` instead:
|
|
1722
|
+
*
|
|
860
1723
|
* @example
|
|
861
1724
|
* ```ts
|
|
862
1725
|
* const getUser = defineRequest({
|
|
863
1726
|
* method: 'GET',
|
|
864
1727
|
* path: '/users/:id',
|
|
865
|
-
* input: struct.object({ id: struct.number() }),
|
|
866
|
-
* builder: (input) => ({ path: { id: input.id } }),
|
|
1728
|
+
* input: struct.object({ id: struct.number(), verbose: struct.boolean() }),
|
|
1729
|
+
* builder: (input) => ({ path: { id: input.id }, query: { verbose: input.verbose } }),
|
|
867
1730
|
* output: struct.object({ id: struct.number(), name: struct.string() }),
|
|
868
|
-
* error: struct.object({ message: struct.string() }),
|
|
869
1731
|
* })
|
|
870
1732
|
* ```
|
|
1733
|
+
* @group http
|
|
1734
|
+
* @exampleFile examples/api/define-request.ts#example
|
|
871
1735
|
*/
|
|
872
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> & {
|
|
873
1737
|
input?: TInput;
|
|
@@ -876,33 +1740,62 @@ export declare function defineRequest<const M extends string, const P extends st
|
|
|
876
1740
|
path: P;
|
|
877
1741
|
} & BuilderField<NoInfer<TInput>, HttpRequestShape<NoInfer<P>, NoInfer<M>>>): RequestEndpoint<TInput, TOutput, TError>;
|
|
878
1742
|
/**
|
|
879
|
-
*
|
|
880
|
-
*
|
|
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.
|
|
881
1759
|
*
|
|
882
1760
|
* @param clientConfig - Resolved HTTP client configuration (endpoint, interceptors, handler).
|
|
883
1761
|
* @param command - Command from a `defineRequest` endpoint.
|
|
884
1762
|
* @param options - Per-request progress and cancellation options.
|
|
885
1763
|
* @returns An await-result tuple of success body or typed request error.
|
|
1764
|
+
* @group http
|
|
886
1765
|
*/
|
|
887
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>>;
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
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
|
|
1777
|
+
*/
|
|
891
1778
|
export type EventStructs = {
|
|
892
1779
|
[key: string]: AnyStruct;
|
|
893
1780
|
};
|
|
894
1781
|
type EventName<TKey extends string | number> = `${TKey}`;
|
|
895
|
-
type KnownEventUnion<TEvents extends EventStructs> = { [K in keyof TEvents & (number | string)]: EventName<K> extends
|
|
1782
|
+
type KnownEventUnion<TEvents extends EventStructs> = { [K in keyof TEvents & (number | string)]: EventName<K> extends "default" ? never : {
|
|
896
1783
|
data: Infer<TEvents[K]>;
|
|
897
1784
|
event: EventName<K>;
|
|
898
1785
|
id?: string;
|
|
899
1786
|
}; }[keyof TEvents & (number | string)];
|
|
900
|
-
type DefaultEventUnion<TEvents extends EventStructs> =
|
|
901
|
-
data: Infer<TEvents[
|
|
1787
|
+
type DefaultEventUnion<TEvents extends EventStructs> = "default" extends keyof TEvents ? {
|
|
1788
|
+
data: Infer<TEvents["default"]>;
|
|
902
1789
|
event: string;
|
|
903
1790
|
id?: string;
|
|
904
1791
|
} : never;
|
|
905
|
-
/**
|
|
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
|
+
*/
|
|
906
1799
|
export type EventStreamData<TEvents extends EventStructs> = TEvents extends EventStructs ? KnownEventUnion<TEvents> | DefaultEventUnion<TEvents> : never;
|
|
907
1800
|
interface EventStreamDefinitionBase<TEvents extends EventStructs = EventStructs> {
|
|
908
1801
|
events: TEvents;
|
|
@@ -918,23 +1811,67 @@ type EventStreamErrorField<TError extends AnyStruct | undefined> = [TError] exte
|
|
|
918
1811
|
} : {
|
|
919
1812
|
error: TError;
|
|
920
1813
|
};
|
|
921
|
-
/**
|
|
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
|
+
*/
|
|
922
1829
|
export type EventStreamDefinition<TInput extends AnyStruct | undefined = undefined, TEvents extends EventStructs = EventStructs, TError extends AnyStruct | undefined = undefined> = EventStreamDefinitionBase<TEvents> & EventStreamErrorField<TError> & {
|
|
923
1830
|
builder?: OutboundBuilder;
|
|
924
1831
|
input?: TInput;
|
|
925
1832
|
};
|
|
926
|
-
/**
|
|
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
|
+
*/
|
|
927
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];
|
|
928
|
-
/**
|
|
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
|
+
*/
|
|
929
1852
|
export interface EventStreamCommand<TInput extends AnyStruct | undefined, TEvents extends EventStructs, TError extends AnyStruct | undefined = undefined> extends BaseCommand<typeof EVENT_STREAM_COMMAND> {
|
|
930
1853
|
readonly endpoint: ResolvedEventStreamDefinition<TInput, TEvents, TError>;
|
|
931
1854
|
readonly input: EndpointInput<TInput> | undefined;
|
|
932
1855
|
}
|
|
933
|
-
/**
|
|
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
|
+
*/
|
|
934
1865
|
export type EventStreamExecuteOptions = UseCancellationConfig & {
|
|
935
1866
|
signal?: AbortSignal;
|
|
936
1867
|
};
|
|
937
|
-
/**
|
|
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
|
+
*/
|
|
938
1875
|
export type EventStreamEndpoint<TInput extends AnyStruct | undefined, TEvents extends EventStructs, TError extends AnyStruct | undefined = undefined> = EndpointCallable<TInput, EventStreamCommand<TInput, TEvents, TError>>;
|
|
939
1876
|
type ResolvedEventStreamDefinition<TInput extends AnyStruct | undefined = undefined, TEvents extends EventStructs = EventStructs, TError extends AnyStruct | undefined = undefined> = EventStreamDefinition<TInput, TEvents, TError> & {
|
|
940
1877
|
readonly method: string;
|
|
@@ -945,20 +1882,17 @@ type ResolvedEventStreamDefinition<TInput extends AnyStruct | undefined = undefi
|
|
|
945
1882
|
* Pass path, event structs, and buffer/queue limits. Call the returned endpoint
|
|
946
1883
|
* with input to get an `EventStreamCommand` for `client.execute`.
|
|
947
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
|
+
*
|
|
948
1889
|
* @param definition - SSE contract (path, events, buffer limits, optional input).
|
|
949
1890
|
* @returns An endpoint that creates `EventStreamCommand` values from input.
|
|
950
1891
|
*
|
|
951
|
-
* @
|
|
952
|
-
*
|
|
953
|
-
* const useEvents = defineEventStream({
|
|
954
|
-
* maxBufferSize: 1024,
|
|
955
|
-
* maxQueueSize: 16,
|
|
956
|
-
* path: '/events',
|
|
957
|
-
* events: { message: struct.object({ text: struct.string() }) },
|
|
958
|
-
* })
|
|
959
|
-
* ```
|
|
1892
|
+
* @group sse
|
|
1893
|
+
* @exampleFile examples/api/define-event-stream.ts#example
|
|
960
1894
|
*/
|
|
961
|
-
export declare function defineEventStream<const P extends string, const M extends string =
|
|
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> & {
|
|
962
1896
|
input?: TInput;
|
|
963
1897
|
method?: M;
|
|
964
1898
|
path: P;
|
|
@@ -968,14 +1902,15 @@ export declare function defineEventStream<const P extends string, const M extend
|
|
|
968
1902
|
*
|
|
969
1903
|
* Prefer `client.execute(command)` in application code; this is the low-level entry used by the client.
|
|
970
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
|
+
*
|
|
971
1907
|
* @param clientConfig - Resolved client configuration.
|
|
972
1908
|
* @param command - SSE command from an `EventStreamEndpoint`.
|
|
973
1909
|
* @param options - Optional execute options (abort, timeout).
|
|
974
1910
|
* @returns Await-result tuple of `[error, stream, open]`.
|
|
1911
|
+
* @group sse
|
|
975
1912
|
*/
|
|
976
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>>;
|
|
977
|
-
//#endregion
|
|
978
|
-
//#region src/client/command.d.ts
|
|
979
1914
|
declare const COMMAND_TYPE: unique symbol;
|
|
980
1915
|
declare const HTTP_COMMAND: unique symbol;
|
|
981
1916
|
declare const EVENT_STREAM_COMMAND: unique symbol;
|
|
@@ -988,21 +1923,46 @@ interface BaseCommand<TCommandType extends CommandType> {
|
|
|
988
1923
|
readonly [COMMAND_EXECUTE]: CommandExecutor;
|
|
989
1924
|
readonly [COMMAND_TYPE]: TCommandType;
|
|
990
1925
|
}
|
|
991
|
-
//#endregion
|
|
992
|
-
//#region src/web_socket/codec.d.ts
|
|
993
1926
|
interface WebSocketCloseSnapshot {
|
|
994
1927
|
cause?: unknown;
|
|
995
1928
|
code?: number;
|
|
996
1929
|
reason?: string;
|
|
997
1930
|
wasClean?: boolean;
|
|
998
1931
|
}
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
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
|
+
*/
|
|
1004
1958
|
export type WebSocketIncomingData<TIncoming extends AnyStruct> = Infer<TIncoming>;
|
|
1005
|
-
/**
|
|
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
|
+
*/
|
|
1006
1966
|
export type WebSocketOutgoingData<TOutgoing extends AnyStruct | undefined> = TOutgoing extends AnyStruct ? StructInput<TOutgoing> : never;
|
|
1007
1967
|
interface WebSocketDefinitionBase<TIncoming extends AnyStruct = AnyStruct, TOutgoing extends AnyStruct | undefined = undefined> {
|
|
1008
1968
|
incoming: TIncoming;
|
|
@@ -1013,38 +1973,113 @@ interface WebSocketDefinitionBase<TIncoming extends AnyStruct = AnyStruct, TOutg
|
|
|
1013
1973
|
path: string;
|
|
1014
1974
|
protocols?: readonly string[];
|
|
1015
1975
|
}
|
|
1016
|
-
/**
|
|
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
|
+
*/
|
|
1017
1995
|
export type WebSocketDefinition<TInput extends AnyStruct | undefined = undefined, TIncoming extends AnyStruct = AnyStruct, TOutgoing extends AnyStruct | undefined = undefined> = WebSocketDefinitionBase<TIncoming, TOutgoing> & {
|
|
1018
1996
|
builder?: OutboundBuilder;
|
|
1019
1997
|
input?: TInput;
|
|
1020
1998
|
};
|
|
1021
|
-
/**
|
|
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
|
+
*/
|
|
1022
2012
|
export interface WebSocketConnectionInfo {
|
|
1023
2013
|
extensions?: string;
|
|
1024
2014
|
generation: number;
|
|
1025
2015
|
protocol?: string;
|
|
1026
2016
|
url?: string;
|
|
1027
2017
|
}
|
|
1028
|
-
/**
|
|
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
|
+
*/
|
|
1029
2033
|
export type WebSocketCloseInfo = {
|
|
1030
|
-
kind:
|
|
2034
|
+
kind: "closed";
|
|
1031
2035
|
code?: number;
|
|
1032
2036
|
reason?: string;
|
|
1033
2037
|
wasClean?: boolean;
|
|
1034
2038
|
} | {
|
|
1035
|
-
kind:
|
|
2039
|
+
kind: "aborted";
|
|
1036
2040
|
cause?: unknown;
|
|
1037
2041
|
code?: number;
|
|
1038
2042
|
reason?: string;
|
|
1039
2043
|
wasClean?: boolean;
|
|
1040
2044
|
} | {
|
|
1041
|
-
kind:
|
|
2045
|
+
kind: "error";
|
|
1042
2046
|
cause: unknown;
|
|
1043
2047
|
code?: number;
|
|
1044
2048
|
reason?: string;
|
|
1045
2049
|
wasClean?: boolean;
|
|
1046
2050
|
};
|
|
1047
|
-
/**
|
|
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
|
+
*/
|
|
1048
2083
|
export interface WebSocketSession<TIncoming = unknown, TOutgoing = never> extends AsyncDisposable {
|
|
1049
2084
|
readonly bufferedAmount: number;
|
|
1050
2085
|
readonly connection: WebSocketConnectionInfo;
|
|
@@ -1057,34 +2092,133 @@ export interface WebSocketSession<TIncoming = unknown, TOutgoing = never> extend
|
|
|
1057
2092
|
onStateChange(listener: (state: WebSocketState) => void): () => void;
|
|
1058
2093
|
send(message: TOutgoing): void;
|
|
1059
2094
|
}
|
|
1060
|
-
/**
|
|
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
|
+
*/
|
|
1061
2106
|
export type SocketAwaitResult<TIncoming, TOutgoing = never> = [error: null, socket: WebSocketSession<TIncoming, TOutgoing>, connection: WebSocketConnectionInfo] | [error: Fault<undefined>, socket: undefined, connection: WebSocketConnectionInfo | undefined];
|
|
1062
2107
|
interface UseWebSocketBaseConfig<TIncoming = unknown, TOutgoing = unknown> {
|
|
1063
|
-
beforeConnect?: ClientWebSocketOptions[
|
|
2108
|
+
beforeConnect?: ClientWebSocketOptions["beforeConnect"];
|
|
1064
2109
|
heartbeat?: WebSocketHeartbeatConfig<TIncoming, TOutgoing>;
|
|
1065
2110
|
protocols?: readonly string[];
|
|
1066
|
-
reconnect?: ClientWebSocketOptions[
|
|
2111
|
+
reconnect?: ClientWebSocketOptions["reconnect"];
|
|
1067
2112
|
}
|
|
1068
|
-
/**
|
|
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
|
+
*/
|
|
1069
2124
|
export type UseWebSocketConfig<TIncoming = unknown, TOutgoing = unknown> = UseWebSocketBaseConfig<TIncoming, TOutgoing> & UseCancellationConfig;
|
|
1070
|
-
/**
|
|
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
|
+
*/
|
|
1071
2159
|
export type WebSocketExecuteOptions<TIncoming = unknown, TOutgoing = unknown> = UseWebSocketConfig<TIncoming, TOutgoing> & {
|
|
1072
2160
|
signal?: AbortSignal;
|
|
1073
2161
|
};
|
|
1074
|
-
/**
|
|
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
|
+
*/
|
|
1075
2171
|
export interface WebSocketCommand<TInput extends AnyStruct | undefined, TIncoming extends AnyStruct, TOutgoing extends AnyStruct | undefined> extends BaseCommand<typeof WEB_SOCKET_COMMAND> {
|
|
1076
2172
|
readonly endpoint: WebSocketDefinition<TInput, TIncoming, TOutgoing>;
|
|
1077
2173
|
readonly input: EndpointInput<TInput> | undefined;
|
|
1078
2174
|
}
|
|
1079
|
-
/**
|
|
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
|
+
*/
|
|
1080
2190
|
export type WebSocketEndpoint<TInput extends AnyStruct | undefined, TIncoming extends AnyStruct, TOutgoing extends AnyStruct | undefined> = EndpointCallable<TInput, WebSocketCommand<TInput, TIncoming, TOutgoing>>;
|
|
1081
|
-
/**
|
|
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
|
+
*/
|
|
1082
2198
|
export type ManualSocketCloseReason = {
|
|
1083
2199
|
code?: number;
|
|
1084
|
-
kind:
|
|
2200
|
+
kind: "manual-web-socket-close";
|
|
1085
2201
|
reason?: string;
|
|
1086
2202
|
};
|
|
1087
|
-
/**
|
|
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
|
+
*/
|
|
1088
2222
|
export interface WebSocketHeartbeatConfig<TIncoming = unknown, TOutgoing = unknown> {
|
|
1089
2223
|
intervalMs: number;
|
|
1090
2224
|
isAck?: (message: TIncoming) => boolean;
|
|
@@ -1097,20 +2231,17 @@ export interface WebSocketHeartbeatConfig<TIncoming = unknown, TOutgoing = unkno
|
|
|
1097
2231
|
* Pass path, incoming (and optional outgoing) message structs, and queue limits.
|
|
1098
2232
|
* Call the returned endpoint with input to get a `WebSocketCommand` for `client.execute`.
|
|
1099
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.
|
|
2237
|
+
*
|
|
1100
2238
|
* @param definition - WebSocket contract (path, structs, queue limits, optional input).
|
|
1101
2239
|
* @returns An endpoint that creates `WebSocketCommand` values from input.
|
|
1102
2240
|
*
|
|
1103
|
-
* @
|
|
1104
|
-
*
|
|
1105
|
-
* const useChat = defineWebSocket({
|
|
1106
|
-
* maxIncomingQueueSize: 16,
|
|
1107
|
-
* path: '/ws/chat',
|
|
1108
|
-
* incoming: struct.object({ text: struct.string(), type: struct.literal('message') }),
|
|
1109
|
-
* outgoing: struct.object({ text: struct.string(), type: struct.literal('message') }),
|
|
1110
|
-
* })
|
|
1111
|
-
* ```
|
|
2241
|
+
* @group web-socket
|
|
2242
|
+
* @exampleFile examples/api/define-web-socket.ts#example
|
|
1112
2243
|
*/
|
|
1113
|
-
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>,
|
|
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"> & {
|
|
1114
2245
|
input?: TInput;
|
|
1115
2246
|
path: P;
|
|
1116
2247
|
} & BuilderField<NoInfer<TInput>, WebSocketRequestShape<NoInfer<P>>>): WebSocketEndpoint<TInput, TIncoming, TOutgoing>;
|
|
@@ -1119,75 +2250,168 @@ export declare function defineWebSocket<const P extends string, TInput extends A
|
|
|
1119
2250
|
*
|
|
1120
2251
|
* Prefer `client.execute(command)` in application code; this is the low-level entry used by the client.
|
|
1121
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
|
+
*
|
|
1122
2257
|
* @param clientConfig - Resolved client configuration.
|
|
1123
2258
|
* @param command - WebSocket command from a `WebSocketEndpoint`.
|
|
1124
2259
|
* @param options - Optional execute options (heartbeat, reconnect, abort).
|
|
1125
2260
|
* @returns Await-result tuple of `[error, socket, connection]`.
|
|
2261
|
+
* @group web-socket
|
|
1126
2262
|
*/
|
|
1127
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>>>;
|
|
1128
|
-
/**
|
|
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
|
|
2280
|
+
*/
|
|
1129
2281
|
export type SocketLifecycleOutcome = {
|
|
1130
2282
|
closeInfo: WebSocketCloseSnapshot;
|
|
1131
2283
|
connection?: WebSocketConnectionInfo;
|
|
1132
2284
|
cause?: unknown;
|
|
1133
2285
|
opened: boolean;
|
|
1134
2286
|
};
|
|
1135
|
-
//#endregion
|
|
1136
|
-
//#region src/interceptor/interceptor.d.ts
|
|
1137
2287
|
/**
|
|
1138
|
-
* Continuation that runs the rest of the HTTP interceptor chain
|
|
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`.
|
|
1139
2296
|
*
|
|
1140
|
-
*
|
|
1141
|
-
* then the Fetch handler. It is not a re-dispatch of `client.execute` / `axios(config)`
|
|
1142
|
-
* from the outside of the onion. Call `next` again for a safe replay (for example one
|
|
1143
|
-
* 401 refresh) from the same interceptor; outer layers do not run a second time.
|
|
2297
|
+
* @group interceptors
|
|
1144
2298
|
*/
|
|
1145
2299
|
type HttpInterceptorNext = (req: HttpRequest) => Promise<HttpResponse<unknown>>;
|
|
1146
2300
|
/**
|
|
1147
|
-
* HTTP interceptor function: may inspect
|
|
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
|
|
1148
2307
|
*/
|
|
1149
2308
|
type InterceptorFn = (req: HttpRequest, next: HttpInterceptorNext) => Promise<HttpResponse<unknown>>;
|
|
1150
2309
|
/**
|
|
1151
|
-
* Tagged HTTP interceptor
|
|
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
|
|
1152
2316
|
*/
|
|
1153
2317
|
interface HttpInterceptor {
|
|
1154
|
-
kind:
|
|
2318
|
+
kind: "http";
|
|
1155
2319
|
fn: InterceptorFn;
|
|
1156
2320
|
}
|
|
1157
2321
|
/**
|
|
1158
|
-
*
|
|
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`.
|
|
1159
2332
|
*
|
|
1160
2333
|
* @param fn - HTTP interceptor function.
|
|
1161
2334
|
* @returns An `HttpInterceptor` with `kind: 'http'`.
|
|
2335
|
+
* @group interceptors
|
|
1162
2336
|
*/
|
|
1163
2337
|
export declare function createHttpInterceptor(fn: InterceptorFn): HttpInterceptor;
|
|
1164
2338
|
/**
|
|
1165
|
-
* Terminal SSE
|
|
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
|
|
1166
2351
|
*/
|
|
1167
2352
|
type SSEHandler = (req: HttpRequest) => Promise<EventStreamHandle<unknown>>;
|
|
1168
2353
|
/**
|
|
1169
|
-
* SSE interceptor function: may inspect
|
|
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
|
|
1170
2365
|
*/
|
|
1171
2366
|
type SSEInterceptorFn = (req: HttpRequest, next: SSEHandler) => Promise<EventStreamHandle<unknown>>;
|
|
1172
2367
|
/**
|
|
1173
|
-
* 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
|
|
1174
2375
|
*/
|
|
1175
2376
|
interface SSEInterceptor {
|
|
1176
|
-
kind:
|
|
2377
|
+
kind: "sse";
|
|
1177
2378
|
fn: SSEInterceptorFn;
|
|
1178
2379
|
}
|
|
1179
2380
|
/**
|
|
1180
|
-
*
|
|
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.
|
|
1181
2394
|
*
|
|
1182
2395
|
* @param fn - SSE interceptor function.
|
|
1183
2396
|
* @returns An `SSEInterceptor` with `kind: 'sse'`.
|
|
2397
|
+
* @group interceptors
|
|
1184
2398
|
*/
|
|
1185
2399
|
export declare function createSSEInterceptor(fn: SSEInterceptorFn): SSEInterceptor;
|
|
1186
2400
|
/**
|
|
1187
|
-
* Minimal WebSocket session surface
|
|
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`.
|
|
1188
2406
|
*
|
|
1189
|
-
*
|
|
1190
|
-
* (
|
|
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
|
|
1191
2415
|
*/
|
|
1192
2416
|
interface WebSocketSessionLike extends AsyncDisposable {
|
|
1193
2417
|
readonly bufferedAmount: number;
|
|
@@ -1207,42 +2431,75 @@ interface WebSocketSessionLike extends AsyncDisposable {
|
|
|
1207
2431
|
send(message: unknown): void;
|
|
1208
2432
|
}
|
|
1209
2433
|
/**
|
|
1210
|
-
* Terminal WebSocket
|
|
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
|
|
1211
2442
|
*/
|
|
1212
2443
|
type WebSocketHandler = (req: HttpRequest) => Promise<WebSocketSessionLike>;
|
|
1213
2444
|
/**
|
|
1214
|
-
* WebSocket interceptor function: may inspect
|
|
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
|
|
1215
2455
|
*/
|
|
1216
2456
|
type WebSocketInterceptorFn = (req: HttpRequest, next: WebSocketHandler) => Promise<WebSocketSessionLike>;
|
|
1217
2457
|
/**
|
|
1218
|
-
* 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
|
|
1219
2465
|
*/
|
|
1220
2466
|
interface WebSocketInterceptor {
|
|
1221
|
-
kind:
|
|
2467
|
+
kind: "web-socket";
|
|
1222
2468
|
fn: WebSocketInterceptorFn;
|
|
1223
2469
|
}
|
|
1224
2470
|
/**
|
|
1225
|
-
*
|
|
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.
|
|
1226
2477
|
*
|
|
1227
2478
|
* @param fn - WebSocket interceptor function.
|
|
1228
2479
|
* @returns A `WebSocketInterceptor` with `kind: 'web-socket'`.
|
|
2480
|
+
* @group interceptors
|
|
1229
2481
|
*/
|
|
1230
2482
|
export declare function createWebSocketInterceptor(fn: WebSocketInterceptorFn): WebSocketInterceptor;
|
|
1231
2483
|
/**
|
|
1232
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
|
|
1233
2491
|
*/
|
|
1234
2492
|
type Interceptor = HttpInterceptor | SSEInterceptor | WebSocketInterceptor;
|
|
1235
|
-
//#endregion
|
|
1236
|
-
//#region src/client/config.d.ts
|
|
1237
2493
|
/**
|
|
1238
2494
|
* Serializes an encoded query object into a query string.
|
|
1239
2495
|
*
|
|
1240
|
-
* `query` is the wire object: aliases already applied, `Date` values already ISO
|
|
1241
|
-
*
|
|
1242
|
-
*
|
|
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)`.
|
|
2497
|
+
*
|
|
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.
|
|
1243
2499
|
*
|
|
1244
2500
|
* @param query - Encoded query object.
|
|
1245
2501
|
* @returns The query string without a leading `?`.
|
|
2502
|
+
* @group client
|
|
1246
2503
|
*/
|
|
1247
2504
|
type QuerySerializer = (query: {
|
|
1248
2505
|
[key: string]: unknown;
|
|
@@ -1250,24 +2507,52 @@ type QuerySerializer = (query: {
|
|
|
1250
2507
|
/**
|
|
1251
2508
|
* Fetch-compatible function used as an HTTP or SSE handle.
|
|
1252
2509
|
*
|
|
1253
|
-
* This is the `(input, init?) => Promise<Response>` call signature Defjs invokes,
|
|
1254
|
-
*
|
|
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
|
|
1255
2515
|
*/
|
|
1256
2516
|
type FetchHandle = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
1257
2517
|
/**
|
|
1258
|
-
* Minimal WebSocket
|
|
2518
|
+
* Minimal WebSocket instance surface used by the client transport.
|
|
1259
2519
|
*
|
|
1260
|
-
*
|
|
1261
|
-
*
|
|
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`.
|
|
2521
|
+
*
|
|
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
|
|
1262
2525
|
*/
|
|
1263
|
-
type WebSocketHandle = Omit<WebSocket,
|
|
2526
|
+
type WebSocketHandle = Omit<WebSocket, "binaryType" | "CLOSED" | "CLOSING" | "CONNECTING" | "dispatchEvent" | "onclose" | "onerror" | "onmessage" | "onopen" | "OPEN" | "readyState"> & {
|
|
1264
2527
|
readonly readyState: number;
|
|
1265
2528
|
};
|
|
1266
|
-
/**
|
|
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
|
+
*/
|
|
1267
2538
|
type WebSocketHandleConstructor = {
|
|
1268
2539
|
readonly OPEN: number;
|
|
1269
2540
|
} & (new (...args: never[]) => WebSocketHandle);
|
|
1270
|
-
/**
|
|
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
|
+
*/
|
|
1271
2556
|
interface ClientWebSocketOptions {
|
|
1272
2557
|
/** WebSocket constructor; defaults to `globalThis.WebSocket`. */
|
|
1273
2558
|
handle?: WebSocketHandleConstructor;
|
|
@@ -1291,15 +2576,15 @@ interface ClientWebSocketOptions {
|
|
|
1291
2576
|
protocols?: readonly string[];
|
|
1292
2577
|
/** Automatic reconnect policy after unexpected closes. */
|
|
1293
2578
|
reconnect?: {
|
|
1294
|
-
/**
|
|
2579
|
+
/** Retries per outage; a successful open resets the budget. Non-negative safe integer; defaults to 3. `0` disables. */
|
|
1295
2580
|
attempts?: number;
|
|
1296
2581
|
/** Initial backoff delay in milliseconds. */
|
|
1297
2582
|
delayMs?: number;
|
|
1298
|
-
/** Multiplier applied
|
|
2583
|
+
/** Multiplier applied after each failed attempt of the current outage. A successful `open` resets the exponent. */
|
|
1299
2584
|
factor?: number;
|
|
1300
2585
|
/** Random jitter factor applied to the delay (0–1). */
|
|
1301
2586
|
jitter?: number;
|
|
1302
|
-
/**
|
|
2587
|
+
/** Non-negative cap on the final backoff delay, including jitter; `Infinity` removes the configured cap. */
|
|
1303
2588
|
maxDelayMs?: number;
|
|
1304
2589
|
/** Return `false` to skip reconnecting for this close. */
|
|
1305
2590
|
shouldReconnect?: (context: {
|
|
@@ -1311,13 +2596,25 @@ interface ClientWebSocketOptions {
|
|
|
1311
2596
|
}) => boolean;
|
|
1312
2597
|
};
|
|
1313
2598
|
}
|
|
1314
|
-
/**
|
|
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
|
+
*/
|
|
1315
2612
|
interface ClientSSEOptions {
|
|
1316
|
-
/** 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. */
|
|
1317
2614
|
handle?: FetchHandle;
|
|
1318
2615
|
/** Called when an event has no matching struct or fails validation. */
|
|
1319
2616
|
onInvalidEvent?: (context: {
|
|
1320
|
-
reason:
|
|
2617
|
+
reason: "missing-struct" | "validation-failed";
|
|
1321
2618
|
message: {
|
|
1322
2619
|
id: string;
|
|
1323
2620
|
event: string;
|
|
@@ -1328,21 +2625,25 @@ interface ClientSSEOptions {
|
|
|
1328
2625
|
}) => unknown;
|
|
1329
2626
|
/** Automatic reconnect policy after stream failures. */
|
|
1330
2627
|
reconnect?: {
|
|
1331
|
-
/**
|
|
2628
|
+
/** Retries per outage; a successful handshake resets the budget. Non-negative safe integer; defaults to 3. `0` disables. */
|
|
1332
2629
|
attempts?: number;
|
|
1333
|
-
/** Initial backoff delay in milliseconds. */
|
|
2630
|
+
/** Initial backoff delay in milliseconds, finite and non-negative. Defaults to 1000. */
|
|
1334
2631
|
delayMs?: number;
|
|
1335
|
-
/**
|
|
2632
|
+
/** Finite positive multiplier per failed retry, reset after a successful handshake. Defaults to 1. */
|
|
1336
2633
|
factor?: number;
|
|
1337
|
-
/**
|
|
2634
|
+
/** Finite random jitter factor applied to the delay (0–1). Defaults to 0. */
|
|
1338
2635
|
jitter?: number;
|
|
1339
|
-
/**
|
|
2636
|
+
/** Non-negative cap on the final delay, including jitter; no configured cap when omitted or `Infinity`. */
|
|
1340
2637
|
maxDelayMs?: number;
|
|
1341
|
-
/**
|
|
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
|
+
*/
|
|
1342
2642
|
shouldReconnect?: (context: {
|
|
1343
2643
|
attempt: number;
|
|
1344
2644
|
cause?: unknown;
|
|
1345
2645
|
lastEventId: string;
|
|
2646
|
+
method: string;
|
|
1346
2647
|
open?: {
|
|
1347
2648
|
response: {
|
|
1348
2649
|
status: number;
|
|
@@ -1354,7 +2655,13 @@ interface ClientSSEOptions {
|
|
|
1354
2655
|
}) => boolean | Promise<boolean>;
|
|
1355
2656
|
};
|
|
1356
2657
|
}
|
|
1357
|
-
/**
|
|
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
|
+
*/
|
|
1358
2665
|
interface ClientSSEConfig extends ClientSSEOptions {
|
|
1359
2666
|
/** Fetch implementation used to open the event stream. */
|
|
1360
2667
|
handle: FetchHandle;
|
|
@@ -1365,7 +2672,7 @@ interface HttpClientOptions {
|
|
|
1365
2672
|
endpoint: string;
|
|
1366
2673
|
/** Static default headers merged under command headers for HTTP/SSE. */
|
|
1367
2674
|
headers?: Headers;
|
|
1368
|
-
/** HTTP transport overrides. */
|
|
2675
|
+
/** HTTP transport overrides; the default resolves global `fetch` on each call. */
|
|
1369
2676
|
http?: {
|
|
1370
2677
|
handle?: FetchHandle;
|
|
1371
2678
|
};
|
|
@@ -1389,7 +2696,13 @@ interface HttpClientOptions {
|
|
|
1389
2696
|
/** When `true`, credentials are included with requests. */
|
|
1390
2697
|
withCredentials?: boolean;
|
|
1391
2698
|
}
|
|
1392
|
-
/**
|
|
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
|
+
*/
|
|
1393
2706
|
interface ClientOptions extends HttpClientOptions {
|
|
1394
2707
|
/** SSE transport and reconnect options. */
|
|
1395
2708
|
sse?: ClientSSEOptions;
|
|
@@ -1426,158 +2739,304 @@ interface HttpClientConfig {
|
|
|
1426
2739
|
/** When `true`, credentials are included with requests. */
|
|
1427
2740
|
withCredentials?: boolean;
|
|
1428
2741
|
}
|
|
1429
|
-
/**
|
|
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
|
+
*/
|
|
1430
2751
|
interface ClientConfig extends HttpClientConfig {
|
|
1431
2752
|
/** Resolved SSE configuration. */
|
|
1432
2753
|
sse: ClientSSEConfig;
|
|
1433
2754
|
/** Resolved WebSocket configuration. */
|
|
1434
2755
|
webSocket: ClientWebSocketOptions;
|
|
1435
2756
|
}
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
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
|
+
*/
|
|
1439
2765
|
type ClientOption = (config: ClientConfig) => void;
|
|
1440
2766
|
/**
|
|
1441
|
-
* 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:`.
|
|
1442
2770
|
*
|
|
1443
|
-
*
|
|
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.
|
|
2772
|
+
*
|
|
2773
|
+
* @param endpoint - Absolute base endpoint. Missing or invalid endpoints fail request building.
|
|
1444
2774
|
* @returns An option that writes `config.endpoint`.
|
|
2775
|
+
* @group client
|
|
1445
2776
|
*/
|
|
1446
2777
|
export declare function withEndpoint(endpoint: string): ClientOption;
|
|
1447
2778
|
/**
|
|
1448
|
-
* Sets static default headers for HTTP
|
|
2779
|
+
* Sets static default headers for HTTP and SSE requests built by this client.
|
|
2780
|
+
*
|
|
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.
|
|
1449
2782
|
*
|
|
1450
|
-
*
|
|
1451
|
-
*
|
|
1452
|
-
* or a refreshed credential
|
|
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.
|
|
1453
2786
|
*
|
|
1454
2787
|
* @param headers - `HeadersInit` bag cloned onto the client config.
|
|
1455
2788
|
* @returns An option that writes `config.headers`.
|
|
2789
|
+
* @group client
|
|
1456
2790
|
*/
|
|
1457
2791
|
export declare function withHeaders(headers: HeadersInit): ClientOption;
|
|
1458
2792
|
/**
|
|
1459
|
-
* Sets the default timeout for HTTP `execute` only.
|
|
2793
|
+
* Sets the default timeout, in milliseconds, for HTTP `execute` only.
|
|
2794
|
+
*
|
|
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.
|
|
1460
2796
|
*
|
|
1461
|
-
*
|
|
1462
|
-
* `{ timeout }` overrides this default. Execute `{ abort }` combines with the
|
|
1463
|
-
* default (abort → `ABORTED`, timer → `TIMEOUT`). SSE and WebSocket ignore this
|
|
1464
|
-
* option. XOR still rejects execute options that pass both `abort` and `timeout`.
|
|
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.
|
|
1465
2798
|
*
|
|
1466
2799
|
* @param ms - Positive safe integer timeout in milliseconds.
|
|
1467
2800
|
* @returns An option that writes `config.timeout`.
|
|
2801
|
+
* @group client
|
|
1468
2802
|
*/
|
|
1469
2803
|
export declare function withTimeout(ms: number): ClientOption;
|
|
1470
2804
|
/**
|
|
1471
2805
|
* Appends interceptors to the client interceptor chain.
|
|
1472
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
|
+
*
|
|
1473
2809
|
* @param interceptors - Interceptors applied in registration order.
|
|
1474
2810
|
* @returns An option that pushes onto `config.interceptors`.
|
|
2811
|
+
* @group client
|
|
1475
2812
|
*/
|
|
1476
2813
|
export declare function withInterceptors(...interceptors: Interceptor[]): ClientOption;
|
|
1477
2814
|
/**
|
|
1478
|
-
*
|
|
2815
|
+
* Replaces query encoding for HTTP, SSE, and WebSocket requests.
|
|
2816
|
+
*
|
|
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.
|
|
1479
2818
|
*
|
|
1480
|
-
*
|
|
1481
|
-
* Without this option, queries use `URLSearchParams`: arrays repeat keys and a nested
|
|
1482
|
-
* object fails. A builder that returns `URLSearchParams` skips this serializer.
|
|
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.
|
|
1483
2820
|
*
|
|
1484
2821
|
* @param serializer - Function that turns an encoded query object into a string.
|
|
1485
2822
|
* @returns An option that sets `config.querySerializer`.
|
|
2823
|
+
* @group client
|
|
1486
2824
|
*/
|
|
1487
2825
|
export declare function withQuerySerializer(serializer: QuerySerializer): ClientOption;
|
|
1488
2826
|
/**
|
|
1489
|
-
*
|
|
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}.
|
|
1490
2830
|
*
|
|
1491
2831
|
* @param fetchImpl - `(input, init?) => Promise<Response>`. Not `typeof fetch`, so
|
|
1492
2832
|
* a mock does not need host extras such as `preconnect`.
|
|
1493
2833
|
* @returns An option that sets `config.http.handle`.
|
|
2834
|
+
* @group client
|
|
1494
2835
|
*/
|
|
1495
2836
|
export declare function withHTTPHandle(fetchImpl: FetchHandle): ClientOption;
|
|
1496
2837
|
/**
|
|
1497
|
-
*
|
|
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}.
|
|
1498
2841
|
*
|
|
1499
2842
|
* @param fetchImpl - `(input, init?) => Promise<Response>`. Not `typeof fetch`, so
|
|
1500
2843
|
* a mock does not need host extras such as `preconnect`.
|
|
1501
2844
|
* @returns An option that sets `config.sse.handle`.
|
|
2845
|
+
* @group client
|
|
1502
2846
|
*/
|
|
1503
2847
|
export declare function withSSEHandle(fetchImpl: FetchHandle): ClientOption;
|
|
1504
2848
|
/**
|
|
1505
|
-
*
|
|
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.
|
|
1506
2852
|
*
|
|
1507
2853
|
* @param WebSocketImpl - Constructor that returns a `WebSocketHandle`.
|
|
1508
2854
|
* @returns An option that sets `config.webSocket.handle`.
|
|
2855
|
+
* @group client
|
|
1509
2856
|
*/
|
|
1510
2857
|
export declare function withWebSocketHandle(WebSocketImpl: WebSocketHandleConstructor): ClientOption;
|
|
1511
2858
|
/**
|
|
1512
|
-
* Runs
|
|
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.
|
|
1513
2871
|
*
|
|
1514
2872
|
* @param beforeConnect - Hook receiving attempt index and abort signal.
|
|
1515
2873
|
* @returns An option that sets `config.webSocket.beforeConnect`.
|
|
2874
|
+
* @group client
|
|
1516
2875
|
*/
|
|
1517
|
-
export declare function withWebSocketBeforeConnect(beforeConnect: ClientWebSocketOptions[
|
|
2876
|
+
export declare function withWebSocketBeforeConnect(beforeConnect: ClientWebSocketOptions["beforeConnect"]): ClientOption;
|
|
1518
2877
|
/**
|
|
1519
|
-
*
|
|
2878
|
+
* Sets the WebSocket subprotocols offered during the handshake.
|
|
1520
2879
|
*
|
|
1521
|
-
*
|
|
1522
|
-
*
|
|
1523
|
-
*
|
|
1524
|
-
* `['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.
|
|
1525
2883
|
*
|
|
1526
2884
|
* @param protocols - Subprotocol list for the WebSocket constructor.
|
|
1527
2885
|
* @returns An option that sets `config.webSocket.protocols`.
|
|
2886
|
+
* @group client
|
|
1528
2887
|
*/
|
|
1529
2888
|
export declare function withWebSocketProtocols(protocols: readonly string[]): ClientOption;
|
|
1530
2889
|
/**
|
|
1531
|
-
* Enables WebSocket heartbeat
|
|
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.
|
|
1532
2909
|
*
|
|
1533
2910
|
* @param options - Heartbeat interval, payload, timeout, and ACK predicate.
|
|
1534
2911
|
* @returns An option that sets `config.webSocket.heartbeat`.
|
|
2912
|
+
* @group client
|
|
1535
2913
|
*/
|
|
1536
|
-
export declare function withWebSocketHeartbeat(options: ClientWebSocketOptions[
|
|
2914
|
+
export declare function withWebSocketHeartbeat(options: ClientWebSocketOptions["heartbeat"]): ClientOption;
|
|
1537
2915
|
/**
|
|
1538
|
-
* Configures automatic
|
|
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.
|
|
1539
2936
|
*
|
|
1540
2937
|
* @param options - Retry budget, backoff, and `shouldReconnect` gate.
|
|
1541
2938
|
* @returns An option that sets `config.webSocket.reconnect`.
|
|
2939
|
+
* @group client
|
|
1542
2940
|
*/
|
|
1543
|
-
export declare function withWebSocketReconnect(options: ClientWebSocketOptions[
|
|
2941
|
+
export declare function withWebSocketReconnect(options: ClientWebSocketOptions["reconnect"]): ClientOption;
|
|
1544
2942
|
/**
|
|
1545
|
-
*
|
|
2943
|
+
* Observes SSE events that have no matching event struct or fail validation.
|
|
2944
|
+
*
|
|
2945
|
+
* The handler may be async and is awaited. The callback receives:
|
|
2946
|
+
*
|
|
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.
|
|
1546
2959
|
*
|
|
1547
|
-
* @param handler -
|
|
2960
|
+
* @param handler - Observer callback for invalid events.
|
|
1548
2961
|
* @returns An option that sets `config.sse.onInvalidEvent`.
|
|
2962
|
+
* @group client
|
|
1549
2963
|
*/
|
|
1550
|
-
export declare function withSSEOnInvalidEvent(handler: ClientSSEOptions[
|
|
2964
|
+
export declare function withSSEOnInvalidEvent(handler: ClientSSEOptions["onInvalidEvent"]): ClientOption;
|
|
1551
2965
|
/**
|
|
1552
|
-
* Configures automatic
|
|
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.
|
|
1553
2997
|
*
|
|
1554
2998
|
* @param options - Retry budget, backoff, and `shouldReconnect` gate.
|
|
1555
2999
|
* @returns An option that sets `config.sse.reconnect`.
|
|
3000
|
+
* @group client
|
|
1556
3001
|
*/
|
|
1557
|
-
export declare function withSSEReconnect(options: ClientSSEOptions[
|
|
3002
|
+
export declare function withSSEReconnect(options: ClientSSEOptions["reconnect"]): ClientOption;
|
|
1558
3003
|
/**
|
|
1559
3004
|
* Enables XSRF cookie-to-header token forwarding for HTTP requests.
|
|
1560
3005
|
*
|
|
1561
|
-
*
|
|
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.
|
|
1562
3011
|
*
|
|
1563
3012
|
* @param options - Cookie/header names and optional token provider.
|
|
1564
3013
|
* @returns An option that sets `config.xsrf`.
|
|
3014
|
+
* @group client
|
|
1565
3015
|
*/
|
|
1566
|
-
export declare function withXSRF(options?: HttpClientOptions[
|
|
3016
|
+
export declare function withXSRF(options?: HttpClientOptions["xsrf"]): ClientOption;
|
|
1567
3017
|
/**
|
|
1568
|
-
* Sets whether HTTP
|
|
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.
|
|
1569
3021
|
*
|
|
1570
3022
|
* @param value - When `true`, credentials are sent with cross-origin requests.
|
|
1571
3023
|
* @returns An option that sets `config.withCredentials`.
|
|
3024
|
+
* @group client
|
|
1572
3025
|
*/
|
|
1573
3026
|
export declare function withCredentials(value: boolean): ClientOption;
|
|
1574
|
-
//#endregion
|
|
1575
|
-
//#region src/client/client.d.ts
|
|
1576
3027
|
declare const CLIENT: unique symbol;
|
|
1577
3028
|
/**
|
|
1578
|
-
*
|
|
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}
|
|
1579
3037
|
*/
|
|
1580
3038
|
type Client = {
|
|
3039
|
+
/** Internal symbol key for the resolved client configuration. */
|
|
1581
3040
|
readonly [CLIENT]: ClientConfig;
|
|
1582
3041
|
/** Open an SSE command and return stream open/await results. */
|
|
1583
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>>;
|
|
@@ -1587,45 +3046,69 @@ type Client = {
|
|
|
1587
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>>;
|
|
1588
3047
|
};
|
|
1589
3048
|
/**
|
|
1590
|
-
*
|
|
3049
|
+
* Creates a Defjs client from option helpers.
|
|
1591
3050
|
*
|
|
1592
|
-
* Options
|
|
1593
|
-
* 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.
|
|
1594
3052
|
*
|
|
1595
3053
|
* @param options - Client option helpers such as `withEndpoint`.
|
|
1596
3054
|
* @returns A client that can `execute` HTTP, SSE, and WebSocket commands.
|
|
1597
3055
|
*
|
|
1598
|
-
* @
|
|
1599
|
-
*
|
|
1600
|
-
*
|
|
1601
|
-
*
|
|
3056
|
+
* @group client
|
|
3057
|
+
* @see {@link Client}
|
|
3058
|
+
* @see {@link ClientOption}
|
|
3059
|
+
* @exampleFile examples/api/create-client.ts#example
|
|
1602
3060
|
*/
|
|
1603
3061
|
export declare function createClient(...options: ClientOption[]): Client;
|
|
1604
|
-
//#endregion
|
|
1605
|
-
//#region src/http/transport/fetch.d.ts
|
|
1606
3062
|
/**
|
|
1607
3063
|
* Default HTTP transport: perform `httpRequest` with `fetch` and return an `HttpResponse`.
|
|
1608
|
-
*
|
|
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.
|
|
1609
3069
|
*
|
|
1610
3070
|
* @param httpRequest - Normalized request (URL, headers, body, abort, progress hooks).
|
|
1611
3071
|
* @param fetchImpl - Fetch implementation; defaults to global `fetch`. The library default handle is called as
|
|
1612
|
-
* `(url, init)` and its body is read in one piece unless download progress is observed. Any other handle receives a
|
|
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
|
|
1613
3073
|
* `Request` and its body is read chunk by chunk.
|
|
1614
|
-
* @returns Parsed `HttpResponse`. Rejects when no response reached the client,
|
|
1615
|
-
*
|
|
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
|
|
1616
3077
|
*/
|
|
1617
3078
|
export declare function fetchHandler(httpRequest: HttpRequest, fetchImpl?: FetchHandle): Promise<HttpResponse<unknown>>;
|
|
1618
|
-
|
|
1619
|
-
|
|
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
|
+
*/
|
|
1620
3096
|
export declare function createResolvedRequestUrl(baseEndpoint: string, path: string, queryString?: string): URL;
|
|
1621
|
-
//#endregion
|
|
1622
|
-
//#region src/interceptor/basic_auth.d.ts
|
|
1623
3097
|
type BasicCredential = {
|
|
1624
3098
|
username: string;
|
|
1625
3099
|
password: string;
|
|
1626
3100
|
};
|
|
1627
3101
|
/**
|
|
1628
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
|
|
1629
3112
|
*/
|
|
1630
3113
|
type BasicAuthInterceptorOptions = {
|
|
1631
3114
|
encode?: (credential: BasicCredential) => string;
|
|
@@ -1633,18 +3116,32 @@ type BasicAuthInterceptorOptions = {
|
|
|
1633
3116
|
/**
|
|
1634
3117
|
* HTTP interceptor that sets an `Authorization: Basic …` header from credentials.
|
|
1635
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
|
+
*
|
|
1636
3127
|
* @param fn - Factory returning username/password for each request.
|
|
1637
3128
|
* @param options - Optional custom encoder (defaults to `btoa`).
|
|
1638
3129
|
* @returns An `HttpInterceptor` that injects basic auth.
|
|
3130
|
+
* @group interceptors
|
|
1639
3131
|
*/
|
|
1640
3132
|
export declare function basicAuthHttpInterceptor(fn: () => BasicCredential, options?: BasicAuthInterceptorOptions): HttpInterceptor;
|
|
1641
3133
|
/**
|
|
1642
3134
|
* SSE interceptor that sets an `Authorization: Basic …` header from credentials.
|
|
1643
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
|
+
*
|
|
1644
3141
|
* @param fn - Factory returning username/password for each request.
|
|
1645
3142
|
* @param options - Optional custom encoder (defaults to `btoa`).
|
|
1646
3143
|
* @returns An `SSEInterceptor` that injects basic auth.
|
|
3144
|
+
* @group interceptors
|
|
1647
3145
|
*/
|
|
1648
3146
|
export declare function basicAuthSSEInterceptor(fn: () => BasicCredential, options?: BasicAuthInterceptorOptions): SSEInterceptor;
|
|
1649
|
-
//#endregion
|
|
1650
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 };
|