@shoppexio/storefront 1.0.85 → 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 +12 -0
- package/README.md +2 -0
- package/dist/index.cjs +77 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -8
- package/dist/index.d.ts +15 -8
- package/dist/index.js +77 -29
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1701,7 +1701,14 @@ declare function validateCoupon(code: string, productOrOptions?: string | Coupon
|
|
|
1701
1701
|
* Note: Shoppex has shop-level feedback, not product-level reviews.
|
|
1702
1702
|
*/
|
|
1703
1703
|
|
|
1704
|
-
|
|
1704
|
+
interface ShopReviewsPageOptions {
|
|
1705
|
+
/**
|
|
1706
|
+
* One product's reviews, by the product's public id (the `id` the catalog
|
|
1707
|
+
* carries). The page's `summary` then counts that product's reviews only.
|
|
1708
|
+
*/
|
|
1709
|
+
product?: string | null;
|
|
1710
|
+
}
|
|
1711
|
+
declare function getShopReviewsPage(cursor?: string | null, options?: ShopReviewsPageOptions): Promise<SDKResponse<ShopReviewsPage>>;
|
|
1705
1712
|
declare function getShopReviews(): Promise<SDKResponse<Feedback[]>>;
|
|
1706
1713
|
|
|
1707
1714
|
/**
|
|
@@ -1916,14 +1923,14 @@ declare function trackPageView(cartValue?: number, itemCount?: number): Promise<
|
|
|
1916
1923
|
/**
|
|
1917
1924
|
* Reports a cart change for the live visitor map without counting a page view
|
|
1918
1925
|
* (`event_type: 'presence'`). Use it when the cart changes on a page that was
|
|
1919
|
-
* already counted. A cart state the server
|
|
1920
|
-
*
|
|
1921
|
-
*
|
|
1922
|
-
*
|
|
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.
|
|
1923
1930
|
*
|
|
1924
|
-
* The check runs when the change's turn
|
|
1925
|
-
*
|
|
1926
|
-
* state is on the wire must still go out
|
|
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.
|
|
1927
1934
|
*/
|
|
1928
1935
|
declare function trackCartActivity(cartValue: number, itemCount: number): Promise<void>;
|
|
1929
1936
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1701,7 +1701,14 @@ declare function validateCoupon(code: string, productOrOptions?: string | Coupon
|
|
|
1701
1701
|
* Note: Shoppex has shop-level feedback, not product-level reviews.
|
|
1702
1702
|
*/
|
|
1703
1703
|
|
|
1704
|
-
|
|
1704
|
+
interface ShopReviewsPageOptions {
|
|
1705
|
+
/**
|
|
1706
|
+
* One product's reviews, by the product's public id (the `id` the catalog
|
|
1707
|
+
* carries). The page's `summary` then counts that product's reviews only.
|
|
1708
|
+
*/
|
|
1709
|
+
product?: string | null;
|
|
1710
|
+
}
|
|
1711
|
+
declare function getShopReviewsPage(cursor?: string | null, options?: ShopReviewsPageOptions): Promise<SDKResponse<ShopReviewsPage>>;
|
|
1705
1712
|
declare function getShopReviews(): Promise<SDKResponse<Feedback[]>>;
|
|
1706
1713
|
|
|
1707
1714
|
/**
|
|
@@ -1916,14 +1923,14 @@ declare function trackPageView(cartValue?: number, itemCount?: number): Promise<
|
|
|
1916
1923
|
/**
|
|
1917
1924
|
* Reports a cart change for the live visitor map without counting a page view
|
|
1918
1925
|
* (`event_type: 'presence'`). Use it when the cart changes on a page that was
|
|
1919
|
-
* already counted. A cart state the server
|
|
1920
|
-
*
|
|
1921
|
-
*
|
|
1922
|
-
*
|
|
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.
|
|
1923
1930
|
*
|
|
1924
|
-
* The check runs when the change's turn
|
|
1925
|
-
*
|
|
1926
|
-
* state is on the wire must still go out
|
|
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.
|
|
1927
1934
|
*/
|
|
1928
1935
|
declare function trackCartActivity(cartValue: number, itemCount: number): Promise<void>;
|
|
1929
1936
|
|
package/dist/index.js
CHANGED
|
@@ -5372,13 +5372,17 @@ function normalizeFeedback(raw, index) {
|
|
|
5372
5372
|
created_at
|
|
5373
5373
|
};
|
|
5374
5374
|
}
|
|
5375
|
-
async function getShopReviewsPage(cursor) {
|
|
5375
|
+
async function getShopReviewsPage(cursor, options = {}) {
|
|
5376
5376
|
const config = getConfig();
|
|
5377
5377
|
const query = new URLSearchParams();
|
|
5378
5378
|
query.set("limit", String(REVIEWS_PAGE_LIMIT));
|
|
5379
5379
|
if (typeof cursor === "string" && cursor.trim().length > 0) {
|
|
5380
5380
|
query.set("cursor", cursor);
|
|
5381
5381
|
}
|
|
5382
|
+
const product = typeof options.product === "string" ? options.product.trim() : "";
|
|
5383
|
+
if (product) {
|
|
5384
|
+
query.set("product", product);
|
|
5385
|
+
}
|
|
5382
5386
|
const querySuffix = `?${query.toString()}`;
|
|
5383
5387
|
const response = await get(
|
|
5384
5388
|
`${buildEndpoint("/v1/storefront/feedback/shop/:storeSlug", {
|
|
@@ -5386,7 +5390,7 @@ async function getShopReviewsPage(cursor) {
|
|
|
5386
5390
|
})}${querySuffix}`,
|
|
5387
5391
|
{
|
|
5388
5392
|
cache: {
|
|
5389
|
-
key: `reviews:${config.storeSlug}:${cursor ?? "start"}:${REVIEWS_PAGE_LIMIT}`,
|
|
5393
|
+
key: `reviews:${config.storeSlug}:${product || "all"}:${cursor ?? "start"}:${REVIEWS_PAGE_LIMIT}`,
|
|
5390
5394
|
ttl: REVIEWS_CACHE_TTL,
|
|
5391
5395
|
staleWhileRevalidate: true
|
|
5392
5396
|
}
|
|
@@ -5401,10 +5405,7 @@ async function getShopReviewsPage(cursor) {
|
|
|
5401
5405
|
}
|
|
5402
5406
|
};
|
|
5403
5407
|
}
|
|
5404
|
-
return
|
|
5405
|
-
success: false,
|
|
5406
|
-
message: response.message
|
|
5407
|
-
};
|
|
5408
|
+
return forwardFailure(response);
|
|
5408
5409
|
}
|
|
5409
5410
|
async function getShopReviews() {
|
|
5410
5411
|
const allFeedback = [];
|
|
@@ -5874,10 +5875,7 @@ async function getPages() {
|
|
|
5874
5875
|
data: response.data.pages
|
|
5875
5876
|
};
|
|
5876
5877
|
}
|
|
5877
|
-
return
|
|
5878
|
-
success: false,
|
|
5879
|
-
message: response.message
|
|
5880
|
-
};
|
|
5878
|
+
return forwardFailure(response);
|
|
5881
5879
|
}
|
|
5882
5880
|
async function getPage(slug) {
|
|
5883
5881
|
const config = getConfig();
|
|
@@ -5900,10 +5898,7 @@ async function getPage(slug) {
|
|
|
5900
5898
|
data: response.data.page
|
|
5901
5899
|
};
|
|
5902
5900
|
}
|
|
5903
|
-
return
|
|
5904
|
-
success: false,
|
|
5905
|
-
message: response.message
|
|
5906
|
-
};
|
|
5901
|
+
return forwardFailure(response);
|
|
5907
5902
|
}
|
|
5908
5903
|
|
|
5909
5904
|
// ../sdk/src/modules/navigation.ts
|
|
@@ -6087,6 +6082,7 @@ function isPlatformPageTrackerActive() {
|
|
|
6087
6082
|
// ../sdk/src/modules/analytics.ts
|
|
6088
6083
|
var CONFIRMED_CART_STORAGE_PREFIX = "reported_cart_";
|
|
6089
6084
|
var IN_FLIGHT_CART_STORAGE_PREFIX = "reporting_cart_";
|
|
6085
|
+
var CART_SEQUENCE_STORAGE_PREFIX = "presence_cart_seq_";
|
|
6090
6086
|
var CART_CLAIM_TTL_MS = 1e4;
|
|
6091
6087
|
function confirmedCartKey(storeSlug) {
|
|
6092
6088
|
return `${CONFIRMED_CART_STORAGE_PREFIX}${storeSlug}`;
|
|
@@ -6094,33 +6090,77 @@ function confirmedCartKey(storeSlug) {
|
|
|
6094
6090
|
function inFlightCartKey(storeSlug) {
|
|
6095
6091
|
return `${IN_FLIGHT_CART_STORAGE_PREFIX}${storeSlug}`;
|
|
6096
6092
|
}
|
|
6093
|
+
function cartSequenceKey(storeSlug) {
|
|
6094
|
+
return `${CART_SEQUENCE_STORAGE_PREFIX}${storeSlug}`;
|
|
6095
|
+
}
|
|
6097
6096
|
function cartSnapshot(cartValue, itemCount) {
|
|
6098
6097
|
return `${cartValue ?? ""}|${itemCount ?? ""}`;
|
|
6099
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
|
+
}
|
|
6100
6136
|
function readInFlightCart(storeSlug) {
|
|
6101
6137
|
const claim = getItem(inFlightCartKey(storeSlug));
|
|
6102
|
-
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") {
|
|
6103
6139
|
return null;
|
|
6104
6140
|
}
|
|
6105
6141
|
return claim;
|
|
6106
6142
|
}
|
|
6107
6143
|
function isCartReportedOrInFlight(storeSlug, snapshot) {
|
|
6108
|
-
|
|
6144
|
+
const confirmed = readConfirmedCart(storeSlug);
|
|
6109
6145
|
const claim = readInFlightCart(storeSlug);
|
|
6110
|
-
|
|
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;
|
|
6111
6149
|
}
|
|
6112
6150
|
function createClaimToken() {
|
|
6113
6151
|
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
6114
6152
|
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;
|
|
6115
6153
|
}
|
|
6116
|
-
function claimCartReport(storeSlug, cartValue, itemCount) {
|
|
6154
|
+
function claimCartReport(storeSlug, seq, cartValue, itemCount) {
|
|
6117
6155
|
const snapshot = cartSnapshot(cartValue, itemCount);
|
|
6118
6156
|
const token = createClaimToken();
|
|
6119
|
-
setItem(inFlightCartKey(storeSlug), { snapshot, token, at: Date.now() });
|
|
6157
|
+
setItem(inFlightCartKey(storeSlug), { snapshot, seq, token, at: Date.now() });
|
|
6120
6158
|
return (delivered) => {
|
|
6121
|
-
if (
|
|
6122
|
-
|
|
6123
|
-
|
|
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));
|
|
6124
6164
|
};
|
|
6125
6165
|
}
|
|
6126
6166
|
var cartSendTail = Promise.resolve();
|
|
@@ -6161,42 +6201,50 @@ async function postPing(body) {
|
|
|
6161
6201
|
async function trackPageView(cartValue, itemCount) {
|
|
6162
6202
|
if (!isInitialized()) return;
|
|
6163
6203
|
if (typeof document === "undefined") return;
|
|
6204
|
+
const config = getConfig();
|
|
6164
6205
|
const carriesCart = cartValue !== void 0 || itemCount !== void 0;
|
|
6206
|
+
const reservedOrder = carriesCart ? reserveCartOrder(config.storeSlug) : void 0;
|
|
6165
6207
|
if (isPlatformPageTrackerActive()) {
|
|
6166
|
-
if (
|
|
6208
|
+
if (reservedOrder) await enqueueCartSend(async () => sendCartActivity(await reservedOrder, cartValue, itemCount));
|
|
6167
6209
|
return;
|
|
6168
6210
|
}
|
|
6169
|
-
const config = getConfig();
|
|
6170
6211
|
const sendView = async () => {
|
|
6171
|
-
const
|
|
6212
|
+
const order2 = reservedOrder ? await reservedOrder : void 0;
|
|
6213
|
+
const settle = order2 ? claimCartReport(config.storeSlug, order2.seq, cartValue, itemCount) : void 0;
|
|
6172
6214
|
const delivered = await postPing({
|
|
6173
6215
|
referer: document.referrer || void 0,
|
|
6174
6216
|
cart_value: cartValue,
|
|
6175
6217
|
item_count: itemCount,
|
|
6218
|
+
cart_seq: order2?.seq,
|
|
6219
|
+
cart_seq_scope: order2?.scope,
|
|
6176
6220
|
connection_id: getStorefrontConnectionId(config.storeSlug) ?? void 0,
|
|
6177
6221
|
path: resolveStorefrontPagePath(),
|
|
6178
6222
|
utm: resolveUtmParameters(config.storeSlug)
|
|
6179
6223
|
});
|
|
6180
6224
|
settle?.(delivered);
|
|
6181
6225
|
};
|
|
6182
|
-
await (
|
|
6226
|
+
await (reservedOrder ? enqueueCartSend(sendView) : sendView());
|
|
6183
6227
|
}
|
|
6184
6228
|
async function trackCartActivity(cartValue, itemCount) {
|
|
6185
6229
|
if (!isInitialized()) return;
|
|
6186
6230
|
if (typeof document === "undefined") return;
|
|
6187
6231
|
const snapshot = cartSnapshot(cartValue, itemCount);
|
|
6232
|
+
const reservedOrder = reserveCartOrder(getConfig().storeSlug);
|
|
6188
6233
|
await enqueueCartSend(async (isLatest) => {
|
|
6234
|
+
const order2 = await reservedOrder;
|
|
6189
6235
|
if (!isLatest() || isCartReportedOrInFlight(getConfig().storeSlug, snapshot)) return;
|
|
6190
|
-
await sendCartActivity(cartValue, itemCount);
|
|
6236
|
+
await sendCartActivity(order2, cartValue, itemCount);
|
|
6191
6237
|
});
|
|
6192
6238
|
}
|
|
6193
|
-
async function sendCartActivity(cartValue, itemCount) {
|
|
6239
|
+
async function sendCartActivity(order2, cartValue, itemCount) {
|
|
6194
6240
|
const config = getConfig();
|
|
6195
|
-
const settle = claimCartReport(config.storeSlug, cartValue, itemCount);
|
|
6241
|
+
const settle = claimCartReport(config.storeSlug, order2.seq, cartValue, itemCount);
|
|
6196
6242
|
const delivered = await postPing({
|
|
6197
6243
|
event_type: "presence",
|
|
6198
6244
|
cart_value: cartValue,
|
|
6199
6245
|
item_count: itemCount,
|
|
6246
|
+
cart_seq: order2.seq,
|
|
6247
|
+
cart_seq_scope: order2.scope,
|
|
6200
6248
|
connection_id: getStorefrontConnectionId(config.storeSlug) ?? void 0,
|
|
6201
6249
|
path: resolveStorefrontPagePath()
|
|
6202
6250
|
});
|