@shoppexio/storefront 1.0.102 → 1.0.104
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/README.md +2 -2
- package/dist/{chunk-ESI3HWQF.js → chunk-BW2EO2WP.js} +23 -1
- package/dist/{chunk-ESI3HWQF.js.map → chunk-BW2EO2WP.js.map} +1 -1
- package/dist/customer.cjs +22 -0
- package/dist/customer.cjs.map +1 -1
- package/dist/customer.js +1 -1
- package/dist/index.cjs +905 -671
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -2
- package/dist/index.d.ts +24 -2
- package/dist/index.js +884 -675
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -48,6 +48,7 @@ __export(src_exports, {
|
|
|
48
48
|
createTicket: () => createTicket,
|
|
49
49
|
dashboard: () => dashboard,
|
|
50
50
|
default: () => src_default,
|
|
51
|
+
discardTicketImage: () => discardTicketImage,
|
|
51
52
|
emailPreferences: () => emailPreferences,
|
|
52
53
|
enrollAsReseller: () => enrollAsReseller,
|
|
53
54
|
ensureCartLineId: () => ensureCartLineId,
|
|
@@ -115,11 +116,13 @@ __export(src_exports, {
|
|
|
115
116
|
touchStorefrontPresence: () => touchStorefrontPresence,
|
|
116
117
|
trackCartActivity: () => trackCartActivity,
|
|
117
118
|
trackPageView: () => trackPageView,
|
|
119
|
+
trackProductView: () => trackProductView,
|
|
118
120
|
undoSubscriptionCancel: () => undoSubscriptionCancel,
|
|
119
121
|
updateAvatar: () => updateAvatar,
|
|
120
122
|
updateEmailPreferences: () => updateEmailPreferences,
|
|
121
123
|
updateNotificationPreferences: () => updateNotificationPreferences,
|
|
122
124
|
updateProfile: () => updateProfile,
|
|
125
|
+
uploadTicketImage: () => uploadTicketImage,
|
|
123
126
|
validateStorefrontCustomFieldValue: () => validateStorefrontCustomFieldValue,
|
|
124
127
|
verifyOtp: () => verifyOtp,
|
|
125
128
|
warranties: () => warranties
|
|
@@ -399,27 +402,9 @@ function buildEndpoint(template, params) {
|
|
|
399
402
|
}
|
|
400
403
|
|
|
401
404
|
// ../sdk/src/core/telemetry.ts
|
|
402
|
-
var CONNECTION_ID_STORAGE_PREFIX = "presence_connection_";
|
|
403
405
|
var CLIENT_ERROR_DEDUPE_WINDOW_MS = 15e3;
|
|
404
406
|
var CLIENT_ERROR_TEXT_MAX_LENGTH = 500;
|
|
405
407
|
var CLIENT_ERROR_URL_MAX_LENGTH = 2e3;
|
|
406
|
-
function createPresenceConnectionId() {
|
|
407
|
-
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
408
|
-
return crypto.randomUUID();
|
|
409
|
-
}
|
|
410
|
-
return `spx_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
|
|
411
|
-
}
|
|
412
|
-
function getStorefrontConnectionId(storeSlug) {
|
|
413
|
-
if (typeof window === "undefined") return null;
|
|
414
|
-
const storageKey2 = `${CONNECTION_ID_STORAGE_PREFIX}${storeSlug}`;
|
|
415
|
-
const existing = getItem(storageKey2);
|
|
416
|
-
if (existing && existing.trim()) {
|
|
417
|
-
return existing;
|
|
418
|
-
}
|
|
419
|
-
const nextId = createPresenceConnectionId();
|
|
420
|
-
setItem(storageKey2, nextId);
|
|
421
|
-
return nextId;
|
|
422
|
-
}
|
|
423
408
|
function getRecentClientErrorKey(payload) {
|
|
424
409
|
return [
|
|
425
410
|
payload.method,
|
|
@@ -449,7 +434,6 @@ async function reportStorefrontClientError(payload) {
|
|
|
449
434
|
const body = JSON.stringify({
|
|
450
435
|
event_type: "client_error",
|
|
451
436
|
referer: document.referrer || void 0,
|
|
452
|
-
connection_id: getStorefrontConnectionId(config2.storeSlug) ?? void 0,
|
|
453
437
|
client_error: {
|
|
454
438
|
source: payload.source ?? "sdk",
|
|
455
439
|
phase: payload.phase ?? "request",
|
|
@@ -981,266 +965,747 @@ async function captureAffiliateFromUrl(param = "ref") {
|
|
|
981
965
|
return holdsOtherCode ? held.code : code;
|
|
982
966
|
}
|
|
983
967
|
|
|
984
|
-
// ../
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
968
|
+
// ../contracts/src/storefront-consent-choice.ts
|
|
969
|
+
var COOKIE_CONSENT_VERSION = 1;
|
|
970
|
+
var COOKIE_CONSENT_COOKIE = "shoppex_consent";
|
|
971
|
+
var COOKIE_CONSENT_CHECKOUT_HASH_KEY = "shoppex_consent";
|
|
972
|
+
var COOKIE_CONSENT_VALUE = new RegExp(`^v${COOKIE_CONSENT_VERSION}\\.f([01])\\.a([01])\\.m([01])$`);
|
|
973
|
+
function parseCookieConsentValue(value) {
|
|
974
|
+
const match = value ? COOKIE_CONSENT_VALUE.exec(value) : null;
|
|
975
|
+
return match ? { functional: match[1] === "1", analytics: match[2] === "1", marketing: match[3] === "1" } : null;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// ../sdk/src/core/analytics-session.ts
|
|
979
|
+
var ANALYTICS_SESSION_IDLE_MS = 30 * 60 * 1e3;
|
|
980
|
+
var ANALYTICS_VISITS_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
981
|
+
var ANALYTICS_VISITS_MAX = 20;
|
|
982
|
+
var MAX_ENGAGED_MS_PER_PAGE = 30 * 60 * 1e3;
|
|
983
|
+
var UTM_STORAGE_PREFIX = "shoppex:utm:";
|
|
984
|
+
var analyticsSessionKey = (storeSlug) => `analytics_session_${storeSlug}`;
|
|
985
|
+
var analyticsVisitsKey = (storeSlug) => `analytics_visits_${storeSlug}`;
|
|
986
|
+
function pageLanding() {
|
|
987
|
+
let r = "";
|
|
988
|
+
try {
|
|
989
|
+
r = document.referrer ? new URL(document.referrer).hostname : "";
|
|
990
|
+
} catch {
|
|
990
991
|
}
|
|
991
|
-
|
|
992
|
+
const params = new URLSearchParams(window.location?.search ?? "");
|
|
993
|
+
const u = ["source", "medium", "campaign"].map((key) => params.get(`utm_${key}`)?.trim().toLowerCase() ?? "").join("|");
|
|
994
|
+
return { r: r === window.location?.hostname ? "" : r, u: u === "||" ? "" : u };
|
|
992
995
|
}
|
|
993
|
-
function
|
|
994
|
-
if (
|
|
995
|
-
|
|
996
|
+
function readAnalyticsConsent() {
|
|
997
|
+
if (typeof document === "undefined" || typeof navigator === "undefined") return "unknown";
|
|
998
|
+
const privacy = navigator;
|
|
999
|
+
if (privacy.globalPrivacyControl === true || privacy.doNotTrack === "1") return "declined";
|
|
1000
|
+
const prefix = `${COOKIE_CONSENT_COOKIE}=`;
|
|
1001
|
+
let raw;
|
|
1002
|
+
try {
|
|
1003
|
+
raw = document.cookie.split(/;\s*/).find((part) => part.startsWith(prefix))?.slice(prefix.length);
|
|
1004
|
+
} catch {
|
|
1005
|
+
return "unknown";
|
|
1006
|
+
}
|
|
1007
|
+
const choice = parseCookieConsentValue(raw ?? null);
|
|
1008
|
+
if (!choice) return "unknown";
|
|
1009
|
+
return choice.analytics ? "granted" : "declined";
|
|
996
1010
|
}
|
|
997
|
-
function
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
return
|
|
1011
|
+
function readVisits(storeSlug, now) {
|
|
1012
|
+
const stored = getItem(analyticsVisitsKey(storeSlug));
|
|
1013
|
+
if (!Array.isArray(stored)) return [];
|
|
1014
|
+
return stored.filter((visit2) => typeof visit2?.id === "string" && typeof visit2.at === "number" && now - visit2.at < ANALYTICS_VISITS_WINDOW_MS);
|
|
1001
1015
|
}
|
|
1002
|
-
function
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
variant_id: input2.variant_id,
|
|
1006
|
-
price_variant_id: input2.price_variant_id ?? null,
|
|
1007
|
-
addons: normalizeAddons(input2.addons),
|
|
1008
|
-
custom_fields: normalizeCustomFields(input2.custom_fields),
|
|
1009
|
-
unit_price: typeof input2.price_data?.unit_price === "number" && Number.isFinite(input2.price_data.unit_price) ? input2.price_data.unit_price : null,
|
|
1010
|
-
pay_what_you_want_price: typeof input2.pay_what_you_want_price === "number" && Number.isFinite(input2.pay_what_you_want_price) ? input2.pay_what_you_want_price : null
|
|
1011
|
-
};
|
|
1012
|
-
return hashString(JSON.stringify(payload));
|
|
1016
|
+
function forgetAnalyticsData(storeSlug) {
|
|
1017
|
+
removeItem(analyticsSessionKey(storeSlug), "sessionStorage");
|
|
1018
|
+
removeItem(analyticsVisitsKey(storeSlug));
|
|
1013
1019
|
}
|
|
1014
|
-
function
|
|
1015
|
-
|
|
1016
|
-
|
|
1020
|
+
function analyticsSessionId(storeSlug, options, now = Date.now()) {
|
|
1021
|
+
removeItem(`presence_connection_${storeSlug}`);
|
|
1022
|
+
if (readAnalyticsConsent() !== "granted") {
|
|
1023
|
+
forgetAnalyticsData(storeSlug);
|
|
1024
|
+
return null;
|
|
1017
1025
|
}
|
|
1018
|
-
|
|
1026
|
+
const stored = getItem(analyticsSessionKey(storeSlug), "sessionStorage");
|
|
1027
|
+
const live = stored && typeof stored.id === "string" && typeof stored.at === "number" && now - stored.at < ANALYTICS_SESSION_IDLE_MS ? stored.id : null;
|
|
1028
|
+
if (!options.touch) return live;
|
|
1029
|
+
const landing = pageLanding();
|
|
1030
|
+
const newLanding = landing.r && landing.r !== stored?.r || landing.u && landing.u !== stored?.u;
|
|
1031
|
+
if (live && !newLanding) {
|
|
1032
|
+
setItem(analyticsSessionKey(storeSlug), { ...stored, id: live, at: now }, "sessionStorage");
|
|
1033
|
+
return live;
|
|
1034
|
+
}
|
|
1035
|
+
if (typeof crypto === "undefined" || typeof crypto.randomUUID !== "function") return null;
|
|
1036
|
+
if (landing.r) {
|
|
1037
|
+
try {
|
|
1038
|
+
globalThis.localStorage.removeItem(storefrontStorageKey(`${UTM_STORAGE_PREFIX}${storeSlug}`));
|
|
1039
|
+
} catch {
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
const id = crypto.randomUUID();
|
|
1043
|
+
setItem(analyticsSessionKey(storeSlug), { id, at: now, ...landing }, "sessionStorage");
|
|
1044
|
+
setItem(
|
|
1045
|
+
analyticsVisitsKey(storeSlug),
|
|
1046
|
+
[{ id, at: now }, ...readVisits(storeSlug, now)].slice(0, ANALYTICS_VISITS_MAX)
|
|
1047
|
+
);
|
|
1048
|
+
return id;
|
|
1049
|
+
}
|
|
1050
|
+
function checkoutAttribution(storeSlug, now = Date.now()) {
|
|
1051
|
+
const consent = readAnalyticsConsent();
|
|
1052
|
+
if (consent === "declined") {
|
|
1053
|
+
forgetAnalyticsData(storeSlug);
|
|
1054
|
+
return { declined: true };
|
|
1055
|
+
}
|
|
1056
|
+
if (consent === "unknown") return void 0;
|
|
1057
|
+
const sessionIds = readVisits(storeSlug, now).map((visit2) => visit2.id);
|
|
1058
|
+
return sessionIds.length > 0 ? { session_ids: sessionIds } : void 0;
|
|
1059
|
+
}
|
|
1060
|
+
var engagedMs = 0;
|
|
1061
|
+
var visibleSince = null;
|
|
1062
|
+
var clockInstalled = false;
|
|
1063
|
+
var pageViewed = false;
|
|
1064
|
+
var pageSession = null;
|
|
1065
|
+
function markPageViewed(sessionId) {
|
|
1066
|
+
pageViewed = true;
|
|
1067
|
+
pageSession = sessionId;
|
|
1068
|
+
}
|
|
1069
|
+
function currentPageSession() {
|
|
1070
|
+
return pageSession;
|
|
1071
|
+
}
|
|
1072
|
+
function isPageViewed() {
|
|
1073
|
+
return pageViewed;
|
|
1074
|
+
}
|
|
1075
|
+
function settleClock(now) {
|
|
1076
|
+
if (visibleSince !== null) {
|
|
1077
|
+
engagedMs += now - visibleSince;
|
|
1078
|
+
visibleSince = document.visibilityState === "visible" ? now : null;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
var consentListenerInstalled = false;
|
|
1082
|
+
function onAnalyticsConsent(onYes) {
|
|
1083
|
+
if (consentListenerInstalled || typeof window === "undefined") return;
|
|
1084
|
+
consentListenerInstalled = true;
|
|
1085
|
+
window.addEventListener("shoppex:consent", () => {
|
|
1086
|
+
if (readAnalyticsConsent() === "granted") onYes();
|
|
1087
|
+
});
|
|
1019
1088
|
}
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1089
|
+
function installEngagedClock(onHidden) {
|
|
1090
|
+
if (clockInstalled || typeof document === "undefined") return;
|
|
1091
|
+
clockInstalled = true;
|
|
1092
|
+
visibleSince = document.visibilityState === "visible" ? Date.now() : null;
|
|
1093
|
+
document.addEventListener("visibilitychange", () => {
|
|
1094
|
+
const now = Date.now();
|
|
1095
|
+
if (document.visibilityState === "visible") {
|
|
1096
|
+
visibleSince ?? (visibleSince = now);
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
settleClock(now);
|
|
1100
|
+
visibleSince = null;
|
|
1101
|
+
onHidden();
|
|
1102
|
+
});
|
|
1103
|
+
window.addEventListener("pagehide", () => {
|
|
1104
|
+
settleClock(Date.now());
|
|
1105
|
+
visibleSince = null;
|
|
1106
|
+
onHidden();
|
|
1107
|
+
});
|
|
1108
|
+
window.addEventListener("pageshow", (event) => {
|
|
1109
|
+
if (event.persisted && document.visibilityState === "visible") visibleSince = Date.now();
|
|
1110
|
+
});
|
|
1034
1111
|
}
|
|
1035
|
-
function
|
|
1036
|
-
|
|
1037
|
-
|
|
1112
|
+
function takeEngagedMs(now = Date.now()) {
|
|
1113
|
+
settleClock(now);
|
|
1114
|
+
const taken = Math.min(Math.max(0, Math.round(engagedMs)), MAX_ENGAGED_MS_PER_PAGE);
|
|
1115
|
+
engagedMs = 0;
|
|
1116
|
+
return taken;
|
|
1038
1117
|
}
|
|
1039
1118
|
|
|
1040
|
-
// ../sdk/src/
|
|
1041
|
-
var
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1119
|
+
// ../sdk/src/core/attribution.ts
|
|
1120
|
+
var TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
1121
|
+
var UTM_KEYS = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"];
|
|
1122
|
+
var MAX_VALUE_LENGTH = 255;
|
|
1123
|
+
function readFromUrl(search) {
|
|
1124
|
+
let params;
|
|
1125
|
+
try {
|
|
1126
|
+
params = new URLSearchParams(search);
|
|
1127
|
+
} catch {
|
|
1128
|
+
return {};
|
|
1129
|
+
}
|
|
1130
|
+
const utm = {};
|
|
1131
|
+
for (const key of UTM_KEYS) {
|
|
1132
|
+
const value = params.get(key)?.trim();
|
|
1133
|
+
if (value) {
|
|
1134
|
+
utm[key] = value.slice(0, MAX_VALUE_LENGTH);
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
return utm;
|
|
1054
1138
|
}
|
|
1055
|
-
function
|
|
1056
|
-
return
|
|
1139
|
+
function storageKey(storeSlug) {
|
|
1140
|
+
return `${UTM_STORAGE_PREFIX}${storeSlug}`;
|
|
1057
1141
|
}
|
|
1058
|
-
function
|
|
1059
|
-
|
|
1142
|
+
function readStored(storeSlug) {
|
|
1143
|
+
try {
|
|
1144
|
+
const raw = window.localStorage.getItem(storefrontStorageKey(storageKey(storeSlug)));
|
|
1145
|
+
if (!raw) return {};
|
|
1146
|
+
const parsed = JSON.parse(raw);
|
|
1147
|
+
if (!parsed || typeof parsed.at !== "number" || typeof parsed.utm !== "object") return {};
|
|
1148
|
+
if (Date.now() - parsed.at > TTL_MS) {
|
|
1149
|
+
window.localStorage.removeItem(storefrontStorageKey(storageKey(storeSlug)));
|
|
1150
|
+
return {};
|
|
1151
|
+
}
|
|
1152
|
+
const utm = {};
|
|
1153
|
+
for (const key of UTM_KEYS) {
|
|
1154
|
+
const value = parsed.utm?.[key];
|
|
1155
|
+
if (typeof value === "string" && value.length > 0) {
|
|
1156
|
+
utm[key] = value.slice(0, MAX_VALUE_LENGTH);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
return utm;
|
|
1160
|
+
} catch {
|
|
1161
|
+
return {};
|
|
1162
|
+
}
|
|
1060
1163
|
}
|
|
1061
|
-
function
|
|
1062
|
-
|
|
1164
|
+
function writeStored(storeSlug, utm) {
|
|
1165
|
+
try {
|
|
1166
|
+
const payload = { utm, at: Date.now() };
|
|
1167
|
+
window.localStorage.setItem(storefrontStorageKey(storageKey(storeSlug)), JSON.stringify(payload));
|
|
1168
|
+
} catch {
|
|
1169
|
+
}
|
|
1063
1170
|
}
|
|
1064
|
-
function
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
hash2 ^= value.charCodeAt(i);
|
|
1068
|
-
hash2 += (hash2 << 1) + (hash2 << 4) + (hash2 << 7) + (hash2 << 8) + (hash2 << 24);
|
|
1171
|
+
function resolveUtmParameters(storeSlug) {
|
|
1172
|
+
if (typeof window === "undefined" || typeof window.location === "undefined") {
|
|
1173
|
+
return void 0;
|
|
1069
1174
|
}
|
|
1070
|
-
|
|
1175
|
+
const fromUrl = readFromUrl(window.location.search);
|
|
1176
|
+
if (readAnalyticsConsent() !== "granted") {
|
|
1177
|
+
try {
|
|
1178
|
+
window.localStorage.removeItem(storefrontStorageKey(storageKey(storeSlug)));
|
|
1179
|
+
} catch {
|
|
1180
|
+
}
|
|
1181
|
+
return Object.keys(fromUrl).length > 0 ? fromUrl : void 0;
|
|
1182
|
+
}
|
|
1183
|
+
if (Object.keys(fromUrl).length > 0) {
|
|
1184
|
+
writeStored(storeSlug, fromUrl);
|
|
1185
|
+
return fromUrl;
|
|
1186
|
+
}
|
|
1187
|
+
const stored = readStored(storeSlug);
|
|
1188
|
+
return Object.keys(stored).length > 0 ? stored : void 0;
|
|
1071
1189
|
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1190
|
+
|
|
1191
|
+
// ../sdk/src/core/page-path.ts
|
|
1192
|
+
var MAX_PATH_LENGTH = 256;
|
|
1193
|
+
function resolveStorefrontPagePath() {
|
|
1194
|
+
if (typeof window === "undefined") return void 0;
|
|
1195
|
+
let pathname;
|
|
1196
|
+
try {
|
|
1197
|
+
const location = window.location;
|
|
1198
|
+
pathname = typeof location?.pathname === "string" && location.pathname ? location.pathname : new URL(String(location?.href ?? "")).pathname;
|
|
1199
|
+
} catch {
|
|
1200
|
+
return void 0;
|
|
1201
|
+
}
|
|
1202
|
+
if (!pathname || !pathname.startsWith("/")) return void 0;
|
|
1203
|
+
return pathname.slice(0, MAX_PATH_LENGTH);
|
|
1074
1204
|
}
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1205
|
+
|
|
1206
|
+
// ../sdk/src/core/page-tracker.ts
|
|
1207
|
+
var PLATFORM_PAGE_TRACKER_FLAG = "__shoppexPageTracker";
|
|
1208
|
+
function isPlatformPageTrackerActive() {
|
|
1209
|
+
if (typeof window === "undefined") return false;
|
|
1210
|
+
try {
|
|
1211
|
+
return Boolean(window[PLATFORM_PAGE_TRACKER_FLAG]);
|
|
1212
|
+
} catch {
|
|
1213
|
+
return false;
|
|
1078
1214
|
}
|
|
1079
|
-
return Math.floor(value);
|
|
1080
1215
|
}
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1216
|
+
|
|
1217
|
+
// ../sdk/src/modules/analytics.ts
|
|
1218
|
+
var CONFIRMED_CART_STORAGE_PREFIX = "reported_cart_";
|
|
1219
|
+
var IN_FLIGHT_CART_STORAGE_PREFIX = "reporting_cart_";
|
|
1220
|
+
var CART_SEQUENCE_STORAGE_PREFIX = "presence_cart_seq_";
|
|
1221
|
+
var CART_CLAIM_TTL_MS = 1e4;
|
|
1222
|
+
function confirmedCartKey(storeSlug) {
|
|
1223
|
+
return `${CONFIRMED_CART_STORAGE_PREFIX}${storeSlug}`;
|
|
1084
1224
|
}
|
|
1085
|
-
function
|
|
1086
|
-
|
|
1087
|
-
|
|
1225
|
+
function inFlightCartKey(storeSlug) {
|
|
1226
|
+
return `${IN_FLIGHT_CART_STORAGE_PREFIX}${storeSlug}`;
|
|
1227
|
+
}
|
|
1228
|
+
function cartSequenceKey(storeSlug) {
|
|
1229
|
+
return `${CART_SEQUENCE_STORAGE_PREFIX}${storeSlug}`;
|
|
1230
|
+
}
|
|
1231
|
+
function cartSnapshot(cartValue, itemCount) {
|
|
1232
|
+
return `${cartValue ?? ""}|${itemCount ?? ""}`;
|
|
1233
|
+
}
|
|
1234
|
+
function isCartSequence(value) {
|
|
1235
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
1236
|
+
}
|
|
1237
|
+
var CART_SEQUENCE_SCOPE_MAX_LENGTH = 64;
|
|
1238
|
+
function readCartCounter(storeSlug) {
|
|
1239
|
+
const counter = getItem(cartSequenceKey(storeSlug));
|
|
1240
|
+
if (!counter || typeof counter.scope !== "string" || counter.scope.length === 0 || counter.scope.length > CART_SEQUENCE_SCOPE_MAX_LENGTH || !isCartSequence(counter.seq)) {
|
|
1088
1241
|
return null;
|
|
1089
1242
|
}
|
|
1090
|
-
|
|
1091
|
-
const code = typeof record2.code === "string" ? normalizeCouponCode(record2.code) : null;
|
|
1092
|
-
const source = record2.source === "COUPON" || record2.source === "AFFILIATE" ? record2.source : null;
|
|
1093
|
-
return code && source ? { code, source } : null;
|
|
1243
|
+
return counter;
|
|
1094
1244
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
const
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
if (!productId || !variantId || !Number.isFinite(quantity) || quantity < 1) {
|
|
1105
|
-
continue;
|
|
1106
|
-
}
|
|
1107
|
-
const item = {
|
|
1108
|
-
line_id: "",
|
|
1109
|
-
product_id: productId,
|
|
1110
|
-
variant_id: variantId,
|
|
1111
|
-
quantity: Math.floor(quantity)
|
|
1112
|
-
};
|
|
1113
|
-
if (typeof record2.price_variant_id === "string") {
|
|
1114
|
-
item.price_variant_id = record2.price_variant_id;
|
|
1115
|
-
}
|
|
1116
|
-
if (record2.price_data && typeof record2.price_data === "object") {
|
|
1117
|
-
const priceData = record2.price_data;
|
|
1118
|
-
if (typeof priceData.unit_price === "number" && Number.isFinite(priceData.unit_price)) {
|
|
1119
|
-
item.price_data = { unit_price: priceData.unit_price };
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
if (typeof record2.pay_what_you_want_price === "number" && Number.isFinite(record2.pay_what_you_want_price)) {
|
|
1123
|
-
item.pay_what_you_want_price = record2.pay_what_you_want_price;
|
|
1124
|
-
}
|
|
1125
|
-
if (Array.isArray(record2.addons)) {
|
|
1126
|
-
item.addons = record2.addons;
|
|
1127
|
-
}
|
|
1128
|
-
if (record2.custom_fields && typeof record2.custom_fields === "object" && !Array.isArray(record2.custom_fields)) {
|
|
1129
|
-
item.custom_fields = record2.custom_fields;
|
|
1130
|
-
}
|
|
1131
|
-
if (typeof record2.title === "string") {
|
|
1132
|
-
item.title = record2.title;
|
|
1133
|
-
}
|
|
1134
|
-
if (typeof record2.variant_title === "string") {
|
|
1135
|
-
item.variant_title = record2.variant_title;
|
|
1136
|
-
}
|
|
1137
|
-
if (typeof record2.image_url === "string") {
|
|
1138
|
-
item.image_url = record2.image_url;
|
|
1139
|
-
}
|
|
1140
|
-
if (Array.isArray(record2.addon_labels)) {
|
|
1141
|
-
item.addon_labels = record2.addon_labels.filter((label) => typeof label === "string");
|
|
1142
|
-
}
|
|
1143
|
-
if (typeof record2.max_quantity === "number" && Number.isFinite(record2.max_quantity)) {
|
|
1144
|
-
item.max_quantity = record2.max_quantity;
|
|
1145
|
-
}
|
|
1146
|
-
if (typeof record2.min_quantity === "number" && Number.isFinite(record2.min_quantity)) {
|
|
1147
|
-
item.min_quantity = record2.min_quantity;
|
|
1148
|
-
}
|
|
1149
|
-
clampQuantityToBounds(item);
|
|
1150
|
-
const storedLineId = typeof record2.line_id === "string" ? record2.line_id.trim() : "";
|
|
1151
|
-
item.line_id = storedLineId || computeCartLineId(item);
|
|
1152
|
-
normalized.push(ensureCartLineId(item));
|
|
1153
|
-
}
|
|
1154
|
-
return normalized;
|
|
1245
|
+
var lastCartOrder = null;
|
|
1246
|
+
function nextCartOrder(storeSlug) {
|
|
1247
|
+
const stored = readCartCounter(storeSlug);
|
|
1248
|
+
const scope = stored?.scope ?? lastCartOrder?.scope ?? createClaimToken();
|
|
1249
|
+
const floor = Math.max(stored?.seq ?? 0, lastCartOrder?.scope === scope ? lastCartOrder.seq : 0);
|
|
1250
|
+
const order2 = { scope, seq: Math.max(Date.now(), floor + 1) };
|
|
1251
|
+
lastCartOrder = order2;
|
|
1252
|
+
setItem(cartSequenceKey(storeSlug), order2);
|
|
1253
|
+
return order2;
|
|
1155
1254
|
}
|
|
1156
|
-
function
|
|
1157
|
-
|
|
1255
|
+
function cartSequenceLocks() {
|
|
1256
|
+
if (typeof navigator === "undefined") return null;
|
|
1257
|
+
const locks = navigator.locks;
|
|
1258
|
+
return locks && typeof locks.request === "function" ? locks : null;
|
|
1158
1259
|
}
|
|
1159
|
-
function
|
|
1160
|
-
|
|
1260
|
+
function reserveCartOrder(storeSlug) {
|
|
1261
|
+
const take = () => nextCartOrder(storeSlug);
|
|
1262
|
+
const locks = cartSequenceLocks();
|
|
1263
|
+
if (!locks) return Promise.resolve(take());
|
|
1264
|
+
return locks.request(cartSequenceKey(storeSlug), take).catch(take);
|
|
1161
1265
|
}
|
|
1162
|
-
function
|
|
1163
|
-
|
|
1266
|
+
function readConfirmedCart(storeSlug) {
|
|
1267
|
+
const confirmed = getItem(confirmedCartKey(storeSlug));
|
|
1268
|
+
if (!confirmed || typeof confirmed.snapshot !== "string" || !isCartSequence(confirmed.seq)) return null;
|
|
1269
|
+
return confirmed;
|
|
1164
1270
|
}
|
|
1165
|
-
function
|
|
1166
|
-
const
|
|
1167
|
-
if (!
|
|
1168
|
-
removeCartStorage("coupon");
|
|
1271
|
+
function readInFlightCart(storeSlug) {
|
|
1272
|
+
const claim2 = getItem(inFlightCartKey(storeSlug));
|
|
1273
|
+
if (!claim2 || typeof claim2.snapshot !== "string" || !isCartSequence(claim2.seq) || typeof claim2.token !== "string" || typeof claim2.at !== "number") {
|
|
1169
1274
|
return null;
|
|
1170
1275
|
}
|
|
1171
|
-
|
|
1172
|
-
return normalizedCoupon;
|
|
1276
|
+
return claim2;
|
|
1173
1277
|
}
|
|
1174
|
-
function
|
|
1175
|
-
|
|
1278
|
+
function isCartReportedOrInFlight(storeSlug, snapshot) {
|
|
1279
|
+
const confirmed = readConfirmedCart(storeSlug);
|
|
1280
|
+
const claim2 = readInFlightCart(storeSlug);
|
|
1281
|
+
const liveClaim = claim2 && Date.now() - claim2.at < CART_CLAIM_TTL_MS ? claim2 : null;
|
|
1282
|
+
const expected = liveClaim && (!confirmed || liveClaim.seq > confirmed.seq) ? liveClaim : confirmed;
|
|
1283
|
+
return expected?.snapshot === snapshot;
|
|
1176
1284
|
}
|
|
1177
|
-
function
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
const
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1285
|
+
function createClaimToken() {
|
|
1286
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
1287
|
+
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;
|
|
1288
|
+
}
|
|
1289
|
+
function claimCartReport(storeSlug, seq, cartValue, itemCount) {
|
|
1290
|
+
const snapshot = cartSnapshot(cartValue, itemCount);
|
|
1291
|
+
const token = createClaimToken();
|
|
1292
|
+
setItem(inFlightCartKey(storeSlug), { snapshot, seq, token, at: Date.now() });
|
|
1293
|
+
return (delivered) => {
|
|
1294
|
+
if (delivered) {
|
|
1295
|
+
const confirmed = readConfirmedCart(storeSlug);
|
|
1296
|
+
if (!confirmed || seq > confirmed.seq) setItem(confirmedCartKey(storeSlug), { snapshot, seq });
|
|
1297
|
+
}
|
|
1298
|
+
if (readInFlightCart(storeSlug)?.token === token) removeItem(inFlightCartKey(storeSlug));
|
|
1187
1299
|
};
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1300
|
+
}
|
|
1301
|
+
var cartSendTail = Promise.resolve();
|
|
1302
|
+
var latestCartRequest = 0;
|
|
1303
|
+
function enqueueCartSend(send) {
|
|
1304
|
+
const request2 = ++latestCartRequest;
|
|
1305
|
+
const run = () => send(() => request2 === latestCartRequest);
|
|
1306
|
+
cartSendTail = cartSendTail.then(run, run);
|
|
1307
|
+
return cartSendTail;
|
|
1308
|
+
}
|
|
1309
|
+
async function postPing(body) {
|
|
1310
|
+
const config2 = getConfig();
|
|
1311
|
+
const controller = typeof AbortController === "undefined" ? void 0 : new AbortController();
|
|
1312
|
+
let timer;
|
|
1313
|
+
const timedOut = new Promise((resolve) => {
|
|
1314
|
+
timer = setTimeout(() => {
|
|
1315
|
+
controller?.abort();
|
|
1316
|
+
resolve(false);
|
|
1317
|
+
}, CART_CLAIM_TTL_MS);
|
|
1318
|
+
});
|
|
1319
|
+
try {
|
|
1320
|
+
const endpoint = buildEndpoint("/v1/storefront/shops/:storeSlug/ping", {
|
|
1321
|
+
storeSlug: config2.storeSlug
|
|
1322
|
+
});
|
|
1323
|
+
const delivered = fetch(`${config2.apiBaseUrl}${endpoint}`, {
|
|
1324
|
+
method: "POST",
|
|
1325
|
+
headers: { "Content-Type": "application/json" },
|
|
1326
|
+
body: JSON.stringify(body),
|
|
1327
|
+
signal: controller?.signal
|
|
1328
|
+
}).then((response) => response.ok);
|
|
1329
|
+
return await Promise.race([delivered, timedOut]);
|
|
1330
|
+
} catch {
|
|
1331
|
+
return false;
|
|
1332
|
+
} finally {
|
|
1333
|
+
clearTimeout(timer);
|
|
1191
1334
|
}
|
|
1192
1335
|
}
|
|
1193
|
-
function
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
const
|
|
1197
|
-
const
|
|
1198
|
-
const
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1336
|
+
async function trackPageView(cartValue, itemCount) {
|
|
1337
|
+
if (!isInitialized()) return;
|
|
1338
|
+
if (typeof document === "undefined") return;
|
|
1339
|
+
const config2 = getConfig();
|
|
1340
|
+
const carriesCart = cartValue !== void 0 || itemCount !== void 0;
|
|
1341
|
+
const reservedOrder = carriesCart ? reserveCartOrder(config2.storeSlug) : void 0;
|
|
1342
|
+
if (isPlatformPageTrackerActive()) {
|
|
1343
|
+
if (reservedOrder) await enqueueCartSend(async () => sendCartActivity(await reservedOrder, cartValue, itemCount));
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
const sendView = async () => {
|
|
1347
|
+
const order2 = reservedOrder ? await reservedOrder : void 0;
|
|
1348
|
+
const settle2 = order2 ? claimCartReport(config2.storeSlug, order2.seq, cartValue, itemCount) : void 0;
|
|
1349
|
+
const sessionId = analyticsSessionId(config2.storeSlug, { touch: true });
|
|
1350
|
+
const previousSession = currentPageSession();
|
|
1351
|
+
if (previousSession && previousSession !== sessionId) reportPageLeave();
|
|
1352
|
+
markPageViewed(sessionId);
|
|
1353
|
+
if (sessionId) installEngagedClock(reportPageLeave);
|
|
1354
|
+
onAnalyticsConsent(startSessionOnConsent);
|
|
1355
|
+
const delivered = await postPing({
|
|
1356
|
+
referer: document.referrer || void 0,
|
|
1357
|
+
cart_value: cartValue,
|
|
1358
|
+
item_count: itemCount,
|
|
1359
|
+
cart_seq: order2?.seq,
|
|
1360
|
+
cart_seq_scope: order2?.scope,
|
|
1361
|
+
session_id: sessionId ?? void 0,
|
|
1362
|
+
// Time on screen of the page before this one (client-side navigation).
|
|
1363
|
+
engaged_ms: sessionId ? takeEngagedMs() || void 0 : void 0,
|
|
1364
|
+
path: resolveStorefrontPagePath(),
|
|
1365
|
+
utm: resolveUtmParameters(config2.storeSlug)
|
|
1366
|
+
});
|
|
1367
|
+
settle2?.(delivered);
|
|
1203
1368
|
};
|
|
1204
|
-
|
|
1369
|
+
await (reservedOrder ? enqueueCartSend(sendView) : sendView());
|
|
1205
1370
|
}
|
|
1206
|
-
function
|
|
1207
|
-
|
|
1208
|
-
|
|
1371
|
+
async function trackCartActivity(cartValue, itemCount) {
|
|
1372
|
+
if (!isInitialized()) return;
|
|
1373
|
+
if (typeof document === "undefined") return;
|
|
1374
|
+
const snapshot = cartSnapshot(cartValue, itemCount);
|
|
1375
|
+
const reservedOrder = reserveCartOrder(getConfig().storeSlug);
|
|
1376
|
+
await enqueueCartSend(async (isLatest) => {
|
|
1377
|
+
const order2 = await reservedOrder;
|
|
1378
|
+
if (!isLatest() || isCartReportedOrInFlight(getConfig().storeSlug, snapshot)) return;
|
|
1379
|
+
await sendCartActivity(order2, cartValue, itemCount);
|
|
1380
|
+
});
|
|
1209
1381
|
}
|
|
1210
|
-
function
|
|
1211
|
-
const
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1382
|
+
async function sendCartActivity(order2, cartValue, itemCount) {
|
|
1383
|
+
const config2 = getConfig();
|
|
1384
|
+
const settle2 = claimCartReport(config2.storeSlug, order2.seq, cartValue, itemCount);
|
|
1385
|
+
const delivered = await postPing({
|
|
1386
|
+
event_type: "presence",
|
|
1387
|
+
cart_value: cartValue,
|
|
1388
|
+
item_count: itemCount,
|
|
1389
|
+
cart_seq: order2.seq,
|
|
1390
|
+
cart_seq_scope: order2.scope,
|
|
1391
|
+
// A cart marks the analytics session as one that did not bounce.
|
|
1392
|
+
session_id: analyticsSessionId(config2.storeSlug, { touch: false }) ?? void 0,
|
|
1393
|
+
path: resolveStorefrontPagePath()
|
|
1394
|
+
});
|
|
1395
|
+
settle2(delivered);
|
|
1218
1396
|
}
|
|
1219
|
-
function
|
|
1220
|
-
|
|
1221
|
-
|
|
1397
|
+
async function trackProductView(productId) {
|
|
1398
|
+
if (!isInitialized() || typeof document === "undefined" || !productId) return;
|
|
1399
|
+
await sendVisitEvent({ event_type: "product_view", product_id: productId });
|
|
1222
1400
|
}
|
|
1223
|
-
function
|
|
1224
|
-
if (!
|
|
1225
|
-
|
|
1401
|
+
async function trackCartAdd(productId, variantId, quantity) {
|
|
1402
|
+
if (!isInitialized() || typeof document === "undefined" || quantity < 1) return;
|
|
1403
|
+
await sendVisitEvent({ event_type: "cart_add", product_id: productId, variant_id: variantId, quantity });
|
|
1404
|
+
}
|
|
1405
|
+
async function sendVisitEvent(body) {
|
|
1406
|
+
const config2 = getConfig();
|
|
1407
|
+
if (config2.preview) return;
|
|
1408
|
+
await postPing({
|
|
1409
|
+
...body,
|
|
1410
|
+
referer: document.referrer || void 0,
|
|
1411
|
+
session_id: analyticsSessionId(config2.storeSlug, { touch: true }) ?? void 0,
|
|
1412
|
+
path: resolveStorefrontPagePath(),
|
|
1413
|
+
utm: resolveUtmParameters(config2.storeSlug)
|
|
1414
|
+
});
|
|
1415
|
+
}
|
|
1416
|
+
function startSessionOnConsent() {
|
|
1417
|
+
const config2 = getConfig();
|
|
1418
|
+
if (!isPageViewed() || analyticsSessionId(config2.storeSlug, { touch: false })) return;
|
|
1419
|
+
const sessionId = analyticsSessionId(config2.storeSlug, { touch: true });
|
|
1420
|
+
if (!sessionId) return;
|
|
1421
|
+
markPageViewed(sessionId);
|
|
1422
|
+
installEngagedClock(reportPageLeave);
|
|
1423
|
+
void postPing({
|
|
1424
|
+
event_type: "session_start",
|
|
1425
|
+
referer: document.referrer || void 0,
|
|
1426
|
+
session_id: sessionId,
|
|
1427
|
+
path: resolveStorefrontPagePath(),
|
|
1428
|
+
utm: resolveUtmParameters(config2.storeSlug)
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
1431
|
+
function reportPageLeave() {
|
|
1432
|
+
const config2 = getConfig();
|
|
1433
|
+
const sessionId = currentPageSession();
|
|
1434
|
+
const engagedMs2 = takeEngagedMs();
|
|
1435
|
+
if (!sessionId || engagedMs2 === 0 || readAnalyticsConsent() !== "granted") return;
|
|
1436
|
+
try {
|
|
1437
|
+
const endpoint = buildEndpoint("/v1/storefront/shops/:storeSlug/ping", { storeSlug: config2.storeSlug });
|
|
1438
|
+
void fetch(`${config2.apiBaseUrl}${endpoint}`, {
|
|
1439
|
+
method: "POST",
|
|
1440
|
+
headers: { "Content-Type": "application/json" },
|
|
1441
|
+
body: JSON.stringify({ event_type: "page_leave", session_id: sessionId, engaged_ms: engagedMs2 }),
|
|
1442
|
+
keepalive: true
|
|
1443
|
+
}).catch(() => {
|
|
1444
|
+
});
|
|
1445
|
+
} catch {
|
|
1226
1446
|
}
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
// ../sdk/src/utils/cart-line-id.ts
|
|
1450
|
+
function hashString(value) {
|
|
1451
|
+
let hash2 = 2166136261;
|
|
1452
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
1453
|
+
hash2 ^= value.charCodeAt(i);
|
|
1454
|
+
hash2 += (hash2 << 1) + (hash2 << 4) + (hash2 << 7) + (hash2 << 8) + (hash2 << 24);
|
|
1230
1455
|
}
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1456
|
+
return (hash2 >>> 0).toString(16);
|
|
1457
|
+
}
|
|
1458
|
+
function normalizeAddons(addons) {
|
|
1459
|
+
if (!addons?.length) return [];
|
|
1460
|
+
return [...addons].map((addon) => ({ id: addon.id, quantity: addon.quantity ?? 1 })).sort((a, b) => a.id.localeCompare(b.id));
|
|
1461
|
+
}
|
|
1462
|
+
function normalizeCustomFields(fields) {
|
|
1463
|
+
if (!fields) return {};
|
|
1464
|
+
const entries = Object.entries(fields).filter(([, value]) => typeof value === "string").sort(([a], [b]) => a.localeCompare(b));
|
|
1465
|
+
return Object.fromEntries(entries);
|
|
1466
|
+
}
|
|
1467
|
+
function computeCartLineId(input2) {
|
|
1468
|
+
const payload = {
|
|
1469
|
+
product_id: input2.product_id,
|
|
1470
|
+
variant_id: input2.variant_id,
|
|
1471
|
+
price_variant_id: input2.price_variant_id ?? null,
|
|
1472
|
+
addons: normalizeAddons(input2.addons),
|
|
1473
|
+
custom_fields: normalizeCustomFields(input2.custom_fields),
|
|
1474
|
+
unit_price: typeof input2.price_data?.unit_price === "number" && Number.isFinite(input2.price_data.unit_price) ? input2.price_data.unit_price : null,
|
|
1475
|
+
pay_what_you_want_price: typeof input2.pay_what_you_want_price === "number" && Number.isFinite(input2.pay_what_you_want_price) ? input2.pay_what_you_want_price : null
|
|
1476
|
+
};
|
|
1477
|
+
return hashString(JSON.stringify(payload));
|
|
1478
|
+
}
|
|
1479
|
+
function ensureCartLineId(item) {
|
|
1480
|
+
if (typeof item.line_id === "string" && item.line_id.trim()) {
|
|
1481
|
+
return item;
|
|
1482
|
+
}
|
|
1483
|
+
return { ...item, line_id: computeCartLineId(item) };
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
// ../contracts/src/catalog-unit-price.ts
|
|
1487
|
+
var CATALOG_UNIT_PRICE_DECIMAL_PLACES = 3;
|
|
1488
|
+
var PAYABLE_AMOUNT_DECIMAL_PLACES = 2;
|
|
1489
|
+
var CATALOG_UNIT_PRICE_FORMAT_OPTIONS = {
|
|
1490
|
+
maximumFractionDigits: CATALOG_UNIT_PRICE_DECIMAL_PLACES
|
|
1491
|
+
};
|
|
1492
|
+
function roundPayableAmount(value) {
|
|
1493
|
+
if (!Number.isFinite(value)) return value;
|
|
1494
|
+
const sign = value < 0 ? -1 : 1;
|
|
1495
|
+
const shifted = shiftDecimal(Math.abs(value), PAYABLE_AMOUNT_DECIMAL_PLACES);
|
|
1496
|
+
const rounded = Math.round(shifted);
|
|
1497
|
+
if (rounded === 0) return 0;
|
|
1498
|
+
return sign * shiftDecimal(rounded, -PAYABLE_AMOUNT_DECIMAL_PLACES);
|
|
1499
|
+
}
|
|
1500
|
+
function shiftDecimal(value, places) {
|
|
1501
|
+
const [coefficient, currentExponent = "0"] = String(value).split("e");
|
|
1502
|
+
return Number(`${coefficient}e${Number(currentExponent) + places}`);
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
// ../sdk/src/modules/cart.ts
|
|
1506
|
+
var STORAGE_KEYS = {
|
|
1507
|
+
cart: "cart",
|
|
1508
|
+
cartBackup: "cart_backup",
|
|
1509
|
+
meta: "cart_meta",
|
|
1510
|
+
metaBackup: "cart_backup_meta",
|
|
1511
|
+
coupon: "cart_coupon",
|
|
1512
|
+
couponBackup: "cart_coupon_backup"
|
|
1513
|
+
};
|
|
1514
|
+
function getStorageKey(type) {
|
|
1515
|
+
return `${STORAGE_KEYS[type]}_${getConfig().storeSlug}`;
|
|
1516
|
+
}
|
|
1517
|
+
function cartStorageArea() {
|
|
1518
|
+
return getConfig().preview ? "sessionStorage" : "localStorage";
|
|
1519
|
+
}
|
|
1520
|
+
function readCartStorage(type) {
|
|
1521
|
+
return getItem(getStorageKey(type), cartStorageArea());
|
|
1522
|
+
}
|
|
1523
|
+
function writeCartStorage(type, value) {
|
|
1524
|
+
setItem(getStorageKey(type), value, cartStorageArea());
|
|
1525
|
+
}
|
|
1526
|
+
function removeCartStorage(type) {
|
|
1527
|
+
removeItem(getStorageKey(type), cartStorageArea());
|
|
1528
|
+
}
|
|
1529
|
+
function hashString2(value) {
|
|
1530
|
+
let hash2 = 2166136261;
|
|
1531
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
1532
|
+
hash2 ^= value.charCodeAt(i);
|
|
1533
|
+
hash2 += (hash2 << 1) + (hash2 << 4) + (hash2 << 7) + (hash2 << 8) + (hash2 << 24);
|
|
1534
|
+
}
|
|
1535
|
+
return (hash2 >>> 0).toString(16);
|
|
1536
|
+
}
|
|
1537
|
+
function computeChecksum(cart) {
|
|
1538
|
+
return hashString2(JSON.stringify(cart));
|
|
1539
|
+
}
|
|
1540
|
+
function normalizeQuantity(value) {
|
|
1541
|
+
if (!Number.isFinite(value)) {
|
|
1542
|
+
throw new CartError("quantity must be a finite number");
|
|
1543
|
+
}
|
|
1544
|
+
return Math.floor(value);
|
|
1545
|
+
}
|
|
1546
|
+
function normalizeCouponCode(value) {
|
|
1547
|
+
const normalized = value?.trim().toUpperCase();
|
|
1548
|
+
return normalized ? normalized : null;
|
|
1549
|
+
}
|
|
1550
|
+
function getStoredCartCode() {
|
|
1551
|
+
const raw = readCartStorage("coupon");
|
|
1552
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
1553
|
+
return null;
|
|
1554
|
+
}
|
|
1555
|
+
const record2 = raw;
|
|
1556
|
+
const code = typeof record2.code === "string" ? normalizeCouponCode(record2.code) : null;
|
|
1557
|
+
const source = record2.source === "COUPON" || record2.source === "AFFILIATE" ? record2.source : null;
|
|
1558
|
+
return code && source ? { code, source } : null;
|
|
1559
|
+
}
|
|
1560
|
+
function normalizeCartItems(value) {
|
|
1561
|
+
if (!Array.isArray(value)) return [];
|
|
1562
|
+
const normalized = [];
|
|
1563
|
+
for (const entry of value) {
|
|
1564
|
+
if (!entry || typeof entry !== "object") continue;
|
|
1565
|
+
const record2 = entry;
|
|
1566
|
+
const productId = typeof record2.product_id === "string" ? record2.product_id.trim() : "";
|
|
1567
|
+
const variantId = typeof record2.variant_id === "string" ? record2.variant_id.trim() : "";
|
|
1568
|
+
const quantity = Number(record2.quantity);
|
|
1569
|
+
if (!productId || !variantId || !Number.isFinite(quantity) || quantity < 1) {
|
|
1570
|
+
continue;
|
|
1571
|
+
}
|
|
1572
|
+
const item = {
|
|
1573
|
+
line_id: "",
|
|
1574
|
+
product_id: productId,
|
|
1575
|
+
variant_id: variantId,
|
|
1576
|
+
quantity: Math.floor(quantity)
|
|
1577
|
+
};
|
|
1578
|
+
if (typeof record2.price_variant_id === "string") {
|
|
1579
|
+
item.price_variant_id = record2.price_variant_id;
|
|
1580
|
+
}
|
|
1581
|
+
if (record2.price_data && typeof record2.price_data === "object") {
|
|
1582
|
+
const priceData = record2.price_data;
|
|
1583
|
+
if (typeof priceData.unit_price === "number" && Number.isFinite(priceData.unit_price)) {
|
|
1584
|
+
item.price_data = { unit_price: priceData.unit_price };
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
if (typeof record2.pay_what_you_want_price === "number" && Number.isFinite(record2.pay_what_you_want_price)) {
|
|
1588
|
+
item.pay_what_you_want_price = record2.pay_what_you_want_price;
|
|
1589
|
+
}
|
|
1590
|
+
if (Array.isArray(record2.addons)) {
|
|
1591
|
+
item.addons = record2.addons;
|
|
1592
|
+
}
|
|
1593
|
+
if (record2.custom_fields && typeof record2.custom_fields === "object" && !Array.isArray(record2.custom_fields)) {
|
|
1594
|
+
item.custom_fields = record2.custom_fields;
|
|
1595
|
+
}
|
|
1596
|
+
if (typeof record2.title === "string") {
|
|
1597
|
+
item.title = record2.title;
|
|
1598
|
+
}
|
|
1599
|
+
if (typeof record2.variant_title === "string") {
|
|
1600
|
+
item.variant_title = record2.variant_title;
|
|
1601
|
+
}
|
|
1602
|
+
if (typeof record2.image_url === "string") {
|
|
1603
|
+
item.image_url = record2.image_url;
|
|
1604
|
+
}
|
|
1605
|
+
if (Array.isArray(record2.addon_labels)) {
|
|
1606
|
+
item.addon_labels = record2.addon_labels.filter((label) => typeof label === "string");
|
|
1607
|
+
}
|
|
1608
|
+
if (typeof record2.max_quantity === "number" && Number.isFinite(record2.max_quantity)) {
|
|
1609
|
+
item.max_quantity = record2.max_quantity;
|
|
1610
|
+
}
|
|
1611
|
+
if (typeof record2.min_quantity === "number" && Number.isFinite(record2.min_quantity)) {
|
|
1612
|
+
item.min_quantity = record2.min_quantity;
|
|
1613
|
+
}
|
|
1614
|
+
clampQuantityToBounds(item);
|
|
1615
|
+
const storedLineId = typeof record2.line_id === "string" ? record2.line_id.trim() : "";
|
|
1616
|
+
item.line_id = storedLineId || computeCartLineId(item);
|
|
1617
|
+
normalized.push(ensureCartLineId(item));
|
|
1618
|
+
}
|
|
1619
|
+
return normalized;
|
|
1620
|
+
}
|
|
1621
|
+
function getCartMetadata() {
|
|
1622
|
+
return readCartStorage("meta");
|
|
1623
|
+
}
|
|
1624
|
+
function getCartCoupon() {
|
|
1625
|
+
return getStoredCartCode()?.code ?? null;
|
|
1626
|
+
}
|
|
1627
|
+
function getCartCouponSource() {
|
|
1628
|
+
return getStoredCartCode()?.source ?? null;
|
|
1629
|
+
}
|
|
1630
|
+
function setCartCoupon(coupon, source = "COUPON") {
|
|
1631
|
+
const normalizedCoupon = normalizeCouponCode(coupon);
|
|
1632
|
+
if (!normalizedCoupon) {
|
|
1633
|
+
removeCartStorage("coupon");
|
|
1634
|
+
return null;
|
|
1635
|
+
}
|
|
1636
|
+
writeCartStorage("coupon", { code: normalizedCoupon, source });
|
|
1637
|
+
return normalizedCoupon;
|
|
1638
|
+
}
|
|
1639
|
+
function clearCartCoupon() {
|
|
1640
|
+
removeCartStorage("coupon");
|
|
1641
|
+
}
|
|
1642
|
+
function writeCart(cart) {
|
|
1643
|
+
const normalizedCart = normalizeCartItems(cart);
|
|
1644
|
+
writeCartStorage("cart", normalizedCart);
|
|
1645
|
+
const now = Date.now();
|
|
1646
|
+
const previous = getCartMetadata();
|
|
1647
|
+
const nextMeta = {
|
|
1648
|
+
created_at: previous?.created_at ?? now,
|
|
1649
|
+
last_modified: now,
|
|
1650
|
+
version: (previous?.version ?? 0) + 1,
|
|
1651
|
+
checksum: computeChecksum(normalizedCart)
|
|
1652
|
+
};
|
|
1653
|
+
writeCartStorage("meta", nextMeta);
|
|
1654
|
+
if (normalizedCart.length === 0) {
|
|
1655
|
+
clearCartCoupon();
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
function setCartWithMetadata(cart, metadata) {
|
|
1659
|
+
const normalizedCart = normalizeCartItems(cart);
|
|
1660
|
+
writeCartStorage("cart", normalizedCart);
|
|
1661
|
+
const now = Date.now();
|
|
1662
|
+
const base = metadata ?? getCartMetadata();
|
|
1663
|
+
const nextMeta = {
|
|
1664
|
+
created_at: base?.created_at ?? now,
|
|
1665
|
+
last_modified: now,
|
|
1666
|
+
version: base?.version ?? 1,
|
|
1667
|
+
checksum: computeChecksum(normalizedCart)
|
|
1668
|
+
};
|
|
1669
|
+
writeCartStorage("meta", nextMeta);
|
|
1670
|
+
}
|
|
1671
|
+
function getCart() {
|
|
1672
|
+
const raw = readCartStorage("cart");
|
|
1673
|
+
return normalizeCartItems(raw);
|
|
1674
|
+
}
|
|
1675
|
+
function resolveCartLineId(productId, variantId, cart = getCart()) {
|
|
1676
|
+
const matches = cart.filter(
|
|
1677
|
+
(item) => item.product_id === productId && item.variant_id === variantId
|
|
1678
|
+
);
|
|
1679
|
+
if (matches.length === 0) {
|
|
1680
|
+
throw new CartError(`No cart line found for ${productId}/${variantId}`);
|
|
1681
|
+
}
|
|
1682
|
+
return matches[0].line_id;
|
|
1683
|
+
}
|
|
1684
|
+
function getCartItemCount() {
|
|
1685
|
+
const cart = getCart();
|
|
1686
|
+
return cart.reduce((sum, item) => sum + item.quantity, 0);
|
|
1687
|
+
}
|
|
1688
|
+
function addToCart(productId, variantId, quantity = 1, options) {
|
|
1689
|
+
if (!productId || !variantId) {
|
|
1690
|
+
throw new CartError("product_id and variant_id are required");
|
|
1691
|
+
}
|
|
1692
|
+
const normalizedQuantity = normalizeQuantity(quantity);
|
|
1693
|
+
if (normalizedQuantity < 1) {
|
|
1694
|
+
throw new CartError("quantity must be at least 1");
|
|
1695
|
+
}
|
|
1696
|
+
const cart = getCart();
|
|
1697
|
+
const lineId = computeCartLineId({
|
|
1698
|
+
product_id: productId,
|
|
1699
|
+
variant_id: variantId,
|
|
1700
|
+
addons: options?.addons,
|
|
1701
|
+
custom_fields: options?.custom_fields,
|
|
1702
|
+
price_variant_id: options?.price_variant_id,
|
|
1703
|
+
price_data: options?.price_data,
|
|
1704
|
+
pay_what_you_want_price: options?.pay_what_you_want_price
|
|
1705
|
+
});
|
|
1706
|
+
const existingIndex = cart.findIndex((item) => item.line_id === lineId);
|
|
1707
|
+
const quantityBeforeAdd = existingIndex >= 0 ? cart[existingIndex].quantity : 0;
|
|
1708
|
+
if (existingIndex >= 0) {
|
|
1244
1709
|
cart[existingIndex].quantity += normalizedQuantity;
|
|
1245
1710
|
if (options?.addons) {
|
|
1246
1711
|
cart[existingIndex].addons = options.addons;
|
|
@@ -1286,6 +1751,7 @@ function addToCart(productId, variantId, quantity = 1, options) {
|
|
|
1286
1751
|
if (intendedQuantity > quantityBeforeAdd && persistedLine?.quantity === intendedQuantity) {
|
|
1287
1752
|
const cartAffiliateCode = getCartCouponSource() === "AFFILIATE" ? getCartCoupon() : null;
|
|
1288
1753
|
void trackAffiliateEvent("add_to_cart", cartAffiliateCode ? { code: cartAffiliateCode } : void 0);
|
|
1754
|
+
void trackCartAdd(productId, variantId, intendedQuantity - quantityBeforeAdd);
|
|
1289
1755
|
}
|
|
1290
1756
|
}
|
|
1291
1757
|
function clampQuantityToBounds(item) {
|
|
@@ -1373,6 +1839,8 @@ function setCartItem(productId, variantId, quantity = 1, options) {
|
|
|
1373
1839
|
clampQuantityToBounds(pushed);
|
|
1374
1840
|
cart.push(pushed);
|
|
1375
1841
|
writeCart(cart);
|
|
1842
|
+
const persistedLine = getCart().find((item) => item.line_id === lineId);
|
|
1843
|
+
if (persistedLine) void trackCartAdd(productId, variantId, persistedLine.quantity);
|
|
1376
1844
|
}
|
|
1377
1845
|
function updateCartItem(lineId, updates) {
|
|
1378
1846
|
const cart = getCart();
|
|
@@ -1466,436 +1934,172 @@ function createCartBackup() {
|
|
|
1466
1934
|
writeCartStorage("metaBackup", {
|
|
1467
1935
|
created_at: now,
|
|
1468
1936
|
last_modified: now,
|
|
1469
|
-
version: 1,
|
|
1470
|
-
checksum: computeChecksum(cart)
|
|
1471
|
-
});
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
function restoreCartFromBackup() {
|
|
1475
|
-
const backupRaw = readCartStorage("cartBackup");
|
|
1476
|
-
const backup = normalizeCartItems(backupRaw);
|
|
1477
|
-
const backupMeta = readCartStorage("metaBackup");
|
|
1478
|
-
const backupCoupon = readCartStorage("couponBackup");
|
|
1479
|
-
if (backup && backup.length > 0) {
|
|
1480
|
-
setCartWithMetadata(backup, backupMeta);
|
|
1481
|
-
if (backupCoupon && typeof backupCoupon === "object" && !Array.isArray(backupCoupon)) {
|
|
1482
|
-
const record2 = backupCoupon;
|
|
1483
|
-
const source = record2.source === "COUPON" || record2.source === "AFFILIATE" ? record2.source : null;
|
|
1484
|
-
if (typeof record2.code === "string" && source) {
|
|
1485
|
-
setCartCoupon(record2.code, source);
|
|
1486
|
-
} else {
|
|
1487
|
-
clearCartCoupon();
|
|
1488
|
-
}
|
|
1489
|
-
} else {
|
|
1490
|
-
clearCartCoupon();
|
|
1491
|
-
}
|
|
1492
|
-
return true;
|
|
1493
|
-
}
|
|
1494
|
-
return false;
|
|
1495
|
-
}
|
|
1496
|
-
function mergeBaskets(items) {
|
|
1497
|
-
const cart = getCart();
|
|
1498
|
-
for (const incoming of items) {
|
|
1499
|
-
const productId = typeof incoming.product_id === "string" ? incoming.product_id.trim() : "";
|
|
1500
|
-
const variantId = typeof incoming.variant_id === "string" ? incoming.variant_id.trim() : "";
|
|
1501
|
-
if (!productId || !variantId) {
|
|
1502
|
-
continue;
|
|
1503
|
-
}
|
|
1504
|
-
let normalizedQuantity;
|
|
1505
|
-
try {
|
|
1506
|
-
normalizedQuantity = normalizeQuantity(incoming.quantity);
|
|
1507
|
-
} catch {
|
|
1508
|
-
continue;
|
|
1509
|
-
}
|
|
1510
|
-
if (normalizedQuantity < 1) {
|
|
1511
|
-
continue;
|
|
1512
|
-
}
|
|
1513
|
-
const mergedLine = ensureCartLineId({
|
|
1514
|
-
...incoming,
|
|
1515
|
-
line_id: incoming.line_id ?? computeCartLineId(incoming),
|
|
1516
|
-
product_id: productId,
|
|
1517
|
-
variant_id: variantId,
|
|
1518
|
-
quantity: normalizedQuantity
|
|
1519
|
-
});
|
|
1520
|
-
const index = cart.findIndex((item) => item.line_id === mergedLine.line_id);
|
|
1521
|
-
if (index >= 0) {
|
|
1522
|
-
cart[index].quantity = Math.max(cart[index].quantity, normalizedQuantity);
|
|
1523
|
-
clampQuantityToBounds(cart[index]);
|
|
1524
|
-
} else {
|
|
1525
|
-
clampQuantityToBounds(mergedLine);
|
|
1526
|
-
cart.push(mergedLine);
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
writeCart(cart);
|
|
1530
|
-
return cart;
|
|
1531
|
-
}
|
|
1532
|
-
function moveBasketItem(fromProductId, fromVariantId, toProductId, toVariantId) {
|
|
1533
|
-
const cart = getCart();
|
|
1534
|
-
const fromIndex = cart.findIndex(
|
|
1535
|
-
(item) => item.product_id === fromProductId && item.variant_id === fromVariantId
|
|
1536
|
-
);
|
|
1537
|
-
if (fromIndex < 0) {
|
|
1538
|
-
throw new CartError("Item not found in cart");
|
|
1539
|
-
}
|
|
1540
|
-
const [fromItem] = cart.splice(fromIndex, 1);
|
|
1541
|
-
const toLineId = computeCartLineId({
|
|
1542
|
-
...fromItem,
|
|
1543
|
-
product_id: toProductId,
|
|
1544
|
-
variant_id: toVariantId
|
|
1545
|
-
});
|
|
1546
|
-
const toIndex = cart.findIndex((item) => item.line_id === toLineId);
|
|
1547
|
-
if (toIndex >= 0) {
|
|
1548
|
-
cart[toIndex].quantity += fromItem.quantity;
|
|
1549
|
-
clampQuantityToBounds(cart[toIndex]);
|
|
1550
|
-
} else {
|
|
1551
|
-
const moved = ensureCartLineId({
|
|
1552
|
-
...fromItem,
|
|
1553
|
-
product_id: toProductId,
|
|
1554
|
-
variant_id: toVariantId,
|
|
1555
|
-
line_id: toLineId
|
|
1556
|
-
});
|
|
1557
|
-
clampQuantityToBounds(moved);
|
|
1558
|
-
cart.push(moved);
|
|
1559
|
-
}
|
|
1560
|
-
writeCart(cart);
|
|
1561
|
-
}
|
|
1562
|
-
function validateCartIntegrity() {
|
|
1563
|
-
const cart = getCart();
|
|
1564
|
-
const metadata = getCartMetadata();
|
|
1565
|
-
const checksum = computeChecksum(cart);
|
|
1566
|
-
if (!metadata) {
|
|
1567
|
-
setCartWithMetadata(cart, null);
|
|
1568
|
-
return true;
|
|
1569
|
-
}
|
|
1570
|
-
return metadata.checksum === checksum;
|
|
1571
|
-
}
|
|
1572
|
-
function getCartStats() {
|
|
1573
|
-
const cart = getCart();
|
|
1574
|
-
const integrityValid = validateCartIntegrity();
|
|
1575
|
-
const metadata = getCartMetadata();
|
|
1576
|
-
const backup = readCartStorage("cartBackup") ?? [];
|
|
1577
|
-
const hasCompletePriceSnapshots = cart.length > 0 && cart.every((item) => typeof item.price_data?.unit_price === "number");
|
|
1578
|
-
const totalPrice = cart.reduce((sum, item) => {
|
|
1579
|
-
const unitPrice = item.price_data?.unit_price ?? 0;
|
|
1580
|
-
return sum + roundPayableAmount(unitPrice * item.quantity);
|
|
1581
|
-
}, 0);
|
|
1582
|
-
return {
|
|
1583
|
-
item_count: cart.length,
|
|
1584
|
-
total_quantity: cart.reduce((sum, item) => sum + item.quantity, 0),
|
|
1585
|
-
last_modified: metadata?.last_modified ?? 0,
|
|
1586
|
-
version: metadata?.version ?? 0,
|
|
1587
|
-
has_backup: backup.length > 0,
|
|
1588
|
-
integrity_valid: integrityValid,
|
|
1589
|
-
total_price: roundPayableAmount(totalPrice),
|
|
1590
|
-
total_price_is_estimate: cart.length > 0 && !hasCompletePriceSnapshots
|
|
1591
|
-
};
|
|
1592
|
-
}
|
|
1593
|
-
function getCartPayload(coupon) {
|
|
1594
|
-
const config2 = getConfig();
|
|
1595
|
-
const normalizedCoupon = normalizeCouponCode(coupon) ?? getCartCoupon() ?? void 0;
|
|
1596
|
-
return {
|
|
1597
|
-
store_slug: config2.storeSlug,
|
|
1598
|
-
items: getCart(),
|
|
1599
|
-
coupon: normalizedCoupon
|
|
1600
|
-
};
|
|
1601
|
-
}
|
|
1602
|
-
function getAmbientAffiliateCodeForQuote() {
|
|
1603
|
-
const storedCode = getCartCoupon();
|
|
1604
|
-
const storedSource = getCartCouponSource();
|
|
1605
|
-
if (storedSource === "AFFILIATE" && storedCode) {
|
|
1606
|
-
return storedCode.trim().toLowerCase();
|
|
1607
|
-
}
|
|
1608
|
-
return getAffiliateCode();
|
|
1609
|
-
}
|
|
1610
|
-
var latestQuoteToken = null;
|
|
1611
|
-
var quoteSequence = 0;
|
|
1612
|
-
function getLatestQuoteToken() {
|
|
1613
|
-
return latestQuoteToken;
|
|
1614
|
-
}
|
|
1615
|
-
function clearLatestQuoteToken() {
|
|
1616
|
-
latestQuoteToken = null;
|
|
1617
|
-
quoteSequence += 1;
|
|
1618
|
-
}
|
|
1619
|
-
async function quoteCart(coupon, currency) {
|
|
1620
|
-
const sequence = quoteSequence += 1;
|
|
1621
|
-
const payload = getCartPayload(coupon);
|
|
1622
|
-
const normalizedCurrency = normalizeRequestedCurrency(currency) ?? resolveBuyerCurrency(getConfig().currency);
|
|
1623
|
-
const response = await post("/v1/storefront/cart/quote", {
|
|
1624
|
-
shop_slug: payload.store_slug,
|
|
1625
|
-
cart: payload.items,
|
|
1626
|
-
coupon: payload.coupon,
|
|
1627
|
-
affiliate_code: getAmbientAffiliateCodeForQuote() ?? void 0,
|
|
1628
|
-
currency: normalizedCurrency ?? void 0
|
|
1629
|
-
});
|
|
1630
|
-
if (sequence !== quoteSequence) return response;
|
|
1631
|
-
if (response.success) {
|
|
1632
|
-
latestQuoteToken = typeof response.data?.quote_token === "string" ? response.data.quote_token : null;
|
|
1633
|
-
}
|
|
1634
|
-
return response;
|
|
1635
|
-
}
|
|
1636
|
-
|
|
1637
|
-
// ../sdk/src/core/attribution.ts
|
|
1638
|
-
var STORAGE_PREFIX2 = "shoppex:utm:";
|
|
1639
|
-
var TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
1640
|
-
var UTM_KEYS = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"];
|
|
1641
|
-
var MAX_VALUE_LENGTH = 255;
|
|
1642
|
-
function readFromUrl(search) {
|
|
1643
|
-
let params;
|
|
1644
|
-
try {
|
|
1645
|
-
params = new URLSearchParams(search);
|
|
1646
|
-
} catch {
|
|
1647
|
-
return {};
|
|
1648
|
-
}
|
|
1649
|
-
const utm = {};
|
|
1650
|
-
for (const key of UTM_KEYS) {
|
|
1651
|
-
const value = params.get(key)?.trim();
|
|
1652
|
-
if (value) {
|
|
1653
|
-
utm[key] = value.slice(0, MAX_VALUE_LENGTH);
|
|
1654
|
-
}
|
|
1655
|
-
}
|
|
1656
|
-
return utm;
|
|
1657
|
-
}
|
|
1658
|
-
function storageKey(storeSlug) {
|
|
1659
|
-
return `${STORAGE_PREFIX2}${storeSlug}`;
|
|
1660
|
-
}
|
|
1661
|
-
function readStored(storeSlug) {
|
|
1662
|
-
try {
|
|
1663
|
-
const raw = window.localStorage.getItem(storefrontStorageKey(storageKey(storeSlug)));
|
|
1664
|
-
if (!raw) return {};
|
|
1665
|
-
const parsed = JSON.parse(raw);
|
|
1666
|
-
if (!parsed || typeof parsed.at !== "number" || typeof parsed.utm !== "object") return {};
|
|
1667
|
-
if (Date.now() - parsed.at > TTL_MS) {
|
|
1668
|
-
window.localStorage.removeItem(storefrontStorageKey(storageKey(storeSlug)));
|
|
1669
|
-
return {};
|
|
1670
|
-
}
|
|
1671
|
-
const utm = {};
|
|
1672
|
-
for (const key of UTM_KEYS) {
|
|
1673
|
-
const value = parsed.utm?.[key];
|
|
1674
|
-
if (typeof value === "string" && value.length > 0) {
|
|
1675
|
-
utm[key] = value.slice(0, MAX_VALUE_LENGTH);
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1678
|
-
return utm;
|
|
1679
|
-
} catch {
|
|
1680
|
-
return {};
|
|
1681
|
-
}
|
|
1682
|
-
}
|
|
1683
|
-
function writeStored(storeSlug, utm) {
|
|
1684
|
-
try {
|
|
1685
|
-
const payload = { utm, at: Date.now() };
|
|
1686
|
-
window.localStorage.setItem(storefrontStorageKey(storageKey(storeSlug)), JSON.stringify(payload));
|
|
1687
|
-
} catch {
|
|
1688
|
-
}
|
|
1689
|
-
}
|
|
1690
|
-
function resolveUtmParameters(storeSlug) {
|
|
1691
|
-
if (typeof window === "undefined" || typeof window.location === "undefined") {
|
|
1692
|
-
return void 0;
|
|
1693
|
-
}
|
|
1694
|
-
const fromUrl = readFromUrl(window.location.search);
|
|
1695
|
-
if (Object.keys(fromUrl).length > 0) {
|
|
1696
|
-
writeStored(storeSlug, fromUrl);
|
|
1697
|
-
return fromUrl;
|
|
1698
|
-
}
|
|
1699
|
-
const stored = readStored(storeSlug);
|
|
1700
|
-
return Object.keys(stored).length > 0 ? stored : void 0;
|
|
1701
|
-
}
|
|
1702
|
-
|
|
1703
|
-
// ../sdk/src/core/page-path.ts
|
|
1704
|
-
var MAX_PATH_LENGTH = 256;
|
|
1705
|
-
function resolveStorefrontPagePath() {
|
|
1706
|
-
if (typeof window === "undefined") return void 0;
|
|
1707
|
-
let pathname;
|
|
1708
|
-
try {
|
|
1709
|
-
const location = window.location;
|
|
1710
|
-
pathname = typeof location?.pathname === "string" && location.pathname ? location.pathname : new URL(String(location?.href ?? "")).pathname;
|
|
1711
|
-
} catch {
|
|
1712
|
-
return void 0;
|
|
1713
|
-
}
|
|
1714
|
-
if (!pathname || !pathname.startsWith("/")) return void 0;
|
|
1715
|
-
return pathname.slice(0, MAX_PATH_LENGTH);
|
|
1716
|
-
}
|
|
1717
|
-
|
|
1718
|
-
// ../sdk/src/core/page-tracker.ts
|
|
1719
|
-
var PLATFORM_PAGE_TRACKER_FLAG = "__shoppexPageTracker";
|
|
1720
|
-
function isPlatformPageTrackerActive() {
|
|
1721
|
-
if (typeof window === "undefined") return false;
|
|
1722
|
-
try {
|
|
1723
|
-
return Boolean(window[PLATFORM_PAGE_TRACKER_FLAG]);
|
|
1724
|
-
} catch {
|
|
1725
|
-
return false;
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
|
|
1729
|
-
// ../sdk/src/modules/analytics.ts
|
|
1730
|
-
var CONFIRMED_CART_STORAGE_PREFIX = "reported_cart_";
|
|
1731
|
-
var IN_FLIGHT_CART_STORAGE_PREFIX = "reporting_cart_";
|
|
1732
|
-
var CART_SEQUENCE_STORAGE_PREFIX = "presence_cart_seq_";
|
|
1733
|
-
var CART_CLAIM_TTL_MS = 1e4;
|
|
1734
|
-
function confirmedCartKey(storeSlug) {
|
|
1735
|
-
return `${CONFIRMED_CART_STORAGE_PREFIX}${storeSlug}`;
|
|
1736
|
-
}
|
|
1737
|
-
function inFlightCartKey(storeSlug) {
|
|
1738
|
-
return `${IN_FLIGHT_CART_STORAGE_PREFIX}${storeSlug}`;
|
|
1739
|
-
}
|
|
1740
|
-
function cartSequenceKey(storeSlug) {
|
|
1741
|
-
return `${CART_SEQUENCE_STORAGE_PREFIX}${storeSlug}`;
|
|
1742
|
-
}
|
|
1743
|
-
function cartSnapshot(cartValue, itemCount) {
|
|
1744
|
-
return `${cartValue ?? ""}|${itemCount ?? ""}`;
|
|
1745
|
-
}
|
|
1746
|
-
function isCartSequence(value) {
|
|
1747
|
-
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
1748
|
-
}
|
|
1749
|
-
var CART_SEQUENCE_SCOPE_MAX_LENGTH = 64;
|
|
1750
|
-
function readCartCounter(storeSlug) {
|
|
1751
|
-
const counter = getItem(cartSequenceKey(storeSlug));
|
|
1752
|
-
if (!counter || typeof counter.scope !== "string" || counter.scope.length === 0 || counter.scope.length > CART_SEQUENCE_SCOPE_MAX_LENGTH || !isCartSequence(counter.seq)) {
|
|
1753
|
-
return null;
|
|
1754
|
-
}
|
|
1755
|
-
return counter;
|
|
1756
|
-
}
|
|
1757
|
-
var lastCartOrder = null;
|
|
1758
|
-
function nextCartOrder(storeSlug) {
|
|
1759
|
-
const stored = readCartCounter(storeSlug);
|
|
1760
|
-
const scope = stored?.scope ?? lastCartOrder?.scope ?? createClaimToken();
|
|
1761
|
-
const floor = Math.max(stored?.seq ?? 0, lastCartOrder?.scope === scope ? lastCartOrder.seq : 0);
|
|
1762
|
-
const order2 = { scope, seq: Math.max(Date.now(), floor + 1) };
|
|
1763
|
-
lastCartOrder = order2;
|
|
1764
|
-
setItem(cartSequenceKey(storeSlug), order2);
|
|
1765
|
-
return order2;
|
|
1766
|
-
}
|
|
1767
|
-
function cartSequenceLocks() {
|
|
1768
|
-
if (typeof navigator === "undefined") return null;
|
|
1769
|
-
const locks = navigator.locks;
|
|
1770
|
-
return locks && typeof locks.request === "function" ? locks : null;
|
|
1771
|
-
}
|
|
1772
|
-
function reserveCartOrder(storeSlug) {
|
|
1773
|
-
const take = () => nextCartOrder(storeSlug);
|
|
1774
|
-
const locks = cartSequenceLocks();
|
|
1775
|
-
if (!locks) return Promise.resolve(take());
|
|
1776
|
-
return locks.request(cartSequenceKey(storeSlug), take).catch(take);
|
|
1777
|
-
}
|
|
1778
|
-
function readConfirmedCart(storeSlug) {
|
|
1779
|
-
const confirmed = getItem(confirmedCartKey(storeSlug));
|
|
1780
|
-
if (!confirmed || typeof confirmed.snapshot !== "string" || !isCartSequence(confirmed.seq)) return null;
|
|
1781
|
-
return confirmed;
|
|
1782
|
-
}
|
|
1783
|
-
function readInFlightCart(storeSlug) {
|
|
1784
|
-
const claim2 = getItem(inFlightCartKey(storeSlug));
|
|
1785
|
-
if (!claim2 || typeof claim2.snapshot !== "string" || !isCartSequence(claim2.seq) || typeof claim2.token !== "string" || typeof claim2.at !== "number") {
|
|
1786
|
-
return null;
|
|
1787
|
-
}
|
|
1788
|
-
return claim2;
|
|
1789
|
-
}
|
|
1790
|
-
function isCartReportedOrInFlight(storeSlug, snapshot) {
|
|
1791
|
-
const confirmed = readConfirmedCart(storeSlug);
|
|
1792
|
-
const claim2 = readInFlightCart(storeSlug);
|
|
1793
|
-
const liveClaim = claim2 && Date.now() - claim2.at < CART_CLAIM_TTL_MS ? claim2 : null;
|
|
1794
|
-
const expected = liveClaim && (!confirmed || liveClaim.seq > confirmed.seq) ? liveClaim : confirmed;
|
|
1795
|
-
return expected?.snapshot === snapshot;
|
|
1796
|
-
}
|
|
1797
|
-
function createClaimToken() {
|
|
1798
|
-
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
1799
|
-
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;
|
|
1937
|
+
version: 1,
|
|
1938
|
+
checksum: computeChecksum(cart)
|
|
1939
|
+
});
|
|
1940
|
+
}
|
|
1800
1941
|
}
|
|
1801
|
-
function
|
|
1802
|
-
const
|
|
1803
|
-
const
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1942
|
+
function restoreCartFromBackup() {
|
|
1943
|
+
const backupRaw = readCartStorage("cartBackup");
|
|
1944
|
+
const backup = normalizeCartItems(backupRaw);
|
|
1945
|
+
const backupMeta = readCartStorage("metaBackup");
|
|
1946
|
+
const backupCoupon = readCartStorage("couponBackup");
|
|
1947
|
+
if (backup && backup.length > 0) {
|
|
1948
|
+
setCartWithMetadata(backup, backupMeta);
|
|
1949
|
+
if (backupCoupon && typeof backupCoupon === "object" && !Array.isArray(backupCoupon)) {
|
|
1950
|
+
const record2 = backupCoupon;
|
|
1951
|
+
const source = record2.source === "COUPON" || record2.source === "AFFILIATE" ? record2.source : null;
|
|
1952
|
+
if (typeof record2.code === "string" && source) {
|
|
1953
|
+
setCartCoupon(record2.code, source);
|
|
1954
|
+
} else {
|
|
1955
|
+
clearCartCoupon();
|
|
1956
|
+
}
|
|
1957
|
+
} else {
|
|
1958
|
+
clearCartCoupon();
|
|
1809
1959
|
}
|
|
1810
|
-
|
|
1811
|
-
}
|
|
1960
|
+
return true;
|
|
1961
|
+
}
|
|
1962
|
+
return false;
|
|
1812
1963
|
}
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1964
|
+
function mergeBaskets(items) {
|
|
1965
|
+
const cart = getCart();
|
|
1966
|
+
for (const incoming of items) {
|
|
1967
|
+
const productId = typeof incoming.product_id === "string" ? incoming.product_id.trim() : "";
|
|
1968
|
+
const variantId = typeof incoming.variant_id === "string" ? incoming.variant_id.trim() : "";
|
|
1969
|
+
if (!productId || !variantId) {
|
|
1970
|
+
continue;
|
|
1971
|
+
}
|
|
1972
|
+
let normalizedQuantity;
|
|
1973
|
+
try {
|
|
1974
|
+
normalizedQuantity = normalizeQuantity(incoming.quantity);
|
|
1975
|
+
} catch {
|
|
1976
|
+
continue;
|
|
1977
|
+
}
|
|
1978
|
+
if (normalizedQuantity < 1) {
|
|
1979
|
+
continue;
|
|
1980
|
+
}
|
|
1981
|
+
const mergedLine = ensureCartLineId({
|
|
1982
|
+
...incoming,
|
|
1983
|
+
line_id: incoming.line_id ?? computeCartLineId(incoming),
|
|
1984
|
+
product_id: productId,
|
|
1985
|
+
variant_id: variantId,
|
|
1986
|
+
quantity: normalizedQuantity
|
|
1987
|
+
});
|
|
1988
|
+
const index = cart.findIndex((item) => item.line_id === mergedLine.line_id);
|
|
1989
|
+
if (index >= 0) {
|
|
1990
|
+
cart[index].quantity = Math.max(cart[index].quantity, normalizedQuantity);
|
|
1991
|
+
clampQuantityToBounds(cart[index]);
|
|
1992
|
+
} else {
|
|
1993
|
+
clampQuantityToBounds(mergedLine);
|
|
1994
|
+
cart.push(mergedLine);
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
writeCart(cart);
|
|
1998
|
+
return cart;
|
|
1820
1999
|
}
|
|
1821
|
-
|
|
1822
|
-
const
|
|
1823
|
-
const
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
2000
|
+
function moveBasketItem(fromProductId, fromVariantId, toProductId, toVariantId) {
|
|
2001
|
+
const cart = getCart();
|
|
2002
|
+
const fromIndex = cart.findIndex(
|
|
2003
|
+
(item) => item.product_id === fromProductId && item.variant_id === fromVariantId
|
|
2004
|
+
);
|
|
2005
|
+
if (fromIndex < 0) {
|
|
2006
|
+
throw new CartError("Item not found in cart");
|
|
2007
|
+
}
|
|
2008
|
+
const [fromItem] = cart.splice(fromIndex, 1);
|
|
2009
|
+
const toLineId = computeCartLineId({
|
|
2010
|
+
...fromItem,
|
|
2011
|
+
product_id: toProductId,
|
|
2012
|
+
variant_id: toVariantId
|
|
1830
2013
|
});
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
2014
|
+
const toIndex = cart.findIndex((item) => item.line_id === toLineId);
|
|
2015
|
+
if (toIndex >= 0) {
|
|
2016
|
+
cart[toIndex].quantity += fromItem.quantity;
|
|
2017
|
+
clampQuantityToBounds(cart[toIndex]);
|
|
2018
|
+
} else {
|
|
2019
|
+
const moved = ensureCartLineId({
|
|
2020
|
+
...fromItem,
|
|
2021
|
+
product_id: toProductId,
|
|
2022
|
+
variant_id: toVariantId,
|
|
2023
|
+
line_id: toLineId
|
|
1834
2024
|
});
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
headers: { "Content-Type": "application/json" },
|
|
1838
|
-
body: JSON.stringify(body),
|
|
1839
|
-
signal: controller?.signal
|
|
1840
|
-
}).then((response) => response.ok);
|
|
1841
|
-
return await Promise.race([delivered, timedOut]);
|
|
1842
|
-
} catch {
|
|
1843
|
-
return false;
|
|
1844
|
-
} finally {
|
|
1845
|
-
clearTimeout(timer);
|
|
2025
|
+
clampQuantityToBounds(moved);
|
|
2026
|
+
cart.push(moved);
|
|
1846
2027
|
}
|
|
2028
|
+
writeCart(cart);
|
|
1847
2029
|
}
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
const
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
if (reservedOrder) await enqueueCartSend(async () => sendCartActivity(await reservedOrder, cartValue, itemCount));
|
|
1856
|
-
return;
|
|
2030
|
+
function validateCartIntegrity() {
|
|
2031
|
+
const cart = getCart();
|
|
2032
|
+
const metadata = getCartMetadata();
|
|
2033
|
+
const checksum = computeChecksum(cart);
|
|
2034
|
+
if (!metadata) {
|
|
2035
|
+
setCartWithMetadata(cart, null);
|
|
2036
|
+
return true;
|
|
1857
2037
|
}
|
|
1858
|
-
|
|
1859
|
-
const order2 = reservedOrder ? await reservedOrder : void 0;
|
|
1860
|
-
const settle2 = order2 ? claimCartReport(config2.storeSlug, order2.seq, cartValue, itemCount) : void 0;
|
|
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
|
-
connection_id: getStorefrontConnectionId(config2.storeSlug) ?? void 0,
|
|
1868
|
-
path: resolveStorefrontPagePath(),
|
|
1869
|
-
utm: resolveUtmParameters(config2.storeSlug)
|
|
1870
|
-
});
|
|
1871
|
-
settle2?.(delivered);
|
|
1872
|
-
};
|
|
1873
|
-
await (reservedOrder ? enqueueCartSend(sendView) : sendView());
|
|
2038
|
+
return metadata.checksum === checksum;
|
|
1874
2039
|
}
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
const
|
|
1879
|
-
const
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
});
|
|
2040
|
+
function getCartStats() {
|
|
2041
|
+
const cart = getCart();
|
|
2042
|
+
const integrityValid = validateCartIntegrity();
|
|
2043
|
+
const metadata = getCartMetadata();
|
|
2044
|
+
const backup = readCartStorage("cartBackup") ?? [];
|
|
2045
|
+
const hasCompletePriceSnapshots = cart.length > 0 && cart.every((item) => typeof item.price_data?.unit_price === "number");
|
|
2046
|
+
const totalPrice = cart.reduce((sum, item) => {
|
|
2047
|
+
const unitPrice = item.price_data?.unit_price ?? 0;
|
|
2048
|
+
return sum + roundPayableAmount(unitPrice * item.quantity);
|
|
2049
|
+
}, 0);
|
|
2050
|
+
return {
|
|
2051
|
+
item_count: cart.length,
|
|
2052
|
+
total_quantity: cart.reduce((sum, item) => sum + item.quantity, 0),
|
|
2053
|
+
last_modified: metadata?.last_modified ?? 0,
|
|
2054
|
+
version: metadata?.version ?? 0,
|
|
2055
|
+
has_backup: backup.length > 0,
|
|
2056
|
+
integrity_valid: integrityValid,
|
|
2057
|
+
total_price: roundPayableAmount(totalPrice),
|
|
2058
|
+
total_price_is_estimate: cart.length > 0 && !hasCompletePriceSnapshots
|
|
2059
|
+
};
|
|
1885
2060
|
}
|
|
1886
|
-
|
|
2061
|
+
function getCartPayload(coupon) {
|
|
1887
2062
|
const config2 = getConfig();
|
|
1888
|
-
const
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
2063
|
+
const normalizedCoupon = normalizeCouponCode(coupon) ?? getCartCoupon() ?? void 0;
|
|
2064
|
+
return {
|
|
2065
|
+
store_slug: config2.storeSlug,
|
|
2066
|
+
items: getCart(),
|
|
2067
|
+
coupon: normalizedCoupon
|
|
2068
|
+
};
|
|
2069
|
+
}
|
|
2070
|
+
function getAmbientAffiliateCodeForQuote() {
|
|
2071
|
+
const storedCode = getCartCoupon();
|
|
2072
|
+
const storedSource = getCartCouponSource();
|
|
2073
|
+
if (storedSource === "AFFILIATE" && storedCode) {
|
|
2074
|
+
return storedCode.trim().toLowerCase();
|
|
2075
|
+
}
|
|
2076
|
+
return getAffiliateCode();
|
|
2077
|
+
}
|
|
2078
|
+
var latestQuoteToken = null;
|
|
2079
|
+
var quoteSequence = 0;
|
|
2080
|
+
function getLatestQuoteToken() {
|
|
2081
|
+
return latestQuoteToken;
|
|
2082
|
+
}
|
|
2083
|
+
function clearLatestQuoteToken() {
|
|
2084
|
+
latestQuoteToken = null;
|
|
2085
|
+
quoteSequence += 1;
|
|
2086
|
+
}
|
|
2087
|
+
async function quoteCart(coupon, currency) {
|
|
2088
|
+
const sequence = quoteSequence += 1;
|
|
2089
|
+
const payload = getCartPayload(coupon);
|
|
2090
|
+
const normalizedCurrency = normalizeRequestedCurrency(currency) ?? resolveBuyerCurrency(getConfig().currency);
|
|
2091
|
+
const response = await post("/v1/storefront/cart/quote", {
|
|
2092
|
+
shop_slug: payload.store_slug,
|
|
2093
|
+
cart: payload.items,
|
|
2094
|
+
coupon: payload.coupon,
|
|
2095
|
+
affiliate_code: getAmbientAffiliateCodeForQuote() ?? void 0,
|
|
2096
|
+
currency: normalizedCurrency ?? void 0
|
|
1897
2097
|
});
|
|
1898
|
-
|
|
2098
|
+
if (sequence !== quoteSequence) return response;
|
|
2099
|
+
if (response.success) {
|
|
2100
|
+
latestQuoteToken = typeof response.data?.quote_token === "string" ? response.data.quote_token : null;
|
|
2101
|
+
}
|
|
2102
|
+
return response;
|
|
1899
2103
|
}
|
|
1900
2104
|
|
|
1901
2105
|
// ../sdk/src/storefront-storage-sdk.ts
|
|
@@ -1941,7 +2145,8 @@ var storefrontStorageSdk = {
|
|
|
1941
2145
|
trackAffiliateEvent,
|
|
1942
2146
|
// Analytics
|
|
1943
2147
|
trackPageView,
|
|
1944
|
-
trackCartActivity
|
|
2148
|
+
trackCartActivity,
|
|
2149
|
+
trackProductView
|
|
1945
2150
|
};
|
|
1946
2151
|
|
|
1947
2152
|
// ../../node_modules/.bun/openapi-fetch@0.17.0/node_modules/openapi-fetch/dist/index.mjs
|
|
@@ -2789,16 +2994,6 @@ function isSafeHttpsRedirectTemplateUrl(url2) {
|
|
|
2789
2994
|
}
|
|
2790
2995
|
}
|
|
2791
2996
|
|
|
2792
|
-
// ../contracts/src/storefront-consent-choice.ts
|
|
2793
|
-
var COOKIE_CONSENT_VERSION = 1;
|
|
2794
|
-
var COOKIE_CONSENT_COOKIE = "shoppex_consent";
|
|
2795
|
-
var COOKIE_CONSENT_CHECKOUT_HASH_KEY = "shoppex_consent";
|
|
2796
|
-
var COOKIE_CONSENT_VALUE = new RegExp(`^v${COOKIE_CONSENT_VERSION}\\.f([01])\\.a([01])\\.m([01])$`);
|
|
2797
|
-
function parseCookieConsentValue(value) {
|
|
2798
|
-
const match = value ? COOKIE_CONSENT_VALUE.exec(value) : null;
|
|
2799
|
-
return match ? { functional: match[1] === "1", analytics: match[2] === "1", marketing: match[3] === "1" } : null;
|
|
2800
|
-
}
|
|
2801
|
-
|
|
2802
2997
|
// ../sdk/src/modules/checkout.ts
|
|
2803
2998
|
var CheckoutCreateError = class _CheckoutCreateError extends Error {
|
|
2804
2999
|
constructor(message, options = {}) {
|
|
@@ -3097,7 +3292,10 @@ async function performCheckout(resolvedOptions) {
|
|
|
3097
3292
|
// automatically so the server can refuse an invoice priced above it.
|
|
3098
3293
|
// Undefined when nothing has been quoted this session — the endpoint
|
|
3099
3294
|
// treats that exactly as an older SDK.
|
|
3100
|
-
quote_token: resolvedOptions.quoteToken !== void 0 ? resolvedOptions.quoteToken ?? void 0 : getLatestQuoteToken() ?? void 0
|
|
3295
|
+
quote_token: resolvedOptions.quoteToken !== void 0 ? resolvedOptions.quoteToken ?? void 0 : getLatestQuoteToken() ?? void 0,
|
|
3296
|
+
// Where the buyer came from: their analytics sessions after a yes on the
|
|
3297
|
+
// cookie banner, `declined` after a no. The server looks up the sources.
|
|
3298
|
+
attribution: checkoutAttribution(config2.storeSlug)
|
|
3101
3299
|
};
|
|
3102
3300
|
const createAttempt = acquireCheckoutCreateIdempotency(checkoutRequestTarget, createIntent);
|
|
3103
3301
|
const response = await post(
|
|
@@ -3946,7 +4144,6 @@ async function touchStorefrontPresence() {
|
|
|
3946
4144
|
{
|
|
3947
4145
|
event_type: "presence",
|
|
3948
4146
|
referer: document.referrer || void 0,
|
|
3949
|
-
connection_id: getStorefrontConnectionId(config2.storeSlug) ?? void 0,
|
|
3950
4147
|
path: resolveStorefrontPagePath(),
|
|
3951
4148
|
utm: resolveUtmParameters(config2.storeSlug)
|
|
3952
4149
|
},
|
|
@@ -24196,6 +24393,27 @@ var customerPortalAffiliateLedgerStatusSchema = external_exports.enum([
|
|
|
24196
24393
|
"split",
|
|
24197
24394
|
"voided"
|
|
24198
24395
|
]);
|
|
24396
|
+
var customerPortalAffiliateCommissionBreakdownSchema = external_exports.object({
|
|
24397
|
+
lines: external_exports.array(external_exports.object({
|
|
24398
|
+
product_id: external_exports.string().nullable(),
|
|
24399
|
+
title: external_exports.string(),
|
|
24400
|
+
base_usd: external_exports.string(),
|
|
24401
|
+
percent: external_exports.number(),
|
|
24402
|
+
source: external_exports.enum(["PRODUCT_DISABLED", "LINK", "AFFILIATE", "TIER", "PRODUCT", "SHOP_DEFAULT"]),
|
|
24403
|
+
amount_usd: external_exports.string()
|
|
24404
|
+
})),
|
|
24405
|
+
base_usd: external_exports.string(),
|
|
24406
|
+
gross_usd: external_exports.string(),
|
|
24407
|
+
cap_usd: nullableStringSchema,
|
|
24408
|
+
cap_applied: external_exports.boolean(),
|
|
24409
|
+
minimum_order_usd: nullableStringSchema,
|
|
24410
|
+
minimum_met: external_exports.boolean(),
|
|
24411
|
+
shareback_usd: external_exports.string(),
|
|
24412
|
+
amount_usd: external_exports.string(),
|
|
24413
|
+
tier_name: nullableStringSchema,
|
|
24414
|
+
attribution_source: external_exports.enum(["CODE", "BINDING"]),
|
|
24415
|
+
bound_at: nullableStringSchema
|
|
24416
|
+
});
|
|
24199
24417
|
var customerPortalAffiliateSummarySchema = external_exports.object({
|
|
24200
24418
|
customer: external_exports.object({
|
|
24201
24419
|
id: external_exports.string(),
|
|
@@ -24238,6 +24456,7 @@ var customerPortalAffiliateSummarySchema = external_exports.object({
|
|
|
24238
24456
|
store_credit_amount: external_exports.string(),
|
|
24239
24457
|
base_amount: external_exports.string(),
|
|
24240
24458
|
discount_amount: external_exports.string(),
|
|
24459
|
+
breakdown: customerPortalAffiliateCommissionBreakdownSchema.nullable(),
|
|
24241
24460
|
status: external_exports.string(),
|
|
24242
24461
|
created_at: nullableStringSchema,
|
|
24243
24462
|
paid_at: nullableStringSchema,
|
|
@@ -24701,19 +24920,29 @@ function createTicket(payload) {
|
|
|
24701
24920
|
body: {
|
|
24702
24921
|
...payload.title !== void 0 ? { title: payload.title } : {},
|
|
24703
24922
|
message: payload.message,
|
|
24704
|
-
...payload.invoice_id !== void 0 ? { invoice_id: payload.invoice_id } : {}
|
|
24923
|
+
...payload.invoice_id !== void 0 ? { invoice_id: payload.invoice_id } : {},
|
|
24924
|
+
...payload.attachment_ids?.length ? { attachment_ids: payload.attachment_ids } : {}
|
|
24705
24925
|
}
|
|
24706
24926
|
});
|
|
24707
24927
|
}
|
|
24708
24928
|
function ticket(uniqid) {
|
|
24709
24929
|
return requestCustomer(`/tickets/${encodeURIComponent(uniqid)}`);
|
|
24710
24930
|
}
|
|
24711
|
-
function replyToTicket(uniqid, message) {
|
|
24931
|
+
function replyToTicket(uniqid, message, attachmentIds = []) {
|
|
24712
24932
|
return requestCustomer(`/tickets/${encodeURIComponent(uniqid)}/reply`, {
|
|
24713
24933
|
method: "POST",
|
|
24714
|
-
body: { message }
|
|
24934
|
+
body: attachmentIds.length > 0 ? { message, attachment_ids: attachmentIds } : { message }
|
|
24715
24935
|
});
|
|
24716
24936
|
}
|
|
24937
|
+
function uploadTicketImage(file2, ticket2) {
|
|
24938
|
+
const form = new FormData();
|
|
24939
|
+
form.append("file", file2);
|
|
24940
|
+
if (ticket2) form.append("ticket", ticket2);
|
|
24941
|
+
return requestCustomer("/ticket-attachments", { method: "POST", formData: form });
|
|
24942
|
+
}
|
|
24943
|
+
function discardTicketImage(id) {
|
|
24944
|
+
return requestCustomer(`/ticket-attachments/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
24945
|
+
}
|
|
24717
24946
|
function updateProfile(patch) {
|
|
24718
24947
|
return requestCustomer("/profile", {
|
|
24719
24948
|
method: "POST",
|
|
@@ -24839,6 +25068,8 @@ var shoppex = {
|
|
|
24839
25068
|
createTicket,
|
|
24840
25069
|
ticket,
|
|
24841
25070
|
replyToTicket,
|
|
25071
|
+
uploadTicketImage,
|
|
25072
|
+
discardTicketImage,
|
|
24842
25073
|
updateProfile,
|
|
24843
25074
|
updateAvatar,
|
|
24844
25075
|
removeAvatar,
|
|
@@ -24896,6 +25127,7 @@ if (typeof window !== "undefined") {
|
|
|
24896
25127
|
createAffiliateLink,
|
|
24897
25128
|
createTicket,
|
|
24898
25129
|
dashboard,
|
|
25130
|
+
discardTicketImage,
|
|
24899
25131
|
emailPreferences,
|
|
24900
25132
|
enrollAsReseller,
|
|
24901
25133
|
ensureCartLineId,
|
|
@@ -24963,11 +25195,13 @@ if (typeof window !== "undefined") {
|
|
|
24963
25195
|
touchStorefrontPresence,
|
|
24964
25196
|
trackCartActivity,
|
|
24965
25197
|
trackPageView,
|
|
25198
|
+
trackProductView,
|
|
24966
25199
|
undoSubscriptionCancel,
|
|
24967
25200
|
updateAvatar,
|
|
24968
25201
|
updateEmailPreferences,
|
|
24969
25202
|
updateNotificationPreferences,
|
|
24970
25203
|
updateProfile,
|
|
25204
|
+
uploadTicketImage,
|
|
24971
25205
|
validateStorefrontCustomFieldValue,
|
|
24972
25206
|
verifyOtp,
|
|
24973
25207
|
warranties
|