accessio 1.4.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 +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/fetchAdapter.cjs +4 -0
- package/cjs/core/fetchAdapter.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/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/package.json +2 -2
- package/src/accessio.ts +44 -15
- package/src/core/accessioError.ts +31 -0
- package/src/core/fetchAdapter.ts +3 -0
- package/src/core/retry.ts +8 -5
- package/src/helpers/flattenHeaders.ts +4 -1
- package/src/helpers/rateLimiter.ts +35 -3
- package/src/helpers/toFormData.ts +5 -1
- package/src/types.ts +2 -1
package/cjs/accessio.cjs
CHANGED
|
@@ -200,16 +200,12 @@ class Accessio {
|
|
|
200
200
|
);
|
|
201
201
|
if (!response.data) return;
|
|
202
202
|
const reader = response.data.getReader();
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
const lines = buffer.split("\n");
|
|
210
|
-
buffer = lines.pop() || "";
|
|
211
|
-
for (const line of lines) {
|
|
212
|
-
if (line.trim().startsWith("data:")) {
|
|
203
|
+
try {
|
|
204
|
+
const decoder = new TextDecoder();
|
|
205
|
+
let buffer = "";
|
|
206
|
+
const processLine = function* (line) {
|
|
207
|
+
const trimmed = line.trim();
|
|
208
|
+
if (trimmed.startsWith("data:")) {
|
|
213
209
|
const dataStr = line.replace(/^data:\s*/, "");
|
|
214
210
|
if (dataStr === "[DONE]") return;
|
|
215
211
|
try {
|
|
@@ -217,13 +213,33 @@ class Accessio {
|
|
|
217
213
|
} catch (e) {
|
|
218
214
|
yield dataStr;
|
|
219
215
|
}
|
|
220
|
-
} else if (
|
|
216
|
+
} else if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
221
217
|
try {
|
|
222
218
|
yield JSON.parse(line);
|
|
223
219
|
} catch (e) {
|
|
224
220
|
}
|
|
225
221
|
}
|
|
222
|
+
};
|
|
223
|
+
while (true) {
|
|
224
|
+
const { done, value } = await reader.read();
|
|
225
|
+
if (done) break;
|
|
226
|
+
buffer += decoder.decode(value, { stream: true });
|
|
227
|
+
const lines = buffer.split("\n");
|
|
228
|
+
buffer = lines.pop() || "";
|
|
229
|
+
for (const line of lines) {
|
|
230
|
+
yield* processLine(line);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
buffer += decoder.decode(new Uint8Array(), { stream: false });
|
|
234
|
+
if (buffer.trim()) {
|
|
235
|
+
yield* processLine(buffer);
|
|
236
|
+
}
|
|
237
|
+
} finally {
|
|
238
|
+
try {
|
|
239
|
+
await reader.cancel();
|
|
240
|
+
} catch {
|
|
226
241
|
}
|
|
242
|
+
reader.releaseLock();
|
|
227
243
|
}
|
|
228
244
|
}
|
|
229
245
|
async *autoPaginate(url, config) {
|
|
@@ -231,13 +247,14 @@ class Accessio {
|
|
|
231
247
|
let currentConfig = config || {};
|
|
232
248
|
while (nextUrl) {
|
|
233
249
|
const response = await this.get(nextUrl, currentConfig);
|
|
234
|
-
const
|
|
250
|
+
const data = response.data;
|
|
251
|
+
const items = Array.isArray(data) ? data : data && typeof data === "object" ? data.data : null;
|
|
235
252
|
if (Array.isArray(items)) {
|
|
236
253
|
for (const item of items) {
|
|
237
254
|
yield item;
|
|
238
255
|
}
|
|
239
256
|
}
|
|
240
|
-
nextUrl =
|
|
257
|
+
nextUrl = data && typeof data === "object" ? data.next || data.links?.next || null : null;
|
|
241
258
|
if (nextUrl) {
|
|
242
259
|
currentConfig = (0, import_mergeConfig.default)(currentConfig, { url: nextUrl, params: {} });
|
|
243
260
|
}
|
package/cjs/accessio.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/accessio.ts"],"sourcesContent":["import InterceptorManager from './interceptors/interceptorManager';\nimport AccessioError from './core/accessioError';\nimport mergeConfig from './core/mergeConfig';\nimport dispatchRequest from './core/request';\nimport buildURL from './core/buildURL';\nimport retryRequest from './core/retry';\nimport { logRequest, logResponse, logError } from './helpers/debug';\nimport { rateLimitedRequest } from './helpers/rateLimiter';\nimport { toFormData } from './helpers/toFormData';\nimport type {\n AccessioRequestConfig,\n AccessioResponse,\n Interceptors,\n InterceptorHandler,\n} from './types';\nimport defaultsConfig from './defaults/index';\n\nfunction runRequestInterceptorsSync(\n startConfig: AccessioRequestConfig,\n interceptors: InterceptorHandler[],\n): Promise<AccessioRequestConfig> {\n let cfg = startConfig;\n let rejectReason: any = null;\n let isRejected = false;\n\n for (const interceptor of interceptors) {\n if (!isRejected) {\n try {\n if (interceptor.fulfilled) {\n cfg = (interceptor.fulfilled as any)(cfg) as AccessioRequestConfig;\n }\n } catch (err) {\n rejectReason = err;\n isRejected = true;\n }\n } else if (interceptor.rejected) {\n try {\n cfg = interceptor.rejected(rejectReason) as AccessioRequestConfig;\n isRejected = false;\n } catch (err) {\n rejectReason = err;\n isRejected = true;\n }\n }\n }\n\n return isRejected ? Promise.reject(rejectReason) : Promise.resolve(cfg);\n}\n\nfunction runRequestInterceptorsAsync(\n startConfig: AccessioRequestConfig,\n interceptors: InterceptorHandler[],\n): Promise<AccessioRequestConfig> {\n let promise: Promise<any> = Promise.resolve(startConfig);\n for (const interceptor of interceptors) {\n promise = promise.then(\n (value: any) => (interceptor.fulfilled ? (interceptor.fulfilled as any)(value) : value),\n interceptor.rejected ?? undefined,\n );\n }\n return promise as Promise<AccessioRequestConfig>;\n}\n\nfunction dispatchAndRetry(cfg: AccessioRequestConfig): Promise<AccessioResponse> {\n const fullUrl = buildURL(cfg.url ?? '', cfg.baseURL, cfg.params, cfg.paramsSerializer);\n logRequest(cfg, fullUrl);\n\n const enrichedCfg = fullUrl !== (cfg.url || '') ? { ...cfg, _builtUrl: fullUrl } : cfg;\n\n const dispatchFn = cfg.rateLimiter\n ? (config: AccessioRequestConfig) =>\n rateLimitedRequest(dispatchRequest, config.rateLimiter!, config)\n : dispatchRequest;\n\n return retryRequest(dispatchFn, enrichedCfg);\n}\n\nexport class Accessio {\n defaults: AccessioRequestConfig;\n interceptors: Interceptors;\n\n constructor(instanceConfig: AccessioRequestConfig = {}) {\n this.defaults = mergeConfig(defaultsConfig, instanceConfig);\n this.interceptors = {\n request: new InterceptorManager(),\n response: new InterceptorManager(),\n };\n }\n\n request<T = any>(\n configOrUrl: string | AccessioRequestConfig,\n config?: AccessioRequestConfig,\n ): Promise<AccessioResponse<T>> {\n if (typeof configOrUrl === 'string') {\n config = { ...config, url: configOrUrl };\n } else {\n config = configOrUrl ? { ...configOrUrl } : {};\n }\n\n const mergedConfig = mergeConfig(this.defaults, config);\n\n mergedConfig.method = (mergedConfig.method || 'get').toLowerCase();\n\n if (!mergedConfig.url && !mergedConfig.baseURL) {\n throw new AccessioError(\n 'Request URL is required. Provide a `url` or `baseURL` in the config.',\n AccessioError.ERR_BAD_OPTION,\n mergedConfig,\n null,\n null,\n );\n }\n\n const { requestInterceptors, responseInterceptors, synchronous } =\n this.collectInterceptors(mergedConfig);\n\n let promise: Promise<any> = synchronous\n ? runRequestInterceptorsSync(mergedConfig, requestInterceptors)\n : runRequestInterceptorsAsync(mergedConfig, requestInterceptors);\n\n promise = promise.then((cfg: AccessioRequestConfig) => dispatchAndRetry(cfg));\n\n promise = promise.then(\n (value: AccessioResponse) => {\n logResponse(value);\n return value;\n },\n (error: any) => {\n logError(error, mergedConfig);\n throw error;\n },\n );\n\n for (const interceptor of responseInterceptors) {\n promise = promise.then((value: any) => {\n if (interceptor.fulfilled) {\n return (interceptor.fulfilled as any)(value);\n }\n return value;\n }, interceptor.rejected ?? undefined);\n }\n\n return promise;\n }\n\n private collectInterceptors(mergedConfig: AccessioRequestConfig): {\n requestInterceptors: InterceptorHandler[];\n responseInterceptors: InterceptorHandler[];\n synchronous: boolean;\n } {\n const requestInterceptors: InterceptorHandler[] = [];\n const responseInterceptors: InterceptorHandler[] = [];\n let synchronous = true;\n\n this.interceptors.request.forEach((interceptor: InterceptorHandler) => {\n if (interceptor.runWhen && !interceptor.runWhen(mergedConfig)) return;\n synchronous = synchronous && interceptor.synchronous;\n requestInterceptors.unshift(interceptor);\n });\n\n this.interceptors.response.forEach((interceptor: InterceptorHandler) => {\n responseInterceptors.push(interceptor);\n });\n\n return { requestInterceptors, responseInterceptors, synchronous };\n }\n\n getUri(config?: AccessioRequestConfig): string {\n const merged = mergeConfig(this.defaults, config);\n return buildURL(merged.url ?? '', merged.baseURL, merged.params, merged.paramsSerializer);\n }\n\n get<T = any>(url: string, config?: AccessioRequestConfig): Promise<AccessioResponse<T>> {\n return this.request<T>(mergeConfig(config || {}, { method: 'get', url }));\n }\n\n delete<T = any>(url: string, config?: AccessioRequestConfig): Promise<AccessioResponse<T>> {\n return this.request<T>(mergeConfig(config || {}, { method: 'delete', url }));\n }\n\n head<T = any>(url: string, config?: AccessioRequestConfig): Promise<AccessioResponse<T>> {\n return this.request<T>(mergeConfig(config || {}, { method: 'head', url }));\n }\n\n options<T = any>(url: string, config?: AccessioRequestConfig): Promise<AccessioResponse<T>> {\n return this.request<T>(mergeConfig(config || {}, { method: 'options', url }));\n }\n\n post<T = any>(\n url: string,\n data?: any,\n config?: AccessioRequestConfig,\n ): Promise<AccessioResponse<T>> {\n return this.request<T>(mergeConfig(config || {}, { method: 'post', url, data }));\n }\n\n put<T = any>(\n url: string,\n data?: any,\n config?: AccessioRequestConfig,\n ): Promise<AccessioResponse<T>> {\n return this.request<T>(mergeConfig(config || {}, { method: 'put', url, data }));\n }\n\n patch<T = any>(\n url: string,\n data?: any,\n config?: AccessioRequestConfig,\n ): Promise<AccessioResponse<T>> {\n return this.request<T>(mergeConfig(config || {}, { method: 'patch', url, data }));\n }\n\n private formRequest<T = any>(\n method: 'post' | 'put' | 'patch',\n url: string,\n data?: any,\n config?: AccessioRequestConfig,\n ): Promise<AccessioResponse<T>> {\n const formData = data && !(data instanceof FormData) ? toFormData(data) : data;\n return this.request<T>(\n mergeConfig(config || {}, {\n method,\n url,\n data: formData,\n headers: { 'Content-Type': 'multipart/form-data' },\n }),\n );\n }\n\n postForm<T = any>(\n url: string,\n data?: any,\n config?: AccessioRequestConfig,\n ): Promise<AccessioResponse<T>> {\n return this.formRequest<T>('post', url, data, config);\n }\n\n putForm<T = any>(\n url: string,\n data?: any,\n config?: AccessioRequestConfig,\n ): Promise<AccessioResponse<T>> {\n return this.formRequest<T>('put', url, data, config);\n }\n\n patchForm<T = any>(\n url: string,\n data?: any,\n config?: AccessioRequestConfig,\n ): Promise<AccessioResponse<T>> {\n return this.formRequest<T>('patch', url, data, config);\n }\n\n async *stream<T = any>(\n url: string,\n config?: AccessioRequestConfig,\n ): AsyncGenerator<T, void, unknown> {\n const response = await this.request<ReadableStream<Uint8Array>>(\n mergeConfig(config || {}, { method: 'get', url, responseType: 'stream' }),\n );\n if (!response.data) return;\n\n const reader = response.data.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (line.trim().startsWith('data:')) {\n const dataStr = line.replace(/^data:\\s*/, '');\n if (dataStr === '[DONE]') return;\n try {\n yield JSON.parse(dataStr);\n } catch (e) {\n yield dataStr as any;\n }\n } else if (line.trim().startsWith('{') || line.trim().startsWith('[')) {\n try {\n yield JSON.parse(line);\n } catch (e) {\n // ignore partial json\n }\n }\n }\n }\n }\n\n async *autoPaginate<T = any>(\n url: string,\n config?: AccessioRequestConfig,\n ): AsyncGenerator<T, void, unknown> {\n let nextUrl: string | null = url;\n let currentConfig = config || {};\n\n while (nextUrl) {\n const response: AccessioResponse<any> = await this.get(nextUrl, currentConfig);\n\n const items = Array.isArray(response.data) ? response.data : response.data.data;\n if (Array.isArray(items)) {\n for (const item of items) {\n yield item;\n }\n }\n\n nextUrl = response.data.next || response.data.links?.next || null;\n if (nextUrl) {\n currentConfig = mergeConfig(currentConfig, { url: nextUrl, params: {} });\n }\n }\n }\n\n gql<T = any>(\n url: string,\n query: string,\n variables?: Record<string, any>,\n config?: AccessioRequestConfig,\n ): Promise<AccessioResponse<T>> {\n return this.post<T>(url, { query, variables }, config);\n }\n}\n\nexport default Accessio;\n"],"mappings":"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","names":["buildURL","dispatchRequest","retryRequest","mergeConfig","defaultsConfig","InterceptorManager","AccessioError"]}
|
|
1
|
+
{"version":3,"sources":["../src/accessio.ts"],"sourcesContent":["import InterceptorManager from './interceptors/interceptorManager';\nimport AccessioError from './core/accessioError';\nimport mergeConfig from './core/mergeConfig';\nimport dispatchRequest from './core/request';\nimport buildURL from './core/buildURL';\nimport retryRequest from './core/retry';\nimport { logRequest, logResponse, logError } from './helpers/debug';\nimport { rateLimitedRequest } from './helpers/rateLimiter';\nimport { toFormData } from './helpers/toFormData';\nimport type {\n AccessioRequestConfig,\n AccessioResponse,\n Interceptors,\n InterceptorHandler,\n} from './types';\nimport defaultsConfig from './defaults/index';\n\nfunction runRequestInterceptorsSync(\n startConfig: AccessioRequestConfig,\n interceptors: InterceptorHandler[],\n): Promise<AccessioRequestConfig> {\n let cfg = startConfig;\n let rejectReason: any = null;\n let isRejected = false;\n\n for (const interceptor of interceptors) {\n if (!isRejected) {\n try {\n if (interceptor.fulfilled) {\n cfg = (interceptor.fulfilled as any)(cfg) as AccessioRequestConfig;\n }\n } catch (err) {\n rejectReason = err;\n isRejected = true;\n }\n } else if (interceptor.rejected) {\n try {\n cfg = interceptor.rejected(rejectReason) as AccessioRequestConfig;\n isRejected = false;\n } catch (err) {\n rejectReason = err;\n isRejected = true;\n }\n }\n }\n\n return isRejected ? Promise.reject(rejectReason) : Promise.resolve(cfg);\n}\n\nfunction runRequestInterceptorsAsync(\n startConfig: AccessioRequestConfig,\n interceptors: InterceptorHandler[],\n): Promise<AccessioRequestConfig> {\n let promise: Promise<any> = Promise.resolve(startConfig);\n for (const interceptor of interceptors) {\n promise = promise.then(\n (value: any) => (interceptor.fulfilled ? (interceptor.fulfilled as any)(value) : value),\n interceptor.rejected ?? undefined,\n );\n }\n return promise as Promise<AccessioRequestConfig>;\n}\n\nfunction dispatchAndRetry(cfg: AccessioRequestConfig): Promise<AccessioResponse> {\n const fullUrl = buildURL(cfg.url ?? '', cfg.baseURL, cfg.params, cfg.paramsSerializer);\n logRequest(cfg, fullUrl);\n\n const enrichedCfg = fullUrl !== (cfg.url || '') ? { ...cfg, _builtUrl: fullUrl } : cfg;\n\n const dispatchFn = cfg.rateLimiter\n ? (config: AccessioRequestConfig) =>\n rateLimitedRequest(dispatchRequest, config.rateLimiter!, config)\n : dispatchRequest;\n\n return retryRequest(dispatchFn, enrichedCfg);\n}\n\nexport class Accessio {\n defaults: AccessioRequestConfig;\n interceptors: Interceptors;\n\n constructor(instanceConfig: AccessioRequestConfig = {}) {\n this.defaults = mergeConfig(defaultsConfig, instanceConfig);\n this.interceptors = {\n request: new InterceptorManager(),\n response: new InterceptorManager(),\n };\n }\n\n request<T = any>(\n configOrUrl: string | AccessioRequestConfig,\n config?: AccessioRequestConfig,\n ): Promise<AccessioResponse<T>> {\n if (typeof configOrUrl === 'string') {\n config = { ...config, url: configOrUrl };\n } else {\n config = configOrUrl ? { ...configOrUrl } : {};\n }\n\n const mergedConfig = mergeConfig(this.defaults, config);\n\n mergedConfig.method = (mergedConfig.method || 'get').toLowerCase();\n\n if (!mergedConfig.url && !mergedConfig.baseURL) {\n throw new AccessioError(\n 'Request URL is required. Provide a `url` or `baseURL` in the config.',\n AccessioError.ERR_BAD_OPTION,\n mergedConfig,\n null,\n null,\n );\n }\n\n const { requestInterceptors, responseInterceptors, synchronous } =\n this.collectInterceptors(mergedConfig);\n\n let promise: Promise<any> = synchronous\n ? runRequestInterceptorsSync(mergedConfig, requestInterceptors)\n : runRequestInterceptorsAsync(mergedConfig, requestInterceptors);\n\n promise = promise.then((cfg: AccessioRequestConfig) => dispatchAndRetry(cfg));\n\n promise = promise.then(\n (value: AccessioResponse) => {\n logResponse(value);\n return value;\n },\n (error: any) => {\n logError(error, mergedConfig);\n throw error;\n },\n );\n\n for (const interceptor of responseInterceptors) {\n promise = promise.then((value: any) => {\n if (interceptor.fulfilled) {\n return (interceptor.fulfilled as any)(value);\n }\n return value;\n }, interceptor.rejected ?? undefined);\n }\n\n return promise;\n }\n\n private collectInterceptors(mergedConfig: AccessioRequestConfig): {\n requestInterceptors: InterceptorHandler[];\n responseInterceptors: InterceptorHandler[];\n synchronous: boolean;\n } {\n const requestInterceptors: InterceptorHandler[] = [];\n const responseInterceptors: InterceptorHandler[] = [];\n let synchronous = true;\n\n this.interceptors.request.forEach((interceptor: InterceptorHandler) => {\n if (interceptor.runWhen && !interceptor.runWhen(mergedConfig)) return;\n synchronous = synchronous && interceptor.synchronous;\n requestInterceptors.unshift(interceptor);\n });\n\n this.interceptors.response.forEach((interceptor: InterceptorHandler) => {\n responseInterceptors.push(interceptor);\n });\n\n return { requestInterceptors, responseInterceptors, synchronous };\n }\n\n getUri(config?: AccessioRequestConfig): string {\n const merged = mergeConfig(this.defaults, config);\n return buildURL(merged.url ?? '', merged.baseURL, merged.params, merged.paramsSerializer);\n }\n\n get<T = any>(url: string, config?: AccessioRequestConfig): Promise<AccessioResponse<T>> {\n return this.request<T>(mergeConfig(config || {}, { method: 'get', url }));\n }\n\n delete<T = any>(url: string, config?: AccessioRequestConfig): Promise<AccessioResponse<T>> {\n return this.request<T>(mergeConfig(config || {}, { method: 'delete', url }));\n }\n\n head<T = any>(url: string, config?: AccessioRequestConfig): Promise<AccessioResponse<T>> {\n return this.request<T>(mergeConfig(config || {}, { method: 'head', url }));\n }\n\n options<T = any>(url: string, config?: AccessioRequestConfig): Promise<AccessioResponse<T>> {\n return this.request<T>(mergeConfig(config || {}, { method: 'options', url }));\n }\n\n post<T = any>(\n url: string,\n data?: any,\n config?: AccessioRequestConfig,\n ): Promise<AccessioResponse<T>> {\n return this.request<T>(mergeConfig(config || {}, { method: 'post', url, data }));\n }\n\n put<T = any>(\n url: string,\n data?: any,\n config?: AccessioRequestConfig,\n ): Promise<AccessioResponse<T>> {\n return this.request<T>(mergeConfig(config || {}, { method: 'put', url, data }));\n }\n\n patch<T = any>(\n url: string,\n data?: any,\n config?: AccessioRequestConfig,\n ): Promise<AccessioResponse<T>> {\n return this.request<T>(mergeConfig(config || {}, { method: 'patch', url, data }));\n }\n\n private formRequest<T = any>(\n method: 'post' | 'put' | 'patch',\n url: string,\n data?: any,\n config?: AccessioRequestConfig,\n ): Promise<AccessioResponse<T>> {\n const formData = data && !(data instanceof FormData) ? toFormData(data) : data;\n return this.request<T>(\n mergeConfig(config || {}, {\n method,\n url,\n data: formData,\n headers: { 'Content-Type': 'multipart/form-data' },\n }),\n );\n }\n\n postForm<T = any>(\n url: string,\n data?: any,\n config?: AccessioRequestConfig,\n ): Promise<AccessioResponse<T>> {\n return this.formRequest<T>('post', url, data, config);\n }\n\n putForm<T = any>(\n url: string,\n data?: any,\n config?: AccessioRequestConfig,\n ): Promise<AccessioResponse<T>> {\n return this.formRequest<T>('put', url, data, config);\n }\n\n patchForm<T = any>(\n url: string,\n data?: any,\n config?: AccessioRequestConfig,\n ): Promise<AccessioResponse<T>> {\n return this.formRequest<T>('patch', url, data, config);\n }\n\n async *stream<T = any>(\n url: string,\n config?: AccessioRequestConfig,\n ): AsyncGenerator<T, void, unknown> {\n const response = await this.request<ReadableStream<Uint8Array>>(\n mergeConfig(config || {}, { method: 'get', url, responseType: 'stream' }),\n );\n if (!response.data) return;\n\n const reader = response.data.getReader();\n try {\n const decoder = new TextDecoder();\n let buffer = '';\n\n const processLine = function* (line: string) {\n const trimmed = line.trim();\n if (trimmed.startsWith('data:')) {\n const dataStr = line.replace(/^data:\\s*/, '');\n if (dataStr === '[DONE]') return;\n try {\n yield JSON.parse(dataStr);\n } catch (e) {\n yield dataStr as any;\n }\n } else if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n try {\n yield JSON.parse(line);\n } catch (e) {\n // ignore partial json\n }\n }\n };\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n yield* processLine(line);\n }\n }\n\n buffer += decoder.decode(new Uint8Array(), { stream: false });\n if (buffer.trim()) {\n yield* processLine(buffer);\n }\n } finally {\n try {\n await reader.cancel();\n } catch {\n // ignore errors on cancel\n }\n reader.releaseLock();\n }\n }\n\n async *autoPaginate<T = any>(\n url: string,\n config?: AccessioRequestConfig,\n ): AsyncGenerator<T, void, unknown> {\n let nextUrl: string | null = url;\n let currentConfig = config || {};\n\n while (nextUrl) {\n const response: AccessioResponse<any> = await this.get(nextUrl, currentConfig);\n\n const data = response.data;\n const items = Array.isArray(data)\n ? data\n : data && typeof data === 'object'\n ? (data as any).data\n : null;\n\n if (Array.isArray(items)) {\n for (const item of items) {\n yield item;\n }\n }\n\n nextUrl =\n data && typeof data === 'object'\n ? (data as any).next || (data as any).links?.next || null\n : null;\n\n if (nextUrl) {\n currentConfig = mergeConfig(currentConfig, { url: nextUrl, params: {} });\n }\n }\n }\n\n gql<T = any>(\n url: string,\n query: string,\n variables?: Record<string, any>,\n config?: AccessioRequestConfig,\n ): Promise<AccessioResponse<T>> {\n return this.post<T>(url, { query, variables }, config);\n }\n}\n\nexport default Accessio;\n"],"mappings":"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","names":["buildURL","dispatchRequest","retryRequest","mergeConfig","defaultsConfig","InterceptorManager","AccessioError"]}
|
|
@@ -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,QAAI,MAAM,mBAAmB;AAC3B,YAAM,kBAAkB,MAAM,aAAa;AAAA,IAC7C;AAAA,EACF;AAAA,EAEA,SAAkC;AAChC,WAAO;AAAA,MACL,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd,MAAM,KAAK;AAAA,MACX,QAAQ,KAAK,WAAW,KAAK,SAAS,SAAS;AAAA,MAC/C,QAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAAA,EAEA,OAAO,KACL,OACA,MACA,QACA,SACA,UACe;AACf,UAAM,gBAAgB,IAAI,cAAc,MAAM,SAAS,MAAM,QAAQ,SAAS,QAAQ;AACtF,kBAAc,QAAQ;AACtB,kBAAc,QAAQ,MAAM;AAC5B,WAAO;AAAA,EACT;AACF;AAEA,IAAO,wBAAQ;","names":["ErrorCodes"]}
|
|
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.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":"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","names":["ErrorCodes"]}
|
|
@@ -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/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"]}
|
|
@@ -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":[]}
|
|
@@ -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":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "accessio",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Fast, flexible HTTP client — simple, modular, and dependency-free",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./cjs/index.cjs",
|
|
@@ -106,4 +106,4 @@
|
|
|
106
106
|
"typescript-eslint": "^8.59.3",
|
|
107
107
|
"vitest": "^3.1.0"
|
|
108
108
|
}
|
|
109
|
-
}
|
|
109
|
+
}
|
package/src/accessio.ts
CHANGED
|
@@ -261,19 +261,13 @@ export class Accessio {
|
|
|
261
261
|
if (!response.data) return;
|
|
262
262
|
|
|
263
263
|
const reader = response.data.getReader();
|
|
264
|
-
|
|
265
|
-
|
|
264
|
+
try {
|
|
265
|
+
const decoder = new TextDecoder();
|
|
266
|
+
let buffer = '';
|
|
266
267
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
buffer += decoder.decode(value, { stream: true });
|
|
272
|
-
const lines = buffer.split('\n');
|
|
273
|
-
buffer = lines.pop() || '';
|
|
274
|
-
|
|
275
|
-
for (const line of lines) {
|
|
276
|
-
if (line.trim().startsWith('data:')) {
|
|
268
|
+
const processLine = function* (line: string) {
|
|
269
|
+
const trimmed = line.trim();
|
|
270
|
+
if (trimmed.startsWith('data:')) {
|
|
277
271
|
const dataStr = line.replace(/^data:\s*/, '');
|
|
278
272
|
if (dataStr === '[DONE]') return;
|
|
279
273
|
try {
|
|
@@ -281,14 +275,39 @@ export class Accessio {
|
|
|
281
275
|
} catch (e) {
|
|
282
276
|
yield dataStr as any;
|
|
283
277
|
}
|
|
284
|
-
} else if (
|
|
278
|
+
} else if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
|
|
285
279
|
try {
|
|
286
280
|
yield JSON.parse(line);
|
|
287
281
|
} catch (e) {
|
|
288
282
|
// ignore partial json
|
|
289
283
|
}
|
|
290
284
|
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
while (true) {
|
|
288
|
+
const { done, value } = await reader.read();
|
|
289
|
+
if (done) break;
|
|
290
|
+
|
|
291
|
+
buffer += decoder.decode(value, { stream: true });
|
|
292
|
+
const lines = buffer.split('\n');
|
|
293
|
+
buffer = lines.pop() || '';
|
|
294
|
+
|
|
295
|
+
for (const line of lines) {
|
|
296
|
+
yield* processLine(line);
|
|
297
|
+
}
|
|
291
298
|
}
|
|
299
|
+
|
|
300
|
+
buffer += decoder.decode(new Uint8Array(), { stream: false });
|
|
301
|
+
if (buffer.trim()) {
|
|
302
|
+
yield* processLine(buffer);
|
|
303
|
+
}
|
|
304
|
+
} finally {
|
|
305
|
+
try {
|
|
306
|
+
await reader.cancel();
|
|
307
|
+
} catch {
|
|
308
|
+
// ignore errors on cancel
|
|
309
|
+
}
|
|
310
|
+
reader.releaseLock();
|
|
292
311
|
}
|
|
293
312
|
}
|
|
294
313
|
|
|
@@ -302,14 +321,24 @@ export class Accessio {
|
|
|
302
321
|
while (nextUrl) {
|
|
303
322
|
const response: AccessioResponse<any> = await this.get(nextUrl, currentConfig);
|
|
304
323
|
|
|
305
|
-
const
|
|
324
|
+
const data = response.data;
|
|
325
|
+
const items = Array.isArray(data)
|
|
326
|
+
? data
|
|
327
|
+
: data && typeof data === 'object'
|
|
328
|
+
? (data as any).data
|
|
329
|
+
: null;
|
|
330
|
+
|
|
306
331
|
if (Array.isArray(items)) {
|
|
307
332
|
for (const item of items) {
|
|
308
333
|
yield item;
|
|
309
334
|
}
|
|
310
335
|
}
|
|
311
336
|
|
|
312
|
-
nextUrl =
|
|
337
|
+
nextUrl =
|
|
338
|
+
data && typeof data === 'object'
|
|
339
|
+
? (data as any).next || (data as any).links?.next || null
|
|
340
|
+
: null;
|
|
341
|
+
|
|
313
342
|
if (nextUrl) {
|
|
314
343
|
currentConfig = mergeConfig(currentConfig, { url: nextUrl, params: {} });
|
|
315
344
|
}
|
|
@@ -42,11 +42,42 @@ export function redactBody(value: unknown, seen?: WeakSet<object>): unknown {
|
|
|
42
42
|
return out;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
function redactParams(params: unknown): unknown {
|
|
46
|
+
if (!params || typeof params !== 'object') return params;
|
|
47
|
+
const out: Record<string, unknown> = {};
|
|
48
|
+
for (const key of Object.keys(params as Record<string, unknown>)) {
|
|
49
|
+
const value = (params as Record<string, unknown>)[key];
|
|
50
|
+
if (SENSITIVE_BODY_KEY.test(key)) {
|
|
51
|
+
out[key] = '[REDACTED]';
|
|
52
|
+
} else if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
53
|
+
out[key] = redactParams(value);
|
|
54
|
+
} else {
|
|
55
|
+
out[key] = value;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function redactURL(url: string | undefined): string | undefined {
|
|
62
|
+
if (!url) return url;
|
|
63
|
+
// Match inline credentials: http://user:pass@host
|
|
64
|
+
return url.replace(/^([a-z][a-z\d+\-.]*:\/\/)([^/]+)@/i, (match, protocol, userInfo) => {
|
|
65
|
+
const parts = userInfo.split(':');
|
|
66
|
+
if (parts.length > 1) {
|
|
67
|
+
return `${protocol}${parts[0]}:[REDACTED]@`;
|
|
68
|
+
}
|
|
69
|
+
return `${protocol}[REDACTED]@`;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
45
73
|
export function redactConfig(config: AccessioRequestConfig | null): AccessioRequestConfig | null {
|
|
46
74
|
if (!config) return config;
|
|
47
75
|
const clone = { ...config } as AccessioRequestConfig & { auth?: unknown };
|
|
48
76
|
if ('auth' in clone) delete clone.auth;
|
|
49
77
|
if (clone.headers) clone.headers = redactHeaders(clone.headers) as typeof clone.headers;
|
|
78
|
+
if (clone.params) clone.params = redactParams(clone.params) as typeof clone.params;
|
|
79
|
+
if (clone.url) clone.url = redactURL(clone.url);
|
|
80
|
+
if (clone._builtUrl) clone._builtUrl = redactURL(clone._builtUrl);
|
|
50
81
|
return clone;
|
|
51
82
|
}
|
|
52
83
|
|
package/src/core/fetchAdapter.ts
CHANGED
package/src/core/retry.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
function isUnretriableBody(data: unknown): boolean {
|
|
11
11
|
if (data == null) return false;
|
|
12
12
|
if (typeof ReadableStream !== 'undefined' && data instanceof ReadableStream) return true;
|
|
13
|
+
if (data && typeof (data as any).pipe === 'function') return true;
|
|
13
14
|
return false;
|
|
14
15
|
}
|
|
15
16
|
|
|
@@ -33,10 +34,11 @@ function defaultRetryCondition(error: any): boolean {
|
|
|
33
34
|
return false;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
function calculateDelay(attempt: number, baseDelay: number): number {
|
|
37
|
+
function calculateDelay(attempt: number, baseDelay: number, maxDelay: number = 30000): number {
|
|
37
38
|
const exponentialDelay = baseDelay * Math.pow(2, attempt);
|
|
38
39
|
const jitter = exponentialDelay * 0.25 * (Math.random() * 2 - 1);
|
|
39
|
-
|
|
40
|
+
const calculated = Math.round(exponentialDelay + jitter);
|
|
41
|
+
return Math.min(calculated, maxDelay);
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
function sleep(ms: number, options?: { signal?: AbortSignal }): Promise<void> {
|
|
@@ -89,8 +91,9 @@ async function retryRequest(
|
|
|
89
91
|
} catch (error) {
|
|
90
92
|
lastError = error;
|
|
91
93
|
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
+
const is429 = (error as any).response?.status === 429;
|
|
95
|
+
const attemptLimit = is429 && config.retryOn429 ? Math.max(maxRetries, 3) : maxRetries;
|
|
96
|
+
const shouldRetry = attempt < attemptLimit && retryCondition(error as AccessioError);
|
|
94
97
|
|
|
95
98
|
if (!shouldRetry) {
|
|
96
99
|
throw error;
|
|
@@ -107,7 +110,7 @@ async function retryRequest(
|
|
|
107
110
|
);
|
|
108
111
|
}
|
|
109
112
|
|
|
110
|
-
let delay = calculateDelay(attempt, retryDelay);
|
|
113
|
+
let delay = calculateDelay(attempt, retryDelay, config.maxRetryDelay ?? 30000);
|
|
111
114
|
|
|
112
115
|
if (config.retryOn429 && (error as any).response?.status === 429) {
|
|
113
116
|
const headers = (error as any).response?.headers;
|
|
@@ -76,10 +76,13 @@ export function removeContentType(headers: Record<string, string | string[]>): v
|
|
|
76
76
|
export function buildFetchHeaders(headers: Record<string, string | string[]>): Headers {
|
|
77
77
|
const fetchHeaders = new Headers();
|
|
78
78
|
for (const [key, value] of Object.entries(headers)) {
|
|
79
|
+
if (value === undefined || value === null) continue;
|
|
79
80
|
assertSafeHeader(key, value);
|
|
80
81
|
if (Array.isArray(value)) {
|
|
81
82
|
for (const v of value) {
|
|
82
|
-
|
|
83
|
+
if (v !== undefined && v !== null) {
|
|
84
|
+
fetchHeaders.append(key, v);
|
|
85
|
+
}
|
|
83
86
|
}
|
|
84
87
|
} else {
|
|
85
88
|
fetchHeaders.set(key, value);
|
|
@@ -23,11 +23,15 @@ export function createRateLimiter(
|
|
|
23
23
|
let destroyed = false;
|
|
24
24
|
const queue: QueueItem[] = [];
|
|
25
25
|
|
|
26
|
-
function acquire(): Promise<void> {
|
|
26
|
+
function acquire(signal?: AbortSignal): Promise<void> {
|
|
27
27
|
if (destroyed) {
|
|
28
28
|
return Promise.reject(new Error('[Accessio] Rate limiter has been destroyed'));
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
if (signal?.aborted) {
|
|
32
|
+
return Promise.reject(signal.reason || new Error('Request aborted'));
|
|
33
|
+
}
|
|
34
|
+
|
|
31
35
|
if (active < maxConcurrent) {
|
|
32
36
|
active++;
|
|
33
37
|
return Promise.resolve();
|
|
@@ -40,7 +44,35 @@ export function createRateLimiter(
|
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
return new Promise((resolve, reject) => {
|
|
43
|
-
|
|
47
|
+
let onAbort: (() => void) | undefined;
|
|
48
|
+
|
|
49
|
+
const item = {
|
|
50
|
+
resolve: () => {
|
|
51
|
+
if (signal && onAbort) {
|
|
52
|
+
signal.removeEventListener('abort', onAbort);
|
|
53
|
+
}
|
|
54
|
+
resolve();
|
|
55
|
+
},
|
|
56
|
+
reject: (err: Error) => {
|
|
57
|
+
if (signal && onAbort) {
|
|
58
|
+
signal.removeEventListener('abort', onAbort);
|
|
59
|
+
}
|
|
60
|
+
reject(err);
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
queue.push(item);
|
|
65
|
+
|
|
66
|
+
if (signal) {
|
|
67
|
+
onAbort = () => {
|
|
68
|
+
const index = queue.indexOf(item);
|
|
69
|
+
if (index !== -1) {
|
|
70
|
+
queue.splice(index, 1);
|
|
71
|
+
}
|
|
72
|
+
reject(signal.reason || new Error('Request aborted'));
|
|
73
|
+
};
|
|
74
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
75
|
+
}
|
|
44
76
|
});
|
|
45
77
|
}
|
|
46
78
|
|
|
@@ -85,7 +117,7 @@ export async function rateLimitedRequest<T = unknown>(
|
|
|
85
117
|
limiter: RateLimiter,
|
|
86
118
|
config: AccessioRequestConfig,
|
|
87
119
|
): Promise<AccessioResponse<T>> {
|
|
88
|
-
await limiter.acquire();
|
|
120
|
+
await limiter.acquire(config.signal);
|
|
89
121
|
try {
|
|
90
122
|
return await dispatchFn(config);
|
|
91
123
|
} finally {
|
|
@@ -8,7 +8,11 @@ export function toFormData(obj: any, form?: FormData, namespace?: string): FormD
|
|
|
8
8
|
|
|
9
9
|
if (obj instanceof Date) {
|
|
10
10
|
fd.append(namespace || '', obj.toISOString());
|
|
11
|
-
} else if (
|
|
11
|
+
} else if (
|
|
12
|
+
typeof obj === 'object' &&
|
|
13
|
+
!(typeof File !== 'undefined' && obj instanceof File) &&
|
|
14
|
+
!(typeof Blob !== 'undefined' && obj instanceof Blob)
|
|
15
|
+
) {
|
|
12
16
|
Object.keys(obj).forEach((key) => {
|
|
13
17
|
if (Array.isArray(obj)) {
|
|
14
18
|
formKey = namespace ? `${namespace}[${key}]` : key;
|
package/src/types.ts
CHANGED
|
@@ -96,6 +96,7 @@ export interface AccessioRequestConfig {
|
|
|
96
96
|
schema?: SchemaValidator;
|
|
97
97
|
fetch?: typeof fetch;
|
|
98
98
|
retryOn429?: boolean;
|
|
99
|
+
maxRetryDelay?: number;
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
export interface AccessioResponse<T = unknown> {
|
|
@@ -120,7 +121,7 @@ export interface AccessioError extends Error {
|
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
export interface RateLimiter {
|
|
123
|
-
acquire: () => Promise<void>;
|
|
124
|
+
acquire: (signal?: AbortSignal) => Promise<void>;
|
|
124
125
|
release: () => void;
|
|
125
126
|
destroy: () => void;
|
|
126
127
|
pending: number;
|