accessio 1.3.0 → 1.5.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/cjs/accessio.cjs +98 -97
- package/cjs/accessio.cjs.map +1 -1
- package/cjs/core/accessioError.cjs +51 -1
- package/cjs/core/accessioError.cjs.map +1 -1
- package/cjs/core/buildURL.cjs +10 -4
- package/cjs/core/buildURL.cjs.map +1 -1
- package/cjs/core/fetchAdapter.cjs +125 -105
- package/cjs/core/fetchAdapter.cjs.map +1 -1
- package/cjs/core/request.cjs +73 -23
- 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/debug.cjs +7 -1
- package/cjs/helpers/debug.cjs.map +1 -1
- package/cjs/helpers/flattenHeaders.cjs +4 -1
- package/cjs/helpers/flattenHeaders.cjs.map +1 -1
- package/cjs/helpers/rateLimiter.cjs +31 -3
- package/cjs/helpers/rateLimiter.cjs.map +1 -1
- package/cjs/helpers/settle.cjs +1 -1
- package/cjs/helpers/settle.cjs.map +1 -1
- package/cjs/helpers/toFormData.cjs +1 -1
- package/cjs/helpers/toFormData.cjs.map +1 -1
- package/cjs/interceptors/interceptorManager.cjs +25 -18
- package/cjs/interceptors/interceptorManager.cjs.map +1 -1
- package/index.d.ts +82 -21
- package/package.json +1 -1
- package/src/accessio.ts +148 -113
- package/src/core/accessioError.ts +57 -1
- package/src/core/buildURL.ts +14 -4
- package/src/core/fetchAdapter.ts +155 -125
- package/src/core/request.ts +85 -27
- package/src/core/retry.ts +8 -5
- package/src/helpers/debug.ts +7 -2
- package/src/helpers/flattenHeaders.ts +4 -1
- package/src/helpers/rateLimiter.ts +35 -3
- package/src/helpers/settle.ts +1 -1
- package/src/helpers/toFormData.ts +5 -1
- package/src/interceptors/interceptorManager.ts +26 -19
- package/src/types.ts +2 -1
package/cjs/helpers/debug.cjs
CHANGED
|
@@ -23,6 +23,7 @@ __export(debug_exports, {
|
|
|
23
23
|
logResponse: () => logResponse
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(debug_exports);
|
|
26
|
+
var import_accessioError = require("../core/accessioError");
|
|
26
27
|
function formatBytes(bytes) {
|
|
27
28
|
if (bytes === 0) return "0 B";
|
|
28
29
|
const sizes = ["B", "KB", "MB", "GB"];
|
|
@@ -46,7 +47,12 @@ function logRequest(config, fullUrl) {
|
|
|
46
47
|
parts.push(` Params: ${JSON.stringify(safe.params)}`);
|
|
47
48
|
}
|
|
48
49
|
if (config.data && typeof config.data === "object") {
|
|
49
|
-
|
|
50
|
+
let preview;
|
|
51
|
+
try {
|
|
52
|
+
preview = JSON.stringify((0, import_accessioError.redactBody)(config.data));
|
|
53
|
+
} catch {
|
|
54
|
+
preview = "[unserializable body]";
|
|
55
|
+
}
|
|
50
56
|
const truncated = preview.length > 200 ? `${preview.substring(0, 200)}...` : preview;
|
|
51
57
|
parts.push(` Body: ${truncated}`);
|
|
52
58
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/helpers/debug.ts"],"sourcesContent":["import type { AccessioRequestConfig, AccessioResponse } from '../types';\nimport AccessioError from '../core/accessioError';\n\nfunction formatBytes(bytes: number): string {\n if (bytes === 0) return '0 B';\n const sizes = ['B', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\n return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${sizes[i]}`;\n}\n\nfunction sanitizeConfigForLog(config: AccessioRequestConfig): {\n params: Record<string, unknown> | undefined;\n timeout: number | undefined;\n retry: number | undefined;\n} {\n return {\n params: config.params,\n timeout: config.timeout,\n retry: config.retry,\n };\n}\n\nexport function logRequest(config: AccessioRequestConfig, fullUrl: string): void {\n if (!config.debug) return;\n\n const safe = sanitizeConfigForLog(config);\n\n const method = (config.method || 'GET').toUpperCase();\n const url = fullUrl || config.url || '';\n\n const parts: string[] = [`🐦⬛ [Accessio] → ${method} ${url}`];\n\n if (safe.params && Object.keys(safe.params).length > 0) {\n parts.push(` Params: ${JSON.stringify(safe.params)}`);\n }\n\n if (config.data && typeof config.data === 'object') {\n
|
|
1
|
+
{"version":3,"sources":["../../src/helpers/debug.ts"],"sourcesContent":["import type { AccessioRequestConfig, AccessioResponse } from '../types';\nimport AccessioError, { redactBody } from '../core/accessioError';\n\nfunction formatBytes(bytes: number): string {\n if (bytes === 0) return '0 B';\n const sizes = ['B', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\n return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${sizes[i]}`;\n}\n\nfunction sanitizeConfigForLog(config: AccessioRequestConfig): {\n params: Record<string, unknown> | undefined;\n timeout: number | undefined;\n retry: number | undefined;\n} {\n return {\n params: config.params,\n timeout: config.timeout,\n retry: config.retry,\n };\n}\n\nexport function logRequest(config: AccessioRequestConfig, fullUrl: string): void {\n if (!config.debug) return;\n\n const safe = sanitizeConfigForLog(config);\n\n const method = (config.method || 'GET').toUpperCase();\n const url = fullUrl || config.url || '';\n\n const parts: string[] = [`🐦⬛ [Accessio] → ${method} ${url}`];\n\n if (safe.params && Object.keys(safe.params).length > 0) {\n parts.push(` Params: ${JSON.stringify(safe.params)}`);\n }\n\n if (config.data && typeof config.data === 'object') {\n let preview: string;\n try {\n preview = JSON.stringify(redactBody(config.data));\n } catch {\n preview = '[unserializable body]';\n }\n const truncated = preview.length > 200 ? `${preview.substring(0, 200)}...` : preview;\n parts.push(` Body: ${truncated}`);\n }\n\n if (safe.timeout) {\n parts.push(` Timeout: ${safe.timeout}ms`);\n }\n\n if (safe.retry) {\n parts.push(` Retry: ${safe.retry}x`);\n }\n\n console.log(parts.join('\\n'));\n}\n\nexport function logResponse(response: AccessioResponse): void {\n if (!response.config || !response.config.debug) return;\n const status = response.status;\n const statusText = response.statusText || '';\n const duration = response.duration != null ? `${response.duration}ms` : '??';\n\n const statusIcon = status >= 200 && status < 300 ? '✅' : status >= 400 ? '❌' : '⚠️';\n\n const parts: string[] = [`🐦⬛ [Accessio] ← ${statusIcon} ${status} ${statusText} (${duration})`];\n\n if (response.data) {\n try {\n const size =\n typeof response.data === 'string'\n ? response.data.length\n : JSON.stringify(response.data).length;\n parts.push(` Size: ~${formatBytes(size)}`);\n } catch {\n // ignore\n }\n }\n\n console.log(parts.join('\\n'));\n}\n\nexport function logError(error: AccessioError, config?: AccessioRequestConfig): void {\n if (!config || !config.debug) return;\n\n const parts: string[] = [`🐦⬛ [Accessio] ← ❌ ERROR: ${error.message}`];\n\n if (error.code) {\n parts.push(` Code: ${error.code}`);\n }\n\n if (error.response) {\n parts.push(` Status: ${error.response.status}`);\n }\n\n console.log(parts.join('\\n'));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,2BAA0C;AAE1C,SAAS,YAAY,OAAuB;AAC1C,MAAI,UAAU,EAAG,QAAO;AACxB,QAAM,QAAQ,CAAC,KAAK,MAAM,MAAM,IAAI;AACpC,QAAM,IAAI,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,IAAI,CAAC;AACrD,SAAO,IAAI,QAAQ,KAAK,IAAI,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC;AAC9D;AAEA,SAAS,qBAAqB,QAI5B;AACA,SAAO;AAAA,IACL,QAAQ,OAAO;AAAA,IACf,SAAS,OAAO;AAAA,IAChB,OAAO,OAAO;AAAA,EAChB;AACF;AAEO,SAAS,WAAW,QAA+B,SAAuB;AAC/E,MAAI,CAAC,OAAO,MAAO;AAEnB,QAAM,OAAO,qBAAqB,MAAM;AAExC,QAAM,UAAU,OAAO,UAAU,OAAO,YAAY;AACpD,QAAM,MAAM,WAAW,OAAO,OAAO;AAErC,QAAM,QAAkB,CAAC,2CAAqB,MAAM,IAAI,GAAG,EAAE;AAE7D,MAAI,KAAK,UAAU,OAAO,KAAK,KAAK,MAAM,EAAE,SAAS,GAAG;AACtD,UAAM,KAAK,cAAc,KAAK,UAAU,KAAK,MAAM,CAAC,EAAE;AAAA,EACxD;AAEA,MAAI,OAAO,QAAQ,OAAO,OAAO,SAAS,UAAU;AAClD,QAAI;AACJ,QAAI;AACF,gBAAU,KAAK,cAAU,iCAAW,OAAO,IAAI,CAAC;AAAA,IAClD,QAAQ;AACN,gBAAU;AAAA,IACZ;AACA,UAAM,YAAY,QAAQ,SAAS,MAAM,GAAG,QAAQ,UAAU,GAAG,GAAG,CAAC,QAAQ;AAC7E,UAAM,KAAK,YAAY,SAAS,EAAE;AAAA,EACpC;AAEA,MAAI,KAAK,SAAS;AAChB,UAAM,KAAK,eAAe,KAAK,OAAO,IAAI;AAAA,EAC5C;AAEA,MAAI,KAAK,OAAO;AACd,UAAM,KAAK,aAAa,KAAK,KAAK,GAAG;AAAA,EACvC;AAEA,UAAQ,IAAI,MAAM,KAAK,IAAI,CAAC;AAC9B;AAEO,SAAS,YAAY,UAAkC;AAC5D,MAAI,CAAC,SAAS,UAAU,CAAC,SAAS,OAAO,MAAO;AAChD,QAAM,SAAS,SAAS;AACxB,QAAM,aAAa,SAAS,cAAc;AAC1C,QAAM,WAAW,SAAS,YAAY,OAAO,GAAG,SAAS,QAAQ,OAAO;AAExE,QAAM,aAAa,UAAU,OAAO,SAAS,MAAM,WAAM,UAAU,MAAM,WAAM;AAE/E,QAAM,QAAkB,CAAC,2CAAqB,UAAU,IAAI,MAAM,IAAI,UAAU,KAAK,QAAQ,GAAG;AAEhG,MAAI,SAAS,MAAM;AACjB,QAAI;AACF,YAAM,OACJ,OAAO,SAAS,SAAS,WACrB,SAAS,KAAK,SACd,KAAK,UAAU,SAAS,IAAI,EAAE;AACpC,YAAM,KAAK,aAAa,YAAY,IAAI,CAAC,EAAE;AAAA,IAC7C,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,UAAQ,IAAI,MAAM,KAAK,IAAI,CAAC;AAC9B;AAEO,SAAS,SAAS,OAAsB,QAAsC;AACnF,MAAI,CAAC,UAAU,CAAC,OAAO,MAAO;AAE9B,QAAM,QAAkB,CAAC,yDAA8B,MAAM,OAAO,EAAE;AAEtE,MAAI,MAAM,MAAM;AACd,UAAM,KAAK,YAAY,MAAM,IAAI,EAAE;AAAA,EACrC;AAEA,MAAI,MAAM,UAAU;AAClB,UAAM,KAAK,cAAc,MAAM,SAAS,MAAM,EAAE;AAAA,EAClD;AAEA,UAAQ,IAAI,MAAM,KAAK,IAAI,CAAC;AAC9B;","names":[]}
|
|
@@ -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"]}
|
|
@@ -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 {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/helpers/rateLimiter.ts"],"sourcesContent":["import type { RateLimiter, AccessioRequestConfig, AccessioResponse } from '../types';\n\ninterface QueueItem {\n resolve: () => void;\n reject: (reason: Error) => void;\n}\n\nexport function createRateLimiter(\n maxConcurrent: number = Infinity,\n maxQueueSize: number = Infinity,\n): RateLimiter {\n if (maxConcurrent !== Infinity && (!Number.isInteger(maxConcurrent) || maxConcurrent < 1)) {\n throw new RangeError(\n `[Accessio] maxConcurrent must be a positive integer or Infinity, got: ${maxConcurrent}`,\n );\n }\n if (maxQueueSize !== Infinity && (!Number.isInteger(maxQueueSize) || maxQueueSize < 1)) {\n throw new RangeError(\n `[Accessio] maxQueueSize must be a positive integer or Infinity, got: ${maxQueueSize}`,\n );\n }\n let active = 0;\n let destroyed = false;\n const queue: QueueItem[] = [];\n\n function acquire(): Promise<void> {\n if (destroyed) {\n return Promise.reject(new Error('[Accessio] Rate limiter has been destroyed'));\n }\n\n if (active < maxConcurrent) {\n active++;\n return Promise.resolve();\n }\n\n if (queue.length >= maxQueueSize) {\n return Promise.reject(\n new Error(`[Accessio] Rate limiter queue size exceeded maxQueueSize (${maxQueueSize})`),\n );\n }\n\n return new Promise((resolve, reject) => {\n queue.push({
|
|
1
|
+
{"version":3,"sources":["../../src/helpers/rateLimiter.ts"],"sourcesContent":["import type { RateLimiter, AccessioRequestConfig, AccessioResponse } from '../types';\n\ninterface QueueItem {\n resolve: () => void;\n reject: (reason: Error) => void;\n}\n\nexport function createRateLimiter(\n maxConcurrent: number = Infinity,\n maxQueueSize: number = Infinity,\n): RateLimiter {\n if (maxConcurrent !== Infinity && (!Number.isInteger(maxConcurrent) || maxConcurrent < 1)) {\n throw new RangeError(\n `[Accessio] maxConcurrent must be a positive integer or Infinity, got: ${maxConcurrent}`,\n );\n }\n if (maxQueueSize !== Infinity && (!Number.isInteger(maxQueueSize) || maxQueueSize < 1)) {\n throw new RangeError(\n `[Accessio] maxQueueSize must be a positive integer or Infinity, got: ${maxQueueSize}`,\n );\n }\n let active = 0;\n let destroyed = false;\n const queue: QueueItem[] = [];\n\n function acquire(signal?: AbortSignal): Promise<void> {\n if (destroyed) {\n return Promise.reject(new Error('[Accessio] Rate limiter has been destroyed'));\n }\n\n if (signal?.aborted) {\n return Promise.reject(signal.reason || new Error('Request aborted'));\n }\n\n if (active < maxConcurrent) {\n active++;\n return Promise.resolve();\n }\n\n if (queue.length >= maxQueueSize) {\n return Promise.reject(\n new Error(`[Accessio] Rate limiter queue size exceeded maxQueueSize (${maxQueueSize})`),\n );\n }\n\n return new Promise((resolve, reject) => {\n let onAbort: (() => void) | undefined;\n\n const item = {\n resolve: () => {\n if (signal && onAbort) {\n signal.removeEventListener('abort', onAbort);\n }\n resolve();\n },\n reject: (err: Error) => {\n if (signal && onAbort) {\n signal.removeEventListener('abort', onAbort);\n }\n reject(err);\n },\n };\n\n queue.push(item);\n\n if (signal) {\n onAbort = () => {\n const index = queue.indexOf(item);\n if (index !== -1) {\n queue.splice(index, 1);\n }\n reject(signal.reason || new Error('Request aborted'));\n };\n signal.addEventListener('abort', onAbort, { once: true });\n }\n });\n }\n\n function release(): void {\n if (destroyed) return;\n if (active <= 0) return;\n\n const next = queue.shift();\n if (next) {\n next.resolve();\n return;\n }\n active--;\n }\n\n function destroy(): void {\n destroyed = true;\n const reason = new Error('[Accessio] Rate limiter destroyed — pending request cancelled');\n while (queue.length > 0) {\n queue.shift()!.reject(reason);\n }\n }\n\n return {\n acquire,\n release,\n destroy,\n get pending() {\n return queue.length;\n },\n get active() {\n return active;\n },\n get destroyed() {\n return destroyed;\n },\n };\n}\n\nexport async function rateLimitedRequest<T = unknown>(\n dispatchFn: (config: AccessioRequestConfig) => Promise<AccessioResponse<T>>,\n limiter: RateLimiter,\n config: AccessioRequestConfig,\n): Promise<AccessioResponse<T>> {\n await limiter.acquire(config.signal);\n try {\n return await dispatchFn(config);\n } finally {\n limiter.release();\n }\n}\n\nexport default createRateLimiter;\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOO,SAAS,kBACd,gBAAwB,UACxB,eAAuB,UACV;AACb,MAAI,kBAAkB,aAAa,CAAC,OAAO,UAAU,aAAa,KAAK,gBAAgB,IAAI;AACzF,UAAM,IAAI;AAAA,MACR,yEAAyE,aAAa;AAAA,IACxF;AAAA,EACF;AACA,MAAI,iBAAiB,aAAa,CAAC,OAAO,UAAU,YAAY,KAAK,eAAe,IAAI;AACtF,UAAM,IAAI;AAAA,MACR,wEAAwE,YAAY;AAAA,IACtF;AAAA,EACF;AACA,MAAI,SAAS;AACb,MAAI,YAAY;AAChB,QAAM,QAAqB,CAAC;AAE5B,WAAS,QAAQ,QAAqC;AACpD,QAAI,WAAW;AACb,aAAO,QAAQ,OAAO,IAAI,MAAM,4CAA4C,CAAC;AAAA,IAC/E;AAEA,QAAI,QAAQ,SAAS;AACnB,aAAO,QAAQ,OAAO,OAAO,UAAU,IAAI,MAAM,iBAAiB,CAAC;AAAA,IACrE;AAEA,QAAI,SAAS,eAAe;AAC1B;AACA,aAAO,QAAQ,QAAQ;AAAA,IACzB;AAEA,QAAI,MAAM,UAAU,cAAc;AAChC,aAAO,QAAQ;AAAA,QACb,IAAI,MAAM,6DAA6D,YAAY,GAAG;AAAA,MACxF;AAAA,IACF;AAEA,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI;AAEJ,YAAM,OAAO;AAAA,QACX,SAAS,MAAM;AACb,cAAI,UAAU,SAAS;AACrB,mBAAO,oBAAoB,SAAS,OAAO;AAAA,UAC7C;AACA,kBAAQ;AAAA,QACV;AAAA,QACA,QAAQ,CAAC,QAAe;AACtB,cAAI,UAAU,SAAS;AACrB,mBAAO,oBAAoB,SAAS,OAAO;AAAA,UAC7C;AACA,iBAAO,GAAG;AAAA,QACZ;AAAA,MACF;AAEA,YAAM,KAAK,IAAI;AAEf,UAAI,QAAQ;AACV,kBAAU,MAAM;AACd,gBAAM,QAAQ,MAAM,QAAQ,IAAI;AAChC,cAAI,UAAU,IAAI;AAChB,kBAAM,OAAO,OAAO,CAAC;AAAA,UACvB;AACA,iBAAO,OAAO,UAAU,IAAI,MAAM,iBAAiB,CAAC;AAAA,QACtD;AACA,eAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAAA,MAC1D;AAAA,IACF,CAAC;AAAA,EACH;AAEA,WAAS,UAAgB;AACvB,QAAI,UAAW;AACf,QAAI,UAAU,EAAG;AAEjB,UAAM,OAAO,MAAM,MAAM;AACzB,QAAI,MAAM;AACR,WAAK,QAAQ;AACb;AAAA,IACF;AACA;AAAA,EACF;AAEA,WAAS,UAAgB;AACvB,gBAAY;AACZ,UAAM,SAAS,IAAI,MAAM,oEAA+D;AACxF,WAAO,MAAM,SAAS,GAAG;AACvB,YAAM,MAAM,EAAG,OAAO,MAAM;AAAA,IAC9B;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,IAAI,UAAU;AACZ,aAAO,MAAM;AAAA,IACf;AAAA,IACA,IAAI,SAAS;AACX,aAAO;AAAA,IACT;AAAA,IACA,IAAI,YAAY;AACd,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,eAAsB,mBACpB,YACA,SACA,QAC8B;AAC9B,QAAM,QAAQ,QAAQ,OAAO,MAAM;AACnC,MAAI;AACF,WAAO,MAAM,WAAW,MAAM;AAAA,EAChC,UAAE;AACA,YAAQ,QAAQ;AAAA,EAClB;AACF;AAEA,IAAO,sBAAQ;","names":[]}
|
package/cjs/helpers/settle.cjs
CHANGED
|
@@ -34,7 +34,7 @@ module.exports = __toCommonJS(settle_exports);
|
|
|
34
34
|
var import_accessioError = __toESM(require("../core/accessioError"), 1);
|
|
35
35
|
function settle(resolve, reject, response, config) {
|
|
36
36
|
const validateStatus = config.validateStatus;
|
|
37
|
-
if (!
|
|
37
|
+
if (!validateStatus || validateStatus(response.status)) {
|
|
38
38
|
resolve(response);
|
|
39
39
|
} else {
|
|
40
40
|
const error = new import_accessioError.default(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/helpers/settle.ts"],"sourcesContent":["import AccessioError from '../core/accessioError';\nimport type { AccessioResponse, AccessioRequestConfig } from '../types';\n\nexport default function settle(\n resolve: (value: AccessioResponse) => void,\n reject: (reason: AccessioError) => void,\n response: AccessioResponse,\n config: AccessioRequestConfig,\n): void {\n const validateStatus = config.validateStatus;\n\n if (!
|
|
1
|
+
{"version":3,"sources":["../../src/helpers/settle.ts"],"sourcesContent":["import AccessioError from '../core/accessioError';\nimport type { AccessioResponse, AccessioRequestConfig } from '../types';\n\nexport default function settle(\n resolve: (value: AccessioResponse) => void,\n reject: (reason: AccessioError) => void,\n response: AccessioResponse,\n config: AccessioRequestConfig,\n): void {\n const validateStatus = config.validateStatus;\n\n if (!validateStatus || validateStatus(response.status)) {\n resolve(response);\n } else {\n const error = new AccessioError(\n `Request failed with status code ${response.status}`,\n response.status >= 400 && response.status < 500\n ? AccessioError.ERR_BAD_REQUEST\n : AccessioError.ERR_BAD_RESPONSE,\n config,\n response.request,\n response,\n );\n reject(error);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2BAA0B;AAGX,SAAR,OACL,SACA,QACA,UACA,QACM;AACN,QAAM,iBAAiB,OAAO;AAE9B,MAAI,CAAC,kBAAkB,eAAe,SAAS,MAAM,GAAG;AACtD,YAAQ,QAAQ;AAAA,EAClB,OAAO;AACL,UAAM,QAAQ,IAAI,qBAAAA;AAAA,MAChB,mCAAmC,SAAS,MAAM;AAAA,MAClD,SAAS,UAAU,OAAO,SAAS,SAAS,MACxC,qBAAAA,QAAc,kBACd,qBAAAA,QAAc;AAAA,MAClB;AAAA,MACA,SAAS;AAAA,MACT;AAAA,IACF;AACA,WAAO,KAAK;AAAA,EACd;AACF;","names":["AccessioError"]}
|
|
@@ -29,7 +29,7 @@ function toFormData(obj, form, namespace) {
|
|
|
29
29
|
}
|
|
30
30
|
if (obj instanceof Date) {
|
|
31
31
|
fd.append(namespace || "", obj.toISOString());
|
|
32
|
-
} else if (typeof obj === "object" && !(obj instanceof File) && !(obj instanceof Blob)) {
|
|
32
|
+
} else if (typeof obj === "object" && !(typeof File !== "undefined" && obj instanceof File) && !(typeof Blob !== "undefined" && obj instanceof Blob)) {
|
|
33
33
|
Object.keys(obj).forEach((key) => {
|
|
34
34
|
if (Array.isArray(obj)) {
|
|
35
35
|
formKey = namespace ? `${namespace}[${key}]` : key;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/helpers/toFormData.ts"],"sourcesContent":["export function toFormData(obj: any, form?: FormData, namespace?: string): FormData {\n const fd = form || new FormData();\n let formKey: string;\n\n if (obj === null || obj === undefined) {\n return fd;\n }\n\n if (obj instanceof Date) {\n fd.append(namespace || '', obj.toISOString());\n } else if (typeof obj === 'object' &&
|
|
1
|
+
{"version":3,"sources":["../../src/helpers/toFormData.ts"],"sourcesContent":["export function toFormData(obj: any, form?: FormData, namespace?: string): FormData {\n const fd = form || new FormData();\n let formKey: string;\n\n if (obj === null || obj === undefined) {\n return fd;\n }\n\n if (obj instanceof Date) {\n fd.append(namespace || '', obj.toISOString());\n } else if (\n typeof obj === 'object' &&\n !(typeof File !== 'undefined' && obj instanceof File) &&\n !(typeof Blob !== 'undefined' && obj instanceof Blob)\n ) {\n Object.keys(obj).forEach((key) => {\n if (Array.isArray(obj)) {\n formKey = namespace ? `${namespace}[${key}]` : key;\n } else {\n formKey = namespace ? `${namespace}.${key}` : key;\n }\n toFormData(obj[key], fd, formKey);\n });\n } else {\n fd.append(namespace || '', obj);\n }\n\n return fd;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAO,SAAS,WAAW,KAAU,MAAiB,WAA8B;AAClF,QAAM,KAAK,QAAQ,IAAI,SAAS;AAChC,MAAI;AAEJ,MAAI,QAAQ,QAAQ,QAAQ,QAAW;AACrC,WAAO;AAAA,EACT;AAEA,MAAI,eAAe,MAAM;AACvB,OAAG,OAAO,aAAa,IAAI,IAAI,YAAY,CAAC;AAAA,EAC9C,WACE,OAAO,QAAQ,YACf,EAAE,OAAO,SAAS,eAAe,eAAe,SAChD,EAAE,OAAO,SAAS,eAAe,eAAe,OAChD;AACA,WAAO,KAAK,GAAG,EAAE,QAAQ,CAAC,QAAQ;AAChC,UAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,kBAAU,YAAY,GAAG,SAAS,IAAI,GAAG,MAAM;AAAA,MACjD,OAAO;AACL,kBAAU,YAAY,GAAG,SAAS,IAAI,GAAG,KAAK;AAAA,MAChD;AACA,iBAAW,IAAI,GAAG,GAAG,IAAI,OAAO;AAAA,IAClC,CAAC;AAAA,EACH,OAAO;AACL,OAAG,OAAO,aAAa,IAAI,GAAG;AAAA,EAChC;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -23,41 +23,48 @@ __export(interceptorManager_exports, {
|
|
|
23
23
|
});
|
|
24
24
|
module.exports = __toCommonJS(interceptorManager_exports);
|
|
25
25
|
class InterceptorManager {
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
_handlers;
|
|
27
|
+
_nextId;
|
|
28
28
|
constructor() {
|
|
29
|
-
this.
|
|
30
|
-
this.
|
|
29
|
+
this._handlers = /* @__PURE__ */ new Map();
|
|
30
|
+
this._nextId = 0;
|
|
31
31
|
}
|
|
32
32
|
use(fulfilled, rejected, options = {}) {
|
|
33
|
-
this.
|
|
33
|
+
const id = this._nextId++;
|
|
34
|
+
this._handlers.set(id, {
|
|
34
35
|
fulfilled: fulfilled || null,
|
|
35
36
|
rejected: rejected || null,
|
|
36
37
|
synchronous: options.synchronous || false,
|
|
37
38
|
runWhen: options.runWhen || null
|
|
38
39
|
});
|
|
39
|
-
|
|
40
|
-
return this.handlers.length - 1;
|
|
40
|
+
return id;
|
|
41
41
|
}
|
|
42
42
|
eject(id) {
|
|
43
|
-
|
|
44
|
-
this.handlers[id] = null;
|
|
45
|
-
this._activeCount--;
|
|
46
|
-
}
|
|
43
|
+
this._handlers.delete(id);
|
|
47
44
|
}
|
|
48
45
|
clear() {
|
|
49
|
-
this.
|
|
50
|
-
this._activeCount = 0;
|
|
46
|
+
this._handlers.clear();
|
|
51
47
|
}
|
|
52
48
|
forEach(fn) {
|
|
53
|
-
for (const handler of this.
|
|
54
|
-
|
|
55
|
-
fn(handler);
|
|
56
|
-
}
|
|
49
|
+
for (const handler of this._handlers.values()) {
|
|
50
|
+
fn(handler);
|
|
57
51
|
}
|
|
58
52
|
}
|
|
59
53
|
get size() {
|
|
60
|
-
return this.
|
|
54
|
+
return this._handlers.size;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Snapshot view for backward-compat introspection. Slot index = interceptor ID;
|
|
58
|
+
* ejected IDs appear as `null`. Reading this builds a fresh array each time —
|
|
59
|
+
* prefer `forEach`/`size` in hot paths.
|
|
60
|
+
*/
|
|
61
|
+
get handlers() {
|
|
62
|
+
const max = this._nextId;
|
|
63
|
+
const out = new Array(max);
|
|
64
|
+
for (let i = 0; i < max; i++) {
|
|
65
|
+
out[i] = this._handlers.get(i) ?? null;
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
61
68
|
}
|
|
62
69
|
}
|
|
63
70
|
var interceptorManager_default = InterceptorManager;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/interceptors/interceptorManager.ts"],"sourcesContent":["import type { TransformFunction, InterceptorHandler, InterceptorOptions } from '../types';\n\nexport class InterceptorManager {\n
|
|
1
|
+
{"version":3,"sources":["../../src/interceptors/interceptorManager.ts"],"sourcesContent":["import type { TransformFunction, InterceptorHandler, InterceptorOptions } from '../types';\n\nexport class InterceptorManager {\n private _handlers: Map<number, InterceptorHandler>;\n private _nextId: number;\n\n constructor() {\n this._handlers = new Map();\n this._nextId = 0;\n }\n\n use(\n fulfilled: TransformFunction | null,\n rejected?: ((error: unknown) => unknown) | null,\n options: InterceptorOptions = {},\n ): number {\n const id = this._nextId++;\n this._handlers.set(id, {\n fulfilled: fulfilled || null,\n rejected: rejected || null,\n synchronous: options.synchronous || false,\n runWhen: options.runWhen || null,\n });\n return id;\n }\n\n eject(id: number): void {\n this._handlers.delete(id);\n }\n\n clear(): void {\n this._handlers.clear();\n }\n\n forEach(fn: (handler: InterceptorHandler) => void): void {\n for (const handler of this._handlers.values()) {\n fn(handler);\n }\n }\n\n get size(): number {\n return this._handlers.size;\n }\n\n /**\n * Snapshot view for backward-compat introspection. Slot index = interceptor ID;\n * ejected IDs appear as `null`. Reading this builds a fresh array each time —\n * prefer `forEach`/`size` in hot paths.\n */\n get handlers(): Array<InterceptorHandler | null> {\n const max = this._nextId;\n const out: Array<InterceptorHandler | null> = new Array(max);\n for (let i = 0; i < max; i++) {\n out[i] = this._handlers.get(i) ?? null;\n }\n return out;\n }\n}\n\nexport default InterceptorManager;\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEO,MAAM,mBAAmB;AAAA,EACtB;AAAA,EACA;AAAA,EAER,cAAc;AACZ,SAAK,YAAY,oBAAI,IAAI;AACzB,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,IACE,WACA,UACA,UAA8B,CAAC,GACvB;AACR,UAAM,KAAK,KAAK;AAChB,SAAK,UAAU,IAAI,IAAI;AAAA,MACrB,WAAW,aAAa;AAAA,MACxB,UAAU,YAAY;AAAA,MACtB,aAAa,QAAQ,eAAe;AAAA,MACpC,SAAS,QAAQ,WAAW;AAAA,IAC9B,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,IAAkB;AACtB,SAAK,UAAU,OAAO,EAAE;AAAA,EAC1B;AAAA,EAEA,QAAc;AACZ,SAAK,UAAU,MAAM;AAAA,EACvB;AAAA,EAEA,QAAQ,IAAiD;AACvD,eAAW,WAAW,KAAK,UAAU,OAAO,GAAG;AAC7C,SAAG,OAAO;AAAA,IACZ;AAAA,EACF;AAAA,EAEA,IAAI,OAAe;AACjB,WAAO,KAAK,UAAU;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,WAA6C;AAC/C,UAAM,MAAM,KAAK;AACjB,UAAM,MAAwC,IAAI,MAAM,GAAG;AAC3D,aAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,UAAI,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,KAAK;AAAA,IACpC;AACA,WAAO;AAAA,EACT;AACF;AAEA,IAAO,6BAAQ;","names":[]}
|
package/index.d.ts
CHANGED
|
@@ -6,19 +6,44 @@
|
|
|
6
6
|
* Avoids ambiguous intersection of Record<string, string> & { common?, get?, ... }
|
|
7
7
|
* which TypeScript accepts but can confuse type consumers.
|
|
8
8
|
*/
|
|
9
|
+
export type HeaderValue = string | string[];
|
|
10
|
+
|
|
9
11
|
export type AccessioHeaders = {
|
|
10
|
-
common?: Record<string,
|
|
11
|
-
get?: Record<string,
|
|
12
|
-
post?: Record<string,
|
|
13
|
-
put?: Record<string,
|
|
14
|
-
patch?: Record<string,
|
|
15
|
-
delete?: Record<string,
|
|
16
|
-
head?: Record<string,
|
|
17
|
-
options?: Record<string,
|
|
18
|
-
/** Any additional custom headers */
|
|
19
|
-
[key: string]:
|
|
12
|
+
common?: Record<string, HeaderValue>;
|
|
13
|
+
get?: Record<string, HeaderValue>;
|
|
14
|
+
post?: Record<string, HeaderValue>;
|
|
15
|
+
put?: Record<string, HeaderValue>;
|
|
16
|
+
patch?: Record<string, HeaderValue>;
|
|
17
|
+
delete?: Record<string, HeaderValue>;
|
|
18
|
+
head?: Record<string, HeaderValue>;
|
|
19
|
+
options?: Record<string, HeaderValue>;
|
|
20
|
+
/** Any additional custom headers (flat or per-method-nested) */
|
|
21
|
+
[key: string]: HeaderValue | Record<string, HeaderValue> | undefined;
|
|
20
22
|
};
|
|
21
23
|
|
|
24
|
+
export interface AccessioHooks {
|
|
25
|
+
onBeforeRequest?: (config: AccessioRequestConfig) => void | Promise<void>;
|
|
26
|
+
onRequestResponse?: (response: AccessioResponse) => void | Promise<void>;
|
|
27
|
+
onRequestError?: (error: AccessioError) => void | Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface CacheProvider {
|
|
31
|
+
get(key: string): Promise<any> | any;
|
|
32
|
+
set(key: string, value: any, ttl?: number): Promise<void> | void;
|
|
33
|
+
delete(key: string): Promise<void> | void;
|
|
34
|
+
clear(): Promise<void> | void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface SchemaValidator<T = any> {
|
|
38
|
+
parse(data: unknown): T;
|
|
39
|
+
parseAsync?(data: unknown): Promise<T>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type TransformFunction = (
|
|
43
|
+
data: any,
|
|
44
|
+
headers: Record<string, HeaderValue>,
|
|
45
|
+
) => any | Promise<any>;
|
|
46
|
+
|
|
22
47
|
export interface AccessioRequestConfig {
|
|
23
48
|
/** Request URL (path or full URL) */
|
|
24
49
|
url?: string;
|
|
@@ -66,17 +91,13 @@ export interface AccessioRequestConfig {
|
|
|
66
91
|
responseType?: "json" | "text" | "blob" | "arraybuffer" | "stream";
|
|
67
92
|
|
|
68
93
|
/** Transform functions applied to request data */
|
|
69
|
-
transformRequest?:
|
|
70
|
-
(data: any, headers?: Record<string, string>) => any
|
|
71
|
-
>;
|
|
94
|
+
transformRequest?: TransformFunction | TransformFunction[];
|
|
72
95
|
|
|
73
96
|
/** Transform functions applied to response data */
|
|
74
|
-
transformResponse?:
|
|
75
|
-
(data: any, headers?: Record<string, string>) => any
|
|
76
|
-
>;
|
|
97
|
+
transformResponse?: TransformFunction | TransformFunction[];
|
|
77
98
|
|
|
78
|
-
/** Function to determine if a status code should resolve or reject */
|
|
79
|
-
validateStatus?: (status: number) => boolean;
|
|
99
|
+
/** Function to determine if a status code should resolve or reject. Pass `null` to disable. */
|
|
100
|
+
validateStatus?: ((status: number) => boolean) | null;
|
|
80
101
|
|
|
81
102
|
/** AbortSignal for request cancellation */
|
|
82
103
|
signal?: AbortSignal;
|
|
@@ -105,6 +126,9 @@ export interface AccessioRequestConfig {
|
|
|
105
126
|
config: AccessioRequestConfig,
|
|
106
127
|
) => void;
|
|
107
128
|
|
|
129
|
+
/** Retry on HTTP 429 (Too Many Requests), honoring Retry-After when present */
|
|
130
|
+
retryOn429?: boolean;
|
|
131
|
+
|
|
108
132
|
// ── Debug ──────────────────────────────────────────
|
|
109
133
|
|
|
110
134
|
/** Enable debug logging for this request */
|
|
@@ -115,6 +139,43 @@ export interface AccessioRequestConfig {
|
|
|
115
139
|
/** Rate limiter instance to control concurrent requests */
|
|
116
140
|
rateLimiter?: RateLimiter;
|
|
117
141
|
|
|
142
|
+
// ── Caching / dedupe ───────────────────────────────
|
|
143
|
+
|
|
144
|
+
/** Dedupe in-flight identical GETs (cache/dedupe key is per fullURL+auth+accept+responseType+withCredentials) */
|
|
145
|
+
dedupe?: boolean;
|
|
146
|
+
|
|
147
|
+
/** Enable response caching for GETs. `true` uses the default in-memory cache; pass a provider to customize. */
|
|
148
|
+
cache?: boolean | CacheProvider;
|
|
149
|
+
|
|
150
|
+
/** TTL in ms for cached responses (when supported by the provider) */
|
|
151
|
+
cacheTTL?: number;
|
|
152
|
+
|
|
153
|
+
// ── Response handling ──────────────────────────────
|
|
154
|
+
|
|
155
|
+
/** Maximum allowed response Content-Length in bytes */
|
|
156
|
+
maxContentLength?: number;
|
|
157
|
+
|
|
158
|
+
/** Schema validator applied to response data (Zod-compatible: `parse` / `parseAsync`) */
|
|
159
|
+
schema?: SchemaValidator;
|
|
160
|
+
|
|
161
|
+
/** Progress callback for downloads. Wraps the response body in a passthrough ReadableStream. */
|
|
162
|
+
onDownloadProgress?: (progressEvent: { loaded: number; total: number }) => void;
|
|
163
|
+
|
|
164
|
+
// ── Lifecycle hooks ────────────────────────────────
|
|
165
|
+
|
|
166
|
+
hooks?: AccessioHooks;
|
|
167
|
+
|
|
168
|
+
// ── Adapter / runtime ──────────────────────────────
|
|
169
|
+
|
|
170
|
+
/** Custom fetch implementation (defaults to global `fetch`) */
|
|
171
|
+
fetch?: typeof fetch;
|
|
172
|
+
|
|
173
|
+
/** Undici dispatcher (Node.js) */
|
|
174
|
+
dispatcher?: unknown;
|
|
175
|
+
|
|
176
|
+
/** Node.js http(s).Agent */
|
|
177
|
+
agent?: unknown;
|
|
178
|
+
|
|
118
179
|
/** Allow any additional custom properties */
|
|
119
180
|
[key: string]: any;
|
|
120
181
|
}
|
|
@@ -129,8 +190,8 @@ export interface AccessioResponse<T = any> {
|
|
|
129
190
|
/** HTTP status text */
|
|
130
191
|
statusText: string;
|
|
131
192
|
|
|
132
|
-
/** Response headers (lowercased keys) */
|
|
133
|
-
headers: Record<string,
|
|
193
|
+
/** Response headers (lowercased keys; repeated headers become string arrays) */
|
|
194
|
+
headers: Record<string, HeaderValue>;
|
|
134
195
|
|
|
135
196
|
/** The config used for this request */
|
|
136
197
|
config: AccessioRequestConfig;
|
|
@@ -458,4 +519,4 @@ export const createRateLimiter: (maxConcurrent?: number) => RateLimiter;
|
|
|
458
519
|
|
|
459
520
|
export function logRequest(config: AccessioRequestConfig, fullUrl: string): void;
|
|
460
521
|
export function logResponse(response: AccessioResponse): void;
|
|
461
|
-
export function logError(error:
|
|
522
|
+
export function logError(error: AccessioError, config?: AccessioRequestConfig): void;
|