@usethrottle/catalog 1.3.0 → 1.4.1

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 +22 -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-bG1XHquN.d.cts} +1 -1
  8. package/dist/{catalog-error-CSoPS0-d.d.ts → catalog-error-c3BVgU6k.d.ts} +1 -1
  9. package/dist/{chunk-2YQNMBDI.cjs → chunk-2ODFFMZ4.cjs} +274 -38
  10. package/dist/chunk-5AJS3CX5.cjs +192 -0
  11. package/dist/{chunk-CADIHOIR.js → chunk-6K7H7HBD.js} +89 -2
  12. package/dist/chunk-P3ABGAF5.js +192 -0
  13. package/dist/{chunk-3DLH56OX.js → chunk-STNQNHNZ.js} +258 -22
  14. package/dist/{chunk-GU63WFXI.cjs → chunk-VOOX3WJY.cjs} +102 -15
  15. package/dist/{client-6j1Iz-iJ.d.cts → client-CGGlbacq.d.cts} +1 -1
  16. package/dist/{client-B3mL2-xJ.d.ts → client-CNsVr2lR.d.ts} +1 -1
  17. package/dist/{contract-types-BRyonOYH.d.cts → contract-types-BJMeJSTz.d.cts} +269 -2
  18. package/dist/{contract-types-BRyonOYH.d.ts → contract-types-BJMeJSTz.d.ts} +269 -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-BtBxMbM5.d.cts} +40 -3
  40. package/dist/{types-BfveENg1.d.ts → types-dKvIfrVH.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-UWf6Pi5g.d.cts → use-query-C2rfTJd4.d.cts} +1 -1
  44. package/dist/{use-query-CR7r5tam.d.ts → use-query-DeCj1hXt.d.ts} +1 -1
  45. package/package.json +16 -2
package/dist/next.js ADDED
@@ -0,0 +1,180 @@
1
+ import {
2
+ noteMinGeneration,
3
+ scopesToTags
4
+ } from "./chunk-P3ABGAF5.js";
5
+ import {
6
+ SDK_VERSION,
7
+ TAG_MAX_CHARS
8
+ } from "./chunk-STNQNHNZ.js";
9
+ import "./chunk-PKBMQBKP.js";
10
+
11
+ // src/next/verify.ts
12
+ var HEX_SHA256 = /^[0-9a-fA-F]{64}$/u;
13
+ var TOLERANCE_SECONDS = 300;
14
+ var MAX_V1 = 8;
15
+ var MAX_HEADER_BYTES = 1024;
16
+ var encoder = new TextEncoder();
17
+ async function hmac(secret, payload) {
18
+ const key = await crypto.subtle.importKey(
19
+ "raw",
20
+ encoder.encode(secret),
21
+ { name: "HMAC", hash: "SHA-256" },
22
+ false,
23
+ ["sign"]
24
+ );
25
+ return new Uint8Array(
26
+ await crypto.subtle.sign("HMAC", key, encoder.encode(payload))
27
+ );
28
+ }
29
+ function fromHex(hex) {
30
+ return Uint8Array.from(
31
+ { length: hex.length / 2 },
32
+ (_, i) => Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16)
33
+ );
34
+ }
35
+ function constantTimeEqual(left, right) {
36
+ const length = Math.max(left.length, right.length);
37
+ let difference = left.length ^ right.length;
38
+ for (let i = 0; i < length; i += 1)
39
+ difference |= (left[i] ?? 0) ^ (right[i] ?? 0);
40
+ return difference === 0;
41
+ }
42
+ async function verifyCatalogWebhook(input) {
43
+ try {
44
+ const { secret, rawBody, signature } = input;
45
+ const now = Math.floor(Date.now() / 1e3);
46
+ if (secret.length === 0 || rawBody.length === 0 || signature.length === 0 || encoder.encode(signature).byteLength > MAX_HEADER_BYTES)
47
+ return false;
48
+ const parts = signature.split(",").map((part) => part.split("="));
49
+ if (parts.some((part) => part.length !== 2)) return false;
50
+ const timestamps = parts.filter(([k]) => k === "t").map(([, v]) => v ?? "");
51
+ const digests = parts.filter(([k]) => k === "v1").map(([, v]) => v ?? "");
52
+ if (parts.some(([k]) => k !== "t" && k !== "v1") || timestamps.length !== 1 || digests.length === 0 || digests.length > MAX_V1 || digests.some((d) => !HEX_SHA256.test(d)))
53
+ return false;
54
+ const timestampText = timestamps[0] ?? "";
55
+ if (!/^(?:0|[1-9][0-9]*)$/u.test(timestampText)) return false;
56
+ const timestamp = Number(timestampText);
57
+ if (!Number.isSafeInteger(timestamp) || Math.abs(now - timestamp) > TOLERANCE_SECONDS)
58
+ return false;
59
+ const expected = await hmac(secret, `${timestampText}.${rawBody}`);
60
+ let matched = false;
61
+ for (const digest of digests)
62
+ matched = constantTimeEqual(expected, fromHex(digest)) || matched;
63
+ return matched;
64
+ } catch {
65
+ return false;
66
+ }
67
+ }
68
+
69
+ // src/next/revalidate.ts
70
+ var SIGNATURE_HEADER = "x-catalog-signature";
71
+ var MAX_BODY_BYTES = 64 * 1024;
72
+ async function readCapped(req) {
73
+ const declared = Number(req.headers.get("content-length"));
74
+ if (Number.isFinite(declared) && declared > MAX_BODY_BYTES) return null;
75
+ if (req.body === null) return "";
76
+ const reader = req.body.getReader();
77
+ const decoder = new TextDecoder();
78
+ let text = "";
79
+ let size = 0;
80
+ for (; ; ) {
81
+ const { done, value } = await reader.read();
82
+ if (done) return text + decoder.decode();
83
+ size += value.byteLength;
84
+ if (size > MAX_BODY_BYTES) {
85
+ await reader.cancel();
86
+ return null;
87
+ }
88
+ text += decoder.decode(value, { stream: true });
89
+ }
90
+ }
91
+ var json = (status, body) => new Response(JSON.stringify(body), {
92
+ status,
93
+ headers: {
94
+ "content-type": "application/json",
95
+ "cache-control": "no-store"
96
+ }
97
+ });
98
+ function parseNotice(rawBody) {
99
+ let value;
100
+ try {
101
+ value = JSON.parse(rawBody);
102
+ } catch {
103
+ return null;
104
+ }
105
+ if (typeof value !== "object" || value === null) return null;
106
+ const event = value;
107
+ const { generation, scopes } = event;
108
+ if (event.type !== "storefront.changed" || typeof generation !== "number" || !Number.isSafeInteger(generation) || generation < 0 || !Array.isArray(scopes) || scopes.length === 0 || !scopes.every(
109
+ (scope) => typeof scope === "string" && scope.length > 0 && scope.length <= TAG_MAX_CHARS
110
+ ) || event.test !== void 0 && typeof event.test !== "boolean")
111
+ return null;
112
+ return {
113
+ generation,
114
+ scopes,
115
+ test: event.test === true
116
+ };
117
+ }
118
+ async function defaultRevalidateTag() {
119
+ const { revalidateTag } = await import("next/cache");
120
+ return (tag) => revalidateTag(tag, { expire: 0 });
121
+ }
122
+ function createRevalidateHandler(options) {
123
+ return async (req) => {
124
+ const list = typeof options.secret === "string" || options.secret === void 0 ? [options.secret] : options.secret;
125
+ const secrets = list.filter(
126
+ (secret) => typeof secret === "string" && secret !== ""
127
+ );
128
+ if (secrets.length === 0)
129
+ return json(500, {
130
+ error: "createRevalidateHandler has no signing secret: set the webhook secret (for example THROTTLE_WEBHOOK_SECRET) in this environment."
131
+ });
132
+ const rawBody = await readCapped(req);
133
+ if (rawBody === null) return json(413, { error: "body too large" });
134
+ const signature = req.headers.get(SIGNATURE_HEADER) ?? "";
135
+ let verified = false;
136
+ for (const secret of secrets)
137
+ verified = await verifyCatalogWebhook({ rawBody, signature, secret }) || verified;
138
+ if (!verified) return json(401, { error: "invalid signature" });
139
+ const notice = parseNotice(rawBody);
140
+ if (notice === null) return json(400, { error: "invalid body" });
141
+ if (notice.test) return json(200, { revalidated: [] });
142
+ const tags = scopesToTags(notice.scopes);
143
+ const revalidate = options.revalidateTag ?? await defaultRevalidateTag();
144
+ for (const tag of tags) revalidate(tag);
145
+ noteMinGeneration(notice.generation, options.floorKey);
146
+ return json(200, { revalidated: tags });
147
+ };
148
+ }
149
+
150
+ // src/next/preview.ts
151
+ var PREVIEW_COOKIE = "throttle_preview";
152
+ var PREVIEW_PARAM = "throttle_preview";
153
+ var PREVIEW_MAX_AGE_S = 3600;
154
+ function previewMiddleware(req) {
155
+ const url = new URL(req.url);
156
+ const token = url.searchParams.get(PREVIEW_PARAM);
157
+ if (token === null || token === "") return null;
158
+ url.searchParams.delete(PREVIEW_PARAM);
159
+ return new Response(null, {
160
+ status: 307,
161
+ headers: {
162
+ location: url.toString(),
163
+ "set-cookie": `${PREVIEW_COOKIE}=${encodeURIComponent(token)}; Max-Age=${PREVIEW_MAX_AGE_S}; Path=/; HttpOnly; Secure; SameSite=None`,
164
+ "cache-control": "no-store"
165
+ }
166
+ });
167
+ }
168
+ async function getPreviewToken() {
169
+ const { cookies } = await import("next/headers");
170
+ const store = await cookies();
171
+ const value = store.get(PREVIEW_COOKIE)?.value;
172
+ return value === void 0 || value === "" ? void 0 : value;
173
+ }
174
+ export {
175
+ SDK_VERSION,
176
+ createRevalidateHandler,
177
+ getPreviewToken,
178
+ noteMinGeneration,
179
+ previewMiddleware
180
+ };
@@ -3,14 +3,14 @@
3
3
 
4
4
 
5
5
 
6
- var _chunkGU63WFXIcjs = require('./chunk-GU63WFXI.cjs');
7
- require('./chunk-2YQNMBDI.cjs');
6
+ var _chunkVOOX3WJYcjs = require('./chunk-VOOX3WJY.cjs');
7
+ require('./chunk-2ODFFMZ4.cjs');
8
8
  require('./chunk-ETV4XYOV.cjs');
9
9
 
10
10
  // src/react/extras/index.ts
11
11
  function useTags(options = {}) {
12
- const extra = _chunkGU63WFXIcjs.defined.call(void 0, { q: options.q, limit: options.limit });
13
- return _chunkGU63WFXIcjs.useQuery.call(void 0,
12
+ const extra = _chunkVOOX3WJYcjs.defined.call(void 0, { q: options.q, limit: options.limit });
13
+ return _chunkVOOX3WJYcjs.useQuery.call(void 0,
14
14
  "tags",
15
15
  extra,
16
16
  (client, call) => client.tags.list({ ...call, ...extra }),
@@ -18,8 +18,8 @@ function useTags(options = {}) {
18
18
  );
19
19
  }
20
20
  function useCollections(options = {}) {
21
- const extra = _chunkGU63WFXIcjs.defined.call(void 0, { limit: options.limit, cursor: options.cursor });
22
- return _chunkGU63WFXIcjs.useQuery.call(void 0,
21
+ const extra = _chunkVOOX3WJYcjs.defined.call(void 0, { limit: options.limit, cursor: options.cursor });
22
+ return _chunkVOOX3WJYcjs.useQuery.call(void 0,
23
23
  "collections",
24
24
  extra,
25
25
  (client, call) => client.collections.list({ ...call, ...extra }),
@@ -27,16 +27,34 @@ function useCollections(options = {}) {
27
27
  );
28
28
  }
29
29
  function useReviewEligibility(handle, options = {}) {
30
- const customerKey = _chunkGU63WFXIcjs.useCustomerKey.call(void 0, );
31
- return _chunkGU63WFXIcjs.useQuery.call(void 0,
30
+ const customerKey = _chunkVOOX3WJYcjs.useCustomerKey.call(void 0, );
31
+ return _chunkVOOX3WJYcjs.useQuery.call(void 0,
32
32
  "review-eligibility",
33
33
  { handle },
34
34
  handle === null || customerKey === null ? null : (client, call) => client.reviews.eligibility(handle, call),
35
35
  { ...options, staleTimeMs: _nullishCoalesce(options.staleTimeMs, () => ( 0)) }
36
36
  );
37
37
  }
38
+ function usePages(options) {
39
+ return _chunkVOOX3WJYcjs.useQuery.call(void 0,
40
+ "pages",
41
+ null,
42
+ (client, call) => client.pages.list(call),
43
+ options
44
+ );
45
+ }
46
+ function usePage(handle, options) {
47
+ return _chunkVOOX3WJYcjs.useQuery.call(void 0,
48
+ "page",
49
+ { handle },
50
+ handle === null ? null : (client, call) => client.pages.get(handle, call),
51
+ options
52
+ );
53
+ }
54
+
55
+
38
56
 
39
57
 
40
58
 
41
59
 
42
- exports.useCollections = useCollections; exports.useReviewEligibility = useReviewEligibility; exports.useTags = useTags;
60
+ exports.useCollections = useCollections; exports.usePage = usePage; exports.usePages = usePages; exports.useReviewEligibility = useReviewEligibility; exports.useTags = useTags;
@@ -1,9 +1,9 @@
1
1
  import { CatalogClient } from './index.cjs';
2
2
  import { QueryOptions, QueryResult } from './react.cjs';
3
- import './types-D0PywKnN.cjs';
3
+ import './types-BtBxMbM5.cjs';
4
4
  import '@usethrottle/money';
5
- import './contract-types-BRyonOYH.cjs';
6
- import './catalog-error-BOaSKgY3.cjs';
5
+ import './contract-types-BJMeJSTz.cjs';
6
+ import './catalog-error-bG1XHquN.cjs';
7
7
 
8
8
  type Answer<F extends (...args: never[]) => unknown> = Awaited<ReturnType<F>>;
9
9
  declare function useTags(options?: QueryOptions<Answer<CatalogClient['tags']['list']>> & {
@@ -19,5 +19,7 @@ declare function useCollections(options?: QueryOptions<Answer<CatalogClient['col
19
19
  * signed out; asks on every mount (`staleTimeMs` 0).
20
20
  */
21
21
  declare function useReviewEligibility(handle: string | null, options?: QueryOptions<Answer<CatalogClient['reviews']['eligibility']>>): QueryResult<Answer<CatalogClient['reviews']['eligibility']>>;
22
+ declare function usePages(options?: QueryOptions<Answer<CatalogClient['pages']['list']>>): QueryResult<Answer<CatalogClient['pages']['list']>>;
23
+ declare function usePage(handle: string | null, options?: QueryOptions<Answer<CatalogClient['pages']['get']>>): QueryResult<Answer<CatalogClient['pages']['get']>>;
22
24
 
23
- export { useCollections, useReviewEligibility, useTags };
25
+ export { useCollections, usePage, usePages, useReviewEligibility, useTags };
@@ -1,9 +1,9 @@
1
1
  import { CatalogClient } from './index.js';
2
2
  import { QueryOptions, QueryResult } from './react.js';
3
- import './types-BfveENg1.js';
3
+ import './types-dKvIfrVH.js';
4
4
  import '@usethrottle/money';
5
- import './contract-types-BRyonOYH.js';
6
- import './catalog-error-CSoPS0-d.js';
5
+ import './contract-types-BJMeJSTz.js';
6
+ import './catalog-error-c3BVgU6k.js';
7
7
 
8
8
  type Answer<F extends (...args: never[]) => unknown> = Awaited<ReturnType<F>>;
9
9
  declare function useTags(options?: QueryOptions<Answer<CatalogClient['tags']['list']>> & {
@@ -19,5 +19,7 @@ declare function useCollections(options?: QueryOptions<Answer<CatalogClient['col
19
19
  * signed out; asks on every mount (`staleTimeMs` 0).
20
20
  */
21
21
  declare function useReviewEligibility(handle: string | null, options?: QueryOptions<Answer<CatalogClient['reviews']['eligibility']>>): QueryResult<Answer<CatalogClient['reviews']['eligibility']>>;
22
+ declare function usePages(options?: QueryOptions<Answer<CatalogClient['pages']['list']>>): QueryResult<Answer<CatalogClient['pages']['list']>>;
23
+ declare function usePage(handle: string | null, options?: QueryOptions<Answer<CatalogClient['pages']['get']>>): QueryResult<Answer<CatalogClient['pages']['get']>>;
22
24
 
23
- export { useCollections, useReviewEligibility, useTags };
25
+ export { useCollections, usePage, usePages, useReviewEligibility, useTags };
@@ -3,8 +3,8 @@ import {
3
3
  defined,
4
4
  useCustomerKey,
5
5
  useQuery
6
- } from "./chunk-CADIHOIR.js";
7
- import "./chunk-3DLH56OX.js";
6
+ } from "./chunk-6K7H7HBD.js";
7
+ import "./chunk-STNQNHNZ.js";
8
8
  import "./chunk-PKBMQBKP.js";
9
9
 
10
10
  // src/react/extras/index.ts
@@ -35,8 +35,26 @@ function useReviewEligibility(handle, options = {}) {
35
35
  { ...options, staleTimeMs: options.staleTimeMs ?? 0 }
36
36
  );
37
37
  }
38
+ function usePages(options) {
39
+ return useQuery(
40
+ "pages",
41
+ null,
42
+ (client, call) => client.pages.list(call),
43
+ options
44
+ );
45
+ }
46
+ function usePage(handle, options) {
47
+ return useQuery(
48
+ "page",
49
+ { handle },
50
+ handle === null ? null : (client, call) => client.pages.get(handle, call),
51
+ options
52
+ );
53
+ }
38
54
  export {
39
55
  useCollections,
56
+ usePage,
57
+ usePages,
40
58
  useReviewEligibility,
41
59
  useTags
42
60
  };
package/dist/react.cjs CHANGED
@@ -15,15 +15,17 @@
15
15
 
16
16
 
17
17
 
18
- var _chunkGU63WFXIcjs = require('./chunk-GU63WFXI.cjs');
19
18
 
20
19
 
20
+ var _chunkVOOX3WJYcjs = require('./chunk-VOOX3WJY.cjs');
21
21
 
22
22
 
23
23
 
24
24
 
25
25
 
26
- var _chunk2YQNMBDIcjs = require('./chunk-2YQNMBDI.cjs');
26
+
27
+
28
+ var _chunk2ODFFMZ4cjs = require('./chunk-2ODFFMZ4.cjs');
27
29
  require('./chunk-ETV4XYOV.cjs');
28
30
 
29
31
  // src/react/use-cart.ts
@@ -40,7 +42,7 @@ function newKey() {
40
42
  return out;
41
43
  }
42
44
  function refuse(message, path) {
43
- return new (0, _chunk2YQNMBDIcjs.CatalogError)({
45
+ return new (0, _chunk2ODFFMZ4cjs.CatalogError)({
44
46
  code: "INVALID_REQUEST",
45
47
  message,
46
48
  statusCode: 0,
@@ -53,7 +55,7 @@ var UNSAFE = /[\x00-\x20\x7f]/u;
53
55
  function isHttpUrl(value) {
54
56
  return typeof value === "string" && /^https?:\/\/[^/?#\\]/iu.test(value) && !UNSAFE.test(value);
55
57
  }
56
- var wrap = (error) => _chunk2YQNMBDIcjs.isCatalogError.call(void 0, error) ? error : new (0, _chunk2YQNMBDIcjs.CatalogError)({
58
+ var wrap = (error) => _chunk2ODFFMZ4cjs.isCatalogError.call(void 0, error) ? error : new (0, _chunk2ODFFMZ4cjs.CatalogError)({
57
59
  code: "CLIENT_ERROR",
58
60
  message: "The action failed.",
59
61
  statusCode: 0,
@@ -64,7 +66,7 @@ var wrap = (error) => _chunk2YQNMBDIcjs.isCatalogError.call(void 0, error) ? err
64
66
  function checkPause(store) {
65
67
  const until = store.pausedUntil();
66
68
  if (until > 0) {
67
- throw new (0, _chunk2YQNMBDIcjs.RateLimitedError)({
69
+ throw new (0, _chunk2ODFFMZ4cjs.RateLimitedError)({
68
70
  code: "RATE_LIMITED",
69
71
  message: "Too many requests.",
70
72
  statusCode: 429,
@@ -74,7 +76,7 @@ function checkPause(store) {
74
76
  }
75
77
  }
76
78
  function notePause(store, error) {
77
- if (error instanceof _chunk2YQNMBDIcjs.RateLimitedError && error.source === "catalog" && error.code === "RATE_LIMITED")
79
+ if (error instanceof _chunk2ODFFMZ4cjs.RateLimitedError && error.source === "catalog" && error.code === "RATE_LIMITED")
78
80
  store.pause(Date.now() + error.retryAfterSeconds * 1e3, error);
79
81
  }
80
82
  function createBox(initial) {
@@ -109,7 +111,7 @@ var ENDED = /* @__PURE__ */ new Set([
109
111
  "cart_not_open",
110
112
  "cart_not_found"
111
113
  ]);
112
- var ended = (error) => _chunk2YQNMBDIcjs.isCatalogError.call(void 0, error) && ENDED.has(error.code);
114
+ var ended = (error) => _chunk2ODFFMZ4cjs.isCatalogError.call(void 0, error) && ENDED.has(error.code);
113
115
  var converted = (error) => _optionalChain([error, 'access', _2 => _2.details, 'optionalAccess', _3 => _3.reason]) === "converted";
114
116
  function itemsOf(cart) {
115
117
  const items = /* @__PURE__ */ new Map();
@@ -153,7 +155,7 @@ function createController(home) {
153
155
  throttleBaseUrl === void 0 ? void 0 : { throttleBaseUrl }
154
156
  );
155
157
  };
156
- const fallbackUrl = (id) => `${(_nullishCoalesce(ctx().throttleCheckoutUrl, () => ( _chunk2YQNMBDIcjs.THROTTLE_CHECKOUT_URL))).replace(/\/+$/u, "")}/cart/${encodeURIComponent(id)}`;
158
+ const fallbackUrl = (id) => `${(_nullishCoalesce(ctx().throttleCheckoutUrl, () => ( _chunk2ODFFMZ4cjs.THROTTLE_CHECKOUT_URL))).replace(/\/+$/u, "")}/cart/${encodeURIComponent(id)}`;
157
159
  const live = async (signal, value) => {
158
160
  const out = await value;
159
161
  if (signal.aborted) throw signal.reason;
@@ -615,7 +617,7 @@ function createController(home) {
615
617
  };
616
618
  }
617
619
  function useCart(options = {}) {
618
- const context = _chunkGU63WFXIcjs.useCatalogContext.call(void 0, );
620
+ const context = _chunkVOOX3WJYcjs.useCatalogContext.call(void 0, );
619
621
  const home = context.cart;
620
622
  const controller = _nullishCoalesce(home.current, () => ( (home.current = createController(home))));
621
623
  const state = _react.useSyncExternalStore.call(void 0,
@@ -689,7 +691,7 @@ function useCart(options = {}) {
689
691
  { signal: callSignal }
690
692
  );
691
693
  } catch (error) {
692
- const repriced = _chunk2YQNMBDIcjs.isCatalogError.call(void 0, error) && error.code === "cart_prices_changed" ? _optionalChain([error, 'access', _41 => _41.details, 'optionalAccess', _42 => _42.cart]) : void 0;
694
+ const repriced = _chunk2ODFFMZ4cjs.isCatalogError.call(void 0, error) && error.code === "cart_prices_changed" ? _optionalChain([error, 'access', _41 => _41.details, 'optionalAccess', _42 => _42.cart]) : void 0;
693
695
  if (!callSignal.aborted && typeof repriced === "object" && repriced !== null)
694
696
  controller.box.set({ cart: repriced });
695
697
  throw error;
@@ -709,7 +711,7 @@ function useCart(options = {}) {
709
711
  if (out === void 0)
710
712
  throw _nullishCoalesce(met, () => ( refuse("There is no cart to check out.")));
711
713
  if (!isHttpUrl(out.checkoutUrl))
712
- throw new (0, _chunk2YQNMBDIcjs.CatalogError)({
714
+ throw new (0, _chunk2ODFFMZ4cjs.CatalogError)({
713
715
  code: "INVALID_RESPONSE",
714
716
  message: "Throttle answered a checkout URL that is not http or https.",
715
717
  statusCode: 200,
@@ -727,7 +729,7 @@ function useCart(options = {}) {
727
729
  const owner = controller.owner();
728
730
  const view = owner !== void 0 && owner !== null && owner !== customerKey ? { ...EMPTY, pending: state.pending } : state;
729
731
  const shownCurrency = _nullishCoalesce(_nullishCoalesce(_optionalChain([view, 'access', _43 => _43.cart, 'optionalAccess', _44 => _44.currency]), () => ( controller.wanted(options))), () => ( ""));
730
- const format = (minor) => _chunk2YQNMBDIcjs.formatMoney.call(void 0, minor, shownCurrency);
732
+ const format = (minor) => _chunk2ODFFMZ4cjs.formatMoney.call(void 0, minor, shownCurrency);
731
733
  return { ...view, ...actions, format };
732
734
  }
733
735
 
@@ -739,7 +741,7 @@ var IDLE = {
739
741
  result: void 0
740
742
  };
741
743
  function useSubmitReview(handle) {
742
- const { client, store, customerKey } = _chunkGU63WFXIcjs.useCatalogContext.call(void 0, );
744
+ const { client, store, customerKey } = _chunkVOOX3WJYcjs.useCatalogContext.call(void 0, );
743
745
  const scope = _react.useMemo.call(void 0,
744
746
  () => ({
745
747
  box: createBox(IDLE),
@@ -810,4 +812,6 @@ function useSubmitReview(handle) {
810
812
 
811
813
 
812
814
 
813
- exports.CatalogProvider = _chunkGU63WFXIcjs.CatalogProvider; exports.SDK_VERSION = _chunk2YQNMBDIcjs.SDK_VERSION; exports.catalogQueryKey = _chunkGU63WFXIcjs.catalogQueryKey; exports.createMemoryCartStorage = _chunkGU63WFXIcjs.createMemoryCartStorage; exports.useCart = useCart; exports.useCatalog = _chunkGU63WFXIcjs.useCatalog; exports.useCategories = _chunkGU63WFXIcjs.useCategories; exports.useCollection = _chunkGU63WFXIcjs.useCollection; exports.useConfig = _chunkGU63WFXIcjs.useConfig; exports.useCustomerKey = _chunkGU63WFXIcjs.useCustomerKey; exports.useFilterValues = _chunkGU63WFXIcjs.useFilterValues; exports.useProduct = _chunkGU63WFXIcjs.useProduct; exports.useProducts = _chunkGU63WFXIcjs.useProducts; exports.useRelated = _chunkGU63WFXIcjs.useRelated; exports.useReviews = _chunkGU63WFXIcjs.useReviews; exports.useSubmitReview = useSubmitReview; exports.useSuggest = _chunkGU63WFXIcjs.useSuggest;
815
+
816
+
817
+ exports.CatalogProvider = _chunkVOOX3WJYcjs.CatalogProvider; exports.SDK_VERSION = _chunk2ODFFMZ4cjs.SDK_VERSION; exports.catalogQueryKey = _chunkVOOX3WJYcjs.catalogQueryKey; exports.createMemoryCartStorage = _chunkVOOX3WJYcjs.createMemoryCartStorage; exports.useCart = useCart; exports.useCatalog = _chunkVOOX3WJYcjs.useCatalog; exports.useCategories = _chunkVOOX3WJYcjs.useCategories; exports.useCollection = _chunkVOOX3WJYcjs.useCollection; exports.useConfig = _chunkVOOX3WJYcjs.useConfig; exports.useCustomerKey = _chunkVOOX3WJYcjs.useCustomerKey; exports.useFilterValues = _chunkVOOX3WJYcjs.useFilterValues; exports.useGenerationWatch = _chunkVOOX3WJYcjs.useGenerationWatch; exports.useProduct = _chunkVOOX3WJYcjs.useProduct; exports.useProducts = _chunkVOOX3WJYcjs.useProducts; exports.useRelated = _chunkVOOX3WJYcjs.useRelated; exports.useReviews = _chunkVOOX3WJYcjs.useReviews; exports.useStorefrontSettings = _chunkVOOX3WJYcjs.useStorefrontSettings; exports.useSubmitReview = useSubmitReview; exports.useSuggest = _chunkVOOX3WJYcjs.useSuggest;
package/dist/react.d.cts CHANGED
@@ -1,11 +1,11 @@
1
1
  export { ReviewEligibility } from './index.cjs';
2
2
  import { ReactNode } from 'react';
3
3
  import { CatalogClient } from './index.cjs';
4
- import { C as CustomerCartSource } from './types-D0PywKnN.cjs';
4
+ import { C as CustomerCartSource } from './types-BtBxMbM5.cjs';
5
5
  import { CategoryTreeDepth, ProductsQuery, ThrottleCart, CartWarning } from './index.cjs';
6
- export { O as OpenCartRef } from './types-D0PywKnN.cjs';
7
- import { Q as QueryOptions, a as QueryResult } from './use-query-UWf6Pi5g.cjs';
8
- export { c as catalogQueryKey } from './use-query-UWf6Pi5g.cjs';
6
+ export { O as OpenCartRef } from './types-BtBxMbM5.cjs';
7
+ import { Q as QueryOptions, a as QueryResult } from './use-query-C2rfTJd4.cjs';
8
+ export { c as catalogQueryKey } from './use-query-C2rfTJd4.cjs';
9
9
  import { CatalogError } from './admin.cjs';
10
10
  import { ReviewSubmitAnswer, ReviewSubmitInput } from './index.cjs';
11
11
  export { S as SDK_VERSION } from './version-ev3p03v2.cjs';
@@ -99,6 +99,23 @@ declare function useReviews(handle: string | null, options?: QueryOptions<Answer
99
99
  cursor?: string;
100
100
  }): QueryResult<Answer<ReviewsList>>;
101
101
  declare function useConfig(options?: QueryOptions<Answer<CatalogClient['config']>>): QueryResult<Answer<CatalogClient['config']>>;
102
+ /** The storefront settings with what versions them: `{ settings, settingsVersion, generation }`. */
103
+ declare function useStorefrontSettings(options?: QueryOptions<Answer<CatalogClient['settings']>>): QueryResult<Answer<CatalogClient['settings']>>;
104
+ /**
105
+ * Watches the storefront's generation: every `intervalMs` (60 s), while the
106
+ * page is visible, it asks `config` (conditionally, so an unchanged answer is
107
+ * a `304`), and checks at once when the page becomes visible after being
108
+ * hidden for at least the interval (or before a first answer). When the
109
+ * generation changes, the provider's cache is invalidated (hooks on screen
110
+ * keep their data and read again) and every mounted watcher's `onChange` is
111
+ * called once with the new generation. One timer and one request per
112
+ * provider however many watchers are mounted (the first one's `intervalMs`
113
+ * applies). Nothing runs on the server.
114
+ */
115
+ declare function useGenerationWatch({ intervalMs, onChange, }?: {
116
+ intervalMs?: number;
117
+ onChange?: (generation: number) => void;
118
+ }): void;
102
119
  /** The values of one filter for a selection. */
103
120
  declare function useFilterValues(key: string | null, query?: ProductsQuery, options?: QueryOptions<Answer<CatalogClient['filters']['values']>>): QueryResult<Answer<CatalogClient['filters']['values']>>;
104
121
 
@@ -184,5 +201,5 @@ type SubmitReviewResult = SubmitReviewState & {
184
201
  */
185
202
  declare function useSubmitReview(handle: string): SubmitReviewResult;
186
203
 
187
- export { CatalogProvider, CustomerCartSource, QueryOptions, QueryResult, createMemoryCartStorage, useCart, useCatalog, useCategories, useCollection, useConfig, useCustomerKey, useFilterValues, useProduct, useProducts, useRelated, useReviews, useSubmitReview, useSuggest };
204
+ export { CatalogProvider, CustomerCartSource, QueryOptions, QueryResult, createMemoryCartStorage, useCart, useCatalog, useCategories, useCollection, useConfig, useCustomerKey, useFilterValues, useGenerationWatch, useProduct, useProducts, useRelated, useReviews, useStorefrontSettings, useSubmitReview, useSuggest };
188
205
  export type { CartOptions, CartState, CartStorage, CartUrls, CatalogProviderProps, SubmitReviewResult, SubmitReviewState, UseCartResult };
package/dist/react.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  export { ReviewEligibility } from './index.js';
2
2
  import { ReactNode } from 'react';
3
3
  import { CatalogClient } from './index.js';
4
- import { C as CustomerCartSource } from './types-BfveENg1.js';
4
+ import { C as CustomerCartSource } from './types-dKvIfrVH.js';
5
5
  import { CategoryTreeDepth, ProductsQuery, ThrottleCart, CartWarning } from './index.js';
6
- export { O as OpenCartRef } from './types-BfveENg1.js';
7
- import { Q as QueryOptions, a as QueryResult } from './use-query-CR7r5tam.js';
8
- export { c as catalogQueryKey } from './use-query-CR7r5tam.js';
6
+ export { O as OpenCartRef } from './types-dKvIfrVH.js';
7
+ import { Q as QueryOptions, a as QueryResult } from './use-query-DeCj1hXt.js';
8
+ export { c as catalogQueryKey } from './use-query-DeCj1hXt.js';
9
9
  import { CatalogError } from './admin.js';
10
10
  import { ReviewSubmitAnswer, ReviewSubmitInput } from './index.js';
11
11
  export { S as SDK_VERSION } from './version-ev3p03v2.js';
@@ -99,6 +99,23 @@ declare function useReviews(handle: string | null, options?: QueryOptions<Answer
99
99
  cursor?: string;
100
100
  }): QueryResult<Answer<ReviewsList>>;
101
101
  declare function useConfig(options?: QueryOptions<Answer<CatalogClient['config']>>): QueryResult<Answer<CatalogClient['config']>>;
102
+ /** The storefront settings with what versions them: `{ settings, settingsVersion, generation }`. */
103
+ declare function useStorefrontSettings(options?: QueryOptions<Answer<CatalogClient['settings']>>): QueryResult<Answer<CatalogClient['settings']>>;
104
+ /**
105
+ * Watches the storefront's generation: every `intervalMs` (60 s), while the
106
+ * page is visible, it asks `config` (conditionally, so an unchanged answer is
107
+ * a `304`), and checks at once when the page becomes visible after being
108
+ * hidden for at least the interval (or before a first answer). When the
109
+ * generation changes, the provider's cache is invalidated (hooks on screen
110
+ * keep their data and read again) and every mounted watcher's `onChange` is
111
+ * called once with the new generation. One timer and one request per
112
+ * provider however many watchers are mounted (the first one's `intervalMs`
113
+ * applies). Nothing runs on the server.
114
+ */
115
+ declare function useGenerationWatch({ intervalMs, onChange, }?: {
116
+ intervalMs?: number;
117
+ onChange?: (generation: number) => void;
118
+ }): void;
102
119
  /** The values of one filter for a selection. */
103
120
  declare function useFilterValues(key: string | null, query?: ProductsQuery, options?: QueryOptions<Answer<CatalogClient['filters']['values']>>): QueryResult<Answer<CatalogClient['filters']['values']>>;
104
121
 
@@ -184,5 +201,5 @@ type SubmitReviewResult = SubmitReviewState & {
184
201
  */
185
202
  declare function useSubmitReview(handle: string): SubmitReviewResult;
186
203
 
187
- export { CatalogProvider, CustomerCartSource, QueryOptions, QueryResult, createMemoryCartStorage, useCart, useCatalog, useCategories, useCollection, useConfig, useCustomerKey, useFilterValues, useProduct, useProducts, useRelated, useReviews, useSubmitReview, useSuggest };
204
+ export { CatalogProvider, CustomerCartSource, QueryOptions, QueryResult, createMemoryCartStorage, useCart, useCatalog, useCategories, useCollection, useConfig, useCustomerKey, useFilterValues, useGenerationWatch, useProduct, useProducts, useRelated, useReviews, useStorefrontSettings, useSubmitReview, useSuggest };
188
205
  export type { CartOptions, CartState, CartStorage, CartUrls, CatalogProviderProps, SubmitReviewResult, SubmitReviewState, UseCartResult };
package/dist/react.js CHANGED
@@ -10,12 +10,14 @@ import {
10
10
  useConfig,
11
11
  useCustomerKey,
12
12
  useFilterValues,
13
+ useGenerationWatch,
13
14
  useProduct,
14
15
  useProducts,
15
16
  useRelated,
16
17
  useReviews,
18
+ useStorefrontSettings,
17
19
  useSuggest
18
- } from "./chunk-CADIHOIR.js";
20
+ } from "./chunk-6K7H7HBD.js";
19
21
  import {
20
22
  CatalogError,
21
23
  RateLimitedError,
@@ -23,7 +25,7 @@ import {
23
25
  THROTTLE_CHECKOUT_URL,
24
26
  formatMoney,
25
27
  isCatalogError
26
- } from "./chunk-3DLH56OX.js";
28
+ } from "./chunk-STNQNHNZ.js";
27
29
  import "./chunk-PKBMQBKP.js";
28
30
 
29
31
  // src/react/use-cart.ts
@@ -804,10 +806,12 @@ export {
804
806
  useConfig,
805
807
  useCustomerKey,
806
808
  useFilterValues,
809
+ useGenerationWatch,
807
810
  useProduct,
808
811
  useProducts,
809
812
  useRelated,
810
813
  useReviews,
814
+ useStorefrontSettings,
811
815
  useSubmitReview,
812
816
  useSuggest
813
817
  };