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