@shoppexio/storefront 1.0.103 → 1.0.105
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 +10 -0
- package/dist/{chunk-ESI3HWQF.js → chunk-NTLZJQ6I.js} +58 -3
- package/dist/{chunk-ESI3HWQF.js.map → chunk-NTLZJQ6I.js.map} +1 -1
- package/dist/customer.cjs +56 -42
- package/dist/customer.cjs.map +1 -1
- package/dist/customer.d.cts +5 -13
- package/dist/customer.d.ts +5 -13
- package/dist/customer.js +2 -41
- package/dist/customer.js.map +1 -1
- package/dist/index.cjs +1122 -999
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -3
- package/dist/index.d.ts +34 -3
- package/dist/index.js +1063 -997
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -5,11 +5,12 @@ import {
|
|
|
5
5
|
customerPortalDashboardSchema,
|
|
6
6
|
customerPortalInvoiceDetailSchema,
|
|
7
7
|
customerPortalNotificationPreferencesSchema,
|
|
8
|
+
customerPortalOrderReviewSchema,
|
|
8
9
|
customerPortalPaginatedInvoicesSchema,
|
|
9
10
|
customerWarrantyClaimSchema,
|
|
10
11
|
customerWarrantyListSchema,
|
|
11
12
|
storefrontStorageKey
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-NTLZJQ6I.js";
|
|
13
14
|
|
|
14
15
|
// ../sdk/src/core/errors.ts
|
|
15
16
|
var CURRENCY_UNAVAILABLE_ERROR_CODE = "errors.storefront.currency_unavailable";
|
|
@@ -836,1101 +837,1141 @@ async function captureAffiliateFromUrl(param = "ref") {
|
|
|
836
837
|
return holdsOtherCode ? held.code : code;
|
|
837
838
|
}
|
|
838
839
|
|
|
839
|
-
// ../
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
840
|
+
// ../contracts/src/storefront-consent-choice.ts
|
|
841
|
+
var COOKIE_CONSENT_VERSION = 1;
|
|
842
|
+
var COOKIE_CONSENT_COOKIE = "shoppex_consent";
|
|
843
|
+
var COOKIE_CONSENT_CHECKOUT_HASH_KEY = "shoppex_consent";
|
|
844
|
+
var COOKIE_CONSENT_VALUE = new RegExp(`^v${COOKIE_CONSENT_VERSION}\\.f([01])\\.a([01])\\.m([01])$`);
|
|
845
|
+
function parseCookieConsentValue(value) {
|
|
846
|
+
const match = value ? COOKIE_CONSENT_VALUE.exec(value) : null;
|
|
847
|
+
return match ? { functional: match[1] === "1", analytics: match[2] === "1", marketing: match[3] === "1" } : null;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// ../sdk/src/core/analytics-session.ts
|
|
851
|
+
var ANALYTICS_SESSION_IDLE_MS = 30 * 60 * 1e3;
|
|
852
|
+
var ANALYTICS_VISITS_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
853
|
+
var ANALYTICS_VISITS_MAX = 20;
|
|
854
|
+
var MAX_ENGAGED_MS_PER_PAGE = 30 * 60 * 1e3;
|
|
855
|
+
var UTM_STORAGE_PREFIX = "shoppex:utm:";
|
|
856
|
+
var analyticsSessionKey = (storeSlug) => `analytics_session_${storeSlug}`;
|
|
857
|
+
var analyticsVisitsKey = (storeSlug) => `analytics_visits_${storeSlug}`;
|
|
858
|
+
function pageLanding() {
|
|
859
|
+
let r = "";
|
|
860
|
+
try {
|
|
861
|
+
r = document.referrer ? new URL(document.referrer).hostname : "";
|
|
862
|
+
} catch {
|
|
845
863
|
}
|
|
846
|
-
|
|
864
|
+
const params = new URLSearchParams(window.location?.search ?? "");
|
|
865
|
+
const u = ["source", "medium", "campaign"].map((key) => params.get(`utm_${key}`)?.trim().toLowerCase() ?? "").join("|");
|
|
866
|
+
return { r: r === window.location?.hostname ? "" : r, u: u === "||" ? "" : u };
|
|
847
867
|
}
|
|
848
|
-
function
|
|
849
|
-
if (
|
|
850
|
-
|
|
868
|
+
function readAnalyticsConsent() {
|
|
869
|
+
if (typeof document === "undefined" || typeof navigator === "undefined") return "unknown";
|
|
870
|
+
const privacy = navigator;
|
|
871
|
+
if (privacy.globalPrivacyControl === true || privacy.doNotTrack === "1") return "declined";
|
|
872
|
+
const prefix = `${COOKIE_CONSENT_COOKIE}=`;
|
|
873
|
+
let raw;
|
|
874
|
+
try {
|
|
875
|
+
raw = document.cookie.split(/;\s*/).find((part) => part.startsWith(prefix))?.slice(prefix.length);
|
|
876
|
+
} catch {
|
|
877
|
+
return "unknown";
|
|
878
|
+
}
|
|
879
|
+
const choice = parseCookieConsentValue(raw ?? null);
|
|
880
|
+
if (!choice) return "unknown";
|
|
881
|
+
return choice.analytics ? "granted" : "declined";
|
|
851
882
|
}
|
|
852
|
-
function
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
return
|
|
883
|
+
function readVisits(storeSlug, now) {
|
|
884
|
+
const stored = getItem(analyticsVisitsKey(storeSlug));
|
|
885
|
+
if (!Array.isArray(stored)) return [];
|
|
886
|
+
return stored.filter((visit) => typeof visit?.id === "string" && typeof visit.at === "number" && now - visit.at < ANALYTICS_VISITS_WINDOW_MS);
|
|
856
887
|
}
|
|
857
|
-
function
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
variant_id: input.variant_id,
|
|
861
|
-
price_variant_id: input.price_variant_id ?? null,
|
|
862
|
-
addons: normalizeAddons(input.addons),
|
|
863
|
-
custom_fields: normalizeCustomFields(input.custom_fields),
|
|
864
|
-
unit_price: typeof input.price_data?.unit_price === "number" && Number.isFinite(input.price_data.unit_price) ? input.price_data.unit_price : null,
|
|
865
|
-
pay_what_you_want_price: typeof input.pay_what_you_want_price === "number" && Number.isFinite(input.pay_what_you_want_price) ? input.pay_what_you_want_price : null
|
|
866
|
-
};
|
|
867
|
-
return hashString(JSON.stringify(payload));
|
|
888
|
+
function forgetAnalyticsData(storeSlug) {
|
|
889
|
+
removeItem(analyticsSessionKey(storeSlug), "sessionStorage");
|
|
890
|
+
removeItem(analyticsVisitsKey(storeSlug));
|
|
868
891
|
}
|
|
869
|
-
function
|
|
870
|
-
|
|
871
|
-
|
|
892
|
+
function analyticsSessionId(storeSlug, options, now = Date.now()) {
|
|
893
|
+
removeItem(`presence_connection_${storeSlug}`);
|
|
894
|
+
if (readAnalyticsConsent() !== "granted") {
|
|
895
|
+
forgetAnalyticsData(storeSlug);
|
|
896
|
+
return null;
|
|
872
897
|
}
|
|
873
|
-
|
|
898
|
+
const stored = getItem(analyticsSessionKey(storeSlug), "sessionStorage");
|
|
899
|
+
const live = stored && typeof stored.id === "string" && typeof stored.at === "number" && now - stored.at < ANALYTICS_SESSION_IDLE_MS ? stored.id : null;
|
|
900
|
+
if (!options.touch) return live;
|
|
901
|
+
const landing = pageLanding();
|
|
902
|
+
const newLanding = landing.r && landing.r !== stored?.r || landing.u && landing.u !== stored?.u;
|
|
903
|
+
if (live && !newLanding) {
|
|
904
|
+
setItem(analyticsSessionKey(storeSlug), { ...stored, id: live, at: now }, "sessionStorage");
|
|
905
|
+
return live;
|
|
906
|
+
}
|
|
907
|
+
if (typeof crypto === "undefined" || typeof crypto.randomUUID !== "function") return null;
|
|
908
|
+
if (landing.r) {
|
|
909
|
+
try {
|
|
910
|
+
globalThis.localStorage.removeItem(storefrontStorageKey(`${UTM_STORAGE_PREFIX}${storeSlug}`));
|
|
911
|
+
} catch {
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
const id = crypto.randomUUID();
|
|
915
|
+
setItem(analyticsSessionKey(storeSlug), { id, at: now, ...landing }, "sessionStorage");
|
|
916
|
+
setItem(
|
|
917
|
+
analyticsVisitsKey(storeSlug),
|
|
918
|
+
[{ id, at: now }, ...readVisits(storeSlug, now)].slice(0, ANALYTICS_VISITS_MAX)
|
|
919
|
+
);
|
|
920
|
+
return id;
|
|
874
921
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
const sign = value < 0 ? -1 : 1;
|
|
885
|
-
const shifted = shiftDecimal(Math.abs(value), PAYABLE_AMOUNT_DECIMAL_PLACES);
|
|
886
|
-
const rounded = Math.round(shifted);
|
|
887
|
-
if (rounded === 0) return 0;
|
|
888
|
-
return sign * shiftDecimal(rounded, -PAYABLE_AMOUNT_DECIMAL_PLACES);
|
|
922
|
+
function checkoutAttribution(storeSlug, now = Date.now()) {
|
|
923
|
+
const consent = readAnalyticsConsent();
|
|
924
|
+
if (consent === "declined") {
|
|
925
|
+
forgetAnalyticsData(storeSlug);
|
|
926
|
+
return { declined: true };
|
|
927
|
+
}
|
|
928
|
+
if (consent === "unknown") return void 0;
|
|
929
|
+
const sessionIds = readVisits(storeSlug, now).map((visit) => visit.id);
|
|
930
|
+
return sessionIds.length > 0 ? { session_ids: sessionIds } : void 0;
|
|
889
931
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
932
|
+
var engagedMs = 0;
|
|
933
|
+
var visibleSince = null;
|
|
934
|
+
var clockInstalled = false;
|
|
935
|
+
var pageViewed = false;
|
|
936
|
+
var pageSession = null;
|
|
937
|
+
function markPageViewed(sessionId) {
|
|
938
|
+
pageViewed = true;
|
|
939
|
+
pageSession = sessionId;
|
|
893
940
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
var STORAGE_KEYS = {
|
|
897
|
-
cart: "cart",
|
|
898
|
-
cartBackup: "cart_backup",
|
|
899
|
-
meta: "cart_meta",
|
|
900
|
-
metaBackup: "cart_backup_meta",
|
|
901
|
-
coupon: "cart_coupon",
|
|
902
|
-
couponBackup: "cart_coupon_backup"
|
|
903
|
-
};
|
|
904
|
-
function getStorageKey(type) {
|
|
905
|
-
return `${STORAGE_KEYS[type]}_${getConfig().storeSlug}`;
|
|
941
|
+
function currentPageSession() {
|
|
942
|
+
return pageSession;
|
|
906
943
|
}
|
|
907
|
-
function
|
|
908
|
-
return
|
|
944
|
+
function isPageViewed() {
|
|
945
|
+
return pageViewed;
|
|
909
946
|
}
|
|
910
|
-
function
|
|
911
|
-
|
|
947
|
+
function settleClock(now) {
|
|
948
|
+
if (visibleSince !== null) {
|
|
949
|
+
engagedMs += now - visibleSince;
|
|
950
|
+
visibleSince = document.visibilityState === "visible" ? now : null;
|
|
951
|
+
}
|
|
912
952
|
}
|
|
913
|
-
|
|
914
|
-
|
|
953
|
+
var consentListenerInstalled = false;
|
|
954
|
+
function onAnalyticsConsent(onYes) {
|
|
955
|
+
if (consentListenerInstalled || typeof window === "undefined") return;
|
|
956
|
+
consentListenerInstalled = true;
|
|
957
|
+
window.addEventListener("shoppex:consent", () => {
|
|
958
|
+
if (readAnalyticsConsent() === "granted") onYes();
|
|
959
|
+
});
|
|
915
960
|
}
|
|
916
|
-
function
|
|
917
|
-
|
|
961
|
+
function installEngagedClock(onHidden) {
|
|
962
|
+
if (clockInstalled || typeof document === "undefined") return;
|
|
963
|
+
clockInstalled = true;
|
|
964
|
+
visibleSince = document.visibilityState === "visible" ? Date.now() : null;
|
|
965
|
+
document.addEventListener("visibilitychange", () => {
|
|
966
|
+
const now = Date.now();
|
|
967
|
+
if (document.visibilityState === "visible") {
|
|
968
|
+
visibleSince ?? (visibleSince = now);
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
settleClock(now);
|
|
972
|
+
visibleSince = null;
|
|
973
|
+
onHidden();
|
|
974
|
+
});
|
|
975
|
+
window.addEventListener("pagehide", () => {
|
|
976
|
+
settleClock(Date.now());
|
|
977
|
+
visibleSince = null;
|
|
978
|
+
onHidden();
|
|
979
|
+
});
|
|
980
|
+
window.addEventListener("pageshow", (event) => {
|
|
981
|
+
if (event.persisted && document.visibilityState === "visible") visibleSince = Date.now();
|
|
982
|
+
});
|
|
918
983
|
}
|
|
919
|
-
function
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
984
|
+
function takeEngagedMs(now = Date.now()) {
|
|
985
|
+
settleClock(now);
|
|
986
|
+
const taken = Math.min(Math.max(0, Math.round(engagedMs)), MAX_ENGAGED_MS_PER_PAGE);
|
|
987
|
+
engagedMs = 0;
|
|
988
|
+
return taken;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// ../sdk/src/core/attribution.ts
|
|
992
|
+
var TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
993
|
+
var UTM_KEYS = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"];
|
|
994
|
+
var MAX_VALUE_LENGTH = 255;
|
|
995
|
+
function readFromUrl(search) {
|
|
996
|
+
let params;
|
|
997
|
+
try {
|
|
998
|
+
params = new URLSearchParams(search);
|
|
999
|
+
} catch {
|
|
1000
|
+
return {};
|
|
924
1001
|
}
|
|
925
|
-
|
|
1002
|
+
const utm = {};
|
|
1003
|
+
for (const key of UTM_KEYS) {
|
|
1004
|
+
const value = params.get(key)?.trim();
|
|
1005
|
+
if (value) {
|
|
1006
|
+
utm[key] = value.slice(0, MAX_VALUE_LENGTH);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
return utm;
|
|
926
1010
|
}
|
|
927
|
-
function
|
|
928
|
-
return
|
|
1011
|
+
function storageKey(storeSlug) {
|
|
1012
|
+
return `${UTM_STORAGE_PREFIX}${storeSlug}`;
|
|
929
1013
|
}
|
|
930
|
-
function
|
|
931
|
-
|
|
932
|
-
|
|
1014
|
+
function readStored(storeSlug) {
|
|
1015
|
+
try {
|
|
1016
|
+
const raw = window.localStorage.getItem(storefrontStorageKey(storageKey(storeSlug)));
|
|
1017
|
+
if (!raw) return {};
|
|
1018
|
+
const parsed = JSON.parse(raw);
|
|
1019
|
+
if (!parsed || typeof parsed.at !== "number" || typeof parsed.utm !== "object") return {};
|
|
1020
|
+
if (Date.now() - parsed.at > TTL_MS) {
|
|
1021
|
+
window.localStorage.removeItem(storefrontStorageKey(storageKey(storeSlug)));
|
|
1022
|
+
return {};
|
|
1023
|
+
}
|
|
1024
|
+
const utm = {};
|
|
1025
|
+
for (const key of UTM_KEYS) {
|
|
1026
|
+
const value = parsed.utm?.[key];
|
|
1027
|
+
if (typeof value === "string" && value.length > 0) {
|
|
1028
|
+
utm[key] = value.slice(0, MAX_VALUE_LENGTH);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
return utm;
|
|
1032
|
+
} catch {
|
|
1033
|
+
return {};
|
|
933
1034
|
}
|
|
934
|
-
return Math.floor(value);
|
|
935
1035
|
}
|
|
936
|
-
function
|
|
937
|
-
|
|
938
|
-
|
|
1036
|
+
function writeStored(storeSlug, utm) {
|
|
1037
|
+
try {
|
|
1038
|
+
const payload = { utm, at: Date.now() };
|
|
1039
|
+
window.localStorage.setItem(storefrontStorageKey(storageKey(storeSlug)), JSON.stringify(payload));
|
|
1040
|
+
} catch {
|
|
1041
|
+
}
|
|
939
1042
|
}
|
|
940
|
-
function
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
return null;
|
|
1043
|
+
function resolveUtmParameters(storeSlug) {
|
|
1044
|
+
if (typeof window === "undefined" || typeof window.location === "undefined") {
|
|
1045
|
+
return void 0;
|
|
944
1046
|
}
|
|
945
|
-
const
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
}
|
|
950
|
-
function normalizeCartItems(value) {
|
|
951
|
-
if (!Array.isArray(value)) return [];
|
|
952
|
-
const normalized = [];
|
|
953
|
-
for (const entry of value) {
|
|
954
|
-
if (!entry || typeof entry !== "object") continue;
|
|
955
|
-
const record = entry;
|
|
956
|
-
const productId = typeof record.product_id === "string" ? record.product_id.trim() : "";
|
|
957
|
-
const variantId = typeof record.variant_id === "string" ? record.variant_id.trim() : "";
|
|
958
|
-
const quantity = Number(record.quantity);
|
|
959
|
-
if (!productId || !variantId || !Number.isFinite(quantity) || quantity < 1) {
|
|
960
|
-
continue;
|
|
961
|
-
}
|
|
962
|
-
const item = {
|
|
963
|
-
line_id: "",
|
|
964
|
-
product_id: productId,
|
|
965
|
-
variant_id: variantId,
|
|
966
|
-
quantity: Math.floor(quantity)
|
|
967
|
-
};
|
|
968
|
-
if (typeof record.price_variant_id === "string") {
|
|
969
|
-
item.price_variant_id = record.price_variant_id;
|
|
970
|
-
}
|
|
971
|
-
if (record.price_data && typeof record.price_data === "object") {
|
|
972
|
-
const priceData = record.price_data;
|
|
973
|
-
if (typeof priceData.unit_price === "number" && Number.isFinite(priceData.unit_price)) {
|
|
974
|
-
item.price_data = { unit_price: priceData.unit_price };
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
if (typeof record.pay_what_you_want_price === "number" && Number.isFinite(record.pay_what_you_want_price)) {
|
|
978
|
-
item.pay_what_you_want_price = record.pay_what_you_want_price;
|
|
979
|
-
}
|
|
980
|
-
if (Array.isArray(record.addons)) {
|
|
981
|
-
item.addons = record.addons;
|
|
982
|
-
}
|
|
983
|
-
if (record.custom_fields && typeof record.custom_fields === "object" && !Array.isArray(record.custom_fields)) {
|
|
984
|
-
item.custom_fields = record.custom_fields;
|
|
985
|
-
}
|
|
986
|
-
if (typeof record.title === "string") {
|
|
987
|
-
item.title = record.title;
|
|
988
|
-
}
|
|
989
|
-
if (typeof record.variant_title === "string") {
|
|
990
|
-
item.variant_title = record.variant_title;
|
|
991
|
-
}
|
|
992
|
-
if (typeof record.image_url === "string") {
|
|
993
|
-
item.image_url = record.image_url;
|
|
994
|
-
}
|
|
995
|
-
if (Array.isArray(record.addon_labels)) {
|
|
996
|
-
item.addon_labels = record.addon_labels.filter((label) => typeof label === "string");
|
|
997
|
-
}
|
|
998
|
-
if (typeof record.max_quantity === "number" && Number.isFinite(record.max_quantity)) {
|
|
999
|
-
item.max_quantity = record.max_quantity;
|
|
1000
|
-
}
|
|
1001
|
-
if (typeof record.min_quantity === "number" && Number.isFinite(record.min_quantity)) {
|
|
1002
|
-
item.min_quantity = record.min_quantity;
|
|
1047
|
+
const fromUrl = readFromUrl(window.location.search);
|
|
1048
|
+
if (readAnalyticsConsent() !== "granted") {
|
|
1049
|
+
try {
|
|
1050
|
+
window.localStorage.removeItem(storefrontStorageKey(storageKey(storeSlug)));
|
|
1051
|
+
} catch {
|
|
1003
1052
|
}
|
|
1004
|
-
|
|
1005
|
-
const storedLineId = typeof record.line_id === "string" ? record.line_id.trim() : "";
|
|
1006
|
-
item.line_id = storedLineId || computeCartLineId(item);
|
|
1007
|
-
normalized.push(ensureCartLineId(item));
|
|
1053
|
+
return Object.keys(fromUrl).length > 0 ? fromUrl : void 0;
|
|
1008
1054
|
}
|
|
1009
|
-
|
|
1055
|
+
if (Object.keys(fromUrl).length > 0) {
|
|
1056
|
+
writeStored(storeSlug, fromUrl);
|
|
1057
|
+
return fromUrl;
|
|
1058
|
+
}
|
|
1059
|
+
const stored = readStored(storeSlug);
|
|
1060
|
+
return Object.keys(stored).length > 0 ? stored : void 0;
|
|
1010
1061
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1062
|
+
|
|
1063
|
+
// ../sdk/src/core/page-path.ts
|
|
1064
|
+
var MAX_PATH_LENGTH = 256;
|
|
1065
|
+
function resolveStorefrontPagePath() {
|
|
1066
|
+
if (typeof window === "undefined") return void 0;
|
|
1067
|
+
let pathname;
|
|
1068
|
+
try {
|
|
1069
|
+
const location = window.location;
|
|
1070
|
+
pathname = typeof location?.pathname === "string" && location.pathname ? location.pathname : new URL(String(location?.href ?? "")).pathname;
|
|
1071
|
+
} catch {
|
|
1072
|
+
return void 0;
|
|
1073
|
+
}
|
|
1074
|
+
if (!pathname || !pathname.startsWith("/")) return void 0;
|
|
1075
|
+
return pathname.slice(0, MAX_PATH_LENGTH);
|
|
1013
1076
|
}
|
|
1014
|
-
|
|
1015
|
-
|
|
1077
|
+
|
|
1078
|
+
// ../sdk/src/core/page-tracker.ts
|
|
1079
|
+
var PLATFORM_PAGE_TRACKER_FLAG = "__shoppexPageTracker";
|
|
1080
|
+
function isPlatformPageTrackerActive() {
|
|
1081
|
+
if (typeof window === "undefined") return false;
|
|
1082
|
+
try {
|
|
1083
|
+
return Boolean(window[PLATFORM_PAGE_TRACKER_FLAG]);
|
|
1084
|
+
} catch {
|
|
1085
|
+
return false;
|
|
1086
|
+
}
|
|
1016
1087
|
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1088
|
+
|
|
1089
|
+
// ../sdk/src/modules/analytics.ts
|
|
1090
|
+
var CONFIRMED_CART_STORAGE_PREFIX = "reported_cart_";
|
|
1091
|
+
var IN_FLIGHT_CART_STORAGE_PREFIX = "reporting_cart_";
|
|
1092
|
+
var CART_SEQUENCE_STORAGE_PREFIX = "presence_cart_seq_";
|
|
1093
|
+
var CART_CLAIM_TTL_MS = 1e4;
|
|
1094
|
+
function confirmedCartKey(storeSlug) {
|
|
1095
|
+
return `${CONFIRMED_CART_STORAGE_PREFIX}${storeSlug}`;
|
|
1019
1096
|
}
|
|
1020
|
-
function
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1097
|
+
function inFlightCartKey(storeSlug) {
|
|
1098
|
+
return `${IN_FLIGHT_CART_STORAGE_PREFIX}${storeSlug}`;
|
|
1099
|
+
}
|
|
1100
|
+
function cartSequenceKey(storeSlug) {
|
|
1101
|
+
return `${CART_SEQUENCE_STORAGE_PREFIX}${storeSlug}`;
|
|
1102
|
+
}
|
|
1103
|
+
function cartSnapshot(cartValue, itemCount) {
|
|
1104
|
+
return `${cartValue ?? ""}|${itemCount ?? ""}`;
|
|
1105
|
+
}
|
|
1106
|
+
function isCartSequence(value) {
|
|
1107
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
1108
|
+
}
|
|
1109
|
+
var CART_SEQUENCE_SCOPE_MAX_LENGTH = 64;
|
|
1110
|
+
function readCartCounter(storeSlug) {
|
|
1111
|
+
const counter = getItem(cartSequenceKey(storeSlug));
|
|
1112
|
+
if (!counter || typeof counter.scope !== "string" || counter.scope.length === 0 || counter.scope.length > CART_SEQUENCE_SCOPE_MAX_LENGTH || !isCartSequence(counter.seq)) {
|
|
1024
1113
|
return null;
|
|
1025
1114
|
}
|
|
1026
|
-
|
|
1027
|
-
return normalizedCoupon;
|
|
1115
|
+
return counter;
|
|
1028
1116
|
}
|
|
1029
|
-
|
|
1030
|
-
|
|
1117
|
+
var lastCartOrder = null;
|
|
1118
|
+
function nextCartOrder(storeSlug) {
|
|
1119
|
+
const stored = readCartCounter(storeSlug);
|
|
1120
|
+
const scope = stored?.scope ?? lastCartOrder?.scope ?? createClaimToken();
|
|
1121
|
+
const floor = Math.max(stored?.seq ?? 0, lastCartOrder?.scope === scope ? lastCartOrder.seq : 0);
|
|
1122
|
+
const order2 = { scope, seq: Math.max(Date.now(), floor + 1) };
|
|
1123
|
+
lastCartOrder = order2;
|
|
1124
|
+
setItem(cartSequenceKey(storeSlug), order2);
|
|
1125
|
+
return order2;
|
|
1031
1126
|
}
|
|
1032
|
-
function
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
const previous = getCartMetadata();
|
|
1037
|
-
const nextMeta = {
|
|
1038
|
-
created_at: previous?.created_at ?? now,
|
|
1039
|
-
last_modified: now,
|
|
1040
|
-
version: (previous?.version ?? 0) + 1,
|
|
1041
|
-
checksum: computeChecksum(normalizedCart)
|
|
1042
|
-
};
|
|
1043
|
-
writeCartStorage("meta", nextMeta);
|
|
1044
|
-
if (normalizedCart.length === 0) {
|
|
1045
|
-
clearCartCoupon();
|
|
1046
|
-
}
|
|
1127
|
+
function cartSequenceLocks() {
|
|
1128
|
+
if (typeof navigator === "undefined") return null;
|
|
1129
|
+
const locks = navigator.locks;
|
|
1130
|
+
return locks && typeof locks.request === "function" ? locks : null;
|
|
1047
1131
|
}
|
|
1048
|
-
function
|
|
1049
|
-
const
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
const nextMeta = {
|
|
1054
|
-
created_at: base?.created_at ?? now,
|
|
1055
|
-
last_modified: now,
|
|
1056
|
-
version: base?.version ?? 1,
|
|
1057
|
-
checksum: computeChecksum(normalizedCart)
|
|
1058
|
-
};
|
|
1059
|
-
writeCartStorage("meta", nextMeta);
|
|
1132
|
+
function reserveCartOrder(storeSlug) {
|
|
1133
|
+
const take = () => nextCartOrder(storeSlug);
|
|
1134
|
+
const locks = cartSequenceLocks();
|
|
1135
|
+
if (!locks) return Promise.resolve(take());
|
|
1136
|
+
return locks.request(cartSequenceKey(storeSlug), take).catch(take);
|
|
1060
1137
|
}
|
|
1061
|
-
function
|
|
1062
|
-
const
|
|
1063
|
-
|
|
1138
|
+
function readConfirmedCart(storeSlug) {
|
|
1139
|
+
const confirmed = getItem(confirmedCartKey(storeSlug));
|
|
1140
|
+
if (!confirmed || typeof confirmed.snapshot !== "string" || !isCartSequence(confirmed.seq)) return null;
|
|
1141
|
+
return confirmed;
|
|
1064
1142
|
}
|
|
1065
|
-
function
|
|
1066
|
-
const
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
if (matches.length === 0) {
|
|
1070
|
-
throw new CartError(`No cart line found for ${productId}/${variantId}`);
|
|
1143
|
+
function readInFlightCart(storeSlug) {
|
|
1144
|
+
const claim = getItem(inFlightCartKey(storeSlug));
|
|
1145
|
+
if (!claim || typeof claim.snapshot !== "string" || !isCartSequence(claim.seq) || typeof claim.token !== "string" || typeof claim.at !== "number") {
|
|
1146
|
+
return null;
|
|
1071
1147
|
}
|
|
1072
|
-
return
|
|
1148
|
+
return claim;
|
|
1073
1149
|
}
|
|
1074
|
-
function
|
|
1075
|
-
const
|
|
1076
|
-
|
|
1150
|
+
function isCartReportedOrInFlight(storeSlug, snapshot) {
|
|
1151
|
+
const confirmed = readConfirmedCart(storeSlug);
|
|
1152
|
+
const claim = readInFlightCart(storeSlug);
|
|
1153
|
+
const liveClaim = claim && Date.now() - claim.at < CART_CLAIM_TTL_MS ? claim : null;
|
|
1154
|
+
const expected = liveClaim && (!confirmed || liveClaim.seq > confirmed.seq) ? liveClaim : confirmed;
|
|
1155
|
+
return expected?.snapshot === snapshot;
|
|
1077
1156
|
}
|
|
1078
|
-
function
|
|
1079
|
-
if (
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
addons: options?.addons,
|
|
1091
|
-
custom_fields: options?.custom_fields,
|
|
1092
|
-
price_variant_id: options?.price_variant_id,
|
|
1093
|
-
price_data: options?.price_data,
|
|
1094
|
-
pay_what_you_want_price: options?.pay_what_you_want_price
|
|
1095
|
-
});
|
|
1096
|
-
const existingIndex = cart.findIndex((item) => item.line_id === lineId);
|
|
1097
|
-
const quantityBeforeAdd = existingIndex >= 0 ? cart[existingIndex].quantity : 0;
|
|
1098
|
-
if (existingIndex >= 0) {
|
|
1099
|
-
cart[existingIndex].quantity += normalizedQuantity;
|
|
1100
|
-
if (options?.addons) {
|
|
1101
|
-
cart[existingIndex].addons = options.addons;
|
|
1102
|
-
}
|
|
1103
|
-
if (options?.custom_fields) {
|
|
1104
|
-
cart[existingIndex].custom_fields = options.custom_fields;
|
|
1105
|
-
}
|
|
1106
|
-
if (options?.price_variant_id) {
|
|
1107
|
-
cart[existingIndex].price_variant_id = options.price_variant_id;
|
|
1108
|
-
}
|
|
1109
|
-
if (options?.price_data) {
|
|
1110
|
-
cart[existingIndex].price_data = options.price_data;
|
|
1157
|
+
function createClaimToken() {
|
|
1158
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
1159
|
+
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;
|
|
1160
|
+
}
|
|
1161
|
+
function claimCartReport(storeSlug, seq, cartValue, itemCount) {
|
|
1162
|
+
const snapshot = cartSnapshot(cartValue, itemCount);
|
|
1163
|
+
const token = createClaimToken();
|
|
1164
|
+
setItem(inFlightCartKey(storeSlug), { snapshot, seq, token, at: Date.now() });
|
|
1165
|
+
return (delivered) => {
|
|
1166
|
+
if (delivered) {
|
|
1167
|
+
const confirmed = readConfirmedCart(storeSlug);
|
|
1168
|
+
if (!confirmed || seq > confirmed.seq) setItem(confirmedCartKey(storeSlug), { snapshot, seq });
|
|
1111
1169
|
}
|
|
1112
|
-
if (
|
|
1113
|
-
|
|
1114
|
-
}
|
|
1115
|
-
applyDisplaySnapshot(cart[existingIndex], options);
|
|
1116
|
-
clampQuantityToBounds(cart[existingIndex]);
|
|
1117
|
-
} else {
|
|
1118
|
-
const pushed = {
|
|
1119
|
-
line_id: lineId,
|
|
1120
|
-
product_id: productId,
|
|
1121
|
-
variant_id: variantId,
|
|
1122
|
-
quantity: normalizedQuantity,
|
|
1123
|
-
addons: options?.addons,
|
|
1124
|
-
custom_fields: options?.custom_fields,
|
|
1125
|
-
price_variant_id: options?.price_variant_id,
|
|
1126
|
-
price_data: options?.price_data,
|
|
1127
|
-
pay_what_you_want_price: options?.pay_what_you_want_price,
|
|
1128
|
-
title: options?.title,
|
|
1129
|
-
variant_title: options?.variant_title,
|
|
1130
|
-
image_url: options?.image_url,
|
|
1131
|
-
addon_labels: options?.addon_labels,
|
|
1132
|
-
max_quantity: options?.max_quantity,
|
|
1133
|
-
min_quantity: options?.min_quantity
|
|
1134
|
-
};
|
|
1135
|
-
clampQuantityToBounds(pushed);
|
|
1136
|
-
cart.push(pushed);
|
|
1137
|
-
}
|
|
1138
|
-
const intendedQuantity = (existingIndex >= 0 ? cart[existingIndex] : cart[cart.length - 1]).quantity;
|
|
1139
|
-
writeCart(cart);
|
|
1140
|
-
const persistedLine = getCart().find((item) => item.line_id === lineId);
|
|
1141
|
-
if (intendedQuantity > quantityBeforeAdd && persistedLine?.quantity === intendedQuantity) {
|
|
1142
|
-
const cartAffiliateCode = getCartCouponSource() === "AFFILIATE" ? getCartCoupon() : null;
|
|
1143
|
-
void trackAffiliateEvent("add_to_cart", cartAffiliateCode ? { code: cartAffiliateCode } : void 0);
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
function clampQuantityToBounds(item) {
|
|
1147
|
-
const min = item.min_quantity;
|
|
1148
|
-
if (typeof min === "number" && Number.isFinite(min) && item.quantity < min) {
|
|
1149
|
-
item.quantity = Math.floor(min);
|
|
1150
|
-
}
|
|
1151
|
-
const max = item.max_quantity;
|
|
1152
|
-
if (typeof max === "number" && Number.isFinite(max) && item.quantity > max) {
|
|
1153
|
-
item.quantity = Math.floor(max);
|
|
1154
|
-
}
|
|
1155
|
-
if (item.quantity < 1) {
|
|
1156
|
-
item.quantity = 1;
|
|
1157
|
-
}
|
|
1170
|
+
if (readInFlightCart(storeSlug)?.token === token) removeItem(inFlightCartKey(storeSlug));
|
|
1171
|
+
};
|
|
1158
1172
|
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
item.min_quantity = options.min_quantity;
|
|
1167
|
-
return;
|
|
1168
|
-
}
|
|
1169
|
-
if (options?.title !== void 0) {
|
|
1170
|
-
item.title = options.title;
|
|
1171
|
-
}
|
|
1172
|
-
if (options?.variant_title !== void 0) {
|
|
1173
|
-
item.variant_title = options.variant_title;
|
|
1174
|
-
}
|
|
1175
|
-
if (options?.image_url !== void 0) {
|
|
1176
|
-
item.image_url = options.image_url;
|
|
1177
|
-
}
|
|
1178
|
-
if (options?.addon_labels !== void 0) {
|
|
1179
|
-
item.addon_labels = options.addon_labels;
|
|
1180
|
-
}
|
|
1181
|
-
if (options?.max_quantity !== void 0) {
|
|
1182
|
-
item.max_quantity = options.max_quantity;
|
|
1183
|
-
}
|
|
1184
|
-
if (options?.min_quantity !== void 0) {
|
|
1185
|
-
item.min_quantity = options.min_quantity;
|
|
1186
|
-
}
|
|
1173
|
+
var cartSendTail = Promise.resolve();
|
|
1174
|
+
var latestCartRequest = 0;
|
|
1175
|
+
function enqueueCartSend(send) {
|
|
1176
|
+
const request2 = ++latestCartRequest;
|
|
1177
|
+
const run = () => send(() => request2 === latestCartRequest);
|
|
1178
|
+
cartSendTail = cartSendTail.then(run, run);
|
|
1179
|
+
return cartSendTail;
|
|
1187
1180
|
}
|
|
1188
|
-
function
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
const
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
const lineId = computeCartLineId({
|
|
1198
|
-
product_id: productId,
|
|
1199
|
-
variant_id: variantId,
|
|
1200
|
-
addons: options?.addons,
|
|
1201
|
-
custom_fields: options?.custom_fields,
|
|
1202
|
-
price_variant_id: options?.price_variant_id,
|
|
1203
|
-
price_data: options?.price_data,
|
|
1204
|
-
pay_what_you_want_price: options?.pay_what_you_want_price
|
|
1181
|
+
async function postPing(body) {
|
|
1182
|
+
const config = getConfig();
|
|
1183
|
+
const controller = typeof AbortController === "undefined" ? void 0 : new AbortController();
|
|
1184
|
+
let timer;
|
|
1185
|
+
const timedOut = new Promise((resolve) => {
|
|
1186
|
+
timer = setTimeout(() => {
|
|
1187
|
+
controller?.abort();
|
|
1188
|
+
resolve(false);
|
|
1189
|
+
}, CART_CLAIM_TTL_MS);
|
|
1205
1190
|
});
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
title: options?.title,
|
|
1222
|
-
variant_title: options?.variant_title,
|
|
1223
|
-
image_url: options?.image_url,
|
|
1224
|
-
addon_labels: options?.addon_labels,
|
|
1225
|
-
max_quantity: options?.max_quantity,
|
|
1226
|
-
min_quantity: options?.min_quantity
|
|
1227
|
-
};
|
|
1228
|
-
clampQuantityToBounds(pushed);
|
|
1229
|
-
cart.push(pushed);
|
|
1230
|
-
writeCart(cart);
|
|
1231
|
-
}
|
|
1232
|
-
function updateCartItem(lineId, updates) {
|
|
1233
|
-
const cart = getCart();
|
|
1234
|
-
const normalizedLineId = lineId.trim();
|
|
1235
|
-
if (!normalizedLineId) {
|
|
1236
|
-
throw new CartError("line_id is required");
|
|
1237
|
-
}
|
|
1238
|
-
const index = cart.findIndex((item) => item.line_id === normalizedLineId);
|
|
1239
|
-
if (index < 0) {
|
|
1240
|
-
throw new CartError("Item not found in cart");
|
|
1241
|
-
}
|
|
1242
|
-
const before = JSON.stringify(cart);
|
|
1243
|
-
if (updates.quantity !== void 0) {
|
|
1244
|
-
const normalizedQuantity = normalizeQuantity(updates.quantity);
|
|
1245
|
-
if (normalizedQuantity < 1) {
|
|
1246
|
-
cart.splice(index, 1);
|
|
1247
|
-
writeCart(cart);
|
|
1248
|
-
return;
|
|
1249
|
-
}
|
|
1250
|
-
cart[index].quantity = normalizedQuantity;
|
|
1251
|
-
}
|
|
1252
|
-
if (updates.addons !== void 0) {
|
|
1253
|
-
cart[index].addons = updates.addons;
|
|
1254
|
-
}
|
|
1255
|
-
if (updates.custom_fields !== void 0) {
|
|
1256
|
-
cart[index].custom_fields = updates.custom_fields;
|
|
1257
|
-
}
|
|
1258
|
-
if (updates.price_variant_id !== void 0) {
|
|
1259
|
-
cart[index].price_variant_id = updates.price_variant_id;
|
|
1260
|
-
}
|
|
1261
|
-
if (updates.price_data !== void 0) {
|
|
1262
|
-
cart[index].price_data = updates.price_data;
|
|
1263
|
-
}
|
|
1264
|
-
if (updates.pay_what_you_want_price !== void 0) {
|
|
1265
|
-
cart[index].pay_what_you_want_price = updates.pay_what_you_want_price;
|
|
1266
|
-
}
|
|
1267
|
-
if (updates.variant_title !== void 0) {
|
|
1268
|
-
if (updates.variant_title === null) delete cart[index].variant_title;
|
|
1269
|
-
else cart[index].variant_title = updates.variant_title;
|
|
1270
|
-
}
|
|
1271
|
-
if (updates.max_quantity !== void 0) {
|
|
1272
|
-
if (updates.max_quantity === null) delete cart[index].max_quantity;
|
|
1273
|
-
else cart[index].max_quantity = updates.max_quantity;
|
|
1274
|
-
}
|
|
1275
|
-
if (updates.min_quantity !== void 0) {
|
|
1276
|
-
if (updates.min_quantity === null) delete cart[index].min_quantity;
|
|
1277
|
-
else cart[index].min_quantity = updates.min_quantity;
|
|
1278
|
-
}
|
|
1279
|
-
clampQuantityToBounds(cart[index]);
|
|
1280
|
-
const nextLineId = computeCartLineId(cart[index]);
|
|
1281
|
-
const duplicateIndex = cart.findIndex((item, itemIndex) => itemIndex !== index && item.line_id === nextLineId);
|
|
1282
|
-
if (duplicateIndex >= 0) {
|
|
1283
|
-
cart[duplicateIndex].quantity += cart[index].quantity;
|
|
1284
|
-
clampQuantityToBounds(cart[duplicateIndex]);
|
|
1285
|
-
cart.splice(index, 1);
|
|
1286
|
-
} else {
|
|
1287
|
-
cart[index].line_id = nextLineId;
|
|
1191
|
+
try {
|
|
1192
|
+
const endpoint = buildEndpoint("/v1/storefront/shops/:storeSlug/ping", {
|
|
1193
|
+
storeSlug: config.storeSlug
|
|
1194
|
+
});
|
|
1195
|
+
const delivered = fetch(`${config.apiBaseUrl}${endpoint}`, {
|
|
1196
|
+
method: "POST",
|
|
1197
|
+
headers: { "Content-Type": "application/json" },
|
|
1198
|
+
body: JSON.stringify(body),
|
|
1199
|
+
signal: controller?.signal
|
|
1200
|
+
}).then((response) => response.ok);
|
|
1201
|
+
return await Promise.race([delivered, timedOut]);
|
|
1202
|
+
} catch {
|
|
1203
|
+
return false;
|
|
1204
|
+
} finally {
|
|
1205
|
+
clearTimeout(timer);
|
|
1288
1206
|
}
|
|
1289
|
-
if (JSON.stringify(cart) === before) return;
|
|
1290
|
-
writeCart(cart);
|
|
1291
1207
|
}
|
|
1292
|
-
function
|
|
1293
|
-
|
|
1294
|
-
if (
|
|
1295
|
-
|
|
1208
|
+
async function trackPageView(cartValue, itemCount) {
|
|
1209
|
+
if (!isInitialized()) return;
|
|
1210
|
+
if (typeof document === "undefined") return;
|
|
1211
|
+
const config = getConfig();
|
|
1212
|
+
const carriesCart = cartValue !== void 0 || itemCount !== void 0;
|
|
1213
|
+
const reservedOrder = carriesCart ? reserveCartOrder(config.storeSlug) : void 0;
|
|
1214
|
+
if (isPlatformPageTrackerActive()) {
|
|
1215
|
+
if (reservedOrder) await enqueueCartSend(async () => sendCartActivity(await reservedOrder, cartValue, itemCount));
|
|
1216
|
+
return;
|
|
1296
1217
|
}
|
|
1297
|
-
const
|
|
1298
|
-
|
|
1299
|
-
|
|
1218
|
+
const sendView = async () => {
|
|
1219
|
+
const order2 = reservedOrder ? await reservedOrder : void 0;
|
|
1220
|
+
const settle = order2 ? claimCartReport(config.storeSlug, order2.seq, cartValue, itemCount) : void 0;
|
|
1221
|
+
const sessionId = analyticsSessionId(config.storeSlug, { touch: true });
|
|
1222
|
+
const previousSession = currentPageSession();
|
|
1223
|
+
if (previousSession && previousSession !== sessionId) reportPageLeave();
|
|
1224
|
+
markPageViewed(sessionId);
|
|
1225
|
+
if (sessionId) installEngagedClock(reportPageLeave);
|
|
1226
|
+
onAnalyticsConsent(startSessionOnConsent);
|
|
1227
|
+
const delivered = await postPing({
|
|
1228
|
+
referer: document.referrer || void 0,
|
|
1229
|
+
cart_value: cartValue,
|
|
1230
|
+
item_count: itemCount,
|
|
1231
|
+
cart_seq: order2?.seq,
|
|
1232
|
+
cart_seq_scope: order2?.scope,
|
|
1233
|
+
session_id: sessionId ?? void 0,
|
|
1234
|
+
// Time on screen of the page before this one (client-side navigation).
|
|
1235
|
+
engaged_ms: sessionId ? takeEngagedMs() || void 0 : void 0,
|
|
1236
|
+
path: resolveStorefrontPagePath(),
|
|
1237
|
+
utm: resolveUtmParameters(config.storeSlug)
|
|
1238
|
+
});
|
|
1239
|
+
settle?.(delivered);
|
|
1240
|
+
};
|
|
1241
|
+
await (reservedOrder ? enqueueCartSend(sendView) : sendView());
|
|
1300
1242
|
}
|
|
1301
|
-
function
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1243
|
+
async function trackCartActivity(cartValue, itemCount) {
|
|
1244
|
+
if (!isInitialized()) return;
|
|
1245
|
+
if (typeof document === "undefined") return;
|
|
1246
|
+
const snapshot = cartSnapshot(cartValue, itemCount);
|
|
1247
|
+
const reservedOrder = reserveCartOrder(getConfig().storeSlug);
|
|
1248
|
+
await enqueueCartSend(async (isLatest) => {
|
|
1249
|
+
const order2 = await reservedOrder;
|
|
1250
|
+
if (!isLatest() || isCartReportedOrInFlight(getConfig().storeSlug, snapshot)) return;
|
|
1251
|
+
await sendCartActivity(order2, cartValue, itemCount);
|
|
1252
|
+
});
|
|
1306
1253
|
}
|
|
1307
|
-
function
|
|
1308
|
-
const
|
|
1309
|
-
|
|
1310
|
-
const
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
}
|
|
1320
|
-
|
|
1321
|
-
writeCartStorage("metaBackup", {
|
|
1322
|
-
created_at: now,
|
|
1323
|
-
last_modified: now,
|
|
1324
|
-
version: 1,
|
|
1325
|
-
checksum: computeChecksum(cart)
|
|
1326
|
-
});
|
|
1327
|
-
}
|
|
1254
|
+
async function sendCartActivity(order2, cartValue, itemCount) {
|
|
1255
|
+
const config = getConfig();
|
|
1256
|
+
const settle = claimCartReport(config.storeSlug, order2.seq, cartValue, itemCount);
|
|
1257
|
+
const delivered = await postPing({
|
|
1258
|
+
event_type: "presence",
|
|
1259
|
+
cart_value: cartValue,
|
|
1260
|
+
item_count: itemCount,
|
|
1261
|
+
cart_seq: order2.seq,
|
|
1262
|
+
cart_seq_scope: order2.scope,
|
|
1263
|
+
// A cart marks the analytics session as one that did not bounce.
|
|
1264
|
+
session_id: analyticsSessionId(config.storeSlug, { touch: false }) ?? void 0,
|
|
1265
|
+
path: resolveStorefrontPagePath()
|
|
1266
|
+
});
|
|
1267
|
+
settle(delivered);
|
|
1328
1268
|
}
|
|
1329
|
-
function
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
const backupMeta = readCartStorage("metaBackup");
|
|
1333
|
-
const backupCoupon = readCartStorage("couponBackup");
|
|
1334
|
-
if (backup && backup.length > 0) {
|
|
1335
|
-
setCartWithMetadata(backup, backupMeta);
|
|
1336
|
-
if (backupCoupon && typeof backupCoupon === "object" && !Array.isArray(backupCoupon)) {
|
|
1337
|
-
const record = backupCoupon;
|
|
1338
|
-
const source = record.source === "COUPON" || record.source === "AFFILIATE" ? record.source : null;
|
|
1339
|
-
if (typeof record.code === "string" && source) {
|
|
1340
|
-
setCartCoupon(record.code, source);
|
|
1341
|
-
} else {
|
|
1342
|
-
clearCartCoupon();
|
|
1343
|
-
}
|
|
1344
|
-
} else {
|
|
1345
|
-
clearCartCoupon();
|
|
1346
|
-
}
|
|
1347
|
-
return true;
|
|
1348
|
-
}
|
|
1349
|
-
return false;
|
|
1269
|
+
async function trackProductView(productId) {
|
|
1270
|
+
if (!isInitialized() || typeof document === "undefined" || !productId) return;
|
|
1271
|
+
await sendVisitEvent({ event_type: "product_view", product_id: productId });
|
|
1350
1272
|
}
|
|
1351
|
-
function
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
const productId = typeof incoming.product_id === "string" ? incoming.product_id.trim() : "";
|
|
1355
|
-
const variantId = typeof incoming.variant_id === "string" ? incoming.variant_id.trim() : "";
|
|
1356
|
-
if (!productId || !variantId) {
|
|
1357
|
-
continue;
|
|
1358
|
-
}
|
|
1359
|
-
let normalizedQuantity;
|
|
1360
|
-
try {
|
|
1361
|
-
normalizedQuantity = normalizeQuantity(incoming.quantity);
|
|
1362
|
-
} catch {
|
|
1363
|
-
continue;
|
|
1364
|
-
}
|
|
1365
|
-
if (normalizedQuantity < 1) {
|
|
1366
|
-
continue;
|
|
1367
|
-
}
|
|
1368
|
-
const mergedLine = ensureCartLineId({
|
|
1369
|
-
...incoming,
|
|
1370
|
-
line_id: incoming.line_id ?? computeCartLineId(incoming),
|
|
1371
|
-
product_id: productId,
|
|
1372
|
-
variant_id: variantId,
|
|
1373
|
-
quantity: normalizedQuantity
|
|
1374
|
-
});
|
|
1375
|
-
const index = cart.findIndex((item) => item.line_id === mergedLine.line_id);
|
|
1376
|
-
if (index >= 0) {
|
|
1377
|
-
cart[index].quantity = Math.max(cart[index].quantity, normalizedQuantity);
|
|
1378
|
-
clampQuantityToBounds(cart[index]);
|
|
1379
|
-
} else {
|
|
1380
|
-
clampQuantityToBounds(mergedLine);
|
|
1381
|
-
cart.push(mergedLine);
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1384
|
-
writeCart(cart);
|
|
1385
|
-
return cart;
|
|
1273
|
+
async function trackCartAdd(productId, variantId, quantity) {
|
|
1274
|
+
if (!isInitialized() || typeof document === "undefined" || quantity < 1) return;
|
|
1275
|
+
await sendVisitEvent({ event_type: "cart_add", product_id: productId, variant_id: variantId, quantity });
|
|
1386
1276
|
}
|
|
1387
|
-
function
|
|
1388
|
-
const
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
const toLineId = computeCartLineId({
|
|
1397
|
-
...fromItem,
|
|
1398
|
-
product_id: toProductId,
|
|
1399
|
-
variant_id: toVariantId
|
|
1277
|
+
async function sendVisitEvent(body) {
|
|
1278
|
+
const config = getConfig();
|
|
1279
|
+
if (config.preview) return;
|
|
1280
|
+
await postPing({
|
|
1281
|
+
...body,
|
|
1282
|
+
referer: document.referrer || void 0,
|
|
1283
|
+
session_id: analyticsSessionId(config.storeSlug, { touch: true }) ?? void 0,
|
|
1284
|
+
path: resolveStorefrontPagePath(),
|
|
1285
|
+
utm: resolveUtmParameters(config.storeSlug)
|
|
1400
1286
|
});
|
|
1401
|
-
const toIndex = cart.findIndex((item) => item.line_id === toLineId);
|
|
1402
|
-
if (toIndex >= 0) {
|
|
1403
|
-
cart[toIndex].quantity += fromItem.quantity;
|
|
1404
|
-
clampQuantityToBounds(cart[toIndex]);
|
|
1405
|
-
} else {
|
|
1406
|
-
const moved = ensureCartLineId({
|
|
1407
|
-
...fromItem,
|
|
1408
|
-
product_id: toProductId,
|
|
1409
|
-
variant_id: toVariantId,
|
|
1410
|
-
line_id: toLineId
|
|
1411
|
-
});
|
|
1412
|
-
clampQuantityToBounds(moved);
|
|
1413
|
-
cart.push(moved);
|
|
1414
|
-
}
|
|
1415
|
-
writeCart(cart);
|
|
1416
|
-
}
|
|
1417
|
-
function validateCartIntegrity() {
|
|
1418
|
-
const cart = getCart();
|
|
1419
|
-
const metadata = getCartMetadata();
|
|
1420
|
-
const checksum = computeChecksum(cart);
|
|
1421
|
-
if (!metadata) {
|
|
1422
|
-
setCartWithMetadata(cart, null);
|
|
1423
|
-
return true;
|
|
1424
|
-
}
|
|
1425
|
-
return metadata.checksum === checksum;
|
|
1426
1287
|
}
|
|
1427
|
-
function
|
|
1428
|
-
const
|
|
1429
|
-
|
|
1430
|
-
const
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
version: metadata?.version ?? 0,
|
|
1442
|
-
has_backup: backup.length > 0,
|
|
1443
|
-
integrity_valid: integrityValid,
|
|
1444
|
-
total_price: roundPayableAmount(totalPrice),
|
|
1445
|
-
total_price_is_estimate: cart.length > 0 && !hasCompletePriceSnapshots
|
|
1446
|
-
};
|
|
1288
|
+
function startSessionOnConsent() {
|
|
1289
|
+
const config = getConfig();
|
|
1290
|
+
if (!isPageViewed() || analyticsSessionId(config.storeSlug, { touch: false })) return;
|
|
1291
|
+
const sessionId = analyticsSessionId(config.storeSlug, { touch: true });
|
|
1292
|
+
if (!sessionId) return;
|
|
1293
|
+
markPageViewed(sessionId);
|
|
1294
|
+
installEngagedClock(reportPageLeave);
|
|
1295
|
+
void postPing({
|
|
1296
|
+
event_type: "session_start",
|
|
1297
|
+
referer: document.referrer || void 0,
|
|
1298
|
+
session_id: sessionId,
|
|
1299
|
+
path: resolveStorefrontPagePath(),
|
|
1300
|
+
utm: resolveUtmParameters(config.storeSlug)
|
|
1301
|
+
});
|
|
1447
1302
|
}
|
|
1448
|
-
function
|
|
1303
|
+
function reportPageLeave() {
|
|
1449
1304
|
const config = getConfig();
|
|
1450
|
-
const
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1305
|
+
const sessionId = currentPageSession();
|
|
1306
|
+
const engagedMs2 = takeEngagedMs();
|
|
1307
|
+
if (!sessionId || engagedMs2 === 0 || readAnalyticsConsent() !== "granted") return;
|
|
1308
|
+
try {
|
|
1309
|
+
const endpoint = buildEndpoint("/v1/storefront/shops/:storeSlug/ping", { storeSlug: config.storeSlug });
|
|
1310
|
+
void fetch(`${config.apiBaseUrl}${endpoint}`, {
|
|
1311
|
+
method: "POST",
|
|
1312
|
+
headers: { "Content-Type": "application/json" },
|
|
1313
|
+
body: JSON.stringify({ event_type: "page_leave", session_id: sessionId, engaged_ms: engagedMs2 }),
|
|
1314
|
+
keepalive: true
|
|
1315
|
+
}).catch(() => {
|
|
1316
|
+
});
|
|
1317
|
+
} catch {
|
|
1318
|
+
}
|
|
1456
1319
|
}
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1320
|
+
|
|
1321
|
+
// ../sdk/src/utils/cart-line-id.ts
|
|
1322
|
+
function hashString(value) {
|
|
1323
|
+
let hash = 2166136261;
|
|
1324
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
1325
|
+
hash ^= value.charCodeAt(i);
|
|
1326
|
+
hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
|
|
1462
1327
|
}
|
|
1463
|
-
return
|
|
1328
|
+
return (hash >>> 0).toString(16);
|
|
1464
1329
|
}
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
return latestQuoteToken;
|
|
1330
|
+
function normalizeAddons(addons) {
|
|
1331
|
+
if (!addons?.length) return [];
|
|
1332
|
+
return [...addons].map((addon) => ({ id: addon.id, quantity: addon.quantity ?? 1 })).sort((a, b) => a.id.localeCompare(b.id));
|
|
1469
1333
|
}
|
|
1470
|
-
function
|
|
1471
|
-
|
|
1472
|
-
|
|
1334
|
+
function normalizeCustomFields(fields) {
|
|
1335
|
+
if (!fields) return {};
|
|
1336
|
+
const entries = Object.entries(fields).filter(([, value]) => typeof value === "string").sort(([a], [b]) => a.localeCompare(b));
|
|
1337
|
+
return Object.fromEntries(entries);
|
|
1473
1338
|
}
|
|
1474
|
-
|
|
1475
|
-
const
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1339
|
+
function computeCartLineId(input) {
|
|
1340
|
+
const payload = {
|
|
1341
|
+
product_id: input.product_id,
|
|
1342
|
+
variant_id: input.variant_id,
|
|
1343
|
+
price_variant_id: input.price_variant_id ?? null,
|
|
1344
|
+
addons: normalizeAddons(input.addons),
|
|
1345
|
+
custom_fields: normalizeCustomFields(input.custom_fields),
|
|
1346
|
+
unit_price: typeof input.price_data?.unit_price === "number" && Number.isFinite(input.price_data.unit_price) ? input.price_data.unit_price : null,
|
|
1347
|
+
pay_what_you_want_price: typeof input.pay_what_you_want_price === "number" && Number.isFinite(input.pay_what_you_want_price) ? input.pay_what_you_want_price : null
|
|
1348
|
+
};
|
|
1349
|
+
return hashString(JSON.stringify(payload));
|
|
1350
|
+
}
|
|
1351
|
+
function ensureCartLineId(item) {
|
|
1352
|
+
if (typeof item.line_id === "string" && item.line_id.trim()) {
|
|
1353
|
+
return item;
|
|
1488
1354
|
}
|
|
1489
|
-
return
|
|
1355
|
+
return { ...item, line_id: computeCartLineId(item) };
|
|
1490
1356
|
}
|
|
1491
1357
|
|
|
1492
|
-
// ../contracts/src/
|
|
1493
|
-
var
|
|
1494
|
-
var
|
|
1495
|
-
var
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1358
|
+
// ../contracts/src/catalog-unit-price.ts
|
|
1359
|
+
var CATALOG_UNIT_PRICE_DECIMAL_PLACES = 3;
|
|
1360
|
+
var PAYABLE_AMOUNT_DECIMAL_PLACES = 2;
|
|
1361
|
+
var CATALOG_UNIT_PRICE_FORMAT_OPTIONS = {
|
|
1362
|
+
maximumFractionDigits: CATALOG_UNIT_PRICE_DECIMAL_PLACES
|
|
1363
|
+
};
|
|
1364
|
+
function roundPayableAmount(value) {
|
|
1365
|
+
if (!Number.isFinite(value)) return value;
|
|
1366
|
+
const sign = value < 0 ? -1 : 1;
|
|
1367
|
+
const shifted = shiftDecimal(Math.abs(value), PAYABLE_AMOUNT_DECIMAL_PLACES);
|
|
1368
|
+
const rounded = Math.round(shifted);
|
|
1369
|
+
if (rounded === 0) return 0;
|
|
1370
|
+
return sign * shiftDecimal(rounded, -PAYABLE_AMOUNT_DECIMAL_PLACES);
|
|
1371
|
+
}
|
|
1372
|
+
function shiftDecimal(value, places) {
|
|
1373
|
+
const [coefficient, currentExponent = "0"] = String(value).split("e");
|
|
1374
|
+
return Number(`${coefficient}e${Number(currentExponent) + places}`);
|
|
1500
1375
|
}
|
|
1501
1376
|
|
|
1502
|
-
// ../sdk/src/
|
|
1503
|
-
var
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1377
|
+
// ../sdk/src/modules/cart.ts
|
|
1378
|
+
var STORAGE_KEYS = {
|
|
1379
|
+
cart: "cart",
|
|
1380
|
+
cartBackup: "cart_backup",
|
|
1381
|
+
meta: "cart_meta",
|
|
1382
|
+
metaBackup: "cart_backup_meta",
|
|
1383
|
+
coupon: "cart_coupon",
|
|
1384
|
+
couponBackup: "cart_coupon_backup"
|
|
1385
|
+
};
|
|
1386
|
+
function getStorageKey(type) {
|
|
1387
|
+
return `${STORAGE_KEYS[type]}_${getConfig().storeSlug}`;
|
|
1388
|
+
}
|
|
1389
|
+
function cartStorageArea() {
|
|
1390
|
+
return getConfig().preview ? "sessionStorage" : "localStorage";
|
|
1391
|
+
}
|
|
1392
|
+
function readCartStorage(type) {
|
|
1393
|
+
return getItem(getStorageKey(type), cartStorageArea());
|
|
1394
|
+
}
|
|
1395
|
+
function writeCartStorage(type, value) {
|
|
1396
|
+
setItem(getStorageKey(type), value, cartStorageArea());
|
|
1397
|
+
}
|
|
1398
|
+
function removeCartStorage(type) {
|
|
1399
|
+
removeItem(getStorageKey(type), cartStorageArea());
|
|
1400
|
+
}
|
|
1401
|
+
function hashString2(value) {
|
|
1402
|
+
let hash = 2166136261;
|
|
1403
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
1404
|
+
hash ^= value.charCodeAt(i);
|
|
1405
|
+
hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
|
|
1519
1406
|
}
|
|
1520
|
-
|
|
1521
|
-
if (!choice) return "unknown";
|
|
1522
|
-
return choice.analytics ? "granted" : "declined";
|
|
1407
|
+
return (hash >>> 0).toString(16);
|
|
1523
1408
|
}
|
|
1524
|
-
function
|
|
1525
|
-
|
|
1526
|
-
if (!Array.isArray(stored)) return [];
|
|
1527
|
-
return stored.filter((visit) => typeof visit?.id === "string" && typeof visit.at === "number" && now - visit.at < ANALYTICS_VISITS_WINDOW_MS);
|
|
1409
|
+
function computeChecksum(cart) {
|
|
1410
|
+
return hashString2(JSON.stringify(cart));
|
|
1528
1411
|
}
|
|
1529
|
-
function
|
|
1530
|
-
|
|
1531
|
-
|
|
1412
|
+
function normalizeQuantity(value) {
|
|
1413
|
+
if (!Number.isFinite(value)) {
|
|
1414
|
+
throw new CartError("quantity must be a finite number");
|
|
1415
|
+
}
|
|
1416
|
+
return Math.floor(value);
|
|
1532
1417
|
}
|
|
1533
|
-
function
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1418
|
+
function normalizeCouponCode(value) {
|
|
1419
|
+
const normalized = value?.trim().toUpperCase();
|
|
1420
|
+
return normalized ? normalized : null;
|
|
1421
|
+
}
|
|
1422
|
+
function getStoredCartCode() {
|
|
1423
|
+
const raw = readCartStorage("coupon");
|
|
1424
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
1537
1425
|
return null;
|
|
1538
1426
|
}
|
|
1539
|
-
const
|
|
1540
|
-
const
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
setItem(analyticsSessionKey(storeSlug), { id: live, at: now }, "sessionStorage");
|
|
1544
|
-
return live;
|
|
1545
|
-
}
|
|
1546
|
-
if (typeof crypto === "undefined" || typeof crypto.randomUUID !== "function") return null;
|
|
1547
|
-
const id = crypto.randomUUID();
|
|
1548
|
-
setItem(analyticsSessionKey(storeSlug), { id, at: now }, "sessionStorage");
|
|
1549
|
-
setItem(
|
|
1550
|
-
analyticsVisitsKey(storeSlug),
|
|
1551
|
-
[{ id, at: now }, ...readVisits(storeSlug, now)].slice(0, ANALYTICS_VISITS_MAX)
|
|
1552
|
-
);
|
|
1553
|
-
return id;
|
|
1427
|
+
const record = raw;
|
|
1428
|
+
const code = typeof record.code === "string" ? normalizeCouponCode(record.code) : null;
|
|
1429
|
+
const source = record.source === "COUPON" || record.source === "AFFILIATE" ? record.source : null;
|
|
1430
|
+
return code && source ? { code, source } : null;
|
|
1554
1431
|
}
|
|
1555
|
-
function
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1432
|
+
function normalizeCartItems(value) {
|
|
1433
|
+
if (!Array.isArray(value)) return [];
|
|
1434
|
+
const normalized = [];
|
|
1435
|
+
for (const entry of value) {
|
|
1436
|
+
if (!entry || typeof entry !== "object") continue;
|
|
1437
|
+
const record = entry;
|
|
1438
|
+
const productId = typeof record.product_id === "string" ? record.product_id.trim() : "";
|
|
1439
|
+
const variantId = typeof record.variant_id === "string" ? record.variant_id.trim() : "";
|
|
1440
|
+
const quantity = Number(record.quantity);
|
|
1441
|
+
if (!productId || !variantId || !Number.isFinite(quantity) || quantity < 1) {
|
|
1442
|
+
continue;
|
|
1443
|
+
}
|
|
1444
|
+
const item = {
|
|
1445
|
+
line_id: "",
|
|
1446
|
+
product_id: productId,
|
|
1447
|
+
variant_id: variantId,
|
|
1448
|
+
quantity: Math.floor(quantity)
|
|
1449
|
+
};
|
|
1450
|
+
if (typeof record.price_variant_id === "string") {
|
|
1451
|
+
item.price_variant_id = record.price_variant_id;
|
|
1452
|
+
}
|
|
1453
|
+
if (record.price_data && typeof record.price_data === "object") {
|
|
1454
|
+
const priceData = record.price_data;
|
|
1455
|
+
if (typeof priceData.unit_price === "number" && Number.isFinite(priceData.unit_price)) {
|
|
1456
|
+
item.price_data = { unit_price: priceData.unit_price };
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
if (typeof record.pay_what_you_want_price === "number" && Number.isFinite(record.pay_what_you_want_price)) {
|
|
1460
|
+
item.pay_what_you_want_price = record.pay_what_you_want_price;
|
|
1461
|
+
}
|
|
1462
|
+
if (Array.isArray(record.addons)) {
|
|
1463
|
+
item.addons = record.addons;
|
|
1464
|
+
}
|
|
1465
|
+
if (record.custom_fields && typeof record.custom_fields === "object" && !Array.isArray(record.custom_fields)) {
|
|
1466
|
+
item.custom_fields = record.custom_fields;
|
|
1467
|
+
}
|
|
1468
|
+
if (typeof record.title === "string") {
|
|
1469
|
+
item.title = record.title;
|
|
1470
|
+
}
|
|
1471
|
+
if (typeof record.variant_title === "string") {
|
|
1472
|
+
item.variant_title = record.variant_title;
|
|
1473
|
+
}
|
|
1474
|
+
if (typeof record.image_url === "string") {
|
|
1475
|
+
item.image_url = record.image_url;
|
|
1476
|
+
}
|
|
1477
|
+
if (Array.isArray(record.addon_labels)) {
|
|
1478
|
+
item.addon_labels = record.addon_labels.filter((label) => typeof label === "string");
|
|
1479
|
+
}
|
|
1480
|
+
if (typeof record.max_quantity === "number" && Number.isFinite(record.max_quantity)) {
|
|
1481
|
+
item.max_quantity = record.max_quantity;
|
|
1482
|
+
}
|
|
1483
|
+
if (typeof record.min_quantity === "number" && Number.isFinite(record.min_quantity)) {
|
|
1484
|
+
item.min_quantity = record.min_quantity;
|
|
1485
|
+
}
|
|
1486
|
+
clampQuantityToBounds(item);
|
|
1487
|
+
const storedLineId = typeof record.line_id === "string" ? record.line_id.trim() : "";
|
|
1488
|
+
item.line_id = storedLineId || computeCartLineId(item);
|
|
1489
|
+
normalized.push(ensureCartLineId(item));
|
|
1560
1490
|
}
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1491
|
+
return normalized;
|
|
1492
|
+
}
|
|
1493
|
+
function getCartMetadata() {
|
|
1494
|
+
return readCartStorage("meta");
|
|
1495
|
+
}
|
|
1496
|
+
function getCartCoupon() {
|
|
1497
|
+
return getStoredCartCode()?.code ?? null;
|
|
1564
1498
|
}
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
var clockInstalled = false;
|
|
1568
|
-
var pageViewed = false;
|
|
1569
|
-
var pageSession = null;
|
|
1570
|
-
function markPageViewed(sessionId) {
|
|
1571
|
-
pageViewed = true;
|
|
1572
|
-
pageSession = sessionId;
|
|
1499
|
+
function getCartCouponSource() {
|
|
1500
|
+
return getStoredCartCode()?.source ?? null;
|
|
1573
1501
|
}
|
|
1574
|
-
function
|
|
1575
|
-
|
|
1502
|
+
function setCartCoupon(coupon, source = "COUPON") {
|
|
1503
|
+
const normalizedCoupon = normalizeCouponCode(coupon);
|
|
1504
|
+
if (!normalizedCoupon) {
|
|
1505
|
+
removeCartStorage("coupon");
|
|
1506
|
+
return null;
|
|
1507
|
+
}
|
|
1508
|
+
writeCartStorage("coupon", { code: normalizedCoupon, source });
|
|
1509
|
+
return normalizedCoupon;
|
|
1576
1510
|
}
|
|
1577
|
-
function
|
|
1578
|
-
|
|
1511
|
+
function clearCartCoupon() {
|
|
1512
|
+
removeCartStorage("coupon");
|
|
1579
1513
|
}
|
|
1580
|
-
function
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1514
|
+
function writeCart(cart) {
|
|
1515
|
+
const normalizedCart = normalizeCartItems(cart);
|
|
1516
|
+
writeCartStorage("cart", normalizedCart);
|
|
1517
|
+
const now = Date.now();
|
|
1518
|
+
const previous = getCartMetadata();
|
|
1519
|
+
const nextMeta = {
|
|
1520
|
+
created_at: previous?.created_at ?? now,
|
|
1521
|
+
last_modified: now,
|
|
1522
|
+
version: (previous?.version ?? 0) + 1,
|
|
1523
|
+
checksum: computeChecksum(normalizedCart)
|
|
1524
|
+
};
|
|
1525
|
+
writeCartStorage("meta", nextMeta);
|
|
1526
|
+
if (normalizedCart.length === 0) {
|
|
1527
|
+
clearCartCoupon();
|
|
1584
1528
|
}
|
|
1585
1529
|
}
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1530
|
+
function setCartWithMetadata(cart, metadata) {
|
|
1531
|
+
const normalizedCart = normalizeCartItems(cart);
|
|
1532
|
+
writeCartStorage("cart", normalizedCart);
|
|
1533
|
+
const now = Date.now();
|
|
1534
|
+
const base = metadata ?? getCartMetadata();
|
|
1535
|
+
const nextMeta = {
|
|
1536
|
+
created_at: base?.created_at ?? now,
|
|
1537
|
+
last_modified: now,
|
|
1538
|
+
version: base?.version ?? 1,
|
|
1539
|
+
checksum: computeChecksum(normalizedCart)
|
|
1540
|
+
};
|
|
1541
|
+
writeCartStorage("meta", nextMeta);
|
|
1593
1542
|
}
|
|
1594
|
-
function
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
visibleSince = document.visibilityState === "visible" ? Date.now() : null;
|
|
1598
|
-
document.addEventListener("visibilitychange", () => {
|
|
1599
|
-
const now = Date.now();
|
|
1600
|
-
if (document.visibilityState === "visible") {
|
|
1601
|
-
visibleSince ?? (visibleSince = now);
|
|
1602
|
-
return;
|
|
1603
|
-
}
|
|
1604
|
-
settleClock(now);
|
|
1605
|
-
visibleSince = null;
|
|
1606
|
-
onHidden();
|
|
1607
|
-
});
|
|
1608
|
-
window.addEventListener("pagehide", () => {
|
|
1609
|
-
settleClock(Date.now());
|
|
1610
|
-
visibleSince = null;
|
|
1611
|
-
onHidden();
|
|
1612
|
-
});
|
|
1613
|
-
window.addEventListener("pageshow", (event) => {
|
|
1614
|
-
if (event.persisted && document.visibilityState === "visible") visibleSince = Date.now();
|
|
1615
|
-
});
|
|
1543
|
+
function getCart() {
|
|
1544
|
+
const raw = readCartStorage("cart");
|
|
1545
|
+
return normalizeCartItems(raw);
|
|
1616
1546
|
}
|
|
1617
|
-
function
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1547
|
+
function resolveCartLineId(productId, variantId, cart = getCart()) {
|
|
1548
|
+
const matches = cart.filter(
|
|
1549
|
+
(item) => item.product_id === productId && item.variant_id === variantId
|
|
1550
|
+
);
|
|
1551
|
+
if (matches.length === 0) {
|
|
1552
|
+
throw new CartError(`No cart line found for ${productId}/${variantId}`);
|
|
1553
|
+
}
|
|
1554
|
+
return matches[0].line_id;
|
|
1622
1555
|
}
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
let params;
|
|
1631
|
-
try {
|
|
1632
|
-
params = new URLSearchParams(search);
|
|
1633
|
-
} catch {
|
|
1634
|
-
return {};
|
|
1556
|
+
function getCartItemCount() {
|
|
1557
|
+
const cart = getCart();
|
|
1558
|
+
return cart.reduce((sum, item) => sum + item.quantity, 0);
|
|
1559
|
+
}
|
|
1560
|
+
function addToCart(productId, variantId, quantity = 1, options) {
|
|
1561
|
+
if (!productId || !variantId) {
|
|
1562
|
+
throw new CartError("product_id and variant_id are required");
|
|
1635
1563
|
}
|
|
1636
|
-
const
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1564
|
+
const normalizedQuantity = normalizeQuantity(quantity);
|
|
1565
|
+
if (normalizedQuantity < 1) {
|
|
1566
|
+
throw new CartError("quantity must be at least 1");
|
|
1567
|
+
}
|
|
1568
|
+
const cart = getCart();
|
|
1569
|
+
const lineId = computeCartLineId({
|
|
1570
|
+
product_id: productId,
|
|
1571
|
+
variant_id: variantId,
|
|
1572
|
+
addons: options?.addons,
|
|
1573
|
+
custom_fields: options?.custom_fields,
|
|
1574
|
+
price_variant_id: options?.price_variant_id,
|
|
1575
|
+
price_data: options?.price_data,
|
|
1576
|
+
pay_what_you_want_price: options?.pay_what_you_want_price
|
|
1577
|
+
});
|
|
1578
|
+
const existingIndex = cart.findIndex((item) => item.line_id === lineId);
|
|
1579
|
+
const quantityBeforeAdd = existingIndex >= 0 ? cart[existingIndex].quantity : 0;
|
|
1580
|
+
if (existingIndex >= 0) {
|
|
1581
|
+
cart[existingIndex].quantity += normalizedQuantity;
|
|
1582
|
+
if (options?.addons) {
|
|
1583
|
+
cart[existingIndex].addons = options.addons;
|
|
1584
|
+
}
|
|
1585
|
+
if (options?.custom_fields) {
|
|
1586
|
+
cart[existingIndex].custom_fields = options.custom_fields;
|
|
1587
|
+
}
|
|
1588
|
+
if (options?.price_variant_id) {
|
|
1589
|
+
cart[existingIndex].price_variant_id = options.price_variant_id;
|
|
1590
|
+
}
|
|
1591
|
+
if (options?.price_data) {
|
|
1592
|
+
cart[existingIndex].price_data = options.price_data;
|
|
1641
1593
|
}
|
|
1594
|
+
if (options?.pay_what_you_want_price !== void 0) {
|
|
1595
|
+
cart[existingIndex].pay_what_you_want_price = options.pay_what_you_want_price;
|
|
1596
|
+
}
|
|
1597
|
+
applyDisplaySnapshot(cart[existingIndex], options);
|
|
1598
|
+
clampQuantityToBounds(cart[existingIndex]);
|
|
1599
|
+
} else {
|
|
1600
|
+
const pushed = {
|
|
1601
|
+
line_id: lineId,
|
|
1602
|
+
product_id: productId,
|
|
1603
|
+
variant_id: variantId,
|
|
1604
|
+
quantity: normalizedQuantity,
|
|
1605
|
+
addons: options?.addons,
|
|
1606
|
+
custom_fields: options?.custom_fields,
|
|
1607
|
+
price_variant_id: options?.price_variant_id,
|
|
1608
|
+
price_data: options?.price_data,
|
|
1609
|
+
pay_what_you_want_price: options?.pay_what_you_want_price,
|
|
1610
|
+
title: options?.title,
|
|
1611
|
+
variant_title: options?.variant_title,
|
|
1612
|
+
image_url: options?.image_url,
|
|
1613
|
+
addon_labels: options?.addon_labels,
|
|
1614
|
+
max_quantity: options?.max_quantity,
|
|
1615
|
+
min_quantity: options?.min_quantity
|
|
1616
|
+
};
|
|
1617
|
+
clampQuantityToBounds(pushed);
|
|
1618
|
+
cart.push(pushed);
|
|
1619
|
+
}
|
|
1620
|
+
const intendedQuantity = (existingIndex >= 0 ? cart[existingIndex] : cart[cart.length - 1]).quantity;
|
|
1621
|
+
writeCart(cart);
|
|
1622
|
+
const persistedLine = getCart().find((item) => item.line_id === lineId);
|
|
1623
|
+
if (intendedQuantity > quantityBeforeAdd && persistedLine?.quantity === intendedQuantity) {
|
|
1624
|
+
const cartAffiliateCode = getCartCouponSource() === "AFFILIATE" ? getCartCoupon() : null;
|
|
1625
|
+
void trackAffiliateEvent("add_to_cart", cartAffiliateCode ? { code: cartAffiliateCode } : void 0);
|
|
1626
|
+
void trackCartAdd(productId, variantId, intendedQuantity - quantityBeforeAdd);
|
|
1642
1627
|
}
|
|
1643
|
-
return utm;
|
|
1644
1628
|
}
|
|
1645
|
-
function
|
|
1646
|
-
|
|
1629
|
+
function clampQuantityToBounds(item) {
|
|
1630
|
+
const min = item.min_quantity;
|
|
1631
|
+
if (typeof min === "number" && Number.isFinite(min) && item.quantity < min) {
|
|
1632
|
+
item.quantity = Math.floor(min);
|
|
1633
|
+
}
|
|
1634
|
+
const max = item.max_quantity;
|
|
1635
|
+
if (typeof max === "number" && Number.isFinite(max) && item.quantity > max) {
|
|
1636
|
+
item.quantity = Math.floor(max);
|
|
1637
|
+
}
|
|
1638
|
+
if (item.quantity < 1) {
|
|
1639
|
+
item.quantity = 1;
|
|
1640
|
+
}
|
|
1647
1641
|
}
|
|
1648
|
-
function
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1642
|
+
function applyDisplaySnapshot(item, options) {
|
|
1643
|
+
if (options?.replace_display_snapshot) {
|
|
1644
|
+
item.title = options.title;
|
|
1645
|
+
item.variant_title = options.variant_title;
|
|
1646
|
+
item.image_url = options.image_url;
|
|
1647
|
+
item.addon_labels = options.addon_labels;
|
|
1648
|
+
item.max_quantity = options.max_quantity;
|
|
1649
|
+
item.min_quantity = options.min_quantity;
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
if (options?.title !== void 0) {
|
|
1653
|
+
item.title = options.title;
|
|
1654
|
+
}
|
|
1655
|
+
if (options?.variant_title !== void 0) {
|
|
1656
|
+
item.variant_title = options.variant_title;
|
|
1657
|
+
}
|
|
1658
|
+
if (options?.image_url !== void 0) {
|
|
1659
|
+
item.image_url = options.image_url;
|
|
1660
|
+
}
|
|
1661
|
+
if (options?.addon_labels !== void 0) {
|
|
1662
|
+
item.addon_labels = options.addon_labels;
|
|
1663
|
+
}
|
|
1664
|
+
if (options?.max_quantity !== void 0) {
|
|
1665
|
+
item.max_quantity = options.max_quantity;
|
|
1666
|
+
}
|
|
1667
|
+
if (options?.min_quantity !== void 0) {
|
|
1668
|
+
item.min_quantity = options.min_quantity;
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
function setCartItem(productId, variantId, quantity = 1, options) {
|
|
1672
|
+
if (!productId || !variantId) {
|
|
1673
|
+
throw new CartError("product_id and variant_id are required");
|
|
1674
|
+
}
|
|
1675
|
+
const normalizedQuantity = normalizeQuantity(quantity);
|
|
1676
|
+
if (normalizedQuantity < 1) {
|
|
1677
|
+
throw new CartError("quantity must be at least 1");
|
|
1678
|
+
}
|
|
1679
|
+
const cart = getCart();
|
|
1680
|
+
const lineId = computeCartLineId({
|
|
1681
|
+
product_id: productId,
|
|
1682
|
+
variant_id: variantId,
|
|
1683
|
+
addons: options?.addons,
|
|
1684
|
+
custom_fields: options?.custom_fields,
|
|
1685
|
+
price_variant_id: options?.price_variant_id,
|
|
1686
|
+
price_data: options?.price_data,
|
|
1687
|
+
pay_what_you_want_price: options?.pay_what_you_want_price
|
|
1688
|
+
});
|
|
1689
|
+
for (let index = cart.length - 1; index >= 0; index -= 1) {
|
|
1690
|
+
if (cart[index].product_id === productId && cart[index].variant_id === variantId) {
|
|
1691
|
+
cart.splice(index, 1);
|
|
1664
1692
|
}
|
|
1665
|
-
return utm;
|
|
1666
|
-
} catch {
|
|
1667
|
-
return {};
|
|
1668
1693
|
}
|
|
1694
|
+
const pushed = {
|
|
1695
|
+
line_id: lineId,
|
|
1696
|
+
product_id: productId,
|
|
1697
|
+
variant_id: variantId,
|
|
1698
|
+
quantity: normalizedQuantity,
|
|
1699
|
+
addons: options?.addons,
|
|
1700
|
+
custom_fields: options?.custom_fields,
|
|
1701
|
+
price_variant_id: options?.price_variant_id,
|
|
1702
|
+
price_data: options?.price_data,
|
|
1703
|
+
pay_what_you_want_price: options?.pay_what_you_want_price,
|
|
1704
|
+
title: options?.title,
|
|
1705
|
+
variant_title: options?.variant_title,
|
|
1706
|
+
image_url: options?.image_url,
|
|
1707
|
+
addon_labels: options?.addon_labels,
|
|
1708
|
+
max_quantity: options?.max_quantity,
|
|
1709
|
+
min_quantity: options?.min_quantity
|
|
1710
|
+
};
|
|
1711
|
+
clampQuantityToBounds(pushed);
|
|
1712
|
+
cart.push(pushed);
|
|
1713
|
+
writeCart(cart);
|
|
1714
|
+
const persistedLine = getCart().find((item) => item.line_id === lineId);
|
|
1715
|
+
if (persistedLine) void trackCartAdd(productId, variantId, persistedLine.quantity);
|
|
1669
1716
|
}
|
|
1670
|
-
function
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1717
|
+
function updateCartItem(lineId, updates) {
|
|
1718
|
+
const cart = getCart();
|
|
1719
|
+
const normalizedLineId = lineId.trim();
|
|
1720
|
+
if (!normalizedLineId) {
|
|
1721
|
+
throw new CartError("line_id is required");
|
|
1675
1722
|
}
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
return void 0;
|
|
1723
|
+
const index = cart.findIndex((item) => item.line_id === normalizedLineId);
|
|
1724
|
+
if (index < 0) {
|
|
1725
|
+
throw new CartError("Item not found in cart");
|
|
1680
1726
|
}
|
|
1681
|
-
const
|
|
1682
|
-
if (
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1727
|
+
const before = JSON.stringify(cart);
|
|
1728
|
+
if (updates.quantity !== void 0) {
|
|
1729
|
+
const normalizedQuantity = normalizeQuantity(updates.quantity);
|
|
1730
|
+
if (normalizedQuantity < 1) {
|
|
1731
|
+
cart.splice(index, 1);
|
|
1732
|
+
writeCart(cart);
|
|
1733
|
+
return;
|
|
1686
1734
|
}
|
|
1687
|
-
|
|
1735
|
+
cart[index].quantity = normalizedQuantity;
|
|
1688
1736
|
}
|
|
1689
|
-
if (
|
|
1690
|
-
|
|
1691
|
-
return fromUrl;
|
|
1737
|
+
if (updates.addons !== void 0) {
|
|
1738
|
+
cart[index].addons = updates.addons;
|
|
1692
1739
|
}
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
}
|
|
1696
|
-
|
|
1697
|
-
// ../sdk/src/core/page-path.ts
|
|
1698
|
-
var MAX_PATH_LENGTH = 256;
|
|
1699
|
-
function resolveStorefrontPagePath() {
|
|
1700
|
-
if (typeof window === "undefined") return void 0;
|
|
1701
|
-
let pathname;
|
|
1702
|
-
try {
|
|
1703
|
-
const location = window.location;
|
|
1704
|
-
pathname = typeof location?.pathname === "string" && location.pathname ? location.pathname : new URL(String(location?.href ?? "")).pathname;
|
|
1705
|
-
} catch {
|
|
1706
|
-
return void 0;
|
|
1740
|
+
if (updates.custom_fields !== void 0) {
|
|
1741
|
+
cart[index].custom_fields = updates.custom_fields;
|
|
1707
1742
|
}
|
|
1708
|
-
if (
|
|
1709
|
-
|
|
1710
|
-
}
|
|
1711
|
-
|
|
1712
|
-
// ../sdk/src/core/page-tracker.ts
|
|
1713
|
-
var PLATFORM_PAGE_TRACKER_FLAG = "__shoppexPageTracker";
|
|
1714
|
-
function isPlatformPageTrackerActive() {
|
|
1715
|
-
if (typeof window === "undefined") return false;
|
|
1716
|
-
try {
|
|
1717
|
-
return Boolean(window[PLATFORM_PAGE_TRACKER_FLAG]);
|
|
1718
|
-
} catch {
|
|
1719
|
-
return false;
|
|
1743
|
+
if (updates.price_variant_id !== void 0) {
|
|
1744
|
+
cart[index].price_variant_id = updates.price_variant_id;
|
|
1720
1745
|
}
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
// ../sdk/src/modules/analytics.ts
|
|
1724
|
-
var CONFIRMED_CART_STORAGE_PREFIX = "reported_cart_";
|
|
1725
|
-
var IN_FLIGHT_CART_STORAGE_PREFIX = "reporting_cart_";
|
|
1726
|
-
var CART_SEQUENCE_STORAGE_PREFIX = "presence_cart_seq_";
|
|
1727
|
-
var CART_CLAIM_TTL_MS = 1e4;
|
|
1728
|
-
function confirmedCartKey(storeSlug) {
|
|
1729
|
-
return `${CONFIRMED_CART_STORAGE_PREFIX}${storeSlug}`;
|
|
1730
|
-
}
|
|
1731
|
-
function inFlightCartKey(storeSlug) {
|
|
1732
|
-
return `${IN_FLIGHT_CART_STORAGE_PREFIX}${storeSlug}`;
|
|
1733
|
-
}
|
|
1734
|
-
function cartSequenceKey(storeSlug) {
|
|
1735
|
-
return `${CART_SEQUENCE_STORAGE_PREFIX}${storeSlug}`;
|
|
1736
|
-
}
|
|
1737
|
-
function cartSnapshot(cartValue, itemCount) {
|
|
1738
|
-
return `${cartValue ?? ""}|${itemCount ?? ""}`;
|
|
1739
|
-
}
|
|
1740
|
-
function isCartSequence(value) {
|
|
1741
|
-
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
1742
|
-
}
|
|
1743
|
-
var CART_SEQUENCE_SCOPE_MAX_LENGTH = 64;
|
|
1744
|
-
function readCartCounter(storeSlug) {
|
|
1745
|
-
const counter = getItem(cartSequenceKey(storeSlug));
|
|
1746
|
-
if (!counter || typeof counter.scope !== "string" || counter.scope.length === 0 || counter.scope.length > CART_SEQUENCE_SCOPE_MAX_LENGTH || !isCartSequence(counter.seq)) {
|
|
1747
|
-
return null;
|
|
1746
|
+
if (updates.price_data !== void 0) {
|
|
1747
|
+
cart[index].price_data = updates.price_data;
|
|
1748
1748
|
}
|
|
1749
|
-
|
|
1749
|
+
if (updates.pay_what_you_want_price !== void 0) {
|
|
1750
|
+
cart[index].pay_what_you_want_price = updates.pay_what_you_want_price;
|
|
1751
|
+
}
|
|
1752
|
+
if (updates.variant_title !== void 0) {
|
|
1753
|
+
if (updates.variant_title === null) delete cart[index].variant_title;
|
|
1754
|
+
else cart[index].variant_title = updates.variant_title;
|
|
1755
|
+
}
|
|
1756
|
+
if (updates.max_quantity !== void 0) {
|
|
1757
|
+
if (updates.max_quantity === null) delete cart[index].max_quantity;
|
|
1758
|
+
else cart[index].max_quantity = updates.max_quantity;
|
|
1759
|
+
}
|
|
1760
|
+
if (updates.min_quantity !== void 0) {
|
|
1761
|
+
if (updates.min_quantity === null) delete cart[index].min_quantity;
|
|
1762
|
+
else cart[index].min_quantity = updates.min_quantity;
|
|
1763
|
+
}
|
|
1764
|
+
clampQuantityToBounds(cart[index]);
|
|
1765
|
+
const nextLineId = computeCartLineId(cart[index]);
|
|
1766
|
+
const duplicateIndex = cart.findIndex((item, itemIndex) => itemIndex !== index && item.line_id === nextLineId);
|
|
1767
|
+
if (duplicateIndex >= 0) {
|
|
1768
|
+
cart[duplicateIndex].quantity += cart[index].quantity;
|
|
1769
|
+
clampQuantityToBounds(cart[duplicateIndex]);
|
|
1770
|
+
cart.splice(index, 1);
|
|
1771
|
+
} else {
|
|
1772
|
+
cart[index].line_id = nextLineId;
|
|
1773
|
+
}
|
|
1774
|
+
if (JSON.stringify(cart) === before) return;
|
|
1775
|
+
writeCart(cart);
|
|
1750
1776
|
}
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
const
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
return order2;
|
|
1777
|
+
function removeFromCart(lineId) {
|
|
1778
|
+
const normalizedLineId = lineId.trim();
|
|
1779
|
+
if (!normalizedLineId) {
|
|
1780
|
+
throw new CartError("line_id is required");
|
|
1781
|
+
}
|
|
1782
|
+
const cart = getCart();
|
|
1783
|
+
const filtered = cart.filter((item) => item.line_id !== normalizedLineId);
|
|
1784
|
+
writeCart(filtered);
|
|
1760
1785
|
}
|
|
1761
|
-
function
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1786
|
+
function clearCart() {
|
|
1787
|
+
removeCartStorage("cart");
|
|
1788
|
+
removeCartStorage("meta");
|
|
1789
|
+
clearCartCoupon();
|
|
1790
|
+
clearLatestQuoteToken();
|
|
1765
1791
|
}
|
|
1766
|
-
function
|
|
1767
|
-
const
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1792
|
+
function createCartBackup() {
|
|
1793
|
+
const cart = getCart();
|
|
1794
|
+
writeCartStorage("cartBackup", cart);
|
|
1795
|
+
const cartCode = getStoredCartCode();
|
|
1796
|
+
if (cartCode) {
|
|
1797
|
+
writeCartStorage("couponBackup", cartCode);
|
|
1798
|
+
} else {
|
|
1799
|
+
removeCartStorage("couponBackup");
|
|
1800
|
+
}
|
|
1801
|
+
const metadata = getCartMetadata();
|
|
1802
|
+
if (metadata) {
|
|
1803
|
+
writeCartStorage("metaBackup", metadata);
|
|
1804
|
+
} else {
|
|
1805
|
+
const now = Date.now();
|
|
1806
|
+
writeCartStorage("metaBackup", {
|
|
1807
|
+
created_at: now,
|
|
1808
|
+
last_modified: now,
|
|
1809
|
+
version: 1,
|
|
1810
|
+
checksum: computeChecksum(cart)
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1771
1813
|
}
|
|
1772
|
-
function
|
|
1773
|
-
const
|
|
1774
|
-
|
|
1775
|
-
|
|
1814
|
+
function restoreCartFromBackup() {
|
|
1815
|
+
const backupRaw = readCartStorage("cartBackup");
|
|
1816
|
+
const backup = normalizeCartItems(backupRaw);
|
|
1817
|
+
const backupMeta = readCartStorage("metaBackup");
|
|
1818
|
+
const backupCoupon = readCartStorage("couponBackup");
|
|
1819
|
+
if (backup && backup.length > 0) {
|
|
1820
|
+
setCartWithMetadata(backup, backupMeta);
|
|
1821
|
+
if (backupCoupon && typeof backupCoupon === "object" && !Array.isArray(backupCoupon)) {
|
|
1822
|
+
const record = backupCoupon;
|
|
1823
|
+
const source = record.source === "COUPON" || record.source === "AFFILIATE" ? record.source : null;
|
|
1824
|
+
if (typeof record.code === "string" && source) {
|
|
1825
|
+
setCartCoupon(record.code, source);
|
|
1826
|
+
} else {
|
|
1827
|
+
clearCartCoupon();
|
|
1828
|
+
}
|
|
1829
|
+
} else {
|
|
1830
|
+
clearCartCoupon();
|
|
1831
|
+
}
|
|
1832
|
+
return true;
|
|
1833
|
+
}
|
|
1834
|
+
return false;
|
|
1776
1835
|
}
|
|
1777
|
-
function
|
|
1778
|
-
const
|
|
1779
|
-
|
|
1780
|
-
|
|
1836
|
+
function mergeBaskets(items) {
|
|
1837
|
+
const cart = getCart();
|
|
1838
|
+
for (const incoming of items) {
|
|
1839
|
+
const productId = typeof incoming.product_id === "string" ? incoming.product_id.trim() : "";
|
|
1840
|
+
const variantId = typeof incoming.variant_id === "string" ? incoming.variant_id.trim() : "";
|
|
1841
|
+
if (!productId || !variantId) {
|
|
1842
|
+
continue;
|
|
1843
|
+
}
|
|
1844
|
+
let normalizedQuantity;
|
|
1845
|
+
try {
|
|
1846
|
+
normalizedQuantity = normalizeQuantity(incoming.quantity);
|
|
1847
|
+
} catch {
|
|
1848
|
+
continue;
|
|
1849
|
+
}
|
|
1850
|
+
if (normalizedQuantity < 1) {
|
|
1851
|
+
continue;
|
|
1852
|
+
}
|
|
1853
|
+
const mergedLine = ensureCartLineId({
|
|
1854
|
+
...incoming,
|
|
1855
|
+
line_id: incoming.line_id ?? computeCartLineId(incoming),
|
|
1856
|
+
product_id: productId,
|
|
1857
|
+
variant_id: variantId,
|
|
1858
|
+
quantity: normalizedQuantity
|
|
1859
|
+
});
|
|
1860
|
+
const index = cart.findIndex((item) => item.line_id === mergedLine.line_id);
|
|
1861
|
+
if (index >= 0) {
|
|
1862
|
+
cart[index].quantity = Math.max(cart[index].quantity, normalizedQuantity);
|
|
1863
|
+
clampQuantityToBounds(cart[index]);
|
|
1864
|
+
} else {
|
|
1865
|
+
clampQuantityToBounds(mergedLine);
|
|
1866
|
+
cart.push(mergedLine);
|
|
1867
|
+
}
|
|
1781
1868
|
}
|
|
1782
|
-
|
|
1869
|
+
writeCart(cart);
|
|
1870
|
+
return cart;
|
|
1783
1871
|
}
|
|
1784
|
-
function
|
|
1785
|
-
const
|
|
1786
|
-
const
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1872
|
+
function moveBasketItem(fromProductId, fromVariantId, toProductId, toVariantId) {
|
|
1873
|
+
const cart = getCart();
|
|
1874
|
+
const fromIndex = cart.findIndex(
|
|
1875
|
+
(item) => item.product_id === fromProductId && item.variant_id === fromVariantId
|
|
1876
|
+
);
|
|
1877
|
+
if (fromIndex < 0) {
|
|
1878
|
+
throw new CartError("Item not found in cart");
|
|
1879
|
+
}
|
|
1880
|
+
const [fromItem] = cart.splice(fromIndex, 1);
|
|
1881
|
+
const toLineId = computeCartLineId({
|
|
1882
|
+
...fromItem,
|
|
1883
|
+
product_id: toProductId,
|
|
1884
|
+
variant_id: toVariantId
|
|
1885
|
+
});
|
|
1886
|
+
const toIndex = cart.findIndex((item) => item.line_id === toLineId);
|
|
1887
|
+
if (toIndex >= 0) {
|
|
1888
|
+
cart[toIndex].quantity += fromItem.quantity;
|
|
1889
|
+
clampQuantityToBounds(cart[toIndex]);
|
|
1890
|
+
} else {
|
|
1891
|
+
const moved = ensureCartLineId({
|
|
1892
|
+
...fromItem,
|
|
1893
|
+
product_id: toProductId,
|
|
1894
|
+
variant_id: toVariantId,
|
|
1895
|
+
line_id: toLineId
|
|
1896
|
+
});
|
|
1897
|
+
clampQuantityToBounds(moved);
|
|
1898
|
+
cart.push(moved);
|
|
1899
|
+
}
|
|
1900
|
+
writeCart(cart);
|
|
1790
1901
|
}
|
|
1791
|
-
function
|
|
1792
|
-
|
|
1793
|
-
|
|
1902
|
+
function validateCartIntegrity() {
|
|
1903
|
+
const cart = getCart();
|
|
1904
|
+
const metadata = getCartMetadata();
|
|
1905
|
+
const checksum = computeChecksum(cart);
|
|
1906
|
+
if (!metadata) {
|
|
1907
|
+
setCartWithMetadata(cart, null);
|
|
1908
|
+
return true;
|
|
1909
|
+
}
|
|
1910
|
+
return metadata.checksum === checksum;
|
|
1794
1911
|
}
|
|
1795
|
-
function
|
|
1796
|
-
const
|
|
1797
|
-
const
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1912
|
+
function getCartStats() {
|
|
1913
|
+
const cart = getCart();
|
|
1914
|
+
const integrityValid = validateCartIntegrity();
|
|
1915
|
+
const metadata = getCartMetadata();
|
|
1916
|
+
const backup = readCartStorage("cartBackup") ?? [];
|
|
1917
|
+
const hasCompletePriceSnapshots = cart.length > 0 && cart.every((item) => typeof item.price_data?.unit_price === "number");
|
|
1918
|
+
const totalPrice = cart.reduce((sum, item) => {
|
|
1919
|
+
const unitPrice = item.price_data?.unit_price ?? 0;
|
|
1920
|
+
return sum + roundPayableAmount(unitPrice * item.quantity);
|
|
1921
|
+
}, 0);
|
|
1922
|
+
return {
|
|
1923
|
+
item_count: cart.length,
|
|
1924
|
+
total_quantity: cart.reduce((sum, item) => sum + item.quantity, 0),
|
|
1925
|
+
last_modified: metadata?.last_modified ?? 0,
|
|
1926
|
+
version: metadata?.version ?? 0,
|
|
1927
|
+
has_backup: backup.length > 0,
|
|
1928
|
+
integrity_valid: integrityValid,
|
|
1929
|
+
total_price: roundPayableAmount(totalPrice),
|
|
1930
|
+
total_price_is_estimate: cart.length > 0 && !hasCompletePriceSnapshots
|
|
1805
1931
|
};
|
|
1806
1932
|
}
|
|
1807
|
-
|
|
1808
|
-
var latestCartRequest = 0;
|
|
1809
|
-
function enqueueCartSend(send) {
|
|
1810
|
-
const request2 = ++latestCartRequest;
|
|
1811
|
-
const run = () => send(() => request2 === latestCartRequest);
|
|
1812
|
-
cartSendTail = cartSendTail.then(run, run);
|
|
1813
|
-
return cartSendTail;
|
|
1814
|
-
}
|
|
1815
|
-
async function postPing(body) {
|
|
1933
|
+
function getCartPayload(coupon) {
|
|
1816
1934
|
const config = getConfig();
|
|
1817
|
-
const
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
}, CART_CLAIM_TTL_MS);
|
|
1824
|
-
});
|
|
1825
|
-
try {
|
|
1826
|
-
const endpoint = buildEndpoint("/v1/storefront/shops/:storeSlug/ping", {
|
|
1827
|
-
storeSlug: config.storeSlug
|
|
1828
|
-
});
|
|
1829
|
-
const delivered = fetch(`${config.apiBaseUrl}${endpoint}`, {
|
|
1830
|
-
method: "POST",
|
|
1831
|
-
headers: { "Content-Type": "application/json" },
|
|
1832
|
-
body: JSON.stringify(body),
|
|
1833
|
-
signal: controller?.signal
|
|
1834
|
-
}).then((response) => response.ok);
|
|
1835
|
-
return await Promise.race([delivered, timedOut]);
|
|
1836
|
-
} catch {
|
|
1837
|
-
return false;
|
|
1838
|
-
} finally {
|
|
1839
|
-
clearTimeout(timer);
|
|
1840
|
-
}
|
|
1935
|
+
const normalizedCoupon = normalizeCouponCode(coupon) ?? getCartCoupon() ?? void 0;
|
|
1936
|
+
return {
|
|
1937
|
+
store_slug: config.storeSlug,
|
|
1938
|
+
items: getCart(),
|
|
1939
|
+
coupon: normalizedCoupon
|
|
1940
|
+
};
|
|
1841
1941
|
}
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
const reservedOrder = carriesCart ? reserveCartOrder(config.storeSlug) : void 0;
|
|
1848
|
-
if (isPlatformPageTrackerActive()) {
|
|
1849
|
-
if (reservedOrder) await enqueueCartSend(async () => sendCartActivity(await reservedOrder, cartValue, itemCount));
|
|
1850
|
-
return;
|
|
1942
|
+
function getAmbientAffiliateCodeForQuote() {
|
|
1943
|
+
const storedCode = getCartCoupon();
|
|
1944
|
+
const storedSource = getCartCouponSource();
|
|
1945
|
+
if (storedSource === "AFFILIATE" && storedCode) {
|
|
1946
|
+
return storedCode.trim().toLowerCase();
|
|
1851
1947
|
}
|
|
1852
|
-
|
|
1853
|
-
const order2 = reservedOrder ? await reservedOrder : void 0;
|
|
1854
|
-
const settle = order2 ? claimCartReport(config.storeSlug, order2.seq, cartValue, itemCount) : void 0;
|
|
1855
|
-
const sessionId = analyticsSessionId(config.storeSlug, { touch: true });
|
|
1856
|
-
const previousSession = currentPageSession();
|
|
1857
|
-
if (previousSession && previousSession !== sessionId) reportPageLeave();
|
|
1858
|
-
markPageViewed(sessionId);
|
|
1859
|
-
if (sessionId) installEngagedClock(reportPageLeave);
|
|
1860
|
-
onAnalyticsConsent(startSessionOnConsent);
|
|
1861
|
-
const delivered = await postPing({
|
|
1862
|
-
referer: document.referrer || void 0,
|
|
1863
|
-
cart_value: cartValue,
|
|
1864
|
-
item_count: itemCount,
|
|
1865
|
-
cart_seq: order2?.seq,
|
|
1866
|
-
cart_seq_scope: order2?.scope,
|
|
1867
|
-
session_id: sessionId ?? void 0,
|
|
1868
|
-
// Time on screen of the page before this one (client-side navigation).
|
|
1869
|
-
engaged_ms: sessionId ? takeEngagedMs() || void 0 : void 0,
|
|
1870
|
-
path: resolveStorefrontPagePath(),
|
|
1871
|
-
utm: resolveUtmParameters(config.storeSlug)
|
|
1872
|
-
});
|
|
1873
|
-
settle?.(delivered);
|
|
1874
|
-
};
|
|
1875
|
-
await (reservedOrder ? enqueueCartSend(sendView) : sendView());
|
|
1948
|
+
return getAffiliateCode();
|
|
1876
1949
|
}
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
const reservedOrder = reserveCartOrder(getConfig().storeSlug);
|
|
1882
|
-
await enqueueCartSend(async (isLatest) => {
|
|
1883
|
-
const order2 = await reservedOrder;
|
|
1884
|
-
if (!isLatest() || isCartReportedOrInFlight(getConfig().storeSlug, snapshot)) return;
|
|
1885
|
-
await sendCartActivity(order2, cartValue, itemCount);
|
|
1886
|
-
});
|
|
1950
|
+
var latestQuoteToken = null;
|
|
1951
|
+
var quoteSequence = 0;
|
|
1952
|
+
function getLatestQuoteToken() {
|
|
1953
|
+
return latestQuoteToken;
|
|
1887
1954
|
}
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
const delivered = await postPing({
|
|
1892
|
-
event_type: "presence",
|
|
1893
|
-
cart_value: cartValue,
|
|
1894
|
-
item_count: itemCount,
|
|
1895
|
-
cart_seq: order2.seq,
|
|
1896
|
-
cart_seq_scope: order2.scope,
|
|
1897
|
-
// A cart marks the analytics session as one that did not bounce.
|
|
1898
|
-
session_id: analyticsSessionId(config.storeSlug, { touch: false }) ?? void 0,
|
|
1899
|
-
path: resolveStorefrontPagePath()
|
|
1900
|
-
});
|
|
1901
|
-
settle(delivered);
|
|
1955
|
+
function clearLatestQuoteToken() {
|
|
1956
|
+
latestQuoteToken = null;
|
|
1957
|
+
quoteSequence += 1;
|
|
1902
1958
|
}
|
|
1903
|
-
function
|
|
1904
|
-
const
|
|
1905
|
-
|
|
1906
|
-
const
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
session_id: sessionId,
|
|
1914
|
-
path: resolveStorefrontPagePath(),
|
|
1915
|
-
utm: resolveUtmParameters(config.storeSlug)
|
|
1959
|
+
async function quoteCart(coupon, currency) {
|
|
1960
|
+
const sequence = quoteSequence += 1;
|
|
1961
|
+
const payload = getCartPayload(coupon);
|
|
1962
|
+
const normalizedCurrency = normalizeRequestedCurrency(currency) ?? resolveBuyerCurrency(getConfig().currency);
|
|
1963
|
+
const response = await post("/v1/storefront/cart/quote", {
|
|
1964
|
+
shop_slug: payload.store_slug,
|
|
1965
|
+
cart: payload.items,
|
|
1966
|
+
coupon: payload.coupon,
|
|
1967
|
+
affiliate_code: getAmbientAffiliateCodeForQuote() ?? void 0,
|
|
1968
|
+
currency: normalizedCurrency ?? void 0
|
|
1916
1969
|
});
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
const sessionId = currentPageSession();
|
|
1921
|
-
const engagedMs2 = takeEngagedMs();
|
|
1922
|
-
if (!sessionId || engagedMs2 === 0 || readAnalyticsConsent() !== "granted") return;
|
|
1923
|
-
try {
|
|
1924
|
-
const endpoint = buildEndpoint("/v1/storefront/shops/:storeSlug/ping", { storeSlug: config.storeSlug });
|
|
1925
|
-
void fetch(`${config.apiBaseUrl}${endpoint}`, {
|
|
1926
|
-
method: "POST",
|
|
1927
|
-
headers: { "Content-Type": "application/json" },
|
|
1928
|
-
body: JSON.stringify({ event_type: "page_leave", session_id: sessionId, engaged_ms: engagedMs2 }),
|
|
1929
|
-
keepalive: true
|
|
1930
|
-
}).catch(() => {
|
|
1931
|
-
});
|
|
1932
|
-
} catch {
|
|
1970
|
+
if (sequence !== quoteSequence) return response;
|
|
1971
|
+
if (response.success) {
|
|
1972
|
+
latestQuoteToken = typeof response.data?.quote_token === "string" ? response.data.quote_token : null;
|
|
1933
1973
|
}
|
|
1974
|
+
return response;
|
|
1934
1975
|
}
|
|
1935
1976
|
|
|
1936
1977
|
// ../sdk/src/storefront-storage-sdk.ts
|
|
@@ -1976,7 +2017,8 @@ var storefrontStorageSdk = {
|
|
|
1976
2017
|
trackAffiliateEvent,
|
|
1977
2018
|
// Analytics
|
|
1978
2019
|
trackPageView,
|
|
1979
|
-
trackCartActivity
|
|
2020
|
+
trackCartActivity,
|
|
2021
|
+
trackProductView
|
|
1980
2022
|
};
|
|
1981
2023
|
|
|
1982
2024
|
// ../../node_modules/.bun/openapi-fetch@0.17.0/node_modules/openapi-fetch/dist/index.mjs
|
|
@@ -2785,15 +2827,21 @@ async function getCategories() {
|
|
|
2785
2827
|
}
|
|
2786
2828
|
|
|
2787
2829
|
// ../contracts/src/redirect-link-template.ts
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2830
|
+
function isInsideDoubleBrace(raw) {
|
|
2831
|
+
return raw.startsWith("{{");
|
|
2832
|
+
}
|
|
2833
|
+
function placeholderMatches(template, patterns) {
|
|
2834
|
+
return [
|
|
2835
|
+
...template.matchAll(patterns.double),
|
|
2836
|
+
...[...template.matchAll(patterns.single)].filter((match) => !isInsideDoubleBrace(match[0]))
|
|
2837
|
+
];
|
|
2838
|
+
}
|
|
2839
|
+
var REMAINING_PLACEHOLDER_PATTERNS = {
|
|
2840
|
+
double: /\{\{[\s\S]*?\}\}/g,
|
|
2841
|
+
single: /\{?\{[^{}]+\}(?!\})/g
|
|
2842
|
+
};
|
|
2792
2843
|
function findDanglingBraceFragments(template) {
|
|
2793
|
-
|
|
2794
|
-
for (const pattern of REMAINING_PLACEHOLDER_PATTERNS) {
|
|
2795
|
-
stripped = stripped.replace(pattern, "");
|
|
2796
|
-
}
|
|
2844
|
+
const stripped = template.replace(REMAINING_PLACEHOLDER_PATTERNS.double, "").replace(REMAINING_PLACEHOLDER_PATTERNS.single, (raw) => isInsideDoubleBrace(raw) ? raw : "");
|
|
2797
2845
|
return /[{}]/.test(stripped) ? [...new Set(stripped.match(/[{}]+/g) ?? [])] : [];
|
|
2798
2846
|
}
|
|
2799
2847
|
function findRemainingProductRedirectPlaceholders(template) {
|
|
@@ -2801,10 +2849,8 @@ function findRemainingProductRedirectPlaceholders(template) {
|
|
|
2801
2849
|
return [];
|
|
2802
2850
|
}
|
|
2803
2851
|
const remaining = /* @__PURE__ */ new Set();
|
|
2804
|
-
for (const
|
|
2805
|
-
|
|
2806
|
-
remaining.add(match[0]);
|
|
2807
|
-
}
|
|
2852
|
+
for (const match of placeholderMatches(template, REMAINING_PLACEHOLDER_PATTERNS)) {
|
|
2853
|
+
remaining.add(match[0]);
|
|
2808
2854
|
}
|
|
2809
2855
|
for (const fragment of findDanglingBraceFragments(template)) {
|
|
2810
2856
|
remaining.add(fragment);
|
|
@@ -4442,6 +4488,9 @@ function order(id) {
|
|
|
4442
4488
|
schema: customerPortalInvoiceDetailSchema
|
|
4443
4489
|
});
|
|
4444
4490
|
}
|
|
4491
|
+
function orderReview(uniqid) {
|
|
4492
|
+
return requestCustomer(`/invoices/${encodeURIComponent(uniqid)}/review`, { schema: customerPortalOrderReviewSchema });
|
|
4493
|
+
}
|
|
4445
4494
|
async function invoicePdf(id) {
|
|
4446
4495
|
try {
|
|
4447
4496
|
const response = await fetch(`${CUSTOMER_API_PREFIX}/invoices/${encodeURIComponent(id)}/pdf`, {
|
|
@@ -4574,19 +4623,29 @@ function createTicket(payload) {
|
|
|
4574
4623
|
body: {
|
|
4575
4624
|
...payload.title !== void 0 ? { title: payload.title } : {},
|
|
4576
4625
|
message: payload.message,
|
|
4577
|
-
...payload.invoice_id !== void 0 ? { invoice_id: payload.invoice_id } : {}
|
|
4626
|
+
...payload.invoice_id !== void 0 ? { invoice_id: payload.invoice_id } : {},
|
|
4627
|
+
...payload.attachment_ids?.length ? { attachment_ids: payload.attachment_ids } : {}
|
|
4578
4628
|
}
|
|
4579
4629
|
});
|
|
4580
4630
|
}
|
|
4581
4631
|
function ticket(uniqid) {
|
|
4582
4632
|
return requestCustomer(`/tickets/${encodeURIComponent(uniqid)}`);
|
|
4583
4633
|
}
|
|
4584
|
-
function replyToTicket(uniqid, message) {
|
|
4634
|
+
function replyToTicket(uniqid, message, attachmentIds = []) {
|
|
4585
4635
|
return requestCustomer(`/tickets/${encodeURIComponent(uniqid)}/reply`, {
|
|
4586
4636
|
method: "POST",
|
|
4587
|
-
body: { message }
|
|
4637
|
+
body: attachmentIds.length > 0 ? { message, attachment_ids: attachmentIds } : { message }
|
|
4588
4638
|
});
|
|
4589
4639
|
}
|
|
4640
|
+
function uploadTicketImage(file, ticket2) {
|
|
4641
|
+
const form = new FormData();
|
|
4642
|
+
form.append("file", file);
|
|
4643
|
+
if (ticket2) form.append("ticket", ticket2);
|
|
4644
|
+
return requestCustomer("/ticket-attachments", { method: "POST", formData: form });
|
|
4645
|
+
}
|
|
4646
|
+
function discardTicketImage(id) {
|
|
4647
|
+
return requestCustomer(`/ticket-attachments/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
4648
|
+
}
|
|
4590
4649
|
function updateProfile(patch) {
|
|
4591
4650
|
return requestCustomer("/profile", {
|
|
4592
4651
|
method: "POST",
|
|
@@ -4697,6 +4756,7 @@ var shoppex = {
|
|
|
4697
4756
|
redeemLoyaltyPoints,
|
|
4698
4757
|
warranties,
|
|
4699
4758
|
claimWarranty,
|
|
4759
|
+
orderReview,
|
|
4700
4760
|
resetLicenseHwid,
|
|
4701
4761
|
subscriptionBillingHistory,
|
|
4702
4762
|
cancelSubscription,
|
|
@@ -4712,6 +4772,8 @@ var shoppex = {
|
|
|
4712
4772
|
createTicket,
|
|
4713
4773
|
ticket,
|
|
4714
4774
|
replyToTicket,
|
|
4775
|
+
uploadTicketImage,
|
|
4776
|
+
discardTicketImage,
|
|
4715
4777
|
updateProfile,
|
|
4716
4778
|
updateAvatar,
|
|
4717
4779
|
removeAvatar,
|
|
@@ -4769,6 +4831,7 @@ export {
|
|
|
4769
4831
|
createTicket,
|
|
4770
4832
|
dashboard,
|
|
4771
4833
|
src_default as default,
|
|
4834
|
+
discardTicketImage,
|
|
4772
4835
|
emailPreferences,
|
|
4773
4836
|
enrollAsReseller,
|
|
4774
4837
|
ensureCartLineId,
|
|
@@ -4800,6 +4863,7 @@ export {
|
|
|
4800
4863
|
normalizeStorefrontCustomFields,
|
|
4801
4864
|
notificationPreferences,
|
|
4802
4865
|
order,
|
|
4866
|
+
orderReview,
|
|
4803
4867
|
orders,
|
|
4804
4868
|
pauseSubscription,
|
|
4805
4869
|
productMatchesSearchQuery,
|
|
@@ -4836,11 +4900,13 @@ export {
|
|
|
4836
4900
|
touchStorefrontPresence,
|
|
4837
4901
|
trackCartActivity,
|
|
4838
4902
|
trackPageView,
|
|
4903
|
+
trackProductView,
|
|
4839
4904
|
undoSubscriptionCancel,
|
|
4840
4905
|
updateAvatar,
|
|
4841
4906
|
updateEmailPreferences,
|
|
4842
4907
|
updateNotificationPreferences,
|
|
4843
4908
|
updateProfile,
|
|
4909
|
+
uploadTicketImage,
|
|
4844
4910
|
validateStorefrontCustomFieldValue,
|
|
4845
4911
|
verifyOtp,
|
|
4846
4912
|
warranties
|