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.cjs';
|
|
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.cjs';
|
|
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,2 @@
|
|
|
1
|
+
"use strict";var p=Object.defineProperty;var w=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var v=Object.prototype.hasOwnProperty;var P=(n,e)=>{for(var r in e)p(n,r,{get:e[r],enumerable:!0})},E=(n,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of k(e))!v.call(n,o)&&o!==r&&p(n,o,{get:()=>e[o],enumerable:!(s=w(e,o))||s.enumerable});return n};var C=n=>E(p({},"__esModule",{value:!0}),n);var L={};P(L,{LRUCache:()=>g,MockAdapter:()=>f,cachePlugin:()=>x,loggerPlugin:()=>R,mockPlugin:()=>b});module.exports=C(L);var y={debug:0,info:1,warn:2,error:3,none:4};function R(n){let e=n?.level??"info",r=n?.logger??{debug:console.debug,info:console.info,warn:console.warn,error:console.error},s=n?.logRequestBody??!1,o=n?.logResponseBody??!1,d=n?.logRequestHeaders??!1,h=n?.logResponseHeaders??!1;function a(c){return y[c]>=y[e]}return{name:"logger",install(c){c.interceptors.request.use(t=>{if(a("info")){let i={method:t.method,url:t.url,requestId:t.requestId};if(d&&t.headers){let u=t.headers instanceof Headers?Object.fromEntries(t.headers.entries()):t.headers;i.headers=u}s&&t.body&&(i.body=t.body),r.info("[httix] Request:",i)}return t}),c.interceptors.response.use(t=>{if(a("info")){let i={status:t.status,statusText:t.statusText,timing:t.timing,requestId:t.config.requestId};h&&(i.headers=Object.fromEntries(t.headers.entries())),o&&(i.data=t.data),r.info("[httix] Response:",i)}return t},t=>{a("error")&&r.error("[httix] Error:",{message:t.message,name:t.name,requestId:t.config?.requestId})})},cleanup(){}}}var g=class{cache=new Map;maxSize;ttl;constructor(e=100,r=3e5){this.maxSize=e,this.ttl=r}get(e){let r=this.cache.get(e);if(r){if(Date.now()-r.timestamp>this.ttl){this.cache.delete(e);return}return this.cache.delete(e),this.cache.set(e,r),r}}getAllowingStale(e){let r=this.cache.get(e);if(r)return this.cache.delete(e),this.cache.set(e,r),r}set(e,r){if(this.cache.delete(e),this.cache.size>=this.maxSize){let s=this.cache.keys().next().value;s!==void 0&&this.cache.delete(s)}this.cache.set(e,r)}has(e){return this.get(e)!==void 0}delete(e){return this.cache.delete(e)}clear(){this.cache.clear()}get size(){return this.cache.size}keys(){return Array.from(this.cache.keys())}};function T(n){let e=new URL(n.url,n.baseURL||"http://localhost"),r=n.query?"?"+new URLSearchParams(Object.entries(n.query).filter(([,s])=>s!=null).map(([s,o])=>[s,String(o)])).toString():"";return`${n.method||"GET"} ${e.origin}${e.pathname}${r}`}function x(n){let e=n?.maxSize??100,r=n?.ttl??3e5,s=n?.staleWhileRevalidate??!1,o=n?.swrWindow??6e4,d=n?.methods??["GET"],h=n?.generateKey??T,a=new g(e,r);return{name:"cache",install(c){c.interceptors.request.use(t=>{if(!d.includes(t.method||"GET"))return t;let i=h(t),u=s?a.getAllowingStale(i):a.get(i);if(u){let l=Date.now()-u.timestamp<=r,H=s&&Date.now()-u.timestamp<=r+o;if(l||H){let m=t;m._cacheEntry=u,m._cacheKey=i,m._cacheHit=!0}}return t}),c.interceptors.response.use(t=>{let i=t.config;if(i._cacheHit&&i._cacheEntry){let l=i._cacheEntry;return{data:l.data,status:l.status,statusText:l.statusText,headers:l.headers,ok:l.status>=200&&l.status<300,raw:t.raw,timing:t.timing,config:t.config}}let u=t.config.method||"GET";if(d.includes(u)&&t.ok){let l=h(t.config);a.set(l,{data:t.data,status:t.status,statusText:t.statusText,headers:t.headers,timing:t.timing,timestamp:Date.now(),config:t.config,raw:t.raw})}return t},()=>{})},cache:a,invalidate(c){c&&a.delete(c)},invalidatePattern(c){for(let t of a.keys())c.test(t)&&a.delete(t)},clear(){a.clear()},getStats(){return{size:a.size,maxSize:e,ttl:r}}}}var f=class{handlers=[];history={get:[],post:[],put:[],patch:[],delete:[],head:[],options:[]};originalFetch=null;isActive=!1;activate(e){this.isActive||(this.isActive=!0,this.originalFetch=globalThis.fetch,globalThis.fetch=this.mockFetch.bind(this))}deactivate(){this.isActive&&(this.isActive=!1,this.originalFetch&&(globalThis.fetch=this.originalFetch,this.originalFetch=null))}mockFetch(e,r){let s=typeof e=="string"?e:e instanceof URL?e.href:e.url,o=e instanceof Request,d=(r?.method??(o?e.method:"GET")).toUpperCase(),h={method:d,url:s,body:r?.body?JSON.parse(String(r.body)):void 0,headers:r?.headers?r.headers instanceof Headers?Object.fromEntries(r.headers.entries()):r.headers:o?Object.fromEntries(e.headers.entries()):void 0,config:{url:s,method:d}},a=d.toLowerCase(),c=this.history[a];c&&c.push(h);for(let t of this.handlers){if(t.method!==d)continue;if(typeof t.urlPattern=="string"?s===t.urlPattern||s.endsWith(t.urlPattern):t.urlPattern.test(s)){let u=t.handler(h.config);return Promise.resolve(new Response(JSON.stringify(u.data),{status:u.status,headers:{"Content-Type":"application/json",...u.headers}}))}}return Promise.resolve(new Response(JSON.stringify({error:"No mock handler found"}),{status:404,headers:{"Content-Type":"application/json"}}))}onGet(e){return this.on("GET",e)}onPost(e){return this.on("POST",e)}onPut(e){return this.on("PUT",e)}onPatch(e){return this.on("PATCH",e)}onDelete(e){return this.on("DELETE",e)}on(e,r){return{reply:(s,o,d)=>(this.handlers.push({method:e,urlPattern:r,handler:()=>({status:s,data:o,headers:d})}),this)}}getHistory(){return this.history}reset(){this.handlers=[],this.history={get:[],post:[],put:[],patch:[],delete:[],head:[],options:[]}}restore(){this.deactivate(),this.reset()}};function b(){let n=new f;return{name:"mock",install(e){n.activate(e)},cleanup(){n.restore()},adapter:n,onGet:n.onGet.bind(n),onPost:n.onPost.bind(n),onPut:n.onPut.bind(n),onPatch:n.onPatch.bind(n),onDelete:n.onDelete.bind(n),getHistory(){return n.getHistory()},restore(){n.restore()}}}0&&(module.exports={LRUCache,MockAdapter,cachePlugin,loggerPlugin,mockPlugin});
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/plugins/index.ts","../../../src/plugins/logger.ts","../../../src/plugins/cache.ts","../../../src/plugins/mock.ts"],"sourcesContent":["/**\n * httix — Plugin entry point\n *\n * Re-exports all built-in plugins and their associated types.\n */\n\nexport { loggerPlugin } from './logger';\nexport type { LoggerPluginConfig, LogLevel } from './logger';\n\nexport { cachePlugin, LRUCache } from './cache';\nexport type { CachePluginConfig } from './cache';\n\nexport { mockPlugin, MockAdapter } from './mock';\nexport type { MockHistoryEntry } from './mock';\n","/**\n * httix — Logger plugin\n *\n * Logs request and response lifecycle events via configurable log levels.\n */\n\nimport type { HttixClient, HttixPlugin, HttixResponse } from '../core/types';\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'none';\n\nexport interface LoggerPluginConfig {\n /** Minimum log level (default: 'info') */\n level?: LogLevel;\n /** Custom logger function (default: console) */\n logger?: {\n debug: (...args: unknown[]) => void;\n info: (...args: unknown[]) => void;\n warn: (...args: unknown[]) => void;\n error: (...args: unknown[]) => void;\n };\n /** Whether to log request body (default: false) */\n logRequestBody?: boolean;\n /** Whether to log response body (default: false) */\n logResponseBody?: boolean;\n /** Whether to log request headers (default: false) */\n logRequestHeaders?: boolean;\n /** Whether to log response headers (default: false) */\n logResponseHeaders?: boolean;\n}\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n none: 4,\n};\n\nexport function loggerPlugin(config?: LoggerPluginConfig): HttixPlugin {\n const level = config?.level ?? 'info';\n const logger = config?.logger ?? {\n debug: console.debug,\n info: console.info,\n warn: console.warn,\n error: console.error,\n };\n const logBody = config?.logRequestBody ?? false;\n const logResponseBody = config?.logResponseBody ?? false;\n const logReqHeaders = config?.logRequestHeaders ?? false;\n const logResHeaders = config?.logResponseHeaders ?? false;\n\n function shouldLog(messageLevel: LogLevel): boolean {\n return LOG_LEVELS[messageLevel] >= LOG_LEVELS[level];\n }\n\n return {\n name: 'logger',\n\n install(client: HttixClient) {\n // Request interceptor — log outgoing request\n client.interceptors.request.use((reqConfig) => {\n if (shouldLog('info')) {\n const logObj: Record<string, unknown> = {\n method: reqConfig.method,\n url: reqConfig.url,\n requestId: reqConfig.requestId,\n };\n if (logReqHeaders && reqConfig.headers) {\n const h =\n reqConfig.headers instanceof Headers\n ? Object.fromEntries(reqConfig.headers.entries())\n : reqConfig.headers;\n logObj.headers = h;\n }\n if (logBody && reqConfig.body) {\n logObj.body = reqConfig.body;\n }\n logger.info('[httix] Request:', logObj);\n }\n return reqConfig;\n });\n\n // Response interceptor — log incoming response\n client.interceptors.response.use(\n (response: HttixResponse<unknown>): HttixResponse<unknown> => {\n if (shouldLog('info')) {\n const logObj: Record<string, unknown> = {\n status: response.status,\n statusText: response.statusText,\n timing: response.timing,\n requestId: response.config.requestId,\n };\n if (logResHeaders) {\n logObj.headers = Object.fromEntries(response.headers.entries());\n }\n if (logResponseBody) {\n logObj.data = response.data;\n }\n logger.info('[httix] Response:', logObj);\n }\n return response;\n },\n (error) => {\n if (shouldLog('error')) {\n logger.error('[httix] Error:', {\n message: error.message,\n name: error.name,\n requestId: error.config?.requestId,\n });\n }\n // Return void so the error continues to propagate\n return;\n },\n );\n },\n\n cleanup() {\n // Interceptors persist on the client; no additional cleanup needed.\n },\n };\n}\n","/**\n * httix — Cache plugin\n *\n * LRU response cache with configurable TTL, size, stale-while-revalidate,\n * and per-method caching control.\n */\n\nimport type {\n HttixClient,\n HttixPlugin,\n HttixResponse,\n HttixRequestConfig,\n} from '../core/types';\n\nexport interface CachePluginConfig {\n /** Maximum cache entries (default: 100) */\n maxSize?: number;\n /** TTL in ms (default: 300000 = 5 minutes) */\n ttl?: number;\n /** Whether to enable stale-while-revalidate (default: false) */\n staleWhileRevalidate?: boolean;\n /** SWR window in ms (default: 60000 = 1 minute) */\n swrWindow?: number;\n /** Custom cache key function */\n generateKey?: (config: HttixRequestConfig) => string;\n /** Which methods to cache (default: ['GET']) */\n methods?: string[];\n /** Whether to respect Cache-Control headers (default: true) */\n respectCacheControl?: boolean;\n}\n\ninterface CacheEntry<T = unknown> {\n data: T;\n status: number;\n statusText: string;\n headers: Headers;\n timing: number;\n timestamp: number;\n config: HttixRequestConfig;\n raw: Response;\n}\n\n// ---------------------------------------------------------------------------\n// LRUCache\n// ---------------------------------------------------------------------------\n\nexport class LRUCache<T = unknown> {\n private cache = new Map<string, CacheEntry<T>>();\n private maxSize: number;\n private ttl: number;\n\n constructor(maxSize = 100, ttl = 300_000) {\n this.maxSize = maxSize;\n this.ttl = ttl;\n }\n\n get(key: string): CacheEntry<T> | undefined {\n const entry = this.cache.get(key);\n if (!entry) return undefined;\n\n if (Date.now() - entry.timestamp > this.ttl) {\n this.cache.delete(key);\n return undefined;\n }\n\n // Move to end (most recently used)\n this.cache.delete(key);\n this.cache.set(key, entry);\n\n return entry;\n }\n\n /**\n * Retrieve an entry even if it has expired (TTL exceeded).\n * Returns undefined only when the key does not exist at all.\n */\n getAllowingStale(key: string): CacheEntry<T> | undefined {\n const entry = this.cache.get(key);\n if (!entry) return undefined;\n\n // Move to end (most recently used)\n this.cache.delete(key);\n this.cache.set(key, entry);\n\n return entry;\n }\n\n set(key: string, entry: CacheEntry<T>): void {\n // Delete if exists (to update position)\n this.cache.delete(key);\n\n // Evict oldest if at capacity\n if (this.cache.size >= this.maxSize) {\n const firstKey = this.cache.keys().next().value;\n if (firstKey !== undefined) {\n this.cache.delete(firstKey);\n }\n }\n\n this.cache.set(key, entry);\n }\n\n has(key: string): boolean {\n return this.get(key) !== undefined;\n }\n\n delete(key: string): boolean {\n return this.cache.delete(key);\n }\n\n clear(): void {\n this.cache.clear();\n }\n\n get size(): number {\n return this.cache.size;\n }\n\n keys(): string[] {\n return Array.from(this.cache.keys());\n }\n}\n\n// ---------------------------------------------------------------------------\n// Default key generator\n// ---------------------------------------------------------------------------\n\nfunction defaultKeyGenerator(config: HttixRequestConfig): string {\n const url = new URL(config.url, config.baseURL || 'http://localhost');\n const query = config.query\n ? '?' +\n new URLSearchParams(\n Object.entries(config.query)\n .filter(([, v]) => v != null)\n .map(([k, v]) => [k, String(v)]),\n ).toString()\n : '';\n return `${config.method || 'GET'} ${url.origin}${url.pathname}${query}`;\n}\n\n// ---------------------------------------------------------------------------\n// Cache key attachment — type augmentation\n// ---------------------------------------------------------------------------\n\ninterface CacheRequestConfig extends HttixRequestConfig {\n _cacheEntry?: CacheEntry;\n _cacheKey?: string;\n _cacheHit?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// cachePlugin\n// ---------------------------------------------------------------------------\n\nexport function cachePlugin(\n config?: CachePluginConfig,\n): HttixPlugin & {\n cache: LRUCache;\n invalidate: (key?: string) => void;\n invalidatePattern: (pattern: RegExp) => void;\n clear: () => void;\n getStats: () => { size: number; maxSize: number; ttl: number };\n} {\n const maxSize = config?.maxSize ?? 100;\n const ttl = config?.ttl ?? 300_000;\n const staleWhileRevalidate = config?.staleWhileRevalidate ?? false;\n const swrWindow = config?.swrWindow ?? 60_000;\n const methods = config?.methods ?? ['GET'];\n const keyGen = config?.generateKey ?? defaultKeyGenerator;\n\n const cache = new LRUCache(maxSize, ttl);\n\n return {\n name: 'cache',\n\n install(client: HttixClient) {\n // -- Request interceptor: check for cache hit --------------------------\n client.interceptors.request.use((reqConfig: HttixRequestConfig) => {\n // Only cache configured methods\n if (!methods.includes(reqConfig.method || 'GET')) {\n return reqConfig;\n }\n\n const key = keyGen(reqConfig);\n\n // When SWR is enabled, use getAllowingStale first so that expired\n // entries are not prematurely evicted before the SWR check runs.\n const rawEntry = staleWhileRevalidate\n ? cache.getAllowingStale(key)\n : cache.get(key);\n\n if (rawEntry) {\n const isFresh = Date.now() - rawEntry.timestamp <= ttl;\n const isStaleRevalidate = staleWhileRevalidate && Date.now() - rawEntry.timestamp <= ttl + swrWindow;\n if (isFresh || isStaleRevalidate) {\n // Fresh cache hit OR stale entry within SWR window\n const tagged = reqConfig as CacheRequestConfig;\n tagged._cacheEntry = rawEntry;\n tagged._cacheKey = key;\n tagged._cacheHit = true;\n }\n }\n\n return reqConfig;\n });\n\n // -- Response interceptor: serve from cache or store --------------------\n client.interceptors.response.use(\n (response: HttixResponse<unknown>): HttixResponse<unknown> => {\n const taggedConfig = response.config as CacheRequestConfig;\n\n if (taggedConfig._cacheHit && taggedConfig._cacheEntry) {\n // Return cached response, reusing the real raw Response for\n // compatibility with HttixResponse consumers.\n const entry = taggedConfig._cacheEntry;\n return {\n data: entry.data,\n status: entry.status,\n statusText: entry.statusText,\n headers: entry.headers,\n ok: entry.status >= 200 && entry.status < 300,\n raw: response.raw,\n timing: response.timing,\n config: response.config,\n } as HttixResponse<unknown>;\n }\n\n // Store fresh response in cache\n const method = response.config.method || 'GET';\n if (methods.includes(method) && response.ok) {\n const key = keyGen(response.config);\n cache.set(key, {\n data: response.data,\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n timing: response.timing,\n timestamp: Date.now(),\n config: response.config,\n raw: response.raw,\n });\n }\n\n return response;\n },\n // Error handler — don't cache error responses, let error propagate.\n () => {\n return;\n },\n );\n },\n\n cache,\n\n invalidate(key?: string) {\n if (key) {\n cache.delete(key);\n }\n },\n\n invalidatePattern(pattern: RegExp) {\n for (const k of cache.keys()) {\n if (pattern.test(k)) {\n cache.delete(k);\n }\n }\n },\n\n clear() {\n cache.clear();\n },\n\n getStats() {\n return { size: cache.size, maxSize, ttl };\n },\n };\n}\n","/**\n * httix — Mock plugin\n *\n * Replaces the global fetch with an in-memory mock adapter so tests can\n * define request handlers and verify request history without hitting real\n * endpoints.\n */\n\nimport type {\n HttixClient,\n HttixPlugin,\n HttixRequestConfig,\n} from '../core/types';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface MockHandler {\n method: string;\n urlPattern: string | RegExp;\n handler: (\n config: HttixRequestConfig,\n ) => { status: number; data: unknown; headers?: Record<string, string> };\n}\n\nexport interface MockHistoryEntry {\n method: string;\n url: string;\n body?: unknown;\n headers?: Record<string, string>;\n config: HttixRequestConfig;\n}\n\n// ---------------------------------------------------------------------------\n// MockAdapter\n// ---------------------------------------------------------------------------\n\nexport class MockAdapter {\n private handlers: MockHandler[] = [];\n private history: Record<string, MockHistoryEntry[]> = {\n get: [],\n post: [],\n put: [],\n patch: [],\n delete: [],\n head: [],\n options: [],\n };\n private originalFetch: typeof globalThis.fetch | null = null;\n private isActive = false;\n\n /** Activate the mock — replace global fetch. */\n activate(_client: HttixClient): void {\n if (this.isActive) return;\n this.isActive = true;\n this.originalFetch = globalThis.fetch;\n globalThis.fetch = this.mockFetch.bind(this) as typeof globalThis.fetch;\n }\n\n /** Deactivate — restore the original fetch. */\n deactivate(): void {\n if (!this.isActive) return;\n this.isActive = false;\n if (this.originalFetch) {\n globalThis.fetch = this.originalFetch;\n this.originalFetch = null;\n }\n }\n\n /** Internal mock fetch implementation. */\n private mockFetch(\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> {\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.href\n : (input as Request).url;\n const isRequestObject = input instanceof Request;\n const method = (\n init?.method\n ?? (isRequestObject ? (input as Request).method : 'GET')\n ).toUpperCase();\n\n // Record in history\n const historyEntry: MockHistoryEntry = {\n method,\n url,\n body: init?.body\n ? JSON.parse(String(init.body))\n : isRequestObject\n ? undefined\n : undefined,\n headers: init?.headers\n ? init.headers instanceof Headers\n ? Object.fromEntries(init.headers.entries())\n : (init.headers as Record<string, string>)\n : isRequestObject\n ? Object.fromEntries((input as Request).headers.entries())\n : undefined,\n config: { url, method: method as HttixRequestConfig['method'] },\n };\n const methodLower = method.toLowerCase();\n const historyEntries = this.history[methodLower];\n if (historyEntries) {\n historyEntries.push(historyEntry);\n }\n\n // Find matching handler\n for (const handler of this.handlers) {\n if (handler.method !== method) continue;\n\n const matched =\n typeof handler.urlPattern === 'string'\n ? url === handler.urlPattern || url.endsWith(handler.urlPattern)\n : handler.urlPattern.test(url);\n\n if (matched) {\n const result = handler.handler(historyEntry.config);\n return Promise.resolve(\n new Response(JSON.stringify(result.data), {\n status: result.status,\n headers: { 'Content-Type': 'application/json', ...result.headers },\n }),\n );\n }\n }\n\n // No match found — return 404\n return Promise.resolve(\n new Response(JSON.stringify({ error: 'No mock handler found' }), {\n status: 404,\n headers: { 'Content-Type': 'application/json' },\n }),\n );\n }\n\n // -- Fluent API for registering handlers ----------------------------------\n\n onGet(\n url: string | RegExp,\n ): { reply: (status: number, data: unknown, headers?: Record<string, string>) => MockAdapter } {\n return this.on('GET', url);\n }\n\n onPost(\n url: string | RegExp,\n ): { reply: (status: number, data: unknown, headers?: Record<string, string>) => MockAdapter } {\n return this.on('POST', url);\n }\n\n onPut(\n url: string | RegExp,\n ): { reply: (status: number, data: unknown, headers?: Record<string, string>) => MockAdapter } {\n return this.on('PUT', url);\n }\n\n onPatch(\n url: string | RegExp,\n ): { reply: (status: number, data: unknown, headers?: Record<string, string>) => MockAdapter } {\n return this.on('PATCH', url);\n }\n\n onDelete(\n url: string | RegExp,\n ): { reply: (status: number, data: unknown, headers?: Record<string, string>) => MockAdapter } {\n return this.on('DELETE', url);\n }\n\n private on(\n method: string,\n url: string | RegExp,\n ): { reply: (status: number, data: unknown, headers?: Record<string, string>) => MockAdapter } {\n return {\n reply: (\n status: number,\n data: unknown,\n headers?: Record<string, string>,\n ) => {\n this.handlers.push({\n method,\n urlPattern: url,\n handler: () => ({ status, data, headers }),\n });\n return this;\n },\n };\n }\n\n /** Access recorded request history. */\n getHistory(): Record<string, MockHistoryEntry[]> {\n return this.history;\n }\n\n /** Clear all handlers and history (adapter stays active). */\n reset(): void {\n this.handlers = [];\n this.history = {\n get: [],\n post: [],\n put: [],\n patch: [],\n delete: [],\n head: [],\n options: [],\n };\n }\n\n /** Fully deactivate and reset. */\n restore(): void {\n this.deactivate();\n this.reset();\n }\n}\n\n// ---------------------------------------------------------------------------\n// mockPlugin factory\n// ---------------------------------------------------------------------------\n\nexport function mockPlugin(): HttixPlugin & {\n adapter: MockAdapter;\n onGet: MockAdapter['onGet'];\n onPost: MockAdapter['onPost'];\n onPut: MockAdapter['onPut'];\n onPatch: MockAdapter['onPatch'];\n onDelete: MockAdapter['onDelete'];\n getHistory: () => Record<string, MockHistoryEntry[]>;\n restore: () => void;\n} {\n const adapter = new MockAdapter();\n\n return {\n name: 'mock',\n\n install(client: HttixClient) {\n adapter.activate(client);\n },\n\n cleanup() {\n adapter.restore();\n },\n\n adapter,\n\n onGet: adapter.onGet.bind(adapter),\n onPost: adapter.onPost.bind(adapter),\n onPut: adapter.onPut.bind(adapter),\n onPatch: adapter.onPatch.bind(adapter),\n onDelete: adapter.onDelete.bind(adapter),\n\n getHistory() {\n return adapter.getHistory();\n },\n\n restore() {\n adapter.restore();\n },\n };\n}\n"],"mappings":"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","names":["plugins_exports","__export","LRUCache","MockAdapter","cachePlugin","loggerPlugin","mockPlugin","__toCommonJS","LOG_LEVELS","loggerPlugin","config","level","logger","logBody","logResponseBody","logReqHeaders","logResHeaders","shouldLog","messageLevel","client","reqConfig","logObj","h","response","error","LRUCache","maxSize","ttl","key","entry","firstKey","defaultKeyGenerator","config","url","query","v","k","cachePlugin","staleWhileRevalidate","swrWindow","methods","keyGen","cache","client","reqConfig","rawEntry","isFresh","isStaleRevalidate","tagged","response","taggedConfig","method","pattern","MockAdapter","_client","input","init","url","isRequestObject","method","historyEntry","methodLower","historyEntries","handler","result","status","data","headers","mockPlugin","adapter","client"]}
|