accessio 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +165 -207
- package/cjs/accessio.cjs +30 -13
- package/cjs/accessio.cjs.map +1 -1
- package/cjs/core/accessioError.cjs +28 -0
- package/cjs/core/accessioError.cjs.map +1 -1
- package/cjs/core/buildURL.cjs +11 -8
- package/cjs/core/buildURL.cjs.map +1 -1
- package/cjs/core/fetchAdapter.cjs +4 -0
- package/cjs/core/fetchAdapter.cjs.map +1 -1
- package/cjs/core/mergeConfig.cjs +1 -1
- package/cjs/core/mergeConfig.cjs.map +1 -1
- package/cjs/core/request.cjs +43 -15
- package/cjs/core/request.cjs.map +1 -1
- package/cjs/core/retry.cjs +8 -5
- package/cjs/core/retry.cjs.map +1 -1
- package/cjs/helpers/flattenHeaders.cjs +4 -1
- package/cjs/helpers/flattenHeaders.cjs.map +1 -1
- package/cjs/helpers/memoryCache.cjs +19 -1
- package/cjs/helpers/memoryCache.cjs.map +1 -1
- package/cjs/helpers/rateLimiter.cjs +31 -3
- package/cjs/helpers/rateLimiter.cjs.map +1 -1
- package/cjs/helpers/toFormData.cjs +1 -1
- package/cjs/helpers/toFormData.cjs.map +1 -1
- package/index.d.ts +7 -0
- package/package.json +5 -4
- package/src/accessio.ts +44 -15
- package/src/core/accessioError.ts +33 -2
- package/src/core/buildURL.ts +16 -13
- package/src/core/fetchAdapter.ts +3 -0
- package/src/core/mergeConfig.ts +1 -1
- package/src/core/request.ts +54 -16
- package/src/core/retry.ts +8 -5
- package/src/helpers/flattenHeaders.ts +4 -1
- package/src/helpers/memoryCache.ts +24 -1
- package/src/helpers/rateLimiter.ts +35 -3
- package/src/helpers/toFormData.ts +5 -1
- package/src/types.ts +7 -1
|
@@ -70,11 +70,39 @@ function redactBody(value, seen) {
|
|
|
70
70
|
}
|
|
71
71
|
return out;
|
|
72
72
|
}
|
|
73
|
+
function redactParams(params) {
|
|
74
|
+
if (!params || typeof params !== "object") return params;
|
|
75
|
+
const out = {};
|
|
76
|
+
for (const key of Object.keys(params)) {
|
|
77
|
+
const value = params[key];
|
|
78
|
+
if (SENSITIVE_BODY_KEY.test(key)) {
|
|
79
|
+
out[key] = "[REDACTED]";
|
|
80
|
+
} else if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
81
|
+
out[key] = redactParams(value);
|
|
82
|
+
} else {
|
|
83
|
+
out[key] = value;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
function redactURL(url) {
|
|
89
|
+
if (!url) return url;
|
|
90
|
+
return url.replace(/^([a-z][a-z\d+\-.]*:\/\/)([^/]+)@/i, (match, protocol, userInfo) => {
|
|
91
|
+
const parts = userInfo.split(":");
|
|
92
|
+
if (parts.length > 1) {
|
|
93
|
+
return `${protocol}${parts[0]}:[REDACTED]@`;
|
|
94
|
+
}
|
|
95
|
+
return `${protocol}[REDACTED]@`;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
73
98
|
function redactConfig(config) {
|
|
74
99
|
if (!config) return config;
|
|
75
100
|
const clone = { ...config };
|
|
76
101
|
if ("auth" in clone) delete clone.auth;
|
|
77
102
|
if (clone.headers) clone.headers = redactHeaders(clone.headers);
|
|
103
|
+
if (clone.params) clone.params = redactParams(clone.params);
|
|
104
|
+
if (clone.url) clone.url = redactURL(clone.url);
|
|
105
|
+
if (clone._builtUrl) clone._builtUrl = redactURL(clone._builtUrl);
|
|
78
106
|
return clone;
|
|
79
107
|
}
|
|
80
108
|
class AccessioError extends Error {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/accessioError.ts"],"sourcesContent":["import ErrorCodes from '../constants/errorCodes';\nimport type { AccessioRequestConfig, AccessioResponse } from '../types';\n\nfunction redactHeaders(headers: unknown): unknown {\n if (!headers || typeof headers !== 'object') return headers;\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(headers as Record<string, unknown>)) {\n const value = (headers as Record<string, unknown>)[key];\n if (/^authorization$/i.test(key) || /^cookie$/i.test(key) || /^set-cookie$/i.test(key)) {\n out[key] = '[REDACTED]';\n } else if (value && typeof value === 'object' && !Array.isArray(value)) {\n out[key] = redactHeaders(value);\n } else {\n out[key] = value;\n }\n }\n return out;\n}\n\nconst SENSITIVE_BODY_KEY =\n /^(password|passwd|pwd|token|access_token|refresh_token|id_token|authorization|api[_-]?key|secret|client[_-]?secret|cookie|set[_-]?cookie|private[_-]?key|session)$/i;\n\nexport function redactBody(value: unknown, seen?: WeakSet<object>): unknown {\n if (value === null || typeof value !== 'object') return value;\n const visited = seen ?? new WeakSet<object>();\n if (visited.has(value as object)) return '[Circular]';\n visited.add(value as object);\n\n if (Array.isArray(value)) {\n return value.map((item) => redactBody(item, visited));\n }\n\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>)) {\n const v = (value as Record<string, unknown>)[key];\n if (SENSITIVE_BODY_KEY.test(key)) {\n out[key] = '[REDACTED]';\n } else {\n out[key] = redactBody(v, visited);\n }\n }\n return out;\n}\n\nexport function redactConfig(config: AccessioRequestConfig | null): AccessioRequestConfig | null {\n if (!config) return config;\n const clone = { ...config } as AccessioRequestConfig & { auth?: unknown };\n if ('auth' in clone) delete clone.auth;\n if (clone.headers) clone.headers = redactHeaders(clone.headers) as typeof clone.headers;\n return clone;\n}\n\nexport class AccessioError extends Error {\n static ERR_BAD_OPTION_VALUE: string = ErrorCodes.ERR_BAD_OPTION_VALUE;\n static ERR_BAD_OPTION: string = ErrorCodes.ERR_BAD_OPTION;\n static ECONNABORTED: string = ErrorCodes.ECONNABORTED;\n static ETIMEDOUT: string = ErrorCodes.ETIMEDOUT;\n static ERR_NETWORK: string = ErrorCodes.ERR_NETWORK;\n static ERR_FR_TOO_MANY_REDIRECTS: string = ErrorCodes.ERR_FR_TOO_MANY_REDIRECTS;\n static ERR_BAD_RESPONSE: string = ErrorCodes.ERR_BAD_RESPONSE;\n static ERR_BAD_REQUEST: string = ErrorCodes.ERR_BAD_REQUEST;\n static ERR_CANCELED: string = ErrorCodes.ERR_CANCELED;\n static ERR_NOT_SUPPORT: string = ErrorCodes.ERR_NOT_SUPPORT;\n static ERR_INVALID_URL: string = ErrorCodes.ERR_INVALID_URL;\n\n readonly code: string | null;\n readonly config: AccessioRequestConfig | null;\n readonly request: unknown;\n readonly response: AccessioResponse | null;\n readonly isAccessioError: true;\n cause?: Error;\n override name = 'AccessioError' as const;\n\n constructor(\n message: string,\n code: string | null,\n config: AccessioRequestConfig | null,\n request: unknown,\n response: AccessioResponse | null,\n ) {\n super(message);\n this.name = 'AccessioError';\n this.code = code ?? null;\n this.config = redactConfig(config ?? null);\n this.request = request ?? null;\n this.response = response ?? null;\n this.isAccessioError = true;\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, AccessioError);\n }\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n status: this.response ? this.response.status : null,\n config: this.config,\n };\n }\n\n static from(\n error: Error,\n code: string,\n config: AccessioRequestConfig | null,\n request: unknown,\n response: AccessioResponse | null,\n ): AccessioError {\n const accessioError = new AccessioError(error.message, code, config, request, response);\n accessioError.cause = error;\n accessioError.stack = error.stack;\n return accessioError;\n }\n}\n\nexport default AccessioError;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAAuB;AAGvB,SAAS,cAAc,SAA2B;AAChD,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAM,MAA+B,CAAC;AACtC,aAAW,OAAO,OAAO,KAAK,OAAkC,GAAG;AACjE,UAAM,QAAS,QAAoC,GAAG;AACtD,QAAI,mBAAmB,KAAK,GAAG,KAAK,YAAY,KAAK,GAAG,KAAK,gBAAgB,KAAK,GAAG,GAAG;AACtF,UAAI,GAAG,IAAI;AAAA,IACb,WAAW,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AACtE,UAAI,GAAG,IAAI,cAAc,KAAK;AAAA,IAChC,OAAO;AACL,UAAI,GAAG,IAAI;AAAA,IACb;AAAA,EACF;AACA,SAAO;AACT;AAEA,MAAM,qBACJ;AAEK,SAAS,WAAW,OAAgB,MAAiC;AAC1E,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;AACxD,QAAM,UAAU,QAAQ,oBAAI,QAAgB;AAC5C,MAAI,QAAQ,IAAI,KAAe,EAAG,QAAO;AACzC,UAAQ,IAAI,KAAe;AAE3B,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,CAAC,SAAS,WAAW,MAAM,OAAO,CAAC;AAAA,EACtD;AAEA,QAAM,MAA+B,CAAC;AACtC,aAAW,OAAO,OAAO,KAAK,KAAgC,GAAG;AAC/D,UAAM,IAAK,MAAkC,GAAG;AAChD,QAAI,mBAAmB,KAAK,GAAG,GAAG;AAChC,UAAI,GAAG,IAAI;AAAA,IACb,OAAO;AACL,UAAI,GAAG,IAAI,WAAW,GAAG,OAAO;AAAA,IAClC;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,aAAa,QAAoE;AAC/F,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,QAAQ,EAAE,GAAG,OAAO;AAC1B,MAAI,UAAU,MAAO,QAAO,MAAM;AAClC,MAAI,MAAM,QAAS,OAAM,UAAU,cAAc,MAAM,OAAO;AAC9D,SAAO;AACT;AAEO,MAAM,sBAAsB,MAAM;AAAA,EACvC,OAAO,uBAA+B,kBAAAA,QAAW;AAAA,EACjD,OAAO,iBAAyB,kBAAAA,QAAW;AAAA,EAC3C,OAAO,eAAuB,kBAAAA,QAAW;AAAA,EACzC,OAAO,YAAoB,kBAAAA,QAAW;AAAA,EACtC,OAAO,cAAsB,kBAAAA,QAAW;AAAA,EACxC,OAAO,4BAAoC,kBAAAA,QAAW;AAAA,EACtD,OAAO,mBAA2B,kBAAAA,QAAW;AAAA,EAC7C,OAAO,kBAA0B,kBAAAA,QAAW;AAAA,EAC5C,OAAO,eAAuB,kBAAAA,QAAW;AAAA,EACzC,OAAO,kBAA0B,kBAAAA,QAAW;AAAA,EAC5C,OAAO,kBAA0B,kBAAAA,QAAW;AAAA,EAEnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT;AAAA,EACS,OAAO;AAAA,EAEhB,YACE,SACA,MACA,QACA,SACA,UACA;AACA,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO,QAAQ;AACpB,SAAK,SAAS,aAAa,UAAU,IAAI;AACzC,SAAK,UAAU,WAAW;AAC1B,SAAK,WAAW,YAAY;AAC5B,SAAK,kBAAkB;AAEvB,
|
|
1
|
+
{"version":3,"sources":["../../src/core/accessioError.ts"],"sourcesContent":["import ErrorCodes from '../constants/errorCodes';\nimport type { AccessioRequestConfig, AccessioResponse } from '../types';\n\nfunction redactHeaders(headers: unknown): unknown {\n if (!headers || typeof headers !== 'object') return headers;\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(headers as Record<string, unknown>)) {\n const value = (headers as Record<string, unknown>)[key];\n if (/^authorization$/i.test(key) || /^cookie$/i.test(key) || /^set-cookie$/i.test(key)) {\n out[key] = '[REDACTED]';\n } else if (value && typeof value === 'object' && !Array.isArray(value)) {\n out[key] = redactHeaders(value);\n } else {\n out[key] = value;\n }\n }\n return out;\n}\n\nconst SENSITIVE_BODY_KEY =\n /^(password|passwd|pwd|token|access_token|refresh_token|id_token|authorization|api[_-]?key|secret|client[_-]?secret|cookie|set[_-]?cookie|private[_-]?key|session)$/i;\n\nexport function redactBody(value: unknown, seen?: WeakSet<object>): unknown {\n if (value === null || typeof value !== 'object') return value;\n const visited = seen ?? new WeakSet<object>();\n if (visited.has(value as object)) return '[Circular]';\n visited.add(value as object);\n\n if (Array.isArray(value)) {\n return value.map((item) => redactBody(item, visited));\n }\n\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>)) {\n const v = (value as Record<string, unknown>)[key];\n if (SENSITIVE_BODY_KEY.test(key)) {\n out[key] = '[REDACTED]';\n } else {\n out[key] = redactBody(v, visited);\n }\n }\n return out;\n}\n\nfunction redactParams(params: unknown): unknown {\n if (!params || typeof params !== 'object') return params;\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(params as Record<string, unknown>)) {\n const value = (params as Record<string, unknown>)[key];\n if (SENSITIVE_BODY_KEY.test(key)) {\n out[key] = '[REDACTED]';\n } else if (value && typeof value === 'object' && !Array.isArray(value)) {\n out[key] = redactParams(value);\n } else {\n out[key] = value;\n }\n }\n return out;\n}\n\nfunction redactURL(url: string | undefined): string | undefined {\n if (!url) return url;\n // Match inline credentials: http://user:pass@host\n return url.replace(/^([a-z][a-z\\d+\\-.]*:\\/\\/)([^/]+)@/i, (match, protocol, userInfo) => {\n const parts = userInfo.split(':');\n if (parts.length > 1) {\n return `${protocol}${parts[0]}:[REDACTED]@`;\n }\n return `${protocol}[REDACTED]@`;\n });\n}\n\nexport function redactConfig(config: AccessioRequestConfig | null): AccessioRequestConfig | null {\n if (!config) return config;\n const clone = { ...config } as AccessioRequestConfig & { auth?: unknown };\n if ('auth' in clone) delete clone.auth;\n if (clone.headers) clone.headers = redactHeaders(clone.headers) as typeof clone.headers;\n if (clone.params) clone.params = redactParams(clone.params) as typeof clone.params;\n if (clone.url) clone.url = redactURL(clone.url);\n if (clone._builtUrl) clone._builtUrl = redactURL(clone._builtUrl);\n return clone;\n}\n\nexport class AccessioError extends Error {\n static ERR_BAD_OPTION_VALUE: string = ErrorCodes.ERR_BAD_OPTION_VALUE;\n static ERR_BAD_OPTION: string = ErrorCodes.ERR_BAD_OPTION;\n static ECONNABORTED: string = ErrorCodes.ECONNABORTED;\n static ETIMEDOUT: string = ErrorCodes.ETIMEDOUT;\n static ERR_NETWORK: string = ErrorCodes.ERR_NETWORK;\n static ERR_FR_TOO_MANY_REDIRECTS: string = ErrorCodes.ERR_FR_TOO_MANY_REDIRECTS;\n static ERR_BAD_RESPONSE: string = ErrorCodes.ERR_BAD_RESPONSE;\n static ERR_BAD_REQUEST: string = ErrorCodes.ERR_BAD_REQUEST;\n static ERR_CANCELED: string = ErrorCodes.ERR_CANCELED;\n static ERR_NOT_SUPPORT: string = ErrorCodes.ERR_NOT_SUPPORT;\n static ERR_INVALID_URL: string = ErrorCodes.ERR_INVALID_URL;\n\n readonly code: string | null;\n readonly config: AccessioRequestConfig | null;\n readonly request: unknown;\n readonly response: AccessioResponse | null;\n readonly isAccessioError: true;\n cause?: Error;\n override name = 'AccessioError' as const;\n\n constructor(\n message: string,\n code: string | null,\n config: AccessioRequestConfig | null,\n request: unknown,\n response: AccessioResponse | null,\n ) {\n super(message);\n this.name = 'AccessioError';\n this.code = code ?? null;\n this.config = redactConfig(config ?? null);\n this.request = request ?? null;\n this.response = response ?? null;\n this.isAccessioError = true;\n\n if ((Error as any).captureStackTrace) {\n (Error as any).captureStackTrace(this, AccessioError);\n }\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n status: this.response ? this.response.status : null,\n config: this.config,\n };\n }\n\n static from(\n error: Error,\n code: string,\n config: AccessioRequestConfig | null,\n request: unknown,\n response: AccessioResponse | null,\n ): AccessioError {\n const accessioError = new AccessioError(error.message, code, config, request, response);\n accessioError.cause = error;\n accessioError.stack = error.stack;\n return accessioError;\n }\n}\n\nexport default AccessioError;\n"],"mappings":"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","names":["ErrorCodes"]}
|
package/cjs/core/buildURL.cjs
CHANGED
|
@@ -84,15 +84,18 @@ function buildURL(url, baseURL, params, paramsSerializer) {
|
|
|
84
84
|
if (key === "__proto__" || key === "prototype" || key === "constructor") continue;
|
|
85
85
|
unusedParams[key] = params[key];
|
|
86
86
|
}
|
|
87
|
-
fullURL = fullURL.replace(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
fullURL = fullURL.replace(
|
|
88
|
+
/(?::([a-zA-Z_][a-zA-Z0-9_]*))|(?:{([a-zA-Z_][a-zA-Z0-9_]*)})/g,
|
|
89
|
+
(match, p1, p2) => {
|
|
90
|
+
const key = p1 || p2;
|
|
91
|
+
if (key && Object.prototype.hasOwnProperty.call(unusedParams, key) && unusedParams[key] !== void 0) {
|
|
92
|
+
const val = unusedParams[key];
|
|
93
|
+
delete unusedParams[key];
|
|
94
|
+
return encodeURIComponent(String(val));
|
|
95
|
+
}
|
|
96
|
+
return match;
|
|
93
97
|
}
|
|
94
|
-
|
|
95
|
-
});
|
|
98
|
+
);
|
|
96
99
|
finalParams = unusedParams;
|
|
97
100
|
}
|
|
98
101
|
const serialized = serializeParams(finalParams, paramsSerializer);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/buildURL.ts"],"sourcesContent":["import type { ParamsSerializer } from '../types';\n\nfunction serializeParams(\n params: Record<string, unknown>,\n paramsSerializer?: ParamsSerializer,\n): string {\n if (!params) return '';\n\n if (typeof paramsSerializer === 'function') {\n return paramsSerializer(params);\n }\n\n if (typeof URLSearchParams !== 'undefined' && params instanceof URLSearchParams) {\n return params.toString();\n }\n\n const parts: string[] = [];\n\n function encode(prefix: string, value: unknown): void {\n if (value === null || value === undefined) {\n return;\n }\n\n if (Array.isArray(value)) {\n value.forEach((item, index) => {\n if (typeof item === 'object' && item !== null) {\n encode(`${prefix}[${index}]`, item);\n } else {\n encode(prefix, item);\n }\n });\n } else if (typeof value === 'object' && !(value instanceof Date)) {\n Object.keys(value as Record<string, unknown>).forEach((key) => {\n encode(`${prefix}[${key}]`, (value as Record<string, unknown>)[key]);\n });\n } else {\n const encodedValue = value instanceof Date ? value.toISOString() : value;\n parts.push(`${encodeURIComponent(prefix)}=${encodeURIComponent(encodedValue as string)}`);\n }\n }\n\n Object.keys(params).forEach((key) => {\n encode(key, params[key]);\n });\n\n return parts.join('&');\n}\n\nfunction combineURLs(baseURL: string, relativeURL: string): string {\n if (!baseURL) return relativeURL || '';\n if (!relativeURL) return baseURL;\n\n let base = baseURL;\n while (base.endsWith('/')) {\n base = base.slice(0, -1);\n }\n\n let relative = relativeURL;\n while (relative.startsWith('/')) {\n relative = relative.slice(1);\n }\n\n return `${base}/${relative}`;\n}\n\nfunction isAbsoluteURL(url: string): boolean {\n return /^([a-z][a-z\\d+\\-.]*:)/i.test(url);\n}\n\nexport default function buildURL(\n url: string,\n baseURL?: string,\n params?: Record<string, unknown>,\n paramsSerializer?: ParamsSerializer,\n): string {\n let fullURL = baseURL && !isAbsoluteURL(url) ? combineURLs(baseURL, url) : url || '';\n\n let finalParams = params;\n if (params && typeof params === 'object' && !(params instanceof URLSearchParams)) {\n const unusedParams: Record<string, unknown> = {};\n for (const key of Object.keys(params)) {\n if (key === '__proto__' || key === 'prototype' || key === 'constructor') continue;\n unusedParams[key] = (params as Record<string, unknown>)[key];\n }\n fullURL = fullURL.replace(/(?::([a-zA-Z0-9_]
|
|
1
|
+
{"version":3,"sources":["../../src/core/buildURL.ts"],"sourcesContent":["import type { ParamsSerializer } from '../types';\n\nfunction serializeParams(\n params: Record<string, unknown>,\n paramsSerializer?: ParamsSerializer,\n): string {\n if (!params) return '';\n\n if (typeof paramsSerializer === 'function') {\n return paramsSerializer(params);\n }\n\n if (typeof URLSearchParams !== 'undefined' && params instanceof URLSearchParams) {\n return params.toString();\n }\n\n const parts: string[] = [];\n\n function encode(prefix: string, value: unknown): void {\n if (value === null || value === undefined) {\n return;\n }\n\n if (Array.isArray(value)) {\n value.forEach((item, index) => {\n if (typeof item === 'object' && item !== null) {\n encode(`${prefix}[${index}]`, item);\n } else {\n encode(prefix, item);\n }\n });\n } else if (typeof value === 'object' && !(value instanceof Date)) {\n Object.keys(value as Record<string, unknown>).forEach((key) => {\n encode(`${prefix}[${key}]`, (value as Record<string, unknown>)[key]);\n });\n } else {\n const encodedValue = value instanceof Date ? value.toISOString() : value;\n parts.push(`${encodeURIComponent(prefix)}=${encodeURIComponent(encodedValue as string)}`);\n }\n }\n\n Object.keys(params).forEach((key) => {\n encode(key, params[key]);\n });\n\n return parts.join('&');\n}\n\nfunction combineURLs(baseURL: string, relativeURL: string): string {\n if (!baseURL) return relativeURL || '';\n if (!relativeURL) return baseURL;\n\n let base = baseURL;\n while (base.endsWith('/')) {\n base = base.slice(0, -1);\n }\n\n let relative = relativeURL;\n while (relative.startsWith('/')) {\n relative = relative.slice(1);\n }\n\n return `${base}/${relative}`;\n}\n\nfunction isAbsoluteURL(url: string): boolean {\n return /^([a-z][a-z\\d+\\-.]*:)/i.test(url);\n}\n\nexport default function buildURL(\n url: string,\n baseURL?: string,\n params?: Record<string, unknown>,\n paramsSerializer?: ParamsSerializer,\n): string {\n let fullURL = baseURL && !isAbsoluteURL(url) ? combineURLs(baseURL, url) : url || '';\n\n let finalParams = params;\n if (params && typeof params === 'object' && !(params instanceof URLSearchParams)) {\n const unusedParams: Record<string, unknown> = {};\n for (const key of Object.keys(params)) {\n if (key === '__proto__' || key === 'prototype' || key === 'constructor') continue;\n unusedParams[key] = (params as Record<string, unknown>)[key];\n }\n fullURL = fullURL.replace(\n /(?::([a-zA-Z_][a-zA-Z0-9_]*))|(?:{([a-zA-Z_][a-zA-Z0-9_]*)})/g,\n (match, p1, p2) => {\n const key = p1 || p2;\n if (\n key &&\n Object.prototype.hasOwnProperty.call(unusedParams, key) &&\n unusedParams[key] !== undefined\n ) {\n const val = unusedParams[key];\n delete unusedParams[key];\n return encodeURIComponent(String(val));\n }\n return match;\n },\n );\n finalParams = unusedParams;\n }\n\n const serialized = serializeParams(finalParams as Record<string, unknown>, paramsSerializer);\n if (serialized) {\n const hashIndex = fullURL.indexOf('#');\n let fragment = '';\n if (hashIndex !== -1) {\n fragment = fullURL.slice(hashIndex);\n fullURL = fullURL.slice(0, hashIndex);\n }\n fullURL += (fullURL.indexOf('?') === -1 ? '?' : '&') + serialized + fragment;\n }\n\n return fullURL;\n}\n\nexport { serializeParams, combineURLs, isAbsoluteURL };\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,SAAS,gBACP,QACA,kBACQ;AACR,MAAI,CAAC,OAAQ,QAAO;AAEpB,MAAI,OAAO,qBAAqB,YAAY;AAC1C,WAAO,iBAAiB,MAAM;AAAA,EAChC;AAEA,MAAI,OAAO,oBAAoB,eAAe,kBAAkB,iBAAiB;AAC/E,WAAO,OAAO,SAAS;AAAA,EACzB;AAEA,QAAM,QAAkB,CAAC;AAEzB,WAAS,OAAO,QAAgB,OAAsB;AACpD,QAAI,UAAU,QAAQ,UAAU,QAAW;AACzC;AAAA,IACF;AAEA,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,YAAM,QAAQ,CAAC,MAAM,UAAU;AAC7B,YAAI,OAAO,SAAS,YAAY,SAAS,MAAM;AAC7C,iBAAO,GAAG,MAAM,IAAI,KAAK,KAAK,IAAI;AAAA,QACpC,OAAO;AACL,iBAAO,QAAQ,IAAI;AAAA,QACrB;AAAA,MACF,CAAC;AAAA,IACH,WAAW,OAAO,UAAU,YAAY,EAAE,iBAAiB,OAAO;AAChE,aAAO,KAAK,KAAgC,EAAE,QAAQ,CAAC,QAAQ;AAC7D,eAAO,GAAG,MAAM,IAAI,GAAG,KAAM,MAAkC,GAAG,CAAC;AAAA,MACrE,CAAC;AAAA,IACH,OAAO;AACL,YAAM,eAAe,iBAAiB,OAAO,MAAM,YAAY,IAAI;AACnE,YAAM,KAAK,GAAG,mBAAmB,MAAM,CAAC,IAAI,mBAAmB,YAAsB,CAAC,EAAE;AAAA,IAC1F;AAAA,EACF;AAEA,SAAO,KAAK,MAAM,EAAE,QAAQ,CAAC,QAAQ;AACnC,WAAO,KAAK,OAAO,GAAG,CAAC;AAAA,EACzB,CAAC;AAED,SAAO,MAAM,KAAK,GAAG;AACvB;AAEA,SAAS,YAAY,SAAiB,aAA6B;AACjE,MAAI,CAAC,QAAS,QAAO,eAAe;AACpC,MAAI,CAAC,YAAa,QAAO;AAEzB,MAAI,OAAO;AACX,SAAO,KAAK,SAAS,GAAG,GAAG;AACzB,WAAO,KAAK,MAAM,GAAG,EAAE;AAAA,EACzB;AAEA,MAAI,WAAW;AACf,SAAO,SAAS,WAAW,GAAG,GAAG;AAC/B,eAAW,SAAS,MAAM,CAAC;AAAA,EAC7B;AAEA,SAAO,GAAG,IAAI,IAAI,QAAQ;AAC5B;AAEA,SAAS,cAAc,KAAsB;AAC3C,SAAO,yBAAyB,KAAK,GAAG;AAC1C;AAEe,SAAR,SACL,KACA,SACA,QACA,kBACQ;AACR,MAAI,UAAU,WAAW,CAAC,cAAc,GAAG,IAAI,YAAY,SAAS,GAAG,IAAI,OAAO;AAElF,MAAI,cAAc;AAClB,MAAI,UAAU,OAAO,WAAW,YAAY,EAAE,kBAAkB,kBAAkB;AAChF,UAAM,eAAwC,CAAC;AAC/C,eAAW,OAAO,OAAO,KAAK,MAAM,GAAG;AACrC,UAAI,QAAQ,eAAe,QAAQ,eAAe,QAAQ,cAAe;AACzE,mBAAa,GAAG,IAAK,OAAmC,GAAG;AAAA,IAC7D;AACA,cAAU,QAAQ;AAAA,MAChB;AAAA,MACA,CAAC,OAAO,IAAI,OAAO;AACjB,cAAM,MAAM,MAAM;AAClB,YACE,OACA,OAAO,UAAU,eAAe,KAAK,cAAc,GAAG,KACtD,aAAa,GAAG,MAAM,QACtB;AACA,gBAAM,MAAM,aAAa,GAAG;AAC5B,iBAAO,aAAa,GAAG;AACvB,iBAAO,mBAAmB,OAAO,GAAG,CAAC;AAAA,QACvC;AACA,eAAO;AAAA,MACT;AAAA,IACF;AACA,kBAAc;AAAA,EAChB;AAEA,QAAM,aAAa,gBAAgB,aAAwC,gBAAgB;AAC3F,MAAI,YAAY;AACd,UAAM,YAAY,QAAQ,QAAQ,GAAG;AACrC,QAAI,WAAW;AACf,QAAI,cAAc,IAAI;AACpB,iBAAW,QAAQ,MAAM,SAAS;AAClC,gBAAU,QAAQ,MAAM,GAAG,SAAS;AAAA,IACtC;AACA,gBAAY,QAAQ,QAAQ,GAAG,MAAM,KAAK,MAAM,OAAO,aAAa;AAAA,EACtE;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/fetchAdapter.ts"],"sourcesContent":["import AccessioError from './accessioError';\nimport parseHeaders from '../helpers/parseHeaders';\nimport type { AccessioRequestConfig, AccessioResponse } from '../types';\n\nasync function readResponseData(\n fetchResponse: Response,\n config: AccessioRequestConfig,\n): Promise<unknown> {\n const responseType = config.responseType || 'json';\n switch (responseType) {\n case 'arraybuffer':\n return await fetchResponse.arrayBuffer();\n case 'blob':\n return await fetchResponse.blob();\n case 'stream':\n return fetchResponse.body;\n case 'json':\n default: {\n const contentType = fetchResponse.headers.get('content-type') || '';\n if (contentType.includes('application/json')) {\n const text = await fetchResponse.text();\n if (!text) return '';\n try {\n return JSON.parse(text);\n } catch (err) {\n throw new AccessioError(\n `Failed to parse JSON response: ${(err as Error).message}. Raw body: ${\n text.length > 500 ? `${text.slice(0, 500)}…` : text\n }`,\n AccessioError.ERR_BAD_RESPONSE,\n config,\n fetchResponse,\n null,\n );\n }\n }\n return await fetchResponse.text();\n }\n }\n}\n\nfunction assertValidURL(fullURL: string, config: AccessioRequestConfig): void {\n if (!fullURL || !/^[a-z][a-z\\d+\\-.]*:/i.test(fullURL)) return;\n try {\n new URL(fullURL);\n } catch {\n throw new AccessioError(\n `Invalid URL: ${fullURL}`,\n AccessioError.ERR_INVALID_URL,\n config,\n null,\n null,\n );\n }\n}\n\ninterface AbortWiring {\n isTimedOut: () => boolean;\n cleanup: () => void;\n}\n\nfunction setupAbort(config: AccessioRequestConfig, fetchOptions: RequestInit): AbortWiring {\n if (\n config.timeout !== undefined &&\n (typeof config.timeout !== 'number' || isNaN(config.timeout) || config.timeout < 0)\n ) {\n throw new AccessioError(\n `Invalid timeout value: ${config.timeout}`,\n AccessioError.ERR_BAD_OPTION_VALUE,\n config,\n null,\n null,\n );\n }\n\n const timeoutValue = Number(config.timeout);\n const hasTimeout = !isNaN(timeoutValue) && timeoutValue > 0;\n\n if (!hasTimeout) {\n if (config.signal) fetchOptions.signal = config.signal;\n return { isTimedOut: () => false, cleanup: () => {} };\n }\n\n let timedOut = false;\n const abortController = new AbortController();\n const timeoutId = setTimeout(() => {\n timedOut = true;\n abortController.abort(\n new AccessioError(\n `timeout of ${timeoutValue}ms exceeded`,\n AccessioError.ETIMEDOUT,\n config,\n null,\n null,\n ),\n );\n }, timeoutValue);\n\n let onUserAbort: (() => void) | null = null;\n\n if (config.signal) {\n if (typeof AbortSignal.any === 'function') {\n fetchOptions.signal = AbortSignal.any([config.signal, abortController.signal]);\n } else {\n if (config.signal.aborted) {\n abortController.abort(config.signal.reason);\n } else {\n onUserAbort = () => {\n if (!timedOut) abortController.abort(config.signal!.reason);\n };\n config.signal.addEventListener('abort', onUserAbort, { once: true });\n }\n fetchOptions.signal = abortController.signal;\n }\n } else {\n fetchOptions.signal = abortController.signal;\n }\n\n return {\n isTimedOut: () => timedOut,\n cleanup: () => {\n clearTimeout(timeoutId);\n if (onUserAbort && config.signal) {\n config.signal.removeEventListener('abort', onUserAbort);\n }\n },\n };\n}\n\nfunction wrapDownloadProgress(fetchResponse: Response, config: AccessioRequestConfig): Response {\n if (!config.onDownloadProgress || !fetchResponse.body || config.responseType === 'stream') {\n return fetchResponse;\n }\n\n const contentLength = fetchResponse.headers.get('content-length');\n const total = contentLength ? parseInt(contentLength, 10) : 0;\n let loaded = 0;\n\n const reader = fetchResponse.body.getReader();\n const stream = new ReadableStream({\n async start(controller) {\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n controller.close();\n break;\n }\n loaded += value.byteLength;\n config.onDownloadProgress!({ loaded, total });\n controller.enqueue(value);\n }\n } catch (e) {\n controller.error(e);\n }\n },\n });\n\n return new Response(stream, {\n headers: fetchResponse.headers,\n status: fetchResponse.status,\n statusText: fetchResponse.statusText,\n });\n}\n\nfunction classifyFetchError(\n error: unknown,\n config: AccessioRequestConfig,\n isTimedOut: boolean,\n): AccessioError {\n if (error instanceof AccessioError) return error;\n\n if (isTimedOut) {\n return new AccessioError(\n `timeout of ${config.timeout}ms exceeded`,\n AccessioError.ETIMEDOUT,\n config,\n null,\n null,\n );\n }\n\n const isAbort =\n (error instanceof Error && error.name === 'AbortError') || !!config.signal?.aborted;\n if (isAbort) {\n return new AccessioError('Request aborted', AccessioError.ERR_CANCELED, config, null, null);\n }\n\n return AccessioError.from(\n error instanceof Error ? error : new Error(String(error)),\n AccessioError.ERR_NETWORK,\n config,\n null,\n null,\n );\n}\n\nexport default async function fetchAdapter(\n config: AccessioRequestConfig,\n fullURL: string,\n fetchOptions: RequestInit,\n requestStartTime: number,\n): Promise<AccessioResponse> {\n assertValidURL(fullURL, config);\n\n const abort = setupAbort(config, fetchOptions);\n\n try {\n const fetchImpl = config.fetch || fetch;\n const rawResponse = await fetchImpl(fullURL, fetchOptions);\n const fetchResponse = wrapDownloadProgress(rawResponse, config);\n\n const contentLength = fetchResponse.headers.get('content-length');\n if (\n contentLength &&\n config.maxContentLength &&\n parseInt(contentLength, 10) > config.maxContentLength\n ) {\n throw new AccessioError(\n `maxContentLength size of ${config.maxContentLength} exceeded`,\n AccessioError.ERR_BAD_RESPONSE,\n config,\n fetchResponse,\n null,\n );\n }\n\n let responseData: unknown;\n try {\n responseData = await readResponseData(fetchResponse, config);\n if (config.schema) {\n if (typeof config.schema.parseAsync === 'function') {\n responseData = await config.schema.parseAsync(responseData);\n } else {\n responseData = config.schema.parse(responseData);\n }\n }\n } catch (readError) {\n if (readError instanceof AccessioError) throw readError;\n throw AccessioError.from(\n readError as Error,\n AccessioError.ERR_BAD_RESPONSE,\n config,\n fetchResponse,\n null,\n );\n }\n\n return {\n data: responseData,\n status: fetchResponse.status,\n statusText: fetchResponse.statusText,\n headers: parseHeaders(fetchResponse.headers),\n config,\n request: fetchResponse,\n duration: Date.now() - requestStartTime,\n };\n } catch (error) {\n throw classifyFetchError(error, config, abort.isTimedOut());\n } finally {\n abort.cleanup();\n }\n}\n"],"mappings":"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","names":["AccessioError","parseHeaders"]}
|
|
1
|
+
{"version":3,"sources":["../../src/core/fetchAdapter.ts"],"sourcesContent":["import AccessioError from './accessioError';\nimport parseHeaders from '../helpers/parseHeaders';\nimport type { AccessioRequestConfig, AccessioResponse } from '../types';\n\nasync function readResponseData(\n fetchResponse: Response,\n config: AccessioRequestConfig,\n): Promise<unknown> {\n const responseType = config.responseType || 'json';\n switch (responseType) {\n case 'arraybuffer':\n return await fetchResponse.arrayBuffer();\n case 'blob':\n return await fetchResponse.blob();\n case 'stream':\n return fetchResponse.body;\n case 'json':\n default: {\n const contentType = fetchResponse.headers.get('content-type') || '';\n if (contentType.includes('application/json')) {\n const text = await fetchResponse.text();\n if (!text) return '';\n try {\n return JSON.parse(text);\n } catch (err) {\n throw new AccessioError(\n `Failed to parse JSON response: ${(err as Error).message}. Raw body: ${\n text.length > 500 ? `${text.slice(0, 500)}…` : text\n }`,\n AccessioError.ERR_BAD_RESPONSE,\n config,\n fetchResponse,\n null,\n );\n }\n }\n return await fetchResponse.text();\n }\n }\n}\n\nfunction assertValidURL(fullURL: string, config: AccessioRequestConfig): void {\n if (!fullURL || !/^[a-z][a-z\\d+\\-.]*:/i.test(fullURL)) return;\n try {\n new URL(fullURL);\n } catch {\n throw new AccessioError(\n `Invalid URL: ${fullURL}`,\n AccessioError.ERR_INVALID_URL,\n config,\n null,\n null,\n );\n }\n}\n\ninterface AbortWiring {\n isTimedOut: () => boolean;\n cleanup: () => void;\n}\n\nfunction setupAbort(config: AccessioRequestConfig, fetchOptions: RequestInit): AbortWiring {\n if (\n config.timeout !== undefined &&\n (typeof config.timeout !== 'number' || isNaN(config.timeout) || config.timeout < 0)\n ) {\n throw new AccessioError(\n `Invalid timeout value: ${config.timeout}`,\n AccessioError.ERR_BAD_OPTION_VALUE,\n config,\n null,\n null,\n );\n }\n\n const timeoutValue = Number(config.timeout);\n const hasTimeout = !isNaN(timeoutValue) && timeoutValue > 0;\n\n if (!hasTimeout) {\n if (config.signal) fetchOptions.signal = config.signal;\n return { isTimedOut: () => false, cleanup: () => {} };\n }\n\n let timedOut = false;\n const abortController = new AbortController();\n const timeoutId = setTimeout(() => {\n timedOut = true;\n abortController.abort(\n new AccessioError(\n `timeout of ${timeoutValue}ms exceeded`,\n AccessioError.ETIMEDOUT,\n config,\n null,\n null,\n ),\n );\n }, timeoutValue);\n\n let onUserAbort: (() => void) | null = null;\n\n if (config.signal) {\n if (typeof AbortSignal.any === 'function') {\n fetchOptions.signal = AbortSignal.any([config.signal, abortController.signal]);\n } else {\n if (config.signal.aborted) {\n abortController.abort(config.signal.reason);\n } else {\n onUserAbort = () => {\n if (!timedOut) abortController.abort(config.signal!.reason);\n };\n config.signal.addEventListener('abort', onUserAbort, { once: true });\n }\n fetchOptions.signal = abortController.signal;\n }\n } else {\n fetchOptions.signal = abortController.signal;\n }\n\n return {\n isTimedOut: () => timedOut,\n cleanup: () => {\n clearTimeout(timeoutId);\n if (onUserAbort && config.signal) {\n config.signal.removeEventListener('abort', onUserAbort);\n }\n },\n };\n}\n\nfunction wrapDownloadProgress(fetchResponse: Response, config: AccessioRequestConfig): Response {\n if (!config.onDownloadProgress || !fetchResponse.body || config.responseType === 'stream') {\n return fetchResponse;\n }\n\n const contentLength = fetchResponse.headers.get('content-length');\n const total = contentLength ? parseInt(contentLength, 10) : 0;\n let loaded = 0;\n\n const reader = fetchResponse.body.getReader();\n const stream = new ReadableStream({\n async start(controller) {\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n controller.close();\n break;\n }\n loaded += value.byteLength;\n config.onDownloadProgress!({ loaded, total });\n controller.enqueue(value);\n }\n } catch (e) {\n controller.error(e);\n }\n },\n cancel(reason) {\n reader.cancel(reason).catch(() => {});\n },\n });\n\n return new Response(stream, {\n headers: fetchResponse.headers,\n status: fetchResponse.status,\n statusText: fetchResponse.statusText,\n });\n}\n\nfunction classifyFetchError(\n error: unknown,\n config: AccessioRequestConfig,\n isTimedOut: boolean,\n): AccessioError {\n if (error instanceof AccessioError) return error;\n\n if (isTimedOut) {\n return new AccessioError(\n `timeout of ${config.timeout}ms exceeded`,\n AccessioError.ETIMEDOUT,\n config,\n null,\n null,\n );\n }\n\n const isAbort =\n (error instanceof Error && error.name === 'AbortError') || !!config.signal?.aborted;\n if (isAbort) {\n return new AccessioError('Request aborted', AccessioError.ERR_CANCELED, config, null, null);\n }\n\n return AccessioError.from(\n error instanceof Error ? error : new Error(String(error)),\n AccessioError.ERR_NETWORK,\n config,\n null,\n null,\n );\n}\n\nexport default async function fetchAdapter(\n config: AccessioRequestConfig,\n fullURL: string,\n fetchOptions: RequestInit,\n requestStartTime: number,\n): Promise<AccessioResponse> {\n assertValidURL(fullURL, config);\n\n const abort = setupAbort(config, fetchOptions);\n\n try {\n const fetchImpl = config.fetch || fetch;\n const rawResponse = await fetchImpl(fullURL, fetchOptions);\n const fetchResponse = wrapDownloadProgress(rawResponse, config);\n\n const contentLength = fetchResponse.headers.get('content-length');\n if (\n contentLength &&\n config.maxContentLength &&\n parseInt(contentLength, 10) > config.maxContentLength\n ) {\n throw new AccessioError(\n `maxContentLength size of ${config.maxContentLength} exceeded`,\n AccessioError.ERR_BAD_RESPONSE,\n config,\n fetchResponse,\n null,\n );\n }\n\n let responseData: unknown;\n try {\n responseData = await readResponseData(fetchResponse, config);\n if (config.schema) {\n if (typeof config.schema.parseAsync === 'function') {\n responseData = await config.schema.parseAsync(responseData);\n } else {\n responseData = config.schema.parse(responseData);\n }\n }\n } catch (readError) {\n if (readError instanceof AccessioError) throw readError;\n throw AccessioError.from(\n readError as Error,\n AccessioError.ERR_BAD_RESPONSE,\n config,\n fetchResponse,\n null,\n );\n }\n\n return {\n data: responseData,\n status: fetchResponse.status,\n statusText: fetchResponse.statusText,\n headers: parseHeaders(fetchResponse.headers),\n config,\n request: fetchResponse,\n duration: Date.now() - requestStartTime,\n };\n } catch (error) {\n throw classifyFetchError(error, config, abort.isTimedOut());\n } finally {\n abort.cleanup();\n }\n}\n"],"mappings":"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","names":["AccessioError","parseHeaders"]}
|
package/cjs/core/mergeConfig.cjs
CHANGED
|
@@ -45,7 +45,7 @@ function deepMerge(...sources) {
|
|
|
45
45
|
return result;
|
|
46
46
|
}
|
|
47
47
|
const requestOnlyKeys = /* @__PURE__ */ new Set(["url", "data", "signal"]);
|
|
48
|
-
const deepMergeKeys = /* @__PURE__ */ new Set(["headers"]);
|
|
48
|
+
const deepMergeKeys = /* @__PURE__ */ new Set(["headers", "params", "hooks"]);
|
|
49
49
|
function mergeConfig(config1 = {}, config2 = {}) {
|
|
50
50
|
const merged = /* @__PURE__ */ Object.create(null);
|
|
51
51
|
const allKeys = /* @__PURE__ */ new Set([...Object.keys(config1), ...Object.keys(config2)]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/mergeConfig.ts"],"sourcesContent":["import type { AccessioRequestConfig } from '../types';\n\nfunction deepMerge(...sources: any[]): Record<string, any> {\n const result: Record<string, any> = Object.create(null);\n\n for (const source of sources) {\n if (!source || typeof source !== 'object') continue;\n\n for (const key of Object.keys(source)) {\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue;\n\n const value = source[key];\n\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n if (\n value instanceof Date ||\n value instanceof RegExp ||\n value instanceof Map ||\n value instanceof Set ||\n value instanceof Error ||\n (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) ||\n (typeof Blob !== 'undefined' && value instanceof Blob)\n ) {\n result[key] = value;\n } else if (result[key] && typeof result[key] === 'object' && !Array.isArray(result[key])) {\n result[key] = deepMerge(result[key], value);\n } else {\n result[key] = deepMerge(value);\n }\n } else if (value !== undefined) {\n result[key] = value;\n }\n }\n }\n\n return result;\n}\n\nconst requestOnlyKeys = new Set<string>(['url', 'data', 'signal']);\nconst deepMergeKeys = new Set<string>(['headers']);\n\nexport default function mergeConfig(\n config1: AccessioRequestConfig = {},\n config2: AccessioRequestConfig = {},\n): AccessioRequestConfig {\n const merged: any = Object.create(null);\n\n const allKeys = new Set<string>([...Object.keys(config1), ...Object.keys(config2)]);\n\n for (const key of allKeys) {\n const val1 = config1[key as keyof AccessioRequestConfig];\n const val2 = config2[key as keyof AccessioRequestConfig];\n\n if (requestOnlyKeys.has(key)) {\n if (val2 !== undefined) {\n merged[key] = val2;\n }\n } else if (deepMergeKeys.has(key)) {\n merged[key] = deepMerge(val1 || {}, val2 || {});\n } else {\n merged[key] = val2 !== undefined ? val2 : val1;\n }\n }\n\n return merged;\n}\n\nexport { deepMerge };\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,SAAS,aAAa,SAAqC;AACzD,QAAM,SAA8B,uBAAO,OAAO,IAAI;AAEtD,aAAW,UAAU,SAAS;AAC5B,QAAI,CAAC,UAAU,OAAO,WAAW,SAAU;AAE3C,eAAW,OAAO,OAAO,KAAK,MAAM,GAAG;AACrC,UAAI,QAAQ,eAAe,QAAQ,iBAAiB,QAAQ,YAAa;AAEzE,YAAM,QAAQ,OAAO,GAAG;AAExB,UAAI,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AAC/D,YACE,iBAAiB,QACjB,iBAAiB,UACjB,iBAAiB,OACjB,iBAAiB,OACjB,iBAAiB,SAChB,OAAO,gBAAgB,eAAe,iBAAiB,eACvD,OAAO,SAAS,eAAe,iBAAiB,MACjD;AACA,iBAAO,GAAG,IAAI;AAAA,QAChB,WAAW,OAAO,GAAG,KAAK,OAAO,OAAO,GAAG,MAAM,YAAY,CAAC,MAAM,QAAQ,OAAO,GAAG,CAAC,GAAG;AACxF,iBAAO,GAAG,IAAI,UAAU,OAAO,GAAG,GAAG,KAAK;AAAA,QAC5C,OAAO;AACL,iBAAO,GAAG,IAAI,UAAU,KAAK;AAAA,QAC/B;AAAA,MACF,WAAW,UAAU,QAAW;AAC9B,eAAO,GAAG,IAAI;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,MAAM,kBAAkB,oBAAI,IAAY,CAAC,OAAO,QAAQ,QAAQ,CAAC;AACjE,MAAM,gBAAgB,oBAAI,IAAY,CAAC,
|
|
1
|
+
{"version":3,"sources":["../../src/core/mergeConfig.ts"],"sourcesContent":["import type { AccessioRequestConfig } from '../types';\n\nfunction deepMerge(...sources: any[]): Record<string, any> {\n const result: Record<string, any> = Object.create(null);\n\n for (const source of sources) {\n if (!source || typeof source !== 'object') continue;\n\n for (const key of Object.keys(source)) {\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue;\n\n const value = source[key];\n\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n if (\n value instanceof Date ||\n value instanceof RegExp ||\n value instanceof Map ||\n value instanceof Set ||\n value instanceof Error ||\n (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) ||\n (typeof Blob !== 'undefined' && value instanceof Blob)\n ) {\n result[key] = value;\n } else if (result[key] && typeof result[key] === 'object' && !Array.isArray(result[key])) {\n result[key] = deepMerge(result[key], value);\n } else {\n result[key] = deepMerge(value);\n }\n } else if (value !== undefined) {\n result[key] = value;\n }\n }\n }\n\n return result;\n}\n\nconst requestOnlyKeys = new Set<string>(['url', 'data', 'signal']);\nconst deepMergeKeys = new Set<string>(['headers', 'params', 'hooks']);\n\nexport default function mergeConfig(\n config1: AccessioRequestConfig = {},\n config2: AccessioRequestConfig = {},\n): AccessioRequestConfig {\n const merged: any = Object.create(null);\n\n const allKeys = new Set<string>([...Object.keys(config1), ...Object.keys(config2)]);\n\n for (const key of allKeys) {\n const val1 = config1[key as keyof AccessioRequestConfig];\n const val2 = config2[key as keyof AccessioRequestConfig];\n\n if (requestOnlyKeys.has(key)) {\n if (val2 !== undefined) {\n merged[key] = val2;\n }\n } else if (deepMergeKeys.has(key)) {\n merged[key] = deepMerge(val1 || {}, val2 || {});\n } else {\n merged[key] = val2 !== undefined ? val2 : val1;\n }\n }\n\n return merged;\n}\n\nexport { deepMerge };\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,SAAS,aAAa,SAAqC;AACzD,QAAM,SAA8B,uBAAO,OAAO,IAAI;AAEtD,aAAW,UAAU,SAAS;AAC5B,QAAI,CAAC,UAAU,OAAO,WAAW,SAAU;AAE3C,eAAW,OAAO,OAAO,KAAK,MAAM,GAAG;AACrC,UAAI,QAAQ,eAAe,QAAQ,iBAAiB,QAAQ,YAAa;AAEzE,YAAM,QAAQ,OAAO,GAAG;AAExB,UAAI,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AAC/D,YACE,iBAAiB,QACjB,iBAAiB,UACjB,iBAAiB,OACjB,iBAAiB,OACjB,iBAAiB,SAChB,OAAO,gBAAgB,eAAe,iBAAiB,eACvD,OAAO,SAAS,eAAe,iBAAiB,MACjD;AACA,iBAAO,GAAG,IAAI;AAAA,QAChB,WAAW,OAAO,GAAG,KAAK,OAAO,OAAO,GAAG,MAAM,YAAY,CAAC,MAAM,QAAQ,OAAO,GAAG,CAAC,GAAG;AACxF,iBAAO,GAAG,IAAI,UAAU,OAAO,GAAG,GAAG,KAAK;AAAA,QAC5C,OAAO;AACL,iBAAO,GAAG,IAAI,UAAU,KAAK;AAAA,QAC/B;AAAA,MACF,WAAW,UAAU,QAAW;AAC9B,eAAO,GAAG,IAAI;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,MAAM,kBAAkB,oBAAI,IAAY,CAAC,OAAO,QAAQ,QAAQ,CAAC;AACjE,MAAM,gBAAgB,oBAAI,IAAY,CAAC,WAAW,UAAU,OAAO,CAAC;AAErD,SAAR,YACL,UAAiC,CAAC,GAClC,UAAiC,CAAC,GACX;AACvB,QAAM,SAAc,uBAAO,OAAO,IAAI;AAEtC,QAAM,UAAU,oBAAI,IAAY,CAAC,GAAG,OAAO,KAAK,OAAO,GAAG,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC;AAElF,aAAW,OAAO,SAAS;AACzB,UAAM,OAAO,QAAQ,GAAkC;AACvD,UAAM,OAAO,QAAQ,GAAkC;AAEvD,QAAI,gBAAgB,IAAI,GAAG,GAAG;AAC5B,UAAI,SAAS,QAAW;AACtB,eAAO,GAAG,IAAI;AAAA,MAChB;AAAA,IACF,WAAW,cAAc,IAAI,GAAG,GAAG;AACjC,aAAO,GAAG,IAAI,UAAU,QAAQ,CAAC,GAAG,QAAQ,CAAC,CAAC;AAAA,IAChD,OAAO;AACL,aAAO,GAAG,IAAI,SAAS,SAAY,OAAO;AAAA,IAC5C;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
|
package/cjs/core/request.cjs
CHANGED
|
@@ -41,23 +41,22 @@ var import_flattenHeaders = require("../helpers/flattenHeaders");
|
|
|
41
41
|
var import_auth = require("../helpers/auth");
|
|
42
42
|
var import_fetchAdapter = __toESM(require("./fetchAdapter"), 1);
|
|
43
43
|
var import_memoryCache = require("../helpers/memoryCache");
|
|
44
|
-
function lookupHeader(headers, name) {
|
|
45
|
-
const target = name.toLowerCase();
|
|
46
|
-
for (const k of Object.keys(headers)) {
|
|
47
|
-
if (k.toLowerCase() === target) {
|
|
48
|
-
const v = headers[k];
|
|
49
|
-
return Array.isArray(v) ? v.join(",") : v ?? "";
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return "";
|
|
53
|
-
}
|
|
54
44
|
function buildCacheKey(config, fullURL, flatHeaders) {
|
|
45
|
+
if (typeof config.cacheKeySerializer === "function") {
|
|
46
|
+
return config.cacheKeySerializer(config, fullURL, flatHeaders);
|
|
47
|
+
}
|
|
55
48
|
const method = (config.method || "GET").toUpperCase();
|
|
56
|
-
const auth = lookupHeader(flatHeaders, "authorization");
|
|
57
|
-
const accept = lookupHeader(flatHeaders, "accept");
|
|
58
49
|
const withCreds = config.withCredentials ? "1" : "0";
|
|
59
50
|
const respType = config.responseType || "json";
|
|
60
|
-
|
|
51
|
+
const serializedHeaders = Object.keys(flatHeaders).sort().filter(
|
|
52
|
+
(k) => !["user-agent", "connection", "host", "content-length", "accept-encoding"].includes(
|
|
53
|
+
k.toLowerCase()
|
|
54
|
+
)
|
|
55
|
+
).map((k) => {
|
|
56
|
+
const val = flatHeaders[k];
|
|
57
|
+
return `${k.toLowerCase()}=${Array.isArray(val) ? val.join(",") : val}`;
|
|
58
|
+
}).join("&");
|
|
59
|
+
return `${method}:${fullURL}|h:${serializedHeaders}|c=${withCreds}|t=${respType}`;
|
|
61
60
|
}
|
|
62
61
|
function buildTransformArray(transform) {
|
|
63
62
|
if (!transform) return [];
|
|
@@ -127,8 +126,37 @@ async function dispatchRequest(config) {
|
|
|
127
126
|
if (isGet && config.dedupe) {
|
|
128
127
|
const inflight = activeRequests.get(cacheKey);
|
|
129
128
|
if (inflight) {
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
try {
|
|
130
|
+
const shared = await inflight;
|
|
131
|
+
const response = finalizeResponse(shared, config);
|
|
132
|
+
const settled = await new Promise((resolve, reject) => {
|
|
133
|
+
(0, import_settle.default)(
|
|
134
|
+
resolve,
|
|
135
|
+
reject,
|
|
136
|
+
response,
|
|
137
|
+
config
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
if (config.hooks?.onRequestResponse) {
|
|
141
|
+
await config.hooks.onRequestResponse(settled);
|
|
142
|
+
}
|
|
143
|
+
return settled;
|
|
144
|
+
} catch (error) {
|
|
145
|
+
let finalError = error;
|
|
146
|
+
if (error instanceof import_accessioError.default) {
|
|
147
|
+
finalError = import_accessioError.default.from(
|
|
148
|
+
error,
|
|
149
|
+
error.code || "ERR_DEDUPE",
|
|
150
|
+
config,
|
|
151
|
+
error.request,
|
|
152
|
+
error.response
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
if (config.hooks?.onRequestError && finalError instanceof import_accessioError.default) {
|
|
156
|
+
await config.hooks.onRequestError(finalError);
|
|
157
|
+
}
|
|
158
|
+
throw finalError;
|
|
159
|
+
}
|
|
132
160
|
}
|
|
133
161
|
}
|
|
134
162
|
const performRequest = async () => {
|
package/cjs/core/request.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/request.ts"],"sourcesContent":["import buildURL from './buildURL';\nimport AccessioError, { redactConfig } from './accessioError';\nimport { ERR_BAD_OPTION } from '../constants/errorCodes';\nimport transformData from '../helpers/transformData';\nimport settle from '../helpers/settle';\nimport { flattenHeaders, removeContentType, buildFetchHeaders } from '../helpers/flattenHeaders';\nimport { setBasicAuth } from '../helpers/auth';\nimport fetchAdapter from './fetchAdapter';\nimport { defaultMemoryCache } from '../helpers/memoryCache';\nimport type { AccessioRequestConfig, AccessioResponse, TransformFunction } from '../types';\n\ntype HeadersConfig = Record<string, Record<string, string | string[]>>;\ntype FlatHeaders = Record<string, string | string[]>;\n\nfunction lookupHeader(headers: FlatHeaders, name: string): string {\n const target = name.toLowerCase();\n for (const k of Object.keys(headers)) {\n if (k.toLowerCase() === target) {\n const v = headers[k];\n return Array.isArray(v) ? v.join(',') : (v ?? '');\n }\n }\n return '';\n}\n\nfunction buildCacheKey(\n config: AccessioRequestConfig,\n fullURL: string,\n flatHeaders: FlatHeaders,\n): string {\n const method = (config.method || 'GET').toUpperCase();\n const auth = lookupHeader(flatHeaders, 'authorization');\n const accept = lookupHeader(flatHeaders, 'accept');\n const withCreds = config.withCredentials ? '1' : '0';\n const respType = config.responseType || 'json';\n return `${method}:${fullURL}|a=${auth}|x=${accept}|c=${withCreds}|t=${respType}`;\n}\n\nfunction buildTransformArray(\n transform: TransformFunction | TransformFunction[] | undefined,\n): TransformFunction[] {\n if (!transform) return [];\n if (Array.isArray(transform)) return transform;\n return [transform];\n}\n\nconst DEFAULT_ALLOWED_PROTOCOLS = ['http:', 'https:'];\n\nfunction assertAllowedProtocol(fullURL: string, config: AccessioRequestConfig): void {\n if (config.allowedProtocols === null) return;\n const allowed = config.allowedProtocols ?? DEFAULT_ALLOWED_PROTOCOLS;\n\n let scheme: string | null = null;\n const match = /^([a-z][a-z\\d+\\-.]*):/i.exec(fullURL);\n if (match) scheme = `${match[1].toLowerCase()}:`;\n if (!scheme) return;\n\n if (!allowed.includes(scheme)) {\n throw new AccessioError(\n `URL protocol \"${scheme}\" is not allowed. Allowed: ${allowed.join(', ')}. ` +\n 'Set config.allowedProtocols to extend, or null to disable the check.',\n ERR_BAD_OPTION,\n config,\n null,\n null,\n );\n }\n}\n\nconst activeRequests = new Map<string, Promise<AccessioResponse>>();\nconst MAX_ACTIVE_REQUESTS = 1024;\n\nexport function __activeRequestsSize(): number {\n return activeRequests.size;\n}\n\nfunction trackActiveRequest(key: string, promise: Promise<AccessioResponse>): void {\n activeRequests.set(key, promise);\n // Evict the oldest entry if we've grown past the cap. Map preserves insertion order.\n while (activeRequests.size > MAX_ACTIVE_REQUESTS) {\n const oldest = activeRequests.keys().next().value;\n if (oldest === undefined || oldest === key) break;\n activeRequests.delete(oldest);\n }\n}\n\nexport default async function dispatchRequest(\n config: AccessioRequestConfig,\n): Promise<AccessioResponse> {\n const fullURL =\n config._builtUrl ||\n buildURL(\n config.url ?? '',\n config.baseURL,\n config.params as Record<string, unknown> | undefined,\n config.paramsSerializer,\n );\n\n assertAllowedProtocol(fullURL, config);\n\n if (config.hooks?.onBeforeRequest) {\n await config.hooks.onBeforeRequest(config);\n }\n\n const flatHeaders = flattenHeaders(config.headers as HeadersConfig | undefined, config.method);\n setBasicAuth(config, flatHeaders);\n\n const isGet = (config.method || 'GET').toUpperCase() === 'GET';\n const cacheKey = isGet ? buildCacheKey(config, fullURL, flatHeaders) : '';\n\n if (isGet && config.cache) {\n const cacheProvider = typeof config.cache === 'object' ? config.cache : defaultMemoryCache;\n const cached = await cacheProvider.get(cacheKey);\n if (cached) {\n const cachedView: AccessioResponse = {\n ...cached,\n config: redactConfig(config) as typeof cached.config,\n };\n if (config.hooks?.onRequestResponse) {\n await config.hooks.onRequestResponse(cachedView);\n }\n return cachedView;\n }\n }\n\n if (isGet && config.dedupe) {\n const inflight = activeRequests.get(cacheKey);\n if (inflight) {\n const shared = await inflight;\n return finalizeResponse(shared, config);\n }\n }\n\n const performRequest = async (): Promise<AccessioResponse> => {\n const requestTransforms = buildTransformArray(config.transformRequest);\n const requestData = await transformData(requestTransforms, config.data, flatHeaders, config);\n\n if (\n requestData === null ||\n requestData === undefined ||\n (typeof FormData !== 'undefined' && requestData instanceof FormData)\n ) {\n removeContentType(flatHeaders);\n }\n\n const fetchOptions: RequestInit = {\n method: (config.method || 'GET').toUpperCase(),\n headers: buildFetchHeaders(flatHeaders),\n };\n\n const methodsWithBody = ['POST', 'PUT', 'PATCH', 'DELETE'];\n if (\n methodsWithBody.includes(fetchOptions.method!) &&\n requestData !== undefined &&\n requestData !== null\n ) {\n fetchOptions.body = requestData as BodyInit;\n }\n\n if (config.withCredentials) {\n fetchOptions.credentials = 'include';\n }\n\n if (config.dispatcher) {\n (fetchOptions as any).dispatcher = config.dispatcher;\n }\n if (config.agent) {\n (fetchOptions as any).agent = config.agent;\n }\n\n const requestStartTime = Date.now();\n const response = await fetchAdapter(config, fullURL, fetchOptions, requestStartTime);\n\n const responseTransforms = buildTransformArray(config.transformResponse);\n response.data = await transformData(\n responseTransforms,\n response.data,\n response.headers,\n config,\n 'response',\n );\n\n return response;\n };\n\n const promise = performRequest();\n\n if (isGet && config.dedupe) {\n trackActiveRequest(cacheKey, promise);\n const cleanup = () => {\n if (activeRequests.get(cacheKey) === promise) {\n activeRequests.delete(cacheKey);\n }\n };\n promise.then(cleanup, cleanup);\n }\n\n try {\n const shared = await promise;\n const response = finalizeResponse(shared, config);\n\n if (isGet && config.cache) {\n const cacheProvider = typeof config.cache === 'object' ? config.cache : defaultMemoryCache;\n await cacheProvider.set(cacheKey, shared, config.cacheTTL);\n }\n\n const settled = await new Promise<AccessioResponse>((resolve, reject) => {\n settle(\n resolve as (value: AccessioResponse) => void,\n reject as (reason: AccessioError) => void,\n response,\n config,\n );\n });\n\n if (config.hooks?.onRequestResponse) {\n await config.hooks.onRequestResponse(settled);\n }\n\n return settled;\n } catch (error) {\n if (config.hooks?.onRequestError && error instanceof AccessioError) {\n await config.hooks.onRequestError(error);\n }\n throw error;\n }\n}\n\nfunction finalizeResponse(\n shared: AccessioResponse,\n config: AccessioRequestConfig,\n): AccessioResponse {\n return {\n ...shared,\n config: redactConfig(config) as typeof shared.config,\n };\n}\n"],"mappings":"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","names":["AccessioError","buildURL","transformData","fetchAdapter","settle"]}
|
|
1
|
+
{"version":3,"sources":["../../src/core/request.ts"],"sourcesContent":["import buildURL from './buildURL';\nimport AccessioError, { redactConfig } from './accessioError';\nimport { ERR_BAD_OPTION } from '../constants/errorCodes';\nimport transformData from '../helpers/transformData';\nimport settle from '../helpers/settle';\nimport { flattenHeaders, removeContentType, buildFetchHeaders } from '../helpers/flattenHeaders';\nimport { setBasicAuth } from '../helpers/auth';\nimport fetchAdapter from './fetchAdapter';\nimport { defaultMemoryCache } from '../helpers/memoryCache';\nimport type { AccessioRequestConfig, AccessioResponse, TransformFunction } from '../types';\n\ntype HeadersConfig = Record<string, Record<string, string | string[]>>;\ntype FlatHeaders = Record<string, string | string[]>;\n\nfunction buildCacheKey(\n config: AccessioRequestConfig,\n fullURL: string,\n flatHeaders: FlatHeaders,\n): string {\n if (typeof config.cacheKeySerializer === 'function') {\n return config.cacheKeySerializer(config, fullURL, flatHeaders);\n }\n const method = (config.method || 'GET').toUpperCase();\n const withCreds = config.withCredentials ? '1' : '0';\n const respType = config.responseType || 'json';\n\n // Sort and serialize headers dynamically to prevent collisions,\n // excluding environment-specific transient headers.\n const serializedHeaders = Object.keys(flatHeaders)\n .sort()\n .filter(\n (k) =>\n !['user-agent', 'connection', 'host', 'content-length', 'accept-encoding'].includes(\n k.toLowerCase(),\n ),\n )\n .map((k) => {\n const val = flatHeaders[k];\n return `${k.toLowerCase()}=${Array.isArray(val) ? val.join(',') : val}`;\n })\n .join('&');\n\n return `${method}:${fullURL}|h:${serializedHeaders}|c=${withCreds}|t=${respType}`;\n}\n\nfunction buildTransformArray(\n transform: TransformFunction | TransformFunction[] | undefined,\n): TransformFunction[] {\n if (!transform) return [];\n if (Array.isArray(transform)) return transform;\n return [transform];\n}\n\nconst DEFAULT_ALLOWED_PROTOCOLS = ['http:', 'https:'];\n\nfunction assertAllowedProtocol(fullURL: string, config: AccessioRequestConfig): void {\n if (config.allowedProtocols === null) return;\n const allowed = config.allowedProtocols ?? DEFAULT_ALLOWED_PROTOCOLS;\n\n let scheme: string | null = null;\n const match = /^([a-z][a-z\\d+\\-.]*):/i.exec(fullURL);\n if (match) scheme = `${match[1].toLowerCase()}:`;\n if (!scheme) return;\n\n if (!allowed.includes(scheme)) {\n throw new AccessioError(\n `URL protocol \"${scheme}\" is not allowed. Allowed: ${allowed.join(', ')}. ` +\n 'Set config.allowedProtocols to extend, or null to disable the check.',\n ERR_BAD_OPTION,\n config,\n null,\n null,\n );\n }\n}\n\nconst activeRequests = new Map<string, Promise<AccessioResponse>>();\nconst MAX_ACTIVE_REQUESTS = 1024;\n\nexport function __activeRequestsSize(): number {\n return activeRequests.size;\n}\n\nfunction trackActiveRequest(key: string, promise: Promise<AccessioResponse>): void {\n activeRequests.set(key, promise);\n // Evict the oldest entry if we've grown past the cap. Map preserves insertion order.\n while (activeRequests.size > MAX_ACTIVE_REQUESTS) {\n const oldest = activeRequests.keys().next().value;\n if (oldest === undefined || oldest === key) break;\n activeRequests.delete(oldest);\n }\n}\n\nexport default async function dispatchRequest(\n config: AccessioRequestConfig,\n): Promise<AccessioResponse> {\n const fullURL =\n config._builtUrl ||\n buildURL(\n config.url ?? '',\n config.baseURL,\n config.params as Record<string, unknown> | undefined,\n config.paramsSerializer,\n );\n\n assertAllowedProtocol(fullURL, config);\n\n if (config.hooks?.onBeforeRequest) {\n await config.hooks.onBeforeRequest(config);\n }\n\n const flatHeaders = flattenHeaders(config.headers as HeadersConfig | undefined, config.method);\n setBasicAuth(config, flatHeaders);\n\n const isGet = (config.method || 'GET').toUpperCase() === 'GET';\n const cacheKey = isGet ? buildCacheKey(config, fullURL, flatHeaders) : '';\n\n if (isGet && config.cache) {\n const cacheProvider = typeof config.cache === 'object' ? config.cache : defaultMemoryCache;\n const cached = await cacheProvider.get(cacheKey);\n if (cached) {\n const cachedView: AccessioResponse = {\n ...cached,\n config: redactConfig(config) as typeof cached.config,\n };\n if (config.hooks?.onRequestResponse) {\n await config.hooks.onRequestResponse(cachedView);\n }\n return cachedView;\n }\n }\n\n if (isGet && config.dedupe) {\n const inflight = activeRequests.get(cacheKey);\n if (inflight) {\n try {\n const shared = await inflight;\n const response = finalizeResponse(shared, config);\n const settled = await new Promise<AccessioResponse>((resolve, reject) => {\n settle(\n resolve as (value: AccessioResponse) => void,\n reject as (reason: AccessioError) => void,\n response,\n config,\n );\n });\n\n if (config.hooks?.onRequestResponse) {\n await config.hooks.onRequestResponse(settled);\n }\n\n return settled;\n } catch (error) {\n let finalError = error;\n if (error instanceof AccessioError) {\n finalError = AccessioError.from(\n error,\n error.code || 'ERR_DEDUPE',\n config,\n error.request,\n error.response,\n );\n }\n if (config.hooks?.onRequestError && finalError instanceof AccessioError) {\n await config.hooks.onRequestError(finalError);\n }\n throw finalError;\n }\n }\n }\n\n const performRequest = async (): Promise<AccessioResponse> => {\n const requestTransforms = buildTransformArray(config.transformRequest);\n const requestData = await transformData(requestTransforms, config.data, flatHeaders, config);\n\n if (\n requestData === null ||\n requestData === undefined ||\n (typeof FormData !== 'undefined' && requestData instanceof FormData)\n ) {\n removeContentType(flatHeaders);\n }\n\n const fetchOptions: RequestInit = {\n method: (config.method || 'GET').toUpperCase(),\n headers: buildFetchHeaders(flatHeaders),\n };\n\n const methodsWithBody = ['POST', 'PUT', 'PATCH', 'DELETE'];\n if (\n methodsWithBody.includes(fetchOptions.method!) &&\n requestData !== undefined &&\n requestData !== null\n ) {\n fetchOptions.body = requestData as BodyInit;\n }\n\n if (config.withCredentials) {\n fetchOptions.credentials = 'include';\n }\n\n if (config.dispatcher) {\n (fetchOptions as any).dispatcher = config.dispatcher;\n }\n if (config.agent) {\n (fetchOptions as any).agent = config.agent;\n }\n\n const requestStartTime = Date.now();\n const response = await fetchAdapter(config, fullURL, fetchOptions, requestStartTime);\n\n const responseTransforms = buildTransformArray(config.transformResponse);\n response.data = await transformData(\n responseTransforms,\n response.data,\n response.headers,\n config,\n 'response',\n );\n\n return response;\n };\n\n const promise = performRequest();\n\n if (isGet && config.dedupe) {\n trackActiveRequest(cacheKey, promise);\n const cleanup = () => {\n if (activeRequests.get(cacheKey) === promise) {\n activeRequests.delete(cacheKey);\n }\n };\n promise.then(cleanup, cleanup);\n }\n\n try {\n const shared = await promise;\n const response = finalizeResponse(shared, config);\n\n if (isGet && config.cache) {\n const cacheProvider = typeof config.cache === 'object' ? config.cache : defaultMemoryCache;\n await cacheProvider.set(cacheKey, shared, config.cacheTTL);\n }\n\n const settled = await new Promise<AccessioResponse>((resolve, reject) => {\n settle(\n resolve as (value: AccessioResponse) => void,\n reject as (reason: AccessioError) => void,\n response,\n config,\n );\n });\n\n if (config.hooks?.onRequestResponse) {\n await config.hooks.onRequestResponse(settled);\n }\n\n return settled;\n } catch (error) {\n if (config.hooks?.onRequestError && error instanceof AccessioError) {\n await config.hooks.onRequestError(error);\n }\n throw error;\n }\n}\n\nfunction finalizeResponse(\n shared: AccessioResponse,\n config: AccessioRequestConfig,\n): AccessioResponse {\n return {\n ...shared,\n config: redactConfig(config) as typeof shared.config,\n };\n}\n"],"mappings":"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","names":["AccessioError","buildURL","settle","transformData","fetchAdapter"]}
|
package/cjs/core/retry.cjs
CHANGED
|
@@ -38,6 +38,7 @@ var import_accessioError = __toESM(require("./accessioError"), 1);
|
|
|
38
38
|
function isUnretriableBody(data) {
|
|
39
39
|
if (data == null) return false;
|
|
40
40
|
if (typeof ReadableStream !== "undefined" && data instanceof ReadableStream) return true;
|
|
41
|
+
if (data && typeof data.pipe === "function") return true;
|
|
41
42
|
return false;
|
|
42
43
|
}
|
|
43
44
|
function defaultRetryCondition(error) {
|
|
@@ -55,10 +56,11 @@ function defaultRetryCondition(error) {
|
|
|
55
56
|
}
|
|
56
57
|
return false;
|
|
57
58
|
}
|
|
58
|
-
function calculateDelay(attempt, baseDelay) {
|
|
59
|
+
function calculateDelay(attempt, baseDelay, maxDelay = 3e4) {
|
|
59
60
|
const exponentialDelay = baseDelay * Math.pow(2, attempt);
|
|
60
61
|
const jitter = exponentialDelay * 0.25 * (Math.random() * 2 - 1);
|
|
61
|
-
|
|
62
|
+
const calculated = Math.round(exponentialDelay + jitter);
|
|
63
|
+
return Math.min(calculated, maxDelay);
|
|
62
64
|
}
|
|
63
65
|
function sleep(ms, options) {
|
|
64
66
|
return new Promise((resolve, reject) => {
|
|
@@ -97,8 +99,9 @@ async function retryRequest(dispatchFn, config) {
|
|
|
97
99
|
return response;
|
|
98
100
|
} catch (error) {
|
|
99
101
|
lastError = error;
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
+
const is429 = error.response?.status === 429;
|
|
103
|
+
const attemptLimit = is429 && config.retryOn429 ? Math.max(maxRetries, 3) : maxRetries;
|
|
104
|
+
const shouldRetry = attempt < attemptLimit && retryCondition(error);
|
|
102
105
|
if (!shouldRetry) {
|
|
103
106
|
throw error;
|
|
104
107
|
}
|
|
@@ -111,7 +114,7 @@ async function retryRequest(dispatchFn, config) {
|
|
|
111
114
|
error.response ?? null
|
|
112
115
|
);
|
|
113
116
|
}
|
|
114
|
-
let delay = calculateDelay(attempt, retryDelay);
|
|
117
|
+
let delay = calculateDelay(attempt, retryDelay, config.maxRetryDelay ?? 3e4);
|
|
115
118
|
if (config.retryOn429 && error.response?.status === 429) {
|
|
116
119
|
const headers = error.response?.headers;
|
|
117
120
|
const retryAfterStr = headers?.["retry-after"] || headers?.["Retry-After"];
|
package/cjs/core/retry.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/retry.ts"],"sourcesContent":["import { ERR_BAD_OPTION, ERR_CANCELED, ERR_NETWORK } from '../constants/errorCodes';\nimport AccessioErrorClass from './accessioError';\nimport type {\n AccessioRequestConfig,\n AccessioError,\n RetryConditionFunction,\n OnRetryFunction,\n} from '../types';\n\nfunction isUnretriableBody(data: unknown): boolean {\n if (data == null) return false;\n if (typeof ReadableStream !== 'undefined' && data instanceof ReadableStream) return true;\n return false;\n}\n\nfunction defaultRetryCondition(error: any): boolean {\n if (error.code === ERR_CANCELED) {\n return false;\n }\n\n if (error.code === ERR_NETWORK) {\n return true;\n }\n\n if (error.response && error.response.status >= 500) {\n return true;\n }\n\n if (error.config?.retryOn429 && error.response && error.response.status === 429) {\n return true;\n }\n\n return false;\n}\n\nfunction calculateDelay(attempt: number, baseDelay: number): number {\n const exponentialDelay = baseDelay * Math.pow(2, attempt);\n const jitter = exponentialDelay * 0.25 * (Math.random() * 2 - 1);\n
|
|
1
|
+
{"version":3,"sources":["../../src/core/retry.ts"],"sourcesContent":["import { ERR_BAD_OPTION, ERR_CANCELED, ERR_NETWORK } from '../constants/errorCodes';\nimport AccessioErrorClass from './accessioError';\nimport type {\n AccessioRequestConfig,\n AccessioError,\n RetryConditionFunction,\n OnRetryFunction,\n} from '../types';\n\nfunction isUnretriableBody(data: unknown): boolean {\n if (data == null) return false;\n if (typeof ReadableStream !== 'undefined' && data instanceof ReadableStream) return true;\n if (data && typeof (data as any).pipe === 'function') return true;\n return false;\n}\n\nfunction defaultRetryCondition(error: any): boolean {\n if (error.code === ERR_CANCELED) {\n return false;\n }\n\n if (error.code === ERR_NETWORK) {\n return true;\n }\n\n if (error.response && error.response.status >= 500) {\n return true;\n }\n\n if (error.config?.retryOn429 && error.response && error.response.status === 429) {\n return true;\n }\n\n return false;\n}\n\nfunction calculateDelay(attempt: number, baseDelay: number, maxDelay: number = 30000): number {\n const exponentialDelay = baseDelay * Math.pow(2, attempt);\n const jitter = exponentialDelay * 0.25 * (Math.random() * 2 - 1);\n const calculated = Math.round(exponentialDelay + jitter);\n return Math.min(calculated, maxDelay);\n}\n\nfunction sleep(ms: number, options?: { signal?: AbortSignal }): Promise<void> {\n return new Promise((resolve, reject) => {\n let onAbort: (() => void) | undefined;\n\n const timeoutId = setTimeout(() => {\n if (options?.signal && onAbort) {\n options.signal.removeEventListener('abort', onAbort);\n }\n resolve();\n }, ms);\n\n if (options?.signal) {\n if (options.signal.aborted) {\n clearTimeout(timeoutId);\n return reject(options.signal.reason || new Error('Sleep aborted'));\n }\n\n onAbort = () => {\n clearTimeout(timeoutId);\n reject(options.signal!.reason || new Error('Sleep aborted'));\n };\n\n options.signal.addEventListener('abort', onAbort, { once: true });\n }\n });\n}\n\nasync function retryRequest(\n dispatchFn: (config: AccessioRequestConfig) => Promise<any>,\n config: AccessioRequestConfig,\n): Promise<any> {\n const maxRetries = config.retry ?? 0;\n\n if (maxRetries <= 0 && !config.retryOn429) {\n return dispatchFn(config);\n }\n\n const retryDelay = config.retryDelay ?? 1000;\n const retryCondition: RetryConditionFunction = config.retryCondition ?? defaultRetryCondition;\n\n let lastError: any;\n const actualMaxRetries = Math.max(maxRetries, config.retryOn429 ? 3 : 0);\n\n for (let attempt = 0; attempt <= actualMaxRetries; attempt++) {\n try {\n const response = await dispatchFn(config);\n return response;\n } catch (error) {\n lastError = error;\n\n const is429 = (error as any).response?.status === 429;\n const attemptLimit = is429 && config.retryOn429 ? Math.max(maxRetries, 3) : maxRetries;\n const shouldRetry = attempt < attemptLimit && retryCondition(error as AccessioError);\n\n if (!shouldRetry) {\n throw error;\n }\n\n if (isUnretriableBody(config.data)) {\n throw new AccessioErrorClass(\n 'Request body is a ReadableStream and cannot be retried after consumption. ' +\n 'Buffer the stream upstream or set retry: 0 for this call.',\n ERR_BAD_OPTION,\n config,\n null,\n (error as AccessioError).response ?? null,\n );\n }\n\n let delay = calculateDelay(attempt, retryDelay, config.maxRetryDelay ?? 30000);\n\n if (config.retryOn429 && (error as any).response?.status === 429) {\n const headers = (error as any).response?.headers;\n const retryAfterStr = headers?.['retry-after'] || headers?.['Retry-After'];\n if (retryAfterStr) {\n const parsed = parseInt(retryAfterStr, 10);\n if (!isNaN(parsed)) {\n delay = parsed * 1000;\n } else {\n const date = new Date(retryAfterStr);\n if (!isNaN(date.getTime())) {\n delay = Math.max(0, date.getTime() - Date.now());\n }\n }\n }\n }\n\n if (typeof config.onRetry === 'function') {\n (config.onRetry as OnRetryFunction)(attempt + 1, error as AccessioError, config);\n }\n\n await sleep(delay, { signal: config.signal });\n }\n }\n\n throw lastError;\n}\n\nexport { defaultRetryCondition, calculateDelay };\nexport default retryRequest;\n"],"mappings":"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","names":["AccessioErrorClass"]}
|
|
@@ -95,10 +95,13 @@ function removeContentType(headers) {
|
|
|
95
95
|
function buildFetchHeaders(headers) {
|
|
96
96
|
const fetchHeaders = new Headers();
|
|
97
97
|
for (const [key, value] of Object.entries(headers)) {
|
|
98
|
+
if (value === void 0 || value === null) continue;
|
|
98
99
|
assertSafeHeader(key, value);
|
|
99
100
|
if (Array.isArray(value)) {
|
|
100
101
|
for (const v of value) {
|
|
101
|
-
|
|
102
|
+
if (v !== void 0 && v !== null) {
|
|
103
|
+
fetchHeaders.append(key, v);
|
|
104
|
+
}
|
|
102
105
|
}
|
|
103
106
|
} else {
|
|
104
107
|
fetchHeaders.set(key, value);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/helpers/flattenHeaders.ts"],"sourcesContent":["import AccessioError from '../core/accessioError';\nimport { ERR_BAD_OPTION } from '../constants/errorCodes';\n\nconst HEADER_FORBIDDEN_CHAR = /[\\r\\n\\0]/;\n\nfunction assertSafeHeader(name: string, value: string | string[]): void {\n if (typeof name !== 'string' || HEADER_FORBIDDEN_CHAR.test(name)) {\n throw new AccessioError(\n `Invalid header name \"${String(name)}\": CR, LF and NUL are not allowed`,\n ERR_BAD_OPTION,\n null,\n null,\n null,\n );\n }\n const values = Array.isArray(value) ? value : [value];\n for (const v of values) {\n if (typeof v === 'string' && HEADER_FORBIDDEN_CHAR.test(v)) {\n throw new AccessioError(\n `Invalid value for header \"${name}\": CR, LF and NUL are not allowed`,\n ERR_BAD_OPTION,\n null,\n null,\n null,\n );\n }\n }\n}\n\nconst METHOD_KEYS = new Set<string>([\n 'common',\n 'delete',\n 'get',\n 'head',\n 'options',\n 'post',\n 'put',\n 'patch',\n]);\n\ntype HeadersConfig = Record<string, Record<string, string | string[]>>;\n\nexport function flattenHeaders(\n headers: HeadersConfig | undefined,\n method?: string,\n): Record<string, string | string[]> {\n if (!headers) return {};\n\n const merged: Record<string, string | string[]> = {};\n const methodLower = (method || 'get').toLowerCase();\n\n if (headers['common']) {\n Object.assign(merged, headers['common']);\n }\n\n if (headers[methodLower]) {\n Object.assign(merged, headers[methodLower]);\n }\n\n for (const key in headers) {\n if (Object.prototype.hasOwnProperty.call(headers, key) && !METHOD_KEYS.has(key)) {\n merged[key] = headers[key] as unknown as string | string[];\n }\n }\n\n return merged;\n}\n\nexport function removeContentType(headers: Record<string, string | string[]>): void {\n const keys = Object.keys(headers).filter((k) => k.toLowerCase() === 'content-type');\n for (const key of keys) {\n delete headers[key];\n }\n}\n\nexport function buildFetchHeaders(headers: Record<string, string | string[]>): Headers {\n const fetchHeaders = new Headers();\n for (const [key, value] of Object.entries(headers)) {\n assertSafeHeader(key, value);\n if (Array.isArray(value)) {\n for (const v of value) {\n fetchHeaders.append(key, v);\n }\n } else {\n fetchHeaders.set(key, value);\n }\n }\n return fetchHeaders;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2BAA0B;AAC1B,wBAA+B;AAE/B,MAAM,wBAAwB;AAE9B,SAAS,iBAAiB,MAAc,OAAgC;AACtE,MAAI,OAAO,SAAS,YAAY,sBAAsB,KAAK,IAAI,GAAG;AAChE,UAAM,IAAI,qBAAAA;AAAA,MACR,wBAAwB,OAAO,IAAI,CAAC;AAAA,MACpC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,QAAM,SAAS,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AACpD,aAAW,KAAK,QAAQ;AACtB,QAAI,OAAO,MAAM,YAAY,sBAAsB,KAAK,CAAC,GAAG;AAC1D,YAAM,IAAI,qBAAAA;AAAA,QACR,6BAA6B,IAAI;AAAA,QACjC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,MAAM,cAAc,oBAAI,IAAY;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAIM,SAAS,eACd,SACA,QACmC;AACnC,MAAI,CAAC,QAAS,QAAO,CAAC;AAEtB,QAAM,SAA4C,CAAC;AACnD,QAAM,eAAe,UAAU,OAAO,YAAY;AAElD,MAAI,QAAQ,QAAQ,GAAG;AACrB,WAAO,OAAO,QAAQ,QAAQ,QAAQ,CAAC;AAAA,EACzC;AAEA,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,OAAO,QAAQ,QAAQ,WAAW,CAAC;AAAA,EAC5C;AAEA,aAAW,OAAO,SAAS;AACzB,QAAI,OAAO,UAAU,eAAe,KAAK,SAAS,GAAG,KAAK,CAAC,YAAY,IAAI,GAAG,GAAG;AAC/E,aAAO,GAAG,IAAI,QAAQ,GAAG;AAAA,IAC3B;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,kBAAkB,SAAkD;AAClF,QAAM,OAAO,OAAO,KAAK,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,YAAY,MAAM,cAAc;AAClF,aAAW,OAAO,MAAM;AACtB,WAAO,QAAQ,GAAG;AAAA,EACpB;AACF;AAEO,SAAS,kBAAkB,SAAqD;AACrF,QAAM,eAAe,IAAI,QAAQ;AACjC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AAClD,qBAAiB,KAAK,KAAK;AAC3B,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,iBAAW,KAAK,OAAO;AACrB,
|
|
1
|
+
{"version":3,"sources":["../../src/helpers/flattenHeaders.ts"],"sourcesContent":["import AccessioError from '../core/accessioError';\nimport { ERR_BAD_OPTION } from '../constants/errorCodes';\n\nconst HEADER_FORBIDDEN_CHAR = /[\\r\\n\\0]/;\n\nfunction assertSafeHeader(name: string, value: string | string[]): void {\n if (typeof name !== 'string' || HEADER_FORBIDDEN_CHAR.test(name)) {\n throw new AccessioError(\n `Invalid header name \"${String(name)}\": CR, LF and NUL are not allowed`,\n ERR_BAD_OPTION,\n null,\n null,\n null,\n );\n }\n const values = Array.isArray(value) ? value : [value];\n for (const v of values) {\n if (typeof v === 'string' && HEADER_FORBIDDEN_CHAR.test(v)) {\n throw new AccessioError(\n `Invalid value for header \"${name}\": CR, LF and NUL are not allowed`,\n ERR_BAD_OPTION,\n null,\n null,\n null,\n );\n }\n }\n}\n\nconst METHOD_KEYS = new Set<string>([\n 'common',\n 'delete',\n 'get',\n 'head',\n 'options',\n 'post',\n 'put',\n 'patch',\n]);\n\ntype HeadersConfig = Record<string, Record<string, string | string[]>>;\n\nexport function flattenHeaders(\n headers: HeadersConfig | undefined,\n method?: string,\n): Record<string, string | string[]> {\n if (!headers) return {};\n\n const merged: Record<string, string | string[]> = {};\n const methodLower = (method || 'get').toLowerCase();\n\n if (headers['common']) {\n Object.assign(merged, headers['common']);\n }\n\n if (headers[methodLower]) {\n Object.assign(merged, headers[methodLower]);\n }\n\n for (const key in headers) {\n if (Object.prototype.hasOwnProperty.call(headers, key) && !METHOD_KEYS.has(key)) {\n merged[key] = headers[key] as unknown as string | string[];\n }\n }\n\n return merged;\n}\n\nexport function removeContentType(headers: Record<string, string | string[]>): void {\n const keys = Object.keys(headers).filter((k) => k.toLowerCase() === 'content-type');\n for (const key of keys) {\n delete headers[key];\n }\n}\n\nexport function buildFetchHeaders(headers: Record<string, string | string[]>): Headers {\n const fetchHeaders = new Headers();\n for (const [key, value] of Object.entries(headers)) {\n if (value === undefined || value === null) continue;\n assertSafeHeader(key, value);\n if (Array.isArray(value)) {\n for (const v of value) {\n if (v !== undefined && v !== null) {\n fetchHeaders.append(key, v);\n }\n }\n } else {\n fetchHeaders.set(key, value);\n }\n }\n return fetchHeaders;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2BAA0B;AAC1B,wBAA+B;AAE/B,MAAM,wBAAwB;AAE9B,SAAS,iBAAiB,MAAc,OAAgC;AACtE,MAAI,OAAO,SAAS,YAAY,sBAAsB,KAAK,IAAI,GAAG;AAChE,UAAM,IAAI,qBAAAA;AAAA,MACR,wBAAwB,OAAO,IAAI,CAAC;AAAA,MACpC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,QAAM,SAAS,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AACpD,aAAW,KAAK,QAAQ;AACtB,QAAI,OAAO,MAAM,YAAY,sBAAsB,KAAK,CAAC,GAAG;AAC1D,YAAM,IAAI,qBAAAA;AAAA,QACR,6BAA6B,IAAI;AAAA,QACjC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,MAAM,cAAc,oBAAI,IAAY;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAIM,SAAS,eACd,SACA,QACmC;AACnC,MAAI,CAAC,QAAS,QAAO,CAAC;AAEtB,QAAM,SAA4C,CAAC;AACnD,QAAM,eAAe,UAAU,OAAO,YAAY;AAElD,MAAI,QAAQ,QAAQ,GAAG;AACrB,WAAO,OAAO,QAAQ,QAAQ,QAAQ,CAAC;AAAA,EACzC;AAEA,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,OAAO,QAAQ,QAAQ,WAAW,CAAC;AAAA,EAC5C;AAEA,aAAW,OAAO,SAAS;AACzB,QAAI,OAAO,UAAU,eAAe,KAAK,SAAS,GAAG,KAAK,CAAC,YAAY,IAAI,GAAG,GAAG;AAC/E,aAAO,GAAG,IAAI,QAAQ,GAAG;AAAA,IAC3B;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,kBAAkB,SAAkD;AAClF,QAAM,OAAO,OAAO,KAAK,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,YAAY,MAAM,cAAc;AAClF,aAAW,OAAO,MAAM;AACtB,WAAO,QAAQ,GAAG;AAAA,EACpB;AACF;AAEO,SAAS,kBAAkB,SAAqD;AACrF,QAAM,eAAe,IAAI,QAAQ;AACjC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AAClD,QAAI,UAAU,UAAa,UAAU,KAAM;AAC3C,qBAAiB,KAAK,KAAK;AAC3B,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,iBAAW,KAAK,OAAO;AACrB,YAAI,MAAM,UAAa,MAAM,MAAM;AACjC,uBAAa,OAAO,KAAK,CAAC;AAAA,QAC5B;AAAA,MACF;AAAA,IACF,OAAO;AACL,mBAAa,IAAI,KAAK,KAAK;AAAA,IAC7B;AAAA,EACF;AACA,SAAO;AACT;","names":["AccessioError"]}
|
|
@@ -18,11 +18,16 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var memoryCache_exports = {};
|
|
20
20
|
__export(memoryCache_exports, {
|
|
21
|
+
MemoryCache: () => MemoryCache,
|
|
21
22
|
defaultMemoryCache: () => defaultMemoryCache
|
|
22
23
|
});
|
|
23
24
|
module.exports = __toCommonJS(memoryCache_exports);
|
|
24
25
|
class MemoryCache {
|
|
25
26
|
cache = /* @__PURE__ */ new Map();
|
|
27
|
+
maxItems;
|
|
28
|
+
constructor(maxItems = 1e3) {
|
|
29
|
+
this.maxItems = maxItems;
|
|
30
|
+
}
|
|
26
31
|
get(key) {
|
|
27
32
|
const item = this.cache.get(key);
|
|
28
33
|
if (!item) return null;
|
|
@@ -33,7 +38,19 @@ class MemoryCache {
|
|
|
33
38
|
return item.value;
|
|
34
39
|
}
|
|
35
40
|
set(key, value, ttl) {
|
|
36
|
-
const
|
|
41
|
+
const now = Date.now();
|
|
42
|
+
for (const [k, item] of this.cache.entries()) {
|
|
43
|
+
if (item.expiry && now > item.expiry) {
|
|
44
|
+
this.cache.delete(k);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (this.cache.size >= this.maxItems) {
|
|
48
|
+
const oldest = this.cache.keys().next().value;
|
|
49
|
+
if (oldest !== void 0) {
|
|
50
|
+
this.cache.delete(oldest);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const expiry = ttl ? now + ttl : null;
|
|
37
54
|
this.cache.set(key, { value, expiry });
|
|
38
55
|
}
|
|
39
56
|
delete(key) {
|
|
@@ -46,6 +63,7 @@ class MemoryCache {
|
|
|
46
63
|
const defaultMemoryCache = new MemoryCache();
|
|
47
64
|
// Annotate the CommonJS export names for ESM import in node:
|
|
48
65
|
0 && (module.exports = {
|
|
66
|
+
MemoryCache,
|
|
49
67
|
defaultMemoryCache
|
|
50
68
|
});
|
|
51
69
|
//# sourceMappingURL=memoryCache.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/helpers/memoryCache.ts"],"sourcesContent":["import type { CacheProvider } from '../types';\n\nclass MemoryCache implements CacheProvider {\n private cache = new Map<string, { value: any; expiry: number | null }>();\n\n get(key: string) {\n const item = this.cache.get(key);\n if (!item) return null;\n if (item.expiry && Date.now() > item.expiry) {\n this.cache.delete(key);\n return null;\n }\n return item.value;\n }\n\n set(key: string, value: any, ttl?: number) {\n const
|
|
1
|
+
{"version":3,"sources":["../../src/helpers/memoryCache.ts"],"sourcesContent":["import type { CacheProvider } from '../types';\n\nclass MemoryCache implements CacheProvider {\n private cache = new Map<string, { value: any; expiry: number | null }>();\n private maxItems: number;\n\n constructor(maxItems: number = 1000) {\n this.maxItems = maxItems;\n }\n\n get(key: string) {\n const item = this.cache.get(key);\n if (!item) return null;\n if (item.expiry && Date.now() > item.expiry) {\n this.cache.delete(key);\n return null;\n }\n return item.value;\n }\n\n set(key: string, value: any, ttl?: number) {\n const now = Date.now();\n\n // Proactively evict all expired items first\n for (const [k, item] of this.cache.entries()) {\n if (item.expiry && now > item.expiry) {\n this.cache.delete(k);\n }\n }\n\n // Evict oldest item if we are still at limit\n if (this.cache.size >= this.maxItems) {\n const oldest = this.cache.keys().next().value;\n if (oldest !== undefined) {\n this.cache.delete(oldest);\n }\n }\n\n const expiry = ttl ? now + ttl : null;\n this.cache.set(key, { value, expiry });\n }\n\n delete(key: string) {\n this.cache.delete(key);\n }\n\n clear() {\n this.cache.clear();\n }\n}\n\nexport const defaultMemoryCache = new MemoryCache();\nexport { MemoryCache };\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,MAAM,YAAqC;AAAA,EACjC,QAAQ,oBAAI,IAAmD;AAAA,EAC/D;AAAA,EAER,YAAY,WAAmB,KAAM;AACnC,SAAK,WAAW;AAAA,EAClB;AAAA,EAEA,IAAI,KAAa;AACf,UAAM,OAAO,KAAK,MAAM,IAAI,GAAG;AAC/B,QAAI,CAAC,KAAM,QAAO;AAClB,QAAI,KAAK,UAAU,KAAK,IAAI,IAAI,KAAK,QAAQ;AAC3C,WAAK,MAAM,OAAO,GAAG;AACrB,aAAO;AAAA,IACT;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,KAAa,OAAY,KAAc;AACzC,UAAM,MAAM,KAAK,IAAI;AAGrB,eAAW,CAAC,GAAG,IAAI,KAAK,KAAK,MAAM,QAAQ,GAAG;AAC5C,UAAI,KAAK,UAAU,MAAM,KAAK,QAAQ;AACpC,aAAK,MAAM,OAAO,CAAC;AAAA,MACrB;AAAA,IACF;AAGA,QAAI,KAAK,MAAM,QAAQ,KAAK,UAAU;AACpC,YAAM,SAAS,KAAK,MAAM,KAAK,EAAE,KAAK,EAAE;AACxC,UAAI,WAAW,QAAW;AACxB,aAAK,MAAM,OAAO,MAAM;AAAA,MAC1B;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,MAAM,MAAM;AACjC,SAAK,MAAM,IAAI,KAAK,EAAE,OAAO,OAAO,CAAC;AAAA,EACvC;AAAA,EAEA,OAAO,KAAa;AAClB,SAAK,MAAM,OAAO,GAAG;AAAA,EACvB;AAAA,EAEA,QAAQ;AACN,SAAK,MAAM,MAAM;AAAA,EACnB;AACF;AAEO,MAAM,qBAAqB,IAAI,YAAY;","names":[]}
|
|
@@ -37,10 +37,13 @@ function createRateLimiter(maxConcurrent = Infinity, maxQueueSize = Infinity) {
|
|
|
37
37
|
let active = 0;
|
|
38
38
|
let destroyed = false;
|
|
39
39
|
const queue = [];
|
|
40
|
-
function acquire() {
|
|
40
|
+
function acquire(signal) {
|
|
41
41
|
if (destroyed) {
|
|
42
42
|
return Promise.reject(new Error("[Accessio] Rate limiter has been destroyed"));
|
|
43
43
|
}
|
|
44
|
+
if (signal?.aborted) {
|
|
45
|
+
return Promise.reject(signal.reason || new Error("Request aborted"));
|
|
46
|
+
}
|
|
44
47
|
if (active < maxConcurrent) {
|
|
45
48
|
active++;
|
|
46
49
|
return Promise.resolve();
|
|
@@ -51,7 +54,32 @@ function createRateLimiter(maxConcurrent = Infinity, maxQueueSize = Infinity) {
|
|
|
51
54
|
);
|
|
52
55
|
}
|
|
53
56
|
return new Promise((resolve, reject) => {
|
|
54
|
-
|
|
57
|
+
let onAbort;
|
|
58
|
+
const item = {
|
|
59
|
+
resolve: () => {
|
|
60
|
+
if (signal && onAbort) {
|
|
61
|
+
signal.removeEventListener("abort", onAbort);
|
|
62
|
+
}
|
|
63
|
+
resolve();
|
|
64
|
+
},
|
|
65
|
+
reject: (err) => {
|
|
66
|
+
if (signal && onAbort) {
|
|
67
|
+
signal.removeEventListener("abort", onAbort);
|
|
68
|
+
}
|
|
69
|
+
reject(err);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
queue.push(item);
|
|
73
|
+
if (signal) {
|
|
74
|
+
onAbort = () => {
|
|
75
|
+
const index = queue.indexOf(item);
|
|
76
|
+
if (index !== -1) {
|
|
77
|
+
queue.splice(index, 1);
|
|
78
|
+
}
|
|
79
|
+
reject(signal.reason || new Error("Request aborted"));
|
|
80
|
+
};
|
|
81
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
82
|
+
}
|
|
55
83
|
});
|
|
56
84
|
}
|
|
57
85
|
function release() {
|
|
@@ -87,7 +115,7 @@ function createRateLimiter(maxConcurrent = Infinity, maxQueueSize = Infinity) {
|
|
|
87
115
|
};
|
|
88
116
|
}
|
|
89
117
|
async function rateLimitedRequest(dispatchFn, limiter, config) {
|
|
90
|
-
await limiter.acquire();
|
|
118
|
+
await limiter.acquire(config.signal);
|
|
91
119
|
try {
|
|
92
120
|
return await dispatchFn(config);
|
|
93
121
|
} finally {
|