@shoppexio/storefront 1.0.76 → 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 +13 -0
- package/dist/customer.cjs +1 -1
- package/dist/customer.cjs.map +1 -1
- package/dist/customer.d.cts +1 -1
- package/dist/customer.d.ts +1 -1
- package/dist/customer.js +1 -1
- package/dist/customer.js.map +1 -1
- package/dist/index.cjs +50 -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 +50 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1576,7 +1576,20 @@ interface SearchOptions {
|
|
|
1576
1576
|
declare function searchCatalogItems(query: string, options?: SearchOptions): Promise<SDKResponse<StorefrontCatalogSearchItem[]>>;
|
|
1577
1577
|
declare function searchProducts(query: string, options?: SearchOptions): Promise<SDKResponse<Product[]>>;
|
|
1578
1578
|
|
|
1579
|
-
|
|
1579
|
+
type AffiliateAttributionMode = 'LAST_CLICK' | 'FIRST_CLICK';
|
|
1580
|
+
declare function setAffiliateCode(code: string | null | undefined, ttlDays?: number, mode?: AffiliateAttributionMode): string | null;
|
|
1581
|
+
/**
|
|
1582
|
+
* The stored attribution, or null when nothing valid is held.
|
|
1583
|
+
*
|
|
1584
|
+
* Unlike `getAffiliateCode()` this also reports the mode the entry was written
|
|
1585
|
+
* under, which is what `captureAffiliateFromUrl` needs BEFORE it decides
|
|
1586
|
+
* whether it may overwrite.
|
|
1587
|
+
*/
|
|
1588
|
+
declare function getStoredAffiliate(): {
|
|
1589
|
+
code: string;
|
|
1590
|
+
mode: AffiliateAttributionMode;
|
|
1591
|
+
windowDays: number;
|
|
1592
|
+
} | null;
|
|
1580
1593
|
declare function clearAffiliateCode(): void;
|
|
1581
1594
|
declare function getAffiliateCode(): string | null;
|
|
1582
1595
|
declare function trackAffiliateEvent(eventType: 'add_to_cart' | 'checkout_started', options?: {
|
|
@@ -1599,11 +1612,25 @@ declare function trackAffiliateEvent(eventType: 'add_to_cart' | 'checkout_starte
|
|
|
1599
1612
|
declare function validateAffiliateCode(code: string): Promise<SDKResponse<AffiliateValidation>>;
|
|
1600
1613
|
declare function applyAffiliateCode(code: string): Promise<SDKResponse<AffiliateValidation>>;
|
|
1601
1614
|
/**
|
|
1602
|
-
* Capture an affiliate code from the current URL and store it
|
|
1615
|
+
* Capture an affiliate code from the current URL and store it (ADR-0067).
|
|
1603
1616
|
*
|
|
1604
1617
|
* Example:
|
|
1605
1618
|
* - URL: https://mystore.com/product/abc?ref=deadbeef
|
|
1606
1619
|
* - captureAffiliateFromUrl() stores "deadbeef" and returns it.
|
|
1620
|
+
*
|
|
1621
|
+
* Under LAST_CLICK (the default, and the only behaviour before ADR-0067) a new
|
|
1622
|
+
* `?ref=` replaces whatever was stored. Under FIRST_CLICK an existing,
|
|
1623
|
+
* unexpired attribution wins and the new code is NOT stored — the landing is
|
|
1624
|
+
* still reported to the server so the affiliate's click statistics stay
|
|
1625
|
+
* honest, but the credit stays with whoever was there first.
|
|
1626
|
+
*
|
|
1627
|
+
* The mode comes from the server, which means the FIRST landing in a browser
|
|
1628
|
+
* can never be blocked by it — correct, because there is nothing to protect
|
|
1629
|
+
* yet. From then on the mode travels with the stored entry.
|
|
1630
|
+
*
|
|
1631
|
+
* Enforcement is client-side. A buyer who edits localStorage can re-attribute
|
|
1632
|
+
* their own visit; this is the same trust level as the window itself and must
|
|
1633
|
+
* not be presented to merchants as fraud protection.
|
|
1607
1634
|
*/
|
|
1608
1635
|
declare function captureAffiliateFromUrl(param?: string): Promise<string | null>;
|
|
1609
1636
|
|
|
@@ -1926,6 +1953,7 @@ declare const shoppex: {
|
|
|
1926
1953
|
validateAffiliateCode: typeof validateAffiliateCode;
|
|
1927
1954
|
applyAffiliateCode: typeof applyAffiliateCode;
|
|
1928
1955
|
getAffiliateCode: typeof getAffiliateCode;
|
|
1956
|
+
getStoredAffiliate: typeof getStoredAffiliate;
|
|
1929
1957
|
setAffiliateCode: typeof setAffiliateCode;
|
|
1930
1958
|
clearAffiliateCode: typeof clearAffiliateCode;
|
|
1931
1959
|
trackAffiliateEvent: typeof trackAffiliateEvent;
|
package/dist/index.d.ts
CHANGED
|
@@ -1576,7 +1576,20 @@ interface SearchOptions {
|
|
|
1576
1576
|
declare function searchCatalogItems(query: string, options?: SearchOptions): Promise<SDKResponse<StorefrontCatalogSearchItem[]>>;
|
|
1577
1577
|
declare function searchProducts(query: string, options?: SearchOptions): Promise<SDKResponse<Product[]>>;
|
|
1578
1578
|
|
|
1579
|
-
|
|
1579
|
+
type AffiliateAttributionMode = 'LAST_CLICK' | 'FIRST_CLICK';
|
|
1580
|
+
declare function setAffiliateCode(code: string | null | undefined, ttlDays?: number, mode?: AffiliateAttributionMode): string | null;
|
|
1581
|
+
/**
|
|
1582
|
+
* The stored attribution, or null when nothing valid is held.
|
|
1583
|
+
*
|
|
1584
|
+
* Unlike `getAffiliateCode()` this also reports the mode the entry was written
|
|
1585
|
+
* under, which is what `captureAffiliateFromUrl` needs BEFORE it decides
|
|
1586
|
+
* whether it may overwrite.
|
|
1587
|
+
*/
|
|
1588
|
+
declare function getStoredAffiliate(): {
|
|
1589
|
+
code: string;
|
|
1590
|
+
mode: AffiliateAttributionMode;
|
|
1591
|
+
windowDays: number;
|
|
1592
|
+
} | null;
|
|
1580
1593
|
declare function clearAffiliateCode(): void;
|
|
1581
1594
|
declare function getAffiliateCode(): string | null;
|
|
1582
1595
|
declare function trackAffiliateEvent(eventType: 'add_to_cart' | 'checkout_started', options?: {
|
|
@@ -1599,11 +1612,25 @@ declare function trackAffiliateEvent(eventType: 'add_to_cart' | 'checkout_starte
|
|
|
1599
1612
|
declare function validateAffiliateCode(code: string): Promise<SDKResponse<AffiliateValidation>>;
|
|
1600
1613
|
declare function applyAffiliateCode(code: string): Promise<SDKResponse<AffiliateValidation>>;
|
|
1601
1614
|
/**
|
|
1602
|
-
* Capture an affiliate code from the current URL and store it
|
|
1615
|
+
* Capture an affiliate code from the current URL and store it (ADR-0067).
|
|
1603
1616
|
*
|
|
1604
1617
|
* Example:
|
|
1605
1618
|
* - URL: https://mystore.com/product/abc?ref=deadbeef
|
|
1606
1619
|
* - captureAffiliateFromUrl() stores "deadbeef" and returns it.
|
|
1620
|
+
*
|
|
1621
|
+
* Under LAST_CLICK (the default, and the only behaviour before ADR-0067) a new
|
|
1622
|
+
* `?ref=` replaces whatever was stored. Under FIRST_CLICK an existing,
|
|
1623
|
+
* unexpired attribution wins and the new code is NOT stored — the landing is
|
|
1624
|
+
* still reported to the server so the affiliate's click statistics stay
|
|
1625
|
+
* honest, but the credit stays with whoever was there first.
|
|
1626
|
+
*
|
|
1627
|
+
* The mode comes from the server, which means the FIRST landing in a browser
|
|
1628
|
+
* can never be blocked by it — correct, because there is nothing to protect
|
|
1629
|
+
* yet. From then on the mode travels with the stored entry.
|
|
1630
|
+
*
|
|
1631
|
+
* Enforcement is client-side. A buyer who edits localStorage can re-attribute
|
|
1632
|
+
* their own visit; this is the same trust level as the window itself and must
|
|
1633
|
+
* not be presented to merchants as fraud protection.
|
|
1607
1634
|
*/
|
|
1608
1635
|
declare function captureAffiliateFromUrl(param?: string): Promise<string | null>;
|
|
1609
1636
|
|
|
@@ -1926,6 +1953,7 @@ declare const shoppex: {
|
|
|
1926
1953
|
validateAffiliateCode: typeof validateAffiliateCode;
|
|
1927
1954
|
applyAffiliateCode: typeof applyAffiliateCode;
|
|
1928
1955
|
getAffiliateCode: typeof getAffiliateCode;
|
|
1956
|
+
getStoredAffiliate: typeof getStoredAffiliate;
|
|
1929
1957
|
setAffiliateCode: typeof setAffiliateCode;
|
|
1930
1958
|
clearAffiliateCode: typeof clearAffiliateCode;
|
|
1931
1959
|
trackAffiliateEvent: typeof trackAffiliateEvent;
|
package/dist/index.js
CHANGED
|
@@ -2417,6 +2417,18 @@ var publicInvoiceWireSchema = external_exports.object({
|
|
|
2417
2417
|
* the reader: this schema is not it.
|
|
2418
2418
|
*/
|
|
2419
2419
|
coupon_applied: external_exports.boolean().optional(),
|
|
2420
|
+
/**
|
|
2421
|
+
* How much of the order the applied coupon reaches, in lines — null when it
|
|
2422
|
+
* covers every line or no coupon is on the order. Counts only, so nothing
|
|
2423
|
+
* about which products the merchant scoped the code to leaves the server.
|
|
2424
|
+
* The discount line already shows the reduced amount; this is what lets the
|
|
2425
|
+
* checkout say "applied to 1 of 3 items" instead of leaving a smaller-than-
|
|
2426
|
+
* advertised discount to read as a broken code.
|
|
2427
|
+
*/
|
|
2428
|
+
coupon_scope: external_exports.object({
|
|
2429
|
+
eligible_line_count: external_exports.number(),
|
|
2430
|
+
line_count: external_exports.number()
|
|
2431
|
+
}).nullable().optional(),
|
|
2420
2432
|
crypto_mode: external_exports.string().nullable().optional(),
|
|
2421
2433
|
crypto_transactions: external_exports.array(cryptoTransactionSchema).optional(),
|
|
2422
2434
|
currency: external_exports.string().nullable(),
|
|
@@ -3878,6 +3890,9 @@ var SESSION_STORAGE_KEY = "shoppex:affiliate_session:v1";
|
|
|
3878
3890
|
var DEFAULT_TTL_DAYS = 30;
|
|
3879
3891
|
var FALLBACK_SESSION_KEY_LENGTH = 24;
|
|
3880
3892
|
var SESSION_KEY_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
3893
|
+
function normalizeMode(value) {
|
|
3894
|
+
return value === "FIRST_CLICK" ? "FIRST_CLICK" : "LAST_CLICK";
|
|
3895
|
+
}
|
|
3881
3896
|
function nowMs() {
|
|
3882
3897
|
return Date.now();
|
|
3883
3898
|
}
|
|
@@ -3895,7 +3910,7 @@ function safeRead() {
|
|
|
3895
3910
|
if (!raw) return null;
|
|
3896
3911
|
const parsed = JSON.parse(raw);
|
|
3897
3912
|
if (!parsed || typeof parsed.code !== "string" || typeof parsed.expiresAt !== "number") return null;
|
|
3898
|
-
return parsed;
|
|
3913
|
+
return parsed.mode === void 0 ? parsed : { ...parsed, mode: normalizeMode(parsed.mode) };
|
|
3899
3914
|
} catch {
|
|
3900
3915
|
return null;
|
|
3901
3916
|
}
|
|
@@ -3907,15 +3922,32 @@ function safeWrite(value) {
|
|
|
3907
3922
|
} catch {
|
|
3908
3923
|
}
|
|
3909
3924
|
}
|
|
3910
|
-
function setAffiliateCode(code, ttlDays = DEFAULT_TTL_DAYS) {
|
|
3925
|
+
function setAffiliateCode(code, ttlDays = DEFAULT_TTL_DAYS, mode) {
|
|
3911
3926
|
const normalized = normalizeAffiliateCode(code);
|
|
3912
3927
|
if (!normalized) {
|
|
3913
3928
|
clearAffiliateCode();
|
|
3914
3929
|
return null;
|
|
3915
3930
|
}
|
|
3916
|
-
safeWrite({
|
|
3931
|
+
safeWrite({
|
|
3932
|
+
code: normalized,
|
|
3933
|
+
expiresAt: nowMs() + ttlMs(ttlDays),
|
|
3934
|
+
windowDays: ttlDays,
|
|
3935
|
+
...mode ? { mode } : {}
|
|
3936
|
+
});
|
|
3917
3937
|
return normalized;
|
|
3918
3938
|
}
|
|
3939
|
+
function getStoredAffiliate() {
|
|
3940
|
+
const stored = safeRead();
|
|
3941
|
+
if (!stored) return null;
|
|
3942
|
+
if (stored.expiresAt <= nowMs()) {
|
|
3943
|
+
clearAffiliateCode();
|
|
3944
|
+
return null;
|
|
3945
|
+
}
|
|
3946
|
+
const code = normalizeAffiliateCode(stored.code);
|
|
3947
|
+
if (!code) return null;
|
|
3948
|
+
const windowDays = typeof stored.windowDays === "number" && stored.windowDays > 0 ? stored.windowDays : DEFAULT_TTL_DAYS;
|
|
3949
|
+
return { code, mode: normalizeMode(stored.mode), windowDays };
|
|
3950
|
+
}
|
|
3919
3951
|
function clearAffiliateCode() {
|
|
3920
3952
|
if (typeof window === "undefined") return;
|
|
3921
3953
|
try {
|
|
@@ -4061,7 +4093,11 @@ async function captureAffiliateFromUrl(param = "ref") {
|
|
|
4061
4093
|
}
|
|
4062
4094
|
code = normalizeAffiliateCode(code);
|
|
4063
4095
|
if (!code) return null;
|
|
4064
|
-
|
|
4096
|
+
const held = getStoredAffiliate();
|
|
4097
|
+
const mayHoldFirstClick = held !== null && held.mode === "FIRST_CLICK" && held.code !== code;
|
|
4098
|
+
if (!mayHoldFirstClick) {
|
|
4099
|
+
setAffiliateCode(code, held?.windowDays ?? DEFAULT_TTL_DAYS, held?.mode);
|
|
4100
|
+
}
|
|
4065
4101
|
if (isInitialized()) {
|
|
4066
4102
|
try {
|
|
4067
4103
|
const config = getConfig();
|
|
@@ -4070,8 +4106,14 @@ async function captureAffiliateFromUrl(param = "ref") {
|
|
|
4070
4106
|
{ shop_slug: config.storeSlug, code },
|
|
4071
4107
|
{ retries: 0 }
|
|
4072
4108
|
);
|
|
4109
|
+
const serverMode = res.success && res.data?.attribution_mode ? normalizeMode(res.data.attribution_mode) : null;
|
|
4110
|
+
const serverWindow = res.success && typeof res.data?.attribution_window_days === "number" ? res.data.attribution_window_days : DEFAULT_TTL_DAYS;
|
|
4111
|
+
const holdsFirstClick = mayHoldFirstClick && serverMode !== "LAST_CLICK";
|
|
4112
|
+
if (holdsFirstClick) {
|
|
4113
|
+
return held.code;
|
|
4114
|
+
}
|
|
4073
4115
|
if (res.success && res.data?.accepted && res.data.affiliate_code) {
|
|
4074
|
-
setAffiliateCode(res.data.affiliate_code);
|
|
4116
|
+
setAffiliateCode(res.data.affiliate_code, serverWindow, serverMode ?? void 0);
|
|
4075
4117
|
return res.data.affiliate_code;
|
|
4076
4118
|
}
|
|
4077
4119
|
if (res.success && res.data && res.data.accepted === false) {
|
|
@@ -4080,10 +4122,10 @@ async function captureAffiliateFromUrl(param = "ref") {
|
|
|
4080
4122
|
}
|
|
4081
4123
|
return code;
|
|
4082
4124
|
} catch {
|
|
4083
|
-
return code;
|
|
4125
|
+
return mayHoldFirstClick ? held.code : code;
|
|
4084
4126
|
}
|
|
4085
4127
|
}
|
|
4086
|
-
return code;
|
|
4128
|
+
return mayHoldFirstClick ? held.code : code;
|
|
4087
4129
|
}
|
|
4088
4130
|
|
|
4089
4131
|
// ../sdk/src/utils/cart-line-id.ts
|
|
@@ -6168,6 +6210,7 @@ var shoppex = {
|
|
|
6168
6210
|
validateAffiliateCode,
|
|
6169
6211
|
applyAffiliateCode,
|
|
6170
6212
|
getAffiliateCode,
|
|
6213
|
+
getStoredAffiliate,
|
|
6171
6214
|
setAffiliateCode,
|
|
6172
6215
|
clearAffiliateCode,
|
|
6173
6216
|
trackAffiliateEvent,
|