@zayne-labs/callapi 1.11.14 → 1.11.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/{common-Clx7i8bR.d.ts → common-Sj5tn_Fb.d.ts} +22 -17
- package/dist/esm/{body-B9WKokQt.js → guards-ClpaRdJN.js} +73 -73
- package/dist/esm/guards-ClpaRdJN.js.map +1 -0
- package/dist/esm/index.d.ts +6 -5
- package/dist/esm/index.js +366 -365
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/utils/external/index.d.ts +1 -1
- package/dist/esm/utils/external/index.js +1 -1
- package/package.json +1 -1
- package/dist/esm/body-B9WKokQt.js.map +0 -1
package/dist/esm/index.js
CHANGED
|
@@ -1,270 +1,7 @@
|
|
|
1
1
|
import { n as requestOptionDefaults, r as defineEnum, t as extraOptionDefaults } from "./defaults-BD3B1uIH.js";
|
|
2
|
-
import { _ as isReadableStream,
|
|
2
|
+
import { _ as isReadableStream, a as isValidationErrorInstance, b as isValidJsonString, d as isBoolean, f as isFunction, g as isQueryString, h as isPromise, l as toQueryString, m as isPlainObject, n as isHTTPErrorInstance, o as HTTPError, p as isObject, s as ValidationError, u as isArray, v as isSerializable, y as isString } from "./guards-ClpaRdJN.js";
|
|
3
3
|
import { n as fetchSpecificKeys, t as fallBackRouteSchemaKey } from "./validation-MjkoG9bG.js";
|
|
4
4
|
|
|
5
|
-
//#region src/result.ts
|
|
6
|
-
const getResponseType = (response, parser) => ({
|
|
7
|
-
arrayBuffer: () => response.arrayBuffer(),
|
|
8
|
-
blob: () => response.blob(),
|
|
9
|
-
formData: () => response.formData(),
|
|
10
|
-
json: async () => {
|
|
11
|
-
return parser(await response.text());
|
|
12
|
-
},
|
|
13
|
-
stream: () => response.body,
|
|
14
|
-
text: () => response.text()
|
|
15
|
-
});
|
|
16
|
-
const textTypes = new Set([
|
|
17
|
-
"image/svg",
|
|
18
|
-
"application/xml",
|
|
19
|
-
"application/xhtml",
|
|
20
|
-
"application/html"
|
|
21
|
-
]);
|
|
22
|
-
const JSON_REGEX = /^application\/(?:[\w!#$%&*.^`~-]*\+)?json(;.+)?$/i;
|
|
23
|
-
const detectResponseType = (response) => {
|
|
24
|
-
const initContentType = response.headers.get("content-type");
|
|
25
|
-
if (!initContentType) return extraOptionDefaults.responseType;
|
|
26
|
-
const contentType = initContentType.split(";")[0] ?? "";
|
|
27
|
-
if (JSON_REGEX.test(contentType)) return "json";
|
|
28
|
-
if (textTypes.has(contentType) || contentType.startsWith("text/")) return "text";
|
|
29
|
-
return "blob";
|
|
30
|
-
};
|
|
31
|
-
const resolveResponseData = (response, responseType, parser) => {
|
|
32
|
-
const selectedParser = parser ?? extraOptionDefaults.responseParser;
|
|
33
|
-
const selectedResponseType = responseType ?? detectResponseType(response);
|
|
34
|
-
const RESPONSE_TYPE_LOOKUP = getResponseType(response, selectedParser);
|
|
35
|
-
if (!Object.hasOwn(RESPONSE_TYPE_LOOKUP, selectedResponseType)) throw new Error(`Invalid response type: ${responseType}`);
|
|
36
|
-
return RESPONSE_TYPE_LOOKUP[selectedResponseType]();
|
|
37
|
-
};
|
|
38
|
-
const getResultModeMap = (details) => {
|
|
39
|
-
return {
|
|
40
|
-
all: () => details,
|
|
41
|
-
onlyData: () => details.data,
|
|
42
|
-
onlyResponse: () => details.response
|
|
43
|
-
};
|
|
44
|
-
};
|
|
45
|
-
const resolveSuccessResult = (data, info) => {
|
|
46
|
-
const { response, resultMode } = info;
|
|
47
|
-
return getResultModeMap({
|
|
48
|
-
data,
|
|
49
|
-
error: null,
|
|
50
|
-
response
|
|
51
|
-
})[resultMode ?? "all"]();
|
|
52
|
-
};
|
|
53
|
-
const resolveErrorResult = (error, info) => {
|
|
54
|
-
const { cloneResponse, message: customErrorMessage, resultMode } = info;
|
|
55
|
-
let errorDetails = {
|
|
56
|
-
data: null,
|
|
57
|
-
error: {
|
|
58
|
-
errorData: false,
|
|
59
|
-
message: customErrorMessage ?? error.message,
|
|
60
|
-
name: error.name,
|
|
61
|
-
originalError: error
|
|
62
|
-
},
|
|
63
|
-
response: null
|
|
64
|
-
};
|
|
65
|
-
if (isValidationErrorInstance(error)) {
|
|
66
|
-
const { errorData, message, response } = error;
|
|
67
|
-
errorDetails = {
|
|
68
|
-
data: null,
|
|
69
|
-
error: {
|
|
70
|
-
errorData,
|
|
71
|
-
issueCause: error.issueCause,
|
|
72
|
-
message,
|
|
73
|
-
name: "ValidationError",
|
|
74
|
-
originalError: error
|
|
75
|
-
},
|
|
76
|
-
response
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
if (isHTTPErrorInstance(error)) {
|
|
80
|
-
const { errorData, message, name, response } = error;
|
|
81
|
-
errorDetails = {
|
|
82
|
-
data: null,
|
|
83
|
-
error: {
|
|
84
|
-
errorData,
|
|
85
|
-
message,
|
|
86
|
-
name,
|
|
87
|
-
originalError: error
|
|
88
|
-
},
|
|
89
|
-
response: cloneResponse ? response.clone() : response
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
const errorResult = getResultModeMap(errorDetails)[resultMode ?? "all"]();
|
|
93
|
-
return {
|
|
94
|
-
errorDetails,
|
|
95
|
-
errorResult
|
|
96
|
-
};
|
|
97
|
-
};
|
|
98
|
-
const getCustomizedErrorResult = (errorResult, customErrorInfo) => {
|
|
99
|
-
if (!errorResult) return null;
|
|
100
|
-
const { message = errorResult.error.message } = customErrorInfo;
|
|
101
|
-
return {
|
|
102
|
-
...errorResult,
|
|
103
|
-
error: {
|
|
104
|
-
...errorResult.error,
|
|
105
|
-
message
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
//#endregion
|
|
111
|
-
//#region src/hooks.ts
|
|
112
|
-
const getHookRegistriesAndKeys = () => {
|
|
113
|
-
const hookRegistries = {
|
|
114
|
-
onError: /* @__PURE__ */ new Set(),
|
|
115
|
-
onRequest: /* @__PURE__ */ new Set(),
|
|
116
|
-
onRequestError: /* @__PURE__ */ new Set(),
|
|
117
|
-
onRequestReady: /* @__PURE__ */ new Set(),
|
|
118
|
-
onRequestStream: /* @__PURE__ */ new Set(),
|
|
119
|
-
onResponse: /* @__PURE__ */ new Set(),
|
|
120
|
-
onResponseError: /* @__PURE__ */ new Set(),
|
|
121
|
-
onResponseStream: /* @__PURE__ */ new Set(),
|
|
122
|
-
onRetry: /* @__PURE__ */ new Set(),
|
|
123
|
-
onSuccess: /* @__PURE__ */ new Set(),
|
|
124
|
-
onValidationError: /* @__PURE__ */ new Set()
|
|
125
|
-
};
|
|
126
|
-
return {
|
|
127
|
-
hookRegistries,
|
|
128
|
-
hookRegistryKeys: Object.keys(hookRegistries)
|
|
129
|
-
};
|
|
130
|
-
};
|
|
131
|
-
const composeHooksFromArray = (hooksArray, hooksExecutionMode) => {
|
|
132
|
-
const composedHook = async (ctx) => {
|
|
133
|
-
switch (hooksExecutionMode) {
|
|
134
|
-
case "parallel":
|
|
135
|
-
await Promise.all(hooksArray.map((uniqueHook) => uniqueHook?.(ctx)));
|
|
136
|
-
break;
|
|
137
|
-
case "sequential":
|
|
138
|
-
for (const hook of hooksArray) await hook?.(ctx);
|
|
139
|
-
break;
|
|
140
|
-
default:
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
return composedHook;
|
|
144
|
-
};
|
|
145
|
-
const executeHooks = async (...hookResultsOrPromise) => {
|
|
146
|
-
await Promise.all(hookResultsOrPromise);
|
|
147
|
-
};
|
|
148
|
-
const executeHooksInCatchBlock = async (hookResultsOrPromise, hookInfo) => {
|
|
149
|
-
const { errorInfo, shouldThrowOnError } = hookInfo;
|
|
150
|
-
try {
|
|
151
|
-
await Promise.all(hookResultsOrPromise);
|
|
152
|
-
return null;
|
|
153
|
-
} catch (hookError) {
|
|
154
|
-
if (shouldThrowOnError) throw hookError;
|
|
155
|
-
const { errorResult } = resolveErrorResult(hookError, errorInfo);
|
|
156
|
-
return errorResult;
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
//#endregion
|
|
161
|
-
//#region src/stream.ts
|
|
162
|
-
const createProgressEvent = (options) => {
|
|
163
|
-
const { chunk, totalBytes, transferredBytes } = options;
|
|
164
|
-
return {
|
|
165
|
-
chunk,
|
|
166
|
-
progress: Math.round(transferredBytes / totalBytes * 100) || 0,
|
|
167
|
-
totalBytes,
|
|
168
|
-
transferredBytes
|
|
169
|
-
};
|
|
170
|
-
};
|
|
171
|
-
const calculateTotalBytesFromBody = async (requestBody, existingTotalBytes) => {
|
|
172
|
-
let totalBytes = existingTotalBytes;
|
|
173
|
-
if (!requestBody) return totalBytes;
|
|
174
|
-
for await (const chunk of requestBody) totalBytes += chunk.byteLength;
|
|
175
|
-
return totalBytes;
|
|
176
|
-
};
|
|
177
|
-
const toStreamableRequest = async (context) => {
|
|
178
|
-
const { baseConfig, config, options, request } = context;
|
|
179
|
-
if (!options.onRequestStream || !isReadableStream(request.body)) return request;
|
|
180
|
-
const requestInstance = new Request(options.fullURL, {
|
|
181
|
-
...request,
|
|
182
|
-
duplex: "half"
|
|
183
|
-
});
|
|
184
|
-
const contentLength = requestInstance.headers.get("content-length");
|
|
185
|
-
let totalBytes = Number(contentLength ?? 0);
|
|
186
|
-
const shouldForcefullyCalcStreamSize = isObject(options.forcefullyCalculateStreamSize) ? options.forcefullyCalculateStreamSize.request : options.forcefullyCalculateStreamSize;
|
|
187
|
-
if (!contentLength && shouldForcefullyCalcStreamSize) totalBytes = await calculateTotalBytesFromBody(requestInstance.clone().body, totalBytes);
|
|
188
|
-
let transferredBytes = 0;
|
|
189
|
-
const stream = new ReadableStream({ start: async (controller) => {
|
|
190
|
-
const body = requestInstance.body;
|
|
191
|
-
if (!body) return;
|
|
192
|
-
const requestStreamContext = {
|
|
193
|
-
baseConfig,
|
|
194
|
-
config,
|
|
195
|
-
event: createProgressEvent({
|
|
196
|
-
chunk: new Uint8Array(),
|
|
197
|
-
totalBytes,
|
|
198
|
-
transferredBytes
|
|
199
|
-
}),
|
|
200
|
-
options,
|
|
201
|
-
request,
|
|
202
|
-
requestInstance
|
|
203
|
-
};
|
|
204
|
-
await executeHooks(options.onRequestStream?.(requestStreamContext));
|
|
205
|
-
for await (const chunk of body) {
|
|
206
|
-
transferredBytes += chunk.byteLength;
|
|
207
|
-
totalBytes = Math.max(totalBytes, transferredBytes);
|
|
208
|
-
await executeHooks(options.onRequestStream?.({
|
|
209
|
-
...requestStreamContext,
|
|
210
|
-
event: createProgressEvent({
|
|
211
|
-
chunk,
|
|
212
|
-
totalBytes,
|
|
213
|
-
transferredBytes
|
|
214
|
-
})
|
|
215
|
-
}));
|
|
216
|
-
controller.enqueue(chunk);
|
|
217
|
-
}
|
|
218
|
-
controller.close();
|
|
219
|
-
} });
|
|
220
|
-
return new Request(requestInstance, {
|
|
221
|
-
body: stream,
|
|
222
|
-
duplex: "half"
|
|
223
|
-
});
|
|
224
|
-
};
|
|
225
|
-
const toStreamableResponse = async (context) => {
|
|
226
|
-
const { baseConfig, config, options, request, response } = context;
|
|
227
|
-
if (!options.onResponseStream || !response.body) return response;
|
|
228
|
-
const contentLength = response.headers.get("content-length");
|
|
229
|
-
let totalBytes = Number(contentLength ?? 0);
|
|
230
|
-
const shouldForceContentLengthCalc = isObject(options.forcefullyCalculateStreamSize) ? options.forcefullyCalculateStreamSize.response : options.forcefullyCalculateStreamSize;
|
|
231
|
-
if (!contentLength && shouldForceContentLengthCalc) totalBytes = await calculateTotalBytesFromBody(response.clone().body, totalBytes);
|
|
232
|
-
let transferredBytes = 0;
|
|
233
|
-
const stream = new ReadableStream({ start: async (controller) => {
|
|
234
|
-
const body = response.body;
|
|
235
|
-
if (!body) return;
|
|
236
|
-
const responseStreamContext = {
|
|
237
|
-
baseConfig,
|
|
238
|
-
config,
|
|
239
|
-
event: createProgressEvent({
|
|
240
|
-
chunk: new Uint8Array(),
|
|
241
|
-
totalBytes,
|
|
242
|
-
transferredBytes
|
|
243
|
-
}),
|
|
244
|
-
options,
|
|
245
|
-
request,
|
|
246
|
-
response
|
|
247
|
-
};
|
|
248
|
-
await executeHooks(options.onResponseStream?.(responseStreamContext));
|
|
249
|
-
for await (const chunk of body) {
|
|
250
|
-
transferredBytes += chunk.byteLength;
|
|
251
|
-
totalBytes = Math.max(totalBytes, transferredBytes);
|
|
252
|
-
await executeHooks(options.onResponseStream?.({
|
|
253
|
-
...responseStreamContext,
|
|
254
|
-
event: createProgressEvent({
|
|
255
|
-
chunk,
|
|
256
|
-
totalBytes,
|
|
257
|
-
transferredBytes
|
|
258
|
-
})
|
|
259
|
-
}));
|
|
260
|
-
controller.enqueue(chunk);
|
|
261
|
-
}
|
|
262
|
-
controller.close();
|
|
263
|
-
} });
|
|
264
|
-
return new Response(stream, response);
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
//#endregion
|
|
268
5
|
//#region src/auth.ts
|
|
269
6
|
const resolveAuthValue = (value) => isFunction(value) ? value() : value;
|
|
270
7
|
const getAuthHeader = async (auth) => {
|
|
@@ -533,122 +270,386 @@ const getFullAndNormalizedURL = (options) => {
|
|
|
533
270
|
const normalizedInitURL = normalizeURL(initURL);
|
|
534
271
|
const urlWithMergedQueryAndParams = mergeUrlWithQuery(mergeUrlWithParams(normalizedInitURL, params), query);
|
|
535
272
|
return {
|
|
536
|
-
fullURL: !urlWithMergedQueryAndParams.startsWith("http") && baseURL ? `${baseURL}${urlWithMergedQueryAndParams}` : urlWithMergedQueryAndParams,
|
|
537
|
-
normalizedInitURL
|
|
273
|
+
fullURL: !urlWithMergedQueryAndParams.startsWith("http") && baseURL ? `${baseURL}${urlWithMergedQueryAndParams}` : urlWithMergedQueryAndParams,
|
|
274
|
+
normalizedInitURL
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
//#endregion
|
|
279
|
+
//#region src/utils/polyfills/combinedSignal.ts
|
|
280
|
+
const createCombinedSignalPolyfill = (signals) => {
|
|
281
|
+
const controller = new AbortController();
|
|
282
|
+
const handleAbort = (actualSignal) => {
|
|
283
|
+
if (controller.signal.aborted) return;
|
|
284
|
+
controller.abort(actualSignal.reason);
|
|
285
|
+
};
|
|
286
|
+
for (const actualSignal of signals) {
|
|
287
|
+
if (actualSignal.aborted) {
|
|
288
|
+
handleAbort(actualSignal);
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
actualSignal.addEventListener("abort", () => handleAbort(actualSignal), { signal: controller.signal });
|
|
292
|
+
}
|
|
293
|
+
return controller.signal;
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
//#endregion
|
|
297
|
+
//#region src/utils/polyfills/timeoutSignal.ts
|
|
298
|
+
const createTimeoutSignalPolyfill = (milliseconds) => {
|
|
299
|
+
const controller = new AbortController();
|
|
300
|
+
const reason = new DOMException("Request timed out", "TimeoutError");
|
|
301
|
+
const timeout = setTimeout(() => controller.abort(reason), milliseconds);
|
|
302
|
+
controller.signal.addEventListener("abort", () => clearTimeout(timeout));
|
|
303
|
+
return controller.signal;
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
//#endregion
|
|
307
|
+
//#region src/utils/common.ts
|
|
308
|
+
const omitKeys = (initialObject, keysToOmit) => {
|
|
309
|
+
const updatedObject = {};
|
|
310
|
+
const keysToOmitSet = new Set(keysToOmit);
|
|
311
|
+
for (const [key, value] of Object.entries(initialObject)) if (!keysToOmitSet.has(key)) updatedObject[key] = value;
|
|
312
|
+
return updatedObject;
|
|
313
|
+
};
|
|
314
|
+
const pickKeys = (initialObject, keysToPick) => {
|
|
315
|
+
const updatedObject = {};
|
|
316
|
+
const keysToPickSet = new Set(keysToPick);
|
|
317
|
+
for (const [key, value] of Object.entries(initialObject)) if (keysToPickSet.has(key)) updatedObject[key] = value;
|
|
318
|
+
return updatedObject;
|
|
319
|
+
};
|
|
320
|
+
const splitBaseConfig = (baseConfig) => [pickKeys(baseConfig, fetchSpecificKeys), omitKeys(baseConfig, fetchSpecificKeys)];
|
|
321
|
+
const splitConfig = (config) => [pickKeys(config, fetchSpecificKeys), omitKeys(config, fetchSpecificKeys)];
|
|
322
|
+
const objectifyHeaders = (headers) => {
|
|
323
|
+
if (!headers || isPlainObject(headers)) return headers;
|
|
324
|
+
return Object.fromEntries(headers);
|
|
325
|
+
};
|
|
326
|
+
const getHeaders = async (options) => {
|
|
327
|
+
const { auth, body, headers } = options;
|
|
328
|
+
if (!(Boolean(headers) || Boolean(body) || Boolean(auth))) return;
|
|
329
|
+
const headersObject = {
|
|
330
|
+
...await getAuthHeader(auth),
|
|
331
|
+
...objectifyHeaders(headers)
|
|
332
|
+
};
|
|
333
|
+
if (isQueryString(body)) {
|
|
334
|
+
headersObject["Content-Type"] = "application/x-www-form-urlencoded";
|
|
335
|
+
return headersObject;
|
|
336
|
+
}
|
|
337
|
+
if (isSerializable(body) || isValidJsonString(body)) {
|
|
338
|
+
headersObject["Content-Type"] = "application/json";
|
|
339
|
+
headersObject.Accept = "application/json";
|
|
340
|
+
}
|
|
341
|
+
return headersObject;
|
|
342
|
+
};
|
|
343
|
+
const getMethod = (ctx) => {
|
|
344
|
+
const { initURL, method } = ctx;
|
|
345
|
+
return method?.toUpperCase() ?? extractMethodFromURL(initURL)?.toUpperCase() ?? requestOptionDefaults.method;
|
|
346
|
+
};
|
|
347
|
+
const getBody = (options) => {
|
|
348
|
+
const { body, bodySerializer } = options;
|
|
349
|
+
if (isSerializable(body)) return (bodySerializer ?? extraOptionDefaults.bodySerializer)(body);
|
|
350
|
+
return body;
|
|
351
|
+
};
|
|
352
|
+
const getInitFetchImpl = (customFetchImpl) => {
|
|
353
|
+
if (customFetchImpl) return customFetchImpl;
|
|
354
|
+
if (typeof globalThis !== "undefined" && isFunction(globalThis.fetch)) return globalThis.fetch;
|
|
355
|
+
throw new Error("No fetch implementation found");
|
|
356
|
+
};
|
|
357
|
+
const getFetchImpl = (context) => {
|
|
358
|
+
const { customFetchImpl, fetchMiddleware, requestContext } = context;
|
|
359
|
+
const initFetchImpl = getInitFetchImpl(customFetchImpl);
|
|
360
|
+
return fetchMiddleware ? fetchMiddleware({
|
|
361
|
+
...requestContext,
|
|
362
|
+
fetchImpl: initFetchImpl
|
|
363
|
+
}) : initFetchImpl;
|
|
364
|
+
};
|
|
365
|
+
const waitFor = (delay) => {
|
|
366
|
+
if (delay === 0) return;
|
|
367
|
+
return new Promise((resolve) => setTimeout(resolve, delay));
|
|
368
|
+
};
|
|
369
|
+
const createCombinedSignal = (...signals) => {
|
|
370
|
+
const cleanedSignals = signals.filter((signal) => signal != null);
|
|
371
|
+
if (!("any" in AbortSignal)) return createCombinedSignalPolyfill(cleanedSignals);
|
|
372
|
+
return AbortSignal.any(cleanedSignals);
|
|
373
|
+
};
|
|
374
|
+
const createTimeoutSignal = (milliseconds) => {
|
|
375
|
+
if (milliseconds == null) return null;
|
|
376
|
+
if (!("timeout" in AbortSignal)) return createTimeoutSignalPolyfill(milliseconds);
|
|
377
|
+
return AbortSignal.timeout(milliseconds);
|
|
378
|
+
};
|
|
379
|
+
const deterministicHashFn = (value) => {
|
|
380
|
+
return JSON.stringify(value, (_, val) => {
|
|
381
|
+
if (!isPlainObject(val)) return val;
|
|
382
|
+
const sortedKeys = Object.keys(val).toSorted();
|
|
383
|
+
const result = {};
|
|
384
|
+
for (const key of sortedKeys) result[key] = val[key];
|
|
385
|
+
return result;
|
|
386
|
+
});
|
|
387
|
+
};
|
|
388
|
+
const toArray = (value) => isArray(value) ? value : [value];
|
|
389
|
+
|
|
390
|
+
//#endregion
|
|
391
|
+
//#region src/result.ts
|
|
392
|
+
const getResponseType = (response, parser) => ({
|
|
393
|
+
arrayBuffer: () => response.arrayBuffer(),
|
|
394
|
+
blob: () => response.blob(),
|
|
395
|
+
formData: () => response.formData(),
|
|
396
|
+
json: async () => {
|
|
397
|
+
return parser(await response.text());
|
|
398
|
+
},
|
|
399
|
+
stream: () => response.body,
|
|
400
|
+
text: () => response.text()
|
|
401
|
+
});
|
|
402
|
+
const textTypes = new Set([
|
|
403
|
+
"image/svg",
|
|
404
|
+
"application/xml",
|
|
405
|
+
"application/xhtml",
|
|
406
|
+
"application/html"
|
|
407
|
+
]);
|
|
408
|
+
const JSON_REGEX = /^application\/(?:[\w!#$%&*.^`~-]*\+)?json(;.+)?$/i;
|
|
409
|
+
const detectResponseType = (response) => {
|
|
410
|
+
const initContentType = response.headers.get("content-type");
|
|
411
|
+
if (!initContentType) return extraOptionDefaults.responseType;
|
|
412
|
+
const contentType = initContentType.split(";")[0] ?? "";
|
|
413
|
+
if (JSON_REGEX.test(contentType)) return "json";
|
|
414
|
+
if (textTypes.has(contentType) || contentType.startsWith("text/")) return "text";
|
|
415
|
+
return "blob";
|
|
416
|
+
};
|
|
417
|
+
const resolveResponseData = (response, responseType, parser) => {
|
|
418
|
+
const selectedParser = parser ?? extraOptionDefaults.responseParser;
|
|
419
|
+
const selectedResponseType = responseType ?? detectResponseType(response);
|
|
420
|
+
const RESPONSE_TYPE_LOOKUP = getResponseType(response, selectedParser);
|
|
421
|
+
if (!Object.hasOwn(RESPONSE_TYPE_LOOKUP, selectedResponseType)) throw new Error(`Invalid response type: ${responseType}`);
|
|
422
|
+
return RESPONSE_TYPE_LOOKUP[selectedResponseType]();
|
|
423
|
+
};
|
|
424
|
+
const getResultModeMap = (details) => {
|
|
425
|
+
return {
|
|
426
|
+
all: () => details,
|
|
427
|
+
onlyData: () => details.data,
|
|
428
|
+
onlyResponse: () => details.response,
|
|
429
|
+
withoutResponse: () => omitKeys(details, ["response"])
|
|
430
|
+
};
|
|
431
|
+
};
|
|
432
|
+
const resolveSuccessResult = (data, info) => {
|
|
433
|
+
const { response, resultMode } = info;
|
|
434
|
+
return getResultModeMap({
|
|
435
|
+
data,
|
|
436
|
+
error: null,
|
|
437
|
+
response
|
|
438
|
+
})[resultMode ?? "all"]();
|
|
439
|
+
};
|
|
440
|
+
const resolveErrorResult = (error, info) => {
|
|
441
|
+
const { cloneResponse, message: customErrorMessage, resultMode } = info;
|
|
442
|
+
let errorDetails = {
|
|
443
|
+
data: null,
|
|
444
|
+
error: {
|
|
445
|
+
errorData: false,
|
|
446
|
+
message: customErrorMessage ?? error.message,
|
|
447
|
+
name: error.name,
|
|
448
|
+
originalError: error
|
|
449
|
+
},
|
|
450
|
+
response: null
|
|
451
|
+
};
|
|
452
|
+
if (isValidationErrorInstance(error)) {
|
|
453
|
+
const { errorData, message, response } = error;
|
|
454
|
+
errorDetails = {
|
|
455
|
+
data: null,
|
|
456
|
+
error: {
|
|
457
|
+
errorData,
|
|
458
|
+
issueCause: error.issueCause,
|
|
459
|
+
message,
|
|
460
|
+
name: "ValidationError",
|
|
461
|
+
originalError: error
|
|
462
|
+
},
|
|
463
|
+
response
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
if (isHTTPErrorInstance(error)) {
|
|
467
|
+
const { errorData, message, name, response } = error;
|
|
468
|
+
errorDetails = {
|
|
469
|
+
data: null,
|
|
470
|
+
error: {
|
|
471
|
+
errorData,
|
|
472
|
+
message,
|
|
473
|
+
name,
|
|
474
|
+
originalError: error
|
|
475
|
+
},
|
|
476
|
+
response: cloneResponse ? response.clone() : response
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
const errorResult = getResultModeMap(errorDetails)[resultMode ?? "all"]();
|
|
480
|
+
return {
|
|
481
|
+
errorDetails,
|
|
482
|
+
errorResult
|
|
483
|
+
};
|
|
484
|
+
};
|
|
485
|
+
const getCustomizedErrorResult = (errorResult, customErrorInfo) => {
|
|
486
|
+
if (!errorResult) return null;
|
|
487
|
+
const { message = errorResult.error.message } = customErrorInfo;
|
|
488
|
+
return {
|
|
489
|
+
...errorResult,
|
|
490
|
+
error: {
|
|
491
|
+
...errorResult.error,
|
|
492
|
+
message
|
|
493
|
+
}
|
|
538
494
|
};
|
|
539
495
|
};
|
|
540
496
|
|
|
541
497
|
//#endregion
|
|
542
|
-
//#region src/
|
|
543
|
-
const
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
498
|
+
//#region src/hooks.ts
|
|
499
|
+
const getHookRegistriesAndKeys = () => {
|
|
500
|
+
const hookRegistries = {
|
|
501
|
+
onError: /* @__PURE__ */ new Set(),
|
|
502
|
+
onRequest: /* @__PURE__ */ new Set(),
|
|
503
|
+
onRequestError: /* @__PURE__ */ new Set(),
|
|
504
|
+
onRequestReady: /* @__PURE__ */ new Set(),
|
|
505
|
+
onRequestStream: /* @__PURE__ */ new Set(),
|
|
506
|
+
onResponse: /* @__PURE__ */ new Set(),
|
|
507
|
+
onResponseError: /* @__PURE__ */ new Set(),
|
|
508
|
+
onResponseStream: /* @__PURE__ */ new Set(),
|
|
509
|
+
onRetry: /* @__PURE__ */ new Set(),
|
|
510
|
+
onSuccess: /* @__PURE__ */ new Set(),
|
|
511
|
+
onValidationError: /* @__PURE__ */ new Set()
|
|
548
512
|
};
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
513
|
+
return {
|
|
514
|
+
hookRegistries,
|
|
515
|
+
hookRegistryKeys: Object.keys(hookRegistries)
|
|
516
|
+
};
|
|
517
|
+
};
|
|
518
|
+
const composeHooksFromArray = (hooksArray, hooksExecutionMode) => {
|
|
519
|
+
const composedHook = async (ctx) => {
|
|
520
|
+
switch (hooksExecutionMode) {
|
|
521
|
+
case "parallel":
|
|
522
|
+
await Promise.all(hooksArray.map((uniqueHook) => uniqueHook?.(ctx)));
|
|
523
|
+
break;
|
|
524
|
+
case "sequential":
|
|
525
|
+
for (const hook of hooksArray) await hook?.(ctx);
|
|
526
|
+
break;
|
|
527
|
+
default:
|
|
553
528
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
return controller.signal;
|
|
529
|
+
};
|
|
530
|
+
return composedHook;
|
|
557
531
|
};
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
const
|
|
562
|
-
const
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
532
|
+
const executeHooks = async (...hookResultsOrPromise) => {
|
|
533
|
+
await Promise.all(hookResultsOrPromise);
|
|
534
|
+
};
|
|
535
|
+
const executeHooksInCatchBlock = async (hookResultsOrPromise, hookInfo) => {
|
|
536
|
+
const { errorInfo, shouldThrowOnError } = hookInfo;
|
|
537
|
+
try {
|
|
538
|
+
await Promise.all(hookResultsOrPromise);
|
|
539
|
+
return null;
|
|
540
|
+
} catch (hookError) {
|
|
541
|
+
if (shouldThrowOnError) throw hookError;
|
|
542
|
+
const { errorResult } = resolveErrorResult(hookError, errorInfo);
|
|
543
|
+
return errorResult;
|
|
544
|
+
}
|
|
567
545
|
};
|
|
568
546
|
|
|
569
547
|
//#endregion
|
|
570
|
-
//#region src/
|
|
571
|
-
const
|
|
572
|
-
const
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
const updatedObject = {};
|
|
579
|
-
const keysToPickSet = new Set(keysToPick);
|
|
580
|
-
for (const [key, value] of Object.entries(initialObject)) if (keysToPickSet.has(key)) updatedObject[key] = value;
|
|
581
|
-
return updatedObject;
|
|
582
|
-
};
|
|
583
|
-
const splitBaseConfig = (baseConfig) => [pickKeys(baseConfig, fetchSpecificKeys), omitKeys(baseConfig, fetchSpecificKeys)];
|
|
584
|
-
const splitConfig = (config) => [pickKeys(config, fetchSpecificKeys), omitKeys(config, fetchSpecificKeys)];
|
|
585
|
-
const objectifyHeaders = (headers) => {
|
|
586
|
-
if (!headers || isPlainObject(headers)) return headers;
|
|
587
|
-
return Object.fromEntries(headers);
|
|
588
|
-
};
|
|
589
|
-
const getHeaders = async (options) => {
|
|
590
|
-
const { auth, body, headers } = options;
|
|
591
|
-
if (!(Boolean(headers) || Boolean(body) || Boolean(auth))) return;
|
|
592
|
-
const headersObject = {
|
|
593
|
-
...await getAuthHeader(auth),
|
|
594
|
-
...objectifyHeaders(headers)
|
|
548
|
+
//#region src/stream.ts
|
|
549
|
+
const createProgressEvent = (options) => {
|
|
550
|
+
const { chunk, totalBytes, transferredBytes } = options;
|
|
551
|
+
return {
|
|
552
|
+
chunk,
|
|
553
|
+
progress: Math.round(transferredBytes / totalBytes * 100) || 0,
|
|
554
|
+
totalBytes,
|
|
555
|
+
transferredBytes
|
|
595
556
|
};
|
|
596
|
-
if (isQueryString(body)) {
|
|
597
|
-
headersObject["Content-Type"] = "application/x-www-form-urlencoded";
|
|
598
|
-
return headersObject;
|
|
599
|
-
}
|
|
600
|
-
if (isSerializable(body) || isValidJsonString(body)) {
|
|
601
|
-
headersObject["Content-Type"] = "application/json";
|
|
602
|
-
headersObject.Accept = "application/json";
|
|
603
|
-
}
|
|
604
|
-
return headersObject;
|
|
605
|
-
};
|
|
606
|
-
const getMethod = (ctx) => {
|
|
607
|
-
const { initURL, method } = ctx;
|
|
608
|
-
return method?.toUpperCase() ?? extractMethodFromURL(initURL)?.toUpperCase() ?? requestOptionDefaults.method;
|
|
609
|
-
};
|
|
610
|
-
const getBody = (options) => {
|
|
611
|
-
const { body, bodySerializer } = options;
|
|
612
|
-
if (isSerializable(body)) return (bodySerializer ?? extraOptionDefaults.bodySerializer)(body);
|
|
613
|
-
return body;
|
|
614
|
-
};
|
|
615
|
-
const getInitFetchImpl = (customFetchImpl) => {
|
|
616
|
-
if (customFetchImpl) return customFetchImpl;
|
|
617
|
-
if (typeof globalThis !== "undefined" && isFunction(globalThis.fetch)) return globalThis.fetch;
|
|
618
|
-
throw new Error("No fetch implementation found");
|
|
619
|
-
};
|
|
620
|
-
const getFetchImpl = (context) => {
|
|
621
|
-
const { customFetchImpl, fetchMiddleware, requestContext } = context;
|
|
622
|
-
const initFetchImpl = getInitFetchImpl(customFetchImpl);
|
|
623
|
-
return fetchMiddleware ? fetchMiddleware({
|
|
624
|
-
...requestContext,
|
|
625
|
-
fetchImpl: initFetchImpl
|
|
626
|
-
}) : initFetchImpl;
|
|
627
|
-
};
|
|
628
|
-
const waitFor = (delay) => {
|
|
629
|
-
if (delay === 0) return;
|
|
630
|
-
return new Promise((resolve) => setTimeout(resolve, delay));
|
|
631
|
-
};
|
|
632
|
-
const createCombinedSignal = (...signals) => {
|
|
633
|
-
const cleanedSignals = signals.filter((signal) => signal != null);
|
|
634
|
-
if (!("any" in AbortSignal)) return createCombinedSignalPolyfill(cleanedSignals);
|
|
635
|
-
return AbortSignal.any(cleanedSignals);
|
|
636
557
|
};
|
|
637
|
-
const
|
|
638
|
-
|
|
639
|
-
if (!
|
|
640
|
-
|
|
558
|
+
const calculateTotalBytesFromBody = async (requestBody, existingTotalBytes) => {
|
|
559
|
+
let totalBytes = existingTotalBytes;
|
|
560
|
+
if (!requestBody) return totalBytes;
|
|
561
|
+
for await (const chunk of requestBody) totalBytes += chunk.byteLength;
|
|
562
|
+
return totalBytes;
|
|
641
563
|
};
|
|
642
|
-
const
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
564
|
+
const toStreamableRequest = async (context) => {
|
|
565
|
+
const { baseConfig, config, options, request } = context;
|
|
566
|
+
if (!options.onRequestStream || !isReadableStream(request.body)) return request;
|
|
567
|
+
const requestInstance = new Request(options.fullURL, {
|
|
568
|
+
...request,
|
|
569
|
+
duplex: "half"
|
|
570
|
+
});
|
|
571
|
+
const contentLength = requestInstance.headers.get("content-length");
|
|
572
|
+
let totalBytes = Number(contentLength ?? 0);
|
|
573
|
+
const shouldForcefullyCalcStreamSize = isObject(options.forcefullyCalculateStreamSize) ? options.forcefullyCalculateStreamSize.request : options.forcefullyCalculateStreamSize;
|
|
574
|
+
if (!contentLength && shouldForcefullyCalcStreamSize) totalBytes = await calculateTotalBytesFromBody(requestInstance.clone().body, totalBytes);
|
|
575
|
+
let transferredBytes = 0;
|
|
576
|
+
const stream = new ReadableStream({ start: async (controller) => {
|
|
577
|
+
const body = requestInstance.body;
|
|
578
|
+
if (!body) return;
|
|
579
|
+
const requestStreamContext = {
|
|
580
|
+
baseConfig,
|
|
581
|
+
config,
|
|
582
|
+
event: createProgressEvent({
|
|
583
|
+
chunk: new Uint8Array(),
|
|
584
|
+
totalBytes,
|
|
585
|
+
transferredBytes
|
|
586
|
+
}),
|
|
587
|
+
options,
|
|
588
|
+
request,
|
|
589
|
+
requestInstance
|
|
590
|
+
};
|
|
591
|
+
await executeHooks(options.onRequestStream?.(requestStreamContext));
|
|
592
|
+
for await (const chunk of body) {
|
|
593
|
+
transferredBytes += chunk.byteLength;
|
|
594
|
+
totalBytes = Math.max(totalBytes, transferredBytes);
|
|
595
|
+
await executeHooks(options.onRequestStream?.({
|
|
596
|
+
...requestStreamContext,
|
|
597
|
+
event: createProgressEvent({
|
|
598
|
+
chunk,
|
|
599
|
+
totalBytes,
|
|
600
|
+
transferredBytes
|
|
601
|
+
})
|
|
602
|
+
}));
|
|
603
|
+
controller.enqueue(chunk);
|
|
604
|
+
}
|
|
605
|
+
controller.close();
|
|
606
|
+
} });
|
|
607
|
+
return new Request(requestInstance, {
|
|
608
|
+
body: stream,
|
|
609
|
+
duplex: "half"
|
|
649
610
|
});
|
|
650
611
|
};
|
|
651
|
-
const
|
|
612
|
+
const toStreamableResponse = async (context) => {
|
|
613
|
+
const { baseConfig, config, options, request, response } = context;
|
|
614
|
+
if (!options.onResponseStream || !response.body) return response;
|
|
615
|
+
const contentLength = response.headers.get("content-length");
|
|
616
|
+
let totalBytes = Number(contentLength ?? 0);
|
|
617
|
+
const shouldForceContentLengthCalc = isObject(options.forcefullyCalculateStreamSize) ? options.forcefullyCalculateStreamSize.response : options.forcefullyCalculateStreamSize;
|
|
618
|
+
if (!contentLength && shouldForceContentLengthCalc) totalBytes = await calculateTotalBytesFromBody(response.clone().body, totalBytes);
|
|
619
|
+
let transferredBytes = 0;
|
|
620
|
+
const stream = new ReadableStream({ start: async (controller) => {
|
|
621
|
+
const body = response.body;
|
|
622
|
+
if (!body) return;
|
|
623
|
+
const responseStreamContext = {
|
|
624
|
+
baseConfig,
|
|
625
|
+
config,
|
|
626
|
+
event: createProgressEvent({
|
|
627
|
+
chunk: new Uint8Array(),
|
|
628
|
+
totalBytes,
|
|
629
|
+
transferredBytes
|
|
630
|
+
}),
|
|
631
|
+
options,
|
|
632
|
+
request,
|
|
633
|
+
response
|
|
634
|
+
};
|
|
635
|
+
await executeHooks(options.onResponseStream?.(responseStreamContext));
|
|
636
|
+
for await (const chunk of body) {
|
|
637
|
+
transferredBytes += chunk.byteLength;
|
|
638
|
+
totalBytes = Math.max(totalBytes, transferredBytes);
|
|
639
|
+
await executeHooks(options.onResponseStream?.({
|
|
640
|
+
...responseStreamContext,
|
|
641
|
+
event: createProgressEvent({
|
|
642
|
+
chunk,
|
|
643
|
+
totalBytes,
|
|
644
|
+
transferredBytes
|
|
645
|
+
})
|
|
646
|
+
}));
|
|
647
|
+
controller.enqueue(chunk);
|
|
648
|
+
}
|
|
649
|
+
controller.close();
|
|
650
|
+
} });
|
|
651
|
+
return new Response(stream, response);
|
|
652
|
+
};
|
|
652
653
|
|
|
653
654
|
//#endregion
|
|
654
655
|
//#region src/dedupe.ts
|