@http-client-toolkit/core 0.0.1 → 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 CHANGED
@@ -1,13 +1,27 @@
1
1
  'use strict';
2
2
 
3
- var axios = require('axios');
4
3
  var zod = require('zod');
5
4
  var crypto = require('crypto');
6
5
 
7
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
-
9
- var axios__default = /*#__PURE__*/_interopDefault(axios);
10
-
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));
11
25
  var __async = (__this, __arguments, generator) => {
12
26
  return new Promise((resolve, reject) => {
13
27
  var fulfilled = (value) => {
@@ -29,6 +43,231 @@ var __async = (__this, __arguments, generator) => {
29
43
  });
30
44
  };
31
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
+
32
271
  // src/errors/http-client-error.ts
33
272
  var HttpClientError = class extends Error {
34
273
  constructor(message, statusCode) {
@@ -263,16 +502,18 @@ function wait(ms, signal) {
263
502
  var HttpClient = class {
264
503
  constructor(stores = {}, options = {}) {
265
504
  this.serverCooldowns = /* @__PURE__ */ new Map();
266
- var _a, _b, _c;
267
- this._http = axios__default.default.create();
505
+ this.pendingRevalidations = [];
506
+ var _a, _b, _c, _d;
268
507
  this.stores = stores;
269
508
  this.options = {
270
509
  defaultCacheTTL: (_a = options.defaultCacheTTL) != null ? _a : 3600,
271
510
  throwOnRateLimit: (_b = options.throwOnRateLimit) != null ? _b : true,
272
511
  maxWaitTime: (_c = options.maxWaitTime) != null ? _c : 6e4,
512
+ respectCacheHeaders: (_d = options.respectCacheHeaders) != null ? _d : false,
273
513
  responseTransformer: options.responseTransformer,
274
514
  errorHandler: options.errorHandler,
275
515
  responseHandler: options.responseHandler,
516
+ cacheHeaderOverrides: options.cacheHeaderOverrides,
276
517
  rateLimitHeaders: this.normalizeRateLimitHeaders(
277
518
  options.rateLimitHeaders
278
519
  )
@@ -361,6 +602,15 @@ var HttpClient = class {
361
602
  if (!headers) {
362
603
  return void 0;
363
604
  }
605
+ if (headers instanceof Headers) {
606
+ for (const rawName of names) {
607
+ const value = headers.get(rawName);
608
+ if (value !== null) {
609
+ return value;
610
+ }
611
+ }
612
+ return void 0;
613
+ }
364
614
  for (const rawName of names) {
365
615
  const name = rawName.toLowerCase();
366
616
  const value = (_a = headers[name]) != null ? _a : headers[rawName];
@@ -487,17 +737,24 @@ var HttpClient = class {
487
737
  return __async(this, null, function* () {
488
738
  const rateLimit = this.stores.rateLimit;
489
739
  const startedAt = Date.now();
740
+ const hasAtomicAcquire = typeof rateLimit.acquire === "function";
741
+ const canProceedNow = () => __async(this, null, function* () {
742
+ if (hasAtomicAcquire) {
743
+ return rateLimit.acquire(resource, priority);
744
+ }
745
+ return rateLimit.canProceed(resource, priority);
746
+ });
490
747
  if (this.options.throwOnRateLimit) {
491
- const canProceed = yield rateLimit.canProceed(resource, priority);
748
+ const canProceed = yield canProceedNow();
492
749
  if (!canProceed) {
493
750
  const waitTime = yield rateLimit.getWaitTime(resource, priority);
494
751
  throw new Error(
495
752
  `Rate limit exceeded for resource '${resource}'. Wait ${waitTime}ms before retrying.`
496
753
  );
497
754
  }
498
- return;
755
+ return hasAtomicAcquire;
499
756
  }
500
- while (!(yield rateLimit.canProceed(resource, priority))) {
757
+ while (!(yield canProceedNow())) {
501
758
  const suggestedWaitMs = yield rateLimit.getWaitTime(resource, priority);
502
759
  const elapsedMs = Date.now() - startedAt;
503
760
  const remainingWaitBudgetMs = this.options.maxWaitTime - elapsedMs;
@@ -509,34 +766,181 @@ var HttpClient = class {
509
766
  const waitTime = suggestedWaitMs > 0 ? Math.min(suggestedWaitMs, remainingWaitBudgetMs) : Math.min(25, remainingWaitBudgetMs);
510
767
  yield wait(waitTime, signal);
511
768
  }
769
+ return hasAtomicAcquire;
512
770
  });
513
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
+ }
514
847
  generateClientError(err) {
515
- var _a, _b, _c;
848
+ var _a, _b;
516
849
  if (this.options.errorHandler) {
517
850
  return this.options.errorHandler(err);
518
851
  }
519
852
  if (err instanceof HttpClientError) {
520
853
  return err;
521
854
  }
522
- const error = err;
523
- const statusCode = (_a = error.response) == null ? void 0 : _a.status;
524
- const errorMessage = (_c = (_b = error.response) == null ? void 0 : _b.data) == null ? void 0 : _c.message;
525
- const message = `${error.message}${errorMessage ? `, ${errorMessage}` : ""}`;
855
+ const responseError = err;
856
+ const statusCode = typeof ((_a = responseError.response) == null ? void 0 : _a.status) === "number" ? responseError.response.status : void 0;
857
+ const responseData = (_b = responseError.response) == null ? void 0 : _b.data;
858
+ const derivedResponseMessage = typeof responseData === "object" && responseData !== null ? responseData.message : void 0;
859
+ const responseMessage = typeof derivedResponseMessage === "string" ? derivedResponseMessage : void 0;
860
+ const errorMessage = err instanceof Error ? err.message : typeof err.message === "string" ? err.message : "Unknown error";
861
+ const message = `${errorMessage}${responseMessage ? `, ${responseMessage}` : ""}`;
526
862
  return new HttpClientError(message, statusCode);
527
863
  }
864
+ parseResponseBody(response) {
865
+ return __async(this, null, function* () {
866
+ var _a, _b;
867
+ if (response.status === 204 || response.status === 205) {
868
+ return { data: void 0 };
869
+ }
870
+ const rawBody = yield response.text();
871
+ if (!rawBody) {
872
+ return { data: void 0 };
873
+ }
874
+ const contentType = (_b = (_a = response.headers.get("content-type")) == null ? void 0 : _a.toLowerCase()) != null ? _b : "";
875
+ const shouldAttemptJsonParsing = contentType.includes("application/json") || contentType.includes("+json") || rawBody.trimStart().startsWith("{") || rawBody.trimStart().startsWith("[");
876
+ if (!shouldAttemptJsonParsing) {
877
+ return { data: rawBody };
878
+ }
879
+ try {
880
+ const parsed = JSON.parse(rawBody);
881
+ if (typeof parsed === "object" && parsed !== null) {
882
+ return { data: parsed };
883
+ }
884
+ return { data: parsed };
885
+ } catch (e) {
886
+ return { data: rawBody };
887
+ }
888
+ });
889
+ }
528
890
  get(_0) {
529
891
  return __async(this, arguments, function* (url, options = {}) {
892
+ var _a, _b;
530
893
  const { signal, priority = "background" } = options;
531
894
  const { endpoint, params } = this.parseUrlForHashing(url);
532
895
  const hash = hashRequest(endpoint, params);
533
896
  const resource = this.inferResource(url);
897
+ let staleEntry;
898
+ let staleCandidate;
534
899
  try {
535
900
  yield this.enforceServerCooldown(url, signal);
536
901
  if (this.stores.cache) {
537
902
  const cachedResult = yield this.stores.cache.get(hash);
538
903
  if (cachedResult !== void 0) {
539
- return cachedResult;
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
+ }
540
944
  }
541
945
  }
542
946
  if (this.stores.dedupe) {
@@ -556,16 +960,59 @@ var HttpClient = class {
556
960
  yield this.stores.dedupe.register(hash);
557
961
  }
558
962
  }
963
+ let alreadyRecordedRateLimit = false;
559
964
  if (this.stores.rateLimit) {
560
- yield this.enforceStoreRateLimit(resource, priority, signal);
965
+ alreadyRecordedRateLimit = yield this.enforceStoreRateLimit(
966
+ resource,
967
+ priority,
968
+ signal
969
+ );
561
970
  }
562
- const response = yield this._http.get(url, { signal });
563
- this.applyServerRateLimitHints(
564
- url,
565
- response.headers,
566
- response.status
567
- );
568
- let data = response.data;
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);
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
+ }
1003
+ const parsedBody = yield this.parseResponseBody(response);
1004
+ if (!response.ok) {
1005
+ const error = {
1006
+ message: `Request failed with status ${response.status}`,
1007
+ response: {
1008
+ status: response.status,
1009
+ data: parsedBody.data,
1010
+ headers: response.headers
1011
+ }
1012
+ };
1013
+ throw error;
1014
+ }
1015
+ let data = parsedBody.data;
569
1016
  if (this.options.responseTransformer && data) {
570
1017
  data = this.options.responseTransformer(data);
571
1018
  }
@@ -573,25 +1020,44 @@ var HttpClient = class {
573
1020
  data = this.options.responseHandler(data);
574
1021
  }
575
1022
  const result = data;
576
- if (this.stores.rateLimit) {
1023
+ if (this.stores.rateLimit && !alreadyRecordedRateLimit) {
577
1024
  const rateLimit = this.stores.rateLimit;
578
1025
  yield rateLimit.record(resource, priority);
579
1026
  }
580
1027
  if (this.stores.cache) {
581
- yield this.stores.cache.set(hash, result, this.options.defaultCacheTTL);
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
+ }
582
1049
  }
583
1050
  if (this.stores.dedupe) {
584
1051
  yield this.stores.dedupe.complete(hash, result);
585
1052
  }
586
1053
  return result;
587
1054
  } catch (error) {
588
- const axiosError = error;
589
- if (axiosError.response) {
590
- this.applyServerRateLimitHints(
591
- url,
592
- axiosError.response.headers,
593
- axiosError.response.status
594
- );
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;
595
1061
  }
596
1062
  if (this.stores.dedupe) {
597
1063
  yield this.stores.dedupe.fail(hash, error);
@@ -599,6 +1065,9 @@ var HttpClient = class {
599
1065
  if (error instanceof Error && error.name === "AbortError") {
600
1066
  throw error;
601
1067
  }
1068
+ if (error instanceof HttpClientError) {
1069
+ throw error;
1070
+ }
602
1071
  throw this.generateClientError(error);
603
1072
  }
604
1073
  });
@@ -610,6 +1079,18 @@ exports.AdaptiveConfigSchema = AdaptiveConfigSchema;
610
1079
  exports.DEFAULT_RATE_LIMIT = DEFAULT_RATE_LIMIT;
611
1080
  exports.HttpClient = HttpClient;
612
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;
613
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;
614
1095
  //# sourceMappingURL=index.cjs.map
615
1096
  //# sourceMappingURL=index.cjs.map