@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.cjs
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
3
3
|
var zod = require('zod');
|
|
4
4
|
var crypto = require('crypto');
|
|
5
5
|
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __defProps = Object.defineProperties;
|
|
8
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
9
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
10
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
11
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
12
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
13
|
+
var __spreadValues = (a, b) => {
|
|
14
|
+
for (var prop in b || (b = {}))
|
|
15
|
+
if (__hasOwnProp.call(b, prop))
|
|
16
|
+
__defNormalProp(a, prop, b[prop]);
|
|
17
|
+
if (__getOwnPropSymbols)
|
|
18
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
19
|
+
if (__propIsEnum.call(b, prop))
|
|
20
|
+
__defNormalProp(a, prop, b[prop]);
|
|
21
|
+
}
|
|
22
|
+
return a;
|
|
23
|
+
};
|
|
24
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
6
25
|
var __async = (__this, __arguments, generator) => {
|
|
7
26
|
return new Promise((resolve, reject) => {
|
|
8
27
|
var fulfilled = (value) => {
|
|
@@ -24,6 +43,231 @@ var __async = (__this, __arguments, generator) => {
|
|
|
24
43
|
});
|
|
25
44
|
};
|
|
26
45
|
|
|
46
|
+
// src/cache/cache-control-parser.ts
|
|
47
|
+
var EMPTY_DIRECTIVES = {
|
|
48
|
+
noCache: false,
|
|
49
|
+
noStore: false,
|
|
50
|
+
mustRevalidate: false,
|
|
51
|
+
proxyRevalidate: false,
|
|
52
|
+
public: false,
|
|
53
|
+
private: false,
|
|
54
|
+
immutable: false
|
|
55
|
+
};
|
|
56
|
+
function parseSeconds(raw) {
|
|
57
|
+
if (raw === void 0) return void 0;
|
|
58
|
+
const n = Number.parseInt(raw.trim(), 10);
|
|
59
|
+
return Number.isFinite(n) && n >= 0 ? n : void 0;
|
|
60
|
+
}
|
|
61
|
+
function parseCacheControl(header) {
|
|
62
|
+
if (!header) return __spreadValues({}, EMPTY_DIRECTIVES);
|
|
63
|
+
const result = __spreadValues({}, EMPTY_DIRECTIVES);
|
|
64
|
+
for (const part of header.split(",")) {
|
|
65
|
+
const trimmed = part.trim();
|
|
66
|
+
if (!trimmed) continue;
|
|
67
|
+
const eqIdx = trimmed.indexOf("=");
|
|
68
|
+
const key = (eqIdx === -1 ? trimmed : trimmed.slice(0, eqIdx)).trim().toLowerCase();
|
|
69
|
+
const value = eqIdx === -1 ? void 0 : trimmed.slice(eqIdx + 1).trim();
|
|
70
|
+
switch (key) {
|
|
71
|
+
case "max-age":
|
|
72
|
+
result.maxAge = parseSeconds(value);
|
|
73
|
+
break;
|
|
74
|
+
case "s-maxage":
|
|
75
|
+
result.sMaxAge = parseSeconds(value);
|
|
76
|
+
break;
|
|
77
|
+
case "no-cache":
|
|
78
|
+
result.noCache = true;
|
|
79
|
+
break;
|
|
80
|
+
case "no-store":
|
|
81
|
+
result.noStore = true;
|
|
82
|
+
break;
|
|
83
|
+
case "must-revalidate":
|
|
84
|
+
result.mustRevalidate = true;
|
|
85
|
+
break;
|
|
86
|
+
case "proxy-revalidate":
|
|
87
|
+
result.proxyRevalidate = true;
|
|
88
|
+
break;
|
|
89
|
+
case "public":
|
|
90
|
+
result.public = true;
|
|
91
|
+
break;
|
|
92
|
+
case "private":
|
|
93
|
+
result.private = true;
|
|
94
|
+
break;
|
|
95
|
+
case "immutable":
|
|
96
|
+
result.immutable = true;
|
|
97
|
+
break;
|
|
98
|
+
case "stale-while-revalidate":
|
|
99
|
+
result.staleWhileRevalidate = parseSeconds(value);
|
|
100
|
+
break;
|
|
101
|
+
case "stale-if-error":
|
|
102
|
+
result.staleIfError = parseSeconds(value);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/cache/cache-entry.ts
|
|
110
|
+
function isCacheEntry(value) {
|
|
111
|
+
return typeof value === "object" && value !== null && value.__cacheEntry === true && "value" in value && "metadata" in value;
|
|
112
|
+
}
|
|
113
|
+
function parseHttpDate(value) {
|
|
114
|
+
if (value === null || value === void 0) return void 0;
|
|
115
|
+
const trimmed = value.trim();
|
|
116
|
+
if (!trimmed) return void 0;
|
|
117
|
+
if (trimmed === "0") return 0;
|
|
118
|
+
const ms = Date.parse(trimmed);
|
|
119
|
+
return Number.isFinite(ms) ? ms : void 0;
|
|
120
|
+
}
|
|
121
|
+
function createCacheEntry(value, headers, statusCode) {
|
|
122
|
+
var _a, _b, _c, _d;
|
|
123
|
+
const now = Date.now();
|
|
124
|
+
const dateMs = (_a = parseHttpDate(headers.get("date"))) != null ? _a : now;
|
|
125
|
+
const ageRaw = headers.get("age");
|
|
126
|
+
const ageHeader = ageRaw !== null ? Number.parseInt(ageRaw.trim(), 10) || 0 : 0;
|
|
127
|
+
return {
|
|
128
|
+
__cacheEntry: true,
|
|
129
|
+
value,
|
|
130
|
+
metadata: {
|
|
131
|
+
etag: (_b = headers.get("etag")) != null ? _b : void 0,
|
|
132
|
+
lastModified: (_c = headers.get("last-modified")) != null ? _c : void 0,
|
|
133
|
+
cacheControl: parseCacheControl(headers.get("cache-control")),
|
|
134
|
+
responseDate: dateMs,
|
|
135
|
+
storedAt: now,
|
|
136
|
+
ageHeader,
|
|
137
|
+
varyHeaders: (_d = headers.get("vary")) != null ? _d : void 0,
|
|
138
|
+
statusCode,
|
|
139
|
+
expires: parseHttpDate(headers.get("expires"))
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function refreshCacheEntry(existing, newHeaders) {
|
|
144
|
+
var _a;
|
|
145
|
+
const now = Date.now();
|
|
146
|
+
const dateMs = (_a = parseHttpDate(newHeaders.get("date"))) != null ? _a : now;
|
|
147
|
+
const ageRaw = newHeaders.get("age");
|
|
148
|
+
const ageHeader = ageRaw !== null ? Number.parseInt(ageRaw.trim(), 10) || 0 : 0;
|
|
149
|
+
const newCacheControl = newHeaders.get("cache-control");
|
|
150
|
+
const newEtag = newHeaders.get("etag");
|
|
151
|
+
const newLastModified = newHeaders.get("last-modified");
|
|
152
|
+
const newExpires = newHeaders.get("expires");
|
|
153
|
+
const newVary = newHeaders.get("vary");
|
|
154
|
+
return {
|
|
155
|
+
__cacheEntry: true,
|
|
156
|
+
value: existing.value,
|
|
157
|
+
metadata: __spreadProps(__spreadValues({}, existing.metadata), {
|
|
158
|
+
cacheControl: newCacheControl ? parseCacheControl(newCacheControl) : existing.metadata.cacheControl,
|
|
159
|
+
etag: newEtag != null ? newEtag : existing.metadata.etag,
|
|
160
|
+
lastModified: newLastModified != null ? newLastModified : existing.metadata.lastModified,
|
|
161
|
+
responseDate: dateMs,
|
|
162
|
+
storedAt: now,
|
|
163
|
+
ageHeader,
|
|
164
|
+
expires: newExpires !== null ? parseHttpDate(newExpires) : existing.metadata.expires,
|
|
165
|
+
varyHeaders: newVary != null ? newVary : existing.metadata.varyHeaders
|
|
166
|
+
// statusCode stays the same (the original 200, not 304)
|
|
167
|
+
})
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/cache/freshness.ts
|
|
172
|
+
function calculateFreshnessLifetime(metadata) {
|
|
173
|
+
const { cacheControl } = metadata;
|
|
174
|
+
if (cacheControl.maxAge !== void 0) {
|
|
175
|
+
return cacheControl.maxAge;
|
|
176
|
+
}
|
|
177
|
+
if (metadata.expires !== void 0) {
|
|
178
|
+
if (metadata.expires === 0) return 0;
|
|
179
|
+
const delta = (metadata.expires - metadata.responseDate) / 1e3;
|
|
180
|
+
return Math.max(0, delta);
|
|
181
|
+
}
|
|
182
|
+
if (metadata.lastModified) {
|
|
183
|
+
const lastModMs = Date.parse(metadata.lastModified);
|
|
184
|
+
if (Number.isFinite(lastModMs)) {
|
|
185
|
+
const age = (metadata.responseDate - lastModMs) / 1e3;
|
|
186
|
+
if (age > 0) {
|
|
187
|
+
return Math.floor(age * 0.1);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
function calculateCurrentAge(metadata, now) {
|
|
194
|
+
const currentTime = now != null ? now : Date.now();
|
|
195
|
+
const responseTime = metadata.storedAt;
|
|
196
|
+
const apparentAge = Math.max(
|
|
197
|
+
0,
|
|
198
|
+
(responseTime - metadata.responseDate) / 1e3
|
|
199
|
+
);
|
|
200
|
+
const correctedAgeValue = metadata.ageHeader;
|
|
201
|
+
const correctedInitialAge = Math.max(apparentAge, correctedAgeValue);
|
|
202
|
+
const residentTime = (currentTime - responseTime) / 1e3;
|
|
203
|
+
return correctedInitialAge + residentTime;
|
|
204
|
+
}
|
|
205
|
+
function getFreshnessStatus(metadata, now) {
|
|
206
|
+
const { cacheControl } = metadata;
|
|
207
|
+
if (cacheControl.noCache) {
|
|
208
|
+
return "no-cache";
|
|
209
|
+
}
|
|
210
|
+
const freshnessLifetime = calculateFreshnessLifetime(metadata);
|
|
211
|
+
const currentAge = calculateCurrentAge(metadata, now);
|
|
212
|
+
if (freshnessLifetime > currentAge) {
|
|
213
|
+
return "fresh";
|
|
214
|
+
}
|
|
215
|
+
const staleness = currentAge - freshnessLifetime;
|
|
216
|
+
if (cacheControl.mustRevalidate) {
|
|
217
|
+
return "must-revalidate";
|
|
218
|
+
}
|
|
219
|
+
if (cacheControl.staleWhileRevalidate !== void 0 && staleness <= cacheControl.staleWhileRevalidate) {
|
|
220
|
+
return "stale-while-revalidate";
|
|
221
|
+
}
|
|
222
|
+
if (cacheControl.staleIfError !== void 0 && staleness <= cacheControl.staleIfError) {
|
|
223
|
+
return "stale-if-error";
|
|
224
|
+
}
|
|
225
|
+
return "stale";
|
|
226
|
+
}
|
|
227
|
+
function calculateStoreTTL(metadata, defaultTTL) {
|
|
228
|
+
var _a, _b;
|
|
229
|
+
const freshness = calculateFreshnessLifetime(metadata);
|
|
230
|
+
if (freshness === 0 && metadata.cacheControl.maxAge === void 0) {
|
|
231
|
+
return defaultTTL;
|
|
232
|
+
}
|
|
233
|
+
const swrWindow = (_a = metadata.cacheControl.staleWhileRevalidate) != null ? _a : 0;
|
|
234
|
+
const sieWindow = (_b = metadata.cacheControl.staleIfError) != null ? _b : 0;
|
|
235
|
+
const staleWindow = Math.max(swrWindow, sieWindow);
|
|
236
|
+
return freshness + staleWindow;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// src/cache/vary.ts
|
|
240
|
+
function parseVaryHeader(varyHeader) {
|
|
241
|
+
if (!varyHeader) return [];
|
|
242
|
+
const trimmed = varyHeader.trim();
|
|
243
|
+
if (trimmed === "*") return ["*"];
|
|
244
|
+
return trimmed.split(",").map((f) => f.trim().toLowerCase()).filter(Boolean);
|
|
245
|
+
}
|
|
246
|
+
function captureVaryValues(varyFields, requestHeaders) {
|
|
247
|
+
var _a;
|
|
248
|
+
const values = {};
|
|
249
|
+
for (const field of varyFields) {
|
|
250
|
+
const lower = field.toLowerCase();
|
|
251
|
+
values[lower] = (_a = requestHeaders[lower]) != null ? _a : requestHeaders[field];
|
|
252
|
+
}
|
|
253
|
+
return values;
|
|
254
|
+
}
|
|
255
|
+
function varyMatches(cachedVaryValues, cachedVaryHeader, currentRequestHeaders) {
|
|
256
|
+
var _a;
|
|
257
|
+
if (!cachedVaryHeader) return true;
|
|
258
|
+
const fields = parseVaryHeader(cachedVaryHeader);
|
|
259
|
+
if (fields.length === 0) return true;
|
|
260
|
+
if (fields[0] === "*") return false;
|
|
261
|
+
if (!cachedVaryValues) return false;
|
|
262
|
+
for (const field of fields) {
|
|
263
|
+
const lower = field.toLowerCase();
|
|
264
|
+
const cachedVal = cachedVaryValues[lower];
|
|
265
|
+
const currentVal = (_a = currentRequestHeaders[lower]) != null ? _a : currentRequestHeaders[field];
|
|
266
|
+
if (cachedVal !== currentVal) return false;
|
|
267
|
+
}
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
|
|
27
271
|
// src/errors/http-client-error.ts
|
|
28
272
|
var HttpClientError = class extends Error {
|
|
29
273
|
constructor(message, statusCode) {
|
|
@@ -258,15 +502,18 @@ function wait(ms, signal) {
|
|
|
258
502
|
var HttpClient = class {
|
|
259
503
|
constructor(stores = {}, options = {}) {
|
|
260
504
|
this.serverCooldowns = /* @__PURE__ */ new Map();
|
|
261
|
-
|
|
505
|
+
this.pendingRevalidations = [];
|
|
506
|
+
var _a, _b, _c, _d;
|
|
262
507
|
this.stores = stores;
|
|
263
508
|
this.options = {
|
|
264
509
|
defaultCacheTTL: (_a = options.defaultCacheTTL) != null ? _a : 3600,
|
|
265
510
|
throwOnRateLimit: (_b = options.throwOnRateLimit) != null ? _b : true,
|
|
266
511
|
maxWaitTime: (_c = options.maxWaitTime) != null ? _c : 6e4,
|
|
512
|
+
respectCacheHeaders: (_d = options.respectCacheHeaders) != null ? _d : false,
|
|
267
513
|
responseTransformer: options.responseTransformer,
|
|
268
514
|
errorHandler: options.errorHandler,
|
|
269
515
|
responseHandler: options.responseHandler,
|
|
516
|
+
cacheHeaderOverrides: options.cacheHeaderOverrides,
|
|
270
517
|
rateLimitHeaders: this.normalizeRateLimitHeaders(
|
|
271
518
|
options.rateLimitHeaders
|
|
272
519
|
)
|
|
@@ -522,6 +769,81 @@ var HttpClient = class {
|
|
|
522
769
|
return hasAtomicAcquire;
|
|
523
770
|
});
|
|
524
771
|
}
|
|
772
|
+
/**
|
|
773
|
+
* Wait for all pending background revalidations to complete.
|
|
774
|
+
* Primarily useful in tests to avoid dangling promises.
|
|
775
|
+
*/
|
|
776
|
+
flushRevalidations() {
|
|
777
|
+
return __async(this, null, function* () {
|
|
778
|
+
yield Promise.allSettled(this.pendingRevalidations);
|
|
779
|
+
this.pendingRevalidations = [];
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
backgroundRevalidate(url, hash, entry) {
|
|
783
|
+
return __async(this, null, function* () {
|
|
784
|
+
var _a, _b;
|
|
785
|
+
const headers = new Headers();
|
|
786
|
+
if (entry.metadata.etag) {
|
|
787
|
+
headers.set("If-None-Match", entry.metadata.etag);
|
|
788
|
+
}
|
|
789
|
+
if (entry.metadata.lastModified) {
|
|
790
|
+
headers.set("If-Modified-Since", entry.metadata.lastModified);
|
|
791
|
+
}
|
|
792
|
+
try {
|
|
793
|
+
const response = yield fetch(url, { headers });
|
|
794
|
+
this.applyServerRateLimitHints(url, response.headers, response.status);
|
|
795
|
+
if (response.status === 304) {
|
|
796
|
+
const refreshed = refreshCacheEntry(entry, response.headers);
|
|
797
|
+
const ttl = this.clampTTL(
|
|
798
|
+
calculateStoreTTL(refreshed.metadata, this.options.defaultCacheTTL)
|
|
799
|
+
);
|
|
800
|
+
yield (_a = this.stores.cache) == null ? void 0 : _a.set(hash, refreshed, ttl);
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
if (response.ok) {
|
|
804
|
+
const parsedBody = yield this.parseResponseBody(response);
|
|
805
|
+
let data = parsedBody.data;
|
|
806
|
+
if (this.options.responseTransformer && data) {
|
|
807
|
+
data = this.options.responseTransformer(data);
|
|
808
|
+
}
|
|
809
|
+
if (this.options.responseHandler) {
|
|
810
|
+
data = this.options.responseHandler(data);
|
|
811
|
+
}
|
|
812
|
+
const newEntry = createCacheEntry(
|
|
813
|
+
data,
|
|
814
|
+
response.headers,
|
|
815
|
+
response.status
|
|
816
|
+
);
|
|
817
|
+
const ttl = this.clampTTL(
|
|
818
|
+
calculateStoreTTL(newEntry.metadata, this.options.defaultCacheTTL)
|
|
819
|
+
);
|
|
820
|
+
yield (_b = this.stores.cache) == null ? void 0 : _b.set(hash, newEntry, ttl);
|
|
821
|
+
}
|
|
822
|
+
} catch (e) {
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
clampTTL(ttl) {
|
|
827
|
+
const overrides = this.options.cacheHeaderOverrides;
|
|
828
|
+
if (!overrides) return ttl;
|
|
829
|
+
let clamped = ttl;
|
|
830
|
+
if (overrides.minimumTTL !== void 0) {
|
|
831
|
+
clamped = Math.max(clamped, overrides.minimumTTL);
|
|
832
|
+
}
|
|
833
|
+
if (overrides.maximumTTL !== void 0) {
|
|
834
|
+
clamped = Math.min(clamped, overrides.maximumTTL);
|
|
835
|
+
}
|
|
836
|
+
return clamped;
|
|
837
|
+
}
|
|
838
|
+
isServerErrorOrNetworkFailure(error) {
|
|
839
|
+
var _a;
|
|
840
|
+
if (typeof error === "object" && error !== null && "response" in error) {
|
|
841
|
+
const status = (_a = error.response) == null ? void 0 : _a.status;
|
|
842
|
+
if (typeof status === "number" && status >= 500) return true;
|
|
843
|
+
}
|
|
844
|
+
if (error instanceof TypeError) return true;
|
|
845
|
+
return false;
|
|
846
|
+
}
|
|
525
847
|
generateClientError(err) {
|
|
526
848
|
var _a, _b;
|
|
527
849
|
if (this.options.errorHandler) {
|
|
@@ -567,16 +889,58 @@ var HttpClient = class {
|
|
|
567
889
|
}
|
|
568
890
|
get(_0) {
|
|
569
891
|
return __async(this, arguments, function* (url, options = {}) {
|
|
892
|
+
var _a, _b;
|
|
570
893
|
const { signal, priority = "background" } = options;
|
|
571
894
|
const { endpoint, params } = this.parseUrlForHashing(url);
|
|
572
895
|
const hash = hashRequest(endpoint, params);
|
|
573
896
|
const resource = this.inferResource(url);
|
|
897
|
+
let staleEntry;
|
|
898
|
+
let staleCandidate;
|
|
574
899
|
try {
|
|
575
900
|
yield this.enforceServerCooldown(url, signal);
|
|
576
901
|
if (this.stores.cache) {
|
|
577
902
|
const cachedResult = yield this.stores.cache.get(hash);
|
|
578
903
|
if (cachedResult !== void 0) {
|
|
579
|
-
|
|
904
|
+
if (this.options.respectCacheHeaders && isCacheEntry(cachedResult)) {
|
|
905
|
+
const entry = cachedResult;
|
|
906
|
+
const status = getFreshnessStatus(entry.metadata);
|
|
907
|
+
switch (status) {
|
|
908
|
+
case "fresh":
|
|
909
|
+
return entry.value;
|
|
910
|
+
case "no-cache":
|
|
911
|
+
if ((_a = this.options.cacheHeaderOverrides) == null ? void 0 : _a.ignoreNoCache) {
|
|
912
|
+
return entry.value;
|
|
913
|
+
}
|
|
914
|
+
staleEntry = entry;
|
|
915
|
+
break;
|
|
916
|
+
case "must-revalidate":
|
|
917
|
+
staleEntry = entry;
|
|
918
|
+
break;
|
|
919
|
+
case "stale-while-revalidate": {
|
|
920
|
+
const revalidation = this.backgroundRevalidate(
|
|
921
|
+
url,
|
|
922
|
+
hash,
|
|
923
|
+
entry
|
|
924
|
+
);
|
|
925
|
+
this.pendingRevalidations.push(revalidation);
|
|
926
|
+
revalidation.finally(() => {
|
|
927
|
+
this.pendingRevalidations = this.pendingRevalidations.filter(
|
|
928
|
+
(p) => p !== revalidation
|
|
929
|
+
);
|
|
930
|
+
});
|
|
931
|
+
return entry.value;
|
|
932
|
+
}
|
|
933
|
+
case "stale-if-error":
|
|
934
|
+
staleCandidate = entry;
|
|
935
|
+
staleEntry = entry;
|
|
936
|
+
break;
|
|
937
|
+
case "stale":
|
|
938
|
+
staleEntry = entry;
|
|
939
|
+
break;
|
|
940
|
+
}
|
|
941
|
+
} else if (!this.options.respectCacheHeaders) {
|
|
942
|
+
return cachedResult;
|
|
943
|
+
}
|
|
580
944
|
}
|
|
581
945
|
}
|
|
582
946
|
if (this.stores.dedupe) {
|
|
@@ -604,8 +968,38 @@ var HttpClient = class {
|
|
|
604
968
|
signal
|
|
605
969
|
);
|
|
606
970
|
}
|
|
607
|
-
const
|
|
971
|
+
const fetchInit = { signal };
|
|
972
|
+
if (this.options.respectCacheHeaders && staleEntry) {
|
|
973
|
+
const conditionalHeaders = new Headers();
|
|
974
|
+
if (staleEntry.metadata.etag) {
|
|
975
|
+
conditionalHeaders.set("If-None-Match", staleEntry.metadata.etag);
|
|
976
|
+
}
|
|
977
|
+
if (staleEntry.metadata.lastModified) {
|
|
978
|
+
conditionalHeaders.set(
|
|
979
|
+
"If-Modified-Since",
|
|
980
|
+
staleEntry.metadata.lastModified
|
|
981
|
+
);
|
|
982
|
+
}
|
|
983
|
+
if ([...conditionalHeaders].length > 0) {
|
|
984
|
+
fetchInit.headers = conditionalHeaders;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
const response = yield fetch(url, fetchInit);
|
|
608
988
|
this.applyServerRateLimitHints(url, response.headers, response.status);
|
|
989
|
+
if (this.options.respectCacheHeaders && response.status === 304 && staleEntry) {
|
|
990
|
+
const refreshed = refreshCacheEntry(staleEntry, response.headers);
|
|
991
|
+
const ttl = this.clampTTL(
|
|
992
|
+
calculateStoreTTL(refreshed.metadata, this.options.defaultCacheTTL)
|
|
993
|
+
);
|
|
994
|
+
if (this.stores.cache) {
|
|
995
|
+
yield this.stores.cache.set(hash, refreshed, ttl);
|
|
996
|
+
}
|
|
997
|
+
const result2 = refreshed.value;
|
|
998
|
+
if (this.stores.dedupe) {
|
|
999
|
+
yield this.stores.dedupe.complete(hash, result2);
|
|
1000
|
+
}
|
|
1001
|
+
return result2;
|
|
1002
|
+
}
|
|
609
1003
|
const parsedBody = yield this.parseResponseBody(response);
|
|
610
1004
|
if (!response.ok) {
|
|
611
1005
|
const error = {
|
|
@@ -631,13 +1025,40 @@ var HttpClient = class {
|
|
|
631
1025
|
yield rateLimit.record(resource, priority);
|
|
632
1026
|
}
|
|
633
1027
|
if (this.stores.cache) {
|
|
634
|
-
|
|
1028
|
+
if (this.options.respectCacheHeaders) {
|
|
1029
|
+
const cc = parseCacheControl(response.headers.get("cache-control"));
|
|
1030
|
+
const shouldStore = !cc.noStore || ((_b = this.options.cacheHeaderOverrides) == null ? void 0 : _b.ignoreNoStore);
|
|
1031
|
+
if (shouldStore) {
|
|
1032
|
+
const entry = createCacheEntry(
|
|
1033
|
+
result,
|
|
1034
|
+
response.headers,
|
|
1035
|
+
response.status
|
|
1036
|
+
);
|
|
1037
|
+
const ttl = this.clampTTL(
|
|
1038
|
+
calculateStoreTTL(entry.metadata, this.options.defaultCacheTTL)
|
|
1039
|
+
);
|
|
1040
|
+
yield this.stores.cache.set(hash, entry, ttl);
|
|
1041
|
+
}
|
|
1042
|
+
} else {
|
|
1043
|
+
yield this.stores.cache.set(
|
|
1044
|
+
hash,
|
|
1045
|
+
result,
|
|
1046
|
+
this.options.defaultCacheTTL
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
635
1049
|
}
|
|
636
1050
|
if (this.stores.dedupe) {
|
|
637
1051
|
yield this.stores.dedupe.complete(hash, result);
|
|
638
1052
|
}
|
|
639
1053
|
return result;
|
|
640
1054
|
} catch (error) {
|
|
1055
|
+
if (this.options.respectCacheHeaders && staleCandidate && this.isServerErrorOrNetworkFailure(error)) {
|
|
1056
|
+
const result = staleCandidate.value;
|
|
1057
|
+
if (this.stores.dedupe) {
|
|
1058
|
+
yield this.stores.dedupe.complete(hash, result);
|
|
1059
|
+
}
|
|
1060
|
+
return result;
|
|
1061
|
+
}
|
|
641
1062
|
if (this.stores.dedupe) {
|
|
642
1063
|
yield this.stores.dedupe.fail(hash, error);
|
|
643
1064
|
}
|
|
@@ -658,6 +1079,18 @@ exports.AdaptiveConfigSchema = AdaptiveConfigSchema;
|
|
|
658
1079
|
exports.DEFAULT_RATE_LIMIT = DEFAULT_RATE_LIMIT;
|
|
659
1080
|
exports.HttpClient = HttpClient;
|
|
660
1081
|
exports.HttpClientError = HttpClientError;
|
|
1082
|
+
exports.calculateCurrentAge = calculateCurrentAge;
|
|
1083
|
+
exports.calculateFreshnessLifetime = calculateFreshnessLifetime;
|
|
1084
|
+
exports.calculateStoreTTL = calculateStoreTTL;
|
|
1085
|
+
exports.captureVaryValues = captureVaryValues;
|
|
1086
|
+
exports.createCacheEntry = createCacheEntry;
|
|
1087
|
+
exports.getFreshnessStatus = getFreshnessStatus;
|
|
661
1088
|
exports.hashRequest = hashRequest;
|
|
1089
|
+
exports.isCacheEntry = isCacheEntry;
|
|
1090
|
+
exports.parseCacheControl = parseCacheControl;
|
|
1091
|
+
exports.parseHttpDate = parseHttpDate;
|
|
1092
|
+
exports.parseVaryHeader = parseVaryHeader;
|
|
1093
|
+
exports.refreshCacheEntry = refreshCacheEntry;
|
|
1094
|
+
exports.varyMatches = varyMatches;
|
|
662
1095
|
//# sourceMappingURL=index.cjs.map
|
|
663
1096
|
//# sourceMappingURL=index.cjs.map
|