@nxgt/httpyz 0.3.0 → 0.3.1

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.
@@ -0,0 +1,1051 @@
1
+ // src/cancel/abort.ts
2
+ function isAbortError2(error) {
3
+ if (typeof DOMException !== "undefined" && error instanceof DOMException) {
4
+ return error.name === "AbortError" || error.name === "TimeoutError";
5
+ }
6
+ return error instanceof Error && error.name === "AbortError";
7
+ }
8
+ var cancelled = (message) => new DOMException(message, "AbortError");
9
+ function abortable(promise, signal) {
10
+ if (!signal)
11
+ return promise;
12
+ if (signal.aborted)
13
+ return Promise.reject(signal.reason);
14
+ return new Promise((resolve, reject) => {
15
+ const stop = () => reject(signal.reason);
16
+ signal.addEventListener("abort", stop, { once: true });
17
+ promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", stop));
18
+ });
19
+ }
20
+ function joinSignals(...signals) {
21
+ const present = signals.filter((signal) => signal != null);
22
+ return present.length > 1 ? AbortSignal.any(present) : present[0];
23
+ }
24
+
25
+ // src/errors/errors.ts
26
+ class ClientError2 extends Error {
27
+ constructor(context, message, options) {
28
+ const route = `${context.method.toUpperCase()} ${context.path}`;
29
+ const call = context.operationId === undefined ? route : `${context.operationId} (${route})`;
30
+ super(`${call}: ${message}`, options);
31
+ this.name = "ClientError";
32
+ this.operationId = context.operationId;
33
+ this.method = context.method;
34
+ this.path = context.path;
35
+ }
36
+ }
37
+
38
+ class NetworkError2 extends ClientError2 {
39
+ constructor(context, options) {
40
+ super(context, "no reply came back", options);
41
+ this.name = "NetworkError";
42
+ }
43
+ }
44
+
45
+ class TimeoutError2 extends ClientError2 {
46
+ constructor(context, timeout, options) {
47
+ super(context, `no reply within ${timeout} ms`, options);
48
+ this.name = "TimeoutError";
49
+ this.timeout = timeout;
50
+ }
51
+ }
52
+
53
+ class UndeclaredStatusError2 extends ClientError2 {
54
+ constructor(context, response) {
55
+ super(context, `no ${response.status} reply is declared`);
56
+ this.name = "UndeclaredStatusError";
57
+ this.status = response.status;
58
+ this.response = response;
59
+ }
60
+ }
61
+
62
+ class ValidationError2 extends ClientError2 {
63
+ constructor(context, failure) {
64
+ super(context, failure.issues.map((issue) => issue.message).join("; "));
65
+ this.name = "ValidationError";
66
+ this.failure = failure;
67
+ }
68
+ }
69
+
70
+ class ReplyStatusError2 extends Error {
71
+ constructor(reply, expected) {
72
+ const wanted = expected.length > 0 ? expected.join(" or ") : "2xx";
73
+ super(`Expected a ${wanted} reply, got ${reply.status}`);
74
+ this.name = "ReplyStatusError";
75
+ this.status = reply.status;
76
+ this.data = reply.data;
77
+ this.response = reply.response;
78
+ }
79
+ }
80
+
81
+ // src/schema/standard-schema.ts
82
+ async function check2(schema, value, target) {
83
+ const result = await schema["~standard"].validate(value);
84
+ if (result.issues === undefined)
85
+ return { ok: true, value: result.value };
86
+ return {
87
+ ok: false,
88
+ issues: result.issues.map((issue) => {
89
+ const { code } = issue;
90
+ return {
91
+ target,
92
+ path: (issue.path ?? []).map((key) => typeof key === "object" ? key.key : key).filter((key) => typeof key !== "symbol"),
93
+ code: typeof code === "string" ? code : "custom",
94
+ message: issue.message
95
+ };
96
+ })
97
+ };
98
+ }
99
+
100
+ // src/request/encode.ts
101
+ var text2 = (value) => value instanceof Date ? value.toISOString() : String(value);
102
+ var absent2 = (value) => value === undefined || value === null;
103
+ function fillPath(context, param) {
104
+ return context.path.replace(/\{([^}]+)\}/g, (_, name) => {
105
+ const value = param?.[name];
106
+ if (absent2(value)) {
107
+ throw new TypeError(`${context.method.toUpperCase()} ${context.path}: the path parameter ${name} is missing`);
108
+ }
109
+ return encodeURIComponent(text2(value));
110
+ });
111
+ }
112
+ function writeQuery(query) {
113
+ if (query === undefined)
114
+ return "";
115
+ if (query instanceof URLSearchParams)
116
+ return query.toString();
117
+ const search = new URLSearchParams;
118
+ for (const [name, value] of Object.entries(query)) {
119
+ for (const item of Array.isArray(value) ? value : [value]) {
120
+ if (!absent2(item))
121
+ search.append(name, text2(item));
122
+ }
123
+ }
124
+ return search.toString();
125
+ }
126
+ function joinUrl(baseUrl, path, query) {
127
+ const root = baseUrl === undefined ? "" : String(baseUrl).replace(/\/+$/, "");
128
+ return `${root}${path}${query === "" ? "" : `?${query}`}`;
129
+ }
130
+ function writeBody(input, headers, own) {
131
+ const type = (fallback) => headers.set("content-type", own ?? fallback);
132
+ if (input.json !== undefined) {
133
+ type("application/json");
134
+ return JSON.stringify(input.json);
135
+ }
136
+ if (input.form !== undefined) {
137
+ headers.delete("content-type");
138
+ const { form } = input;
139
+ if (form instanceof FormData || form instanceof URLSearchParams) {
140
+ return form;
141
+ }
142
+ return [...fields2(form)].some(([, value]) => value instanceof Blob) ? toFormData2(form) : toSearchParams2(form);
143
+ }
144
+ if (input.text !== undefined) {
145
+ type("text/plain");
146
+ return input.text;
147
+ }
148
+ if (input.body !== undefined) {
149
+ const { body } = input;
150
+ type(body instanceof Blob && body.type !== "" ? body.type : "application/octet-stream");
151
+ return body;
152
+ }
153
+ return;
154
+ }
155
+ function* fields2(form) {
156
+ for (const [name, value] of Object.entries(form)) {
157
+ for (const item of Array.isArray(value) ? value : [value]) {
158
+ if (absent2(item))
159
+ continue;
160
+ if (item instanceof Blob)
161
+ yield [name, item];
162
+ else if (typeof item === "object" && !(item instanceof Date)) {
163
+ yield [name, JSON.stringify(item)];
164
+ } else
165
+ yield [name, text2(item)];
166
+ }
167
+ }
168
+ }
169
+ function toFormData2(form) {
170
+ const data = new FormData;
171
+ for (const [name, value] of fields2(form))
172
+ data.append(name, value);
173
+ return data;
174
+ }
175
+ function toSearchParams2(form) {
176
+ const data = new URLSearchParams;
177
+ for (const [name, value] of fields2(form)) {
178
+ if (value instanceof Blob) {
179
+ throw new TypeError(`${name} is a file: an application/x-www-form-urlencoded body cannot carry one`);
180
+ }
181
+ data.append(name, value);
182
+ }
183
+ return data;
184
+ }
185
+
186
+ // src/cancel/latest.ts
187
+ function createLatest() {
188
+ const running = new Map;
189
+ return (key) => {
190
+ running.get(key)?.abort(cancelled(`A later call with latest: '${key}' replaced this one`));
191
+ const controller = new AbortController;
192
+ running.set(key, controller);
193
+ return controller.signal;
194
+ };
195
+ }
196
+
197
+ // src/middleware/auth.ts
198
+ function auth({
199
+ token,
200
+ refresh,
201
+ scheme = "Bearer"
202
+ }) {
203
+ let refreshing;
204
+ const sign = (request, value) => {
205
+ if (value)
206
+ request.headers.set("authorization", `${scheme} ${value}`);
207
+ return request;
208
+ };
209
+ return async (request, next) => {
210
+ const used = await token();
211
+ const replay = refresh ? request.clone() : undefined;
212
+ const response = await next(sign(request, used));
213
+ if (response.status !== 401 || !replay || !refresh)
214
+ return response;
215
+ try {
216
+ if (await token() === used) {
217
+ refreshing ??= Promise.resolve().then(refresh).finally(() => {
218
+ refreshing = undefined;
219
+ });
220
+ await abortable(refreshing, request.signal);
221
+ }
222
+ } catch (error) {
223
+ if (request.signal.aborted) {
224
+ await response.body?.cancel();
225
+ throw error;
226
+ }
227
+ return response;
228
+ }
229
+ const fresh = await token();
230
+ if (!fresh || fresh === used)
231
+ return response;
232
+ await response.body?.cancel();
233
+ return next(sign(replay, fresh));
234
+ };
235
+ }
236
+
237
+ // src/middleware/compose.ts
238
+ var compose = (middlewares, send, call) => middlewares.reduceRight((next, middleware) => (request) => middleware(request, next, call), send);
239
+
240
+ // src/middleware/retry.ts
241
+ var REPEATABLE = [
242
+ "get",
243
+ "head",
244
+ "options",
245
+ "put",
246
+ "delete",
247
+ "trace",
248
+ "query"
249
+ ];
250
+ var TRANSIENT = [408, 429, 502, 503, 504];
251
+ var backoff = (attempt) => Math.random() * 300 * 2 ** (attempt - 1);
252
+ function retrySettings(retry) {
253
+ if (retry === undefined || retry === false)
254
+ return;
255
+ const options = typeof retry === "number" ? { attempts: retry } : retry;
256
+ const attempts = options.attempts ?? 2;
257
+ if (attempts <= 0)
258
+ return;
259
+ return {
260
+ attempts,
261
+ methods: options.methods ?? REPEATABLE,
262
+ statuses: options.statuses ?? TRANSIENT,
263
+ delay: options.delay ?? backoff,
264
+ maxDelay: options.maxDelay ?? 1e4
265
+ };
266
+ }
267
+ function retryDelay(response, attempt, settings) {
268
+ const header = response?.headers.get("retry-after")?.trim();
269
+ if (header) {
270
+ const wait = /^\d+(?:\.\d+)?$/.test(header) ? Number(header) * 1000 : Date.parse(header) - Date.now();
271
+ if (!Number.isNaN(wait)) {
272
+ return wait > settings.maxDelay ? undefined : Math.max(0, wait);
273
+ }
274
+ }
275
+ return Math.min(Math.max(0, settings.delay(attempt)), settings.maxDelay);
276
+ }
277
+ var sleep = (ms, signal) => new Promise((resolve, reject) => {
278
+ if (signal.aborted)
279
+ return reject(signal.reason);
280
+ const stop = () => {
281
+ clearTimeout(timer);
282
+ reject(signal.reason);
283
+ };
284
+ const timer = setTimeout(() => {
285
+ signal.removeEventListener("abort", stop);
286
+ resolve();
287
+ }, ms);
288
+ signal.addEventListener("abort", stop, { once: true });
289
+ });
290
+ function retrying(settings) {
291
+ return async (request, next) => {
292
+ const method = request.method.toLowerCase();
293
+ if (!settings.methods.includes(method))
294
+ return next(request);
295
+ for (let attempt = 1;; attempt++) {
296
+ const last = attempt > settings.attempts;
297
+ const sent = last ? request : request.clone();
298
+ let wait;
299
+ try {
300
+ const response = await next(sent);
301
+ if (last || !settings.statuses.includes(response.status)) {
302
+ return response;
303
+ }
304
+ wait = retryDelay(response, attempt, settings);
305
+ if (wait === undefined)
306
+ return response;
307
+ await response.body?.cancel();
308
+ } catch (error) {
309
+ if (last || !(error instanceof NetworkError2))
310
+ throw error;
311
+ wait = retryDelay(undefined, attempt, settings) ?? 0;
312
+ }
313
+ await sleep(wait, request.signal);
314
+ }
315
+ };
316
+ }
317
+
318
+ // src/reply/media-type.ts
319
+ var mediaType = (header) => (header.split(";")[0] ?? "").trim().toLowerCase();
320
+ function mediaKind(type) {
321
+ if (type === "application/json" || /^application\/[\w.-]+\+json$/.test(type)) {
322
+ return "json";
323
+ }
324
+ if (type === "application/x-www-form-urlencoded" || type === "multipart/form-data") {
325
+ return "form";
326
+ }
327
+ return type.startsWith("text/") ? "text" : "binary";
328
+ }
329
+ function declaredType(content, type) {
330
+ const types = Object.keys(content);
331
+ const exact = types.find((key) => key.toLowerCase() === type);
332
+ if (exact !== undefined)
333
+ return exact;
334
+ const range = `${type.split("/")[0]}/*`;
335
+ if (content[range])
336
+ return range;
337
+ if (content["*/*"])
338
+ return "*/*";
339
+ const kind = mediaKind(type);
340
+ if (kind !== "json" && kind !== "text")
341
+ return;
342
+ return types.find((key) => content[key]?.kind === kind);
343
+ }
344
+
345
+ // src/reply/read-reply.ts
346
+ var isSchema = (value) => typeof value === "object" && value !== null && ("~standard" in value);
347
+ function toSpec(responses) {
348
+ const spec = {};
349
+ for (const [status, declared] of Object.entries(responses)) {
350
+ const content = {};
351
+ if (isSchema(declared)) {
352
+ content["application/json"] = { kind: "json", schema: declared };
353
+ } else if (declared !== null) {
354
+ for (const [type, schema] of Object.entries(declared)) {
355
+ content[type] = {
356
+ kind: mediaKind(type.toLowerCase()),
357
+ ...schema === null ? {} : { schema }
358
+ };
359
+ }
360
+ }
361
+ spec[Number(status)] = content;
362
+ }
363
+ return spec;
364
+ }
365
+ async function readReply(context, spec, response, settings) {
366
+ const { status } = response;
367
+ const header = response.headers.get("content-type");
368
+ const refused = (issues) => new ValidationError2(context, {
369
+ kind: "response",
370
+ ...context,
371
+ status,
372
+ issues
373
+ });
374
+ const failure = (code, message) => refused([{ target: "response", path: [], code, message }]);
375
+ const parse = (text) => {
376
+ try {
377
+ return JSON.parse(text);
378
+ } catch {
379
+ throw failure("invalid_json", `the ${status} reply is not valid JSON`);
380
+ }
381
+ };
382
+ if (!spec) {
383
+ const type = header === null ? undefined : mediaType(header);
384
+ const reply = (data) => ({ status, type, data, response });
385
+ if (response.body === null)
386
+ return reply(undefined);
387
+ switch (type === undefined ? undefined : mediaKind(type)) {
388
+ case "json": {
389
+ const text = await response.text();
390
+ return reply(text === "" ? undefined : parse(text));
391
+ }
392
+ case "form":
393
+ return reply(await response.formData());
394
+ case "binary":
395
+ return reply(await response.blob());
396
+ default: {
397
+ const text = await response.text();
398
+ return reply(type === undefined && text === "" ? undefined : text);
399
+ }
400
+ }
401
+ }
402
+ const declared = spec[status];
403
+ if (!declared)
404
+ throw new UndeclaredStatusError2(context, response);
405
+ const types = Object.keys(declared);
406
+ if (types.length === 0) {
407
+ return { status, type: undefined, data: undefined, response };
408
+ }
409
+ const type = header === null ? types.length === 1 ? types[0] : undefined : declaredType(declared, mediaType(header));
410
+ const media = type === undefined ? undefined : declared[type];
411
+ if (type === undefined || media === undefined) {
412
+ throw failure("invalid_content_type", `a ${status} reply is ${types.join(" or ")}, not ${header ?? "untyped"}`);
413
+ }
414
+ const reply = (data) => ({ status, type, data, response });
415
+ const settle = async (data) => {
416
+ if (!settings.validate || !media.schema)
417
+ return reply(data);
418
+ const result = await check2(media.schema, data, "response");
419
+ if (!result.ok)
420
+ throw refused(result.issues);
421
+ return reply(settings.decode ? result.value : data);
422
+ };
423
+ switch (media.kind) {
424
+ case "json": {
425
+ const text = await response.text();
426
+ if (text === "" && !(settings.validate && media.schema)) {
427
+ return reply(undefined);
428
+ }
429
+ return settle(parse(text));
430
+ }
431
+ case "text":
432
+ return settle(await response.text());
433
+ case "form":
434
+ return reply(await response.formData());
435
+ default:
436
+ return reply(await response.blob());
437
+ }
438
+ }
439
+
440
+ // src/stream/connection.ts
441
+ var failure = (context, status, issues) => new ValidationError2(context, {
442
+ kind: "response",
443
+ ...context,
444
+ status,
445
+ issues
446
+ });
447
+ async function opened(settings, response, types) {
448
+ const { status } = response;
449
+ if (status === 204) {
450
+ await response.body?.cancel();
451
+ return false;
452
+ }
453
+ if (!response.ok)
454
+ throw new UndeclaredStatusError2(settings.context, response);
455
+ const header = response.headers.get("content-type");
456
+ if (header === null || !types.includes(mediaType(header)) || !response.body) {
457
+ await response.body?.cancel();
458
+ throw failure(settings.context, status, [
459
+ {
460
+ target: "response",
461
+ path: [],
462
+ code: "invalid_content_type",
463
+ message: `a ${status} reply is ${types.join(" or ")}, not ${header ?? "untyped"}`
464
+ }
465
+ ]);
466
+ }
467
+ return true;
468
+ }
469
+ async function settle(settings, status, text, schema, what) {
470
+ let value;
471
+ try {
472
+ value = JSON.parse(text);
473
+ } catch {
474
+ throw failure(settings.context, status, [
475
+ {
476
+ target: "response",
477
+ path: [],
478
+ code: "invalid_json",
479
+ message: `${what} is not valid JSON`
480
+ }
481
+ ]);
482
+ }
483
+ if (!schema || !settings.validate)
484
+ return value;
485
+ const result = await check2(schema, value, "response");
486
+ if (!result.ok)
487
+ throw failure(settings.context, status, result.issues);
488
+ return settings.decode ? result.value : value;
489
+ }
490
+ function pause(ms, ...signals) {
491
+ const live = signals.filter((each) => !!each);
492
+ return new Promise((resolve) => {
493
+ const done = () => {
494
+ clearTimeout(timer);
495
+ for (const each of live)
496
+ each.removeEventListener("abort", done);
497
+ resolve();
498
+ };
499
+ const timer = setTimeout(done, ms);
500
+ for (const each of live) {
501
+ if (each.aborted)
502
+ return done();
503
+ each.addEventListener("abort", done, { once: true });
504
+ }
505
+ });
506
+ }
507
+
508
+ class Reading {
509
+ #reader;
510
+ #signal;
511
+ #halt;
512
+ constructor(signal) {
513
+ this.closer = new AbortController;
514
+ this.#halt = () => {
515
+ this.#reader?.cancel().catch(() => {});
516
+ };
517
+ this.#signal = signal;
518
+ this.closer.signal.addEventListener("abort", this.#halt);
519
+ signal?.addEventListener("abort", this.#halt);
520
+ }
521
+ read(response) {
522
+ const body = response.body;
523
+ this.#reader = body.pipeThrough(new TextDecoderStream).getReader();
524
+ return this.#reader;
525
+ }
526
+ stopped() {
527
+ this.#signal?.throwIfAborted();
528
+ return this.closer.signal.aborted;
529
+ }
530
+ release() {
531
+ this.#reader?.cancel().catch(() => {});
532
+ this.#reader = undefined;
533
+ this.#signal?.removeEventListener("abort", this.#halt);
534
+ }
535
+ close() {
536
+ this.closer.abort();
537
+ }
538
+ }
539
+
540
+ // src/stream/sse-parser.ts
541
+ var LINE_END = /\r\n|\r|\n/g;
542
+ function createSseParser(lastEventId) {
543
+ let buffer = "";
544
+ let started = false;
545
+ let afterCr = false;
546
+ let data = "";
547
+ let type = "";
548
+ let id = lastEventId ?? "";
549
+ let dispatchedId = lastEventId;
550
+ let retry;
551
+ const dispatch = (events) => {
552
+ dispatchedId = id === "" ? undefined : id;
553
+ if (data === "") {
554
+ type = "";
555
+ return;
556
+ }
557
+ events.push({
558
+ event: type === "" ? "message" : type,
559
+ data: data.endsWith(`
560
+ `) ? data.slice(0, -1) : data,
561
+ id: dispatchedId
562
+ });
563
+ data = "";
564
+ type = "";
565
+ };
566
+ const line = (text, events) => {
567
+ if (text === "")
568
+ return dispatch(events);
569
+ if (text.startsWith(":"))
570
+ return;
571
+ const colon = text.indexOf(":");
572
+ const field = colon === -1 ? text : text.slice(0, colon);
573
+ let value = colon === -1 ? "" : text.slice(colon + 1);
574
+ if (value.startsWith(" "))
575
+ value = value.slice(1);
576
+ switch (field) {
577
+ case "event":
578
+ type = value;
579
+ break;
580
+ case "data":
581
+ data += `${value}
582
+ `;
583
+ break;
584
+ case "id":
585
+ if (!value.includes("\x00"))
586
+ id = value;
587
+ break;
588
+ case "retry":
589
+ if (/^\d+$/.test(value))
590
+ retry = Number(value);
591
+ break;
592
+ }
593
+ };
594
+ return {
595
+ push(chunk) {
596
+ let text = chunk;
597
+ if (!started && text !== "") {
598
+ started = true;
599
+ if (text.startsWith("\uFEFF"))
600
+ text = text.slice(1);
601
+ }
602
+ if (afterCr && text !== "") {
603
+ afterCr = false;
604
+ if (text.startsWith(`
605
+ `))
606
+ text = text.slice(1);
607
+ }
608
+ buffer += text;
609
+ const events = [];
610
+ let start = 0;
611
+ LINE_END.lastIndex = 0;
612
+ for (let end = LINE_END.exec(buffer);end !== null; end = LINE_END.exec(buffer)) {
613
+ if (end[0] === "\r" && end.index === buffer.length - 1) {
614
+ afterCr = true;
615
+ }
616
+ line(buffer.slice(start, end.index), events);
617
+ start = end.index + end[0].length;
618
+ }
619
+ buffer = buffer.slice(start);
620
+ return events;
621
+ },
622
+ get retry() {
623
+ return retry;
624
+ },
625
+ get lastEventId() {
626
+ return dispatchedId;
627
+ }
628
+ };
629
+ }
630
+
631
+ // src/stream/event-stream.ts
632
+ function eventStream(settings) {
633
+ const reading = new Reading(settings.signal);
634
+ let lastEventId = settings.lastEventId;
635
+ let delay = settings.reconnect ? settings.reconnect.delay : 0;
636
+ let started = false;
637
+ const declared = async (raw, status) => {
638
+ const { events } = settings;
639
+ if (!events)
640
+ return raw;
641
+ if (!Object.hasOwn(events, raw.event)) {
642
+ settings.onUnknownEvent?.(raw);
643
+ return;
644
+ }
645
+ const schema = events[raw.event];
646
+ if (!schema)
647
+ return raw;
648
+ const what = `the ${raw.event} event's data`;
649
+ const data = await settle(settings, status, raw.data, schema, what);
650
+ return { event: raw.event, data, id: raw.id };
651
+ };
652
+ const again = async (failures) => {
653
+ const { reconnect } = settings;
654
+ if (!reconnect || failures >= reconnect.attempts)
655
+ return false;
656
+ await pause(delay, reading.closer.signal, settings.signal);
657
+ return !reading.stopped();
658
+ };
659
+ async function* run() {
660
+ let failures = 0;
661
+ try {
662
+ for (;; ) {
663
+ let response;
664
+ try {
665
+ response = await settings.open(reading.closer.signal, lastEventId);
666
+ } catch (error) {
667
+ if (reading.closer.signal.aborted)
668
+ return;
669
+ if (error instanceof NetworkError2 && await again(failures++)) {
670
+ continue;
671
+ }
672
+ throw error;
673
+ }
674
+ if (!await opened(settings, response, ["text/event-stream"])) {
675
+ return;
676
+ }
677
+ const parser = createSseParser(lastEventId);
678
+ const reader = reading.read(response);
679
+ let dropped;
680
+ for (;; ) {
681
+ let chunk;
682
+ try {
683
+ chunk = await reader.read();
684
+ } catch (error) {
685
+ if (reading.stopped())
686
+ return;
687
+ dropped = error;
688
+ break;
689
+ }
690
+ if (reading.stopped())
691
+ return;
692
+ if (chunk.done)
693
+ break;
694
+ const events = parser.push(chunk.value);
695
+ lastEventId = parser.lastEventId;
696
+ delay = parser.retry ?? delay;
697
+ for (const raw of events) {
698
+ failures = 0;
699
+ const event = await declared(raw, response.status);
700
+ if (event !== undefined)
701
+ yield event;
702
+ }
703
+ }
704
+ if (!await again(failures++)) {
705
+ if (dropped === undefined)
706
+ return;
707
+ throw new NetworkError2(settings.context, { cause: dropped });
708
+ }
709
+ }
710
+ } finally {
711
+ reading.release();
712
+ }
713
+ }
714
+ return {
715
+ [Symbol.asyncIterator]() {
716
+ if (started)
717
+ throw new TypeError("A stream is read once");
718
+ started = true;
719
+ return run();
720
+ },
721
+ close: () => reading.close(),
722
+ get lastEventId() {
723
+ return lastEventId;
724
+ }
725
+ };
726
+ }
727
+
728
+ // src/stream/line-parser.ts
729
+ var RS = String.fromCharCode(30);
730
+ function createLineParser() {
731
+ let buffer = "";
732
+ const records = (texts) => texts.map((text) => text.trim()).filter((text) => text !== "");
733
+ return {
734
+ push(chunk) {
735
+ const parts = (buffer + chunk).replaceAll(RS, `
736
+ `).split(/\r?\n/);
737
+ buffer = parts.pop() ?? "";
738
+ return records(parts);
739
+ },
740
+ end() {
741
+ const rest = buffer;
742
+ buffer = "";
743
+ return records([rest]);
744
+ }
745
+ };
746
+ }
747
+
748
+ // src/stream/line-stream.ts
749
+ var LINE_TYPES = [
750
+ "application/jsonl",
751
+ "application/x-ndjson",
752
+ "application/ndjson",
753
+ "application/jsonlines",
754
+ "application/x-jsonlines",
755
+ "application/json-seq"
756
+ ];
757
+ function lineStream(settings) {
758
+ const reading = new Reading(settings.signal);
759
+ let started = false;
760
+ async function* run() {
761
+ try {
762
+ let response;
763
+ try {
764
+ response = await settings.open(reading.closer.signal, undefined);
765
+ } catch (error) {
766
+ if (reading.closer.signal.aborted)
767
+ return;
768
+ throw error;
769
+ }
770
+ if (!await opened(settings, response, LINE_TYPES))
771
+ return;
772
+ const parser = createLineParser();
773
+ const reader = reading.read(response);
774
+ for (;; ) {
775
+ let chunk;
776
+ try {
777
+ chunk = await reader.read();
778
+ } catch (error) {
779
+ if (reading.stopped())
780
+ return;
781
+ throw new NetworkError2(settings.context, { cause: error });
782
+ }
783
+ if (reading.stopped())
784
+ return;
785
+ const texts = chunk.done ? parser.end() : parser.push(chunk.value);
786
+ for (const text of texts) {
787
+ yield await settle(settings, response.status, text, settings.item, "a line");
788
+ }
789
+ if (chunk.done)
790
+ return;
791
+ }
792
+ } finally {
793
+ reading.release();
794
+ }
795
+ }
796
+ return {
797
+ [Symbol.asyncIterator]() {
798
+ if (started)
799
+ throw new TypeError("A stream is read once");
800
+ started = true;
801
+ return run();
802
+ },
803
+ close: () => reading.close()
804
+ };
805
+ }
806
+
807
+ // src/client/create-http-client.ts
808
+ var METHODS2 = [
809
+ "get",
810
+ "put",
811
+ "post",
812
+ "delete",
813
+ "options",
814
+ "head",
815
+ "patch",
816
+ "trace",
817
+ "query"
818
+ ];
819
+ var RECONNECT_DELAY = 3000;
820
+ var contextOf = (method, path, operationId) => operationId === undefined ? { method, path } : { method, path, operationId };
821
+ function createHttpClient2(options = {}) {
822
+ const signer = options.auth ? auth(options.auth) : undefined;
823
+ const claim = createLatest();
824
+ const latestSignal = (key) => key === undefined ? undefined : claim(key);
825
+ const sharedHeaders = async () => new Headers(typeof options.headers === "function" ? await options.headers() : options.headers);
826
+ const withDeadline = (timeout, signal) => {
827
+ const deadline = timeout === undefined ? undefined : AbortSignal.timeout(timeout);
828
+ const signals = [signal, deadline].filter((each) => each != null);
829
+ return {
830
+ deadline,
831
+ signal: signals.length > 1 ? AbortSignal.any(signals) : signals[0]
832
+ };
833
+ };
834
+ const dispatch = async (request, context, timeout, deadline, retry, added) => {
835
+ const fetch = options.fetch ?? ((sent) => globalThis.fetch(sent));
836
+ const send = async (sent) => {
837
+ try {
838
+ sent.signal.throwIfAborted();
839
+ return await fetch(sent);
840
+ } catch (error) {
841
+ if (request.signal.aborted)
842
+ throw error;
843
+ throw new NetworkError2(context, { cause: error });
844
+ }
845
+ };
846
+ const again = retrySettings(retry);
847
+ const layers = [
848
+ ...again ? [retrying(again)] : [],
849
+ ...signer ? [signer] : [],
850
+ ...options.use ?? [],
851
+ ...added
852
+ ];
853
+ try {
854
+ return await compose(layers, send, context)(request);
855
+ } catch (error) {
856
+ if (deadline?.aborted && timeout !== undefined) {
857
+ throw new TimeoutError2(context, timeout, { cause: error });
858
+ }
859
+ throw error;
860
+ }
861
+ };
862
+ const build = async (method, path, given, adjust) => {
863
+ const {
864
+ param,
865
+ query,
866
+ json,
867
+ form,
868
+ text,
869
+ body,
870
+ operationId,
871
+ timeout = options.timeout,
872
+ retry = options.retry,
873
+ headers: own,
874
+ signal,
875
+ ...rest
876
+ } = given;
877
+ const context = contextOf(method, path, operationId);
878
+ const headers = await sharedHeaders();
879
+ const callHeaders = new Headers(own);
880
+ callHeaders.forEach((value, name) => {
881
+ headers.set(name, value);
882
+ });
883
+ adjust?.(headers);
884
+ const url = joinUrl(options.baseUrl, fillPath(context, param), writeQuery(query));
885
+ const payload = writeBody({ json, form, text, body }, headers, callHeaders.get("content-type"));
886
+ const init = {
887
+ ...options.init,
888
+ ...rest,
889
+ method: method.toUpperCase(),
890
+ headers,
891
+ body: payload,
892
+ ...payload instanceof ReadableStream ? { duplex: "half" } : {}
893
+ };
894
+ return { context, url, init, timeout, retry, signal: signal ?? undefined };
895
+ };
896
+ const request = async (method, path, given, added) => {
897
+ const {
898
+ responses,
899
+ validate = true,
900
+ decode = true,
901
+ latest,
902
+ ...call
903
+ } = given;
904
+ const replaced = latestSignal(latest);
905
+ const built = await build(method, path, call);
906
+ const { deadline, signal } = withDeadline(built.timeout, joinSignals(built.signal, replaced));
907
+ const response = await dispatch(new Request(built.url, { ...built.init, signal }), built.context, built.timeout, deadline, built.retry, added);
908
+ return readReply(built.context, responses === undefined ? undefined : toSpec(responses), response, { validate, decode });
909
+ };
910
+ const opener = (method, path, call, accept, added) => async (stream, lastEventId) => {
911
+ const built = await build(method, path, call, (headers) => {
912
+ if (!headers.has("accept"))
913
+ headers.set("accept", accept);
914
+ if (lastEventId !== undefined) {
915
+ headers.set("last-event-id", lastEventId);
916
+ }
917
+ });
918
+ const deadline = new AbortController;
919
+ const clock = built.timeout === undefined ? undefined : setTimeout(() => deadline.abort(new DOMException("The stream did not open", "TimeoutError")), built.timeout);
920
+ const signals = [stream, deadline.signal];
921
+ if (built.signal)
922
+ signals.push(built.signal);
923
+ try {
924
+ return await dispatch(new Request(built.url, {
925
+ ...built.init,
926
+ signal: AbortSignal.any(signals)
927
+ }), built.context, built.timeout, deadline.signal, built.retry, added);
928
+ } finally {
929
+ clearTimeout(clock);
930
+ }
931
+ };
932
+ const events = (path, given, added) => {
933
+ const {
934
+ events,
935
+ onUnknownEvent,
936
+ reconnect,
937
+ lastEventId,
938
+ validate = true,
939
+ decode = true,
940
+ method = "get",
941
+ latest,
942
+ ...own
943
+ } = given;
944
+ const call = {
945
+ ...own,
946
+ signal: joinSignals(own.signal, latestSignal(latest))
947
+ };
948
+ const reconnects = reconnect === undefined ? method !== "post" && method !== "patch" : reconnect !== false;
949
+ const settings = typeof reconnect === "object" ? reconnect : {};
950
+ return eventStream({
951
+ context: contextOf(method, path, call.operationId),
952
+ open: opener(method, path, call, "text/event-stream", added),
953
+ signal: call.signal ?? undefined,
954
+ validate,
955
+ decode,
956
+ events,
957
+ onUnknownEvent,
958
+ reconnect: reconnects && {
959
+ attempts: settings.attempts ?? Number.POSITIVE_INFINITY,
960
+ delay: settings.delay ?? RECONNECT_DELAY
961
+ },
962
+ lastEventId
963
+ });
964
+ };
965
+ const lines = (path, given, added) => {
966
+ const {
967
+ item,
968
+ validate = true,
969
+ decode = true,
970
+ method = "get",
971
+ latest,
972
+ ...own
973
+ } = given;
974
+ const call = {
975
+ ...own,
976
+ signal: joinSignals(own.signal, latestSignal(latest))
977
+ };
978
+ return lineStream({
979
+ context: contextOf(method, path, call.operationId),
980
+ open: opener(method, path, call, LINE_TYPES.slice(0, 2).join(", "), added),
981
+ signal: call.signal ?? undefined,
982
+ validate,
983
+ decode,
984
+ item
985
+ });
986
+ };
987
+ const send = async (request, {
988
+ timeout = options.timeout,
989
+ retry = options.retry,
990
+ operationId,
991
+ latest
992
+ }, added) => {
993
+ const replaced = latestSignal(latest);
994
+ const method = request.method.toLowerCase();
995
+ const context = contextOf(method, new URL(request.url).pathname, operationId);
996
+ const headers = await sharedHeaders();
997
+ request.headers.forEach((value, name) => {
998
+ headers.set(name, value);
999
+ });
1000
+ const { deadline, signal } = withDeadline(timeout, joinSignals(request.signal, replaced));
1001
+ const sent = new Request(request, { headers, signal });
1002
+ return dispatch(sent, context, timeout, deadline, retry, added);
1003
+ };
1004
+ const surface = (scope, inherited) => {
1005
+ const own = [];
1006
+ const layers = () => [...inherited(), ...own];
1007
+ const scoped = (given) => {
1008
+ const extra = scope();
1009
+ const own = given ?? {};
1010
+ return extra ? { ...own, signal: joinSignals(own.signal, extra) } : own;
1011
+ };
1012
+ const client = {
1013
+ request: (method, path, given) => request(method, path, scoped(given), layers()),
1014
+ send: (sent, given) => {
1015
+ const extra = scope();
1016
+ return send(extra ? new Request(sent, { signal: joinSignals(sent.signal, extra) }) : sent, given ?? {}, layers());
1017
+ },
1018
+ events: (path, given) => events(path, scoped(given), layers()),
1019
+ lines: (path, given) => lines(path, scoped(given), layers()),
1020
+ use: (...middlewares) => {
1021
+ own.push(...middlewares);
1022
+ return client;
1023
+ },
1024
+ group: () => {
1025
+ let controller = new AbortController;
1026
+ const group = surface(() => joinSignals(scope(), controller.signal), layers);
1027
+ group.cancel = (reason) => {
1028
+ controller.abort(reason ?? cancelled("The group was cancelled"));
1029
+ controller = new AbortController;
1030
+ };
1031
+ Object.defineProperty(group, "signal", {
1032
+ enumerable: true,
1033
+ get: () => controller.signal
1034
+ });
1035
+ return group;
1036
+ }
1037
+ };
1038
+ for (const method of METHODS2) {
1039
+ client[method] = (path, given) => request(method, path, scoped(given), layers());
1040
+ }
1041
+ return client;
1042
+ };
1043
+ return surface(() => {
1044
+ return;
1045
+ }, () => []);
1046
+ }
1047
+
1048
+ export { isAbortError2, ClientError2, NetworkError2, TimeoutError2, UndeclaredStatusError2, ValidationError2, ReplyStatusError2, check2, text2, absent2, fields2, toFormData2, toSearchParams2, METHODS2, createHttpClient2 };
1049
+
1050
+ //# debugId=FE0AED608D09223164756E2164756E21
1051
+ //# sourceMappingURL=index-52w12dh5.js.map