@vio-live/web-sdk 0.23.1 → 0.26.0
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/README.md +191 -21
- package/dist/{chunk-BRJMT7P4.js → chunk-5KWICUME.js} +1321 -333
- package/dist/chunk-5KWICUME.js.map +1 -0
- package/dist/{chunk-UVDOKFGD.js → chunk-A3LN5JEM.js} +235 -58
- package/dist/chunk-A3LN5JEM.js.map +1 -0
- package/dist/chunk-NCP76AXW.js +3 -0
- package/dist/{chunk-45YKS5UG.js.map → chunk-NCP76AXW.js.map} +1 -1
- package/dist/core/index.d.ts +1284 -596
- package/dist/core/index.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +3 -3
- package/dist/{tokens-DDfZuHF6.d.ts → tokens-Bn7fK9ta.d.ts} +72 -0
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +3 -3
- package/package.json +2 -1
- package/dist/chunk-45YKS5UG.js +0 -3
- package/dist/chunk-BRJMT7P4.js.map +0 -1
- package/dist/chunk-UVDOKFGD.js.map +0 -1
|
@@ -11,6 +11,261 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
11
11
|
return result;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
+
// src/core/slots.ts
|
|
15
|
+
var SLOT_ID_MAX_LENGTH = 100;
|
|
16
|
+
var SLOT_DECLARATION_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
17
|
+
var SLOT_DECLARATION_FAILURE_TTL_MS = 60 * 60 * 1e3;
|
|
18
|
+
var MAX_LOCATIONS_PER_DECLARATION = 100;
|
|
19
|
+
var MAX_DECLARATIONS_PER_LOAD = 3;
|
|
20
|
+
var SLOT_KIND_MAX_LENGTH = 32;
|
|
21
|
+
function componentKindOf(templateId, slot) {
|
|
22
|
+
const hint = typeof templateId === "string" ? templateId.trim().toLowerCase() : "";
|
|
23
|
+
if (hint) return hint.replace(/^(product|vev)-/, "").slice(0, SLOT_KIND_MAX_LENGTH) || void 0;
|
|
24
|
+
const id = typeof slot === "string" ? slot.toLowerCase() : "";
|
|
25
|
+
const m = id.match(/(?:^|-)(card|carousel|grid|detail|banner|list|hero|stepper)(?:-|$)/);
|
|
26
|
+
return m?.[1];
|
|
27
|
+
}
|
|
28
|
+
function normalizeSlotKind(value) {
|
|
29
|
+
if (typeof value !== "string") return void 0;
|
|
30
|
+
const kind = value.trim().toLowerCase().slice(0, SLOT_KIND_MAX_LENGTH).trim();
|
|
31
|
+
return kind || void 0;
|
|
32
|
+
}
|
|
33
|
+
function resolveSlotCampaign(boot, slot, now = Date.now(), platform, pageCampaignId, sponsorId) {
|
|
34
|
+
if (!boot) return null;
|
|
35
|
+
const page = activeCampaignEntry(boot, pageCampaignId);
|
|
36
|
+
const assignment = findSlotAssignmentDetail(boot, slot, now, platform);
|
|
37
|
+
const sponsor = toNumber(sponsorId);
|
|
38
|
+
if (assignment && assignmentApplies(assignment, page, sponsor)) return assignment.campaign;
|
|
39
|
+
return activeCampaignEntry(boot, pageCampaignId, sponsor) ?? (sponsor !== void 0 ? sponsorCampaignEntry(boot, sponsor) : null) ?? defaultCampaignEntry(boot);
|
|
40
|
+
}
|
|
41
|
+
function sponsorCampaignEntry(boot, sponsorId) {
|
|
42
|
+
const id = toNumber(sponsorId);
|
|
43
|
+
if (!boot || id === void 0) return null;
|
|
44
|
+
try {
|
|
45
|
+
const matches = activeCampaigns(boot).filter((c) => campaignSponsorIds(c).includes(id));
|
|
46
|
+
if (matches.length === 0) return null;
|
|
47
|
+
const defaultId = toNumber(boot.campaign?.id);
|
|
48
|
+
return matches.find((c) => toNumber(c.id) === defaultId) ?? matches[0] ?? null;
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function assignmentApplies(assignment, page, sponsorId) {
|
|
54
|
+
if (assignment.slot.source !== "page") return true;
|
|
55
|
+
if (isStalePageAssignment(assignment, page)) return false;
|
|
56
|
+
return sponsorId === void 0 || campaignSponsorIds(assignment.campaign).includes(sponsorId);
|
|
57
|
+
}
|
|
58
|
+
function isStalePageAssignment(assignment, page) {
|
|
59
|
+
return !!page && assignment.slot.source === "page" && toNumber(page.id) !== toNumber(assignment.campaign.id);
|
|
60
|
+
}
|
|
61
|
+
function toCampaignId(value) {
|
|
62
|
+
let n;
|
|
63
|
+
if (typeof value === "number") n = value;
|
|
64
|
+
else if (typeof value === "string" && /^\s*\d+\s*$/.test(value)) n = Number(value);
|
|
65
|
+
else return void 0;
|
|
66
|
+
return Number.isSafeInteger(n) && n > 0 ? n : void 0;
|
|
67
|
+
}
|
|
68
|
+
function campaignCandidates(hint) {
|
|
69
|
+
const raw = Array.isArray(hint) ? hint : [hint];
|
|
70
|
+
const out = [];
|
|
71
|
+
for (const v of raw) {
|
|
72
|
+
const id = toCampaignId(v);
|
|
73
|
+
if (id !== void 0 && !out.includes(id)) out.push(id);
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
function activeCampaignEntry(boot, hint, sponsorId) {
|
|
78
|
+
if (!boot) return null;
|
|
79
|
+
try {
|
|
80
|
+
const active = activeCampaigns(boot);
|
|
81
|
+
for (const id of campaignCandidates(hint)) {
|
|
82
|
+
const entry = active.find((c) => toNumber(c.id) === id);
|
|
83
|
+
if (!entry) continue;
|
|
84
|
+
if (sponsorId !== void 0 && !campaignSponsorIds(entry).includes(sponsorId)) continue;
|
|
85
|
+
return entry;
|
|
86
|
+
}
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
function campaignSponsorIds(campaign) {
|
|
92
|
+
const ids = [];
|
|
93
|
+
const primary = toNumber(campaign.primarySponsor?.id);
|
|
94
|
+
if (primary !== void 0) ids.push(primary);
|
|
95
|
+
const secondaries = Array.isArray(campaign.secondarySponsors) ? campaign.secondarySponsors : [];
|
|
96
|
+
for (const s of secondaries) {
|
|
97
|
+
const id = toNumber(s?.id);
|
|
98
|
+
if (id !== void 0) ids.push(id);
|
|
99
|
+
}
|
|
100
|
+
return ids;
|
|
101
|
+
}
|
|
102
|
+
function findSlotAssignmentDetail(boot, slot, now = Date.now(), platform) {
|
|
103
|
+
const name = typeof slot === "string" ? slot.trim() : "";
|
|
104
|
+
if (!boot || !name) return null;
|
|
105
|
+
try {
|
|
106
|
+
const matches = [];
|
|
107
|
+
for (const campaign of activeCampaigns(boot)) {
|
|
108
|
+
const match = slotsOf(campaign).find((s) => assignmentMatches(s, name, now, platform));
|
|
109
|
+
if (match) matches.push({ campaign, slot: match });
|
|
110
|
+
}
|
|
111
|
+
if (matches.length === 0) return null;
|
|
112
|
+
const defaultId = toNumber(boot.campaign?.id);
|
|
113
|
+
return matches.find((m) => toNumber(m.campaign.id) === defaultId) ?? matches[0] ?? null;
|
|
114
|
+
} catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function defaultCampaignEntry(boot) {
|
|
119
|
+
if (!boot?.campaign || typeof boot.campaign !== "object") return null;
|
|
120
|
+
const defaultId = toNumber(boot.campaign.id);
|
|
121
|
+
if (defaultId === void 0) return null;
|
|
122
|
+
try {
|
|
123
|
+
const listed = wellFormedCampaigns(boot).find((c) => toNumber(c.id) === defaultId);
|
|
124
|
+
if (listed) return listed;
|
|
125
|
+
} catch {
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
...boot.campaign,
|
|
129
|
+
isDefault: true,
|
|
130
|
+
primarySponsor: boot.primarySponsor ?? null,
|
|
131
|
+
secondarySponsors: Array.isArray(boot.secondarySponsors) ? boot.secondarySponsors : [],
|
|
132
|
+
slots: []
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function wellFormedCampaigns(boot) {
|
|
136
|
+
const list = Array.isArray(boot.campaigns) ? boot.campaigns : [];
|
|
137
|
+
return list.filter((c) => !!c && typeof c === "object");
|
|
138
|
+
}
|
|
139
|
+
function activeCampaigns(boot) {
|
|
140
|
+
return wellFormedCampaigns(boot).filter((c) => c.status === "active");
|
|
141
|
+
}
|
|
142
|
+
function slotsOf(campaign) {
|
|
143
|
+
const slots = Array.isArray(campaign.slots) ? campaign.slots : [];
|
|
144
|
+
return slots.filter((s) => !!s && typeof s === "object");
|
|
145
|
+
}
|
|
146
|
+
function assignmentMatches(s, name, now, platform) {
|
|
147
|
+
if (typeof s.name !== "string" || s.name.trim() !== name) return false;
|
|
148
|
+
if (platform && s.platform != null && s.platform !== platform) return false;
|
|
149
|
+
const start = parseTime(s.startDate);
|
|
150
|
+
if (start !== void 0 && start > now) return false;
|
|
151
|
+
const end = parseTime(s.endDate);
|
|
152
|
+
if (end !== void 0 && now >= end) return false;
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
function parseTime(value) {
|
|
156
|
+
if (value == null || value === "") return void 0;
|
|
157
|
+
const t = Date.parse(String(value));
|
|
158
|
+
return Number.isFinite(t) ? t : void 0;
|
|
159
|
+
}
|
|
160
|
+
function toNumber(value) {
|
|
161
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
162
|
+
const n = Number(value);
|
|
163
|
+
return Number.isFinite(n) ? n : void 0;
|
|
164
|
+
}
|
|
165
|
+
function normalizeSlotId(value) {
|
|
166
|
+
if (typeof value !== "string") return "";
|
|
167
|
+
return value.trim().slice(0, SLOT_ID_MAX_LENGTH).trim();
|
|
168
|
+
}
|
|
169
|
+
function normalizeSlotLocations(slots) {
|
|
170
|
+
const out = [];
|
|
171
|
+
const seen = /* @__PURE__ */ new Set();
|
|
172
|
+
if (!Array.isArray(slots)) return out;
|
|
173
|
+
for (const raw of slots) {
|
|
174
|
+
const id = normalizeSlotId(typeof raw === "string" ? raw : raw?.id);
|
|
175
|
+
if (!id || seen.has(id)) continue;
|
|
176
|
+
seen.add(id);
|
|
177
|
+
const displayName = raw && typeof raw === "object" && typeof raw.displayName === "string" ? raw.displayName.trim() : "";
|
|
178
|
+
const campaignId = raw && typeof raw === "object" ? toCampaignId(raw.campaignId) : void 0;
|
|
179
|
+
const kind = raw && typeof raw === "object" ? normalizeSlotKind(raw.kind) : void 0;
|
|
180
|
+
const location2 = { id };
|
|
181
|
+
if (displayName) location2.displayName = displayName;
|
|
182
|
+
if (campaignId !== void 0) location2.campaignId = campaignId;
|
|
183
|
+
if (kind) location2.kind = kind;
|
|
184
|
+
out.push(location2);
|
|
185
|
+
}
|
|
186
|
+
return out;
|
|
187
|
+
}
|
|
188
|
+
function autoSlotId(kind, ordinal, scope) {
|
|
189
|
+
const tail = `${kind}-${ordinal + 1}`;
|
|
190
|
+
let prefix = normalizeScope(scope);
|
|
191
|
+
const room = SLOT_ID_MAX_LENGTH - tail.length - 1;
|
|
192
|
+
if (prefix.length > room) prefix = prefix.slice(0, Math.max(0, room)).replace(/-+$/, "");
|
|
193
|
+
return `${prefix ? `${prefix}-` : ""}${tail}`;
|
|
194
|
+
}
|
|
195
|
+
function autoSlotDisplayName(kind, ordinal, scopeName) {
|
|
196
|
+
const label = `${kind.charAt(0).toUpperCase()}${kind.slice(1)} ${ordinal + 1}`;
|
|
197
|
+
const name = typeof scopeName === "string" ? scopeName.trim() : "";
|
|
198
|
+
return name ? `${label} \xB7 ${name}` : label;
|
|
199
|
+
}
|
|
200
|
+
function normalizeScope(scope) {
|
|
201
|
+
if (typeof scope !== "string") return "";
|
|
202
|
+
return scope.trim().replace(/\s+/g, "-");
|
|
203
|
+
}
|
|
204
|
+
var SLOT_DECLARATION_MEMORY_CAP = 500;
|
|
205
|
+
function slotDeclarationKey(location2, pageCampaignId) {
|
|
206
|
+
const campaignId = toCampaignId(location2.campaignId) ?? toCampaignId(pageCampaignId);
|
|
207
|
+
const kind = normalizeSlotKind(location2.kind);
|
|
208
|
+
return location2.id + (location2.displayName ? `${location2.displayName}` : "") + (campaignId !== void 0 ? `${campaignId}` : "") + (kind !== void 0 ? `${kind}` : "");
|
|
209
|
+
}
|
|
210
|
+
function pendingSlotKeys(memory, keys, now = Date.now(), ttl = SLOT_DECLARATION_TTL_MS) {
|
|
211
|
+
return keys.filter((key) => {
|
|
212
|
+
const at = memory?.[key];
|
|
213
|
+
if (typeof at !== "number" || !Number.isFinite(at)) return true;
|
|
214
|
+
return now - at >= ttl || now < at;
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
function slotKeyId(key) {
|
|
218
|
+
const cut4 = key.search(/[\u001f\u001e\u001d]/);
|
|
219
|
+
return cut4 >= 0 ? key.slice(0, cut4) : key;
|
|
220
|
+
}
|
|
221
|
+
function rememberSlotKeys(memory, keys, at, cap = SLOT_DECLARATION_MEMORY_CAP) {
|
|
222
|
+
const next = { ...memory ?? {} };
|
|
223
|
+
const ids = new Set(keys.map(slotKeyId));
|
|
224
|
+
for (const existing of Object.keys(next)) {
|
|
225
|
+
if (ids.has(slotKeyId(existing)) && !keys.includes(existing)) delete next[existing];
|
|
226
|
+
}
|
|
227
|
+
for (const key of keys) next[key] = at;
|
|
228
|
+
const entries = Object.entries(next);
|
|
229
|
+
if (entries.length <= cap) return next;
|
|
230
|
+
entries.sort((a, b) => b[1] - a[1]);
|
|
231
|
+
return Object.fromEntries(entries.slice(0, cap));
|
|
232
|
+
}
|
|
233
|
+
function slotDeclarationStorageKey(platform, apiKey) {
|
|
234
|
+
return `vio.slots.${platform}.${fnv1a(apiKey)}`;
|
|
235
|
+
}
|
|
236
|
+
function fnv1a(input) {
|
|
237
|
+
let hash = 2166136261;
|
|
238
|
+
for (let i = 0; i < input.length; i++) {
|
|
239
|
+
hash ^= input.charCodeAt(i);
|
|
240
|
+
hash = Math.imul(hash, 16777619) >>> 0;
|
|
241
|
+
}
|
|
242
|
+
return hash.toString(16).padStart(8, "0");
|
|
243
|
+
}
|
|
244
|
+
function readSlotDeclaration(storageKey) {
|
|
245
|
+
try {
|
|
246
|
+
if (typeof localStorage === "undefined") return {};
|
|
247
|
+
const raw = localStorage.getItem(storageKey);
|
|
248
|
+
if (!raw) return {};
|
|
249
|
+
const parsed = JSON.parse(raw);
|
|
250
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
251
|
+
if (Array.isArray(parsed.keys)) return {};
|
|
252
|
+
const out = {};
|
|
253
|
+
for (const [key, at] of Object.entries(parsed)) {
|
|
254
|
+
if (typeof at === "number" && Number.isFinite(at)) out[key] = at;
|
|
255
|
+
}
|
|
256
|
+
return out;
|
|
257
|
+
} catch {
|
|
258
|
+
return {};
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function writeSlotDeclaration(storageKey, memory) {
|
|
262
|
+
try {
|
|
263
|
+
if (typeof localStorage === "undefined") return;
|
|
264
|
+
localStorage.setItem(storageKey, JSON.stringify(memory));
|
|
265
|
+
} catch {
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
14
269
|
// src/core/configuration.ts
|
|
15
270
|
var DEFAULT_URLS = {
|
|
16
271
|
// The Vio Backend development environment was removed on 2026-09-16; its REST
|
|
@@ -49,6 +304,10 @@ var ConfigurationSingleton = class {
|
|
|
49
304
|
platform: input.platform ?? "web",
|
|
50
305
|
contentId: input.contentId,
|
|
51
306
|
contentTitle: input.contentTitle,
|
|
307
|
+
campaignId: toCampaignId(input.campaignId),
|
|
308
|
+
debug: input.debug === true,
|
|
309
|
+
// Only the two words the backend knows; anything else is "not said".
|
|
310
|
+
privacyMode: input.privacyMode === "minimal" || input.privacyMode === "standard" ? input.privacyMode : void 0,
|
|
52
311
|
apiBase: input.apiBase ?? defaults.apiBase,
|
|
53
312
|
graphQLBase: input.graphQLBase ?? defaults.graphQLBase,
|
|
54
313
|
eventsBase: input.eventsBase ?? defaults.eventsBase,
|
|
@@ -138,6 +397,14 @@ var VioApi = class {
|
|
|
138
397
|
async declareManifest(body) {
|
|
139
398
|
return this.post("/v2/mobile/components/manifest", body);
|
|
140
399
|
}
|
|
400
|
+
/**
|
|
401
|
+
* The surface of the key (`GET /v2/mobile/surface`): its id, name, status
|
|
402
|
+
* and `privacyMode` — what decides whether the order attribution may carry
|
|
403
|
+
* the entry context (ADR-0035). A 404 means a service that predates it.
|
|
404
|
+
*/
|
|
405
|
+
async surface() {
|
|
406
|
+
return this.get("/v2/mobile/surface");
|
|
407
|
+
}
|
|
141
408
|
/**
|
|
142
409
|
* Get placement components for a campaign. Used by the carousel auto-fetch
|
|
143
410
|
* mode (`<vio-product-carousel campaign-id="36" location="news-favorites">`).
|
|
@@ -457,6 +724,214 @@ function createCommerceClient(options) {
|
|
|
457
724
|
return new CommerceClient(options);
|
|
458
725
|
}
|
|
459
726
|
|
|
727
|
+
// src/core/content.ts
|
|
728
|
+
var CONTENT_ID_QUERY = "vio_content";
|
|
729
|
+
var CONTENT_TITLE_QUERY = "vio_content_title";
|
|
730
|
+
var CONTENT_ID_MAX_LENGTH = 128;
|
|
731
|
+
var CONTENT_TITLE_MAX_LENGTH = 255;
|
|
732
|
+
var CONTENT_URL_MAX_LENGTH = 2048;
|
|
733
|
+
function originAndPath(raw, max = CONTENT_URL_MAX_LENGTH) {
|
|
734
|
+
const value = text(raw);
|
|
735
|
+
if (!value) return void 0;
|
|
736
|
+
try {
|
|
737
|
+
const url = new URL(value);
|
|
738
|
+
return cutTo(`${url.origin}${url.pathname}`, max);
|
|
739
|
+
} catch {
|
|
740
|
+
const end = value.search(/[?#]/);
|
|
741
|
+
return cutTo(end >= 0 ? value.slice(0, end) : value, max);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
function cutTo(value, max) {
|
|
745
|
+
const out = value.length > max ? value.slice(0, max) : value;
|
|
746
|
+
return out || void 0;
|
|
747
|
+
}
|
|
748
|
+
function capContentUrl(value) {
|
|
749
|
+
const url = text(value);
|
|
750
|
+
if (!url) return void 0;
|
|
751
|
+
if (url.length <= CONTENT_URL_MAX_LENGTH) return url;
|
|
752
|
+
const cut4 = url.search(/[?#]/);
|
|
753
|
+
const withoutQuery = cut4 >= 0 ? url.slice(0, cut4) : url;
|
|
754
|
+
return withoutQuery.length <= CONTENT_URL_MAX_LENGTH ? withoutQuery : withoutQuery.slice(0, CONTENT_URL_MAX_LENGTH);
|
|
755
|
+
}
|
|
756
|
+
function resolveContent(init, doc = typeof document !== "undefined" ? document : null, loc = typeof location !== "undefined" ? location : null) {
|
|
757
|
+
const out = {};
|
|
758
|
+
try {
|
|
759
|
+
const url = originAndPath(loc?.href);
|
|
760
|
+
if (url !== void 0) out.url = url;
|
|
761
|
+
} catch {
|
|
762
|
+
}
|
|
763
|
+
try {
|
|
764
|
+
const query = parseQuery(loc?.search);
|
|
765
|
+
const id = fits(text(init?.contentId)) ?? fits(text(query.get(CONTENT_ID_QUERY))) ?? fits(text(meta(doc, "name", "vio:content-id"))) ?? fits(stripQueryAndHash(text(attr(doc, 'link[rel="canonical"]', "href")))) ?? fits(stripQueryAndHash(text(meta(doc, "property", "og:url")))) ?? fits(text(loc?.pathname));
|
|
766
|
+
const title = text(init?.contentTitle) ?? text(query.get(CONTENT_TITLE_QUERY)) ?? text(meta(doc, "name", "vio:content-title")) ?? text(meta(doc, "property", "og:title")) ?? text(doc?.title);
|
|
767
|
+
if (id !== void 0) out.id = id;
|
|
768
|
+
if (title !== void 0) out.title = title.slice(0, CONTENT_TITLE_MAX_LENGTH);
|
|
769
|
+
return out;
|
|
770
|
+
} catch {
|
|
771
|
+
return out;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
function fits(value) {
|
|
775
|
+
return value !== void 0 && value.length <= CONTENT_ID_MAX_LENGTH ? value : void 0;
|
|
776
|
+
}
|
|
777
|
+
function text(value) {
|
|
778
|
+
if (value === void 0 || value === null) return void 0;
|
|
779
|
+
const s = String(value).trim();
|
|
780
|
+
return s ? s : void 0;
|
|
781
|
+
}
|
|
782
|
+
function parseQuery(search) {
|
|
783
|
+
try {
|
|
784
|
+
return new URLSearchParams(typeof search === "string" ? search : "");
|
|
785
|
+
} catch {
|
|
786
|
+
return new URLSearchParams();
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
function meta(doc, key, value) {
|
|
790
|
+
return attr(doc, `meta[${key}="${value}"]`, "content");
|
|
791
|
+
}
|
|
792
|
+
function attr(doc, selector, name) {
|
|
793
|
+
try {
|
|
794
|
+
if (!doc || typeof doc.querySelector !== "function") return void 0;
|
|
795
|
+
const el = doc.querySelector(selector);
|
|
796
|
+
const v = el?.getAttribute?.(name);
|
|
797
|
+
return typeof v === "string" ? v : void 0;
|
|
798
|
+
} catch {
|
|
799
|
+
return void 0;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
function stripQueryAndHash(url) {
|
|
803
|
+
if (!url) return void 0;
|
|
804
|
+
const cut4 = url.search(/[?#]/);
|
|
805
|
+
const clean = cut4 >= 0 ? url.slice(0, cut4) : url;
|
|
806
|
+
return clean.trim() || void 0;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// src/core/cart/origin-wire.ts
|
|
810
|
+
var GRAPHQL_INT_MAX = 2147483647;
|
|
811
|
+
var LINE_ORIGIN_CAPS = Object.freeze({
|
|
812
|
+
locationId: SLOT_ID_MAX_LENGTH,
|
|
813
|
+
componentKind: SLOT_KIND_MAX_LENGTH,
|
|
814
|
+
contentUrl: 2048,
|
|
815
|
+
contentTitle: CONTENT_TITLE_MAX_LENGTH
|
|
816
|
+
});
|
|
817
|
+
function lineOriginWire(origin, options = {}) {
|
|
818
|
+
if (!origin || typeof origin !== "object") return void 0;
|
|
819
|
+
const page = options.page ?? {};
|
|
820
|
+
const standard = options.privacyMode === "standard";
|
|
821
|
+
const out = {};
|
|
822
|
+
const campaignId = Number(origin.campaignId);
|
|
823
|
+
if (Number.isInteger(campaignId) && campaignId > 0 && campaignId <= GRAPHQL_INT_MAX) out.campaign_id = campaignId;
|
|
824
|
+
const locationId = cut(origin.slot, LINE_ORIGIN_CAPS.locationId);
|
|
825
|
+
if (locationId && standard) out.location_id = locationId;
|
|
826
|
+
const kind = cut(origin.componentKind, LINE_ORIGIN_CAPS.componentKind) ?? componentKindOf(void 0, origin.slot);
|
|
827
|
+
if (kind) out.component_kind = kind;
|
|
828
|
+
if (!out.campaign_id && !out.location_id && !out.component_kind) return void 0;
|
|
829
|
+
if (!standard) return out;
|
|
830
|
+
const url = originAndPath(origin.contentUrl, LINE_ORIGIN_CAPS.contentUrl) ?? originAndPath(page.contentUrl, LINE_ORIGIN_CAPS.contentUrl);
|
|
831
|
+
if (url) out.content_url = url;
|
|
832
|
+
const title = cut(origin.contentTitle, LINE_ORIGIN_CAPS.contentTitle) ?? cut(page.contentTitle, LINE_ORIGIN_CAPS.contentTitle);
|
|
833
|
+
if (title) out.content_title = title;
|
|
834
|
+
return out;
|
|
835
|
+
}
|
|
836
|
+
function currentOriginPage() {
|
|
837
|
+
const out = {};
|
|
838
|
+
try {
|
|
839
|
+
const facade = globalThis.__VIO_FACADE__;
|
|
840
|
+
const content = typeof facade?.content === "function" ? facade.content() : resolveContent();
|
|
841
|
+
const url = originAndPath(content?.url, LINE_ORIGIN_CAPS.contentUrl);
|
|
842
|
+
if (url) out.contentUrl = url;
|
|
843
|
+
const title = cut(content?.title, LINE_ORIGIN_CAPS.contentTitle);
|
|
844
|
+
if (title) out.contentTitle = title;
|
|
845
|
+
} catch {
|
|
846
|
+
}
|
|
847
|
+
return out;
|
|
848
|
+
}
|
|
849
|
+
function cut(value, max) {
|
|
850
|
+
if (typeof value !== "string" && typeof value !== "number") return void 0;
|
|
851
|
+
const text3 = String(value).trim();
|
|
852
|
+
if (!text3) return void 0;
|
|
853
|
+
return text3.length > max ? text3.slice(0, max).trim() || void 0 : text3;
|
|
854
|
+
}
|
|
855
|
+
async function currentPrivacyMode() {
|
|
856
|
+
try {
|
|
857
|
+
const facade = globalThis.__VIO_FACADE__;
|
|
858
|
+
if (typeof facade?.privacyMode !== "function") return "unknown";
|
|
859
|
+
const mode = await facade.privacyMode();
|
|
860
|
+
return mode === "standard" || mode === "minimal" ? mode : "unknown";
|
|
861
|
+
} catch {
|
|
862
|
+
return "unknown";
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// src/core/buyer-ref.ts
|
|
867
|
+
var BUYER_REF_MAX_LENGTH = 128;
|
|
868
|
+
var BUYER_REF_PATTERN = /^[A-Za-z0-9._~-]+$/;
|
|
869
|
+
function normalizeBuyerRef(value) {
|
|
870
|
+
if (typeof value !== "string") return void 0;
|
|
871
|
+
const ref = value.trim();
|
|
872
|
+
if (!ref || ref.length > BUYER_REF_MAX_LENGTH || !BUYER_REF_PATTERN.test(ref)) return void 0;
|
|
873
|
+
return ref;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
// src/core/api/gateway-features.ts
|
|
877
|
+
var FEATURE_TOKENS = {
|
|
878
|
+
buyer_ref: ["buyer_ref"],
|
|
879
|
+
line_origin: ["origin", "CartLineOriginInput"],
|
|
880
|
+
checkout_attribution: ["attribution", "CheckoutAttributionInput", "CheckoutAttributionEntryInput"]
|
|
881
|
+
};
|
|
882
|
+
var UNKNOWN_PATTERN = /cannot query field|unknown field|unknown argument|unknown type|is not defined by type|not defined by type|field "[^"]+" is not defined/i;
|
|
883
|
+
var refused = /* @__PURE__ */ new Set();
|
|
884
|
+
var warned = /* @__PURE__ */ new Set();
|
|
885
|
+
function gatewayHas(feature) {
|
|
886
|
+
return !refused.has(feature);
|
|
887
|
+
}
|
|
888
|
+
function resetGatewayFeatures(feature) {
|
|
889
|
+
if (feature) {
|
|
890
|
+
refused.delete(feature);
|
|
891
|
+
warned.delete(feature);
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
refused.clear();
|
|
895
|
+
warned.clear();
|
|
896
|
+
}
|
|
897
|
+
function messagesOf(err) {
|
|
898
|
+
if (!err || typeof err !== "object") return [];
|
|
899
|
+
const { message, graphql } = err;
|
|
900
|
+
return [message, ...(graphql?.errors ?? []).map((e) => e?.message)].filter(
|
|
901
|
+
(m) => typeof m === "string"
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
function ranSomething(err) {
|
|
905
|
+
if (!err || typeof err !== "object") return false;
|
|
906
|
+
const graphql = err.graphql;
|
|
907
|
+
if (graphql?.hasData === true) return true;
|
|
908
|
+
return (graphql?.errors ?? []).some((e) => Array.isArray(e?.path) && e.path.length > 0);
|
|
909
|
+
}
|
|
910
|
+
function isUnknownTokenError(err, token) {
|
|
911
|
+
if (ranSomething(err)) return false;
|
|
912
|
+
return messagesOf(err).some((m) => m.includes(`"${token}"`) && UNKNOWN_PATTERN.test(m));
|
|
913
|
+
}
|
|
914
|
+
function isUnknownFeatureError(err, feature) {
|
|
915
|
+
return FEATURE_TOKENS[feature].some((token) => isUnknownTokenError(err, token));
|
|
916
|
+
}
|
|
917
|
+
async function withGatewayFeature(feature, request) {
|
|
918
|
+
if (!gatewayHas(feature)) return request(false);
|
|
919
|
+
try {
|
|
920
|
+
return await request(true);
|
|
921
|
+
} catch (err) {
|
|
922
|
+
if (!isUnknownFeatureError(err, feature)) throw err;
|
|
923
|
+
refused.add(feature);
|
|
924
|
+
if (!warned.has(feature) && typeof console !== "undefined") {
|
|
925
|
+
warned.add(feature);
|
|
926
|
+
console.warn(
|
|
927
|
+
`[Vio] the commerce gateway does not know "${FEATURE_TOKENS[feature][0]}" (${feature}) yet \u2014 sent without it on this page load:`,
|
|
928
|
+
messagesOf(err)[0] ?? ""
|
|
929
|
+
);
|
|
930
|
+
}
|
|
931
|
+
return request(false);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
460
935
|
// src/core/api/cart-queries.ts
|
|
461
936
|
var CREATE_CART_MUTATION = `
|
|
462
937
|
mutation CreateCart($customerSessionId: String!, $currency: String!, $shippingCountry: String) {
|
|
@@ -674,10 +1149,10 @@ mutation DeleteItem($cartId: String!, $cartItemId: String!) {
|
|
|
674
1149
|
}
|
|
675
1150
|
}
|
|
676
1151
|
`;
|
|
677
|
-
var
|
|
678
|
-
mutation UpdateItem($cartId: String!, $cartItemId: String!, $shippingId: String, $qty: Int) {
|
|
1152
|
+
var updateItemMutation = (withExtra) => `
|
|
1153
|
+
mutation UpdateItem($cartId: String!, $cartItemId: String!, $shippingId: String, $qty: Int${withExtra ? ", $origin: CartLineOriginInput" : ""}) {
|
|
679
1154
|
Cart {
|
|
680
|
-
UpdateItem(cart_id: $cartId, cart_item_id: $cartItemId, shipping_id: $shippingId, qty: $qty) {
|
|
1155
|
+
UpdateItem(cart_id: $cartId, cart_item_id: $cartItemId, shipping_id: $shippingId, qty: $qty${withExtra ? ", origin: $origin" : ""}) {
|
|
681
1156
|
cart_id
|
|
682
1157
|
customer_session_id
|
|
683
1158
|
shipping_country
|
|
@@ -746,6 +1221,8 @@ mutation UpdateItem($cartId: String!, $cartItemId: String!, $shippingId: String,
|
|
|
746
1221
|
}
|
|
747
1222
|
}
|
|
748
1223
|
`;
|
|
1224
|
+
var UPDATE_ITEM_MUTATION = updateItemMutation(false);
|
|
1225
|
+
var UPDATE_ITEM_WITH_ORIGIN_MUTATION = updateItemMutation(true);
|
|
749
1226
|
var GET_CART_QUERY = `
|
|
750
1227
|
query GetCart($cartId: String!) {
|
|
751
1228
|
Cart {
|
|
@@ -959,10 +1436,10 @@ mutation UpdateShippingsBySupplier($cartId: String!, $data: [DataUpdateShippings
|
|
|
959
1436
|
}
|
|
960
1437
|
}
|
|
961
1438
|
`;
|
|
962
|
-
var
|
|
963
|
-
mutation CreateCheckout($cartId: String
|
|
1439
|
+
var createCheckoutMutation = (withExtra) => `
|
|
1440
|
+
mutation CreateCheckout($cartId: String!${withExtra ? ", $attribution: CheckoutAttributionInput" : ""}) {
|
|
964
1441
|
Checkout {
|
|
965
|
-
CreateCheckout(cart_id: $cartId) {
|
|
1442
|
+
CreateCheckout(cart_id: $cartId${withExtra ? ", attribution: $attribution" : ""}) {
|
|
966
1443
|
buyer_accepts_purchase_conditions
|
|
967
1444
|
buyer_accepts_terms_conditions
|
|
968
1445
|
created_at
|
|
@@ -1091,10 +1568,12 @@ mutation CreateCheckout($cartId: String!) {
|
|
|
1091
1568
|
}
|
|
1092
1569
|
}
|
|
1093
1570
|
`;
|
|
1094
|
-
var
|
|
1095
|
-
|
|
1571
|
+
var CREATE_CHECKOUT_MUTATION = createCheckoutMutation(false);
|
|
1572
|
+
var CREATE_CHECKOUT_WITH_ATTRIBUTION_MUTATION = createCheckoutMutation(true);
|
|
1573
|
+
var updateCheckoutMutation = (withExtra) => `
|
|
1574
|
+
mutation UpdateCheckout($checkoutId: String!, $status: String, $successUrl: String, $email: String, $cancelUrl: String, $paymentMethod: String, $shippingAddress: AddressArgs, $billingAddress: AddressArgs, $buyerAcceptsPurchaseConditions: Boolean, $buyerAcceptsTermsConditions: Boolean${withExtra ? ", $attribution: CheckoutAttributionInput" : ""}) {
|
|
1096
1575
|
Checkout {
|
|
1097
|
-
UpdateCheckout(checkout_id: $checkoutId, status: $status, success_url: $successUrl, email: $email, cancel_url: $cancelUrl, payment_method: $paymentMethod, shipping_address: $shippingAddress, billing_address: $billingAddress, buyer_accepts_purchase_conditions: $buyerAcceptsPurchaseConditions, buyer_accepts_terms_conditions: $buyerAcceptsTermsConditions) {
|
|
1576
|
+
UpdateCheckout(checkout_id: $checkoutId, status: $status, success_url: $successUrl, email: $email, cancel_url: $cancelUrl, payment_method: $paymentMethod, shipping_address: $shippingAddress, billing_address: $billingAddress, buyer_accepts_purchase_conditions: $buyerAcceptsPurchaseConditions, buyer_accepts_terms_conditions: $buyerAcceptsTermsConditions${withExtra ? ", attribution: $attribution" : ""}) {
|
|
1098
1577
|
buyer_accepts_purchase_conditions
|
|
1099
1578
|
buyer_accepts_terms_conditions
|
|
1100
1579
|
created_at
|
|
@@ -1223,6 +1702,8 @@ mutation UpdateCheckout($checkoutId: String!, $status: String, $successUrl: Stri
|
|
|
1223
1702
|
}
|
|
1224
1703
|
}
|
|
1225
1704
|
`;
|
|
1705
|
+
var UPDATE_CHECKOUT_MUTATION = updateCheckoutMutation(false);
|
|
1706
|
+
var UPDATE_CHECKOUT_WITH_ATTRIBUTION_MUTATION = updateCheckoutMutation(true);
|
|
1226
1707
|
var GET_CHECKOUT_QUERY = `
|
|
1227
1708
|
query GetCheckout($checkoutId: String!) {
|
|
1228
1709
|
Checkout {
|
|
@@ -1445,7 +1926,9 @@ async function executeCartGraphQL(query, variables, options = {}) {
|
|
|
1445
1926
|
const json = await Promise.race([res.json().catch(() => ({})), timedOut]);
|
|
1446
1927
|
if (!res.ok || json.errors) {
|
|
1447
1928
|
const errMsg = json.errors?.[0]?.message || `GraphQL request failed (HTTP ${res.status})`;
|
|
1448
|
-
throw new Error(errMsg)
|
|
1929
|
+
throw Object.assign(new Error(errMsg), {
|
|
1930
|
+
graphql: { hasData: json.data !== void 0 && json.data !== null, errors: json.errors ?? [] }
|
|
1931
|
+
});
|
|
1449
1932
|
}
|
|
1450
1933
|
return json;
|
|
1451
1934
|
} finally {
|
|
@@ -1465,19 +1948,37 @@ async function createCart(customerSessionId, currency, shippingCountry, options)
|
|
|
1465
1948
|
return json?.data?.Cart?.CreateCart;
|
|
1466
1949
|
}
|
|
1467
1950
|
async function addItem(cartId, lineItems, options) {
|
|
1468
|
-
const
|
|
1951
|
+
const attributed = lineItems.some((li) => !!li?.origin);
|
|
1952
|
+
const json = attributed ? await withGatewayFeature(
|
|
1953
|
+
"line_origin",
|
|
1954
|
+
(supported) => executeCartGraphQL(
|
|
1955
|
+
ADD_ITEM_MUTATION,
|
|
1956
|
+
{ cartId, lineItems: supported ? lineItems : lineItems.map(withoutOrigin) },
|
|
1957
|
+
options
|
|
1958
|
+
)
|
|
1959
|
+
) : await executeCartGraphQL(ADD_ITEM_MUTATION, { cartId, lineItems }, options);
|
|
1469
1960
|
return json?.data?.Cart?.AddItem;
|
|
1470
1961
|
}
|
|
1962
|
+
function withoutOrigin(lineItem) {
|
|
1963
|
+
if (!lineItem || typeof lineItem !== "object") return lineItem;
|
|
1964
|
+
const { origin: _origin, ...rest } = lineItem;
|
|
1965
|
+
return rest;
|
|
1966
|
+
}
|
|
1471
1967
|
async function deleteItem(cartId, cartItemId, options) {
|
|
1472
1968
|
const json = await executeCartGraphQL(DELETE_ITEM_MUTATION, { cartId, cartItemId }, options);
|
|
1473
1969
|
return json?.data?.Cart?.DeleteItem;
|
|
1474
1970
|
}
|
|
1475
|
-
async function updateItem(cartId, cartItemId, shippingId, qty, options) {
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1971
|
+
async function updateItem(cartId, cartItemId, shippingId, qty, options, extra = {}) {
|
|
1972
|
+
const variables = { cartId, cartItemId, shippingId: shippingId || null, qty: qty != null ? qty : null };
|
|
1973
|
+
const origin = extra.origin;
|
|
1974
|
+
const json = origin ? await withGatewayFeature(
|
|
1975
|
+
"line_origin",
|
|
1976
|
+
(supported) => executeCartGraphQL(
|
|
1977
|
+
supported ? UPDATE_ITEM_WITH_ORIGIN_MUTATION : UPDATE_ITEM_MUTATION,
|
|
1978
|
+
supported ? { ...variables, origin } : variables,
|
|
1979
|
+
options
|
|
1980
|
+
)
|
|
1981
|
+
) : await executeCartGraphQL(UPDATE_ITEM_MUTATION, variables, options);
|
|
1481
1982
|
return json?.data?.Cart?.UpdateItem;
|
|
1482
1983
|
}
|
|
1483
1984
|
async function getCart(cartId, options) {
|
|
@@ -1496,32 +1997,68 @@ async function updateShippingsBySupplier(cartId, data, options) {
|
|
|
1496
1997
|
);
|
|
1497
1998
|
return json?.data?.Cart?.UpdateShippingsBySupplier;
|
|
1498
1999
|
}
|
|
1499
|
-
async function createCheckout(cartId, options) {
|
|
1500
|
-
const
|
|
2000
|
+
async function createCheckout(cartId, options, extra = {}) {
|
|
2001
|
+
const attribution = extra.attribution;
|
|
2002
|
+
const json = attribution ? await withGatewayFeature(
|
|
2003
|
+
"checkout_attribution",
|
|
2004
|
+
(supported) => executeCartGraphQL(
|
|
2005
|
+
supported ? CREATE_CHECKOUT_WITH_ATTRIBUTION_MUTATION : CREATE_CHECKOUT_MUTATION,
|
|
2006
|
+
supported ? { cartId, attribution } : { cartId },
|
|
2007
|
+
options
|
|
2008
|
+
)
|
|
2009
|
+
) : await executeCartGraphQL(CREATE_CHECKOUT_MUTATION, { cartId }, options);
|
|
1501
2010
|
return json?.data?.Checkout?.CreateCheckout;
|
|
1502
2011
|
}
|
|
1503
2012
|
async function updateCheckout(variables, options) {
|
|
1504
|
-
const
|
|
2013
|
+
const { attribution, ...legacy } = variables;
|
|
2014
|
+
const json = attribution ? await withGatewayFeature(
|
|
2015
|
+
"checkout_attribution",
|
|
2016
|
+
(supported) => executeCartGraphQL(
|
|
2017
|
+
supported ? UPDATE_CHECKOUT_WITH_ATTRIBUTION_MUTATION : UPDATE_CHECKOUT_MUTATION,
|
|
2018
|
+
supported ? variables : legacy,
|
|
2019
|
+
options
|
|
2020
|
+
)
|
|
2021
|
+
) : await executeCartGraphQL(UPDATE_CHECKOUT_MUTATION, legacy, options);
|
|
1505
2022
|
return json?.data?.Checkout?.UpdateCheckout;
|
|
1506
2023
|
}
|
|
1507
2024
|
async function getCheckout(checkoutId, options) {
|
|
1508
2025
|
const json = await executeCartGraphQL(GET_CHECKOUT_QUERY, { checkoutId }, options);
|
|
1509
2026
|
return json?.data?.Checkout?.GetCheckout;
|
|
1510
2027
|
}
|
|
1511
|
-
var
|
|
2028
|
+
var BUYER_REF_FIELD = "buyer_ref";
|
|
2029
|
+
function gatewayHasBuyerRef() {
|
|
2030
|
+
return gatewayHas("buyer_ref");
|
|
2031
|
+
}
|
|
2032
|
+
function withBuyerRef(request) {
|
|
2033
|
+
return withGatewayFeature("buyer_ref", request);
|
|
2034
|
+
}
|
|
2035
|
+
var orderNumberQuery = (withRef) => `
|
|
1512
2036
|
query GetCheckoutOrderNumber($checkoutId: String!) {
|
|
1513
2037
|
Checkout {
|
|
1514
2038
|
GetCheckout(checkout_id: $checkoutId) {
|
|
1515
2039
|
id
|
|
1516
|
-
order_id
|
|
2040
|
+
order_id${withRef ? `
|
|
2041
|
+
${BUYER_REF_FIELD}` : ""}
|
|
1517
2042
|
}
|
|
1518
2043
|
}
|
|
1519
2044
|
}
|
|
1520
2045
|
`;
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
2046
|
+
var GET_CHECKOUT_ORDER_NUMBER_QUERY = orderNumberQuery(true);
|
|
2047
|
+
var GET_CHECKOUT_ORDER_NUMBER_QUERY_LEGACY = orderNumberQuery(false);
|
|
2048
|
+
async function getCheckoutReference(checkoutId, options) {
|
|
2049
|
+
const json = await withBuyerRef(
|
|
2050
|
+
(withRef) => executeCartGraphQL(
|
|
2051
|
+
withRef ? GET_CHECKOUT_ORDER_NUMBER_QUERY : GET_CHECKOUT_ORDER_NUMBER_QUERY_LEGACY,
|
|
2052
|
+
{ checkoutId },
|
|
2053
|
+
options
|
|
2054
|
+
)
|
|
2055
|
+
);
|
|
2056
|
+
const checkout = json?.data?.Checkout?.GetCheckout;
|
|
2057
|
+
const n = Number(checkout?.order_id);
|
|
2058
|
+
return {
|
|
2059
|
+
orderNumber: Number.isInteger(n) && n > 0 ? n : null,
|
|
2060
|
+
buyerRef: normalizeBuyerRef(checkout?.[BUYER_REF_FIELD]) ?? null
|
|
2061
|
+
};
|
|
1525
2062
|
}
|
|
1526
2063
|
async function getAvailablePaymentMethods(options) {
|
|
1527
2064
|
const json = await executeCartGraphQL(GET_AVAILABLE_PAYMENT_METHODS_QUERY, {}, options);
|
|
@@ -1555,18 +2092,27 @@ async function createPaymentApplePay(variables, options) {
|
|
|
1555
2092
|
const json = await executeCartGraphQL(CREATE_PAYMENT_APPLE_PAY_MUTATION, variables, options);
|
|
1556
2093
|
return json?.data?.Payment?.CreatePaymentApplePay;
|
|
1557
2094
|
}
|
|
1558
|
-
var
|
|
2095
|
+
var confirmApplePayMutation = (withRef) => `
|
|
1559
2096
|
mutation ConfirmPaymentApplePay($checkoutId: String!, $applePayToken: String!, $email: String, $shippingAddress: ApplePayAddressInput) {
|
|
1560
2097
|
Payment {
|
|
1561
2098
|
ConfirmPaymentApplePay(checkout_id: $checkoutId, apple_pay_token: $applePayToken, email: $email, shipping_address: $shippingAddress) {
|
|
1562
2099
|
order_id
|
|
1563
|
-
status
|
|
2100
|
+
status${withRef ? `
|
|
2101
|
+
${BUYER_REF_FIELD}` : ""}
|
|
1564
2102
|
}
|
|
1565
2103
|
}
|
|
1566
2104
|
}
|
|
1567
2105
|
`;
|
|
2106
|
+
var CONFIRM_PAYMENT_APPLE_PAY_MUTATION = confirmApplePayMutation(true);
|
|
2107
|
+
var CONFIRM_PAYMENT_APPLE_PAY_MUTATION_LEGACY = confirmApplePayMutation(false);
|
|
1568
2108
|
async function confirmPaymentApplePay(variables, options) {
|
|
1569
|
-
const json = await
|
|
2109
|
+
const json = await withBuyerRef(
|
|
2110
|
+
(withRef) => executeCartGraphQL(
|
|
2111
|
+
withRef ? CONFIRM_PAYMENT_APPLE_PAY_MUTATION : CONFIRM_PAYMENT_APPLE_PAY_MUTATION_LEGACY,
|
|
2112
|
+
variables,
|
|
2113
|
+
options
|
|
2114
|
+
)
|
|
2115
|
+
);
|
|
1570
2116
|
return json?.data?.Payment?.ConfirmPaymentApplePay;
|
|
1571
2117
|
}
|
|
1572
2118
|
|
|
@@ -1604,6 +2150,33 @@ async function getVippsStatus(variables, options) {
|
|
|
1604
2150
|
|
|
1605
2151
|
// src/core/cart/cart-manager.ts
|
|
1606
2152
|
var STORAGE_KEY = "vio.cart.v1";
|
|
2153
|
+
function finalTarget(sync) {
|
|
2154
|
+
return sync.steps.reduce((quantity, step) => quantity + step.delta, sync.sent);
|
|
2155
|
+
}
|
|
2156
|
+
function queueStep(sync, target, origin) {
|
|
2157
|
+
const delta = target - finalTarget(sync);
|
|
2158
|
+
if (delta === 0) return;
|
|
2159
|
+
if (delta > 0) {
|
|
2160
|
+
const last = sync.steps[sync.steps.length - 1];
|
|
2161
|
+
if (last && last.delta > 0 && sameOrigin(last.origin ?? {}, origin ?? {})) last.delta += delta;
|
|
2162
|
+
else sync.steps.push({ delta, origin });
|
|
2163
|
+
return;
|
|
2164
|
+
}
|
|
2165
|
+
let left = -delta;
|
|
2166
|
+
while (left > 0 && sync.steps.length > 0) {
|
|
2167
|
+
const last = sync.steps[sync.steps.length - 1];
|
|
2168
|
+
if (last.delta <= 0) break;
|
|
2169
|
+
const take = Math.min(last.delta, left);
|
|
2170
|
+
last.delta -= take;
|
|
2171
|
+
left -= take;
|
|
2172
|
+
if (last.delta === 0) sync.steps.pop();
|
|
2173
|
+
}
|
|
2174
|
+
if (left > 0) {
|
|
2175
|
+
const last = sync.steps[sync.steps.length - 1];
|
|
2176
|
+
if (last && last.delta < 0) last.delta -= left;
|
|
2177
|
+
else sync.steps.push({ delta: -left });
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
1607
2180
|
function lineId() {
|
|
1608
2181
|
return "li_" + Math.random().toString(36).slice(2, 10) + Date.now().toString(36);
|
|
1609
2182
|
}
|
|
@@ -1695,7 +2268,7 @@ var CartManager = class extends EventTarget {
|
|
|
1695
2268
|
* undefined when no quantity sync is active for it. */
|
|
1696
2269
|
intendedQuantity(sponsorId, cartItemId) {
|
|
1697
2270
|
const sync = this.qtySyncs.get(this.qtySyncKey(sponsorId, cartItemId));
|
|
1698
|
-
return sync ? sync
|
|
2271
|
+
return sync ? finalTarget(sync) : void 0;
|
|
1699
2272
|
}
|
|
1700
2273
|
/** Apply a backend response only if no newer response was applied already.
|
|
1701
2274
|
* Returns false when it was discarded as stale. */
|
|
@@ -1858,7 +2431,8 @@ var CartManager = class extends EventTarget {
|
|
|
1858
2431
|
currency: li.price?.currency_code || backendCart.currency || matchedLocal?.currency || "NOK",
|
|
1859
2432
|
imageUrl,
|
|
1860
2433
|
quantity: intended ?? li.quantity,
|
|
1861
|
-
// The server
|
|
2434
|
+
// The server keeps its own copy of the origins (per request); the
|
|
2435
|
+
// cart read model does not answer them, so keep the local ones. They
|
|
1862
2436
|
// are squared with its quantity (a stock clamp) only when no OTHER
|
|
1863
2437
|
// mutation is in flight for this sponsor — an AddItem answering
|
|
1864
2438
|
// while another is pending reports a quantity that is already old
|
|
@@ -1973,11 +2547,16 @@ var CartManager = class extends EventTarget {
|
|
|
1973
2547
|
const cartId = await this.ensureCartId(opts.sponsorId, opts.currency || void 0);
|
|
1974
2548
|
if (cartId) {
|
|
1975
2549
|
const gqlOpts = await getCartGraphQLOptions(opts.sponsorId);
|
|
2550
|
+
const origin = opts.origin ? lineOriginWire(normalizeOrigin(opts.origin), {
|
|
2551
|
+
page: currentOriginPage(),
|
|
2552
|
+
privacyMode: await currentPrivacyMode()
|
|
2553
|
+
}) : void 0;
|
|
1976
2554
|
const lineItemsInput = [
|
|
1977
2555
|
{
|
|
1978
2556
|
product_id: Number(opts.productId || 0),
|
|
1979
2557
|
quantity: qty,
|
|
1980
|
-
variant_id: opts.variantId != null ? Number(opts.variantId) : null
|
|
2558
|
+
variant_id: opts.variantId != null ? Number(opts.variantId) : null,
|
|
2559
|
+
...origin ? { origin } : {}
|
|
1981
2560
|
}
|
|
1982
2561
|
];
|
|
1983
2562
|
const resCart = await addItem(cartId, lineItemsInput, gqlOpts);
|
|
@@ -2030,8 +2609,10 @@ var CartManager = class extends EventTarget {
|
|
|
2030
2609
|
if (target === item.quantity) return;
|
|
2031
2610
|
const targetItemId = item.cartItemId;
|
|
2032
2611
|
const removedOrigin = latestOrigin(item);
|
|
2033
|
-
|
|
2612
|
+
const increase = target > item.quantity;
|
|
2613
|
+
if (increase) addOriginQuantity(item, opts.origin, target - item.quantity);
|
|
2034
2614
|
else removeOriginQuantity(item, item.quantity - target);
|
|
2615
|
+
const addedOrigin = increase ? latestOrigin(item) : void 0;
|
|
2035
2616
|
if (target <= 0) {
|
|
2036
2617
|
cart.items = cart.items.filter((i) => i !== item);
|
|
2037
2618
|
if (typeof document !== "undefined") {
|
|
@@ -2066,37 +2647,47 @@ var CartManager = class extends EventTarget {
|
|
|
2066
2647
|
this.pendingQtyIntents.set(this.intentKey(item.productId, item.variantId), target);
|
|
2067
2648
|
return;
|
|
2068
2649
|
}
|
|
2069
|
-
this.syncQuantity(sponsorId, cart.cartId, targetItemId, target);
|
|
2650
|
+
this.syncQuantity(sponsorId, cart.cartId, targetItemId, target, addedOrigin);
|
|
2070
2651
|
}
|
|
2071
2652
|
/** Send a line's target quantity to the backend — ONE request in flight per
|
|
2072
|
-
* line.
|
|
2073
|
-
*
|
|
2074
|
-
*
|
|
2075
|
-
*
|
|
2076
|
-
*
|
|
2077
|
-
*
|
|
2078
|
-
|
|
2653
|
+
* line. Targets that arrive while one is in flight queue behind it as
|
|
2654
|
+
* steps (`queueStep`): reduced like the line's origins, sent in order once
|
|
2655
|
+
* the current request settles — one request per queued increase from a
|
|
2656
|
+
* different origin, one for a net decrease — so the server ends on the
|
|
2657
|
+
* last intent, credits every increase to its own origin, and never sees two
|
|
2658
|
+
* updates racing each other. While the sync is active the line keeps its
|
|
2659
|
+
* optimistic quantity through any backend response (see
|
|
2660
|
+
* `updateFromBackendCart`); a rejection drops what was queued and
|
|
2661
|
+
* reconciles the cart from the server. */
|
|
2662
|
+
syncQuantity(sponsorId, backendCartId, cartItemId, target, origin) {
|
|
2079
2663
|
const key = this.qtySyncKey(sponsorId, cartItemId);
|
|
2080
2664
|
const active = this.qtySyncs.get(key);
|
|
2081
2665
|
if (active) {
|
|
2082
|
-
active
|
|
2666
|
+
queueStep(active, target, origin);
|
|
2083
2667
|
return;
|
|
2084
2668
|
}
|
|
2085
|
-
const sync = { sent: target };
|
|
2669
|
+
const sync = { sent: target, steps: [] };
|
|
2086
2670
|
this.qtySyncs.set(key, sync);
|
|
2087
2671
|
let settling;
|
|
2088
2672
|
const run = (async () => {
|
|
2089
2673
|
let next = target;
|
|
2674
|
+
let nextOrigin = origin;
|
|
2090
2675
|
let removalRetried = false;
|
|
2091
2676
|
while (next !== void 0) {
|
|
2092
|
-
sync.sent = next;
|
|
2093
|
-
sync.pending = void 0;
|
|
2094
2677
|
const seq = this.nextSeq(sponsorId);
|
|
2095
2678
|
try {
|
|
2096
2679
|
const gqlOpts = await getCartGraphQLOptions(sponsorId);
|
|
2097
|
-
const
|
|
2098
|
-
|
|
2099
|
-
|
|
2680
|
+
const originWire = next > 0 && nextOrigin ? lineOriginWire(nextOrigin, { page: currentOriginPage(), privacyMode: await currentPrivacyMode() }) : void 0;
|
|
2681
|
+
const resCart = next <= 0 ? await deleteItem(backendCartId, cartItemId, gqlOpts) : originWire ? await updateItem(backendCartId, cartItemId, null, next, gqlOpts, { origin: originWire }) : await updateItem(backendCartId, cartItemId, null, next, gqlOpts);
|
|
2682
|
+
const step = sync.steps.shift();
|
|
2683
|
+
if (step) {
|
|
2684
|
+
next = sync.sent + step.delta;
|
|
2685
|
+
nextOrigin = step.delta > 0 ? step.origin : void 0;
|
|
2686
|
+
sync.sent = next;
|
|
2687
|
+
} else {
|
|
2688
|
+
next = void 0;
|
|
2689
|
+
nextOrigin = void 0;
|
|
2690
|
+
}
|
|
2100
2691
|
if (next === void 0) this.qtySyncs.delete(key);
|
|
2101
2692
|
if (resCart && Array.isArray(resCart.line_items)) {
|
|
2102
2693
|
const applied = this.applyBackendCart(sponsorId, resCart, seq, settling);
|
|
@@ -2105,12 +2696,13 @@ var CartManager = class extends EventTarget {
|
|
|
2105
2696
|
}
|
|
2106
2697
|
}
|
|
2107
2698
|
} catch (err) {
|
|
2108
|
-
const retryRemoval = !removalRetried && sync.
|
|
2699
|
+
const retryRemoval = !removalRetried && sync.steps.length > 0 && finalTarget(sync) <= 0;
|
|
2700
|
+
sync.steps = [];
|
|
2109
2701
|
next = void 0;
|
|
2702
|
+
nextOrigin = void 0;
|
|
2110
2703
|
if (retryRemoval) {
|
|
2111
2704
|
removalRetried = true;
|
|
2112
2705
|
sync.sent = 0;
|
|
2113
|
-
sync.pending = void 0;
|
|
2114
2706
|
next = 0;
|
|
2115
2707
|
} else {
|
|
2116
2708
|
this.qtySyncs.delete(key);
|
|
@@ -2247,10 +2839,17 @@ function normalizeOrigin(origin) {
|
|
|
2247
2839
|
if (origin.contentId !== void 0 && origin.contentId !== null && String(origin.contentId).trim()) {
|
|
2248
2840
|
out.contentId = String(origin.contentId).trim();
|
|
2249
2841
|
}
|
|
2842
|
+
const kind = componentKindOf(origin.componentKind ?? origin.componentTemplateId);
|
|
2843
|
+
if (kind) out.componentKind = kind;
|
|
2844
|
+
const contentUrl = originAndPath(origin.contentUrl);
|
|
2845
|
+
if (contentUrl) out.contentUrl = contentUrl;
|
|
2846
|
+
if (typeof origin.contentTitle === "string" && origin.contentTitle.trim()) {
|
|
2847
|
+
out.contentTitle = origin.contentTitle.trim().slice(0, CONTENT_TITLE_MAX_LENGTH);
|
|
2848
|
+
}
|
|
2250
2849
|
return out;
|
|
2251
2850
|
}
|
|
2252
2851
|
function sameOrigin(a, b) {
|
|
2253
|
-
return a.campaignId === b.campaignId && a.slot === b.slot && a.contentId === b.contentId;
|
|
2852
|
+
return a.campaignId === b.campaignId && a.slot === b.slot && a.contentId === b.contentId && a.componentKind === b.componentKind;
|
|
2254
2853
|
}
|
|
2255
2854
|
function latestOrigin(line) {
|
|
2256
2855
|
const origins = Array.isArray(line.origins) ? line.origins : [];
|
|
@@ -2514,7 +3113,7 @@ async function prepareApplePay(config) {
|
|
|
2514
3113
|
}
|
|
2515
3114
|
|
|
2516
3115
|
// src/core/version.ts
|
|
2517
|
-
var SDK_VERSION = "0.
|
|
3116
|
+
var SDK_VERSION = "0.25.0";
|
|
2518
3117
|
|
|
2519
3118
|
// src/core/checkout/payments/stripe-embedded.ts
|
|
2520
3119
|
var CREATE_PAYMENT_INTENT_STRIPE_MUTATION = `
|
|
@@ -3822,13 +4421,14 @@ mutation CreatePaymentKlarnaNative($checkoutId: String!, $countryCode: String, $
|
|
|
3822
4421
|
}
|
|
3823
4422
|
}
|
|
3824
4423
|
`;
|
|
3825
|
-
var
|
|
4424
|
+
var confirmPaymentKlarnaNativeMutation = (withRef) => `
|
|
3826
4425
|
mutation ConfirmPaymentKlarnaNative($checkoutId: String!, $authorizationToken: String!, $autoCapture: Boolean, $billingAddress: KlarnaNativeAddressInput, $shippingAddress: KlarnaNativeAddressInput, $customer: KlarnaNativeCustomerInput) {
|
|
3827
4426
|
Payment {
|
|
3828
4427
|
ConfirmPaymentKlarnaNative(checkout_id: $checkoutId, authorization_token: $authorizationToken, auto_capture: $autoCapture, billing_address: $billingAddress, shipping_address: $shippingAddress, customer: $customer) {
|
|
3829
4428
|
order_id
|
|
3830
4429
|
checkout_id
|
|
3831
|
-
fraud_status
|
|
4430
|
+
fraud_status${withRef ? `
|
|
4431
|
+
${BUYER_REF_FIELD}` : ""}
|
|
3832
4432
|
order {
|
|
3833
4433
|
order_id
|
|
3834
4434
|
status
|
|
@@ -4010,9 +4610,10 @@ async function executeKlarnaGraphQL(query, variables, sponsorId) {
|
|
|
4010
4610
|
return executeCartGraphQL(query, variables, await getCartGraphQLOptions(sponsorId));
|
|
4011
4611
|
}
|
|
4012
4612
|
var CheckoutManager = class _CheckoutManager extends EventTarget {
|
|
4013
|
-
constructor(cartManager) {
|
|
4613
|
+
constructor(cartManager, deps = {}) {
|
|
4014
4614
|
super();
|
|
4015
4615
|
this.cartManager = cartManager;
|
|
4616
|
+
this.deps = deps;
|
|
4016
4617
|
this.state = null;
|
|
4017
4618
|
/** Counts `open()` calls — see CheckoutState.session. */
|
|
4018
4619
|
this.sessionSeq = 0;
|
|
@@ -4052,6 +4653,8 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
|
|
|
4052
4653
|
this.paymentMethodsCache = /* @__PURE__ */ new Map();
|
|
4053
4654
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4054
4655
|
this.paymentMethodsPromises = /* @__PURE__ */ new Map();
|
|
4656
|
+
/** In-flight `getOrderReference` per checkout id (see there). */
|
|
4657
|
+
this.referenceInFlight = /* @__PURE__ */ new Map();
|
|
4055
4658
|
this.kustomController = null;
|
|
4056
4659
|
preloadStripeJs();
|
|
4057
4660
|
}
|
|
@@ -4079,7 +4682,20 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
|
|
|
4079
4682
|
}
|
|
4080
4683
|
// MARK: - Backend checkout (Vio Commerce GraphQL)
|
|
4081
4684
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
4082
|
-
/**
|
|
4685
|
+
/**
|
|
4686
|
+
* The attribution of the order this sponsor's cart is about to become
|
|
4687
|
+
* (ADR-0035 phase B), through the facade; `undefined` when there is none
|
|
4688
|
+
* to give or it fails — attribution never costs a checkout.
|
|
4689
|
+
*/
|
|
4690
|
+
async attributionFor(sponsorId) {
|
|
4691
|
+
if (!sponsorId || typeof this.deps.orderAttribution !== "function") return void 0;
|
|
4692
|
+
try {
|
|
4693
|
+
return await this.deps.orderAttribution(sponsorId) ?? void 0;
|
|
4694
|
+
} catch {
|
|
4695
|
+
return void 0;
|
|
4696
|
+
}
|
|
4697
|
+
}
|
|
4698
|
+
/** Create the backend checkout for the sponsor's cart (auto-accepts terms); born attributed when the facade knows the attribution. */
|
|
4083
4699
|
async createCheckout(sponsorId) {
|
|
4084
4700
|
await this.cartManager.waitForCartMutations(sponsorId);
|
|
4085
4701
|
const cart = this.cartManager.getCart(sponsorId);
|
|
@@ -4091,7 +4707,8 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
|
|
|
4091
4707
|
}
|
|
4092
4708
|
}
|
|
4093
4709
|
const opts = await getCartGraphQLOptions(sponsorId);
|
|
4094
|
-
const
|
|
4710
|
+
const attribution = await this.attributionFor(sponsorId);
|
|
4711
|
+
const checkout = attribution ? await createCheckout(cartId, opts, { attribution }) : await createCheckout(cartId, opts);
|
|
4095
4712
|
if (checkout?.id) {
|
|
4096
4713
|
this.state = {
|
|
4097
4714
|
...this.state ?? { sponsorId, subtotal: 0, currency: getGlobalCurrency() },
|
|
@@ -4122,12 +4739,15 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
|
|
|
4122
4739
|
}
|
|
4123
4740
|
return checkout;
|
|
4124
4741
|
}
|
|
4125
|
-
/** Update the backend checkout (terms are re-asserted unless overridden). */
|
|
4742
|
+
/** Update the backend checkout (terms are re-asserted unless overridden; the attribution rides along unless the caller gives its own). */
|
|
4126
4743
|
async updateCheckout(variables, sponsorId) {
|
|
4127
|
-
const
|
|
4744
|
+
const spId = sponsorId ?? this.state?.sponsorId;
|
|
4745
|
+
const opts = await getCartGraphQLOptions(spId);
|
|
4746
|
+
const attribution = "attribution" in variables ? void 0 : await this.attributionFor(spId);
|
|
4128
4747
|
const fullVars = {
|
|
4129
4748
|
buyerAcceptsPurchaseConditions: true,
|
|
4130
4749
|
buyerAcceptsTermsConditions: true,
|
|
4750
|
+
...attribution ? { attribution } : {},
|
|
4131
4751
|
...variables
|
|
4132
4752
|
};
|
|
4133
4753
|
const updated = await updateCheckout(fullVars, opts);
|
|
@@ -4147,13 +4767,30 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
|
|
|
4147
4767
|
* receipt, and a receipt does not fail for it.
|
|
4148
4768
|
*/
|
|
4149
4769
|
async getOrderNumber(checkoutId, sponsorId) {
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4770
|
+
return (await this.getOrderReference(checkoutId, sponsorId)).orderNumber;
|
|
4771
|
+
}
|
|
4772
|
+
/**
|
|
4773
|
+
* What a paid checkout says of itself: the order number (as above) and the
|
|
4774
|
+
* buyer reference — shopcart's keyed hash of the buyer's email, what the
|
|
4775
|
+
* `vio:payment-success` detail carries as `buyerRef` and analytics sends as
|
|
4776
|
+
* `buyer_id`. Never throws; `null` for whatever the API does not say.
|
|
4777
|
+
*/
|
|
4778
|
+
async getOrderReference(checkoutId, sponsorId) {
|
|
4779
|
+
if (!checkoutId) return { orderNumber: null, buyerRef: null };
|
|
4780
|
+
const inFlight = this.referenceInFlight.get(checkoutId);
|
|
4781
|
+
if (inFlight) return inFlight;
|
|
4782
|
+
const request = (async () => {
|
|
4783
|
+
try {
|
|
4784
|
+
const opts = await getCartGraphQLOptions(sponsorId ?? this.state?.sponsorId);
|
|
4785
|
+
return await getCheckoutReference(checkoutId, opts);
|
|
4786
|
+
} catch {
|
|
4787
|
+
return { orderNumber: null, buyerRef: null };
|
|
4788
|
+
} finally {
|
|
4789
|
+
this.referenceInFlight.delete(checkoutId);
|
|
4790
|
+
}
|
|
4791
|
+
})();
|
|
4792
|
+
this.referenceInFlight.set(checkoutId, request);
|
|
4793
|
+
return request;
|
|
4157
4794
|
}
|
|
4158
4795
|
/**
|
|
4159
4796
|
* Payment methods enabled for the sponsor (backend-configured). Memoised per
|
|
@@ -4284,9 +4921,10 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
|
|
|
4284
4921
|
throw new Error(`[CheckoutManager] Failed to obtain cart_id for sponsor ${spId}`);
|
|
4285
4922
|
}
|
|
4286
4923
|
const opts = await getCartGraphQLOptions(spId);
|
|
4924
|
+
const attribution = await this.attributionFor(spId);
|
|
4287
4925
|
let checkoutId = this.state?.checkoutId;
|
|
4288
4926
|
if (!checkoutId) {
|
|
4289
|
-
const checkoutRes = await createCheckout(cartId, opts);
|
|
4927
|
+
const checkoutRes = attribution ? await createCheckout(cartId, opts, { attribution }) : await createCheckout(cartId, opts);
|
|
4290
4928
|
checkoutId = checkoutRes?.id;
|
|
4291
4929
|
}
|
|
4292
4930
|
if (!checkoutId) {
|
|
@@ -4305,6 +4943,7 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
|
|
|
4305
4943
|
...hasEmail ? { email: emailVal } : {},
|
|
4306
4944
|
buyerAcceptsPurchaseConditions: true,
|
|
4307
4945
|
buyerAcceptsTermsConditions: true,
|
|
4946
|
+
...attribution ? { attribution } : {},
|
|
4308
4947
|
...extraUpdateVars
|
|
4309
4948
|
};
|
|
4310
4949
|
if (addressObj) {
|
|
@@ -5300,7 +5939,8 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
|
|
|
5300
5939
|
`[VioCheckout] Apple Pay confirmation failed (status: ${confirmRes?.status ?? "none"})`
|
|
5301
5940
|
);
|
|
5302
5941
|
}
|
|
5303
|
-
|
|
5942
|
+
const buyerRef = normalizeBuyerRef(confirmRes?.[BUYER_REF_FIELD]);
|
|
5943
|
+
return { orderId: confirmRes?.order_id, status: confirmRes?.status, ...buyerRef ? { buyerRef } : {} };
|
|
5304
5944
|
};
|
|
5305
5945
|
}
|
|
5306
5946
|
static {
|
|
@@ -5803,16 +6443,19 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
|
|
|
5803
6443
|
shippingAddress: buildKlarnaAddress(address),
|
|
5804
6444
|
customer: buildKlarnaCustomer(address)
|
|
5805
6445
|
};
|
|
5806
|
-
const json = await
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
6446
|
+
const json = await withBuyerRef(
|
|
6447
|
+
(withRef) => executeKlarnaGraphQL(
|
|
6448
|
+
confirmPaymentKlarnaNativeMutation(withRef),
|
|
6449
|
+
variables,
|
|
6450
|
+
ctx?.sponsorId ?? this.state?.sponsorId
|
|
6451
|
+
)
|
|
5810
6452
|
);
|
|
5811
6453
|
const confirmRes = json?.data?.Payment?.ConfirmPaymentKlarnaNative;
|
|
5812
6454
|
if (!confirmRes) {
|
|
5813
6455
|
throw new Error("Klarna order confirmation failed: missing response data");
|
|
5814
6456
|
}
|
|
5815
6457
|
const orderData = confirmRes.order || confirmRes;
|
|
6458
|
+
const buyerRef = normalizeBuyerRef(confirmRes[BUYER_REF_FIELD]);
|
|
5816
6459
|
if (typeof sessionStorage !== "undefined") {
|
|
5817
6460
|
sessionStorage.setItem("vio.klarna.confirmed.v1", authorizationToken);
|
|
5818
6461
|
sessionStorage.removeItem(KLARNA_PENDING_KEY);
|
|
@@ -5830,7 +6473,8 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
|
|
|
5830
6473
|
result: {
|
|
5831
6474
|
...result,
|
|
5832
6475
|
order: orderData,
|
|
5833
|
-
chargedTotal: typeof orderData?.order_amount === "number" ? orderData.order_amount / 100 : ctx.orderAmount / 100
|
|
6476
|
+
chargedTotal: typeof orderData?.order_amount === "number" ? orderData.order_amount / 100 : ctx.orderAmount / 100,
|
|
6477
|
+
...buyerRef ? { buyerRef } : {}
|
|
5834
6478
|
}
|
|
5835
6479
|
}
|
|
5836
6480
|
})
|
|
@@ -5883,220 +6527,177 @@ var CheckoutManager = class _CheckoutManager extends EventTarget {
|
|
|
5883
6527
|
}
|
|
5884
6528
|
};
|
|
5885
6529
|
function toSmallestUnit(amount, currency) {
|
|
5886
|
-
const zeroDecimal = /* @__PURE__ */ new Set(["JPY", "KRW", "VND"]);
|
|
5887
|
-
if (zeroDecimal.has(currency.toUpperCase())) return Math.round(amount);
|
|
5888
|
-
return Math.round(amount * 100);
|
|
5889
|
-
}
|
|
5890
|
-
|
|
5891
|
-
// src/core/
|
|
5892
|
-
var
|
|
5893
|
-
var
|
|
5894
|
-
var
|
|
5895
|
-
var
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
const out = {};
|
|
5902
|
-
if (id !== void 0) out.id = id;
|
|
5903
|
-
if (title !== void 0) out.title = title.slice(0, CONTENT_TITLE_MAX_LENGTH);
|
|
5904
|
-
return out;
|
|
5905
|
-
} catch {
|
|
5906
|
-
return {};
|
|
5907
|
-
}
|
|
5908
|
-
}
|
|
5909
|
-
function fits(value) {
|
|
5910
|
-
return value !== void 0 && value.length <= CONTENT_ID_MAX_LENGTH ? value : void 0;
|
|
5911
|
-
}
|
|
5912
|
-
function text(value) {
|
|
5913
|
-
if (value === void 0 || value === null) return void 0;
|
|
5914
|
-
const s = String(value).trim();
|
|
5915
|
-
return s ? s : void 0;
|
|
5916
|
-
}
|
|
5917
|
-
function parseQuery(search) {
|
|
5918
|
-
try {
|
|
5919
|
-
return new URLSearchParams(typeof search === "string" ? search : "");
|
|
5920
|
-
} catch {
|
|
5921
|
-
return new URLSearchParams();
|
|
5922
|
-
}
|
|
5923
|
-
}
|
|
5924
|
-
function meta(doc, key, value) {
|
|
5925
|
-
return attr(doc, `meta[${key}="${value}"]`, "content");
|
|
5926
|
-
}
|
|
5927
|
-
function attr(doc, selector, name) {
|
|
5928
|
-
try {
|
|
5929
|
-
if (!doc || typeof doc.querySelector !== "function") return void 0;
|
|
5930
|
-
const el = doc.querySelector(selector);
|
|
5931
|
-
const v = el?.getAttribute?.(name);
|
|
5932
|
-
return typeof v === "string" ? v : void 0;
|
|
5933
|
-
} catch {
|
|
5934
|
-
return void 0;
|
|
5935
|
-
}
|
|
5936
|
-
}
|
|
5937
|
-
function stripQueryAndHash(url) {
|
|
5938
|
-
if (!url) return void 0;
|
|
5939
|
-
const cut = url.search(/[?#]/);
|
|
5940
|
-
const clean = cut >= 0 ? url.slice(0, cut) : url;
|
|
5941
|
-
return clean.trim() || void 0;
|
|
5942
|
-
}
|
|
5943
|
-
|
|
5944
|
-
// src/core/slots.ts
|
|
5945
|
-
var SLOT_ID_MAX_LENGTH = 100;
|
|
5946
|
-
var SLOT_DECLARATION_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
5947
|
-
var SLOT_DECLARATION_FAILURE_TTL_MS = 60 * 60 * 1e3;
|
|
5948
|
-
var MAX_LOCATIONS_PER_DECLARATION = 100;
|
|
5949
|
-
var MAX_DECLARATIONS_PER_LOAD = 3;
|
|
5950
|
-
function resolveSlotCampaign(boot, slot, now = Date.now(), platform) {
|
|
5951
|
-
if (!boot) return null;
|
|
5952
|
-
return findSlotAssignment(boot, slot, now, platform) ?? defaultCampaignEntry(boot);
|
|
5953
|
-
}
|
|
5954
|
-
function findSlotAssignment(boot, slot, now = Date.now(), platform) {
|
|
5955
|
-
const name = typeof slot === "string" ? slot.trim() : "";
|
|
5956
|
-
if (!boot || !name) return null;
|
|
5957
|
-
try {
|
|
5958
|
-
const matches = activeCampaigns(boot).filter(
|
|
5959
|
-
(c) => slotsOf(c).some((s) => assignmentMatches(s, name, now, platform))
|
|
5960
|
-
);
|
|
5961
|
-
if (matches.length === 0) return null;
|
|
5962
|
-
const defaultId = toNumber(boot.campaign?.id);
|
|
5963
|
-
return matches.find((c) => toNumber(c.id) === defaultId) ?? matches[0] ?? null;
|
|
5964
|
-
} catch {
|
|
5965
|
-
return null;
|
|
5966
|
-
}
|
|
5967
|
-
}
|
|
5968
|
-
function defaultCampaignEntry(boot) {
|
|
5969
|
-
if (!boot?.campaign || typeof boot.campaign !== "object") return null;
|
|
5970
|
-
const defaultId = toNumber(boot.campaign.id);
|
|
5971
|
-
if (defaultId === void 0) return null;
|
|
5972
|
-
try {
|
|
5973
|
-
const listed = wellFormedCampaigns(boot).find((c) => toNumber(c.id) === defaultId);
|
|
5974
|
-
if (listed) return listed;
|
|
5975
|
-
} catch {
|
|
5976
|
-
}
|
|
6530
|
+
const zeroDecimal = /* @__PURE__ */ new Set(["JPY", "KRW", "VND"]);
|
|
6531
|
+
if (zeroDecimal.has(currency.toUpperCase())) return Math.round(amount);
|
|
6532
|
+
return Math.round(amount * 100);
|
|
6533
|
+
}
|
|
6534
|
+
|
|
6535
|
+
// src/core/analytics/client-context.ts
|
|
6536
|
+
var UTM_KEYS = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"];
|
|
6537
|
+
var CLIENT_LANGUAGE_MAX_LENGTH = 16;
|
|
6538
|
+
var CLIENT_REFERRER_MAX_LENGTH = 2048;
|
|
6539
|
+
var CLIENT_UTM_MAX_LENGTH = 128;
|
|
6540
|
+
var CLIENT_TZ_MAX_LENGTH = 64;
|
|
6541
|
+
var CLIENT_SCREEN_MAX = 65535;
|
|
6542
|
+
var CLIENT_LANDING_URL_MAX_LENGTH = 2048;
|
|
6543
|
+
var LANDING_KEY = "vio.landing.v1";
|
|
6544
|
+
function defaultClientEnvironment() {
|
|
5977
6545
|
return {
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
6546
|
+
navigator: typeof navigator !== "undefined" ? navigator : null,
|
|
6547
|
+
document: typeof document !== "undefined" ? document : null,
|
|
6548
|
+
location: typeof location !== "undefined" ? location : null,
|
|
6549
|
+
screen: typeof screen !== "undefined" ? screen : null,
|
|
6550
|
+
storage: sessionStorageOrNull(),
|
|
6551
|
+
timeZone: () => Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
5983
6552
|
};
|
|
5984
6553
|
}
|
|
5985
|
-
function
|
|
5986
|
-
|
|
5987
|
-
|
|
6554
|
+
function resolveLanding(env = defaultClientEnvironment(), persist = true) {
|
|
6555
|
+
try {
|
|
6556
|
+
const utm = utmOf(env.location?.search);
|
|
6557
|
+
const raw = text2(env.document?.referrer);
|
|
6558
|
+
const internal = isSameHost(raw, env.location?.host);
|
|
6559
|
+
const stored = readLanding(env.storage);
|
|
6560
|
+
if ((utm === void 0 || internal) && stored) return stored;
|
|
6561
|
+
const landing = {};
|
|
6562
|
+
const referrer = internal ? void 0 : landingReferrer(raw);
|
|
6563
|
+
if (referrer) landing.referrer = referrer;
|
|
6564
|
+
if (utm) landing.utm = utm;
|
|
6565
|
+
const url = landingUrl(env.location?.href);
|
|
6566
|
+
if (url) landing.url = url;
|
|
6567
|
+
if (persist) writeLanding(env.storage, landing);
|
|
6568
|
+
return landing;
|
|
6569
|
+
} catch {
|
|
6570
|
+
return {};
|
|
6571
|
+
}
|
|
5988
6572
|
}
|
|
5989
|
-
function
|
|
5990
|
-
return
|
|
6573
|
+
function landingReferrer(raw) {
|
|
6574
|
+
return originAndPath(raw, CLIENT_REFERRER_MAX_LENGTH);
|
|
5991
6575
|
}
|
|
5992
|
-
function
|
|
5993
|
-
|
|
5994
|
-
return slots.filter((s) => !!s && typeof s === "object");
|
|
6576
|
+
function landingUrl(raw) {
|
|
6577
|
+
return originAndPath(raw, CLIENT_LANDING_URL_MAX_LENGTH);
|
|
5995
6578
|
}
|
|
5996
|
-
function
|
|
5997
|
-
|
|
5998
|
-
if (platform && s.platform != null && s.platform !== platform) return false;
|
|
5999
|
-
const start = parseTime(s.startDate);
|
|
6000
|
-
if (start !== void 0 && start > now) return false;
|
|
6001
|
-
const end = parseTime(s.endDate);
|
|
6002
|
-
if (end !== void 0 && now >= end) return false;
|
|
6003
|
-
return true;
|
|
6579
|
+
function storedLanding(storage2 = defaultClientEnvironment().storage) {
|
|
6580
|
+
return readLanding(storage2);
|
|
6004
6581
|
}
|
|
6005
|
-
function
|
|
6006
|
-
|
|
6007
|
-
const
|
|
6008
|
-
|
|
6582
|
+
function isSameHost(referrer, host) {
|
|
6583
|
+
const value = text2(referrer);
|
|
6584
|
+
const own = text2(host);
|
|
6585
|
+
if (!value || !own) return false;
|
|
6586
|
+
try {
|
|
6587
|
+
return new URL(value).host.toLowerCase() === own.toLowerCase();
|
|
6588
|
+
} catch {
|
|
6589
|
+
return false;
|
|
6590
|
+
}
|
|
6009
6591
|
}
|
|
6010
|
-
function
|
|
6011
|
-
|
|
6012
|
-
const n = Number(value);
|
|
6013
|
-
return Number.isFinite(n) ? n : void 0;
|
|
6592
|
+
function persistLanding(landing, storage2 = defaultClientEnvironment().storage) {
|
|
6593
|
+
writeLanding(storage2, landing);
|
|
6014
6594
|
}
|
|
6015
|
-
function
|
|
6016
|
-
|
|
6017
|
-
|
|
6595
|
+
function removeLanding(storage2 = defaultClientEnvironment().storage) {
|
|
6596
|
+
try {
|
|
6597
|
+
storage2?.removeItem?.(LANDING_KEY);
|
|
6598
|
+
} catch {
|
|
6599
|
+
}
|
|
6018
6600
|
}
|
|
6019
|
-
function
|
|
6020
|
-
const out =
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
const
|
|
6025
|
-
if (
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6601
|
+
function resolveClientContext(env = defaultClientEnvironment(), landing = resolveLanding(env)) {
|
|
6602
|
+
const out = {};
|
|
6603
|
+
try {
|
|
6604
|
+
const language = cut2(text2(env.navigator?.language), CLIENT_LANGUAGE_MAX_LENGTH);
|
|
6605
|
+
if (language) out.language = language;
|
|
6606
|
+
const referrer = cut2(landing.referrer, CLIENT_REFERRER_MAX_LENGTH);
|
|
6607
|
+
if (referrer) out.referrer = referrer;
|
|
6608
|
+
for (const key of UTM_KEYS) {
|
|
6609
|
+
const value = cut2(landing.utm?.[key], CLIENT_UTM_MAX_LENGTH);
|
|
6610
|
+
if (value) out[key] = value;
|
|
6611
|
+
}
|
|
6612
|
+
const w = dimension(env.screen?.width);
|
|
6613
|
+
const h = dimension(env.screen?.height);
|
|
6614
|
+
if (w !== void 0) out.screen_w = w;
|
|
6615
|
+
if (h !== void 0) out.screen_h = h;
|
|
6616
|
+
const tz = cut2(text2(safeCall(env.timeZone)), CLIENT_TZ_MAX_LENGTH);
|
|
6617
|
+
if (tz) out.tz = tz;
|
|
6618
|
+
} catch {
|
|
6029
6619
|
}
|
|
6030
6620
|
return out;
|
|
6031
6621
|
}
|
|
6032
|
-
function
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
}
|
|
6039
|
-
function autoSlotDisplayName(kind, ordinal, scopeName) {
|
|
6040
|
-
const label = `${kind.charAt(0).toUpperCase()}${kind.slice(1)} ${ordinal + 1}`;
|
|
6041
|
-
const name = typeof scopeName === "string" ? scopeName.trim() : "";
|
|
6042
|
-
return name ? `${label} \xB7 ${name}` : label;
|
|
6043
|
-
}
|
|
6044
|
-
function normalizeScope(scope) {
|
|
6045
|
-
if (typeof scope !== "string") return "";
|
|
6046
|
-
return scope.trim().replace(/\s+/g, "-");
|
|
6047
|
-
}
|
|
6048
|
-
var SLOT_DECLARATION_MEMORY_CAP = 500;
|
|
6049
|
-
function slotDeclarationKey(location2) {
|
|
6050
|
-
return location2.displayName ? `${location2.id}${location2.displayName}` : location2.id;
|
|
6051
|
-
}
|
|
6052
|
-
function pendingSlotKeys(memory, keys, now = Date.now(), ttl = SLOT_DECLARATION_TTL_MS) {
|
|
6053
|
-
return keys.filter((key) => {
|
|
6054
|
-
const at = memory?.[key];
|
|
6055
|
-
if (typeof at !== "number" || !Number.isFinite(at)) return true;
|
|
6056
|
-
return now - at >= ttl || now < at;
|
|
6057
|
-
});
|
|
6058
|
-
}
|
|
6059
|
-
function rememberSlotKeys(memory, keys, at, cap = SLOT_DECLARATION_MEMORY_CAP) {
|
|
6060
|
-
const next = { ...memory ?? {} };
|
|
6061
|
-
for (const key of keys) next[key] = at;
|
|
6062
|
-
const entries = Object.entries(next);
|
|
6063
|
-
if (entries.length <= cap) return next;
|
|
6064
|
-
entries.sort((a, b) => b[1] - a[1]);
|
|
6065
|
-
return Object.fromEntries(entries.slice(0, cap));
|
|
6066
|
-
}
|
|
6067
|
-
function slotDeclarationStorageKey(platform, apiKey) {
|
|
6068
|
-
return `vio.slots.${platform}.${fnv1a(apiKey)}`;
|
|
6069
|
-
}
|
|
6070
|
-
function fnv1a(input) {
|
|
6071
|
-
let hash = 2166136261;
|
|
6072
|
-
for (let i = 0; i < input.length; i++) {
|
|
6073
|
-
hash ^= input.charCodeAt(i);
|
|
6074
|
-
hash = Math.imul(hash, 16777619) >>> 0;
|
|
6622
|
+
function utmOf(search) {
|
|
6623
|
+
let params;
|
|
6624
|
+
try {
|
|
6625
|
+
params = new URLSearchParams(typeof search === "string" ? search : "");
|
|
6626
|
+
} catch {
|
|
6627
|
+
return void 0;
|
|
6075
6628
|
}
|
|
6076
|
-
|
|
6629
|
+
const out = {};
|
|
6630
|
+
let any = false;
|
|
6631
|
+
for (const key of UTM_KEYS) {
|
|
6632
|
+
const value = cut2(text2(params.get(key)), CLIENT_UTM_MAX_LENGTH);
|
|
6633
|
+
if (value) {
|
|
6634
|
+
out[key] = value;
|
|
6635
|
+
any = true;
|
|
6636
|
+
}
|
|
6637
|
+
}
|
|
6638
|
+
return any ? out : void 0;
|
|
6077
6639
|
}
|
|
6078
|
-
function
|
|
6640
|
+
function readLanding(storage2) {
|
|
6079
6641
|
try {
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
if (!raw) return {};
|
|
6642
|
+
const raw = storage2?.getItem(LANDING_KEY);
|
|
6643
|
+
if (!raw) return null;
|
|
6083
6644
|
const parsed = JSON.parse(raw);
|
|
6084
|
-
if (!parsed || typeof parsed !== "object"
|
|
6085
|
-
|
|
6086
|
-
const
|
|
6087
|
-
|
|
6088
|
-
|
|
6645
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
6646
|
+
const p = parsed;
|
|
6647
|
+
const landing = {};
|
|
6648
|
+
const referrer = cut2(text2(p.referrer), CLIENT_REFERRER_MAX_LENGTH);
|
|
6649
|
+
if (referrer) landing.referrer = referrer;
|
|
6650
|
+
const url = cut2(text2(p.url), CLIENT_LANDING_URL_MAX_LENGTH);
|
|
6651
|
+
if (url) landing.url = url;
|
|
6652
|
+
if (p.utm && typeof p.utm === "object") {
|
|
6653
|
+
const utm = {};
|
|
6654
|
+
let any = false;
|
|
6655
|
+
for (const key of UTM_KEYS) {
|
|
6656
|
+
const value = cut2(text2(p.utm[key]), CLIENT_UTM_MAX_LENGTH);
|
|
6657
|
+
if (value) {
|
|
6658
|
+
utm[key] = value;
|
|
6659
|
+
any = true;
|
|
6660
|
+
}
|
|
6661
|
+
}
|
|
6662
|
+
if (any) landing.utm = utm;
|
|
6089
6663
|
}
|
|
6090
|
-
return
|
|
6664
|
+
return landing;
|
|
6091
6665
|
} catch {
|
|
6092
|
-
return
|
|
6666
|
+
return null;
|
|
6093
6667
|
}
|
|
6094
6668
|
}
|
|
6095
|
-
function
|
|
6669
|
+
function writeLanding(storage2, landing) {
|
|
6096
6670
|
try {
|
|
6097
|
-
|
|
6098
|
-
|
|
6671
|
+
storage2?.setItem(LANDING_KEY, JSON.stringify(landing));
|
|
6672
|
+
} catch {
|
|
6673
|
+
}
|
|
6674
|
+
}
|
|
6675
|
+
function sessionStorageOrNull() {
|
|
6676
|
+
try {
|
|
6677
|
+
return typeof sessionStorage !== "undefined" && sessionStorage ? sessionStorage : null;
|
|
6678
|
+
} catch {
|
|
6679
|
+
return null;
|
|
6680
|
+
}
|
|
6681
|
+
}
|
|
6682
|
+
function text2(value) {
|
|
6683
|
+
if (value === void 0 || value === null) return void 0;
|
|
6684
|
+
const s = String(value).trim();
|
|
6685
|
+
return s ? s : void 0;
|
|
6686
|
+
}
|
|
6687
|
+
function cut2(value, max) {
|
|
6688
|
+
if (value === void 0) return void 0;
|
|
6689
|
+
return value.length > max ? value.slice(0, max) : value;
|
|
6690
|
+
}
|
|
6691
|
+
function dimension(value) {
|
|
6692
|
+
const n = typeof value === "number" ? value : Number(value);
|
|
6693
|
+
if (!Number.isFinite(n) || n < 0) return void 0;
|
|
6694
|
+
return Math.min(Math.floor(n), CLIENT_SCREEN_MAX);
|
|
6695
|
+
}
|
|
6696
|
+
function safeCall(fn) {
|
|
6697
|
+
try {
|
|
6698
|
+
return fn ? fn() : void 0;
|
|
6099
6699
|
} catch {
|
|
6700
|
+
return void 0;
|
|
6100
6701
|
}
|
|
6101
6702
|
}
|
|
6102
6703
|
|
|
@@ -6198,8 +6799,16 @@ var AnalyticsManager = class extends EventTarget {
|
|
|
6198
6799
|
/** Last NON-EMPTY cart snapshot — `purchase` uses it because several flows
|
|
6199
6800
|
* (Apple Pay) clear the cart BEFORE `vio:payment-success` fires. */
|
|
6200
6801
|
this.lastCartSnapshot = null;
|
|
6802
|
+
/** The `client` block (language, landing referrer / utm, screen, tz): read once per page load, reused on every event. */
|
|
6803
|
+
this.client = null;
|
|
6804
|
+
/** The landing it was read from — persisted later when consent arrives after the first event. */
|
|
6805
|
+
this.landing = null;
|
|
6201
6806
|
this.teardown = [];
|
|
6202
6807
|
}
|
|
6808
|
+
/** The landing this manager resolved on this page load (the first event's), or null before it; read only — for the order's entry context when nothing is stored yet. */
|
|
6809
|
+
get currentLanding() {
|
|
6810
|
+
return this.landing;
|
|
6811
|
+
}
|
|
6203
6812
|
// MARK: - Public API
|
|
6204
6813
|
/**
|
|
6205
6814
|
* Install listeners + begin flushing. Explicit on purpose (no module
|
|
@@ -6217,10 +6826,12 @@ var AnalyticsManager = class extends EventTarget {
|
|
|
6217
6826
|
if (granted) {
|
|
6218
6827
|
if (this.anonIdCache) safeStorageSet(ANON_KEY, this.anonIdCache);
|
|
6219
6828
|
if (this.sessionId) this.persistSession();
|
|
6829
|
+
if (this.landing) persistLanding(this.landing);
|
|
6220
6830
|
return;
|
|
6221
6831
|
}
|
|
6222
6832
|
safeStorageRemove(ANON_KEY);
|
|
6223
6833
|
safeStorageRemove(SESSION_KEY);
|
|
6834
|
+
removeLanding();
|
|
6224
6835
|
}
|
|
6225
6836
|
/** ¿Podemos escribir en el dispositivo? */
|
|
6226
6837
|
get canPersist() {
|
|
@@ -6381,7 +6992,7 @@ var AnalyticsManager = class extends EventTarget {
|
|
|
6381
6992
|
// one is usually the tracking tail, not the page. The other host-given
|
|
6382
6993
|
// fields (broadcastId, componentTemplateId, errorCode, variant, …) are
|
|
6383
6994
|
// the host's responsibility: the SDK does not touch them.
|
|
6384
|
-
content_url:
|
|
6995
|
+
content_url: capContentUrl(ctx.contentUrl ?? (typeof location !== "undefined" ? location.href : void 0)),
|
|
6385
6996
|
// The host's own content id — its join key back onto its reporting.
|
|
6386
6997
|
// Explicit on the event when it fits, else what the SDK resolved for
|
|
6387
6998
|
// the page (already capped per source), else nothing.
|
|
@@ -6409,8 +7020,13 @@ var AnalyticsManager = class extends EventTarget {
|
|
|
6409
7020
|
value: com.value,
|
|
6410
7021
|
currency: com.currency,
|
|
6411
7022
|
order_id: com.orderId,
|
|
6412
|
-
payment_method: com.paymentMethod
|
|
7023
|
+
payment_method: com.paymentMethod,
|
|
7024
|
+
// Only a reference survives — never an email, whatever a host passes —
|
|
7025
|
+
// and only with consent to persist: a visit measured with ephemeral
|
|
7026
|
+
// ids does not carry a stable identifier of a person either.
|
|
7027
|
+
buyer_id: this.canPersist ? normalizeBuyerRef(com.buyerId) : void 0
|
|
6413
7028
|
}) : void 0;
|
|
7029
|
+
const client = this.clientBlock();
|
|
6414
7030
|
return {
|
|
6415
7031
|
event_id: uuid(),
|
|
6416
7032
|
name,
|
|
@@ -6423,9 +7039,22 @@ var AnalyticsManager = class extends EventTarget {
|
|
|
6423
7039
|
...this.externalUserId ? { external_user_id: this.externalUserId } : {},
|
|
6424
7040
|
...Object.keys(context).length ? { context } : {},
|
|
6425
7041
|
...commerce ? { commerce } : {},
|
|
7042
|
+
...client ? { client } : {},
|
|
6426
7043
|
...opts.props ? { props: opts.props } : {}
|
|
6427
7044
|
};
|
|
6428
7045
|
}
|
|
7046
|
+
/**
|
|
7047
|
+
* The `client` block: read once — the landing is remembered for the session
|
|
7048
|
+
* only when we may persist — then the same (frozen) object on every event.
|
|
7049
|
+
* `null` when there is nothing to say (no browser).
|
|
7050
|
+
*/
|
|
7051
|
+
clientBlock() {
|
|
7052
|
+
if (!this.client) {
|
|
7053
|
+
this.landing = resolveLanding(void 0, this.canPersist);
|
|
7054
|
+
this.client = Object.freeze(resolveClientContext(void 0, this.landing));
|
|
7055
|
+
}
|
|
7056
|
+
return Object.keys(this.client).length ? this.client : null;
|
|
7057
|
+
}
|
|
6429
7058
|
/** Guarda la sesión solo si podemos escribir en el dispositivo. */
|
|
6430
7059
|
persistSession(now = Date.now()) {
|
|
6431
7060
|
if (!this.canPersist || !this.sessionId) return;
|
|
@@ -6539,7 +7168,7 @@ var AnalyticsManager = class extends EventTarget {
|
|
|
6539
7168
|
const d = ev.detail ?? {};
|
|
6540
7169
|
const line = this.findLine(d.productId, d.variantId);
|
|
6541
7170
|
this.track("add_to_cart", {
|
|
6542
|
-
context: { sponsorId: d.sponsorId !== void 0 ? Number(d.sponsorId) : void 0, campaignId: campaignOf(d), slot: slotOf(d), ...contentOf(d) },
|
|
7171
|
+
context: { sponsorId: d.sponsorId !== void 0 ? Number(d.sponsorId) : void 0, campaignId: campaignOf(d), slot: slotOf(d), componentTemplateId: templateOf(d), ...contentOf(d) },
|
|
6543
7172
|
commerce: {
|
|
6544
7173
|
items: [
|
|
6545
7174
|
{
|
|
@@ -6589,7 +7218,7 @@ var AnalyticsManager = class extends EventTarget {
|
|
|
6589
7218
|
if (d.productId === void 0) return;
|
|
6590
7219
|
const price = Number(d.price);
|
|
6591
7220
|
this.track("select_item", {
|
|
6592
|
-
context: { sponsorId: d.sponsorId !== void 0 ? Number(d.sponsorId) : void 0, campaignId: campaignOf(d), slot: slotOf(d), ...contentOf(d) },
|
|
7221
|
+
context: { sponsorId: d.sponsorId !== void 0 ? Number(d.sponsorId) : void 0, campaignId: campaignOf(d), slot: slotOf(d), componentTemplateId: templateOf(d), ...contentOf(d) },
|
|
6593
7222
|
commerce: {
|
|
6594
7223
|
items: [
|
|
6595
7224
|
{
|
|
@@ -6608,13 +7237,15 @@ var AnalyticsManager = class extends EventTarget {
|
|
|
6608
7237
|
});
|
|
6609
7238
|
on("vio:checkout-open", (ev) => {
|
|
6610
7239
|
const d = ev.detail ?? {};
|
|
6611
|
-
const base = { sponsorId: d.sponsorId, campaignId: campaignOf(d), slot: slotOf(d), ...contentOf(d) };
|
|
7240
|
+
const base = { sponsorId: d.sponsorId, campaignId: campaignOf(d), slot: slotOf(d), componentTemplateId: templateOf(d), ...contentOf(d) };
|
|
7241
|
+
const buyerId = buyerRefOf(d);
|
|
7242
|
+
const buyer = buyerId !== void 0 ? { buyerId } : {};
|
|
6612
7243
|
const { slices, explicit } = this.cartSlices(d.sponsorId);
|
|
6613
7244
|
if (slices.length > 1) {
|
|
6614
7245
|
for (const slice of slices) {
|
|
6615
7246
|
this.track("begin_checkout", {
|
|
6616
7247
|
context: this.sliceContext(slice, base),
|
|
6617
|
-
commerce: sliceCommerce(slice),
|
|
7248
|
+
commerce: { ...sliceCommerce(slice), ...buyer },
|
|
6618
7249
|
props: { slice: slice.slice, slices: slice.slices }
|
|
6619
7250
|
});
|
|
6620
7251
|
}
|
|
@@ -6623,7 +7254,7 @@ var AnalyticsManager = class extends EventTarget {
|
|
|
6623
7254
|
const snap = this.cartCommerce(d.sponsorId);
|
|
6624
7255
|
this.track("begin_checkout", {
|
|
6625
7256
|
context: this.singleSliceContext(slices[0], explicit, base),
|
|
6626
|
-
...snap ? { commerce: snap } : {}
|
|
7257
|
+
...snap || buyerId !== void 0 ? { commerce: { ...snap ?? {}, ...buyer } } : {}
|
|
6627
7258
|
});
|
|
6628
7259
|
});
|
|
6629
7260
|
on("vio:payment-success", (ev) => {
|
|
@@ -6817,27 +7448,27 @@ function vioCart() {
|
|
|
6817
7448
|
return null;
|
|
6818
7449
|
}
|
|
6819
7450
|
}
|
|
6820
|
-
function resolveCampaignId(ctx, boot) {
|
|
7451
|
+
function resolveCampaignId(ctx, boot, opts = {}) {
|
|
6821
7452
|
if (ctx.campaignId !== void 0) return ctx.campaignId;
|
|
6822
7453
|
const defaultId = boot?.campaign?.id;
|
|
7454
|
+
const config = Configuration.isInitialized ? Configuration.get() : void 0;
|
|
7455
|
+
const sponsorId = toNumber(ctx.sponsorId);
|
|
7456
|
+
const candidates = [
|
|
7457
|
+
...Array.isArray(opts.pageCampaignId) ? opts.pageCampaignId : [opts.pageCampaignId],
|
|
7458
|
+
config?.campaignId
|
|
7459
|
+
];
|
|
7460
|
+
const page = activeCampaignEntry(boot, candidates, sponsorId);
|
|
6823
7461
|
if (ctx.slot) {
|
|
6824
|
-
const
|
|
6825
|
-
|
|
6826
|
-
|
|
7462
|
+
const assignment = findSlotAssignmentDetail(boot, ctx.slot, Date.now(), config?.platform);
|
|
7463
|
+
if (assignment && assignmentApplies(assignment, activeCampaignEntry(boot, candidates), sponsorId)) {
|
|
7464
|
+
const assigned = toNumber(assignment.campaign.id);
|
|
7465
|
+
if (assigned !== void 0) return assigned;
|
|
7466
|
+
}
|
|
6827
7467
|
}
|
|
6828
|
-
const
|
|
7468
|
+
const pageId = toNumber(page?.id);
|
|
7469
|
+
if (pageId !== void 0) return pageId;
|
|
6829
7470
|
if (sponsorId === void 0) return defaultId;
|
|
6830
|
-
|
|
6831
|
-
const campaigns = Array.isArray(boot?.campaigns) ? boot.campaigns : [];
|
|
6832
|
-
const matches = campaigns.filter(
|
|
6833
|
-
(c) => !!c && typeof c === "object" && c.status === "active" && campaignSponsorIds(c).includes(sponsorId)
|
|
6834
|
-
);
|
|
6835
|
-
const chosen = matches.find((c) => c.id === defaultId) ?? matches[0];
|
|
6836
|
-
const chosenId = toNumber(chosen?.id);
|
|
6837
|
-
if (chosenId !== void 0) return chosenId;
|
|
6838
|
-
} catch {
|
|
6839
|
-
}
|
|
6840
|
-
return defaultId;
|
|
7471
|
+
return toNumber(sponsorCampaignEntry(boot, sponsorId)?.id) ?? defaultId;
|
|
6841
7472
|
}
|
|
6842
7473
|
function campaignOf(detail) {
|
|
6843
7474
|
return toNumber(detail.campaignId);
|
|
@@ -6851,20 +7482,16 @@ function capTitle(value) {
|
|
|
6851
7482
|
if (!value) return void 0;
|
|
6852
7483
|
return value.length > CONTENT_TITLE_MAX_LENGTH ? value.slice(0, CONTENT_TITLE_MAX_LENGTH) : value;
|
|
6853
7484
|
}
|
|
6854
|
-
var CONTENT_URL_MAX_LENGTH = 2048;
|
|
6855
|
-
function capUrl(value) {
|
|
6856
|
-
if (!value) return void 0;
|
|
6857
|
-
if (value.length <= CONTENT_URL_MAX_LENGTH) return value;
|
|
6858
|
-
const cut = value.search(/[?#]/);
|
|
6859
|
-
const withoutQuery = cut >= 0 ? value.slice(0, cut) : value;
|
|
6860
|
-
return withoutQuery.length <= CONTENT_URL_MAX_LENGTH ? withoutQuery : withoutQuery.slice(0, CONTENT_URL_MAX_LENGTH);
|
|
6861
|
-
}
|
|
6862
7485
|
function sliceCommerce(slice) {
|
|
6863
7486
|
return { items: slice.items, value: slice.value, currency: slice.currency };
|
|
6864
7487
|
}
|
|
6865
7488
|
function slotOf(detail) {
|
|
6866
7489
|
return typeof detail.slot === "string" && detail.slot.trim() ? detail.slot.trim() : void 0;
|
|
6867
7490
|
}
|
|
7491
|
+
function templateOf(detail) {
|
|
7492
|
+
const value = typeof detail.componentTemplateId === "string" ? detail.componentTemplateId.trim() : "";
|
|
7493
|
+
return fitsId(value || void 0);
|
|
7494
|
+
}
|
|
6868
7495
|
function contentOf(detail) {
|
|
6869
7496
|
const out = {};
|
|
6870
7497
|
if (typeof detail.contentId === "number" && Number.isFinite(detail.contentId)) out.contentId = detail.contentId;
|
|
@@ -6872,21 +7499,33 @@ function contentOf(detail) {
|
|
|
6872
7499
|
if (typeof detail.contentTitle === "string" && detail.contentTitle.trim()) out.contentTitle = detail.contentTitle.trim();
|
|
6873
7500
|
return out;
|
|
6874
7501
|
}
|
|
6875
|
-
function
|
|
6876
|
-
|
|
6877
|
-
const
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
7502
|
+
function buyerRefOf(d) {
|
|
7503
|
+
if (!d) return void 0;
|
|
7504
|
+
const r = d.result ?? {};
|
|
7505
|
+
const candidates = [
|
|
7506
|
+
d.buyerRef,
|
|
7507
|
+
d.buyer_ref,
|
|
7508
|
+
r.buyerRef,
|
|
7509
|
+
r.buyer_ref,
|
|
7510
|
+
r.confirmation?.buyerRef,
|
|
7511
|
+
r.confirmation?.buyer_ref,
|
|
7512
|
+
r.checkout?.buyerRef,
|
|
7513
|
+
r.checkout?.buyer_ref
|
|
7514
|
+
];
|
|
7515
|
+
for (const candidate of candidates) {
|
|
7516
|
+
const ref = normalizeBuyerRef(candidate);
|
|
7517
|
+
if (ref) return ref;
|
|
6883
7518
|
}
|
|
6884
|
-
return
|
|
7519
|
+
return void 0;
|
|
6885
7520
|
}
|
|
6886
7521
|
function purchaseFromPaymentSuccess(d) {
|
|
6887
7522
|
const r = d.result ?? {};
|
|
6888
7523
|
const orderId = firstId(d.orderId, r.confirmation?.orderId, d.checkoutId);
|
|
6889
|
-
|
|
7524
|
+
const buyerId = buyerRefOf(d);
|
|
7525
|
+
return {
|
|
7526
|
+
...orderId !== void 0 ? { orderId } : {},
|
|
7527
|
+
...buyerId !== void 0 ? { buyerId } : {}
|
|
7528
|
+
};
|
|
6890
7529
|
}
|
|
6891
7530
|
function firstId(...candidates) {
|
|
6892
7531
|
for (const c of candidates) {
|
|
@@ -6931,8 +7570,87 @@ function pushGa4(event) {
|
|
|
6931
7570
|
});
|
|
6932
7571
|
}
|
|
6933
7572
|
|
|
7573
|
+
// src/core/checkout/attribution.ts
|
|
7574
|
+
var CHECKOUT_ATTRIBUTION_CAPS = Object.freeze({
|
|
7575
|
+
surfaceKey: 255,
|
|
7576
|
+
contentUrl: 2048,
|
|
7577
|
+
contentTitle: CONTENT_TITLE_MAX_LENGTH,
|
|
7578
|
+
referrer: CLIENT_REFERRER_MAX_LENGTH,
|
|
7579
|
+
utm: CLIENT_UTM_MAX_LENGTH,
|
|
7580
|
+
landingUrl: CLIENT_LANDING_URL_MAX_LENGTH
|
|
7581
|
+
});
|
|
7582
|
+
function checkoutAttributionOf(input) {
|
|
7583
|
+
const out = {};
|
|
7584
|
+
const surfaceKey = cut3(input.surfaceKey, CHECKOUT_ATTRIBUTION_CAPS.surfaceKey);
|
|
7585
|
+
if (surfaceKey) out.surface_key = surfaceKey;
|
|
7586
|
+
const campaignId = positiveInt(input.pageCampaignId) ?? dominantCampaignOf(input.slices);
|
|
7587
|
+
if (campaignId !== void 0) out.campaign_id = campaignId;
|
|
7588
|
+
if (input.privacyMode === "standard") {
|
|
7589
|
+
const url = originAndPath(input.content?.url, CHECKOUT_ATTRIBUTION_CAPS.contentUrl);
|
|
7590
|
+
if (url) out.content_url = url;
|
|
7591
|
+
const title = cut3(input.content?.title, CHECKOUT_ATTRIBUTION_CAPS.contentTitle);
|
|
7592
|
+
if (title) out.content_title = title;
|
|
7593
|
+
const entry = entryContextOf(input.landing);
|
|
7594
|
+
if (entry) out.entry = entry;
|
|
7595
|
+
}
|
|
7596
|
+
return Object.keys(out).length ? out : void 0;
|
|
7597
|
+
}
|
|
7598
|
+
function entryContextOf(landing) {
|
|
7599
|
+
if (!landing || typeof landing !== "object") return void 0;
|
|
7600
|
+
const out = {};
|
|
7601
|
+
const referrer = cut3(landing.referrer, CHECKOUT_ATTRIBUTION_CAPS.referrer);
|
|
7602
|
+
if (referrer) out.referrer = referrer;
|
|
7603
|
+
const source = cut3(landing.utm?.utm_source, CHECKOUT_ATTRIBUTION_CAPS.utm);
|
|
7604
|
+
if (source) out.utm_source = source;
|
|
7605
|
+
const medium = cut3(landing.utm?.utm_medium, CHECKOUT_ATTRIBUTION_CAPS.utm);
|
|
7606
|
+
if (medium) out.utm_medium = medium;
|
|
7607
|
+
const campaign = cut3(landing.utm?.utm_campaign, CHECKOUT_ATTRIBUTION_CAPS.utm);
|
|
7608
|
+
if (campaign) out.utm_campaign = campaign;
|
|
7609
|
+
const landingUrl2 = cut3(landing.url, CHECKOUT_ATTRIBUTION_CAPS.landingUrl);
|
|
7610
|
+
if (landingUrl2) out.landing_url = landingUrl2;
|
|
7611
|
+
return Object.keys(out).length ? out : void 0;
|
|
7612
|
+
}
|
|
7613
|
+
function dominantCampaignOf(slices) {
|
|
7614
|
+
if (!Array.isArray(slices)) return void 0;
|
|
7615
|
+
let best;
|
|
7616
|
+
for (const slice of slices) {
|
|
7617
|
+
const campaignId = positiveInt(slice?.campaignId);
|
|
7618
|
+
if (campaignId === void 0) continue;
|
|
7619
|
+
const items = Array.isArray(slice.items) ? slice.items : [];
|
|
7620
|
+
const units = items.reduce((sum, item) => sum + (Number(item?.quantity) > 0 ? Number(item.quantity) : 0), 0);
|
|
7621
|
+
const value = Number(slice.value) || 0;
|
|
7622
|
+
if (!best || units > best.units || units === best.units && value > best.value) {
|
|
7623
|
+
best = { campaignId, units, value };
|
|
7624
|
+
}
|
|
7625
|
+
}
|
|
7626
|
+
return best?.campaignId;
|
|
7627
|
+
}
|
|
7628
|
+
function positiveInt(value) {
|
|
7629
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
7630
|
+
const n = Number(value);
|
|
7631
|
+
return Number.isInteger(n) && n > 0 && n <= GRAPHQL_INT_MAX ? n : void 0;
|
|
7632
|
+
}
|
|
7633
|
+
function cut3(value, max) {
|
|
7634
|
+
if (typeof value !== "string" && typeof value !== "number") return void 0;
|
|
7635
|
+
const text3 = String(value).trim();
|
|
7636
|
+
if (!text3) return void 0;
|
|
7637
|
+
return text3.length > max ? text3.slice(0, max).trim() || void 0 : text3;
|
|
7638
|
+
}
|
|
7639
|
+
|
|
6934
7640
|
// src/core/client.ts
|
|
6935
7641
|
var SLOT_DECLARATION_DEBOUNCE_MS = 1e3;
|
|
7642
|
+
var PRIVACY_MODE_TIMEOUT_MS = 1500;
|
|
7643
|
+
var PRIVACY_UNKNOWN_TTL_MS = 6e4;
|
|
7644
|
+
function privacyModeOf(value) {
|
|
7645
|
+
return value === "minimal" || value === "standard" ? value : void 0;
|
|
7646
|
+
}
|
|
7647
|
+
function privacyModeOfSurface(res) {
|
|
7648
|
+
const surface = res?.surface;
|
|
7649
|
+
if (!surface || typeof surface !== "object") return "unknown";
|
|
7650
|
+
const raw = surface.privacyMode;
|
|
7651
|
+
if (raw === void 0 || raw === null) return "standard";
|
|
7652
|
+
return privacyModeOf(raw) ?? "unknown";
|
|
7653
|
+
}
|
|
6936
7654
|
var VioFacade = class {
|
|
6937
7655
|
constructor() {
|
|
6938
7656
|
this.apiInstance = null;
|
|
@@ -6946,8 +7664,11 @@ var VioFacade = class {
|
|
|
6946
7664
|
* (another key or environment) is still in flight after a re-init; its answer
|
|
6947
7665
|
* belongs to that configuration and must not land in this one. */
|
|
6948
7666
|
this.generation = 0;
|
|
6949
|
-
/** Slot ids present on the page (`Vio.registerSlot`): how many elements hold each
|
|
7667
|
+
/** Slot ids present on the page (`Vio.registerSlot`): how many elements hold each, its display name, its own campaign and its kind. */
|
|
6950
7668
|
this.slotRegistry = /* @__PURE__ */ new Map();
|
|
7669
|
+
/** Slots already warned about (`dashboard`: the dashboard's assignment stands; `replaced`: this page moved it). */
|
|
7670
|
+
this.warnedDashboardSlots = /* @__PURE__ */ new Set();
|
|
7671
|
+
this.warnedReplacedSlots = /* @__PURE__ */ new Set();
|
|
6951
7672
|
/** Pending debounced declaration of the registry. */
|
|
6952
7673
|
this.slotDeclareTimer = null;
|
|
6953
7674
|
/** A name appeared (or the key changed) since the registry was last declared. */
|
|
@@ -6962,6 +7683,10 @@ var VioFacade = class {
|
|
|
6962
7683
|
this.cartManager = null;
|
|
6963
7684
|
this.checkoutManager = null;
|
|
6964
7685
|
this.analyticsManager = null;
|
|
7686
|
+
/** What the surface answered of its privacy mode (`GET /v2/mobile/surface`) and when; `unknown` expires after a minute. */
|
|
7687
|
+
this.surfaceMode = null;
|
|
7688
|
+
/** The request in flight for it, if any. */
|
|
7689
|
+
this.surfaceRequest = null;
|
|
6965
7690
|
}
|
|
6966
7691
|
init(config) {
|
|
6967
7692
|
Configuration.init(config);
|
|
@@ -6971,6 +7696,9 @@ var VioFacade = class {
|
|
|
6971
7696
|
this.cachedBootstrap = null;
|
|
6972
7697
|
this.bootstrapFailedAt = 0;
|
|
6973
7698
|
this.generation++;
|
|
7699
|
+
this.surfaceMode = null;
|
|
7700
|
+
this.surfaceRequest = null;
|
|
7701
|
+
this.preloadPrivacyMode();
|
|
6974
7702
|
this.checkoutManager?.clearPaymentMethodsCache();
|
|
6975
7703
|
if (this.slotDeclareTimer) {
|
|
6976
7704
|
clearTimeout(this.slotDeclareTimer);
|
|
@@ -6980,6 +7708,8 @@ var VioFacade = class {
|
|
|
6980
7708
|
this.declarationsThisLoad = 0;
|
|
6981
7709
|
this.warnedLocationCap = false;
|
|
6982
7710
|
this.warnedDeclarationCap = false;
|
|
7711
|
+
this.warnedDashboardSlots.clear();
|
|
7712
|
+
this.warnedReplacedSlots.clear();
|
|
6983
7713
|
}
|
|
6984
7714
|
/**
|
|
6985
7715
|
* The content (article, page) this SDK instance is rendered in — what every
|
|
@@ -7010,9 +7740,13 @@ var VioFacade = class {
|
|
|
7010
7740
|
if (!this.cartManager) this.cartManager = new CartManager();
|
|
7011
7741
|
return this.cartManager;
|
|
7012
7742
|
}
|
|
7013
|
-
/** Checkout flow manager. Subscribes to cart state via the CartManager. */
|
|
7743
|
+
/** Checkout flow manager. Subscribes to cart state via the CartManager; asks here for the order's attribution. */
|
|
7014
7744
|
get checkout() {
|
|
7015
|
-
if (!this.checkoutManager)
|
|
7745
|
+
if (!this.checkoutManager) {
|
|
7746
|
+
this.checkoutManager = new CheckoutManager(this.cart, {
|
|
7747
|
+
orderAttribution: (sponsorId) => this.orderAttribution(sponsorId)
|
|
7748
|
+
});
|
|
7749
|
+
}
|
|
7016
7750
|
return this.checkoutManager;
|
|
7017
7751
|
}
|
|
7018
7752
|
/**
|
|
@@ -7054,6 +7788,7 @@ var VioFacade = class {
|
|
|
7054
7788
|
this.cachedBootstrap = res;
|
|
7055
7789
|
this.bootstrapFailedAt = 0;
|
|
7056
7790
|
if (this.slotsDirty) this.scheduleSlotDeclaration();
|
|
7791
|
+
this.preloadPrivacyMode();
|
|
7057
7792
|
return res;
|
|
7058
7793
|
},
|
|
7059
7794
|
(err) => {
|
|
@@ -7117,9 +7852,14 @@ var VioFacade = class {
|
|
|
7117
7852
|
const cfg = Configuration.get();
|
|
7118
7853
|
storageKey = slotDeclarationStorageKey(cfg.platform, cfg.apiKey);
|
|
7119
7854
|
now = Date.now();
|
|
7855
|
+
const pageCampaignId = cfg.campaignId;
|
|
7856
|
+
for (const l of all) {
|
|
7857
|
+
if (l.campaignId !== void 0 && l.campaignId === pageCampaignId) delete l.campaignId;
|
|
7858
|
+
}
|
|
7120
7859
|
const memory = readSlotDeclaration(storageKey);
|
|
7121
|
-
const
|
|
7122
|
-
|
|
7860
|
+
const keyOf = (l) => slotDeclarationKey(l, pageCampaignId);
|
|
7861
|
+
const pending2 = new Set(pendingSlotKeys(memory, all.map(keyOf), now));
|
|
7862
|
+
let locations = opts.force ? all : all.filter((l) => pending2.has(keyOf(l)));
|
|
7123
7863
|
if (locations.length === 0) return null;
|
|
7124
7864
|
if (locations.length > MAX_LOCATIONS_PER_DECLARATION) {
|
|
7125
7865
|
if (!this.warnedLocationCap) {
|
|
@@ -7130,7 +7870,7 @@ var VioFacade = class {
|
|
|
7130
7870
|
}
|
|
7131
7871
|
locations = locations.slice(0, MAX_LOCATIONS_PER_DECLARATION);
|
|
7132
7872
|
}
|
|
7133
|
-
keys = locations.map(
|
|
7873
|
+
keys = locations.map(keyOf);
|
|
7134
7874
|
if (!opts.force && this.declarationsThisLoad >= MAX_DECLARATIONS_PER_LOAD) {
|
|
7135
7875
|
if (!this.warnedDeclarationCap) {
|
|
7136
7876
|
this.warnedDeclarationCap = true;
|
|
@@ -7144,10 +7884,16 @@ var VioFacade = class {
|
|
|
7144
7884
|
const result = await this.api.declareManifest({
|
|
7145
7885
|
platform: cfg.platform,
|
|
7146
7886
|
mode: "merge",
|
|
7147
|
-
locations
|
|
7887
|
+
locations,
|
|
7888
|
+
...pageCampaignId !== void 0 ? { campaignId: pageCampaignId } : {}
|
|
7148
7889
|
});
|
|
7890
|
+
this.reportAssignments(result, locations, pageCampaignId, cfg.debug);
|
|
7149
7891
|
if (result?.mode === "merge") {
|
|
7150
|
-
|
|
7892
|
+
const unhandled = this.campaignNotTakenUp(result, locations, pageCampaignId);
|
|
7893
|
+
const settled = keys.filter((k) => !unhandled.has(k));
|
|
7894
|
+
let next = rememberSlotKeys(memory, settled, now);
|
|
7895
|
+
if (unhandled.size > 0) next = rememberSlotKeys(next, [...unhandled], failedAt(now));
|
|
7896
|
+
writeSlotDeclaration(storageKey, next);
|
|
7151
7897
|
} else {
|
|
7152
7898
|
writeSlotDeclaration(storageKey, rememberSlotKeys(memory, keys, failedAt(now)));
|
|
7153
7899
|
warn(
|
|
@@ -7166,21 +7912,34 @@ var VioFacade = class {
|
|
|
7166
7912
|
/**
|
|
7167
7913
|
* Register a slot present on the page (the `<vio-*>` elements call it with
|
|
7168
7914
|
* their effective id — the host's `vio-slot` or the auto-generated one —
|
|
7169
|
-
*
|
|
7170
|
-
*
|
|
7171
|
-
*
|
|
7172
|
-
*
|
|
7173
|
-
*
|
|
7915
|
+
* the optional display name, and the element's own campaign when it has
|
|
7916
|
+
* one: `vio-campaign` or an ancestor's `data-vio-campaign`). The registry is
|
|
7917
|
+
* declared ONCE, debounced, after the first successful bootstrap — and again
|
|
7918
|
+
* whenever a new id, display name, campaign or kind appears. A `displayName` /
|
|
7919
|
+
* `campaignId` given later for an id already registered replaces the one
|
|
7920
|
+
* kept; `undefined` leaves it alone. The third argument is the campaign id
|
|
7921
|
+
* (as before) or `{ campaignId?, kind? }`, and a fourth `{ kind? }` may
|
|
7922
|
+
* follow a positional campaign — the elements call
|
|
7923
|
+
* `registerSlot(id, name, campaignId, { kind })` so a facade from an older
|
|
7924
|
+
* bundle, which reads only three arguments, still gets the campaign. `kind`
|
|
7925
|
+
* is what kind of element holds the slot (`card` | `carousel` | `detail`);
|
|
7926
|
+
* the FIRST kind an id gets stays (the cards a carousel renders share its
|
|
7927
|
+
* id: the slot is the carousel's). The returned function unregisters;
|
|
7928
|
+
* removal never re-declares.
|
|
7174
7929
|
*/
|
|
7175
|
-
registerSlot(name, displayName) {
|
|
7930
|
+
registerSlot(name, displayName, campaignIdOrOptions, options) {
|
|
7176
7931
|
const id = normalizeSlotId(name);
|
|
7177
7932
|
if (!id) return () => {
|
|
7178
7933
|
};
|
|
7179
7934
|
const label = typeof displayName === "string" && displayName.trim() ? displayName.trim() : void 0;
|
|
7935
|
+
const positional = campaignIdOrOptions !== null && typeof campaignIdOrOptions === "object" ? campaignIdOrOptions : { campaignId: campaignIdOrOptions };
|
|
7936
|
+
const merged = { ...positional, ...options ?? {} };
|
|
7937
|
+
const campaign = toCampaignId(merged.campaignId);
|
|
7938
|
+
const kind = normalizeSlotKind(merged.kind);
|
|
7180
7939
|
const entry = this.slotRegistry.get(id);
|
|
7181
7940
|
let changed = false;
|
|
7182
7941
|
if (!entry) {
|
|
7183
|
-
this.slotRegistry.set(id, { count: 1, displayName: label });
|
|
7942
|
+
this.slotRegistry.set(id, { count: 1, displayName: label, campaignId: campaign, kind });
|
|
7184
7943
|
changed = true;
|
|
7185
7944
|
} else {
|
|
7186
7945
|
entry.count++;
|
|
@@ -7188,6 +7947,14 @@ var VioFacade = class {
|
|
|
7188
7947
|
entry.displayName = label;
|
|
7189
7948
|
changed = true;
|
|
7190
7949
|
}
|
|
7950
|
+
if (campaign !== void 0 && campaign !== entry.campaignId) {
|
|
7951
|
+
entry.campaignId = campaign;
|
|
7952
|
+
changed = true;
|
|
7953
|
+
}
|
|
7954
|
+
if (kind !== void 0 && entry.kind === void 0) {
|
|
7955
|
+
entry.kind = kind;
|
|
7956
|
+
changed = true;
|
|
7957
|
+
}
|
|
7191
7958
|
}
|
|
7192
7959
|
if (changed) {
|
|
7193
7960
|
this.slotsDirty = true;
|
|
@@ -7208,9 +7975,30 @@ var VioFacade = class {
|
|
|
7208
7975
|
* open campaign for the init platform, else the surface's default. With no
|
|
7209
7976
|
* slot, the default. `null` before the bootstrap. See `resolveSlotCampaign`.
|
|
7210
7977
|
*/
|
|
7211
|
-
campaignForSlot(slot) {
|
|
7212
|
-
const
|
|
7213
|
-
return resolveSlotCampaign(
|
|
7978
|
+
campaignForSlot(slot, hint) {
|
|
7979
|
+
const config = Configuration.isInitialized ? Configuration.get() : void 0;
|
|
7980
|
+
return resolveSlotCampaign(
|
|
7981
|
+
this.cachedBootstrap,
|
|
7982
|
+
slot ?? null,
|
|
7983
|
+
Date.now(),
|
|
7984
|
+
config?.platform,
|
|
7985
|
+
this.pageCampaignCandidates(hint?.campaignId),
|
|
7986
|
+
toNumber(hint?.sponsorId)
|
|
7987
|
+
);
|
|
7988
|
+
}
|
|
7989
|
+
/** The page-campaign candidates in precedence order: the element's (own, ancestor), then `Vio.init`'s. */
|
|
7990
|
+
pageCampaignCandidates(hint) {
|
|
7991
|
+
const config = Configuration.isInitialized ? Configuration.get() : void 0;
|
|
7992
|
+
return campaignCandidates([...Array.isArray(hint) ? hint : [hint], config?.campaignId]);
|
|
7993
|
+
}
|
|
7994
|
+
/**
|
|
7995
|
+
* The campaign the PAGE belongs to (`Vio.init({ campaignId })`, ADR-0034
|
|
7996
|
+
* amendment 3), as the last bootstrap lists it — only when it is there and
|
|
7997
|
+
* active; `null` otherwise (no page campaign, paused, unknown, no bootstrap).
|
|
7998
|
+
*/
|
|
7999
|
+
pageCampaign() {
|
|
8000
|
+
const config = Configuration.isInitialized ? Configuration.get() : void 0;
|
|
8001
|
+
return activeCampaignEntry(this.cachedBootstrap, config?.campaignId);
|
|
7214
8002
|
}
|
|
7215
8003
|
/**
|
|
7216
8004
|
* THE attribution helper — the SDK elements, Vev and Replit all use this
|
|
@@ -7222,8 +8010,12 @@ var VioFacade = class {
|
|
|
7222
8010
|
*
|
|
7223
8011
|
* `hint.sponsorId` — when the caller already knows the sponsor (a card with
|
|
7224
8012
|
* `sponsor-id`) it is kept as `sponsorId`, and the campaign follows the same
|
|
7225
|
-
* cascade analytics uses (slot assignment → the sponsor's
|
|
7226
|
-
* default), so what the cart remembers matches what the
|
|
8013
|
+
* cascade analytics uses (slot assignment → page campaign → the sponsor's
|
|
8014
|
+
* active campaign → default), so what the cart remembers matches what the
|
|
8015
|
+
* events say — the page campaign applies there only when that sponsor takes
|
|
8016
|
+
* part in it. `hint.campaignId` — the element's own page campaign
|
|
8017
|
+
* candidates (`vio-campaign`, an ancestor's `data-vio-campaign`), ahead of
|
|
8018
|
+
* `Vio.init`'s; an unusable one falls through to the next; `0` = unset.
|
|
7227
8019
|
*/
|
|
7228
8020
|
attribution(slot, hint) {
|
|
7229
8021
|
const out = {};
|
|
@@ -7232,13 +8024,16 @@ var VioFacade = class {
|
|
|
7232
8024
|
if (name) out.slot = name;
|
|
7233
8025
|
const boot = this.cachedBootstrap;
|
|
7234
8026
|
const hinted = toNumber(hint?.sponsorId);
|
|
8027
|
+
const pageCampaignId = this.pageCampaignCandidates(hint?.campaignId);
|
|
7235
8028
|
if (boot) {
|
|
7236
8029
|
if (hinted !== void 0) {
|
|
7237
|
-
const campaignId = resolveCampaignId({ slot: name || void 0, sponsorId: hinted }, boot
|
|
8030
|
+
const campaignId = resolveCampaignId({ slot: name || void 0, sponsorId: hinted }, boot, {
|
|
8031
|
+
pageCampaignId
|
|
8032
|
+
});
|
|
7238
8033
|
if (campaignId !== void 0) out.campaignId = campaignId;
|
|
7239
8034
|
out.sponsorId = hinted;
|
|
7240
8035
|
} else {
|
|
7241
|
-
const campaign = this.campaignForSlot(name || null);
|
|
8036
|
+
const campaign = this.campaignForSlot(name || null, { campaignId: pageCampaignId });
|
|
7242
8037
|
const campaignId = toNumber(campaign?.id);
|
|
7243
8038
|
if (campaignId !== void 0) out.campaignId = campaignId;
|
|
7244
8039
|
const sponsorId = toNumber(campaign?.primarySponsor?.id);
|
|
@@ -7250,10 +8045,155 @@ var VioFacade = class {
|
|
|
7250
8045
|
const content = this.content();
|
|
7251
8046
|
if (content.id) out.contentId = content.id;
|
|
7252
8047
|
if (content.title) out.contentTitle = content.title;
|
|
8048
|
+
if (content.url) out.contentUrl = content.url;
|
|
7253
8049
|
} catch {
|
|
7254
8050
|
}
|
|
7255
8051
|
return out;
|
|
7256
8052
|
}
|
|
8053
|
+
/**
|
|
8054
|
+
* What the surface answered and is still believed: `standard` / `minimal`
|
|
8055
|
+
* for the configuration; `unknown` for a minute after a timeout or a
|
|
8056
|
+
* failure (then it is asked again). `null` = nothing believed.
|
|
8057
|
+
*/
|
|
8058
|
+
believedSurfaceMode() {
|
|
8059
|
+
const answer = this.surfaceMode;
|
|
8060
|
+
if (!answer) return null;
|
|
8061
|
+
if (answer.mode !== "unknown") return answer.mode;
|
|
8062
|
+
return Date.now() - answer.at < PRIVACY_UNKNOWN_TTL_MS ? "unknown" : null;
|
|
8063
|
+
}
|
|
8064
|
+
/**
|
|
8065
|
+
* Ask the surface its privacy mode (`GET /v2/mobile/surface`), once —
|
|
8066
|
+
* started at `init()` and again by `bootstrap()`, never by the payment
|
|
8067
|
+
* path, which only reads what is known (`privacyModeNow`). Resolves what
|
|
8068
|
+
* was learnt within 1.5 s: the mode; `standard` for a service that predates
|
|
8069
|
+
* the endpoint (404) or a surface that names none; `unknown` for a word the
|
|
8070
|
+
* SDK does not know, a timeout or any other failure — believed for a
|
|
8071
|
+
* minute, then asked again. An answer that arrives after the cap still
|
|
8072
|
+
* lands for the next step. Never throws.
|
|
8073
|
+
*/
|
|
8074
|
+
preloadPrivacyMode() {
|
|
8075
|
+
try {
|
|
8076
|
+
if (this.surfaceRequest) return this.surfaceRequest;
|
|
8077
|
+
const believed = this.believedSurfaceMode();
|
|
8078
|
+
if (believed) return Promise.resolve(believed);
|
|
8079
|
+
if (!Configuration.isInitialized) return Promise.resolve("unknown");
|
|
8080
|
+
const now = Date.now();
|
|
8081
|
+
if (Configuration.get().privacyMode === "minimal") {
|
|
8082
|
+
this.surfaceMode = { mode: "minimal", at: now };
|
|
8083
|
+
return Promise.resolve("minimal");
|
|
8084
|
+
}
|
|
8085
|
+
const generation = this.generation;
|
|
8086
|
+
const learnt = (mode) => {
|
|
8087
|
+
if (generation !== this.generation) return;
|
|
8088
|
+
if (mode === "unknown" && this.surfaceMode && this.surfaceMode.mode !== "unknown") return;
|
|
8089
|
+
this.surfaceMode = { mode, at: Date.now() };
|
|
8090
|
+
};
|
|
8091
|
+
const asked = this.api.surface().then(
|
|
8092
|
+
(res) => privacyModeOfSurface(res),
|
|
8093
|
+
(err) => err instanceof VioApiError && err.status === 404 ? "standard" : "unknown"
|
|
8094
|
+
);
|
|
8095
|
+
let timer;
|
|
8096
|
+
const capped = new Promise((resolve) => {
|
|
8097
|
+
timer = setTimeout(() => resolve("unknown"), PRIVACY_MODE_TIMEOUT_MS);
|
|
8098
|
+
});
|
|
8099
|
+
const request = Promise.race([asked, capped]).then((mode) => {
|
|
8100
|
+
clearTimeout(timer);
|
|
8101
|
+
learnt(mode);
|
|
8102
|
+
if (this.surfaceRequest === request) this.surfaceRequest = null;
|
|
8103
|
+
return mode;
|
|
8104
|
+
});
|
|
8105
|
+
void asked.then((mode) => {
|
|
8106
|
+
clearTimeout(timer);
|
|
8107
|
+
learnt(mode);
|
|
8108
|
+
});
|
|
8109
|
+
this.surfaceRequest = request;
|
|
8110
|
+
return request;
|
|
8111
|
+
} catch {
|
|
8112
|
+
return Promise.resolve("unknown");
|
|
8113
|
+
}
|
|
8114
|
+
}
|
|
8115
|
+
/**
|
|
8116
|
+
* How much of the visit the order attribution may carry (ADR-0035): the
|
|
8117
|
+
* surface's privacy mode, as known RIGHT NOW, without waiting —
|
|
8118
|
+
* `Vio.init({ privacyMode: 'minimal' })` when the host says so, else what
|
|
8119
|
+
* the bootstrap says of the surface, else what the surface answered to the
|
|
8120
|
+
* ask `init()` started (`preloadPrivacyMode`), else the host's `standard`
|
|
8121
|
+
* once the surface could not tell. `unknown` while nothing is known: then
|
|
8122
|
+
* the page, the slot and the entry stay off what goes to Commerce, because
|
|
8123
|
+
* what can reveal which screen someone was on, and why, is only sent when
|
|
8124
|
+
* the surface is known to allow it. The payment path reads this one.
|
|
8125
|
+
*/
|
|
8126
|
+
privacyModeNow() {
|
|
8127
|
+
try {
|
|
8128
|
+
const config = Configuration.isInitialized ? Configuration.get() : void 0;
|
|
8129
|
+
if (config?.privacyMode === "minimal") return "minimal";
|
|
8130
|
+
const fromBootstrap = privacyModeOf(this.cachedBootstrap?.surface?.privacyMode);
|
|
8131
|
+
if (fromBootstrap) return fromBootstrap;
|
|
8132
|
+
if (!config) return "unknown";
|
|
8133
|
+
const believed = this.believedSurfaceMode();
|
|
8134
|
+
if (believed === "standard" || believed === "minimal") return believed;
|
|
8135
|
+
if (!believed) this.preloadPrivacyMode();
|
|
8136
|
+
return believed === "unknown" ? config.privacyMode ?? "unknown" : "unknown";
|
|
8137
|
+
} catch {
|
|
8138
|
+
return "unknown";
|
|
8139
|
+
}
|
|
8140
|
+
}
|
|
8141
|
+
/**
|
|
8142
|
+
* `privacyModeNow`, but waiting for the ask in flight when there is one
|
|
8143
|
+
* (at most its 1.5 s cap) — for the cart's lines, which are not the
|
|
8144
|
+
* payment path. Never throws.
|
|
8145
|
+
*/
|
|
8146
|
+
async privacyMode() {
|
|
8147
|
+
try {
|
|
8148
|
+
const now = this.privacyModeNow();
|
|
8149
|
+
if (now !== "unknown") return now;
|
|
8150
|
+
if (!this.surfaceRequest) return "unknown";
|
|
8151
|
+
const asked = await this.surfaceRequest;
|
|
8152
|
+
if (asked !== "unknown") return asked;
|
|
8153
|
+
const config = Configuration.isInitialized ? Configuration.get() : void 0;
|
|
8154
|
+
return config?.privacyMode ?? "unknown";
|
|
8155
|
+
} catch {
|
|
8156
|
+
return "unknown";
|
|
8157
|
+
}
|
|
8158
|
+
}
|
|
8159
|
+
/**
|
|
8160
|
+
* The attribution of the order a sponsor's cart is about to become
|
|
8161
|
+
* (ADR-0035 phase B) — what `CreateCheckout` / `UpdateCheckout` send as
|
|
8162
|
+
* `attribution` and shopcart writes on the order: the surface (its SDK
|
|
8163
|
+
* key), the page's campaign (`pageCampaign()`; without one, the campaign
|
|
8164
|
+
* that explains most units of the cart under the same rule `purchase` is
|
|
8165
|
+
* split by — `cartSlices`) and, on a surface KNOWN to be `standard`
|
|
8166
|
+
* (`privacyModeNow`), the page (`content()`'s url as origin + path, and
|
|
8167
|
+
* title) and where the session came in: the landing the session REMEMBERS
|
|
8168
|
+
* (`storedLanding`, else the one analytics resolved on this page) — never
|
|
8169
|
+
* the page the shopper pays on taken for a landing. Synchronous: nothing
|
|
8170
|
+
* on the way to a payment waits for it. snake_case, capped to Commerce's
|
|
8171
|
+
* limits. `undefined` when there is nothing to say; never throws.
|
|
8172
|
+
*/
|
|
8173
|
+
orderAttribution(sponsorId) {
|
|
8174
|
+
try {
|
|
8175
|
+
const config = Configuration.isInitialized ? Configuration.get() : void 0;
|
|
8176
|
+
const content = this.content();
|
|
8177
|
+
return checkoutAttributionOf({
|
|
8178
|
+
surfaceKey: config?.apiKey,
|
|
8179
|
+
pageCampaignId: toNumber(this.pageCampaign()?.id),
|
|
8180
|
+
slices: this.cartSlices(sponsorId),
|
|
8181
|
+
content: { url: content.url, title: content.title },
|
|
8182
|
+
landing: this.rememberedLanding(),
|
|
8183
|
+
privacyMode: this.privacyModeNow()
|
|
8184
|
+
});
|
|
8185
|
+
} catch {
|
|
8186
|
+
return void 0;
|
|
8187
|
+
}
|
|
8188
|
+
}
|
|
8189
|
+
/** The landing the session remembers: what the first page stored, else what analytics resolved on this page load (consent pending); null otherwise. */
|
|
8190
|
+
rememberedLanding() {
|
|
8191
|
+
try {
|
|
8192
|
+
return storedLanding() ?? this.analyticsManager?.currentLanding ?? null;
|
|
8193
|
+
} catch {
|
|
8194
|
+
return null;
|
|
8195
|
+
}
|
|
8196
|
+
}
|
|
7257
8197
|
/**
|
|
7258
8198
|
* The cart split per campaign — the SAME rule `begin_checkout` / `purchase`
|
|
7259
8199
|
* use, for hosts with their own analytics sink (Vev). Every unit of every
|
|
@@ -7283,15 +8223,63 @@ var VioFacade = class {
|
|
|
7283
8223
|
return [];
|
|
7284
8224
|
}
|
|
7285
8225
|
}
|
|
8226
|
+
/**
|
|
8227
|
+
* What the micro did with the page's campaign per slot (`assignment`):
|
|
8228
|
+
* `console.debug` when `Vio.init({ debug: true })`; a warning once per
|
|
8229
|
+
* slot when the dashboard's own assignment stands (only for a slot this
|
|
8230
|
+
* page actually sent a campaign for) and once per slot when this page moved
|
|
8231
|
+
* it from an earlier page assignment (`replaced`).
|
|
8232
|
+
*/
|
|
8233
|
+
reportAssignments(result, sent, pageCampaignId, debug) {
|
|
8234
|
+
const answered = Array.isArray(result?.locations) ? result.locations : [];
|
|
8235
|
+
const campaignFor = (id) => sent.find((l) => l.id === id)?.campaignId ?? pageCampaignId;
|
|
8236
|
+
for (const l of answered) {
|
|
8237
|
+
if (!l || typeof l !== "object" || !l.assignment) continue;
|
|
8238
|
+
if (debug && typeof console !== "undefined") console.debug("[Vio] slot", l.locationId, "assignment:", l.assignment);
|
|
8239
|
+
const sentCampaign = campaignFor(l.locationId);
|
|
8240
|
+
if (sentCampaign === void 0) continue;
|
|
8241
|
+
if (l.assignment === "dashboard" && !this.warnedDashboardSlots.has(l.locationId)) {
|
|
8242
|
+
this.warnedDashboardSlots.add(l.locationId);
|
|
8243
|
+
warn(`slot ${l.locationId} is assigned from the dashboard; the page's campaign is ignored there`);
|
|
8244
|
+
}
|
|
8245
|
+
if (l.assignment === "replaced" && !this.warnedReplacedSlots.has(l.locationId)) {
|
|
8246
|
+
this.warnedReplacedSlots.add(l.locationId);
|
|
8247
|
+
warn(`slot ${l.locationId} moved to campaign ${sentCampaign} by this page (it replaced an earlier page assignment)`);
|
|
8248
|
+
}
|
|
8249
|
+
}
|
|
8250
|
+
}
|
|
8251
|
+
/**
|
|
8252
|
+
* The memory keys of the sent locations whose campaign the micro did not
|
|
8253
|
+
* take up: every location when a campaign was sent and no answer carries
|
|
8254
|
+
* `assignment` (an older micro), or the ones answered `none` for a campaign
|
|
8255
|
+
* the bootstrap lists as active. They are remembered with the 1 h TTL.
|
|
8256
|
+
*/
|
|
8257
|
+
campaignNotTakenUp(result, sent, pageCampaignId) {
|
|
8258
|
+
const out = /* @__PURE__ */ new Set();
|
|
8259
|
+
const withCampaign = sent.filter((l) => (l.campaignId ?? pageCampaignId) !== void 0);
|
|
8260
|
+
if (withCampaign.length === 0) return out;
|
|
8261
|
+
const answered = Array.isArray(result?.locations) ? result.locations : [];
|
|
8262
|
+
const anyAssignment = answered.some((l) => l && typeof l === "object" && !!l.assignment);
|
|
8263
|
+
for (const l of withCampaign) {
|
|
8264
|
+
const answer = answered.find((a) => a && a.locationId === l.id);
|
|
8265
|
+
const campaignId = l.campaignId ?? pageCampaignId;
|
|
8266
|
+
const notTaken = !anyAssignment || answer?.assignment === "none" && activeCampaignEntry(this.cachedBootstrap, campaignId) !== null;
|
|
8267
|
+
if (notTaken) out.add(slotDeclarationKey(l, pageCampaignId));
|
|
8268
|
+
}
|
|
8269
|
+
return out;
|
|
8270
|
+
}
|
|
7286
8271
|
scheduleSlotDeclaration() {
|
|
7287
8272
|
if (this.slotDeclareTimer) clearTimeout(this.slotDeclareTimer);
|
|
7288
8273
|
this.slotDeclareTimer = setTimeout(() => {
|
|
7289
8274
|
this.slotDeclareTimer = null;
|
|
7290
8275
|
this.slotsDirty = false;
|
|
7291
8276
|
void this.declareSlots(
|
|
7292
|
-
[...this.slotRegistry.entries()].map(
|
|
7293
|
-
|
|
7294
|
-
|
|
8277
|
+
[...this.slotRegistry.entries()].map(([id, { displayName, campaignId, kind }]) => ({
|
|
8278
|
+
id,
|
|
8279
|
+
...displayName ? { displayName } : {},
|
|
8280
|
+
...campaignId !== void 0 ? { campaignId } : {},
|
|
8281
|
+
...kind ? { kind } : {}
|
|
8282
|
+
}))
|
|
7295
8283
|
);
|
|
7296
8284
|
}, SLOT_DECLARATION_DEBOUNCE_MS);
|
|
7297
8285
|
}
|
|
@@ -7370,6 +8358,6 @@ function vippsButtonAttributes(verb = "buy", opts = {}) {
|
|
|
7370
8358
|
};
|
|
7371
8359
|
}
|
|
7372
8360
|
|
|
7373
|
-
export { ADD_ITEM_MUTATION, ADYEN_RETURN_METHOD, ADYEN_RETURN_QUERY_KEYS, AdyenOriginError, AnalyticsManager, CREATE_CART_MUTATION, CREATE_CHECKOUT_MUTATION, CREATE_PAYMENT_STRIPE_MUTATION, CREATE_PAYMENT_VIPPS_MUTATION, CartManager, CheckoutManager, CommerceClient, Configuration, DELETE_ITEM_MUTATION, GET_AVAILABLE_PAYMENT_METHODS_QUERY, GET_CART_QUERY, GET_CHECKOUT_QUERY, GET_LINE_ITEMS_BY_SUPPLIER_QUERY, GET_VIPPS_STATUS_QUERY, KUSTOM_RETURN_QUERY_KEYS, SDK_VERSION, UPDATE_CHECKOUT_MUTATION, UPDATE_ITEM_MUTATION, UPDATE_SHIPPINGS_BY_SUPPLIER_MUTATION, VIPPS_BUTTON_SCRIPT, VIPPS_BUTTON_TAG, Vio, VioApi, VioApiError, __decorateClass, addItem, adyenReturnOutcome, adyenReturnParams, autoSlotDisplayName, autoSlotId, cartSlicesFrom, checkApplePayAvailability, createCart, createCheckout, createCommerceClient, createPaymentStripe, createPaymentVipps, createVioApi, deleteItem, displayPrice, ensureVippsButton, executeCartGraphQL, formatPrice, getAvailablePaymentMethods, getCart, getCartGraphQLOptions, getCheckout, getCustomerSessionId, getGlobalCurrency, getLineItemsBySupplier, getVippsStatus, kustomReturnOutcome, lineOrigins, nexiReturnPaymentId, normalizeOrigin, normalizeScope, normalizeSlotId, primaryImageUrl, purchaseFromPaymentSuccess, readNexiPending, resolveCampaignId, resolveContent, resolveSlotCampaign, sameOrigin, showApplePaySheet, updateCheckout, updateItem, updateShippingsBySupplier, vippsButtonAttributes };
|
|
7374
|
-
//# sourceMappingURL=chunk-
|
|
7375
|
-
//# sourceMappingURL=chunk-
|
|
8361
|
+
export { ADD_ITEM_MUTATION, ADYEN_RETURN_METHOD, ADYEN_RETURN_QUERY_KEYS, AdyenOriginError, AnalyticsManager, BUYER_REF_MAX_LENGTH, CHECKOUT_ATTRIBUTION_CAPS, CLIENT_LANDING_URL_MAX_LENGTH, CONTENT_URL_MAX_LENGTH, CREATE_CART_MUTATION, CREATE_CHECKOUT_MUTATION, CREATE_CHECKOUT_WITH_ATTRIBUTION_MUTATION, CREATE_PAYMENT_STRIPE_MUTATION, CREATE_PAYMENT_VIPPS_MUTATION, CartManager, CheckoutManager, CommerceClient, Configuration, DELETE_ITEM_MUTATION, GET_AVAILABLE_PAYMENT_METHODS_QUERY, GET_CART_QUERY, GET_CHECKOUT_QUERY, GET_LINE_ITEMS_BY_SUPPLIER_QUERY, GET_VIPPS_STATUS_QUERY, GRAPHQL_INT_MAX, KUSTOM_RETURN_QUERY_KEYS, LINE_ORIGIN_CAPS, SDK_VERSION, UPDATE_CHECKOUT_MUTATION, UPDATE_CHECKOUT_WITH_ATTRIBUTION_MUTATION, UPDATE_ITEM_MUTATION, UPDATE_ITEM_WITH_ORIGIN_MUTATION, UPDATE_SHIPPINGS_BY_SUPPLIER_MUTATION, VIPPS_BUTTON_SCRIPT, VIPPS_BUTTON_TAG, Vio, VioApi, VioApiError, __decorateClass, activeCampaignEntry, addItem, adyenReturnOutcome, adyenReturnParams, autoSlotDisplayName, autoSlotId, buyerRefOf, campaignCandidates, capContentUrl, cartSlicesFrom, checkApplePayAvailability, checkoutAttributionOf, componentKindOf, createCart, createCheckout, createCommerceClient, createPaymentStripe, createPaymentVipps, createVioApi, deleteItem, displayPrice, dominantCampaignOf, ensureVippsButton, entryContextOf, executeCartGraphQL, formatPrice, gatewayHas, gatewayHasBuyerRef, getAvailablePaymentMethods, getCart, getCartGraphQLOptions, getCheckout, getCheckoutReference, getCustomerSessionId, getGlobalCurrency, getLineItemsBySupplier, getVippsStatus, isUnknownFeatureError, isUnknownTokenError, kustomReturnOutcome, landingUrl, lineOriginWire, lineOrigins, nexiReturnPaymentId, normalizeBuyerRef, normalizeOrigin, normalizeScope, normalizeSlotId, normalizeSlotKind, originAndPath, primaryImageUrl, purchaseFromPaymentSuccess, readNexiPending, resetGatewayFeatures, resolveCampaignId, resolveClientContext, resolveContent, resolveLanding, resolveSlotCampaign, sameOrigin, showApplePaySheet, sponsorCampaignEntry, storedLanding, toCampaignId, updateCheckout, updateItem, updateShippingsBySupplier, vippsButtonAttributes, withGatewayFeature };
|
|
8362
|
+
//# sourceMappingURL=chunk-5KWICUME.js.map
|
|
8363
|
+
//# sourceMappingURL=chunk-5KWICUME.js.map
|