@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/CHANGELOG.md +7 -0
- package/dist/index.cjs +68 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +68 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.88
|
|
4
|
+
|
|
5
|
+
- Cart pings carry `cart_seq` and `cart_seq_scope`, a sequence number taken when the cart state is reported and shared by all tabs of the shop (the current time in ms, or one above the highest sequence any tab handed out). The counter record holds the scope (created on first use) and is updated under a Web Lock (`navigator.locks`), so two tabs never hand out the same sequence or create two scopes; where Web Locks are unavailable it is updated without the lock. The server keeps the cart with the highest sequence of the scope and ignores an older one that arrives later, so a slow ping from one tab can no longer overwrite a newer cart another tab already reported.
|
|
6
|
+
- The confirmed cart state records its sequence and is only replaced by a delivered state with a higher one, the same rule the server applies. A cart state counts as already reported when it is the state the server will hold: a newer state another tab is sending, else the confirmed state. So reverting to the confirmed cart while another tab sends a newer one still goes out.
|
|
7
|
+
- Each tab still sends its cart states one at a time and drops a waiting state once a newer one is asked for; this now only saves pings, the order comes from the sequence.
|
|
8
|
+
- Page views and heartbeats without a cart are unchanged and carry no sequence.
|
|
9
|
+
|
|
3
10
|
## 1.0.87
|
|
4
11
|
|
|
5
12
|
- `getPage()`, `getPages()` and `getShopReviewsPage()` keep the transport `status` (and `code`) on a failure, like the other modules, so a storefront can tell a page that does not exist (404) from an API it could not reach.
|
package/dist/index.cjs
CHANGED
|
@@ -26634,6 +26634,7 @@ function isPlatformPageTrackerActive() {
|
|
|
26634
26634
|
// ../sdk/src/modules/analytics.ts
|
|
26635
26635
|
var CONFIRMED_CART_STORAGE_PREFIX = "reported_cart_";
|
|
26636
26636
|
var IN_FLIGHT_CART_STORAGE_PREFIX = "reporting_cart_";
|
|
26637
|
+
var CART_SEQUENCE_STORAGE_PREFIX = "presence_cart_seq_";
|
|
26637
26638
|
var CART_CLAIM_TTL_MS = 1e4;
|
|
26638
26639
|
function confirmedCartKey(storeSlug) {
|
|
26639
26640
|
return `${CONFIRMED_CART_STORAGE_PREFIX}${storeSlug}`;
|
|
@@ -26641,33 +26642,77 @@ function confirmedCartKey(storeSlug) {
|
|
|
26641
26642
|
function inFlightCartKey(storeSlug) {
|
|
26642
26643
|
return `${IN_FLIGHT_CART_STORAGE_PREFIX}${storeSlug}`;
|
|
26643
26644
|
}
|
|
26645
|
+
function cartSequenceKey(storeSlug) {
|
|
26646
|
+
return `${CART_SEQUENCE_STORAGE_PREFIX}${storeSlug}`;
|
|
26647
|
+
}
|
|
26644
26648
|
function cartSnapshot(cartValue, itemCount) {
|
|
26645
26649
|
return `${cartValue ?? ""}|${itemCount ?? ""}`;
|
|
26646
26650
|
}
|
|
26651
|
+
function isCartSequence(value) {
|
|
26652
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
26653
|
+
}
|
|
26654
|
+
var CART_SEQUENCE_SCOPE_MAX_LENGTH = 64;
|
|
26655
|
+
function readCartCounter(storeSlug) {
|
|
26656
|
+
const counter = getItem(cartSequenceKey(storeSlug));
|
|
26657
|
+
if (!counter || typeof counter.scope !== "string" || counter.scope.length === 0 || counter.scope.length > CART_SEQUENCE_SCOPE_MAX_LENGTH || !isCartSequence(counter.seq)) {
|
|
26658
|
+
return null;
|
|
26659
|
+
}
|
|
26660
|
+
return counter;
|
|
26661
|
+
}
|
|
26662
|
+
var lastCartOrder = null;
|
|
26663
|
+
function nextCartOrder(storeSlug) {
|
|
26664
|
+
const stored = readCartCounter(storeSlug);
|
|
26665
|
+
const scope = stored?.scope ?? lastCartOrder?.scope ?? createClaimToken();
|
|
26666
|
+
const floor = Math.max(stored?.seq ?? 0, lastCartOrder?.scope === scope ? lastCartOrder.seq : 0);
|
|
26667
|
+
const order2 = { scope, seq: Math.max(Date.now(), floor + 1) };
|
|
26668
|
+
lastCartOrder = order2;
|
|
26669
|
+
setItem(cartSequenceKey(storeSlug), order2);
|
|
26670
|
+
return order2;
|
|
26671
|
+
}
|
|
26672
|
+
function cartSequenceLocks() {
|
|
26673
|
+
if (typeof navigator === "undefined") return null;
|
|
26674
|
+
const locks = navigator.locks;
|
|
26675
|
+
return locks && typeof locks.request === "function" ? locks : null;
|
|
26676
|
+
}
|
|
26677
|
+
function reserveCartOrder(storeSlug) {
|
|
26678
|
+
const take = () => nextCartOrder(storeSlug);
|
|
26679
|
+
const locks = cartSequenceLocks();
|
|
26680
|
+
if (!locks) return Promise.resolve(take());
|
|
26681
|
+
return locks.request(cartSequenceKey(storeSlug), take).catch(take);
|
|
26682
|
+
}
|
|
26683
|
+
function readConfirmedCart(storeSlug) {
|
|
26684
|
+
const confirmed = getItem(confirmedCartKey(storeSlug));
|
|
26685
|
+
if (!confirmed || typeof confirmed.snapshot !== "string" || !isCartSequence(confirmed.seq)) return null;
|
|
26686
|
+
return confirmed;
|
|
26687
|
+
}
|
|
26647
26688
|
function readInFlightCart(storeSlug) {
|
|
26648
26689
|
const claim2 = getItem(inFlightCartKey(storeSlug));
|
|
26649
|
-
if (!claim2 || typeof claim2.snapshot !== "string" || typeof claim2.token !== "string" || typeof claim2.at !== "number") {
|
|
26690
|
+
if (!claim2 || typeof claim2.snapshot !== "string" || !isCartSequence(claim2.seq) || typeof claim2.token !== "string" || typeof claim2.at !== "number") {
|
|
26650
26691
|
return null;
|
|
26651
26692
|
}
|
|
26652
26693
|
return claim2;
|
|
26653
26694
|
}
|
|
26654
26695
|
function isCartReportedOrInFlight(storeSlug, snapshot) {
|
|
26655
|
-
|
|
26696
|
+
const confirmed = readConfirmedCart(storeSlug);
|
|
26656
26697
|
const claim2 = readInFlightCart(storeSlug);
|
|
26657
|
-
|
|
26698
|
+
const liveClaim = claim2 && Date.now() - claim2.at < CART_CLAIM_TTL_MS ? claim2 : null;
|
|
26699
|
+
const expected = liveClaim && (!confirmed || liveClaim.seq > confirmed.seq) ? liveClaim : confirmed;
|
|
26700
|
+
return expected?.snapshot === snapshot;
|
|
26658
26701
|
}
|
|
26659
26702
|
function createClaimToken() {
|
|
26660
26703
|
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
26661
26704
|
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;
|
|
26662
26705
|
}
|
|
26663
|
-
function claimCartReport(storeSlug, cartValue, itemCount) {
|
|
26706
|
+
function claimCartReport(storeSlug, seq, cartValue, itemCount) {
|
|
26664
26707
|
const snapshot = cartSnapshot(cartValue, itemCount);
|
|
26665
26708
|
const token = createClaimToken();
|
|
26666
|
-
setItem(inFlightCartKey(storeSlug), { snapshot, token, at: Date.now() });
|
|
26709
|
+
setItem(inFlightCartKey(storeSlug), { snapshot, seq, token, at: Date.now() });
|
|
26667
26710
|
return (delivered) => {
|
|
26668
|
-
if (
|
|
26669
|
-
|
|
26670
|
-
|
|
26711
|
+
if (delivered) {
|
|
26712
|
+
const confirmed = readConfirmedCart(storeSlug);
|
|
26713
|
+
if (!confirmed || seq > confirmed.seq) setItem(confirmedCartKey(storeSlug), { snapshot, seq });
|
|
26714
|
+
}
|
|
26715
|
+
if (readInFlightCart(storeSlug)?.token === token) removeItem(inFlightCartKey(storeSlug));
|
|
26671
26716
|
};
|
|
26672
26717
|
}
|
|
26673
26718
|
var cartSendTail = Promise.resolve();
|
|
@@ -26708,42 +26753,50 @@ async function postPing(body) {
|
|
|
26708
26753
|
async function trackPageView(cartValue, itemCount) {
|
|
26709
26754
|
if (!isInitialized()) return;
|
|
26710
26755
|
if (typeof document === "undefined") return;
|
|
26756
|
+
const config2 = getConfig();
|
|
26711
26757
|
const carriesCart = cartValue !== void 0 || itemCount !== void 0;
|
|
26758
|
+
const reservedOrder = carriesCart ? reserveCartOrder(config2.storeSlug) : void 0;
|
|
26712
26759
|
if (isPlatformPageTrackerActive()) {
|
|
26713
|
-
if (
|
|
26760
|
+
if (reservedOrder) await enqueueCartSend(async () => sendCartActivity(await reservedOrder, cartValue, itemCount));
|
|
26714
26761
|
return;
|
|
26715
26762
|
}
|
|
26716
|
-
const config2 = getConfig();
|
|
26717
26763
|
const sendView = async () => {
|
|
26718
|
-
const
|
|
26764
|
+
const order2 = reservedOrder ? await reservedOrder : void 0;
|
|
26765
|
+
const settle2 = order2 ? claimCartReport(config2.storeSlug, order2.seq, cartValue, itemCount) : void 0;
|
|
26719
26766
|
const delivered = await postPing({
|
|
26720
26767
|
referer: document.referrer || void 0,
|
|
26721
26768
|
cart_value: cartValue,
|
|
26722
26769
|
item_count: itemCount,
|
|
26770
|
+
cart_seq: order2?.seq,
|
|
26771
|
+
cart_seq_scope: order2?.scope,
|
|
26723
26772
|
connection_id: getStorefrontConnectionId(config2.storeSlug) ?? void 0,
|
|
26724
26773
|
path: resolveStorefrontPagePath(),
|
|
26725
26774
|
utm: resolveUtmParameters(config2.storeSlug)
|
|
26726
26775
|
});
|
|
26727
26776
|
settle2?.(delivered);
|
|
26728
26777
|
};
|
|
26729
|
-
await (
|
|
26778
|
+
await (reservedOrder ? enqueueCartSend(sendView) : sendView());
|
|
26730
26779
|
}
|
|
26731
26780
|
async function trackCartActivity(cartValue, itemCount) {
|
|
26732
26781
|
if (!isInitialized()) return;
|
|
26733
26782
|
if (typeof document === "undefined") return;
|
|
26734
26783
|
const snapshot = cartSnapshot(cartValue, itemCount);
|
|
26784
|
+
const reservedOrder = reserveCartOrder(getConfig().storeSlug);
|
|
26735
26785
|
await enqueueCartSend(async (isLatest) => {
|
|
26786
|
+
const order2 = await reservedOrder;
|
|
26736
26787
|
if (!isLatest() || isCartReportedOrInFlight(getConfig().storeSlug, snapshot)) return;
|
|
26737
|
-
await sendCartActivity(cartValue, itemCount);
|
|
26788
|
+
await sendCartActivity(order2, cartValue, itemCount);
|
|
26738
26789
|
});
|
|
26739
26790
|
}
|
|
26740
|
-
async function sendCartActivity(cartValue, itemCount) {
|
|
26791
|
+
async function sendCartActivity(order2, cartValue, itemCount) {
|
|
26741
26792
|
const config2 = getConfig();
|
|
26742
|
-
const settle2 = claimCartReport(config2.storeSlug, cartValue, itemCount);
|
|
26793
|
+
const settle2 = claimCartReport(config2.storeSlug, order2.seq, cartValue, itemCount);
|
|
26743
26794
|
const delivered = await postPing({
|
|
26744
26795
|
event_type: "presence",
|
|
26745
26796
|
cart_value: cartValue,
|
|
26746
26797
|
item_count: itemCount,
|
|
26798
|
+
cart_seq: order2.seq,
|
|
26799
|
+
cart_seq_scope: order2.scope,
|
|
26747
26800
|
connection_id: getStorefrontConnectionId(config2.storeSlug) ?? void 0,
|
|
26748
26801
|
path: resolveStorefrontPagePath()
|
|
26749
26802
|
});
|