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 +165 -207
- package/cjs/core/accessioError.cjs.map +1 -1
- package/cjs/core/buildURL.cjs +11 -8
- package/cjs/core/buildURL.cjs.map +1 -1
- package/cjs/core/fetchAdapter.cjs +9 -1
- package/cjs/core/fetchAdapter.cjs.map +1 -1
- package/cjs/core/mergeConfig.cjs +1 -1
- package/cjs/core/mergeConfig.cjs.map +1 -1
- package/cjs/core/request.cjs +43 -15
- package/cjs/core/request.cjs.map +1 -1
- package/cjs/defaults/transforms.cjs +4 -1
- package/cjs/defaults/transforms.cjs.map +1 -1
- package/cjs/helpers/flattenHeaders.cjs +16 -3
- package/cjs/helpers/flattenHeaders.cjs.map +1 -1
- package/cjs/helpers/memoryCache.cjs +26 -1
- package/cjs/helpers/memoryCache.cjs.map +1 -1
- package/cjs/helpers/parseHeaders.cjs +9 -6
- package/cjs/helpers/parseHeaders.cjs.map +1 -1
- package/cjs/helpers/transformData.cjs +1 -1
- package/cjs/helpers/transformData.cjs.map +1 -1
- package/index.d.ts +7 -0
- package/package.json +4 -3
- package/src/core/accessioError.ts +2 -2
- package/src/core/buildURL.ts +16 -13
- package/src/core/fetchAdapter.ts +14 -1
- package/src/core/mergeConfig.ts +1 -1
- package/src/core/request.ts +54 -16
- package/src/defaults/transforms.ts +4 -1
- package/src/helpers/flattenHeaders.ts +17 -3
- package/src/helpers/memoryCache.ts +33 -1
- package/src/helpers/parseHeaders.ts +10 -7
- package/src/helpers/transformData.ts +1 -1
- package/src/types.ts +6 -0
package/README.md
CHANGED
|
@@ -2,298 +2,256 @@
|
|
|
2
2
|
|
|
3
3
|
**Fast, flexible HTTP client — simple, modular, and dependency-free.**
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
##
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
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
|
|
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
|
|
58
|
-
const
|
|
59
|
-
name: '
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
##
|
|
76
|
+
## 🛠️ Advanced Features
|
|
125
77
|
|
|
126
|
-
###
|
|
78
|
+
### 🛡️ Auto-Redaction (Zero-leak Logs)
|
|
127
79
|
|
|
128
|
-
|
|
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
|
-
|
|
132
|
-
accessio.
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
###
|
|
148
|
-
|
|
149
|
-
`Accessio` provides a structured error object with specific codes to help you handle failures gracefully.
|
|
113
|
+
### 🔁 Automatic Retries & Backoff
|
|
150
114
|
|
|
151
|
-
|
|
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:
|
|
168
|
-
retryDelay: 1000, //
|
|
169
|
-
|
|
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
|
-
|
|
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
|
|
181
|
-
const api = accessio.create({ rateLimiter: limiter });
|
|
139
|
+
const api = accessio.create({ rateLimiter });
|
|
182
140
|
|
|
183
|
-
//
|
|
184
|
-
const
|
|
185
|
-
|
|
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
|
-
###
|
|
146
|
+
### 🌊 SSE & Newline JSON Streaming
|
|
192
147
|
|
|
193
|
-
|
|
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
|
-
//
|
|
197
|
-
|
|
198
|
-
//
|
|
199
|
-
|
|
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
|
-
###
|
|
157
|
+
### 📂 Auto-Pagination
|
|
204
158
|
|
|
205
|
-
|
|
159
|
+
Avoid boilerplate code for pagination. Accessio can auto-follow `next` and `links.next` properties automatically:
|
|
206
160
|
|
|
207
161
|
```typescript
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
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
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
181
|
+
const response = await api.get('/users/123', {
|
|
182
|
+
schema: UserSchema, // Throws AccessioError (ERR_BAD_RESPONSE) if validation fails
|
|
183
|
+
});
|
|
231
184
|
|
|
232
|
-
|
|
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
|
-
###
|
|
188
|
+
### 📦 Request Deduplication & Caching
|
|
239
189
|
|
|
240
|
-
|
|
190
|
+
Optimize application performance by preventing duplicate queries and utilizing caching.
|
|
241
191
|
|
|
242
192
|
```typescript
|
|
243
|
-
|
|
244
|
-
|
|
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
|
-
###
|
|
203
|
+
### 🔗 Interceptors & Hooks
|
|
249
204
|
|
|
250
|
-
|
|
205
|
+
Modify requests and responses at runtime, or listen to client lifecycles:
|
|
251
206
|
|
|
252
207
|
```typescript
|
|
253
|
-
const
|
|
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
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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
|
-
##
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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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
|
|
1
|
+
{"version":3,"sources":["../../src/core/accessioError.ts"],"sourcesContent":["import ErrorCodes from '../constants/errorCodes';\nimport type { AccessioRequestConfig, AccessioResponse } from '../types';\n\nfunction redactHeaders(headers: unknown): unknown {\n if (!headers || typeof headers !== 'object') return headers;\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(headers as Record<string, unknown>)) {\n const value = (headers as Record<string, unknown>)[key];\n if (/^authorization$/i.test(key) || /^cookie$/i.test(key) || /^set-cookie$/i.test(key)) {\n out[key] = '[REDACTED]';\n } else if (value && typeof value === 'object' && !Array.isArray(value)) {\n out[key] = redactHeaders(value);\n } else {\n out[key] = value;\n }\n }\n return out;\n}\n\nconst SENSITIVE_BODY_KEY =\n /^(password|passwd|pwd|token|access_token|refresh_token|id_token|authorization|api[_-]?key|secret|client[_-]?secret|cookie|set[_-]?cookie|private[_-]?key|session)$/i;\n\nexport function redactBody(value: unknown, seen?: WeakSet<object>): unknown {\n if (value === null || typeof value !== 'object') return value;\n const visited = seen ?? new WeakSet<object>();\n if (visited.has(value as object)) return '[Circular]';\n visited.add(value as object);\n\n if (Array.isArray(value)) {\n return value.map((item) => redactBody(item, visited));\n }\n\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>)) {\n const v = (value as Record<string, unknown>)[key];\n if (SENSITIVE_BODY_KEY.test(key)) {\n out[key] = '[REDACTED]';\n } else {\n out[key] = redactBody(v, visited);\n }\n }\n return out;\n}\n\nfunction redactParams(params: unknown): unknown {\n if (!params || typeof params !== 'object') return params;\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(params as Record<string, unknown>)) {\n const value = (params as Record<string, unknown>)[key];\n if (SENSITIVE_BODY_KEY.test(key)) {\n out[key] = '[REDACTED]';\n } else if (value && typeof value === 'object' && !Array.isArray(value)) {\n out[key] = redactParams(value);\n } else {\n out[key] = value;\n }\n }\n return out;\n}\n\nfunction redactURL(url: string | undefined): string | undefined {\n if (!url) return url;\n // Match inline credentials: http://user:pass@host\n return url.replace(/^([a-z][a-z\\d+\\-.]*:\\/\\/)([^/]+)@/i, (match, protocol, userInfo) => {\n const parts = userInfo.split(':');\n if (parts.length > 1) {\n return `${protocol}${parts[0]}:[REDACTED]@`;\n }\n return `${protocol}[REDACTED]@`;\n });\n}\n\nexport function redactConfig(config: AccessioRequestConfig | null): AccessioRequestConfig | null {\n if (!config) return config;\n const clone = { ...config } as AccessioRequestConfig & { auth?: unknown };\n if ('auth' in clone) delete clone.auth;\n if (clone.headers) clone.headers = redactHeaders(clone.headers) as typeof clone.headers;\n if (clone.params) clone.params = redactParams(clone.params) as typeof clone.params;\n if (clone.url) clone.url = redactURL(clone.url);\n if (clone._builtUrl) clone._builtUrl = redactURL(clone._builtUrl);\n return clone;\n}\n\nexport class AccessioError extends Error {\n static ERR_BAD_OPTION_VALUE: string = ErrorCodes.ERR_BAD_OPTION_VALUE;\n static ERR_BAD_OPTION: string = ErrorCodes.ERR_BAD_OPTION;\n static ECONNABORTED: string = ErrorCodes.ECONNABORTED;\n static ETIMEDOUT: string = ErrorCodes.ETIMEDOUT;\n static ERR_NETWORK: string = ErrorCodes.ERR_NETWORK;\n static ERR_FR_TOO_MANY_REDIRECTS: string = ErrorCodes.ERR_FR_TOO_MANY_REDIRECTS;\n static ERR_BAD_RESPONSE: string = ErrorCodes.ERR_BAD_RESPONSE;\n static ERR_BAD_REQUEST: string = ErrorCodes.ERR_BAD_REQUEST;\n static ERR_CANCELED: string = ErrorCodes.ERR_CANCELED;\n static ERR_NOT_SUPPORT: string = ErrorCodes.ERR_NOT_SUPPORT;\n static ERR_INVALID_URL: string = ErrorCodes.ERR_INVALID_URL;\n\n readonly code: string | null;\n readonly config: AccessioRequestConfig | null;\n readonly request: unknown;\n readonly response: AccessioResponse | null;\n readonly isAccessioError: true;\n cause?: Error;\n override name = 'AccessioError' as const;\n\n constructor(\n message: string,\n code: string | null,\n config: AccessioRequestConfig | null,\n request: unknown,\n response: AccessioResponse | null,\n ) {\n super(message);\n this.name = 'AccessioError';\n this.code = code ?? null;\n this.config = redactConfig(config ?? null);\n this.request = request ?? null;\n this.response = response ?? null;\n this.isAccessioError = true;\n\n if ((Error as any).captureStackTrace) {\n (Error as any).captureStackTrace(this, AccessioError);\n }\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n status: this.response ? this.response.status : null,\n config: this.config,\n };\n }\n\n static from(\n error: Error,\n code: string,\n config: AccessioRequestConfig | null,\n request: unknown,\n response: AccessioResponse | null,\n ): AccessioError {\n const accessioError = new AccessioError(error.message, code, config, request, response);\n accessioError.cause = error;\n accessioError.stack = error.stack;\n return accessioError;\n }\n}\n\nexport default AccessioError;\n"],"mappings":"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","names":["ErrorCodes"]}
|
package/cjs/core/buildURL.cjs
CHANGED
|
@@ -84,15 +84,18 @@ function buildURL(url, baseURL, params, paramsSerializer) {
|
|
|
84
84
|
if (key === "__proto__" || key === "prototype" || key === "constructor") continue;
|
|
85
85
|
unusedParams[key] = params[key];
|
|
86
86
|
}
|
|
87
|
-
fullURL = fullURL.replace(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
fullURL = fullURL.replace(
|
|
88
|
+
/(?::([a-zA-Z_][a-zA-Z0-9_]*))|(?:{([a-zA-Z_][a-zA-Z0-9_]*)})/g,
|
|
89
|
+
(match, p1, p2) => {
|
|
90
|
+
const key = p1 || p2;
|
|
91
|
+
if (key && Object.prototype.hasOwnProperty.call(unusedParams, key) && unusedParams[key] !== void 0) {
|
|
92
|
+
const val = unusedParams[key];
|
|
93
|
+
delete unusedParams[key];
|
|
94
|
+
return encodeURIComponent(String(val));
|
|
95
|
+
}
|
|
96
|
+
return match;
|
|
93
97
|
}
|
|
94
|
-
|
|
95
|
-
});
|
|
98
|
+
);
|
|
96
99
|
finalParams = unusedParams;
|
|
97
100
|
}
|
|
98
101
|
const serialized = serializeParams(finalParams, paramsSerializer);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/buildURL.ts"],"sourcesContent":["import type { ParamsSerializer } from '../types';\n\nfunction serializeParams(\n params: Record<string, unknown>,\n paramsSerializer?: ParamsSerializer,\n): string {\n if (!params) return '';\n\n if (typeof paramsSerializer === 'function') {\n return paramsSerializer(params);\n }\n\n if (typeof URLSearchParams !== 'undefined' && params instanceof URLSearchParams) {\n return params.toString();\n }\n\n const parts: string[] = [];\n\n function encode(prefix: string, value: unknown): void {\n if (value === null || value === undefined) {\n return;\n }\n\n if (Array.isArray(value)) {\n value.forEach((item, index) => {\n if (typeof item === 'object' && item !== null) {\n encode(`${prefix}[${index}]`, item);\n } else {\n encode(prefix, item);\n }\n });\n } else if (typeof value === 'object' && !(value instanceof Date)) {\n Object.keys(value as Record<string, unknown>).forEach((key) => {\n encode(`${prefix}[${key}]`, (value as Record<string, unknown>)[key]);\n });\n } else {\n const encodedValue = value instanceof Date ? value.toISOString() : value;\n parts.push(`${encodeURIComponent(prefix)}=${encodeURIComponent(encodedValue as string)}`);\n }\n }\n\n Object.keys(params).forEach((key) => {\n encode(key, params[key]);\n });\n\n return parts.join('&');\n}\n\nfunction combineURLs(baseURL: string, relativeURL: string): string {\n if (!baseURL) return relativeURL || '';\n if (!relativeURL) return baseURL;\n\n let base = baseURL;\n while (base.endsWith('/')) {\n base = base.slice(0, -1);\n }\n\n let relative = relativeURL;\n while (relative.startsWith('/')) {\n relative = relative.slice(1);\n }\n\n return `${base}/${relative}`;\n}\n\nfunction isAbsoluteURL(url: string): boolean {\n return /^([a-z][a-z\\d+\\-.]*:)/i.test(url);\n}\n\nexport default function buildURL(\n url: string,\n baseURL?: string,\n params?: Record<string, unknown>,\n paramsSerializer?: ParamsSerializer,\n): string {\n let fullURL = baseURL && !isAbsoluteURL(url) ? combineURLs(baseURL, url) : url || '';\n\n let finalParams = params;\n if (params && typeof params === 'object' && !(params instanceof URLSearchParams)) {\n const unusedParams: Record<string, unknown> = {};\n for (const key of Object.keys(params)) {\n if (key === '__proto__' || key === 'prototype' || key === 'constructor') continue;\n unusedParams[key] = (params as Record<string, unknown>)[key];\n }\n fullURL = fullURL.replace(/(?::([a-zA-Z0-9_]
|
|
1
|
+
{"version":3,"sources":["../../src/core/buildURL.ts"],"sourcesContent":["import type { ParamsSerializer } from '../types';\n\nfunction serializeParams(\n params: Record<string, unknown>,\n paramsSerializer?: ParamsSerializer,\n): string {\n if (!params) return '';\n\n if (typeof paramsSerializer === 'function') {\n return paramsSerializer(params);\n }\n\n if (typeof URLSearchParams !== 'undefined' && params instanceof URLSearchParams) {\n return params.toString();\n }\n\n const parts: string[] = [];\n\n function encode(prefix: string, value: unknown): void {\n if (value === null || value === undefined) {\n return;\n }\n\n if (Array.isArray(value)) {\n value.forEach((item, index) => {\n if (typeof item === 'object' && item !== null) {\n encode(`${prefix}[${index}]`, item);\n } else {\n encode(prefix, item);\n }\n });\n } else if (typeof value === 'object' && !(value instanceof Date)) {\n Object.keys(value as Record<string, unknown>).forEach((key) => {\n encode(`${prefix}[${key}]`, (value as Record<string, unknown>)[key]);\n });\n } else {\n const encodedValue = value instanceof Date ? value.toISOString() : value;\n parts.push(`${encodeURIComponent(prefix)}=${encodeURIComponent(encodedValue as string)}`);\n }\n }\n\n Object.keys(params).forEach((key) => {\n encode(key, params[key]);\n });\n\n return parts.join('&');\n}\n\nfunction combineURLs(baseURL: string, relativeURL: string): string {\n if (!baseURL) return relativeURL || '';\n if (!relativeURL) return baseURL;\n\n let base = baseURL;\n while (base.endsWith('/')) {\n base = base.slice(0, -1);\n }\n\n let relative = relativeURL;\n while (relative.startsWith('/')) {\n relative = relative.slice(1);\n }\n\n return `${base}/${relative}`;\n}\n\nfunction isAbsoluteURL(url: string): boolean {\n return /^([a-z][a-z\\d+\\-.]*:)/i.test(url);\n}\n\nexport default function buildURL(\n url: string,\n baseURL?: string,\n params?: Record<string, unknown>,\n paramsSerializer?: ParamsSerializer,\n): string {\n let fullURL = baseURL && !isAbsoluteURL(url) ? combineURLs(baseURL, url) : url || '';\n\n let finalParams = params;\n if (params && typeof params === 'object' && !(params instanceof URLSearchParams)) {\n const unusedParams: Record<string, unknown> = {};\n for (const key of Object.keys(params)) {\n if (key === '__proto__' || key === 'prototype' || key === 'constructor') continue;\n unusedParams[key] = (params as Record<string, unknown>)[key];\n }\n fullURL = fullURL.replace(\n /(?::([a-zA-Z_][a-zA-Z0-9_]*))|(?:{([a-zA-Z_][a-zA-Z0-9_]*)})/g,\n (match, p1, p2) => {\n const key = p1 || p2;\n if (\n key &&\n Object.prototype.hasOwnProperty.call(unusedParams, key) &&\n unusedParams[key] !== undefined\n ) {\n const val = unusedParams[key];\n delete unusedParams[key];\n return encodeURIComponent(String(val));\n }\n return match;\n },\n );\n finalParams = unusedParams;\n }\n\n const serialized = serializeParams(finalParams as Record<string, unknown>, paramsSerializer);\n if (serialized) {\n const hashIndex = fullURL.indexOf('#');\n let fragment = '';\n if (hashIndex !== -1) {\n fragment = fullURL.slice(hashIndex);\n fullURL = fullURL.slice(0, hashIndex);\n }\n fullURL += (fullURL.indexOf('?') === -1 ? '?' : '&') + serialized + fragment;\n }\n\n return fullURL;\n}\n\nexport { serializeParams, combineURLs, isAbsoluteURL };\n"],"mappings":"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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
|
-
|
|
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)),
|