@defjs/core 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (5) hide show
  1. package/README.md +25 -71
  2. package/index.d.ts +387 -525
  3. package/index.js +742 -995
  4. package/index.min.js +20 -20
  5. package/package.json +1 -1
package/index.js CHANGED
@@ -1,23 +1,3 @@
1
- // src/client/command.ts
2
- const COMMAND_TYPE = Symbol.for("defjs.command.type");
3
- const HTTP_COMMAND = Symbol.for("defjs.command.http");
4
- const EVENT_STREAM_COMMAND = Symbol.for("defjs.command.event-stream");
5
- const WEB_SOCKET_COMMAND = Symbol.for("defjs.command.web-socket");
6
- /** The command's own runner, so `client.execute` never imports a transport the app does not use. */
7
- const COMMAND_EXECUTE = Symbol.for("defjs.command.execute");
8
- function commandTypeOf(value) {
9
- if (typeof value !== "object" || value === null || !(COMMAND_TYPE in value)) return;
10
- return value[COMMAND_TYPE];
11
- }
12
- function isHttpCommand(value) {
13
- return commandTypeOf(value) === HTTP_COMMAND;
14
- }
15
- function isEventStreamCommand(value) {
16
- return commandTypeOf(value) === EVENT_STREAM_COMMAND;
17
- }
18
- function isWebSocketCommand(value) {
19
- return commandTypeOf(value) === WEB_SOCKET_COMMAND;
20
- }
21
1
  // src/client/config.ts
22
2
  const PLATFORM_FETCH = globalThis.fetch;
23
3
  const PLATFORM_FETCH_HANDLE = PLATFORM_FETCH?.bind(globalThis);
@@ -61,149 +41,498 @@ function createClientConfig() {
61
41
  };
62
42
  }
63
43
  // src/client/client.ts
44
+ /**
45
+ * Internal symbol key for a client's base configuration and request options.
46
+ *
47
+ * Application code passes the client to an operation. It does not call the client or read this property.
48
+ *
49
+ * @group client
50
+ */
64
51
  const CLIENT = Symbol("Client");
52
+ const KNOWN_FIELDS = [
53
+ "signal",
54
+ "onUploadProgress",
55
+ "onDownloadProgress",
56
+ "beforeConnect",
57
+ "protocols",
58
+ "heartbeat",
59
+ "reconnect"
60
+ ];
61
+ function snapshot(patch) {
62
+ const out = {};
63
+ if (patch === void 0) return out;
64
+ for (const key of KNOWN_FIELDS) {
65
+ const value = patch[key];
66
+ if (value !== void 0) out[key] = value;
67
+ }
68
+ return out;
69
+ }
70
+ function createClientValue(config, options) {
71
+ return { [CLIENT]: {
72
+ config,
73
+ options
74
+ } };
75
+ }
65
76
  /**
66
- * Creates a Defjs client from option helpers.
77
+ * Creates a Defjs {@link DerivableClient} from option helpers.
67
78
  *
68
- * 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.
79
+ * 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 an operation invokes transport.
80
+ *
81
+ * The returned factory is a function. Calling it with {@link RequestOptions} returns a {@link Client} value for one call and performs no I/O; see {@link DerivableClient} for the snapshot rules. The factory itself is also a `Client` and carries no request options, so it can be passed to an operation directly. The base configuration is shared by every client value and is not frozen at runtime. It is fixed by convention: nothing in the library changes it, and applications should not either.
69
82
  *
70
83
  * @param options - Client option helpers such as `withEndpoint`.
71
- * @returns A client that can `execute` HTTP, SSE, and WebSocket commands.
84
+ * @returns A callable factory that carries the resolved base configuration.
72
85
  *
73
86
  * @group client
87
+ * @see {@link DerivableClient}
74
88
  * @see {@link Client}
75
89
  * @see {@link ClientOption}
90
+ * @see {@link RequestOptions}
76
91
  * @exampleFile examples/api/create-client.ts#example
77
92
  */
78
93
  function createClient(...options) {
79
- const conf = createClientConfig();
80
- for (const option of options) option(conf);
81
- function execute(command, options) {
82
- if (isHttpCommand(command) || isEventStreamCommand(command) || isWebSocketCommand(command)) return command[COMMAND_EXECUTE](conf, command, options);
83
- return Promise.reject(/* @__PURE__ */ new Error("Unsupported command"));
84
- }
85
- return {
86
- [CLIENT]: conf,
87
- execute
94
+ const config = createClientConfig();
95
+ for (const option of options) option(config);
96
+ function factory(patch) {
97
+ return createClientValue(config, snapshot(patch));
98
+ }
99
+ factory[CLIENT] = {
100
+ config,
101
+ options: {}
88
102
  };
103
+ return factory;
89
104
  }
90
- // src/struct/symbols.ts
91
- const DEFINITION = Symbol.for("defjs.struct.definition");
92
- const OMIT = Symbol("defjs.struct.omit");
105
+ // src/client/option.ts
93
106
  /**
94
- * Stands in for a parsed value when parsing fails; the issue waits in the slot that `failure()` fills.
95
- * Not in the symbol registry, so no input value can ever equal it.
107
+ * Sets the valid absolute base URL prepended to relative request paths.
108
+ *
109
+ * 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 operations, an `http:` base becomes `ws:` and `https:` becomes `wss:`.
110
+ *
111
+ * 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.
112
+ *
113
+ * @param endpoint - Absolute base endpoint. Missing or invalid endpoints fail request building.
114
+ * @returns An option that writes `config.endpoint`.
115
+ * @group client
96
116
  */
97
- const FAILED = Symbol("defjs.struct.failed");
117
+ function withEndpoint(endpoint) {
118
+ return (config) => {
119
+ config.endpoint = endpoint;
120
+ };
121
+ }
98
122
  /**
99
- * Type-only export of the internal enumerable tag key on objects produced while parsing endpoint input, used by the outbound encoder.
123
+ * Sets static default headers for HTTP and SSE requests built by this client.
100
124
  *
101
- * `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.
125
+ * 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.
102
126
  *
103
- * 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.
127
+ * 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.
104
128
  *
105
- * @internal
106
- * @group struct
129
+ * 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.
130
+ *
131
+ * @param headers - `HeadersInit` bag cloned onto the client config.
132
+ * @returns An option that writes `config.headers`.
133
+ * @group client
107
134
  */
108
- const STRUCT = Symbol.for("defjs.struct.STRUCT");
109
- // src/struct/guards.ts
110
- const KNOWN_KINDS = /* @__PURE__ */ new Set([
111
- "any",
112
- "array",
113
- "arrayBuffer",
114
- "bigint",
115
- "blob",
116
- "boolean",
117
- "date",
118
- "discriminatedUnion",
119
- "enum",
120
- "file",
121
- "intersection",
122
- "literal",
123
- "null",
124
- "number",
125
- "object",
126
- "or",
127
- "record",
128
- "string",
129
- "tuple",
130
- "unknown"
131
- ]);
132
- function isStruct(value) {
133
- if (typeof value !== "object" || value === null || !Object.hasOwn(value, DEFINITION)) return false;
134
- const definition = value[DEFINITION];
135
- if (typeof definition !== "object" || definition === null) return false;
136
- if (!KNOWN_KINDS.has(definition.kind)) return false;
137
- const flags = definition.flags;
138
- return typeof flags === "object" && flags !== null && typeof flags.nullable === "boolean" && typeof flags.optional === "boolean";
135
+ function withHeaders(headers) {
136
+ return (config) => {
137
+ config.headers = new Headers(headers);
138
+ };
139
139
  }
140
- // src/struct/guide.ts
141
- const GUIDE_SET = Symbol.for("defjs.struct.guideSet");
142
- function isGuideSet(value) {
143
- return typeof value === "object" && value !== null && value[GUIDE_SET] === true;
140
+ /**
141
+ * Appends interceptors to the client interceptor chain.
142
+ *
143
+ * 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. When an operation runs, only the interceptors of that transport run, in their registration order.
144
+ *
145
+ * @param interceptors - Interceptors applied in registration order.
146
+ * @returns An option that pushes onto `config.interceptors`.
147
+ * @group client
148
+ */
149
+ function withInterceptors(...interceptors) {
150
+ return (config) => {
151
+ config.interceptors.push(...interceptors);
152
+ };
144
153
  }
145
- function guideTag(members) {
146
- const unique = [];
147
- for (const member of members) if (!unique.includes(member)) unique.push(member);
148
- if (unique.length === 0) return;
149
- if (unique.length === 1) return unique[0];
150
- return {
151
- [GUIDE_SET]: true,
152
- members: unique
154
+ /**
155
+ * Replaces query encoding for HTTP, SSE, and WebSocket requests.
156
+ *
157
+ * `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.
158
+ *
159
+ * 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.
160
+ *
161
+ * @param serializer - Function that turns an encoded query object into a string.
162
+ * @returns An option that sets `config.querySerializer`.
163
+ * @group client
164
+ */
165
+ function withQuerySerializer(serializer) {
166
+ return (config) => {
167
+ config.querySerializer = serializer;
153
168
  };
154
169
  }
155
- function guideMembers(guide) {
156
- return isGuideSet(guide) ? guide.members : [guide];
170
+ /**
171
+ * Replaces the Fetch-compatible function used for HTTP requests.
172
+ *
173
+ * 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}.
174
+ *
175
+ * @param fetchImpl - `(input, init?) => Promise<Response>`. Not `typeof fetch`, so
176
+ * a mock does not need host extras such as `preconnect`.
177
+ * @returns An option that sets `config.http.handle`.
178
+ * @group client
179
+ */
180
+ function withHTTPHandle(fetchImpl) {
181
+ return (config) => {
182
+ config.http.handle = fetchImpl;
183
+ };
157
184
  }
158
- function combineGuides(left, right) {
159
- if (!left) return right;
160
- if (!right) return left;
161
- return guideTag([...guideMembers(left), ...guideMembers(right)]);
185
+ /**
186
+ * Replaces the Fetch-compatible function used to open SSE event streams.
187
+ *
188
+ * The default is the platform `fetch`. `fetchImpl` is a {@link FetchHandle}. It only affects SSE. HTTP uses {@link withHTTPHandle}.
189
+ *
190
+ * @param fetchImpl - `(input, init?) => Promise<Response>`. Not `typeof fetch`, so
191
+ * a mock does not need host extras such as `preconnect`.
192
+ * @returns An option that sets `config.sse.handle`.
193
+ * @group client
194
+ */
195
+ function withSSEHandle(fetchImpl) {
196
+ return (config) => {
197
+ config.sse.handle = fetchImpl;
198
+ };
162
199
  }
163
- function readOwnGuide(value, guides) {
164
- const tag = guides?.get(value) ?? value[STRUCT];
165
- if (isGuideSet(tag)) return tag;
166
- if (!isStruct(tag)) return;
167
- const kind = tag[DEFINITION].kind;
168
- if (kind === "object" || kind === "record") return tag;
200
+ /**
201
+ * Replaces the WebSocket constructor used for socket connections.
202
+ *
203
+ * The default is `globalThis.WebSocket`. `WebSocketImpl` is a {@link WebSocketHandleConstructor} that returns a {@link WebSocketHandle}. If no constructor is available, a WebSocket operation fails with an `ENV_UNSUPPORTED` fault.
204
+ *
205
+ * @param WebSocketImpl - Constructor that returns a `WebSocketHandle`.
206
+ * @returns An option that sets `config.webSocket.handle`.
207
+ * @group client
208
+ */
209
+ function withWebSocketHandle(WebSocketImpl) {
210
+ return (config) => {
211
+ config.webSocket.handle = WebSocketImpl;
212
+ };
169
213
  }
170
- /** Tag a null-prototype node this parse just created. Enumerable so object spread keeps its guide. */
171
- function tagOwnNode(value, tag) {
172
- value[STRUCT] = tag;
214
+ /**
215
+ * Runs a hook before the initial WebSocket connect and before every reconnect.
216
+ *
217
+ * The hook type is:
218
+ *
219
+ * ```ts
220
+ * (context: { attempt: number; signal: AbortSignal }) => void | Promise<void>
221
+ * ```
222
+ *
223
+ * - `attempt` is `0` for the first connect and grows by one for each reconnect.
224
+ * - Pass `signal` into token refreshes and other async work so cancelling the session stops them.
225
+ * - If the hook throws, the attempt fails with an `EXT_HOOK_FAILED` fault.
226
+ * - A `beforeConnect` in the request options replaces this one for that call.
227
+ *
228
+ * @param beforeConnect - Hook receiving attempt index and abort signal.
229
+ * @returns An option that sets `config.webSocket.beforeConnect`.
230
+ * @group client
231
+ */
232
+ function withWebSocketBeforeConnect(beforeConnect) {
233
+ return (config) => {
234
+ config.webSocket.beforeConnect = beforeConnect;
235
+ };
173
236
  }
174
237
  /**
175
- * Deep-merge two intersection outputs.
238
+ * Sets the WebSocket subprotocols offered during the handshake.
176
239
  *
177
- * `Object.is` keeps NaN and distinguishes signed zeroes. Equal-time Dates are one instant even
178
- * though the instances differ. Plain objects are copied onto a null prototype so `__proto__` stays
179
- * an own data key. Anything else, including arrays of different lengths, is a conflict.
240
+ * 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']`.
180
241
  *
181
- * `tagNodes` folds each side's parse tag into the new object at this level. One-sided children are
182
- * copied with the tag their own parser already wrote, so a later walk is not required. Null and
183
- * undefined never enter this branch. `depth` is the caller's logical path length, not a fresh merge budget.
184
- * Distinct containers consume a level even when they came from opaque leaves; identical references do not recurse.
242
+ * Precedence, highest first: the `protocols` request option, this client option, then `protocols` on the endpoint definition.
243
+ *
244
+ * @param protocols - Subprotocol list for the WebSocket constructor.
245
+ * @returns An option that sets `config.webSocket.protocols`.
246
+ * @group client
185
247
  */
186
- function mergeIntersectionValues(left, right, tagNodes, depth, context) {
187
- if (Object.is(left, right)) return {
188
- ok: true,
189
- value: left
248
+ function withWebSocketProtocols(protocols) {
249
+ return (config) => {
250
+ config.webSocket.protocols = protocols;
190
251
  };
191
- if (left instanceof Date && right instanceof Date && left.getTime() === right.getTime()) return {
192
- ok: true,
193
- value: left
252
+ }
253
+ /**
254
+ * Enables WebSocket heartbeat messages with optional acknowledgement detection.
255
+ *
256
+ * Passing `undefined` clears an earlier setting. The option type is:
257
+ *
258
+ * ```ts
259
+ * {
260
+ * intervalMs: number
261
+ * isAck?: (message: unknown) => boolean
262
+ * message?: <T = unknown>() => T | unknown
263
+ * timeoutMs?: number
264
+ * }
265
+ * ```
266
+ *
267
+ * - 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`.
268
+ * - 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`.
269
+ * - If no ack arrives within `timeoutMs`, or a heartbeat cannot be sent, the session fails. This does not trigger a reconnect.
270
+ * - Heartbeat uses the same JSON guard as `session.send`: a value JSON cannot represent after struct encoding causes `TypeError` and ends the logical session.
271
+ * - `intervalMs` and `timeoutMs` must be positive, finite, and at most `2_147_483_647`; otherwise the operation fails with `REQ_OPTIONS_INVALID`.
272
+ * - A `heartbeat` in the request options replaces this one for that call.
273
+ *
274
+ * @param options - Heartbeat interval, payload, timeout, and ACK predicate.
275
+ * @returns An option that sets `config.webSocket.heartbeat`.
276
+ * @group client
277
+ */
278
+ function withWebSocketHeartbeat(options) {
279
+ return (config) => {
280
+ config.webSocket.heartbeat = options;
194
281
  };
195
- if (isPlainObject(left) && isPlainObject(right)) {
196
- if (depth >= 256) return {
197
- ok: false,
198
- path: [],
199
- received: right
200
- };
201
- const output = nullObject();
202
- for (const key of Object.keys(left)) output[key] = left[key];
203
- for (const key of Object.keys(right)) {
204
- if (!hasOwnKey(output, key)) {
205
- output[key] = right[key];
206
- continue;
282
+ }
283
+ /**
284
+ * Configures automatic reconnection when the socket closes for any reason other than a manual `session.close()`.
285
+ *
286
+ * Reconnection is opt-in: without this option a close ends the session. Passing `undefined` clears an earlier setting. The option type is:
287
+ *
288
+ * ```ts
289
+ * {
290
+ * attempts?: number
291
+ * delayMs?: number
292
+ * factor?: number
293
+ * jitter?: number
294
+ * maxDelayMs?: number
295
+ * shouldReconnect?: (context: { attempt: number; cause?: unknown; code?: number; reason?: string; wasClean?: boolean }) => boolean
296
+ * }
297
+ * ```
298
+ *
299
+ * - `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.
300
+ * - `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.
301
+ * - `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()`.
302
+ * - 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 the operation 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.
303
+ * - A `reconnect` in the request options replaces this one for that call.
304
+ *
305
+ * @param options - Retry budget, backoff, and `shouldReconnect` gate.
306
+ * @returns An option that sets `config.webSocket.reconnect`.
307
+ * @group client
308
+ */
309
+ function withWebSocketReconnect(options) {
310
+ return (config) => {
311
+ config.webSocket.reconnect = options;
312
+ };
313
+ }
314
+ /**
315
+ * Observes SSE events that have no matching event struct or fail validation.
316
+ *
317
+ * The handler may be async and is awaited. The callback receives:
318
+ *
319
+ * ```ts
320
+ * {
321
+ * reason: 'missing-struct' | 'validation-failed'
322
+ * message: { id: string; event: string; data: string }
323
+ * cause?: unknown
324
+ * signal: AbortSignal
325
+ * }
326
+ * ```
327
+ *
328
+ * - The invalid event is **always dropped**; it is never delivered to the stream. The callback only observes it.
329
+ * - The return value is ignored. An error thrown by the callback is swallowed and does not close the stream, unless `signal` has already aborted.
330
+ * - `cause` is set for `validation-failed`. Without a handler the invalid event is dropped silently.
331
+ *
332
+ * @param handler - Observer callback for invalid events.
333
+ * @returns An option that sets `config.sse.onInvalidEvent`.
334
+ * @group client
335
+ */
336
+ function withSSEOnInvalidEvent(handler) {
337
+ return (config) => {
338
+ config.sse.onInvalidEvent = handler;
339
+ };
340
+ }
341
+ /**
342
+ * Configures automatic reconnection after an SSE stream fails.
343
+ *
344
+ * Reconnection is opt-in: without this option a failed stream is not retried. Passing `undefined` clears an earlier setting.
345
+ *
346
+ * ```ts
347
+ * type SSEReconnect = {
348
+ * attempts?: number;
349
+ * delayMs?: number;
350
+ * factor?: number;
351
+ * jitter?: number;
352
+ * maxDelayMs?: number;
353
+ * shouldReconnect?: (context: {
354
+ * attempt: number;
355
+ * cause?: unknown;
356
+ * lastEventId: string;
357
+ * method: string;
358
+ * open?: {
359
+ * response: { status: number; statusText: string; url: string };
360
+ * url: string;
361
+ * };
362
+ * }) => boolean | Promise<boolean>;
363
+ * };
364
+ * ```
365
+ *
366
+ * - `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.
367
+ * - 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.
368
+ * - A request with a `ReadableStream` body never reconnects, and `shouldReconnect` is not called for it.
369
+ * - `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.
370
+ * - `delayMs` is only the starting base delay (`1000` by default). A server `retry:` field (digits only, in milliseconds) replaces it for later attempts.
371
+ * - 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.
372
+ * - 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` when the operation runs.
373
+ *
374
+ * @param options - Retry budget, backoff, and `shouldReconnect` gate.
375
+ * @returns An option that sets `config.sse.reconnect`.
376
+ * @group client
377
+ */
378
+ function withSSEReconnect(options) {
379
+ return (config) => {
380
+ config.sse.reconnect = options;
381
+ };
382
+ }
383
+ /**
384
+ * Enables XSRF cookie-to-header token forwarding for HTTP requests.
385
+ *
386
+ * SSE and WebSocket requests do not get the header. `cookieName` defaults to `XSRF-TOKEN` and `headerName` to `X-XSRF-TOKEN` when names are omitted.
387
+ *
388
+ * - 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.
389
+ * - 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.
390
+ * - 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.
391
+ *
392
+ * @param options - Cookie/header names and optional token provider.
393
+ * @returns An option that sets `config.xsrf`.
394
+ * @group client
395
+ */
396
+ function withXSRF(options = {}) {
397
+ return (config) => {
398
+ config.xsrf = {
399
+ cookieName: options.cookieName ?? "XSRF-TOKEN",
400
+ headerName: options.headerName ?? "X-XSRF-TOKEN",
401
+ tokenProvider: options.tokenProvider
402
+ };
403
+ };
404
+ }
405
+ /**
406
+ * Sets whether HTTP and SSE requests include credentials.
407
+ *
408
+ * 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.
409
+ *
410
+ * @param value - When `true`, credentials are sent with cross-origin requests.
411
+ * @returns An option that sets `config.withCredentials`.
412
+ * @group client
413
+ */
414
+ function withCredentials(value) {
415
+ return (config) => {
416
+ config.withCredentials = value;
417
+ };
418
+ }
419
+ // src/struct/symbols.ts
420
+ const DEFINITION = Symbol.for("defjs.struct.definition");
421
+ const OMIT = Symbol("defjs.struct.omit");
422
+ /**
423
+ * Stands in for a parsed value when parsing fails; the issue waits in the slot that `failure()` fills.
424
+ * Not in the symbol registry, so no input value can ever equal it.
425
+ */
426
+ const FAILED = Symbol("defjs.struct.failed");
427
+ /**
428
+ * Type-only export of the internal enumerable tag key on objects produced while parsing endpoint input, used by the outbound encoder.
429
+ *
430
+ * `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.
431
+ *
432
+ * 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.
433
+ *
434
+ * @internal
435
+ * @group struct
436
+ */
437
+ const STRUCT = Symbol.for("defjs.struct.STRUCT");
438
+ // src/struct/guards.ts
439
+ const KNOWN_KINDS = /* @__PURE__ */ new Set([
440
+ "any",
441
+ "array",
442
+ "arrayBuffer",
443
+ "bigint",
444
+ "blob",
445
+ "boolean",
446
+ "date",
447
+ "discriminatedUnion",
448
+ "enum",
449
+ "file",
450
+ "intersection",
451
+ "literal",
452
+ "null",
453
+ "number",
454
+ "object",
455
+ "or",
456
+ "record",
457
+ "string",
458
+ "tuple",
459
+ "unknown"
460
+ ]);
461
+ function isStruct(value) {
462
+ if (typeof value !== "object" || value === null || !Object.hasOwn(value, DEFINITION)) return false;
463
+ const definition = value[DEFINITION];
464
+ if (typeof definition !== "object" || definition === null) return false;
465
+ if (!KNOWN_KINDS.has(definition.kind)) return false;
466
+ const flags = definition.flags;
467
+ return typeof flags === "object" && flags !== null && typeof flags.nullable === "boolean" && typeof flags.optional === "boolean";
468
+ }
469
+ // src/struct/guide.ts
470
+ const GUIDE_SET = Symbol.for("defjs.struct.guideSet");
471
+ function isGuideSet(value) {
472
+ return typeof value === "object" && value !== null && value[GUIDE_SET] === true;
473
+ }
474
+ function guideTag(members) {
475
+ const unique = [];
476
+ for (const member of members) if (!unique.includes(member)) unique.push(member);
477
+ if (unique.length === 0) return;
478
+ if (unique.length === 1) return unique[0];
479
+ return {
480
+ [GUIDE_SET]: true,
481
+ members: unique
482
+ };
483
+ }
484
+ function guideMembers(guide) {
485
+ return isGuideSet(guide) ? guide.members : [guide];
486
+ }
487
+ function combineGuides(left, right) {
488
+ if (!left) return right;
489
+ if (!right) return left;
490
+ return guideTag([...guideMembers(left), ...guideMembers(right)]);
491
+ }
492
+ function readOwnGuide(value, guides) {
493
+ const tag = guides?.get(value) ?? value[STRUCT];
494
+ if (isGuideSet(tag)) return tag;
495
+ if (!isStruct(tag)) return;
496
+ const kind = tag[DEFINITION].kind;
497
+ if (kind === "object" || kind === "record") return tag;
498
+ }
499
+ /** Tag a null-prototype node this parse just created. Enumerable so object spread keeps its guide. */
500
+ function tagOwnNode(value, tag) {
501
+ value[STRUCT] = tag;
502
+ }
503
+ /**
504
+ * Deep-merge two intersection outputs.
505
+ *
506
+ * `Object.is` keeps NaN and distinguishes signed zeroes. Equal-time Dates are one instant even
507
+ * though the instances differ. Plain objects are copied onto a null prototype so `__proto__` stays
508
+ * an own data key. Anything else, including arrays of different lengths, is a conflict.
509
+ *
510
+ * `tagNodes` folds each side's parse tag into the new object at this level. One-sided children are
511
+ * copied with the tag their own parser already wrote, so a later walk is not required. Null and
512
+ * undefined never enter this branch. `depth` is the caller's logical path length, not a fresh merge budget.
513
+ * Distinct containers consume a level even when they came from opaque leaves; identical references do not recurse.
514
+ */
515
+ function mergeIntersectionValues(left, right, tagNodes, depth, context) {
516
+ if (Object.is(left, right)) return {
517
+ ok: true,
518
+ value: left
519
+ };
520
+ if (left instanceof Date && right instanceof Date && left.getTime() === right.getTime()) return {
521
+ ok: true,
522
+ value: left
523
+ };
524
+ if (isPlainObject(left) && isPlainObject(right)) {
525
+ if (depth >= 256) return {
526
+ ok: false,
527
+ path: [],
528
+ received: right
529
+ };
530
+ const output = nullObject();
531
+ for (const key of Object.keys(left)) output[key] = left[key];
532
+ for (const key of Object.keys(right)) {
533
+ if (!hasOwnKey(output, key)) {
534
+ output[key] = right[key];
535
+ continue;
207
536
  }
208
537
  const merged = mergeIntersectionValues(output[key], right[key], tagNodes, depth + 1, context);
209
538
  if (!merged.ok) return {
@@ -452,7 +781,7 @@ function appendRecordToHeaders(headers, value) {
452
781
  /**
453
782
  * Resolves a relative definition path under the base endpoint's origin and directory into a URL.
454
783
  *
455
- * During `client.execute`, a missing, relative, or invalid base or an illegal
784
+ * During an operation, a missing, relative, or invalid base or an illegal
456
785
  * path becomes `REQ_BUILD_FAILED` before any interceptor or transport runs,
457
786
  * including a short-circuiting interceptor, for HTTP, SSE, and WebSocket alike.
458
787
  * An interceptor's final `req.endpoint` is also validated against its final
@@ -774,527 +1103,32 @@ function createPreflightFault(code, cause, response) {
774
1103
  /**
775
1104
  * Builds a `NET_*` `PreflightFault`, choosing the code from the shape of `cause`.
776
1105
  *
777
- * Optional `response` metadata is attached when the transport already had some
778
- * to report. The cause is kept as `cause`.
779
- *
780
- * - `NET_ABORTED` when {@link isAbortCause} recognizes an abort-branded `Error`
781
- * (including `ERR_ABORTED` from another Core copy) or a `DOMException` named
782
- * `AbortError`. The message is `Request was aborted`.
783
- * - `NET_TIMEOUT` when {@link isTimeoutCause} recognizes a timeout-branded
784
- * `Error` (including `ERR_TIMEOUT` from another Core copy) or a `DOMException`
785
- * or `Error` named `TimeoutError`. The message is the cause's message, or
786
- * `Request timed out` when that is empty.
787
- * - `NET_UNREACHABLE` for anything else, with the cause's message.
788
- *
789
- * @param cause - Underlying cancellation, timeout, or transport failure.
790
- * @param response - Metadata, only where the transport already had some to report.
791
- * @returns A `NET_ABORTED`, `NET_TIMEOUT`, or `NET_UNREACHABLE` fault.
792
- * @group errors
793
- */
794
- function createNetworkFault(cause, response) {
795
- if (isAbortCause(cause)) return withResponse(createFault(ERR_ABORTED.message, cause), "NET_ABORTED", response);
796
- if (isTimeoutCause(cause)) return withResponse(createFault(cause instanceof Error && cause.message ? cause.message : ERR_TIMEOUT.message, cause), "NET_TIMEOUT", response);
797
- return withResponse(createFault(describeCause(cause), cause), "NET_UNREACHABLE", response);
798
- }
799
- function withResponse(fault, code, response) {
800
- fault.code = code;
801
- if (response !== void 0) fault.response = response;
802
- return fault;
803
- }
804
- // src/internal/abort.ts
805
- const ABORT_TIMEOUT_CONFLICT_MESSAGE = "abort and timeout cannot be used together";
806
- const MAX_TIMER_DELAY_MS$2 = 2147483647;
807
- function snapshotCancellationConfig(config) {
808
- return {
809
- abort: config.abort,
810
- signal: config.signal,
811
- timeout: config.timeout
812
- };
813
- }
814
- function hasAbortTimeoutConflict(config) {
815
- return config !== void 0 && config.abort !== void 0 && config.timeout !== void 0;
816
- }
817
- /** The cancellation cause for an already-aborted signal, or `undefined` while it is still live. */
818
- function resolveAbortCause(signal) {
819
- if (!signal.aborted) return;
820
- return resolveAbortedCause(signal);
821
- }
822
- /** `ERR_TIMEOUT` or `ERR_ABORTED`, chosen from the signal's abort reason, including another copy's timeout sentinel. */
823
- function resolveAbortedCause(signal) {
824
- return isTimeoutCause(signal.reason) ? ERR_TIMEOUT : ERR_ABORTED;
825
- }
826
- /** `REQ_OPTIONS_INVALID` for execute options that pass both `abort` and `timeout`. */
827
- function createAbortTimeoutConflictFault() {
828
- return createPreflightFault("REQ_OPTIONS_INVALID", /* @__PURE__ */ new Error(ABORT_TIMEOUT_CONFLICT_MESSAGE));
829
- }
830
- /** The cancellation fault for an already-aborted signal, or `undefined` while it is still live. */
831
- function resolveAbortFault(signal) {
832
- if (!signal.aborted) return;
833
- return resolveAbortedFault(signal);
834
- }
835
- /** `NET_ABORTED` or `NET_TIMEOUT`, chosen from the signal's abort reason. */
836
- function resolveAbortedFault(signal) {
837
- return createNetworkFault(resolveAbortedCause(signal));
838
- }
839
- function validateTransportTimeout(timeout) {
840
- if (typeof timeout !== "undefined" && (!Number.isSafeInteger(timeout) || timeout <= 0 || timeout > MAX_TIMER_DELAY_MS$2)) throw new RangeError(`Request timeout must be a positive safe integer no greater than ${MAX_TIMER_DELAY_MS$2}`);
841
- }
842
- async function awaitWithSignal(run, signal) {
843
- signal.throwIfAborted();
844
- let rejectAbort;
845
- const aborted = new Promise((_resolve, reject) => {
846
- rejectAbort = reject;
847
- });
848
- const onAbort = () => rejectAbort(signal.reason);
849
- signal.addEventListener("abort", onAbort, { once: true });
850
- const task = Promise.resolve().then(() => {
851
- signal.throwIfAborted();
852
- return run();
853
- });
854
- task.catch(() => void 0);
855
- try {
856
- const value = await Promise.race([task, aborted]);
857
- signal.throwIfAborted();
858
- return value;
859
- } finally {
860
- signal.removeEventListener("abort", onAbort);
861
- }
862
- }
863
- /**
864
- * Arm `timeout` before synchronous parse and builder work.
865
- *
866
- * A timer task cannot run until that work yields. `expiredFault` also watches
867
- * the wall clock so a microtask transport cannot succeed after the deadline.
868
- * `release` clears the timer and makes a later wall-clock check a no-op, so a
869
- * settled startup can no longer abort the request signal.
870
- */
871
- function armTransportDeadline(timeout) {
872
- if (typeof timeout !== "number") return {
873
- expiredFault: () => void 0,
874
- release: () => void 0,
875
- releaseOnAbort: () => void 0,
876
- signal: void 0
877
- };
878
- const startedAt = Date.now();
879
- const controller = new AbortController();
880
- let released = false;
881
- let watched;
882
- const timer = setTimeout(() => {
883
- controller.abort(ERR_TIMEOUT);
884
- }, timeout);
885
- const release = () => {
886
- if (released) return;
887
- released = true;
888
- clearTimeout(timer);
889
- watched?.removeEventListener("abort", release);
890
- };
891
- return {
892
- release,
893
- releaseOnAbort(signal) {
894
- watched = signal;
895
- signal.addEventListener("abort", release, { once: true });
896
- },
897
- signal: controller.signal,
898
- expiredFault() {
899
- if (released || Date.now() - startedAt < timeout) return;
900
- controller.abort(ERR_TIMEOUT);
901
- return resolveAbortedFault(controller.signal);
902
- }
903
- };
904
- }
905
- function resolveTransportCancellation(deadline, signal, fallback) {
906
- return resolveAbortFault(signal) ?? deadline.expiredFault() ?? fallback;
907
- }
908
- const forwardingSignals = /* @__PURE__ */ new WeakMap();
909
- /** Release listeners owned by a signal returned from `mergeAbortSignals`. */
910
- function releaseAbortSignal(signal) {
911
- forwardingSignals.get(signal)?.release();
912
- }
913
- function mergeAbortSignals(controller, signals, timeout) {
914
- validateTransportTimeout(timeout);
915
- const hasTimeout = typeof timeout === "number";
916
- if (!hasTimeout && signals.length > 0 && signals.every((signal) => signal === void 0 || signal === controller)) return controller;
917
- const merged = [];
918
- const seen = /* @__PURE__ */ new Set();
919
- const append = (signal) => {
920
- const state = forwardingSignals.get(signal);
921
- if (state?.active) {
922
- for (const source of state.sources) append(source);
923
- return;
924
- }
925
- if (!seen.has(signal)) {
926
- seen.add(signal);
927
- merged.push(signal);
928
- }
929
- };
930
- append(controller);
931
- for (const signal of signals) if (signal) append(signal);
932
- if (!hasTimeout && merged.length === 1) return merged[0];
933
- let timeoutTimer;
934
- const combinedController = new AbortController();
935
- const listeners = [];
936
- let abortWithReason;
937
- const state = {
938
- active: true,
939
- release() {
940
- if (!state.active) return;
941
- state.active = false;
942
- for (const [source, listener] of listeners) source.removeEventListener("abort", listener);
943
- if (timeoutTimer !== void 0) clearTimeout(timeoutTimer);
944
- },
945
- sources: merged
946
- };
947
- forwardingSignals.set(combinedController.signal, state);
948
- abortWithReason = (reason) => {
949
- if (combinedController.signal.aborted) return;
950
- combinedController.abort(reason);
951
- state.release();
952
- };
953
- if (hasTimeout) timeoutTimer = setTimeout(() => {
954
- abortWithReason(ERR_TIMEOUT);
955
- }, timeout);
956
- const alreadyAborted = merged.find((signal) => signal.aborted);
957
- if (alreadyAborted) {
958
- abortWithReason(alreadyAborted.reason);
959
- return combinedController.signal;
960
- }
961
- for (const source of merged) {
962
- const listener = () => abortWithReason(source.reason);
963
- listeners.push([source, listener]);
964
- source.addEventListener("abort", listener, { once: true });
965
- }
966
- return combinedController.signal;
967
- }
968
- // src/client/option.ts
969
- /**
970
- * Sets the valid absolute base URL prepended to relative request paths.
971
- *
972
- * 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:`.
973
- *
974
- * 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.
975
- *
976
- * @param endpoint - Absolute base endpoint. Missing or invalid endpoints fail request building.
977
- * @returns An option that writes `config.endpoint`.
978
- * @group client
979
- */
980
- function withEndpoint(endpoint) {
981
- return (config) => {
982
- config.endpoint = endpoint;
983
- };
984
- }
985
- /**
986
- * Sets static default headers for HTTP and SSE requests built by this client.
987
- *
988
- * 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.
989
- *
990
- * 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.
991
- *
992
- * 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.
993
- *
994
- * @param headers - `HeadersInit` bag cloned onto the client config.
995
- * @returns An option that writes `config.headers`.
996
- * @group client
997
- */
998
- function withHeaders(headers) {
999
- return (config) => {
1000
- config.headers = new Headers(headers);
1001
- };
1002
- }
1003
- /**
1004
- * Sets the default timeout, in milliseconds, for HTTP `execute` only.
1005
- *
1006
- * 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.
1007
- *
1008
- * 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.
1009
- *
1010
- * @param ms - Positive safe integer timeout in milliseconds.
1011
- * @returns An option that writes `config.timeout`.
1012
- * @group client
1013
- */
1014
- function withTimeout(ms) {
1015
- validateTransportTimeout(ms);
1016
- return (config) => {
1017
- config.timeout = ms;
1018
- };
1019
- }
1020
- /**
1021
- * Appends interceptors to the client interceptor chain.
1022
- *
1023
- * 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.
1024
- *
1025
- * @param interceptors - Interceptors applied in registration order.
1026
- * @returns An option that pushes onto `config.interceptors`.
1027
- * @group client
1028
- */
1029
- function withInterceptors(...interceptors) {
1030
- return (config) => {
1031
- config.interceptors.push(...interceptors);
1032
- };
1033
- }
1034
- /**
1035
- * Replaces query encoding for HTTP, SSE, and WebSocket requests.
1036
- *
1037
- * `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.
1038
- *
1039
- * 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.
1040
- *
1041
- * @param serializer - Function that turns an encoded query object into a string.
1042
- * @returns An option that sets `config.querySerializer`.
1043
- * @group client
1044
- */
1045
- function withQuerySerializer(serializer) {
1046
- return (config) => {
1047
- config.querySerializer = serializer;
1048
- };
1049
- }
1050
- /**
1051
- * Replaces the Fetch-compatible function used for HTTP requests.
1052
- *
1053
- * 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}.
1054
- *
1055
- * @param fetchImpl - `(input, init?) => Promise<Response>`. Not `typeof fetch`, so
1056
- * a mock does not need host extras such as `preconnect`.
1057
- * @returns An option that sets `config.http.handle`.
1058
- * @group client
1059
- */
1060
- function withHTTPHandle(fetchImpl) {
1061
- return (config) => {
1062
- config.http.handle = fetchImpl;
1063
- };
1064
- }
1065
- /**
1066
- * Replaces the Fetch-compatible function used to open SSE event streams.
1067
- *
1068
- * The default is the platform `fetch`. `fetchImpl` is a {@link FetchHandle}. It only affects SSE. HTTP uses {@link withHTTPHandle}.
1069
- *
1070
- * @param fetchImpl - `(input, init?) => Promise<Response>`. Not `typeof fetch`, so
1071
- * a mock does not need host extras such as `preconnect`.
1072
- * @returns An option that sets `config.sse.handle`.
1073
- * @group client
1074
- */
1075
- function withSSEHandle(fetchImpl) {
1076
- return (config) => {
1077
- config.sse.handle = fetchImpl;
1078
- };
1079
- }
1080
- /**
1081
- * Replaces the WebSocket constructor used for socket connections.
1082
- *
1083
- * 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.
1084
- *
1085
- * @param WebSocketImpl - Constructor that returns a `WebSocketHandle`.
1086
- * @returns An option that sets `config.webSocket.handle`.
1087
- * @group client
1088
- */
1089
- function withWebSocketHandle(WebSocketImpl) {
1090
- return (config) => {
1091
- config.webSocket.handle = WebSocketImpl;
1092
- };
1093
- }
1094
- /**
1095
- * Runs a hook before the initial WebSocket connect and before every reconnect.
1096
- *
1097
- * The hook type is:
1098
- *
1099
- * ```ts
1100
- * (context: { attempt: number; signal: AbortSignal }) => void | Promise<void>
1101
- * ```
1102
- *
1103
- * - `attempt` is `0` for the first connect and grows by one for each reconnect.
1104
- * - Pass `signal` into token refreshes and other async work so cancelling the session stops them.
1105
- * - If the hook throws, the attempt fails with an `EXT_HOOK_FAILED` fault.
1106
- * - A `beforeConnect` in the execute options replaces this one for that call.
1107
- *
1108
- * @param beforeConnect - Hook receiving attempt index and abort signal.
1109
- * @returns An option that sets `config.webSocket.beforeConnect`.
1110
- * @group client
1111
- */
1112
- function withWebSocketBeforeConnect(beforeConnect) {
1113
- return (config) => {
1114
- config.webSocket.beforeConnect = beforeConnect;
1115
- };
1116
- }
1117
- /**
1118
- * Sets the WebSocket subprotocols offered during the handshake.
1119
- *
1120
- * 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']`.
1121
- *
1122
- * Precedence, highest first: the `protocols` execute option, this client option, then `protocols` on the endpoint definition.
1123
- *
1124
- * @param protocols - Subprotocol list for the WebSocket constructor.
1125
- * @returns An option that sets `config.webSocket.protocols`.
1126
- * @group client
1127
- */
1128
- function withWebSocketProtocols(protocols) {
1129
- return (config) => {
1130
- config.webSocket.protocols = protocols;
1131
- };
1132
- }
1133
- /**
1134
- * Enables WebSocket heartbeat messages with optional acknowledgement detection.
1135
- *
1136
- * Passing `undefined` clears an earlier setting. The option type is:
1137
- *
1138
- * ```ts
1139
- * {
1140
- * intervalMs: number
1141
- * isAck?: (message: unknown) => boolean
1142
- * message?: <T = unknown>() => T | unknown
1143
- * timeoutMs?: number
1144
- * }
1145
- * ```
1146
- *
1147
- * - 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`.
1148
- * - 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`.
1149
- * - If no ack arrives within `timeoutMs`, or a heartbeat cannot be sent, the session fails. This does not trigger a reconnect.
1150
- * - Heartbeat uses the same JSON guard as `session.send`: a value JSON cannot represent after struct encoding causes `TypeError` and ends the logical session.
1151
- * - `intervalMs` and `timeoutMs` must be positive, finite, and at most `2_147_483_647`; otherwise execute fails with `REQ_OPTIONS_INVALID`.
1152
- * - A `heartbeat` in the execute options replaces this one for that call.
1153
- *
1154
- * @param options - Heartbeat interval, payload, timeout, and ACK predicate.
1155
- * @returns An option that sets `config.webSocket.heartbeat`.
1156
- * @group client
1157
- */
1158
- function withWebSocketHeartbeat(options) {
1159
- return (config) => {
1160
- config.webSocket.heartbeat = options;
1161
- };
1162
- }
1163
- /**
1164
- * Configures automatic reconnection when the socket closes for any reason other than a manual `session.close()`.
1165
- *
1166
- * Reconnection is opt-in: without this option a close ends the session. Passing `undefined` clears an earlier setting. The option type is:
1167
- *
1168
- * ```ts
1169
- * {
1170
- * attempts?: number
1171
- * delayMs?: number
1172
- * factor?: number
1173
- * jitter?: number
1174
- * maxDelayMs?: number
1175
- * shouldReconnect?: (context: { attempt: number; cause?: unknown; code?: number; reason?: string; wasClean?: boolean }) => boolean
1176
- * }
1177
- * ```
1178
- *
1179
- * - `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.
1180
- * - `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.
1181
- * - `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()`.
1182
- * - 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.
1183
- * - A `reconnect` in the execute options replaces this one for that call.
1184
- *
1185
- * @param options - Retry budget, backoff, and `shouldReconnect` gate.
1186
- * @returns An option that sets `config.webSocket.reconnect`.
1187
- * @group client
1188
- */
1189
- function withWebSocketReconnect(options) {
1190
- return (config) => {
1191
- config.webSocket.reconnect = options;
1192
- };
1193
- }
1194
- /**
1195
- * Observes SSE events that have no matching event struct or fail validation.
1196
- *
1197
- * The handler may be async and is awaited. The callback receives:
1198
- *
1199
- * ```ts
1200
- * {
1201
- * reason: 'missing-struct' | 'validation-failed'
1202
- * message: { id: string; event: string; data: string }
1203
- * cause?: unknown
1204
- * signal: AbortSignal
1205
- * }
1206
- * ```
1207
- *
1208
- * - The invalid event is **always dropped**; it is never delivered to the stream. The callback only observes it.
1209
- * - The return value is ignored. An error thrown by the callback is swallowed and does not close the stream, unless `signal` has already aborted.
1210
- * - `cause` is set for `validation-failed`. Without a handler the invalid event is dropped silently.
1211
- *
1212
- * @param handler - Observer callback for invalid events.
1213
- * @returns An option that sets `config.sse.onInvalidEvent`.
1214
- * @group client
1215
- */
1216
- function withSSEOnInvalidEvent(handler) {
1217
- return (config) => {
1218
- config.sse.onInvalidEvent = handler;
1219
- };
1220
- }
1221
- /**
1222
- * Configures automatic reconnection after an SSE stream fails.
1223
- *
1224
- * Reconnection is opt-in: without this option a failed stream is not retried. Passing `undefined` clears an earlier setting.
1225
- *
1226
- * ```ts
1227
- * type SSEReconnect = {
1228
- * attempts?: number;
1229
- * delayMs?: number;
1230
- * factor?: number;
1231
- * jitter?: number;
1232
- * maxDelayMs?: number;
1233
- * shouldReconnect?: (context: {
1234
- * attempt: number;
1235
- * cause?: unknown;
1236
- * lastEventId: string;
1237
- * method: string;
1238
- * open?: {
1239
- * response: { status: number; statusText: string; url: string };
1240
- * url: string;
1241
- * };
1242
- * }) => boolean | Promise<boolean>;
1243
- * };
1244
- * ```
1245
- *
1246
- * - `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.
1247
- * - 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.
1248
- * - A request with a `ReadableStream` body never reconnects, and `shouldReconnect` is not called for it.
1249
- * - `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.
1250
- * - `delayMs` is only the starting base delay (`1000` by default). A server `retry:` field (digits only, in milliseconds) replaces it for later attempts.
1251
- * - 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.
1252
- * - 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.
1253
- *
1254
- * @param options - Retry budget, backoff, and `shouldReconnect` gate.
1255
- * @returns An option that sets `config.sse.reconnect`.
1256
- * @group client
1257
- */
1258
- function withSSEReconnect(options) {
1259
- return (config) => {
1260
- config.sse.reconnect = options;
1261
- };
1262
- }
1263
- /**
1264
- * Enables XSRF cookie-to-header token forwarding for HTTP requests.
1265
- *
1266
- * SSE and WebSocket requests do not get the header. `cookieName` defaults to `XSRF-TOKEN` and `headerName` to `X-XSRF-TOKEN` when names are omitted.
1267
- *
1268
- * - 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.
1269
- * - 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.
1270
- * - 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.
1271
- *
1272
- * @param options - Cookie/header names and optional token provider.
1273
- * @returns An option that sets `config.xsrf`.
1274
- * @group client
1275
- */
1276
- function withXSRF(options = {}) {
1277
- return (config) => {
1278
- config.xsrf = {
1279
- cookieName: options.cookieName ?? "XSRF-TOKEN",
1280
- headerName: options.headerName ?? "X-XSRF-TOKEN",
1281
- tokenProvider: options.tokenProvider
1282
- };
1283
- };
1284
- }
1285
- /**
1286
- * Sets whether HTTP and SSE requests include credentials.
1287
- *
1288
- * 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.
1106
+ * Optional `response` metadata is attached when the transport already had some
1107
+ * to report. The cause is kept as `cause`.
1289
1108
  *
1290
- * @param value - When `true`, credentials are sent with cross-origin requests.
1291
- * @returns An option that sets `config.withCredentials`.
1292
- * @group client
1109
+ * - `NET_ABORTED` when {@link isAbortCause} recognizes an abort-branded `Error`
1110
+ * (including `ERR_ABORTED` from another Core copy) or a `DOMException` named
1111
+ * `AbortError`. The message is `Request was aborted`.
1112
+ * - `NET_TIMEOUT` when {@link isTimeoutCause} recognizes a timeout-branded
1113
+ * `Error` (including `ERR_TIMEOUT` from another Core copy) or a `DOMException`
1114
+ * or `Error` named `TimeoutError`. The message is the cause's message, or
1115
+ * `Request timed out` when that is empty.
1116
+ * - `NET_UNREACHABLE` for anything else, with the cause's message.
1117
+ *
1118
+ * @param cause - Underlying cancellation, timeout, or transport failure.
1119
+ * @param response - Metadata, only where the transport already had some to report.
1120
+ * @returns A `NET_ABORTED`, `NET_TIMEOUT`, or `NET_UNREACHABLE` fault.
1121
+ * @group errors
1293
1122
  */
1294
- function withCredentials(value) {
1295
- return (config) => {
1296
- config.withCredentials = value;
1297
- };
1123
+ function createNetworkFault(cause, response) {
1124
+ if (isAbortCause(cause)) return withResponse(createFault(ERR_ABORTED.message, cause), "NET_ABORTED", response);
1125
+ if (isTimeoutCause(cause)) return withResponse(createFault(cause instanceof Error && cause.message ? cause.message : ERR_TIMEOUT.message, cause), "NET_TIMEOUT", response);
1126
+ return withResponse(createFault(describeCause(cause), cause), "NET_UNREACHABLE", response);
1127
+ }
1128
+ function withResponse(fault, code, response) {
1129
+ fault.code = code;
1130
+ if (response !== void 0) fault.response = response;
1131
+ return fault;
1298
1132
  }
1299
1133
  // src/interceptor/interceptor.ts
1300
1134
  /**
@@ -1303,10 +1137,10 @@ function withCredentials(value) {
1303
1137
  * Returns `{ kind: 'http', fn }`. Call `next(req)` (optionally with a modified
1304
1138
  * request) or short-circuit with `makeResponse`. `next` runs only the
1305
1139
  * interceptors registered after this one and then the fetch handler. HTTP
1306
- * interceptors do not run for SSE or WebSocket commands.
1140
+ * interceptors do not run for SSE or WebSocket operations.
1307
1141
  *
1308
1142
  * A deterministic URL error from `next(req)` rejects the returned promise, so
1309
- * `.then` or `.catch` recovery runs; if it is not recovered, `client.execute`
1143
+ * `.then` or `.catch` recovery runs; if it is not recovered, the operation
1310
1144
  * still reports `REQ_BUILD_FAILED`.
1311
1145
  *
1312
1146
  * @param fn - HTTP interceptor function.
@@ -1322,12 +1156,12 @@ function createHttpInterceptor(fn) {
1322
1156
  /**
1323
1157
  * Wraps an `SSEInterceptorFn` as a tagged `SSEInterceptor`.
1324
1158
  *
1325
- * Returns `{ kind: 'sse', fn }`. SSE interceptors run only for SSE commands, in
1159
+ * Returns `{ kind: 'sse', fn }`. SSE interceptors run only for SSE operations, in
1326
1160
  * registration order; `next(req)` opens the stream through the remaining
1327
1161
  * interceptors.
1328
1162
  *
1329
1163
  * A deterministic URL error from `next(req)` rejects the returned promise, so
1330
- * `.then` or `.catch` recovery runs; if it is not recovered, `client.execute`
1164
+ * `.then` or `.catch` recovery runs; if it is not recovered, the operation
1331
1165
  * still reports `REQ_BUILD_FAILED`.
1332
1166
  *
1333
1167
  * `EventStreamHandle` extends `AsyncDisposable`. A structural short-circuit
@@ -1348,7 +1182,7 @@ function createSSEInterceptor(fn) {
1348
1182
  * Wraps a `WebSocketInterceptorFn` as a tagged `WebSocketInterceptor`.
1349
1183
  *
1350
1184
  * Returns `{ kind: 'web-socket', fn }`. WebSocket interceptors run only for
1351
- * WebSocket commands. `next` and the interceptor return a
1185
+ * WebSocket operations. `next` and the interceptor return a
1352
1186
  * {@link WebSocketSessionLike}, the interceptor-facing session. A wrapper or
1353
1187
  * short-circuit session must forward its `[Symbol.asyncDispose]()` teardown.
1354
1188
  *
@@ -1377,6 +1211,99 @@ function resolveSSEInterceptors(interceptors) {
1377
1211
  function resolveWebSocketInterceptors(interceptors) {
1378
1212
  return interceptors.filter((i) => i.kind === "web-socket").map((i) => i.fn);
1379
1213
  }
1214
+ // src/internal/abort.ts
1215
+ /** The cancellation cause for an already-aborted signal, or `undefined` while it is still live. */
1216
+ function resolveAbortCause(signal) {
1217
+ if (!signal.aborted) return;
1218
+ return resolveAbortedCause(signal);
1219
+ }
1220
+ /** `ERR_TIMEOUT` or `ERR_ABORTED`, chosen from the signal's abort reason, including another copy's timeout sentinel. */
1221
+ function resolveAbortedCause(signal) {
1222
+ return isTimeoutCause(signal.reason) ? ERR_TIMEOUT : ERR_ABORTED;
1223
+ }
1224
+ /** The cancellation fault for an already-aborted signal, or `undefined` while it is still live. */
1225
+ function resolveAbortFault(signal) {
1226
+ if (!signal.aborted) return;
1227
+ return resolveAbortedFault(signal);
1228
+ }
1229
+ /** `NET_ABORTED` or `NET_TIMEOUT`, chosen from the signal's abort reason. */
1230
+ function resolveAbortedFault(signal) {
1231
+ return createNetworkFault(resolveAbortedCause(signal));
1232
+ }
1233
+ async function awaitWithSignal(run, signal) {
1234
+ signal.throwIfAborted();
1235
+ let rejectAbort;
1236
+ const aborted = new Promise((_resolve, reject) => {
1237
+ rejectAbort = reject;
1238
+ });
1239
+ const onAbort = () => rejectAbort(signal.reason);
1240
+ signal.addEventListener("abort", onAbort, { once: true });
1241
+ const task = Promise.resolve().then(() => {
1242
+ signal.throwIfAborted();
1243
+ return run();
1244
+ });
1245
+ task.catch(() => void 0);
1246
+ try {
1247
+ const value = await Promise.race([task, aborted]);
1248
+ signal.throwIfAborted();
1249
+ return value;
1250
+ } finally {
1251
+ signal.removeEventListener("abort", onAbort);
1252
+ }
1253
+ }
1254
+ const forwardingSignals = /* @__PURE__ */ new WeakMap();
1255
+ /** Release listeners owned by a signal returned from `mergeAbortSignals`. */
1256
+ function releaseAbortSignal(signal) {
1257
+ forwardingSignals.get(signal)?.release();
1258
+ }
1259
+ function mergeAbortSignals(controller, signals) {
1260
+ if (signals.length > 0 && signals.every((signal) => signal === void 0 || signal === controller)) return controller;
1261
+ const merged = [];
1262
+ const seen = /* @__PURE__ */ new Set();
1263
+ const append = (signal) => {
1264
+ const state = forwardingSignals.get(signal);
1265
+ if (state?.active) {
1266
+ for (const source of state.sources) append(source);
1267
+ return;
1268
+ }
1269
+ if (!seen.has(signal)) {
1270
+ seen.add(signal);
1271
+ merged.push(signal);
1272
+ }
1273
+ };
1274
+ append(controller);
1275
+ for (const signal of signals) if (signal) append(signal);
1276
+ if (merged.length === 1) return merged[0];
1277
+ const combinedController = new AbortController();
1278
+ const listeners = [];
1279
+ let abortWithReason;
1280
+ const state = {
1281
+ active: true,
1282
+ release() {
1283
+ if (!state.active) return;
1284
+ state.active = false;
1285
+ for (const [source, listener] of listeners) source.removeEventListener("abort", listener);
1286
+ },
1287
+ sources: merged
1288
+ };
1289
+ forwardingSignals.set(combinedController.signal, state);
1290
+ abortWithReason = (reason) => {
1291
+ if (combinedController.signal.aborted) return;
1292
+ combinedController.abort(reason);
1293
+ state.release();
1294
+ };
1295
+ const alreadyAborted = merged.find((signal) => signal.aborted);
1296
+ if (alreadyAborted) {
1297
+ abortWithReason(alreadyAborted.reason);
1298
+ return combinedController.signal;
1299
+ }
1300
+ for (const source of merged) {
1301
+ const listener = () => abortWithReason(source.reason);
1302
+ listeners.push([source, listener]);
1303
+ source.addEventListener("abort", listener, { once: true });
1304
+ }
1305
+ return combinedController.signal;
1306
+ }
1380
1307
  // src/internal/bytes.ts
1381
1308
  const textDecoder = new TextDecoder();
1382
1309
  function decodeBytes(value) {
@@ -1774,7 +1701,9 @@ var StructError = class extends Error {
1774
1701
  }
1775
1702
  };
1776
1703
  function createFormattedError() {
1777
- return Object.assign(Object.create(null), { _errors: [] });
1704
+ const node = Object.create(null);
1705
+ node._errors = [];
1706
+ return node;
1778
1707
  }
1779
1708
  function formatErrorTreeKey(segment) {
1780
1709
  const key = String(segment);
@@ -2696,7 +2625,7 @@ function createBaseTransportRequest(method, path, input, builder, options) {
2696
2625
  const declaredContentType = headers.has("content-type");
2697
2626
  if (options.transport === "sse" && built.defaultContentType && !declaredContentType) headers.set("content-type", built.defaultContentType);
2698
2627
  const request = {
2699
- abort: options.abort,
2628
+ signal: options.signal,
2700
2629
  baseEndpoint: options.baseEndpoint,
2701
2630
  body: built.body,
2702
2631
  bodyContentTypeSource: built.body,
@@ -2706,7 +2635,6 @@ function createBaseTransportRequest(method, path, input, builder, options) {
2706
2635
  operation: options.operation,
2707
2636
  queryParams,
2708
2637
  queryString,
2709
- timeout: options.timeout,
2710
2638
  withCredentials: options.withCredentials ?? false
2711
2639
  };
2712
2640
  resolveRequestUrl(request);
@@ -2718,14 +2646,13 @@ function createBaseTransportRequest(method, path, input, builder, options) {
2718
2646
  function createHttpRequest(method, path, input, builder, options) {
2719
2647
  return {
2720
2648
  ...createBaseTransportRequest(method, path, input, builder, {
2721
- abort: options.abort,
2649
+ signal: options.signal,
2722
2650
  baseEndpoint: options.baseEndpoint,
2723
2651
  defaultHeaders: options.defaultHeaders,
2724
2652
  inputDeclared: options.inputDeclared,
2725
2653
  inputGuides: options.inputGuides,
2726
2654
  operation: options.operation,
2727
2655
  querySerializer: options.querySerializer,
2728
- timeout: options.timeout,
2729
2656
  transport: "http",
2730
2657
  withCredentials: options.withCredentials
2731
2658
  }),
@@ -2743,7 +2670,7 @@ function createHttpRequest(method, path, input, builder, options) {
2743
2670
  *
2744
2671
  * An upload observer can fail before any response exists, so `meta` is absent then. Download
2745
2672
  * observers, truncated reads, and representation checks already have a response. Those steps
2746
- * are indistinguishable once they collapse into a bare cause, so command execution maps
2673
+ * are indistinguishable once they collapse into a bare cause, so operation execution maps
2747
2674
  * `code` straight through.
2748
2675
  *
2749
2676
  * Caller cancellation is deliberately never tagged: it stays the raw abort reason so the
@@ -3014,8 +2941,8 @@ function createFetchRequestInit(request) {
3014
2941
  return createFetchInitBase(request, {
3015
2942
  defaultAccept: "application/json, text/plain, */*",
3016
2943
  headers,
3017
- signal: request.abort,
3018
- wrapReadableStreamBody: uploadProgress ? (body) => wrapUploadProgressStream(body, uploadProgress, getContentLength(headers), request.abort) : void 0
2944
+ signal: request.signal,
2945
+ wrapReadableStreamBody: uploadProgress ? (body) => wrapUploadProgressStream(body, uploadProgress, getContentLength(headers), request.signal) : void 0
3019
2946
  });
3020
2947
  }
3021
2948
  function createFetchRequest(request) {
@@ -3075,7 +3002,7 @@ async function parseFetchResponse(httpRequest, response, fallbackUrl, platform)
3075
3002
  throw new BodyFailure("RES_MEDIA_TYPE_INVALID", /* @__PURE__ */ new TypeError(`Expected a JSON media type, received ${contentType || "<none>"}`), meta);
3076
3003
  }
3077
3004
  if (response.body && platform && !downloadProgress) {
3078
- const bytes = await readWholeBody(response, httpRequest.abort, meta);
3005
+ const bytes = await readWholeBody(response, httpRequest.signal, meta);
3079
3006
  try {
3080
3007
  body = parseBytesBody(responseType, bytes, contentType);
3081
3008
  } catch (error) {
@@ -3084,7 +3011,7 @@ async function parseFetchResponse(httpRequest, response, fallbackUrl, platform)
3084
3011
  } else if (response.body) {
3085
3012
  const chunks = [];
3086
3013
  const reader = response.body.getReader();
3087
- const signal = httpRequest.abort;
3014
+ const signal = httpRequest.signal;
3088
3015
  const cancelRead = () => void reader.cancel(signal?.reason).catch(() => void 0);
3089
3016
  signal?.addEventListener("abort", cancelRead, { once: true });
3090
3017
  let receivedLength = 0;
@@ -3140,7 +3067,7 @@ async function parseFetchResponse(httpRequest, response, fallbackUrl, platform)
3140
3067
  * global `fetch` resolved at send time. It rejects, rather than resolving with
3141
3068
  * a synthetic response, when no response reached the client.
3142
3069
  *
3143
- * @param httpRequest - Normalized request (URL, headers, body, abort, progress hooks).
3070
+ * @param httpRequest - Normalized request (URL, headers, body, signal, progress hooks).
3144
3071
  * @param fetchImpl - Fetch implementation; defaults to global `fetch`. The library default handle is called as
3145
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
3146
3073
  * `Request` and its body is read chunk by chunk.
@@ -3168,13 +3095,13 @@ async function fetchHandler(httpRequest, fetchImpl = resolveGlobalFetch()) {
3168
3095
  try {
3169
3096
  response = await sendFetchRequest(observed, fetchImpl);
3170
3097
  } catch (error) {
3171
- throw observerFailure !== void 0 && !httpRequest.abort?.aborted ? observerFailure : error;
3098
+ throw observerFailure !== void 0 && !httpRequest.signal?.aborted ? observerFailure : error;
3172
3099
  }
3173
3100
  if (observerFailure !== void 0) throw observerFailure;
3174
3101
  return response;
3175
3102
  }
3176
3103
  async function sendFetchRequest(httpRequest, fetchImpl) {
3177
- const abortSignal = httpRequest.abort;
3104
+ const abortSignal = httpRequest.signal;
3178
3105
  const platform = fetchImpl === PLATFORM_FETCH_HANDLE;
3179
3106
  let prepared = httpRequest;
3180
3107
  let request;
@@ -3219,7 +3146,7 @@ function isBrowserWindowOrWorker() {
3219
3146
  async function prepareUploadProgressRequest(request) {
3220
3147
  const { uploadProgress, body } = request;
3221
3148
  if (!uploadProgress || body == null || isReadableStreamBody(body) || !supportsStreamingRequestBody() || isBrowserWindowOrWorker()) return request;
3222
- request.abort?.throwIfAborted();
3149
+ request.signal?.throwIfAborted();
3223
3150
  const converted = convertBodyToUploadStream(body);
3224
3151
  if (!converted) return request;
3225
3152
  const headers = new Headers(request.headers);
@@ -3233,7 +3160,7 @@ async function prepareUploadProgressRequest(request) {
3233
3160
  total
3234
3161
  };
3235
3162
  try {
3236
- if (request.abort) await awaitWithSignal(() => uploadProgress(startEvent), request.abort);
3163
+ if (request.signal) await awaitWithSignal(() => uploadProgress(startEvent), request.signal);
3237
3164
  else await uploadProgress(startEvent);
3238
3165
  } catch (error) {
3239
3166
  converted.stream.cancel(error).catch(() => void 0);
@@ -3272,8 +3199,7 @@ function convertBodyToUploadStream(body) {
3272
3199
  * Declares a typed HTTP request endpoint.
3273
3200
  *
3274
3201
  * Pass method, path, an optional input struct, and the single `output` and
3275
- * `error` structs. Call the returned endpoint with input to get an `HttpCommand`
3276
- * for `client.execute`.
3202
+ * `error` structs. Call the returned operation with a client and, when declared, the input.
3277
3203
  *
3278
3204
  * The contract is `method`, `path`, an optional `input` struct, optional
3279
3205
  * `output` and `error` structs, and an optional `operation`. A declared `input`
@@ -3289,7 +3215,7 @@ function convertBodyToUploadStream(body) {
3289
3215
  * omitting one means that body is never read.
3290
3216
  *
3291
3217
  * @param definition - Request contract (method, path, input, builder, output).
3292
- * @returns An endpoint that creates `HttpCommand` values from input.
3218
+ * @returns An operation `(client, ...input) => Promise<error-first tuple>`.
3293
3219
  *
3294
3220
  * The checked example file passes the whole input object as `path`. This snippet maps individual fields into `path` and `query` instead:
3295
3221
  *
@@ -3308,43 +3234,10 @@ function convertBodyToUploadStream(body) {
3308
3234
  */
3309
3235
  function defineRequest(definition) {
3310
3236
  const endpoint = { ...definition };
3311
- function create(input) {
3312
- return {
3313
- [COMMAND_EXECUTE]: executeHttpCommand,
3314
- [COMMAND_TYPE]: HTTP_COMMAND,
3315
- definition: endpoint,
3316
- input
3317
- };
3318
- }
3319
- return ((input) => create(input));
3237
+ return (async (client, input) => executeHttpCommand(client[CLIENT].config, endpoint, input, client[CLIENT].options));
3320
3238
  }
3321
- /**
3322
- * Runs an `HttpCommand` against client config: validate input, send the request, and parse the response.
3323
- *
3324
- * Low-level entry used by `client.execute`. Prefer `client.execute(command, options)`
3325
- * in application code. `HttpClientConfig` is the HTTP part of `ClientConfig`, so
3326
- * a `ClientConfig` is accepted. `HttpExecuteAwaitResult` is the internal result
3327
- * tuple.
3328
- *
3329
- * The result is `[null, data, response]` or `[fault, undefined, undefined]`.
3330
- * `data` is the decoded `output` body, or `undefined` when none is declared.
3331
- * `response` is `DecodedResponse` when `output` is declared and `HttpMeta`
3332
- * otherwise. The fault is `Fault<TError>`. A non-2xx status is `HTTP_STATUS`
3333
- * when no `error` struct is declared (its `data` is `undefined`) or when the
3334
- * declared body decodes (its `data` is that body). When a declared `error` body
3335
- * cannot be read, the fault is a `RES_*` decode fault instead. On failure the
3336
- * tuple's third slot is `undefined`; the fault carries the response metadata.
3337
- * `options.timeout` falls back to the client's `withTimeout` default when omitted.
3338
- *
3339
- * @param clientConfig - Resolved HTTP client configuration (endpoint, interceptors, handler).
3340
- * @param command - Command from a `defineRequest` endpoint.
3341
- * @param options - Per-request progress and cancellation options.
3342
- * @returns An await-result tuple of success body or typed request error.
3343
- * @group http
3344
- */
3345
- async function executeHttpCommand(clientConfig, command, options) {
3346
- const { definition, input } = command;
3347
- const config = options ?? {};
3239
+ async function executeHttpCommand(clientConfig, definition, input, options) {
3240
+ const config = options;
3348
3241
  const fail = (fault) => {
3349
3242
  return [
3350
3243
  fault,
@@ -3352,34 +3245,10 @@ async function executeHttpCommand(clientConfig, command, options) {
3352
3245
  void 0
3353
3246
  ];
3354
3247
  };
3355
- let cancellation;
3356
- try {
3357
- cancellation = snapshotCancellationConfig({
3358
- abort: config.abort,
3359
- signal: config.signal,
3360
- timeout: config.timeout !== void 0 ? config.timeout : clientConfig.timeout
3361
- });
3362
- } catch (error) {
3363
- return fail(createPreflightFault("REQ_OPTIONS_INVALID", error));
3364
- }
3365
- if (hasAbortTimeoutConflict(config)) return fail(createAbortTimeoutConflictFault());
3366
- try {
3367
- validateTransportTimeout(cancellation.timeout);
3368
- } catch (error) {
3369
- return fail(createPreflightFault("REQ_OPTIONS_INVALID", error));
3370
- }
3371
- const controller = new AbortController();
3372
- const preAbortedSignal = [cancellation.abort, cancellation.signal].find((signal) => signal?.aborted);
3373
- if (preAbortedSignal) return fail(resolveAbortedFault(preAbortedSignal));
3374
- const deadline = armTransportDeadline(cancellation.timeout);
3375
- const requestSignal = cancellation.abort === void 0 && cancellation.signal === void 0 && deadline.signal ? deadline.signal : mergeAbortSignals(controller.signal, [
3376
- cancellation.abort,
3377
- cancellation.signal,
3378
- deadline.signal
3379
- ]);
3380
- deadline.releaseOnAbort(requestSignal);
3248
+ const callerAbort = config.signal;
3249
+ if (callerAbort?.aborted) return fail(resolveAbortedFault(callerAbort));
3250
+ const requestSignal = mergeAbortSignals(new AbortController().signal, [callerAbort]);
3381
3251
  const stopBeforeTransport = (fault) => {
3382
- deadline.release();
3383
3252
  releaseAbortSignal(requestSignal);
3384
3253
  return fail(fault);
3385
3254
  };
@@ -3390,14 +3259,14 @@ async function executeHttpCommand(clientConfig, command, options) {
3390
3259
  try {
3391
3260
  parsedInput = parseEndpointInput(definition.input, input, inputGuides);
3392
3261
  } catch (error) {
3393
- return stopBeforeTransport(resolveTransportCancellation(deadline, requestSignal, createPreflightFault("REQ_INPUT_INVALID", error)));
3262
+ return stopBeforeTransport(resolveAbortFault(requestSignal) ?? createPreflightFault("REQ_INPUT_INVALID", error));
3394
3263
  }
3395
- const parsedCancellation = resolveTransportCancellation(deadline, requestSignal);
3264
+ const parsedCancellation = resolveAbortFault(requestSignal);
3396
3265
  if (parsedCancellation) return stopBeforeTransport(parsedCancellation);
3397
3266
  let request;
3398
3267
  try {
3399
3268
  request = createHttpRequest(definition.method, definition.path, parsedInput, definition.builder, {
3400
- abort: requestSignal,
3269
+ signal: requestSignal,
3401
3270
  baseEndpoint: clientConfig.endpoint,
3402
3271
  defaultHeaders: clientConfig.headers,
3403
3272
  downloadProgress: config.onDownloadProgress,
@@ -3407,17 +3276,15 @@ async function executeHttpCommand(clientConfig, command, options) {
3407
3276
  operation: definition.operation,
3408
3277
  querySerializer: clientConfig.querySerializer,
3409
3278
  responseType,
3410
- timeout: cancellation.timeout,
3411
3279
  uploadProgress: config.onUploadProgress,
3412
3280
  withCredentials: clientConfig.withCredentials,
3413
3281
  xsrf: clientConfig.xsrf
3414
3282
  });
3415
3283
  } catch (error) {
3416
- return stopBeforeTransport(resolveTransportCancellation(deadline, requestSignal, createPreflightFault("REQ_BUILD_FAILED", error)));
3284
+ return stopBeforeTransport(resolveAbortFault(requestSignal) ?? createPreflightFault("REQ_BUILD_FAILED", error));
3417
3285
  }
3418
- const builtCancellation = resolveTransportCancellation(deadline, requestSignal);
3286
+ const builtCancellation = resolveAbortFault(requestSignal);
3419
3287
  if (builtCancellation) return stopBeforeTransport(builtCancellation);
3420
- const releaseRequestTimeout = deadline.release;
3421
3288
  const transportOrigin = createTransportOrigin();
3422
3289
  const transportController = new AbortController();
3423
3290
  let chainSettled = false;
@@ -3436,8 +3303,8 @@ async function executeHttpCommand(clientConfig, command, options) {
3436
3303
  rejected.catch(() => void 0);
3437
3304
  return rejected;
3438
3305
  }
3439
- const transportAbort = nextRequest.abort === void 0 || nextRequest.abort === requestSignal ? transportController.signal : mergeAbortSignals(transportController.signal, [nextRequest.abort]);
3440
- if (resolveTransportCancellation(deadline, transportAbort)) {
3306
+ const transportAbort = nextRequest.signal === void 0 || nextRequest.signal === requestSignal ? transportController.signal : mergeAbortSignals(transportController.signal, [nextRequest.signal]);
3307
+ if (resolveAbortFault(transportAbort)) {
3441
3308
  const rejected = Promise.reject(transportOrigin.mark(resolveAbortCause(transportAbort)));
3442
3309
  releaseAbortSignal(transportAbort);
3443
3310
  rejected.catch(() => void 0);
@@ -3445,7 +3312,7 @@ async function executeHttpCommand(clientConfig, command, options) {
3445
3312
  }
3446
3313
  const pending = fetchHandler({
3447
3314
  ...nextRequest,
3448
- abort: transportAbort
3315
+ signal: transportAbort
3449
3316
  }, clientConfig.http.handle).catch((error) => {
3450
3317
  throw transportOrigin.mark(resolveAbortCause(transportAbort) ?? error);
3451
3318
  }).finally(() => {
@@ -3469,7 +3336,7 @@ async function executeHttpCommand(clientConfig, command, options) {
3469
3336
  response = settled;
3470
3337
  } catch (error) {
3471
3338
  const fromTransport = transportOrigin.has(error);
3472
- const cancelled = resolveTransportCancellation(deadline, requestSignal);
3339
+ const cancelled = resolveAbortFault(requestSignal);
3473
3340
  if (cancelled) {
3474
3341
  abortTransport();
3475
3342
  if (inflightTransport) await Promise.allSettled([inflightTransport]);
@@ -3486,19 +3353,13 @@ async function executeHttpCommand(clientConfig, command, options) {
3486
3353
  requestSignal.removeEventListener("abort", abortTransport);
3487
3354
  chainSettled = true;
3488
3355
  if (!transportController.signal.aborted) transportController.abort(/* @__PURE__ */ new Error("HTTP interceptor chain settled"));
3489
- if (!response) {
3490
- releaseRequestTimeout();
3491
- releaseAbortSignal(requestSignal);
3492
- }
3356
+ if (!response) releaseAbortSignal(requestSignal);
3493
3357
  }
3494
3358
  const settledResponse = response;
3495
3359
  const meta = toHttpMeta(settledResponse);
3496
3360
  const struct = settledResponse.ok ? definition.output : definition.error;
3497
3361
  if (!struct) {
3498
- const cancelled = resolveTransportCancellation(deadline, requestSignal);
3499
- releaseRequestTimeout();
3500
3362
  releaseAbortSignal(requestSignal);
3501
- if (cancelled) return fail(cancelled);
3502
3363
  if (settledResponse.ok) return [
3503
3364
  null,
3504
3365
  void 0,
@@ -3509,16 +3370,13 @@ async function executeHttpCommand(clientConfig, command, options) {
3509
3370
  let decoded;
3510
3371
  try {
3511
3372
  decoded = await decodeDeclaredBody(struct, settledResponse, requestSignal);
3512
- const cancelled = resolveTransportCancellation(deadline, requestSignal);
3513
- if (cancelled) return fail(cancelled);
3514
3373
  } catch (error) {
3515
- const cancelled = resolveTransportCancellation(deadline, requestSignal);
3374
+ const cancelled = resolveAbortFault(requestSignal);
3516
3375
  if (cancelled) return fail(cancelled);
3517
3376
  if (error instanceof JsonMediaTypeError) return fail(createDecodeFault("RES_MEDIA_TYPE_INVALID", error, meta));
3518
3377
  if (error instanceof StructError) return fail(createDecodeFault("RES_STRUCT_MISMATCH", error, meta));
3519
3378
  return fail(createDecodeFault("RES_DECODE_FAILED", error, meta));
3520
3379
  } finally {
3521
- releaseRequestTimeout();
3522
3380
  releaseAbortSignal(requestSignal);
3523
3381
  }
3524
3382
  const decodedResponse = {
@@ -3608,7 +3466,7 @@ function createBasicAuthModifier(fn, options) {
3608
3466
  * `encode`, calling `basicAuthHttpInterceptor` throws
3609
3467
  * `Error: BasicAuthInterceptor is not supported in this environment` when `btoa`
3610
3468
  * is not available in the runtime. Use {@link basicAuthSSEInterceptor} for SSE
3611
- * commands.
3469
+ * operations.
3612
3470
  *
3613
3471
  * @param fn - Factory returning username/password for each request.
3614
3472
  * @param options - Optional custom encoder (defaults to `btoa`).
@@ -4010,7 +3868,7 @@ async function fetchEventStream(request, options) {
4010
3868
  }
4011
3869
  async function start() {
4012
3870
  while (!closeController.signal.aborted) {
4013
- const attemptAbort = mergeAbortSignals(closeController.signal, [request.abort]);
3871
+ const attemptAbort = mergeAbortSignals(closeController.signal, [request.signal]);
4014
3872
  let response;
4015
3873
  let readerStarted = false;
4016
3874
  try {
@@ -4047,8 +3905,8 @@ async function fetchEventStream(request, options) {
4047
3905
  return;
4048
3906
  } catch (error) {
4049
3907
  if (response?.body && !readerStarted) response.body.cancel(error).catch(() => void 0);
4050
- const normalizedError = normalizeAbortError(error, request.abort, closeController.signal);
4051
- if (closeController.signal.aborted || request.abort?.aborted) {
3908
+ const normalizedError = normalizeAbortError(error, request.signal, closeController.signal);
3909
+ if (closeController.signal.aborted || request.signal?.aborted) {
4052
3910
  finishAborted(attemptAbort);
4053
3911
  return;
4054
3912
  }
@@ -4056,7 +3914,7 @@ async function fetchEventStream(request, options) {
4056
3914
  const fatalError = toEventStreamFatalError(retryError);
4057
3915
  if (fatalError) {
4058
3916
  await observeFatalError(fatalError, attemptAbort);
4059
- if (closeController.signal.aborted || request.abort?.aborted) finishAborted(attemptAbort);
3917
+ if (closeController.signal.aborted || request.signal?.aborted) finishAborted(attemptAbort);
4060
3918
  else finishError(fatalError);
4061
3919
  return;
4062
3920
  }
@@ -4068,7 +3926,7 @@ async function fetchEventStream(request, options) {
4068
3926
  try {
4069
3927
  retryDelay = await resolveRetryDelay(retryError, attemptAbort);
4070
3928
  } catch (policyError) {
4071
- if (closeController.signal.aborted || request.abort?.aborted) finishAborted(attemptAbort);
3929
+ if (closeController.signal.aborted || request.signal?.aborted) finishAborted(attemptAbort);
4072
3930
  else finishError(policyError);
4073
3931
  return;
4074
3932
  }
@@ -4080,7 +3938,7 @@ async function fetchEventStream(request, options) {
4080
3938
  await wait(retryDelay, attemptAbort);
4081
3939
  } catch (waitError) {
4082
3940
  /* istanbul ignore else -- @preserve */
4083
- if (closeController.signal.aborted || request.abort?.aborted) finishAborted(attemptAbort);
3941
+ if (closeController.signal.aborted || request.signal?.aborted) finishAborted(attemptAbort);
4084
3942
  else finishError(waitError);
4085
3943
  return;
4086
3944
  }
@@ -4298,15 +4156,15 @@ function toCloseReason(reason) {
4298
4156
  /**
4299
4157
  * Declare a typed Server-Sent Events endpoint.
4300
4158
  *
4301
- * Pass path, event structs, and buffer/queue limits. Call the returned endpoint
4302
- * with input to get an `EventStreamCommand` for `client.execute`.
4159
+ * Pass path, event structs, and buffer/queue limits. Call the returned operation
4160
+ * with a client and, when declared, the input.
4303
4161
  *
4304
4162
  * - **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`.
4305
4163
  *
4306
4164
  * `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.
4307
4165
  *
4308
4166
  * @param definition - SSE contract (path, events, buffer limits, optional input).
4309
- * @returns An endpoint that creates `EventStreamCommand` values from input.
4167
+ * @returns An operation `(client, ...input) => Promise<error-first tuple>`.
4310
4168
  *
4311
4169
  * @group sse
4312
4170
  * @exampleFile examples/api/define-event-stream.ts#example
@@ -4316,61 +4174,12 @@ function defineEventStream(definition) {
4316
4174
  ...definition,
4317
4175
  method: definition.method ?? "GET"
4318
4176
  };
4319
- function create(input) {
4320
- return {
4321
- [COMMAND_EXECUTE]: executeEventStreamCommand,
4322
- [COMMAND_TYPE]: EVENT_STREAM_COMMAND,
4323
- endpoint,
4324
- input
4325
- };
4326
- }
4327
- return ((input) => create(input));
4328
- }
4329
- /**
4330
- * Open an SSE command against the given client config.
4331
- *
4332
- * Prefer `client.execute(command)` in application code; this is the low-level entry used by the client.
4333
- *
4334
- * 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.
4335
- *
4336
- * @param clientConfig - Resolved client configuration.
4337
- * @param command - SSE command from an `EventStreamEndpoint`.
4338
- * @param options - Optional execute options (abort, timeout).
4339
- * @returns Await-result tuple of `[error, stream, open]`.
4340
- * @group sse
4341
- */
4342
- async function executeEventStreamCommand(clientConfig, command, options) {
4343
- const { endpoint, input } = command;
4344
- return runEventStreamCommand(clientConfig, endpoint, input, options ?? {}, new AbortController());
4177
+ return (async (client, input) => runEventStreamCommand(client[CLIENT].config, endpoint, input, client[CLIENT].options, new AbortController()));
4345
4178
  }
4346
4179
  async function runEventStreamCommand(clientConfig, endpoint, input, config, controller) {
4347
- let cancellation;
4348
- try {
4349
- cancellation = snapshotCancellationConfig(config);
4350
- } catch (error) {
4351
- return [
4352
- createPreflightFault("REQ_OPTIONS_INVALID", error),
4353
- void 0,
4354
- void 0
4355
- ];
4356
- }
4357
- if (hasAbortTimeoutConflict(cancellation)) return [
4358
- createAbortTimeoutConflictFault(),
4359
- void 0,
4360
- void 0
4361
- ];
4362
- try {
4363
- validateTransportTimeout(cancellation.timeout);
4364
- } catch (error) {
4365
- return [
4366
- createPreflightFault("REQ_OPTIONS_INVALID", error),
4367
- void 0,
4368
- void 0
4369
- ];
4370
- }
4371
- const preAbortedSignal = [cancellation.abort, cancellation.signal].find((signal) => signal?.aborted);
4372
- if (preAbortedSignal) return [
4373
- resolveAbortedFault(preAbortedSignal),
4180
+ const callerAbort = config.signal;
4181
+ if (callerAbort?.aborted) return [
4182
+ resolveAbortedFault(callerAbort),
4374
4183
  void 0,
4375
4184
  void 0
4376
4185
  ];
@@ -4390,15 +4199,8 @@ async function runEventStreamCommand(clientConfig, endpoint, input, config, cont
4390
4199
  void 0
4391
4200
  ];
4392
4201
  }
4393
- const deadline = armTransportDeadline(cancellation.timeout);
4394
- const requestSignal = mergeAbortSignals(controller.signal, [
4395
- cancellation.abort,
4396
- cancellation.signal,
4397
- deadline.signal
4398
- ]);
4399
- deadline.releaseOnAbort(requestSignal);
4202
+ const requestSignal = mergeAbortSignals(controller.signal, [callerAbort]);
4400
4203
  const stopBeforeTransport = (fault) => {
4401
- deadline.release();
4402
4204
  releaseAbortSignal(requestSignal);
4403
4205
  return [
4404
4206
  fault,
@@ -4411,28 +4213,27 @@ async function runEventStreamCommand(clientConfig, endpoint, input, config, cont
4411
4213
  try {
4412
4214
  parsedInput = parseEndpointInput(endpoint.input, input, inputGuides);
4413
4215
  } catch (error) {
4414
- return stopBeforeTransport(resolveTransportCancellation(deadline, requestSignal, createPreflightFault("REQ_INPUT_INVALID", error)));
4216
+ return stopBeforeTransport(resolveAbortFault(requestSignal) ?? createPreflightFault("REQ_INPUT_INVALID", error));
4415
4217
  }
4416
- const parsedCancellation = resolveTransportCancellation(deadline, requestSignal);
4218
+ const parsedCancellation = resolveAbortFault(requestSignal);
4417
4219
  if (parsedCancellation) return stopBeforeTransport(parsedCancellation);
4418
4220
  let request;
4419
4221
  try {
4420
4222
  request = createBaseTransportRequest(endpoint.method, endpoint.path, parsedInput, endpoint.builder, {
4421
- abort: requestSignal,
4223
+ signal: requestSignal,
4422
4224
  baseEndpoint: clientConfig.endpoint,
4423
4225
  defaultHeaders: clientConfig.headers,
4424
4226
  inputDeclared: endpoint.input !== void 0,
4425
4227
  inputGuides,
4426
4228
  operation: endpoint.operation,
4427
4229
  querySerializer: clientConfig.querySerializer,
4428
- timeout: cancellation.timeout,
4429
4230
  transport: "sse",
4430
4231
  withCredentials: clientConfig.withCredentials
4431
4232
  });
4432
4233
  } catch (error) {
4433
- return stopBeforeTransport(resolveTransportCancellation(deadline, requestSignal, createPreflightFault("REQ_BUILD_FAILED", error)));
4234
+ return stopBeforeTransport(resolveAbortFault(requestSignal) ?? createPreflightFault("REQ_BUILD_FAILED", error));
4434
4235
  }
4435
- const builtCancellation = resolveTransportCancellation(deadline, requestSignal);
4236
+ const builtCancellation = resolveAbortFault(requestSignal);
4436
4237
  if (builtCancellation) return stopBeforeTransport(builtCancellation);
4437
4238
  let chainSettled = false;
4438
4239
  let streamDelivered = false;
@@ -4454,8 +4255,8 @@ async function runEventStreamCommand(clientConfig, endpoint, input, config, cont
4454
4255
  rejected.catch(() => void 0);
4455
4256
  return rejected;
4456
4257
  }
4457
- const transportAbort = mergeAbortSignals(requestSignal, [req.abort]);
4458
- if (resolveTransportCancellation(deadline, transportAbort)) {
4258
+ const transportAbort = mergeAbortSignals(requestSignal, [req.signal]);
4259
+ if (resolveAbortFault(transportAbort)) {
4459
4260
  const rejected = Promise.reject(transportOrigin.mark(resolveAbortCause(transportAbort)));
4460
4261
  releaseAbortSignal(transportAbort);
4461
4262
  rejected.catch(() => void 0);
@@ -4478,7 +4279,7 @@ async function runEventStreamCommand(clientConfig, endpoint, input, config, cont
4478
4279
  const failure = {};
4479
4280
  const promise = fetchEventStream({
4480
4281
  ...req,
4481
- abort: streamAbort
4282
+ signal: streamAbort
4482
4283
  }, {
4483
4284
  fetch: clientConfig.sse.handle,
4484
4285
  failure,
@@ -4536,7 +4337,6 @@ async function runEventStreamCommand(clientConfig, endpoint, input, config, cont
4536
4337
  openInfo
4537
4338
  ];
4538
4339
  } finally {
4539
- deadline.release();
4540
4340
  if (!streamDelivered) releaseAbortSignal(requestSignal);
4541
4341
  }
4542
4342
  function discardOwnedStreams(reason, delivered) {
@@ -5330,7 +5130,7 @@ const struct = {
5330
5130
  function createWebSocketRequest(params) {
5331
5131
  const { queryParams, queryString } = resolveTransportQuery(params.query, params.querySerializer);
5332
5132
  const request = {
5333
- abort: params.abort,
5133
+ signal: params.signal,
5334
5134
  baseEndpoint: params.baseEndpoint,
5335
5135
  endpoint: fillUrl(params.path, params.params),
5336
5136
  headers: new Headers(),
@@ -5467,70 +5267,97 @@ function shouldReconnect(config, outcome, attempt) {
5467
5267
  * Declare a typed WebSocket endpoint.
5468
5268
  *
5469
5269
  * Pass path, incoming (and optional outgoing) message structs, and queue limits.
5470
- * Call the returned endpoint with input to get a `WebSocketCommand` for `client.execute`.
5270
+ * Call the returned operation with a client and, when declared, the input.
5471
5271
  *
5472
5272
  * - **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`.
5473
5273
  *
5474
5274
  * A WebSocket `builder` may return `path` and `query` only; `headers` and `body` are rejected. `WebSocketDefinitionBase` and `BuilderField` are internal helper types.
5475
5275
  *
5476
5276
  * @param definition - WebSocket contract (path, structs, queue limits, optional input).
5477
- * @returns An endpoint that creates `WebSocketCommand` values from input.
5277
+ * @returns An operation `(client, ...input) => Promise<error-first tuple>`.
5478
5278
  *
5479
5279
  * @group web-socket
5480
5280
  * @exampleFile examples/api/define-web-socket.ts#example
5481
5281
  */
5482
5282
  function defineWebSocket(definition) {
5483
5283
  const endpoint = { ...definition };
5484
- function create(input) {
5485
- return {
5486
- [COMMAND_EXECUTE]: executeWebSocketCommand,
5487
- [COMMAND_TYPE]: WEB_SOCKET_COMMAND,
5488
- endpoint,
5489
- input
5284
+ return (async (client, input) => {
5285
+ const requestOptions = client[CLIENT].options;
5286
+ const controller = new AbortController();
5287
+ const socketState = {
5288
+ listeners: {
5289
+ runtimeError: /* @__PURE__ */ new Set(),
5290
+ stateChange: /* @__PURE__ */ new Set()
5291
+ },
5292
+ status: "idle",
5293
+ transition: 0
5490
5294
  };
5295
+ return runWebSocketCommand(client[CLIENT].config, endpoint, input, requestOptions, controller, socketState);
5296
+ });
5297
+ }
5298
+ function readQueueLimits(maxIncomingQueueSize, maxOutgoingQueueSize) {
5299
+ try {
5300
+ validateWebSocketQueueLimits(maxIncomingQueueSize, maxOutgoingQueueSize);
5301
+ return [void 0, true];
5302
+ } catch (error) {
5303
+ return [createPreflightFault("REQ_OPTIONS_INVALID", error), void 0];
5491
5304
  }
5492
- return ((input) => create(input));
5493
5305
  }
5494
- async function runWebSocketCommand(clientConfig, endpoint, input, config, controller, state) {
5495
- setSocketState(state, "connecting");
5496
- let cancellationConfig;
5306
+ function readCallOptions(config, webSocket) {
5497
5307
  try {
5498
- cancellationConfig = snapshotCancellationConfig(config);
5308
+ const beforeConnect = config.beforeConnect;
5309
+ const configuredProtocols = config.protocols;
5310
+ const protocols = configuredProtocols ? [...configuredProtocols] : void 0;
5311
+ const reconnectOption = config.reconnect ?? webSocket.reconnect;
5312
+ const heartbeatOption = config.heartbeat ?? webSocket.heartbeat;
5313
+ const heartbeat = heartbeatOption ? {
5314
+ intervalMs: heartbeatOption.intervalMs,
5315
+ isAck: heartbeatOption.isAck,
5316
+ message: heartbeatOption.message,
5317
+ timeoutMs: heartbeatOption.timeoutMs
5318
+ } : void 0;
5319
+ validateHeartbeatConfig(heartbeat);
5320
+ return [void 0, {
5321
+ beforeConnect,
5322
+ heartbeat,
5323
+ protocols,
5324
+ reconnect: normalizeReconnectConfig(reconnectOption)
5325
+ }];
5499
5326
  } catch (error) {
5500
- const definitionError = createPreflightFault("REQ_OPTIONS_INVALID", error);
5501
- state.error = definitionError;
5502
- setSocketState(state, "error");
5503
- return [
5504
- definitionError,
5505
- void 0,
5506
- void 0
5507
- ];
5327
+ return [createPreflightFault("REQ_OPTIONS_INVALID", error), void 0];
5508
5328
  }
5509
- if (hasAbortTimeoutConflict(cancellationConfig)) {
5510
- const definitionError = createAbortTimeoutConflictFault();
5511
- state.error = definitionError;
5512
- setSocketState(state, "error");
5513
- return [
5514
- definitionError,
5515
- void 0,
5516
- void 0
5517
- ];
5329
+ }
5330
+ function parseInput(inputStruct, input, guides, signal) {
5331
+ try {
5332
+ return [void 0, parseEndpointInput(inputStruct, input, guides)];
5333
+ } catch (error) {
5334
+ return [resolveAbortFault(signal) ?? createPreflightFault("REQ_INPUT_INVALID", error), void 0];
5518
5335
  }
5336
+ }
5337
+ function buildOutbound(parsedInput, builder, inputDeclared, guides, signal) {
5519
5338
  try {
5520
- validateTransportTimeout(cancellationConfig.timeout);
5339
+ return [void 0, materializeOutboundRequest(runOutboundBuilder(parsedInput, builder, inputDeclared), "webSocket", guides)];
5521
5340
  } catch (error) {
5522
- const definitionError = createPreflightFault("REQ_OPTIONS_INVALID", error);
5523
- state.error = definitionError;
5524
- setSocketState(state, "error");
5525
- return [
5526
- definitionError,
5527
- void 0,
5528
- void 0
5529
- ];
5341
+ return [resolveAbortFault(signal) ?? createPreflightFault("REQ_BUILD_FAILED", error), void 0];
5342
+ }
5343
+ }
5344
+ function createSocketRequest(args) {
5345
+ try {
5346
+ return [void 0, createWebSocketRequest(args)];
5347
+ } catch (error) {
5348
+ return [resolveAbortFault(args.signal) ?? createPreflightFault("REQ_BUILD_FAILED", error), void 0];
5530
5349
  }
5531
- const preAbortedSignal = [cancellationConfig.abort, cancellationConfig.signal].find((signal) => signal?.aborted);
5532
- if (preAbortedSignal) {
5533
- const aborted = resolveAbortedFault(preAbortedSignal);
5350
+ }
5351
+ function resolveSocketConstructor(handle) {
5352
+ const candidate = handle ?? globalThis.WebSocket;
5353
+ if (typeof candidate !== "function") return [createPreflightFault("ENV_UNSUPPORTED", /* @__PURE__ */ new Error("WebSocket is not supported in current runtime")), void 0];
5354
+ return [void 0, candidate];
5355
+ }
5356
+ async function runWebSocketCommand(clientConfig, endpoint, input, config, controller, state) {
5357
+ setSocketState(state, "connecting");
5358
+ const callerAbort = config.signal;
5359
+ if (callerAbort?.aborted) {
5360
+ const aborted = resolveAbortedFault(callerAbort);
5534
5361
  state.error = aborted;
5535
5362
  setSocketState(state, aborted.code === "NET_ABORTED" ? "aborted" : "error");
5536
5363
  return [
@@ -5539,56 +5366,26 @@ async function runWebSocketCommand(clientConfig, endpoint, input, config, contro
5539
5366
  void 0
5540
5367
  ];
5541
5368
  }
5542
- try {
5543
- validateWebSocketQueueLimits(endpoint.maxIncomingQueueSize, endpoint.maxOutgoingQueueSize);
5544
- } catch (error) {
5545
- const definitionError = createPreflightFault("REQ_OPTIONS_INVALID", error);
5546
- state.error = definitionError;
5547
- setSocketState(state, "error");
5548
- return [
5549
- definitionError,
5550
- void 0,
5551
- void 0
5552
- ];
5553
- }
5554
- let beforeConnectOption;
5555
- let protocolsOption;
5556
- let reconnect;
5557
- let heartbeatConfig;
5558
- try {
5559
- beforeConnectOption = config.beforeConnect;
5560
- const configuredProtocols = config.protocols;
5561
- protocolsOption = configuredProtocols ? [...configuredProtocols] : void 0;
5562
- const reconnectOption = config.reconnect ?? clientConfig.webSocket.reconnect;
5563
- const heartbeatOption = config.heartbeat ?? clientConfig.webSocket.heartbeat;
5564
- heartbeatConfig = heartbeatOption ? {
5565
- intervalMs: heartbeatOption.intervalMs,
5566
- isAck: heartbeatOption.isAck,
5567
- message: heartbeatOption.message,
5568
- timeoutMs: heartbeatOption.timeoutMs
5569
- } : void 0;
5570
- validateHeartbeatConfig(heartbeatConfig);
5571
- reconnect = normalizeReconnectConfig(reconnectOption);
5572
- } catch (error) {
5573
- const definitionError = createPreflightFault("REQ_OPTIONS_INVALID", error);
5574
- state.error = definitionError;
5369
+ const stopInvalidOptions = (fault) => {
5370
+ state.error = fault;
5575
5371
  setSocketState(state, "error");
5576
5372
  return [
5577
- definitionError,
5373
+ fault,
5578
5374
  void 0,
5579
5375
  void 0
5580
5376
  ];
5581
- }
5582
- const deadline = armTransportDeadline(cancellationConfig.timeout);
5583
- const chainSignal = mergeAbortSignals(new AbortController().signal, [
5584
- cancellationConfig.abort,
5585
- cancellationConfig.signal,
5586
- deadline.signal
5587
- ]);
5377
+ };
5378
+ const [queueFault] = readQueueLimits(endpoint.maxIncomingQueueSize, endpoint.maxOutgoingQueueSize);
5379
+ if (queueFault) return stopInvalidOptions(queueFault);
5380
+ const [optionsFault, callOptions] = readCallOptions(config, clientConfig.webSocket);
5381
+ if (optionsFault) return stopInvalidOptions(optionsFault);
5382
+ const beforeConnectOption = callOptions.beforeConnect;
5383
+ const protocolsOption = callOptions.protocols;
5384
+ const reconnect = callOptions.reconnect;
5385
+ const heartbeatConfig = callOptions.heartbeat;
5386
+ const chainSignal = mergeAbortSignals(new AbortController().signal, [callerAbort]);
5588
5387
  const signal = mergeAbortSignals(controller.signal, [chainSignal]);
5589
- deadline.releaseOnAbort(chainSignal);
5590
5388
  const stopBeforeTransport = (fault) => {
5591
- deadline.release();
5592
5389
  releaseAbortSignal(chainSignal);
5593
5390
  releaseAbortSignal(signal);
5594
5391
  state.error = fault;
@@ -5600,39 +5397,28 @@ async function runWebSocketCommand(clientConfig, endpoint, input, config, contro
5600
5397
  ];
5601
5398
  };
5602
5399
  const inputGuides = /* @__PURE__ */ new WeakMap();
5603
- let parsedInput;
5604
- try {
5605
- parsedInput = parseEndpointInput(endpoint.input, input, inputGuides);
5606
- } catch (error) {
5607
- return stopBeforeTransport(resolveTransportCancellation(deadline, signal, createPreflightFault("REQ_INPUT_INVALID", error)));
5608
- }
5609
- const parsedCancellation = resolveTransportCancellation(deadline, signal);
5400
+ const [inputFault, parsedInput] = parseInput(endpoint.input, input, inputGuides, signal);
5401
+ if (inputFault) return stopBeforeTransport(inputFault);
5402
+ const parsedCancellation = resolveAbortFault(signal);
5610
5403
  if (parsedCancellation) return stopBeforeTransport(parsedCancellation);
5611
- let built;
5612
- try {
5613
- built = materializeOutboundRequest(runOutboundBuilder(parsedInput, endpoint.builder, endpoint.input !== void 0), "webSocket", inputGuides);
5614
- } catch (error) {
5615
- return stopBeforeTransport(resolveTransportCancellation(deadline, signal, createPreflightFault("REQ_BUILD_FAILED", error)));
5616
- }
5617
- const builtCancellation = resolveTransportCancellation(deadline, signal);
5404
+ const [buildFault, built] = buildOutbound(parsedInput, endpoint.builder, endpoint.input !== void 0, inputGuides, signal);
5405
+ if (buildFault) return stopBeforeTransport(buildFault);
5406
+ const builtCancellation = resolveAbortFault(signal);
5618
5407
  if (builtCancellation) return stopBeforeTransport(builtCancellation);
5619
- let wsRequest;
5620
- try {
5621
- wsRequest = createWebSocketRequest({
5622
- abort: signal,
5623
- baseEndpoint: clientConfig.endpoint,
5624
- operation: endpoint.operation,
5625
- params: built.params,
5626
- path: endpoint.path,
5627
- query: built.query,
5628
- querySerializer: clientConfig.querySerializer,
5629
- withCredentials: clientConfig.withCredentials
5630
- });
5631
- } catch (error) {
5632
- return stopBeforeTransport(resolveTransportCancellation(deadline, signal, createPreflightFault("REQ_BUILD_FAILED", error)));
5633
- }
5634
- const WebSocketCtor = clientConfig.webSocket.handle ?? globalThis.WebSocket;
5635
- if (typeof WebSocketCtor !== "function") return stopBeforeTransport(createPreflightFault("ENV_UNSUPPORTED", /* @__PURE__ */ new Error("WebSocket is not supported in current runtime")));
5408
+ const [requestFault, wsRequest] = createSocketRequest({
5409
+ signal,
5410
+ baseEndpoint: clientConfig.endpoint,
5411
+ operation: endpoint.operation,
5412
+ params: built.params,
5413
+ path: endpoint.path,
5414
+ query: built.query,
5415
+ querySerializer: clientConfig.querySerializer,
5416
+ withCredentials: clientConfig.withCredentials
5417
+ });
5418
+ if (requestFault) return stopBeforeTransport(requestFault);
5419
+ const constructorStage = resolveSocketConstructor(clientConfig.webSocket.handle);
5420
+ if (constructorStage[0]) return stopBeforeTransport(constructorStage[0]);
5421
+ const WebSocketCtor = constructorStage[1];
5636
5422
  const WebSocketImpl = WebSocketCtor;
5637
5423
  let startupConnection;
5638
5424
  const undeliveredSessions = /* @__PURE__ */ new Set();
@@ -5648,7 +5434,7 @@ async function runWebSocketCommand(clientConfig, endpoint, input, config, contro
5648
5434
  wsHandlerInvoked = true;
5649
5435
  const executionSignal = signal;
5650
5436
  const pendingSession = new Promise((resolveSession, rejectSession) => {
5651
- const signal = mergeAbortSignals(executionSignal, [req.abort]);
5437
+ const signal = mergeAbortSignals(executionSignal, [req.signal]);
5652
5438
  const incomingQueue = new AsyncQueue({ maxSize: endpoint.maxIncomingQueueSize });
5653
5439
  const closedDeferred = Promise.withResolvers();
5654
5440
  const sessionController = {
@@ -5800,15 +5586,6 @@ async function runWebSocketCommand(clientConfig, endpoint, input, config, contro
5800
5586
  };
5801
5587
  }
5802
5588
  async function connectOnce(url, protocols) {
5803
- const due = resolveTransportCancellation(deadline, signal);
5804
- if (due) return {
5805
- cause: due,
5806
- closeInfo: {
5807
- cause: due,
5808
- reason: due.message
5809
- },
5810
- opened: false
5811
- };
5812
5589
  let socket;
5813
5590
  try {
5814
5591
  socket = protocols.length > 0 ? new WebSocketImpl(url, [...protocols]) : new WebSocketImpl(url);
@@ -5900,7 +5677,6 @@ async function runWebSocketCommand(clientConfig, endpoint, input, config, contro
5900
5677
  if (!startupSettled) {
5901
5678
  startupConnection = latestConnection;
5902
5679
  startupSettled = true;
5903
- deadline.release();
5904
5680
  resolveSession(session);
5905
5681
  }
5906
5682
  if (state.status === "open") startHeartbeat(socket, sessionController, heartbeatConfig, endpoint.outgoing, (cause) => {
@@ -6047,7 +5823,6 @@ async function runWebSocketCommand(clientConfig, endpoint, input, config, contro
6047
5823
  /* istanbul ignore if -- @preserve defensive once-only gate: terminal callers and reentrant state transitions check finished before invoking finish */
6048
5824
  if (finished) return;
6049
5825
  finished = true;
6050
- deadline.release();
6051
5826
  stopHeartbeat(sessionController);
6052
5827
  const activeSocket = sessionController.currentSocket;
6053
5828
  if (final.kind !== "closed" && activeSocket && !options.skipNativeClose) requestNativeCloseBestEffort(activeSocket);
@@ -6162,7 +5937,6 @@ async function runWebSocketCommand(clientConfig, endpoint, input, config, contro
6162
5937
  const publicConnection = session?.connection;
6163
5938
  if (typeof session?.closed?.then !== "function" || typeof session.close !== "function" || typeof session.send !== "function" || typeof session.receive?.[Symbol.asyncIterator] !== "function" || typeof session[Symbol.asyncDispose] !== "function" || typeof session.onRuntimeError !== "function" || typeof session.onStateChange !== "function" || typeof publicConnection?.generation !== "number" || typeof session.bufferedAmount !== "number" || typeof session.state !== "string") throw new TypeError("WebSocket interceptor must resolve a WebSocket session");
6164
5939
  chainSettled = true;
6165
- deadline.release();
6166
5940
  releaseAbortSignal(chainSignal);
6167
5941
  let deliveredSession = [...undeliveredSessions].some((createdSession) => createdSession === session || createdSession.closed === session.closed);
6168
5942
  const pendingSessionSnapshot = [...pendingSessions];
@@ -6188,7 +5962,6 @@ async function runWebSocketCommand(clientConfig, endpoint, input, config, contro
6188
5962
  ];
6189
5963
  } catch (error) {
6190
5964
  chainSettled = true;
6191
- deadline.release();
6192
5965
  releaseAbortSignal(chainSignal);
6193
5966
  const signalError = resolveAbortFault(chainSignal);
6194
5967
  const requestError = signalError ?? (isFault(error) ? error : createPreflightFault("EXT_INTERCEPTOR_FAILED", error));
@@ -6212,32 +5985,6 @@ async function runWebSocketCommand(clientConfig, endpoint, input, config, contro
6212
5985
  ];
6213
5986
  }
6214
5987
  }
6215
- /**
6216
- * Open a WebSocket command against the given client config.
6217
- *
6218
- * Prefer `client.execute(command)` in application code; this is the low-level entry used by the client.
6219
- *
6220
- * The result is `[null, session, connection]` or `[fault, undefined, connection | undefined]`, where the fault is `Fault<undefined>`.
6221
- *
6222
- * WebSocket execute options can override the client's `beforeConnect`, `heartbeat`, `protocols`, and `reconnect`.
6223
- *
6224
- * @param clientConfig - Resolved client configuration.
6225
- * @param command - WebSocket command from a `WebSocketEndpoint`.
6226
- * @param options - Optional execute options (heartbeat, reconnect, abort).
6227
- * @returns Await-result tuple of `[error, socket, connection]`.
6228
- * @group web-socket
6229
- */
6230
- async function executeWebSocketCommand(clientConfig, command, options) {
6231
- const { endpoint, input } = command;
6232
- return runWebSocketCommand(clientConfig, endpoint, input, options ?? {}, new AbortController(), {
6233
- listeners: {
6234
- runtimeError: /* @__PURE__ */ new Set(),
6235
- stateChange: /* @__PURE__ */ new Set()
6236
- },
6237
- status: "idle",
6238
- transition: 0
6239
- });
6240
- }
6241
5988
  const SOCKET_CLOSE_GRACE_MS = 1e3;
6242
5989
  const noop = () => void 0;
6243
5990
  function createWebSocketSession(outgoing, queue, closed, state, sessionController, sendQueue, maxOutgoingQueueSize, openState, isUnavailable, requestClose, disposeSession) {
@@ -6413,4 +6160,4 @@ function faultFromCloseInfo(closeInfo) {
6413
6160
  function isFault(value) {
6414
6161
  return typeof value === "object" && value !== null && "code" in value && typeof value.code === "string" && value instanceof Error;
6415
6162
  }
6416
- export { ERR_ABORTED, ERR_TIMEOUT, StructError, basicAuthHttpInterceptor, basicAuthSSEInterceptor, createClient, createDecodeFault, createHttpInterceptor, createHttpStatusFault, createNetworkFault, createPreflightFault, createResolvedRequestUrl, createSSEInterceptor, createUndecodedHttpStatusFault, createWebSocketInterceptor, defineEventStream, defineRequest, defineWebSocket, executeEventStreamCommand, executeHttpCommand, executeWebSocketCommand, fetchHandler, isAbortCause, isTimeoutCause, makeResponse, struct, withCredentials, withEndpoint, withHTTPHandle, withHeaders, withInterceptors, withQuerySerializer, withSSEHandle, withSSEOnInvalidEvent, withSSEReconnect, withTimeout, withWebSocketBeforeConnect, withWebSocketHandle, withWebSocketHeartbeat, withWebSocketProtocols, withWebSocketReconnect, withXSRF };
6163
+ export { ERR_ABORTED, ERR_TIMEOUT, StructError, basicAuthHttpInterceptor, basicAuthSSEInterceptor, createClient, createDecodeFault, createHttpInterceptor, createHttpStatusFault, createNetworkFault, createPreflightFault, createResolvedRequestUrl, createSSEInterceptor, createUndecodedHttpStatusFault, createWebSocketInterceptor, defineEventStream, defineRequest, defineWebSocket, fetchHandler, isAbortCause, isTimeoutCause, makeResponse, struct, withCredentials, withEndpoint, withHTTPHandle, withHeaders, withInterceptors, withQuerySerializer, withSSEHandle, withSSEOnInvalidEvent, withSSEReconnect, withWebSocketBeforeConnect, withWebSocketHandle, withWebSocketHeartbeat, withWebSocketProtocols, withWebSocketReconnect, withXSRF };