accessio 1.4.0 → 1.6.0

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