@shoppexio/storefront 1.0.84 → 1.0.85
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 +6 -0
- package/dist/index.cjs +101 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -3
- package/dist/index.d.ts +8 -3
- package/dist/index.js +101 -49
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.85
|
|
4
|
+
|
|
5
|
+
- Cart reporting keeps two shared values: the cart state the server confirmed (written only after a 2xx response) and a short-lived in-flight claim that lets another tab skip the `storage` echo while a send runs. Only the newest claim settles: a send whose claim was replaced confirms and clears nothing, and a failed send (network error, non-OK response, or no answer within 10 seconds) writes nothing, so an unsent cart can never look reported. A tab sends its cart states one at a time and in order, dropping a waiting state once a newer one is asked for, so an older cart cannot reach the server after a newer one. Whether a change is already reported is decided when its turn comes, so reverting to the confirmed cart while another state is being sent still goes out.
|
|
6
|
+
- On code storefronts, `trackPageView(cartValue, itemCount)` passes a partial cart through unchanged instead of filling the missing field with 0.
|
|
7
|
+
- Storefront client-error reports cut `endpoint`/`message` to 500 and `page_url`/`request_url` to 2000 characters, the bounds the ping endpoint enforces, so a long page URL no longer gets the report rejected.
|
|
8
|
+
|
|
3
9
|
## 1.0.84
|
|
4
10
|
|
|
5
11
|
- `touchStorefrontPresence()` sends `event_type: "presence"`. The server refreshes the live session but no longer counts the ping as a shop view, so an idle tab with `sx-online-users` (or any theme that touches presence on a timer) stops adding a view every few minutes.
|
package/dist/index.cjs
CHANGED
|
@@ -23870,6 +23870,8 @@ function removeItem(key) {
|
|
|
23870
23870
|
// ../sdk/src/core/telemetry.ts
|
|
23871
23871
|
var CONNECTION_ID_STORAGE_PREFIX = "presence_connection_";
|
|
23872
23872
|
var CLIENT_ERROR_DEDUPE_WINDOW_MS = 15e3;
|
|
23873
|
+
var CLIENT_ERROR_TEXT_MAX_LENGTH = 500;
|
|
23874
|
+
var CLIENT_ERROR_URL_MAX_LENGTH = 2e3;
|
|
23873
23875
|
function createPresenceConnectionId() {
|
|
23874
23876
|
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
23875
23877
|
return crypto.randomUUID();
|
|
@@ -23920,14 +23922,14 @@ async function reportStorefrontClientError(payload) {
|
|
|
23920
23922
|
client_error: {
|
|
23921
23923
|
source: payload.source ?? "sdk",
|
|
23922
23924
|
phase: payload.phase ?? "request",
|
|
23923
|
-
endpoint: payload.endpoint,
|
|
23925
|
+
endpoint: payload.endpoint.slice(0, CLIENT_ERROR_TEXT_MAX_LENGTH),
|
|
23924
23926
|
method: payload.method,
|
|
23925
|
-
message: payload.message,
|
|
23927
|
+
message: payload.message.slice(0, CLIENT_ERROR_TEXT_MAX_LENGTH),
|
|
23926
23928
|
status_code: payload.statusCode,
|
|
23927
23929
|
attempt_count: payload.attemptCount,
|
|
23928
23930
|
response_received: payload.responseReceived ?? false,
|
|
23929
|
-
page_url: payload.pageUrl ?? window.location.href,
|
|
23930
|
-
request_url: payload.requestUrl,
|
|
23931
|
+
page_url: (payload.pageUrl ?? window.location.href).slice(0, CLIENT_ERROR_URL_MAX_LENGTH),
|
|
23932
|
+
request_url: payload.requestUrl?.slice(0, CLIENT_ERROR_URL_MAX_LENGTH),
|
|
23931
23933
|
online: payload.online ?? (typeof navigator !== "undefined" ? navigator.onLine : void 0),
|
|
23932
23934
|
visibility_state: payload.visibilityState ?? (typeof document !== "undefined" ? document.visibilityState : void 0)
|
|
23933
23935
|
}
|
|
@@ -26635,72 +26637,122 @@ function isPlatformPageTrackerActive() {
|
|
|
26635
26637
|
}
|
|
26636
26638
|
|
|
26637
26639
|
// ../sdk/src/modules/analytics.ts
|
|
26638
|
-
var
|
|
26639
|
-
|
|
26640
|
-
|
|
26640
|
+
var CONFIRMED_CART_STORAGE_PREFIX = "reported_cart_";
|
|
26641
|
+
var IN_FLIGHT_CART_STORAGE_PREFIX = "reporting_cart_";
|
|
26642
|
+
var CART_CLAIM_TTL_MS = 1e4;
|
|
26643
|
+
function confirmedCartKey(storeSlug) {
|
|
26644
|
+
return `${CONFIRMED_CART_STORAGE_PREFIX}${storeSlug}`;
|
|
26645
|
+
}
|
|
26646
|
+
function inFlightCartKey(storeSlug) {
|
|
26647
|
+
return `${IN_FLIGHT_CART_STORAGE_PREFIX}${storeSlug}`;
|
|
26641
26648
|
}
|
|
26642
26649
|
function cartSnapshot(cartValue, itemCount) {
|
|
26643
26650
|
return `${cartValue ?? ""}|${itemCount ?? ""}`;
|
|
26644
26651
|
}
|
|
26645
|
-
function
|
|
26646
|
-
|
|
26647
|
-
|
|
26648
|
-
|
|
26649
|
-
if (!isInitialized()) return;
|
|
26650
|
-
if (typeof document === "undefined") return;
|
|
26651
|
-
const carriesCart = cartValue !== void 0 || itemCount !== void 0;
|
|
26652
|
-
if (isPlatformPageTrackerActive()) {
|
|
26653
|
-
if (carriesCart) await sendCartActivity(cartValue ?? 0, itemCount ?? 0);
|
|
26654
|
-
return;
|
|
26652
|
+
function readInFlightCart(storeSlug) {
|
|
26653
|
+
const claim2 = getItem(inFlightCartKey(storeSlug));
|
|
26654
|
+
if (!claim2 || typeof claim2.snapshot !== "string" || typeof claim2.token !== "string" || typeof claim2.at !== "number") {
|
|
26655
|
+
return null;
|
|
26655
26656
|
}
|
|
26657
|
+
return claim2;
|
|
26658
|
+
}
|
|
26659
|
+
function isCartReportedOrInFlight(storeSlug, snapshot) {
|
|
26660
|
+
if (getItem(confirmedCartKey(storeSlug)) === snapshot) return true;
|
|
26661
|
+
const claim2 = readInFlightCart(storeSlug);
|
|
26662
|
+
return claim2 !== null && claim2.snapshot === snapshot && Date.now() - claim2.at < CART_CLAIM_TTL_MS;
|
|
26663
|
+
}
|
|
26664
|
+
function createClaimToken() {
|
|
26665
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
26666
|
+
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;
|
|
26667
|
+
}
|
|
26668
|
+
function claimCartReport(storeSlug, cartValue, itemCount) {
|
|
26669
|
+
const snapshot = cartSnapshot(cartValue, itemCount);
|
|
26670
|
+
const token = createClaimToken();
|
|
26671
|
+
setItem(inFlightCartKey(storeSlug), { snapshot, token, at: Date.now() });
|
|
26672
|
+
return (delivered) => {
|
|
26673
|
+
if (readInFlightCart(storeSlug)?.token !== token) return;
|
|
26674
|
+
if (delivered) setItem(confirmedCartKey(storeSlug), snapshot);
|
|
26675
|
+
removeItem(inFlightCartKey(storeSlug));
|
|
26676
|
+
};
|
|
26677
|
+
}
|
|
26678
|
+
var cartSendTail = Promise.resolve();
|
|
26679
|
+
var latestCartRequest = 0;
|
|
26680
|
+
function enqueueCartSend(send) {
|
|
26681
|
+
const request2 = ++latestCartRequest;
|
|
26682
|
+
const run = () => send(() => request2 === latestCartRequest);
|
|
26683
|
+
cartSendTail = cartSendTail.then(run, run);
|
|
26684
|
+
return cartSendTail;
|
|
26685
|
+
}
|
|
26686
|
+
async function postPing(body) {
|
|
26656
26687
|
const config2 = getConfig();
|
|
26657
|
-
const
|
|
26658
|
-
|
|
26688
|
+
const controller = typeof AbortController === "undefined" ? void 0 : new AbortController();
|
|
26689
|
+
let timer;
|
|
26690
|
+
const timedOut = new Promise((resolve) => {
|
|
26691
|
+
timer = setTimeout(() => {
|
|
26692
|
+
controller?.abort();
|
|
26693
|
+
resolve(false);
|
|
26694
|
+
}, CART_CLAIM_TTL_MS);
|
|
26695
|
+
});
|
|
26659
26696
|
try {
|
|
26660
26697
|
const endpoint = buildEndpoint("/v1/storefront/shops/:storeSlug/ping", {
|
|
26661
26698
|
storeSlug: config2.storeSlug
|
|
26662
26699
|
});
|
|
26663
|
-
|
|
26700
|
+
const delivered = fetch(`${config2.apiBaseUrl}${endpoint}`, {
|
|
26664
26701
|
method: "POST",
|
|
26665
26702
|
headers: { "Content-Type": "application/json" },
|
|
26666
|
-
body: JSON.stringify(
|
|
26667
|
-
|
|
26668
|
-
|
|
26669
|
-
|
|
26670
|
-
connection_id: connectionId ?? void 0,
|
|
26671
|
-
path: resolveStorefrontPagePath(),
|
|
26672
|
-
utm: resolveUtmParameters(config2.storeSlug)
|
|
26673
|
-
})
|
|
26674
|
-
});
|
|
26703
|
+
body: JSON.stringify(body),
|
|
26704
|
+
signal: controller?.signal
|
|
26705
|
+
}).then((response) => response.ok);
|
|
26706
|
+
return await Promise.race([delivered, timedOut]);
|
|
26675
26707
|
} catch {
|
|
26708
|
+
return false;
|
|
26709
|
+
} finally {
|
|
26710
|
+
clearTimeout(timer);
|
|
26676
26711
|
}
|
|
26677
26712
|
}
|
|
26713
|
+
async function trackPageView(cartValue, itemCount) {
|
|
26714
|
+
if (!isInitialized()) return;
|
|
26715
|
+
if (typeof document === "undefined") return;
|
|
26716
|
+
const carriesCart = cartValue !== void 0 || itemCount !== void 0;
|
|
26717
|
+
if (isPlatformPageTrackerActive()) {
|
|
26718
|
+
if (carriesCart) await enqueueCartSend(() => sendCartActivity(cartValue, itemCount));
|
|
26719
|
+
return;
|
|
26720
|
+
}
|
|
26721
|
+
const config2 = getConfig();
|
|
26722
|
+
const sendView = async () => {
|
|
26723
|
+
const settle2 = carriesCart ? claimCartReport(config2.storeSlug, cartValue, itemCount) : void 0;
|
|
26724
|
+
const delivered = await postPing({
|
|
26725
|
+
referer: document.referrer || void 0,
|
|
26726
|
+
cart_value: cartValue,
|
|
26727
|
+
item_count: itemCount,
|
|
26728
|
+
connection_id: getStorefrontConnectionId(config2.storeSlug) ?? void 0,
|
|
26729
|
+
path: resolveStorefrontPagePath(),
|
|
26730
|
+
utm: resolveUtmParameters(config2.storeSlug)
|
|
26731
|
+
});
|
|
26732
|
+
settle2?.(delivered);
|
|
26733
|
+
};
|
|
26734
|
+
await (carriesCart ? enqueueCartSend(sendView) : sendView());
|
|
26735
|
+
}
|
|
26678
26736
|
async function trackCartActivity(cartValue, itemCount) {
|
|
26679
26737
|
if (!isInitialized()) return;
|
|
26680
26738
|
if (typeof document === "undefined") return;
|
|
26681
|
-
|
|
26682
|
-
await
|
|
26739
|
+
const snapshot = cartSnapshot(cartValue, itemCount);
|
|
26740
|
+
await enqueueCartSend(async (isLatest) => {
|
|
26741
|
+
if (!isLatest() || isCartReportedOrInFlight(getConfig().storeSlug, snapshot)) return;
|
|
26742
|
+
await sendCartActivity(cartValue, itemCount);
|
|
26743
|
+
});
|
|
26683
26744
|
}
|
|
26684
26745
|
async function sendCartActivity(cartValue, itemCount) {
|
|
26685
26746
|
const config2 = getConfig();
|
|
26686
|
-
|
|
26687
|
-
|
|
26688
|
-
|
|
26689
|
-
|
|
26690
|
-
|
|
26691
|
-
|
|
26692
|
-
|
|
26693
|
-
|
|
26694
|
-
|
|
26695
|
-
event_type: "presence",
|
|
26696
|
-
cart_value: cartValue,
|
|
26697
|
-
item_count: itemCount,
|
|
26698
|
-
connection_id: getStorefrontConnectionId(config2.storeSlug) ?? void 0,
|
|
26699
|
-
path: resolveStorefrontPagePath()
|
|
26700
|
-
})
|
|
26701
|
-
});
|
|
26702
|
-
} catch {
|
|
26703
|
-
}
|
|
26747
|
+
const settle2 = claimCartReport(config2.storeSlug, cartValue, itemCount);
|
|
26748
|
+
const delivered = await postPing({
|
|
26749
|
+
event_type: "presence",
|
|
26750
|
+
cart_value: cartValue,
|
|
26751
|
+
item_count: itemCount,
|
|
26752
|
+
connection_id: getStorefrontConnectionId(config2.storeSlug) ?? void 0,
|
|
26753
|
+
path: resolveStorefrontPagePath()
|
|
26754
|
+
});
|
|
26755
|
+
settle2(delivered);
|
|
26704
26756
|
}
|
|
26705
26757
|
|
|
26706
26758
|
// ../sdk/src/modules/presence.ts
|