@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 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({ code: normalized, expiresAt: nowMs() + ttlMs(ttlDays) });
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
- setAffiliateCode(code);
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,