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