@shoppexio/storefront 1.0.78 → 1.0.80
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 +14 -0
- package/dist/index.cjs +53 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +53 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -591,6 +591,33 @@ interface AffiliateValidation {
|
|
|
591
591
|
discount_active: boolean;
|
|
592
592
|
discount_percent: number;
|
|
593
593
|
message?: string;
|
|
594
|
+
/**
|
|
595
|
+
* The attribution policy that governs this code (ADR-0067).
|
|
596
|
+
*
|
|
597
|
+
* Absent when the code did not resolve, or when an older API answered. A
|
|
598
|
+
* caller storing the code itself must apply these rather than the 30-day
|
|
599
|
+
* last-click default, or a first-click tier and any non-30-day window are
|
|
600
|
+
* silently lost.
|
|
601
|
+
*/
|
|
602
|
+
attribution_mode?: 'LAST_CLICK' | 'FIRST_CLICK';
|
|
603
|
+
attribution_window_days?: number;
|
|
604
|
+
/**
|
|
605
|
+
* The policy of the attribution the browser already holds, when one was sent.
|
|
606
|
+
* Null when that code no longer resolves — the stored mode is then the only
|
|
607
|
+
* information left.
|
|
608
|
+
*/
|
|
609
|
+
held_attribution_mode?: 'LAST_CLICK' | 'FIRST_CLICK' | null;
|
|
610
|
+
/**
|
|
611
|
+
* `applyAffiliateCode` only: whether the code you passed is the one now
|
|
612
|
+
* attributed.
|
|
613
|
+
*
|
|
614
|
+
* False when an unexpired first-click attribution kept the sale — the code
|
|
615
|
+
* you passed is still valid, it just did not win. `affiliate_code` then
|
|
616
|
+
* carries the code that DID, because the documented integration feeds that
|
|
617
|
+
* field straight into `checkout({ affiliateCode })` and an explicit checkout
|
|
618
|
+
* code overrides storage.
|
|
619
|
+
*/
|
|
620
|
+
applied?: boolean;
|
|
594
621
|
}
|
|
595
622
|
interface ProductGroup {
|
|
596
623
|
id?: string;
|
|
@@ -1589,6 +1616,12 @@ declare function getStoredAffiliate(): {
|
|
|
1589
1616
|
code: string;
|
|
1590
1617
|
mode: AffiliateAttributionMode;
|
|
1591
1618
|
windowDays: number;
|
|
1619
|
+
/**
|
|
1620
|
+
* When this entry expires, as written. Reported so a hold that has to be put
|
|
1621
|
+
* back after an early overwrite keeps its original expiry — the window runs
|
|
1622
|
+
* from the click that earned it, not from the landing that failed to take it.
|
|
1623
|
+
*/
|
|
1624
|
+
expiresAt: number;
|
|
1592
1625
|
} | null;
|
|
1593
1626
|
declare function clearAffiliateCode(): void;
|
|
1594
1627
|
declare function getAffiliateCode(): string | null;
|
|
@@ -1610,6 +1643,23 @@ declare function trackAffiliateEvent(eventType: 'add_to_cart' | 'checkout_starte
|
|
|
1610
1643
|
dedupeKey?: string;
|
|
1611
1644
|
}): Promise<void>;
|
|
1612
1645
|
declare function validateAffiliateCode(code: string): Promise<SDKResponse<AffiliateValidation>>;
|
|
1646
|
+
/**
|
|
1647
|
+
* Validate a manually entered code and store it (ADR-0067).
|
|
1648
|
+
*
|
|
1649
|
+
* Attribution is governed by the same rules as a `?ref=` landing, deliberately:
|
|
1650
|
+
* a themed "apply code" box must not be a way around first-click, or the rule
|
|
1651
|
+
* is bypassed by any storefront that offers one. So a still-protected hold
|
|
1652
|
+
* keeps the sale, and the stored entry always carries the mode and window the
|
|
1653
|
+
* server just reported — the old unconditional `setAffiliateCode(code)` wrote
|
|
1654
|
+
* a 30-day last-click entry whatever the shop or tier had configured.
|
|
1655
|
+
*
|
|
1656
|
+
* The result reports what is now ATTRIBUTED, not merely what was validated:
|
|
1657
|
+
* `affiliate_code` carries the winning code and `applied` says whether that is
|
|
1658
|
+
* the one you passed. The documented integration hands `affiliate_code`
|
|
1659
|
+
* straight to `checkout({ affiliateCode })`, and an explicit checkout code
|
|
1660
|
+
* overrides storage — so returning the losing code here would credit it and
|
|
1661
|
+
* walk right around the hold this function just honoured.
|
|
1662
|
+
*/
|
|
1613
1663
|
declare function applyAffiliateCode(code: string): Promise<SDKResponse<AffiliateValidation>>;
|
|
1614
1664
|
/**
|
|
1615
1665
|
* Capture an affiliate code from the current URL and store it (ADR-0067).
|
package/dist/index.d.ts
CHANGED
|
@@ -591,6 +591,33 @@ interface AffiliateValidation {
|
|
|
591
591
|
discount_active: boolean;
|
|
592
592
|
discount_percent: number;
|
|
593
593
|
message?: string;
|
|
594
|
+
/**
|
|
595
|
+
* The attribution policy that governs this code (ADR-0067).
|
|
596
|
+
*
|
|
597
|
+
* Absent when the code did not resolve, or when an older API answered. A
|
|
598
|
+
* caller storing the code itself must apply these rather than the 30-day
|
|
599
|
+
* last-click default, or a first-click tier and any non-30-day window are
|
|
600
|
+
* silently lost.
|
|
601
|
+
*/
|
|
602
|
+
attribution_mode?: 'LAST_CLICK' | 'FIRST_CLICK';
|
|
603
|
+
attribution_window_days?: number;
|
|
604
|
+
/**
|
|
605
|
+
* The policy of the attribution the browser already holds, when one was sent.
|
|
606
|
+
* Null when that code no longer resolves — the stored mode is then the only
|
|
607
|
+
* information left.
|
|
608
|
+
*/
|
|
609
|
+
held_attribution_mode?: 'LAST_CLICK' | 'FIRST_CLICK' | null;
|
|
610
|
+
/**
|
|
611
|
+
* `applyAffiliateCode` only: whether the code you passed is the one now
|
|
612
|
+
* attributed.
|
|
613
|
+
*
|
|
614
|
+
* False when an unexpired first-click attribution kept the sale — the code
|
|
615
|
+
* you passed is still valid, it just did not win. `affiliate_code` then
|
|
616
|
+
* carries the code that DID, because the documented integration feeds that
|
|
617
|
+
* field straight into `checkout({ affiliateCode })` and an explicit checkout
|
|
618
|
+
* code overrides storage.
|
|
619
|
+
*/
|
|
620
|
+
applied?: boolean;
|
|
594
621
|
}
|
|
595
622
|
interface ProductGroup {
|
|
596
623
|
id?: string;
|
|
@@ -1589,6 +1616,12 @@ declare function getStoredAffiliate(): {
|
|
|
1589
1616
|
code: string;
|
|
1590
1617
|
mode: AffiliateAttributionMode;
|
|
1591
1618
|
windowDays: number;
|
|
1619
|
+
/**
|
|
1620
|
+
* When this entry expires, as written. Reported so a hold that has to be put
|
|
1621
|
+
* back after an early overwrite keeps its original expiry — the window runs
|
|
1622
|
+
* from the click that earned it, not from the landing that failed to take it.
|
|
1623
|
+
*/
|
|
1624
|
+
expiresAt: number;
|
|
1592
1625
|
} | null;
|
|
1593
1626
|
declare function clearAffiliateCode(): void;
|
|
1594
1627
|
declare function getAffiliateCode(): string | null;
|
|
@@ -1610,6 +1643,23 @@ declare function trackAffiliateEvent(eventType: 'add_to_cart' | 'checkout_starte
|
|
|
1610
1643
|
dedupeKey?: string;
|
|
1611
1644
|
}): Promise<void>;
|
|
1612
1645
|
declare function validateAffiliateCode(code: string): Promise<SDKResponse<AffiliateValidation>>;
|
|
1646
|
+
/**
|
|
1647
|
+
* Validate a manually entered code and store it (ADR-0067).
|
|
1648
|
+
*
|
|
1649
|
+
* Attribution is governed by the same rules as a `?ref=` landing, deliberately:
|
|
1650
|
+
* a themed "apply code" box must not be a way around first-click, or the rule
|
|
1651
|
+
* is bypassed by any storefront that offers one. So a still-protected hold
|
|
1652
|
+
* keeps the sale, and the stored entry always carries the mode and window the
|
|
1653
|
+
* server just reported — the old unconditional `setAffiliateCode(code)` wrote
|
|
1654
|
+
* a 30-day last-click entry whatever the shop or tier had configured.
|
|
1655
|
+
*
|
|
1656
|
+
* The result reports what is now ATTRIBUTED, not merely what was validated:
|
|
1657
|
+
* `affiliate_code` carries the winning code and `applied` says whether that is
|
|
1658
|
+
* the one you passed. The documented integration hands `affiliate_code`
|
|
1659
|
+
* straight to `checkout({ affiliateCode })`, and an explicit checkout code
|
|
1660
|
+
* overrides storage — so returning the losing code here would credit it and
|
|
1661
|
+
* walk right around the hold this function just honoured.
|
|
1662
|
+
*/
|
|
1613
1663
|
declare function applyAffiliateCode(code: string): Promise<SDKResponse<AffiliateValidation>>;
|
|
1614
1664
|
/**
|
|
1615
1665
|
* Capture an affiliate code from the current URL and store it (ADR-0067).
|
package/dist/index.js
CHANGED
|
@@ -3922,6 +3922,9 @@ function safeWrite(value) {
|
|
|
3922
3922
|
} catch {
|
|
3923
3923
|
}
|
|
3924
3924
|
}
|
|
3925
|
+
function restoreStoredAffiliate(value) {
|
|
3926
|
+
safeWrite(value);
|
|
3927
|
+
}
|
|
3925
3928
|
function setAffiliateCode(code, ttlDays = DEFAULT_TTL_DAYS, mode) {
|
|
3926
3929
|
const normalized = normalizeAffiliateCode(code);
|
|
3927
3930
|
if (!normalized) {
|
|
@@ -3946,7 +3949,7 @@ function getStoredAffiliate() {
|
|
|
3946
3949
|
const code = normalizeAffiliateCode(stored.code);
|
|
3947
3950
|
if (!code) return null;
|
|
3948
3951
|
const windowDays = typeof stored.windowDays === "number" && stored.windowDays > 0 ? stored.windowDays : DEFAULT_TTL_DAYS;
|
|
3949
|
-
return { code, mode: normalizeMode(stored.mode), windowDays };
|
|
3952
|
+
return { code, mode: normalizeMode(stored.mode), windowDays, expiresAt: stored.expiresAt };
|
|
3950
3953
|
}
|
|
3951
3954
|
function clearAffiliateCode() {
|
|
3952
3955
|
if (typeof window === "undefined") return;
|
|
@@ -4037,11 +4040,13 @@ async function validateAffiliateCode(code) {
|
|
|
4037
4040
|
};
|
|
4038
4041
|
}
|
|
4039
4042
|
const config = getConfig();
|
|
4043
|
+
const held = getStoredAffiliate();
|
|
4040
4044
|
const response = await post(
|
|
4041
4045
|
"/v1/storefront/affiliates/resolve",
|
|
4042
4046
|
{
|
|
4043
4047
|
shop_slug: config.storeSlug,
|
|
4044
|
-
code: normalizedCode
|
|
4048
|
+
code: normalizedCode,
|
|
4049
|
+
...held && held.code !== normalizedCode ? { held_code: held.code } : {}
|
|
4045
4050
|
},
|
|
4046
4051
|
{ retries: 0 }
|
|
4047
4052
|
);
|
|
@@ -4069,15 +4074,36 @@ async function validateAffiliateCode(code) {
|
|
|
4069
4074
|
...response.data.program_enabled !== void 0 ? { program_enabled: response.data.program_enabled } : {},
|
|
4070
4075
|
affiliate_code: normalizeAffiliateCode(response.data.affiliate_code),
|
|
4071
4076
|
discount_active: Boolean(response.data.discount_active),
|
|
4072
|
-
discount_percent: Number(response.data.discount_percent ?? 0)
|
|
4077
|
+
discount_percent: Number(response.data.discount_percent ?? 0),
|
|
4078
|
+
// Passed through rather than dropped: a caller that stores the code has
|
|
4079
|
+
// no other way to learn the policy, and defaulting to 30-day last-click
|
|
4080
|
+
// would quietly discard a first-click tier and any custom window.
|
|
4081
|
+
attribution_mode: normalizeMode(response.data.attribution_mode),
|
|
4082
|
+
attribution_window_days: typeof response.data.attribution_window_days === "number" && response.data.attribution_window_days >= 1 ? Math.trunc(response.data.attribution_window_days) : DEFAULT_TTL_DAYS,
|
|
4083
|
+
held_attribution_mode: response.data.held_attribution_mode === "FIRST_CLICK" || response.data.held_attribution_mode === "LAST_CLICK" ? response.data.held_attribution_mode : null
|
|
4073
4084
|
},
|
|
4074
4085
|
...response.message ? { message: response.message } : {}
|
|
4075
4086
|
};
|
|
4076
4087
|
}
|
|
4077
4088
|
async function applyAffiliateCode(code) {
|
|
4089
|
+
const held = getStoredAffiliate();
|
|
4078
4090
|
const result = await validateAffiliateCode(code);
|
|
4079
4091
|
if (result.success && result.data?.affiliate_code) {
|
|
4080
|
-
|
|
4092
|
+
const applied = result.data.affiliate_code;
|
|
4093
|
+
const holdsOtherCode = held !== null && held.code !== applied;
|
|
4094
|
+
const heldMode = holdsOtherCode ? result.data.held_attribution_mode ?? held.mode : null;
|
|
4095
|
+
if (heldMode === "FIRST_CLICK" && held) {
|
|
4096
|
+
return {
|
|
4097
|
+
...result,
|
|
4098
|
+
data: { ...result.data, affiliate_code: held.code, applied: false }
|
|
4099
|
+
};
|
|
4100
|
+
}
|
|
4101
|
+
setAffiliateCode(
|
|
4102
|
+
applied,
|
|
4103
|
+
result.data.attribution_window_days ?? DEFAULT_TTL_DAYS,
|
|
4104
|
+
result.data.attribution_mode
|
|
4105
|
+
);
|
|
4106
|
+
return { ...result, data: { ...result.data, applied: true } };
|
|
4081
4107
|
}
|
|
4082
4108
|
return result;
|
|
4083
4109
|
}
|
|
@@ -4094,8 +4120,8 @@ async function captureAffiliateFromUrl(param = "ref") {
|
|
|
4094
4120
|
code = normalizeAffiliateCode(code);
|
|
4095
4121
|
if (!code) return null;
|
|
4096
4122
|
const held = getStoredAffiliate();
|
|
4097
|
-
const
|
|
4098
|
-
if (!
|
|
4123
|
+
const holdsOtherCode = held !== null && held.code !== code;
|
|
4124
|
+
if (!holdsOtherCode) {
|
|
4099
4125
|
setAffiliateCode(code, held?.windowDays ?? DEFAULT_TTL_DAYS, held?.mode);
|
|
4100
4126
|
}
|
|
4101
4127
|
if (isInitialized()) {
|
|
@@ -4103,13 +4129,27 @@ async function captureAffiliateFromUrl(param = "ref") {
|
|
|
4103
4129
|
const config = getConfig();
|
|
4104
4130
|
const res = await post(
|
|
4105
4131
|
"/v1/storefront/affiliates/attribution",
|
|
4106
|
-
{
|
|
4132
|
+
{
|
|
4133
|
+
shop_slug: config.storeSlug,
|
|
4134
|
+
code,
|
|
4135
|
+
// Sent so the server can report the policy of the attribution we
|
|
4136
|
+
// already hold. Its mode — not the arriving code's — decides whether
|
|
4137
|
+
// that hold may be released.
|
|
4138
|
+
...held ? { held_code: held.code } : {}
|
|
4139
|
+
},
|
|
4107
4140
|
{ retries: 0 }
|
|
4108
4141
|
);
|
|
4109
4142
|
const serverMode = res.success && res.data?.attribution_mode ? normalizeMode(res.data.attribution_mode) : null;
|
|
4110
4143
|
const serverWindow = res.success && typeof res.data?.attribution_window_days === "number" ? res.data.attribution_window_days : DEFAULT_TTL_DAYS;
|
|
4111
|
-
const
|
|
4112
|
-
|
|
4144
|
+
const heldMode = res.success && res.data?.held_attribution_mode ? normalizeMode(res.data.held_attribution_mode) : null;
|
|
4145
|
+
const holdsFirstClick = holdsOtherCode && (heldMode ?? held?.mode) === "FIRST_CLICK";
|
|
4146
|
+
if (holdsFirstClick && held) {
|
|
4147
|
+
restoreStoredAffiliate({
|
|
4148
|
+
code: held.code,
|
|
4149
|
+
expiresAt: held.expiresAt,
|
|
4150
|
+
windowDays: held.windowDays,
|
|
4151
|
+
mode: heldMode ?? held.mode
|
|
4152
|
+
});
|
|
4113
4153
|
return held.code;
|
|
4114
4154
|
}
|
|
4115
4155
|
if (res.success && res.data?.accepted && res.data.affiliate_code) {
|
|
@@ -4117,15 +4157,16 @@ async function captureAffiliateFromUrl(param = "ref") {
|
|
|
4117
4157
|
return res.data.affiliate_code;
|
|
4118
4158
|
}
|
|
4119
4159
|
if (res.success && res.data && res.data.accepted === false) {
|
|
4160
|
+
if (holdsOtherCode) return held.code;
|
|
4120
4161
|
clearAffiliateCode();
|
|
4121
4162
|
return null;
|
|
4122
4163
|
}
|
|
4123
|
-
return code;
|
|
4164
|
+
return holdsOtherCode ? held.code : code;
|
|
4124
4165
|
} catch {
|
|
4125
|
-
return
|
|
4166
|
+
return holdsOtherCode ? held.code : code;
|
|
4126
4167
|
}
|
|
4127
4168
|
}
|
|
4128
|
-
return
|
|
4169
|
+
return holdsOtherCode ? held.code : code;
|
|
4129
4170
|
}
|
|
4130
4171
|
|
|
4131
4172
|
// ../sdk/src/utils/cart-line-id.ts
|