@cimplify/sdk 0.100.6 → 0.102.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 (70) hide show
  1. package/dist/advanced.d.mts +3 -3
  2. package/dist/advanced.d.ts +3 -3
  3. package/dist/advanced.js +20 -20
  4. package/dist/advanced.mjs +1 -1
  5. package/dist/{chunk-QPRI2NII.js → chunk-2P4KXFB6.js} +6 -0
  6. package/dist/{chunk-4G5UWFC3.mjs → chunk-3V4DVXV3.mjs} +6 -1
  7. package/dist/{chunk-KLJLKT6C.mjs → chunk-4KNFBPV2.mjs} +10 -3
  8. package/dist/{chunk-RQPXLXB2.mjs → chunk-DCMXUKDQ.mjs} +30 -0
  9. package/dist/{chunk-BMFWQNYZ.mjs → chunk-DKLHCZ5M.mjs} +4 -1
  10. package/dist/{chunk-PNTINRAU.js → chunk-EAZGA7LD.js} +30 -0
  11. package/dist/{chunk-BSZJSR62.js → chunk-LOWSMGRT.js} +12 -4
  12. package/dist/{chunk-D3FWJB7Y.js → chunk-YDA46OZ6.js} +50 -47
  13. package/dist/{chunk-3HU5I5VG.js → chunk-YER7DIOM.js} +15 -0
  14. package/dist/{chunk-Z7M6GAHS.mjs → chunk-Z3K2D3TE.mjs} +15 -0
  15. package/dist/{client-BwRAAk4y.d.mts → client-B40KFazg.d.ts} +15 -2
  16. package/dist/{client-DGrvLiJv.d.mts → client-BdErclfU.d.mts} +2 -2
  17. package/dist/{client-CrPglBBz.d.ts → client-BizGfqB7.d.mts} +15 -2
  18. package/dist/{client-CPcZIrjC.d.ts → client-nsiMwuC1.d.ts} +2 -2
  19. package/dist/{index-D-LUYk1Y.d.mts → index-DpmnI__D.d.mts} +3 -3
  20. package/dist/{index-B_RYmvlF.d.ts → index-nEyXWIyR.d.ts} +3 -3
  21. package/dist/index.d.mts +19 -8
  22. package/dist/index.d.ts +19 -8
  23. package/dist/index.js +92 -88
  24. package/dist/index.mjs +4 -4
  25. package/dist/mock/cli.mjs +30 -0
  26. package/dist/mock/library.js +30 -0
  27. package/dist/mock/library.mjs +30 -0
  28. package/dist/mock/msw.js +30 -0
  29. package/dist/mock/msw.mjs +30 -0
  30. package/dist/{payment-BSzy7sly.d.mts → payment-l0rkieEQ.d.mts} +1 -1
  31. package/dist/{payment-jK8ztICx.d.ts → payment-vzNnIi1n.d.ts} +1 -1
  32. package/dist/{price-CJLfcYKd.d.ts → price-BstChV9a.d.ts} +1 -1
  33. package/dist/{price-D96h6c6L.d.mts → price-CS4E5zLS.d.mts} +1 -1
  34. package/dist/{product-CeV8euw4.d.mts → product-Byf0unSy.d.mts} +745 -625
  35. package/dist/{product-CeV8euw4.d.ts → product-Byf0unSy.d.ts} +745 -625
  36. package/dist/react.d.mts +9 -4
  37. package/dist/react.d.ts +9 -4
  38. package/dist/react.js +137 -92
  39. package/dist/react.mjs +58 -13
  40. package/dist/server/evict.d.mts +12 -8
  41. package/dist/server/evict.d.ts +12 -8
  42. package/dist/server/evict.js +53 -7
  43. package/dist/server/evict.mjs +53 -7
  44. package/dist/server/tanstack.d.mts +4 -2
  45. package/dist/server/tanstack.d.ts +4 -2
  46. package/dist/server/tanstack.js +53 -14
  47. package/dist/server/tanstack.mjs +53 -14
  48. package/dist/{server-zyrpzJVn.d.ts → server-Cj40R2ZD.d.ts} +13 -1
  49. package/dist/{server-DRAW2OOX.d.mts → server-DqbZh8Bk.d.mts} +13 -1
  50. package/dist/server.d.mts +4 -4
  51. package/dist/server.d.ts +4 -4
  52. package/dist/server.js +7 -7
  53. package/dist/server.mjs +2 -2
  54. package/dist/testing/msw.d.mts +2 -2
  55. package/dist/testing/msw.d.ts +2 -2
  56. package/dist/testing/msw.js +2 -2
  57. package/dist/testing/msw.mjs +1 -1
  58. package/dist/testing/suite.d.mts +6 -6
  59. package/dist/testing/suite.d.ts +6 -6
  60. package/dist/testing/suite.js +23 -23
  61. package/dist/testing/suite.mjs +4 -4
  62. package/dist/testing.d.mts +52 -21
  63. package/dist/testing.d.ts +52 -21
  64. package/dist/testing.js +83 -79
  65. package/dist/testing.mjs +5 -5
  66. package/dist/utils.d.mts +4 -4
  67. package/dist/utils.d.ts +4 -4
  68. package/package.json +1 -1
  69. package/registry/cart-summary.json +1 -1
  70. package/registry/chat-widget.json +1 -1
package/dist/react.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
  import { splitGenUIContent, messagePreview } from './chunk-U4MW6PYU.mjs';
3
3
  export { messagePreview, splitGenUIContent } from './chunk-U4MW6PYU.mjs';
4
- import { DURATION_UNIT, getVariantDisplayName, INPUT_FIELD_TYPE, isMultiDayService, serviceBillingMultiplier, PRODUCT_TYPE, BOUNDARY_KIND, resolveGallery, chatMessageCards, RENDER_HINT, DEFAULT_CDN_BASE_URL } from './chunk-4G5UWFC3.mjs';
5
- export { getVariantDisplayName } from './chunk-4G5UWFC3.mjs';
6
- import { SESSION_CLAIMED, SESSION_CLEARED, createCimplifyClient, AUTH_TOKEN_CHANGED, MUTATION_ERROR } from './chunk-BMFWQNYZ.mjs';
7
- import { generateIdempotencyKey, ELEMENT_TYPES, EVENT_TYPES, startSignIn } from './chunk-Z7M6GAHS.mjs';
4
+ import { DURATION_UNIT, getVariantDisplayName, INPUT_FIELD_TYPE, isMultiDayService, serviceBillingMultiplier, PRODUCT_TYPE, BOUNDARY_KIND, resolveGallery, chatMessageCards, RENDER_HINT, displaysGrossPrices, DEFAULT_CDN_BASE_URL } from './chunk-3V4DVXV3.mjs';
5
+ export { getVariantDisplayName } from './chunk-3V4DVXV3.mjs';
6
+ import { SESSION_CLAIMED, SESSION_CLEARED, createCimplifyClient, AUTH_TOKEN_CHANGED, MUTATION_ERROR } from './chunk-DKLHCZ5M.mjs';
7
+ import { generateIdempotencyKey, ELEMENT_TYPES, EVENT_TYPES, startSignIn } from './chunk-Z3K2D3TE.mjs';
8
8
  import { parsePrice, formatMoney, formatPrice, getVariantUnitPrice, getProductCurrency, getUnitPriceAtQuantity, isOnSale, getDiscountPercentage, getBasePrice, getPriceRange, formatPriceRange } from './chunk-DLW3RWRK.mjs';
9
9
  import { isSupportedCurrency, currencyCode, CimplifyError } from './chunk-O3YUHLZL.mjs';
10
10
  import './chunk-3G6RQLXK.mjs';
@@ -430,6 +430,8 @@ function transformToCheckoutCart(cart, fx, checkout) {
430
430
  total: amt(cart.pricing.total_price),
431
431
  currency: fx?.displayCurrency ?? cart.pricing.currency,
432
432
  tax_inclusive: cart.pricing.tax_inclusive ?? false,
433
+ tax_rate: cart.pricing.tax_rate,
434
+ price_display_mode: cart.pricing.price_display_mode,
433
435
  deposit_required: cart.pricing.deposit_required ?? false,
434
436
  deposit_amount: cart.pricing.deposit_amount != null ? amt(cart.pricing.deposit_amount) : void 0,
435
437
  requires_delivery: cart.requires_delivery
@@ -682,7 +684,18 @@ function clearAllCartStores() {
682
684
  cartStores.clear();
683
685
  }
684
686
  function emptyBase(currency, isLoading = true) {
685
- return { items: [], subtotal: 0, tax: 0, total: 0, currency, isLoading };
687
+ return {
688
+ items: [],
689
+ subtotal: 0,
690
+ tax: 0,
691
+ taxRate: null,
692
+ taxInclusive: false,
693
+ displayGross: false,
694
+ taxJurisdictionLabel: null,
695
+ total: 0,
696
+ currency,
697
+ isLoading
698
+ };
686
699
  }
687
700
  function buildStoreKey(client, locationId) {
688
701
  return [client.getPublicKey() || "__no_public_key__", locationId || "__no_location__"].join(":");
@@ -775,6 +788,10 @@ function mapServerCart(serverCart, fallbackCurrency) {
775
788
  items,
776
789
  subtotal: roundMoney(toNumber(serverCart.pricing.subtotal)),
777
790
  tax: roundMoney(toNumber(serverCart.pricing.tax_amount)),
791
+ taxRate: serverCart.pricing.tax_rate != null ? toNumber(serverCart.pricing.tax_rate) : null,
792
+ taxInclusive: serverCart.pricing.tax_inclusive,
793
+ displayGross: displaysGrossPrices(serverCart.pricing),
794
+ taxJurisdictionLabel: serverCart.pricing.tax_jurisdiction_label ?? null,
778
795
  total: roundMoney(toNumber(serverCart.pricing.total_price)),
779
796
  currency: serverCart.pricing.currency || fallbackCurrency,
780
797
  isLoading: false
@@ -1106,6 +1123,10 @@ function useCart(options = {}) {
1106
1123
  itemCount,
1107
1124
  subtotal: view.subtotal,
1108
1125
  tax: view.tax,
1126
+ taxRate: view.taxRate,
1127
+ taxInclusive: view.taxInclusive,
1128
+ displayGross: view.displayGross,
1129
+ taxJurisdictionLabel: view.taxJurisdictionLabel,
1109
1130
  total: view.total,
1110
1131
  currency: view.currency,
1111
1132
  isEmpty: itemCount === 0,
@@ -10961,6 +10982,9 @@ function CartSummary({
10961
10982
  itemCount,
10962
10983
  subtotal,
10963
10984
  tax,
10985
+ taxRate,
10986
+ taxInclusive,
10987
+ taxJurisdictionLabel,
10964
10988
  total,
10965
10989
  isEmpty,
10966
10990
  isOptimistic: isOptimistic2,
@@ -11093,7 +11117,13 @@ function CartSummary({
11093
11117
  "data-cimplify-cart-tax": true,
11094
11118
  className: cn("flex items-baseline justify-between", classNames?.tax),
11095
11119
  children: [
11096
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Tax" }),
11120
+ /* @__PURE__ */ jsxs("span", { className: "flex items-baseline gap-1.5 text-muted-foreground", children: [
11121
+ taxInclusive ? "Tax, included in prices" : taxJurisdictionLabel ? `Sales tax, ${taxJurisdictionLabel}` : "Sales tax",
11122
+ taxRate != null && taxRate > 0 && /* @__PURE__ */ jsxs("span", { className: "rounded-full bg-muted px-1.5 py-0.5 text-[11px] font-medium", children: [
11123
+ taxRate,
11124
+ "%"
11125
+ ] })
11126
+ ] }),
11097
11127
  /* @__PURE__ */ jsx(Price, { amount: tax, currency: displayCurrency, className: "tabular-nums" })
11098
11128
  ]
11099
11129
  }
@@ -20234,7 +20264,20 @@ function ReplyCardView({
20234
20264
  }
20235
20265
  );
20236
20266
  }
20237
- var MAX_ATTACHMENT_BYTES = 5 * 1024 * 1024;
20267
+ var MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024;
20268
+ var DOCUMENT_MIME_TYPES = /* @__PURE__ */ new Set([
20269
+ "application/pdf",
20270
+ "text/plain",
20271
+ "text/csv",
20272
+ "application/msword",
20273
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
20274
+ "application/vnd.ms-excel",
20275
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
20276
+ ]);
20277
+ var ATTACHMENT_ACCEPT = ["image/*", ...DOCUMENT_MIME_TYPES].join(",");
20278
+ function isAllowedAttachment(file) {
20279
+ return file.type.startsWith("image/") || DOCUMENT_MIME_TYPES.has(file.type);
20280
+ }
20238
20281
  var KEEP_DISMISSED_KEY = "cimplify_keep_chat_dismissed";
20239
20282
  var AT_BOTTOM_SLACK_PX = 48;
20240
20283
  function ChatWidget({
@@ -20280,16 +20323,18 @@ function ChatWidget({
20280
20323
  openIfExisting
20281
20324
  } = useChat({ client, pollInterval });
20282
20325
  const showTyping = isSending || awaitingReply;
20283
- const attachPhoto = useCallback(
20326
+ const attachFile = useCallback(
20284
20327
  async (file) => {
20285
20328
  if (!client) return;
20286
20329
  setAttachError(null);
20287
- if (!file.type.startsWith("image/")) {
20288
- setAttachError(new CimplifyError("invalid_attachment", "Only photos can be attached."));
20330
+ if (!isAllowedAttachment(file)) {
20331
+ setAttachError(
20332
+ new CimplifyError("invalid_attachment", "Only photos and documents can be attached.")
20333
+ );
20289
20334
  return;
20290
20335
  }
20291
20336
  if (file.size > MAX_ATTACHMENT_BYTES) {
20292
- setAttachError(new CimplifyError("attachment_too_large", "Photos must be under 5MB."));
20337
+ setAttachError(new CimplifyError("attachment_too_large", "Files must be under 25MB."));
20293
20338
  return;
20294
20339
  }
20295
20340
  const uploaded = await client.uploads.upload(file);
@@ -20381,7 +20426,7 @@ function ChatWidget({
20381
20426
  onCancelReply: () => setReplyTo(null),
20382
20427
  onRequestHuman: () => void requestHuman(),
20383
20428
  onLoadOlder: () => void loadOlder(),
20384
- onAttach: attachPhoto,
20429
+ onAttach: attachFile,
20385
20430
  onStartConversation: startConversation,
20386
20431
  currency,
20387
20432
  onGenUIAction,
@@ -21005,7 +21050,7 @@ function ChatInput({
21005
21050
  {
21006
21051
  ref: fileRef,
21007
21052
  type: "file",
21008
- accept: "image/*",
21053
+ accept: ATTACHMENT_ACCEPT,
21009
21054
  className: "hidden",
21010
21055
  "aria-hidden": "true",
21011
21056
  tabIndex: -1,
@@ -7,17 +7,21 @@ import { IncrementalCache } from '@opennextjs/aws/types/overrides';
7
7
  * model that fits Cimplify's event-driven domain:
8
8
  *
9
9
  * - `get`: standard R2 fetch. Returns the cached value or null.
10
- * - `set`: writes to R2 AND emits a tag→cache_key index update to the shared
11
- * `CIMPLIFY_PATH_INDEX` Durable Object (hosted in cimplify-tag-cache).
10
+ * - `set`: reserves a tag→cache_key index update in the shared
11
+ * `CIMPLIFY_PATH_INDEX` Durable Object, writes the value to R2, then
12
+ * confirms that no eviction crossed the write. A tagged value is never
13
+ * cached unless its eviction index is durable and its reservation remains
14
+ * current.
12
15
  * - `delete`: standard R2 delete (called by opennext on explicit invalidate;
13
16
  * also called by us from the Rust EvictionDispatcher path on tag events).
14
17
  *
15
- * The Rust EvictionDispatcher reads the index on bus events, deletes the
16
- * matching R2 entries directly, and prunes the index — there is no SWR
17
- * "background refresh" path. opennext's queue and `isStale` checks become
18
- * no-ops; cached entries are by definition fresh (existed at write time and
19
- * haven't been evicted), and absence means the next request synchronously
20
- * re-renders.
18
+ * The Rust EvictionDispatcher signals the tag-cache worker on bus events. That
19
+ * worker fences new writes, reads the index, deletes matching R2 entries, and
20
+ * purges the dispatch worker's per-script HTML cache tag. A write whose
21
+ * reservation was crossed by that fence deletes its just-written R2 object.
22
+ * There is no SWR "background refresh" path: opennext's queue and `isStale`
23
+ * checks are no-ops, and an R2 miss makes the next request synchronously
24
+ * re-render.
21
25
  *
22
26
  * Why this exists: WfP user workers cannot have `WORKER_SELF_REFERENCE`,
23
27
  * which is the binding every built-in opennext queue requires. Rather than
@@ -7,17 +7,21 @@ import { IncrementalCache } from '@opennextjs/aws/types/overrides';
7
7
  * model that fits Cimplify's event-driven domain:
8
8
  *
9
9
  * - `get`: standard R2 fetch. Returns the cached value or null.
10
- * - `set`: writes to R2 AND emits a tag→cache_key index update to the shared
11
- * `CIMPLIFY_PATH_INDEX` Durable Object (hosted in cimplify-tag-cache).
10
+ * - `set`: reserves a tag→cache_key index update in the shared
11
+ * `CIMPLIFY_PATH_INDEX` Durable Object, writes the value to R2, then
12
+ * confirms that no eviction crossed the write. A tagged value is never
13
+ * cached unless its eviction index is durable and its reservation remains
14
+ * current.
12
15
  * - `delete`: standard R2 delete (called by opennext on explicit invalidate;
13
16
  * also called by us from the Rust EvictionDispatcher path on tag events).
14
17
  *
15
- * The Rust EvictionDispatcher reads the index on bus events, deletes the
16
- * matching R2 entries directly, and prunes the index — there is no SWR
17
- * "background refresh" path. opennext's queue and `isStale` checks become
18
- * no-ops; cached entries are by definition fresh (existed at write time and
19
- * haven't been evicted), and absence means the next request synchronously
20
- * re-renders.
18
+ * The Rust EvictionDispatcher signals the tag-cache worker on bus events. That
19
+ * worker fences new writes, reads the index, deletes matching R2 entries, and
20
+ * purges the dispatch worker's per-script HTML cache tag. A write whose
21
+ * reservation was crossed by that fence deletes its just-written R2 object.
22
+ * There is no SWR "background refresh" path: opennext's queue and `isStale`
23
+ * checks are no-ops, and an R2 miss makes the next request synchronously
24
+ * re-render.
21
25
  *
22
26
  * Why this exists: WfP user workers cannot have `WORKER_SELF_REFERENCE`,
23
27
  * which is the binding every built-in opennext queue requires. Rather than
@@ -24,6 +24,18 @@ function pathIndexFor(env) {
24
24
  if (!ns || !prefix) return null;
25
25
  return ns.get(ns.idFromName(prefix));
26
26
  }
27
+ function isAcceptedReservation(value) {
28
+ if (typeof value !== "object" || value === null) return false;
29
+ const reservation = value;
30
+ return reservation.accepted === true && typeof reservation.generation === "number" && Number.isSafeInteger(reservation.generation) && reservation.generation >= 0;
31
+ }
32
+ async function removeRejectedWrite(r2, key) {
33
+ try {
34
+ await r2.delete(key);
35
+ } catch {
36
+ reportRejectedTaggedWrite("r2-compensating-delete-failed");
37
+ }
38
+ }
27
39
  var evictIncrementalCache = {
28
40
  name: "cimplify-evict-r2-cache",
29
41
  async get(key, cacheType) {
@@ -46,18 +58,43 @@ var evictIncrementalCache = {
46
58
  const r2 = env[R2_BUCKET_BINDING];
47
59
  if (!r2) return;
48
60
  const k = r2Key(env, key, cacheType);
61
+ const tags = cimplifyTags(value);
62
+ let index = null;
63
+ let reservation = null;
64
+ if (tags.length > 0) {
65
+ index = pathIndexFor(env);
66
+ if (!index) {
67
+ reportRejectedTaggedWrite("path-index-not-configured");
68
+ return;
69
+ }
70
+ try {
71
+ const candidate = await index.indexAdd(key, k, tags, cacheType === "fetch");
72
+ if (!isAcceptedReservation(candidate)) {
73
+ const fenceActive = typeof candidate === "object" && candidate !== null && candidate.accepted === false;
74
+ reportRejectedTaggedWrite(
75
+ fenceActive ? "path-index-eviction-fence-active" : "path-index-invalid-reservation"
76
+ );
77
+ return;
78
+ }
79
+ reservation = candidate;
80
+ } catch {
81
+ reportRejectedTaggedWrite("path-index-write-failed");
82
+ return;
83
+ }
84
+ }
49
85
  try {
50
86
  await r2.put(k, JSON.stringify(value));
51
87
  } catch {
52
88
  return;
53
89
  }
54
- const tags = cimplifyTags(value);
55
- if (tags.length === 0) return;
56
- const index = pathIndexFor(env);
57
- if (!index) return;
58
- try {
59
- await index.indexAdd(key, k, tags, cacheType === "fetch");
60
- } catch {
90
+ if (index && reservation) {
91
+ try {
92
+ if (await index.indexConfirm(reservation.generation, tags)) return;
93
+ reportRejectedTaggedWrite("path-index-reservation-invalidated");
94
+ } catch {
95
+ reportRejectedTaggedWrite("path-index-confirm-failed");
96
+ }
97
+ await removeRejectedWrite(r2, k);
61
98
  }
62
99
  },
63
100
  async delete(key) {
@@ -70,6 +107,15 @@ var evictIncrementalCache = {
70
107
  }
71
108
  }
72
109
  };
110
+ function reportRejectedTaggedWrite(reason) {
111
+ console.error(
112
+ JSON.stringify({
113
+ level: "error",
114
+ event: "storefront_cache_write_rejected",
115
+ reason
116
+ })
117
+ );
118
+ }
73
119
  var evict_incremental_cache_default = evictIncrementalCache;
74
120
 
75
121
  exports.evictIncrementalCache = evict_incremental_cache_default;
@@ -22,6 +22,18 @@ function pathIndexFor(env) {
22
22
  if (!ns || !prefix) return null;
23
23
  return ns.get(ns.idFromName(prefix));
24
24
  }
25
+ function isAcceptedReservation(value) {
26
+ if (typeof value !== "object" || value === null) return false;
27
+ const reservation = value;
28
+ return reservation.accepted === true && typeof reservation.generation === "number" && Number.isSafeInteger(reservation.generation) && reservation.generation >= 0;
29
+ }
30
+ async function removeRejectedWrite(r2, key) {
31
+ try {
32
+ await r2.delete(key);
33
+ } catch {
34
+ reportRejectedTaggedWrite("r2-compensating-delete-failed");
35
+ }
36
+ }
25
37
  var evictIncrementalCache = {
26
38
  name: "cimplify-evict-r2-cache",
27
39
  async get(key, cacheType) {
@@ -44,18 +56,43 @@ var evictIncrementalCache = {
44
56
  const r2 = env[R2_BUCKET_BINDING];
45
57
  if (!r2) return;
46
58
  const k = r2Key(env, key, cacheType);
59
+ const tags = cimplifyTags(value);
60
+ let index = null;
61
+ let reservation = null;
62
+ if (tags.length > 0) {
63
+ index = pathIndexFor(env);
64
+ if (!index) {
65
+ reportRejectedTaggedWrite("path-index-not-configured");
66
+ return;
67
+ }
68
+ try {
69
+ const candidate = await index.indexAdd(key, k, tags, cacheType === "fetch");
70
+ if (!isAcceptedReservation(candidate)) {
71
+ const fenceActive = typeof candidate === "object" && candidate !== null && candidate.accepted === false;
72
+ reportRejectedTaggedWrite(
73
+ fenceActive ? "path-index-eviction-fence-active" : "path-index-invalid-reservation"
74
+ );
75
+ return;
76
+ }
77
+ reservation = candidate;
78
+ } catch {
79
+ reportRejectedTaggedWrite("path-index-write-failed");
80
+ return;
81
+ }
82
+ }
47
83
  try {
48
84
  await r2.put(k, JSON.stringify(value));
49
85
  } catch {
50
86
  return;
51
87
  }
52
- const tags = cimplifyTags(value);
53
- if (tags.length === 0) return;
54
- const index = pathIndexFor(env);
55
- if (!index) return;
56
- try {
57
- await index.indexAdd(key, k, tags, cacheType === "fetch");
58
- } catch {
88
+ if (index && reservation) {
89
+ try {
90
+ if (await index.indexConfirm(reservation.generation, tags)) return;
91
+ reportRejectedTaggedWrite("path-index-reservation-invalidated");
92
+ } catch {
93
+ reportRejectedTaggedWrite("path-index-confirm-failed");
94
+ }
95
+ await removeRejectedWrite(r2, k);
59
96
  }
60
97
  },
61
98
  async delete(key) {
@@ -68,6 +105,15 @@ var evictIncrementalCache = {
68
105
  }
69
106
  }
70
107
  };
108
+ function reportRejectedTaggedWrite(reason) {
109
+ console.error(
110
+ JSON.stringify({
111
+ level: "error",
112
+ event: "storefront_cache_write_rejected",
113
+ reason
114
+ })
115
+ );
116
+ }
71
117
  var evict_incremental_cache_default = evictIncrementalCache;
72
118
 
73
119
  export { evict_incremental_cache_default as evictIncrementalCache };
@@ -9,8 +9,10 @@ import { R2Bucket, DurableObjectNamespace } from '@cloudflare/workers-types';
9
9
  * - CIMPLIFY_PATH_INDEX
10
10
  *
11
11
  * This driver consumes those bindings and mirrors the Next/OpenNext eviction
12
- * contract: write to R2, then register cimplify:* tags in PathIndex so the
13
- * central eviction worker can delete the exact R2 object and purge route URLs.
12
+ * contract: reserve cimplify:* tags in PathIndex, write to R2, then confirm
13
+ * that no eviction crossed the write. Tagged values fail closed when indexing
14
+ * is unavailable or their reservation is invalidated, so the central eviction
15
+ * worker can always delete exact R2 objects before purging edge HTML.
14
16
  */
15
17
 
16
18
  declare const TANSTACK_R2_BUCKET_BINDING = "CIMPLIFY_CACHE_R2_BUCKET";
@@ -9,8 +9,10 @@ import { R2Bucket, DurableObjectNamespace } from '@cloudflare/workers-types';
9
9
  * - CIMPLIFY_PATH_INDEX
10
10
  *
11
11
  * This driver consumes those bindings and mirrors the Next/OpenNext eviction
12
- * contract: write to R2, then register cimplify:* tags in PathIndex so the
13
- * central eviction worker can delete the exact R2 object and purge route URLs.
12
+ * contract: reserve cimplify:* tags in PathIndex, write to R2, then confirm
13
+ * that no eviction crossed the write. Tagged values fail closed when indexing
14
+ * is unavailable or their reservation is invalidated, so the central eviction
15
+ * worker can always delete exact R2 objects before purging edge HTML.
14
16
  */
15
17
 
16
18
  declare const TANSTACK_R2_BUCKET_BINDING = "CIMPLIFY_CACHE_R2_BUCKET";
@@ -120,13 +120,27 @@ async function resolveRuntimeEnv(options) {
120
120
  pathIndex: env?.[TANSTACK_PATH_INDEX_BINDING]
121
121
  };
122
122
  }
123
- async function registerIndex(env, cacheKey, r2Key, tags, isFetch) {
124
- if (tags.length === 0) return;
123
+ function isAcceptedReservation(value) {
124
+ if (!isRecord(value)) return false;
125
+ return value.accepted === true && typeof value.generation === "number" && Number.isSafeInteger(value.generation) && value.generation >= 0;
126
+ }
127
+ async function reserveIndex(env, cacheKey, r2Key, tags, isFetch) {
128
+ if (tags.length === 0) return null;
125
129
  const index = pathIndexFor(env);
126
- if (!index) return;
130
+ if (!index) return null;
131
+ try {
132
+ const reservation = await index.indexAdd(cacheKey, r2Key, tags, isFetch);
133
+ if (!isAcceptedReservation(reservation)) return null;
134
+ return { index, generation: reservation.generation };
135
+ } catch {
136
+ return null;
137
+ }
138
+ }
139
+ async function removeRejectedWrite(r2, r2Key) {
127
140
  try {
128
- await index.indexAdd(cacheKey, r2Key, tags, isFetch);
141
+ await r2.delete(r2Key);
129
142
  } catch {
143
+ reportRejectedTaggedWrite("r2-compensating-delete-failed");
130
144
  }
131
145
  }
132
146
  async function listR2Keys(r2, prefix) {
@@ -142,23 +156,48 @@ async function listR2Keys(r2, prefix) {
142
156
  async function putAndIndex(runtimeEnv, driverOptions, key, storedValue, indexValue, options) {
143
157
  const namespace = driverOptions.namespace ?? TANSTACK_CACHE_NAMESPACE;
144
158
  const r2Key = cimplifyTanstackR2Key(runtimeEnv.prefix, key, namespace);
159
+ let tags;
160
+ let cacheKey;
161
+ let isFetch;
145
162
  try {
146
- await runtimeEnv.r2.put(r2Key, storedValue);
163
+ tags = extractTags(key, indexValue, options, driverOptions);
164
+ cacheKey = extractCacheKey(key, indexValue, options, driverOptions);
165
+ isFetch = extractIsFetch(key, indexValue, options, driverOptions);
147
166
  } catch {
167
+ reportRejectedTaggedWrite("invalid-index-metadata");
148
168
  return;
149
169
  }
170
+ let reservation = null;
171
+ if (tags.length > 0) {
172
+ reservation = await reserveIndex(runtimeEnv, cacheKey, r2Key, tags, isFetch);
173
+ if (!reservation) {
174
+ reportRejectedTaggedWrite("path-index-write-rejected");
175
+ return;
176
+ }
177
+ }
150
178
  try {
151
- const tags = extractTags(key, indexValue, options, driverOptions);
152
- if (tags.length === 0) return;
153
- await registerIndex(
154
- runtimeEnv,
155
- extractCacheKey(key, indexValue, options, driverOptions),
156
- r2Key,
157
- tags,
158
- extractIsFetch(key, indexValue, options, driverOptions)
159
- );
179
+ await runtimeEnv.r2.put(r2Key, storedValue);
160
180
  } catch {
181
+ return;
161
182
  }
183
+ if (reservation) {
184
+ try {
185
+ if (await reservation.index.indexConfirm(reservation.generation, tags)) return;
186
+ reportRejectedTaggedWrite("path-index-reservation-invalidated");
187
+ } catch {
188
+ reportRejectedTaggedWrite("path-index-confirm-failed");
189
+ }
190
+ await removeRejectedWrite(runtimeEnv.r2, r2Key);
191
+ }
192
+ }
193
+ function reportRejectedTaggedWrite(reason) {
194
+ console.error(
195
+ JSON.stringify({
196
+ level: "error",
197
+ event: "storefront_cache_write_rejected",
198
+ reason
199
+ })
200
+ );
162
201
  }
163
202
  async function getRawItem(driverOptions, key) {
164
203
  const runtimeEnv = await resolveRuntimeEnv(driverOptions);
@@ -118,13 +118,27 @@ async function resolveRuntimeEnv(options) {
118
118
  pathIndex: env?.[TANSTACK_PATH_INDEX_BINDING]
119
119
  };
120
120
  }
121
- async function registerIndex(env, cacheKey, r2Key, tags, isFetch) {
122
- if (tags.length === 0) return;
121
+ function isAcceptedReservation(value) {
122
+ if (!isRecord(value)) return false;
123
+ return value.accepted === true && typeof value.generation === "number" && Number.isSafeInteger(value.generation) && value.generation >= 0;
124
+ }
125
+ async function reserveIndex(env, cacheKey, r2Key, tags, isFetch) {
126
+ if (tags.length === 0) return null;
123
127
  const index = pathIndexFor(env);
124
- if (!index) return;
128
+ if (!index) return null;
129
+ try {
130
+ const reservation = await index.indexAdd(cacheKey, r2Key, tags, isFetch);
131
+ if (!isAcceptedReservation(reservation)) return null;
132
+ return { index, generation: reservation.generation };
133
+ } catch {
134
+ return null;
135
+ }
136
+ }
137
+ async function removeRejectedWrite(r2, r2Key) {
125
138
  try {
126
- await index.indexAdd(cacheKey, r2Key, tags, isFetch);
139
+ await r2.delete(r2Key);
127
140
  } catch {
141
+ reportRejectedTaggedWrite("r2-compensating-delete-failed");
128
142
  }
129
143
  }
130
144
  async function listR2Keys(r2, prefix) {
@@ -140,23 +154,48 @@ async function listR2Keys(r2, prefix) {
140
154
  async function putAndIndex(runtimeEnv, driverOptions, key, storedValue, indexValue, options) {
141
155
  const namespace = driverOptions.namespace ?? TANSTACK_CACHE_NAMESPACE;
142
156
  const r2Key = cimplifyTanstackR2Key(runtimeEnv.prefix, key, namespace);
157
+ let tags;
158
+ let cacheKey;
159
+ let isFetch;
143
160
  try {
144
- await runtimeEnv.r2.put(r2Key, storedValue);
161
+ tags = extractTags(key, indexValue, options, driverOptions);
162
+ cacheKey = extractCacheKey(key, indexValue, options, driverOptions);
163
+ isFetch = extractIsFetch(key, indexValue, options, driverOptions);
145
164
  } catch {
165
+ reportRejectedTaggedWrite("invalid-index-metadata");
146
166
  return;
147
167
  }
168
+ let reservation = null;
169
+ if (tags.length > 0) {
170
+ reservation = await reserveIndex(runtimeEnv, cacheKey, r2Key, tags, isFetch);
171
+ if (!reservation) {
172
+ reportRejectedTaggedWrite("path-index-write-rejected");
173
+ return;
174
+ }
175
+ }
148
176
  try {
149
- const tags = extractTags(key, indexValue, options, driverOptions);
150
- if (tags.length === 0) return;
151
- await registerIndex(
152
- runtimeEnv,
153
- extractCacheKey(key, indexValue, options, driverOptions),
154
- r2Key,
155
- tags,
156
- extractIsFetch(key, indexValue, options, driverOptions)
157
- );
177
+ await runtimeEnv.r2.put(r2Key, storedValue);
158
178
  } catch {
179
+ return;
159
180
  }
181
+ if (reservation) {
182
+ try {
183
+ if (await reservation.index.indexConfirm(reservation.generation, tags)) return;
184
+ reportRejectedTaggedWrite("path-index-reservation-invalidated");
185
+ } catch {
186
+ reportRejectedTaggedWrite("path-index-confirm-failed");
187
+ }
188
+ await removeRejectedWrite(runtimeEnv.r2, r2Key);
189
+ }
190
+ }
191
+ function reportRejectedTaggedWrite(reason) {
192
+ console.error(
193
+ JSON.stringify({
194
+ level: "error",
195
+ event: "storefront_cache_write_rejected",
196
+ reason
197
+ })
198
+ );
160
199
  }
161
200
  async function getRawItem(driverOptions, key) {
162
201
  const runtimeEnv = await resolveRuntimeEnv(driverOptions);
@@ -1,5 +1,5 @@
1
1
  import { Hono } from 'hono';
2
- import { b3 as ProductInputField } from './product-CeV8euw4.js';
2
+ import { b6 as ProductInputField } from './product-Byf0unSy.js';
3
3
 
4
4
  interface Clock {
5
5
  now(): Date;
@@ -702,11 +702,23 @@ interface ComponentSelectionInput {
702
702
  quantity?: number;
703
703
  }
704
704
 
705
+ interface DestinationQuoteBody {
706
+ order_type: string;
707
+ address_info: {
708
+ street_address?: string;
709
+ city?: string;
710
+ region?: string;
711
+ latitude?: number;
712
+ longitude?: number;
713
+ };
714
+ delivery_rate_id?: string | null;
715
+ }
705
716
  interface CartService {
706
717
  get(session: Session): MockCart;
707
718
  getItems(session: Session): MockCartItem[];
708
719
  getCount(session: Session): number;
709
720
  getTotal(session: Session): string;
721
+ quoteDestination(session: Session, body: DestinationQuoteBody): unknown;
710
722
  addItem(session: Session, body: AddItemInput): unknown;
711
723
  updateQuantity(session: Session, cartItemId: string, quantity: number): unknown;
712
724
  removeItem(session: Session, cartItemId: string): unknown;
@@ -1,5 +1,5 @@
1
1
  import { Hono } from 'hono';
2
- import { b3 as ProductInputField } from './product-CeV8euw4.mjs';
2
+ import { b6 as ProductInputField } from './product-Byf0unSy.mjs';
3
3
 
4
4
  interface Clock {
5
5
  now(): Date;
@@ -702,11 +702,23 @@ interface ComponentSelectionInput {
702
702
  quantity?: number;
703
703
  }
704
704
 
705
+ interface DestinationQuoteBody {
706
+ order_type: string;
707
+ address_info: {
708
+ street_address?: string;
709
+ city?: string;
710
+ region?: string;
711
+ latitude?: number;
712
+ longitude?: number;
713
+ };
714
+ delivery_rate_id?: string | null;
715
+ }
705
716
  interface CartService {
706
717
  get(session: Session): MockCart;
707
718
  getItems(session: Session): MockCartItem[];
708
719
  getCount(session: Session): number;
709
720
  getTotal(session: Session): string;
721
+ quoteDestination(session: Session, body: DestinationQuoteBody): unknown;
710
722
  addItem(session: Session, body: AddItemInput): unknown;
711
723
  updateQuantity(session: Session, cartItemId: string, quantity: number): unknown;
712
724
  removeItem(session: Session, cartItemId: string): unknown;