httix-http 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 +1184 -0
- package/dist/cjs/index.cjs +7 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs/index.d.cts +592 -0
- package/dist/cjs/plugins/index.cjs +2 -0
- package/dist/cjs/plugins/index.cjs.map +1 -0
- package/dist/cjs/plugins/index.d.cts +154 -0
- package/dist/cjs/types-DkChbb42.d.cts +340 -0
- package/dist/esm/index.d.ts +592 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/plugins/index.d.ts +154 -0
- package/dist/esm/plugins/index.js +2 -0
- package/dist/esm/plugins/index.js.map +1 -0
- package/dist/esm/types-DkChbb42.d.ts +340 -0
- package/package.json +114 -0
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
import { I as InterceptorManager$1, a as InterceptorHandler, H as HttixClient, b as HttixConfig, R as RequestInterceptor, c as RequestErrorInterceptor, d as ResponseInterceptor, e as ResponseErrorInterceptor, f as HttixRequestConfig, S as SSEEvent, g as HttixResponse, h as RequestBody, M as MiddlewareFn, i as HttixAbortError, j as RetryConfig, k as MiddlewareContext, D as DownloadProgress, A as AuthConfig, B as BearerAuthConfig, P as PaginationConfig } from './types-DkChbb42.js';
|
|
2
|
+
export { l as ApiKeyAuthConfig, m as BackoffStrategy, n as BasicAuthConfig, o as DedupConfig, p as HttixError, q as HttixHeaders, r as HttixPlugin, s as HttixRequestError, t as HttixResponseError, u as HttixRetryError, v as HttixTimeoutError, w as HttpMethod, x as PaginationStyle, y as PathParams, Q as QueryParamValue, z as QueryParams, C as RateLimitConfig, E as StreamConfig } from './types-DkChbb42.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* httix — Interceptor management and execution
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Manages a list of interceptor handlers for requests or responses.
|
|
10
|
+
* Handlers can be added, ejected by id, or cleared entirely.
|
|
11
|
+
*/
|
|
12
|
+
declare class InterceptorManager<F, E> implements InterceptorManager$1<F, E> {
|
|
13
|
+
handlers: InterceptorHandler<F, E>[];
|
|
14
|
+
use(fulfilled: F, rejected?: E): number;
|
|
15
|
+
eject(id: number): void;
|
|
16
|
+
clear(): void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* httix — Main HTTP client implementation
|
|
21
|
+
*
|
|
22
|
+
* The HttixClientImpl class is the heart of the library. It orchestrates
|
|
23
|
+
* configuration merging, middleware, interceptors, authentication,
|
|
24
|
+
* deduplication, rate limiting, retries, timeouts, streaming, and
|
|
25
|
+
* pagination into a single cohesive request pipeline.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
declare class HttixClientImpl implements HttixClient {
|
|
29
|
+
/** Merged default configuration for this client instance. */
|
|
30
|
+
readonly defaults: HttixConfig;
|
|
31
|
+
/** Interceptor managers for request and response pipelines. */
|
|
32
|
+
readonly interceptors: {
|
|
33
|
+
request: InterceptorManager<RequestInterceptor, RequestErrorInterceptor>;
|
|
34
|
+
response: InterceptorManager<ResponseInterceptor<unknown>, ResponseErrorInterceptor>;
|
|
35
|
+
};
|
|
36
|
+
/** Stream utilities bound to this client. */
|
|
37
|
+
readonly stream: {
|
|
38
|
+
sse: (url: string, config?: Partial<HttixRequestConfig>) => AsyncIterable<SSEEvent>;
|
|
39
|
+
ndjson: <T = unknown>(url: string, config?: Partial<HttixRequestConfig>) => AsyncIterable<T>;
|
|
40
|
+
};
|
|
41
|
+
/** Pagination helper bound to this client. */
|
|
42
|
+
readonly paginate: HttixClient['paginate'];
|
|
43
|
+
private middlewares;
|
|
44
|
+
private deduplicator?;
|
|
45
|
+
private dedupConfig?;
|
|
46
|
+
private rateLimiter?;
|
|
47
|
+
private authConfig?;
|
|
48
|
+
private pendingControllers;
|
|
49
|
+
constructor(config?: Partial<HttixConfig>);
|
|
50
|
+
request<T = unknown>(config: HttixRequestConfig): Promise<HttixResponse<T>>;
|
|
51
|
+
get<T = unknown>(url: string, config?: Partial<HttixRequestConfig>): Promise<HttixResponse<T>>;
|
|
52
|
+
post<T = unknown>(url: string, body?: RequestBody, config?: Partial<HttixRequestConfig>): Promise<HttixResponse<T>>;
|
|
53
|
+
put<T = unknown>(url: string, body?: RequestBody, config?: Partial<HttixRequestConfig>): Promise<HttixResponse<T>>;
|
|
54
|
+
patch<T = unknown>(url: string, body?: RequestBody, config?: Partial<HttixRequestConfig>): Promise<HttixResponse<T>>;
|
|
55
|
+
delete<T = unknown>(url: string, config?: Partial<HttixRequestConfig>): Promise<HttixResponse<T>>;
|
|
56
|
+
head(url: string, config?: Partial<HttixRequestConfig>): Promise<HttixResponse<void>>;
|
|
57
|
+
options(url: string, config?: Partial<HttixRequestConfig>): Promise<HttixResponse<void>>;
|
|
58
|
+
use<T = unknown>(middleware: MiddlewareFn<T>): void;
|
|
59
|
+
create(overrides?: Partial<HttixConfig>): HttixClient;
|
|
60
|
+
/**
|
|
61
|
+
* Abort every in-flight request managed by this client.
|
|
62
|
+
*/
|
|
63
|
+
cancelAll(reason?: string): void;
|
|
64
|
+
/**
|
|
65
|
+
* Check whether an error is a cancellation (abort) error.
|
|
66
|
+
*/
|
|
67
|
+
isCancel(error: unknown): error is HttixAbortError;
|
|
68
|
+
/**
|
|
69
|
+
* Wraps the actual fetch in deduplication and rate-limiting layers.
|
|
70
|
+
*/
|
|
71
|
+
private executeWithDedupAndRateLimit;
|
|
72
|
+
private isDedupEnabled;
|
|
73
|
+
private generateDedupKey;
|
|
74
|
+
/**
|
|
75
|
+
* Build the native Request, execute fetch with retry support, parse the
|
|
76
|
+
* response body, and handle success / error paths.
|
|
77
|
+
*/
|
|
78
|
+
private doFetch;
|
|
79
|
+
/**
|
|
80
|
+
* Post-retry response processing:
|
|
81
|
+
* - 2xx → run response interceptors
|
|
82
|
+
* - non-2xx + throwOnError → run error interceptors, then throw
|
|
83
|
+
* - non-2xx + !throwOnError → return as-is
|
|
84
|
+
*/
|
|
85
|
+
private processResponse;
|
|
86
|
+
/**
|
|
87
|
+
* Execute an SSE stream request.
|
|
88
|
+
*
|
|
89
|
+
* Applies interceptors and auth, then returns an async iterable of
|
|
90
|
+
* SSEEvent objects by piping the response body through parseSSE().
|
|
91
|
+
*/
|
|
92
|
+
private executeSSE;
|
|
93
|
+
/**
|
|
94
|
+
* Execute an NDJSON stream request.
|
|
95
|
+
*
|
|
96
|
+
* Applies interceptors and auth, then returns an async iterable of parsed
|
|
97
|
+
* JSON objects by piping the response body through parseNDJSON().
|
|
98
|
+
*/
|
|
99
|
+
private executeNDJSON;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Create a new HttixClient with the given configuration.
|
|
103
|
+
*
|
|
104
|
+
* This is the recommended entry-point for creating client instances.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* import { createHttix } from 'httix';
|
|
109
|
+
*
|
|
110
|
+
* const client = createHttix({
|
|
111
|
+
* baseURL: 'https://api.example.com',
|
|
112
|
+
* headers: { 'X-App-Version': '1.0' },
|
|
113
|
+
* auth: { type: 'bearer', token: 'my-token' },
|
|
114
|
+
* });
|
|
115
|
+
*
|
|
116
|
+
* const { data } = await client.get('/users');
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
declare function createHttix(config?: Partial<HttixConfig>): HttixClient;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* httix — Default configuration values
|
|
123
|
+
*/
|
|
124
|
+
|
|
125
|
+
/** Default retry configuration */
|
|
126
|
+
declare const DEFAULT_RETRY: Required<RetryConfig>;
|
|
127
|
+
/** Default timeout in milliseconds */
|
|
128
|
+
declare const DEFAULT_TIMEOUT = 30000;
|
|
129
|
+
/** Default headers */
|
|
130
|
+
declare const DEFAULT_HEADERS: Record<string, string>;
|
|
131
|
+
/** Default request configuration */
|
|
132
|
+
declare const DEFAULT_REQUEST_CONFIG: Partial<HttixRequestConfig>;
|
|
133
|
+
/** Default client configuration */
|
|
134
|
+
declare const DEFAULT_CONFIG: HttixConfig;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* httix — Cancellation / abort utilities
|
|
138
|
+
*/
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* A cancellation token wraps an AbortController, exposing its signal and
|
|
142
|
+
* a promise that rejects when the token is cancelled.
|
|
143
|
+
*/
|
|
144
|
+
interface CancelToken {
|
|
145
|
+
signal: AbortSignal;
|
|
146
|
+
promise: Promise<never>;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Create a new cancel token and its associated cancel function.
|
|
150
|
+
*
|
|
151
|
+
* The returned `cancel` function triggers the underlying AbortController,
|
|
152
|
+
* causing the `signal` to abort and the `promise` to reject.
|
|
153
|
+
*/
|
|
154
|
+
declare function createCancelToken(): {
|
|
155
|
+
token: CancelToken;
|
|
156
|
+
cancel: (reason?: string) => void;
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Check whether an error is a cancellation (abort) error.
|
|
160
|
+
*/
|
|
161
|
+
declare function isCancel(error: unknown): boolean;
|
|
162
|
+
/**
|
|
163
|
+
* Create a new HttixAbortError, typically used when a request is
|
|
164
|
+
* cancelled programmatically.
|
|
165
|
+
*/
|
|
166
|
+
declare function createCancelError(reason?: string, config?: HttixRequestConfig): HttixAbortError;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* httix — Request deduplication
|
|
170
|
+
*/
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Deduplicates in-flight requests and optionally caches responses
|
|
174
|
+
* for a configurable TTL.
|
|
175
|
+
*
|
|
176
|
+
* - When `ttl` is 0 (default), only coalesces concurrent requests with
|
|
177
|
+
* the same key — once the request resolves, subsequent calls execute
|
|
178
|
+
* a new request.
|
|
179
|
+
* - When `ttl` > 0, resolved responses are cached for the specified
|
|
180
|
+
* duration and returned for matching keys without re-executing.
|
|
181
|
+
*/
|
|
182
|
+
declare class RequestDeduplicator {
|
|
183
|
+
private inflight;
|
|
184
|
+
private cache;
|
|
185
|
+
private ttl;
|
|
186
|
+
constructor(ttl?: number);
|
|
187
|
+
/**
|
|
188
|
+
* Deduplicate a request by key.
|
|
189
|
+
*
|
|
190
|
+
* If a cached response is available (and not expired), return it.
|
|
191
|
+
* If a request is already in-flight, return the same promise.
|
|
192
|
+
* Otherwise, execute `requestFn`, cache the result (if TTL > 0),
|
|
193
|
+
* and return it.
|
|
194
|
+
*/
|
|
195
|
+
dedup<T>(key: string, requestFn: () => Promise<T>): Promise<T>;
|
|
196
|
+
/**
|
|
197
|
+
* Generate a deduplication key from a request config.
|
|
198
|
+
*
|
|
199
|
+
* The key is composed of the HTTP method, origin, pathname, and
|
|
200
|
+
* sorted query parameters.
|
|
201
|
+
*/
|
|
202
|
+
generateKey(config: HttixRequestConfig): string;
|
|
203
|
+
/**
|
|
204
|
+
* Clear all in-flight requests and cached responses.
|
|
205
|
+
*/
|
|
206
|
+
clear(): void;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* httix — Rate limiting
|
|
211
|
+
*/
|
|
212
|
+
/**
|
|
213
|
+
* Token-bucket-style rate limiter that limits the number of concurrent
|
|
214
|
+
* requests within a sliding time window per key.
|
|
215
|
+
*
|
|
216
|
+
* When the maximum number of requests for a key is reached, additional
|
|
217
|
+
* requests are queued and drained when the next interval starts.
|
|
218
|
+
*/
|
|
219
|
+
declare class RateLimiter {
|
|
220
|
+
private maxRequests;
|
|
221
|
+
private interval;
|
|
222
|
+
private queues;
|
|
223
|
+
private activeCounts;
|
|
224
|
+
private timers;
|
|
225
|
+
constructor(maxRequests: number, interval: number);
|
|
226
|
+
/**
|
|
227
|
+
* Throttle a request function by key.
|
|
228
|
+
*
|
|
229
|
+
* If the number of active requests for the given key is below
|
|
230
|
+
* `maxRequests`, the request executes immediately. Otherwise, it is
|
|
231
|
+
* queued and will execute when the next interval starts.
|
|
232
|
+
*/
|
|
233
|
+
throttle(key: string, requestFn: () => Promise<unknown>): Promise<unknown>;
|
|
234
|
+
/**
|
|
235
|
+
* Drain queued requests for a given key, processing up to maxRequests
|
|
236
|
+
* and starting a new interval timer.
|
|
237
|
+
*/
|
|
238
|
+
private drainQueue;
|
|
239
|
+
/**
|
|
240
|
+
* Clear all queues, timers, and active counts.
|
|
241
|
+
*/
|
|
242
|
+
clear(): void;
|
|
243
|
+
/**
|
|
244
|
+
* Get the number of queued (waiting) requests for a given key.
|
|
245
|
+
*/
|
|
246
|
+
getQueueSize(key: string): number;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* httix — Koa-style middleware composition
|
|
251
|
+
*/
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Compose an array of middleware functions into a single function.
|
|
255
|
+
*
|
|
256
|
+
* The composition follows the Koa "onion" model: each middleware is called
|
|
257
|
+
* with a context and a `next` function that invokes the next middleware in
|
|
258
|
+
* the chain. Code before `await next()` runs on the way in; code after
|
|
259
|
+
* `await next()` runs on the way out (in reverse order).
|
|
260
|
+
*
|
|
261
|
+
* If no middlewares are provided, the composed function simply calls `next`.
|
|
262
|
+
*
|
|
263
|
+
* @throws {Error} If `next()` is called more than once in a single middleware.
|
|
264
|
+
*/
|
|
265
|
+
declare function composeMiddleware<T>(middlewares: MiddlewareFn<T>[]): (ctx: MiddlewareContext, next: () => Promise<void>) => Promise<void>;
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* httix — Streaming utilities (SSE, NDJSON, progress tracking)
|
|
269
|
+
*/
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Parse a ReadableStream of bytes as Server-Sent Events (SSE).
|
|
273
|
+
*
|
|
274
|
+
* Returns an async iterable that yields SSEEvent objects. Handles
|
|
275
|
+
* partial chunks, multi-line data fields, and the standard SSE fields
|
|
276
|
+
* (event, data, id, retry).
|
|
277
|
+
*
|
|
278
|
+
* The stream reader is released when the iterator is broken or returns.
|
|
279
|
+
*/
|
|
280
|
+
declare function parseSSE(stream: ReadableStream<Uint8Array>): AsyncIterable<SSEEvent>;
|
|
281
|
+
/**
|
|
282
|
+
* Parse a ReadableStream of bytes as Newline-Delimited JSON (NDJSON).
|
|
283
|
+
*
|
|
284
|
+
* Returns an async iterable that yields parsed JSON objects of type T.
|
|
285
|
+
* Empty lines are skipped. Partial lines are buffered until a newline
|
|
286
|
+
* arrives.
|
|
287
|
+
*
|
|
288
|
+
* The stream reader is released when the iterator is broken or returns.
|
|
289
|
+
*/
|
|
290
|
+
declare function parseNDJSON<T>(stream: ReadableStream<Uint8Array>): AsyncIterable<T>;
|
|
291
|
+
/**
|
|
292
|
+
* Create a progress-tracking wrapper around a ReadableStream.
|
|
293
|
+
*
|
|
294
|
+
* Returns a new ReadableStream that transparently passes through all
|
|
295
|
+
* chunks while invoking `onProgress` for each chunk received, reporting
|
|
296
|
+
* the number of bytes loaded so far.
|
|
297
|
+
*
|
|
298
|
+
* If `total` is provided (e.g., from Content-Length), `percent` will
|
|
299
|
+
* reflect completion percentage; otherwise it defaults to 0.
|
|
300
|
+
*/
|
|
301
|
+
declare function createProgressReader(body: ReadableStream<Uint8Array>, onProgress: (progress: DownloadProgress) => void, total?: number): ReadableStream<Uint8Array>;
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* httix — Retry logic
|
|
305
|
+
*/
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Parse a Retry-After header value.
|
|
309
|
+
*
|
|
310
|
+
* - If the value is a plain number, it represents seconds to wait.
|
|
311
|
+
* - If the value is an ISO date string, calculate the number of seconds
|
|
312
|
+
* from now until that date.
|
|
313
|
+
* - Returns null if the value cannot be parsed.
|
|
314
|
+
*/
|
|
315
|
+
declare function parseRetryAfter(value: string | null): number | null;
|
|
316
|
+
/**
|
|
317
|
+
* Execute a request function with retry support.
|
|
318
|
+
*
|
|
319
|
+
* Merges the provided retry configuration with defaults, evaluates retry
|
|
320
|
+
* conditions on each failure, applies backoff delay, and throws
|
|
321
|
+
* HttixRetryError if all attempts are exhausted.
|
|
322
|
+
*/
|
|
323
|
+
declare function retryRequest<T>(fn: () => Promise<HttixResponse<T>>, config: RetryConfig | false | undefined, requestConfig: HttixRequestConfig): Promise<HttixResponse<T>>;
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* httix — Timeout management via AbortController
|
|
327
|
+
*/
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Create an AbortController that will automatically abort after the
|
|
331
|
+
* specified timeout. The abort reason is set to a HttixTimeoutError.
|
|
332
|
+
*
|
|
333
|
+
* If timeout is 0 or negative, the controller will never abort.
|
|
334
|
+
*/
|
|
335
|
+
declare function createTimeoutController(timeout: number, config: HttixRequestConfig): AbortController;
|
|
336
|
+
/**
|
|
337
|
+
* Clear a pending timeout timer on an AbortController, preventing it from
|
|
338
|
+
* firing if the request completes before the timeout.
|
|
339
|
+
*/
|
|
340
|
+
declare function clearTimeoutController(controller: AbortController): void;
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* httix — Authentication utilities
|
|
344
|
+
*/
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Apply authentication headers or query parameters to a request config
|
|
348
|
+
* based on the provided auth configuration.
|
|
349
|
+
*
|
|
350
|
+
* Returns a new config object — the original is not mutated.
|
|
351
|
+
*/
|
|
352
|
+
declare function applyAuth(config: HttixRequestConfig, authConfig: AuthConfig): Promise<HttixRequestConfig>;
|
|
353
|
+
/**
|
|
354
|
+
* Create a request interceptor that applies authentication to every
|
|
355
|
+
* outgoing request.
|
|
356
|
+
*/
|
|
357
|
+
declare function createAuthInterceptor(authConfig: AuthConfig): RequestInterceptor;
|
|
358
|
+
/**
|
|
359
|
+
* Create a response error interceptor that handles 401 Unauthorized
|
|
360
|
+
* errors by refreshing the bearer token and retrying the original request.
|
|
361
|
+
*
|
|
362
|
+
* If no `refreshToken` is configured, returns a no-op interceptor.
|
|
363
|
+
* Concurrent 401 errors are deduplicated — only one token refresh
|
|
364
|
+
* happens at a time, and other callers wait for the same refresh.
|
|
365
|
+
*/
|
|
366
|
+
declare function createAuthRefreshHandler(authConfig: BearerAuthConfig): ResponseErrorInterceptor;
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* httix — Pagination utilities
|
|
370
|
+
*/
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Parse an HTTP Link header into a record of rel → URL mappings.
|
|
374
|
+
*
|
|
375
|
+
* Input format: `<url>; rel="next", <url>; rel="last"`
|
|
376
|
+
*
|
|
377
|
+
* Returns an object like `{ next: 'https://...', last: 'https://...' }`.
|
|
378
|
+
*/
|
|
379
|
+
declare function parseLinkHeader(linkHeader: string): Record<string, string>;
|
|
380
|
+
/**
|
|
381
|
+
* Create a paginator function that returns an async iterable of pages.
|
|
382
|
+
*
|
|
383
|
+
* Supports three pagination styles:
|
|
384
|
+
* - **offset**: Tracks offset and incrementing by pageSize each iteration.
|
|
385
|
+
* - **cursor**: Extracts a cursor from the response data and passes it
|
|
386
|
+
* as a query parameter for the next request.
|
|
387
|
+
* - **link**: Extracts the next URL from the Link response header.
|
|
388
|
+
*
|
|
389
|
+
* The iterable respects `maxPages`, `stopCondition`, and `dataExtractor`
|
|
390
|
+
* from the PaginationConfig.
|
|
391
|
+
*/
|
|
392
|
+
declare function createPaginator<T>(client: HttixClient): (url: string, config?: Partial<HttixRequestConfig> & {
|
|
393
|
+
pagination?: PaginationConfig<T>;
|
|
394
|
+
}) => AsyncIterable<T[]>;
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* httix — GET method factory
|
|
398
|
+
*
|
|
399
|
+
* Creates a standalone GET function bound to a client instance.
|
|
400
|
+
* Useful for composition patterns where methods are passed around
|
|
401
|
+
* independently of the client object.
|
|
402
|
+
*/
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Create a GET method bound to the given client.
|
|
406
|
+
*
|
|
407
|
+
* @example
|
|
408
|
+
* ```ts
|
|
409
|
+
* const get = createGetMethod(client);
|
|
410
|
+
* const { data } = await get<User[]>('/users');
|
|
411
|
+
* ```
|
|
412
|
+
*/
|
|
413
|
+
declare function createGetMethod(client: HttixClient): HttixClient['get'];
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* httix — POST method factory
|
|
417
|
+
*
|
|
418
|
+
* Creates a standalone POST function bound to a client instance.
|
|
419
|
+
*/
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Create a POST method bound to the given client.
|
|
423
|
+
*
|
|
424
|
+
* @example
|
|
425
|
+
* ```ts
|
|
426
|
+
* const post = createPostMethod(client);
|
|
427
|
+
* const { data } = await post<User>('/users', { name: 'Alice' });
|
|
428
|
+
* ```
|
|
429
|
+
*/
|
|
430
|
+
declare function createPostMethod(client: HttixClient): HttixClient['post'];
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* httix — PUT method factory
|
|
434
|
+
*
|
|
435
|
+
* Creates a standalone PUT function bound to a client instance.
|
|
436
|
+
*/
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Create a PUT method bound to the given client.
|
|
440
|
+
*
|
|
441
|
+
* @example
|
|
442
|
+
* ```ts
|
|
443
|
+
* const put = createPutMethod(client);
|
|
444
|
+
* const { data } = await put<User>('/users/1', { name: 'Bob' });
|
|
445
|
+
* ```
|
|
446
|
+
*/
|
|
447
|
+
declare function createPutMethod(client: HttixClient): HttixClient['put'];
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* httix — PATCH method factory
|
|
451
|
+
*
|
|
452
|
+
* Creates a standalone PATCH function bound to a client instance.
|
|
453
|
+
*/
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Create a PATCH method bound to the given client.
|
|
457
|
+
*
|
|
458
|
+
* @example
|
|
459
|
+
* ```ts
|
|
460
|
+
* const patch = createPatchMethod(client);
|
|
461
|
+
* const { data } = await patch<User>('/users/1', { name: 'Charlie' });
|
|
462
|
+
* ```
|
|
463
|
+
*/
|
|
464
|
+
declare function createPatchMethod(client: HttixClient): HttixClient['patch'];
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* httix — DELETE method factory
|
|
468
|
+
*
|
|
469
|
+
* Creates a standalone DELETE function bound to a client instance.
|
|
470
|
+
* Supports an optional body (some APIs accept request bodies with DELETE).
|
|
471
|
+
*/
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Create a DELETE method bound to the given client.
|
|
475
|
+
*
|
|
476
|
+
* @example
|
|
477
|
+
* ```ts
|
|
478
|
+
* const remove = createDeleteMethod(client);
|
|
479
|
+
* const { data } = await remove<void>('/users/1');
|
|
480
|
+
*
|
|
481
|
+
* // With body (API-specific)
|
|
482
|
+
* const { data } = await remove<void>('/batch', { ids: [1, 2, 3] });
|
|
483
|
+
* ```
|
|
484
|
+
*/
|
|
485
|
+
declare function createDeleteMethod(client: HttixClient): (url: string, body?: RequestBody, config?: Partial<HttixRequestConfig>) => Promise<HttixResponse<unknown>>;
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* httix — HEAD method factory
|
|
489
|
+
*
|
|
490
|
+
* Creates a standalone HEAD function bound to a client instance.
|
|
491
|
+
* HEAD requests must not have a body, so only headers and status are returned.
|
|
492
|
+
*/
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Create a HEAD method bound to the given client.
|
|
496
|
+
*
|
|
497
|
+
* The response type is `void` because HEAD responses have no body.
|
|
498
|
+
*
|
|
499
|
+
* @example
|
|
500
|
+
* ```ts
|
|
501
|
+
* const head = createHeadMethod(client);
|
|
502
|
+
* const { status, headers } = await head('/users/1');
|
|
503
|
+
* if (status === 200) {
|
|
504
|
+
* const contentLength = headers.get('content-length');
|
|
505
|
+
* }
|
|
506
|
+
* ```
|
|
507
|
+
*/
|
|
508
|
+
declare function createHeadMethod(client: HttixClient): HttixClient['head'];
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* httix — OPTIONS method factory
|
|
512
|
+
*
|
|
513
|
+
* Creates a standalone OPTIONS function bound to a client instance.
|
|
514
|
+
* OPTIONS requests are typically used for CORS preflight or discovering
|
|
515
|
+
* allowed methods on a resource.
|
|
516
|
+
*/
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Create an OPTIONS method bound to the given client.
|
|
520
|
+
*
|
|
521
|
+
* The response type is `void` because OPTIONS responses typically have no body.
|
|
522
|
+
*
|
|
523
|
+
* @example
|
|
524
|
+
* ```ts
|
|
525
|
+
* const options = createOptionsMethod(client);
|
|
526
|
+
* const { headers } = await options('/users/1');
|
|
527
|
+
* const allow = headers.get('allow'); // e.g. "GET, PUT, DELETE"
|
|
528
|
+
* ```
|
|
529
|
+
*/
|
|
530
|
+
declare function createOptionsMethod(client: HttixClient): HttixClient['options'];
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* httix — Generic request method factory
|
|
534
|
+
*
|
|
535
|
+
* Creates a standalone request function bound to a client instance.
|
|
536
|
+
* This is the lowest-level factory — the caller provides the full
|
|
537
|
+
* HttixRequestConfig including method, url, headers, body, etc.
|
|
538
|
+
*/
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Create a generic request method bound to the given client.
|
|
542
|
+
*
|
|
543
|
+
* @example
|
|
544
|
+
* ```ts
|
|
545
|
+
* const request = createRequestMethod(client);
|
|
546
|
+
* const { data } = await request<User>({ url: '/users/1', method: 'GET' });
|
|
547
|
+
* ```
|
|
548
|
+
*/
|
|
549
|
+
declare function createRequestMethod(client: HttixClient): HttixClient['request'];
|
|
550
|
+
|
|
551
|
+
declare const httix: {
|
|
552
|
+
/** Core request method */
|
|
553
|
+
request: <T = unknown>(config: HttixRequestConfig) => Promise<HttixResponse<T>>;
|
|
554
|
+
/** HTTP method shortcuts */
|
|
555
|
+
get: <T = unknown>(url: string, config?: Partial<HttixRequestConfig>) => Promise<HttixResponse<T>>;
|
|
556
|
+
post: <T = unknown>(url: string, body?: RequestBody, config?: Partial<HttixRequestConfig>) => Promise<HttixResponse<T>>;
|
|
557
|
+
put: <T = unknown>(url: string, body?: RequestBody, config?: Partial<HttixRequestConfig>) => Promise<HttixResponse<T>>;
|
|
558
|
+
patch: <T = unknown>(url: string, body?: RequestBody, config?: Partial<HttixRequestConfig>) => Promise<HttixResponse<T>>;
|
|
559
|
+
delete: <T = unknown>(url: string, config?: Partial<HttixRequestConfig>) => Promise<HttixResponse<T>>;
|
|
560
|
+
head: (url: string, config?: Partial<HttixRequestConfig>) => Promise<HttixResponse<void>>;
|
|
561
|
+
options: (url: string, config?: Partial<HttixRequestConfig>) => Promise<HttixResponse<void>>;
|
|
562
|
+
/** Interceptor managers */
|
|
563
|
+
interceptors: {
|
|
564
|
+
request: InterceptorManager<RequestInterceptor, RequestErrorInterceptor>;
|
|
565
|
+
response: InterceptorManager<ResponseInterceptor<unknown>, ResponseErrorInterceptor>;
|
|
566
|
+
};
|
|
567
|
+
/** Stream utilities */
|
|
568
|
+
stream: {
|
|
569
|
+
sse: (url: string, config?: Partial<HttixRequestConfig>) => AsyncIterable<SSEEvent>;
|
|
570
|
+
ndjson: <T = unknown>(url: string, config?: Partial<HttixRequestConfig>) => AsyncIterable<T>;
|
|
571
|
+
};
|
|
572
|
+
/** Pagination helper */
|
|
573
|
+
paginate: <T = unknown>(url: string, config?: Partial<HttixRequestConfig> & {
|
|
574
|
+
pagination?: PaginationConfig<T>;
|
|
575
|
+
}) => AsyncIterable<T[]>;
|
|
576
|
+
/** Client defaults */
|
|
577
|
+
defaults: HttixConfig;
|
|
578
|
+
/** Register middleware */
|
|
579
|
+
use: <T = unknown>(middleware: MiddlewareFn<T>) => void;
|
|
580
|
+
/** Create a new client with merged configuration */
|
|
581
|
+
create: (config?: Partial<HttixConfig>) => HttixClientImpl;
|
|
582
|
+
/** Cancel all in-flight requests */
|
|
583
|
+
cancelAll: (reason?: string) => void;
|
|
584
|
+
/** Check whether an error is a cancellation error */
|
|
585
|
+
isCancel: typeof isCancel;
|
|
586
|
+
/**
|
|
587
|
+
* Alias for {@link createHttix} — create a new client instance.
|
|
588
|
+
*/
|
|
589
|
+
createHttix: (config?: Partial<HttixConfig>) => HttixClientImpl;
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
export { AuthConfig, BearerAuthConfig, DEFAULT_CONFIG, DEFAULT_HEADERS, DEFAULT_REQUEST_CONFIG, DEFAULT_RETRY, DEFAULT_TIMEOUT, DownloadProgress, HttixAbortError, HttixClientImpl, HttixClient as HttixClientInterface, HttixConfig, HttixRequestConfig, HttixResponse, InterceptorHandler, InterceptorManager, InterceptorManager$1 as InterceptorManagerInterface, MiddlewareContext, MiddlewareFn, PaginationConfig, RateLimiter, RequestBody, RequestDeduplicator, RequestErrorInterceptor, RequestInterceptor, ResponseErrorInterceptor, ResponseInterceptor, RetryConfig, SSEEvent, applyAuth, clearTimeoutController, composeMiddleware, createAuthInterceptor, createAuthRefreshHandler, createCancelError, createCancelToken, createDeleteMethod, createGetMethod, createHeadMethod, createHttix, createOptionsMethod, createPaginator, createPatchMethod, createPostMethod, createProgressReader, createPutMethod, createRequestMethod, createTimeoutController, httix as default, isCancel, parseLinkHeader, parseNDJSON, parseRetryAfter, parseSSE, retryRequest };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
var d={attempts:3,backoff:"exponential",baseDelay:1e3,maxDelay:3e4,jitter:!0,retryOn:[408,429,500,502,503,504],retryOnNetworkError:!0,retryOnSafeMethodsOnly:!1,retryCondition:()=>!0,onRetry:()=>{}},j=3e4,v={Accept:"application/json, text/plain, */*","Accept-Encoding":"gzip, deflate, br","Accept-Language":"*"},me={method:"GET",timeout:j,throwOnError:!0,credentials:"same-origin",mode:"cors",redirect:"follow",cache:"default"},U={url:"",baseURL:"",headers:v,timeout:j,throwOnError:!0,credentials:"same-origin",mode:"cors",redirect:"follow",cache:"default",retry:d,dedup:!1};var R=class extends Error{name="HttixError";config;cause;constructor(e,t){super(e),this.name="HttixError",this.config=t?.config,this.cause=t?.cause,Object.setPrototypeOf(this,new.target.prototype);let n=Error;n.captureStackTrace&&n.captureStackTrace(this,this.constructor)}},h=class extends R{name="HttixRequestError";constructor(e,t){super(e,t),this.name="HttixRequestError"}},f=class extends R{name="HttixResponseError";status;statusText;data;headers;config;constructor(e,t,n,o,s){let i=`Request failed with status ${e}: ${t}`;super(i,{message:i,config:s}),this.name="HttixResponseError",this.status=e,this.statusText=t,this.data=n,this.headers=o,this.config=s}},T=class extends R{name="HttixTimeoutError";timeout;constructor(e,t){let n=`Request timed out after ${e}ms`;super(n,{message:n,config:t}),this.name="HttixTimeoutError",this.timeout=e}},p=class extends R{name="HttixAbortError";reason;constructor(e,t){let n=e??"Request was aborted";super(n,{message:n,config:t}),this.name="HttixAbortError",this.reason=n}},N=class extends R{name="HttixRetryError";attempts;lastError;constructor(e,t,n){let o=`Request failed after ${e} attempt${e===1?"":"s"}`;super(o,{message:o,config:n,cause:t}),this.name="HttixRetryError",this.attempts=e,this.lastError=t}};function O(r){let e=xe(r.baseURL,r.url,r.params,r.query),t=ye(v,r.headers),n=Re(r.body,t),s={method:r.method??"GET",headers:t,body:n,credentials:r.credentials,mode:r.mode,cache:r.cache,redirect:r.redirect,referrerPolicy:r.referrerPolicy},i=r.timeout??0,a,u;return i>0?(a=new AbortController,u=setTimeout(()=>{a.abort(new DOMException(`Request timed out after ${i}ms`,"TimeoutError"))},i),r.signal?s.signal=he(r.signal,a.signal):s.signal=a.signal):r.signal&&(s.signal=r.signal),{request:new globalThis.Request(e,s),timeoutController:a,timeoutId:u}}function q(r,e){e!==void 0&&clearTimeout(e),r&&r.abort()}function he(...r){let e=new AbortController;for(let t of r){if(t.aborted){e.abort(t.reason);break}t.addEventListener("abort",()=>e.abort(t.reason),{once:!0})}return e.signal}function xe(r,e,t,n){let o=e;if(r){let s=r.endsWith("/")?r.slice(0,-1):r,i=o.startsWith("/")?o:`/${o}`;o=`${s}${i}`}if(t)for(let[s,i]of Object.entries(t))o=o.replace(`:${s}`,encodeURIComponent(String(i)));if(n&&Object.keys(n).length>0){let s=o.includes("?")?"&":"?";o+=`${s}${ge(n)}`}return o}function ge(r){let e=[];for(let[t,n]of Object.entries(r))if(n!=null)if(Array.isArray(n))for(let o of n)o!=null&&e.push(`${encodeURIComponent(t)}=${encodeURIComponent(String(o))}`);else e.push(`${encodeURIComponent(t)}=${encodeURIComponent(String(n))}`);return e.join("&")}function ye(r,e){let t=new Headers;return te(t,r),e&&te(t,e),t}function te(r,e){if(e instanceof Headers)e.forEach((t,n)=>{r.set(n,t)});else for(let[t,n]of Object.entries(e))n!==void 0&&r.set(t,n)}function Re(r,e){if(r!=null){if(typeof r=="string"||r instanceof FormData||r instanceof URLSearchParams||r instanceof Blob||r instanceof ArrayBuffer||r instanceof ReadableStream)return r;if(typeof r=="object"||typeof r=="number"||typeof r=="boolean")return e.has("Content-Type")||e.set("Content-Type","application/json"),JSON.stringify(r)}}function re(r,e,t,n){return{data:t,status:r.status,statusText:r.statusText,headers:r.headers,ok:r.ok,raw:r,timing:n,config:e}}async function ne(r,e){if(typeof e.parseResponse=="function")return await e.parseResponse(r);if(r.status===204)return;let t=r.headers.get("Content-Type")??"";return e.responseType?He(r,e.responseType):Ce(r,t)}async function He(r,e){switch(e){case"json":try{return await r.json()}catch{return}case"text":try{return await r.text()}catch{return}case"blob":try{return await r.blob()}catch{return}case"arrayBuffer":try{return await r.arrayBuffer()}catch{return}}}async function Ce(r,e){let t=e.toLowerCase();if(t.includes("application/json"))try{return await r.json()}catch{return}if(t.includes("text/"))try{return await r.text()}catch{return}if(r.body)try{let n=await r.text();if(!n)return;try{return JSON.parse(n)}catch{return n}}catch{return}}var E=class{handlers=[];use(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1}eject(e){e>=0&&e<this.handlers.length&&(this.handlers[e]=null)}clear(){this.handlers=[]}};async function M(r,e){let t=r;for(let n of e.handlers)if(n!==null)try{t=await n.fulfilled(t)}catch(o){if(n.rejected)try{let s=await n.rejected(o);s!==void 0&&(t=s)}catch{throw o}else throw o}return t}async function oe(r,e){let t=r;for(let n of e.handlers)if(n!==null)try{t=await n.fulfilled(t)}catch(o){if(n.rejected)try{let s=await n.rejected(o);s!==void 0&&(t=s)}catch{throw o}else throw o}return t}async function se(r,e){for(let t of e.handlers)if(t!==null&&t.rejected)try{let n=await t.rejected(r);if(n!=null)return n}catch{continue}throw r}function L(r){return new Promise(e=>setTimeout(e,r))}function ie(){return`req_${Date.now()}_${Math.random().toString(36).slice(2,9)}`}function $(r,e,t,n,o){let s;switch(e){case"fixed":s=t;break;case"linear":s=t*r;break;case"exponential":s=t*Math.pow(2,r-1);break;default:s=t;break}return s=Math.min(s,n),o&&(s=s*(.5+Math.random()*.5)),Math.max(0,s)}var Te=new Set(["GET","HEAD","OPTIONS"]);function B(r){if(r===null)return null;let e=Number(r);if(!Number.isNaN(e)&&e>0&&String(e)===r.trim())return e*1e3;let t=Date.parse(r);if(!Number.isNaN(t)){let n=t-Date.now();return n>0?n:0}return null}async function F(r,e,t){if(e===!1||e===void 0)return r();let n={attempts:e.attempts??d.attempts,backoff:e.backoff??d.backoff,baseDelay:e.baseDelay??d.baseDelay,maxDelay:e.maxDelay??d.maxDelay,jitter:e.jitter??d.jitter,retryOn:e.retryOn??d.retryOn,retryOnNetworkError:e.retryOnNetworkError??d.retryOnNetworkError,retryOnSafeMethodsOnly:e.retryOnSafeMethodsOnly??d.retryOnSafeMethodsOnly,retryCondition:e.retryCondition??d.retryCondition,onRetry:e.onRetry??d.onRetry},o=n.attempts,s=(t.method??"GET").toUpperCase(),i;for(let a=0;a<o;a++)try{let u=await r();if(u.status>=200&&u.status<300||!n.retryOn.includes(u.status))return u;let c=new f(u.status,u.statusText,u.data,u.headers,t);if(i=c,!ae(c,a,o,n,s))throw c;let m=B(u.headers.get("retry-after")),x=$(a+1,n.backoff,n.baseDelay,n.maxDelay,n.jitter),g=m!==null?m:x;n.onRetry(a+1,c,g),await L(g)}catch(u){if(i=u,!ae(u,a,o,n,s))throw u;let c=u,m=c instanceof f?B(c.headers?.get("retry-after")??null):null,x=$(a+1,n.backoff,n.baseDelay,n.maxDelay,n.jitter),g=m!==null?m:x;n.onRetry(a+1,c,g),await L(g)}throw i||new Error("All retry attempts exhausted")}function ae(r,e,t,n,o){return e+1>=t||n.retryOnSafeMethodsOnly&&!Te.has(o)||!n.retryCondition(r)?!1:r instanceof h?n.retryOnNetworkError:r instanceof f?n.retryOn.includes(r.status):!1}var k=class{inflight=new Map;cache=new Map;ttl;constructor(e=0){this.ttl=e}async dedup(e,t){if(this.ttl>0){let s=this.cache.get(e);if(s&&Date.now()-s.timestamp<this.ttl)return s.data}let n=this.inflight.get(e);if(n)return n;let o=t().then(s=>(this.ttl>0&&this.cache.set(e,{data:s,timestamp:Date.now()}),this.inflight.delete(e),s)).catch(s=>{throw this.inflight.delete(e),s});return this.inflight.set(e,o),o}generateKey(e){let t=new URL(e.url,e.baseURL),n=e.query?Object.keys(e.query).sort().map(o=>`${o}=${String(e.query[o])}`).join("&"):"";return`${e.method||"GET"}:${t.origin}${t.pathname}${n?"?"+n:""}`}clear(){this.inflight.clear(),this.cache.clear()}};var A=class{constructor(e,t){this.maxRequests=e;this.interval=t}maxRequests;interval;queues=new Map;activeCounts=new Map;timers=new Map;async throttle(e,t){this.timers.has(e)||this.activeCounts.set(e,0);let n=this.activeCounts.get(e)||0;return n<this.maxRequests?(this.activeCounts.set(e,n+1),n===0&&this.timers.set(e,setTimeout(()=>{this.activeCounts.set(e,0),this.timers.delete(e),this.drainQueue(e)},this.interval)),t()):new Promise(o=>{this.queues.has(e)||this.queues.set(e,[]),this.queues.get(e).push({execute:async()=>{let s=await t();o(s)},resolve:()=>{}})})}drainQueue(e){let t=this.queues.get(e);if(!t||t.length===0)return;let n=t.splice(0,this.maxRequests);this.activeCounts.set(e,n.length),this.timers.set(e,setTimeout(()=>{this.activeCounts.set(e,0),this.timers.delete(e),this.drainQueue(e)},this.interval));for(let o of n)o.execute()}clear(){for(let e of this.timers.values())clearTimeout(e);this.queues.clear(),this.activeCounts.clear(),this.timers.clear()}getQueueSize(e){return this.queues.get(e)?.length||0}};function Q(r){return function(t,n){let o=-1;async function s(i){if(i<=o)throw new Error("next() called multiple times");o=i;let a=r[i];if(!a)return n();await a(t,()=>s(i+1))}return s(0)}}async function ue(r){return typeof r=="function"?r():r}function qe(r){return{...r}}async function P(r,e){let t=qe(r),n={};t.headers&&(t.headers instanceof Headers?t.headers.forEach((s,i)=>{n[i]=s}):Object.assign(n,t.headers)),t.headers=n;let o={...t.query};switch(e.type){case"bearer":{let s=await ue(e.token);n.Authorization=`Bearer ${s}`;break}case"basic":{let s=`${e.username}:${e.password}`,i=btoa(s);n.Authorization=`Basic ${i}`;break}case"apiKey":{let s=await ue(e.value);e.in==="header"?n[e.key]=s:o[e.key]=s;break}}return(Object.keys(o).length>Object.keys(t.query??{}).length||e.type==="apiKey"&&e.in==="query")&&(t.query=o),t}function _(r){return async e=>P(e,r)}function G(r){if(!r.refreshToken)return t=>{};let e=null;return(async t=>{if(!(t instanceof f)||t.status!==401)return;let n=t.config;if(!n)return;e||(e=r.refreshToken().then(a=>(r.onTokenRefresh&&r.onTokenRefresh(a),typeof r.token=="string"&&(r.token=a),e=null,a)).catch(a=>{throw e=null,a}));let o;try{o=await e}catch{throw t}let s=await P(n,r),i={};s.headers&&Object.assign(i,s.headers),i.Authorization=`Bearer ${o}`,s.headers=i})}function ce(r){let e={};if(!r)return e;let t=r.split(",");for(let n of t){let o=n.trim(),s=o.match(/<([^>]+)>/);if(!s)continue;let i=s[1];if(i===void 0)continue;let a=o.match(/rel\s*=\s*"([^"]+)"/);if(!a)continue;let u=a[1];e[u]=i}return e}function z(r){return async function*(t,n){let o=n?.pagination;if(!o)return;let{style:s,pageSize:i=20,maxPages:a=1/0,offsetParam:u="offset",limitParam:c="limit",cursorParam:m="cursor",cursorExtractor:x,linkExtractor:g,dataExtractor:Y,stopCondition:X}=o,b=t,Z=0,S,D=0;for(;b&&D<a;){let w={...n};s==="offset"?w.query={...w.query,[u]:Z,[c]:i}:s==="cursor"&&(w.query={...w.query,[m]:S});let y;s==="link"&&D>0?y=await r.request({url:b,...w}):y=await r.request({url:b,...w}),D++;let C;if(Y?C=Y(y.data):C=y.data??[],X&&X(y.data)){C.length>0&&(yield C);return}if(C.length===0)return;switch(yield C,s){case"offset":{if(C.length<i)return;Z+=i;break}case"cursor":{if(x?S=x(y.data):S=null,!S)return;break}case"link":{if(g)b=g(y.headers)??null;else{let ee=y.headers.get("link");ee?b=ce(ee).next??null:b=null}break}}}}}async function*J(r){let e=r.getReader(),t=new TextDecoder,n="";try{for(;;){let{done:o,value:s}=await e.read();if(o)break;n+=t.decode(s,{stream:!0});let i=n.split(`
|
|
2
|
+
|
|
3
|
+
`);n=i.pop();for(let a of i){let u=le(a);u!==null&&(yield u)}}if(n.trim().length>0){let o=le(n);o!==null&&(yield o)}}finally{e.releaseLock()}}function le(r){let e=r.split(`
|
|
4
|
+
`),t={},n=[];for(let o of e){if(o===""||o.startsWith(":"))continue;let s=o.indexOf(":");if(s===-1){o.trim()==="data"&&n.push("");continue}let i=o.slice(0,s).trim(),a=o.slice(s+1);switch(a.startsWith(" ")&&(a=a.slice(1)),i){case"event":t.type=a;break;case"data":n.push(a);break;case"id":t.id=a;break;case"retry":t.retry=parseInt(a,10);break}}return n.length===0?null:{type:t.type??"message",data:n.join(`
|
|
5
|
+
`),id:t.id,retry:t.retry}}async function*V(r){let e=r.getReader(),t=new TextDecoder,n="";try{for(;;){let{done:s,value:i}=await e.read();if(s)break;n+=t.decode(i,{stream:!0});let a=n.split(`
|
|
6
|
+
`);n=a.pop();for(let u of a){let c=u.trim();c.length!==0&&(yield JSON.parse(c))}}let o=n.trim();o.length>0&&(yield JSON.parse(o))}finally{e.releaseLock()}}function be(r,e,t){let n=0;return new ReadableStream({async start(o){let s=r.getReader();try{for(;;){let{done:i,value:a}=await s.read();if(i){o.close();return}n+=a.byteLength;let u=t!==void 0&&t>0?Math.round(n/t*100):0;e({loaded:n,total:t!==void 0?t:void 0,percent:u}),o.enqueue(a)}}catch(i){o.error(i)}finally{s.releaseLock()}}})}function de(r,e){let t=new Headers;return r&&fe(t,r),e&&fe(t,e),t}function fe(r,e){if(e instanceof Headers)e.forEach((t,n)=>{r.set(n,t)});else for(let[t,n]of Object.entries(e))r.set(t,n)}function I(r,e){let t={...r};for(let n of Object.keys(e)){let o=e[n],s=r[n];if(n==="headers"){t.headers=de(s,o);continue}if(n==="query"){t.query=we(s,o);continue}if(o!==void 0){if(Array.isArray(o)){t[n]=o;continue}if(o!==null&&s!==null&&typeof o=="object"&&typeof s=="object"&&!Array.isArray(o)&&!Array.isArray(s)){t[n]=pe(s,o);continue}t[n]=o}}return t}function we(r,e){return!r&&!e?{}:r?e?{...r,...e}:{...r}:{...e}}function pe(r,e){let t={...r};for(let n of Object.keys(e)){let o=e[n],s=r[n];if(o!==void 0){if(Array.isArray(o)){t[n]=o;continue}if(o!==null&&s!==null&&typeof o=="object"&&typeof s=="object"&&!Array.isArray(o)&&!Array.isArray(s)){t[n]=pe(s,o);continue}t[n]=o}}return t}function Ee(...r){let e=new AbortController;for(let t of r){if(t.aborted){e.abort(t.reason);break}t.addEventListener("abort",()=>e.abort(t.reason),{once:!0})}return e.signal}var H=class r{defaults;interceptors;stream;paginate;middlewares;deduplicator;dedupConfig;rateLimiter;authConfig;pendingControllers;constructor(e){if(this.defaults=I(U,e??{}),this.interceptors={request:new E,response:new E},this.middlewares=this.defaults.middleware?[...this.defaults.middleware]:[],this.dedupConfig=this.defaults.dedup,this.dedupConfig===!0||typeof this.dedupConfig=="object"&&this.dedupConfig.enabled){let t=typeof this.dedupConfig=="object"&&this.dedupConfig.ttl!==void 0?this.dedupConfig.ttl:0;this.deduplicator=new k(t)}this.defaults.rateLimit&&(this.rateLimiter=new A(this.defaults.rateLimit.maxRequests,this.defaults.rateLimit.interval)),this.authConfig=this.defaults.auth,this.authConfig&&(this.interceptors.request.use(_(this.authConfig)),this.authConfig.type==="bearer"&&this.authConfig.refreshToken&&this.interceptors.response.use((t=>t),G(this.authConfig))),this.pendingControllers=new Set,this.stream={sse:this.executeSSE.bind(this),ndjson:this.executeNDJSON.bind(this)},this.paginate=z(this)}async request(e){let t=I(this.defaults,e);t.requestId=e.requestId??ie();let n=new AbortController;this.pendingControllers.add(n);let o=[n.signal];t.signal&&o.unshift(t.signal);let s=Ee(...o);t.signal=s;try{let i={request:t},a;return await Q(this.middlewares)(i,async()=>{let c=await M(i.request,this.interceptors.request);i.request=c,a=await this.executeWithDedupAndRateLimit(c),i.response=a}),a}finally{this.pendingControllers.delete(n)}}async get(e,t){return this.request({...t,url:e,method:"GET"})}async post(e,t,n){return this.request({...n,url:e,method:"POST",body:t})}async put(e,t,n){return this.request({...n,url:e,method:"PUT",body:t})}async patch(e,t,n){return this.request({...n,url:e,method:"PATCH",body:t})}async delete(e,t){return this.request({...t,url:e,method:"DELETE"})}async head(e,t){return this.request({...t,url:e,method:"HEAD"})}async options(e,t){return this.request({...t,url:e,method:"OPTIONS"})}use(e){this.middlewares.push(e)}create(e){let t=I(this.defaults,e??{});return new r(t)}cancelAll(e="All requests cancelled"){for(let t of this.pendingControllers)t.abort(new p(e));this.pendingControllers.clear()}isCancel(e){return e instanceof p}async executeWithDedupAndRateLimit(e){let t=()=>this.doFetch(e),n=this.rateLimiter?()=>this.rateLimiter.throttle(e.url,t):t;if(this.deduplicator&&this.isDedupEnabled()){let o=this.generateDedupKey(e);return this.deduplicator.dedup(o,n)}return n()}isDedupEnabled(){return this.dedupConfig===!1||this.dedupConfig===void 0?!1:this.dedupConfig===!0?!0:this.dedupConfig.enabled}generateDedupKey(e){return typeof this.dedupConfig=="object"&&this.dedupConfig.generateKey?this.dedupConfig.generateKey(e):this.deduplicator.generateKey(e)}async doFetch(e){return F(async()=>{let{request:t,timeoutController:n,timeoutId:o}=O(e),s=Date.now(),i;try{i=await fetch(t)}catch(c){let m=e.signal?.aborted===!0,x=n?.signal?.aborted===!0&&!m;throw q(n,o),m?new p("Request was aborted",e):x?new T(e.timeout??0,e):new h(c instanceof Error?c.message:"Network request failed",{message:"Network request failed",config:e,cause:c instanceof Error?c:void 0})}q(n,o);let a=await ne(i,e),u=Date.now()-s;return re(i,e,a,u)},e.retry,e).then(t=>this.processResponse(t,e))}async processResponse(e,t){if(e.ok)return oe(e,this.interceptors.response);if(t.throwOnError===!1)return e;let n=new f(e.status,e.statusText,e.data,e.headers,t);try{return await se(n,this.interceptors.response)}catch{throw n}}async*executeSSE(e,t){let n=I(this.defaults,{...t,url:e,method:"GET"}),o=await M(n,this.interceptors.request);this.authConfig&&(o=await P(o,this.authConfig));let{request:s,timeoutController:i,timeoutId:a}=O(o);o.timeout===this.defaults.timeout&&o.timeout;try{let u=await fetch(s);if(q(i,a),!u.ok)throw new f(u.status,u.statusText,null,u.headers,o);if(!u.body)throw new h("Response body is null \u2014 cannot parse SSE stream",{message:"Response body is null",config:o});yield*J(u.body)}catch(u){throw q(i,a),u}}async*executeNDJSON(e,t){let n=I(this.defaults,{...t,url:e,method:"GET"}),o=await M(n,this.interceptors.request);this.authConfig&&(o=await P(o,this.authConfig));let{request:s,timeoutController:i,timeoutId:a}=O(o);try{let u=await fetch(s);if(q(i,a),!u.ok)throw new f(u.status,u.statusText,null,u.headers,o);if(!u.body)throw new h("Response body is null \u2014 cannot parse NDJSON stream",{message:"Response body is null",config:o});yield*V(u.body)}catch(u){throw q(i,a),u}}};function Pe(r){return new H(r)}function Ie(){let r=new AbortController,e,t=new Promise((s,i)=>{e=i});return{token:{signal:r.signal,promise:t},cancel:s=>{let i=new p(s);r.abort(i),e(i)}}}function K(r){return r instanceof p}function ke(r,e){return new p(r,e)}var W=new WeakMap;function Ae(r,e){let t=new AbortController;if(r<=0)return t;let n=setTimeout(()=>{let o=new T(r,e);t.abort(o)},r);return W.set(t,n),t}function Se(r){let e=W.get(r);e!==void 0&&(clearTimeout(e),W.delete(r))}function ve(r){return async function(t,n){return r.request({...n,url:t,method:"GET"})}}function Oe(r){return async function(t,n,o){return r.request({...o,url:t,method:"POST",body:n})}}function Me(r){return async function(t,n,o){return r.request({...o,url:t,method:"PUT",body:n})}}function De(r){return async function(t,n,o){return r.request({...o,url:t,method:"PATCH",body:n})}}function je(r){return async function(t,n,o){return r.request({...o,url:t,method:"DELETE",...n!==void 0?{body:n}:{}})}}function Ue(r){return async function(t,n){return r.request({...n,url:t,method:"HEAD"})}}function Ne(r){return async function(t,n){return r.request({...n,url:t,method:"OPTIONS"})}}function Le(r){return async function(t){return r.request(t)}}var l=new H,$e={request:l.request.bind(l),get:l.get.bind(l),post:l.post.bind(l),put:l.put.bind(l),patch:l.patch.bind(l),delete:l.delete.bind(l),head:l.head.bind(l),options:l.options.bind(l),interceptors:l.interceptors,stream:l.stream,paginate:l.paginate,defaults:l.defaults,use:l.use.bind(l),create:r=>new H(r),cancelAll:l.cancelAll.bind(l),isCancel:K,createHttix:r=>new H(r)},zt=$e;export{U as DEFAULT_CONFIG,v as DEFAULT_HEADERS,me as DEFAULT_REQUEST_CONFIG,d as DEFAULT_RETRY,j as DEFAULT_TIMEOUT,p as HttixAbortError,H as HttixClientImpl,R as HttixError,h as HttixRequestError,f as HttixResponseError,N as HttixRetryError,T as HttixTimeoutError,E as InterceptorManager,A as RateLimiter,k as RequestDeduplicator,P as applyAuth,Se as clearTimeoutController,Q as composeMiddleware,_ as createAuthInterceptor,G as createAuthRefreshHandler,ke as createCancelError,Ie as createCancelToken,je as createDeleteMethod,ve as createGetMethod,Ue as createHeadMethod,Pe as createHttix,Ne as createOptionsMethod,z as createPaginator,De as createPatchMethod,Oe as createPostMethod,be as createProgressReader,Me as createPutMethod,Le as createRequestMethod,Ae as createTimeoutController,zt as default,K as isCancel,ce as parseLinkHeader,V as parseNDJSON,B as parseRetryAfter,J as parseSSE,F as retryRequest};
|
|
7
|
+
//# sourceMappingURL=index.js.map
|