@http-client-toolkit/core 0.1.0 → 0.2.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/lib/index.cjs +437 -4
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +183 -1
- package/lib/index.d.ts +183 -1
- package/lib/index.js +426 -5
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,158 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
+
interface CacheControlDirectives {
|
|
4
|
+
maxAge?: number;
|
|
5
|
+
sMaxAge?: number;
|
|
6
|
+
noCache: boolean;
|
|
7
|
+
noStore: boolean;
|
|
8
|
+
mustRevalidate: boolean;
|
|
9
|
+
proxyRevalidate: boolean;
|
|
10
|
+
public: boolean;
|
|
11
|
+
private: boolean;
|
|
12
|
+
immutable: boolean;
|
|
13
|
+
staleWhileRevalidate?: number;
|
|
14
|
+
staleIfError?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Parse a Cache-Control header value into structured directives.
|
|
18
|
+
*
|
|
19
|
+
* Lenient: unrecognised directives are silently ignored, malformed
|
|
20
|
+
* numeric values result in undefined (treated as absent).
|
|
21
|
+
*/
|
|
22
|
+
declare function parseCacheControl(header: string | null | undefined): CacheControlDirectives;
|
|
23
|
+
|
|
24
|
+
interface CacheEntryMetadata {
|
|
25
|
+
/** ETag response header, for If-None-Match conditional requests */
|
|
26
|
+
etag?: string;
|
|
27
|
+
/** Last-Modified response header, for If-Modified-Since conditional requests */
|
|
28
|
+
lastModified?: string;
|
|
29
|
+
/** Parsed Cache-Control directives */
|
|
30
|
+
cacheControl: CacheControlDirectives;
|
|
31
|
+
/**
|
|
32
|
+
* Date response header as epoch ms.
|
|
33
|
+
* Falls back to storedAt if the server didn't send Date.
|
|
34
|
+
*/
|
|
35
|
+
responseDate: number;
|
|
36
|
+
/** Epoch ms when this entry was written to the cache */
|
|
37
|
+
storedAt: number;
|
|
38
|
+
/** Value of the Age response header at receipt time (seconds) */
|
|
39
|
+
ageHeader: number;
|
|
40
|
+
/** Raw Vary header value (e.g. "Accept, Accept-Encoding") */
|
|
41
|
+
varyHeaders?: string;
|
|
42
|
+
/** Captured request header values for Vary matching */
|
|
43
|
+
varyValues?: Record<string, string | undefined>;
|
|
44
|
+
/** HTTP status code of the original response */
|
|
45
|
+
statusCode: number;
|
|
46
|
+
/**
|
|
47
|
+
* Expires header as epoch ms. Used as freshness fallback
|
|
48
|
+
* when Cache-Control max-age is absent.
|
|
49
|
+
*/
|
|
50
|
+
expires?: number;
|
|
51
|
+
}
|
|
52
|
+
interface CacheEntry<T = unknown> {
|
|
53
|
+
/** Discriminant field for the isCacheEntry type guard */
|
|
54
|
+
__cacheEntry: true;
|
|
55
|
+
/** The actual response value the caller requested */
|
|
56
|
+
value: T;
|
|
57
|
+
/** RFC 9111 metadata for freshness/revalidation decisions */
|
|
58
|
+
metadata: CacheEntryMetadata;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Type guard: distinguishes a CacheEntry envelope from a raw cached value.
|
|
62
|
+
*
|
|
63
|
+
* When respectCacheHeaders is enabled on a cache that previously stored raw
|
|
64
|
+
* values, those old entries will fail this check and be treated as cache misses.
|
|
65
|
+
*/
|
|
66
|
+
declare function isCacheEntry<T>(value: unknown): value is CacheEntry<T>;
|
|
67
|
+
/**
|
|
68
|
+
* Parse an HTTP-date string (RFC 7231) into epoch ms.
|
|
69
|
+
* Returns undefined if the value is missing or unparseable.
|
|
70
|
+
*
|
|
71
|
+
* Handles:
|
|
72
|
+
* - IMF-fixdate: "Sun, 06 Nov 1994 08:49:37 GMT"
|
|
73
|
+
* - RFC 850: "Sunday, 06-Nov-94 08:49:37 GMT"
|
|
74
|
+
* - asctime: "Sun Nov 6 08:49:37 1994"
|
|
75
|
+
* - "0" (treated as already-expired per Expires spec)
|
|
76
|
+
*/
|
|
77
|
+
declare function parseHttpDate(value: string | null | undefined): number | undefined;
|
|
78
|
+
/**
|
|
79
|
+
* Create a CacheEntry from a response value and the HTTP response headers.
|
|
80
|
+
*
|
|
81
|
+
* Call this after response body parsing + transformation + validation,
|
|
82
|
+
* right before storing in the cache.
|
|
83
|
+
*/
|
|
84
|
+
declare function createCacheEntry<T>(value: T, headers: Headers, statusCode: number): CacheEntry<T>;
|
|
85
|
+
/**
|
|
86
|
+
* Refresh a cache entry after receiving a 304 Not Modified response.
|
|
87
|
+
*
|
|
88
|
+
* Updates metadata from the 304 response headers while keeping the
|
|
89
|
+
* existing cached value (body). Per RFC 9111 §4.3.4, the 304 response
|
|
90
|
+
* headers replace the stored headers.
|
|
91
|
+
*/
|
|
92
|
+
declare function refreshCacheEntry<T>(existing: CacheEntry<T>, newHeaders: Headers): CacheEntry<T>;
|
|
93
|
+
|
|
94
|
+
type FreshnessStatus = 'fresh' | 'stale' | 'must-revalidate' | 'stale-while-revalidate' | 'stale-if-error' | 'no-cache';
|
|
95
|
+
/**
|
|
96
|
+
* Calculate the freshness lifetime of a cache entry in seconds.
|
|
97
|
+
*
|
|
98
|
+
* Priority order for a private cache (RFC 9111 §4.2.2):
|
|
99
|
+
* 1. max-age (s-maxage is ignored — shared-cache-only)
|
|
100
|
+
* 2. Expires − Date
|
|
101
|
+
* 3. Heuristic: 10% of (Date − Last-Modified)
|
|
102
|
+
* 4. 0 (treat as immediately stale)
|
|
103
|
+
*/
|
|
104
|
+
declare function calculateFreshnessLifetime(metadata: CacheEntryMetadata): number;
|
|
105
|
+
/**
|
|
106
|
+
* Calculate the current age of a cache entry in seconds.
|
|
107
|
+
*
|
|
108
|
+
* Per RFC 9111 §4.2.3:
|
|
109
|
+
* apparent_age = max(0, response_time − date_value)
|
|
110
|
+
* corrected_age_value = age_value + response_delay
|
|
111
|
+
* corrected_initial = max(apparent_age, corrected_age_value)
|
|
112
|
+
* resident_time = now − response_time
|
|
113
|
+
* current_age = corrected_initial + resident_time
|
|
114
|
+
*
|
|
115
|
+
* We approximate response_delay as 0 since we don't track request_time.
|
|
116
|
+
* This is conservative (slightly underestimates age).
|
|
117
|
+
*/
|
|
118
|
+
declare function calculateCurrentAge(metadata: CacheEntryMetadata, now?: number): number;
|
|
119
|
+
/**
|
|
120
|
+
* Determine the freshness status of a cache entry.
|
|
121
|
+
*
|
|
122
|
+
* Returns the most specific applicable status, used by HttpClient
|
|
123
|
+
* to decide whether to serve from cache, revalidate, or re-fetch.
|
|
124
|
+
*/
|
|
125
|
+
declare function getFreshnessStatus(metadata: CacheEntryMetadata, now?: number): FreshnessStatus;
|
|
126
|
+
/**
|
|
127
|
+
* Calculate the TTL to pass to CacheStore.set().
|
|
128
|
+
*
|
|
129
|
+
* This must be long enough to cover the freshness lifetime PLUS any
|
|
130
|
+
* stale-serving windows (SWR, SIE), so the entry remains available
|
|
131
|
+
* in the store during those windows.
|
|
132
|
+
*
|
|
133
|
+
* Falls back to defaultTTL when no cache headers provide a lifetime.
|
|
134
|
+
*/
|
|
135
|
+
declare function calculateStoreTTL(metadata: CacheEntryMetadata, defaultTTL: number): number;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Parse a Vary header value into normalised (lowercased) header names.
|
|
139
|
+
* Returns ['*'] for Vary: * (meaning the response varies on everything).
|
|
140
|
+
*/
|
|
141
|
+
declare function parseVaryHeader(varyHeader: string | null | undefined): Array<string>;
|
|
142
|
+
/**
|
|
143
|
+
* Extract the values of Vary-listed headers from a request.
|
|
144
|
+
* Stored alongside the cache entry so we can compare on lookup.
|
|
145
|
+
*/
|
|
146
|
+
declare function captureVaryValues(varyFields: Array<string>, requestHeaders: Record<string, string | undefined>): Record<string, string | undefined>;
|
|
147
|
+
/**
|
|
148
|
+
* Check whether a cached entry's Vary values match the current request.
|
|
149
|
+
*
|
|
150
|
+
* Returns false if:
|
|
151
|
+
* - Vary includes '*' (never matches — always revalidate)
|
|
152
|
+
* - Any Vary-listed header has a different value in the current request
|
|
153
|
+
*/
|
|
154
|
+
declare function varyMatches(cachedVaryValues: Record<string, string | undefined> | undefined, cachedVaryHeader: string | undefined, currentRequestHeaders: Record<string, string | undefined>): boolean;
|
|
155
|
+
|
|
3
156
|
/**
|
|
4
157
|
* Interface for caching API responses with TTL support
|
|
5
158
|
*/
|
|
@@ -343,10 +496,31 @@ interface HttpClientOptions {
|
|
|
343
496
|
reset?: Array<string>;
|
|
344
497
|
combined?: Array<string>;
|
|
345
498
|
};
|
|
499
|
+
/**
|
|
500
|
+
* When true, the client respects HTTP cache headers (Cache-Control, ETag,
|
|
501
|
+
* Last-Modified, Expires) per RFC 9111. When false (default), caching uses
|
|
502
|
+
* only the static defaultCacheTTL.
|
|
503
|
+
*/
|
|
504
|
+
respectCacheHeaders?: boolean;
|
|
505
|
+
/**
|
|
506
|
+
* Override specific cache header behaviors. Only applies when
|
|
507
|
+
* respectCacheHeaders is true.
|
|
508
|
+
*/
|
|
509
|
+
cacheHeaderOverrides?: {
|
|
510
|
+
/** Cache responses even when Cache-Control: no-store is set */
|
|
511
|
+
ignoreNoStore?: boolean;
|
|
512
|
+
/** Skip revalidation even when Cache-Control: no-cache is set */
|
|
513
|
+
ignoreNoCache?: boolean;
|
|
514
|
+
/** Minimum TTL in seconds — floor on header-derived freshness */
|
|
515
|
+
minimumTTL?: number;
|
|
516
|
+
/** Maximum TTL in seconds — cap on header-derived freshness */
|
|
517
|
+
maximumTTL?: number;
|
|
518
|
+
};
|
|
346
519
|
}
|
|
347
520
|
declare class HttpClient implements HttpClientContract {
|
|
348
521
|
private stores;
|
|
349
522
|
private serverCooldowns;
|
|
523
|
+
private pendingRevalidations;
|
|
350
524
|
private options;
|
|
351
525
|
constructor(stores?: HttpClientStores, options?: HttpClientOptions);
|
|
352
526
|
private normalizeRateLimitHeaders;
|
|
@@ -372,6 +546,14 @@ declare class HttpClient implements HttpClientContract {
|
|
|
372
546
|
private applyServerRateLimitHints;
|
|
373
547
|
private enforceServerCooldown;
|
|
374
548
|
private enforceStoreRateLimit;
|
|
549
|
+
/**
|
|
550
|
+
* Wait for all pending background revalidations to complete.
|
|
551
|
+
* Primarily useful in tests to avoid dangling promises.
|
|
552
|
+
*/
|
|
553
|
+
flushRevalidations(): Promise<void>;
|
|
554
|
+
private backgroundRevalidate;
|
|
555
|
+
private clampTTL;
|
|
556
|
+
private isServerErrorOrNetworkFailure;
|
|
375
557
|
private generateClientError;
|
|
376
558
|
private parseResponseBody;
|
|
377
559
|
get<Result>(url: string, options?: {
|
|
@@ -389,4 +571,4 @@ declare class HttpClientError extends Error {
|
|
|
389
571
|
constructor(message: string, statusCode?: number);
|
|
390
572
|
}
|
|
391
573
|
|
|
392
|
-
export { type ActivityMetrics, AdaptiveCapacityCalculator, type AdaptiveConfig, AdaptiveConfigSchema, type AdaptiveRateLimitStore, type CacheStore, DEFAULT_RATE_LIMIT, type DedupeStore, type DynamicCapacityResult, HttpClient, type HttpClientContract, HttpClientError, type HttpClientOptions, type HttpClientStores, type RateLimitConfig, type RateLimitStore, type RequestPriority, hashRequest };
|
|
574
|
+
export { type ActivityMetrics, AdaptiveCapacityCalculator, type AdaptiveConfig, AdaptiveConfigSchema, type AdaptiveRateLimitStore, type CacheControlDirectives, type CacheEntry, type CacheEntryMetadata, type CacheStore, DEFAULT_RATE_LIMIT, type DedupeStore, type DynamicCapacityResult, type FreshnessStatus, HttpClient, type HttpClientContract, HttpClientError, type HttpClientOptions, type HttpClientStores, type RateLimitConfig, type RateLimitStore, type RequestPriority, calculateCurrentAge, calculateFreshnessLifetime, calculateStoreTTL, captureVaryValues, createCacheEntry, getFreshnessStatus, hashRequest, isCacheEntry, parseCacheControl, parseHttpDate, parseVaryHeader, refreshCacheEntry, varyMatches };
|