@usethrottle/catalog 1.2.0 → 1.3.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 (39) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +7 -7
  3. package/dist/admin.cjs +16 -16
  4. package/dist/admin.d.cts +3 -2
  5. package/dist/admin.d.ts +3 -2
  6. package/dist/admin.js +1 -1
  7. package/dist/{catalog-error-yTBMAdCv.d.cts → catalog-error-BOaSKgY3.d.cts} +11 -1
  8. package/dist/{catalog-error-7DCsTyB2.d.ts → catalog-error-CSoPS0-d.d.ts} +11 -1
  9. package/dist/{chunk-B2DYAHNR.cjs → chunk-2YQNMBDI.cjs} +78 -23
  10. package/dist/{chunk-55G4ZTD5.js → chunk-3DLH56OX.js} +58 -3
  11. package/dist/{chunk-JNBKKAMP.js → chunk-CADIHOIR.js} +9 -1
  12. package/dist/{chunk-TDMDOHD3.cjs → chunk-GU63WFXI.cjs} +16 -8
  13. package/dist/{client-BrHJCyBP.d.cts → client-6j1Iz-iJ.d.cts} +1 -1
  14. package/dist/{client-Dm6I4nAZ.d.ts → client-B3mL2-xJ.d.ts} +1 -1
  15. package/dist/{contract-types-BPSzY_Co.d.ts → contract-types-BRyonOYH.d.cts} +11 -9
  16. package/dist/{contract-types-BPSzY_Co.d.cts → contract-types-BRyonOYH.d.ts} +11 -9
  17. package/dist/index.cjs +24 -2
  18. package/dist/index.d.cts +8 -5
  19. package/dist/index.d.ts +8 -5
  20. package/dist/index.js +25 -3
  21. package/dist/react-extras.cjs +8 -8
  22. package/dist/react-extras.d.cts +4 -3
  23. package/dist/react-extras.d.ts +4 -3
  24. package/dist/react-extras.js +2 -2
  25. package/dist/react.cjs +126 -54
  26. package/dist/react.d.cts +34 -10
  27. package/dist/react.d.ts +34 -10
  28. package/dist/react.js +108 -36
  29. package/dist/server.cjs +13 -13
  30. package/dist/server.d.cts +2 -1
  31. package/dist/server.d.ts +2 -1
  32. package/dist/server.js +1 -1
  33. package/dist/{types-BTrqSprj.d.cts → types-BfveENg1.d.ts} +43 -7
  34. package/dist/{types-BlYCKzB8.d.ts → types-D0PywKnN.d.cts} +43 -7
  35. package/dist/url.d.cts +2 -1
  36. package/dist/url.d.ts +2 -1
  37. package/dist/{use-query-QOEBuYlp.d.ts → use-query-CR7r5tam.d.ts} +1 -1
  38. package/dist/{use-query-BZgjxGPO.d.cts → use-query-UWf6Pi5g.d.cts} +1 -1
  39. package/package.json +3 -2
package/dist/react.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
3
3
 
4
4
 
5
5
 
@@ -15,14 +15,15 @@
15
15
 
16
16
 
17
17
 
18
- var _chunkTDMDOHD3cjs = require('./chunk-TDMDOHD3.cjs');
18
+ var _chunkGU63WFXIcjs = require('./chunk-GU63WFXI.cjs');
19
19
 
20
20
 
21
21
 
22
22
 
23
23
 
24
24
 
25
- var _chunkB2DYAHNRcjs = require('./chunk-B2DYAHNR.cjs');
25
+
26
+ var _chunk2YQNMBDIcjs = require('./chunk-2YQNMBDI.cjs');
26
27
  require('./chunk-ETV4XYOV.cjs');
27
28
 
28
29
  // src/react/use-cart.ts
@@ -39,7 +40,7 @@ function newKey() {
39
40
  return out;
40
41
  }
41
42
  function refuse(message, path) {
42
- return new (0, _chunkB2DYAHNRcjs.CatalogError)({
43
+ return new (0, _chunk2YQNMBDIcjs.CatalogError)({
43
44
  code: "INVALID_REQUEST",
44
45
  message,
45
46
  statusCode: 0,
@@ -52,7 +53,7 @@ var UNSAFE = /[\x00-\x20\x7f]/u;
52
53
  function isHttpUrl(value) {
53
54
  return typeof value === "string" && /^https?:\/\/[^/?#\\]/iu.test(value) && !UNSAFE.test(value);
54
55
  }
55
- var wrap = (error) => _chunkB2DYAHNRcjs.isCatalogError.call(void 0, error) ? error : new (0, _chunkB2DYAHNRcjs.CatalogError)({
56
+ var wrap = (error) => _chunk2YQNMBDIcjs.isCatalogError.call(void 0, error) ? error : new (0, _chunk2YQNMBDIcjs.CatalogError)({
56
57
  code: "CLIENT_ERROR",
57
58
  message: "The action failed.",
58
59
  statusCode: 0,
@@ -63,7 +64,7 @@ var wrap = (error) => _chunkB2DYAHNRcjs.isCatalogError.call(void 0, error) ? err
63
64
  function checkPause(store) {
64
65
  const until = store.pausedUntil();
65
66
  if (until > 0) {
66
- throw new (0, _chunkB2DYAHNRcjs.RateLimitedError)({
67
+ throw new (0, _chunk2YQNMBDIcjs.RateLimitedError)({
67
68
  code: "RATE_LIMITED",
68
69
  message: "Too many requests.",
69
70
  statusCode: 429,
@@ -73,7 +74,7 @@ function checkPause(store) {
73
74
  }
74
75
  }
75
76
  function notePause(store, error) {
76
- if (error instanceof _chunkB2DYAHNRcjs.RateLimitedError && error.source === "catalog" && error.code === "RATE_LIMITED")
77
+ if (error instanceof _chunk2YQNMBDIcjs.RateLimitedError && error.source === "catalog" && error.code === "RATE_LIMITED")
77
78
  store.pause(Date.now() + error.retryAfterSeconds * 1e3, error);
78
79
  }
79
80
  function createBox(initial) {
@@ -99,7 +100,8 @@ var EMPTY = {
99
100
  cartUrl: null,
100
101
  status: "idle",
101
102
  error: void 0,
102
- pending: false
103
+ pending: false,
104
+ warnings: []
103
105
  };
104
106
  var ENDED = /* @__PURE__ */ new Set([
105
107
  "cart_session_expired",
@@ -107,7 +109,7 @@ var ENDED = /* @__PURE__ */ new Set([
107
109
  "cart_not_open",
108
110
  "cart_not_found"
109
111
  ]);
110
- var ended = (error) => _chunkB2DYAHNRcjs.isCatalogError.call(void 0, error) && ENDED.has(error.code);
112
+ var ended = (error) => _chunk2YQNMBDIcjs.isCatalogError.call(void 0, error) && ENDED.has(error.code);
111
113
  var converted = (error) => _optionalChain([error, 'access', _2 => _2.details, 'optionalAccess', _3 => _3.reason]) === "converted";
112
114
  function itemsOf(cart) {
113
115
  const items = /* @__PURE__ */ new Map();
@@ -132,6 +134,7 @@ function createController(home) {
132
134
  moving: void 0,
133
135
  unremoved: false,
134
136
  reads: /* @__PURE__ */ new Map(),
137
+ slots: /* @__PURE__ */ new Set(),
135
138
  abort: new AbortController(),
136
139
  writing: Promise.resolve(),
137
140
  tail: Promise.resolve()
@@ -150,7 +153,7 @@ function createController(home) {
150
153
  throttleBaseUrl === void 0 ? void 0 : { throttleBaseUrl }
151
154
  );
152
155
  };
153
- const fallbackUrl = (id) => `${(_nullishCoalesce(ctx().throttleCheckoutUrl, () => ( _chunkB2DYAHNRcjs.THROTTLE_CHECKOUT_URL))).replace(/\/+$/u, "")}/cart/${encodeURIComponent(id)}`;
156
+ const fallbackUrl = (id) => `${(_nullishCoalesce(ctx().throttleCheckoutUrl, () => ( _chunk2YQNMBDIcjs.THROTTLE_CHECKOUT_URL))).replace(/\/+$/u, "")}/cart/${encodeURIComponent(id)}`;
154
157
  const live = async (signal, value) => {
155
158
  const out = await value;
156
159
  if (signal.aborted) throw signal.reason;
@@ -168,7 +171,7 @@ function createController(home) {
168
171
  if (signal.aborted) throw error;
169
172
  }
170
173
  }
171
- async function find(signal, wantedCurrency) {
174
+ async function find(signal, wantedCurrency, strict = false) {
172
175
  const source = ctx().customerCart;
173
176
  if (source === void 0 || wantedCurrency === void 0) return null;
174
177
  const asked = wantedCurrency.toUpperCase();
@@ -176,10 +179,28 @@ function createController(home) {
176
179
  const found = await live(signal, source.getOpenCart({ currency: asked }));
177
180
  return found !== null && found.currency.toUpperCase() === asked ? found : null;
178
181
  } catch (error) {
179
- if (signal.aborted) throw error;
182
+ if (signal.aborted || strict) throw error;
180
183
  return null;
181
184
  }
182
185
  }
186
+ async function findTarget(signal, code, storage) {
187
+ if (resuming()) {
188
+ const found = await find(signal, code, true);
189
+ return found === null ? null : { id: found.sessionId, cartUrl: fallbackUrl(found.sessionId) };
190
+ }
191
+ if (ctx().customerKey !== null || storage.getFor === void 0) return null;
192
+ const id = await live(signal, storage.getFor(code));
193
+ if (id === null || id === "") return null;
194
+ try {
195
+ const answer = await live(signal, session(id).get({ signal }));
196
+ if (answer.cart.status !== "open" || answer.cart.currency.toUpperCase() !== code)
197
+ return null;
198
+ } catch (error) {
199
+ if (signal.aborted || !ended(error)) throw error;
200
+ return null;
201
+ }
202
+ return { id, cartUrl: fallbackUrl(id) };
203
+ }
183
204
  async function findOnce(signal, storage) {
184
205
  var _a2;
185
206
  if (!resuming()) return;
@@ -197,13 +218,21 @@ function createController(home) {
197
218
  }
198
219
  if (found !== null) await soft(signal, () => adopt(signal, found, storage));
199
220
  }
221
+ async function forgetSlots(storage, codes) {
222
+ for (const code of codes)
223
+ if (storage.removeFor !== void 0) await storage.removeFor(code);
224
+ else await _optionalChain([storage, 'access', _15 => _15.setFor, 'optionalCall', _16 => _16(code, "")]);
225
+ }
226
+ async function save(signal, storage, id) {
227
+ const write = storage.set(id);
228
+ me.writing = Promise.resolve(write).catch(() => void 0);
229
+ await live(signal, write);
230
+ }
200
231
  async function adopt(signal, found, storage) {
201
232
  const id = found.sessionId;
202
233
  const answer = await live(signal, session(id).get({ signal }));
203
234
  me.reads.set(id, Date.now());
204
- const write = storage.set(id);
205
- me.writing = Promise.resolve(write).catch(() => void 0);
206
- await live(signal, write);
235
+ await save(signal, storage, id);
207
236
  box.set({
208
237
  sessionId: id,
209
238
  cartUrl: fallbackUrl(id),
@@ -229,7 +258,7 @@ function createController(home) {
229
258
  if (converted(error)) reset();
230
259
  else box.set({ sessionId: null, cartUrl: null });
231
260
  }
232
- async function create(signal, options, items, whole = false) {
261
+ async function start(signal, options, items) {
233
262
  const context = ctx();
234
263
  const body = {
235
264
  items: [...items].map(([sku, quantity]) => ({ sku, quantity }))
@@ -238,9 +267,9 @@ function createController(home) {
238
267
  if (chosen !== void 0) body.currency = chosen;
239
268
  if (options.locale !== void 0) body.locale = options.locale;
240
269
  const urls = context.cartUrls;
241
- if (_optionalChain([urls, 'optionalAccess', _15 => _15.returnUrl]) !== void 0) body.returnUrl = urls.returnUrl;
242
- if (_optionalChain([urls, 'optionalAccess', _16 => _16.cancelUrl]) !== void 0) body.cancelUrl = urls.cancelUrl;
243
- if (_optionalChain([urls, 'optionalAccess', _17 => _17.storefrontUrl]) !== void 0)
270
+ if (_optionalChain([urls, 'optionalAccess', _17 => _17.returnUrl]) !== void 0) body.returnUrl = urls.returnUrl;
271
+ if (_optionalChain([urls, 'optionalAccess', _18 => _18.cancelUrl]) !== void 0) body.cancelUrl = urls.cancelUrl;
272
+ if (_optionalChain([urls, 'optionalAccess', _19 => _19.storefrontUrl]) !== void 0)
244
273
  body.storefrontUrl = urls.storefrontUrl;
245
274
  let answer;
246
275
  try {
@@ -253,28 +282,18 @@ function createController(home) {
253
282
  throw error;
254
283
  }
255
284
  const id = answer.sessionId;
256
- const cartUrl = isHttpUrl(answer.cartUrl) ? answer.cartUrl : fallbackUrl(id);
257
- if (whole) {
258
- const snapshot = await live(signal, session(id).get({ signal }));
259
- me.reads.set(id, Date.now());
260
- const write = context.cartStorage.set(id);
261
- me.writing = Promise.resolve(write).catch(() => void 0);
262
- await live(signal, write);
263
- box.set({
264
- sessionId: id,
265
- cartUrl,
266
- cart: snapshot.cart,
267
- status: "ready",
268
- error: void 0
269
- });
270
- return;
271
- }
285
+ return {
286
+ id,
287
+ cartUrl: isHttpUrl(answer.cartUrl) ? answer.cartUrl : fallbackUrl(id)
288
+ };
289
+ }
290
+ async function create(signal, options, items) {
291
+ const storage = ctx().cartStorage;
292
+ const { id, cartUrl } = await start(signal, options, items);
272
293
  box.set({ sessionId: id, cartUrl });
273
294
  let unsaved;
274
295
  try {
275
- const write = context.cartStorage.set(id);
276
- me.writing = Promise.resolve(write).catch(() => void 0);
277
- await live(signal, write);
296
+ await save(signal, storage, id);
278
297
  } catch (error) {
279
298
  if (signal.aborted) throw error;
280
299
  unsaved = error;
@@ -432,6 +451,11 @@ function createController(home) {
432
451
  if (signal.aborted) return;
433
452
  if (fresh && !me.unremoved) await restore(signal, guest, false);
434
453
  if (signal.aborted) return;
454
+ const codes = new Set(me.slots);
455
+ const shown = _optionalChain([box, 'access', _20 => _20.get, 'call', _21 => _21(), 'access', _22 => _22.cart, 'optionalAccess', _23 => _23.currency]);
456
+ if (shown !== void 0) codes.add(shown.toUpperCase());
457
+ await live(signal, forgetSlots(guest, codes));
458
+ me.slots.clear();
435
459
  const id = box.get().sessionId;
436
460
  if (id === null) {
437
461
  if (box.get().cart === null && !me.unremoved) {
@@ -472,7 +496,7 @@ function createController(home) {
472
496
  me.abort = new AbortController();
473
497
  me.reads.clear();
474
498
  for (const key2 of _nullishCoalesce(home.shared.looked, () => ( [])))
475
- if (key2.startsWith(`${was}\0`)) _optionalChain([home, 'access', _18 => _18.shared, 'access', _19 => _19.looked, 'optionalAccess', _20 => _20.delete, 'call', _21 => _21(key2)]);
499
+ if (key2.startsWith(`${was}\0`)) _optionalChain([home, 'access', _24 => _24.shared, 'access', _25 => _25.looked, 'optionalAccess', _26 => _26.delete, 'call', _27 => _27(key2)]);
476
500
  currency = void 0;
477
501
  loaded = true;
478
502
  reset();
@@ -480,9 +504,15 @@ function createController(home) {
480
504
  const outgoing = _nullishCoalesce(prior, () => ( me.seen));
481
505
  const left = me.moving;
482
506
  me.moving = void 0;
507
+ const slots = [...me.slots];
508
+ me.slots.clear();
483
509
  const remove = async () => {
484
510
  await outgoing.remove();
485
- if (left !== void 0 && left !== outgoing) await left.remove();
511
+ await forgetSlots(outgoing, slots);
512
+ if (left !== void 0 && left !== outgoing) {
513
+ await left.remove();
514
+ await forgetSlots(left, slots);
515
+ }
486
516
  };
487
517
  me.tail = me.writing.then(remove).catch(remove).then(
488
518
  () => {
@@ -508,6 +538,7 @@ function createController(home) {
508
538
  mutate,
509
539
  create,
510
540
  session,
541
+ wanted,
511
542
  async switchCurrency(signal, options, next) {
512
543
  checkPause(ctx().store);
513
544
  const storage = ctx().cartStorage;
@@ -524,28 +555,67 @@ function createController(home) {
524
555
  }
525
556
  const before = currency;
526
557
  currency = next;
527
- const items = itemsOf(box.get().cart);
528
- if (items.size === 0) {
529
- if (box.get().sessionId !== null) await live(signal, storage.remove());
530
- reset();
558
+ const code = next.toUpperCase();
559
+ const from = box.get().sessionId;
560
+ const cart = box.get().cart;
561
+ const items = itemsOf(cart);
562
+ if (from === null || items.size === 0) {
563
+ if (from !== null) await live(signal, storage.remove());
564
+ if (items.size === 0) reset();
531
565
  return;
532
566
  }
567
+ if (_optionalChain([cart, 'optionalAccess', _28 => _28.currency, 'access', _29 => _29.toUpperCase, 'call', _30 => _30()]) === code) return;
568
+ let target;
569
+ let moved;
533
570
  try {
534
- await create(signal, options, items, true);
571
+ target = await _asyncNullishCoalesce(await findTarget(signal, code, storage), async () => ( await start(signal, options, /* @__PURE__ */ new Map())));
572
+ moved = await live(signal, session(target.id).absorb(from, { signal }));
535
573
  } catch (error) {
536
574
  if (!signal.aborted) currency = before;
537
575
  throw error;
538
576
  }
577
+ me.reads.set(target.id, Date.now());
578
+ box.set({
579
+ sessionId: target.id,
580
+ cartUrl: target.cartUrl,
581
+ cart: moved.cart,
582
+ status: "ready",
583
+ error: void 0,
584
+ warnings: moved.warnings.filter(
585
+ (warning) => warning.code !== "lines_stale"
586
+ )
587
+ });
588
+ let unsaved;
589
+ try {
590
+ await save(signal, storage, target.id);
591
+ } catch (error) {
592
+ if (signal.aborted) throw error;
593
+ unsaved = error;
594
+ try {
595
+ await live(signal, storage.remove());
596
+ } catch (failure) {
597
+ if (signal.aborted) throw failure;
598
+ }
599
+ }
600
+ if (ctx().customerKey === null && storage.setFor !== void 0) {
601
+ me.slots.add(code);
602
+ await live(signal, storage.setFor(code, target.id));
603
+ }
604
+ if (unsaved !== void 0) throw unsaved;
539
605
  },
540
606
  async clear(signal) {
541
- await live(signal, ctx().cartStorage.remove());
607
+ const storage = ctx().cartStorage;
608
+ const shown = _optionalChain([box, 'access', _31 => _31.get, 'call', _32 => _32(), 'access', _33 => _33.cart, 'optionalAccess', _34 => _34.currency]);
609
+ await live(signal, storage.remove());
610
+ if (shown !== void 0)
611
+ await live(signal, _optionalChain([storage, 'access', _35 => _35.removeFor, 'optionalCall', _36 => _36(shown.toUpperCase())]));
542
612
  currency = void 0;
543
613
  reset();
544
614
  }
545
615
  };
546
616
  }
547
617
  function useCart(options = {}) {
548
- const context = _chunkTDMDOHD3cjs.useCatalogContext.call(void 0, );
618
+ const context = _chunkGU63WFXIcjs.useCatalogContext.call(void 0, );
549
619
  const home = context.cart;
550
620
  const controller = _nullishCoalesce(home.current, () => ( (home.current = createController(home))));
551
621
  const state = _react.useSyncExternalStore.call(void 0,
@@ -595,8 +665,8 @@ function useCart(options = {}) {
595
665
  ),
596
666
  checkout: (input) => controller.run(async (signal) => {
597
667
  const { cartUrls } = home.context;
598
- const returnUrl = _nullishCoalesce(_optionalChain([input, 'optionalAccess', _22 => _22.returnUrl]), () => ( _optionalChain([cartUrls, 'optionalAccess', _23 => _23.returnUrl])));
599
- const cancelUrl = _nullishCoalesce(_optionalChain([input, 'optionalAccess', _24 => _24.cancelUrl]), () => ( _optionalChain([cartUrls, 'optionalAccess', _25 => _25.cancelUrl])));
668
+ const returnUrl = _nullishCoalesce(_optionalChain([input, 'optionalAccess', _37 => _37.returnUrl]), () => ( _optionalChain([cartUrls, 'optionalAccess', _38 => _38.returnUrl])));
669
+ const cancelUrl = _nullishCoalesce(_optionalChain([input, 'optionalAccess', _39 => _39.cancelUrl]), () => ( _optionalChain([cartUrls, 'optionalAccess', _40 => _40.cancelUrl])));
600
670
  if (returnUrl === void 0 || cancelUrl === void 0)
601
671
  throw refuse(
602
672
  "checkout() needs returnUrl and cancelUrl, given or from the provider cartUrls."
@@ -619,7 +689,7 @@ function useCart(options = {}) {
619
689
  { signal: callSignal }
620
690
  );
621
691
  } catch (error) {
622
- const repriced = _chunkB2DYAHNRcjs.isCatalogError.call(void 0, error) && error.code === "cart_prices_changed" ? _optionalChain([error, 'access', _26 => _26.details, 'optionalAccess', _27 => _27.cart]) : void 0;
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;
623
693
  if (!callSignal.aborted && typeof repriced === "object" && repriced !== null)
624
694
  controller.box.set({ cart: repriced });
625
695
  throw error;
@@ -639,7 +709,7 @@ function useCart(options = {}) {
639
709
  if (out === void 0)
640
710
  throw _nullishCoalesce(met, () => ( refuse("There is no cart to check out.")));
641
711
  if (!isHttpUrl(out.checkoutUrl))
642
- throw new (0, _chunkB2DYAHNRcjs.CatalogError)({
712
+ throw new (0, _chunk2YQNMBDIcjs.CatalogError)({
643
713
  code: "INVALID_RESPONSE",
644
714
  message: "Throttle answered a checkout URL that is not http or https.",
645
715
  statusCode: 200,
@@ -656,7 +726,9 @@ function useCart(options = {}) {
656
726
  }, [controller, home, currency, locale]);
657
727
  const owner = controller.owner();
658
728
  const view = owner !== void 0 && owner !== null && owner !== customerKey ? { ...EMPTY, pending: state.pending } : state;
659
- return { ...view, ...actions };
729
+ 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);
731
+ return { ...view, ...actions, format };
660
732
  }
661
733
 
662
734
  // src/react/use-submit-review.ts
@@ -667,7 +739,7 @@ var IDLE = {
667
739
  result: void 0
668
740
  };
669
741
  function useSubmitReview(handle) {
670
- const { client, store, customerKey } = _chunkTDMDOHD3cjs.useCatalogContext.call(void 0, );
742
+ const { client, store, customerKey } = _chunkGU63WFXIcjs.useCatalogContext.call(void 0, );
671
743
  const scope = _react.useMemo.call(void 0,
672
744
  () => ({
673
745
  box: createBox(IDLE),
@@ -738,4 +810,4 @@ function useSubmitReview(handle) {
738
810
 
739
811
 
740
812
 
741
- exports.CatalogProvider = _chunkTDMDOHD3cjs.CatalogProvider; exports.SDK_VERSION = _chunkB2DYAHNRcjs.SDK_VERSION; exports.catalogQueryKey = _chunkTDMDOHD3cjs.catalogQueryKey; exports.createMemoryCartStorage = _chunkTDMDOHD3cjs.createMemoryCartStorage; exports.useCart = useCart; exports.useCatalog = _chunkTDMDOHD3cjs.useCatalog; exports.useCategories = _chunkTDMDOHD3cjs.useCategories; exports.useCollection = _chunkTDMDOHD3cjs.useCollection; exports.useConfig = _chunkTDMDOHD3cjs.useConfig; exports.useCustomerKey = _chunkTDMDOHD3cjs.useCustomerKey; exports.useFilterValues = _chunkTDMDOHD3cjs.useFilterValues; exports.useProduct = _chunkTDMDOHD3cjs.useProduct; exports.useProducts = _chunkTDMDOHD3cjs.useProducts; exports.useRelated = _chunkTDMDOHD3cjs.useRelated; exports.useReviews = _chunkTDMDOHD3cjs.useReviews; exports.useSubmitReview = useSubmitReview; exports.useSuggest = _chunkTDMDOHD3cjs.useSuggest;
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;
package/dist/react.d.cts CHANGED
@@ -1,20 +1,32 @@
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-BTrqSprj.cjs';
5
- import { CategoryTreeDepth, ProductsQuery, ThrottleCart } from './index.cjs';
6
- export { O as OpenCartRef } from './types-BTrqSprj.cjs';
7
- import { Q as QueryOptions, a as QueryResult } from './use-query-BZgjxGPO.cjs';
8
- export { c as catalogQueryKey } from './use-query-BZgjxGPO.cjs';
4
+ import { C as CustomerCartSource } from './types-D0PywKnN.cjs';
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';
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';
12
+ import '@usethrottle/money';
12
13
 
13
- /** Where `useCart` keeps the cart session id (P6-R15). The default is memory only. */
14
+ /**
15
+ * Where `useCart` keeps the cart session id (P6-R15). The default is memory
16
+ * only. The optional per-currency slots let a guest's `switchCurrency` find
17
+ * the cart it already has in that currency (MC2-R41); without them a guest
18
+ * switch makes a new cart. They hold a guest's carts only: `useCart` never
19
+ * writes them while signed in, and forgets them on a sign-in, a sign-out or
20
+ * a customer change (`removeFor`, else `setFor(currency, '')`).
21
+ */
14
22
  type CartStorage = {
15
23
  get(): string | null | Promise<string | null>;
16
24
  set(id: string): void | Promise<void>;
17
25
  remove(): void | Promise<void>;
26
+ /** The session id last saved for `currency` (upper case), or `null`. */
27
+ getFor?(currency: string): string | null | Promise<string | null>;
28
+ setFor?(currency: string, id: string): void | Promise<void>;
29
+ removeFor?(currency: string): void | Promise<void>;
18
30
  };
19
31
  /** In memory only: the cart is forgotten on reload. The default `cartStorage`. */
20
32
  declare function createMemoryCartStorage(): CartStorage;
@@ -98,6 +110,12 @@ type CartState = {
98
110
  error: CatalogError | undefined;
99
111
  /** A change is queued or running. */
100
112
  pending: boolean;
113
+ /**
114
+ * What the last `switchCurrency` could not carry over: `lines_removed`
115
+ * (lines with no price in the new currency, left behind) and
116
+ * `discount_dropped` (a code the new currency refuses). Empty otherwise.
117
+ */
118
+ warnings: CartWarning[];
101
119
  };
102
120
  type CartOptions = {
103
121
  currency?: string;
@@ -120,14 +138,20 @@ type UseCartResult = CartState & {
120
138
  checkoutUrl: string;
121
139
  }>;
122
140
  /**
123
- * A new cart with the current lines in `currency` (BC4); the old one is
124
- * left to Throttle. It lands whole or not at all: if the new cart cannot be
125
- * made, read back or saved, it rejects and the old cart, its stored id and
126
- * the old currency stay (the unused new cart is left to Throttle).
141
+ * Moves the cart into `currency`, keeping one cart per currency (MC2-R41):
142
+ * the shopper's open cart in `currency` (the signed-in customer's from
143
+ * `customerCart`, a guest's from `cartStorage.getFor`), else a new empty
144
+ * one, absorbs the current cart, which Throttle then closes as merged. Lines
145
+ * with no price in `currency` stay behind and a discount it refuses is
146
+ * dropped; both show in `warnings`. If anything fails before the absorb
147
+ * lands, it rejects and the old cart, its stored id and the old currency
148
+ * stay (an unused new cart is left to Throttle).
127
149
  */
128
150
  switchCurrency(currency: string): Promise<void>;
129
151
  /** Forgets the cart (no request). */
130
152
  clear(): Promise<void>;
153
+ /** `formatMoney(minor, currency)` in the cart's currency (before a cart, the hook's or the client's). */
154
+ format(minor: number): string;
131
155
  };
132
156
  /**
133
157
  * The provider's cart (P6-R15). The session id is kept in the provider's
package/dist/react.d.ts CHANGED
@@ -1,20 +1,32 @@
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-BlYCKzB8.js';
5
- import { CategoryTreeDepth, ProductsQuery, ThrottleCart } from './index.js';
6
- export { O as OpenCartRef } from './types-BlYCKzB8.js';
7
- import { Q as QueryOptions, a as QueryResult } from './use-query-QOEBuYlp.js';
8
- export { c as catalogQueryKey } from './use-query-QOEBuYlp.js';
4
+ import { C as CustomerCartSource } from './types-BfveENg1.js';
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';
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';
12
+ import '@usethrottle/money';
12
13
 
13
- /** Where `useCart` keeps the cart session id (P6-R15). The default is memory only. */
14
+ /**
15
+ * Where `useCart` keeps the cart session id (P6-R15). The default is memory
16
+ * only. The optional per-currency slots let a guest's `switchCurrency` find
17
+ * the cart it already has in that currency (MC2-R41); without them a guest
18
+ * switch makes a new cart. They hold a guest's carts only: `useCart` never
19
+ * writes them while signed in, and forgets them on a sign-in, a sign-out or
20
+ * a customer change (`removeFor`, else `setFor(currency, '')`).
21
+ */
14
22
  type CartStorage = {
15
23
  get(): string | null | Promise<string | null>;
16
24
  set(id: string): void | Promise<void>;
17
25
  remove(): void | Promise<void>;
26
+ /** The session id last saved for `currency` (upper case), or `null`. */
27
+ getFor?(currency: string): string | null | Promise<string | null>;
28
+ setFor?(currency: string, id: string): void | Promise<void>;
29
+ removeFor?(currency: string): void | Promise<void>;
18
30
  };
19
31
  /** In memory only: the cart is forgotten on reload. The default `cartStorage`. */
20
32
  declare function createMemoryCartStorage(): CartStorage;
@@ -98,6 +110,12 @@ type CartState = {
98
110
  error: CatalogError | undefined;
99
111
  /** A change is queued or running. */
100
112
  pending: boolean;
113
+ /**
114
+ * What the last `switchCurrency` could not carry over: `lines_removed`
115
+ * (lines with no price in the new currency, left behind) and
116
+ * `discount_dropped` (a code the new currency refuses). Empty otherwise.
117
+ */
118
+ warnings: CartWarning[];
101
119
  };
102
120
  type CartOptions = {
103
121
  currency?: string;
@@ -120,14 +138,20 @@ type UseCartResult = CartState & {
120
138
  checkoutUrl: string;
121
139
  }>;
122
140
  /**
123
- * A new cart with the current lines in `currency` (BC4); the old one is
124
- * left to Throttle. It lands whole or not at all: if the new cart cannot be
125
- * made, read back or saved, it rejects and the old cart, its stored id and
126
- * the old currency stay (the unused new cart is left to Throttle).
141
+ * Moves the cart into `currency`, keeping one cart per currency (MC2-R41):
142
+ * the shopper's open cart in `currency` (the signed-in customer's from
143
+ * `customerCart`, a guest's from `cartStorage.getFor`), else a new empty
144
+ * one, absorbs the current cart, which Throttle then closes as merged. Lines
145
+ * with no price in `currency` stay behind and a discount it refuses is
146
+ * dropped; both show in `warnings`. If anything fails before the absorb
147
+ * lands, it rejects and the old cart, its stored id and the old currency
148
+ * stay (an unused new cart is left to Throttle).
127
149
  */
128
150
  switchCurrency(currency: string): Promise<void>;
129
151
  /** Forgets the cart (no request). */
130
152
  clear(): Promise<void>;
153
+ /** `formatMoney(minor, currency)` in the cart's currency (before a cart, the hook's or the client's). */
154
+ format(minor: number): string;
131
155
  };
132
156
  /**
133
157
  * The provider's cart (P6-R15). The session id is kept in the provider's