tanstack-fetch 1.0.0 → 1.0.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.
@@ -1,867 +0,0 @@
1
- // src/plugins/auth.ts
2
- var createAuthInterceptor = (auth) => ({
3
- name: "auth",
4
- order: 15,
5
- onRequest: async (context) => {
6
- const token = await auth.getToken();
7
- if (!token) {
8
- return { action: "continue", context };
9
- }
10
- const header = auth.header ?? "authorization";
11
- const scheme = auth.scheme === void 0 ? "Bearer" : auth.scheme;
12
- const value = scheme ? `${scheme} ${token}` : token;
13
- context.request.headers.set(header, value);
14
- return { action: "continue", context };
15
- }
16
- });
17
-
18
- // src/plugins/status.ts
19
- var resolveStatusHandler = (status, handlers) => {
20
- const exact = handlers[status];
21
- if (exact) {
22
- return exact;
23
- }
24
- if (status >= 500 && status < 600 && handlers["5xx"]) {
25
- return handlers["5xx"];
26
- }
27
- if (status >= 400 && status < 500 && handlers["4xx"]) {
28
- return handlers["4xx"];
29
- }
30
- return handlers.default;
31
- };
32
- var createStatusInterceptor = (handlers) => ({
33
- name: "on-status",
34
- order: 95,
35
- onResponseError: async (context) => {
36
- const status = context.error?.status ?? context.response?.status;
37
- if (status === void 0 || !context.error) {
38
- return { action: "continue", context };
39
- }
40
- const handler = resolveStatusHandler(status, handlers);
41
- if (!handler) {
42
- return { action: "continue", context };
43
- }
44
- const decision = await handler({ status, error: context.error, context });
45
- if (decision?.action === "retry") {
46
- return { action: "retry", delayMs: decision.delayMs };
47
- }
48
- return { action: "continue", context };
49
- }
50
- });
51
-
52
- // src/constants.ts
53
- var DEFAULT_TIMEOUT_MS = 3e4;
54
- var DEFAULT_MAX_RETRIES = 2;
55
- var DEFAULT_INTERCEPTOR_ORDER = 100;
56
- var IDEMPOTENT_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
57
- var RETRY_STATUSES = /* @__PURE__ */ new Set([502, 503, 504]);
58
-
59
- // src/plugins/retry-idempotent.ts
60
- var createRetryIdempotentInterceptor = () => ({
61
- name: "retry-idempotent",
62
- order: 80,
63
- onResponseError: (context) => {
64
- const canRetry = IDEMPOTENT_METHODS.has(context.request.method) && context.meta.attempt < context.meta.maxRetries && Boolean(context.error && RETRY_STATUSES.has(context.error.status));
65
- if (!canRetry) {
66
- return { action: "continue", context };
67
- }
68
- return { action: "retry", delayMs: 200 * (context.meta.attempt + 1) };
69
- }
70
- });
71
-
72
- // src/plugins/sse-resume.ts
73
- var createSseResumeInterceptor = () => ({
74
- name: "sse-resume",
75
- order: 20,
76
- onSseEvent: (context) => {
77
- if (context.event.event === "ping" || context.event.event === "heartbeat") {
78
- return { action: "drop" };
79
- }
80
- return { action: "continue", context };
81
- },
82
- onSseReconnect: (context) => {
83
- if (context.meta.lastEventId) {
84
- context.request.headers.set("last-event-id", context.meta.lastEventId);
85
- }
86
- return { action: "continue", context };
87
- }
88
- });
89
-
90
- // src/plugins/ssr-forward.ts
91
- var createSsrForwardInterceptor = () => ({
92
- name: "ssr-forward",
93
- order: 10,
94
- onRequest: (context) => {
95
- if (context.meta.source === "browser") {
96
- return { action: "skip" };
97
- }
98
- if (context.incoming?.cookie) {
99
- context.request.headers.set("cookie", context.incoming.cookie);
100
- }
101
- if (context.incoming?.authorization) {
102
- context.request.headers.set("authorization", context.incoming.authorization);
103
- }
104
- if (context.incoming?.requestId) {
105
- context.request.headers.set("x-request-id", context.incoming.requestId);
106
- }
107
- return { action: "continue", context };
108
- }
109
- });
110
-
111
- // src/plugins/trace.ts
112
- var createTraceInterceptor = () => ({
113
- name: "trace",
114
- order: 0,
115
- onRequest: (context) => {
116
- const requestId = context.incoming?.requestId ?? globalThis.crypto.randomUUID();
117
- context.request.headers.set("x-request-id", requestId);
118
- context.meta.requestId = requestId;
119
- return { action: "continue", context };
120
- }
121
- });
122
-
123
- // src/plugins/index.ts
124
- var pluginFactories = {
125
- trace: createTraceInterceptor,
126
- "ssr-forward": createSsrForwardInterceptor,
127
- "retry-idempotent": createRetryIdempotentInterceptor,
128
- "sse-resume": createSseResumeInterceptor
129
- };
130
-
131
- // src/fetch-error.ts
132
- var createFetchError = (result) => {
133
- const info = result.ok ? {
134
- status: result.status,
135
- code: "UNKNOWN",
136
- message: "Request failed",
137
- body: result.data
138
- } : result.error;
139
- const error = new Error(info.message);
140
- Object.defineProperties(error, {
141
- name: { value: "FetchError" },
142
- status: { value: info.status },
143
- code: { value: info.code },
144
- body: { value: info.body },
145
- headers: { value: result.headers },
146
- result: {
147
- value: result.ok ? { ok: false, status: info.status, error: info, headers: result.headers } : result
148
- }
149
- });
150
- return error;
151
- };
152
- var isFetchError = (error) => Boolean(
153
- error && typeof error === "object" && error.name === "FetchError" && "status" in error && "code" in error
154
- );
155
- var isAbortError = (error) => error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
156
-
157
- // src/utils/result.ts
158
- var toFetchErrorInfo = (status, body) => {
159
- const record = body && typeof body === "object" ? body : void 0;
160
- const code = typeof record?.code === "string" ? record.code : "HTTP_ERROR";
161
- const message = typeof record?.message === "string" ? record.message : `Request failed with status ${status}`;
162
- return { status, code, message, body };
163
- };
164
- var toOkResult = (status, data, headers) => ({
165
- ok: true,
166
- status,
167
- data,
168
- headers
169
- });
170
- var toErrResult = (status, error, headers) => ({
171
- ok: false,
172
- status,
173
- error,
174
- headers
175
- });
176
- var unwrap = (result) => {
177
- if (result.ok) {
178
- return result.data;
179
- }
180
- throw createFetchError(result);
181
- };
182
- var unwrapAsync = async (promise) => unwrap(await promise);
183
-
184
- // src/plugins/config-interceptors.ts
185
- var mergeStatusHandlers = (options) => {
186
- const handlers = { ...options.onStatus ?? {} };
187
- if (options.onUnauthorized && handlers[401] === void 0) {
188
- handlers[401] = options.onUnauthorized;
189
- }
190
- if (options.onForbidden && handlers[403] === void 0) {
191
- handlers[403] = options.onForbidden;
192
- }
193
- if (options.onNotFound && handlers[404] === void 0) {
194
- handlers[404] = options.onNotFound;
195
- }
196
- if (options.onServerError && handlers["5xx"] === void 0 && handlers[500] === void 0) {
197
- handlers["5xx"] = options.onServerError;
198
- }
199
- const hasHandler = Object.entries(handlers).some(([, handler]) => Boolean(handler));
200
- return hasHandler ? handlers : void 0;
201
- };
202
- var resolveAuthConfig = (options) => {
203
- if (options.auth) {
204
- return options.auth;
205
- }
206
- if (options.getToken) {
207
- return { getToken: options.getToken };
208
- }
209
- return void 0;
210
- };
211
- var createConfigInterceptors = (options) => {
212
- const interceptors = [];
213
- const auth = resolveAuthConfig(options);
214
- if (auth) {
215
- interceptors.push(createAuthInterceptor(auth));
216
- }
217
- const statusHandlers = mergeStatusHandlers(options);
218
- if (statusHandlers) {
219
- interceptors.push(createStatusInterceptor(statusHandlers));
220
- }
221
- return interceptors;
222
- };
223
-
224
- // src/interceptors/run-interceptors.ts
225
- var matchesInterceptor = (interceptor, context) => {
226
- const match = interceptor.match;
227
- if (!match) {
228
- return true;
229
- }
230
- if (match.operation && match.operation !== context.meta.operation) {
231
- return false;
232
- }
233
- if (match.method && match.method !== context.request.method) {
234
- return false;
235
- }
236
- if (match.pathPrefix && !context.request.url.pathname.startsWith(match.pathPrefix)) {
237
- return false;
238
- }
239
- if (match.status !== void 0 && context.response?.status !== match.status) {
240
- return false;
241
- }
242
- return true;
243
- };
244
- var resolveInterceptors = (clientInterceptors, extras) => {
245
- const ejected = new Set(extras?.eject ?? []);
246
- const merged = [...clientInterceptors, ...extras?.use ?? []].filter(
247
- (item) => !ejected.has(item.name)
248
- );
249
- return merged.sort(
250
- (left, right) => (left.order ?? DEFAULT_INTERCEPTOR_ORDER) - (right.order ?? DEFAULT_INTERCEPTOR_ORDER)
251
- );
252
- };
253
- var runHook = async (interceptors, getHandler, context) => {
254
- let current = context;
255
- for (const interceptor of interceptors) {
256
- if (!matchesInterceptor(interceptor, current)) {
257
- continue;
258
- }
259
- const handler = getHandler(interceptor);
260
- if (!handler) {
261
- continue;
262
- }
263
- const decision = await handler(current) ?? { action: "continue", context: current };
264
- if (decision.action === "skip") {
265
- continue;
266
- }
267
- if (decision.action === "drop") {
268
- return { type: "drop" };
269
- }
270
- if (decision.action === "retry") {
271
- return { type: "retry", delayMs: decision.delayMs };
272
- }
273
- if (decision.action === "short-circuit") {
274
- return { type: "short-circuit", result: decision.result };
275
- }
276
- current = decision.context ?? current;
277
- }
278
- return { type: "continue", context: current };
279
- };
280
-
281
- // src/utils/parse-body.ts
282
- var isJsonContentType = (contentType) => Boolean(contentType && contentType.includes("json"));
283
- var parseBody = async (response, parseAs) => {
284
- if (response.status === 204) {
285
- return null;
286
- }
287
- if (parseAs === "blob") {
288
- return response.blob();
289
- }
290
- if (parseAs === "text") {
291
- return response.text();
292
- }
293
- const contentType = response.headers.get("content-type");
294
- if (parseAs === "json" || isJsonContentType(contentType)) {
295
- const text = await response.text();
296
- if (!text) {
297
- return null;
298
- }
299
- return JSON.parse(text);
300
- }
301
- return response.text();
302
- };
303
- var encodeBody = (body, headers) => {
304
- if (body === void 0 || body === null) {
305
- return void 0;
306
- }
307
- if (typeof body === "string" || body instanceof FormData || body instanceof Blob || body instanceof URLSearchParams || body instanceof ArrayBuffer) {
308
- return body;
309
- }
310
- if (!headers.has("content-type")) {
311
- headers.set("content-type", "application/json");
312
- }
313
- return JSON.stringify(body);
314
- };
315
-
316
- // src/request-execute.ts
317
- var executeFetch = async (input) => {
318
- const { context, interceptors, fetchImpl, client, requestOptions } = input;
319
- try {
320
- const response = await fetchImpl(context.request.url, {
321
- method: context.request.method,
322
- headers: context.request.headers,
323
- body: encodeBody(context.request.body, context.request.headers),
324
- signal: context.request.signal,
325
- credentials: client.credentials
326
- });
327
- return handleResponse({ interceptors, context, response, requestOptions });
328
- } catch (error) {
329
- if (isAbortError(error)) {
330
- throw error;
331
- }
332
- context.error = toFetchErrorInfo(0, {
333
- code: "NETWORK_ERROR",
334
- message: error instanceof Error ? error.message : "Network error"
335
- });
336
- const failed = await runHook(interceptors, (item) => item.onRequestError, context);
337
- if (failed.type === "retry") {
338
- return { kind: "retry", delayMs: failed.delayMs };
339
- }
340
- if (failed.type === "short-circuit") {
341
- return { kind: "result", result: failed.result };
342
- }
343
- throw createFetchError(
344
- toErrResult(0, context.error, new Headers())
345
- );
346
- }
347
- };
348
- var handleResponse = async (input) => {
349
- const { interceptors, response, requestOptions } = input;
350
- const context = {
351
- ...input.context,
352
- response,
353
- data: await parseBody(response, requestOptions?.parseAs)
354
- };
355
- const hook = response.ok ? (item) => item.onResponse : (item) => item.onResponseError;
356
- if (!response.ok) {
357
- context.error = toFetchErrorInfo(response.status, context.data);
358
- }
359
- const after = await runHook(interceptors, hook, context);
360
- if (after.type === "retry") {
361
- return { kind: "retry", delayMs: after.delayMs };
362
- }
363
- if (after.type === "short-circuit") {
364
- return { kind: "result", result: after.result };
365
- }
366
- const nextContext = after.type === "continue" ? after.context : context;
367
- const result = response.ok ? toOkResult(response.status, nextContext.data, response.headers) : toErrResult(
368
- response.status,
369
- nextContext.error ?? toFetchErrorInfo(response.status, nextContext.data),
370
- response.headers
371
- );
372
- return { kind: "result", result };
373
- };
374
-
375
- // src/utils/build-url.ts
376
- var fillPath = (path, params) => {
377
- return path.replace(/:([A-Za-z0-9_]+)/g, (_match, key) => {
378
- const value = params?.[key];
379
- if (value === void 0) {
380
- throw new Error(`tanstack-fetch: missing path param "${key}"`);
381
- }
382
- return encodeURIComponent(String(value));
383
- });
384
- };
385
- var appendQuery = (url, query) => {
386
- if (!query) {
387
- return;
388
- }
389
- Object.entries(query).forEach(([key, value]) => {
390
- if (value === void 0 || value === null) {
391
- return;
392
- }
393
- url.searchParams.set(key, String(value));
394
- });
395
- };
396
- var assertAbsoluteUrl = (baseUrl, source) => {
397
- if (source === "browser" || !baseUrl) {
398
- return;
399
- }
400
- if (!/^https?:\/\//i.test(baseUrl)) {
401
- throw new Error("tanstack-fetch: set an absolute baseUrl for SSR and Edge runtimes");
402
- }
403
- };
404
- var buildUrl = (baseUrl, path, params, query) => {
405
- const filledPath = fillPath(path.replace(/\{([A-Za-z0-9_]+)\}/g, ":$1"), params);
406
- const absolutePath = /^https?:\/\//i.test(filledPath) ? filledPath : `${(baseUrl ?? "").replace(/\/$/, "")}/${filledPath.replace(/^\//, "")}`;
407
- const url = new URL(absolutePath);
408
- appendQuery(url, query);
409
- return url;
410
- };
411
-
412
- // src/utils/headers.ts
413
- var toHeaders = (init) => new Headers(init);
414
- var mergeHeaders = (...inits) => {
415
- const headers = new Headers();
416
- inits.forEach((init) => {
417
- if (!init) {
418
- return;
419
- }
420
- new Headers(init).forEach((value, key) => {
421
- headers.set(key, value);
422
- });
423
- });
424
- return headers;
425
- };
426
- var resolveHeaders = async (value) => {
427
- if (!value) {
428
- return new Headers();
429
- }
430
- const resolved = typeof value === "function" ? await value() : value;
431
- return toHeaders(resolved);
432
- };
433
-
434
- // src/utils/signals.ts
435
- var wait = (delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs));
436
- var combineSignals = (signals) => {
437
- const active = signals.filter((signal) => Boolean(signal));
438
- if (active.length === 0) {
439
- return void 0;
440
- }
441
- if (active.length === 1) {
442
- return active[0];
443
- }
444
- if (typeof AbortSignal.any === "function") {
445
- return AbortSignal.any(active);
446
- }
447
- const controller = new AbortController();
448
- active.forEach((signal) => {
449
- if (signal.aborted) {
450
- controller.abort(signal.reason);
451
- return;
452
- }
453
- signal.addEventListener("abort", () => controller.abort(signal.reason), { once: true });
454
- });
455
- return controller.signal;
456
- };
457
- var createTimeoutSignal = (timeoutMs) => {
458
- if (!timeoutMs || timeoutMs <= 0) {
459
- return void 0;
460
- }
461
- const controller = new AbortController();
462
- const timer = setTimeout(
463
- () => controller.abort(new Error("tanstack-fetch: request timed out")),
464
- timeoutMs
465
- );
466
- controller.signal.addEventListener("abort", () => clearTimeout(timer), { once: true });
467
- return controller.signal;
468
- };
469
-
470
- // src/request.ts
471
- var resolveIncoming = async (client) => {
472
- if (!client.incoming) {
473
- return void 0;
474
- }
475
- return typeof client.incoming === "function" ? client.incoming() : client.incoming;
476
- };
477
- var createContext = async (args, attempt) => {
478
- const { method, path, requestOptions, client } = args;
479
- const source = client.source ?? "browser";
480
- assertAbsoluteUrl(client.baseUrl, source);
481
- const url = buildUrl(client.baseUrl, path, requestOptions?.params, requestOptions?.query);
482
- const headers = mergeHeaders(await resolveHeaders(client.headers), requestOptions?.headers);
483
- const timeoutMs = requestOptions?.timeoutMs ?? client.timeoutMs ?? DEFAULT_TIMEOUT_MS;
484
- const signal = combineSignals([requestOptions?.signal, createTimeoutSignal(timeoutMs)]);
485
- return {
486
- request: { method, url, headers, body: requestOptions?.body, signal },
487
- incoming: await resolveIncoming(client),
488
- meta: {
489
- attempt,
490
- maxRetries: client.maxRetries ?? DEFAULT_MAX_RETRIES,
491
- source,
492
- operation: requestOptions?.operation
493
- }
494
- };
495
- };
496
- var sendRequest = async (args) => {
497
- const interceptors = resolveInterceptors(args.interceptors, args.requestOptions?.interceptors);
498
- const fetchImpl = args.client.fetch ?? globalThis.fetch;
499
- if (!fetchImpl) {
500
- throw new Error("tanstack-fetch: fetch is not available");
501
- }
502
- let attempt = 0;
503
- const maxRetries = args.client.maxRetries ?? DEFAULT_MAX_RETRIES;
504
- while (attempt <= maxRetries) {
505
- const outcome = await runAttempt({ args, interceptors, fetchImpl, attempt });
506
- if (outcome.kind === "result") {
507
- return outcome.result;
508
- }
509
- attempt += 1;
510
- if (outcome.delayMs) {
511
- await wait(outcome.delayMs);
512
- }
513
- }
514
- throw new Error("tanstack-fetch: exceeded retry budget");
515
- };
516
- var runAttempt = async (input) => {
517
- let context = await createContext(input.args, input.attempt);
518
- const before = await runHook(input.interceptors, (item) => item.onRequest, context);
519
- if (before.type === "short-circuit") {
520
- return { kind: "result", result: before.result };
521
- }
522
- if (before.type === "retry") {
523
- return { kind: "retry", delayMs: before.delayMs };
524
- }
525
- if (before.type === "continue") {
526
- context = before.context;
527
- }
528
- return executeFetch({
529
- requestOptions: input.args.requestOptions,
530
- client: input.args.client,
531
- interceptors: input.interceptors,
532
- fetchImpl: input.fetchImpl,
533
- context
534
- });
535
- };
536
-
537
- // src/sse-parse.ts
538
- var parseSseBlock = (block) => {
539
- const dataLines = [];
540
- let eventName;
541
- let id;
542
- let retry;
543
- block.split("\n").forEach((line) => {
544
- if (line.startsWith(":")) {
545
- return;
546
- }
547
- if (line.startsWith("data:")) {
548
- dataLines.push(line.slice(5).replace(/^ /, ""));
549
- return;
550
- }
551
- if (line.startsWith("event:")) {
552
- eventName = line.slice(6).replace(/^ /, "");
553
- return;
554
- }
555
- if (line.startsWith("id:")) {
556
- id = line.slice(3).replace(/^ /, "");
557
- return;
558
- }
559
- if (line.startsWith("retry:")) {
560
- const parsed = Number.parseInt(line.slice(6).trim(), 10);
561
- if (!Number.isNaN(parsed)) {
562
- retry = parsed;
563
- }
564
- }
565
- });
566
- if (dataLines.length === 0 && !eventName) {
567
- return void 0;
568
- }
569
- const raw = dataLines.join("\n");
570
- let data = raw;
571
- if (raw) {
572
- try {
573
- data = JSON.parse(raw);
574
- } catch {
575
- data = raw;
576
- }
577
- }
578
- return { event: eventName, data, id, retry };
579
- };
580
- var consumeSseBuffer = (buffer) => {
581
- const normalized = buffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
582
- const chunks = normalized.split("\n\n");
583
- const rest = chunks.pop() ?? "";
584
- const events = chunks.map((block) => parseSseBlock(block.trim())).filter((event) => Boolean(event));
585
- return { events, rest };
586
- };
587
-
588
- // src/sse-context.ts
589
- var resolveIncoming2 = async (client) => {
590
- if (!client.incoming) {
591
- return void 0;
592
- }
593
- return typeof client.incoming === "function" ? client.incoming() : client.incoming;
594
- };
595
- var createSseContext = async (args, attempt, lastEventId) => {
596
- const source = args.client.source ?? "browser";
597
- assertAbsoluteUrl(args.client.baseUrl, source);
598
- const url = buildUrl(
599
- args.client.baseUrl,
600
- args.path,
601
- args.requestOptions?.params,
602
- args.requestOptions?.query
603
- );
604
- const headers = mergeHeaders(
605
- await resolveHeaders(args.client.headers),
606
- args.requestOptions?.headers
607
- );
608
- headers.set("accept", "text/event-stream");
609
- if (lastEventId) {
610
- headers.set("last-event-id", lastEventId);
611
- }
612
- return {
613
- request: {
614
- method: "GET",
615
- url,
616
- headers,
617
- body: args.requestOptions?.body,
618
- signal: args.requestOptions?.signal
619
- },
620
- incoming: await resolveIncoming2(args.client),
621
- meta: {
622
- attempt,
623
- maxRetries: args.client.maxRetries ?? DEFAULT_MAX_RETRIES,
624
- source,
625
- operation: args.requestOptions?.operation,
626
- lastEventId
627
- }
628
- };
629
- };
630
-
631
- // src/sse-iterator.ts
632
- var createSseIterator = (args) => {
633
- const interceptors = resolveInterceptors(args.interceptors, args.requestOptions?.interceptors);
634
- const fetchImpl = args.client.fetch ?? globalThis.fetch;
635
- const queue = [];
636
- let reader;
637
- let buffer = "";
638
- let lastEventId = args.requestOptions?.headers ? new Headers(args.requestOptions.headers).get("last-event-id") ?? void 0 : void 0;
639
- let attempt = 0;
640
- let closed = false;
641
- const pull = async () => {
642
- if (queue.length > 0) {
643
- return { value: queue.shift(), done: false };
644
- }
645
- if (closed) {
646
- return { value: void 0, done: true };
647
- }
648
- if (!reader) {
649
- const started = await openStream();
650
- if (!started) {
651
- closed = true;
652
- return { value: void 0, done: true };
653
- }
654
- }
655
- return readNext();
656
- };
657
- const openStream = async () => {
658
- if (!fetchImpl) {
659
- throw new Error("tanstack-fetch: fetch is not available");
660
- }
661
- let context = await createSseContext(args, attempt, lastEventId);
662
- const before = await runHook(interceptors, (item) => item.onRequest, context);
663
- if (before.type === "short-circuit" || before.type === "retry") {
664
- return false;
665
- }
666
- if (before.type === "continue") {
667
- context = before.context;
668
- }
669
- const response = await fetchImpl(context.request.url, {
670
- method: "GET",
671
- headers: context.request.headers,
672
- signal: context.request.signal,
673
- credentials: args.client.credentials
674
- });
675
- context.response = response;
676
- if (!response.ok || !response.body) {
677
- context.error = toFetchErrorInfo(response.status, await response.text());
678
- const failed = await runHook(interceptors, (item) => item.onSseError, context);
679
- if (failed.type === "retry" && attempt < context.meta.maxRetries) {
680
- attempt += 1;
681
- await wait(failed.delayMs ?? 500);
682
- const reconnect = await runHook(interceptors, (item) => item.onSseReconnect, context);
683
- if (reconnect.type === "continue") {
684
- lastEventId = reconnect.context.meta.lastEventId ?? lastEventId;
685
- }
686
- return openStream();
687
- }
688
- throw Object.assign(new Error(context.error.message), { error: context.error });
689
- }
690
- await runHook(interceptors, (item) => item.onSseOpen, context);
691
- reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
692
- return true;
693
- };
694
- const readNext = async () => {
695
- const currentReader = reader;
696
- if (!currentReader) {
697
- closed = true;
698
- return { value: void 0, done: true };
699
- }
700
- const chunk = await currentReader.read();
701
- if (chunk.done) {
702
- reader = void 0;
703
- closed = true;
704
- return pull();
705
- }
706
- const parsed = consumeSseBuffer(buffer + chunk.value);
707
- buffer = parsed.rest;
708
- for (const event of parsed.events) {
709
- lastEventId = event.id ?? lastEventId;
710
- const context = await createSseContext(args, attempt, lastEventId);
711
- const after = await runHook(interceptors, (item) => item.onSseEvent, { ...context, event });
712
- if (after.type === "drop") {
713
- continue;
714
- }
715
- if (after.type === "continue" && after.context.event) {
716
- queue.push(after.context.event);
717
- }
718
- }
719
- return pull();
720
- };
721
- return { next: pull };
722
- };
723
-
724
- // src/sse.ts
725
- var sendSse = (args) => ({
726
- [Symbol.asyncIterator]: () => createSseIterator(args)
727
- });
728
-
729
- // src/sse-listen.ts
730
- var HANDLER_KEYS = ["onMessage", "onEvent", "onOpen", "onError", "onClose", "lastEventId"];
731
- var splitSseOptions = (options) => {
732
- const handlers = {};
733
- const requestOptions = { ...options ?? {} };
734
- const lastEventId = options?.lastEventId;
735
- for (const key of HANDLER_KEYS) {
736
- if (key === "lastEventId") {
737
- delete requestOptions.lastEventId;
738
- continue;
739
- }
740
- if (options && key in options) {
741
- handlers[key] = options[key];
742
- delete requestOptions[key];
743
- }
744
- }
745
- if (lastEventId) {
746
- const headers = new Headers(requestOptions.headers);
747
- headers.set("last-event-id", lastEventId);
748
- requestOptions.headers = headers;
749
- }
750
- return { handlers, requestOptions, lastEventId };
751
- };
752
- var hasSseHandlers = (options) => Boolean(
753
- options && (options.onMessage || options.onEvent || options.onOpen || options.onError || options.onClose)
754
- );
755
- var listenSse = (args, handlers) => {
756
- const controller = new AbortController();
757
- const signal = combineSignals([args.requestOptions?.signal, controller.signal]);
758
- const run = async () => {
759
- try {
760
- handlers.onOpen?.();
761
- for await (const event of sendSse({
762
- ...args,
763
- requestOptions: { ...args.requestOptions, signal }
764
- })) {
765
- handlers.onEvent?.(event);
766
- handlers.onMessage?.(event.data, event);
767
- }
768
- handlers.onClose?.();
769
- } catch (error) {
770
- if (isAbortError(error) || controller.signal.aborted) {
771
- handlers.onClose?.();
772
- return;
773
- }
774
- handlers.onError?.(error);
775
- }
776
- };
777
- void run();
778
- return {
779
- close: () => controller.abort()
780
- };
781
- };
782
- var createSseApi = (base) => {
783
- const sse = ((path, options) => {
784
- const { handlers, requestOptions } = splitSseOptions(options);
785
- const args = {
786
- path,
787
- requestOptions,
788
- client: base.client,
789
- interceptors: base.interceptors
790
- };
791
- if (hasSseHandlers(options)) {
792
- return listenSse(args, handlers);
793
- }
794
- return sendSse(args);
795
- });
796
- return sse;
797
- };
798
-
799
- // src/create-fetch.ts
800
- var createFetch = (options = {}) => {
801
- const clientOptions = {
802
- throwOnError: true,
803
- ...options
804
- };
805
- const interceptors = [
806
- ...createConfigInterceptors(clientOptions),
807
- ...(clientOptions.plugins ?? []).map((name) => pluginFactories[name]()),
808
- ...clientOptions.interceptors ?? []
809
- ];
810
- const use = (name, interceptor, config) => {
811
- const next = {
812
- ...interceptor,
813
- name,
814
- order: config?.order ?? interceptor.order
815
- };
816
- const existing = interceptors.findIndex((item) => item.name === name);
817
- if (existing >= 0) {
818
- interceptors.splice(existing, 1, next);
819
- return;
820
- }
821
- interceptors.push(next);
822
- };
823
- const eject = (name) => {
824
- const index = interceptors.findIndex((item) => item.name === name);
825
- if (index >= 0) {
826
- interceptors.splice(index, 1);
827
- }
828
- };
829
- const request = (async (method, path, requestOptions) => {
830
- const result = await sendRequest({
831
- method,
832
- path,
833
- requestOptions,
834
- client: clientOptions,
835
- interceptors
836
- });
837
- const shouldThrow = requestOptions?.throwOnError ?? clientOptions.throwOnError ?? true;
838
- if (!result.ok && shouldThrow) {
839
- throw createFetchError(result);
840
- }
841
- if (shouldThrow && result.ok) {
842
- return result.data;
843
- }
844
- return result;
845
- });
846
- const bindMethod = (httpMethod) => {
847
- const bound = (path, requestOptions) => request(httpMethod, path, requestOptions);
848
- return bound;
849
- };
850
- const sse = createSseApi({ client: clientOptions, interceptors });
851
- return {
852
- use,
853
- eject,
854
- request,
855
- get: bindMethod("GET"),
856
- post: bindMethod("POST"),
857
- put: bindMethod("PUT"),
858
- patch: bindMethod("PATCH"),
859
- delete: bindMethod("DELETE"),
860
- sse
861
- };
862
- };
863
- var createClient = createFetch;
864
-
865
- export { createAuthInterceptor, createClient, createFetch, createFetchError, createRetryIdempotentInterceptor, createSseResumeInterceptor, createSsrForwardInterceptor, createStatusInterceptor, createTraceInterceptor, isAbortError, isFetchError, unwrap, unwrapAsync };
866
- //# sourceMappingURL=chunk-T27KPHCL.js.map
867
- //# sourceMappingURL=chunk-T27KPHCL.js.map