accessio 1.5.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,298 +2,256 @@
2
2
 
3
3
  **Fast, flexible HTTP client — simple, modular, and dependency-free.**
4
4
 
5
- `Accessio` is a lightweight, modern HTTP client built on top of the native `fetch` API. It provides a familiar, Promise-based interface with advanced features like interceptors, automatic retries, rate limiting, and structured debug logging, all while maintaining **zero external dependencies**.
5
+ Accessio is a lightweight, modern HTTP client built on top of the native fetch API. It provides a familiar, Promise-based interface with advanced features like interceptors, automatic retries, rate limiting, and structured debug logging, all while maintaining zero external dependencies.
6
6
 
7
7
  ---
8
8
 
9
- ## Features
10
-
11
- - 🚀 **Promise-based** works seamlessly with `async`/`await`
12
- - 🌐 **Isomorphic** runs in both browser and Node.js (≥ 18)
13
- - 🪶 **Zero dependencies** ultra-lightweight, built on native `fetch`
14
- - 🔄 **Interceptors** — transform requests and responses globally or per instance
15
- - ⚙️ **Configurable instances** create multiple API clients with custom defaults
16
- - 🛡️ **Robust Error handling** — structured `AccessioError` with status, config, and response
17
- - 📦 **Dual format** full support for both ESM and CommonJS
18
- - 📐 **TypeScript first** written in TS with comprehensive type definitions
19
- - ⏱️ **Timeout & Cancellation** built-in support via `AbortController` and `signal`
20
- - 🔧 **Transform pipelines** flexible request/response data transformation
21
- - ♻️ **Automatic Retries** smart retry logic with exponential backoff and jitter
22
- - 🚥 **Rate Limiter** built-in concurrency control for high-throughput applications
23
- - 🐞 **Debug Mode** structured, beautiful console logging for easy development
24
- - ⏱️ **Duration Tracking** every response includes precise timing metadata
25
- - 🧬 **GraphQL Support** built-in `gql` method for easy querying
26
- - 📡 **SSE Streaming** async iterators for Server-Sent Events via `stream`
27
- - 📚 **Auto-Pagination** — seamlessly iterate through paginated APIs via `autoPaginate`
28
- - 🛡️ **Schema Validation** — validate responses automatically using Zod or custom schemas
29
- - 🗂️ **Caching & Deduplication** — prevent redundant requests and cache responses
30
- - 🪝 **Lifecycle Hooks** — simple hooks for request/response/error events
9
+ ## Key Features
10
+
11
+ - **Zero Runtime Dependencies**: Keep your bundle size minimal and avoid supply chain vulnerabilities.
12
+ - **Axios-Compatible API**: Direct drop-in replacement with `.get()`, `.post()`, `.request()`, and custom instances via `.create()`.
13
+ - **🛡️ Built-in Security & Auto-Redaction**: Prevents accidental leakage of secrets in error logs. Automatically redacts:
14
+ - `Authorization`, `Cookie`, and `Set-Cookie` headers.
15
+ - Sensitive request/response parameters (e.g., `api_key`, `token`, `password`, `secret`).
16
+ - Inline credentials inside URLs.
17
+ - **⏳ Concurrency Rate Limiter**: Built-in queue-based rate limiter to throttle concurrent requests, complete with immediate queue ejection on `AbortSignal` cancellation.
18
+ - **🔁 Jittered Exponential Backoff Retry**: Automatic retries for network failures or `5xx` status codes. Fully respects the HTTP 429 `Retry-After` header and supports customizable retry conditions and callbacks.
19
+ - **🌊 SSE & Newline JSON Streaming**: Native asynchronous generator-based parsing for Server-Sent Events (SSE) and newline-delimited JSON streams.
20
+ - **📂 Auto-Pagination**: Seamlessly yields paginated items from APIs using page links (e.g., `next` or `links.next`).
21
+ - **🧪 Type-safe Schema Validation**: Validate API response payloads at runtime using Zod, ArkType, or any validation library with a `.parse()` or `.parseAsync()` method.
22
+ - **📦 Request Deduplication**: Automatically coalesces concurrent duplicate GET requests to avoid redundant network traffic.
23
+ - **💾 Memory Caching**: In-memory caching out-of-the-box with custom TTL, or easily swap in a custom storage provider (e.g., Redis, LocalStorage).
24
+ - **🔗 Synchronous & Asynchronous Interceptors**: Hook into the request/response pipeline to dynamically inject headers, handle global errors, or log metrics.
25
+ - **⚓ Lifecycle Hooks**: Granular callbacks (`onBeforeRequest`, `onRequestResponse`, `onRequestError`) for custom instrumentation.
26
+ - **FormData Serialization**: Automatically converts flat or nested JS objects to `FormData` for multipart submissions.
31
27
 
32
28
  ---
33
29
 
34
30
  ## 📦 Installation
35
31
 
36
32
  ```bash
37
- # Using npm
38
33
  npm install accessio
39
-
40
- # Using yarn
41
- yarn add accessio
42
-
43
- # Using pnpm
44
- pnpm add accessio
45
34
  ```
46
35
 
47
36
  ---
48
37
 
49
38
  ## 🚀 Quick Start
50
39
 
40
+ ### Basic Requests
41
+
51
42
  ```typescript
52
43
  import accessio from 'accessio';
53
44
 
54
45
  // Simple GET request
55
- const { data } = await accessio.get('https://api.example.com/users');
46
+ const response = await accessio.get('https://api.example.com/users/123');
47
+ console.log(response.data); // Automatically parsed JSON response
56
48
 
57
- // POST request with JSON body
58
- const response = await accessio.post('https://api.example.com/users', {
59
- name: 'John Doe',
49
+ // POST request with body
50
+ const postResponse = await accessio.post('https://api.example.com/users', {
51
+ name: 'Jane Doe',
60
52
  role: 'Developer',
61
53
  });
62
-
63
- console.log(`User created in ${response.duration}ms`);
64
54
  ```
65
55
 
66
- ---
67
-
68
- ## 📖 API Reference
69
-
70
- ### Request Methods
71
-
72
- | Method | Description |
73
- | :----------------------------------------- | :--------------------------------------- |
74
- | `accessio(config)` | Generic request using config object |
75
- | `accessio.get(url, config?)` | GET request |
76
- | `accessio.post(url, data?, config?)` | POST request |
77
- | `accessio.put(url, data?, config?)` | PUT request |
78
- | `accessio.patch(url, data?, config?)` | PATCH request |
79
- | `accessio.delete(url, config?)` | DELETE request |
80
- | `accessio.head(url, config?)` | HEAD request |
81
- | `accessio.options(url, config?)` | OPTIONS request |
82
- | `accessio.postForm(url, data?, config?)` | POST request with `multipart/form-data` |
83
- | `accessio.putForm(url, data?, config?)` | PUT request with `multipart/form-data` |
84
- | `accessio.patchForm(url, data?, config?)` | PATCH request with `multipart/form-data` |
85
- | `accessio.stream(url, config?)` | Server-Sent Events (SSE) streaming |
86
- | `accessio.autoPaginate(url, config?)` | Async iterator for paginated endpoints |
87
- | `accessio.gql(url, query, vars?, config?)` | GraphQL query/mutation wrapper |
88
-
89
- ### Configuration Options
56
+ ### Custom Instances
90
57
 
91
58
  ```typescript
92
- {
93
- baseURL: 'https://api.example.com', // Base URL for all requests
94
- url: '/users', // Relative or absolute URL
95
- method: 'get', // HTTP method (default: get)
96
- headers: { 'X-Custom': 'val' }, // Custom headers
97
- params: { id: 123 }, // URL query parameters
98
- data: { name: 'John' }, // Request body (JSON/FormData/etc)
99
- timeout: 5000, // Timeout in ms (default: 0)
100
- responseType: 'json', // Expected response: 'json', 'text', 'blob', 'stream'
101
- auth: { username: '', password: '' }, // Basic auth credentials
102
- retry: 3, // Max retry attempts
103
- retryDelay: 1000, // Base delay for exponential backoff
104
- debug: true, // Enable structured logging
105
- rateLimiter: limiter, // Concurrency limiter instance
106
- validateStatus: (s) => s < 400, // Resolve/reject predicate
107
- signal: abortController.signal, // Custom AbortSignal
108
- dedupe: true, // Prevent duplicate in-flight requests
109
- cache: true, // Cache responses (boolean or CacheProvider)
110
- cacheTTL: 60000, // Cache time-to-live in ms
111
- schema: z.object({...}), // Schema validator (e.g., Zod)
112
- fetch: customFetch, // Custom fetch implementation
113
- retryOn429: true, // Automatically retry on rate limits
114
- hooks: { // Lifecycle hooks
115
- onBeforeRequest: (config) => {},
116
- onRequestResponse: (response) => {},
117
- onRequestError: (error) => {}
118
- }
119
- }
59
+ import accessio from 'accessio';
60
+
61
+ // Create a configured instance
62
+ const api = accessio.create({
63
+ baseURL: 'https://api.example.com/v1',
64
+ headers: {
65
+ 'X-Client-Name': 'AccessioClient',
66
+ },
67
+ timeout: 5000, // 5-second timeout
68
+ });
69
+
70
+ // Use instance methods
71
+ const { data } = await api.get('/users');
120
72
  ```
121
73
 
122
74
  ---
123
75
 
124
- ## ♻️ Advanced Usage
76
+ ## 🛠️ Advanced Features
125
77
 
126
- ### Interceptors
78
+ ### 🛡️ Auto-Redaction (Zero-leak Logs)
127
79
 
128
- Interceptors allow you to transform requests or responses before they are handled by `then` or `catch`.
80
+ Accessio is built with security first. If a request fails, sensitive credentials in request/response properties are redacted automatically before being attached to the `AccessioError`.
129
81
 
130
82
  ```typescript
131
- // Add a request interceptor
132
- accessio.interceptors.request.use((config) => {
133
- config.headers['Authorization'] = `Bearer ${storage.getToken()}`;
134
- return config;
135
- });
136
-
137
- // Add a response interceptor
138
- accessio.interceptors.response.use(
139
- (response) => response,
140
- (error) => {
141
- if (error.response?.status === 401) logout();
142
- return Promise.reject(error);
143
- },
144
- );
83
+ try {
84
+ await accessio.get('https://admin:secret_password@api.example.com/users', {
85
+ params: { api_key: 'super_secret_token_123' },
86
+ headers: { Authorization: 'Bearer token_xyz' },
87
+ });
88
+ } catch (error) {
89
+ if (accessio.isAccessioError(error)) {
90
+ console.error(error.toJSON());
91
+ /*
92
+ Outputs:
93
+ {
94
+ "name": "AccessioError",
95
+ "message": "Request failed with status code 401",
96
+ "code": "ERR_BAD_REQUEST",
97
+ "status": 401,
98
+ "config": {
99
+ "url": "https://admin:[REDACTED]@api.example.com/users",
100
+ "params": {
101
+ "api_key": "[REDACTED]"
102
+ },
103
+ "headers": {
104
+ "authorization": "[REDACTED]"
105
+ }
106
+ }
107
+ }
108
+ */
109
+ }
110
+ }
145
111
  ```
146
112
 
147
- ### Error Handling
148
-
149
- `Accessio` provides a structured error object with specific codes to help you handle failures gracefully.
113
+ ### 🔁 Automatic Retries & Backoff
150
114
 
151
- | Code | Description |
152
- | :----------------- | :---------------------------- |
153
- | `ERR_BAD_REQUEST` | 4xx status code |
154
- | `ERR_BAD_RESPONSE` | 5xx status code |
155
- | `ERR_NETWORK` | Network connectivity issues |
156
- | `ETIMEDOUT` | Request exceeded timeout |
157
- | `ERR_CANCELED` | Request was manually aborted |
158
- | `ERR_INVALID_URL` | The provided URL is malformed |
159
- | `ERR_BAD_OPTION` | Invalid configuration option |
160
-
161
- ### Automatic Retries
162
-
163
- `Accessio` includes a powerful retry mechanism that handles network errors and 5xx responses automatically.
115
+ Automatically retry failed requests using exponential backoff with randomized jitter to prevent thundering herds.
164
116
 
165
117
  ```typescript
166
118
  const response = await accessio.get('/flaky-endpoint', {
167
- retry: 5,
168
- retryDelay: 1000, // Delays: 1s, 2s, 4s, 8s, 16s (+/- random jitter)
169
- onRetry: (attempt, error) => console.log(`Retry #${attempt}...`),
119
+ retry: 3, // Max retry attempts
120
+ retryDelay: 1000, // Initial delay in ms (doubles each attempt)
121
+ maxRetryDelay: 10000, // Maximum delay cap
122
+ retryOn429: true, // Respect Retry-After header for HTTP 429 responses
123
+ onRetry: (attempt, error, config) => {
124
+ console.warn(`Retry attempt #${attempt} due to: ${error.message}`);
125
+ },
170
126
  });
171
127
  ```
172
128
 
173
- ### Rate Limiting
129
+ ### ⏳ Concurrency Rate Limiting
174
130
 
175
- Limit concurrent requests globally or per-instance to prevent overloading APIs.
131
+ Throttle outbound requests using a queue-based rate limiter. This is especially useful for third-party APIs with tight request limits.
176
132
 
177
133
  ```typescript
178
- import { createRateLimiter } from 'accessio';
134
+ import accessio, { createRateLimiter } from 'accessio';
135
+
136
+ // Allow a maximum of 2 requests in parallel
137
+ const rateLimiter = createRateLimiter(2);
179
138
 
180
- const limiter = createRateLimiter(5); // Max 5 concurrent requests
181
- const api = accessio.create({ rateLimiter: limiter });
139
+ const api = accessio.create({ rateLimiter });
182
140
 
183
- // Requests will wait in queue if limit is reached
184
- const results = await Promise.all([
185
- api.get('/req-1'),
186
- api.get('/req-2'),
187
- // ...
188
- ]);
141
+ // These will run with a max concurrency of 2, queueing the rest
142
+ const requests = [1, 2, 3, 4, 5].map((id) => api.get(`/users/${id}`));
143
+ const responses = await Promise.all(requests);
189
144
  ```
190
145
 
191
- ### Debug Mode
146
+ ### 🌊 SSE & Newline JSON Streaming
192
147
 
193
- Get beautiful, structured logs in your console by enabling `debug: true`.
148
+ Accessio leverages asynchronous generators to handle incoming response streams dynamically (works with Server-Sent Events or line-by-line JSON streams).
194
149
 
195
150
  ```typescript
196
- // 🐦‍⬛ [accessio] GET https://api.example.com/users
197
- // Params: {"page":1}
198
- // Timeout: 5000ms
199
- // 🐦‍⬛ [accessio] ← ✅ 200 OK (142ms)
200
- // Size: ~3.2 KB
151
+ // Iterating through an AI completion SSE stream
152
+ for await (const chunk of api.stream('/ai/complete')) {
153
+ console.log(chunk); // Parsed JSON chunk, e.g., { text: "hello" }
154
+ }
201
155
  ```
202
156
 
203
- ### Caching & Deduplication
157
+ ### 📂 Auto-Pagination
204
158
 
205
- Prevent duplicate requests and cache responses to improve performance.
159
+ Avoid boilerplate code for pagination. Accessio can auto-follow `next` and `links.next` properties automatically:
206
160
 
207
161
  ```typescript
208
- const api = accessio.create({
209
- dedupe: true, // Prevents identical requests while one is pending
210
- cache: true, // Caches responses in memory
211
- cacheTTL: 5 * 60 * 1000, // Cache for 5 minutes
212
- });
162
+ // Automatically fetches subsequent pages until next link is null
163
+ for await (const item of api.autoPaginate('/users?page=1')) {
164
+ console.log(item.name); // Yields individual items from each page's items array
165
+ }
213
166
  ```
214
167
 
215
- ### Schema Validation
168
+ ### 🧪 Runtime Schema Validation
216
169
 
217
- Automatically parse and validate responses using libraries like Zod.
170
+ Validate your API payloads at runtime using your favorite validation library (e.g., Zod, ArkType, Superstruct).
218
171
 
219
172
  ```typescript
220
173
  import { z } from 'zod';
221
174
 
222
- const userSchema = z.object({ id: z.number(), name: z.string() });
223
-
224
- const response = await accessio.get('/user/1', { schema: userSchema });
225
- // response.data is strictly typed and validated against userSchema
226
- ```
227
-
228
- ### Server-Sent Events (SSE)
175
+ const UserSchema = z.object({
176
+ id: z.string(),
177
+ name: z.string(),
178
+ email: z.string().email(),
179
+ });
229
180
 
230
- Easily consume SSE streams using async iterators.
181
+ const response = await api.get('/users/123', {
182
+ schema: UserSchema, // Throws AccessioError (ERR_BAD_RESPONSE) if validation fails
183
+ });
231
184
 
232
- ```typescript
233
- for await (const chunk of accessio.stream('/stream')) {
234
- console.log(chunk); // Parsed JSON or string data from SSE
235
- }
185
+ const user = response.data; // Fully typed as { id: string; name: string; email: string }
236
186
  ```
237
187
 
238
- ### Auto-Pagination
188
+ ### 📦 Request Deduplication & Caching
239
189
 
240
- Iterate through paginated endpoints effortlessly.
190
+ Optimize application performance by preventing duplicate queries and utilizing caching.
241
191
 
242
192
  ```typescript
243
- for await (const user of accessio.autoPaginate('/users')) {
244
- console.log(user); // Automatically fetches the next page when needed
245
- }
193
+ const api = accessio.create({
194
+ dedupe: true, // Merge concurrent requests targeting the same endpoint
195
+ cache: true, // Enable in-memory cache
196
+ cacheTTL: 60000, // Cache responses for 60 seconds
197
+ });
198
+
199
+ // Executes exactly 1 network call, resolves both promises
200
+ const [res1, res2] = await Promise.all([api.get('/heavy-report'), api.get('/heavy-report')]);
246
201
  ```
247
202
 
248
- ### GraphQL
203
+ ### 🔗 Interceptors & Hooks
249
204
 
250
- Send GraphQL queries and mutations with ease.
205
+ Modify requests and responses at runtime, or listen to client lifecycles:
251
206
 
252
207
  ```typescript
253
- const query = `
254
- query GetUser($id: ID!) {
255
- user(id: $id) { name, email }
256
- }
257
- `;
258
-
259
- const response = await accessio.gql('/graphql', query, { id: '1' });
260
- ```
208
+ const api = accessio.create();
261
209
 
262
- ### Lifecycle Hooks
263
-
264
- Use hooks for simple global or request-specific event handling.
210
+ // Add Request Interceptor
211
+ api.interceptors.request.use((config) => {
212
+ config.headers = config.headers || {};
213
+ config.headers['X-Request-Timestamp'] = Date.now().toString();
214
+ return config;
215
+ });
265
216
 
266
- ```typescript
267
- accessio.create({
268
- hooks: {
269
- onBeforeRequest: (config) => console.log('Starting request...'),
270
- onRequestResponse: (response) => console.log('Request succeeded!'),
271
- onRequestError: (error) => console.error('Request failed!'),
217
+ // Add Response Interceptor
218
+ api.interceptors.response.use(
219
+ (response) => {
220
+ // Modify output data
221
+ return response;
272
222
  },
273
- });
223
+ (error) => {
224
+ // Handle global errors (e.g. token refresh)
225
+ return Promise.reject(error);
226
+ },
227
+ );
274
228
  ```
275
229
 
276
230
  ---
277
231
 
278
- ## 🛠️ Developer Guide
279
-
280
- ### Local Setup
281
-
282
- ```bash
283
- git clone https://github.com/salvatorecorvaglia/accessio.git
284
- cd accessio
285
- npm install
286
- ```
287
-
288
- ### Available Scripts
289
-
290
- - `npm run build`: Generate CommonJS bundles
291
- - `npm run test`: Run the full test suite with Vitest
292
- - `npm run test:coverage`: Run tests with coverage report
293
- - `npm run test:browser`: Run tests in browser environment
294
- - `npm run lint`: Check for code style issues
295
- - `npm run format`: Automatically format the codebase with Prettier
296
- - `npm run typecheck`: Validate TypeScript types
232
+ ## ⚙️ Configuration Options
233
+
234
+ Here is the complete list of config parameters available in `AccessioRequestConfig`:
235
+
236
+ | Option | Type | Default | Description |
237
+ | :----------------- | :-------------------------------------------------------- | :-------------------- | :----------------------------------------------------------------------------- |
238
+ | `baseURL` | `string` | `undefined` | Prepended to relative URLs. |
239
+ | `method` | `string` | `'get'` | HTTP request method (e.g., `'get'`, `'post'`). |
240
+ | `headers` | `Record` | `{}` | Key-value mapping of custom headers. |
241
+ | `params` | `Record` | `undefined` | Query parameters appended to the URL. |
242
+ | `data` | `any` | `undefined` | The payload to send in the request body. |
243
+ | `timeout` | `number` | `0` (disabled) | Request timeout in milliseconds. |
244
+ | `responseType` | `'json' \| 'text' \| 'blob' \| 'arraybuffer' \| 'stream'` | `'json'` | Expected format of the response data. |
245
+ | `retry` | `number` | `0` | Number of times to retry failed requests. |
246
+ | `retryDelay` | `number` | `1000` | Initial delay for exponential backoff (ms). |
247
+ | `retryOn429` | `boolean` | `false` | Automatically retry on 429 using the `Retry-After` header. |
248
+ | `rateLimiter` | `RateLimiter` | `undefined` | A rate limiter instance to enqueue requests. |
249
+ | `dedupe` | `boolean` | `false` | Coalesce concurrent duplicate GET requests. |
250
+ | `cache` | `boolean \| CacheProvider` | `false` | Enable caching using memory or custom provider. |
251
+ | `cacheTTL` | `number` | `undefined` | TTL for cached responses in ms. |
252
+ | `schema` | `SchemaValidator` | `undefined` | Zod/schema parser to validate response body. |
253
+ | `hooks` | `AccessioHooks` | `undefined` | Lifecycle callbacks: `onBeforeRequest`, `onRequestResponse`, `onRequestError`. |
254
+ | `allowedProtocols` | `string[] \| null` | `['http:', 'https:']` | Protocols allowed for requesting. Set to `null` to disable check. |
297
255
 
298
256
  ---
299
257
 
@@ -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\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
+ {"version":3,"sources":["../../src/core/accessioError.ts"],"sourcesContent":["import ErrorCodes from '../constants/errorCodes';\nimport type { AccessioRequestConfig, AccessioResponse } from '../types';\n\nfunction redactHeaders(headers: unknown): unknown {\n if (!headers || typeof headers !== 'object') return headers;\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(headers as Record<string, unknown>)) {\n const value = (headers as Record<string, unknown>)[key];\n if (/^authorization$/i.test(key) || /^cookie$/i.test(key) || /^set-cookie$/i.test(key)) {\n out[key] = '[REDACTED]';\n } else if (value && typeof value === 'object' && !Array.isArray(value)) {\n out[key] = redactHeaders(value);\n } else {\n out[key] = value;\n }\n }\n return out;\n}\n\nconst SENSITIVE_BODY_KEY =\n /^(password|passwd|pwd|token|access_token|refresh_token|id_token|authorization|api[_-]?key|secret|client[_-]?secret|cookie|set[_-]?cookie|private[_-]?key|session)$/i;\n\nexport function redactBody(value: unknown, seen?: WeakSet<object>): unknown {\n if (value === null || typeof value !== 'object') return value;\n const visited = seen ?? new WeakSet<object>();\n if (visited.has(value as object)) return '[Circular]';\n visited.add(value as object);\n\n if (Array.isArray(value)) {\n return value.map((item) => redactBody(item, visited));\n }\n\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>)) {\n const v = (value as Record<string, unknown>)[key];\n if (SENSITIVE_BODY_KEY.test(key)) {\n out[key] = '[REDACTED]';\n } else {\n out[key] = redactBody(v, visited);\n }\n }\n return out;\n}\n\nfunction redactParams(params: unknown): unknown {\n if (!params || typeof params !== 'object') return params;\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(params as Record<string, unknown>)) {\n const value = (params as Record<string, unknown>)[key];\n if (SENSITIVE_BODY_KEY.test(key)) {\n out[key] = '[REDACTED]';\n } else if (value && typeof value === 'object' && !Array.isArray(value)) {\n out[key] = redactParams(value);\n } else {\n out[key] = value;\n }\n }\n return out;\n}\n\nfunction redactURL(url: string | undefined): string | undefined {\n if (!url) return url;\n // Match inline credentials: http://user:pass@host\n return url.replace(/^([a-z][a-z\\d+\\-.]*:\\/\\/)([^/]+)@/i, (match, protocol, userInfo) => {\n const parts = userInfo.split(':');\n if (parts.length > 1) {\n return `${protocol}${parts[0]}:[REDACTED]@`;\n }\n return `${protocol}[REDACTED]@`;\n });\n}\n\nexport function redactConfig(config: AccessioRequestConfig | null): AccessioRequestConfig | null {\n if (!config) return config;\n const clone = { ...config } as AccessioRequestConfig & { auth?: unknown };\n if ('auth' in clone) delete clone.auth;\n if (clone.headers) clone.headers = redactHeaders(clone.headers) as typeof clone.headers;\n if (clone.params) clone.params = redactParams(clone.params) as typeof clone.params;\n if (clone.url) clone.url = redactURL(clone.url);\n if (clone._builtUrl) clone._builtUrl = redactURL(clone._builtUrl);\n return clone;\n}\n\nexport class AccessioError extends Error {\n static ERR_BAD_OPTION_VALUE: string = ErrorCodes.ERR_BAD_OPTION_VALUE;\n static ERR_BAD_OPTION: string = ErrorCodes.ERR_BAD_OPTION;\n static ECONNABORTED: string = ErrorCodes.ECONNABORTED;\n static ETIMEDOUT: string = ErrorCodes.ETIMEDOUT;\n static ERR_NETWORK: string = ErrorCodes.ERR_NETWORK;\n static ERR_FR_TOO_MANY_REDIRECTS: string = ErrorCodes.ERR_FR_TOO_MANY_REDIRECTS;\n static ERR_BAD_RESPONSE: string = ErrorCodes.ERR_BAD_RESPONSE;\n static ERR_BAD_REQUEST: string = ErrorCodes.ERR_BAD_REQUEST;\n static ERR_CANCELED: string = ErrorCodes.ERR_CANCELED;\n static ERR_NOT_SUPPORT: string = ErrorCodes.ERR_NOT_SUPPORT;\n static ERR_INVALID_URL: string = ErrorCodes.ERR_INVALID_URL;\n\n readonly code: string | null;\n readonly config: AccessioRequestConfig | null;\n readonly request: unknown;\n readonly response: AccessioResponse | null;\n readonly isAccessioError: true;\n cause?: Error;\n override name = 'AccessioError' as const;\n\n constructor(\n message: string,\n code: string | null,\n config: AccessioRequestConfig | null,\n request: unknown,\n response: AccessioResponse | null,\n ) {\n super(message);\n this.name = 'AccessioError';\n this.code = code ?? null;\n this.config = redactConfig(config ?? null);\n this.request = request ?? null;\n this.response = response ?? null;\n this.isAccessioError = true;\n\n if ((Error as any).captureStackTrace) {\n (Error as any).captureStackTrace(this, AccessioError);\n }\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n status: this.response ? this.response.status : null,\n config: this.config,\n };\n }\n\n static from(\n error: Error,\n code: string,\n config: AccessioRequestConfig | null,\n request: unknown,\n response: AccessioResponse | null,\n ): AccessioError {\n const accessioError = new AccessioError(error.message, code, config, request, response);\n accessioError.cause = error;\n accessioError.stack = error.stack;\n return accessioError;\n }\n}\n\nexport default AccessioError;\n"],"mappings":"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","names":["ErrorCodes"]}
@@ -84,15 +84,18 @@ function buildURL(url, baseURL, params, paramsSerializer) {
84
84
  if (key === "__proto__" || key === "prototype" || key === "constructor") continue;
85
85
  unusedParams[key] = params[key];
86
86
  }
87
- fullURL = fullURL.replace(/(?::([a-zA-Z0-9_]+))|(?:{([a-zA-Z0-9_]+)})/g, (match, p1, p2) => {
88
- const key = p1 || p2;
89
- if (key && Object.prototype.hasOwnProperty.call(unusedParams, key) && unusedParams[key] !== void 0) {
90
- const val = unusedParams[key];
91
- delete unusedParams[key];
92
- return encodeURIComponent(String(val));
87
+ fullURL = fullURL.replace(
88
+ /(?::([a-zA-Z_][a-zA-Z0-9_]*))|(?:{([a-zA-Z_][a-zA-Z0-9_]*)})/g,
89
+ (match, p1, p2) => {
90
+ const key = p1 || p2;
91
+ if (key && Object.prototype.hasOwnProperty.call(unusedParams, key) && unusedParams[key] !== void 0) {
92
+ const val = unusedParams[key];
93
+ delete unusedParams[key];
94
+ return encodeURIComponent(String(val));
95
+ }
96
+ return match;
93
97
  }
94
- return match;
95
- });
98
+ );
96
99
  finalParams = unusedParams;
97
100
  }
98
101
  const serialized = serializeParams(finalParams, paramsSerializer);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/buildURL.ts"],"sourcesContent":["import type { ParamsSerializer } from '../types';\n\nfunction serializeParams(\n params: Record<string, unknown>,\n paramsSerializer?: ParamsSerializer,\n): string {\n if (!params) return '';\n\n if (typeof paramsSerializer === 'function') {\n return paramsSerializer(params);\n }\n\n if (typeof URLSearchParams !== 'undefined' && params instanceof URLSearchParams) {\n return params.toString();\n }\n\n const parts: string[] = [];\n\n function encode(prefix: string, value: unknown): void {\n if (value === null || value === undefined) {\n return;\n }\n\n if (Array.isArray(value)) {\n value.forEach((item, index) => {\n if (typeof item === 'object' && item !== null) {\n encode(`${prefix}[${index}]`, item);\n } else {\n encode(prefix, item);\n }\n });\n } else if (typeof value === 'object' && !(value instanceof Date)) {\n Object.keys(value as Record<string, unknown>).forEach((key) => {\n encode(`${prefix}[${key}]`, (value as Record<string, unknown>)[key]);\n });\n } else {\n const encodedValue = value instanceof Date ? value.toISOString() : value;\n parts.push(`${encodeURIComponent(prefix)}=${encodeURIComponent(encodedValue as string)}`);\n }\n }\n\n Object.keys(params).forEach((key) => {\n encode(key, params[key]);\n });\n\n return parts.join('&');\n}\n\nfunction combineURLs(baseURL: string, relativeURL: string): string {\n if (!baseURL) return relativeURL || '';\n if (!relativeURL) return baseURL;\n\n let base = baseURL;\n while (base.endsWith('/')) {\n base = base.slice(0, -1);\n }\n\n let relative = relativeURL;\n while (relative.startsWith('/')) {\n relative = relative.slice(1);\n }\n\n return `${base}/${relative}`;\n}\n\nfunction isAbsoluteURL(url: string): boolean {\n return /^([a-z][a-z\\d+\\-.]*:)/i.test(url);\n}\n\nexport default function buildURL(\n url: string,\n baseURL?: string,\n params?: Record<string, unknown>,\n paramsSerializer?: ParamsSerializer,\n): string {\n let fullURL = baseURL && !isAbsoluteURL(url) ? combineURLs(baseURL, url) : url || '';\n\n let finalParams = params;\n if (params && typeof params === 'object' && !(params instanceof URLSearchParams)) {\n const unusedParams: Record<string, unknown> = {};\n for (const key of Object.keys(params)) {\n if (key === '__proto__' || key === 'prototype' || key === 'constructor') continue;\n unusedParams[key] = (params as Record<string, unknown>)[key];\n }\n fullURL = fullURL.replace(/(?::([a-zA-Z0-9_]+))|(?:{([a-zA-Z0-9_]+)})/g, (match, p1, p2) => {\n const key = p1 || p2;\n if (\n key &&\n Object.prototype.hasOwnProperty.call(unusedParams, key) &&\n unusedParams[key] !== undefined\n ) {\n const val = unusedParams[key];\n delete unusedParams[key];\n return encodeURIComponent(String(val));\n }\n return match;\n });\n finalParams = unusedParams;\n }\n\n const serialized = serializeParams(finalParams as Record<string, unknown>, paramsSerializer);\n if (serialized) {\n const hashIndex = fullURL.indexOf('#');\n let fragment = '';\n if (hashIndex !== -1) {\n fragment = fullURL.slice(hashIndex);\n fullURL = fullURL.slice(0, hashIndex);\n }\n fullURL += (fullURL.indexOf('?') === -1 ? '?' : '&') + serialized + fragment;\n }\n\n return fullURL;\n}\n\nexport { serializeParams, combineURLs, isAbsoluteURL };\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/core/buildURL.ts"],"sourcesContent":["import type { ParamsSerializer } from '../types';\n\nfunction serializeParams(\n params: Record<string, unknown>,\n paramsSerializer?: ParamsSerializer,\n): string {\n if (!params) return '';\n\n if (typeof paramsSerializer === 'function') {\n return paramsSerializer(params);\n }\n\n if (typeof URLSearchParams !== 'undefined' && params instanceof URLSearchParams) {\n return params.toString();\n }\n\n const parts: string[] = [];\n\n function encode(prefix: string, value: unknown): void {\n if (value === null || value === undefined) {\n return;\n }\n\n if (Array.isArray(value)) {\n value.forEach((item, index) => {\n if (typeof item === 'object' && item !== null) {\n encode(`${prefix}[${index}]`, item);\n } else {\n encode(prefix, item);\n }\n });\n } else if (typeof value === 'object' && !(value instanceof Date)) {\n Object.keys(value as Record<string, unknown>).forEach((key) => {\n encode(`${prefix}[${key}]`, (value as Record<string, unknown>)[key]);\n });\n } else {\n const encodedValue = value instanceof Date ? value.toISOString() : value;\n parts.push(`${encodeURIComponent(prefix)}=${encodeURIComponent(encodedValue as string)}`);\n }\n }\n\n Object.keys(params).forEach((key) => {\n encode(key, params[key]);\n });\n\n return parts.join('&');\n}\n\nfunction combineURLs(baseURL: string, relativeURL: string): string {\n if (!baseURL) return relativeURL || '';\n if (!relativeURL) return baseURL;\n\n let base = baseURL;\n while (base.endsWith('/')) {\n base = base.slice(0, -1);\n }\n\n let relative = relativeURL;\n while (relative.startsWith('/')) {\n relative = relative.slice(1);\n }\n\n return `${base}/${relative}`;\n}\n\nfunction isAbsoluteURL(url: string): boolean {\n return /^([a-z][a-z\\d+\\-.]*:)/i.test(url);\n}\n\nexport default function buildURL(\n url: string,\n baseURL?: string,\n params?: Record<string, unknown>,\n paramsSerializer?: ParamsSerializer,\n): string {\n let fullURL = baseURL && !isAbsoluteURL(url) ? combineURLs(baseURL, url) : url || '';\n\n let finalParams = params;\n if (params && typeof params === 'object' && !(params instanceof URLSearchParams)) {\n const unusedParams: Record<string, unknown> = {};\n for (const key of Object.keys(params)) {\n if (key === '__proto__' || key === 'prototype' || key === 'constructor') continue;\n unusedParams[key] = (params as Record<string, unknown>)[key];\n }\n fullURL = fullURL.replace(\n /(?::([a-zA-Z_][a-zA-Z0-9_]*))|(?:{([a-zA-Z_][a-zA-Z0-9_]*)})/g,\n (match, p1, p2) => {\n const key = p1 || p2;\n if (\n key &&\n Object.prototype.hasOwnProperty.call(unusedParams, key) &&\n unusedParams[key] !== undefined\n ) {\n const val = unusedParams[key];\n delete unusedParams[key];\n return encodeURIComponent(String(val));\n }\n return match;\n },\n );\n finalParams = unusedParams;\n }\n\n const serialized = serializeParams(finalParams as Record<string, unknown>, paramsSerializer);\n if (serialized) {\n const hashIndex = fullURL.indexOf('#');\n let fragment = '';\n if (hashIndex !== -1) {\n fragment = fullURL.slice(hashIndex);\n fullURL = fullURL.slice(0, hashIndex);\n }\n fullURL += (fullURL.indexOf('?') === -1 ? '?' : '&') + serialized + fragment;\n }\n\n return fullURL;\n}\n\nexport { serializeParams, combineURLs, isAbsoluteURL };\n"],"mappings":"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","names":[]}
@@ -42,6 +42,8 @@ async function readResponseData(fetchResponse, config) {
42
42
  return await fetchResponse.blob();
43
43
  case "stream":
44
44
  return fetchResponse.body;
45
+ case "text":
46
+ return await fetchResponse.text();
45
47
  case "json":
46
48
  default: {
47
49
  const contentType = fetchResponse.headers.get("content-type") || "";
@@ -186,7 +188,13 @@ function classifyFetchError(error, config, isTimedOut) {
186
188
  }
187
189
  const isAbort = error instanceof Error && error.name === "AbortError" || !!config.signal?.aborted;
188
190
  if (isAbort) {
189
- return new import_accessioError.default("Request aborted", import_accessioError.default.ERR_CANCELED, config, null, null);
191
+ const reason = config.signal?.reason;
192
+ const message = reason instanceof Error ? reason.message : typeof reason === "string" ? reason : "Request aborted";
193
+ const err = new import_accessioError.default(message, import_accessioError.default.ERR_CANCELED, config, null, null);
194
+ if (reason instanceof Error) {
195
+ err.cause = reason;
196
+ }
197
+ return err;
190
198
  }
191
199
  return import_accessioError.default.from(
192
200
  error instanceof Error ? error : new Error(String(error)),