accessio 1.0.0 â 1.1.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 +121 -391
- package/cjs/accessio.cjs +23 -52
- package/cjs/accessio.cjs.map +1 -1
- package/cjs/core/accessioError.cjs +1 -7
- package/cjs/core/accessioError.cjs.map +1 -1
- package/cjs/core/buildURL.cjs +1 -5
- package/cjs/core/buildURL.cjs.map +1 -1
- package/cjs/core/mergeConfig.cjs +2 -4
- package/cjs/core/mergeConfig.cjs.map +1 -1
- package/cjs/core/request.cjs +44 -66
- package/cjs/core/request.cjs.map +1 -1
- package/cjs/core/retry.cjs +2 -8
- package/cjs/core/retry.cjs.map +1 -1
- package/cjs/defaults/index.cjs.map +1 -1
- package/cjs/defaults/transforms.cjs.map +1 -1
- package/cjs/helpers/debug.cjs +1 -3
- package/cjs/helpers/debug.cjs.map +1 -1
- package/cjs/helpers/parseHeaders.cjs.map +1 -1
- package/cjs/helpers/rateLimiter.cjs +22 -14
- package/cjs/helpers/rateLimiter.cjs.map +1 -1
- package/cjs/helpers/transformData.cjs.map +1 -1
- package/cjs/index.cjs.map +1 -1
- package/cjs/interceptors/interceptorManager.cjs +40 -3
- package/cjs/interceptors/interceptorManager.cjs.map +1 -1
- package/package.json +6 -14
- package/src/accessio.ts +28 -72
- package/src/core/accessioError.ts +1 -7
- package/src/core/buildURL.ts +4 -13
- package/src/core/mergeConfig.ts +6 -16
- package/src/core/request.ts +44 -74
- package/src/core/retry.ts +4 -15
- package/src/defaults/index.ts +1 -3
- package/src/defaults/transforms.ts +1 -4
- package/src/helpers/debug.ts +12 -22
- package/src/helpers/parseHeaders.ts +1 -3
- package/src/helpers/rateLimiter.ts +28 -23
- package/src/helpers/transformData.ts +1 -4
- package/src/index.ts +3 -11
- package/src/interceptors/interceptorManager.ts +50 -2
- package/src/types.ts +7 -68
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Accessio ðŊ
|
|
2
2
|
|
|
3
|
-
**Fast, flexible HTTP client for Node.js and browsers â simple, modular, and dependency-free
|
|
3
|
+
**Fast, flexible HTTP client for Node.js and browsers â simple, modular, and dependency-free.**
|
|
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**.
|
|
4
6
|
|
|
5
7
|
---
|
|
6
8
|
|
|
@@ -8,32 +10,32 @@
|
|
|
8
10
|
|
|
9
11
|
- ð **Promise-based** â works seamlessly with `async`/`await`
|
|
10
12
|
- ð **Isomorphic** â runs in both browser and Node.js (âĨ 18)
|
|
11
|
-
- ðŠķ **Zero dependencies** â lightweight, built on native `fetch`
|
|
12
|
-
- ð **Interceptors** â transform requests and responses globally
|
|
13
|
+
- ðŠķ **Zero dependencies** â ultra-lightweight, built on native `fetch`
|
|
14
|
+
- ð **Interceptors** â transform requests and responses globally or per instance
|
|
13
15
|
- âïļ **Configurable instances** â create multiple API clients with custom defaults
|
|
14
|
-
- ðĄïļ **Error handling** â structured `AccessioError` with status, config, and response
|
|
15
|
-
- ðĶ **Dual format** â
|
|
16
|
-
- ð **TypeScript
|
|
17
|
-
- âąïļ **Timeout
|
|
18
|
-
- ð§ **Transform pipelines** â
|
|
19
|
-
- âŧïļ **Automatic Retries** â
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
- ðŊ **Familiar API** â intuitive, developer-friendly interface
|
|
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
|
|
24
25
|
|
|
25
26
|
---
|
|
26
27
|
|
|
27
28
|
## ðĶ Installation
|
|
28
29
|
|
|
29
|
-
accessio is available on both the official npm registry (unscoped) and GitHub Packages (scoped).
|
|
30
|
-
|
|
31
30
|
```bash
|
|
32
|
-
#
|
|
31
|
+
# Using npm
|
|
33
32
|
npm install accessio
|
|
34
33
|
|
|
35
|
-
#
|
|
36
|
-
|
|
34
|
+
# Using yarn
|
|
35
|
+
yarn add accessio
|
|
36
|
+
|
|
37
|
+
# Using pnpm
|
|
38
|
+
pnpm add accessio
|
|
37
39
|
```
|
|
38
40
|
|
|
39
41
|
---
|
|
@@ -41,18 +43,18 @@ npm install @salvatorecorvaglia/accessio
|
|
|
41
43
|
## ð Quick Start
|
|
42
44
|
|
|
43
45
|
```typescript
|
|
44
|
-
import accessio from
|
|
46
|
+
import accessio from 'accessio';
|
|
45
47
|
|
|
46
|
-
// GET request
|
|
47
|
-
const
|
|
48
|
-
console.log(response.data);
|
|
48
|
+
// Simple GET request
|
|
49
|
+
const { data } = await accessio.get('https://api.example.com/users');
|
|
49
50
|
|
|
50
51
|
// POST request with JSON body
|
|
51
|
-
const
|
|
52
|
-
name:
|
|
53
|
-
|
|
52
|
+
const response = await accessio.post('https://api.example.com/users', {
|
|
53
|
+
name: 'John Doe',
|
|
54
|
+
role: 'Developer',
|
|
54
55
|
});
|
|
55
|
-
|
|
56
|
+
|
|
57
|
+
console.log(`User created in ${response.duration}ms`);
|
|
56
58
|
```
|
|
57
59
|
|
|
58
60
|
---
|
|
@@ -61,427 +63,155 @@ console.log(newUser.data);
|
|
|
61
63
|
|
|
62
64
|
### Request Methods
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
accessio(
|
|
67
|
-
|
|
68
|
-
accessio.
|
|
69
|
-
accessio.
|
|
70
|
-
accessio.
|
|
71
|
-
accessio.
|
|
72
|
-
accessio.
|
|
73
|
-
accessio.
|
|
74
|
-
accessio.
|
|
75
|
-
accessio.patch(url, data?, config?)
|
|
76
|
-
accessio.postForm(url, data?, config?)
|
|
77
|
-
accessio.putForm(url, data?, config?)
|
|
78
|
-
accessio.patchForm(url, data?, config?)
|
|
79
|
-
accessio.getUri(config)
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### Configuration
|
|
83
|
-
|
|
84
|
-
```javascript
|
|
85
|
-
{
|
|
86
|
-
// URL path (optional if baseURL is set)
|
|
87
|
-
url: '/users',
|
|
88
|
-
|
|
89
|
-
// HTTP method (default: 'get')
|
|
90
|
-
method: 'get',
|
|
91
|
-
|
|
92
|
-
// Base URL prepended to `url` unless `url` is absolute
|
|
93
|
-
baseURL: 'https://api.example.com',
|
|
94
|
-
|
|
95
|
-
// Request headers
|
|
96
|
-
headers: {
|
|
97
|
-
'Authorization': 'Bearer token',
|
|
98
|
-
'X-Custom-Header': 'value'
|
|
99
|
-
},
|
|
100
|
-
|
|
101
|
-
// URL query parameters
|
|
102
|
-
params: { page: 1, limit: 10 },
|
|
103
|
-
|
|
104
|
-
// Custom params serializer
|
|
105
|
-
paramsSerializer: (params) => Qs.stringify(params),
|
|
106
|
-
|
|
107
|
-
// Request body (for POST, PUT, PATCH)
|
|
108
|
-
data: { name: 'John' },
|
|
109
|
-
|
|
110
|
-
// Timeout in milliseconds (0 = no timeout)
|
|
111
|
-
timeout: 5000,
|
|
112
|
-
|
|
113
|
-
// Expected response type: 'json' | 'text' | 'blob' | 'arraybuffer' | 'stream'
|
|
114
|
-
responseType: 'json',
|
|
115
|
-
|
|
116
|
-
// Include credentials in cross-site requests
|
|
117
|
-
withCredentials: false,
|
|
118
|
-
|
|
119
|
-
// Basic auth (sets Authorization header automatically)
|
|
120
|
-
auth: { username: 'user', password: 'secret' },
|
|
121
|
-
|
|
122
|
-
// Transform functions for request/response data
|
|
123
|
-
transformRequest: [(data, headers) => { /* ... */ return data; }],
|
|
124
|
-
transformResponse: [(data) => { /* ... */ return data; }],
|
|
125
|
-
|
|
126
|
-
// Automatic retry strategy
|
|
127
|
-
retry: 3, // Max retry attempts (default: 0)
|
|
128
|
-
retryDelay: 1000, // Base delay in ms, doubles on each attempt (default: 1000)
|
|
129
|
-
retryCondition: (error) => true, // Custom retry predicate
|
|
130
|
-
onRetry: (attempt, error, config) => {}, // Called before each retry
|
|
131
|
-
|
|
132
|
-
// Debug logging
|
|
133
|
-
debug: true, // Enable request/response debug logger
|
|
134
|
-
|
|
135
|
-
// Rate limiting
|
|
136
|
-
rateLimiter: limiter, // Rate limiter instance for concurrency control
|
|
137
|
-
|
|
138
|
-
// Determine which status codes resolve/reject the promise
|
|
139
|
-
validateStatus: (status) => status >= 200 && status < 300,
|
|
140
|
-
|
|
141
|
-
// AbortSignal for request cancellation
|
|
142
|
-
signal: controller.signal
|
|
143
|
-
}
|
|
144
|
-
```
|
|
66
|
+
| Method | Description |
|
|
67
|
+
| :--------------------------------------- | :-------------------------------------- |
|
|
68
|
+
| `accessio(config)` | Generic request using config object |
|
|
69
|
+
| `accessio.get(url, config?)` | GET request |
|
|
70
|
+
| `accessio.post(url, data?, config?)` | POST request |
|
|
71
|
+
| `accessio.put(url, data?, config?)` | PUT request |
|
|
72
|
+
| `accessio.patch(url, data?, config?)` | PATCH request |
|
|
73
|
+
| `accessio.delete(url, config?)` | DELETE request |
|
|
74
|
+
| `accessio.head(url, config?)` | HEAD request |
|
|
75
|
+
| `accessio.options(url, config?)` | OPTIONS request |
|
|
76
|
+
| `accessio.postForm(url, data?, config?)` | POST request with `multipart/form-data` |
|
|
145
77
|
|
|
146
|
-
###
|
|
78
|
+
### Configuration Options
|
|
147
79
|
|
|
148
|
-
```
|
|
80
|
+
```typescript
|
|
149
81
|
{
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
headers: {},
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
82
|
+
baseURL: 'https://api.example.com', // Base URL for all requests
|
|
83
|
+
url: '/users', // Relative or absolute URL
|
|
84
|
+
method: 'get', // HTTP method (default: get)
|
|
85
|
+
headers: { 'X-Custom': 'val' }, // Custom headers
|
|
86
|
+
params: { id: 123 }, // URL query parameters
|
|
87
|
+
data: { name: 'John' }, // Request body (JSON/FormData/etc)
|
|
88
|
+
timeout: 5000, // Timeout in ms (default: 0)
|
|
89
|
+
responseType: 'json', // Expected response: 'json', 'text', 'blob', 'stream'
|
|
90
|
+
auth: { username: '', password: '' }, // Basic auth credentials
|
|
91
|
+
retry: 3, // Max retry attempts
|
|
92
|
+
retryDelay: 1000, // Base delay for exponential backoff
|
|
93
|
+
debug: true, // Enable structured logging
|
|
94
|
+
rateLimiter: limiter, // Concurrency limiter instance
|
|
95
|
+
validateStatus: (s) => s < 400, // Resolve/reject predicate
|
|
96
|
+
signal: abortController.signal, // Custom AbortSignal
|
|
157
97
|
}
|
|
158
98
|
```
|
|
159
99
|
|
|
160
100
|
---
|
|
161
101
|
|
|
162
|
-
##
|
|
102
|
+
## âŧïļ Advanced Usage
|
|
163
103
|
|
|
164
|
-
|
|
165
|
-
import accessio from "accessio";
|
|
104
|
+
### Interceptors
|
|
166
105
|
|
|
167
|
-
|
|
168
|
-
baseURL: "https://api.example.com",
|
|
169
|
-
timeout: 10000,
|
|
170
|
-
headers: {
|
|
171
|
-
Authorization: "Bearer my-token",
|
|
172
|
-
},
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
// All requests will use the instance config
|
|
176
|
-
const users = await api.get("/users");
|
|
177
|
-
const posts = await api.get("/posts", { params: { limit: 5 } });
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
---
|
|
181
|
-
|
|
182
|
-
## ð Interceptors
|
|
106
|
+
Interceptors allow you to transform requests or responses before they are handled by `then` or `catch`.
|
|
183
107
|
|
|
184
108
|
```typescript
|
|
185
|
-
//
|
|
186
|
-
accessio.interceptors.request.use(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
},
|
|
191
|
-
(error) => Promise.reject(error),
|
|
192
|
-
);
|
|
109
|
+
// Add a request interceptor
|
|
110
|
+
accessio.interceptors.request.use((config) => {
|
|
111
|
+
config.headers['Authorization'] = `Bearer ${storage.getToken()}`;
|
|
112
|
+
return config;
|
|
113
|
+
});
|
|
193
114
|
|
|
194
|
-
//
|
|
115
|
+
// Add a response interceptor
|
|
195
116
|
accessio.interceptors.response.use(
|
|
196
117
|
(response) => response,
|
|
197
118
|
(error) => {
|
|
198
|
-
|
|
199
|
-
if (error.response?.status === 401) {
|
|
200
|
-
redirectToLogin();
|
|
201
|
-
}
|
|
119
|
+
if (error.response?.status === 401) logout();
|
|
202
120
|
return Promise.reject(error);
|
|
203
121
|
},
|
|
204
122
|
);
|
|
205
|
-
|
|
206
|
-
// Conditional interceptor â runs only when the predicate returns true
|
|
207
|
-
accessio.interceptors.request.use(
|
|
208
|
-
(config) => {
|
|
209
|
-
/* ... */ return config;
|
|
210
|
-
},
|
|
211
|
-
null,
|
|
212
|
-
{ runWhen: (config) => config.method === "post" },
|
|
213
|
-
);
|
|
214
|
-
|
|
215
|
-
// Remove a single interceptor
|
|
216
|
-
const id = accessio.interceptors.request.use(/* ... */);
|
|
217
|
-
accessio.interceptors.request.eject(id);
|
|
218
|
-
|
|
219
|
-
// Remove all interceptors
|
|
220
|
-
accessio.interceptors.request.clear();
|
|
221
123
|
```
|
|
222
124
|
|
|
223
|
-
|
|
125
|
+
### Error Handling
|
|
224
126
|
|
|
225
|
-
|
|
226
|
-
- Response interceptors run in **normal** order (first added â first executed)
|
|
127
|
+
`accessio` provides a structured error object with specific codes to help you handle failures gracefully.
|
|
227
128
|
|
|
228
|
-
|
|
129
|
+
| Code | Description |
|
|
130
|
+
| :----------------- | :---------------------------- |
|
|
131
|
+
| `ERR_BAD_REQUEST` | 4xx status code |
|
|
132
|
+
| `ERR_BAD_RESPONSE` | 5xx status code |
|
|
133
|
+
| `ERR_NETWORK` | Network connectivity issues |
|
|
134
|
+
| `ETIMEDOUT` | Request exceeded timeout |
|
|
135
|
+
| `ERR_CANCELED` | Request was manually aborted |
|
|
136
|
+
| `ERR_INVALID_URL` | The provided URL is malformed |
|
|
137
|
+
| `ERR_BAD_OPTION` | Invalid configuration option |
|
|
229
138
|
|
|
230
|
-
|
|
139
|
+
### Automatic Retries
|
|
231
140
|
|
|
232
|
-
|
|
233
|
-
try {
|
|
234
|
-
await accessio.get("/might-fail");
|
|
235
|
-
} catch (error) {
|
|
236
|
-
if (accessio.isAccessioError(error)) {
|
|
237
|
-
if (error.response) {
|
|
238
|
-
console.log(error.response.status); // 404, 500, etc.
|
|
239
|
-
console.log(error.response.data); // Response body
|
|
240
|
-
} else if (error.code === "ERR_NETWORK") {
|
|
241
|
-
console.log("Network error â no response received");
|
|
242
|
-
} else if (error.code === "ETIMEDOUT") {
|
|
243
|
-
console.log("Request timed out");
|
|
244
|
-
} else if (accessio.isCancel(error)) {
|
|
245
|
-
console.log("Request was cancelled");
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
console.log(error.config); // Request config
|
|
249
|
-
console.log(error.message); // Error message
|
|
250
|
-
console.log(error.toJSON()); // Serializable summary
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
### Error Codes
|
|
256
|
-
|
|
257
|
-
| Code | Description |
|
|
258
|
-
| --------------------------- | --------------------------------------- |
|
|
259
|
-
| `ERR_BAD_REQUEST` | 4xx status code |
|
|
260
|
-
| `ERR_BAD_RESPONSE` | 5xx status code |
|
|
261
|
-
| `ERR_NETWORK` | Network error (no response received) |
|
|
262
|
-
| `ETIMEDOUT` | Request timed out |
|
|
263
|
-
| `ECONNABORTED` | Request was aborted |
|
|
264
|
-
| `ERR_CANCELED` | Request was cancelled via `AbortSignal` |
|
|
265
|
-
| `ERR_BAD_OPTION` | Invalid or missing configuration option |
|
|
266
|
-
| `ERR_BAD_OPTION_VALUE` | Invalid configuration option value |
|
|
267
|
-
| `ERR_INVALID_URL` | The provided URL is invalid |
|
|
268
|
-
| `ERR_FR_TOO_MANY_REDIRECTS` | Too many redirects |
|
|
269
|
-
| `ERR_NOT_SUPPORT` | Feature not supported in environment |
|
|
270
|
-
|
|
271
|
-
---
|
|
272
|
-
|
|
273
|
-
## ð Concurrent Requests
|
|
141
|
+
`accessio` includes a powerful retry mechanism that handles network errors and 5xx responses automatically.
|
|
274
142
|
|
|
275
|
-
```
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
// Or with the spread helper (deprecated â use modern spread syntax instead)
|
|
282
|
-
accessio.all([accessio.get("/users"), accessio.get("/posts")]).then(
|
|
283
|
-
accessio.spread((users, posts) => {
|
|
284
|
-
console.log(users.data, posts.data);
|
|
285
|
-
}),
|
|
286
|
-
);
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
---
|
|
290
|
-
|
|
291
|
-
## â Cancellation
|
|
292
|
-
|
|
293
|
-
```javascript
|
|
294
|
-
const controller = new AbortController();
|
|
295
|
-
|
|
296
|
-
accessio
|
|
297
|
-
.get("/slow-endpoint", {
|
|
298
|
-
signal: controller.signal,
|
|
299
|
-
})
|
|
300
|
-
.catch((error) => {
|
|
301
|
-
if (accessio.isCancel(error)) {
|
|
302
|
-
console.log("Request cancelled:", error.message);
|
|
303
|
-
}
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
// Cancel the request
|
|
307
|
-
controller.abort();
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
---
|
|
311
|
-
|
|
312
|
-
## ð§ Transform Request / Response
|
|
313
|
-
|
|
314
|
-
```javascript
|
|
315
|
-
const api = accessio.create({
|
|
316
|
-
transformRequest: [
|
|
317
|
-
(data, headers) => {
|
|
318
|
-
// Add a timestamp to every outgoing body
|
|
319
|
-
if (data && typeof data === "object") {
|
|
320
|
-
data.timestamp = Date.now();
|
|
321
|
-
}
|
|
322
|
-
return JSON.stringify(data);
|
|
323
|
-
},
|
|
324
|
-
],
|
|
325
|
-
transformResponse: [
|
|
326
|
-
(data) => {
|
|
327
|
-
if (typeof data === "string") {
|
|
328
|
-
try {
|
|
329
|
-
return JSON.parse(data);
|
|
330
|
-
} catch {}
|
|
331
|
-
}
|
|
332
|
-
return data;
|
|
333
|
-
},
|
|
334
|
-
(data) => {
|
|
335
|
-
// Unwrap a { data: ... } envelope
|
|
336
|
-
return data?.data ?? data;
|
|
337
|
-
},
|
|
338
|
-
],
|
|
339
|
-
});
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
---
|
|
343
|
-
|
|
344
|
-
## âŧïļ Advanced Features
|
|
345
|
-
|
|
346
|
-
### Automatic Retry
|
|
347
|
-
|
|
348
|
-
accessio supports configurable retries with exponential backoff and Âą25% jitter to prevent thundering herd.
|
|
349
|
-
|
|
350
|
-
```javascript
|
|
351
|
-
const response = await accessio.get("/flaky-api", {
|
|
352
|
-
retry: 3, // Retry up to 3 times
|
|
353
|
-
retryDelay: 1000, // Base delay â doubles on each attempt: 1s, 2s, 4s
|
|
354
|
-
|
|
355
|
-
// Optional: custom condition (default: network errors + 5xx)
|
|
356
|
-
retryCondition: (error) => error.response?.status === 503,
|
|
357
|
-
|
|
358
|
-
// Optional: callback before each retry
|
|
359
|
-
onRetry: (attempt, error, config) => {
|
|
360
|
-
console.log(`Retry attempt #${attempt} after: ${error.message}`);
|
|
361
|
-
},
|
|
143
|
+
```typescript
|
|
144
|
+
const response = await accessio.get('/flaky-endpoint', {
|
|
145
|
+
retry: 5,
|
|
146
|
+
retryDelay: 1000, // Delays: 1s, 2s, 4s, 8s, 16s (+/- random jitter)
|
|
147
|
+
onRetry: (attempt, error) => console.log(`Retry #${attempt}...`),
|
|
362
148
|
});
|
|
363
149
|
```
|
|
364
150
|
|
|
365
|
-
The default `retryCondition` retries on:
|
|
366
|
-
|
|
367
|
-
- `ERR_NETWORK` â no response received
|
|
368
|
-
- `ETIMEDOUT` â request timed out
|
|
369
|
-
- 5xx server errors
|
|
370
|
-
- Does **not** retry on `ERR_CANCELED` or 4xx client errors
|
|
371
|
-
|
|
372
151
|
### Rate Limiting
|
|
373
152
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
#### Option 1: Built-in config integration (recommended)
|
|
377
|
-
|
|
378
|
-
```javascript
|
|
379
|
-
import accessio, { createRateLimiter } from "accessio";
|
|
380
|
-
|
|
381
|
-
const limiter = createRateLimiter(5); // max 5 concurrent requests
|
|
153
|
+
Limit concurrent requests globally or per-instance to prevent overloading APIs.
|
|
382
154
|
|
|
383
|
-
|
|
384
|
-
|
|
155
|
+
```typescript
|
|
156
|
+
import { createRateLimiter } from 'accessio';
|
|
385
157
|
|
|
386
|
-
|
|
158
|
+
const limiter = createRateLimiter(5); // Max 5 concurrent requests
|
|
387
159
|
const api = accessio.create({ rateLimiter: limiter });
|
|
388
|
-
const users = await api.get("/users");
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
#### Option 2: Manual acquire / release
|
|
392
160
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
try {
|
|
400
|
-
const res = await accessio.get("/api/data");
|
|
401
|
-
} finally {
|
|
402
|
-
limiter.release();
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// Inspect state
|
|
406
|
-
console.log(limiter.active); // Currently running requests
|
|
407
|
-
console.log(limiter.pending); // Requests waiting in queue
|
|
408
|
-
|
|
409
|
-
// Cleanup on navigation / component unmount â rejects all queued promises
|
|
410
|
-
limiter.destroy();
|
|
411
|
-
console.log(limiter.destroyed); // true
|
|
161
|
+
// Requests will wait in queue if limit is reached
|
|
162
|
+
const results = await Promise.all([
|
|
163
|
+
api.get('/req-1'),
|
|
164
|
+
api.get('/req-2'),
|
|
165
|
+
// ...
|
|
166
|
+
]);
|
|
412
167
|
```
|
|
413
168
|
|
|
414
|
-
### Debug
|
|
169
|
+
### Debug Mode
|
|
415
170
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
```javascript
|
|
419
|
-
const api = accessio.create({ debug: true });
|
|
171
|
+
Get beautiful, structured logs in your console by enabling `debug: true`.
|
|
420
172
|
|
|
173
|
+
```typescript
|
|
421
174
|
// ðĶâ⎠[accessio] â GET https://api.example.com/users
|
|
422
175
|
// Params: {"page":1}
|
|
423
176
|
// Timeout: 5000ms
|
|
424
177
|
// ðĶâ⎠[accessio] â â
200 OK (142ms)
|
|
425
178
|
// Size: ~3.2 KB
|
|
426
|
-
|
|
427
|
-
// Or enable per-request only
|
|
428
|
-
await accessio.get("/debug-this", { debug: true });
|
|
429
179
|
```
|
|
430
180
|
|
|
431
181
|
---
|
|
432
182
|
|
|
433
|
-
##
|
|
183
|
+
## ð ïļ Developer Guide
|
|
434
184
|
|
|
435
|
-
###
|
|
185
|
+
### Local Setup
|
|
436
186
|
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
AccessioError,
|
|
442
|
-
mergeConfig,
|
|
443
|
-
buildURL,
|
|
444
|
-
logRequest,
|
|
445
|
-
logResponse,
|
|
446
|
-
logError,
|
|
447
|
-
} from "accessio";
|
|
187
|
+
```bash
|
|
188
|
+
git clone https://github.com/salvatorecorvaglia/accessio.git
|
|
189
|
+
cd accessio
|
|
190
|
+
npm install
|
|
448
191
|
```
|
|
449
192
|
|
|
450
|
-
###
|
|
193
|
+
### Available Scripts
|
|
451
194
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
### Sub-path imports
|
|
458
|
-
|
|
459
|
-
```javascript
|
|
460
|
-
// Import only what you need
|
|
461
|
-
import { createRateLimiter } from "accessio/helpers/rateLimiter";
|
|
462
|
-
import { logRequest, logResponse, logError } from "accessio/helpers/debug";
|
|
463
|
-
import { buildURL } from "accessio/core/buildURL";
|
|
464
|
-
import { mergeConfig } from "accessio/core/mergeConfig";
|
|
465
|
-
import { dispatchRequest } from "accessio/core/request";
|
|
466
|
-
import { retryRequest } from "accessio/core/retry";
|
|
467
|
-
import { parseHeaders } from "accessio/helpers/parseHeaders";
|
|
468
|
-
import { settle } from "accessio/helpers/settle";
|
|
469
|
-
import { transformData } from "accessio/helpers/transformData";
|
|
470
|
-
```
|
|
195
|
+
- `npm run build`: Generate ESM and CommonJS bundles
|
|
196
|
+
- `npm run test`: Run the full test suite with Vitest
|
|
197
|
+
- `npm run lint`: Check for code style issues
|
|
198
|
+
- `npm run format`: Automatically format the codebase with Prettier
|
|
199
|
+
- `npm run typecheck`: Validate TypeScript types
|
|
471
200
|
|
|
472
201
|
---
|
|
473
202
|
|
|
474
|
-
##
|
|
203
|
+
## ðĪ Contributing
|
|
204
|
+
|
|
205
|
+
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
475
206
|
|
|
476
|
-
|
|
477
|
-
| --------------------------------- | ------------------------------------------- |
|
|
478
|
-
| [CHANGELOG](./CHANGELOG.md) | Version history and release notes |
|
|
479
|
-
| [CONTRIBUTING](./CONTRIBUTING.md) | Guide for contributors |
|
|
480
|
-
| [SECURITY](./SECURITY.md) | Security policy and vulnerability reporting |
|
|
481
|
-
| [CONTRIBUTORS](./CONTRIBUTORS.md) | Project contributors |
|
|
207
|
+
## ð Security
|
|
482
208
|
|
|
483
|
-
|
|
209
|
+
If you discover a security vulnerability, please see our [Security Policy](SECURITY.md).
|
|
484
210
|
|
|
485
|
-
|
|
211
|
+
## ð License
|
|
212
|
+
|
|
213
|
+
Distributed under the MIT License. See [LICENSE](LICENSE) for more information.
|
|
214
|
+
|
|
215
|
+
---
|
|
486
216
|
|
|
487
|
-
|
|
217
|
+
**Author**: [Salvatore Corvaglia](https://github.com/salvatorecorvaglia)
|