@beworke/pixel-bemony 0.1.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +157 -0
- package/README.md +176 -0
- package/RELEASE_CANDIDATE.md +37 -0
- package/dist/browser/index.cjs.js +2598 -0
- package/dist/browser/index.cjs.js.map +1 -0
- package/dist/browser/index.d.mts +184 -0
- package/dist/browser/index.d.ts +184 -0
- package/dist/browser/index.esm.js +2557 -0
- package/dist/browser/index.esm.js.map +1 -0
- package/dist/browser/pixel-bemony.global.js +9848 -0
- package/dist/browser/pixel-bemony.global.js.map +1 -0
- package/dist/createBemonyPixel-BGRD66p-.d.mts +474 -0
- package/dist/createBemonyPixel-BGRD66p-.d.ts +474 -0
- package/dist/index.cjs.js +2154 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.mts +320 -0
- package/dist/index.d.ts +320 -0
- package/dist/index.esm.js +2081 -0
- package/dist/index.esm.js.map +1 -0
- package/docs/BACKEND_PURCHASE_AND_REDIRECTS.md +125 -0
- package/docs/BROWSER_EVENT_ENRICHMENT.md +116 -0
- package/docs/SCENARIOS.md +92 -0
- package/docs/SECURITY_LIMITATIONS.md +45 -0
- package/docs/TEST_MATRIX.md +39 -0
- package/docs/TROUBLESHOOTING.md +81 -0
- package/examples/README.md +73 -0
- package/examples/browser-bootstrap.html +47 -0
- package/examples/checkout-price-code.html +37 -0
- package/examples/funnel/donsell/downsell_1.html +130 -0
- package/examples/funnel/donsell/downsell_2.html +130 -0
- package/examples/funnel/donsell/downsell_3.html +130 -0
- package/examples/funnel/offer-page.html +105 -0
- package/examples/funnel/upsell/upsell_1.html +130 -0
- package/examples/funnel/upsell/upsell_2.html +130 -0
- package/examples/funnel/upsell/upsell_3.html +139 -0
- package/examples/offer-page-price-code.html +22 -0
- package/examples/one-click-price-code.html +38 -0
- package/examples/security/checkout-sim.css +408 -0
- package/examples/security/checkout-sim.js +414 -0
- package/examples/security/one-click/pri_downsell_1.html +85 -0
- package/examples/security/one-click/pri_downsell_1a.html +85 -0
- package/examples/security/one-click/pri_downsell_1b.html +78 -0
- package/examples/security/one-click/pri_downsell_2.html +78 -0
- package/examples/security/one-click/pri_downsell_2a.html +78 -0
- package/examples/security/one-click/pri_downsell_2b.html +78 -0
- package/examples/security/one-click/pri_downsell_3.html +78 -0
- package/examples/security/one-click/pri_downsell_3a.html +78 -0
- package/examples/security/one-click/pri_downsell_3b.html +78 -0
- package/examples/security/one-click/pri_upsell_1.html +78 -0
- package/examples/security/one-click/pri_upsell_1a.html +78 -0
- package/examples/security/one-click/pri_upsell_1b.html +78 -0
- package/examples/security/one-click/pri_upsell_2.html +78 -0
- package/examples/security/one-click/pri_upsell_2a.html +78 -0
- package/examples/security/one-click/pri_upsell_2b.html +78 -0
- package/examples/security/one-click/pri_upsell_3.html +78 -0
- package/examples/security/one-click/pri_upsell_3a.html +78 -0
- package/examples/security/one-click/pri_upsell_3b.html +78 -0
- package/examples/security/one-click/pri_upsell_3c.html +78 -0
- package/examples/security/pri1766322.html +333 -0
- package/examples/security/pri5529930.html +333 -0
- package/examples/security/pri6670988.html +334 -0
- package/examples/security/thank-you.html +65 -0
- package/examples/sim/funnel-flow.js +258 -0
- package/examples/sim/one-click-sim.js +167 -0
- package/examples/upsell-price-code.html +41 -0
- package/examples/url-context-navigation.html +96 -0
- package/package.json +63 -0
|
@@ -0,0 +1,2557 @@
|
|
|
1
|
+
// src/createBemonyPixel.ts
|
|
2
|
+
import { prepareJourneyUrlContext } from "@beworke/pixel";
|
|
3
|
+
import {
|
|
4
|
+
createStorageAdapter,
|
|
5
|
+
StorageServiceImpl
|
|
6
|
+
} from "@beworke/pixel/services";
|
|
7
|
+
|
|
8
|
+
// src/BemonyPixel.ts
|
|
9
|
+
import {
|
|
10
|
+
Pixel
|
|
11
|
+
} from "@beworke/pixel";
|
|
12
|
+
|
|
13
|
+
// src/domain/BemonyLeadInput.ts
|
|
14
|
+
var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
15
|
+
var BLOCKED_FIELD_KEY = /^(card|cvv|cvc|pan|number|expiry|expiration|password|token|cvv2)/i;
|
|
16
|
+
function isValidBemonyLead(input) {
|
|
17
|
+
const email = input.email?.trim();
|
|
18
|
+
const phone = input.phone?.trim();
|
|
19
|
+
const emailOk = Boolean(email && EMAIL_PATTERN.test(email));
|
|
20
|
+
const phoneDigits = phone ? phone.replace(/\D/g, "") : "";
|
|
21
|
+
const phoneOk = phoneDigits.length >= 8;
|
|
22
|
+
return emailOk || phoneOk;
|
|
23
|
+
}
|
|
24
|
+
function leadFingerprint(input) {
|
|
25
|
+
const email = input.email?.trim().toLowerCase() ?? "";
|
|
26
|
+
const phone = input.phone?.replace(/\D/g, "") ?? "";
|
|
27
|
+
const name = input.name?.trim().toLowerCase() ?? "";
|
|
28
|
+
return `${email}|${phone}|${name}`;
|
|
29
|
+
}
|
|
30
|
+
function sanitizeLeadFields(fields) {
|
|
31
|
+
if (!fields) {
|
|
32
|
+
return void 0;
|
|
33
|
+
}
|
|
34
|
+
const result = {};
|
|
35
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
36
|
+
if (BLOCKED_FIELD_KEY.test(key)) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
result[key] = value;
|
|
40
|
+
}
|
|
41
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/domain/BemonyPaymentMethodInput.ts
|
|
45
|
+
var BEMONY_PAYMENT_METHODS = [
|
|
46
|
+
"CREDIT_CARD",
|
|
47
|
+
"PIX",
|
|
48
|
+
"BOLETO",
|
|
49
|
+
"PAYPAL",
|
|
50
|
+
// 'GOOGLE_PAY',
|
|
51
|
+
// 'APPLE_PAY',
|
|
52
|
+
"OTHER"
|
|
53
|
+
];
|
|
54
|
+
function isValidBemonyPaymentMethod(input) {
|
|
55
|
+
if (!input) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
return BEMONY_PAYMENT_METHODS.includes(
|
|
59
|
+
input.paymentMethod
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/domain/resolvePriceCode.ts
|
|
64
|
+
function resolvePriceCode(input) {
|
|
65
|
+
if (!input) {
|
|
66
|
+
return { priceCode: null, conflict: false };
|
|
67
|
+
}
|
|
68
|
+
const fromCode = input.priceCode?.trim() || null;
|
|
69
|
+
const fromLegacy = input.priceId?.trim() || null;
|
|
70
|
+
if (fromCode && fromLegacy && fromCode !== fromLegacy) {
|
|
71
|
+
return { priceCode: fromCode, conflict: true };
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
priceCode: fromCode ?? fromLegacy,
|
|
75
|
+
conflict: false
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/BemonyPixel.ts
|
|
80
|
+
var SAFE_PUBLIC_ORDER_ID = /^[a-zA-Z0-9_.:-]+$/;
|
|
81
|
+
var MAX_PUBLIC_ORDER_ID_LENGTH = 128;
|
|
82
|
+
var BemonyLifecycleError = class extends Error {
|
|
83
|
+
constructor(options) {
|
|
84
|
+
super(options.message);
|
|
85
|
+
this.name = "BemonyLifecycleError";
|
|
86
|
+
this.code = options.code;
|
|
87
|
+
this.state = options.state;
|
|
88
|
+
if (options.cause !== void 0) {
|
|
89
|
+
this.cause = options.cause;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
function requirePriceCode(input, method, state) {
|
|
94
|
+
const { priceCode, conflict } = resolvePriceCode(input);
|
|
95
|
+
if (!priceCode) {
|
|
96
|
+
throw new BemonyLifecycleError({
|
|
97
|
+
code: "INVALID_STATE",
|
|
98
|
+
message: `BemonyPixel.${method} requires priceCode.`,
|
|
99
|
+
state
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
if (conflict && typeof console !== "undefined" && console.debug) {
|
|
103
|
+
console.debug("[BemonyPixel] priceCode/priceId conflict; using priceCode", {
|
|
104
|
+
code: "BEMONY_PRICE_CODE_CONFLICT",
|
|
105
|
+
method
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return priceCode;
|
|
109
|
+
}
|
|
110
|
+
function toViewContentData(input) {
|
|
111
|
+
const priceCode = resolvePriceCode(input).priceCode;
|
|
112
|
+
const contentType = input.productType === "downsell" ? "downsell" : input.productType === "upsell" ? "upsell" : "offer";
|
|
113
|
+
const observedValue = input.observedValue ?? input.price;
|
|
114
|
+
const observedCurrency = input.observedCurrency ?? input.currency;
|
|
115
|
+
return {
|
|
116
|
+
contentId: priceCode,
|
|
117
|
+
contentType,
|
|
118
|
+
productId: priceCode,
|
|
119
|
+
...observedValue !== void 0 ? { value: observedValue } : {},
|
|
120
|
+
...observedCurrency !== void 0 ? { currency: observedCurrency } : {}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function toAddToCartData(input) {
|
|
124
|
+
const priceCode = resolvePriceCode(input).priceCode;
|
|
125
|
+
const observedValue = input.observedValue ?? input.price;
|
|
126
|
+
const observedCurrency = input.observedCurrency ?? input.currency;
|
|
127
|
+
return {
|
|
128
|
+
productId: priceCode,
|
|
129
|
+
quantity: input.quantity ?? 1,
|
|
130
|
+
...observedValue !== void 0 ? { price: observedValue } : {},
|
|
131
|
+
...observedCurrency !== void 0 ? { currency: observedCurrency } : {}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function toLeadData(input) {
|
|
135
|
+
const fields = sanitizeLeadFields(input.fields);
|
|
136
|
+
return {
|
|
137
|
+
...input.email !== void 0 ? { email: input.email.trim() } : {},
|
|
138
|
+
...input.phone !== void 0 ? { phone: input.phone.trim() } : {},
|
|
139
|
+
...input.name !== void 0 ? { name: input.name.trim() } : {},
|
|
140
|
+
...fields !== void 0 ? { fields } : {}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
var BemonyPixel = class {
|
|
144
|
+
constructor(options) {
|
|
145
|
+
this.state = "created";
|
|
146
|
+
this.initPromise = null;
|
|
147
|
+
this.destroyPromise = null;
|
|
148
|
+
this.wrapperListeners = /* @__PURE__ */ new Set();
|
|
149
|
+
this.checkoutStarted = false;
|
|
150
|
+
this.lastLeadFingerprint = null;
|
|
151
|
+
this.selectedBumpIds = /* @__PURE__ */ new Set();
|
|
152
|
+
this.config = options.config;
|
|
153
|
+
this.scenario = options.scenario;
|
|
154
|
+
this.publicOrderId = options.publicOrderId?.trim() || void 0;
|
|
155
|
+
this.publicContext = options.publicContext ? { ...options.publicContext } : {};
|
|
156
|
+
this.autoInitiateCheckoutEnabled = options.autoInitiateCheckout !== false;
|
|
157
|
+
this.noThanksUrl = options.noThanksUrl?.trim() || void 0;
|
|
158
|
+
const createCore = options.createCorePixel ?? ((config) => new Pixel(config));
|
|
159
|
+
this.core = createCore(options.config);
|
|
160
|
+
}
|
|
161
|
+
getScenario() {
|
|
162
|
+
return this.scenario;
|
|
163
|
+
}
|
|
164
|
+
getConfig() {
|
|
165
|
+
return this.config;
|
|
166
|
+
}
|
|
167
|
+
getState() {
|
|
168
|
+
return this.state;
|
|
169
|
+
}
|
|
170
|
+
isInitialized() {
|
|
171
|
+
return this.state === "ready" && this.core.isInitialized();
|
|
172
|
+
}
|
|
173
|
+
getDebugSnapshot() {
|
|
174
|
+
return this.core.getDebugSnapshot();
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* `publicOrderId` do domínio do app (não mapeado para identity do Core).
|
|
178
|
+
* Fallback opcional: param `oid` da URL atual.
|
|
179
|
+
*/
|
|
180
|
+
getPublicOrderId() {
|
|
181
|
+
if (this.publicOrderId) {
|
|
182
|
+
return this.publicOrderId;
|
|
183
|
+
}
|
|
184
|
+
if (typeof window === "undefined" || typeof window.location === "undefined") {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
return new URL(window.location.href).searchParams.get("oid");
|
|
189
|
+
} catch {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Define `publicOrderId` e propaga para managed param `oid` quando suportado.
|
|
195
|
+
*/
|
|
196
|
+
setPublicOrderId(orderId) {
|
|
197
|
+
const trimmed = orderId.trim();
|
|
198
|
+
if (!trimmed || trimmed.length > MAX_PUBLIC_ORDER_ID_LENGTH) {
|
|
199
|
+
throw new BemonyLifecycleError({
|
|
200
|
+
code: "INVALID_STATE",
|
|
201
|
+
message: "setPublicOrderId requires a non-empty orderId within max length.",
|
|
202
|
+
state: this.state
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
if (!SAFE_PUBLIC_ORDER_ID.test(trimmed)) {
|
|
206
|
+
throw new BemonyLifecycleError({
|
|
207
|
+
code: "INVALID_STATE",
|
|
208
|
+
message: "setPublicOrderId orderId contains invalid characters.",
|
|
209
|
+
state: this.state
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
if (this.publicOrderId === trimmed) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
this.publicOrderId = trimmed;
|
|
216
|
+
this.applyPublicOrderIdToManagedParam();
|
|
217
|
+
}
|
|
218
|
+
getPublicSessionId() {
|
|
219
|
+
if (typeof window === "undefined" || typeof window.location === "undefined") {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
try {
|
|
223
|
+
return new URL(window.location.href).searchParams.get("sid");
|
|
224
|
+
} catch {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
selectOffer(input) {
|
|
229
|
+
this.assertReady("selectOffer");
|
|
230
|
+
requirePriceCode(input, "selectOffer", this.state);
|
|
231
|
+
this.core.addToCart(toAddToCartData(input));
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Inicia checkout → `Pixel.initiateCheckout()`.
|
|
235
|
+
* Idempotente: no máximo uma vez por lifecycle da instância.
|
|
236
|
+
*/
|
|
237
|
+
startCheckout(input) {
|
|
238
|
+
this.assertReady("startCheckout");
|
|
239
|
+
if (this.checkoutStarted) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const data = input ? {
|
|
243
|
+
...input.value !== void 0 ? { value: input.value } : {},
|
|
244
|
+
...input.currency !== void 0 ? { currency: input.currency } : {}
|
|
245
|
+
} : void 0;
|
|
246
|
+
this.core.initiateCheckout(data);
|
|
247
|
+
this.checkoutStarted = true;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Captura lead → `Pixel.lead()`.
|
|
251
|
+
* Campos incompletos não disparam. Mesmo fingerprint não reenvia (debounce).
|
|
252
|
+
*/
|
|
253
|
+
captureLead(input) {
|
|
254
|
+
this.assertReady("captureLead");
|
|
255
|
+
if (!isValidBemonyLead(input)) {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
const fingerprint = leadFingerprint(input);
|
|
259
|
+
if (this.lastLeadFingerprint === fingerprint) {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
this.core.lead(toLeadData(input));
|
|
263
|
+
this.lastLeadFingerprint = fingerprint;
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
selectOrderBump(input) {
|
|
267
|
+
this.assertReady("selectOrderBump");
|
|
268
|
+
const priceCode = requirePriceCode(input, "selectOrderBump", this.state);
|
|
269
|
+
this.core.addToCart(toAddToCartData(input));
|
|
270
|
+
this.selectedBumpIds.add(priceCode);
|
|
271
|
+
}
|
|
272
|
+
removeOrderBump(input) {
|
|
273
|
+
this.assertReady("removeOrderBump");
|
|
274
|
+
const priceCode = requirePriceCode(input, "removeOrderBump", this.state);
|
|
275
|
+
if (!this.selectedBumpIds.has(priceCode)) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
this.core.removeFromCart({
|
|
279
|
+
// No adaptador Bemony, o campo genérico productId transporta o priceCode.
|
|
280
|
+
productId: priceCode,
|
|
281
|
+
...input.quantity !== void 0 ? { quantity: input.quantity } : {}
|
|
282
|
+
});
|
|
283
|
+
this.selectedBumpIds.delete(priceCode);
|
|
284
|
+
}
|
|
285
|
+
trackPaymentMethod(input) {
|
|
286
|
+
this.assertReady("trackPaymentMethod");
|
|
287
|
+
if (!isValidBemonyPaymentMethod(input)) {
|
|
288
|
+
throw new BemonyLifecycleError({
|
|
289
|
+
code: "INVALID_STATE",
|
|
290
|
+
message: "trackPaymentMethod requires a valid paymentMethod enum.",
|
|
291
|
+
state: this.state
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
this.core.addPaymentInfo({ paymentMethod: input.paymentMethod });
|
|
295
|
+
}
|
|
296
|
+
viewUpsell(input) {
|
|
297
|
+
this.assertReady("viewUpsell");
|
|
298
|
+
requirePriceCode(input, "viewUpsell", this.state);
|
|
299
|
+
this.core.viewContent(toViewContentData(input));
|
|
300
|
+
}
|
|
301
|
+
acceptUpsell(input) {
|
|
302
|
+
this.assertReady("acceptUpsell");
|
|
303
|
+
requirePriceCode(input, "acceptUpsell", this.state);
|
|
304
|
+
this.core.addToCart(toAddToCartData(input));
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Recusa de upsell/downsell — callback/navegação apenas.
|
|
308
|
+
* Destino: `navigateTo` explícito, senão `noThanksUrl` (data-no-thanks).
|
|
309
|
+
*/
|
|
310
|
+
declineUpsell(options) {
|
|
311
|
+
this.assertReady("declineUpsell");
|
|
312
|
+
try {
|
|
313
|
+
options?.onDecline?.();
|
|
314
|
+
} finally {
|
|
315
|
+
const explicitTarget = options?.navigateTo?.trim() || this.noThanksUrl || void 0;
|
|
316
|
+
const target = explicitTarget ? this.decorateNavigateUrl(explicitTarget) : void 0;
|
|
317
|
+
if (target && typeof window !== "undefined" && typeof window.location?.assign === "function") {
|
|
318
|
+
window.location.assign(target);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Intenção de confirmação one-click → `Pixel.addToCart()`.
|
|
324
|
+
* Não processa pagamento e **não** emite PURCHASE.
|
|
325
|
+
*/
|
|
326
|
+
trackOneClickConfirmation(input) {
|
|
327
|
+
this.assertReady("trackOneClickConfirmation");
|
|
328
|
+
this.assertOneClickReady("trackOneClickConfirmation");
|
|
329
|
+
requirePriceCode(input, "trackOneClickConfirmation", this.state);
|
|
330
|
+
this.core.addToCart(toAddToCartData(input));
|
|
331
|
+
}
|
|
332
|
+
init() {
|
|
333
|
+
if (this.state === "ready") {
|
|
334
|
+
return Promise.resolve();
|
|
335
|
+
}
|
|
336
|
+
if (this.state === "initializing" && this.initPromise) {
|
|
337
|
+
return this.initPromise;
|
|
338
|
+
}
|
|
339
|
+
if (this.state === "destroying" && this.destroyPromise) {
|
|
340
|
+
this.initPromise = this.destroyPromise.catch(() => void 0).then(() => this.startInit());
|
|
341
|
+
return this.initPromise;
|
|
342
|
+
}
|
|
343
|
+
this.initPromise = this.startInit();
|
|
344
|
+
return this.initPromise;
|
|
345
|
+
}
|
|
346
|
+
destroy() {
|
|
347
|
+
if (this.state === "destroyed") {
|
|
348
|
+
return Promise.resolve();
|
|
349
|
+
}
|
|
350
|
+
if (this.state === "destroying" && this.destroyPromise) {
|
|
351
|
+
return this.destroyPromise;
|
|
352
|
+
}
|
|
353
|
+
if (this.state === "initializing" && this.initPromise) {
|
|
354
|
+
this.destroyPromise = this.initPromise.catch(() => void 0).then(() => this.startDestroy());
|
|
355
|
+
return this.destroyPromise;
|
|
356
|
+
}
|
|
357
|
+
this.destroyPromise = this.startDestroy();
|
|
358
|
+
return this.destroyPromise;
|
|
359
|
+
}
|
|
360
|
+
addWrapperListener(cleanup) {
|
|
361
|
+
this.wrapperListeners.add(cleanup);
|
|
362
|
+
return () => {
|
|
363
|
+
this.wrapperListeners.delete(cleanup);
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
assertReady(method) {
|
|
367
|
+
if (this.state !== "ready") {
|
|
368
|
+
throw new BemonyLifecycleError({
|
|
369
|
+
code: "INVALID_STATE",
|
|
370
|
+
message: `BemonyPixel.${method} requires state "ready" (current: "${this.state}").`,
|
|
371
|
+
state: this.state
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
assertOneClickReady(method) {
|
|
376
|
+
if (!this.getPublicOrderId()) {
|
|
377
|
+
throw new BemonyLifecycleError({
|
|
378
|
+
code: "INVALID_STATE",
|
|
379
|
+
message: `BemonyPixel.${method} requires publicOrderId.`,
|
|
380
|
+
state: this.state
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
applyPublicOrderIdToManagedParam() {
|
|
385
|
+
if (!this.publicOrderId || !this.core.setManagedUrlParam) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
this.core.setManagedUrlParam("oid", this.publicOrderId);
|
|
389
|
+
}
|
|
390
|
+
decorateNavigateUrl(rawUrl) {
|
|
391
|
+
try {
|
|
392
|
+
const base = typeof window !== "undefined" && window.location?.href ? window.location.href : "https://localhost/";
|
|
393
|
+
const url = new URL(rawUrl, base);
|
|
394
|
+
const sid = this.getPublicSessionId();
|
|
395
|
+
const oid = this.getPublicOrderId();
|
|
396
|
+
const fid = this.publicContext.funnelId;
|
|
397
|
+
const fsid = this.publicContext.funnelStepId;
|
|
398
|
+
const afid = this.publicContext.affiliateId;
|
|
399
|
+
if (sid && !url.searchParams.has("sid")) {
|
|
400
|
+
url.searchParams.set("sid", sid);
|
|
401
|
+
}
|
|
402
|
+
if (oid && !url.searchParams.has("oid")) {
|
|
403
|
+
url.searchParams.set("oid", oid);
|
|
404
|
+
}
|
|
405
|
+
if (fid && !url.searchParams.has("fid")) {
|
|
406
|
+
url.searchParams.set("fid", fid);
|
|
407
|
+
}
|
|
408
|
+
if (fsid && !url.searchParams.has("fsid")) {
|
|
409
|
+
url.searchParams.set("fsid", fsid);
|
|
410
|
+
}
|
|
411
|
+
if (afid && !url.searchParams.has("afid")) {
|
|
412
|
+
url.searchParams.set("afid", afid);
|
|
413
|
+
}
|
|
414
|
+
return url.toString();
|
|
415
|
+
} catch {
|
|
416
|
+
return rawUrl;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
maybeAutoInitiateCheckout() {
|
|
420
|
+
if (this.scenario !== "checkout" && this.scenario !== "one-click-checkout") {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
if (!this.autoInitiateCheckoutEnabled || this.checkoutStarted) {
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
this.core.initiateCheckout();
|
|
427
|
+
this.checkoutStarted = true;
|
|
428
|
+
}
|
|
429
|
+
resetDomainState() {
|
|
430
|
+
this.checkoutStarted = false;
|
|
431
|
+
this.lastLeadFingerprint = null;
|
|
432
|
+
this.selectedBumpIds.clear();
|
|
433
|
+
this.publicOrderId = void 0;
|
|
434
|
+
}
|
|
435
|
+
startInit() {
|
|
436
|
+
this.state = "initializing";
|
|
437
|
+
return this.core.init().then(() => {
|
|
438
|
+
if (this.publicOrderId) {
|
|
439
|
+
this.applyPublicOrderIdToManagedParam();
|
|
440
|
+
}
|
|
441
|
+
}).then(() => {
|
|
442
|
+
this.state = "ready";
|
|
443
|
+
this.maybeAutoInitiateCheckout();
|
|
444
|
+
}).catch((error) => {
|
|
445
|
+
this.state = "failed";
|
|
446
|
+
return this.safeCoreDestroy().then(() => {
|
|
447
|
+
throw new BemonyLifecycleError({
|
|
448
|
+
code: "INIT_FAILED",
|
|
449
|
+
message: "BemonyPixel init failed.",
|
|
450
|
+
state: this.state,
|
|
451
|
+
cause: error
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
}).finally(() => {
|
|
455
|
+
this.initPromise = null;
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
startDestroy() {
|
|
459
|
+
this.state = "destroying";
|
|
460
|
+
return this.safeCoreDestroy().then(() => {
|
|
461
|
+
this.clearWrapperListeners();
|
|
462
|
+
this.resetDomainState();
|
|
463
|
+
this.state = "destroyed";
|
|
464
|
+
}).catch((error) => {
|
|
465
|
+
this.clearWrapperListeners();
|
|
466
|
+
this.resetDomainState();
|
|
467
|
+
this.state = "destroyed";
|
|
468
|
+
throw new BemonyLifecycleError({
|
|
469
|
+
code: "DESTROY_FAILED",
|
|
470
|
+
message: "BemonyPixel destroy failed.",
|
|
471
|
+
state: this.state,
|
|
472
|
+
cause: error
|
|
473
|
+
});
|
|
474
|
+
}).finally(() => {
|
|
475
|
+
this.destroyPromise = null;
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
async safeCoreDestroy() {
|
|
479
|
+
await this.core.destroy();
|
|
480
|
+
}
|
|
481
|
+
clearWrapperListeners() {
|
|
482
|
+
for (const cleanup of this.wrapperListeners) {
|
|
483
|
+
try {
|
|
484
|
+
cleanup();
|
|
485
|
+
} catch {
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
this.wrapperListeners.clear();
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
// src/createBemonyPixelConfig.ts
|
|
493
|
+
import { DEFAULT_PIXEL_CONFIG } from "@beworke/pixel";
|
|
494
|
+
|
|
495
|
+
// src/domain/BemonyPageContext.ts
|
|
496
|
+
var BemonyValidationError = class extends Error {
|
|
497
|
+
constructor(options) {
|
|
498
|
+
super(options.message);
|
|
499
|
+
this.name = "BemonyValidationError";
|
|
500
|
+
this.code = options.code;
|
|
501
|
+
this.field = options.field;
|
|
502
|
+
this.scenario = options.scenario;
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
function isBemonyPageContextInput(value) {
|
|
506
|
+
return typeof value === "object" && value !== null && "public" in value && typeof value.public === "object" && value.public !== null;
|
|
507
|
+
}
|
|
508
|
+
function normalizePublicPriceFields(publicContext) {
|
|
509
|
+
const priceCode = publicContext.priceCode?.trim() || void 0;
|
|
510
|
+
const priceId = publicContext.priceId?.trim() || void 0;
|
|
511
|
+
const { priceId: _legacy, ...rest } = publicContext;
|
|
512
|
+
if (priceCode) {
|
|
513
|
+
return { ...rest, priceCode };
|
|
514
|
+
}
|
|
515
|
+
if (priceId) {
|
|
516
|
+
return { ...rest, priceCode: priceId };
|
|
517
|
+
}
|
|
518
|
+
return { ...rest };
|
|
519
|
+
}
|
|
520
|
+
function normalizeBemonyPageContextInput(page) {
|
|
521
|
+
if (!page) {
|
|
522
|
+
return { public: {} };
|
|
523
|
+
}
|
|
524
|
+
if (isBemonyPageContextInput(page)) {
|
|
525
|
+
return {
|
|
526
|
+
public: normalizePublicPriceFields({ ...page.public })
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
return {
|
|
530
|
+
public: normalizePublicPriceFields({ ...page })
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// src/domain/resolveBemonyPageContext.ts
|
|
535
|
+
var PUBLIC_URL_PARAM_TO_FIELD = {
|
|
536
|
+
page_id: "pageId",
|
|
537
|
+
funnel_id: "funnelId",
|
|
538
|
+
funnel_step_id: "funnelStepId",
|
|
539
|
+
offer_id: "offerId",
|
|
540
|
+
product_id: "productId",
|
|
541
|
+
price_code: "priceCode",
|
|
542
|
+
price_id: "priceCode",
|
|
543
|
+
checkout_id: "checkoutId",
|
|
544
|
+
afid: "affiliateId",
|
|
545
|
+
fid: "funnelId",
|
|
546
|
+
fsid: "funnelStepId",
|
|
547
|
+
oid: "publicOrderId"
|
|
548
|
+
};
|
|
549
|
+
function pickDefined(source) {
|
|
550
|
+
if (!source) {
|
|
551
|
+
return {};
|
|
552
|
+
}
|
|
553
|
+
const result = {};
|
|
554
|
+
for (const key of Object.keys(source)) {
|
|
555
|
+
const value = source[key];
|
|
556
|
+
if (value !== void 0 && value !== "") {
|
|
557
|
+
result[key] = value;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
return result;
|
|
561
|
+
}
|
|
562
|
+
function publicContextFromUrlParams(urlParams) {
|
|
563
|
+
if (!urlParams) {
|
|
564
|
+
return {};
|
|
565
|
+
}
|
|
566
|
+
const result = {};
|
|
567
|
+
for (const [rawKey, rawValue] of Object.entries(urlParams)) {
|
|
568
|
+
const field = PUBLIC_URL_PARAM_TO_FIELD[rawKey];
|
|
569
|
+
if (!field) {
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
if (rawValue === void 0 || rawValue === "") {
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
575
|
+
result[field] = rawValue;
|
|
576
|
+
}
|
|
577
|
+
return result;
|
|
578
|
+
}
|
|
579
|
+
function resolveBemonyPageContext(sources = {}) {
|
|
580
|
+
const fromScenario = pickDefined(sources.scenarioDefaults);
|
|
581
|
+
const fromMetadata = pickDefined(sources.pageMetadata);
|
|
582
|
+
const fromUrl = publicContextFromUrlParams(sources.urlParams);
|
|
583
|
+
const fromInput = pickDefined(sources.input);
|
|
584
|
+
return {
|
|
585
|
+
...fromScenario,
|
|
586
|
+
...fromMetadata,
|
|
587
|
+
...fromUrl,
|
|
588
|
+
...fromInput
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// src/domain/validateBemonyPageContext.ts
|
|
593
|
+
var MAX_ID_LENGTH = 128;
|
|
594
|
+
var SAFE_ID_PATTERN = /^[a-zA-Z0-9_.:-]+$/;
|
|
595
|
+
var REQUIRED_PUBLIC_FIELDS = {
|
|
596
|
+
"offer-page": [],
|
|
597
|
+
checkout: [],
|
|
598
|
+
"upsell-offer": [],
|
|
599
|
+
"one-click-checkout": [
|
|
600
|
+
"publicOrderId",
|
|
601
|
+
"funnelId",
|
|
602
|
+
"funnelStepId",
|
|
603
|
+
"priceCode"
|
|
604
|
+
],
|
|
605
|
+
"thank-you": []
|
|
606
|
+
};
|
|
607
|
+
function assertSafePublicId(field, value, scenario) {
|
|
608
|
+
const trimmed = value.trim();
|
|
609
|
+
if (!trimmed) {
|
|
610
|
+
throw new BemonyValidationError({
|
|
611
|
+
code: "INVALID_FIELD",
|
|
612
|
+
field,
|
|
613
|
+
scenario,
|
|
614
|
+
message: `Field "${field}" cannot be empty for scenario "${scenario}".`
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
if (trimmed.length > MAX_ID_LENGTH) {
|
|
618
|
+
throw new BemonyValidationError({
|
|
619
|
+
code: "INVALID_FIELD",
|
|
620
|
+
field,
|
|
621
|
+
scenario,
|
|
622
|
+
message: `Field "${field}" exceeds maximum length of ${MAX_ID_LENGTH}.`
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
if (!SAFE_ID_PATTERN.test(trimmed)) {
|
|
626
|
+
throw new BemonyValidationError({
|
|
627
|
+
code: "INVALID_FIELD",
|
|
628
|
+
field,
|
|
629
|
+
scenario,
|
|
630
|
+
message: `Field "${field}" contains invalid characters.`
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
return trimmed;
|
|
634
|
+
}
|
|
635
|
+
function validateBemonyPageContext(options) {
|
|
636
|
+
const { scenario } = options;
|
|
637
|
+
const sanitize = options.sanitize !== false;
|
|
638
|
+
const required = REQUIRED_PUBLIC_FIELDS[scenario];
|
|
639
|
+
const source = options.publicContext;
|
|
640
|
+
const result = {};
|
|
641
|
+
for (const field of Object.keys(
|
|
642
|
+
source
|
|
643
|
+
)) {
|
|
644
|
+
const value = source[field];
|
|
645
|
+
if (value === void 0) {
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
result[field] = sanitize ? assertSafePublicId(field, value, scenario) : value;
|
|
649
|
+
}
|
|
650
|
+
for (const field of required) {
|
|
651
|
+
if (!result[field]) {
|
|
652
|
+
throw new BemonyValidationError({
|
|
653
|
+
code: "MISSING_REQUIRED_FIELD",
|
|
654
|
+
field,
|
|
655
|
+
scenario,
|
|
656
|
+
message: `Field "${field}" is required for scenario "${scenario}".`
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
return result;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// src/platform/defaults.ts
|
|
664
|
+
var BEMONY_STORAGE_PREFIX = "__bemony_pixel";
|
|
665
|
+
var BEMONY_DEFAULT_NAMESPACE = "bemony-web";
|
|
666
|
+
var BEMONY_DEFAULT_API_ENDPOINT = "https://micro-service-pixel-production-4826.up.railway.app";
|
|
667
|
+
var BEMONY_DEFAULT_PIXEL_ID = "pix_7704308";
|
|
668
|
+
var BEMONY_DEFAULT_ALLOWED_DOMAINS = [
|
|
669
|
+
"security.bemony.com",
|
|
670
|
+
/** Serve HTTP local (`pnpm dlx serve`). */
|
|
671
|
+
"localhost",
|
|
672
|
+
"127.0.0.1",
|
|
673
|
+
/**
|
|
674
|
+
* Sentinel interno: permite links relativos / `file://` (hostname vazio)
|
|
675
|
+
* no LinkDecorator — só para simulação local dos examples.
|
|
676
|
+
*/
|
|
677
|
+
"local"
|
|
678
|
+
];
|
|
679
|
+
var BEMONY_DEFAULT_BROWSER_ENVIRONMENT = "production";
|
|
680
|
+
var BEMONY_DEFAULT_BROWSER_SCENARIO = "offer-page";
|
|
681
|
+
var BEMONY_BROWSER_AUTO_INIT_DEFAULTS = Object.freeze({
|
|
682
|
+
environment: BEMONY_DEFAULT_BROWSER_ENVIRONMENT,
|
|
683
|
+
scenario: BEMONY_DEFAULT_BROWSER_SCENARIO,
|
|
684
|
+
platform: Object.freeze({
|
|
685
|
+
pixelId: BEMONY_DEFAULT_PIXEL_ID,
|
|
686
|
+
apiEndpoint: BEMONY_DEFAULT_API_ENDPOINT,
|
|
687
|
+
namespace: BEMONY_DEFAULT_NAMESPACE,
|
|
688
|
+
allowedDomains: Object.freeze([...BEMONY_DEFAULT_ALLOWED_DOMAINS])
|
|
689
|
+
})
|
|
690
|
+
});
|
|
691
|
+
var BEMONY_PLATFORM_DEFAULTS = {
|
|
692
|
+
app: {
|
|
693
|
+
source: "bemony"
|
|
694
|
+
},
|
|
695
|
+
storage: {
|
|
696
|
+
prefix: BEMONY_STORAGE_PREFIX,
|
|
697
|
+
namespace: BEMONY_DEFAULT_NAMESPACE,
|
|
698
|
+
persistence: {
|
|
699
|
+
visitor: "localStorage",
|
|
700
|
+
session: "sessionStorage",
|
|
701
|
+
attribution: "localStorage"
|
|
702
|
+
}
|
|
703
|
+
},
|
|
704
|
+
attribution: {
|
|
705
|
+
enabled: true,
|
|
706
|
+
persist: true,
|
|
707
|
+
captureUrlParams: true,
|
|
708
|
+
captureCookies: true
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
function applyBemonyPlatformDefaults(base, platform) {
|
|
712
|
+
const namespace = platform.namespace ?? BEMONY_DEFAULT_NAMESPACE;
|
|
713
|
+
return {
|
|
714
|
+
...base,
|
|
715
|
+
app: {
|
|
716
|
+
...base.app,
|
|
717
|
+
source: BEMONY_PLATFORM_DEFAULTS.app.source
|
|
718
|
+
},
|
|
719
|
+
identity: {
|
|
720
|
+
...base.identity,
|
|
721
|
+
pixelId: platform.pixelId
|
|
722
|
+
},
|
|
723
|
+
storage: {
|
|
724
|
+
...base.storage,
|
|
725
|
+
prefix: BEMONY_PLATFORM_DEFAULTS.storage.prefix,
|
|
726
|
+
namespace,
|
|
727
|
+
persistence: {
|
|
728
|
+
...base.storage.persistence,
|
|
729
|
+
...BEMONY_PLATFORM_DEFAULTS.storage.persistence
|
|
730
|
+
}
|
|
731
|
+
},
|
|
732
|
+
attribution: {
|
|
733
|
+
...base.attribution,
|
|
734
|
+
...BEMONY_PLATFORM_DEFAULTS.attribution,
|
|
735
|
+
customClickIdParams: base.attribution.customClickIdParams ? [...base.attribution.customClickIdParams] : []
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// src/navigation/BemonyLinkPolicy.ts
|
|
741
|
+
function normalizeAllowedDomain(domain) {
|
|
742
|
+
let value = domain.trim().toLowerCase();
|
|
743
|
+
value = value.replace(/^https?:\/\//, "");
|
|
744
|
+
value = value.split("/")[0] ?? value;
|
|
745
|
+
value = value.split("?")[0] ?? value;
|
|
746
|
+
value = value.split("#")[0] ?? value;
|
|
747
|
+
const withoutPort = value.replace(/:\d+$/, "");
|
|
748
|
+
return withoutPort;
|
|
749
|
+
}
|
|
750
|
+
function normalizeAllowedDomains(domains) {
|
|
751
|
+
const seen = /* @__PURE__ */ new Set();
|
|
752
|
+
const result = [];
|
|
753
|
+
for (const raw of domains) {
|
|
754
|
+
const normalized = normalizeAllowedDomain(raw);
|
|
755
|
+
if (!normalized || seen.has(normalized)) continue;
|
|
756
|
+
seen.add(normalized);
|
|
757
|
+
result.push(normalized);
|
|
758
|
+
}
|
|
759
|
+
return result;
|
|
760
|
+
}
|
|
761
|
+
function createBemonyLinkPolicy(options = {}) {
|
|
762
|
+
const sourceDomains = options.allowedDomains !== void 0 && options.allowedDomains.length > 0 ? options.allowedDomains : BEMONY_DEFAULT_ALLOWED_DOMAINS;
|
|
763
|
+
const allowedDomains = normalizeAllowedDomains([...sourceDomains]);
|
|
764
|
+
const selector = options.selector?.trim();
|
|
765
|
+
return {
|
|
766
|
+
allowedDomains,
|
|
767
|
+
...selector ? { selector } : {},
|
|
768
|
+
strategy: options.strategy ?? "both",
|
|
769
|
+
preserveExisting: options.preserveExisting ?? true,
|
|
770
|
+
overwriteExisting: options.overwriteExisting ?? false,
|
|
771
|
+
decorateMailto: false,
|
|
772
|
+
decorateTel: false,
|
|
773
|
+
decorateHashLinks: false,
|
|
774
|
+
mergeDefaultExcludedDomains: true,
|
|
775
|
+
mergeDefaultExcludeSelectors: true,
|
|
776
|
+
excludeSelectors: options.excludeSelectors ? [...options.excludeSelectors] : void 0
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
function linkPolicyToDecorationConfig(policy) {
|
|
780
|
+
return {
|
|
781
|
+
enabled: true,
|
|
782
|
+
strategy: policy.strategy,
|
|
783
|
+
allowedDomains: [...policy.allowedDomains],
|
|
784
|
+
...policy.selector ? { selector: policy.selector } : {},
|
|
785
|
+
preserveExisting: policy.preserveExisting,
|
|
786
|
+
overwriteExisting: policy.overwriteExisting,
|
|
787
|
+
decorateMailto: policy.decorateMailto,
|
|
788
|
+
decorateTel: policy.decorateTel,
|
|
789
|
+
decorateHashLinks: policy.decorateHashLinks,
|
|
790
|
+
mergeDefaultExcludedDomains: policy.mergeDefaultExcludedDomains,
|
|
791
|
+
mergeDefaultExcludeSelectors: policy.mergeDefaultExcludeSelectors,
|
|
792
|
+
...policy.excludeSelectors ? { excludeSelectors: [...policy.excludeSelectors] } : {}
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// src/navigation/configureBemonyLinks.ts
|
|
797
|
+
var BEMONY_SID_MANAGED_PARAM = {
|
|
798
|
+
key: "public_session",
|
|
799
|
+
param: "sid",
|
|
800
|
+
mode: "read_write",
|
|
801
|
+
writeStrategy: "replaceState",
|
|
802
|
+
generator: { type: "public_id", prefix: "sid" },
|
|
803
|
+
persistence: {
|
|
804
|
+
enabled: true,
|
|
805
|
+
strategy: "localStorage_cookie",
|
|
806
|
+
storageKey: "sid"
|
|
807
|
+
},
|
|
808
|
+
decorateLinks: true
|
|
809
|
+
};
|
|
810
|
+
var BEMONY_AFID_MANAGED_PARAM = {
|
|
811
|
+
key: "affiliate",
|
|
812
|
+
param: "afid",
|
|
813
|
+
mode: "read_write",
|
|
814
|
+
writeStrategy: "replaceState",
|
|
815
|
+
persistence: {
|
|
816
|
+
enabled: true,
|
|
817
|
+
strategy: "localStorage_cookie",
|
|
818
|
+
storageKey: "afid"
|
|
819
|
+
},
|
|
820
|
+
decorateLinks: true
|
|
821
|
+
};
|
|
822
|
+
var BEMONY_OID_MANAGED_PARAM = {
|
|
823
|
+
key: "order_id",
|
|
824
|
+
param: "oid",
|
|
825
|
+
mode: "read_write",
|
|
826
|
+
writeStrategy: "replaceState",
|
|
827
|
+
persistence: {
|
|
828
|
+
enabled: true,
|
|
829
|
+
strategy: "localStorage_cookie",
|
|
830
|
+
storageKey: "oid"
|
|
831
|
+
},
|
|
832
|
+
decorateLinks: true
|
|
833
|
+
};
|
|
834
|
+
var BEMONY_FID_MANAGED_PARAM = {
|
|
835
|
+
key: "funnel_id",
|
|
836
|
+
param: "fid",
|
|
837
|
+
mode: "read_write",
|
|
838
|
+
writeStrategy: "replaceState",
|
|
839
|
+
persistence: {
|
|
840
|
+
enabled: true,
|
|
841
|
+
strategy: "localStorage_cookie",
|
|
842
|
+
storageKey: "fid"
|
|
843
|
+
},
|
|
844
|
+
decorateLinks: true
|
|
845
|
+
};
|
|
846
|
+
var BEMONY_FSID_MANAGED_PARAM = {
|
|
847
|
+
key: "funnel_step_id",
|
|
848
|
+
param: "fsid",
|
|
849
|
+
mode: "read_write",
|
|
850
|
+
writeStrategy: "replaceState",
|
|
851
|
+
persistence: {
|
|
852
|
+
enabled: true,
|
|
853
|
+
strategy: "localStorage_cookie",
|
|
854
|
+
storageKey: "fsid"
|
|
855
|
+
},
|
|
856
|
+
decorateLinks: true
|
|
857
|
+
};
|
|
858
|
+
var BEMONY_FORWARD_DENYLIST = [
|
|
859
|
+
"token",
|
|
860
|
+
"one_click_token",
|
|
861
|
+
"oneclicktoken",
|
|
862
|
+
"oneClickToken",
|
|
863
|
+
"ephemeral_token",
|
|
864
|
+
"payment_token",
|
|
865
|
+
"access_token",
|
|
866
|
+
"refresh_token",
|
|
867
|
+
"id_token",
|
|
868
|
+
"auth_token",
|
|
869
|
+
"secret",
|
|
870
|
+
"password",
|
|
871
|
+
"pass",
|
|
872
|
+
"pwd",
|
|
873
|
+
"email",
|
|
874
|
+
"phone",
|
|
875
|
+
"cpf",
|
|
876
|
+
"cnpj",
|
|
877
|
+
"document",
|
|
878
|
+
"card",
|
|
879
|
+
"cc",
|
|
880
|
+
"credit_card",
|
|
881
|
+
"pan",
|
|
882
|
+
"cvv",
|
|
883
|
+
"cvc",
|
|
884
|
+
"auth",
|
|
885
|
+
"code",
|
|
886
|
+
"sessionid",
|
|
887
|
+
"internalsessionid",
|
|
888
|
+
"pixelsessionid"
|
|
889
|
+
];
|
|
890
|
+
function cloneManagedParam(param) {
|
|
891
|
+
return {
|
|
892
|
+
...param,
|
|
893
|
+
...param.generator ? { generator: { ...param.generator } } : {},
|
|
894
|
+
...param.persistence ? {
|
|
895
|
+
persistence: {
|
|
896
|
+
...param.persistence,
|
|
897
|
+
...param.persistence.cookie ? { cookie: { ...param.persistence.cookie } } : {}
|
|
898
|
+
}
|
|
899
|
+
} : {}
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
function configureBemonyLinks(options = {}) {
|
|
903
|
+
const policy = createBemonyLinkPolicy(options);
|
|
904
|
+
const includeSid = options.includeSidManagedParam !== false;
|
|
905
|
+
const includeAfid = options.includeAfidManagedParam !== false;
|
|
906
|
+
const includeOid = options.includeOidManagedParam !== false;
|
|
907
|
+
const includeFunnel = options.includeFunnelManagedParams !== false;
|
|
908
|
+
const managedParams = [];
|
|
909
|
+
if (includeSid) {
|
|
910
|
+
managedParams.push(cloneManagedParam(BEMONY_SID_MANAGED_PARAM));
|
|
911
|
+
}
|
|
912
|
+
if (includeAfid) {
|
|
913
|
+
managedParams.push(cloneManagedParam(BEMONY_AFID_MANAGED_PARAM));
|
|
914
|
+
}
|
|
915
|
+
if (includeOid) {
|
|
916
|
+
managedParams.push(cloneManagedParam(BEMONY_OID_MANAGED_PARAM));
|
|
917
|
+
}
|
|
918
|
+
if (includeFunnel) {
|
|
919
|
+
managedParams.push(cloneManagedParam(BEMONY_FID_MANAGED_PARAM));
|
|
920
|
+
managedParams.push(cloneManagedParam(BEMONY_FSID_MANAGED_PARAM));
|
|
921
|
+
}
|
|
922
|
+
const denylist = [...BEMONY_FORWARD_DENYLIST];
|
|
923
|
+
return {
|
|
924
|
+
enabled: true,
|
|
925
|
+
queryParams: {
|
|
926
|
+
enabled: true,
|
|
927
|
+
mode: "all_except",
|
|
928
|
+
mergeDefaultDenylist: true,
|
|
929
|
+
denylist: [...denylist]
|
|
930
|
+
},
|
|
931
|
+
managedParams,
|
|
932
|
+
forwardParams: {
|
|
933
|
+
enabled: true,
|
|
934
|
+
mode: "all_except",
|
|
935
|
+
mergeDefaultDenylist: true,
|
|
936
|
+
denylist: [...denylist]
|
|
937
|
+
},
|
|
938
|
+
linkDecoration: linkPolicyToDecorationConfig(policy)
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// src/platform/environments.ts
|
|
943
|
+
var BEMONY_ENVIRONMENTS = [
|
|
944
|
+
"development",
|
|
945
|
+
"staging",
|
|
946
|
+
"production"
|
|
947
|
+
];
|
|
948
|
+
function isNonEmptyEndpoint(value) {
|
|
949
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
950
|
+
}
|
|
951
|
+
function resolveBemonyEnvironment(options) {
|
|
952
|
+
const { environment } = options;
|
|
953
|
+
if (environment === "development") {
|
|
954
|
+
return {
|
|
955
|
+
environment,
|
|
956
|
+
debug: options.debugOverride ?? true,
|
|
957
|
+
apiEndpoint: isNonEmptyEndpoint(options.apiEndpoint) ? options.apiEndpoint.trim() : "",
|
|
958
|
+
transportEnabled: false,
|
|
959
|
+
senderMode: "mock"
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
if (environment === "staging" || environment === "production") {
|
|
963
|
+
if (!isNonEmptyEndpoint(options.apiEndpoint)) {
|
|
964
|
+
throw new BemonyValidationError({
|
|
965
|
+
code: "MISSING_ENDPOINT",
|
|
966
|
+
field: "apiEndpoint",
|
|
967
|
+
message: `apiEndpoint is required for environment "${environment}". Inject an approved endpoint; do not hardcode credentials.`
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
return {
|
|
971
|
+
environment,
|
|
972
|
+
debug: options.debugOverride ?? false,
|
|
973
|
+
apiEndpoint: options.apiEndpoint.trim(),
|
|
974
|
+
transportEnabled: true,
|
|
975
|
+
senderMode: "http"
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
throw new BemonyValidationError({
|
|
979
|
+
code: "INVALID_ENVIRONMENT",
|
|
980
|
+
field: "environment",
|
|
981
|
+
message: "Unsupported Bemony environment."
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
function applyBemonyEnvironment(base, resolution) {
|
|
985
|
+
return {
|
|
986
|
+
...base,
|
|
987
|
+
app: {
|
|
988
|
+
...base.app,
|
|
989
|
+
environment: resolution.environment,
|
|
990
|
+
debug: resolution.debug
|
|
991
|
+
},
|
|
992
|
+
transport: {
|
|
993
|
+
...base.transport,
|
|
994
|
+
apiEndpoint: resolution.apiEndpoint
|
|
995
|
+
},
|
|
996
|
+
modules: {
|
|
997
|
+
...base.modules,
|
|
998
|
+
transport: {
|
|
999
|
+
...base.modules.transport,
|
|
1000
|
+
enabled: resolution.transportEnabled,
|
|
1001
|
+
sender: {
|
|
1002
|
+
...base.modules.transport.sender,
|
|
1003
|
+
mode: resolution.senderMode
|
|
1004
|
+
},
|
|
1005
|
+
mockSender: {
|
|
1006
|
+
...base.modules.transport.mockSender,
|
|
1007
|
+
enabled: resolution.senderMode === "mock"
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
// src/scenarios/checkout.ts
|
|
1015
|
+
function resolveMainProductId(products) {
|
|
1016
|
+
if (!products || products.length === 0) {
|
|
1017
|
+
return void 0;
|
|
1018
|
+
}
|
|
1019
|
+
const main = products.find((product) => product.type === "main");
|
|
1020
|
+
return (main ?? products[0])?.id;
|
|
1021
|
+
}
|
|
1022
|
+
function applyCheckoutScenario(base, options = {
|
|
1023
|
+
autoInitiateCheckout: true,
|
|
1024
|
+
includeOidManagedParam: true
|
|
1025
|
+
}) {
|
|
1026
|
+
const products = (options.products ?? []).map((product) => ({ ...product }));
|
|
1027
|
+
const currency = options.currency ?? base.commerce.currency ?? "BRL";
|
|
1028
|
+
const mainProductId = resolveMainProductId(products);
|
|
1029
|
+
const autoInitiateCheckout = options.autoInitiateCheckout !== false;
|
|
1030
|
+
const allowedDomains = normalizeAllowedDomains(
|
|
1031
|
+
options.allowedDomains !== void 0 && options.allowedDomains.length > 0 ? [...options.allowedDomains] : [...BEMONY_DEFAULT_ALLOWED_DOMAINS]
|
|
1032
|
+
);
|
|
1033
|
+
const urlContext = configureBemonyLinks({
|
|
1034
|
+
allowedDomains,
|
|
1035
|
+
includeOidManagedParam: options.includeOidManagedParam ?? true
|
|
1036
|
+
});
|
|
1037
|
+
const conversionManual = {
|
|
1038
|
+
...base.modules.conversion.manual,
|
|
1039
|
+
lead: { enabled: true },
|
|
1040
|
+
addToCart: { enabled: true },
|
|
1041
|
+
removeFromCart: { enabled: true },
|
|
1042
|
+
initiateCheckout: { enabled: true },
|
|
1043
|
+
addPaymentInfo: { enabled: true },
|
|
1044
|
+
viewContent: { enabled: false },
|
|
1045
|
+
pageView: { enabled: false },
|
|
1046
|
+
purchase: { enabled: false },
|
|
1047
|
+
search: { enabled: false }
|
|
1048
|
+
};
|
|
1049
|
+
return {
|
|
1050
|
+
...base,
|
|
1051
|
+
app: {
|
|
1052
|
+
...base.app,
|
|
1053
|
+
target: "checkout",
|
|
1054
|
+
source: "bemony"
|
|
1055
|
+
},
|
|
1056
|
+
commerce: {
|
|
1057
|
+
...base.commerce,
|
|
1058
|
+
enabled: true,
|
|
1059
|
+
currency,
|
|
1060
|
+
catalog: { products },
|
|
1061
|
+
cart: {
|
|
1062
|
+
...base.commerce.cart,
|
|
1063
|
+
mode: "checkout",
|
|
1064
|
+
initialItems: mainProductId ? [{ productId: mainProductId, quantity: 1 }] : [],
|
|
1065
|
+
allowDynamicItems: true,
|
|
1066
|
+
calculateValueFrom: "cart"
|
|
1067
|
+
},
|
|
1068
|
+
mappings: {
|
|
1069
|
+
attributes: { ...base.commerce.mappings?.attributes ?? {} },
|
|
1070
|
+
fields: { ...base.commerce.mappings?.fields ?? {} }
|
|
1071
|
+
},
|
|
1072
|
+
enrichment: {
|
|
1073
|
+
...base.commerce.enrichment ?? {},
|
|
1074
|
+
initiateCheckout: { enabled: true, source: "cart" },
|
|
1075
|
+
lead: { enabled: false, source: "event_or_cart" },
|
|
1076
|
+
addToCart: { enabled: true, source: "event_or_cart" },
|
|
1077
|
+
removeFromCart: { enabled: true, source: "event_or_cart" },
|
|
1078
|
+
addPaymentInfo: { enabled: true, source: "cart" },
|
|
1079
|
+
purchase: { enabled: false, source: "cart" },
|
|
1080
|
+
viewContent: { enabled: false, source: "event_or_cart" },
|
|
1081
|
+
pageView: { enabled: false, source: "cart" }
|
|
1082
|
+
}
|
|
1083
|
+
},
|
|
1084
|
+
modules: {
|
|
1085
|
+
...base.modules,
|
|
1086
|
+
core: {
|
|
1087
|
+
...base.modules.core,
|
|
1088
|
+
enabled: true,
|
|
1089
|
+
pageView: {
|
|
1090
|
+
...base.modules.core.pageView,
|
|
1091
|
+
enabled: true,
|
|
1092
|
+
trackInitialPageView: true
|
|
1093
|
+
},
|
|
1094
|
+
urlContext
|
|
1095
|
+
},
|
|
1096
|
+
conversion: {
|
|
1097
|
+
...base.modules.conversion,
|
|
1098
|
+
enabled: true,
|
|
1099
|
+
manual: conversionManual,
|
|
1100
|
+
auto: {
|
|
1101
|
+
enabled: true,
|
|
1102
|
+
detectors: {
|
|
1103
|
+
...base.modules.conversion.auto.detectors,
|
|
1104
|
+
addToCart: {
|
|
1105
|
+
enabled: true,
|
|
1106
|
+
sources: [
|
|
1107
|
+
{
|
|
1108
|
+
name: "main-product-after-contact",
|
|
1109
|
+
type: "form",
|
|
1110
|
+
selectorForm: {
|
|
1111
|
+
fields: ["email", "phone"],
|
|
1112
|
+
mode: "all",
|
|
1113
|
+
formSelector: "#checkout-form",
|
|
1114
|
+
trigger: "input",
|
|
1115
|
+
debounceMs: 300,
|
|
1116
|
+
fireOnce: true
|
|
1117
|
+
}
|
|
1118
|
+
},
|
|
1119
|
+
{
|
|
1120
|
+
name: "product-or-bump-click",
|
|
1121
|
+
type: "selector",
|
|
1122
|
+
selector: "add_to_cart",
|
|
1123
|
+
selectorType: "attribute",
|
|
1124
|
+
trigger: "click",
|
|
1125
|
+
fireOnce: false
|
|
1126
|
+
}
|
|
1127
|
+
]
|
|
1128
|
+
},
|
|
1129
|
+
removeFromCart: {
|
|
1130
|
+
enabled: true,
|
|
1131
|
+
sources: [
|
|
1132
|
+
{
|
|
1133
|
+
name: "remove-product-click",
|
|
1134
|
+
type: "selector",
|
|
1135
|
+
selector: "remove_from_cart",
|
|
1136
|
+
selectorType: "attribute",
|
|
1137
|
+
trigger: "click"
|
|
1138
|
+
}
|
|
1139
|
+
]
|
|
1140
|
+
},
|
|
1141
|
+
initiateCheckout: {
|
|
1142
|
+
enabled: autoInitiateCheckout,
|
|
1143
|
+
sources: autoInitiateCheckout ? [
|
|
1144
|
+
{
|
|
1145
|
+
name: "checkout-loaded",
|
|
1146
|
+
type: "lifecycle",
|
|
1147
|
+
trigger: "ready",
|
|
1148
|
+
fireOnce: true
|
|
1149
|
+
}
|
|
1150
|
+
] : []
|
|
1151
|
+
},
|
|
1152
|
+
addPaymentInfo: {
|
|
1153
|
+
enabled: true,
|
|
1154
|
+
sources: [
|
|
1155
|
+
{
|
|
1156
|
+
name: "credit-card-detection",
|
|
1157
|
+
type: "payment",
|
|
1158
|
+
paymentMethod: "CREDIT_CARD",
|
|
1159
|
+
cardDetection: true,
|
|
1160
|
+
autoDispatch: true,
|
|
1161
|
+
formSelector: "#checkout-form",
|
|
1162
|
+
fireOnce: true
|
|
1163
|
+
},
|
|
1164
|
+
{
|
|
1165
|
+
name: "payment-button-click",
|
|
1166
|
+
type: "selector",
|
|
1167
|
+
selector: '[data-track="add_payment_info"]',
|
|
1168
|
+
selectorType: "css",
|
|
1169
|
+
trigger: "click",
|
|
1170
|
+
fireOnce: false
|
|
1171
|
+
}
|
|
1172
|
+
]
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
},
|
|
1177
|
+
behavior: {
|
|
1178
|
+
...base.modules.behavior,
|
|
1179
|
+
enabled: false
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
// src/scenarios/one-click-checkout.ts
|
|
1186
|
+
function resolveCartMode(products) {
|
|
1187
|
+
const list = products ?? [];
|
|
1188
|
+
const isUpsellLike = list.some(
|
|
1189
|
+
(product) => product.type === "upsell" || product.type === "downsell"
|
|
1190
|
+
);
|
|
1191
|
+
return isUpsellLike ? "upsell" : "checkout";
|
|
1192
|
+
}
|
|
1193
|
+
function resolvePrimaryProductId(products) {
|
|
1194
|
+
if (!products || products.length === 0) {
|
|
1195
|
+
return void 0;
|
|
1196
|
+
}
|
|
1197
|
+
const preferred = products.find(
|
|
1198
|
+
(product) => product.type === "upsell" || product.type === "downsell" || product.type === "main"
|
|
1199
|
+
);
|
|
1200
|
+
return (preferred ?? products[0])?.id;
|
|
1201
|
+
}
|
|
1202
|
+
function applyOneClickCheckoutScenario(base, options = {}) {
|
|
1203
|
+
const products = (options.products ?? []).map((product) => ({ ...product }));
|
|
1204
|
+
const currency = options.currency ?? base.commerce.currency ?? "BRL";
|
|
1205
|
+
const cartMode = resolveCartMode(products);
|
|
1206
|
+
const primaryId = resolvePrimaryProductId(products);
|
|
1207
|
+
const urlContext = configureBemonyLinks({
|
|
1208
|
+
allowedDomains: options.allowedDomains ? [...options.allowedDomains] : [],
|
|
1209
|
+
includeOidManagedParam: true
|
|
1210
|
+
});
|
|
1211
|
+
const conversionManual = {
|
|
1212
|
+
...base.modules.conversion.manual,
|
|
1213
|
+
initiateCheckout: { enabled: true },
|
|
1214
|
+
addToCart: { enabled: true },
|
|
1215
|
+
viewContent: { enabled: false },
|
|
1216
|
+
lead: { enabled: false },
|
|
1217
|
+
removeFromCart: { enabled: false },
|
|
1218
|
+
addPaymentInfo: { enabled: false },
|
|
1219
|
+
pageView: { enabled: false },
|
|
1220
|
+
purchase: { enabled: false },
|
|
1221
|
+
search: { enabled: false }
|
|
1222
|
+
};
|
|
1223
|
+
return {
|
|
1224
|
+
...base,
|
|
1225
|
+
app: {
|
|
1226
|
+
...base.app,
|
|
1227
|
+
target: "checkout"
|
|
1228
|
+
},
|
|
1229
|
+
commerce: {
|
|
1230
|
+
...base.commerce,
|
|
1231
|
+
enabled: true,
|
|
1232
|
+
currency,
|
|
1233
|
+
catalog: { products },
|
|
1234
|
+
cart: {
|
|
1235
|
+
...base.commerce.cart,
|
|
1236
|
+
mode: cartMode,
|
|
1237
|
+
initialItems: primaryId ? [{ productId: primaryId, quantity: 1 }] : [],
|
|
1238
|
+
allowDynamicItems: true,
|
|
1239
|
+
calculateValueFrom: "cart"
|
|
1240
|
+
},
|
|
1241
|
+
mappings: {
|
|
1242
|
+
attributes: { ...base.commerce.mappings?.attributes ?? {} },
|
|
1243
|
+
fields: { ...base.commerce.mappings?.fields ?? {} }
|
|
1244
|
+
},
|
|
1245
|
+
enrichment: {
|
|
1246
|
+
...base.commerce.enrichment ?? {},
|
|
1247
|
+
initiateCheckout: { enabled: true, source: "cart" },
|
|
1248
|
+
addToCart: { enabled: true, source: "event_or_cart" },
|
|
1249
|
+
purchase: { enabled: false, source: "cart" },
|
|
1250
|
+
lead: { enabled: false, source: "event_or_cart" },
|
|
1251
|
+
viewContent: { enabled: false, source: "event_or_cart" },
|
|
1252
|
+
pageView: { enabled: false, source: "cart" }
|
|
1253
|
+
}
|
|
1254
|
+
},
|
|
1255
|
+
modules: {
|
|
1256
|
+
...base.modules,
|
|
1257
|
+
core: {
|
|
1258
|
+
...base.modules.core,
|
|
1259
|
+
enabled: true,
|
|
1260
|
+
pageView: {
|
|
1261
|
+
...base.modules.core.pageView,
|
|
1262
|
+
enabled: true,
|
|
1263
|
+
trackInitialPageView: true
|
|
1264
|
+
},
|
|
1265
|
+
urlContext
|
|
1266
|
+
},
|
|
1267
|
+
conversion: {
|
|
1268
|
+
...base.modules.conversion,
|
|
1269
|
+
enabled: true,
|
|
1270
|
+
manual: conversionManual,
|
|
1271
|
+
auto: {
|
|
1272
|
+
...base.modules.conversion.auto,
|
|
1273
|
+
enabled: false
|
|
1274
|
+
}
|
|
1275
|
+
},
|
|
1276
|
+
behavior: {
|
|
1277
|
+
...base.modules.behavior,
|
|
1278
|
+
enabled: false
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
// src/navigation/createBemonyOfferLinkSource.ts
|
|
1285
|
+
function createBemonyOfferLinkSource(allowedDomains) {
|
|
1286
|
+
const hostnames = normalizeAllowedDomains([...allowedDomains]).filter(
|
|
1287
|
+
(host) => host !== "local"
|
|
1288
|
+
);
|
|
1289
|
+
return {
|
|
1290
|
+
name: "bemony-offer-link",
|
|
1291
|
+
type: "link",
|
|
1292
|
+
enabled: true,
|
|
1293
|
+
trigger: "click",
|
|
1294
|
+
fireOnce: false,
|
|
1295
|
+
match: {
|
|
1296
|
+
protocols: ["https:", "http:"],
|
|
1297
|
+
hostnames,
|
|
1298
|
+
path: {
|
|
1299
|
+
segmentPrefixes: ["pri"]
|
|
1300
|
+
}
|
|
1301
|
+
},
|
|
1302
|
+
bindings: {
|
|
1303
|
+
// Core LinkAuto reconhece priceCode; productId no evento = priceCode observado.
|
|
1304
|
+
priceCode: {
|
|
1305
|
+
from: {
|
|
1306
|
+
source: "pathSegment",
|
|
1307
|
+
index: -1
|
|
1308
|
+
},
|
|
1309
|
+
required: true
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
// src/scenarios/offer-page.ts
|
|
1316
|
+
function applyOfferPageScenario(base, options = {}) {
|
|
1317
|
+
const cartMode = options.cartMode ?? "landing_page";
|
|
1318
|
+
const products = (options.products ?? []).map((product) => ({ ...product }));
|
|
1319
|
+
const currency = options.currency ?? base.commerce.currency ?? "BRL";
|
|
1320
|
+
const allowedDomains = normalizeAllowedDomains(
|
|
1321
|
+
options.allowedDomains !== void 0 && options.allowedDomains.length > 0 ? [...options.allowedDomains] : [...BEMONY_DEFAULT_ALLOWED_DOMAINS]
|
|
1322
|
+
);
|
|
1323
|
+
const urlContext = configureBemonyLinks({ allowedDomains });
|
|
1324
|
+
const offerLinkSource = createBemonyOfferLinkSource(allowedDomains);
|
|
1325
|
+
const conversionManual = {
|
|
1326
|
+
...base.modules.conversion.manual,
|
|
1327
|
+
viewContent: { enabled: true },
|
|
1328
|
+
addToCart: { enabled: true },
|
|
1329
|
+
// Offer page não emite estes manualmente nesta etapa.
|
|
1330
|
+
pageView: { enabled: false },
|
|
1331
|
+
purchase: { enabled: false },
|
|
1332
|
+
lead: { enabled: false },
|
|
1333
|
+
removeFromCart: { enabled: false },
|
|
1334
|
+
initiateCheckout: { enabled: false },
|
|
1335
|
+
addPaymentInfo: { enabled: false },
|
|
1336
|
+
search: { enabled: false }
|
|
1337
|
+
};
|
|
1338
|
+
return {
|
|
1339
|
+
...base,
|
|
1340
|
+
app: {
|
|
1341
|
+
...base.app,
|
|
1342
|
+
target: "landing"
|
|
1343
|
+
},
|
|
1344
|
+
commerce: {
|
|
1345
|
+
...base.commerce,
|
|
1346
|
+
enabled: true,
|
|
1347
|
+
currency,
|
|
1348
|
+
catalog: { products },
|
|
1349
|
+
cart: {
|
|
1350
|
+
...base.commerce.cart,
|
|
1351
|
+
mode: cartMode,
|
|
1352
|
+
initialItems: base.commerce.cart?.initialItems ? base.commerce.cart.initialItems.map((item) => ({ ...item })) : [],
|
|
1353
|
+
allowDynamicItems: true,
|
|
1354
|
+
calculateValueFrom: "cart"
|
|
1355
|
+
},
|
|
1356
|
+
mappings: {
|
|
1357
|
+
attributes: { ...base.commerce.mappings?.attributes ?? {} },
|
|
1358
|
+
fields: { ...base.commerce.mappings?.fields ?? {} }
|
|
1359
|
+
},
|
|
1360
|
+
enrichment: {
|
|
1361
|
+
...base.commerce.enrichment ?? {},
|
|
1362
|
+
viewContent: { enabled: true, source: "event_or_cart" },
|
|
1363
|
+
addToCart: { enabled: true, source: "event_or_cart" },
|
|
1364
|
+
pageView: { enabled: false, source: "cart" },
|
|
1365
|
+
purchase: { enabled: false, source: "cart" },
|
|
1366
|
+
lead: { enabled: false, source: "event_or_cart" }
|
|
1367
|
+
}
|
|
1368
|
+
},
|
|
1369
|
+
modules: {
|
|
1370
|
+
...base.modules,
|
|
1371
|
+
core: {
|
|
1372
|
+
...base.modules.core,
|
|
1373
|
+
enabled: true,
|
|
1374
|
+
pageView: {
|
|
1375
|
+
...base.modules.core.pageView,
|
|
1376
|
+
enabled: true,
|
|
1377
|
+
trackInitialPageView: true
|
|
1378
|
+
},
|
|
1379
|
+
urlContext
|
|
1380
|
+
},
|
|
1381
|
+
conversion: {
|
|
1382
|
+
...base.modules.conversion,
|
|
1383
|
+
enabled: true,
|
|
1384
|
+
manual: conversionManual,
|
|
1385
|
+
auto: {
|
|
1386
|
+
...base.modules.conversion.auto,
|
|
1387
|
+
enabled: true,
|
|
1388
|
+
detectors: {
|
|
1389
|
+
...base.modules.conversion.auto.detectors,
|
|
1390
|
+
addToCart: {
|
|
1391
|
+
enabled: true,
|
|
1392
|
+
sources: [offerLinkSource]
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
},
|
|
1397
|
+
behavior: {
|
|
1398
|
+
...base.modules.behavior,
|
|
1399
|
+
enabled: false
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
// src/scenarios/thank-you.ts
|
|
1406
|
+
function applyThankYouScenario(base, options = {}) {
|
|
1407
|
+
const currency = options.currency ?? base.commerce.currency ?? "BRL";
|
|
1408
|
+
const urlContext = configureBemonyLinks({
|
|
1409
|
+
allowedDomains: options.allowedDomains ? [...options.allowedDomains] : [],
|
|
1410
|
+
includeOidManagedParam: true
|
|
1411
|
+
});
|
|
1412
|
+
const conversionManual = {
|
|
1413
|
+
...base.modules.conversion.manual,
|
|
1414
|
+
pageView: { enabled: false },
|
|
1415
|
+
purchase: { enabled: false },
|
|
1416
|
+
lead: { enabled: false },
|
|
1417
|
+
addToCart: { enabled: false },
|
|
1418
|
+
removeFromCart: { enabled: false },
|
|
1419
|
+
initiateCheckout: { enabled: false },
|
|
1420
|
+
addPaymentInfo: { enabled: false },
|
|
1421
|
+
search: { enabled: false },
|
|
1422
|
+
viewContent: { enabled: false }
|
|
1423
|
+
};
|
|
1424
|
+
return {
|
|
1425
|
+
...base,
|
|
1426
|
+
app: {
|
|
1427
|
+
...base.app,
|
|
1428
|
+
target: "custom"
|
|
1429
|
+
},
|
|
1430
|
+
commerce: {
|
|
1431
|
+
...base.commerce,
|
|
1432
|
+
enabled: false,
|
|
1433
|
+
currency,
|
|
1434
|
+
catalog: { products: [] },
|
|
1435
|
+
cart: {
|
|
1436
|
+
...base.commerce.cart,
|
|
1437
|
+
mode: "custom",
|
|
1438
|
+
initialItems: [],
|
|
1439
|
+
allowDynamicItems: false,
|
|
1440
|
+
calculateValueFrom: "cart"
|
|
1441
|
+
},
|
|
1442
|
+
enrichment: {
|
|
1443
|
+
...base.commerce.enrichment ?? {},
|
|
1444
|
+
purchase: { enabled: false, source: "cart" },
|
|
1445
|
+
pageView: { enabled: false, source: "cart" }
|
|
1446
|
+
}
|
|
1447
|
+
},
|
|
1448
|
+
modules: {
|
|
1449
|
+
...base.modules,
|
|
1450
|
+
core: {
|
|
1451
|
+
...base.modules.core,
|
|
1452
|
+
enabled: true,
|
|
1453
|
+
pageView: {
|
|
1454
|
+
...base.modules.core.pageView,
|
|
1455
|
+
enabled: true,
|
|
1456
|
+
trackInitialPageView: true
|
|
1457
|
+
},
|
|
1458
|
+
urlContext
|
|
1459
|
+
},
|
|
1460
|
+
conversion: {
|
|
1461
|
+
...base.modules.conversion,
|
|
1462
|
+
enabled: false,
|
|
1463
|
+
manual: conversionManual,
|
|
1464
|
+
auto: {
|
|
1465
|
+
...base.modules.conversion.auto,
|
|
1466
|
+
enabled: false
|
|
1467
|
+
}
|
|
1468
|
+
},
|
|
1469
|
+
behavior: {
|
|
1470
|
+
...base.modules.behavior,
|
|
1471
|
+
enabled: false
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
// src/scenarios/upsell-offer.ts
|
|
1478
|
+
var BEMONY_UPSELL_BUY_LINK_SELECTOR = 'a[href*="pri"], [data-bemony-offer][href]';
|
|
1479
|
+
function escapeCssAttributeValue(value) {
|
|
1480
|
+
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
|
|
1481
|
+
return CSS.escape(value);
|
|
1482
|
+
}
|
|
1483
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
1484
|
+
}
|
|
1485
|
+
function buildUpsellLinkSelector(noThanksUrl) {
|
|
1486
|
+
const raw = noThanksUrl?.trim();
|
|
1487
|
+
if (!raw) {
|
|
1488
|
+
return BEMONY_UPSELL_BUY_LINK_SELECTOR;
|
|
1489
|
+
}
|
|
1490
|
+
return `${BEMONY_UPSELL_BUY_LINK_SELECTOR}, a[href="${escapeCssAttributeValue(raw)}"]`;
|
|
1491
|
+
}
|
|
1492
|
+
function applyUpsellOfferScenario(base, options = {}) {
|
|
1493
|
+
const products = (options.products ?? []).map((product) => ({ ...product }));
|
|
1494
|
+
const currency = options.currency ?? base.commerce.currency ?? "BRL";
|
|
1495
|
+
const allowedDomains = normalizeAllowedDomains(
|
|
1496
|
+
options.allowedDomains !== void 0 && options.allowedDomains.length > 0 ? [...options.allowedDomains] : [...BEMONY_DEFAULT_ALLOWED_DOMAINS]
|
|
1497
|
+
);
|
|
1498
|
+
const urlContext = configureBemonyLinks({
|
|
1499
|
+
allowedDomains,
|
|
1500
|
+
selector: buildUpsellLinkSelector(options.noThanksUrl),
|
|
1501
|
+
includeOidManagedParam: options.includeOidManagedParam ?? true
|
|
1502
|
+
});
|
|
1503
|
+
const offerLinkSource = createBemonyOfferLinkSource(allowedDomains);
|
|
1504
|
+
const conversionManual = {
|
|
1505
|
+
...base.modules.conversion.manual,
|
|
1506
|
+
viewContent: { enabled: true },
|
|
1507
|
+
addToCart: { enabled: true },
|
|
1508
|
+
pageView: { enabled: false },
|
|
1509
|
+
purchase: { enabled: false },
|
|
1510
|
+
lead: { enabled: false },
|
|
1511
|
+
removeFromCart: { enabled: false },
|
|
1512
|
+
initiateCheckout: { enabled: false },
|
|
1513
|
+
addPaymentInfo: { enabled: false },
|
|
1514
|
+
search: { enabled: false }
|
|
1515
|
+
};
|
|
1516
|
+
return {
|
|
1517
|
+
...base,
|
|
1518
|
+
app: {
|
|
1519
|
+
...base.app,
|
|
1520
|
+
target: "custom"
|
|
1521
|
+
},
|
|
1522
|
+
commerce: {
|
|
1523
|
+
...base.commerce,
|
|
1524
|
+
enabled: true,
|
|
1525
|
+
currency,
|
|
1526
|
+
catalog: { products },
|
|
1527
|
+
cart: {
|
|
1528
|
+
...base.commerce.cart,
|
|
1529
|
+
mode: "upsell",
|
|
1530
|
+
initialItems: [],
|
|
1531
|
+
allowDynamicItems: true,
|
|
1532
|
+
calculateValueFrom: "cart"
|
|
1533
|
+
},
|
|
1534
|
+
mappings: {
|
|
1535
|
+
attributes: { ...base.commerce.mappings?.attributes ?? {} },
|
|
1536
|
+
fields: { ...base.commerce.mappings?.fields ?? {} }
|
|
1537
|
+
},
|
|
1538
|
+
enrichment: {
|
|
1539
|
+
...base.commerce.enrichment ?? {},
|
|
1540
|
+
viewContent: { enabled: true, source: "event_or_cart" },
|
|
1541
|
+
addToCart: { enabled: true, source: "event_or_cart" },
|
|
1542
|
+
pageView: { enabled: false, source: "cart" },
|
|
1543
|
+
purchase: { enabled: false, source: "cart" },
|
|
1544
|
+
lead: { enabled: false, source: "event_or_cart" },
|
|
1545
|
+
initiateCheckout: { enabled: false, source: "cart" }
|
|
1546
|
+
}
|
|
1547
|
+
},
|
|
1548
|
+
modules: {
|
|
1549
|
+
...base.modules,
|
|
1550
|
+
core: {
|
|
1551
|
+
...base.modules.core,
|
|
1552
|
+
enabled: true,
|
|
1553
|
+
pageView: {
|
|
1554
|
+
...base.modules.core.pageView,
|
|
1555
|
+
enabled: true,
|
|
1556
|
+
trackInitialPageView: true
|
|
1557
|
+
},
|
|
1558
|
+
urlContext
|
|
1559
|
+
},
|
|
1560
|
+
conversion: {
|
|
1561
|
+
...base.modules.conversion,
|
|
1562
|
+
enabled: true,
|
|
1563
|
+
manual: conversionManual,
|
|
1564
|
+
auto: {
|
|
1565
|
+
...base.modules.conversion.auto,
|
|
1566
|
+
enabled: true,
|
|
1567
|
+
detectors: {
|
|
1568
|
+
...base.modules.conversion.auto.detectors,
|
|
1569
|
+
addToCart: {
|
|
1570
|
+
enabled: true,
|
|
1571
|
+
sources: [offerLinkSource]
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
},
|
|
1576
|
+
behavior: {
|
|
1577
|
+
...base.modules.behavior,
|
|
1578
|
+
enabled: false
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
// src/createBemonyPixelConfig.ts
|
|
1585
|
+
var SCENARIO_TARGET = {
|
|
1586
|
+
"offer-page": "landing",
|
|
1587
|
+
checkout: "checkout",
|
|
1588
|
+
"upsell-offer": "custom",
|
|
1589
|
+
"one-click-checkout": "checkout",
|
|
1590
|
+
"thank-you": "custom"
|
|
1591
|
+
};
|
|
1592
|
+
var SCENARIO_CART_MODE = {
|
|
1593
|
+
"offer-page": "landing_page",
|
|
1594
|
+
checkout: "checkout",
|
|
1595
|
+
"upsell-offer": "upsell",
|
|
1596
|
+
"one-click-checkout": "checkout",
|
|
1597
|
+
"thank-you": "custom"
|
|
1598
|
+
};
|
|
1599
|
+
var PRODUCT_TYPES = /* @__PURE__ */ new Set([
|
|
1600
|
+
"main",
|
|
1601
|
+
"order_bump",
|
|
1602
|
+
"upsell",
|
|
1603
|
+
"downsell",
|
|
1604
|
+
"cross_sell",
|
|
1605
|
+
"addon",
|
|
1606
|
+
"subscription",
|
|
1607
|
+
"custom",
|
|
1608
|
+
"other"
|
|
1609
|
+
]);
|
|
1610
|
+
function cloneProducts(products) {
|
|
1611
|
+
if (!products) {
|
|
1612
|
+
return [];
|
|
1613
|
+
}
|
|
1614
|
+
return products.map((product) => {
|
|
1615
|
+
const type = product.type && PRODUCT_TYPES.has(product.type) ? product.type : void 0;
|
|
1616
|
+
return {
|
|
1617
|
+
id: product.id,
|
|
1618
|
+
...product.name !== void 0 ? { name: product.name } : {},
|
|
1619
|
+
...type !== void 0 ? { type } : {},
|
|
1620
|
+
...product.price !== void 0 ? { price: product.price } : {},
|
|
1621
|
+
...product.currency !== void 0 ? { currency: product.currency } : {},
|
|
1622
|
+
...product.quantity !== void 0 ? { quantity: product.quantity } : {}
|
|
1623
|
+
};
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
function resolveOfferPageCartMode(commerce) {
|
|
1627
|
+
if (commerce?.cartMode === "single_product") {
|
|
1628
|
+
return "single_product";
|
|
1629
|
+
}
|
|
1630
|
+
return "landing_page";
|
|
1631
|
+
}
|
|
1632
|
+
function mapPublicContextToIdentity(base, publicContext) {
|
|
1633
|
+
return {
|
|
1634
|
+
...base,
|
|
1635
|
+
...publicContext.pageId !== void 0 ? { pageId: publicContext.pageId } : {},
|
|
1636
|
+
...publicContext.funnelId !== void 0 ? { funnelId: publicContext.funnelId } : {},
|
|
1637
|
+
...publicContext.funnelStepId !== void 0 ? { funnelStepId: publicContext.funnelStepId } : {},
|
|
1638
|
+
...publicContext.offerId !== void 0 ? { offerId: publicContext.offerId } : {},
|
|
1639
|
+
...publicContext.affiliateId !== void 0 ? { affiliateId: publicContext.affiliateId } : {},
|
|
1640
|
+
...publicContext.checkoutId !== void 0 ? { checkoutId: publicContext.checkoutId } : {}
|
|
1641
|
+
};
|
|
1642
|
+
}
|
|
1643
|
+
function applyMinimalScenarioPreset(base, scenario, commerce, allowedDomains) {
|
|
1644
|
+
const products = cloneProducts(commerce?.products);
|
|
1645
|
+
const currency = commerce?.currency ?? base.commerce.currency ?? "BRL";
|
|
1646
|
+
const urlContext = configureBemonyLinks({
|
|
1647
|
+
allowedDomains: allowedDomains ? [...allowedDomains] : []
|
|
1648
|
+
});
|
|
1649
|
+
return {
|
|
1650
|
+
...base,
|
|
1651
|
+
app: {
|
|
1652
|
+
...base.app,
|
|
1653
|
+
target: SCENARIO_TARGET[scenario],
|
|
1654
|
+
debug: true
|
|
1655
|
+
},
|
|
1656
|
+
commerce: {
|
|
1657
|
+
...base.commerce,
|
|
1658
|
+
enabled: true,
|
|
1659
|
+
currency,
|
|
1660
|
+
catalog: {
|
|
1661
|
+
products
|
|
1662
|
+
},
|
|
1663
|
+
cart: {
|
|
1664
|
+
...base.commerce.cart,
|
|
1665
|
+
mode: SCENARIO_CART_MODE[scenario],
|
|
1666
|
+
initialItems: base.commerce.cart?.initialItems ? base.commerce.cart.initialItems.map((item) => ({ ...item })) : [],
|
|
1667
|
+
allowDynamicItems: true,
|
|
1668
|
+
calculateValueFrom: "cart"
|
|
1669
|
+
},
|
|
1670
|
+
mappings: {
|
|
1671
|
+
attributes: { ...base.commerce.mappings?.attributes ?? {} },
|
|
1672
|
+
fields: { ...base.commerce.mappings?.fields ?? {} }
|
|
1673
|
+
},
|
|
1674
|
+
enrichment: {
|
|
1675
|
+
...base.commerce.enrichment ?? {}
|
|
1676
|
+
}
|
|
1677
|
+
},
|
|
1678
|
+
modules: {
|
|
1679
|
+
...base.modules,
|
|
1680
|
+
core: {
|
|
1681
|
+
...base.modules.core,
|
|
1682
|
+
urlContext
|
|
1683
|
+
},
|
|
1684
|
+
conversion: {
|
|
1685
|
+
...base.modules.conversion,
|
|
1686
|
+
enabled: false
|
|
1687
|
+
},
|
|
1688
|
+
behavior: {
|
|
1689
|
+
...base.modules.behavior,
|
|
1690
|
+
enabled: false
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
};
|
|
1694
|
+
}
|
|
1695
|
+
function applyScenarioPreset(base, scenario, commerce, allowedDomains, autoInitiateCheckout, noThanksUrl) {
|
|
1696
|
+
if (scenario === "offer-page") {
|
|
1697
|
+
return applyOfferPageScenario(base, {
|
|
1698
|
+
allowedDomains,
|
|
1699
|
+
currency: commerce?.currency,
|
|
1700
|
+
cartMode: resolveOfferPageCartMode(commerce),
|
|
1701
|
+
products: cloneProducts(commerce?.products)
|
|
1702
|
+
});
|
|
1703
|
+
}
|
|
1704
|
+
if (scenario === "checkout") {
|
|
1705
|
+
return applyCheckoutScenario(base, {
|
|
1706
|
+
allowedDomains,
|
|
1707
|
+
currency: commerce?.currency,
|
|
1708
|
+
products: cloneProducts(commerce?.products),
|
|
1709
|
+
autoInitiateCheckout
|
|
1710
|
+
});
|
|
1711
|
+
}
|
|
1712
|
+
if (scenario === "upsell-offer") {
|
|
1713
|
+
return applyUpsellOfferScenario(base, {
|
|
1714
|
+
allowedDomains,
|
|
1715
|
+
currency: commerce?.currency,
|
|
1716
|
+
products: cloneProducts(commerce?.products),
|
|
1717
|
+
...noThanksUrl ? { noThanksUrl } : {}
|
|
1718
|
+
});
|
|
1719
|
+
}
|
|
1720
|
+
if (scenario === "one-click-checkout") {
|
|
1721
|
+
return applyOneClickCheckoutScenario(base, {
|
|
1722
|
+
allowedDomains,
|
|
1723
|
+
currency: commerce?.currency,
|
|
1724
|
+
products: cloneProducts(commerce?.products)
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1727
|
+
if (scenario === "thank-you") {
|
|
1728
|
+
return applyThankYouScenario(base, {
|
|
1729
|
+
allowedDomains,
|
|
1730
|
+
currency: commerce?.currency
|
|
1731
|
+
});
|
|
1732
|
+
}
|
|
1733
|
+
return applyMinimalScenarioPreset(base, scenario, commerce, allowedDomains);
|
|
1734
|
+
}
|
|
1735
|
+
function createBemonyPixelConfig(options) {
|
|
1736
|
+
const pageInput = normalizeBemonyPageContextInput(options.page);
|
|
1737
|
+
const resolvedPublic = resolveBemonyPageContext({
|
|
1738
|
+
input: pageInput.public,
|
|
1739
|
+
urlParams: options.urlParams,
|
|
1740
|
+
pageMetadata: options.pageMetadata,
|
|
1741
|
+
scenarioDefaults: options.scenarioDefaults
|
|
1742
|
+
});
|
|
1743
|
+
const validatedPublic = validateBemonyPageContext({
|
|
1744
|
+
scenario: options.scenario,
|
|
1745
|
+
publicContext: resolvedPublic
|
|
1746
|
+
});
|
|
1747
|
+
const apiEndpoint = options.overrides?.apiEndpoint ?? options.platform.apiEndpoint;
|
|
1748
|
+
const debugOverride = options.overrides?.debug ?? options.platform.debug;
|
|
1749
|
+
const allowedDomains = options.overrides?.allowedDomains ?? options.platform.allowedDomains;
|
|
1750
|
+
const envResolution = resolveBemonyEnvironment({
|
|
1751
|
+
environment: options.environment,
|
|
1752
|
+
apiEndpoint,
|
|
1753
|
+
debugOverride
|
|
1754
|
+
});
|
|
1755
|
+
let config = {
|
|
1756
|
+
...DEFAULT_PIXEL_CONFIG,
|
|
1757
|
+
app: { ...DEFAULT_PIXEL_CONFIG.app },
|
|
1758
|
+
identity: { ...DEFAULT_PIXEL_CONFIG.identity },
|
|
1759
|
+
transport: { ...DEFAULT_PIXEL_CONFIG.transport },
|
|
1760
|
+
storage: {
|
|
1761
|
+
...DEFAULT_PIXEL_CONFIG.storage,
|
|
1762
|
+
queue: DEFAULT_PIXEL_CONFIG.storage.queue ? { ...DEFAULT_PIXEL_CONFIG.storage.queue } : void 0,
|
|
1763
|
+
keys: DEFAULT_PIXEL_CONFIG.storage.keys ? { ...DEFAULT_PIXEL_CONFIG.storage.keys } : void 0,
|
|
1764
|
+
persistence: DEFAULT_PIXEL_CONFIG.storage.persistence ? { ...DEFAULT_PIXEL_CONFIG.storage.persistence } : void 0
|
|
1765
|
+
},
|
|
1766
|
+
attribution: {
|
|
1767
|
+
...DEFAULT_PIXEL_CONFIG.attribution,
|
|
1768
|
+
customClickIdParams: DEFAULT_PIXEL_CONFIG.attribution.customClickIdParams ? [...DEFAULT_PIXEL_CONFIG.attribution.customClickIdParams] : []
|
|
1769
|
+
},
|
|
1770
|
+
commerce: {
|
|
1771
|
+
...DEFAULT_PIXEL_CONFIG.commerce,
|
|
1772
|
+
catalog: {
|
|
1773
|
+
products: DEFAULT_PIXEL_CONFIG.commerce.catalog?.products ? DEFAULT_PIXEL_CONFIG.commerce.catalog.products.map((p) => ({
|
|
1774
|
+
...p
|
|
1775
|
+
})) : []
|
|
1776
|
+
},
|
|
1777
|
+
cart: {
|
|
1778
|
+
...DEFAULT_PIXEL_CONFIG.commerce.cart,
|
|
1779
|
+
initialItems: DEFAULT_PIXEL_CONFIG.commerce.cart?.initialItems ? DEFAULT_PIXEL_CONFIG.commerce.cart.initialItems.map((i) => ({
|
|
1780
|
+
...i
|
|
1781
|
+
})) : []
|
|
1782
|
+
},
|
|
1783
|
+
mappings: {
|
|
1784
|
+
attributes: {
|
|
1785
|
+
...DEFAULT_PIXEL_CONFIG.commerce.mappings?.attributes ?? {}
|
|
1786
|
+
},
|
|
1787
|
+
fields: { ...DEFAULT_PIXEL_CONFIG.commerce.mappings?.fields ?? {} }
|
|
1788
|
+
},
|
|
1789
|
+
enrichment: { ...DEFAULT_PIXEL_CONFIG.commerce.enrichment ?? {} }
|
|
1790
|
+
},
|
|
1791
|
+
modules: {
|
|
1792
|
+
core: { ...DEFAULT_PIXEL_CONFIG.modules.core },
|
|
1793
|
+
behavior: { ...DEFAULT_PIXEL_CONFIG.modules.behavior },
|
|
1794
|
+
conversion: { ...DEFAULT_PIXEL_CONFIG.modules.conversion },
|
|
1795
|
+
transport: {
|
|
1796
|
+
...DEFAULT_PIXEL_CONFIG.modules.transport,
|
|
1797
|
+
sender: { ...DEFAULT_PIXEL_CONFIG.modules.transport.sender },
|
|
1798
|
+
mockSender: { ...DEFAULT_PIXEL_CONFIG.modules.transport.mockSender }
|
|
1799
|
+
},
|
|
1800
|
+
integrations: { ...DEFAULT_PIXEL_CONFIG.modules.integrations },
|
|
1801
|
+
privacy: { ...DEFAULT_PIXEL_CONFIG.modules.privacy }
|
|
1802
|
+
}
|
|
1803
|
+
};
|
|
1804
|
+
config = applyBemonyPlatformDefaults(config, options.platform);
|
|
1805
|
+
config = applyBemonyEnvironment(config, envResolution);
|
|
1806
|
+
config = applyScenarioPreset(
|
|
1807
|
+
config,
|
|
1808
|
+
options.scenario,
|
|
1809
|
+
options.commerce,
|
|
1810
|
+
allowedDomains,
|
|
1811
|
+
options.autoInitiateCheckout,
|
|
1812
|
+
options.noThanksUrl
|
|
1813
|
+
);
|
|
1814
|
+
config = {
|
|
1815
|
+
...config,
|
|
1816
|
+
identity: mapPublicContextToIdentity(config.identity, validatedPublic)
|
|
1817
|
+
};
|
|
1818
|
+
return config;
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
// src/createBemonyPixel.ts
|
|
1822
|
+
function createJourneyStorage() {
|
|
1823
|
+
return new StorageServiceImpl(
|
|
1824
|
+
createStorageAdapter("localStorage"),
|
|
1825
|
+
"bemony",
|
|
1826
|
+
"urlctx"
|
|
1827
|
+
);
|
|
1828
|
+
}
|
|
1829
|
+
function seedJourneyManagedParams(journey, publicContext) {
|
|
1830
|
+
if (publicContext.funnelId) {
|
|
1831
|
+
journey.setManagedParam("fid", publicContext.funnelId);
|
|
1832
|
+
}
|
|
1833
|
+
if (publicContext.funnelStepId) {
|
|
1834
|
+
journey.setManagedParam("fsid", publicContext.funnelStepId);
|
|
1835
|
+
}
|
|
1836
|
+
if (publicContext.publicOrderId) {
|
|
1837
|
+
journey.setManagedParam("oid", publicContext.publicOrderId);
|
|
1838
|
+
}
|
|
1839
|
+
if (publicContext.affiliateId) {
|
|
1840
|
+
journey.setManagedParam("afid", publicContext.affiliateId);
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
async function createBemonyPixel(options) {
|
|
1844
|
+
const pageInput = normalizeBemonyPageContextInput(options.page);
|
|
1845
|
+
const publicContext = validateBemonyPageContext({
|
|
1846
|
+
scenario: options.scenario,
|
|
1847
|
+
publicContext: resolveBemonyPageContext({
|
|
1848
|
+
input: pageInput.public,
|
|
1849
|
+
urlParams: options.urlParams,
|
|
1850
|
+
pageMetadata: options.pageMetadata,
|
|
1851
|
+
scenarioDefaults: options.scenarioDefaults
|
|
1852
|
+
})
|
|
1853
|
+
});
|
|
1854
|
+
const allowedDomains = options.overrides?.allowedDomains ?? options.platform.allowedDomains;
|
|
1855
|
+
const journeyUrlConfig = configureBemonyLinks({
|
|
1856
|
+
allowedDomains: allowedDomains ? [...allowedDomains] : []
|
|
1857
|
+
});
|
|
1858
|
+
const journeyStorage = createJourneyStorage();
|
|
1859
|
+
const journey = prepareJourneyUrlContext(journeyUrlConfig, journeyStorage);
|
|
1860
|
+
seedJourneyManagedParams(journey, publicContext);
|
|
1861
|
+
const config = createBemonyPixelConfig({
|
|
1862
|
+
...options,
|
|
1863
|
+
page: { public: publicContext }
|
|
1864
|
+
});
|
|
1865
|
+
const construct = {
|
|
1866
|
+
config,
|
|
1867
|
+
scenario: options.scenario,
|
|
1868
|
+
publicOrderId: publicContext.publicOrderId,
|
|
1869
|
+
publicContext,
|
|
1870
|
+
autoInitiateCheckout: options.autoInitiateCheckout,
|
|
1871
|
+
...options.noThanksUrl ? { noThanksUrl: options.noThanksUrl } : {},
|
|
1872
|
+
...options.createCorePixel ? { createCorePixel: options.createCorePixel } : {}
|
|
1873
|
+
};
|
|
1874
|
+
try {
|
|
1875
|
+
const instance = new BemonyPixel(construct);
|
|
1876
|
+
await instance.init();
|
|
1877
|
+
return instance;
|
|
1878
|
+
} finally {
|
|
1879
|
+
journey.destroy();
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
// src/browser/errors.ts
|
|
1884
|
+
var BemonyBootstrapError = class extends Error {
|
|
1885
|
+
constructor(message, code, cause) {
|
|
1886
|
+
super(message);
|
|
1887
|
+
this.name = "BemonyBootstrapError";
|
|
1888
|
+
this.code = code;
|
|
1889
|
+
if (cause !== void 0) {
|
|
1890
|
+
this.cause = cause;
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
};
|
|
1894
|
+
|
|
1895
|
+
// src/browser/events.ts
|
|
1896
|
+
var BEMONY_PIXEL_READY_EVENT = "bemony:pixel:ready";
|
|
1897
|
+
var BEMONY_PIXEL_ERROR_EVENT = "bemony:pixel:error";
|
|
1898
|
+
var BEMONY_PIXEL_INITIALIZING_EVENT = "bemony:pixel:initializing";
|
|
1899
|
+
var BEMONY_PIXEL_DESTROYED_EVENT = "bemony:pixel:destroyed";
|
|
1900
|
+
function safeDispatch(type, detail) {
|
|
1901
|
+
if (typeof window === "undefined" || typeof CustomEvent === "undefined") {
|
|
1902
|
+
return;
|
|
1903
|
+
}
|
|
1904
|
+
try {
|
|
1905
|
+
window.dispatchEvent(new CustomEvent(type, { detail }));
|
|
1906
|
+
} catch {
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
function emitBemonyPixelReady(pixel) {
|
|
1910
|
+
safeDispatch(BEMONY_PIXEL_READY_EVENT, {
|
|
1911
|
+
pixel
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1914
|
+
function emitBemonyPixelError(code, message) {
|
|
1915
|
+
safeDispatch(BEMONY_PIXEL_ERROR_EVENT, {
|
|
1916
|
+
code,
|
|
1917
|
+
message
|
|
1918
|
+
});
|
|
1919
|
+
}
|
|
1920
|
+
function emitBemonyPixelInitializing() {
|
|
1921
|
+
safeDispatch(BEMONY_PIXEL_INITIALIZING_EVENT, {});
|
|
1922
|
+
}
|
|
1923
|
+
function emitBemonyPixelDestroyed() {
|
|
1924
|
+
safeDispatch(BEMONY_PIXEL_DESTROYED_EVENT, {});
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
// src/browser/bootstrap.ts
|
|
1928
|
+
var WINDOW_STORE_KEY = "__bemony_pixel_bootstrap__";
|
|
1929
|
+
function assertBrowser() {
|
|
1930
|
+
if (typeof window === "undefined") {
|
|
1931
|
+
throw new BemonyBootstrapError(
|
|
1932
|
+
"bootstrapBemonyPixel s\xF3 pode ser usado no browser (window ausente).",
|
|
1933
|
+
"NOT_BROWSER"
|
|
1934
|
+
);
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
function getStore() {
|
|
1938
|
+
assertBrowser();
|
|
1939
|
+
const win = window;
|
|
1940
|
+
if (!win[WINDOW_STORE_KEY]) {
|
|
1941
|
+
win[WINDOW_STORE_KEY] = {
|
|
1942
|
+
instance: null,
|
|
1943
|
+
pending: null,
|
|
1944
|
+
readyEmittedFor: null
|
|
1945
|
+
};
|
|
1946
|
+
}
|
|
1947
|
+
return win[WINDOW_STORE_KEY];
|
|
1948
|
+
}
|
|
1949
|
+
function isReusableInstance(instance) {
|
|
1950
|
+
const state = instance.getState();
|
|
1951
|
+
return state === "ready" || state === "initializing";
|
|
1952
|
+
}
|
|
1953
|
+
async function bootstrapBemonyPixel(options) {
|
|
1954
|
+
assertBrowser();
|
|
1955
|
+
const store = getStore();
|
|
1956
|
+
const { force = false, waitForConsent, ...pixelOptions } = options;
|
|
1957
|
+
if (!force) {
|
|
1958
|
+
if (store.pending) {
|
|
1959
|
+
return store.pending;
|
|
1960
|
+
}
|
|
1961
|
+
if (store.instance && isReusableInstance(store.instance)) {
|
|
1962
|
+
return store.instance;
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
if (force) {
|
|
1966
|
+
if (store.pending) {
|
|
1967
|
+
try {
|
|
1968
|
+
await store.pending;
|
|
1969
|
+
} catch {
|
|
1970
|
+
}
|
|
1971
|
+
store.pending = null;
|
|
1972
|
+
}
|
|
1973
|
+
if (store.instance) {
|
|
1974
|
+
try {
|
|
1975
|
+
await store.instance.destroy();
|
|
1976
|
+
} catch {
|
|
1977
|
+
}
|
|
1978
|
+
store.instance = null;
|
|
1979
|
+
store.readyEmittedFor = null;
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
if (waitForConsent) {
|
|
1983
|
+
const allowed = await waitForConsent();
|
|
1984
|
+
if (!allowed) {
|
|
1985
|
+
const error = new BemonyBootstrapError(
|
|
1986
|
+
"Consentimento negado \u2014 bootstrap abortado.",
|
|
1987
|
+
"CONSENT_DENIED"
|
|
1988
|
+
);
|
|
1989
|
+
emitBemonyPixelError(error.code, error.message);
|
|
1990
|
+
throw error;
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
emitBemonyPixelInitializing();
|
|
1994
|
+
const pending = createBemonyPixel(pixelOptions).then((instance) => {
|
|
1995
|
+
store.instance = instance;
|
|
1996
|
+
if (store.pending === pending) {
|
|
1997
|
+
store.pending = null;
|
|
1998
|
+
}
|
|
1999
|
+
if (store.readyEmittedFor !== instance) {
|
|
2000
|
+
emitBemonyPixelReady(instance);
|
|
2001
|
+
store.readyEmittedFor = instance;
|
|
2002
|
+
}
|
|
2003
|
+
return instance;
|
|
2004
|
+
}).catch((error) => {
|
|
2005
|
+
if (store.pending === pending) {
|
|
2006
|
+
store.pending = null;
|
|
2007
|
+
}
|
|
2008
|
+
const code = error instanceof BemonyBootstrapError ? error.code : "AUTO_INIT_FAILED";
|
|
2009
|
+
const message = error instanceof Error ? error.message : "Bootstrap falhou";
|
|
2010
|
+
emitBemonyPixelError(code, message);
|
|
2011
|
+
throw error;
|
|
2012
|
+
});
|
|
2013
|
+
store.pending = pending;
|
|
2014
|
+
return pending;
|
|
2015
|
+
}
|
|
2016
|
+
function getBootstrappedBemonyPixel() {
|
|
2017
|
+
if (typeof window === "undefined") {
|
|
2018
|
+
return null;
|
|
2019
|
+
}
|
|
2020
|
+
const store = window[WINDOW_STORE_KEY];
|
|
2021
|
+
return store?.instance ?? null;
|
|
2022
|
+
}
|
|
2023
|
+
function getBootstrappedBemonyPixelPending() {
|
|
2024
|
+
if (typeof window === "undefined") {
|
|
2025
|
+
return null;
|
|
2026
|
+
}
|
|
2027
|
+
const store = window[WINDOW_STORE_KEY];
|
|
2028
|
+
return store?.pending ?? null;
|
|
2029
|
+
}
|
|
2030
|
+
async function destroyBootstrappedBemonyPixel() {
|
|
2031
|
+
if (typeof window === "undefined") {
|
|
2032
|
+
return;
|
|
2033
|
+
}
|
|
2034
|
+
const store = getStore();
|
|
2035
|
+
if (store.pending) {
|
|
2036
|
+
try {
|
|
2037
|
+
await store.pending;
|
|
2038
|
+
} catch {
|
|
2039
|
+
}
|
|
2040
|
+
store.pending = null;
|
|
2041
|
+
}
|
|
2042
|
+
if (store.instance) {
|
|
2043
|
+
await store.instance.destroy();
|
|
2044
|
+
store.instance = null;
|
|
2045
|
+
store.readyEmittedFor = null;
|
|
2046
|
+
emitBemonyPixelDestroyed();
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
|
|
2050
|
+
// src/scenarios/types.ts
|
|
2051
|
+
var BEMONY_SCENARIOS = [
|
|
2052
|
+
"offer-page",
|
|
2053
|
+
"checkout",
|
|
2054
|
+
"upsell-offer",
|
|
2055
|
+
"one-click-checkout",
|
|
2056
|
+
"thank-you"
|
|
2057
|
+
];
|
|
2058
|
+
|
|
2059
|
+
// src/browser/resolveExecutingScript.ts
|
|
2060
|
+
var BEMONY_GLOBAL_BUNDLE_FILENAME = "pixel-bemony.global.js";
|
|
2061
|
+
var LEGACY_SCRIPT_SELECTOR = "script[data-bemony-pixel]";
|
|
2062
|
+
function isHtmlScript(el) {
|
|
2063
|
+
return !!el && typeof HTMLScriptElement !== "undefined" && el instanceof HTMLScriptElement;
|
|
2064
|
+
}
|
|
2065
|
+
function isBemonyGlobalBundleSrc(src) {
|
|
2066
|
+
if (!src || !src.trim()) {
|
|
2067
|
+
return false;
|
|
2068
|
+
}
|
|
2069
|
+
try {
|
|
2070
|
+
const base = typeof location !== "undefined" && location.href ? location.href : "https://example.invalid/";
|
|
2071
|
+
const url = new URL(src.trim(), base);
|
|
2072
|
+
const path = url.pathname.toLowerCase();
|
|
2073
|
+
return path === `/${BEMONY_GLOBAL_BUNDLE_FILENAME}` || path.endsWith(`/${BEMONY_GLOBAL_BUNDLE_FILENAME}`);
|
|
2074
|
+
} catch {
|
|
2075
|
+
const normalized = src.trim().toLowerCase().split(/[?#]/)[0] ?? "";
|
|
2076
|
+
return normalized === BEMONY_GLOBAL_BUNDLE_FILENAME || normalized.endsWith(`/${BEMONY_GLOBAL_BUNDLE_FILENAME}`);
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
function resolveBemonyExecutingScript(input = {}) {
|
|
2080
|
+
if (typeof document === "undefined") {
|
|
2081
|
+
return null;
|
|
2082
|
+
}
|
|
2083
|
+
const current = input.currentScript !== void 0 ? input.currentScript : typeof document.currentScript !== "undefined" ? document.currentScript : null;
|
|
2084
|
+
if (isHtmlScript(current)) {
|
|
2085
|
+
return current;
|
|
2086
|
+
}
|
|
2087
|
+
const root = input.root ?? document;
|
|
2088
|
+
const allScripts = Array.from(root.querySelectorAll("script"));
|
|
2089
|
+
const bySrc = allScripts.filter(
|
|
2090
|
+
(el) => isHtmlScript(el) && isBemonyGlobalBundleSrc(el.getAttribute("src"))
|
|
2091
|
+
);
|
|
2092
|
+
if (bySrc.length === 1) {
|
|
2093
|
+
return bySrc[0];
|
|
2094
|
+
}
|
|
2095
|
+
if (bySrc.length > 1) {
|
|
2096
|
+
throw new BemonyBootstrapError(
|
|
2097
|
+
`Encontrados ${bySrc.length} scripts com src de ${BEMONY_GLOBAL_BUNDLE_FILENAME}; n\xE3o \xE9 poss\xEDvel determinar qual est\xE1 em execu\xE7\xE3o.`,
|
|
2098
|
+
"AMBIGUOUS_SCRIPT"
|
|
2099
|
+
);
|
|
2100
|
+
}
|
|
2101
|
+
const legacy = Array.from(
|
|
2102
|
+
root.querySelectorAll(LEGACY_SCRIPT_SELECTOR)
|
|
2103
|
+
).filter(isHtmlScript);
|
|
2104
|
+
if (legacy.length === 1) {
|
|
2105
|
+
return legacy[0];
|
|
2106
|
+
}
|
|
2107
|
+
return null;
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
// src/browser/resolveAutoInitConfig.ts
|
|
2111
|
+
var BEMONY_FORBIDDEN_DATA_ATTRIBUTES = [
|
|
2112
|
+
"data-token",
|
|
2113
|
+
"data-one-click-token",
|
|
2114
|
+
"data-oneclick-token",
|
|
2115
|
+
"data-ephemeral-token",
|
|
2116
|
+
"data-payment-token",
|
|
2117
|
+
"data-oc-token",
|
|
2118
|
+
"data-secret",
|
|
2119
|
+
"data-auth-token"
|
|
2120
|
+
];
|
|
2121
|
+
var SCRIPT_SELECTOR = "script[data-bemony-pixel]";
|
|
2122
|
+
function isEnvironment(value) {
|
|
2123
|
+
return BEMONY_ENVIRONMENTS.includes(value);
|
|
2124
|
+
}
|
|
2125
|
+
function isScenario(value) {
|
|
2126
|
+
return BEMONY_SCENARIOS.includes(value);
|
|
2127
|
+
}
|
|
2128
|
+
function parseBooleanAttr(value) {
|
|
2129
|
+
if (value === null) return void 0;
|
|
2130
|
+
const normalized = value.trim().toLowerCase();
|
|
2131
|
+
if (normalized === "true" || normalized === "1" || normalized === "") {
|
|
2132
|
+
return true;
|
|
2133
|
+
}
|
|
2134
|
+
if (normalized === "false" || normalized === "0") {
|
|
2135
|
+
return false;
|
|
2136
|
+
}
|
|
2137
|
+
return void 0;
|
|
2138
|
+
}
|
|
2139
|
+
function isAutoInitEnabledFromAttr(value) {
|
|
2140
|
+
if (value === null || value === void 0) {
|
|
2141
|
+
return true;
|
|
2142
|
+
}
|
|
2143
|
+
const parsed = parseBooleanAttr(value);
|
|
2144
|
+
if (parsed === void 0) {
|
|
2145
|
+
throw new BemonyBootstrapError(
|
|
2146
|
+
`data-auto-init inv\xE1lido (recebido: ${value}). Use true/false/1/0.`,
|
|
2147
|
+
"INVALID_ATTRIBUTES"
|
|
2148
|
+
);
|
|
2149
|
+
}
|
|
2150
|
+
return parsed;
|
|
2151
|
+
}
|
|
2152
|
+
function assertNoForbiddenAttributes(el) {
|
|
2153
|
+
for (const name of BEMONY_FORBIDDEN_DATA_ATTRIBUTES) {
|
|
2154
|
+
if (el.hasAttribute(name)) {
|
|
2155
|
+
throw new BemonyBootstrapError(
|
|
2156
|
+
`Atributo proibido em script tag: ${name}. Token one-click n\xE3o pode ir em data-*.`,
|
|
2157
|
+
"FORBIDDEN_ATTRIBUTE"
|
|
2158
|
+
);
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
for (const attr of Array.from(el.attributes)) {
|
|
2162
|
+
if (attr.name.startsWith("data-") && attr.name.includes("token")) {
|
|
2163
|
+
throw new BemonyBootstrapError(
|
|
2164
|
+
`Atributo proibido em script tag: ${attr.name}. Token n\xE3o permitido em data-*.`,
|
|
2165
|
+
"FORBIDDEN_ATTRIBUTE"
|
|
2166
|
+
);
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
function nonEmpty(value) {
|
|
2171
|
+
if (value === null || value === void 0) return void 0;
|
|
2172
|
+
const trimmed = value.trim();
|
|
2173
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
2174
|
+
}
|
|
2175
|
+
function readPublicPageFromDataset(el) {
|
|
2176
|
+
const pageId = nonEmpty(el.getAttribute("data-page-id"));
|
|
2177
|
+
const funnelId = nonEmpty(el.getAttribute("data-funnel-id"));
|
|
2178
|
+
const funnelStepId = nonEmpty(el.getAttribute("data-funnel-step-id"));
|
|
2179
|
+
const offerId = nonEmpty(el.getAttribute("data-offer-id"));
|
|
2180
|
+
const productId = nonEmpty(el.getAttribute("data-product-id"));
|
|
2181
|
+
const priceCode = nonEmpty(el.getAttribute("data-price-code"));
|
|
2182
|
+
const checkoutId = nonEmpty(el.getAttribute("data-checkout-id"));
|
|
2183
|
+
const publicOrderId = nonEmpty(el.getAttribute("data-public-order-id"));
|
|
2184
|
+
if (!pageId && !funnelId && !funnelStepId && !offerId && !productId && !priceCode && !checkoutId && !publicOrderId) {
|
|
2185
|
+
return void 0;
|
|
2186
|
+
}
|
|
2187
|
+
return {
|
|
2188
|
+
...pageId ? { pageId } : {},
|
|
2189
|
+
...funnelId ? { funnelId } : {},
|
|
2190
|
+
...funnelStepId ? { funnelStepId } : {},
|
|
2191
|
+
...offerId ? { offerId } : {},
|
|
2192
|
+
...productId ? { productId } : {},
|
|
2193
|
+
...priceCode ? { priceCode } : {},
|
|
2194
|
+
...checkoutId ? { checkoutId } : {},
|
|
2195
|
+
...publicOrderId ? { publicOrderId } : {}
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
function cloneDefaults() {
|
|
2199
|
+
return {
|
|
2200
|
+
environment: BEMONY_BROWSER_AUTO_INIT_DEFAULTS.environment,
|
|
2201
|
+
scenario: BEMONY_BROWSER_AUTO_INIT_DEFAULTS.scenario,
|
|
2202
|
+
platform: {
|
|
2203
|
+
pixelId: BEMONY_BROWSER_AUTO_INIT_DEFAULTS.platform.pixelId,
|
|
2204
|
+
apiEndpoint: BEMONY_BROWSER_AUTO_INIT_DEFAULTS.platform.apiEndpoint,
|
|
2205
|
+
namespace: BEMONY_BROWSER_AUTO_INIT_DEFAULTS.platform.namespace,
|
|
2206
|
+
allowedDomains: [
|
|
2207
|
+
...BEMONY_BROWSER_AUTO_INIT_DEFAULTS.platform.allowedDomains
|
|
2208
|
+
]
|
|
2209
|
+
}
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
2212
|
+
function mergePlatform(base, overlay) {
|
|
2213
|
+
if (!overlay) return { ...base };
|
|
2214
|
+
const pixelId = overlay.pixelId !== void 0 && overlay.pixelId.trim() !== "" ? overlay.pixelId.trim() : base.pixelId;
|
|
2215
|
+
const apiEndpoint = overlay.apiEndpoint !== void 0 ? overlay.apiEndpoint.trim() || base.apiEndpoint : base.apiEndpoint;
|
|
2216
|
+
const namespace = overlay.namespace !== void 0 ? overlay.namespace.trim() || base.namespace : base.namespace;
|
|
2217
|
+
return {
|
|
2218
|
+
...base,
|
|
2219
|
+
pixelId,
|
|
2220
|
+
...apiEndpoint !== void 0 ? { apiEndpoint } : {},
|
|
2221
|
+
...namespace !== void 0 ? { namespace } : {},
|
|
2222
|
+
...overlay.debug !== void 0 ? { debug: overlay.debug } : {},
|
|
2223
|
+
allowedDomains: overlay.allowedDomains !== void 0 ? (() => {
|
|
2224
|
+
const cleaned = overlay.allowedDomains.filter(
|
|
2225
|
+
(d) => d.trim().length > 0
|
|
2226
|
+
);
|
|
2227
|
+
return cleaned.length > 0 ? cleaned : base.allowedDomains;
|
|
2228
|
+
})() : base.allowedDomains
|
|
2229
|
+
};
|
|
2230
|
+
}
|
|
2231
|
+
function mergePage(base, overlay) {
|
|
2232
|
+
if (!overlay && !base) return void 0;
|
|
2233
|
+
if (!overlay) return base;
|
|
2234
|
+
if (!base) return overlay;
|
|
2235
|
+
const baseNorm = base && "public" in base ? base : { public: base };
|
|
2236
|
+
const overlayNorm = overlay && "public" in overlay ? overlay : { public: overlay };
|
|
2237
|
+
const basePublic = baseNorm && "public" in baseNorm ? baseNorm.public ?? {} : {};
|
|
2238
|
+
const overlayPublic = overlayNorm && "public" in overlayNorm ? overlayNorm.public ?? {} : {};
|
|
2239
|
+
return {
|
|
2240
|
+
...baseNorm,
|
|
2241
|
+
...overlayNorm,
|
|
2242
|
+
public: {
|
|
2243
|
+
...basePublic,
|
|
2244
|
+
...overlayPublic
|
|
2245
|
+
}
|
|
2246
|
+
};
|
|
2247
|
+
}
|
|
2248
|
+
function mergeOverrides(base, overlay) {
|
|
2249
|
+
if (!overlay && !base) return void 0;
|
|
2250
|
+
if (!overlay) return base ? { ...base } : void 0;
|
|
2251
|
+
if (!base) return { ...overlay };
|
|
2252
|
+
const apiEndpoint = overlay.apiEndpoint !== void 0 ? overlay.apiEndpoint.trim() || base.apiEndpoint : base.apiEndpoint;
|
|
2253
|
+
return {
|
|
2254
|
+
...base,
|
|
2255
|
+
...overlay,
|
|
2256
|
+
...apiEndpoint !== void 0 ? { apiEndpoint } : {},
|
|
2257
|
+
allowedDomains: overlay.allowedDomains !== void 0 ? overlay.allowedDomains.filter((d) => d.trim().length > 0) : base.allowedDomains
|
|
2258
|
+
};
|
|
2259
|
+
}
|
|
2260
|
+
function mergeBemonyPixelOptions(base, overlay) {
|
|
2261
|
+
const environment = overlay.environment && isEnvironment(overlay.environment) ? overlay.environment : base.environment;
|
|
2262
|
+
const scenario = overlay.scenario && isScenario(overlay.scenario) ? overlay.scenario : base.scenario;
|
|
2263
|
+
const merged = {
|
|
2264
|
+
environment,
|
|
2265
|
+
scenario,
|
|
2266
|
+
platform: mergePlatform(base.platform, overlay.platform)
|
|
2267
|
+
};
|
|
2268
|
+
const page = mergePage(base.page, overlay.page);
|
|
2269
|
+
if (page !== void 0) merged.page = page;
|
|
2270
|
+
const commerce = overlay.commerce ?? base.commerce;
|
|
2271
|
+
if (commerce !== void 0) merged.commerce = commerce;
|
|
2272
|
+
const overrides = mergeOverrides(base.overrides, overlay.overrides);
|
|
2273
|
+
if (overrides !== void 0) merged.overrides = overrides;
|
|
2274
|
+
const autoInitiateCheckout = overlay.autoInitiateCheckout ?? base.autoInitiateCheckout;
|
|
2275
|
+
if (autoInitiateCheckout !== void 0) {
|
|
2276
|
+
merged.autoInitiateCheckout = autoInitiateCheckout;
|
|
2277
|
+
}
|
|
2278
|
+
const noThanksUrl = overlay.noThanksUrl ?? base.noThanksUrl;
|
|
2279
|
+
if (noThanksUrl !== void 0 && noThanksUrl.trim() !== "") {
|
|
2280
|
+
merged.noThanksUrl = noThanksUrl.trim();
|
|
2281
|
+
}
|
|
2282
|
+
if ("createCorePixel" in overlay && overlay.createCorePixel) {
|
|
2283
|
+
merged.createCorePixel = overlay.createCorePixel;
|
|
2284
|
+
} else if (base.createCorePixel) {
|
|
2285
|
+
merged.createCorePixel = base.createCorePixel;
|
|
2286
|
+
}
|
|
2287
|
+
return merged;
|
|
2288
|
+
}
|
|
2289
|
+
function parseBemonyScriptAttributes(el) {
|
|
2290
|
+
assertNoForbiddenAttributes(el);
|
|
2291
|
+
const htmlEl = el;
|
|
2292
|
+
const environmentRaw = nonEmpty(htmlEl.getAttribute("data-environment"));
|
|
2293
|
+
const scenarioRaw = nonEmpty(htmlEl.getAttribute("data-scenario"));
|
|
2294
|
+
if (environmentRaw !== void 0 && !isEnvironment(environmentRaw)) {
|
|
2295
|
+
throw new BemonyBootstrapError(
|
|
2296
|
+
`data-environment inv\xE1lido (recebido: ${environmentRaw}).`,
|
|
2297
|
+
"INVALID_ATTRIBUTES"
|
|
2298
|
+
);
|
|
2299
|
+
}
|
|
2300
|
+
if (scenarioRaw !== void 0 && !isScenario(scenarioRaw)) {
|
|
2301
|
+
throw new BemonyBootstrapError(
|
|
2302
|
+
`data-scenario inv\xE1lido (recebido: ${scenarioRaw}).`,
|
|
2303
|
+
"INVALID_ATTRIBUTES"
|
|
2304
|
+
);
|
|
2305
|
+
}
|
|
2306
|
+
const pixelId = nonEmpty(htmlEl.getAttribute("data-pixel-id")) ?? nonEmpty(htmlEl.getAttribute("data-funnel-id"));
|
|
2307
|
+
const namespace = nonEmpty(htmlEl.getAttribute("data-namespace"));
|
|
2308
|
+
const apiEndpoint = nonEmpty(htmlEl.getAttribute("data-api-endpoint"));
|
|
2309
|
+
const debug = parseBooleanAttr(htmlEl.getAttribute("data-debug"));
|
|
2310
|
+
const allowedDomainsRaw = nonEmpty(
|
|
2311
|
+
htmlEl.getAttribute("data-allowed-domains")
|
|
2312
|
+
);
|
|
2313
|
+
const allowedDomains = allowedDomainsRaw ? allowedDomainsRaw.split(",").map((d) => d.trim()).filter(Boolean) : void 0;
|
|
2314
|
+
const pagePublic = readPublicPageFromDataset(htmlEl);
|
|
2315
|
+
const partial = {};
|
|
2316
|
+
if (environmentRaw) partial.environment = environmentRaw;
|
|
2317
|
+
if (scenarioRaw) partial.scenario = scenarioRaw;
|
|
2318
|
+
if (pixelId || namespace || apiEndpoint || allowedDomains || debug !== void 0) {
|
|
2319
|
+
partial.platform = {
|
|
2320
|
+
pixelId: pixelId ?? BEMONY_DEFAULT_PIXEL_ID,
|
|
2321
|
+
...namespace ? { namespace } : {},
|
|
2322
|
+
...apiEndpoint ? { apiEndpoint } : {},
|
|
2323
|
+
...allowedDomains ? { allowedDomains } : {},
|
|
2324
|
+
...debug !== void 0 ? { debug } : {}
|
|
2325
|
+
};
|
|
2326
|
+
}
|
|
2327
|
+
if (pagePublic) {
|
|
2328
|
+
partial.page = { public: pagePublic };
|
|
2329
|
+
}
|
|
2330
|
+
if (apiEndpoint || debug !== void 0) {
|
|
2331
|
+
partial.overrides = {
|
|
2332
|
+
...apiEndpoint ? { apiEndpoint } : {},
|
|
2333
|
+
...debug !== void 0 ? { debug } : {}
|
|
2334
|
+
};
|
|
2335
|
+
}
|
|
2336
|
+
const autoInitiateCheckout = parseBooleanAttr(
|
|
2337
|
+
htmlEl.getAttribute("data-auto-initiate-checkout")
|
|
2338
|
+
);
|
|
2339
|
+
if (autoInitiateCheckout !== void 0) {
|
|
2340
|
+
partial.autoInitiateCheckout = autoInitiateCheckout;
|
|
2341
|
+
}
|
|
2342
|
+
const noThanksUrl = nonEmpty(htmlEl.getAttribute("data-no-thanks"));
|
|
2343
|
+
if (noThanksUrl) {
|
|
2344
|
+
partial.noThanksUrl = noThanksUrl;
|
|
2345
|
+
}
|
|
2346
|
+
return partial;
|
|
2347
|
+
}
|
|
2348
|
+
function readWindowConfig() {
|
|
2349
|
+
if (typeof window === "undefined") {
|
|
2350
|
+
return null;
|
|
2351
|
+
}
|
|
2352
|
+
const config = window.BemonyPixelConfig;
|
|
2353
|
+
return config && typeof config === "object" ? config : null;
|
|
2354
|
+
}
|
|
2355
|
+
function assertValidWindowConfigPartial(config) {
|
|
2356
|
+
if (config.environment !== void 0 && !isEnvironment(config.environment)) {
|
|
2357
|
+
throw new BemonyBootstrapError(
|
|
2358
|
+
"window.BemonyPixelConfig.environment inv\xE1lido.",
|
|
2359
|
+
"INVALID_CONFIG"
|
|
2360
|
+
);
|
|
2361
|
+
}
|
|
2362
|
+
if (config.scenario !== void 0 && !isScenario(config.scenario)) {
|
|
2363
|
+
throw new BemonyBootstrapError(
|
|
2364
|
+
"window.BemonyPixelConfig.scenario inv\xE1lido.",
|
|
2365
|
+
"INVALID_CONFIG"
|
|
2366
|
+
);
|
|
2367
|
+
}
|
|
2368
|
+
if (config.platform?.pixelId !== void 0 && config.platform.pixelId.trim() === "") {
|
|
2369
|
+
throw new BemonyBootstrapError(
|
|
2370
|
+
"window.BemonyPixelConfig.platform.pixelId n\xE3o pode ser vazio.",
|
|
2371
|
+
"INVALID_CONFIG"
|
|
2372
|
+
);
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
function resolveAutoInitConfig(input = {}) {
|
|
2376
|
+
const applyDefaults = input.applyDefaults !== false;
|
|
2377
|
+
if (typeof document === "undefined" && typeof window === "undefined") {
|
|
2378
|
+
if (input.config && applyDefaults) {
|
|
2379
|
+
return {
|
|
2380
|
+
options: mergeBemonyPixelOptions(cloneDefaults(), input.config),
|
|
2381
|
+
source: "explicit",
|
|
2382
|
+
sources: ["default", "explicit"],
|
|
2383
|
+
script: null
|
|
2384
|
+
};
|
|
2385
|
+
}
|
|
2386
|
+
if (input.config) {
|
|
2387
|
+
return {
|
|
2388
|
+
options: input.config,
|
|
2389
|
+
source: "explicit",
|
|
2390
|
+
sources: ["explicit"],
|
|
2391
|
+
script: null
|
|
2392
|
+
};
|
|
2393
|
+
}
|
|
2394
|
+
return null;
|
|
2395
|
+
}
|
|
2396
|
+
const sources = [];
|
|
2397
|
+
let options = applyDefaults ? cloneDefaults() : null;
|
|
2398
|
+
if (applyDefaults) {
|
|
2399
|
+
sources.push("default");
|
|
2400
|
+
}
|
|
2401
|
+
const windowConfig = readWindowConfig();
|
|
2402
|
+
if (windowConfig) {
|
|
2403
|
+
assertValidWindowConfigPartial(windowConfig);
|
|
2404
|
+
if (!options) {
|
|
2405
|
+
options = cloneDefaults();
|
|
2406
|
+
if (!sources.includes("default")) sources.push("default");
|
|
2407
|
+
}
|
|
2408
|
+
options = mergeBemonyPixelOptions(options, windowConfig);
|
|
2409
|
+
sources.push("window-config");
|
|
2410
|
+
}
|
|
2411
|
+
let script = input.script ?? null;
|
|
2412
|
+
if (script === null && input.script === void 0) {
|
|
2413
|
+
const root = input.root ?? (typeof document !== "undefined" ? document : void 0);
|
|
2414
|
+
try {
|
|
2415
|
+
script = resolveBemonyExecutingScript({
|
|
2416
|
+
...root ? { root } : {}
|
|
2417
|
+
});
|
|
2418
|
+
} catch (error) {
|
|
2419
|
+
if (error instanceof BemonyBootstrapError && error.code === "AMBIGUOUS_SCRIPT") {
|
|
2420
|
+
throw error;
|
|
2421
|
+
}
|
|
2422
|
+
script = null;
|
|
2423
|
+
}
|
|
2424
|
+
if (!script && root) {
|
|
2425
|
+
const legacyAuto = Array.from(
|
|
2426
|
+
root.querySelectorAll(`${SCRIPT_SELECTOR}[data-auto-init]`)
|
|
2427
|
+
);
|
|
2428
|
+
if (legacyAuto.length > 1) {
|
|
2429
|
+
throw new BemonyBootstrapError(
|
|
2430
|
+
`Encontrados ${legacyAuto.length} scripts com data-bemony-pixel + data-auto-init; use apenas um.`,
|
|
2431
|
+
"MULTIPLE_SCRIPTS"
|
|
2432
|
+
);
|
|
2433
|
+
}
|
|
2434
|
+
if (legacyAuto.length === 1) {
|
|
2435
|
+
script = legacyAuto[0];
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
if (script) {
|
|
2440
|
+
const fromAttrs = parseBemonyScriptAttributes(script);
|
|
2441
|
+
if (!options) {
|
|
2442
|
+
options = cloneDefaults();
|
|
2443
|
+
if (!sources.includes("default")) sources.push("default");
|
|
2444
|
+
}
|
|
2445
|
+
options = mergeBemonyPixelOptions(options, fromAttrs);
|
|
2446
|
+
sources.push("data-attributes");
|
|
2447
|
+
}
|
|
2448
|
+
if (input.config) {
|
|
2449
|
+
if (!options) {
|
|
2450
|
+
options = { ...input.config };
|
|
2451
|
+
sources.push("explicit");
|
|
2452
|
+
} else {
|
|
2453
|
+
options = mergeBemonyPixelOptions(options, input.config);
|
|
2454
|
+
sources.push("explicit");
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
if (!options) {
|
|
2458
|
+
return null;
|
|
2459
|
+
}
|
|
2460
|
+
if (!options.platform.pixelId?.trim()) {
|
|
2461
|
+
options = mergeBemonyPixelOptions(options, {
|
|
2462
|
+
platform: {
|
|
2463
|
+
pixelId: BEMONY_DEFAULT_PIXEL_ID,
|
|
2464
|
+
namespace: BEMONY_DEFAULT_NAMESPACE,
|
|
2465
|
+
apiEndpoint: BEMONY_DEFAULT_API_ENDPOINT
|
|
2466
|
+
}
|
|
2467
|
+
});
|
|
2468
|
+
}
|
|
2469
|
+
if ((options.environment === "staging" || options.environment === "production") && !options.platform.apiEndpoint?.trim() && !options.overrides?.apiEndpoint?.trim()) {
|
|
2470
|
+
options = mergeBemonyPixelOptions(options, {
|
|
2471
|
+
platform: {
|
|
2472
|
+
pixelId: options.platform.pixelId,
|
|
2473
|
+
apiEndpoint: BEMONY_DEFAULT_API_ENDPOINT
|
|
2474
|
+
}
|
|
2475
|
+
});
|
|
2476
|
+
}
|
|
2477
|
+
const highest = sources[sources.length - 1] ?? "default";
|
|
2478
|
+
const source = sources.length > 1 ? "merged" : highest;
|
|
2479
|
+
return {
|
|
2480
|
+
options,
|
|
2481
|
+
source: sources.length === 1 ? highest : source,
|
|
2482
|
+
sources,
|
|
2483
|
+
script
|
|
2484
|
+
};
|
|
2485
|
+
}
|
|
2486
|
+
|
|
2487
|
+
// src/browser/autoInit.ts
|
|
2488
|
+
async function installBemonyPixelAutoInit(options = {}) {
|
|
2489
|
+
if (typeof window === "undefined") {
|
|
2490
|
+
throw new BemonyBootstrapError(
|
|
2491
|
+
"installBemonyPixelAutoInit s\xF3 pode ser usado no browser.",
|
|
2492
|
+
"NOT_BROWSER"
|
|
2493
|
+
);
|
|
2494
|
+
}
|
|
2495
|
+
const script = options.script !== void 0 ? options.script : resolveBemonyExecutingScript({
|
|
2496
|
+
...options.root ? { root: options.root } : {}
|
|
2497
|
+
});
|
|
2498
|
+
if (script) {
|
|
2499
|
+
const autoInitAttr = script.getAttribute("data-auto-init");
|
|
2500
|
+
if (!options.config && autoInitAttr !== null && !isAutoInitEnabledFromAttr(autoInitAttr)) {
|
|
2501
|
+
return null;
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
const resolved = resolveAutoInitConfig({
|
|
2505
|
+
config: options.config,
|
|
2506
|
+
root: options.root,
|
|
2507
|
+
script,
|
|
2508
|
+
applyDefaults: options.applyDefaults
|
|
2509
|
+
});
|
|
2510
|
+
if (!resolved) {
|
|
2511
|
+
return null;
|
|
2512
|
+
}
|
|
2513
|
+
if (options.requireDataAutoInit && !options.config && !resolved.sources.includes("data-attributes")) {
|
|
2514
|
+
return null;
|
|
2515
|
+
}
|
|
2516
|
+
const bootstrapOptions = {
|
|
2517
|
+
...resolved.options,
|
|
2518
|
+
...options.force !== void 0 ? { force: options.force } : {},
|
|
2519
|
+
...options.waitForConsent ? { waitForConsent: options.waitForConsent } : {}
|
|
2520
|
+
};
|
|
2521
|
+
try {
|
|
2522
|
+
const pixel = await bootstrapBemonyPixel(bootstrapOptions);
|
|
2523
|
+
return { pixel, source: resolved.source };
|
|
2524
|
+
} catch (error) {
|
|
2525
|
+
if (error instanceof BemonyBootstrapError) {
|
|
2526
|
+
throw error;
|
|
2527
|
+
}
|
|
2528
|
+
throw new BemonyBootstrapError(
|
|
2529
|
+
error instanceof Error ? error.message : "Auto-init falhou",
|
|
2530
|
+
"AUTO_INIT_FAILED",
|
|
2531
|
+
error
|
|
2532
|
+
);
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
export {
|
|
2536
|
+
BEMONY_FORBIDDEN_DATA_ATTRIBUTES,
|
|
2537
|
+
BEMONY_GLOBAL_BUNDLE_FILENAME,
|
|
2538
|
+
BEMONY_PIXEL_DESTROYED_EVENT,
|
|
2539
|
+
BEMONY_PIXEL_ERROR_EVENT,
|
|
2540
|
+
BEMONY_PIXEL_INITIALIZING_EVENT,
|
|
2541
|
+
BEMONY_PIXEL_READY_EVENT,
|
|
2542
|
+
SCRIPT_SELECTOR as BEMONY_PIXEL_SCRIPT_SELECTOR,
|
|
2543
|
+
BemonyBootstrapError,
|
|
2544
|
+
bootstrapBemonyPixel,
|
|
2545
|
+
destroyBootstrappedBemonyPixel,
|
|
2546
|
+
getBootstrappedBemonyPixel,
|
|
2547
|
+
getBootstrappedBemonyPixelPending,
|
|
2548
|
+
installBemonyPixelAutoInit,
|
|
2549
|
+
isAutoInitEnabledFromAttr,
|
|
2550
|
+
isBemonyGlobalBundleSrc,
|
|
2551
|
+
mergeBemonyPixelOptions,
|
|
2552
|
+
parseBemonyScriptAttributes,
|
|
2553
|
+
parseBooleanAttr,
|
|
2554
|
+
resolveAutoInitConfig,
|
|
2555
|
+
resolveBemonyExecutingScript
|
|
2556
|
+
};
|
|
2557
|
+
//# sourceMappingURL=index.esm.js.map
|