@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.
Files changed (67) hide show
  1. package/CHANGELOG.md +157 -0
  2. package/README.md +176 -0
  3. package/RELEASE_CANDIDATE.md +37 -0
  4. package/dist/browser/index.cjs.js +2598 -0
  5. package/dist/browser/index.cjs.js.map +1 -0
  6. package/dist/browser/index.d.mts +184 -0
  7. package/dist/browser/index.d.ts +184 -0
  8. package/dist/browser/index.esm.js +2557 -0
  9. package/dist/browser/index.esm.js.map +1 -0
  10. package/dist/browser/pixel-bemony.global.js +9848 -0
  11. package/dist/browser/pixel-bemony.global.js.map +1 -0
  12. package/dist/createBemonyPixel-BGRD66p-.d.mts +474 -0
  13. package/dist/createBemonyPixel-BGRD66p-.d.ts +474 -0
  14. package/dist/index.cjs.js +2154 -0
  15. package/dist/index.cjs.js.map +1 -0
  16. package/dist/index.d.mts +320 -0
  17. package/dist/index.d.ts +320 -0
  18. package/dist/index.esm.js +2081 -0
  19. package/dist/index.esm.js.map +1 -0
  20. package/docs/BACKEND_PURCHASE_AND_REDIRECTS.md +125 -0
  21. package/docs/BROWSER_EVENT_ENRICHMENT.md +116 -0
  22. package/docs/SCENARIOS.md +92 -0
  23. package/docs/SECURITY_LIMITATIONS.md +45 -0
  24. package/docs/TEST_MATRIX.md +39 -0
  25. package/docs/TROUBLESHOOTING.md +81 -0
  26. package/examples/README.md +73 -0
  27. package/examples/browser-bootstrap.html +47 -0
  28. package/examples/checkout-price-code.html +37 -0
  29. package/examples/funnel/donsell/downsell_1.html +130 -0
  30. package/examples/funnel/donsell/downsell_2.html +130 -0
  31. package/examples/funnel/donsell/downsell_3.html +130 -0
  32. package/examples/funnel/offer-page.html +105 -0
  33. package/examples/funnel/upsell/upsell_1.html +130 -0
  34. package/examples/funnel/upsell/upsell_2.html +130 -0
  35. package/examples/funnel/upsell/upsell_3.html +139 -0
  36. package/examples/offer-page-price-code.html +22 -0
  37. package/examples/one-click-price-code.html +38 -0
  38. package/examples/security/checkout-sim.css +408 -0
  39. package/examples/security/checkout-sim.js +414 -0
  40. package/examples/security/one-click/pri_downsell_1.html +85 -0
  41. package/examples/security/one-click/pri_downsell_1a.html +85 -0
  42. package/examples/security/one-click/pri_downsell_1b.html +78 -0
  43. package/examples/security/one-click/pri_downsell_2.html +78 -0
  44. package/examples/security/one-click/pri_downsell_2a.html +78 -0
  45. package/examples/security/one-click/pri_downsell_2b.html +78 -0
  46. package/examples/security/one-click/pri_downsell_3.html +78 -0
  47. package/examples/security/one-click/pri_downsell_3a.html +78 -0
  48. package/examples/security/one-click/pri_downsell_3b.html +78 -0
  49. package/examples/security/one-click/pri_upsell_1.html +78 -0
  50. package/examples/security/one-click/pri_upsell_1a.html +78 -0
  51. package/examples/security/one-click/pri_upsell_1b.html +78 -0
  52. package/examples/security/one-click/pri_upsell_2.html +78 -0
  53. package/examples/security/one-click/pri_upsell_2a.html +78 -0
  54. package/examples/security/one-click/pri_upsell_2b.html +78 -0
  55. package/examples/security/one-click/pri_upsell_3.html +78 -0
  56. package/examples/security/one-click/pri_upsell_3a.html +78 -0
  57. package/examples/security/one-click/pri_upsell_3b.html +78 -0
  58. package/examples/security/one-click/pri_upsell_3c.html +78 -0
  59. package/examples/security/pri1766322.html +333 -0
  60. package/examples/security/pri5529930.html +333 -0
  61. package/examples/security/pri6670988.html +334 -0
  62. package/examples/security/thank-you.html +65 -0
  63. package/examples/sim/funnel-flow.js +258 -0
  64. package/examples/sim/one-click-sim.js +167 -0
  65. package/examples/upsell-price-code.html +41 -0
  66. package/examples/url-context-navigation.html +96 -0
  67. 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