@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.js CHANGED
@@ -1,7 +1,25 @@
1
- import axios from 'axios';
2
1
  import { z } from 'zod';
3
2
  import { createHash } from 'crypto';
4
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));
5
23
  var __async = (__this, __arguments, generator) => {
6
24
  return new Promise((resolve, reject) => {
7
25
  var fulfilled = (value) => {
@@ -23,6 +41,231 @@ var __async = (__this, __arguments, generator) => {
23
41
  });
24
42
  };
25
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
+
26
269
  // src/errors/http-client-error.ts
27
270
  var HttpClientError = class extends Error {
28
271
  constructor(message, statusCode) {
@@ -257,16 +500,18 @@ function wait(ms, signal) {
257
500
  var HttpClient = class {
258
501
  constructor(stores = {}, options = {}) {
259
502
  this.serverCooldowns = /* @__PURE__ */ new Map();
260
- var _a, _b, _c;
261
- this._http = axios.create();
503
+ this.pendingRevalidations = [];
504
+ var _a, _b, _c, _d;
262
505
  this.stores = stores;
263
506
  this.options = {
264
507
  defaultCacheTTL: (_a = options.defaultCacheTTL) != null ? _a : 3600,
265
508
  throwOnRateLimit: (_b = options.throwOnRateLimit) != null ? _b : true,
266
509
  maxWaitTime: (_c = options.maxWaitTime) != null ? _c : 6e4,
510
+ respectCacheHeaders: (_d = options.respectCacheHeaders) != null ? _d : false,
267
511
  responseTransformer: options.responseTransformer,
268
512
  errorHandler: options.errorHandler,
269
513
  responseHandler: options.responseHandler,
514
+ cacheHeaderOverrides: options.cacheHeaderOverrides,
270
515
  rateLimitHeaders: this.normalizeRateLimitHeaders(
271
516
  options.rateLimitHeaders
272
517
  )
@@ -355,6 +600,15 @@ var HttpClient = class {
355
600
  if (!headers) {
356
601
  return void 0;
357
602
  }
603
+ if (headers instanceof Headers) {
604
+ for (const rawName of names) {
605
+ const value = headers.get(rawName);
606
+ if (value !== null) {
607
+ return value;
608
+ }
609
+ }
610
+ return void 0;
611
+ }
358
612
  for (const rawName of names) {
359
613
  const name = rawName.toLowerCase();
360
614
  const value = (_a = headers[name]) != null ? _a : headers[rawName];
@@ -481,17 +735,24 @@ var HttpClient = class {
481
735
  return __async(this, null, function* () {
482
736
  const rateLimit = this.stores.rateLimit;
483
737
  const startedAt = Date.now();
738
+ const hasAtomicAcquire = typeof rateLimit.acquire === "function";
739
+ const canProceedNow = () => __async(this, null, function* () {
740
+ if (hasAtomicAcquire) {
741
+ return rateLimit.acquire(resource, priority);
742
+ }
743
+ return rateLimit.canProceed(resource, priority);
744
+ });
484
745
  if (this.options.throwOnRateLimit) {
485
- const canProceed = yield rateLimit.canProceed(resource, priority);
746
+ const canProceed = yield canProceedNow();
486
747
  if (!canProceed) {
487
748
  const waitTime = yield rateLimit.getWaitTime(resource, priority);
488
749
  throw new Error(
489
750
  `Rate limit exceeded for resource '${resource}'. Wait ${waitTime}ms before retrying.`
490
751
  );
491
752
  }
492
- return;
753
+ return hasAtomicAcquire;
493
754
  }
494
- while (!(yield rateLimit.canProceed(resource, priority))) {
755
+ while (!(yield canProceedNow())) {
495
756
  const suggestedWaitMs = yield rateLimit.getWaitTime(resource, priority);
496
757
  const elapsedMs = Date.now() - startedAt;
497
758
  const remainingWaitBudgetMs = this.options.maxWaitTime - elapsedMs;
@@ -503,34 +764,181 @@ var HttpClient = class {
503
764
  const waitTime = suggestedWaitMs > 0 ? Math.min(suggestedWaitMs, remainingWaitBudgetMs) : Math.min(25, remainingWaitBudgetMs);
504
765
  yield wait(waitTime, signal);
505
766
  }
767
+ return hasAtomicAcquire;
768
+ });
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 = [];
506
778
  });
507
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
+ }
508
845
  generateClientError(err) {
509
- var _a, _b, _c;
846
+ var _a, _b;
510
847
  if (this.options.errorHandler) {
511
848
  return this.options.errorHandler(err);
512
849
  }
513
850
  if (err instanceof HttpClientError) {
514
851
  return err;
515
852
  }
516
- const error = err;
517
- const statusCode = (_a = error.response) == null ? void 0 : _a.status;
518
- const errorMessage = (_c = (_b = error.response) == null ? void 0 : _b.data) == null ? void 0 : _c.message;
519
- const message = `${error.message}${errorMessage ? `, ${errorMessage}` : ""}`;
853
+ const responseError = err;
854
+ const statusCode = typeof ((_a = responseError.response) == null ? void 0 : _a.status) === "number" ? responseError.response.status : void 0;
855
+ const responseData = (_b = responseError.response) == null ? void 0 : _b.data;
856
+ const derivedResponseMessage = typeof responseData === "object" && responseData !== null ? responseData.message : void 0;
857
+ const responseMessage = typeof derivedResponseMessage === "string" ? derivedResponseMessage : void 0;
858
+ const errorMessage = err instanceof Error ? err.message : typeof err.message === "string" ? err.message : "Unknown error";
859
+ const message = `${errorMessage}${responseMessage ? `, ${responseMessage}` : ""}`;
520
860
  return new HttpClientError(message, statusCode);
521
861
  }
862
+ parseResponseBody(response) {
863
+ return __async(this, null, function* () {
864
+ var _a, _b;
865
+ if (response.status === 204 || response.status === 205) {
866
+ return { data: void 0 };
867
+ }
868
+ const rawBody = yield response.text();
869
+ if (!rawBody) {
870
+ return { data: void 0 };
871
+ }
872
+ const contentType = (_b = (_a = response.headers.get("content-type")) == null ? void 0 : _a.toLowerCase()) != null ? _b : "";
873
+ const shouldAttemptJsonParsing = contentType.includes("application/json") || contentType.includes("+json") || rawBody.trimStart().startsWith("{") || rawBody.trimStart().startsWith("[");
874
+ if (!shouldAttemptJsonParsing) {
875
+ return { data: rawBody };
876
+ }
877
+ try {
878
+ const parsed = JSON.parse(rawBody);
879
+ if (typeof parsed === "object" && parsed !== null) {
880
+ return { data: parsed };
881
+ }
882
+ return { data: parsed };
883
+ } catch (e) {
884
+ return { data: rawBody };
885
+ }
886
+ });
887
+ }
522
888
  get(_0) {
523
889
  return __async(this, arguments, function* (url, options = {}) {
890
+ var _a, _b;
524
891
  const { signal, priority = "background" } = options;
525
892
  const { endpoint, params } = this.parseUrlForHashing(url);
526
893
  const hash = hashRequest(endpoint, params);
527
894
  const resource = this.inferResource(url);
895
+ let staleEntry;
896
+ let staleCandidate;
528
897
  try {
529
898
  yield this.enforceServerCooldown(url, signal);
530
899
  if (this.stores.cache) {
531
900
  const cachedResult = yield this.stores.cache.get(hash);
532
901
  if (cachedResult !== void 0) {
533
- return cachedResult;
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
+ }
534
942
  }
535
943
  }
536
944
  if (this.stores.dedupe) {
@@ -550,16 +958,59 @@ var HttpClient = class {
550
958
  yield this.stores.dedupe.register(hash);
551
959
  }
552
960
  }
961
+ let alreadyRecordedRateLimit = false;
553
962
  if (this.stores.rateLimit) {
554
- yield this.enforceStoreRateLimit(resource, priority, signal);
963
+ alreadyRecordedRateLimit = yield this.enforceStoreRateLimit(
964
+ resource,
965
+ priority,
966
+ signal
967
+ );
555
968
  }
556
- const response = yield this._http.get(url, { signal });
557
- this.applyServerRateLimitHints(
558
- url,
559
- response.headers,
560
- response.status
561
- );
562
- let data = response.data;
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);
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
+ }
1001
+ const parsedBody = yield this.parseResponseBody(response);
1002
+ if (!response.ok) {
1003
+ const error = {
1004
+ message: `Request failed with status ${response.status}`,
1005
+ response: {
1006
+ status: response.status,
1007
+ data: parsedBody.data,
1008
+ headers: response.headers
1009
+ }
1010
+ };
1011
+ throw error;
1012
+ }
1013
+ let data = parsedBody.data;
563
1014
  if (this.options.responseTransformer && data) {
564
1015
  data = this.options.responseTransformer(data);
565
1016
  }
@@ -567,25 +1018,44 @@ var HttpClient = class {
567
1018
  data = this.options.responseHandler(data);
568
1019
  }
569
1020
  const result = data;
570
- if (this.stores.rateLimit) {
1021
+ if (this.stores.rateLimit && !alreadyRecordedRateLimit) {
571
1022
  const rateLimit = this.stores.rateLimit;
572
1023
  yield rateLimit.record(resource, priority);
573
1024
  }
574
1025
  if (this.stores.cache) {
575
- yield this.stores.cache.set(hash, result, this.options.defaultCacheTTL);
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
+ }
576
1047
  }
577
1048
  if (this.stores.dedupe) {
578
1049
  yield this.stores.dedupe.complete(hash, result);
579
1050
  }
580
1051
  return result;
581
1052
  } catch (error) {
582
- const axiosError = error;
583
- if (axiosError.response) {
584
- this.applyServerRateLimitHints(
585
- url,
586
- axiosError.response.headers,
587
- axiosError.response.status
588
- );
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;
589
1059
  }
590
1060
  if (this.stores.dedupe) {
591
1061
  yield this.stores.dedupe.fail(hash, error);
@@ -593,12 +1063,15 @@ var HttpClient = class {
593
1063
  if (error instanceof Error && error.name === "AbortError") {
594
1064
  throw error;
595
1065
  }
1066
+ if (error instanceof HttpClientError) {
1067
+ throw error;
1068
+ }
596
1069
  throw this.generateClientError(error);
597
1070
  }
598
1071
  });
599
1072
  }
600
1073
  };
601
1074
 
602
- 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 };
603
1076
  //# sourceMappingURL=index.js.map
604
1077
  //# sourceMappingURL=index.js.map