@usethrottle/catalog 1.3.0 → 1.4.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +105 -9
  3. package/dist/admin.cjs +16 -16
  4. package/dist/admin.d.cts +14 -2
  5. package/dist/admin.d.ts +14 -2
  6. package/dist/admin.js +1 -1
  7. package/dist/{catalog-error-BOaSKgY3.d.cts → catalog-error-BKN5hNWP.d.cts} +1 -1
  8. package/dist/{catalog-error-CSoPS0-d.d.ts → catalog-error-BnabMMEy.d.ts} +1 -1
  9. package/dist/{chunk-CADIHOIR.js → chunk-3FX2HISB.js} +89 -2
  10. package/dist/{chunk-2YQNMBDI.cjs → chunk-J2GHGQY6.cjs} +274 -38
  11. package/dist/{chunk-GU63WFXI.cjs → chunk-OKWXS3FD.cjs} +102 -15
  12. package/dist/chunk-PZ3SFYH4.cjs +192 -0
  13. package/dist/chunk-SN6STYPW.js +192 -0
  14. package/dist/{chunk-3DLH56OX.js → chunk-VJHA7J7B.js} +258 -22
  15. package/dist/{client-B3mL2-xJ.d.ts → client-B6CsU_v9.d.ts} +1 -1
  16. package/dist/{client-6j1Iz-iJ.d.cts → client-terkEl6u.d.cts} +1 -1
  17. package/dist/{contract-types-BRyonOYH.d.cts → contract-types-D2-SSPM9.d.cts} +230 -2
  18. package/dist/{contract-types-BRyonOYH.d.ts → contract-types-D2-SSPM9.d.ts} +230 -2
  19. package/dist/index.cjs +12 -2
  20. package/dist/index.d.cts +53 -7
  21. package/dist/index.d.ts +53 -7
  22. package/dist/index.js +11 -1
  23. package/dist/next.cjs +180 -0
  24. package/dist/next.d.cts +60 -0
  25. package/dist/next.d.ts +60 -0
  26. package/dist/next.js +180 -0
  27. package/dist/react-extras.cjs +27 -9
  28. package/dist/react-extras.d.cts +6 -4
  29. package/dist/react-extras.d.ts +6 -4
  30. package/dist/react-extras.js +20 -2
  31. package/dist/react.cjs +18 -14
  32. package/dist/react.d.cts +22 -5
  33. package/dist/react.d.ts +22 -5
  34. package/dist/react.js +6 -2
  35. package/dist/server.cjs +58 -66
  36. package/dist/server.d.cts +57 -10
  37. package/dist/server.d.ts +57 -10
  38. package/dist/server.js +52 -60
  39. package/dist/{types-D0PywKnN.d.cts → types-jNpOwEUw.d.cts} +40 -3
  40. package/dist/{types-BfveENg1.d.ts → types-z57bkjjB.d.ts} +40 -3
  41. package/dist/url.d.cts +1 -1
  42. package/dist/url.d.ts +1 -1
  43. package/dist/{use-query-CR7r5tam.d.ts → use-query-COqRZJnC.d.ts} +1 -1
  44. package/dist/{use-query-UWf6Pi5g.d.cts → use-query-H1xgL3_e.d.cts} +1 -1
  45. package/package.json +15 -1
@@ -6,7 +6,7 @@
6
6
 
7
7
 
8
8
 
9
- var _chunk2YQNMBDIcjs = require('./chunk-2YQNMBDI.cjs');
9
+ var _chunkJ2GHGQY6cjs = require('./chunk-J2GHGQY6.cjs');
10
10
 
11
11
  // src/react/provider.tsx
12
12
 
@@ -32,7 +32,7 @@ function createStore(client, options = {}) {
32
32
  let until = 0;
33
33
  let cause;
34
34
  let pauseTimer;
35
- const pausedError = () => new (0, _chunk2YQNMBDIcjs.RateLimitedError)({
35
+ const pausedError = () => new (0, _chunkJ2GHGQY6cjs.RateLimitedError)({
36
36
  code: _nullishCoalesce(_optionalChain([cause, 'optionalAccess', _ => _.code]), () => ( "RATE_LIMITED")),
37
37
  message: _nullishCoalesce(_optionalChain([cause, 'optionalAccess', _2 => _2.message]), () => ( "Too many requests.")),
38
38
  statusCode: 429,
@@ -54,7 +54,7 @@ function createStore(client, options = {}) {
54
54
  retries: 0
55
55
  };
56
56
  for (const [oldKey, old] of entries) {
57
- if (entries.size < _chunk2YQNMBDIcjs.CACHE_MAX_ENTRIES) break;
57
+ if (entries.size < _chunkJ2GHGQY6cjs.CACHE_MAX_ENTRIES) break;
58
58
  if (old.subs.size === 0 && old.flight === void 0) {
59
59
  clearTimeout(old.timer);
60
60
  entries.delete(oldKey);
@@ -73,7 +73,7 @@ function createStore(client, options = {}) {
73
73
  else entry.indexing = false;
74
74
  }, ms);
75
75
  };
76
- function fetchKey(key, run, { force = false, staleMs = _chunk2YQNMBDIcjs.STALE_MS } = {}) {
76
+ function fetchKey(key, run, { force = false, staleMs = _chunkJ2GHGQY6cjs.STALE_MS } = {}) {
77
77
  const entry = touch(key);
78
78
  entry.run = run;
79
79
  if (entry.promise !== void 0 && !_optionalChain([entry, 'access', _4 => _4.flight, 'optionalAccess', _5 => _5.signal, 'access', _6 => _6.aborted]))
@@ -125,7 +125,7 @@ function createStore(client, options = {}) {
125
125
  return;
126
126
  }
127
127
  if (_optionalChain([result, 'access', _7 => _7.data, 'optionalAccess', _8 => _8.indexing]) === true) {
128
- if (!entry.indexing) later(key, entry, _chunk2YQNMBDIcjs.INDEXING_REVALIDATE_MS);
128
+ if (!entry.indexing) later(key, entry, _chunkJ2GHGQY6cjs.INDEXING_REVALIDATE_MS);
129
129
  } else {
130
130
  entry.indexing = false;
131
131
  }
@@ -138,7 +138,7 @@ function createStore(client, options = {}) {
138
138
  },
139
139
  (error) => {
140
140
  if (!current()) return;
141
- const retryMs = (_nullishCoalesce(error.retryAfterSeconds, () => ( _chunk2YQNMBDIcjs.RATE_LIMIT_DEFAULT_RETRY_S))) * 1e3;
141
+ const retryMs = (_nullishCoalesce(error.retryAfterSeconds, () => ( _chunkJ2GHGQY6cjs.RATE_LIMIT_DEFAULT_RETRY_S))) * 1e3;
142
142
  if (error.statusCode === 429 && error.code === "RATE_LIMITED")
143
143
  pause(now() + retryMs, error);
144
144
  else if (error.code === "STOREFRONT_INDEXING") {
@@ -183,6 +183,21 @@ function createStore(client, options = {}) {
183
183
  }
184
184
  }
185
185
  }
186
+ function invalidate() {
187
+ for (const [key, entry] of [...entries]) {
188
+ clearTimeout(entry.timer);
189
+ clearTimeout(entry.retry);
190
+ _optionalChain([entry, 'access', _12 => _12.flight, 'optionalAccess', _13 => _13.abort, 'call', _14 => _14()]);
191
+ if (entry.subs.size === 0) {
192
+ entries.delete(key);
193
+ } else {
194
+ entry.etag = entry.flight = entry.promise = void 0;
195
+ entry.indexing = false;
196
+ entry.retries = 0;
197
+ if (entry.run) void fetchKey(key, entry.run, { force: true });
198
+ }
199
+ }
200
+ }
186
201
  return {
187
202
  subscribe(key, listener) {
188
203
  const entry = touch(key);
@@ -190,11 +205,11 @@ function createStore(client, options = {}) {
190
205
  return () => {
191
206
  entry.subs.delete(listener);
192
207
  void done.then(() => {
193
- if (entry.subs.size === 0) _optionalChain([entry, 'access', _12 => _12.flight, 'optionalAccess', _13 => _13.abort, 'call', _14 => _14()]);
208
+ if (entry.subs.size === 0) _optionalChain([entry, 'access', _15 => _15.flight, 'optionalAccess', _16 => _16.abort, 'call', _17 => _17()]);
194
209
  });
195
210
  };
196
211
  },
197
- get: (key, initial, stale) => key === null ? EMPTY : _nullishCoalesce(_optionalChain([(_nullishCoalesce(entries.get(key), () => ( (initial === void 0 ? void 0 : touch(key, initial, stale))))), 'optionalAccess', _15 => _15.snap]), () => ( EMPTY)),
212
+ get: (key, initial, stale) => key === null ? EMPTY : _nullishCoalesce(_optionalChain([(_nullishCoalesce(entries.get(key), () => ( (initial === void 0 ? void 0 : touch(key, initial, stale))))), 'optionalAccess', _18 => _18.snap]), () => ( EMPTY)),
198
213
  getServer(key, initial, stale) {
199
214
  if (key === null || initial === void 0) return EMPTY;
200
215
  const entry = _nullishCoalesce(entries.get(key), () => ( touch(key, initial, stale)));
@@ -202,6 +217,7 @@ function createStore(client, options = {}) {
202
217
  },
203
218
  fetch: fetchKey,
204
219
  clear,
220
+ invalidate,
205
221
  pause,
206
222
  pausedUntil: () => until > now() ? until : 0,
207
223
  dispose() {
@@ -249,7 +265,7 @@ function CatalogProvider(props) {
249
265
  const mounted = _react.useRef.call(void 0, null);
250
266
  const store = _react.useMemo.call(void 0, () => {
251
267
  const next = createStore(client);
252
- next.pause(_nullishCoalesce(_optionalChain([mounted, 'access', _16 => _16.current, 'optionalAccess', _17 => _17.pausedUntil, 'call', _18 => _18()]), () => ( 0)));
268
+ next.pause(_nullishCoalesce(_optionalChain([mounted, 'access', _19 => _19.current, 'optionalAccess', _20 => _20.pausedUntil, 'call', _21 => _21()]), () => ( 0)));
253
269
  return next;
254
270
  }, [client, customerKey]);
255
271
  _react.useEffect.call(void 0, () => {
@@ -281,9 +297,9 @@ function CatalogProvider(props) {
281
297
  rest.customerCart,
282
298
  rest.throttleBaseUrl,
283
299
  rest.throttleCheckoutUrl,
284
- _optionalChain([urls, 'optionalAccess', _19 => _19.returnUrl]),
285
- _optionalChain([urls, 'optionalAccess', _20 => _20.cancelUrl]),
286
- _optionalChain([urls, 'optionalAccess', _21 => _21.storefrontUrl])
300
+ _optionalChain([urls, 'optionalAccess', _22 => _22.returnUrl]),
301
+ _optionalChain([urls, 'optionalAccess', _23 => _23.cancelUrl]),
302
+ _optionalChain([urls, 'optionalAccess', _24 => _24.storefrontUrl])
287
303
  ]
288
304
  );
289
305
  cart.context = value;
@@ -417,9 +433,9 @@ function useDebounced(q, ms) {
417
433
  return _react.useSyncExternalStore.call(void 0, box.subscribe, box.get, box.get);
418
434
  }
419
435
  function useSuggest(q, options = {}) {
420
- const settled = useDebounced(q, _nullishCoalesce(options.debounceMs, () => ( _chunk2YQNMBDIcjs.SUGGEST_DEBOUNCE_MS)));
436
+ const settled = useDebounced(q, _nullishCoalesce(options.debounceMs, () => ( _chunkJ2GHGQY6cjs.SUGGEST_DEBOUNCE_MS)));
421
437
  const extra = defined({ limit: options.limit });
422
- const long = (value) => value.trim().length >= _chunk2YQNMBDIcjs.SUGGEST_MIN_CHARS;
438
+ const long = (value) => value.trim().length >= _chunkJ2GHGQY6cjs.SUGGEST_MIN_CHARS;
423
439
  return useQuery(
424
440
  "suggest",
425
441
  { q: settled, ...extra },
@@ -466,6 +482,75 @@ function useConfig(options) {
466
482
  options
467
483
  );
468
484
  }
485
+ function useStorefrontSettings(options) {
486
+ return useQuery(
487
+ "settings",
488
+ null,
489
+ (client, call) => client.settings(call),
490
+ options
491
+ );
492
+ }
493
+ var watches = /* @__PURE__ */ new WeakMap();
494
+ var watchRun = (client, call) => client.config(call);
495
+ var pageDocument = () => Reflect.get(globalThis, "document");
496
+ function watch(store, intervalMs, listener) {
497
+ let w = watches.get(store);
498
+ if (w === void 0) {
499
+ const key = `\0${catalogQueryKey("generation", null)}`;
500
+ const visible = () => pageDocument().visibilityState === "visible";
501
+ const listeners = /* @__PURE__ */ new Set();
502
+ let known;
503
+ let hiddenAt = visible() ? 0 : Date.now();
504
+ const check = async () => {
505
+ if (!visible()) return;
506
+ await store.fetch(key, watchRun, { force: true });
507
+ const data = store.get(key).data;
508
+ if (watches.get(store) !== w || _optionalChain([data, 'optionalAccess', _25 => _25.generation]) === void 0) return;
509
+ const changed = known !== void 0 && data.generation !== known;
510
+ known = data.generation;
511
+ if (changed) {
512
+ store.invalidate();
513
+ for (const { current } of [...listeners]) _optionalChain([current, 'optionalCall', _26 => _26(known)]);
514
+ }
515
+ };
516
+ const onVisibility = () => {
517
+ if (!visible()) hiddenAt = Date.now();
518
+ else {
519
+ if (known === void 0 || Date.now() - hiddenAt >= intervalMs)
520
+ void check();
521
+ hiddenAt = 0;
522
+ }
523
+ };
524
+ void check();
525
+ const timer = setInterval(() => void check(), intervalMs);
526
+ const doc = pageDocument();
527
+ doc.addEventListener("visibilitychange", onVisibility);
528
+ w = {
529
+ listeners,
530
+ stop() {
531
+ clearInterval(timer);
532
+ doc.removeEventListener("visibilitychange", onVisibility);
533
+ watches.delete(store);
534
+ }
535
+ };
536
+ watches.set(store, w);
537
+ }
538
+ const mine = w;
539
+ mine.listeners.add(listener);
540
+ return () => {
541
+ mine.listeners.delete(listener);
542
+ if (mine.listeners.size === 0) mine.stop();
543
+ };
544
+ }
545
+ function useGenerationWatch({
546
+ intervalMs = 6e4,
547
+ onChange
548
+ } = {}) {
549
+ const { store } = useCatalogContext();
550
+ const latest = _react.useRef.call(void 0, onChange);
551
+ latest.current = onChange;
552
+ _react.useEffect.call(void 0, () => watch(store, intervalMs, latest), [store, intervalMs]);
553
+ }
469
554
  function useFilterValues(key, query = {}, options) {
470
555
  return useQuery(
471
556
  "filterValues",
@@ -493,4 +578,6 @@ function useFilterValues(key, query = {}, options) {
493
578
 
494
579
 
495
580
 
496
- exports.createMemoryCartStorage = createMemoryCartStorage; exports.useCatalogContext = useCatalogContext; exports.CatalogProvider = CatalogProvider; exports.useCustomerKey = useCustomerKey; exports.useCatalog = useCatalog; exports.catalogQueryKey = catalogQueryKey; exports.useQuery = useQuery; exports.defined = defined; exports.useProducts = useProducts; exports.useProduct = useProduct; exports.useRelated = useRelated; exports.useSuggest = useSuggest; exports.useCategories = useCategories; exports.useCollection = useCollection; exports.useReviews = useReviews; exports.useConfig = useConfig; exports.useFilterValues = useFilterValues;
581
+
582
+
583
+ exports.createMemoryCartStorage = createMemoryCartStorage; exports.useCatalogContext = useCatalogContext; exports.CatalogProvider = CatalogProvider; exports.useCustomerKey = useCustomerKey; exports.useCatalog = useCatalog; exports.catalogQueryKey = catalogQueryKey; exports.useQuery = useQuery; exports.defined = defined; exports.useProducts = useProducts; exports.useProduct = useProduct; exports.useRelated = useRelated; exports.useSuggest = useSuggest; exports.useCategories = useCategories; exports.useCollection = useCollection; exports.useReviews = useReviews; exports.useConfig = useConfig; exports.useStorefrontSettings = useStorefrontSettings; exports.useGenerationWatch = useGenerationWatch; exports.useFilterValues = useFilterValues;
@@ -0,0 +1,192 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
2
+
3
+
4
+
5
+ var _chunkJ2GHGQY6cjs = require('./chunk-J2GHGQY6.cjs');
6
+
7
+ // src/server/cache-policy.ts
8
+ var UNTIL_CHANGED_REVALIDATE_S = 3600;
9
+ var DEFAULT_CACHE_POLICY = {
10
+ settings: "until-changed",
11
+ navigation: "until-changed",
12
+ pages: "until-changed",
13
+ product: "until-changed",
14
+ listings: "60s",
15
+ facets: "60s",
16
+ related: "60s",
17
+ reviews: "60s",
18
+ search: "60s"
19
+ };
20
+ function decode(segment) {
21
+ try {
22
+ return decodeURIComponent(segment);
23
+ } catch (e) {
24
+ return segment;
25
+ }
26
+ }
27
+ function storefrontRoute(path) {
28
+ const bare = _nullishCoalesce(path.split(/[?#]/u, 1)[0], () => ( ""));
29
+ const at = bare.indexOf("/v1/storefront/");
30
+ const parts = (at < 0 ? bare : bare.slice(at + 15)).replace(/^\//u, "");
31
+ const [first, segment, sub, extra] = parts.split("/");
32
+ if (extra !== void 0) return null;
33
+ if (segment === void 0) {
34
+ switch (first) {
35
+ case "config":
36
+ case "products":
37
+ case "pages":
38
+ case "categories":
39
+ case "tags":
40
+ case "collections":
41
+ case "suggest":
42
+ return { route: first };
43
+ default:
44
+ return null;
45
+ }
46
+ }
47
+ const handle = decode(segment);
48
+ if (handle === "") return null;
49
+ switch (first) {
50
+ case "filters":
51
+ return sub === "values" ? { route: "filters" } : null;
52
+ case "products":
53
+ if (sub === "related" || sub === "reviews") return { route: sub, handle };
54
+ return sub === void 0 ? { route: "product", handle } : null;
55
+ case "pages":
56
+ return sub === void 0 ? { route: "page", handle } : null;
57
+ case "categories":
58
+ return sub === void 0 ? { route: "category", handle } : null;
59
+ case "collections":
60
+ return sub === void 0 ? { route: "collection", handle } : null;
61
+ default:
62
+ return null;
63
+ }
64
+ }
65
+ var KIND_OF_ROUTE = {
66
+ config: "settings",
67
+ pages: "pages",
68
+ page: "pages",
69
+ products: "listings",
70
+ product: "product",
71
+ related: "related",
72
+ reviews: "reviews",
73
+ filters: "facets",
74
+ suggest: "search",
75
+ categories: "listings",
76
+ category: "listings",
77
+ collections: "listings",
78
+ collection: "listings",
79
+ tags: "listings"
80
+ };
81
+ function cacheKindForPath(path) {
82
+ const found = storefrontRoute(path);
83
+ return found === null ? null : _nullishCoalesce(KIND_OF_ROUTE[found.route], () => ( null));
84
+ }
85
+ var DURATION = /^(\d+)([sm])$/u;
86
+ function policyToNext(policy) {
87
+ if (policy === "no-store") return "no-store";
88
+ if (policy === "until-changed")
89
+ return { revalidate: UNTIL_CHANGED_REVALIDATE_S };
90
+ const match = typeof policy === "string" ? DURATION.exec(policy) : null;
91
+ if (match === null) {
92
+ throw new (0, _chunkJ2GHGQY6cjs.CatalogError)({
93
+ code: "INVALID_REQUEST",
94
+ message: "A cache policy is 'until-changed', 'no-store' or a duration such as '90s' or '5m'.",
95
+ statusCode: 0,
96
+ source: "client",
97
+ retryable: false
98
+ });
99
+ }
100
+ const seconds = Number(match[1]) * (match[2] === "m" ? 60 : 1);
101
+ return seconds === 0 ? "no-store" : { revalidate: seconds };
102
+ }
103
+
104
+ // src/server/min-generation.ts
105
+ var EVERY_STORE = Symbol("every-store");
106
+ var floors = /* @__PURE__ */ new Map();
107
+ function noteMinGeneration(generation, floorKey) {
108
+ if (!Number.isSafeInteger(generation) || generation < 0) return;
109
+ const key = _nullishCoalesce(floorKey, () => ( EVERY_STORE));
110
+ const known = floors.get(key);
111
+ if (known === void 0 || generation > known) floors.set(key, generation);
112
+ }
113
+ function pendingMinGeneration(floorKey) {
114
+ const own = floors.get(floorKey);
115
+ const every = floors.get(EVERY_STORE);
116
+ return own === void 0 || every !== void 0 && every > own ? every : own;
117
+ }
118
+ function observeGeneration(generation, floorKey) {
119
+ if (typeof generation !== "number") return;
120
+ for (const key of [floorKey, EVERY_STORE]) {
121
+ const floor = floors.get(key);
122
+ if (floor !== void 0 && generation >= floor) floors.delete(key);
123
+ }
124
+ }
125
+
126
+ // src/server/tags.ts
127
+ function familyOf(route) {
128
+ if (route === "config") return "config";
129
+ return route === "pages" || route === "page" ? "pages" : "data";
130
+ }
131
+ function catalogTags(route, handle) {
132
+ const tags = [_chunkJ2GHGQY6cjs.CACHE_TAG, `${_chunkJ2GHGQY6cjs.CACHE_TAG}:${familyOf(route)}`];
133
+ if (route !== familyOf(route)) tags.push(`${_chunkJ2GHGQY6cjs.CACHE_TAG}:${route}`);
134
+ if (handle !== void 0) tags.push(`${_chunkJ2GHGQY6cjs.CACHE_TAG}:${route}:${handle}`);
135
+ return tags.filter((tag) => tag.length <= _chunkJ2GHGQY6cjs.TAG_MAX_CHARS);
136
+ }
137
+ function tagsForUrl(url, storefront) {
138
+ const path = _nullishCoalesce(url.split(/[?#]/u, 1)[0], () => ( ""));
139
+ if (!path.startsWith(storefront)) return null;
140
+ const found = storefrontRoute(path.slice(storefront.length));
141
+ return found === null ? null : catalogTags(found.route, found.handle);
142
+ }
143
+ var family = (name) => `${_chunkJ2GHGQY6cjs.CACHE_TAG}:${name}`;
144
+ function tagsForScope(scope) {
145
+ const colon = scope.indexOf(":");
146
+ const name = colon < 0 ? scope : scope.slice(0, colon);
147
+ const handle = colon < 0 ? "" : scope.slice(colon + 1);
148
+ switch (scope) {
149
+ case "settings":
150
+ case "navigation":
151
+ return [family("config")];
152
+ case "filters":
153
+ return [family("config"), family("data")];
154
+ case "pages":
155
+ return [family("pages")];
156
+ case "taxonomy":
157
+ case "catalog":
158
+ return [family("data")];
159
+ }
160
+ if (handle !== "") {
161
+ if (name === "product" || name === "collection" || name === "category")
162
+ return [`${family(name)}:${handle}`, family("data")];
163
+ if (name === "reviews")
164
+ return [
165
+ `${family("reviews")}:${handle}`,
166
+ `${family("product")}:${handle}`
167
+ ];
168
+ }
169
+ return [family("data")];
170
+ }
171
+ function scopesToTags(scopes) {
172
+ const out = /* @__PURE__ */ new Set();
173
+ for (const scope of scopes) {
174
+ for (const tag of tagsForScope(scope)) {
175
+ if (tag.length <= _chunkJ2GHGQY6cjs.TAG_MAX_CHARS) out.add(tag);
176
+ }
177
+ }
178
+ return [...out];
179
+ }
180
+
181
+
182
+
183
+
184
+
185
+
186
+
187
+
188
+
189
+
190
+
191
+
192
+ exports.DEFAULT_CACHE_POLICY = DEFAULT_CACHE_POLICY; exports.storefrontRoute = storefrontRoute; exports.cacheKindForPath = cacheKindForPath; exports.policyToNext = policyToNext; exports.noteMinGeneration = noteMinGeneration; exports.pendingMinGeneration = pendingMinGeneration; exports.observeGeneration = observeGeneration; exports.catalogTags = catalogTags; exports.tagsForUrl = tagsForUrl; exports.scopesToTags = scopesToTags;
@@ -0,0 +1,192 @@
1
+ import {
2
+ CACHE_TAG,
3
+ CatalogError,
4
+ TAG_MAX_CHARS
5
+ } from "./chunk-VJHA7J7B.js";
6
+
7
+ // src/server/cache-policy.ts
8
+ var UNTIL_CHANGED_REVALIDATE_S = 3600;
9
+ var DEFAULT_CACHE_POLICY = {
10
+ settings: "until-changed",
11
+ navigation: "until-changed",
12
+ pages: "until-changed",
13
+ product: "until-changed",
14
+ listings: "60s",
15
+ facets: "60s",
16
+ related: "60s",
17
+ reviews: "60s",
18
+ search: "60s"
19
+ };
20
+ function decode(segment) {
21
+ try {
22
+ return decodeURIComponent(segment);
23
+ } catch {
24
+ return segment;
25
+ }
26
+ }
27
+ function storefrontRoute(path) {
28
+ const bare = path.split(/[?#]/u, 1)[0] ?? "";
29
+ const at = bare.indexOf("/v1/storefront/");
30
+ const parts = (at < 0 ? bare : bare.slice(at + 15)).replace(/^\//u, "");
31
+ const [first, segment, sub, extra] = parts.split("/");
32
+ if (extra !== void 0) return null;
33
+ if (segment === void 0) {
34
+ switch (first) {
35
+ case "config":
36
+ case "products":
37
+ case "pages":
38
+ case "categories":
39
+ case "tags":
40
+ case "collections":
41
+ case "suggest":
42
+ return { route: first };
43
+ default:
44
+ return null;
45
+ }
46
+ }
47
+ const handle = decode(segment);
48
+ if (handle === "") return null;
49
+ switch (first) {
50
+ case "filters":
51
+ return sub === "values" ? { route: "filters" } : null;
52
+ case "products":
53
+ if (sub === "related" || sub === "reviews") return { route: sub, handle };
54
+ return sub === void 0 ? { route: "product", handle } : null;
55
+ case "pages":
56
+ return sub === void 0 ? { route: "page", handle } : null;
57
+ case "categories":
58
+ return sub === void 0 ? { route: "category", handle } : null;
59
+ case "collections":
60
+ return sub === void 0 ? { route: "collection", handle } : null;
61
+ default:
62
+ return null;
63
+ }
64
+ }
65
+ var KIND_OF_ROUTE = {
66
+ config: "settings",
67
+ pages: "pages",
68
+ page: "pages",
69
+ products: "listings",
70
+ product: "product",
71
+ related: "related",
72
+ reviews: "reviews",
73
+ filters: "facets",
74
+ suggest: "search",
75
+ categories: "listings",
76
+ category: "listings",
77
+ collections: "listings",
78
+ collection: "listings",
79
+ tags: "listings"
80
+ };
81
+ function cacheKindForPath(path) {
82
+ const found = storefrontRoute(path);
83
+ return found === null ? null : KIND_OF_ROUTE[found.route] ?? null;
84
+ }
85
+ var DURATION = /^(\d+)([sm])$/u;
86
+ function policyToNext(policy) {
87
+ if (policy === "no-store") return "no-store";
88
+ if (policy === "until-changed")
89
+ return { revalidate: UNTIL_CHANGED_REVALIDATE_S };
90
+ const match = typeof policy === "string" ? DURATION.exec(policy) : null;
91
+ if (match === null) {
92
+ throw new CatalogError({
93
+ code: "INVALID_REQUEST",
94
+ message: "A cache policy is 'until-changed', 'no-store' or a duration such as '90s' or '5m'.",
95
+ statusCode: 0,
96
+ source: "client",
97
+ retryable: false
98
+ });
99
+ }
100
+ const seconds = Number(match[1]) * (match[2] === "m" ? 60 : 1);
101
+ return seconds === 0 ? "no-store" : { revalidate: seconds };
102
+ }
103
+
104
+ // src/server/min-generation.ts
105
+ var EVERY_STORE = Symbol("every-store");
106
+ var floors = /* @__PURE__ */ new Map();
107
+ function noteMinGeneration(generation, floorKey) {
108
+ if (!Number.isSafeInteger(generation) || generation < 0) return;
109
+ const key = floorKey ?? EVERY_STORE;
110
+ const known = floors.get(key);
111
+ if (known === void 0 || generation > known) floors.set(key, generation);
112
+ }
113
+ function pendingMinGeneration(floorKey) {
114
+ const own = floors.get(floorKey);
115
+ const every = floors.get(EVERY_STORE);
116
+ return own === void 0 || every !== void 0 && every > own ? every : own;
117
+ }
118
+ function observeGeneration(generation, floorKey) {
119
+ if (typeof generation !== "number") return;
120
+ for (const key of [floorKey, EVERY_STORE]) {
121
+ const floor = floors.get(key);
122
+ if (floor !== void 0 && generation >= floor) floors.delete(key);
123
+ }
124
+ }
125
+
126
+ // src/server/tags.ts
127
+ function familyOf(route) {
128
+ if (route === "config") return "config";
129
+ return route === "pages" || route === "page" ? "pages" : "data";
130
+ }
131
+ function catalogTags(route, handle) {
132
+ const tags = [CACHE_TAG, `${CACHE_TAG}:${familyOf(route)}`];
133
+ if (route !== familyOf(route)) tags.push(`${CACHE_TAG}:${route}`);
134
+ if (handle !== void 0) tags.push(`${CACHE_TAG}:${route}:${handle}`);
135
+ return tags.filter((tag) => tag.length <= TAG_MAX_CHARS);
136
+ }
137
+ function tagsForUrl(url, storefront) {
138
+ const path = url.split(/[?#]/u, 1)[0] ?? "";
139
+ if (!path.startsWith(storefront)) return null;
140
+ const found = storefrontRoute(path.slice(storefront.length));
141
+ return found === null ? null : catalogTags(found.route, found.handle);
142
+ }
143
+ var family = (name) => `${CACHE_TAG}:${name}`;
144
+ function tagsForScope(scope) {
145
+ const colon = scope.indexOf(":");
146
+ const name = colon < 0 ? scope : scope.slice(0, colon);
147
+ const handle = colon < 0 ? "" : scope.slice(colon + 1);
148
+ switch (scope) {
149
+ case "settings":
150
+ case "navigation":
151
+ return [family("config")];
152
+ case "filters":
153
+ return [family("config"), family("data")];
154
+ case "pages":
155
+ return [family("pages")];
156
+ case "taxonomy":
157
+ case "catalog":
158
+ return [family("data")];
159
+ }
160
+ if (handle !== "") {
161
+ if (name === "product" || name === "collection" || name === "category")
162
+ return [`${family(name)}:${handle}`, family("data")];
163
+ if (name === "reviews")
164
+ return [
165
+ `${family("reviews")}:${handle}`,
166
+ `${family("product")}:${handle}`
167
+ ];
168
+ }
169
+ return [family("data")];
170
+ }
171
+ function scopesToTags(scopes) {
172
+ const out = /* @__PURE__ */ new Set();
173
+ for (const scope of scopes) {
174
+ for (const tag of tagsForScope(scope)) {
175
+ if (tag.length <= TAG_MAX_CHARS) out.add(tag);
176
+ }
177
+ }
178
+ return [...out];
179
+ }
180
+
181
+ export {
182
+ DEFAULT_CACHE_POLICY,
183
+ storefrontRoute,
184
+ cacheKindForPath,
185
+ policyToNext,
186
+ noteMinGeneration,
187
+ pendingMinGeneration,
188
+ observeGeneration,
189
+ catalogTags,
190
+ tagsForUrl,
191
+ scopesToTags
192
+ };