@shoppexio/storefront 1.0.84 → 1.0.87
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 +11 -0
- package/README.md +2 -0
- package/dist/index.cjs +110 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -4
- package/dist/index.d.ts +16 -4
- package/dist/index.js +110 -63
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.87
|
|
4
|
+
|
|
5
|
+
- `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.
|
|
6
|
+
- `getShopReviewsPage(cursor, { product })` narrows a page to one product by its public id (the catalog `id`). The page's `summary` then counts that product's reviews only, so a product's review list can page on demand with a correct breakdown.
|
|
7
|
+
|
|
8
|
+
## 1.0.85
|
|
9
|
+
|
|
10
|
+
- 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.
|
|
11
|
+
- On code storefronts, `trackPageView(cartValue, itemCount)` passes a partial cart through unchanged instead of filling the missing field with 0.
|
|
12
|
+
- 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.
|
|
13
|
+
|
|
3
14
|
## 1.0.84
|
|
4
15
|
|
|
5
16
|
- `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/README.md
CHANGED
|
@@ -151,6 +151,8 @@ await refreshLiveUsers();
|
|
|
151
151
|
setInterval(refreshLiveUsers, 30_000);
|
|
152
152
|
```
|
|
153
153
|
|
|
154
|
+
`getShopReviewsPage(cursor, { product })` pages the shop's reviews on demand; pass a product's `id` to page only its reviews, with a `summary` for that product.
|
|
155
|
+
|
|
154
156
|
`touchStorefrontPresence()` keeps the visitor online but never counts as a page view, so it is safe on a timer. Report page views with `trackPageView()` (once per page load or navigation) and later cart changes with `trackCartActivity(cartValue, itemCount)`, which updates the live visitor map without counting a view.
|
|
155
157
|
|
|
156
158
|
## Not this package
|
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
|
}
|
|
@@ -25922,13 +25924,17 @@ function normalizeFeedback(raw, index) {
|
|
|
25922
25924
|
created_at
|
|
25923
25925
|
};
|
|
25924
25926
|
}
|
|
25925
|
-
async function getShopReviewsPage(cursor) {
|
|
25927
|
+
async function getShopReviewsPage(cursor, options = {}) {
|
|
25926
25928
|
const config2 = getConfig();
|
|
25927
25929
|
const query = new URLSearchParams();
|
|
25928
25930
|
query.set("limit", String(REVIEWS_PAGE_LIMIT));
|
|
25929
25931
|
if (typeof cursor === "string" && cursor.trim().length > 0) {
|
|
25930
25932
|
query.set("cursor", cursor);
|
|
25931
25933
|
}
|
|
25934
|
+
const product = typeof options.product === "string" ? options.product.trim() : "";
|
|
25935
|
+
if (product) {
|
|
25936
|
+
query.set("product", product);
|
|
25937
|
+
}
|
|
25932
25938
|
const querySuffix = `?${query.toString()}`;
|
|
25933
25939
|
const response = await get(
|
|
25934
25940
|
`${buildEndpoint("/v1/storefront/feedback/shop/:storeSlug", {
|
|
@@ -25936,7 +25942,7 @@ async function getShopReviewsPage(cursor) {
|
|
|
25936
25942
|
})}${querySuffix}`,
|
|
25937
25943
|
{
|
|
25938
25944
|
cache: {
|
|
25939
|
-
key: `reviews:${config2.storeSlug}:${cursor ?? "start"}:${REVIEWS_PAGE_LIMIT}`,
|
|
25945
|
+
key: `reviews:${config2.storeSlug}:${product || "all"}:${cursor ?? "start"}:${REVIEWS_PAGE_LIMIT}`,
|
|
25940
25946
|
ttl: REVIEWS_CACHE_TTL,
|
|
25941
25947
|
staleWhileRevalidate: true
|
|
25942
25948
|
}
|
|
@@ -25951,10 +25957,7 @@ async function getShopReviewsPage(cursor) {
|
|
|
25951
25957
|
}
|
|
25952
25958
|
};
|
|
25953
25959
|
}
|
|
25954
|
-
return
|
|
25955
|
-
success: false,
|
|
25956
|
-
message: response.message
|
|
25957
|
-
};
|
|
25960
|
+
return forwardFailure(response);
|
|
25958
25961
|
}
|
|
25959
25962
|
async function getShopReviews() {
|
|
25960
25963
|
const allFeedback = [];
|
|
@@ -26424,10 +26427,7 @@ async function getPages() {
|
|
|
26424
26427
|
data: response.data.pages
|
|
26425
26428
|
};
|
|
26426
26429
|
}
|
|
26427
|
-
return
|
|
26428
|
-
success: false,
|
|
26429
|
-
message: response.message
|
|
26430
|
-
};
|
|
26430
|
+
return forwardFailure(response);
|
|
26431
26431
|
}
|
|
26432
26432
|
async function getPage(slug) {
|
|
26433
26433
|
const config2 = getConfig();
|
|
@@ -26450,10 +26450,7 @@ async function getPage(slug) {
|
|
|
26450
26450
|
data: response.data.page
|
|
26451
26451
|
};
|
|
26452
26452
|
}
|
|
26453
|
-
return
|
|
26454
|
-
success: false,
|
|
26455
|
-
message: response.message
|
|
26456
|
-
};
|
|
26453
|
+
return forwardFailure(response);
|
|
26457
26454
|
}
|
|
26458
26455
|
|
|
26459
26456
|
// ../sdk/src/modules/navigation.ts
|
|
@@ -26635,72 +26632,122 @@ function isPlatformPageTrackerActive() {
|
|
|
26635
26632
|
}
|
|
26636
26633
|
|
|
26637
26634
|
// ../sdk/src/modules/analytics.ts
|
|
26638
|
-
var
|
|
26639
|
-
|
|
26640
|
-
|
|
26635
|
+
var CONFIRMED_CART_STORAGE_PREFIX = "reported_cart_";
|
|
26636
|
+
var IN_FLIGHT_CART_STORAGE_PREFIX = "reporting_cart_";
|
|
26637
|
+
var CART_CLAIM_TTL_MS = 1e4;
|
|
26638
|
+
function confirmedCartKey(storeSlug) {
|
|
26639
|
+
return `${CONFIRMED_CART_STORAGE_PREFIX}${storeSlug}`;
|
|
26640
|
+
}
|
|
26641
|
+
function inFlightCartKey(storeSlug) {
|
|
26642
|
+
return `${IN_FLIGHT_CART_STORAGE_PREFIX}${storeSlug}`;
|
|
26641
26643
|
}
|
|
26642
26644
|
function cartSnapshot(cartValue, itemCount) {
|
|
26643
26645
|
return `${cartValue ?? ""}|${itemCount ?? ""}`;
|
|
26644
26646
|
}
|
|
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;
|
|
26647
|
+
function readInFlightCart(storeSlug) {
|
|
26648
|
+
const claim2 = getItem(inFlightCartKey(storeSlug));
|
|
26649
|
+
if (!claim2 || typeof claim2.snapshot !== "string" || typeof claim2.token !== "string" || typeof claim2.at !== "number") {
|
|
26650
|
+
return null;
|
|
26655
26651
|
}
|
|
26652
|
+
return claim2;
|
|
26653
|
+
}
|
|
26654
|
+
function isCartReportedOrInFlight(storeSlug, snapshot) {
|
|
26655
|
+
if (getItem(confirmedCartKey(storeSlug)) === snapshot) return true;
|
|
26656
|
+
const claim2 = readInFlightCart(storeSlug);
|
|
26657
|
+
return claim2 !== null && claim2.snapshot === snapshot && Date.now() - claim2.at < CART_CLAIM_TTL_MS;
|
|
26658
|
+
}
|
|
26659
|
+
function createClaimToken() {
|
|
26660
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
26661
|
+
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;
|
|
26662
|
+
}
|
|
26663
|
+
function claimCartReport(storeSlug, cartValue, itemCount) {
|
|
26664
|
+
const snapshot = cartSnapshot(cartValue, itemCount);
|
|
26665
|
+
const token = createClaimToken();
|
|
26666
|
+
setItem(inFlightCartKey(storeSlug), { snapshot, token, at: Date.now() });
|
|
26667
|
+
return (delivered) => {
|
|
26668
|
+
if (readInFlightCart(storeSlug)?.token !== token) return;
|
|
26669
|
+
if (delivered) setItem(confirmedCartKey(storeSlug), snapshot);
|
|
26670
|
+
removeItem(inFlightCartKey(storeSlug));
|
|
26671
|
+
};
|
|
26672
|
+
}
|
|
26673
|
+
var cartSendTail = Promise.resolve();
|
|
26674
|
+
var latestCartRequest = 0;
|
|
26675
|
+
function enqueueCartSend(send) {
|
|
26676
|
+
const request2 = ++latestCartRequest;
|
|
26677
|
+
const run = () => send(() => request2 === latestCartRequest);
|
|
26678
|
+
cartSendTail = cartSendTail.then(run, run);
|
|
26679
|
+
return cartSendTail;
|
|
26680
|
+
}
|
|
26681
|
+
async function postPing(body) {
|
|
26656
26682
|
const config2 = getConfig();
|
|
26657
|
-
const
|
|
26658
|
-
|
|
26683
|
+
const controller = typeof AbortController === "undefined" ? void 0 : new AbortController();
|
|
26684
|
+
let timer;
|
|
26685
|
+
const timedOut = new Promise((resolve) => {
|
|
26686
|
+
timer = setTimeout(() => {
|
|
26687
|
+
controller?.abort();
|
|
26688
|
+
resolve(false);
|
|
26689
|
+
}, CART_CLAIM_TTL_MS);
|
|
26690
|
+
});
|
|
26659
26691
|
try {
|
|
26660
26692
|
const endpoint = buildEndpoint("/v1/storefront/shops/:storeSlug/ping", {
|
|
26661
26693
|
storeSlug: config2.storeSlug
|
|
26662
26694
|
});
|
|
26663
|
-
|
|
26695
|
+
const delivered = fetch(`${config2.apiBaseUrl}${endpoint}`, {
|
|
26664
26696
|
method: "POST",
|
|
26665
26697
|
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
|
-
});
|
|
26698
|
+
body: JSON.stringify(body),
|
|
26699
|
+
signal: controller?.signal
|
|
26700
|
+
}).then((response) => response.ok);
|
|
26701
|
+
return await Promise.race([delivered, timedOut]);
|
|
26675
26702
|
} catch {
|
|
26703
|
+
return false;
|
|
26704
|
+
} finally {
|
|
26705
|
+
clearTimeout(timer);
|
|
26676
26706
|
}
|
|
26677
26707
|
}
|
|
26708
|
+
async function trackPageView(cartValue, itemCount) {
|
|
26709
|
+
if (!isInitialized()) return;
|
|
26710
|
+
if (typeof document === "undefined") return;
|
|
26711
|
+
const carriesCart = cartValue !== void 0 || itemCount !== void 0;
|
|
26712
|
+
if (isPlatformPageTrackerActive()) {
|
|
26713
|
+
if (carriesCart) await enqueueCartSend(() => sendCartActivity(cartValue, itemCount));
|
|
26714
|
+
return;
|
|
26715
|
+
}
|
|
26716
|
+
const config2 = getConfig();
|
|
26717
|
+
const sendView = async () => {
|
|
26718
|
+
const settle2 = carriesCart ? claimCartReport(config2.storeSlug, cartValue, itemCount) : void 0;
|
|
26719
|
+
const delivered = await postPing({
|
|
26720
|
+
referer: document.referrer || void 0,
|
|
26721
|
+
cart_value: cartValue,
|
|
26722
|
+
item_count: itemCount,
|
|
26723
|
+
connection_id: getStorefrontConnectionId(config2.storeSlug) ?? void 0,
|
|
26724
|
+
path: resolveStorefrontPagePath(),
|
|
26725
|
+
utm: resolveUtmParameters(config2.storeSlug)
|
|
26726
|
+
});
|
|
26727
|
+
settle2?.(delivered);
|
|
26728
|
+
};
|
|
26729
|
+
await (carriesCart ? enqueueCartSend(sendView) : sendView());
|
|
26730
|
+
}
|
|
26678
26731
|
async function trackCartActivity(cartValue, itemCount) {
|
|
26679
26732
|
if (!isInitialized()) return;
|
|
26680
26733
|
if (typeof document === "undefined") return;
|
|
26681
|
-
|
|
26682
|
-
await
|
|
26734
|
+
const snapshot = cartSnapshot(cartValue, itemCount);
|
|
26735
|
+
await enqueueCartSend(async (isLatest) => {
|
|
26736
|
+
if (!isLatest() || isCartReportedOrInFlight(getConfig().storeSlug, snapshot)) return;
|
|
26737
|
+
await sendCartActivity(cartValue, itemCount);
|
|
26738
|
+
});
|
|
26683
26739
|
}
|
|
26684
26740
|
async function sendCartActivity(cartValue, itemCount) {
|
|
26685
26741
|
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
|
-
}
|
|
26742
|
+
const settle2 = claimCartReport(config2.storeSlug, cartValue, itemCount);
|
|
26743
|
+
const delivered = await postPing({
|
|
26744
|
+
event_type: "presence",
|
|
26745
|
+
cart_value: cartValue,
|
|
26746
|
+
item_count: itemCount,
|
|
26747
|
+
connection_id: getStorefrontConnectionId(config2.storeSlug) ?? void 0,
|
|
26748
|
+
path: resolveStorefrontPagePath()
|
|
26749
|
+
});
|
|
26750
|
+
settle2(delivered);
|
|
26704
26751
|
}
|
|
26705
26752
|
|
|
26706
26753
|
// ../sdk/src/modules/presence.ts
|