@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,2081 @@
|
|
|
1
|
+
// src/createBemonyPixelConfig.ts
|
|
2
|
+
import { DEFAULT_PIXEL_CONFIG } from "@beworke/pixel";
|
|
3
|
+
|
|
4
|
+
// src/domain/BemonyPageContext.ts
|
|
5
|
+
var BemonyValidationError = class extends Error {
|
|
6
|
+
constructor(options) {
|
|
7
|
+
super(options.message);
|
|
8
|
+
this.name = "BemonyValidationError";
|
|
9
|
+
this.code = options.code;
|
|
10
|
+
this.field = options.field;
|
|
11
|
+
this.scenario = options.scenario;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
function isBemonyPageContextInput(value) {
|
|
15
|
+
return typeof value === "object" && value !== null && "public" in value && typeof value.public === "object" && value.public !== null;
|
|
16
|
+
}
|
|
17
|
+
function normalizePublicPriceFields(publicContext) {
|
|
18
|
+
const priceCode = publicContext.priceCode?.trim() || void 0;
|
|
19
|
+
const priceId = publicContext.priceId?.trim() || void 0;
|
|
20
|
+
const { priceId: _legacy, ...rest } = publicContext;
|
|
21
|
+
if (priceCode) {
|
|
22
|
+
return { ...rest, priceCode };
|
|
23
|
+
}
|
|
24
|
+
if (priceId) {
|
|
25
|
+
return { ...rest, priceCode: priceId };
|
|
26
|
+
}
|
|
27
|
+
return { ...rest };
|
|
28
|
+
}
|
|
29
|
+
function normalizeBemonyPageContextInput(page) {
|
|
30
|
+
if (!page) {
|
|
31
|
+
return { public: {} };
|
|
32
|
+
}
|
|
33
|
+
if (isBemonyPageContextInput(page)) {
|
|
34
|
+
return {
|
|
35
|
+
public: normalizePublicPriceFields({ ...page.public })
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
public: normalizePublicPriceFields({ ...page })
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/domain/resolveBemonyPageContext.ts
|
|
44
|
+
var PUBLIC_URL_PARAM_TO_FIELD = {
|
|
45
|
+
page_id: "pageId",
|
|
46
|
+
funnel_id: "funnelId",
|
|
47
|
+
funnel_step_id: "funnelStepId",
|
|
48
|
+
offer_id: "offerId",
|
|
49
|
+
product_id: "productId",
|
|
50
|
+
price_code: "priceCode",
|
|
51
|
+
price_id: "priceCode",
|
|
52
|
+
checkout_id: "checkoutId",
|
|
53
|
+
afid: "affiliateId",
|
|
54
|
+
fid: "funnelId",
|
|
55
|
+
fsid: "funnelStepId",
|
|
56
|
+
oid: "publicOrderId"
|
|
57
|
+
};
|
|
58
|
+
function pickDefined(source) {
|
|
59
|
+
if (!source) {
|
|
60
|
+
return {};
|
|
61
|
+
}
|
|
62
|
+
const result = {};
|
|
63
|
+
for (const key of Object.keys(source)) {
|
|
64
|
+
const value = source[key];
|
|
65
|
+
if (value !== void 0 && value !== "") {
|
|
66
|
+
result[key] = value;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
function publicContextFromUrlParams(urlParams) {
|
|
72
|
+
if (!urlParams) {
|
|
73
|
+
return {};
|
|
74
|
+
}
|
|
75
|
+
const result = {};
|
|
76
|
+
for (const [rawKey, rawValue] of Object.entries(urlParams)) {
|
|
77
|
+
const field = PUBLIC_URL_PARAM_TO_FIELD[rawKey];
|
|
78
|
+
if (!field) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (rawValue === void 0 || rawValue === "") {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
result[field] = rawValue;
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
function resolveBemonyPageContext(sources = {}) {
|
|
89
|
+
const fromScenario = pickDefined(sources.scenarioDefaults);
|
|
90
|
+
const fromMetadata = pickDefined(sources.pageMetadata);
|
|
91
|
+
const fromUrl = publicContextFromUrlParams(sources.urlParams);
|
|
92
|
+
const fromInput = pickDefined(sources.input);
|
|
93
|
+
return {
|
|
94
|
+
...fromScenario,
|
|
95
|
+
...fromMetadata,
|
|
96
|
+
...fromUrl,
|
|
97
|
+
...fromInput
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// src/domain/validateBemonyPageContext.ts
|
|
102
|
+
var MAX_ID_LENGTH = 128;
|
|
103
|
+
var SAFE_ID_PATTERN = /^[a-zA-Z0-9_.:-]+$/;
|
|
104
|
+
var REQUIRED_PUBLIC_FIELDS = {
|
|
105
|
+
"offer-page": [],
|
|
106
|
+
checkout: [],
|
|
107
|
+
"upsell-offer": [],
|
|
108
|
+
"one-click-checkout": [
|
|
109
|
+
"publicOrderId",
|
|
110
|
+
"funnelId",
|
|
111
|
+
"funnelStepId",
|
|
112
|
+
"priceCode"
|
|
113
|
+
],
|
|
114
|
+
"thank-you": []
|
|
115
|
+
};
|
|
116
|
+
function assertSafePublicId(field, value, scenario) {
|
|
117
|
+
const trimmed = value.trim();
|
|
118
|
+
if (!trimmed) {
|
|
119
|
+
throw new BemonyValidationError({
|
|
120
|
+
code: "INVALID_FIELD",
|
|
121
|
+
field,
|
|
122
|
+
scenario,
|
|
123
|
+
message: `Field "${field}" cannot be empty for scenario "${scenario}".`
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
if (trimmed.length > MAX_ID_LENGTH) {
|
|
127
|
+
throw new BemonyValidationError({
|
|
128
|
+
code: "INVALID_FIELD",
|
|
129
|
+
field,
|
|
130
|
+
scenario,
|
|
131
|
+
message: `Field "${field}" exceeds maximum length of ${MAX_ID_LENGTH}.`
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
if (!SAFE_ID_PATTERN.test(trimmed)) {
|
|
135
|
+
throw new BemonyValidationError({
|
|
136
|
+
code: "INVALID_FIELD",
|
|
137
|
+
field,
|
|
138
|
+
scenario,
|
|
139
|
+
message: `Field "${field}" contains invalid characters.`
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
return trimmed;
|
|
143
|
+
}
|
|
144
|
+
function validateBemonyPageContext(options) {
|
|
145
|
+
const { scenario } = options;
|
|
146
|
+
const sanitize = options.sanitize !== false;
|
|
147
|
+
const required = REQUIRED_PUBLIC_FIELDS[scenario];
|
|
148
|
+
const source = options.publicContext;
|
|
149
|
+
const result = {};
|
|
150
|
+
for (const field of Object.keys(
|
|
151
|
+
source
|
|
152
|
+
)) {
|
|
153
|
+
const value = source[field];
|
|
154
|
+
if (value === void 0) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
result[field] = sanitize ? assertSafePublicId(field, value, scenario) : value;
|
|
158
|
+
}
|
|
159
|
+
for (const field of required) {
|
|
160
|
+
if (!result[field]) {
|
|
161
|
+
throw new BemonyValidationError({
|
|
162
|
+
code: "MISSING_REQUIRED_FIELD",
|
|
163
|
+
field,
|
|
164
|
+
scenario,
|
|
165
|
+
message: `Field "${field}" is required for scenario "${scenario}".`
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// src/platform/defaults.ts
|
|
173
|
+
var BEMONY_STORAGE_PREFIX = "__bemony_pixel";
|
|
174
|
+
var BEMONY_DEFAULT_NAMESPACE = "bemony-web";
|
|
175
|
+
var BEMONY_DEFAULT_API_ENDPOINT = "https://micro-service-pixel-production-4826.up.railway.app";
|
|
176
|
+
var BEMONY_DEFAULT_PIXEL_ID = "pix_7704308";
|
|
177
|
+
var BEMONY_DEFAULT_ALLOWED_DOMAINS = [
|
|
178
|
+
"security.bemony.com",
|
|
179
|
+
/** Serve HTTP local (`pnpm dlx serve`). */
|
|
180
|
+
"localhost",
|
|
181
|
+
"127.0.0.1",
|
|
182
|
+
/**
|
|
183
|
+
* Sentinel interno: permite links relativos / `file://` (hostname vazio)
|
|
184
|
+
* no LinkDecorator — só para simulação local dos examples.
|
|
185
|
+
*/
|
|
186
|
+
"local"
|
|
187
|
+
];
|
|
188
|
+
var BEMONY_DEFAULT_BROWSER_ENVIRONMENT = "production";
|
|
189
|
+
var BEMONY_DEFAULT_BROWSER_SCENARIO = "offer-page";
|
|
190
|
+
var BEMONY_BROWSER_AUTO_INIT_DEFAULTS = Object.freeze({
|
|
191
|
+
environment: BEMONY_DEFAULT_BROWSER_ENVIRONMENT,
|
|
192
|
+
scenario: BEMONY_DEFAULT_BROWSER_SCENARIO,
|
|
193
|
+
platform: Object.freeze({
|
|
194
|
+
pixelId: BEMONY_DEFAULT_PIXEL_ID,
|
|
195
|
+
apiEndpoint: BEMONY_DEFAULT_API_ENDPOINT,
|
|
196
|
+
namespace: BEMONY_DEFAULT_NAMESPACE,
|
|
197
|
+
allowedDomains: Object.freeze([...BEMONY_DEFAULT_ALLOWED_DOMAINS])
|
|
198
|
+
})
|
|
199
|
+
});
|
|
200
|
+
var BEMONY_PLATFORM_DEFAULTS = {
|
|
201
|
+
app: {
|
|
202
|
+
source: "bemony"
|
|
203
|
+
},
|
|
204
|
+
storage: {
|
|
205
|
+
prefix: BEMONY_STORAGE_PREFIX,
|
|
206
|
+
namespace: BEMONY_DEFAULT_NAMESPACE,
|
|
207
|
+
persistence: {
|
|
208
|
+
visitor: "localStorage",
|
|
209
|
+
session: "sessionStorage",
|
|
210
|
+
attribution: "localStorage"
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
attribution: {
|
|
214
|
+
enabled: true,
|
|
215
|
+
persist: true,
|
|
216
|
+
captureUrlParams: true,
|
|
217
|
+
captureCookies: true
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
function applyBemonyPlatformDefaults(base, platform) {
|
|
221
|
+
const namespace = platform.namespace ?? BEMONY_DEFAULT_NAMESPACE;
|
|
222
|
+
return {
|
|
223
|
+
...base,
|
|
224
|
+
app: {
|
|
225
|
+
...base.app,
|
|
226
|
+
source: BEMONY_PLATFORM_DEFAULTS.app.source
|
|
227
|
+
},
|
|
228
|
+
identity: {
|
|
229
|
+
...base.identity,
|
|
230
|
+
pixelId: platform.pixelId
|
|
231
|
+
},
|
|
232
|
+
storage: {
|
|
233
|
+
...base.storage,
|
|
234
|
+
prefix: BEMONY_PLATFORM_DEFAULTS.storage.prefix,
|
|
235
|
+
namespace,
|
|
236
|
+
persistence: {
|
|
237
|
+
...base.storage.persistence,
|
|
238
|
+
...BEMONY_PLATFORM_DEFAULTS.storage.persistence
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
attribution: {
|
|
242
|
+
...base.attribution,
|
|
243
|
+
...BEMONY_PLATFORM_DEFAULTS.attribution,
|
|
244
|
+
customClickIdParams: base.attribution.customClickIdParams ? [...base.attribution.customClickIdParams] : []
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// src/navigation/BemonyLinkPolicy.ts
|
|
250
|
+
var DEFAULT_BEMONY_LINK_SELECTOR = "[data-bemony-offer][href]";
|
|
251
|
+
function normalizeAllowedDomain(domain) {
|
|
252
|
+
let value = domain.trim().toLowerCase();
|
|
253
|
+
value = value.replace(/^https?:\/\//, "");
|
|
254
|
+
value = value.split("/")[0] ?? value;
|
|
255
|
+
value = value.split("?")[0] ?? value;
|
|
256
|
+
value = value.split("#")[0] ?? value;
|
|
257
|
+
const withoutPort = value.replace(/:\d+$/, "");
|
|
258
|
+
return withoutPort;
|
|
259
|
+
}
|
|
260
|
+
function normalizeAllowedDomains(domains) {
|
|
261
|
+
const seen = /* @__PURE__ */ new Set();
|
|
262
|
+
const result = [];
|
|
263
|
+
for (const raw of domains) {
|
|
264
|
+
const normalized = normalizeAllowedDomain(raw);
|
|
265
|
+
if (!normalized || seen.has(normalized)) continue;
|
|
266
|
+
seen.add(normalized);
|
|
267
|
+
result.push(normalized);
|
|
268
|
+
}
|
|
269
|
+
return result;
|
|
270
|
+
}
|
|
271
|
+
function createBemonyLinkPolicy(options = {}) {
|
|
272
|
+
const sourceDomains = options.allowedDomains !== void 0 && options.allowedDomains.length > 0 ? options.allowedDomains : BEMONY_DEFAULT_ALLOWED_DOMAINS;
|
|
273
|
+
const allowedDomains = normalizeAllowedDomains([...sourceDomains]);
|
|
274
|
+
const selector = options.selector?.trim();
|
|
275
|
+
return {
|
|
276
|
+
allowedDomains,
|
|
277
|
+
...selector ? { selector } : {},
|
|
278
|
+
strategy: options.strategy ?? "both",
|
|
279
|
+
preserveExisting: options.preserveExisting ?? true,
|
|
280
|
+
overwriteExisting: options.overwriteExisting ?? false,
|
|
281
|
+
decorateMailto: false,
|
|
282
|
+
decorateTel: false,
|
|
283
|
+
decorateHashLinks: false,
|
|
284
|
+
mergeDefaultExcludedDomains: true,
|
|
285
|
+
mergeDefaultExcludeSelectors: true,
|
|
286
|
+
excludeSelectors: options.excludeSelectors ? [...options.excludeSelectors] : void 0
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function linkPolicyToDecorationConfig(policy) {
|
|
290
|
+
return {
|
|
291
|
+
enabled: true,
|
|
292
|
+
strategy: policy.strategy,
|
|
293
|
+
allowedDomains: [...policy.allowedDomains],
|
|
294
|
+
...policy.selector ? { selector: policy.selector } : {},
|
|
295
|
+
preserveExisting: policy.preserveExisting,
|
|
296
|
+
overwriteExisting: policy.overwriteExisting,
|
|
297
|
+
decorateMailto: policy.decorateMailto,
|
|
298
|
+
decorateTel: policy.decorateTel,
|
|
299
|
+
decorateHashLinks: policy.decorateHashLinks,
|
|
300
|
+
mergeDefaultExcludedDomains: policy.mergeDefaultExcludedDomains,
|
|
301
|
+
mergeDefaultExcludeSelectors: policy.mergeDefaultExcludeSelectors,
|
|
302
|
+
...policy.excludeSelectors ? { excludeSelectors: [...policy.excludeSelectors] } : {}
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// src/navigation/configureBemonyLinks.ts
|
|
307
|
+
var BEMONY_SID_MANAGED_PARAM = {
|
|
308
|
+
key: "public_session",
|
|
309
|
+
param: "sid",
|
|
310
|
+
mode: "read_write",
|
|
311
|
+
writeStrategy: "replaceState",
|
|
312
|
+
generator: { type: "public_id", prefix: "sid" },
|
|
313
|
+
persistence: {
|
|
314
|
+
enabled: true,
|
|
315
|
+
strategy: "localStorage_cookie",
|
|
316
|
+
storageKey: "sid"
|
|
317
|
+
},
|
|
318
|
+
decorateLinks: true
|
|
319
|
+
};
|
|
320
|
+
var BEMONY_AFID_MANAGED_PARAM = {
|
|
321
|
+
key: "affiliate",
|
|
322
|
+
param: "afid",
|
|
323
|
+
mode: "read_write",
|
|
324
|
+
writeStrategy: "replaceState",
|
|
325
|
+
persistence: {
|
|
326
|
+
enabled: true,
|
|
327
|
+
strategy: "localStorage_cookie",
|
|
328
|
+
storageKey: "afid"
|
|
329
|
+
},
|
|
330
|
+
decorateLinks: true
|
|
331
|
+
};
|
|
332
|
+
var BEMONY_OID_MANAGED_PARAM = {
|
|
333
|
+
key: "order_id",
|
|
334
|
+
param: "oid",
|
|
335
|
+
mode: "read_write",
|
|
336
|
+
writeStrategy: "replaceState",
|
|
337
|
+
persistence: {
|
|
338
|
+
enabled: true,
|
|
339
|
+
strategy: "localStorage_cookie",
|
|
340
|
+
storageKey: "oid"
|
|
341
|
+
},
|
|
342
|
+
decorateLinks: true
|
|
343
|
+
};
|
|
344
|
+
var BEMONY_FID_MANAGED_PARAM = {
|
|
345
|
+
key: "funnel_id",
|
|
346
|
+
param: "fid",
|
|
347
|
+
mode: "read_write",
|
|
348
|
+
writeStrategy: "replaceState",
|
|
349
|
+
persistence: {
|
|
350
|
+
enabled: true,
|
|
351
|
+
strategy: "localStorage_cookie",
|
|
352
|
+
storageKey: "fid"
|
|
353
|
+
},
|
|
354
|
+
decorateLinks: true
|
|
355
|
+
};
|
|
356
|
+
var BEMONY_FSID_MANAGED_PARAM = {
|
|
357
|
+
key: "funnel_step_id",
|
|
358
|
+
param: "fsid",
|
|
359
|
+
mode: "read_write",
|
|
360
|
+
writeStrategy: "replaceState",
|
|
361
|
+
persistence: {
|
|
362
|
+
enabled: true,
|
|
363
|
+
strategy: "localStorage_cookie",
|
|
364
|
+
storageKey: "fsid"
|
|
365
|
+
},
|
|
366
|
+
decorateLinks: true
|
|
367
|
+
};
|
|
368
|
+
var BEMONY_FORWARD_DENYLIST = [
|
|
369
|
+
"token",
|
|
370
|
+
"one_click_token",
|
|
371
|
+
"oneclicktoken",
|
|
372
|
+
"oneClickToken",
|
|
373
|
+
"ephemeral_token",
|
|
374
|
+
"payment_token",
|
|
375
|
+
"access_token",
|
|
376
|
+
"refresh_token",
|
|
377
|
+
"id_token",
|
|
378
|
+
"auth_token",
|
|
379
|
+
"secret",
|
|
380
|
+
"password",
|
|
381
|
+
"pass",
|
|
382
|
+
"pwd",
|
|
383
|
+
"email",
|
|
384
|
+
"phone",
|
|
385
|
+
"cpf",
|
|
386
|
+
"cnpj",
|
|
387
|
+
"document",
|
|
388
|
+
"card",
|
|
389
|
+
"cc",
|
|
390
|
+
"credit_card",
|
|
391
|
+
"pan",
|
|
392
|
+
"cvv",
|
|
393
|
+
"cvc",
|
|
394
|
+
"auth",
|
|
395
|
+
"code",
|
|
396
|
+
"sessionid",
|
|
397
|
+
"internalsessionid",
|
|
398
|
+
"pixelsessionid"
|
|
399
|
+
];
|
|
400
|
+
function cloneManagedParam(param) {
|
|
401
|
+
return {
|
|
402
|
+
...param,
|
|
403
|
+
...param.generator ? { generator: { ...param.generator } } : {},
|
|
404
|
+
...param.persistence ? {
|
|
405
|
+
persistence: {
|
|
406
|
+
...param.persistence,
|
|
407
|
+
...param.persistence.cookie ? { cookie: { ...param.persistence.cookie } } : {}
|
|
408
|
+
}
|
|
409
|
+
} : {}
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
function configureBemonyLinks(options = {}) {
|
|
413
|
+
const policy = createBemonyLinkPolicy(options);
|
|
414
|
+
const includeSid = options.includeSidManagedParam !== false;
|
|
415
|
+
const includeAfid = options.includeAfidManagedParam !== false;
|
|
416
|
+
const includeOid = options.includeOidManagedParam !== false;
|
|
417
|
+
const includeFunnel = options.includeFunnelManagedParams !== false;
|
|
418
|
+
const managedParams = [];
|
|
419
|
+
if (includeSid) {
|
|
420
|
+
managedParams.push(cloneManagedParam(BEMONY_SID_MANAGED_PARAM));
|
|
421
|
+
}
|
|
422
|
+
if (includeAfid) {
|
|
423
|
+
managedParams.push(cloneManagedParam(BEMONY_AFID_MANAGED_PARAM));
|
|
424
|
+
}
|
|
425
|
+
if (includeOid) {
|
|
426
|
+
managedParams.push(cloneManagedParam(BEMONY_OID_MANAGED_PARAM));
|
|
427
|
+
}
|
|
428
|
+
if (includeFunnel) {
|
|
429
|
+
managedParams.push(cloneManagedParam(BEMONY_FID_MANAGED_PARAM));
|
|
430
|
+
managedParams.push(cloneManagedParam(BEMONY_FSID_MANAGED_PARAM));
|
|
431
|
+
}
|
|
432
|
+
const denylist = [...BEMONY_FORWARD_DENYLIST];
|
|
433
|
+
return {
|
|
434
|
+
enabled: true,
|
|
435
|
+
queryParams: {
|
|
436
|
+
enabled: true,
|
|
437
|
+
mode: "all_except",
|
|
438
|
+
mergeDefaultDenylist: true,
|
|
439
|
+
denylist: [...denylist]
|
|
440
|
+
},
|
|
441
|
+
managedParams,
|
|
442
|
+
forwardParams: {
|
|
443
|
+
enabled: true,
|
|
444
|
+
mode: "all_except",
|
|
445
|
+
mergeDefaultDenylist: true,
|
|
446
|
+
denylist: [...denylist]
|
|
447
|
+
},
|
|
448
|
+
linkDecoration: linkPolicyToDecorationConfig(policy)
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// src/platform/environments.ts
|
|
453
|
+
var BEMONY_ENVIRONMENTS = [
|
|
454
|
+
"development",
|
|
455
|
+
"staging",
|
|
456
|
+
"production"
|
|
457
|
+
];
|
|
458
|
+
function isNonEmptyEndpoint(value) {
|
|
459
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
460
|
+
}
|
|
461
|
+
function resolveBemonyEnvironment(options) {
|
|
462
|
+
const { environment } = options;
|
|
463
|
+
if (environment === "development") {
|
|
464
|
+
return {
|
|
465
|
+
environment,
|
|
466
|
+
debug: options.debugOverride ?? true,
|
|
467
|
+
apiEndpoint: isNonEmptyEndpoint(options.apiEndpoint) ? options.apiEndpoint.trim() : "",
|
|
468
|
+
transportEnabled: false,
|
|
469
|
+
senderMode: "mock"
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
if (environment === "staging" || environment === "production") {
|
|
473
|
+
if (!isNonEmptyEndpoint(options.apiEndpoint)) {
|
|
474
|
+
throw new BemonyValidationError({
|
|
475
|
+
code: "MISSING_ENDPOINT",
|
|
476
|
+
field: "apiEndpoint",
|
|
477
|
+
message: `apiEndpoint is required for environment "${environment}". Inject an approved endpoint; do not hardcode credentials.`
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
return {
|
|
481
|
+
environment,
|
|
482
|
+
debug: options.debugOverride ?? false,
|
|
483
|
+
apiEndpoint: options.apiEndpoint.trim(),
|
|
484
|
+
transportEnabled: true,
|
|
485
|
+
senderMode: "http"
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
throw new BemonyValidationError({
|
|
489
|
+
code: "INVALID_ENVIRONMENT",
|
|
490
|
+
field: "environment",
|
|
491
|
+
message: "Unsupported Bemony environment."
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
function applyBemonyEnvironment(base, resolution) {
|
|
495
|
+
return {
|
|
496
|
+
...base,
|
|
497
|
+
app: {
|
|
498
|
+
...base.app,
|
|
499
|
+
environment: resolution.environment,
|
|
500
|
+
debug: resolution.debug
|
|
501
|
+
},
|
|
502
|
+
transport: {
|
|
503
|
+
...base.transport,
|
|
504
|
+
apiEndpoint: resolution.apiEndpoint
|
|
505
|
+
},
|
|
506
|
+
modules: {
|
|
507
|
+
...base.modules,
|
|
508
|
+
transport: {
|
|
509
|
+
...base.modules.transport,
|
|
510
|
+
enabled: resolution.transportEnabled,
|
|
511
|
+
sender: {
|
|
512
|
+
...base.modules.transport.sender,
|
|
513
|
+
mode: resolution.senderMode
|
|
514
|
+
},
|
|
515
|
+
mockSender: {
|
|
516
|
+
...base.modules.transport.mockSender,
|
|
517
|
+
enabled: resolution.senderMode === "mock"
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// src/scenarios/checkout.ts
|
|
525
|
+
function resolveMainProductId(products) {
|
|
526
|
+
if (!products || products.length === 0) {
|
|
527
|
+
return void 0;
|
|
528
|
+
}
|
|
529
|
+
const main = products.find((product) => product.type === "main");
|
|
530
|
+
return (main ?? products[0])?.id;
|
|
531
|
+
}
|
|
532
|
+
function applyCheckoutScenario(base, options = {
|
|
533
|
+
autoInitiateCheckout: true,
|
|
534
|
+
includeOidManagedParam: true
|
|
535
|
+
}) {
|
|
536
|
+
const products = (options.products ?? []).map((product) => ({ ...product }));
|
|
537
|
+
const currency = options.currency ?? base.commerce.currency ?? "BRL";
|
|
538
|
+
const mainProductId = resolveMainProductId(products);
|
|
539
|
+
const autoInitiateCheckout = options.autoInitiateCheckout !== false;
|
|
540
|
+
const allowedDomains = normalizeAllowedDomains(
|
|
541
|
+
options.allowedDomains !== void 0 && options.allowedDomains.length > 0 ? [...options.allowedDomains] : [...BEMONY_DEFAULT_ALLOWED_DOMAINS]
|
|
542
|
+
);
|
|
543
|
+
const urlContext = configureBemonyLinks({
|
|
544
|
+
allowedDomains,
|
|
545
|
+
includeOidManagedParam: options.includeOidManagedParam ?? true
|
|
546
|
+
});
|
|
547
|
+
const conversionManual = {
|
|
548
|
+
...base.modules.conversion.manual,
|
|
549
|
+
lead: { enabled: true },
|
|
550
|
+
addToCart: { enabled: true },
|
|
551
|
+
removeFromCart: { enabled: true },
|
|
552
|
+
initiateCheckout: { enabled: true },
|
|
553
|
+
addPaymentInfo: { enabled: true },
|
|
554
|
+
viewContent: { enabled: false },
|
|
555
|
+
pageView: { enabled: false },
|
|
556
|
+
purchase: { enabled: false },
|
|
557
|
+
search: { enabled: false }
|
|
558
|
+
};
|
|
559
|
+
return {
|
|
560
|
+
...base,
|
|
561
|
+
app: {
|
|
562
|
+
...base.app,
|
|
563
|
+
target: "checkout",
|
|
564
|
+
source: "bemony"
|
|
565
|
+
},
|
|
566
|
+
commerce: {
|
|
567
|
+
...base.commerce,
|
|
568
|
+
enabled: true,
|
|
569
|
+
currency,
|
|
570
|
+
catalog: { products },
|
|
571
|
+
cart: {
|
|
572
|
+
...base.commerce.cart,
|
|
573
|
+
mode: "checkout",
|
|
574
|
+
initialItems: mainProductId ? [{ productId: mainProductId, quantity: 1 }] : [],
|
|
575
|
+
allowDynamicItems: true,
|
|
576
|
+
calculateValueFrom: "cart"
|
|
577
|
+
},
|
|
578
|
+
mappings: {
|
|
579
|
+
attributes: { ...base.commerce.mappings?.attributes ?? {} },
|
|
580
|
+
fields: { ...base.commerce.mappings?.fields ?? {} }
|
|
581
|
+
},
|
|
582
|
+
enrichment: {
|
|
583
|
+
...base.commerce.enrichment ?? {},
|
|
584
|
+
initiateCheckout: { enabled: true, source: "cart" },
|
|
585
|
+
lead: { enabled: false, source: "event_or_cart" },
|
|
586
|
+
addToCart: { enabled: true, source: "event_or_cart" },
|
|
587
|
+
removeFromCart: { enabled: true, source: "event_or_cart" },
|
|
588
|
+
addPaymentInfo: { enabled: true, source: "cart" },
|
|
589
|
+
purchase: { enabled: false, source: "cart" },
|
|
590
|
+
viewContent: { enabled: false, source: "event_or_cart" },
|
|
591
|
+
pageView: { enabled: false, source: "cart" }
|
|
592
|
+
}
|
|
593
|
+
},
|
|
594
|
+
modules: {
|
|
595
|
+
...base.modules,
|
|
596
|
+
core: {
|
|
597
|
+
...base.modules.core,
|
|
598
|
+
enabled: true,
|
|
599
|
+
pageView: {
|
|
600
|
+
...base.modules.core.pageView,
|
|
601
|
+
enabled: true,
|
|
602
|
+
trackInitialPageView: true
|
|
603
|
+
},
|
|
604
|
+
urlContext
|
|
605
|
+
},
|
|
606
|
+
conversion: {
|
|
607
|
+
...base.modules.conversion,
|
|
608
|
+
enabled: true,
|
|
609
|
+
manual: conversionManual,
|
|
610
|
+
auto: {
|
|
611
|
+
enabled: true,
|
|
612
|
+
detectors: {
|
|
613
|
+
...base.modules.conversion.auto.detectors,
|
|
614
|
+
addToCart: {
|
|
615
|
+
enabled: true,
|
|
616
|
+
sources: [
|
|
617
|
+
{
|
|
618
|
+
name: "main-product-after-contact",
|
|
619
|
+
type: "form",
|
|
620
|
+
selectorForm: {
|
|
621
|
+
fields: ["email", "phone"],
|
|
622
|
+
mode: "all",
|
|
623
|
+
formSelector: "#checkout-form",
|
|
624
|
+
trigger: "input",
|
|
625
|
+
debounceMs: 300,
|
|
626
|
+
fireOnce: true
|
|
627
|
+
}
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
name: "product-or-bump-click",
|
|
631
|
+
type: "selector",
|
|
632
|
+
selector: "add_to_cart",
|
|
633
|
+
selectorType: "attribute",
|
|
634
|
+
trigger: "click",
|
|
635
|
+
fireOnce: false
|
|
636
|
+
}
|
|
637
|
+
]
|
|
638
|
+
},
|
|
639
|
+
removeFromCart: {
|
|
640
|
+
enabled: true,
|
|
641
|
+
sources: [
|
|
642
|
+
{
|
|
643
|
+
name: "remove-product-click",
|
|
644
|
+
type: "selector",
|
|
645
|
+
selector: "remove_from_cart",
|
|
646
|
+
selectorType: "attribute",
|
|
647
|
+
trigger: "click"
|
|
648
|
+
}
|
|
649
|
+
]
|
|
650
|
+
},
|
|
651
|
+
initiateCheckout: {
|
|
652
|
+
enabled: autoInitiateCheckout,
|
|
653
|
+
sources: autoInitiateCheckout ? [
|
|
654
|
+
{
|
|
655
|
+
name: "checkout-loaded",
|
|
656
|
+
type: "lifecycle",
|
|
657
|
+
trigger: "ready",
|
|
658
|
+
fireOnce: true
|
|
659
|
+
}
|
|
660
|
+
] : []
|
|
661
|
+
},
|
|
662
|
+
addPaymentInfo: {
|
|
663
|
+
enabled: true,
|
|
664
|
+
sources: [
|
|
665
|
+
{
|
|
666
|
+
name: "credit-card-detection",
|
|
667
|
+
type: "payment",
|
|
668
|
+
paymentMethod: "CREDIT_CARD",
|
|
669
|
+
cardDetection: true,
|
|
670
|
+
autoDispatch: true,
|
|
671
|
+
formSelector: "#checkout-form",
|
|
672
|
+
fireOnce: true
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
name: "payment-button-click",
|
|
676
|
+
type: "selector",
|
|
677
|
+
selector: '[data-track="add_payment_info"]',
|
|
678
|
+
selectorType: "css",
|
|
679
|
+
trigger: "click",
|
|
680
|
+
fireOnce: false
|
|
681
|
+
}
|
|
682
|
+
]
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
},
|
|
687
|
+
behavior: {
|
|
688
|
+
...base.modules.behavior,
|
|
689
|
+
enabled: false
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// src/scenarios/one-click-checkout.ts
|
|
696
|
+
function resolveCartMode(products) {
|
|
697
|
+
const list = products ?? [];
|
|
698
|
+
const isUpsellLike = list.some(
|
|
699
|
+
(product) => product.type === "upsell" || product.type === "downsell"
|
|
700
|
+
);
|
|
701
|
+
return isUpsellLike ? "upsell" : "checkout";
|
|
702
|
+
}
|
|
703
|
+
function resolvePrimaryProductId(products) {
|
|
704
|
+
if (!products || products.length === 0) {
|
|
705
|
+
return void 0;
|
|
706
|
+
}
|
|
707
|
+
const preferred = products.find(
|
|
708
|
+
(product) => product.type === "upsell" || product.type === "downsell" || product.type === "main"
|
|
709
|
+
);
|
|
710
|
+
return (preferred ?? products[0])?.id;
|
|
711
|
+
}
|
|
712
|
+
function applyOneClickCheckoutScenario(base, options = {}) {
|
|
713
|
+
const products = (options.products ?? []).map((product) => ({ ...product }));
|
|
714
|
+
const currency = options.currency ?? base.commerce.currency ?? "BRL";
|
|
715
|
+
const cartMode = resolveCartMode(products);
|
|
716
|
+
const primaryId = resolvePrimaryProductId(products);
|
|
717
|
+
const urlContext = configureBemonyLinks({
|
|
718
|
+
allowedDomains: options.allowedDomains ? [...options.allowedDomains] : [],
|
|
719
|
+
includeOidManagedParam: true
|
|
720
|
+
});
|
|
721
|
+
const conversionManual = {
|
|
722
|
+
...base.modules.conversion.manual,
|
|
723
|
+
initiateCheckout: { enabled: true },
|
|
724
|
+
addToCart: { enabled: true },
|
|
725
|
+
viewContent: { enabled: false },
|
|
726
|
+
lead: { enabled: false },
|
|
727
|
+
removeFromCart: { enabled: false },
|
|
728
|
+
addPaymentInfo: { enabled: false },
|
|
729
|
+
pageView: { enabled: false },
|
|
730
|
+
purchase: { enabled: false },
|
|
731
|
+
search: { enabled: false }
|
|
732
|
+
};
|
|
733
|
+
return {
|
|
734
|
+
...base,
|
|
735
|
+
app: {
|
|
736
|
+
...base.app,
|
|
737
|
+
target: "checkout"
|
|
738
|
+
},
|
|
739
|
+
commerce: {
|
|
740
|
+
...base.commerce,
|
|
741
|
+
enabled: true,
|
|
742
|
+
currency,
|
|
743
|
+
catalog: { products },
|
|
744
|
+
cart: {
|
|
745
|
+
...base.commerce.cart,
|
|
746
|
+
mode: cartMode,
|
|
747
|
+
initialItems: primaryId ? [{ productId: primaryId, quantity: 1 }] : [],
|
|
748
|
+
allowDynamicItems: true,
|
|
749
|
+
calculateValueFrom: "cart"
|
|
750
|
+
},
|
|
751
|
+
mappings: {
|
|
752
|
+
attributes: { ...base.commerce.mappings?.attributes ?? {} },
|
|
753
|
+
fields: { ...base.commerce.mappings?.fields ?? {} }
|
|
754
|
+
},
|
|
755
|
+
enrichment: {
|
|
756
|
+
...base.commerce.enrichment ?? {},
|
|
757
|
+
initiateCheckout: { enabled: true, source: "cart" },
|
|
758
|
+
addToCart: { enabled: true, source: "event_or_cart" },
|
|
759
|
+
purchase: { enabled: false, source: "cart" },
|
|
760
|
+
lead: { enabled: false, source: "event_or_cart" },
|
|
761
|
+
viewContent: { enabled: false, source: "event_or_cart" },
|
|
762
|
+
pageView: { enabled: false, source: "cart" }
|
|
763
|
+
}
|
|
764
|
+
},
|
|
765
|
+
modules: {
|
|
766
|
+
...base.modules,
|
|
767
|
+
core: {
|
|
768
|
+
...base.modules.core,
|
|
769
|
+
enabled: true,
|
|
770
|
+
pageView: {
|
|
771
|
+
...base.modules.core.pageView,
|
|
772
|
+
enabled: true,
|
|
773
|
+
trackInitialPageView: true
|
|
774
|
+
},
|
|
775
|
+
urlContext
|
|
776
|
+
},
|
|
777
|
+
conversion: {
|
|
778
|
+
...base.modules.conversion,
|
|
779
|
+
enabled: true,
|
|
780
|
+
manual: conversionManual,
|
|
781
|
+
auto: {
|
|
782
|
+
...base.modules.conversion.auto,
|
|
783
|
+
enabled: false
|
|
784
|
+
}
|
|
785
|
+
},
|
|
786
|
+
behavior: {
|
|
787
|
+
...base.modules.behavior,
|
|
788
|
+
enabled: false
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// src/navigation/createBemonyOfferLinkSource.ts
|
|
795
|
+
function createBemonyOfferLinkSource(allowedDomains) {
|
|
796
|
+
const hostnames = normalizeAllowedDomains([...allowedDomains]).filter(
|
|
797
|
+
(host) => host !== "local"
|
|
798
|
+
);
|
|
799
|
+
return {
|
|
800
|
+
name: "bemony-offer-link",
|
|
801
|
+
type: "link",
|
|
802
|
+
enabled: true,
|
|
803
|
+
trigger: "click",
|
|
804
|
+
fireOnce: false,
|
|
805
|
+
match: {
|
|
806
|
+
protocols: ["https:", "http:"],
|
|
807
|
+
hostnames,
|
|
808
|
+
path: {
|
|
809
|
+
segmentPrefixes: ["pri"]
|
|
810
|
+
}
|
|
811
|
+
},
|
|
812
|
+
bindings: {
|
|
813
|
+
// Core LinkAuto reconhece priceCode; productId no evento = priceCode observado.
|
|
814
|
+
priceCode: {
|
|
815
|
+
from: {
|
|
816
|
+
source: "pathSegment",
|
|
817
|
+
index: -1
|
|
818
|
+
},
|
|
819
|
+
required: true
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// src/scenarios/offer-page.ts
|
|
826
|
+
function applyOfferPageScenario(base, options = {}) {
|
|
827
|
+
const cartMode = options.cartMode ?? "landing_page";
|
|
828
|
+
const products = (options.products ?? []).map((product) => ({ ...product }));
|
|
829
|
+
const currency = options.currency ?? base.commerce.currency ?? "BRL";
|
|
830
|
+
const allowedDomains = normalizeAllowedDomains(
|
|
831
|
+
options.allowedDomains !== void 0 && options.allowedDomains.length > 0 ? [...options.allowedDomains] : [...BEMONY_DEFAULT_ALLOWED_DOMAINS]
|
|
832
|
+
);
|
|
833
|
+
const urlContext = configureBemonyLinks({ allowedDomains });
|
|
834
|
+
const offerLinkSource = createBemonyOfferLinkSource(allowedDomains);
|
|
835
|
+
const conversionManual = {
|
|
836
|
+
...base.modules.conversion.manual,
|
|
837
|
+
viewContent: { enabled: true },
|
|
838
|
+
addToCart: { enabled: true },
|
|
839
|
+
// Offer page não emite estes manualmente nesta etapa.
|
|
840
|
+
pageView: { enabled: false },
|
|
841
|
+
purchase: { enabled: false },
|
|
842
|
+
lead: { enabled: false },
|
|
843
|
+
removeFromCart: { enabled: false },
|
|
844
|
+
initiateCheckout: { enabled: false },
|
|
845
|
+
addPaymentInfo: { enabled: false },
|
|
846
|
+
search: { enabled: false }
|
|
847
|
+
};
|
|
848
|
+
return {
|
|
849
|
+
...base,
|
|
850
|
+
app: {
|
|
851
|
+
...base.app,
|
|
852
|
+
target: "landing"
|
|
853
|
+
},
|
|
854
|
+
commerce: {
|
|
855
|
+
...base.commerce,
|
|
856
|
+
enabled: true,
|
|
857
|
+
currency,
|
|
858
|
+
catalog: { products },
|
|
859
|
+
cart: {
|
|
860
|
+
...base.commerce.cart,
|
|
861
|
+
mode: cartMode,
|
|
862
|
+
initialItems: base.commerce.cart?.initialItems ? base.commerce.cart.initialItems.map((item) => ({ ...item })) : [],
|
|
863
|
+
allowDynamicItems: true,
|
|
864
|
+
calculateValueFrom: "cart"
|
|
865
|
+
},
|
|
866
|
+
mappings: {
|
|
867
|
+
attributes: { ...base.commerce.mappings?.attributes ?? {} },
|
|
868
|
+
fields: { ...base.commerce.mappings?.fields ?? {} }
|
|
869
|
+
},
|
|
870
|
+
enrichment: {
|
|
871
|
+
...base.commerce.enrichment ?? {},
|
|
872
|
+
viewContent: { enabled: true, source: "event_or_cart" },
|
|
873
|
+
addToCart: { enabled: true, source: "event_or_cart" },
|
|
874
|
+
pageView: { enabled: false, source: "cart" },
|
|
875
|
+
purchase: { enabled: false, source: "cart" },
|
|
876
|
+
lead: { enabled: false, source: "event_or_cart" }
|
|
877
|
+
}
|
|
878
|
+
},
|
|
879
|
+
modules: {
|
|
880
|
+
...base.modules,
|
|
881
|
+
core: {
|
|
882
|
+
...base.modules.core,
|
|
883
|
+
enabled: true,
|
|
884
|
+
pageView: {
|
|
885
|
+
...base.modules.core.pageView,
|
|
886
|
+
enabled: true,
|
|
887
|
+
trackInitialPageView: true
|
|
888
|
+
},
|
|
889
|
+
urlContext
|
|
890
|
+
},
|
|
891
|
+
conversion: {
|
|
892
|
+
...base.modules.conversion,
|
|
893
|
+
enabled: true,
|
|
894
|
+
manual: conversionManual,
|
|
895
|
+
auto: {
|
|
896
|
+
...base.modules.conversion.auto,
|
|
897
|
+
enabled: true,
|
|
898
|
+
detectors: {
|
|
899
|
+
...base.modules.conversion.auto.detectors,
|
|
900
|
+
addToCart: {
|
|
901
|
+
enabled: true,
|
|
902
|
+
sources: [offerLinkSource]
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
},
|
|
907
|
+
behavior: {
|
|
908
|
+
...base.modules.behavior,
|
|
909
|
+
enabled: false
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
// src/scenarios/thank-you.ts
|
|
916
|
+
function applyThankYouScenario(base, options = {}) {
|
|
917
|
+
const currency = options.currency ?? base.commerce.currency ?? "BRL";
|
|
918
|
+
const urlContext = configureBemonyLinks({
|
|
919
|
+
allowedDomains: options.allowedDomains ? [...options.allowedDomains] : [],
|
|
920
|
+
includeOidManagedParam: true
|
|
921
|
+
});
|
|
922
|
+
const conversionManual = {
|
|
923
|
+
...base.modules.conversion.manual,
|
|
924
|
+
pageView: { enabled: false },
|
|
925
|
+
purchase: { enabled: false },
|
|
926
|
+
lead: { enabled: false },
|
|
927
|
+
addToCart: { enabled: false },
|
|
928
|
+
removeFromCart: { enabled: false },
|
|
929
|
+
initiateCheckout: { enabled: false },
|
|
930
|
+
addPaymentInfo: { enabled: false },
|
|
931
|
+
search: { enabled: false },
|
|
932
|
+
viewContent: { enabled: false }
|
|
933
|
+
};
|
|
934
|
+
return {
|
|
935
|
+
...base,
|
|
936
|
+
app: {
|
|
937
|
+
...base.app,
|
|
938
|
+
target: "custom"
|
|
939
|
+
},
|
|
940
|
+
commerce: {
|
|
941
|
+
...base.commerce,
|
|
942
|
+
enabled: false,
|
|
943
|
+
currency,
|
|
944
|
+
catalog: { products: [] },
|
|
945
|
+
cart: {
|
|
946
|
+
...base.commerce.cart,
|
|
947
|
+
mode: "custom",
|
|
948
|
+
initialItems: [],
|
|
949
|
+
allowDynamicItems: false,
|
|
950
|
+
calculateValueFrom: "cart"
|
|
951
|
+
},
|
|
952
|
+
enrichment: {
|
|
953
|
+
...base.commerce.enrichment ?? {},
|
|
954
|
+
purchase: { enabled: false, source: "cart" },
|
|
955
|
+
pageView: { enabled: false, source: "cart" }
|
|
956
|
+
}
|
|
957
|
+
},
|
|
958
|
+
modules: {
|
|
959
|
+
...base.modules,
|
|
960
|
+
core: {
|
|
961
|
+
...base.modules.core,
|
|
962
|
+
enabled: true,
|
|
963
|
+
pageView: {
|
|
964
|
+
...base.modules.core.pageView,
|
|
965
|
+
enabled: true,
|
|
966
|
+
trackInitialPageView: true
|
|
967
|
+
},
|
|
968
|
+
urlContext
|
|
969
|
+
},
|
|
970
|
+
conversion: {
|
|
971
|
+
...base.modules.conversion,
|
|
972
|
+
enabled: false,
|
|
973
|
+
manual: conversionManual,
|
|
974
|
+
auto: {
|
|
975
|
+
...base.modules.conversion.auto,
|
|
976
|
+
enabled: false
|
|
977
|
+
}
|
|
978
|
+
},
|
|
979
|
+
behavior: {
|
|
980
|
+
...base.modules.behavior,
|
|
981
|
+
enabled: false
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// src/scenarios/upsell-offer.ts
|
|
988
|
+
var BEMONY_UPSELL_BUY_LINK_SELECTOR = 'a[href*="pri"], [data-bemony-offer][href]';
|
|
989
|
+
var BEMONY_UPSELL_LINK_SELECTOR = BEMONY_UPSELL_BUY_LINK_SELECTOR;
|
|
990
|
+
function escapeCssAttributeValue(value) {
|
|
991
|
+
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
|
|
992
|
+
return CSS.escape(value);
|
|
993
|
+
}
|
|
994
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
995
|
+
}
|
|
996
|
+
function buildUpsellLinkSelector(noThanksUrl) {
|
|
997
|
+
const raw = noThanksUrl?.trim();
|
|
998
|
+
if (!raw) {
|
|
999
|
+
return BEMONY_UPSELL_BUY_LINK_SELECTOR;
|
|
1000
|
+
}
|
|
1001
|
+
return `${BEMONY_UPSELL_BUY_LINK_SELECTOR}, a[href="${escapeCssAttributeValue(raw)}"]`;
|
|
1002
|
+
}
|
|
1003
|
+
function hrefMatchesNoThanksUrl(href, noThanksUrl, baseHref) {
|
|
1004
|
+
const linkHref = href?.trim();
|
|
1005
|
+
const targetRaw = noThanksUrl.trim();
|
|
1006
|
+
if (!linkHref || linkHref === "#" || !targetRaw) {
|
|
1007
|
+
return false;
|
|
1008
|
+
}
|
|
1009
|
+
if (linkHref === targetRaw) {
|
|
1010
|
+
return true;
|
|
1011
|
+
}
|
|
1012
|
+
try {
|
|
1013
|
+
const base = baseHref ?? (typeof window !== "undefined" && window.location?.href ? window.location.href : "https://localhost/");
|
|
1014
|
+
const left = new URL(linkHref, base);
|
|
1015
|
+
const right = new URL(targetRaw, base);
|
|
1016
|
+
left.hash = "";
|
|
1017
|
+
right.hash = "";
|
|
1018
|
+
return left.href === right.href;
|
|
1019
|
+
} catch {
|
|
1020
|
+
return false;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
function applyUpsellOfferScenario(base, options = {}) {
|
|
1024
|
+
const products = (options.products ?? []).map((product) => ({ ...product }));
|
|
1025
|
+
const currency = options.currency ?? base.commerce.currency ?? "BRL";
|
|
1026
|
+
const allowedDomains = normalizeAllowedDomains(
|
|
1027
|
+
options.allowedDomains !== void 0 && options.allowedDomains.length > 0 ? [...options.allowedDomains] : [...BEMONY_DEFAULT_ALLOWED_DOMAINS]
|
|
1028
|
+
);
|
|
1029
|
+
const urlContext = configureBemonyLinks({
|
|
1030
|
+
allowedDomains,
|
|
1031
|
+
selector: buildUpsellLinkSelector(options.noThanksUrl),
|
|
1032
|
+
includeOidManagedParam: options.includeOidManagedParam ?? true
|
|
1033
|
+
});
|
|
1034
|
+
const offerLinkSource = createBemonyOfferLinkSource(allowedDomains);
|
|
1035
|
+
const conversionManual = {
|
|
1036
|
+
...base.modules.conversion.manual,
|
|
1037
|
+
viewContent: { enabled: true },
|
|
1038
|
+
addToCart: { enabled: true },
|
|
1039
|
+
pageView: { enabled: false },
|
|
1040
|
+
purchase: { enabled: false },
|
|
1041
|
+
lead: { enabled: false },
|
|
1042
|
+
removeFromCart: { enabled: false },
|
|
1043
|
+
initiateCheckout: { enabled: false },
|
|
1044
|
+
addPaymentInfo: { enabled: false },
|
|
1045
|
+
search: { enabled: false }
|
|
1046
|
+
};
|
|
1047
|
+
return {
|
|
1048
|
+
...base,
|
|
1049
|
+
app: {
|
|
1050
|
+
...base.app,
|
|
1051
|
+
target: "custom"
|
|
1052
|
+
},
|
|
1053
|
+
commerce: {
|
|
1054
|
+
...base.commerce,
|
|
1055
|
+
enabled: true,
|
|
1056
|
+
currency,
|
|
1057
|
+
catalog: { products },
|
|
1058
|
+
cart: {
|
|
1059
|
+
...base.commerce.cart,
|
|
1060
|
+
mode: "upsell",
|
|
1061
|
+
initialItems: [],
|
|
1062
|
+
allowDynamicItems: true,
|
|
1063
|
+
calculateValueFrom: "cart"
|
|
1064
|
+
},
|
|
1065
|
+
mappings: {
|
|
1066
|
+
attributes: { ...base.commerce.mappings?.attributes ?? {} },
|
|
1067
|
+
fields: { ...base.commerce.mappings?.fields ?? {} }
|
|
1068
|
+
},
|
|
1069
|
+
enrichment: {
|
|
1070
|
+
...base.commerce.enrichment ?? {},
|
|
1071
|
+
viewContent: { enabled: true, source: "event_or_cart" },
|
|
1072
|
+
addToCart: { enabled: true, source: "event_or_cart" },
|
|
1073
|
+
pageView: { enabled: false, source: "cart" },
|
|
1074
|
+
purchase: { enabled: false, source: "cart" },
|
|
1075
|
+
lead: { enabled: false, source: "event_or_cart" },
|
|
1076
|
+
initiateCheckout: { enabled: false, source: "cart" }
|
|
1077
|
+
}
|
|
1078
|
+
},
|
|
1079
|
+
modules: {
|
|
1080
|
+
...base.modules,
|
|
1081
|
+
core: {
|
|
1082
|
+
...base.modules.core,
|
|
1083
|
+
enabled: true,
|
|
1084
|
+
pageView: {
|
|
1085
|
+
...base.modules.core.pageView,
|
|
1086
|
+
enabled: true,
|
|
1087
|
+
trackInitialPageView: true
|
|
1088
|
+
},
|
|
1089
|
+
urlContext
|
|
1090
|
+
},
|
|
1091
|
+
conversion: {
|
|
1092
|
+
...base.modules.conversion,
|
|
1093
|
+
enabled: true,
|
|
1094
|
+
manual: conversionManual,
|
|
1095
|
+
auto: {
|
|
1096
|
+
...base.modules.conversion.auto,
|
|
1097
|
+
enabled: true,
|
|
1098
|
+
detectors: {
|
|
1099
|
+
...base.modules.conversion.auto.detectors,
|
|
1100
|
+
addToCart: {
|
|
1101
|
+
enabled: true,
|
|
1102
|
+
sources: [offerLinkSource]
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
},
|
|
1107
|
+
behavior: {
|
|
1108
|
+
...base.modules.behavior,
|
|
1109
|
+
enabled: false
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
// src/createBemonyPixelConfig.ts
|
|
1116
|
+
var SCENARIO_TARGET = {
|
|
1117
|
+
"offer-page": "landing",
|
|
1118
|
+
checkout: "checkout",
|
|
1119
|
+
"upsell-offer": "custom",
|
|
1120
|
+
"one-click-checkout": "checkout",
|
|
1121
|
+
"thank-you": "custom"
|
|
1122
|
+
};
|
|
1123
|
+
var SCENARIO_CART_MODE = {
|
|
1124
|
+
"offer-page": "landing_page",
|
|
1125
|
+
checkout: "checkout",
|
|
1126
|
+
"upsell-offer": "upsell",
|
|
1127
|
+
"one-click-checkout": "checkout",
|
|
1128
|
+
"thank-you": "custom"
|
|
1129
|
+
};
|
|
1130
|
+
var PRODUCT_TYPES = /* @__PURE__ */ new Set([
|
|
1131
|
+
"main",
|
|
1132
|
+
"order_bump",
|
|
1133
|
+
"upsell",
|
|
1134
|
+
"downsell",
|
|
1135
|
+
"cross_sell",
|
|
1136
|
+
"addon",
|
|
1137
|
+
"subscription",
|
|
1138
|
+
"custom",
|
|
1139
|
+
"other"
|
|
1140
|
+
]);
|
|
1141
|
+
function cloneProducts(products) {
|
|
1142
|
+
if (!products) {
|
|
1143
|
+
return [];
|
|
1144
|
+
}
|
|
1145
|
+
return products.map((product) => {
|
|
1146
|
+
const type = product.type && PRODUCT_TYPES.has(product.type) ? product.type : void 0;
|
|
1147
|
+
return {
|
|
1148
|
+
id: product.id,
|
|
1149
|
+
...product.name !== void 0 ? { name: product.name } : {},
|
|
1150
|
+
...type !== void 0 ? { type } : {},
|
|
1151
|
+
...product.price !== void 0 ? { price: product.price } : {},
|
|
1152
|
+
...product.currency !== void 0 ? { currency: product.currency } : {},
|
|
1153
|
+
...product.quantity !== void 0 ? { quantity: product.quantity } : {}
|
|
1154
|
+
};
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
function resolveOfferPageCartMode(commerce) {
|
|
1158
|
+
if (commerce?.cartMode === "single_product") {
|
|
1159
|
+
return "single_product";
|
|
1160
|
+
}
|
|
1161
|
+
return "landing_page";
|
|
1162
|
+
}
|
|
1163
|
+
function mapPublicContextToIdentity(base, publicContext) {
|
|
1164
|
+
return {
|
|
1165
|
+
...base,
|
|
1166
|
+
...publicContext.pageId !== void 0 ? { pageId: publicContext.pageId } : {},
|
|
1167
|
+
...publicContext.funnelId !== void 0 ? { funnelId: publicContext.funnelId } : {},
|
|
1168
|
+
...publicContext.funnelStepId !== void 0 ? { funnelStepId: publicContext.funnelStepId } : {},
|
|
1169
|
+
...publicContext.offerId !== void 0 ? { offerId: publicContext.offerId } : {},
|
|
1170
|
+
...publicContext.affiliateId !== void 0 ? { affiliateId: publicContext.affiliateId } : {},
|
|
1171
|
+
...publicContext.checkoutId !== void 0 ? { checkoutId: publicContext.checkoutId } : {}
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
function applyMinimalScenarioPreset(base, scenario, commerce, allowedDomains) {
|
|
1175
|
+
const products = cloneProducts(commerce?.products);
|
|
1176
|
+
const currency = commerce?.currency ?? base.commerce.currency ?? "BRL";
|
|
1177
|
+
const urlContext = configureBemonyLinks({
|
|
1178
|
+
allowedDomains: allowedDomains ? [...allowedDomains] : []
|
|
1179
|
+
});
|
|
1180
|
+
return {
|
|
1181
|
+
...base,
|
|
1182
|
+
app: {
|
|
1183
|
+
...base.app,
|
|
1184
|
+
target: SCENARIO_TARGET[scenario],
|
|
1185
|
+
debug: true
|
|
1186
|
+
},
|
|
1187
|
+
commerce: {
|
|
1188
|
+
...base.commerce,
|
|
1189
|
+
enabled: true,
|
|
1190
|
+
currency,
|
|
1191
|
+
catalog: {
|
|
1192
|
+
products
|
|
1193
|
+
},
|
|
1194
|
+
cart: {
|
|
1195
|
+
...base.commerce.cart,
|
|
1196
|
+
mode: SCENARIO_CART_MODE[scenario],
|
|
1197
|
+
initialItems: base.commerce.cart?.initialItems ? base.commerce.cart.initialItems.map((item) => ({ ...item })) : [],
|
|
1198
|
+
allowDynamicItems: true,
|
|
1199
|
+
calculateValueFrom: "cart"
|
|
1200
|
+
},
|
|
1201
|
+
mappings: {
|
|
1202
|
+
attributes: { ...base.commerce.mappings?.attributes ?? {} },
|
|
1203
|
+
fields: { ...base.commerce.mappings?.fields ?? {} }
|
|
1204
|
+
},
|
|
1205
|
+
enrichment: {
|
|
1206
|
+
...base.commerce.enrichment ?? {}
|
|
1207
|
+
}
|
|
1208
|
+
},
|
|
1209
|
+
modules: {
|
|
1210
|
+
...base.modules,
|
|
1211
|
+
core: {
|
|
1212
|
+
...base.modules.core,
|
|
1213
|
+
urlContext
|
|
1214
|
+
},
|
|
1215
|
+
conversion: {
|
|
1216
|
+
...base.modules.conversion,
|
|
1217
|
+
enabled: false
|
|
1218
|
+
},
|
|
1219
|
+
behavior: {
|
|
1220
|
+
...base.modules.behavior,
|
|
1221
|
+
enabled: false
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
function applyScenarioPreset(base, scenario, commerce, allowedDomains, autoInitiateCheckout, noThanksUrl) {
|
|
1227
|
+
if (scenario === "offer-page") {
|
|
1228
|
+
return applyOfferPageScenario(base, {
|
|
1229
|
+
allowedDomains,
|
|
1230
|
+
currency: commerce?.currency,
|
|
1231
|
+
cartMode: resolveOfferPageCartMode(commerce),
|
|
1232
|
+
products: cloneProducts(commerce?.products)
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
if (scenario === "checkout") {
|
|
1236
|
+
return applyCheckoutScenario(base, {
|
|
1237
|
+
allowedDomains,
|
|
1238
|
+
currency: commerce?.currency,
|
|
1239
|
+
products: cloneProducts(commerce?.products),
|
|
1240
|
+
autoInitiateCheckout
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
if (scenario === "upsell-offer") {
|
|
1244
|
+
return applyUpsellOfferScenario(base, {
|
|
1245
|
+
allowedDomains,
|
|
1246
|
+
currency: commerce?.currency,
|
|
1247
|
+
products: cloneProducts(commerce?.products),
|
|
1248
|
+
...noThanksUrl ? { noThanksUrl } : {}
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
if (scenario === "one-click-checkout") {
|
|
1252
|
+
return applyOneClickCheckoutScenario(base, {
|
|
1253
|
+
allowedDomains,
|
|
1254
|
+
currency: commerce?.currency,
|
|
1255
|
+
products: cloneProducts(commerce?.products)
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
if (scenario === "thank-you") {
|
|
1259
|
+
return applyThankYouScenario(base, {
|
|
1260
|
+
allowedDomains,
|
|
1261
|
+
currency: commerce?.currency
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
return applyMinimalScenarioPreset(base, scenario, commerce, allowedDomains);
|
|
1265
|
+
}
|
|
1266
|
+
function createBemonyPixelConfig(options) {
|
|
1267
|
+
const pageInput = normalizeBemonyPageContextInput(options.page);
|
|
1268
|
+
const resolvedPublic = resolveBemonyPageContext({
|
|
1269
|
+
input: pageInput.public,
|
|
1270
|
+
urlParams: options.urlParams,
|
|
1271
|
+
pageMetadata: options.pageMetadata,
|
|
1272
|
+
scenarioDefaults: options.scenarioDefaults
|
|
1273
|
+
});
|
|
1274
|
+
const validatedPublic = validateBemonyPageContext({
|
|
1275
|
+
scenario: options.scenario,
|
|
1276
|
+
publicContext: resolvedPublic
|
|
1277
|
+
});
|
|
1278
|
+
const apiEndpoint = options.overrides?.apiEndpoint ?? options.platform.apiEndpoint;
|
|
1279
|
+
const debugOverride = options.overrides?.debug ?? options.platform.debug;
|
|
1280
|
+
const allowedDomains = options.overrides?.allowedDomains ?? options.platform.allowedDomains;
|
|
1281
|
+
const envResolution = resolveBemonyEnvironment({
|
|
1282
|
+
environment: options.environment,
|
|
1283
|
+
apiEndpoint,
|
|
1284
|
+
debugOverride
|
|
1285
|
+
});
|
|
1286
|
+
let config = {
|
|
1287
|
+
...DEFAULT_PIXEL_CONFIG,
|
|
1288
|
+
app: { ...DEFAULT_PIXEL_CONFIG.app },
|
|
1289
|
+
identity: { ...DEFAULT_PIXEL_CONFIG.identity },
|
|
1290
|
+
transport: { ...DEFAULT_PIXEL_CONFIG.transport },
|
|
1291
|
+
storage: {
|
|
1292
|
+
...DEFAULT_PIXEL_CONFIG.storage,
|
|
1293
|
+
queue: DEFAULT_PIXEL_CONFIG.storage.queue ? { ...DEFAULT_PIXEL_CONFIG.storage.queue } : void 0,
|
|
1294
|
+
keys: DEFAULT_PIXEL_CONFIG.storage.keys ? { ...DEFAULT_PIXEL_CONFIG.storage.keys } : void 0,
|
|
1295
|
+
persistence: DEFAULT_PIXEL_CONFIG.storage.persistence ? { ...DEFAULT_PIXEL_CONFIG.storage.persistence } : void 0
|
|
1296
|
+
},
|
|
1297
|
+
attribution: {
|
|
1298
|
+
...DEFAULT_PIXEL_CONFIG.attribution,
|
|
1299
|
+
customClickIdParams: DEFAULT_PIXEL_CONFIG.attribution.customClickIdParams ? [...DEFAULT_PIXEL_CONFIG.attribution.customClickIdParams] : []
|
|
1300
|
+
},
|
|
1301
|
+
commerce: {
|
|
1302
|
+
...DEFAULT_PIXEL_CONFIG.commerce,
|
|
1303
|
+
catalog: {
|
|
1304
|
+
products: DEFAULT_PIXEL_CONFIG.commerce.catalog?.products ? DEFAULT_PIXEL_CONFIG.commerce.catalog.products.map((p) => ({
|
|
1305
|
+
...p
|
|
1306
|
+
})) : []
|
|
1307
|
+
},
|
|
1308
|
+
cart: {
|
|
1309
|
+
...DEFAULT_PIXEL_CONFIG.commerce.cart,
|
|
1310
|
+
initialItems: DEFAULT_PIXEL_CONFIG.commerce.cart?.initialItems ? DEFAULT_PIXEL_CONFIG.commerce.cart.initialItems.map((i) => ({
|
|
1311
|
+
...i
|
|
1312
|
+
})) : []
|
|
1313
|
+
},
|
|
1314
|
+
mappings: {
|
|
1315
|
+
attributes: {
|
|
1316
|
+
...DEFAULT_PIXEL_CONFIG.commerce.mappings?.attributes ?? {}
|
|
1317
|
+
},
|
|
1318
|
+
fields: { ...DEFAULT_PIXEL_CONFIG.commerce.mappings?.fields ?? {} }
|
|
1319
|
+
},
|
|
1320
|
+
enrichment: { ...DEFAULT_PIXEL_CONFIG.commerce.enrichment ?? {} }
|
|
1321
|
+
},
|
|
1322
|
+
modules: {
|
|
1323
|
+
core: { ...DEFAULT_PIXEL_CONFIG.modules.core },
|
|
1324
|
+
behavior: { ...DEFAULT_PIXEL_CONFIG.modules.behavior },
|
|
1325
|
+
conversion: { ...DEFAULT_PIXEL_CONFIG.modules.conversion },
|
|
1326
|
+
transport: {
|
|
1327
|
+
...DEFAULT_PIXEL_CONFIG.modules.transport,
|
|
1328
|
+
sender: { ...DEFAULT_PIXEL_CONFIG.modules.transport.sender },
|
|
1329
|
+
mockSender: { ...DEFAULT_PIXEL_CONFIG.modules.transport.mockSender }
|
|
1330
|
+
},
|
|
1331
|
+
integrations: { ...DEFAULT_PIXEL_CONFIG.modules.integrations },
|
|
1332
|
+
privacy: { ...DEFAULT_PIXEL_CONFIG.modules.privacy }
|
|
1333
|
+
}
|
|
1334
|
+
};
|
|
1335
|
+
config = applyBemonyPlatformDefaults(config, options.platform);
|
|
1336
|
+
config = applyBemonyEnvironment(config, envResolution);
|
|
1337
|
+
config = applyScenarioPreset(
|
|
1338
|
+
config,
|
|
1339
|
+
options.scenario,
|
|
1340
|
+
options.commerce,
|
|
1341
|
+
allowedDomains,
|
|
1342
|
+
options.autoInitiateCheckout,
|
|
1343
|
+
options.noThanksUrl
|
|
1344
|
+
);
|
|
1345
|
+
config = {
|
|
1346
|
+
...config,
|
|
1347
|
+
identity: mapPublicContextToIdentity(config.identity, validatedPublic)
|
|
1348
|
+
};
|
|
1349
|
+
return config;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
// src/createBemonyPixel.ts
|
|
1353
|
+
import { prepareJourneyUrlContext } from "@beworke/pixel";
|
|
1354
|
+
import {
|
|
1355
|
+
createStorageAdapter,
|
|
1356
|
+
StorageServiceImpl
|
|
1357
|
+
} from "@beworke/pixel/services";
|
|
1358
|
+
|
|
1359
|
+
// src/BemonyPixel.ts
|
|
1360
|
+
import {
|
|
1361
|
+
Pixel
|
|
1362
|
+
} from "@beworke/pixel";
|
|
1363
|
+
|
|
1364
|
+
// src/domain/BemonyLeadInput.ts
|
|
1365
|
+
var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1366
|
+
var BLOCKED_FIELD_KEY = /^(card|cvv|cvc|pan|number|expiry|expiration|password|token|cvv2)/i;
|
|
1367
|
+
function isValidBemonyLead(input) {
|
|
1368
|
+
const email = input.email?.trim();
|
|
1369
|
+
const phone = input.phone?.trim();
|
|
1370
|
+
const emailOk = Boolean(email && EMAIL_PATTERN.test(email));
|
|
1371
|
+
const phoneDigits = phone ? phone.replace(/\D/g, "") : "";
|
|
1372
|
+
const phoneOk = phoneDigits.length >= 8;
|
|
1373
|
+
return emailOk || phoneOk;
|
|
1374
|
+
}
|
|
1375
|
+
function leadFingerprint(input) {
|
|
1376
|
+
const email = input.email?.trim().toLowerCase() ?? "";
|
|
1377
|
+
const phone = input.phone?.replace(/\D/g, "") ?? "";
|
|
1378
|
+
const name = input.name?.trim().toLowerCase() ?? "";
|
|
1379
|
+
return `${email}|${phone}|${name}`;
|
|
1380
|
+
}
|
|
1381
|
+
function sanitizeLeadFields(fields) {
|
|
1382
|
+
if (!fields) {
|
|
1383
|
+
return void 0;
|
|
1384
|
+
}
|
|
1385
|
+
const result = {};
|
|
1386
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
1387
|
+
if (BLOCKED_FIELD_KEY.test(key)) {
|
|
1388
|
+
continue;
|
|
1389
|
+
}
|
|
1390
|
+
result[key] = value;
|
|
1391
|
+
}
|
|
1392
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
// src/domain/BemonyPaymentMethodInput.ts
|
|
1396
|
+
var BEMONY_PAYMENT_METHODS = [
|
|
1397
|
+
"CREDIT_CARD",
|
|
1398
|
+
"PIX",
|
|
1399
|
+
"BOLETO",
|
|
1400
|
+
"PAYPAL",
|
|
1401
|
+
// 'GOOGLE_PAY',
|
|
1402
|
+
// 'APPLE_PAY',
|
|
1403
|
+
"OTHER"
|
|
1404
|
+
];
|
|
1405
|
+
function isValidBemonyPaymentMethod(input) {
|
|
1406
|
+
if (!input) {
|
|
1407
|
+
return false;
|
|
1408
|
+
}
|
|
1409
|
+
return BEMONY_PAYMENT_METHODS.includes(
|
|
1410
|
+
input.paymentMethod
|
|
1411
|
+
);
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
// src/domain/resolvePriceCode.ts
|
|
1415
|
+
function resolvePriceCode(input) {
|
|
1416
|
+
if (!input) {
|
|
1417
|
+
return { priceCode: null, conflict: false };
|
|
1418
|
+
}
|
|
1419
|
+
const fromCode = input.priceCode?.trim() || null;
|
|
1420
|
+
const fromLegacy = input.priceId?.trim() || null;
|
|
1421
|
+
if (fromCode && fromLegacy && fromCode !== fromLegacy) {
|
|
1422
|
+
return { priceCode: fromCode, conflict: true };
|
|
1423
|
+
}
|
|
1424
|
+
return {
|
|
1425
|
+
priceCode: fromCode ?? fromLegacy,
|
|
1426
|
+
conflict: false
|
|
1427
|
+
};
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
// src/navigation/extractPriceCodeFromUrl.ts
|
|
1431
|
+
function extractPriceCodeFromUrl(url) {
|
|
1432
|
+
let pathname;
|
|
1433
|
+
try {
|
|
1434
|
+
pathname = typeof url === "string" ? new URL(url).pathname : url.pathname;
|
|
1435
|
+
} catch {
|
|
1436
|
+
return null;
|
|
1437
|
+
}
|
|
1438
|
+
const segments = pathname.split("/").filter((segment) => segment.length > 0);
|
|
1439
|
+
for (let i = segments.length - 1; i >= 0; i -= 1) {
|
|
1440
|
+
const segment = segments[i];
|
|
1441
|
+
if (!segment) continue;
|
|
1442
|
+
const bare = segment.replace(/\.[a-zA-Z0-9]+$/, "");
|
|
1443
|
+
if (/^pri[_a-zA-Z0-9]*\d[_a-zA-Z0-9]*$/i.test(bare)) {
|
|
1444
|
+
return bare;
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
return null;
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
// src/BemonyPixel.ts
|
|
1451
|
+
var SAFE_PUBLIC_ORDER_ID = /^[a-zA-Z0-9_.:-]+$/;
|
|
1452
|
+
var MAX_PUBLIC_ORDER_ID_LENGTH = 128;
|
|
1453
|
+
var BemonyLifecycleError = class extends Error {
|
|
1454
|
+
constructor(options) {
|
|
1455
|
+
super(options.message);
|
|
1456
|
+
this.name = "BemonyLifecycleError";
|
|
1457
|
+
this.code = options.code;
|
|
1458
|
+
this.state = options.state;
|
|
1459
|
+
if (options.cause !== void 0) {
|
|
1460
|
+
this.cause = options.cause;
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
};
|
|
1464
|
+
function requirePriceCode(input, method, state) {
|
|
1465
|
+
const { priceCode, conflict } = resolvePriceCode(input);
|
|
1466
|
+
if (!priceCode) {
|
|
1467
|
+
throw new BemonyLifecycleError({
|
|
1468
|
+
code: "INVALID_STATE",
|
|
1469
|
+
message: `BemonyPixel.${method} requires priceCode.`,
|
|
1470
|
+
state
|
|
1471
|
+
});
|
|
1472
|
+
}
|
|
1473
|
+
if (conflict && typeof console !== "undefined" && console.debug) {
|
|
1474
|
+
console.debug("[BemonyPixel] priceCode/priceId conflict; using priceCode", {
|
|
1475
|
+
code: "BEMONY_PRICE_CODE_CONFLICT",
|
|
1476
|
+
method
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1479
|
+
return priceCode;
|
|
1480
|
+
}
|
|
1481
|
+
function toViewContentData(input) {
|
|
1482
|
+
const priceCode = resolvePriceCode(input).priceCode;
|
|
1483
|
+
const contentType = input.productType === "downsell" ? "downsell" : input.productType === "upsell" ? "upsell" : "offer";
|
|
1484
|
+
const observedValue = input.observedValue ?? input.price;
|
|
1485
|
+
const observedCurrency = input.observedCurrency ?? input.currency;
|
|
1486
|
+
return {
|
|
1487
|
+
contentId: priceCode,
|
|
1488
|
+
contentType,
|
|
1489
|
+
productId: priceCode,
|
|
1490
|
+
...observedValue !== void 0 ? { value: observedValue } : {},
|
|
1491
|
+
...observedCurrency !== void 0 ? { currency: observedCurrency } : {}
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
function toAddToCartData(input) {
|
|
1495
|
+
const priceCode = resolvePriceCode(input).priceCode;
|
|
1496
|
+
const observedValue = input.observedValue ?? input.price;
|
|
1497
|
+
const observedCurrency = input.observedCurrency ?? input.currency;
|
|
1498
|
+
return {
|
|
1499
|
+
productId: priceCode,
|
|
1500
|
+
quantity: input.quantity ?? 1,
|
|
1501
|
+
...observedValue !== void 0 ? { price: observedValue } : {},
|
|
1502
|
+
...observedCurrency !== void 0 ? { currency: observedCurrency } : {}
|
|
1503
|
+
};
|
|
1504
|
+
}
|
|
1505
|
+
function toLeadData(input) {
|
|
1506
|
+
const fields = sanitizeLeadFields(input.fields);
|
|
1507
|
+
return {
|
|
1508
|
+
...input.email !== void 0 ? { email: input.email.trim() } : {},
|
|
1509
|
+
...input.phone !== void 0 ? { phone: input.phone.trim() } : {},
|
|
1510
|
+
...input.name !== void 0 ? { name: input.name.trim() } : {},
|
|
1511
|
+
...fields !== void 0 ? { fields } : {}
|
|
1512
|
+
};
|
|
1513
|
+
}
|
|
1514
|
+
function resolvePriceCodeFromAcceptElement(el) {
|
|
1515
|
+
if (el instanceof HTMLAnchorElement && el.href) {
|
|
1516
|
+
const fromHref = extractPriceCodeFromUrl(el.href);
|
|
1517
|
+
if (fromHref) {
|
|
1518
|
+
return fromHref;
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
const href = el.getAttribute("href");
|
|
1522
|
+
if (!href) {
|
|
1523
|
+
return null;
|
|
1524
|
+
}
|
|
1525
|
+
try {
|
|
1526
|
+
const base = typeof window !== "undefined" && window.location?.href ? window.location.href : "https://localhost/";
|
|
1527
|
+
return extractPriceCodeFromUrl(new URL(href, base));
|
|
1528
|
+
} catch {
|
|
1529
|
+
return null;
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
var BemonyPixel = class {
|
|
1533
|
+
constructor(options) {
|
|
1534
|
+
this.state = "created";
|
|
1535
|
+
this.initPromise = null;
|
|
1536
|
+
this.destroyPromise = null;
|
|
1537
|
+
this.wrapperListeners = /* @__PURE__ */ new Set();
|
|
1538
|
+
this.checkoutStarted = false;
|
|
1539
|
+
this.lastLeadFingerprint = null;
|
|
1540
|
+
this.selectedBumpIds = /* @__PURE__ */ new Set();
|
|
1541
|
+
this.config = options.config;
|
|
1542
|
+
this.scenario = options.scenario;
|
|
1543
|
+
this.publicOrderId = options.publicOrderId?.trim() || void 0;
|
|
1544
|
+
this.publicContext = options.publicContext ? { ...options.publicContext } : {};
|
|
1545
|
+
this.autoInitiateCheckoutEnabled = options.autoInitiateCheckout !== false;
|
|
1546
|
+
this.noThanksUrl = options.noThanksUrl?.trim() || void 0;
|
|
1547
|
+
const createCore = options.createCorePixel ?? ((config) => new Pixel(config));
|
|
1548
|
+
this.core = createCore(options.config);
|
|
1549
|
+
}
|
|
1550
|
+
getScenario() {
|
|
1551
|
+
return this.scenario;
|
|
1552
|
+
}
|
|
1553
|
+
getConfig() {
|
|
1554
|
+
return this.config;
|
|
1555
|
+
}
|
|
1556
|
+
getState() {
|
|
1557
|
+
return this.state;
|
|
1558
|
+
}
|
|
1559
|
+
isInitialized() {
|
|
1560
|
+
return this.state === "ready" && this.core.isInitialized();
|
|
1561
|
+
}
|
|
1562
|
+
getDebugSnapshot() {
|
|
1563
|
+
return this.core.getDebugSnapshot();
|
|
1564
|
+
}
|
|
1565
|
+
/**
|
|
1566
|
+
* `publicOrderId` do domínio do app (não mapeado para identity do Core).
|
|
1567
|
+
* Fallback opcional: param `oid` da URL atual.
|
|
1568
|
+
*/
|
|
1569
|
+
getPublicOrderId() {
|
|
1570
|
+
if (this.publicOrderId) {
|
|
1571
|
+
return this.publicOrderId;
|
|
1572
|
+
}
|
|
1573
|
+
if (typeof window === "undefined" || typeof window.location === "undefined") {
|
|
1574
|
+
return null;
|
|
1575
|
+
}
|
|
1576
|
+
try {
|
|
1577
|
+
return new URL(window.location.href).searchParams.get("oid");
|
|
1578
|
+
} catch {
|
|
1579
|
+
return null;
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
/**
|
|
1583
|
+
* Define `publicOrderId` e propaga para managed param `oid` quando suportado.
|
|
1584
|
+
*/
|
|
1585
|
+
setPublicOrderId(orderId) {
|
|
1586
|
+
const trimmed = orderId.trim();
|
|
1587
|
+
if (!trimmed || trimmed.length > MAX_PUBLIC_ORDER_ID_LENGTH) {
|
|
1588
|
+
throw new BemonyLifecycleError({
|
|
1589
|
+
code: "INVALID_STATE",
|
|
1590
|
+
message: "setPublicOrderId requires a non-empty orderId within max length.",
|
|
1591
|
+
state: this.state
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
if (!SAFE_PUBLIC_ORDER_ID.test(trimmed)) {
|
|
1595
|
+
throw new BemonyLifecycleError({
|
|
1596
|
+
code: "INVALID_STATE",
|
|
1597
|
+
message: "setPublicOrderId orderId contains invalid characters.",
|
|
1598
|
+
state: this.state
|
|
1599
|
+
});
|
|
1600
|
+
}
|
|
1601
|
+
if (this.publicOrderId === trimmed) {
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1604
|
+
this.publicOrderId = trimmed;
|
|
1605
|
+
this.applyPublicOrderIdToManagedParam();
|
|
1606
|
+
}
|
|
1607
|
+
getPublicSessionId() {
|
|
1608
|
+
if (typeof window === "undefined" || typeof window.location === "undefined") {
|
|
1609
|
+
return null;
|
|
1610
|
+
}
|
|
1611
|
+
try {
|
|
1612
|
+
return new URL(window.location.href).searchParams.get("sid");
|
|
1613
|
+
} catch {
|
|
1614
|
+
return null;
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
selectOffer(input) {
|
|
1618
|
+
this.assertReady("selectOffer");
|
|
1619
|
+
requirePriceCode(input, "selectOffer", this.state);
|
|
1620
|
+
this.core.addToCart(toAddToCartData(input));
|
|
1621
|
+
}
|
|
1622
|
+
/**
|
|
1623
|
+
* Inicia checkout → `Pixel.initiateCheckout()`.
|
|
1624
|
+
* Idempotente: no máximo uma vez por lifecycle da instância.
|
|
1625
|
+
*/
|
|
1626
|
+
startCheckout(input) {
|
|
1627
|
+
this.assertReady("startCheckout");
|
|
1628
|
+
if (this.checkoutStarted) {
|
|
1629
|
+
return;
|
|
1630
|
+
}
|
|
1631
|
+
const data = input ? {
|
|
1632
|
+
...input.value !== void 0 ? { value: input.value } : {},
|
|
1633
|
+
...input.currency !== void 0 ? { currency: input.currency } : {}
|
|
1634
|
+
} : void 0;
|
|
1635
|
+
this.core.initiateCheckout(data);
|
|
1636
|
+
this.checkoutStarted = true;
|
|
1637
|
+
}
|
|
1638
|
+
/**
|
|
1639
|
+
* Captura lead → `Pixel.lead()`.
|
|
1640
|
+
* Campos incompletos não disparam. Mesmo fingerprint não reenvia (debounce).
|
|
1641
|
+
*/
|
|
1642
|
+
captureLead(input) {
|
|
1643
|
+
this.assertReady("captureLead");
|
|
1644
|
+
if (!isValidBemonyLead(input)) {
|
|
1645
|
+
return false;
|
|
1646
|
+
}
|
|
1647
|
+
const fingerprint = leadFingerprint(input);
|
|
1648
|
+
if (this.lastLeadFingerprint === fingerprint) {
|
|
1649
|
+
return false;
|
|
1650
|
+
}
|
|
1651
|
+
this.core.lead(toLeadData(input));
|
|
1652
|
+
this.lastLeadFingerprint = fingerprint;
|
|
1653
|
+
return true;
|
|
1654
|
+
}
|
|
1655
|
+
selectOrderBump(input) {
|
|
1656
|
+
this.assertReady("selectOrderBump");
|
|
1657
|
+
const priceCode = requirePriceCode(input, "selectOrderBump", this.state);
|
|
1658
|
+
this.core.addToCart(toAddToCartData(input));
|
|
1659
|
+
this.selectedBumpIds.add(priceCode);
|
|
1660
|
+
}
|
|
1661
|
+
removeOrderBump(input) {
|
|
1662
|
+
this.assertReady("removeOrderBump");
|
|
1663
|
+
const priceCode = requirePriceCode(input, "removeOrderBump", this.state);
|
|
1664
|
+
if (!this.selectedBumpIds.has(priceCode)) {
|
|
1665
|
+
return;
|
|
1666
|
+
}
|
|
1667
|
+
this.core.removeFromCart({
|
|
1668
|
+
// No adaptador Bemony, o campo genérico productId transporta o priceCode.
|
|
1669
|
+
productId: priceCode,
|
|
1670
|
+
...input.quantity !== void 0 ? { quantity: input.quantity } : {}
|
|
1671
|
+
});
|
|
1672
|
+
this.selectedBumpIds.delete(priceCode);
|
|
1673
|
+
}
|
|
1674
|
+
trackPaymentMethod(input) {
|
|
1675
|
+
this.assertReady("trackPaymentMethod");
|
|
1676
|
+
if (!isValidBemonyPaymentMethod(input)) {
|
|
1677
|
+
throw new BemonyLifecycleError({
|
|
1678
|
+
code: "INVALID_STATE",
|
|
1679
|
+
message: "trackPaymentMethod requires a valid paymentMethod enum.",
|
|
1680
|
+
state: this.state
|
|
1681
|
+
});
|
|
1682
|
+
}
|
|
1683
|
+
this.core.addPaymentInfo({ paymentMethod: input.paymentMethod });
|
|
1684
|
+
}
|
|
1685
|
+
viewUpsell(input) {
|
|
1686
|
+
this.assertReady("viewUpsell");
|
|
1687
|
+
requirePriceCode(input, "viewUpsell", this.state);
|
|
1688
|
+
this.core.viewContent(toViewContentData(input));
|
|
1689
|
+
}
|
|
1690
|
+
acceptUpsell(input) {
|
|
1691
|
+
this.assertReady("acceptUpsell");
|
|
1692
|
+
requirePriceCode(input, "acceptUpsell", this.state);
|
|
1693
|
+
this.core.addToCart(toAddToCartData(input));
|
|
1694
|
+
}
|
|
1695
|
+
/**
|
|
1696
|
+
* Recusa de upsell/downsell — callback/navegação apenas.
|
|
1697
|
+
* Destino: `navigateTo` explícito, senão `noThanksUrl` (data-no-thanks).
|
|
1698
|
+
*/
|
|
1699
|
+
declineUpsell(options) {
|
|
1700
|
+
this.assertReady("declineUpsell");
|
|
1701
|
+
try {
|
|
1702
|
+
options?.onDecline?.();
|
|
1703
|
+
} finally {
|
|
1704
|
+
const explicitTarget = options?.navigateTo?.trim() || this.noThanksUrl || void 0;
|
|
1705
|
+
const target = explicitTarget ? this.decorateNavigateUrl(explicitTarget) : void 0;
|
|
1706
|
+
if (target && typeof window !== "undefined" && typeof window.location?.assign === "function") {
|
|
1707
|
+
window.location.assign(target);
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
/**
|
|
1712
|
+
* Intenção de confirmação one-click → `Pixel.addToCart()`.
|
|
1713
|
+
* Não processa pagamento e **não** emite PURCHASE.
|
|
1714
|
+
*/
|
|
1715
|
+
trackOneClickConfirmation(input) {
|
|
1716
|
+
this.assertReady("trackOneClickConfirmation");
|
|
1717
|
+
this.assertOneClickReady("trackOneClickConfirmation");
|
|
1718
|
+
requirePriceCode(input, "trackOneClickConfirmation", this.state);
|
|
1719
|
+
this.core.addToCart(toAddToCartData(input));
|
|
1720
|
+
}
|
|
1721
|
+
init() {
|
|
1722
|
+
if (this.state === "ready") {
|
|
1723
|
+
return Promise.resolve();
|
|
1724
|
+
}
|
|
1725
|
+
if (this.state === "initializing" && this.initPromise) {
|
|
1726
|
+
return this.initPromise;
|
|
1727
|
+
}
|
|
1728
|
+
if (this.state === "destroying" && this.destroyPromise) {
|
|
1729
|
+
this.initPromise = this.destroyPromise.catch(() => void 0).then(() => this.startInit());
|
|
1730
|
+
return this.initPromise;
|
|
1731
|
+
}
|
|
1732
|
+
this.initPromise = this.startInit();
|
|
1733
|
+
return this.initPromise;
|
|
1734
|
+
}
|
|
1735
|
+
destroy() {
|
|
1736
|
+
if (this.state === "destroyed") {
|
|
1737
|
+
return Promise.resolve();
|
|
1738
|
+
}
|
|
1739
|
+
if (this.state === "destroying" && this.destroyPromise) {
|
|
1740
|
+
return this.destroyPromise;
|
|
1741
|
+
}
|
|
1742
|
+
if (this.state === "initializing" && this.initPromise) {
|
|
1743
|
+
this.destroyPromise = this.initPromise.catch(() => void 0).then(() => this.startDestroy());
|
|
1744
|
+
return this.destroyPromise;
|
|
1745
|
+
}
|
|
1746
|
+
this.destroyPromise = this.startDestroy();
|
|
1747
|
+
return this.destroyPromise;
|
|
1748
|
+
}
|
|
1749
|
+
addWrapperListener(cleanup) {
|
|
1750
|
+
this.wrapperListeners.add(cleanup);
|
|
1751
|
+
return () => {
|
|
1752
|
+
this.wrapperListeners.delete(cleanup);
|
|
1753
|
+
};
|
|
1754
|
+
}
|
|
1755
|
+
assertReady(method) {
|
|
1756
|
+
if (this.state !== "ready") {
|
|
1757
|
+
throw new BemonyLifecycleError({
|
|
1758
|
+
code: "INVALID_STATE",
|
|
1759
|
+
message: `BemonyPixel.${method} requires state "ready" (current: "${this.state}").`,
|
|
1760
|
+
state: this.state
|
|
1761
|
+
});
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
assertOneClickReady(method) {
|
|
1765
|
+
if (!this.getPublicOrderId()) {
|
|
1766
|
+
throw new BemonyLifecycleError({
|
|
1767
|
+
code: "INVALID_STATE",
|
|
1768
|
+
message: `BemonyPixel.${method} requires publicOrderId.`,
|
|
1769
|
+
state: this.state
|
|
1770
|
+
});
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
applyPublicOrderIdToManagedParam() {
|
|
1774
|
+
if (!this.publicOrderId || !this.core.setManagedUrlParam) {
|
|
1775
|
+
return;
|
|
1776
|
+
}
|
|
1777
|
+
this.core.setManagedUrlParam("oid", this.publicOrderId);
|
|
1778
|
+
}
|
|
1779
|
+
decorateNavigateUrl(rawUrl) {
|
|
1780
|
+
try {
|
|
1781
|
+
const base = typeof window !== "undefined" && window.location?.href ? window.location.href : "https://localhost/";
|
|
1782
|
+
const url = new URL(rawUrl, base);
|
|
1783
|
+
const sid = this.getPublicSessionId();
|
|
1784
|
+
const oid = this.getPublicOrderId();
|
|
1785
|
+
const fid = this.publicContext.funnelId;
|
|
1786
|
+
const fsid = this.publicContext.funnelStepId;
|
|
1787
|
+
const afid = this.publicContext.affiliateId;
|
|
1788
|
+
if (sid && !url.searchParams.has("sid")) {
|
|
1789
|
+
url.searchParams.set("sid", sid);
|
|
1790
|
+
}
|
|
1791
|
+
if (oid && !url.searchParams.has("oid")) {
|
|
1792
|
+
url.searchParams.set("oid", oid);
|
|
1793
|
+
}
|
|
1794
|
+
if (fid && !url.searchParams.has("fid")) {
|
|
1795
|
+
url.searchParams.set("fid", fid);
|
|
1796
|
+
}
|
|
1797
|
+
if (fsid && !url.searchParams.has("fsid")) {
|
|
1798
|
+
url.searchParams.set("fsid", fsid);
|
|
1799
|
+
}
|
|
1800
|
+
if (afid && !url.searchParams.has("afid")) {
|
|
1801
|
+
url.searchParams.set("afid", afid);
|
|
1802
|
+
}
|
|
1803
|
+
return url.toString();
|
|
1804
|
+
} catch {
|
|
1805
|
+
return rawUrl;
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
maybeAutoInitiateCheckout() {
|
|
1809
|
+
if (this.scenario !== "checkout" && this.scenario !== "one-click-checkout") {
|
|
1810
|
+
return;
|
|
1811
|
+
}
|
|
1812
|
+
if (!this.autoInitiateCheckoutEnabled || this.checkoutStarted) {
|
|
1813
|
+
return;
|
|
1814
|
+
}
|
|
1815
|
+
this.core.initiateCheckout();
|
|
1816
|
+
this.checkoutStarted = true;
|
|
1817
|
+
}
|
|
1818
|
+
resetDomainState() {
|
|
1819
|
+
this.checkoutStarted = false;
|
|
1820
|
+
this.lastLeadFingerprint = null;
|
|
1821
|
+
this.selectedBumpIds.clear();
|
|
1822
|
+
this.publicOrderId = void 0;
|
|
1823
|
+
}
|
|
1824
|
+
startInit() {
|
|
1825
|
+
this.state = "initializing";
|
|
1826
|
+
return this.core.init().then(() => {
|
|
1827
|
+
if (this.publicOrderId) {
|
|
1828
|
+
this.applyPublicOrderIdToManagedParam();
|
|
1829
|
+
}
|
|
1830
|
+
}).then(() => {
|
|
1831
|
+
this.state = "ready";
|
|
1832
|
+
this.maybeAutoInitiateCheckout();
|
|
1833
|
+
}).catch((error) => {
|
|
1834
|
+
this.state = "failed";
|
|
1835
|
+
return this.safeCoreDestroy().then(() => {
|
|
1836
|
+
throw new BemonyLifecycleError({
|
|
1837
|
+
code: "INIT_FAILED",
|
|
1838
|
+
message: "BemonyPixel init failed.",
|
|
1839
|
+
state: this.state,
|
|
1840
|
+
cause: error
|
|
1841
|
+
});
|
|
1842
|
+
});
|
|
1843
|
+
}).finally(() => {
|
|
1844
|
+
this.initPromise = null;
|
|
1845
|
+
});
|
|
1846
|
+
}
|
|
1847
|
+
startDestroy() {
|
|
1848
|
+
this.state = "destroying";
|
|
1849
|
+
return this.safeCoreDestroy().then(() => {
|
|
1850
|
+
this.clearWrapperListeners();
|
|
1851
|
+
this.resetDomainState();
|
|
1852
|
+
this.state = "destroyed";
|
|
1853
|
+
}).catch((error) => {
|
|
1854
|
+
this.clearWrapperListeners();
|
|
1855
|
+
this.resetDomainState();
|
|
1856
|
+
this.state = "destroyed";
|
|
1857
|
+
throw new BemonyLifecycleError({
|
|
1858
|
+
code: "DESTROY_FAILED",
|
|
1859
|
+
message: "BemonyPixel destroy failed.",
|
|
1860
|
+
state: this.state,
|
|
1861
|
+
cause: error
|
|
1862
|
+
});
|
|
1863
|
+
}).finally(() => {
|
|
1864
|
+
this.destroyPromise = null;
|
|
1865
|
+
});
|
|
1866
|
+
}
|
|
1867
|
+
async safeCoreDestroy() {
|
|
1868
|
+
await this.core.destroy();
|
|
1869
|
+
}
|
|
1870
|
+
clearWrapperListeners() {
|
|
1871
|
+
for (const cleanup of this.wrapperListeners) {
|
|
1872
|
+
try {
|
|
1873
|
+
cleanup();
|
|
1874
|
+
} catch {
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
this.wrapperListeners.clear();
|
|
1878
|
+
}
|
|
1879
|
+
};
|
|
1880
|
+
|
|
1881
|
+
// src/createBemonyPixel.ts
|
|
1882
|
+
function createJourneyStorage() {
|
|
1883
|
+
return new StorageServiceImpl(
|
|
1884
|
+
createStorageAdapter("localStorage"),
|
|
1885
|
+
"bemony",
|
|
1886
|
+
"urlctx"
|
|
1887
|
+
);
|
|
1888
|
+
}
|
|
1889
|
+
function seedJourneyManagedParams(journey, publicContext) {
|
|
1890
|
+
if (publicContext.funnelId) {
|
|
1891
|
+
journey.setManagedParam("fid", publicContext.funnelId);
|
|
1892
|
+
}
|
|
1893
|
+
if (publicContext.funnelStepId) {
|
|
1894
|
+
journey.setManagedParam("fsid", publicContext.funnelStepId);
|
|
1895
|
+
}
|
|
1896
|
+
if (publicContext.publicOrderId) {
|
|
1897
|
+
journey.setManagedParam("oid", publicContext.publicOrderId);
|
|
1898
|
+
}
|
|
1899
|
+
if (publicContext.affiliateId) {
|
|
1900
|
+
journey.setManagedParam("afid", publicContext.affiliateId);
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
async function createBemonyPixel(options) {
|
|
1904
|
+
const pageInput = normalizeBemonyPageContextInput(options.page);
|
|
1905
|
+
const publicContext = validateBemonyPageContext({
|
|
1906
|
+
scenario: options.scenario,
|
|
1907
|
+
publicContext: resolveBemonyPageContext({
|
|
1908
|
+
input: pageInput.public,
|
|
1909
|
+
urlParams: options.urlParams,
|
|
1910
|
+
pageMetadata: options.pageMetadata,
|
|
1911
|
+
scenarioDefaults: options.scenarioDefaults
|
|
1912
|
+
})
|
|
1913
|
+
});
|
|
1914
|
+
const allowedDomains = options.overrides?.allowedDomains ?? options.platform.allowedDomains;
|
|
1915
|
+
const journeyUrlConfig = configureBemonyLinks({
|
|
1916
|
+
allowedDomains: allowedDomains ? [...allowedDomains] : []
|
|
1917
|
+
});
|
|
1918
|
+
const journeyStorage = createJourneyStorage();
|
|
1919
|
+
const journey = prepareJourneyUrlContext(journeyUrlConfig, journeyStorage);
|
|
1920
|
+
seedJourneyManagedParams(journey, publicContext);
|
|
1921
|
+
const config = createBemonyPixelConfig({
|
|
1922
|
+
...options,
|
|
1923
|
+
page: { public: publicContext }
|
|
1924
|
+
});
|
|
1925
|
+
const construct = {
|
|
1926
|
+
config,
|
|
1927
|
+
scenario: options.scenario,
|
|
1928
|
+
publicOrderId: publicContext.publicOrderId,
|
|
1929
|
+
publicContext,
|
|
1930
|
+
autoInitiateCheckout: options.autoInitiateCheckout,
|
|
1931
|
+
...options.noThanksUrl ? { noThanksUrl: options.noThanksUrl } : {},
|
|
1932
|
+
...options.createCorePixel ? { createCorePixel: options.createCorePixel } : {}
|
|
1933
|
+
};
|
|
1934
|
+
try {
|
|
1935
|
+
const instance = new BemonyPixel(construct);
|
|
1936
|
+
await instance.init();
|
|
1937
|
+
return instance;
|
|
1938
|
+
} finally {
|
|
1939
|
+
journey.destroy();
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
// src/scenarios/types.ts
|
|
1944
|
+
var BEMONY_SCENARIOS = [
|
|
1945
|
+
"offer-page",
|
|
1946
|
+
"checkout",
|
|
1947
|
+
"upsell-offer",
|
|
1948
|
+
"one-click-checkout",
|
|
1949
|
+
"thank-you"
|
|
1950
|
+
];
|
|
1951
|
+
|
|
1952
|
+
// src/contracts/validateBackendPurchase.ts
|
|
1953
|
+
var BLOCKED_REDIRECT_KEYS = /^(token|one_click_token|oneClickToken|access_token|password|card|cvv|pan)$/i;
|
|
1954
|
+
function validateBemonyServerPurchasePlaceholder(payload) {
|
|
1955
|
+
if (!payload || typeof payload !== "object") {
|
|
1956
|
+
throw new BemonyValidationError({
|
|
1957
|
+
code: "INVALID_FIELD",
|
|
1958
|
+
field: "payload",
|
|
1959
|
+
message: "Server purchase payload must be an object (placeholder contract)."
|
|
1960
|
+
});
|
|
1961
|
+
}
|
|
1962
|
+
const value = payload;
|
|
1963
|
+
if (value.type !== "PURCHASE") {
|
|
1964
|
+
throw new BemonyValidationError({
|
|
1965
|
+
code: "INVALID_FIELD",
|
|
1966
|
+
field: "type",
|
|
1967
|
+
message: 'Placeholder purchase payload requires type "PURCHASE".'
|
|
1968
|
+
});
|
|
1969
|
+
}
|
|
1970
|
+
if (typeof value.event_id !== "string" || !value.event_id.trim()) {
|
|
1971
|
+
throw new BemonyValidationError({
|
|
1972
|
+
code: "MISSING_REQUIRED_FIELD",
|
|
1973
|
+
field: "event_id",
|
|
1974
|
+
message: "Placeholder purchase payload requires event_id."
|
|
1975
|
+
});
|
|
1976
|
+
}
|
|
1977
|
+
if (typeof value.occurred_at !== "string" || !value.occurred_at.trim()) {
|
|
1978
|
+
throw new BemonyValidationError({
|
|
1979
|
+
code: "MISSING_REQUIRED_FIELD",
|
|
1980
|
+
field: "occurred_at",
|
|
1981
|
+
message: "Placeholder purchase payload requires occurred_at."
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1984
|
+
return payload;
|
|
1985
|
+
}
|
|
1986
|
+
function assertSafePublicRedirectParams(params) {
|
|
1987
|
+
const attribution = params.attribution ? { ...params.attribution } : void 0;
|
|
1988
|
+
if (attribution) {
|
|
1989
|
+
for (const key of Object.keys(attribution)) {
|
|
1990
|
+
if (BLOCKED_REDIRECT_KEYS.test(key)) {
|
|
1991
|
+
throw new BemonyValidationError({
|
|
1992
|
+
code: "INVALID_FIELD",
|
|
1993
|
+
field: key,
|
|
1994
|
+
message: "Redirect params must not include secrets (token/card/etc.)."
|
|
1995
|
+
});
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
return {
|
|
2000
|
+
...params.sid !== void 0 ? { sid: params.sid } : {},
|
|
2001
|
+
...params.public_order_id !== void 0 ? { public_order_id: params.public_order_id } : {},
|
|
2002
|
+
...params.funnel_id !== void 0 ? { funnel_id: params.funnel_id } : {},
|
|
2003
|
+
...params.offer_id !== void 0 ? { offer_id: params.offer_id } : {},
|
|
2004
|
+
...attribution !== void 0 ? { attribution } : {}
|
|
2005
|
+
};
|
|
2006
|
+
}
|
|
2007
|
+
function buildPublicRedirectQuery(params) {
|
|
2008
|
+
const safe = assertSafePublicRedirectParams(params);
|
|
2009
|
+
const search = new URLSearchParams();
|
|
2010
|
+
if (safe.sid) search.set("sid", safe.sid);
|
|
2011
|
+
if (safe.public_order_id) search.set("public_order_id", safe.public_order_id);
|
|
2012
|
+
if (safe.funnel_id) search.set("funnel_id", safe.funnel_id);
|
|
2013
|
+
if (safe.offer_id) search.set("offer_id", safe.offer_id);
|
|
2014
|
+
if (safe.attribution) {
|
|
2015
|
+
for (const [key, value] of Object.entries(safe.attribution)) {
|
|
2016
|
+
search.set(key, value);
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
const query = search.toString();
|
|
2020
|
+
return query ? `?${query}` : "";
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
// src/domain/BemonyOfferInput.ts
|
|
2024
|
+
function isCompleteBemonyOffer(input) {
|
|
2025
|
+
return resolvePriceCode(input).priceCode !== null;
|
|
2026
|
+
}
|
|
2027
|
+
export {
|
|
2028
|
+
BEMONY_AFID_MANAGED_PARAM,
|
|
2029
|
+
BEMONY_BROWSER_AUTO_INIT_DEFAULTS,
|
|
2030
|
+
BEMONY_DEFAULT_ALLOWED_DOMAINS,
|
|
2031
|
+
BEMONY_DEFAULT_API_ENDPOINT,
|
|
2032
|
+
BEMONY_DEFAULT_BROWSER_ENVIRONMENT,
|
|
2033
|
+
BEMONY_DEFAULT_BROWSER_SCENARIO,
|
|
2034
|
+
BEMONY_DEFAULT_NAMESPACE,
|
|
2035
|
+
BEMONY_DEFAULT_PIXEL_ID,
|
|
2036
|
+
BEMONY_ENVIRONMENTS,
|
|
2037
|
+
BEMONY_FORWARD_DENYLIST,
|
|
2038
|
+
BEMONY_PAYMENT_METHODS,
|
|
2039
|
+
BEMONY_PLATFORM_DEFAULTS,
|
|
2040
|
+
BEMONY_SCENARIOS,
|
|
2041
|
+
BEMONY_SID_MANAGED_PARAM,
|
|
2042
|
+
BEMONY_STORAGE_PREFIX,
|
|
2043
|
+
BEMONY_UPSELL_BUY_LINK_SELECTOR,
|
|
2044
|
+
BEMONY_UPSELL_LINK_SELECTOR,
|
|
2045
|
+
BemonyLifecycleError,
|
|
2046
|
+
BemonyPixel,
|
|
2047
|
+
BemonyValidationError,
|
|
2048
|
+
DEFAULT_BEMONY_LINK_SELECTOR,
|
|
2049
|
+
REQUIRED_PUBLIC_FIELDS,
|
|
2050
|
+
applyCheckoutScenario,
|
|
2051
|
+
applyOfferPageScenario,
|
|
2052
|
+
applyOneClickCheckoutScenario,
|
|
2053
|
+
applyThankYouScenario,
|
|
2054
|
+
applyUpsellOfferScenario,
|
|
2055
|
+
assertSafePublicRedirectParams,
|
|
2056
|
+
buildPublicRedirectQuery,
|
|
2057
|
+
buildUpsellLinkSelector,
|
|
2058
|
+
configureBemonyLinks,
|
|
2059
|
+
createBemonyLinkPolicy,
|
|
2060
|
+
createBemonyOfferLinkSource,
|
|
2061
|
+
createBemonyPixel,
|
|
2062
|
+
createBemonyPixelConfig,
|
|
2063
|
+
extractPriceCodeFromUrl,
|
|
2064
|
+
hrefMatchesNoThanksUrl,
|
|
2065
|
+
isBemonyPageContextInput,
|
|
2066
|
+
isCompleteBemonyOffer,
|
|
2067
|
+
isValidBemonyLead,
|
|
2068
|
+
isValidBemonyPaymentMethod,
|
|
2069
|
+
leadFingerprint,
|
|
2070
|
+
normalizeAllowedDomain,
|
|
2071
|
+
normalizeAllowedDomains,
|
|
2072
|
+
normalizeBemonyPageContextInput,
|
|
2073
|
+
resolveBemonyEnvironment,
|
|
2074
|
+
resolveBemonyPageContext,
|
|
2075
|
+
resolvePriceCode,
|
|
2076
|
+
resolvePriceCodeFromAcceptElement,
|
|
2077
|
+
sanitizeLeadFields,
|
|
2078
|
+
validateBemonyPageContext,
|
|
2079
|
+
validateBemonyServerPurchasePlaceholder
|
|
2080
|
+
};
|
|
2081
|
+
//# sourceMappingURL=index.esm.js.map
|