@shoppexio/storefront 1.0.77 → 1.0.78
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 +9 -0
- package/dist/index.cjs +38 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -2
- package/dist/index.d.ts +30 -2
- package/dist/index.js +38 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.78
|
|
4
|
+
|
|
5
|
+
- `captureAffiliateFromUrl()` honours the shop's attribution mode (ADR-0067). Under `FIRST_CLICK` a held, unexpired attribution wins and a newer `?ref=` no longer replaces it; the landing is still reported so click statistics stay complete. `LAST_CLICK` is unchanged and remains the default.
|
|
6
|
+
- The attribution window is no longer fixed at 30 days: `/attribution` returns `attribution_mode` and `attribution_window_days`, and a newly stored referral uses the shop's (or its tier's) window.
|
|
7
|
+
- New export `getStoredAffiliate()`, returning `{ code, mode }` for the held attribution, or `null`.
|
|
8
|
+
- `setAffiliateCode(code, ttlDays, mode?)` accepts the mode to persist. Existing two-argument calls are unaffected.
|
|
9
|
+
- The `shoppex:affiliate_code:v1` storage key is unchanged and entries written by earlier versions keep working: an entry without a mode reads as `LAST_CLICK`, the behaviour it was written under. No attribution is lost on upgrade.
|
|
10
|
+
- Note: first-click is enforced in the browser, like the window itself. It is an attribution rule, not fraud protection.
|
|
11
|
+
|
|
3
12
|
## 1.0.77
|
|
4
13
|
|
|
5
14
|
- Headless customer replacement requests now require `lineItemId` in the SDK types, matching the live API. Pass the invoice position UUID; omitted or null IDs are not valid. The read-only `replacementEligibility` query still allows omitting its line ID.
|
package/dist/index.cjs
CHANGED
|
@@ -19285,6 +19285,9 @@ var SESSION_STORAGE_KEY = "shoppex:affiliate_session:v1";
|
|
|
19285
19285
|
var DEFAULT_TTL_DAYS = 30;
|
|
19286
19286
|
var FALLBACK_SESSION_KEY_LENGTH = 24;
|
|
19287
19287
|
var SESSION_KEY_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
19288
|
+
function normalizeMode(value) {
|
|
19289
|
+
return value === "FIRST_CLICK" ? "FIRST_CLICK" : "LAST_CLICK";
|
|
19290
|
+
}
|
|
19288
19291
|
function nowMs() {
|
|
19289
19292
|
return Date.now();
|
|
19290
19293
|
}
|
|
@@ -19302,7 +19305,7 @@ function safeRead() {
|
|
|
19302
19305
|
if (!raw) return null;
|
|
19303
19306
|
const parsed = JSON.parse(raw);
|
|
19304
19307
|
if (!parsed || typeof parsed.code !== "string" || typeof parsed.expiresAt !== "number") return null;
|
|
19305
|
-
return parsed;
|
|
19308
|
+
return parsed.mode === void 0 ? parsed : { ...parsed, mode: normalizeMode(parsed.mode) };
|
|
19306
19309
|
} catch {
|
|
19307
19310
|
return null;
|
|
19308
19311
|
}
|
|
@@ -19314,15 +19317,32 @@ function safeWrite(value) {
|
|
|
19314
19317
|
} catch {
|
|
19315
19318
|
}
|
|
19316
19319
|
}
|
|
19317
|
-
function setAffiliateCode(code, ttlDays = DEFAULT_TTL_DAYS) {
|
|
19320
|
+
function setAffiliateCode(code, ttlDays = DEFAULT_TTL_DAYS, mode) {
|
|
19318
19321
|
const normalized = normalizeAffiliateCode(code);
|
|
19319
19322
|
if (!normalized) {
|
|
19320
19323
|
clearAffiliateCode();
|
|
19321
19324
|
return null;
|
|
19322
19325
|
}
|
|
19323
|
-
safeWrite({
|
|
19326
|
+
safeWrite({
|
|
19327
|
+
code: normalized,
|
|
19328
|
+
expiresAt: nowMs() + ttlMs(ttlDays),
|
|
19329
|
+
windowDays: ttlDays,
|
|
19330
|
+
...mode ? { mode } : {}
|
|
19331
|
+
});
|
|
19324
19332
|
return normalized;
|
|
19325
19333
|
}
|
|
19334
|
+
function getStoredAffiliate() {
|
|
19335
|
+
const stored = safeRead();
|
|
19336
|
+
if (!stored) return null;
|
|
19337
|
+
if (stored.expiresAt <= nowMs()) {
|
|
19338
|
+
clearAffiliateCode();
|
|
19339
|
+
return null;
|
|
19340
|
+
}
|
|
19341
|
+
const code = normalizeAffiliateCode(stored.code);
|
|
19342
|
+
if (!code) return null;
|
|
19343
|
+
const windowDays = typeof stored.windowDays === "number" && stored.windowDays > 0 ? stored.windowDays : DEFAULT_TTL_DAYS;
|
|
19344
|
+
return { code, mode: normalizeMode(stored.mode), windowDays };
|
|
19345
|
+
}
|
|
19326
19346
|
function clearAffiliateCode() {
|
|
19327
19347
|
if (typeof window === "undefined") return;
|
|
19328
19348
|
try {
|
|
@@ -19468,7 +19488,11 @@ async function captureAffiliateFromUrl(param = "ref") {
|
|
|
19468
19488
|
}
|
|
19469
19489
|
code = normalizeAffiliateCode(code);
|
|
19470
19490
|
if (!code) return null;
|
|
19471
|
-
|
|
19491
|
+
const held = getStoredAffiliate();
|
|
19492
|
+
const mayHoldFirstClick = held !== null && held.mode === "FIRST_CLICK" && held.code !== code;
|
|
19493
|
+
if (!mayHoldFirstClick) {
|
|
19494
|
+
setAffiliateCode(code, held?.windowDays ?? DEFAULT_TTL_DAYS, held?.mode);
|
|
19495
|
+
}
|
|
19472
19496
|
if (isInitialized()) {
|
|
19473
19497
|
try {
|
|
19474
19498
|
const config2 = getConfig();
|
|
@@ -19477,8 +19501,14 @@ async function captureAffiliateFromUrl(param = "ref") {
|
|
|
19477
19501
|
{ shop_slug: config2.storeSlug, code },
|
|
19478
19502
|
{ retries: 0 }
|
|
19479
19503
|
);
|
|
19504
|
+
const serverMode = res.success && res.data?.attribution_mode ? normalizeMode(res.data.attribution_mode) : null;
|
|
19505
|
+
const serverWindow = res.success && typeof res.data?.attribution_window_days === "number" ? res.data.attribution_window_days : DEFAULT_TTL_DAYS;
|
|
19506
|
+
const holdsFirstClick = mayHoldFirstClick && serverMode !== "LAST_CLICK";
|
|
19507
|
+
if (holdsFirstClick) {
|
|
19508
|
+
return held.code;
|
|
19509
|
+
}
|
|
19480
19510
|
if (res.success && res.data?.accepted && res.data.affiliate_code) {
|
|
19481
|
-
setAffiliateCode(res.data.affiliate_code);
|
|
19511
|
+
setAffiliateCode(res.data.affiliate_code, serverWindow, serverMode ?? void 0);
|
|
19482
19512
|
return res.data.affiliate_code;
|
|
19483
19513
|
}
|
|
19484
19514
|
if (res.success && res.data && res.data.accepted === false) {
|
|
@@ -19487,10 +19517,10 @@ async function captureAffiliateFromUrl(param = "ref") {
|
|
|
19487
19517
|
}
|
|
19488
19518
|
return code;
|
|
19489
19519
|
} catch {
|
|
19490
|
-
return code;
|
|
19520
|
+
return mayHoldFirstClick ? held.code : code;
|
|
19491
19521
|
}
|
|
19492
19522
|
}
|
|
19493
|
-
return code;
|
|
19523
|
+
return mayHoldFirstClick ? held.code : code;
|
|
19494
19524
|
}
|
|
19495
19525
|
|
|
19496
19526
|
// ../sdk/src/utils/cart-line-id.ts
|
|
@@ -21575,6 +21605,7 @@ var shoppex = {
|
|
|
21575
21605
|
validateAffiliateCode,
|
|
21576
21606
|
applyAffiliateCode,
|
|
21577
21607
|
getAffiliateCode,
|
|
21608
|
+
getStoredAffiliate,
|
|
21578
21609
|
setAffiliateCode,
|
|
21579
21610
|
clearAffiliateCode,
|
|
21580
21611
|
trackAffiliateEvent,
|