@shoppexio/storefront 1.0.87 → 1.0.88

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1923,14 +1923,14 @@ declare function trackPageView(cartValue?: number, itemCount?: number): Promise<
1923
1923
  /**
1924
1924
  * Reports a cart change for the live visitor map without counting a page view
1925
1925
  * (`event_type: 'presence'`). Use it when the cart changes on a page that was
1926
- * already counted. A cart state the server already acknowledged, or that a tab
1927
- * of this shop is sending right now, is not sent again, so a cross-tab `storage`
1928
- * echo of a change is not a second ping. A failed send leaves nothing behind:
1929
- * the same state goes out again on the next change or page load.
1926
+ * already counted. A cart state the server holds, or will hold once a send in
1927
+ * flight lands, is not sent again, so a cross-tab `storage` echo of a change is
1928
+ * not a second ping. A failed send leaves nothing behind: the same state goes
1929
+ * out again on the next change or page load.
1930
1930
  *
1931
- * The check runs when the change's turn comes, after this tab's earlier sends
1932
- * settled, never at call time: a revert to the confirmed cart while another
1933
- * state is on the wire must still go out once that send confirms its state.
1931
+ * The sequence is taken at call time; the check runs when the change's turn
1932
+ * comes, after this tab's earlier sends settled: a revert to the confirmed
1933
+ * cart while another state is on the wire must still go out.
1934
1934
  */
1935
1935
  declare function trackCartActivity(cartValue: number, itemCount: number): Promise<void>;
1936
1936
 
package/dist/index.d.ts CHANGED
@@ -1923,14 +1923,14 @@ declare function trackPageView(cartValue?: number, itemCount?: number): Promise<
1923
1923
  /**
1924
1924
  * Reports a cart change for the live visitor map without counting a page view
1925
1925
  * (`event_type: 'presence'`). Use it when the cart changes on a page that was
1926
- * already counted. A cart state the server already acknowledged, or that a tab
1927
- * of this shop is sending right now, is not sent again, so a cross-tab `storage`
1928
- * echo of a change is not a second ping. A failed send leaves nothing behind:
1929
- * the same state goes out again on the next change or page load.
1926
+ * already counted. A cart state the server holds, or will hold once a send in
1927
+ * flight lands, is not sent again, so a cross-tab `storage` echo of a change is
1928
+ * not a second ping. A failed send leaves nothing behind: the same state goes
1929
+ * out again on the next change or page load.
1930
1930
  *
1931
- * The check runs when the change's turn comes, after this tab's earlier sends
1932
- * settled, never at call time: a revert to the confirmed cart while another
1933
- * state is on the wire must still go out once that send confirms its state.
1931
+ * The sequence is taken at call time; the check runs when the change's turn
1932
+ * comes, after this tab's earlier sends settled: a revert to the confirmed
1933
+ * cart while another state is on the wire must still go out.
1934
1934
  */
1935
1935
  declare function trackCartActivity(cartValue: number, itemCount: number): Promise<void>;
1936
1936
 
package/dist/index.js CHANGED
@@ -6082,6 +6082,7 @@ function isPlatformPageTrackerActive() {
6082
6082
  // ../sdk/src/modules/analytics.ts
6083
6083
  var CONFIRMED_CART_STORAGE_PREFIX = "reported_cart_";
6084
6084
  var IN_FLIGHT_CART_STORAGE_PREFIX = "reporting_cart_";
6085
+ var CART_SEQUENCE_STORAGE_PREFIX = "presence_cart_seq_";
6085
6086
  var CART_CLAIM_TTL_MS = 1e4;
6086
6087
  function confirmedCartKey(storeSlug) {
6087
6088
  return `${CONFIRMED_CART_STORAGE_PREFIX}${storeSlug}`;
@@ -6089,33 +6090,77 @@ function confirmedCartKey(storeSlug) {
6089
6090
  function inFlightCartKey(storeSlug) {
6090
6091
  return `${IN_FLIGHT_CART_STORAGE_PREFIX}${storeSlug}`;
6091
6092
  }
6093
+ function cartSequenceKey(storeSlug) {
6094
+ return `${CART_SEQUENCE_STORAGE_PREFIX}${storeSlug}`;
6095
+ }
6092
6096
  function cartSnapshot(cartValue, itemCount) {
6093
6097
  return `${cartValue ?? ""}|${itemCount ?? ""}`;
6094
6098
  }
6099
+ function isCartSequence(value) {
6100
+ return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
6101
+ }
6102
+ var CART_SEQUENCE_SCOPE_MAX_LENGTH = 64;
6103
+ function readCartCounter(storeSlug) {
6104
+ const counter = getItem(cartSequenceKey(storeSlug));
6105
+ if (!counter || typeof counter.scope !== "string" || counter.scope.length === 0 || counter.scope.length > CART_SEQUENCE_SCOPE_MAX_LENGTH || !isCartSequence(counter.seq)) {
6106
+ return null;
6107
+ }
6108
+ return counter;
6109
+ }
6110
+ var lastCartOrder = null;
6111
+ function nextCartOrder(storeSlug) {
6112
+ const stored = readCartCounter(storeSlug);
6113
+ const scope = stored?.scope ?? lastCartOrder?.scope ?? createClaimToken();
6114
+ const floor = Math.max(stored?.seq ?? 0, lastCartOrder?.scope === scope ? lastCartOrder.seq : 0);
6115
+ const order2 = { scope, seq: Math.max(Date.now(), floor + 1) };
6116
+ lastCartOrder = order2;
6117
+ setItem(cartSequenceKey(storeSlug), order2);
6118
+ return order2;
6119
+ }
6120
+ function cartSequenceLocks() {
6121
+ if (typeof navigator === "undefined") return null;
6122
+ const locks = navigator.locks;
6123
+ return locks && typeof locks.request === "function" ? locks : null;
6124
+ }
6125
+ function reserveCartOrder(storeSlug) {
6126
+ const take = () => nextCartOrder(storeSlug);
6127
+ const locks = cartSequenceLocks();
6128
+ if (!locks) return Promise.resolve(take());
6129
+ return locks.request(cartSequenceKey(storeSlug), take).catch(take);
6130
+ }
6131
+ function readConfirmedCart(storeSlug) {
6132
+ const confirmed = getItem(confirmedCartKey(storeSlug));
6133
+ if (!confirmed || typeof confirmed.snapshot !== "string" || !isCartSequence(confirmed.seq)) return null;
6134
+ return confirmed;
6135
+ }
6095
6136
  function readInFlightCart(storeSlug) {
6096
6137
  const claim = getItem(inFlightCartKey(storeSlug));
6097
- if (!claim || typeof claim.snapshot !== "string" || typeof claim.token !== "string" || typeof claim.at !== "number") {
6138
+ if (!claim || typeof claim.snapshot !== "string" || !isCartSequence(claim.seq) || typeof claim.token !== "string" || typeof claim.at !== "number") {
6098
6139
  return null;
6099
6140
  }
6100
6141
  return claim;
6101
6142
  }
6102
6143
  function isCartReportedOrInFlight(storeSlug, snapshot) {
6103
- if (getItem(confirmedCartKey(storeSlug)) === snapshot) return true;
6144
+ const confirmed = readConfirmedCart(storeSlug);
6104
6145
  const claim = readInFlightCart(storeSlug);
6105
- return claim !== null && claim.snapshot === snapshot && Date.now() - claim.at < CART_CLAIM_TTL_MS;
6146
+ const liveClaim = claim && Date.now() - claim.at < CART_CLAIM_TTL_MS ? claim : null;
6147
+ const expected = liveClaim && (!confirmed || liveClaim.seq > confirmed.seq) ? liveClaim : confirmed;
6148
+ return expected?.snapshot === snapshot;
6106
6149
  }
6107
6150
  function createClaimToken() {
6108
6151
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
6109
6152
  return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;
6110
6153
  }
6111
- function claimCartReport(storeSlug, cartValue, itemCount) {
6154
+ function claimCartReport(storeSlug, seq, cartValue, itemCount) {
6112
6155
  const snapshot = cartSnapshot(cartValue, itemCount);
6113
6156
  const token = createClaimToken();
6114
- setItem(inFlightCartKey(storeSlug), { snapshot, token, at: Date.now() });
6157
+ setItem(inFlightCartKey(storeSlug), { snapshot, seq, token, at: Date.now() });
6115
6158
  return (delivered) => {
6116
- if (readInFlightCart(storeSlug)?.token !== token) return;
6117
- if (delivered) setItem(confirmedCartKey(storeSlug), snapshot);
6118
- removeItem(inFlightCartKey(storeSlug));
6159
+ if (delivered) {
6160
+ const confirmed = readConfirmedCart(storeSlug);
6161
+ if (!confirmed || seq > confirmed.seq) setItem(confirmedCartKey(storeSlug), { snapshot, seq });
6162
+ }
6163
+ if (readInFlightCart(storeSlug)?.token === token) removeItem(inFlightCartKey(storeSlug));
6119
6164
  };
6120
6165
  }
6121
6166
  var cartSendTail = Promise.resolve();
@@ -6156,42 +6201,50 @@ async function postPing(body) {
6156
6201
  async function trackPageView(cartValue, itemCount) {
6157
6202
  if (!isInitialized()) return;
6158
6203
  if (typeof document === "undefined") return;
6204
+ const config = getConfig();
6159
6205
  const carriesCart = cartValue !== void 0 || itemCount !== void 0;
6206
+ const reservedOrder = carriesCart ? reserveCartOrder(config.storeSlug) : void 0;
6160
6207
  if (isPlatformPageTrackerActive()) {
6161
- if (carriesCart) await enqueueCartSend(() => sendCartActivity(cartValue, itemCount));
6208
+ if (reservedOrder) await enqueueCartSend(async () => sendCartActivity(await reservedOrder, cartValue, itemCount));
6162
6209
  return;
6163
6210
  }
6164
- const config = getConfig();
6165
6211
  const sendView = async () => {
6166
- const settle = carriesCart ? claimCartReport(config.storeSlug, cartValue, itemCount) : void 0;
6212
+ const order2 = reservedOrder ? await reservedOrder : void 0;
6213
+ const settle = order2 ? claimCartReport(config.storeSlug, order2.seq, cartValue, itemCount) : void 0;
6167
6214
  const delivered = await postPing({
6168
6215
  referer: document.referrer || void 0,
6169
6216
  cart_value: cartValue,
6170
6217
  item_count: itemCount,
6218
+ cart_seq: order2?.seq,
6219
+ cart_seq_scope: order2?.scope,
6171
6220
  connection_id: getStorefrontConnectionId(config.storeSlug) ?? void 0,
6172
6221
  path: resolveStorefrontPagePath(),
6173
6222
  utm: resolveUtmParameters(config.storeSlug)
6174
6223
  });
6175
6224
  settle?.(delivered);
6176
6225
  };
6177
- await (carriesCart ? enqueueCartSend(sendView) : sendView());
6226
+ await (reservedOrder ? enqueueCartSend(sendView) : sendView());
6178
6227
  }
6179
6228
  async function trackCartActivity(cartValue, itemCount) {
6180
6229
  if (!isInitialized()) return;
6181
6230
  if (typeof document === "undefined") return;
6182
6231
  const snapshot = cartSnapshot(cartValue, itemCount);
6232
+ const reservedOrder = reserveCartOrder(getConfig().storeSlug);
6183
6233
  await enqueueCartSend(async (isLatest) => {
6234
+ const order2 = await reservedOrder;
6184
6235
  if (!isLatest() || isCartReportedOrInFlight(getConfig().storeSlug, snapshot)) return;
6185
- await sendCartActivity(cartValue, itemCount);
6236
+ await sendCartActivity(order2, cartValue, itemCount);
6186
6237
  });
6187
6238
  }
6188
- async function sendCartActivity(cartValue, itemCount) {
6239
+ async function sendCartActivity(order2, cartValue, itemCount) {
6189
6240
  const config = getConfig();
6190
- const settle = claimCartReport(config.storeSlug, cartValue, itemCount);
6241
+ const settle = claimCartReport(config.storeSlug, order2.seq, cartValue, itemCount);
6191
6242
  const delivered = await postPing({
6192
6243
  event_type: "presence",
6193
6244
  cart_value: cartValue,
6194
6245
  item_count: itemCount,
6246
+ cart_seq: order2.seq,
6247
+ cart_seq_scope: order2.scope,
6195
6248
  connection_id: getStorefrontConnectionId(config.storeSlug) ?? void 0,
6196
6249
  path: resolveStorefrontPagePath()
6197
6250
  });