@zayne-labs/callapi 1.11.39 → 1.11.41
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/constants/index.d.ts +4 -3
- package/dist/constants/index.js +1 -2
- package/dist/defaults-DG6hZq9w.js +556 -0
- package/dist/defaults-DG6hZq9w.js.map +1 -0
- package/dist/guards-CQCcnQjX.js +22 -0
- package/dist/guards-CQCcnQjX.js.map +1 -0
- package/dist/{index-DLD315Wj.d.ts → index-CCyp1Rmr.d.ts} +48 -45
- package/dist/index.d.ts +2 -2
- package/dist/index.js +71 -481
- package/dist/index.js.map +1 -1
- package/dist/utils/external/index.d.ts +6 -4
- package/dist/utils/external/index.js +115 -2
- package/dist/utils/external/index.js.map +1 -0
- package/package.json +2 -2
- package/dist/defaults-B-dOt2Dd.js +0 -28
- package/dist/defaults-B-dOt2Dd.js.map +0 -1
- package/dist/external-DXaCWLPN.js +0 -230
- package/dist/external-DXaCWLPN.js.map +0 -1
- package/dist/validation-DbbofkNi.js +0 -28
- package/dist/validation-DbbofkNi.js.map +0 -1
- package/dist/validation-Do6HBp6Z.d.ts +0 -9
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { G as FallBackRouteSchemaKey, K as fallBackRouteSchemaKey, et as RequestContext, lt as fetchSpecificKeys } from "../index-CCyp1Rmr.js";
|
|
2
2
|
|
|
3
3
|
//#region src/constants/defaults.d.ts
|
|
4
4
|
declare const extraOptionDefaults: Readonly<Readonly<{
|
|
@@ -8,6 +8,7 @@ declare const extraOptionDefaults: Readonly<Readonly<{
|
|
|
8
8
|
};
|
|
9
9
|
defaultHTTPErrorMessage: string;
|
|
10
10
|
dedupeCacheScope: "local";
|
|
11
|
+
dedupeKey: (ctx: RequestContext) => string;
|
|
11
12
|
dedupeCacheScopeKey: "default";
|
|
12
13
|
dedupeStrategy: "cancel";
|
|
13
14
|
hooksExecutionMode: "parallel";
|
|
@@ -22,9 +23,9 @@ declare const extraOptionDefaults: Readonly<Readonly<{
|
|
|
22
23
|
retryStatusCodes: never[];
|
|
23
24
|
retryStrategy: "linear";
|
|
24
25
|
}>>;
|
|
25
|
-
declare const requestOptionDefaults: Readonly<{
|
|
26
|
+
declare const requestOptionDefaults: Readonly<Readonly<{
|
|
26
27
|
method: "GET";
|
|
27
|
-
}
|
|
28
|
+
}>>;
|
|
28
29
|
//#endregion
|
|
29
30
|
export { FallBackRouteSchemaKey, extraOptionDefaults, fallBackRouteSchemaKey, fetchSpecificKeys, requestOptionDefaults };
|
|
30
31
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/constants/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { n as requestOptionDefaults, t as extraOptionDefaults } from "../defaults-
|
|
2
|
-
import { n as fetchSpecificKeys, t as fallBackRouteSchemaKey } from "../validation-DbbofkNi.js";
|
|
1
|
+
import { S as fetchSpecificKeys, n as requestOptionDefaults, t as extraOptionDefaults, x as fallBackRouteSchemaKey } from "../defaults-DG6hZq9w.js";
|
|
3
2
|
|
|
4
3
|
export { extraOptionDefaults, fallBackRouteSchemaKey, fetchSpecificKeys, requestOptionDefaults };
|
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
//#region src/types/type-helpers.ts
|
|
2
|
+
const defineEnum = (value) => Object.freeze(value);
|
|
3
|
+
|
|
4
|
+
//#endregion
|
|
5
|
+
//#region src/utils/guards.ts
|
|
6
|
+
const isArray = (value) => Array.isArray(value);
|
|
7
|
+
const isBoolean = (value) => typeof value === "boolean";
|
|
8
|
+
const isBlob = (value) => value instanceof Blob;
|
|
9
|
+
const isObject = (value) => {
|
|
10
|
+
return typeof value === "object" && value !== null;
|
|
11
|
+
};
|
|
12
|
+
const hasObjectPrototype = (value) => {
|
|
13
|
+
return Object.prototype.toString.call(value) === "[object Object]";
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* @description Copied from TanStack Query's isPlainObject
|
|
17
|
+
* @see https://github.com/TanStack/query/blob/main/packages/query-core/src/utils.ts#L321
|
|
18
|
+
*/
|
|
19
|
+
const isPlainObject = (value) => {
|
|
20
|
+
if (!hasObjectPrototype(value)) return false;
|
|
21
|
+
const constructor = value?.constructor;
|
|
22
|
+
if (constructor === void 0) return true;
|
|
23
|
+
const prototype = constructor.prototype;
|
|
24
|
+
if (!hasObjectPrototype(prototype)) return false;
|
|
25
|
+
if (!Object.hasOwn(prototype, "isPrototypeOf")) return false;
|
|
26
|
+
if (Object.getPrototypeOf(value) !== Object.prototype) return false;
|
|
27
|
+
return true;
|
|
28
|
+
};
|
|
29
|
+
const isValidJsonString = (value) => {
|
|
30
|
+
if (!isString(value)) return false;
|
|
31
|
+
try {
|
|
32
|
+
JSON.parse(value);
|
|
33
|
+
return true;
|
|
34
|
+
} catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const isSerializableObject = (value) => {
|
|
39
|
+
return isPlainObject(value) || isArray(value) || typeof value?.toJSON === "function";
|
|
40
|
+
};
|
|
41
|
+
const isFunction = (value) => typeof value === "function";
|
|
42
|
+
const isQueryString = (value) => isString(value) && value.includes("=");
|
|
43
|
+
const isString = (value) => typeof value === "string";
|
|
44
|
+
const isPromise = (value) => value instanceof Promise;
|
|
45
|
+
const isReadableStream = (value) => {
|
|
46
|
+
return value instanceof ReadableStream;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/auth.ts
|
|
51
|
+
const resolveAuthValue = (value) => isFunction(value) ? value() : value;
|
|
52
|
+
const getAuthHeader = async (auth) => {
|
|
53
|
+
if (auth === void 0) return;
|
|
54
|
+
if (isPromise(auth) || isFunction(auth) || !isObject(auth)) {
|
|
55
|
+
const authValue = await resolveAuthValue(auth);
|
|
56
|
+
if (authValue === void 0) return;
|
|
57
|
+
return { Authorization: `Bearer ${authValue}` };
|
|
58
|
+
}
|
|
59
|
+
switch (auth.type) {
|
|
60
|
+
case "Basic": {
|
|
61
|
+
const [username, password] = await Promise.all([resolveAuthValue(auth.username), resolveAuthValue(auth.password)]);
|
|
62
|
+
if (username === void 0 || password === void 0) return;
|
|
63
|
+
return { Authorization: `Basic ${globalThis.btoa(`${username}:${password}`)}` };
|
|
64
|
+
}
|
|
65
|
+
case "Bearer": {
|
|
66
|
+
const value = await resolveAuthValue(auth.value);
|
|
67
|
+
if (value === void 0) return;
|
|
68
|
+
return { Authorization: `Bearer ${value}` };
|
|
69
|
+
}
|
|
70
|
+
case "Custom": {
|
|
71
|
+
const [prefix, value] = await Promise.all([resolveAuthValue(auth.prefix), resolveAuthValue(auth.value)]);
|
|
72
|
+
if (value === void 0) return;
|
|
73
|
+
return { Authorization: `${prefix} ${value}` };
|
|
74
|
+
}
|
|
75
|
+
case "Token": {
|
|
76
|
+
const value = await resolveAuthValue(auth.value);
|
|
77
|
+
if (value === void 0) return;
|
|
78
|
+
return { Authorization: `Token ${value}` };
|
|
79
|
+
}
|
|
80
|
+
default: return;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/constants/common.ts
|
|
86
|
+
const fetchSpecificKeys = defineEnum([
|
|
87
|
+
"body",
|
|
88
|
+
"integrity",
|
|
89
|
+
"duplex",
|
|
90
|
+
"method",
|
|
91
|
+
"headers",
|
|
92
|
+
"signal",
|
|
93
|
+
"cache",
|
|
94
|
+
"redirect",
|
|
95
|
+
"window",
|
|
96
|
+
"credentials",
|
|
97
|
+
"keepalive",
|
|
98
|
+
"referrer",
|
|
99
|
+
"priority",
|
|
100
|
+
"mode",
|
|
101
|
+
"referrerPolicy"
|
|
102
|
+
]);
|
|
103
|
+
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/constants/validation.ts
|
|
106
|
+
const fallBackRouteSchemaKey = "@default";
|
|
107
|
+
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region src/utils/external/error.ts
|
|
110
|
+
const httpErrorSymbol = Symbol("HTTPError");
|
|
111
|
+
var HTTPError = class HTTPError extends Error {
|
|
112
|
+
errorData;
|
|
113
|
+
httpErrorSymbol = httpErrorSymbol;
|
|
114
|
+
name = "HTTPError";
|
|
115
|
+
response;
|
|
116
|
+
constructor(errorDetails, errorOptions) {
|
|
117
|
+
const { defaultHTTPErrorMessage, errorData, response } = errorDetails;
|
|
118
|
+
const selectedDefaultErrorMessage = (isString(defaultHTTPErrorMessage) ? defaultHTTPErrorMessage : defaultHTTPErrorMessage?.({
|
|
119
|
+
errorData,
|
|
120
|
+
response
|
|
121
|
+
})) ?? (response.statusText || extraOptionDefaults.defaultHTTPErrorMessage);
|
|
122
|
+
const message = errorData?.message ?? selectedDefaultErrorMessage;
|
|
123
|
+
super(message, errorOptions);
|
|
124
|
+
this.errorData = errorData;
|
|
125
|
+
this.response = response;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* @description Checks if the given error is an instance of HTTPError
|
|
129
|
+
* @param error - The error to check
|
|
130
|
+
* @returns true if the error is an instance of HTTPError, false otherwise
|
|
131
|
+
*/
|
|
132
|
+
static isError(error) {
|
|
133
|
+
if (!isObject(error)) return false;
|
|
134
|
+
if (error instanceof HTTPError) return true;
|
|
135
|
+
const actualError = error;
|
|
136
|
+
return actualError.httpErrorSymbol === httpErrorSymbol && actualError.name === "HTTPError";
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const prettifyPath = (path) => {
|
|
140
|
+
if (!path || path.length === 0) return "";
|
|
141
|
+
return ` → at ${path.map((segment) => isObject(segment) ? segment.key : segment).join(".")}`;
|
|
142
|
+
};
|
|
143
|
+
const prettifyValidationIssues = (issues) => {
|
|
144
|
+
return issues.map((issue) => `✖ ${issue.message}${prettifyPath(issue.path)}`).join(" | ");
|
|
145
|
+
};
|
|
146
|
+
const validationErrorSymbol = Symbol("ValidationErrorSymbol");
|
|
147
|
+
var ValidationError = class ValidationError extends Error {
|
|
148
|
+
errorData;
|
|
149
|
+
issueCause;
|
|
150
|
+
name = "ValidationError";
|
|
151
|
+
response;
|
|
152
|
+
validationErrorSymbol = validationErrorSymbol;
|
|
153
|
+
constructor(details, errorOptions) {
|
|
154
|
+
const { issueCause, issues, response } = details;
|
|
155
|
+
const prettyMessage = prettifyValidationIssues(issues);
|
|
156
|
+
const message = `(${issueCause.toUpperCase()}) - ${prettyMessage}`;
|
|
157
|
+
super(message, errorOptions);
|
|
158
|
+
this.errorData = issues;
|
|
159
|
+
this.response = response;
|
|
160
|
+
this.issueCause = issueCause;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* @description Checks if the given error is an instance of ValidationError
|
|
164
|
+
* @param error - The error to check
|
|
165
|
+
* @returns true if the error is an instance of ValidationError, false otherwise
|
|
166
|
+
*/
|
|
167
|
+
static isError(error) {
|
|
168
|
+
if (!isObject(error)) return false;
|
|
169
|
+
if (error instanceof ValidationError) return true;
|
|
170
|
+
const actualError = error;
|
|
171
|
+
return actualError.validationErrorSymbol === validationErrorSymbol && actualError.name === "ValidationError";
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region src/validation.ts
|
|
177
|
+
const handleValidatorFunction = (validator, inputData) => {
|
|
178
|
+
return new Promise((resolve) => resolve(validator(inputData))).then((value) => ({
|
|
179
|
+
issues: void 0,
|
|
180
|
+
value
|
|
181
|
+
})).catch((error) => ({
|
|
182
|
+
issues: toArray(error),
|
|
183
|
+
value: void 0
|
|
184
|
+
}));
|
|
185
|
+
};
|
|
186
|
+
const getValidatedValue = (inputValue, schema, _ignoredOptions) => {
|
|
187
|
+
if (!schema) return {
|
|
188
|
+
issues: void 0,
|
|
189
|
+
value: inputValue
|
|
190
|
+
};
|
|
191
|
+
return isFunction(schema) ? handleValidatorFunction(schema, inputValue) : schema["~standard"].validate(inputValue);
|
|
192
|
+
};
|
|
193
|
+
const callApiSchemaParser = async (fullSchema, schemaName, options) => {
|
|
194
|
+
const { inputValue, response } = options;
|
|
195
|
+
const schema = fullSchema?.[schemaName];
|
|
196
|
+
const result = await getValidatedValue(inputValue, schema);
|
|
197
|
+
if (result.issues) throw new ValidationError({
|
|
198
|
+
issueCause: schemaName,
|
|
199
|
+
issues: result.issues,
|
|
200
|
+
response: response ?? null
|
|
201
|
+
});
|
|
202
|
+
return result.value;
|
|
203
|
+
};
|
|
204
|
+
const routeKeyMethods = defineEnum([
|
|
205
|
+
"delete",
|
|
206
|
+
"get",
|
|
207
|
+
"patch",
|
|
208
|
+
"post",
|
|
209
|
+
"put"
|
|
210
|
+
]);
|
|
211
|
+
const handleSchemaValidation = async (fullSchema, schemaName, validationOptions) => {
|
|
212
|
+
const { inputValue, response, schemaConfig } = validationOptions;
|
|
213
|
+
const disableRuntimeValidationBooleanObject = isObject(schemaConfig?.disableRuntimeValidation) ? schemaConfig.disableRuntimeValidation : {};
|
|
214
|
+
if (schemaConfig?.disableRuntimeValidation === true || disableRuntimeValidationBooleanObject[schemaName] === true) return inputValue;
|
|
215
|
+
const validResult = await callApiSchemaParser(fullSchema, schemaName, {
|
|
216
|
+
inputValue,
|
|
217
|
+
response
|
|
218
|
+
});
|
|
219
|
+
const disableResultApplicationBooleanObject = isObject(schemaConfig?.disableRuntimeValidationTransform) ? schemaConfig.disableRuntimeValidationTransform : {};
|
|
220
|
+
if (schemaConfig?.disableRuntimeValidationTransform === true || disableResultApplicationBooleanObject[schemaName] === true) return inputValue;
|
|
221
|
+
return validResult;
|
|
222
|
+
};
|
|
223
|
+
const extraOptionsToBeValidated = [
|
|
224
|
+
"meta",
|
|
225
|
+
"params",
|
|
226
|
+
"query",
|
|
227
|
+
"auth"
|
|
228
|
+
];
|
|
229
|
+
const requestOptionsToBeValidated = [
|
|
230
|
+
"body",
|
|
231
|
+
"headers",
|
|
232
|
+
"method"
|
|
233
|
+
];
|
|
234
|
+
const handleOptionsValidation = async (validationOptions) => {
|
|
235
|
+
const { options, request, schema, schemaConfig } = validationOptions;
|
|
236
|
+
const resolvedOptionsToBeValidated = options ? extraOptionsToBeValidated : requestOptionsToBeValidated;
|
|
237
|
+
const resolvedOptions = options ?? request;
|
|
238
|
+
const validationResultArray = await Promise.all(resolvedOptionsToBeValidated.map((schemaName) => handleSchemaValidation(schema, schemaName, {
|
|
239
|
+
inputValue: resolvedOptions[schemaName],
|
|
240
|
+
schemaConfig
|
|
241
|
+
})));
|
|
242
|
+
const validatedResultObject = {};
|
|
243
|
+
for (const [index, schemaName] of resolvedOptionsToBeValidated.entries()) {
|
|
244
|
+
const validationResult = validationResultArray[index];
|
|
245
|
+
if (validationResult === void 0) continue;
|
|
246
|
+
validatedResultObject[schemaName] = validationResult;
|
|
247
|
+
}
|
|
248
|
+
return validatedResultObject;
|
|
249
|
+
};
|
|
250
|
+
const handleConfigValidation = async (validationOptions) => {
|
|
251
|
+
const { baseExtraOptions, currentRouteSchemaKey, extraOptions, options, request } = validationOptions;
|
|
252
|
+
const { currentRouteSchema, resolvedSchema } = getResolvedSchema({
|
|
253
|
+
baseExtraOptions,
|
|
254
|
+
currentRouteSchemaKey,
|
|
255
|
+
extraOptions
|
|
256
|
+
});
|
|
257
|
+
const resolvedSchemaConfig = getResolvedSchemaConfig({
|
|
258
|
+
baseExtraOptions,
|
|
259
|
+
extraOptions
|
|
260
|
+
});
|
|
261
|
+
if (resolvedSchemaConfig?.strict === true && !currentRouteSchema) throw new ValidationError({
|
|
262
|
+
issueCause: "schemaConfig-(strict)",
|
|
263
|
+
issues: [{ message: `Strict Mode - No schema found for route '${currentRouteSchemaKey}' ` }],
|
|
264
|
+
response: null
|
|
265
|
+
});
|
|
266
|
+
const [extraOptionsValidationResult, requestOptionsValidationResult] = await Promise.all([handleOptionsValidation({
|
|
267
|
+
options,
|
|
268
|
+
schema: resolvedSchema,
|
|
269
|
+
schemaConfig: resolvedSchemaConfig
|
|
270
|
+
}), handleOptionsValidation({
|
|
271
|
+
request,
|
|
272
|
+
schema: resolvedSchema,
|
|
273
|
+
schemaConfig: resolvedSchemaConfig
|
|
274
|
+
})]);
|
|
275
|
+
return {
|
|
276
|
+
extraOptionsValidationResult,
|
|
277
|
+
requestOptionsValidationResult,
|
|
278
|
+
resolvedSchema,
|
|
279
|
+
resolvedSchemaConfig
|
|
280
|
+
};
|
|
281
|
+
};
|
|
282
|
+
const getResolvedSchema = (context) => {
|
|
283
|
+
const { baseExtraOptions, currentRouteSchemaKey, extraOptions } = context;
|
|
284
|
+
const fallbackRouteSchema = baseExtraOptions.schema?.routes[fallBackRouteSchemaKey];
|
|
285
|
+
const currentRouteSchema = baseExtraOptions.schema?.routes[currentRouteSchemaKey];
|
|
286
|
+
const resolvedRouteSchema = {
|
|
287
|
+
...fallbackRouteSchema,
|
|
288
|
+
...currentRouteSchema
|
|
289
|
+
};
|
|
290
|
+
return {
|
|
291
|
+
currentRouteSchema,
|
|
292
|
+
resolvedSchema: isFunction(extraOptions.schema) ? extraOptions.schema({
|
|
293
|
+
baseSchemaRoutes: baseExtraOptions.schema?.routes ?? {},
|
|
294
|
+
currentRouteSchema: resolvedRouteSchema ?? {},
|
|
295
|
+
currentRouteSchemaKey
|
|
296
|
+
}) : extraOptions.schema ?? resolvedRouteSchema
|
|
297
|
+
};
|
|
298
|
+
};
|
|
299
|
+
const getResolvedSchemaConfig = (context) => {
|
|
300
|
+
const { baseExtraOptions, extraOptions } = context;
|
|
301
|
+
return isFunction(extraOptions.schemaConfig) ? extraOptions.schemaConfig({ baseSchemaConfig: baseExtraOptions.schema?.config ?? {} }) : extraOptions.schemaConfig ?? baseExtraOptions.schema?.config;
|
|
302
|
+
};
|
|
303
|
+
const removeLeadingSlash = (value) => value.startsWith("/") ? value.slice(1) : value;
|
|
304
|
+
const extractURLParts = (initURL) => {
|
|
305
|
+
return {
|
|
306
|
+
methodFromURL: extractMethodFromURL(initURL),
|
|
307
|
+
pathWithoutMethod: normalizeURL(initURL, { retainLeadingSlashForRelativeURLs: false })
|
|
308
|
+
};
|
|
309
|
+
};
|
|
310
|
+
const mergeURLParts = (options) => {
|
|
311
|
+
const { method, path } = options;
|
|
312
|
+
return method ? `${atSymbol}${method}/${removeLeadingSlash(path)}` : path;
|
|
313
|
+
};
|
|
314
|
+
const getCurrentRouteSchemaKeyAndMainInitURL = (context) => {
|
|
315
|
+
const { baseExtraOptions, extraOptions, initURL } = context;
|
|
316
|
+
const schemaConfig = getResolvedSchemaConfig({
|
|
317
|
+
baseExtraOptions,
|
|
318
|
+
extraOptions
|
|
319
|
+
});
|
|
320
|
+
let currentRouteSchemaKey = initURL;
|
|
321
|
+
let mainInitURL = initURL;
|
|
322
|
+
const { methodFromURL, pathWithoutMethod } = extractURLParts(initURL);
|
|
323
|
+
const prefixWithoutLeadingSlash = schemaConfig?.prefix && removeLeadingSlash(schemaConfig.prefix);
|
|
324
|
+
if (prefixWithoutLeadingSlash && pathWithoutMethod.startsWith(prefixWithoutLeadingSlash)) {
|
|
325
|
+
currentRouteSchemaKey = mergeURLParts({
|
|
326
|
+
method: methodFromURL,
|
|
327
|
+
path: pathWithoutMethod.slice(prefixWithoutLeadingSlash.length)
|
|
328
|
+
});
|
|
329
|
+
mainInitURL = mergeURLParts({
|
|
330
|
+
method: methodFromURL,
|
|
331
|
+
path: pathWithoutMethod.replace(prefixWithoutLeadingSlash, schemaConfig.baseURL ?? "")
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
if (schemaConfig?.baseURL && pathWithoutMethod.startsWith(schemaConfig.baseURL)) currentRouteSchemaKey = mergeURLParts({
|
|
335
|
+
method: methodFromURL,
|
|
336
|
+
path: pathWithoutMethod.slice(schemaConfig.baseURL.length)
|
|
337
|
+
});
|
|
338
|
+
return {
|
|
339
|
+
currentRouteSchemaKey,
|
|
340
|
+
mainInitURL
|
|
341
|
+
};
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
//#endregion
|
|
345
|
+
//#region src/url.ts
|
|
346
|
+
const slash = "/";
|
|
347
|
+
const colon = ":";
|
|
348
|
+
const openBrace = "{";
|
|
349
|
+
const closeBrace = "}";
|
|
350
|
+
const handleArrayParams = (url, params) => {
|
|
351
|
+
let newUrl = url;
|
|
352
|
+
const urlParts = newUrl.split(slash);
|
|
353
|
+
const matchedParamsArray = [];
|
|
354
|
+
for (const part of urlParts) {
|
|
355
|
+
if (!(part.startsWith(colon) || part.startsWith(openBrace) && part.endsWith(closeBrace))) continue;
|
|
356
|
+
matchedParamsArray.push(part);
|
|
357
|
+
}
|
|
358
|
+
for (const [paramIndex, matchedParam] of matchedParamsArray.entries()) {
|
|
359
|
+
const stringParamValue = String(params[paramIndex]);
|
|
360
|
+
newUrl = newUrl.replace(matchedParam, stringParamValue);
|
|
361
|
+
}
|
|
362
|
+
return newUrl;
|
|
363
|
+
};
|
|
364
|
+
const handleObjectParams = (url, params) => {
|
|
365
|
+
let newUrl = url;
|
|
366
|
+
for (const [paramKey, paramValue] of Object.entries(params)) {
|
|
367
|
+
const colonPattern = `${colon}${paramKey}`;
|
|
368
|
+
const bracePattern = `${openBrace}${paramKey}${closeBrace}`;
|
|
369
|
+
const stringValue = String(paramValue);
|
|
370
|
+
newUrl = newUrl.replace(colonPattern, stringValue);
|
|
371
|
+
newUrl = newUrl.replace(bracePattern, stringValue);
|
|
372
|
+
}
|
|
373
|
+
return newUrl;
|
|
374
|
+
};
|
|
375
|
+
const mergeUrlWithParams = (url, params) => {
|
|
376
|
+
if (!params) return url;
|
|
377
|
+
return isArray(params) ? handleArrayParams(url, params) : handleObjectParams(url, params);
|
|
378
|
+
};
|
|
379
|
+
const questionMark = "?";
|
|
380
|
+
const ampersand = "&";
|
|
381
|
+
const mergeUrlWithQuery = (url, query) => {
|
|
382
|
+
if (!query) return url;
|
|
383
|
+
const queryString = new URLSearchParams(query).toString();
|
|
384
|
+
if (queryString.length === 0) return url;
|
|
385
|
+
if (url.endsWith(questionMark)) return `${url}${queryString}`;
|
|
386
|
+
if (url.includes(questionMark)) return `${url}${ampersand}${queryString}`;
|
|
387
|
+
return `${url}${questionMark}${queryString}`;
|
|
388
|
+
};
|
|
389
|
+
/**
|
|
390
|
+
* @description Extracts the HTTP method from method-prefixed route patterns.
|
|
391
|
+
*
|
|
392
|
+
* Analyzes URLs that start with method modifiers (e.g., "@get/", "@post/") and extracts
|
|
393
|
+
* the HTTP method for use in API requests. This enables method specification directly
|
|
394
|
+
* in route definitions.
|
|
395
|
+
*
|
|
396
|
+
* @param initURL - The URL string to analyze for method modifiers
|
|
397
|
+
* @returns The extracted HTTP method (lowercase) if found, otherwise undefined
|
|
398
|
+
*
|
|
399
|
+
* @example
|
|
400
|
+
* ```typescript
|
|
401
|
+
* extractMethodFromURL("@get/users"); // Returns: "get"
|
|
402
|
+
* extractMethodFromURL("@post/users"); // Returns: "post"
|
|
403
|
+
* ```
|
|
404
|
+
*/
|
|
405
|
+
const extractMethodFromURL = (initURL) => {
|
|
406
|
+
if (!initURL?.startsWith("@")) return;
|
|
407
|
+
const methodFromURL = routeKeyMethods.find((method) => initURL.startsWith(`${atSymbol}${method}${slash}`));
|
|
408
|
+
if (!methodFromURL) return;
|
|
409
|
+
return methodFromURL;
|
|
410
|
+
};
|
|
411
|
+
const atSymbol = "@";
|
|
412
|
+
const normalizeURL = (initURL, options = {}) => {
|
|
413
|
+
const { retainLeadingSlashForRelativeURLs = true } = options;
|
|
414
|
+
const methodFromURL = extractMethodFromURL(initURL);
|
|
415
|
+
if (!methodFromURL) return initURL;
|
|
416
|
+
return retainLeadingSlashForRelativeURLs && !initURL.includes("http") ? initURL.replace(`${atSymbol}${methodFromURL}`, "") : initURL.replace(`${atSymbol}${methodFromURL}${slash}`, "");
|
|
417
|
+
};
|
|
418
|
+
const getFullURL = (initURL, baseURL) => {
|
|
419
|
+
if (!baseURL || initURL.startsWith("http")) return initURL;
|
|
420
|
+
return initURL.length > 0 && !initURL.startsWith(slash) && !baseURL.endsWith(slash) ? `${baseURL}${slash}${initURL}` : `${baseURL}${initURL}`;
|
|
421
|
+
};
|
|
422
|
+
const getFullAndNormalizedURL = (options) => {
|
|
423
|
+
const { baseURL, initURL, params, query } = options;
|
|
424
|
+
const normalizedInitURL = normalizeURL(initURL);
|
|
425
|
+
const fullURL = getFullURL(mergeUrlWithQuery(mergeUrlWithParams(normalizedInitURL, params), query), baseURL);
|
|
426
|
+
if (!URL.canParse(fullURL)) {
|
|
427
|
+
const errorMessage = !baseURL ? `Invalid URL '${initURL}'. Are you passing a relative url to CallApi without setting the 'baseURL' option?` : `Invalid URL '${fullURL}'. Please validate that you are passing the correct url.`;
|
|
428
|
+
console.error(errorMessage);
|
|
429
|
+
}
|
|
430
|
+
return {
|
|
431
|
+
fullURL,
|
|
432
|
+
normalizedInitURL
|
|
433
|
+
};
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
//#endregion
|
|
437
|
+
//#region src/utils/common.ts
|
|
438
|
+
const omitKeys = (initialObject, keysToOmit) => {
|
|
439
|
+
const updatedObject = {};
|
|
440
|
+
const keysToOmitSet = new Set(keysToOmit);
|
|
441
|
+
for (const [key, value] of Object.entries(initialObject)) if (!keysToOmitSet.has(key)) updatedObject[key] = value;
|
|
442
|
+
return updatedObject;
|
|
443
|
+
};
|
|
444
|
+
const pickKeys = (initialObject, keysToPick) => {
|
|
445
|
+
const updatedObject = {};
|
|
446
|
+
const keysToPickSet = new Set(keysToPick);
|
|
447
|
+
for (const [key, value] of Object.entries(initialObject)) if (keysToPickSet.has(key)) updatedObject[key] = value;
|
|
448
|
+
return updatedObject;
|
|
449
|
+
};
|
|
450
|
+
const splitBaseConfig = (baseConfig) => [pickKeys(baseConfig, fetchSpecificKeys), omitKeys(baseConfig, fetchSpecificKeys)];
|
|
451
|
+
const splitConfig = (config) => [pickKeys(config, fetchSpecificKeys), omitKeys(config, fetchSpecificKeys)];
|
|
452
|
+
const objectifyHeaders = (headers) => {
|
|
453
|
+
if (!headers) return {};
|
|
454
|
+
if (isPlainObject(headers)) return headers;
|
|
455
|
+
return Object.fromEntries(headers);
|
|
456
|
+
};
|
|
457
|
+
const getResolvedHeaders = (options) => {
|
|
458
|
+
const { baseHeaders, headers } = options;
|
|
459
|
+
return objectifyHeaders(isFunction(headers) ? headers({ baseHeaders: objectifyHeaders(baseHeaders) }) : headers ?? baseHeaders);
|
|
460
|
+
};
|
|
461
|
+
const detectContentTypeHeader = (body) => {
|
|
462
|
+
if (isQueryString(body)) return { "Content-Type": "application/x-www-form-urlencoded" };
|
|
463
|
+
if (isSerializableObject(body) || isValidJsonString(body)) return {
|
|
464
|
+
Accept: "application/json",
|
|
465
|
+
"Content-Type": "application/json"
|
|
466
|
+
};
|
|
467
|
+
return null;
|
|
468
|
+
};
|
|
469
|
+
const getHeaders = async (options) => {
|
|
470
|
+
const { auth, body, resolvedHeaders } = options;
|
|
471
|
+
const authHeaderObject = await getAuthHeader(auth);
|
|
472
|
+
const resolvedHeadersObject = objectifyHeaders(resolvedHeaders);
|
|
473
|
+
if (!(Object.hasOwn(resolvedHeadersObject, "Content-Type") || Object.hasOwn(resolvedHeadersObject, "content-type"))) {
|
|
474
|
+
const contentTypeHeader = detectContentTypeHeader(body);
|
|
475
|
+
contentTypeHeader && Object.assign(resolvedHeadersObject, contentTypeHeader);
|
|
476
|
+
}
|
|
477
|
+
return {
|
|
478
|
+
...authHeaderObject,
|
|
479
|
+
...resolvedHeadersObject
|
|
480
|
+
};
|
|
481
|
+
};
|
|
482
|
+
const getMethod = (ctx) => {
|
|
483
|
+
const { initURL, method } = ctx;
|
|
484
|
+
return method?.toUpperCase() ?? extractMethodFromURL(initURL)?.toUpperCase() ?? requestOptionDefaults.method;
|
|
485
|
+
};
|
|
486
|
+
const getBody = (options) => {
|
|
487
|
+
const { body, bodySerializer, resolvedHeaders } = options;
|
|
488
|
+
const existingContentType = new Headers(resolvedHeaders).get("content-type");
|
|
489
|
+
if (!existingContentType && isSerializableObject(body)) return (bodySerializer ?? extraOptionDefaults.bodySerializer)(body);
|
|
490
|
+
if (existingContentType === "application/x-www-form-urlencoded" && isSerializableObject(body)) return new URLSearchParams(body).toString();
|
|
491
|
+
return body;
|
|
492
|
+
};
|
|
493
|
+
const getInitFetchImpl = (customFetchImpl) => {
|
|
494
|
+
if (customFetchImpl) return customFetchImpl;
|
|
495
|
+
if (typeof globalThis !== "undefined" && isFunction(globalThis.fetch)) return globalThis.fetch;
|
|
496
|
+
throw new Error("No fetch implementation found");
|
|
497
|
+
};
|
|
498
|
+
const getFetchImpl = (context) => {
|
|
499
|
+
const { customFetchImpl, fetchMiddleware, requestContext } = context;
|
|
500
|
+
const initFetchImpl = getInitFetchImpl(customFetchImpl);
|
|
501
|
+
return fetchMiddleware ? fetchMiddleware({
|
|
502
|
+
...requestContext,
|
|
503
|
+
fetchImpl: initFetchImpl
|
|
504
|
+
}) : initFetchImpl;
|
|
505
|
+
};
|
|
506
|
+
const waitFor = (delay) => {
|
|
507
|
+
if (delay === 0) return;
|
|
508
|
+
return new Promise((resolve) => setTimeout(resolve, delay));
|
|
509
|
+
};
|
|
510
|
+
const createCombinedSignal = (...signals) => {
|
|
511
|
+
return AbortSignal.any(signals.filter((signal) => signal != null));
|
|
512
|
+
};
|
|
513
|
+
const createTimeoutSignal = (milliseconds) => {
|
|
514
|
+
if (milliseconds == null) return null;
|
|
515
|
+
return AbortSignal.timeout(milliseconds);
|
|
516
|
+
};
|
|
517
|
+
const deterministicHashFn = (value) => {
|
|
518
|
+
return JSON.stringify(value, (_, val) => {
|
|
519
|
+
if (!isPlainObject(val)) return val;
|
|
520
|
+
const sortedKeys = Object.keys(val).sort();
|
|
521
|
+
const result = {};
|
|
522
|
+
for (const key of sortedKeys) result[key] = val[key];
|
|
523
|
+
return result;
|
|
524
|
+
});
|
|
525
|
+
};
|
|
526
|
+
const toArray = (value) => isArray(value) ? value : [value];
|
|
527
|
+
|
|
528
|
+
//#endregion
|
|
529
|
+
//#region src/constants/defaults.ts
|
|
530
|
+
const extraOptionDefaults = Object.freeze(defineEnum({
|
|
531
|
+
bodySerializer: JSON.stringify,
|
|
532
|
+
defaultHTTPErrorMessage: "Request failed unexpectedly",
|
|
533
|
+
dedupeCacheScope: "local",
|
|
534
|
+
dedupeKey: (ctx) => `${ctx.options.fullURL}-${deterministicHashFn({
|
|
535
|
+
options: ctx.options,
|
|
536
|
+
request: ctx.request
|
|
537
|
+
})}`,
|
|
538
|
+
dedupeCacheScopeKey: "default",
|
|
539
|
+
dedupeStrategy: "cancel",
|
|
540
|
+
hooksExecutionMode: "parallel",
|
|
541
|
+
responseParser: JSON.parse,
|
|
542
|
+
responseType: "json",
|
|
543
|
+
resultMode: "all",
|
|
544
|
+
retryAttempts: 0,
|
|
545
|
+
retryCondition: () => true,
|
|
546
|
+
retryDelay: 1e3,
|
|
547
|
+
retryMaxDelay: 1e4,
|
|
548
|
+
retryMethods: ["GET", "POST"],
|
|
549
|
+
retryStatusCodes: [],
|
|
550
|
+
retryStrategy: "linear"
|
|
551
|
+
}));
|
|
552
|
+
const requestOptionDefaults = Object.freeze(defineEnum({ method: "GET" }));
|
|
553
|
+
|
|
554
|
+
//#endregion
|
|
555
|
+
export { isArray as C, isObject as D, isFunction as E, isReadableStream as O, fetchSpecificKeys as S, isBoolean as T, handleConfigValidation as _, getBody as a, ValidationError as b, getMethod as c, splitBaseConfig as d, splitConfig as f, getValidatedValue as g, getCurrentRouteSchemaKeyAndMainInitURL as h, createTimeoutSignal as i, isString as k, getResolvedHeaders as l, getFullAndNormalizedURL as m, requestOptionDefaults as n, getFetchImpl as o, waitFor as p, createCombinedSignal as r, getHeaders as s, extraOptionDefaults as t, omitKeys as u, handleSchemaValidation as v, isBlob as w, fallBackRouteSchemaKey as x, HTTPError as y };
|
|
556
|
+
//# sourceMappingURL=defaults-DG6hZq9w.js.map
|