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.
@@ -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"]}