accessio 1.0.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/LICENSE +21 -0
- package/README.md +487 -0
- package/cjs/accessio.cjs +208 -0
- package/cjs/accessio.cjs.map +1 -0
- package/cjs/constants/errorCodes.cjs +76 -0
- package/cjs/constants/errorCodes.cjs.map +1 -0
- package/cjs/core/accessioError.cjs +93 -0
- package/cjs/core/accessioError.cjs.map +1 -0
- package/cjs/core/buildURL.cjs +100 -0
- package/cjs/core/buildURL.cjs.map +1 -0
- package/cjs/core/mergeConfig.cjs +73 -0
- package/cjs/core/mergeConfig.cjs.map +1 -0
- package/cjs/core/request.cjs +259 -0
- package/cjs/core/request.cjs.map +1 -0
- package/cjs/core/retry.cjs +109 -0
- package/cjs/core/retry.cjs.map +1 -0
- package/cjs/defaults/index.cjs +55 -0
- package/cjs/defaults/index.cjs.map +1 -0
- package/cjs/defaults/transforms.cjs +59 -0
- package/cjs/defaults/transforms.cjs.map +1 -0
- package/cjs/helpers/debug.cjs +96 -0
- package/cjs/helpers/debug.cjs.map +1 -0
- package/cjs/helpers/parseHeaders.cjs +52 -0
- package/cjs/helpers/parseHeaders.cjs.map +1 -0
- package/cjs/helpers/rateLimiter.cjs +98 -0
- package/cjs/helpers/rateLimiter.cjs.map +1 -0
- package/cjs/helpers/settle.cjs +50 -0
- package/cjs/helpers/settle.cjs.map +1 -0
- package/cjs/helpers/transformData.cjs +57 -0
- package/cjs/helpers/transformData.cjs.map +1 -0
- package/cjs/index.cjs +121 -0
- package/cjs/index.cjs.map +1 -0
- package/cjs/interceptors/interceptorManager.cjs +31 -0
- package/cjs/interceptors/interceptorManager.cjs.map +1 -0
- package/index.d.ts +454 -0
- package/package.json +116 -0
- package/src/accessio.ts +251 -0
- package/src/constants/errorCodes.ts +29 -0
- package/src/core/accessioError.ts +74 -0
- package/src/core/buildURL.ts +99 -0
- package/src/core/mergeConfig.ts +78 -0
- package/src/core/request.ts +284 -0
- package/src/core/retry.ts +117 -0
- package/src/defaults/index.ts +36 -0
- package/src/defaults/transforms.ts +44 -0
- package/src/helpers/debug.ts +103 -0
- package/src/helpers/parseHeaders.ts +35 -0
- package/src/helpers/rateLimiter.ts +96 -0
- package/src/helpers/settle.ts +26 -0
- package/src/helpers/transformData.ts +36 -0
- package/src/index.ts +102 -0
- package/src/interceptors/interceptorManager.ts +5 -0
- package/src/types.ts +159 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 **salvatorecorvaglia** (https://github.com/salvatorecorvaglia)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
# accessio ðŊ
|
|
2
|
+
|
|
3
|
+
**Fast, flexible HTTP client for Node.js and browsers â simple, modular, and dependency-free** â lightweight, modern, zero dependencies.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## âĻ Features
|
|
8
|
+
|
|
9
|
+
- ð **Promise-based** â works seamlessly with `async`/`await`
|
|
10
|
+
- ð **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
|
+
- âïļ **Configurable instances** â create multiple API clients with custom defaults
|
|
14
|
+
- ðĄïļ **Error handling** â structured `AccessioError` with status, config, and response
|
|
15
|
+
- ðĶ **Dual format** â supports both ESM and CommonJS
|
|
16
|
+
- ð **TypeScript support** â full type definitions included
|
|
17
|
+
- âąïļ **Timeout support** â built-in request timeout via `AbortController`
|
|
18
|
+
- ð§ **Transform pipelines** â customize request/response data transformation
|
|
19
|
+
- âŧïļ **Automatic Retries** â built-in retry logic with exponential backoff and jitter
|
|
20
|
+
- âąïļ **Duration tracking** â every response includes a `duration` field in milliseconds
|
|
21
|
+
- ðĨ **Rate Limiter** â limit concurrent requests with acquire/release/destroy lifecycle
|
|
22
|
+
- ð **Debug Mode** â structured console logging for outgoing requests and responses
|
|
23
|
+
- ðŊ **Familiar API** â intuitive, developer-friendly interface
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## ðĶ Installation
|
|
28
|
+
|
|
29
|
+
accessio is available on both the official npm registry (unscoped) and GitHub Packages (scoped).
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Recommended: Standard npm registry
|
|
33
|
+
npm install accessio
|
|
34
|
+
|
|
35
|
+
# Alternative: GitHub Packages
|
|
36
|
+
npm install @salvatorecorvaglia/accessio
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## ð Quick Start
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import accessio from "accessio";
|
|
45
|
+
|
|
46
|
+
// GET request
|
|
47
|
+
const response = await accessio.get("https://api.example.com/users");
|
|
48
|
+
console.log(response.data);
|
|
49
|
+
|
|
50
|
+
// POST request with JSON body
|
|
51
|
+
const newUser = await accessio.post("https://api.example.com/users", {
|
|
52
|
+
name: "John Doe",
|
|
53
|
+
email: "john@example.com",
|
|
54
|
+
});
|
|
55
|
+
console.log(newUser.data);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## ð API Reference
|
|
61
|
+
|
|
62
|
+
### Request Methods
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
accessio(config)
|
|
66
|
+
accessio(url, config?)
|
|
67
|
+
|
|
68
|
+
accessio.request(config)
|
|
69
|
+
accessio.get(url, config?)
|
|
70
|
+
accessio.delete(url, config?)
|
|
71
|
+
accessio.head(url, config?)
|
|
72
|
+
accessio.options(url, config?)
|
|
73
|
+
accessio.post(url, data?, config?)
|
|
74
|
+
accessio.put(url, data?, config?)
|
|
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
|
+
```
|
|
145
|
+
|
|
146
|
+
### Response Object
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
{
|
|
150
|
+
data: {}, // Parsed response body
|
|
151
|
+
status: 200, // HTTP status code
|
|
152
|
+
statusText: 'OK', // HTTP status message
|
|
153
|
+
headers: {}, // Response headers (lowercase keys)
|
|
154
|
+
config: {}, // Request configuration used
|
|
155
|
+
request: {}, // Underlying fetch Response object
|
|
156
|
+
duration: 142 // Request duration in milliseconds
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## ð§ Creating Instances
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import accessio from "accessio";
|
|
166
|
+
|
|
167
|
+
const api = accessio.create({
|
|
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
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
// Request interceptor â runs before every request
|
|
186
|
+
accessio.interceptors.request.use(
|
|
187
|
+
(config) => {
|
|
188
|
+
config.headers["Authorization"] = `Bearer ${getToken()}`;
|
|
189
|
+
return config;
|
|
190
|
+
},
|
|
191
|
+
(error) => Promise.reject(error),
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
// Response interceptor â runs after every response
|
|
195
|
+
accessio.interceptors.response.use(
|
|
196
|
+
(response) => response,
|
|
197
|
+
(error) => {
|
|
198
|
+
// Handle 401 globally
|
|
199
|
+
if (error.response?.status === 401) {
|
|
200
|
+
redirectToLogin();
|
|
201
|
+
}
|
|
202
|
+
return Promise.reject(error);
|
|
203
|
+
},
|
|
204
|
+
);
|
|
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
|
+
```
|
|
222
|
+
|
|
223
|
+
**Execution order:**
|
|
224
|
+
|
|
225
|
+
- Request interceptors run in **reverse** order (last added â first executed)
|
|
226
|
+
- Response interceptors run in **normal** order (first added â first executed)
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## ðĄïļ Error Handling
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
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
|
|
274
|
+
|
|
275
|
+
```javascript
|
|
276
|
+
const [users, posts] = await accessio.all([
|
|
277
|
+
accessio.get("/users"),
|
|
278
|
+
accessio.get("/posts"),
|
|
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
|
+
},
|
|
362
|
+
});
|
|
363
|
+
```
|
|
364
|
+
|
|
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
|
+
### Rate Limiting
|
|
373
|
+
|
|
374
|
+
Control the maximum number of concurrent in-flight requests.
|
|
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
|
|
382
|
+
|
|
383
|
+
// Pass the limiter directly in the request config
|
|
384
|
+
const response = await accessio.get("/api/data", { rateLimiter: limiter });
|
|
385
|
+
|
|
386
|
+
// Or set it as an instance default
|
|
387
|
+
const api = accessio.create({ rateLimiter: limiter });
|
|
388
|
+
const users = await api.get("/users");
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
#### Option 2: Manual acquire / release
|
|
392
|
+
|
|
393
|
+
```javascript
|
|
394
|
+
import { createRateLimiter } from "accessio";
|
|
395
|
+
|
|
396
|
+
const limiter = createRateLimiter(5);
|
|
397
|
+
|
|
398
|
+
await limiter.acquire();
|
|
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
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Debug Logging
|
|
415
|
+
|
|
416
|
+
Enable `debug: true` on an instance or a single request to get structured console output.
|
|
417
|
+
|
|
418
|
+
```javascript
|
|
419
|
+
const api = accessio.create({ debug: true });
|
|
420
|
+
|
|
421
|
+
// ðĶâ⎠[accessio] â GET https://api.example.com/users
|
|
422
|
+
// Params: {"page":1}
|
|
423
|
+
// Timeout: 5000ms
|
|
424
|
+
// ðĶâ⎠[accessio] â â
200 OK (142ms)
|
|
425
|
+
// Size: ~3.2 KB
|
|
426
|
+
|
|
427
|
+
// Or enable per-request only
|
|
428
|
+
await accessio.get("/debug-this", { debug: true });
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
## ðĨ Imports
|
|
434
|
+
|
|
435
|
+
### ESM (recommended)
|
|
436
|
+
|
|
437
|
+
```typescript
|
|
438
|
+
import accessio from "accessio";
|
|
439
|
+
import {
|
|
440
|
+
createRateLimiter,
|
|
441
|
+
AccessioError,
|
|
442
|
+
mergeConfig,
|
|
443
|
+
buildURL,
|
|
444
|
+
logRequest,
|
|
445
|
+
logResponse,
|
|
446
|
+
logError,
|
|
447
|
+
} from "accessio";
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
### CommonJS
|
|
451
|
+
|
|
452
|
+
```typescript
|
|
453
|
+
const accessio = require("accessio");
|
|
454
|
+
const { createRateLimiter } = require("accessio");
|
|
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
|
+
```
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
474
|
+
## ð Documentation
|
|
475
|
+
|
|
476
|
+
| Document | Description |
|
|
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 |
|
|
482
|
+
|
|
483
|
+
## ð Author
|
|
484
|
+
|
|
485
|
+
**Salvatore Corvaglia**
|
|
486
|
+
|
|
487
|
+
- GitHub: [@salvatorecorvaglia](https://github.com/salvatorecorvaglia)
|
package/cjs/accessio.cjs
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var accessio_exports = {};
|
|
30
|
+
__export(accessio_exports, {
|
|
31
|
+
Accessio: () => Accessio,
|
|
32
|
+
default: () => accessio_default
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(accessio_exports);
|
|
35
|
+
var import_interceptorManager = __toESM(require("./interceptors/interceptorManager"), 1);
|
|
36
|
+
var import_accessioError = __toESM(require("./core/accessioError"), 1);
|
|
37
|
+
var import_mergeConfig = __toESM(require("./core/mergeConfig"), 1);
|
|
38
|
+
var import_request = __toESM(require("./core/request"), 1);
|
|
39
|
+
var import_buildURL = __toESM(require("./core/buildURL"), 1);
|
|
40
|
+
var import_retry = __toESM(require("./core/retry"), 1);
|
|
41
|
+
var import_debug = require("./helpers/debug");
|
|
42
|
+
var import_rateLimiter = require("./helpers/rateLimiter");
|
|
43
|
+
class Accessio {
|
|
44
|
+
defaults;
|
|
45
|
+
interceptors;
|
|
46
|
+
constructor(instanceConfig = {}) {
|
|
47
|
+
this.defaults = instanceConfig;
|
|
48
|
+
this.interceptors = {
|
|
49
|
+
request: new import_interceptorManager.default(),
|
|
50
|
+
response: new import_interceptorManager.default()
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
request(configOrUrl, config) {
|
|
54
|
+
if (typeof configOrUrl === "string") {
|
|
55
|
+
config = { ...config, url: configOrUrl };
|
|
56
|
+
} else {
|
|
57
|
+
config = configOrUrl ? { ...configOrUrl } : {};
|
|
58
|
+
}
|
|
59
|
+
const mergedConfig = (0, import_mergeConfig.default)(this.defaults, config);
|
|
60
|
+
mergedConfig.method = (mergedConfig.method || "get").toLowerCase();
|
|
61
|
+
if (!mergedConfig.url && !mergedConfig.baseURL) {
|
|
62
|
+
throw new import_accessioError.default(
|
|
63
|
+
"Request URL is required. Provide a `url` or `baseURL` in the config.",
|
|
64
|
+
import_accessioError.default.ERR_BAD_OPTION,
|
|
65
|
+
mergedConfig,
|
|
66
|
+
null,
|
|
67
|
+
null
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
const requestInterceptors = [];
|
|
71
|
+
const responseInterceptors = [];
|
|
72
|
+
this.interceptors.request.forEach((interceptor) => {
|
|
73
|
+
if (interceptor.runWhen && !interceptor.runWhen(mergedConfig)) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
requestInterceptors.unshift(interceptor);
|
|
77
|
+
});
|
|
78
|
+
this.interceptors.response.forEach((interceptor) => {
|
|
79
|
+
responseInterceptors.push(interceptor);
|
|
80
|
+
});
|
|
81
|
+
let promise = Promise.resolve(mergedConfig);
|
|
82
|
+
for (const interceptor of requestInterceptors) {
|
|
83
|
+
promise = promise.then(
|
|
84
|
+
(value) => {
|
|
85
|
+
if (interceptor.fulfilled) {
|
|
86
|
+
return interceptor.fulfilled(value);
|
|
87
|
+
}
|
|
88
|
+
return value;
|
|
89
|
+
},
|
|
90
|
+
interceptor.rejected
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
promise = promise.then((cfg) => {
|
|
94
|
+
const fullUrl = (0, import_buildURL.default)(
|
|
95
|
+
cfg.url ?? "",
|
|
96
|
+
cfg.baseURL,
|
|
97
|
+
cfg.params,
|
|
98
|
+
cfg.paramsSerializer
|
|
99
|
+
);
|
|
100
|
+
(0, import_debug.logRequest)(cfg, fullUrl);
|
|
101
|
+
const enrichedCfg = fullUrl !== (cfg.url || "") ? { ...cfg, _builtUrl: fullUrl } : cfg;
|
|
102
|
+
const dispatchFn = cfg.rateLimiter ? (config2) => (0, import_rateLimiter.rateLimitedRequest)(import_request.default, config2.rateLimiter, config2) : import_request.default;
|
|
103
|
+
return (0, import_retry.default)(dispatchFn, enrichedCfg);
|
|
104
|
+
});
|
|
105
|
+
promise = promise.then(
|
|
106
|
+
(value) => {
|
|
107
|
+
(0, import_debug.logResponse)(value);
|
|
108
|
+
return value;
|
|
109
|
+
},
|
|
110
|
+
(error) => {
|
|
111
|
+
(0, import_debug.logError)(error, mergedConfig);
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
for (const interceptor of responseInterceptors) {
|
|
116
|
+
promise = promise.then(
|
|
117
|
+
(value) => {
|
|
118
|
+
if (interceptor.fulfilled) {
|
|
119
|
+
return interceptor.fulfilled(value);
|
|
120
|
+
}
|
|
121
|
+
return value;
|
|
122
|
+
},
|
|
123
|
+
interceptor.rejected
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return promise;
|
|
127
|
+
}
|
|
128
|
+
getUri(config) {
|
|
129
|
+
const merged = (0, import_mergeConfig.default)(this.defaults, config);
|
|
130
|
+
return (0, import_buildURL.default)(
|
|
131
|
+
merged.url ?? "",
|
|
132
|
+
merged.baseURL,
|
|
133
|
+
merged.params,
|
|
134
|
+
merged.paramsSerializer
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
get(url, config) {
|
|
138
|
+
return this.request(
|
|
139
|
+
(0, import_mergeConfig.default)(config || {}, { method: "get", url })
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
delete(url, config) {
|
|
143
|
+
return this.request(
|
|
144
|
+
(0, import_mergeConfig.default)(config || {}, { method: "delete", url })
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
head(url, config) {
|
|
148
|
+
return this.request(
|
|
149
|
+
(0, import_mergeConfig.default)(config || {}, { method: "head", url })
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
options(url, config) {
|
|
153
|
+
return this.request(
|
|
154
|
+
(0, import_mergeConfig.default)(config || {}, { method: "options", url })
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
post(url, data, config) {
|
|
158
|
+
return this.request(
|
|
159
|
+
(0, import_mergeConfig.default)(config || {}, { method: "post", url, data })
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
put(url, data, config) {
|
|
163
|
+
return this.request(
|
|
164
|
+
(0, import_mergeConfig.default)(config || {}, { method: "put", url, data })
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
patch(url, data, config) {
|
|
168
|
+
return this.request(
|
|
169
|
+
(0, import_mergeConfig.default)(config || {}, { method: "patch", url, data })
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
postForm(url, data, config) {
|
|
173
|
+
return this.request(
|
|
174
|
+
(0, import_mergeConfig.default)(config || {}, {
|
|
175
|
+
method: "post",
|
|
176
|
+
url,
|
|
177
|
+
data,
|
|
178
|
+
headers: { "Content-Type": "multipart/form-data" }
|
|
179
|
+
})
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
putForm(url, data, config) {
|
|
183
|
+
return this.request(
|
|
184
|
+
(0, import_mergeConfig.default)(config || {}, {
|
|
185
|
+
method: "put",
|
|
186
|
+
url,
|
|
187
|
+
data,
|
|
188
|
+
headers: { "Content-Type": "multipart/form-data" }
|
|
189
|
+
})
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
patchForm(url, data, config) {
|
|
193
|
+
return this.request(
|
|
194
|
+
(0, import_mergeConfig.default)(config || {}, {
|
|
195
|
+
method: "patch",
|
|
196
|
+
url,
|
|
197
|
+
data,
|
|
198
|
+
headers: { "Content-Type": "multipart/form-data" }
|
|
199
|
+
})
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
var accessio_default = Accessio;
|
|
204
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
205
|
+
0 && (module.exports = {
|
|
206
|
+
Accessio
|
|
207
|
+
});
|
|
208
|
+
//# sourceMappingURL=accessio.cjs.map
|