@appfunnel-dev/sdk 2.0.0-canary.1 → 2.0.0-canary.11

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 (58) hide show
  1. package/README.md +1 -1
  2. package/dist/capabilities-Dq22RCr_.d.cts +180 -0
  3. package/dist/capabilities-Dq22RCr_.d.ts +180 -0
  4. package/dist/checkout-ClaO5IYV.d.ts +333 -0
  5. package/dist/checkout-DBp4bCpC.d.cts +333 -0
  6. package/dist/chunk-7JLOF6CJ.cjs +172 -0
  7. package/dist/chunk-7JLOF6CJ.cjs.map +1 -0
  8. package/dist/chunk-EMMSS5I5.cjs +37 -0
  9. package/dist/chunk-EMMSS5I5.cjs.map +1 -0
  10. package/dist/chunk-G3PMV62Z.js +33 -0
  11. package/dist/chunk-G3PMV62Z.js.map +1 -0
  12. package/dist/chunk-JAO6AA4R.js +99 -0
  13. package/dist/chunk-JAO6AA4R.js.map +1 -0
  14. package/dist/chunk-OXQBEKZ5.js +157 -0
  15. package/dist/chunk-OXQBEKZ5.js.map +1 -0
  16. package/dist/chunk-RKPRMTSU.cjs +517 -0
  17. package/dist/chunk-RKPRMTSU.cjs.map +1 -0
  18. package/dist/chunk-SXENKZ4U.js +486 -0
  19. package/dist/chunk-SXENKZ4U.js.map +1 -0
  20. package/dist/chunk-VV7TFC64.cjs +106 -0
  21. package/dist/chunk-VV7TFC64.cjs.map +1 -0
  22. package/dist/chunk-WYUDL4FI.cjs +8 -0
  23. package/dist/chunk-WYUDL4FI.cjs.map +1 -0
  24. package/dist/chunk-ZZJG4EYL.js +6 -0
  25. package/dist/chunk-ZZJG4EYL.js.map +1 -0
  26. package/dist/driver-paddle.cjs +817 -0
  27. package/dist/driver-paddle.cjs.map +1 -0
  28. package/dist/driver-paddle.d.cts +10 -0
  29. package/dist/driver-paddle.d.ts +10 -0
  30. package/dist/driver-paddle.js +814 -0
  31. package/dist/driver-paddle.js.map +1 -0
  32. package/dist/driver-stripe.cjs +2312 -0
  33. package/dist/driver-stripe.cjs.map +1 -0
  34. package/dist/driver-stripe.d.cts +24 -0
  35. package/dist/driver-stripe.d.ts +24 -0
  36. package/dist/driver-stripe.js +2305 -0
  37. package/dist/driver-stripe.js.map +1 -0
  38. package/dist/index.cjs +2677 -846
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +358 -1005
  41. package/dist/index.d.ts +358 -1005
  42. package/dist/index.js +2395 -698
  43. package/dist/index.js.map +1 -1
  44. package/dist/manifest-C2mDXWNU.d.cts +996 -0
  45. package/dist/manifest-C2mDXWNU.d.ts +996 -0
  46. package/dist/manifest-entry.cjs +408 -0
  47. package/dist/manifest-entry.cjs.map +1 -0
  48. package/dist/manifest-entry.d.cts +192 -0
  49. package/dist/manifest-entry.d.ts +192 -0
  50. package/dist/manifest-entry.js +270 -0
  51. package/dist/manifest-entry.js.map +1 -0
  52. package/dist/protocol.cjs +13 -0
  53. package/dist/protocol.cjs.map +1 -0
  54. package/dist/protocol.d.cts +182 -0
  55. package/dist/protocol.d.ts +182 -0
  56. package/dist/protocol.js +4 -0
  57. package/dist/protocol.js.map +1 -0
  58. package/package.json +49 -4
package/dist/index.cjs CHANGED
@@ -1,10 +1,17 @@
1
1
  'use strict';
2
2
 
3
- var react = require('react');
4
- var reactDom = require('react-dom');
5
- var sonner = require('sonner');
3
+ var chunkRKPRMTSU_cjs = require('./chunk-RKPRMTSU.cjs');
4
+ var chunkWYUDL4FI_cjs = require('./chunk-WYUDL4FI.cjs');
5
+ var chunk7JLOF6CJ_cjs = require('./chunk-7JLOF6CJ.cjs');
6
+ require('./chunk-EMMSS5I5.cjs');
7
+ var React = require('react');
8
+ var ReactDOM = require('react-dom');
9
+ var jsxRuntime = require('react/jsx-runtime');
6
10
 
7
- // src/state/namespaces.ts
11
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
+
13
+ var React__default = /*#__PURE__*/_interopDefault(React);
14
+ var ReactDOM__default = /*#__PURE__*/_interopDefault(ReactDOM);
8
15
 
9
16
  // src/state/variableStore.ts
10
17
  var VariableStore = class {
@@ -258,8 +265,12 @@ function collectClickIds() {
258
265
  }
259
266
  function readCookie(name) {
260
267
  if (typeof document === "undefined") return void 0;
261
- const m = document.cookie.match(new RegExp("(?:^|; )" + name + "=([^;]*)"));
262
- return m ? decodeURIComponent(m[1]) : void 0;
268
+ try {
269
+ const m = document.cookie.match(new RegExp("(?:^|; )" + name + "=([^;]*)"));
270
+ return m ? decodeURIComponent(m[1]) : void 0;
271
+ } catch {
272
+ return void 0;
273
+ }
263
274
  }
264
275
  function buildAcquisition(context) {
265
276
  return {
@@ -272,41 +283,44 @@ function buildAcquisition(context) {
272
283
  }
273
284
  function buildContext(opts = {}) {
274
285
  const raw = detect();
275
- const now = opts.now ?? 0;
286
+ const now2 = opts.now ?? 0;
276
287
  const referrer = typeof document !== "undefined" ? document.referrer : "";
277
- const base2 = {
288
+ const base = {
278
289
  device: raw.device,
279
290
  browser: raw.browser,
280
291
  os: raw.os,
281
292
  locale: raw.locale,
282
293
  utm: collectUtm(),
283
294
  clickIds: collectClickIds(),
284
- page: { key: "", slug: "", index: 0, total: 0, progressPercentage: 0, startedAt: now },
285
- session: { id: opts.sessionId ?? null, startedAt: now, referrer },
295
+ page: { key: "", slug: "", index: 0, total: 0, progressPercentage: 0, startedAt: now2 },
296
+ session: { id: opts.sessionId ?? null, startedAt: now2, referrer },
286
297
  identity: { customerId: opts.customerId ?? null, visitorId: opts.visitorId ?? null },
287
298
  system: {
288
299
  funnelId: opts.funnelId ?? "",
289
300
  campaignId: opts.campaignId ?? "",
290
301
  mode: opts.mode ?? "live",
291
- now
302
+ now: now2
292
303
  }
293
304
  };
294
- if (!opts.overrides) return base2;
305
+ if (!opts.overrides) return base;
295
306
  return {
296
- ...base2,
307
+ ...base,
297
308
  ...opts.overrides,
298
- device: { ...base2.device, ...opts.overrides.device },
299
- browser: { ...base2.browser, ...opts.overrides.browser },
300
- os: { ...base2.os, ...opts.overrides.os },
301
- locale: { ...base2.locale, ...opts.overrides.locale },
302
- utm: { ...base2.utm, ...opts.overrides.utm },
303
- clickIds: { ...base2.clickIds, ...opts.overrides.clickIds },
304
- page: { ...base2.page, ...opts.overrides.page },
305
- session: { ...base2.session, ...opts.overrides.session },
306
- identity: { ...base2.identity, ...opts.overrides.identity },
307
- system: { ...base2.system, ...opts.overrides.system }
309
+ device: { ...base.device, ...opts.overrides.device },
310
+ browser: { ...base.browser, ...opts.overrides.browser },
311
+ os: { ...base.os, ...opts.overrides.os },
312
+ locale: { ...base.locale, ...opts.overrides.locale },
313
+ utm: { ...base.utm, ...opts.overrides.utm },
314
+ clickIds: { ...base.clickIds, ...opts.overrides.clickIds },
315
+ page: { ...base.page, ...opts.overrides.page },
316
+ session: { ...base.session, ...opts.overrides.session },
317
+ identity: { ...base.identity, ...opts.overrides.identity },
318
+ system: { ...base.system, ...opts.overrides.system }
308
319
  };
309
320
  }
321
+ function exposurePayload(experimentId, variant, version) {
322
+ return { experimentId, variant, ...version !== void 0 ? { version } : {} };
323
+ }
310
324
  var _eventIdCounter = 0;
311
325
  function newEventId() {
312
326
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
@@ -323,20 +337,20 @@ function createConsoleTracker(opts = {}) {
323
337
  setAcquisition: (acq) => log("acquisition", acq)
324
338
  };
325
339
  }
326
- var TrackerContext = react.createContext(null);
340
+ var TrackerContext = React.createContext(null);
327
341
  function TrackerProvider({
328
342
  tracker,
329
343
  children
330
344
  }) {
331
- return react.createElement(TrackerContext.Provider, { value: tracker }, children);
345
+ return React.createElement(TrackerContext.Provider, { value: tracker }, children);
332
346
  }
333
347
  function useTrackerRef() {
334
- return react.useContext(TrackerContext) ?? FALLBACK;
348
+ return React.useContext(TrackerContext) ?? FALLBACK;
335
349
  }
336
350
  var FALLBACK = createConsoleTracker({ silent: true });
337
351
  function useTracker() {
338
352
  const tracker = useTrackerRef();
339
- const track = react.useCallback(
353
+ const track = React.useCallback(
340
354
  (event, data, userData) => tracker.track(event, data, userData),
341
355
  [tracker]
342
356
  );
@@ -345,7 +359,7 @@ function useTracker() {
345
359
 
346
360
  // src/tracking/bus.ts
347
361
  var MAX_HISTORY = 250;
348
- function createBus(accessors, now = () => Date.now()) {
362
+ function createBus(accessors, now2 = () => Date.now()) {
349
363
  const listeners = /* @__PURE__ */ new Map();
350
364
  const events = [];
351
365
  const on = (event, cb) => {
@@ -358,7 +372,7 @@ function createBus(accessors, now = () => Date.now()) {
358
372
  listeners.get(event)?.delete(cb);
359
373
  };
360
374
  const emit = (event, data) => {
361
- events.push({ event, data, ts: now() });
375
+ events.push({ event, data, ts: now2() });
362
376
  if (events.length > MAX_HISTORY) events.shift();
363
377
  for (const cb of listeners.get(event) ?? []) {
364
378
  try {
@@ -373,7 +387,40 @@ function createBus(accessors, now = () => Date.now()) {
373
387
  }
374
388
  }
375
389
  };
376
- return { on, off, events, emit, ...accessors };
390
+ let nav = null;
391
+ let warnedNoNav = false;
392
+ const currentNav = () => {
393
+ if (!nav && !warnedNoNav) {
394
+ warnedNoNav = true;
395
+ console.warn(
396
+ "[appfunnel] window.appfunnel navigation called before the funnel flow mounted \u2014 ignored"
397
+ );
398
+ }
399
+ return nav;
400
+ };
401
+ const registerNavigation = (n) => {
402
+ nav = n;
403
+ warnedNoNav = false;
404
+ return () => {
405
+ if (nav === n) nav = null;
406
+ };
407
+ };
408
+ const goTo = (pageKey) => currentNav()?.goTo(pageKey) ?? false;
409
+ const goNext = () => currentNav()?.goNext();
410
+ const goPrevious = () => currentNav()?.goPrevious();
411
+ const navigation = () => nav?.snapshot() ?? null;
412
+ return {
413
+ on,
414
+ off,
415
+ events,
416
+ emit,
417
+ registerNavigation,
418
+ goTo,
419
+ goNext,
420
+ goPrevious,
421
+ navigation,
422
+ ...accessors
423
+ };
377
424
  }
378
425
  function withBus(tracker, bus) {
379
426
  return {
@@ -395,7 +442,19 @@ function withBus(tracker, bus) {
395
442
  function attachBus(bus) {
396
443
  if (typeof window === "undefined") return () => {
397
444
  };
398
- const { on, off, getVariable, getVariables, getCurrentPageId, getCustomerId, getVisitorId } = bus;
445
+ const {
446
+ on,
447
+ off,
448
+ getVariable,
449
+ getVariables,
450
+ getCurrentPageId,
451
+ getCustomerId,
452
+ getVisitorId,
453
+ goTo,
454
+ goNext,
455
+ goPrevious,
456
+ navigation
457
+ } = bus;
399
458
  const api = {
400
459
  on,
401
460
  off,
@@ -404,6 +463,11 @@ function attachBus(bus) {
404
463
  getCurrentPageId,
405
464
  getCustomerId,
406
465
  getVisitorId,
466
+ // Flow control (bound closures over the bus's live nav registration).
467
+ goTo,
468
+ goNext,
469
+ goPrevious,
470
+ navigation,
407
471
  get events() {
408
472
  return bus.events;
409
473
  }
@@ -413,21 +477,6 @@ function attachBus(bus) {
413
477
  if (window.appfunnel === api) delete window.appfunnel;
414
478
  };
415
479
  }
416
- var RTL_LANGS = /* @__PURE__ */ new Set(["ar", "he", "fa", "ur", "ps", "sd", "dv", "yi"]);
417
- function isRtl(locale) {
418
- return RTL_LANGS.has(locale.split("-")[0]);
419
- }
420
- var base = (l) => l?.split("-")[0];
421
- function resolveLocale(config, detected, override) {
422
- const supported = config?.supported ?? (config?.default ? [config.default] : ["en"]);
423
- const def = config?.default ?? supported[0] ?? "en";
424
- const pick = (l) => {
425
- if (!l) return void 0;
426
- if (supported.includes(l)) return l;
427
- return supported.find((s) => base(s) === base(l));
428
- };
429
- return pick(override) ?? pick(detected) ?? def;
430
- }
431
480
  function interpolate(str, params) {
432
481
  if (!params) return str;
433
482
  return str.replace(/\{\{(\w+)\}\}/g, (_, k) => k in params ? String(params[k]) : `{{${k}}}`);
@@ -447,7 +496,7 @@ function devWarn(message) {
447
496
  } catch {
448
497
  }
449
498
  }
450
- var LocaleContext = react.createContext(null);
499
+ var LocaleContext = React.createContext(null);
451
500
  function LocaleProvider({
452
501
  config,
453
502
  catalog = {},
@@ -455,26 +504,29 @@ function LocaleProvider({
455
504
  override,
456
505
  children
457
506
  }) {
458
- const [locale, setLocale] = react.useState(() => resolveLocale(config, detected, override));
459
- const value = react.useMemo(
507
+ const [locale, setLocale] = React.useState(() => chunkRKPRMTSU_cjs.resolveLocale(config, detected, override));
508
+ React.useEffect(() => {
509
+ setLocale(chunkRKPRMTSU_cjs.resolveLocale(config, detected, override));
510
+ }, [config, detected, override]);
511
+ const value = React.useMemo(
460
512
  () => ({ locale, setLocale, config, catalog }),
461
513
  [locale, config, catalog]
462
514
  );
463
- return react.createElement(LocaleContext.Provider, { value }, children);
515
+ return React.createElement(LocaleContext.Provider, { value }, children);
464
516
  }
465
517
  function useActiveLocale() {
466
- return react.useContext(LocaleContext)?.locale ?? "en";
518
+ return React.useContext(LocaleContext)?.locale ?? "en";
467
519
  }
468
520
  function useTranslation() {
469
- const ctx = react.useContext(LocaleContext);
521
+ const ctx = React.useContext(LocaleContext);
470
522
  if (!ctx) throw new Error("useTranslation must be used inside <FunnelProvider>");
471
523
  const { locale, setLocale, config, catalog } = ctx;
472
- const chain = react.useMemo(() => {
524
+ const chain = React.useMemo(() => {
473
525
  const def = config?.default;
474
526
  const fb = config?.fallback;
475
527
  return Array.from(new Set([locale, fb, def].filter(Boolean)));
476
528
  }, [locale, config]);
477
- const t = react.useMemo(() => {
529
+ const t = React.useMemo(() => {
478
530
  const fn = ((key, params) => {
479
531
  const raw = lookup(catalog, chain, key);
480
532
  if (raw === void 0) {
@@ -484,17 +536,40 @@ function useTranslation() {
484
536
  return interpolate(raw, params);
485
537
  });
486
538
  fn.plural = (n, forms, params) => {
487
- const rule = new Intl.PluralRules(locale).select(n);
539
+ let rule;
540
+ try {
541
+ rule = new Intl.PluralRules(locale).select(n);
542
+ } catch {
543
+ rule = "other";
544
+ }
488
545
  const form = forms[rule] ?? forms.other ?? "";
489
546
  return interpolate(form.replace(/#/g, String(n)), params);
490
547
  };
491
548
  return fn;
492
549
  }, [catalog, chain, locale]);
493
- const fmt = react.useMemo(
550
+ const fmt = React.useMemo(
494
551
  () => ({
495
- number: (n, opts) => new Intl.NumberFormat(locale, opts).format(n),
496
- date: (d, opts) => new Intl.DateTimeFormat(locale, opts).format(d),
497
- percent: (n, opts) => new Intl.NumberFormat(locale, { style: "percent", ...opts }).format(n)
552
+ number: (n, opts) => {
553
+ try {
554
+ return new Intl.NumberFormat(locale, opts).format(n);
555
+ } catch {
556
+ return String(n);
557
+ }
558
+ },
559
+ date: (d, opts) => {
560
+ try {
561
+ return new Intl.DateTimeFormat(locale, opts).format(d);
562
+ } catch {
563
+ return String(d instanceof Date ? d.toISOString() : d);
564
+ }
565
+ },
566
+ percent: (n, opts) => {
567
+ try {
568
+ return new Intl.NumberFormat(locale, { style: "percent", ...opts }).format(n);
569
+ } catch {
570
+ return `${(n * 100).toFixed(0)}%`;
571
+ }
572
+ }
498
573
  }),
499
574
  [locale]
500
575
  );
@@ -503,351 +578,191 @@ function useTranslation() {
503
578
  fmt,
504
579
  locale,
505
580
  setLocale,
506
- dir: isRtl(locale) ? "rtl" : "ltr",
581
+ dir: chunkRKPRMTSU_cjs.isRtl(locale) ? "rtl" : "ltr",
507
582
  locales: config?.supported ?? [locale]
508
583
  };
509
584
  }
510
585
 
511
586
  // src/commerce/catalog.ts
512
- var PERIOD_DAYS = {
513
- day: 1,
514
- week: 7,
515
- month: 30,
516
- year: 365,
517
- one_time: 0
518
- };
519
- function periodDays(interval, count) {
520
- return PERIOD_DAYS[interval] * count;
521
- }
522
- function intervalLabel(interval, count) {
523
- if (interval === "one_time") return { period: "one-time", periodly: "one-time" };
524
- if (interval === "month" && count === 3) return { period: "quarter", periodly: "quarterly" };
525
- if (interval === "month" && count === 6) return { period: "6 months", periodly: "semiannually" };
526
- if (interval === "week" && count === 2) return { period: "2 weeks", periodly: "biweekly" };
527
- if (count === 1) {
528
- const periodly = { day: "daily", week: "weekly", month: "monthly", year: "yearly" }[interval];
529
- return { period: interval, periodly };
530
- }
531
- return { period: `${count} ${interval}s`, periodly: `every ${count} ${interval}s` };
532
- }
533
- function resolveProduct(input) {
534
- const interval = input.interval ?? "one_time";
535
- const count = input.intervalCount ?? 1;
536
- const days = periodDays(interval, count);
537
- const perDayMinor = days > 0 ? input.amount / days : input.amount;
538
- const label = intervalLabel(interval, count);
539
- return {
540
- id: input.id,
541
- name: input.name ?? input.id,
542
- displayName: input.displayName ?? input.name ?? input.id,
543
- provider: input.provider ?? "stripe",
544
- currency: input.currency,
545
- priceMinor: input.amount,
546
- perDayMinor,
547
- perWeekMinor: perDayMinor * 7,
548
- perMonthMinor: perDayMinor * 30,
549
- perYearMinor: perDayMinor * 365,
550
- period: label.period,
551
- periodly: label.periodly,
552
- hasTrial: (input.trialDays ?? 0) > 0,
553
- trialDays: input.trialDays ?? 0,
554
- trialMinor: input.trialAmount ?? 0
555
- };
556
- }
557
- function buildCatalog(inputs) {
558
- return new Map(inputs.map((i) => [i.id, resolveProduct(i)]));
559
- }
560
- function formatMoney(minor, currency, locale) {
561
- const amount = minor / 100;
562
- const code = currency.toUpperCase();
563
- let formatted;
564
- try {
565
- formatted = new Intl.NumberFormat(locale, { style: "currency", currency: code }).format(amount);
566
- } catch {
567
- formatted = `${code} ${amount.toFixed(2)}`;
568
- }
569
- return { amount, minor, currency: code, formatted };
570
- }
571
- function formatProduct(r, locale) {
572
- const m = (minor) => formatMoney(minor, r.currency, locale);
573
- return {
574
- id: r.id,
575
- name: r.name,
576
- displayName: r.displayName,
577
- provider: r.provider,
578
- price: m(r.priceMinor),
579
- period: r.period,
580
- periodly: r.periodly,
581
- perDay: m(r.perDayMinor),
582
- perWeek: m(r.perWeekMinor),
583
- perMonth: m(r.perMonthMinor),
584
- perYear: m(r.perYearMinor),
585
- hasTrial: r.hasTrial,
586
- trialDays: r.trialDays,
587
- trialPrice: r.hasTrial ? m(r.trialMinor) : null
588
- };
589
- }
590
- var CatalogContext = react.createContext(null);
587
+ var CatalogContext = React.createContext(null);
591
588
  function CatalogProvider({
592
589
  catalog,
593
590
  children
594
591
  }) {
595
- return react.createElement(CatalogContext.Provider, { value: catalog }, children);
592
+ return React.createElement(CatalogContext.Provider, { value: catalog }, children);
596
593
  }
597
594
  function useCatalog() {
598
- return react.useContext(CatalogContext) ?? EMPTY;
595
+ return React.useContext(CatalogContext) ?? EMPTY;
599
596
  }
600
597
  var EMPTY = /* @__PURE__ */ new Map();
601
- function useProduct(id) {
598
+ function useOffering(id) {
602
599
  const catalog = useCatalog();
603
600
  const locale = useActiveLocale();
604
601
  const resolved = id ? catalog.get(id) : void 0;
605
- return react.useMemo(() => resolved ? formatProduct(resolved, locale) : void 0, [resolved, locale]);
602
+ return React.useMemo(
603
+ () => resolved ? chunkRKPRMTSU_cjs.formatOffering(resolved, locale) : void 0,
604
+ [resolved, locale]
605
+ );
606
606
  }
607
- function useProducts() {
607
+ function useOfferings() {
608
608
  const catalog = useCatalog();
609
609
  const locale = useActiveLocale();
610
- return react.useMemo(
611
- () => Array.from(catalog.values()).map((r) => formatProduct(r, locale)),
610
+ return React.useMemo(
611
+ () => Array.from(catalog.values()).map((r) => chunkRKPRMTSU_cjs.formatOffering(r, locale)),
612
612
  [catalog, locale]
613
613
  );
614
614
  }
615
615
 
616
- // src/flow/spine.ts
617
- function readField(s, field) {
618
- let cur = s;
619
- for (const part of field.split(".")) {
620
- if (cur == null) return void 0;
621
- cur = cur[part];
616
+ // src/flow/screenshot.ts
617
+ var MIME = { png: "image/png", jpeg: "image/jpeg", webp: "image/webp" };
618
+ var ScreenshotError = class extends Error {
619
+ constructor(code, message) {
620
+ super(message);
621
+ this.code = code;
622
622
  }
623
- return cur;
624
- }
625
- function evaluateCondition(cond, s) {
626
- const val = readField(s, cond.field);
627
- const { op, value } = cond;
628
- switch (op) {
629
- case "eq":
630
- return val === value;
631
- case "neq":
632
- return val !== value;
633
- case "in":
634
- return Array.isArray(value) && value.includes(val);
635
- case "gt":
636
- return typeof val === "number" && typeof value === "number" && val > value;
637
- case "gte":
638
- return typeof val === "number" && typeof value === "number" && val >= value;
639
- case "lt":
640
- return typeof val === "number" && typeof value === "number" && val < value;
641
- case "lte":
642
- return typeof val === "number" && typeof value === "number" && val <= value;
643
- case "contains":
644
- if (typeof val === "string") return val.includes(String(value));
645
- if (Array.isArray(val)) return val.includes(value);
646
- return false;
647
- case "exists":
648
- return val !== void 0 && val !== null && val !== "";
649
- case "empty":
650
- return val === void 0 || val === null || val === "" || Array.isArray(val) && val.length === 0;
651
- default:
652
- return false;
653
- }
654
- }
655
- function evaluateGate(gate, s) {
656
- return typeof gate === "function" ? gate(s) : evaluateCondition(gate, s);
657
- }
658
- function resolveRoute(routes, s) {
659
- if (!routes) return void 0;
660
- for (const route of routes) {
661
- if (!route.when || evaluateGate(route.when, s)) return route.to;
662
- }
663
- return void 0;
664
- }
665
- function pageMeta(meta) {
666
- return meta;
667
- }
668
- var TYPE_GUARDS = {
669
- paywall: { field: "user.email", op: "exists" },
670
- upsell: { field: "user.email", op: "exists" }
671
623
  };
672
- function entryGuard(meta) {
673
- return meta?.guard ?? (meta?.type ? TYPE_GUARDS[meta.type] : void 0);
674
- }
675
- function definePage(component) {
676
- return component;
677
- }
678
- function nextPage(pages, currentKey, s) {
679
- const idx = pages.findIndex((p) => p.key === currentKey);
680
- if (idx === -1) return void 0;
681
- const routed = resolveRoute(pages[idx].meta?.next, s);
682
- if (routed) return routed;
683
- return pages[idx + 1]?.key;
684
- }
685
- function outgoingKeys(pages, currentKey) {
686
- const idx = pages.findIndex((p) => p.key === currentKey);
687
- if (idx === -1) return [];
688
- const out = /* @__PURE__ */ new Set();
689
- for (const route of pages[idx].meta?.next ?? []) out.add(route.to);
690
- const linear = pages[idx + 1]?.key;
691
- if (linear) out.add(linear);
692
- return [...out];
693
- }
694
- function expectedPathLength(pages, startKey, s) {
695
- const seen = /* @__PURE__ */ new Set();
696
- let key = startKey;
697
- let count = 0;
698
- while (key && !seen.has(key)) {
699
- seen.add(key);
700
- count++;
701
- if (pages.find((p) => p.key === key)?.meta?.type === "finish") break;
702
- key = nextPage(pages, key, s);
703
- }
704
- return count;
705
- }
706
- function defineFunnel(def) {
707
- return def;
708
- }
709
-
710
- // src/flow/experiments.ts
711
- function fnv1a(input) {
712
- let h = 2166136261;
713
- for (let i = 0; i < input.length; i++) {
714
- h ^= input.charCodeAt(i);
715
- h = h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24)) >>> 0;
624
+ function resolveTarget(t) {
625
+ if (!t || t.kind === "page") return document.getElementById("root") ?? document.body;
626
+ if (t.kind === "viewport") return document.body;
627
+ return document.querySelector(t.selector);
628
+ }
629
+ var delay = (ms) => new Promise((r) => setTimeout(r, ms));
630
+ function hideAll(selectors) {
631
+ const touched = [];
632
+ for (const sel of selectors) {
633
+ document.querySelectorAll(sel).forEach((el) => {
634
+ touched.push([el, el.style.visibility]);
635
+ el.style.visibility = "hidden";
636
+ });
716
637
  }
717
- return h >>> 0;
718
- }
719
- function hashToUnit(seed) {
720
- return fnv1a(seed) / 4294967296;
721
- }
722
- function pickByWeight(weights, u) {
723
- const entries = Object.entries(weights).filter(([, w]) => w > 0);
724
- if (entries.length === 0) return Object.keys(weights)[0] ?? "";
725
- const total = entries.reduce((sum, [, w]) => sum + w, 0);
726
- const target = u * total;
727
- let acc = 0;
728
- for (const [key, w] of entries) {
729
- acc += w;
730
- if (target < acc) return key;
638
+ return () => touched.forEach(([el, v]) => el.style.visibility = v);
639
+ }
640
+ async function scrollThrough(el) {
641
+ const prev = el.scrollTop;
642
+ const step = el.clientHeight || window.innerHeight;
643
+ for (let y = 0; y < el.scrollHeight; y += step) {
644
+ el.scrollTop = y;
645
+ await delay(16);
731
646
  }
732
- return entries[entries.length - 1][0];
733
- }
734
- function assignVariant(experimentId, seed, weights) {
735
- return pickByWeight(weights, hashToUnit(`${experimentId}:${seed}`));
736
- }
737
- function bucketingSeed(context) {
738
- return context.identity.customerId ?? context.identity.visitorId ?? null;
739
- }
740
- function parseSlotKey(key) {
741
- const at = key.indexOf("@");
742
- return at === -1 ? { slot: key } : { slot: key.slice(0, at), variant: key.slice(at + 1) };
743
- }
744
- function isVariantKey(key) {
745
- return parseSlotKey(key).variant !== void 0;
746
- }
747
- function weightsOf(exp) {
748
- const out = {};
749
- for (const [label, v] of Object.entries(exp.variants)) out[label] = v.weight;
750
- return out;
647
+ el.scrollTop = prev;
648
+ await delay(16);
649
+ }
650
+ function blobToDataURL(blob) {
651
+ return new Promise((resolve, reject) => {
652
+ const fr = new FileReader();
653
+ fr.onload = () => resolve(String(fr.result));
654
+ fr.onerror = () => reject(fr.error);
655
+ fr.readAsDataURL(blob);
656
+ });
751
657
  }
752
- function resolveExperiments(pages, experiments, seed) {
753
- const assignments = {};
754
- const render = {};
755
- const experimentOf = {};
756
- for (const exp of experiments) {
757
- const status = exp.status ?? "running";
758
- let label;
759
- if (status === "stopped" && exp.winner) label = exp.winner;
760
- else if (status === "running" && seed) label = assignVariant(exp.id, seed, weightsOf(exp));
761
- else continue;
762
- const arm = exp.variants[label];
763
- if (!arm) continue;
764
- assignments[exp.id] = label;
765
- experimentOf[exp.slot] = exp.id;
766
- if (arm.page !== exp.slot) render[exp.slot] = arm.page;
767
- }
768
- const slotOf = {};
769
- const activeKeys = [];
770
- for (const p of pages) {
771
- const { slot, variant } = parseSlotKey(p.key);
772
- if (variant !== void 0) slotOf[p.key] = slot;
773
- else activeKeys.push(p.key);
774
- }
775
- return { assignments, activeKeys, render, slotOf, experimentOf };
776
- }
777
- function validateExperiments(experiments, pages) {
778
- const errors = [];
779
- const warnings = [];
780
- const pageKeys = new Set(pages.map((p) => p.key));
781
- const seenIds = /* @__PURE__ */ new Set();
782
- const slotOwner = /* @__PURE__ */ new Map();
783
- const renderedVariantPages = /* @__PURE__ */ new Set();
784
- for (const exp of experiments) {
785
- const err = (code, message) => errors.push({ experimentId: exp.id, code, message });
786
- const warn = (code, message) => warnings.push({ experimentId: exp.id, code, message });
787
- if (seenIds.has(exp.id)) err("duplicate_id", `Experiment id "${exp.id}" is used more than once.`);
788
- seenIds.add(exp.id);
789
- if (!pageKeys.has(exp.slot)) err("slot_missing", `Slot "${exp.slot}" is not a page in the funnel.`);
790
- if (isVariantKey(exp.slot)) err("slot_is_variant", `Slot "${exp.slot}" is a variant page; a slot must be a real flow page.`);
791
- if (slotOwner.has(exp.slot)) {
792
- err("slot_conflict", `Slot "${exp.slot}" is already targeted by experiment "${slotOwner.get(exp.slot)}".`);
793
- } else {
794
- slotOwner.set(exp.slot, exp.id);
795
- }
796
- const arms = Object.entries(exp.variants);
797
- if (arms.length < 2) warn("single_arm", `Experiment "${exp.id}" has fewer than two variants \u2014 nothing to test.`);
798
- let hasControl = false;
799
- let totalWeight = 0;
800
- for (const [label, arm] of arms) {
801
- if (!pageKeys.has(arm.page)) {
802
- err("variant_page_missing", `Variant "${label}" references page "${arm.page}", which doesn't exist.`);
803
- }
804
- if (arm.page === exp.slot) hasControl = true;
805
- else if (isVariantKey(arm.page)) {
806
- renderedVariantPages.add(arm.page);
807
- if (parseSlotKey(arm.page).slot !== exp.slot) {
808
- err("variant_slot_mismatch", `Variant "${label}" page "${arm.page}" belongs to a different slot than "${exp.slot}".`);
809
- }
810
- }
811
- if (arm.weight < 0) err("negative_weight", `Variant "${label}" has a negative weight (${arm.weight}).`);
812
- totalWeight += arm.weight;
658
+ async function captureScreenshot(params = {}) {
659
+ const el = resolveTarget(params.target);
660
+ if (!el) throw new ScreenshotError("bad_params", "screenshot target not found");
661
+ const restore = params.hideSelectors?.length ? hideAll(params.hideSelectors) : null;
662
+ const scale = Math.min(params.scale ?? 2, params.maxScale ?? 3);
663
+ const format = params.format ?? "png";
664
+ const timeout = params.timeout ?? 1e4;
665
+ try {
666
+ if (params.waitForFonts !== false && document.fonts?.ready) {
667
+ await Promise.race([document.fonts.ready, delay(timeout)]);
813
668
  }
814
- if (!hasControl) err("no_control", `No variant of "${exp.id}" renders the slot "${exp.slot}" itself (the control/baseline).`);
815
- if (totalWeight <= 0) err("no_traffic", `Experiment "${exp.id}" has no positive weight \u2014 no traffic would enter it.`);
816
- if (exp.status === "stopped" && exp.winner && !exp.variants[exp.winner]) {
817
- err("bad_winner", `Stopped experiment "${exp.id}" names winner "${exp.winner}", which isn't one of its variants.`);
669
+ if (params.triggerLazyLoad) await scrollThrough(el);
670
+ if (params.waitForImages !== false) {
671
+ await Promise.all(
672
+ Array.from(el.querySelectorAll("img")).map(
673
+ (img) => typeof img.decode === "function" ? img.decode().catch(() => {
674
+ }) : Promise.resolve()
675
+ )
676
+ );
818
677
  }
819
- }
820
- const running = new Set(experiments.filter((e) => (e.status ?? "running") === "running").flatMap(
821
- (e) => Object.values(e.variants).map((v) => v.page)
822
- ));
823
- for (const p of pages) {
824
- if (isVariantKey(p.key) && !running.has(p.key)) {
825
- warnings.push({ experimentId: "*", code: "orphan_variant", message: `Variant page "${p.key}" is rendered by no running experiment.` });
678
+ const { domToBlob } = await import('modern-screenshot');
679
+ let blob;
680
+ try {
681
+ blob = await domToBlob(el, {
682
+ type: MIME[format],
683
+ quality: params.quality ?? 0.92,
684
+ scale,
685
+ width: params.width,
686
+ height: params.height,
687
+ // `null` = transparent (png/webp); only fall back to white when the caller omitted it. `??`
688
+ // would wrongly collapse an explicit `null` to white.
689
+ backgroundColor: params.backgroundColor === void 0 ? "#ffffff" : params.backgroundColor,
690
+ timeout
691
+ });
692
+ } catch (e) {
693
+ throw new ScreenshotError("failed", `capture failed (an image/font may be served without CORS '*'): ${String(e?.message || e)}`);
826
694
  }
695
+ const result = {
696
+ mime: MIME[format],
697
+ width: Math.round((params.width ?? el.clientWidth) * scale),
698
+ height: Math.round((params.height ?? el.scrollHeight) * scale),
699
+ scale,
700
+ bytesApprox: blob.size
701
+ };
702
+ if ((params.encoding ?? "blob") === "dataURL") result.dataURL = await blobToDataURL(blob);
703
+ else result.blob = blob;
704
+ return result;
705
+ } finally {
706
+ restore?.();
827
707
  }
828
- return { ok: errors.length === 0, errors, warnings };
829
708
  }
830
709
 
831
710
  // src/flow/flow.tsx
832
- var NavContext = react.createContext(null);
833
- var ExperimentContext = react.createContext({});
711
+ var NavContext = React.createContext(null);
712
+ var GroupProgressContext = React.createContext(null);
713
+ var ExperimentContext = React.createContext({});
834
714
  function useExperiment(id) {
835
- return react.useContext(ExperimentContext)[id];
715
+ return React.useContext(ExperimentContext)[id];
836
716
  }
837
- function pageContextFor(currentKey, slug, index, total, startedAt) {
717
+ function pageContextFor(currentKey, slug, group, index, total, startedAt) {
838
718
  const denom = Math.max(total, index + 1);
839
719
  return {
840
720
  key: currentKey,
841
721
  slug,
722
+ group,
842
723
  index,
843
724
  total: denom,
844
725
  progressPercentage: denom > 0 ? Math.min(100, Math.round((index + 1) / denom * 100)) : 0,
845
726
  startedAt
846
727
  };
847
728
  }
729
+ var now = () => typeof performance !== "undefined" ? performance.now() : Date.now();
730
+ function usePageExitTimer(tracker, currentKey) {
731
+ const timer = React.useRef(null);
732
+ const emitExit = React.useCallback(() => {
733
+ const t = timer.current;
734
+ if (!t) return;
735
+ const at = now();
736
+ const hidden = t.hidden + (t.hiddenSince != null ? at - t.hiddenSince : 0);
737
+ const durationMs = Math.round(at - t.enteredAt);
738
+ tracker.track("page.exit", { pageId: t.key, durationMs, activeMs: Math.max(0, Math.round(durationMs - hidden)) });
739
+ }, [tracker]);
740
+ React.useEffect(() => {
741
+ if (typeof document === "undefined") return;
742
+ const onVis = () => {
743
+ const t = timer.current;
744
+ if (!t) return;
745
+ if (document.visibilityState === "hidden") t.hiddenSince = now();
746
+ else if (t.hiddenSince != null) {
747
+ t.hidden += now() - t.hiddenSince;
748
+ t.hiddenSince = null;
749
+ }
750
+ };
751
+ document.addEventListener("visibilitychange", onVis);
752
+ return () => document.removeEventListener("visibilitychange", onVis);
753
+ }, []);
754
+ React.useEffect(() => {
755
+ if (!currentKey) return;
756
+ if (timer.current && timer.current.key !== currentKey) emitExit();
757
+ const hidden = typeof document !== "undefined" && document.visibilityState === "hidden";
758
+ timer.current = { key: currentKey, enteredAt: now(), hidden: 0, hiddenSince: hidden ? now() : null };
759
+ }, [currentKey, emitExit]);
760
+ React.useEffect(() => () => emitExit(), [emitExit]);
761
+ }
848
762
  function FunnelView({
849
763
  pages,
850
764
  initialKey,
765
+ trustInitialKey = false,
851
766
  layout,
852
767
  fallback,
853
768
  prefetch: prefetchMode = "auto",
@@ -855,141 +770,129 @@ function FunnelView({
855
770
  }) {
856
771
  const funnel = useFunnel();
857
772
  const tracker = useTrackerRef();
858
- const components = react.useMemo(() => {
773
+ const components = React.useMemo(() => {
859
774
  const map = /* @__PURE__ */ new Map();
860
775
  for (const p of pages) {
861
776
  if (p.Component) map.set(p.key, p.Component);
862
777
  else if (p.load) {
863
778
  const load = p.load;
864
- map.set(p.key, react.lazy(() => Promise.resolve(load()).then(
779
+ map.set(p.key, React.lazy(() => Promise.resolve(load()).then(
865
780
  (m) => typeof m === "function" ? { default: m } : m
866
781
  )));
867
782
  }
868
783
  }
869
784
  return map;
870
785
  }, [pages]);
871
- const loaders = react.useMemo(
786
+ const loaders = React.useMemo(
872
787
  () => new Map(pages.filter((p) => p.load).map((p) => [p.key, p.load])),
873
788
  [pages]
874
789
  );
875
- const seed = bucketingSeed(funnel.context);
876
- const experiments = react.useMemo(
877
- () => resolveExperiments(pages, funnel.experiments, seed),
790
+ const seedRef = React.useRef(null);
791
+ if (seedRef.current === null) seedRef.current = chunkRKPRMTSU_cjs.bucketingSeed(funnel.context);
792
+ const seed = seedRef.current;
793
+ const experiments = React.useMemo(
794
+ () => chunkRKPRMTSU_cjs.resolveExperiments(pages, funnel.experiments, seed),
878
795
  [pages, funnel.experiments, seed]
879
796
  );
880
- const pageByKey = react.useMemo(() => new Map(pages.map((p) => [p.key, p])), [pages]);
881
- const flowPages = react.useMemo(
882
- () => experiments.activeKeys.map((k) => pageByKey.get(k)).filter(Boolean),
797
+ const toSlot = React.useCallback((k) => experiments.slotOf[k] ?? k, [experiments]);
798
+ const toRenderKey = React.useCallback((k) => experiments.render[k] ?? k, [experiments]);
799
+ const pageByKey = React.useMemo(() => new Map(pages.map((p) => [p.key, p])), [pages]);
800
+ const flowPages = React.useMemo(
801
+ () => experiments.activeKeys.map((k) => pageByKey.get(k)).filter((p) => p !== void 0),
883
802
  [experiments, pageByKey]
884
803
  );
885
- const flow = react.useMemo(
886
- () => flowPages.map((p) => ({ key: p.key, meta: p.meta })),
887
- [flowPages]
804
+ const flow = React.useMemo(
805
+ () => flowPages.map((p) => {
806
+ const servedVariant = experiments.render[p.key];
807
+ const variantNext = servedVariant ? pageByKey.get(servedVariant)?.meta?.next : void 0;
808
+ return variantNext ? { key: p.key, meta: { ...p.meta ?? {}, next: variantNext } } : { key: p.key, meta: p.meta };
809
+ }),
810
+ [flowPages, experiments, pageByKey]
888
811
  );
889
- const [history, setHistory] = react.useState(() => {
812
+ const [history, setHistory] = React.useState(() => {
890
813
  const start = flowPages[0]?.key;
891
- if (!initialKey || initialKey === start) return [start].filter(Boolean);
892
- const target = experiments.slotOf[initialKey] ?? initialKey;
814
+ if (!initialKey || initialKey === start) return start ? [start] : [];
815
+ const target = toSlot(initialKey);
893
816
  const page2 = flowPages.find((p) => p.key === target);
894
- const guard = entryGuard(page2?.meta);
895
- const ok = page2 && (!guard || evaluateGate(guard, funnel.snapshot()));
896
- return [ok ? target : start].filter(Boolean);
817
+ if (trustInitialKey) return page2 ? [target] : start ? [start] : [];
818
+ const guard = chunkRKPRMTSU_cjs.entryGuard(page2?.meta);
819
+ const ok = page2 && (!guard || chunkRKPRMTSU_cjs.evaluateGate(guard, funnel.snapshot()));
820
+ const first = ok ? target : start;
821
+ return first ? [first] : [];
897
822
  });
898
823
  const currentKey = history[history.length - 1];
899
824
  const index = history.length - 1;
900
825
  const startKey = history[0];
901
- const total = react.useMemo(
902
- () => expectedPathLength(flow, startKey, funnel.snapshot()),
826
+ const total = React.useMemo(
827
+ () => chunkRKPRMTSU_cjs.expectedPathLength(flow, startKey, funnel.snapshot()),
903
828
  // currentKey in deps: re-trace as each step's answers settle the branch.
904
829
  [flow, startKey, currentKey, funnel]
905
830
  );
906
831
  const slug = pageByKey.get(currentKey)?.meta?.slug ?? currentKey;
907
- const page = react.useMemo(
908
- () => pageContextFor(currentKey, slug, index, total, funnel.context.system.now),
909
- [currentKey, slug, index, total, funnel.context.system.now]
832
+ const group = pageByKey.get(currentKey)?.meta?.group;
833
+ const page = React.useMemo(
834
+ () => pageContextFor(currentKey, slug, group, index, total, funnel.context.system.now),
835
+ [currentKey, slug, group, index, total, funnel.context.system.now]
836
+ );
837
+ const groups = React.useMemo(
838
+ () => chunkRKPRMTSU_cjs.groupProgress(flow, startKey, currentKey, funnel.snapshot()),
839
+ // currentKey in deps: re-trace as each step's answers settle the branch.
840
+ [flow, startKey, currentKey, funnel]
910
841
  );
911
- funnel.context.page = page;
912
- const started = react.useRef(false);
913
- react.useEffect(() => {
842
+ React.useEffect(() => {
843
+ funnel.context.page = page;
844
+ }, [funnel, page]);
845
+ const started = React.useRef(false);
846
+ React.useEffect(() => {
914
847
  if (started.current) return;
915
848
  started.current = true;
916
849
  tracker.track("funnel.start", {});
917
850
  }, [tracker]);
918
- react.useEffect(() => {
851
+ React.useEffect(() => {
919
852
  if (!currentKey) return;
920
853
  tracker.track("page.view", { pageId: currentKey, pageKey: currentKey, isInitial: index === 0, eventId: newEventId() });
921
854
  }, [tracker, currentKey, index]);
922
- react.useEffect(() => {
855
+ React.useEffect(() => {
923
856
  if (currentKey && onNavigate) onNavigate(currentKey, slug);
924
857
  }, [onNavigate, currentKey, slug]);
925
- const exposed = react.useRef(/* @__PURE__ */ new Set());
926
- react.useEffect(() => {
858
+ const exposed = React.useRef(/* @__PURE__ */ new Set());
859
+ React.useEffect(() => {
927
860
  const expId = experiments.experimentOf[currentKey];
928
861
  if (!expId || exposed.current.has(expId)) return;
929
862
  exposed.current.add(expId);
930
- tracker.track("experiment.exposure", { experimentId: expId, variant: experiments.assignments[expId] });
863
+ tracker.track("experiment.exposure", exposurePayload(expId, experiments.assignments[expId], experiments.versionOf[expId]));
931
864
  }, [tracker, currentKey, experiments]);
932
- const timer = react.useRef(null);
933
- const now = () => typeof performance !== "undefined" ? performance.now() : Date.now();
934
- const emitExit = react.useCallback(() => {
935
- const t = timer.current;
936
- if (!t) return;
937
- const at = now();
938
- const hidden = t.hidden + (t.hiddenSince != null ? at - t.hiddenSince : 0);
939
- const durationMs = Math.round(at - t.enteredAt);
940
- tracker.track("page.exit", { pageId: t.key, durationMs, activeMs: Math.max(0, Math.round(durationMs - hidden)) });
941
- }, [tracker]);
942
- react.useEffect(() => {
943
- if (typeof document === "undefined") return;
944
- const onVis = () => {
945
- const t = timer.current;
946
- if (!t) return;
947
- if (document.visibilityState === "hidden") t.hiddenSince = now();
948
- else if (t.hiddenSince != null) {
949
- t.hidden += now() - t.hiddenSince;
950
- t.hiddenSince = null;
951
- }
952
- };
953
- document.addEventListener("visibilitychange", onVis);
954
- return () => document.removeEventListener("visibilitychange", onVis);
955
- }, []);
956
- react.useEffect(() => {
957
- if (!currentKey) return;
958
- if (timer.current && timer.current.key !== currentKey) emitExit();
959
- const hidden = typeof document !== "undefined" && document.visibilityState === "hidden";
960
- timer.current = { key: currentKey, enteredAt: now(), hidden: 0, hiddenSince: hidden ? now() : null };
961
- }, [currentKey, emitExit]);
962
- react.useEffect(() => () => emitExit(), [emitExit]);
963
- const [isNavigating, startNav] = react.useTransition();
964
- const next = react.useCallback(() => {
965
- const target = nextPage(flow, currentKey, funnel.snapshot());
966
- const resolved = target ? experiments.slotOf[target] ?? target : target;
865
+ usePageExitTimer(tracker, currentKey);
866
+ const [isNavigating, startNav] = React.useTransition();
867
+ const next = React.useCallback(() => {
868
+ const target = chunkRKPRMTSU_cjs.nextPage(flow, currentKey, funnel.snapshot());
869
+ const resolved = target ? toSlot(target) : target;
967
870
  if (resolved) startNav(() => setHistory((h) => [...h, resolved]));
968
- }, [flow, currentKey, funnel, experiments]);
969
- const back = react.useCallback(
871
+ }, [flow, currentKey, funnel, toSlot]);
872
+ const back = React.useCallback(
970
873
  () => startNav(() => setHistory((h) => h.length > 1 ? h.slice(0, -1) : h)),
971
874
  []
972
875
  );
973
- const go = react.useCallback(
974
- (key) => startNav(() => setHistory((h) => [...h, experiments.slotOf[key] ?? key])),
975
- [experiments]
876
+ const go = React.useCallback(
877
+ (key) => startNav(() => setHistory((h) => [...h, toSlot(key)])),
878
+ [toSlot]
976
879
  );
977
- const prefetched = react.useRef(/* @__PURE__ */ new Set());
978
- const prefetch = react.useCallback(
880
+ const prefetched = React.useRef(/* @__PURE__ */ new Set());
881
+ const prefetch = React.useCallback(
979
882
  (key) => {
980
- const target = experiments.render[key] ?? key;
883
+ const target = toRenderKey(key);
981
884
  const load = loaders.get(target);
982
885
  if (!load || prefetched.current.has(target)) return;
983
886
  prefetched.current.add(target);
984
887
  load().catch(() => prefetched.current.delete(target));
985
888
  },
986
- [loaders, experiments]
889
+ [loaders, toRenderKey]
987
890
  );
988
- const nextCandidates = react.useMemo(
989
- () => [...new Set(outgoingKeys(flow, currentKey).map((k) => experiments.render[k] ?? k))],
990
- [flow, currentKey, experiments]
891
+ const nextCandidates = React.useMemo(
892
+ () => [...new Set(chunkRKPRMTSU_cjs.outgoingKeys(flow, currentKey).map(toRenderKey))],
893
+ [flow, currentKey, toRenderKey]
991
894
  );
992
- react.useEffect(() => {
895
+ React.useEffect(() => {
993
896
  if (prefetchMode !== "auto") return;
994
897
  for (const key of nextCandidates) prefetch(key);
995
898
  }, [prefetchMode, nextCandidates, prefetch]);
@@ -1003,36 +906,166 @@ function FunnelView({
1003
906
  canGoBack: history.length > 1,
1004
907
  isNavigating
1005
908
  };
1006
- const renderKey = experiments.render[currentKey] ?? currentKey;
909
+ const bus = useBusInternal();
910
+ const navRef = React.useRef(value);
911
+ navRef.current = value;
912
+ React.useEffect(() => {
913
+ if (!bus) return;
914
+ const control = {
915
+ goTo: (key) => {
916
+ if (!components.has(toRenderKey(key))) return false;
917
+ navRef.current.go(key);
918
+ return true;
919
+ },
920
+ goNext: () => navRef.current.next(),
921
+ goPrevious: () => navRef.current.back(),
922
+ snapshot: () => {
923
+ const v = navRef.current;
924
+ return {
925
+ key: v.page.key,
926
+ index: v.page.index,
927
+ total: v.page.total,
928
+ progressPercentage: v.page.progressPercentage,
929
+ canGoBack: v.canGoBack,
930
+ nextCandidates: v.nextCandidates
931
+ };
932
+ }
933
+ };
934
+ return bus.registerNavigation(control);
935
+ }, [bus]);
936
+ React.useEffect(() => {
937
+ const v = navRef.current;
938
+ bus?.emit("navigation", {
939
+ key: v.page.key,
940
+ index: v.page.index,
941
+ total: v.page.total,
942
+ progressPercentage: v.page.progressPercentage,
943
+ canGoBack: v.canGoBack,
944
+ nextCandidates: v.nextCandidates
945
+ });
946
+ }, [bus, currentKey]);
947
+ const renderKey = toRenderKey(currentKey);
1007
948
  const Current = components.get(renderKey);
1008
- const pageEl = react.createElement(react.Suspense, { fallback: fallback ?? null }, Current ? react.createElement(Current) : null);
1009
- const content = layout ? react.createElement(layout, null, pageEl) : pageEl;
1010
- return react.createElement(
949
+ const pageEl = React.createElement(React.Suspense, { fallback: fallback ?? null }, Current ? React.createElement(Current) : null);
950
+ const content = layout ? React.createElement(layout, null, pageEl) : pageEl;
951
+ return React.createElement(
1011
952
  ExperimentContext.Provider,
1012
953
  { value: experiments.assignments },
1013
- react.createElement(NavContext.Provider, { value }, content)
954
+ React.createElement(
955
+ NavContext.Provider,
956
+ { value },
957
+ React.createElement(GroupProgressContext.Provider, { value: groups }, content)
958
+ )
1014
959
  );
1015
960
  }
1016
961
  function useNavigation() {
1017
- const ctx = react.useContext(NavContext);
962
+ const ctx = React.useContext(NavContext);
1018
963
  if (!ctx) throw new Error("useNavigation must be used inside <FunnelView>");
1019
964
  return ctx;
1020
965
  }
966
+ var EMBED_CAPABILITIES = {
967
+ methods: ["nav.snapshot", "nav.goTo", "nav.goNext", "nav.goPrevious", "screenshot"],
968
+ events: ["navigation"]
969
+ };
970
+ function EmbedBridge() {
971
+ const bus = useBusInternal();
972
+ React.useEffect(() => {
973
+ if (!bus || typeof window === "undefined" || window.parent === window) return;
974
+ const parent = window.parent;
975
+ let disposed = false;
976
+ const send = (msg) => {
977
+ try {
978
+ parent.postMessage({ af: chunkWYUDL4FI_cjs.AF_PROTOCOL, source: "af-preview", ...msg }, "*");
979
+ } catch {
980
+ }
981
+ };
982
+ const hello = () => send({ kind: "hello", role: "funnel", protocol: chunkWYUDL4FI_cjs.AF_PROTOCOL, capabilities: EMBED_CAPABILITIES });
983
+ const sendSnapshot = () => {
984
+ const s = bus.navigation();
985
+ if (s) send({ kind: "event", event: "navigation", data: s });
986
+ };
987
+ async function invoke(method, params) {
988
+ switch (method) {
989
+ case "nav.snapshot":
990
+ return bus.navigation();
991
+ case "nav.goTo": {
992
+ const key = params?.key;
993
+ if (typeof key !== "string" || !bus.goTo(key)) throw { code: "bad_params", message: `unknown page key: ${String(key)}` };
994
+ return void 0;
995
+ }
996
+ case "nav.goNext":
997
+ bus.goNext();
998
+ return void 0;
999
+ case "nav.goPrevious":
1000
+ bus.goPrevious();
1001
+ return void 0;
1002
+ case "screenshot":
1003
+ return await captureScreenshot(params ?? {});
1004
+ default:
1005
+ throw { code: "unknown_method", message: `unknown method: ${method}` };
1006
+ }
1007
+ }
1008
+ async function onMsg(e) {
1009
+ if (e.source !== parent) return;
1010
+ const d = e.data;
1011
+ if (!d || typeof d !== "object" || d.af !== chunkWYUDL4FI_cjs.AF_PROTOCOL || d.source !== "af-host") return;
1012
+ if (d.kind === "hello") {
1013
+ hello();
1014
+ sendSnapshot();
1015
+ return;
1016
+ }
1017
+ if (d.kind !== "request" || typeof d.id !== "string") return;
1018
+ try {
1019
+ const result = await invoke(d.method, d.params);
1020
+ if (!disposed) send({ kind: "response", id: d.id, ok: true, result });
1021
+ } catch (err) {
1022
+ const e2 = err;
1023
+ const error = e2 && typeof e2.code === "string" ? { code: e2.code, message: String(e2.message ?? e2.code) } : { code: "failed", message: String(e2?.message ?? err) };
1024
+ if (!disposed) send({ kind: "response", id: d.id, ok: false, error });
1025
+ }
1026
+ }
1027
+ window.addEventListener("message", onMsg);
1028
+ const offNav = bus.on("navigation", (data) => {
1029
+ if (!disposed) send({ kind: "event", event: "navigation", data });
1030
+ });
1031
+ hello();
1032
+ sendSnapshot();
1033
+ return () => {
1034
+ disposed = true;
1035
+ offNav();
1036
+ window.removeEventListener("message", onMsg);
1037
+ };
1038
+ }, [bus]);
1039
+ return null;
1040
+ }
1021
1041
  function usePage() {
1022
1042
  return useNavigation().page;
1023
1043
  }
1024
- var symModalId = /* @__PURE__ */ Symbol("AppFunnelModalId");
1044
+ function useGroupProgress() {
1045
+ const ctx = React.useContext(GroupProgressContext);
1046
+ if (!ctx) throw new Error("useGroupProgress must be used inside <FunnelView>");
1047
+ return ctx;
1048
+ }
1049
+ var symModalId = /* @__PURE__ */ Symbol("AppfunnelModalId");
1025
1050
  var initialState = {};
1026
- var ModalStateContext = react.createContext(initialState);
1027
- var ModalIdContext = react.createContext(null);
1051
+ var ModalStateContext = React.createContext(initialState);
1052
+ var ModalIdContext = React.createContext(null);
1053
+ var ModalDispatchContext = React.createContext(null);
1028
1054
  var REGISTRY = {};
1029
1055
  var ALREADY_MOUNTED = {};
1030
1056
  var modalCallbacks = {};
1031
1057
  var hideCallbacks = {};
1032
1058
  var uid = 0;
1033
1059
  var getUid = () => `_af_modal_${uid++}`;
1034
- var dispatch = () => {
1035
- throw new Error("No modal dispatch \u2014 render inside <FunnelProvider> (which mounts the modal runtime).");
1060
+ var runtimeStack = [];
1061
+ var dispatch = (action) => {
1062
+ const top = runtimeStack[runtimeStack.length - 1];
1063
+ if (!top) {
1064
+ throw new Error(
1065
+ "No modal dispatch \u2014 render inside <FunnelProvider> (which mounts the modal runtime)."
1066
+ );
1067
+ }
1068
+ top(action);
1036
1069
  };
1037
1070
  var modalReducer = (state = initialState, action) => {
1038
1071
  const { modalId, args, flags } = action.payload;
@@ -1075,10 +1108,10 @@ function registerModal(id, comp, props) {
1075
1108
  function unregisterModal(id) {
1076
1109
  delete REGISTRY[id];
1077
1110
  }
1078
- function showModal(modal, args) {
1111
+ function showModalVia(d, modal, args) {
1079
1112
  const id = idOf(modal);
1080
1113
  if (typeof modal !== "string" && !REGISTRY[id]) registerModal(id, modal);
1081
- dispatch({ type: "show", payload: { modalId: id, args } });
1114
+ d({ type: "show", payload: { modalId: id, args } });
1082
1115
  if (!modalCallbacks[id]) {
1083
1116
  let res;
1084
1117
  let rej;
@@ -1090,9 +1123,12 @@ function showModal(modal, args) {
1090
1123
  }
1091
1124
  return modalCallbacks[id].promise;
1092
1125
  }
1093
- function hideModal(modal) {
1126
+ function showModal(modal, args) {
1127
+ return showModalVia(dispatch, modal, args);
1128
+ }
1129
+ function hideModalVia(d, modal) {
1094
1130
  const id = idOf(modal);
1095
- dispatch({ type: "hide", payload: { modalId: id } });
1131
+ d({ type: "hide", payload: { modalId: id } });
1096
1132
  delete modalCallbacks[id];
1097
1133
  if (!hideCallbacks[id]) {
1098
1134
  let res;
@@ -1105,41 +1141,64 @@ function hideModal(modal) {
1105
1141
  }
1106
1142
  return hideCallbacks[id].promise;
1107
1143
  }
1108
- function removeModal(modal) {
1144
+ function hideModal(modal) {
1145
+ return hideModalVia(dispatch, modal);
1146
+ }
1147
+ function removeModalVia(d, modal) {
1109
1148
  const id = idOf(modal);
1110
- dispatch({ type: "remove", payload: { modalId: id } });
1149
+ d({ type: "remove", payload: { modalId: id } });
1111
1150
  delete modalCallbacks[id];
1112
1151
  delete hideCallbacks[id];
1113
1152
  }
1114
- function setFlags(id, flags) {
1115
- dispatch({ type: "set-flags", payload: { modalId: id, flags } });
1153
+ function removeModal(modal) {
1154
+ removeModalVia(dispatch, modal);
1155
+ }
1156
+ function setFlags(d, id, flags) {
1157
+ d({ type: "set-flags", payload: { modalId: id, flags } });
1116
1158
  }
1117
1159
  function useModal(modal, args) {
1118
- const modals = react.useContext(ModalStateContext);
1119
- const contextId = react.useContext(ModalIdContext);
1160
+ const modals = React.useContext(ModalStateContext);
1161
+ const contextId = React.useContext(ModalIdContext);
1162
+ const d = React.useContext(ModalDispatchContext) ?? dispatch;
1120
1163
  const isComponent = !!modal && typeof modal !== "string";
1121
1164
  const id = modal ? idOf(modal) : contextId;
1122
- if (!id) throw new Error("useModal: no modal id (call inside a defineModal component, or pass an id/component).");
1123
- react.useEffect(() => {
1124
- if (isComponent && !REGISTRY[id]) registerModal(id, modal, args);
1165
+ if (!id)
1166
+ throw new Error(
1167
+ "useModal: no modal id (call inside a defineModal component, or pass an id/component)."
1168
+ );
1169
+ React.useEffect(() => {
1170
+ if (isComponent && !REGISTRY[id])
1171
+ registerModal(id, modal, args);
1125
1172
  }, [isComponent, id, modal, args]);
1126
1173
  const info = modals[id];
1127
- const show = react.useCallback((a) => showModal(id, a), [id]);
1128
- const hide = react.useCallback(() => hideModal(id), [id]);
1129
- const remove = react.useCallback(() => removeModal(id), [id]);
1130
- const resolve = react.useCallback((v) => {
1131
- modalCallbacks[id]?.resolve(v);
1132
- delete modalCallbacks[id];
1133
- }, [id]);
1134
- const reject = react.useCallback((v) => {
1135
- modalCallbacks[id]?.reject(v);
1136
- delete modalCallbacks[id];
1137
- }, [id]);
1138
- const resolveHide = react.useCallback((v) => {
1139
- hideCallbacks[id]?.resolve(v);
1140
- delete hideCallbacks[id];
1141
- }, [id]);
1142
- return react.useMemo(
1174
+ const show = React.useCallback(
1175
+ (a) => showModalVia(d, id, a),
1176
+ [d, id]
1177
+ );
1178
+ const hide = React.useCallback(() => hideModalVia(d, id), [d, id]);
1179
+ const remove = React.useCallback(() => removeModalVia(d, id), [d, id]);
1180
+ const resolve = React.useCallback(
1181
+ (v) => {
1182
+ modalCallbacks[id]?.resolve(v);
1183
+ delete modalCallbacks[id];
1184
+ },
1185
+ [id]
1186
+ );
1187
+ const reject = React.useCallback(
1188
+ (v) => {
1189
+ modalCallbacks[id]?.reject(v);
1190
+ delete modalCallbacks[id];
1191
+ },
1192
+ [id]
1193
+ );
1194
+ const resolveHide = React.useCallback(
1195
+ (v) => {
1196
+ hideCallbacks[id]?.resolve(v);
1197
+ delete hideCallbacks[id];
1198
+ },
1199
+ [id]
1200
+ );
1201
+ return React.useMemo(
1143
1202
  () => ({
1144
1203
  id,
1145
1204
  args: info?.args,
@@ -1152,53 +1211,90 @@ function useModal(modal, args) {
1152
1211
  reject,
1153
1212
  resolveHide
1154
1213
  }),
1155
- [id, info?.args, info?.visible, info?.keepMounted, show, hide, remove, resolve, reject, resolveHide]
1214
+ [
1215
+ id,
1216
+ info?.args,
1217
+ info?.visible,
1218
+ info?.keepMounted,
1219
+ show,
1220
+ hide,
1221
+ remove,
1222
+ resolve,
1223
+ reject,
1224
+ resolveHide
1225
+ ]
1156
1226
  );
1157
1227
  }
1158
1228
  function defineModal(Comp) {
1159
- return function Wrapped({ id, defaultVisible, keepMounted, ...props }) {
1229
+ return function Wrapped({
1230
+ id,
1231
+ defaultVisible,
1232
+ keepMounted,
1233
+ ...props
1234
+ }) {
1160
1235
  const { args, show } = useModal(id);
1161
- const modals = react.useContext(ModalStateContext);
1236
+ const modals = React.useContext(ModalStateContext);
1237
+ const d = React.useContext(ModalDispatchContext) ?? dispatch;
1162
1238
  const shouldMount = !!modals[id];
1163
- react.useEffect(() => {
1239
+ React.useEffect(() => {
1164
1240
  if (defaultVisible) show();
1165
1241
  ALREADY_MOUNTED[id] = true;
1166
1242
  return () => {
1167
1243
  delete ALREADY_MOUNTED[id];
1168
1244
  };
1169
1245
  }, [id, show, defaultVisible]);
1170
- react.useEffect(() => {
1171
- if (keepMounted) setFlags(id, { keepMounted: true });
1172
- }, [id, keepMounted]);
1246
+ React.useEffect(() => {
1247
+ if (keepMounted) setFlags(d, id, { keepMounted: true });
1248
+ }, [d, id, keepMounted]);
1173
1249
  const delayVisible = modals[id]?.delayVisible;
1174
- react.useEffect(() => {
1250
+ React.useEffect(() => {
1175
1251
  if (delayVisible) show(args);
1176
1252
  }, [delayVisible, args, show]);
1177
1253
  if (!shouldMount) return null;
1178
- return react.createElement(
1254
+ return React.createElement(
1179
1255
  ModalIdContext.Provider,
1180
1256
  { value: id },
1181
- react.createElement(Comp, { ...props, ...args })
1257
+ React.createElement(Comp, { ...props, ...args })
1182
1258
  );
1183
1259
  };
1184
1260
  }
1185
1261
  function ModalRoot() {
1186
- const modals = react.useContext(ModalStateContext);
1262
+ const modals = React.useContext(ModalStateContext);
1187
1263
  const toRender = Object.keys(modals).filter((id) => REGISTRY[id]).map((id) => ({ id, ...REGISTRY[id] }));
1188
- return react.createElement(
1264
+ return React.createElement(
1189
1265
  "div",
1190
1266
  { "data-appfunnel-modal-root": "" },
1191
- ...toRender.map((t) => react.createElement(t.comp, { key: t.id, id: t.id, ...t.props }))
1267
+ ...toRender.map(
1268
+ (t) => React.createElement(t.comp, { key: t.id, id: t.id, ...t.props })
1269
+ )
1192
1270
  );
1193
1271
  }
1194
1272
  function ModalRuntime({ children }) {
1195
- const [state, localDispatch] = react.useReducer(modalReducer, initialState);
1196
- dispatch = localDispatch;
1197
- return react.createElement(
1198
- ModalStateContext.Provider,
1199
- { value: state },
1200
- children,
1201
- react.createElement(ModalRoot, null)
1273
+ const [state, localDispatch] = React.useReducer(modalReducer, initialState);
1274
+ React.useEffect(() => {
1275
+ runtimeStack.push(localDispatch);
1276
+ if (runtimeStack.length > 1) {
1277
+ try {
1278
+ console.warn(
1279
+ `[appfunnel] ${runtimeStack.length} modal runtimes are mounted \u2014 the imperative showModal()/hideModal() API binds to the most recent one. Use the useModal() hook inside each tree instead.`
1280
+ );
1281
+ } catch {
1282
+ }
1283
+ }
1284
+ return () => {
1285
+ const i = runtimeStack.indexOf(localDispatch);
1286
+ if (i !== -1) runtimeStack.splice(i, 1);
1287
+ };
1288
+ }, []);
1289
+ return React.createElement(
1290
+ ModalDispatchContext.Provider,
1291
+ { value: localDispatch },
1292
+ React.createElement(
1293
+ ModalStateContext.Provider,
1294
+ { value: state },
1295
+ children,
1296
+ React.createElement(ModalRoot, null)
1297
+ )
1202
1298
  );
1203
1299
  }
1204
1300
 
@@ -1225,7 +1321,7 @@ function OverlayShell({
1225
1321
  panel,
1226
1322
  children
1227
1323
  }) {
1228
- react.useEffect(() => {
1324
+ React.useEffect(() => {
1229
1325
  if (!open || typeof document === "undefined") return;
1230
1326
  const onKey = (e) => {
1231
1327
  if (e.key === "Escape") onClose();
@@ -1239,7 +1335,7 @@ function OverlayShell({
1239
1335
  };
1240
1336
  }, [open, onClose]);
1241
1337
  if (!open || typeof document === "undefined") return null;
1242
- const overlay = react.createElement(
1338
+ const overlay = React.createElement(
1243
1339
  "div",
1244
1340
  {
1245
1341
  role: "dialog",
@@ -1248,19 +1344,19 @@ function OverlayShell({
1248
1344
  "data-appfunnel-overlay": "",
1249
1345
  style: { position: "fixed", inset: 0, zIndex: 1e3, display: "flex", ...container }
1250
1346
  },
1251
- react.createElement("div", {
1347
+ React.createElement("div", {
1252
1348
  "data-appfunnel-overlay-backdrop": "",
1253
1349
  className: backdropClassName,
1254
1350
  onClick: dismissOnBackdrop ? onClose : void 0,
1255
1351
  style: { position: "absolute", inset: 0, background: "rgba(0,0,0,0.5)" }
1256
1352
  }),
1257
- react.createElement(
1353
+ React.createElement(
1258
1354
  "div",
1259
1355
  { "data-appfunnel-overlay-panel": "", className, style: { position: "relative", background: "#fff", ...panel } },
1260
1356
  children
1261
1357
  )
1262
1358
  );
1263
- return reactDom.createPortal(overlay, document.body);
1359
+ return ReactDOM.createPortal(overlay, document.body);
1264
1360
  }
1265
1361
  var SHEET_LAYOUT = {
1266
1362
  bottom: { container: { justifyContent: "flex-end", alignItems: "stretch" }, panel: { borderTopLeftRadius: 16, borderTopRightRadius: 16, padding: 16, maxHeight: "90vh", overflow: "auto" } },
@@ -1271,7 +1367,7 @@ var SHEET_LAYOUT = {
1271
1367
  function Sheet(props) {
1272
1368
  const control = useModalControl();
1273
1369
  const layout = SHEET_LAYOUT[props.side ?? "bottom"];
1274
- return react.createElement(OverlayShell, {
1370
+ return React.createElement(OverlayShell, {
1275
1371
  ...control,
1276
1372
  className: props.className,
1277
1373
  backdropClassName: props.backdropClassName,
@@ -1284,7 +1380,7 @@ function Sheet(props) {
1284
1380
  }
1285
1381
  function Modal(props) {
1286
1382
  const control = useModalControl();
1287
- return react.createElement(OverlayShell, {
1383
+ return React.createElement(OverlayShell, {
1288
1384
  ...control,
1289
1385
  className: props.className,
1290
1386
  backdropClassName: props.backdropClassName,
@@ -1297,263 +1393,412 @@ function Modal(props) {
1297
1393
  }
1298
1394
 
1299
1395
  // src/commerce/checkout.tsx
1300
- var INLINE_SURFACES = /* @__PURE__ */ new Set([
1301
- "express",
1302
- "element",
1303
- "embedded"
1304
- ]);
1305
- function isInlineSurface(surface) {
1306
- return INLINE_SURFACES.has(surface);
1307
- }
1308
- var ON = { purchase: true, wallets: true, offSession: "reliable" };
1309
- var MANAGED = { purchase: true, wallets: true, offSession: "conditional" };
1310
- var ALL_UPSELLS = ["subscription", "one-time", "upgrade"];
1311
- var PROVIDER_PROFILES = {
1312
- // PSP; you hold the PaymentMethod and fire your own off-session PaymentIntent on
1313
- // the Elements surfaces (reliable). **Managed** Checkout (`embedded`/`redirect`)
1314
- // upsells too, but as a direct charge against Stripe's vaulted card → conditional.
1315
- // No provider "popup" surface.
1316
- stripe: {
1317
- isMoR: false,
1318
- offSessionUpsells: ALL_UPSELLS,
1319
- tokenModel: "merchant",
1320
- orchestrator: false,
1321
- surfaces: { express: ON, element: ON, sheet: ON, embedded: MANAGED, redirect: MANAGED }
1322
- },
1323
- // Merchant-of-Record; card entry is always Paddle's frame → no own element/sheet/
1324
- // express. Off-session via subscription one-time charge / collection_mode, but
1325
- // **no 2nd concurrent subscription** (upgrade + one-time only), provider-initiated.
1326
- paddle: {
1327
- isMoR: true,
1328
- offSessionUpsells: ["one-time", "upgrade"],
1329
- tokenModel: "provider",
1330
- orchestrator: false,
1331
- surfaces: { embedded: ON, popup: ON, redirect: ON }
1332
- },
1333
- // Merchant-of-Record; iframe-only (no raw card field). Off-session works
1334
- // (provider-initiated, async via webhooks). `sheet` = our chrome hosting their embed.
1335
- whop: {
1336
- isMoR: true,
1337
- offSessionUpsells: ALL_UPSELLS,
1338
- tokenModel: "provider",
1339
- orchestrator: false,
1340
- surfaces: { embedded: ON, redirect: ON, sheet: ON }
1341
- },
1342
- // Pure orchestrator (PSP-side); Headless renders express/element/sheet/embedded.
1343
- // No hosted redirect page, no popup checkout. Off-session is PSP-conditional.
1344
- primer: {
1345
- isMoR: false,
1346
- offSessionUpsells: ALL_UPSELLS,
1347
- tokenModel: "merchant",
1348
- orchestrator: true,
1349
- surfaces: { express: ON, element: ON, sheet: ON, embedded: ON }
1350
- },
1351
- // PSP **and** orchestrator (own acquiring + connectors routing across Stripe/Adyen/
1352
- // PayPal). One Payment Form (element/sheet/embedded) + hosted page + wallet button.
1353
- // Strong native off-session (Recurring API). No popup.
1354
- solidgate: {
1355
- isMoR: false,
1356
- offSessionUpsells: ALL_UPSELLS,
1357
- tokenModel: "merchant",
1358
- orchestrator: true,
1359
- surfaces: { express: ON, element: ON, sheet: ON, embedded: ON, redirect: ON }
1360
- }
1361
- };
1362
- function surfacesFor(provider) {
1363
- return Object.keys(PROVIDER_PROFILES[provider]?.surfaces ?? {});
1364
- }
1365
- function isMerchantOfRecord(provider) {
1366
- return PROVIDER_PROFILES[provider]?.isMoR ?? false;
1367
- }
1368
- function isOrchestrator(provider) {
1369
- return PROVIDER_PROFILES[provider]?.orchestrator ?? false;
1370
- }
1371
- function validateCheckout(provider, surface) {
1372
- const cap = PROVIDER_PROFILES[provider]?.surfaces[surface];
1373
- if (!cap) return { ok: false, reason: `${provider} has no '${surface}' surface` };
1374
- return cap.purchase ? { ok: true } : { ok: false, reason: `'${surface}' can't take a purchase on ${provider}` };
1375
- }
1376
- function validateUpsell(provider, paywallSurface, kind = "subscription") {
1377
- const profile = PROVIDER_PROFILES[provider];
1378
- if (!profile) return { ok: false, reason: `unknown provider '${provider}'` };
1379
- if (!profile.offSessionUpsells.includes(kind)) {
1380
- const can = profile.offSessionUpsells.length ? ` (it supports: ${profile.offSessionUpsells.join(", ")})` : "";
1381
- return { ok: false, reason: `${provider} can't charge a '${kind}' upsell off-session${can}` };
1382
- }
1383
- const cap = profile.surfaces[paywallSurface];
1384
- if (!cap) return { ok: false, reason: `${provider} has no '${paywallSurface}' surface to upsell after` };
1385
- if (cap.offSession === "none") {
1386
- return { ok: false, reason: `a purchase on '${paywallSurface}' retains no chargeable method for an upsell` };
1387
- }
1388
- if (cap.offSession === "conditional") {
1389
- return { ok: true, note: `off-session upsell after ${provider} '${paywallSurface}' (managed checkout) may decline \u2014 prefer element/sheet/express` };
1390
- }
1391
- return { ok: true };
1392
- }
1393
- function checkoutError(category, message, extra = {}) {
1394
- const retryable = extra.retryable ?? (category === "authentication_required" || category === "requires_payment_method" || category === "processing_error");
1395
- return { category, message, declineCode: extra.declineCode, retryable };
1396
- }
1397
1396
  function createMockDriver(provider = "stripe", options = {}) {
1398
- const success = options.result ?? { ok: true, amountMinor: 0, currency: "USD" };
1399
- const offError = typeof options.failOffSession === "object" ? options.failOffSession : checkoutError("authentication_required", "off-session charge declined (mock)");
1397
+ const success = options.result ?? {
1398
+ ok: true,
1399
+ amountMinor: 0,
1400
+ currency: "USD"
1401
+ };
1402
+ const offError = typeof options.failOffSession === "object" ? options.failOffSession : chunk7JLOF6CJ_cjs.checkoutError(
1403
+ "authentication_required",
1404
+ "off-session charge declined (mock)"
1405
+ );
1400
1406
  return {
1401
1407
  provider,
1402
1408
  start: (req) => {
1403
- if (options.failOffSession && !req.surface) return Promise.resolve({ ok: false, error: offError });
1409
+ if (options.failOffSession && !req.surface)
1410
+ return Promise.resolve({ ok: false, error: offError });
1404
1411
  return Promise.resolve(success);
1405
1412
  },
1406
- renderInline: (req, cb) => react.createElement(
1413
+ renderInline: (req, cb) => React.createElement(
1407
1414
  "button",
1408
1415
  {
1409
1416
  type: "button",
1410
1417
  "data-mock-surface": req.surface,
1411
- onClick: () => success.ok ? cb.onSuccess(success) : cb.onError(success.error ?? checkoutError("processing_error", "failed (mock)"))
1418
+ onClick: () => {
1419
+ cb.onStart?.();
1420
+ if (success.ok) cb.onSuccess(success);
1421
+ else
1422
+ cb.onError(
1423
+ success.error ?? chunk7JLOF6CJ_cjs.checkoutError(
1424
+ "processing_error",
1425
+ "failed (mock)"
1426
+ )
1427
+ );
1428
+ }
1412
1429
  },
1413
1430
  `Pay (mock ${req.surface})`
1414
- )
1431
+ ),
1432
+ resume: () => Promise.resolve(success)
1433
+ };
1434
+ }
1435
+ function driverWithEmail(driver, getEmail) {
1436
+ const enrich = (req) => req.email !== void 0 ? req : { ...req, email: getEmail() };
1437
+ return {
1438
+ ...driver,
1439
+ start: (req) => driver.start(enrich(req)),
1440
+ renderInline: driver.renderInline ? (req, cb) => driver.renderInline(enrich(req), cb) : void 0
1441
+ };
1442
+ }
1443
+ function driverWithCatalog(driver, catalog) {
1444
+ const enrich = (req) => req.catalogKey !== void 0 ? req : { ...req, catalogKey: catalog.get(req.product)?.catalogKey };
1445
+ return {
1446
+ ...driver,
1447
+ start: (req) => driver.start(enrich(req)),
1448
+ renderInline: driver.renderInline ? (req, cb) => driver.renderInline(enrich(req), cb) : void 0
1415
1449
  };
1416
1450
  }
1417
- var DriverContext = react.createContext(null);
1451
+ var DriverContext = React.createContext(null);
1418
1452
  function CheckoutDriverProvider({
1419
1453
  driver,
1420
1454
  children
1421
1455
  }) {
1422
- return react.createElement(DriverContext.Provider, { value: driver }, children);
1456
+ return React.createElement(DriverContext.Provider, { value: driver }, children);
1423
1457
  }
1424
1458
  function useDriver() {
1425
- return react.useContext(DriverContext) ?? FALLBACK_DRIVER;
1459
+ return React.useContext(DriverContext) ?? FALLBACK_DRIVER;
1426
1460
  }
1427
1461
  var FALLBACK_DRIVER = createMockDriver();
1428
1462
  function useCheckout(opts = {}) {
1429
1463
  const driver = useDriver();
1430
1464
  const tracker = useTrackerRef();
1465
+ const catalog = useCatalog();
1431
1466
  const nav = useNavigation();
1432
- const [status, setStatus] = react.useState("idle");
1433
- const [error, setError] = react.useState(null);
1467
+ const [status, setStatus] = React.useState("idle");
1468
+ const [error, setError] = React.useState(null);
1434
1469
  const intent = opts.intent ?? "purchase";
1435
1470
  const kind = opts.kind;
1436
1471
  const advance = opts.advanceOnSuccess ?? true;
1437
- const succeed = react.useCallback(
1438
- (result) => {
1472
+ const productFacts = React.useCallback(
1473
+ (product) => {
1474
+ const resolved = product ? catalog.get(product) : void 0;
1475
+ return {
1476
+ productId: resolved?.catalogKey ?? product,
1477
+ amount: resolved ? resolved.priceMinor / 100 : void 0,
1478
+ currency: resolved?.currency,
1479
+ isSubscription: resolved ? resolved.period !== "one-time" : kind === "subscription" || kind === "upgrade"
1480
+ };
1481
+ },
1482
+ [catalog, kind]
1483
+ );
1484
+ const recovering = React.useRef(false);
1485
+ const failRef = React.useRef(
1486
+ () => {
1487
+ }
1488
+ );
1489
+ const attemptRef = React.useRef(async () => ({ ok: false }));
1490
+ const succeed = React.useCallback(
1491
+ (result, product) => {
1439
1492
  setStatus("success");
1493
+ const { productId, isSubscription } = productFacts(product);
1494
+ const amount = result.amountMinor != null ? result.amountMinor / 100 : void 0;
1440
1495
  tracker.track("purchase.complete", {
1441
- amount: result.amountMinor != null ? result.amountMinor / 100 : void 0,
1496
+ amount,
1442
1497
  currency: result.currency,
1498
+ productId,
1443
1499
  eventId: result.eventId
1444
1500
  // server-minted (browser↔CAPI dedup); undefined if none
1445
1501
  });
1502
+ if (isSubscription) {
1503
+ tracker.track("subscription.created", {
1504
+ amount,
1505
+ currency: result.currency,
1506
+ productId,
1507
+ eventId: result.eventId
1508
+ });
1509
+ }
1446
1510
  opts.onSuccess?.(result);
1447
1511
  if (advance) nav.next();
1448
1512
  },
1449
- [tracker, nav, advance, opts]
1513
+ [tracker, nav, advance, opts, productFacts]
1450
1514
  );
1451
- const fail = react.useCallback(
1452
- (err, product) => {
1453
- setStatus(err.category === "authentication_required" ? "requires_action" : "error");
1454
- setError(err);
1455
- tracker.track("checkout.failed", {
1456
- category: err.category,
1457
- declineCode: err.declineCode,
1458
- productId: product,
1459
- intent
1460
- });
1461
- opts.onError?.(err);
1462
- },
1463
- [tracker, intent, opts]
1464
- );
1465
- const begin = react.useCallback(
1515
+ const succeedRef = React.useRef(succeed);
1516
+ succeedRef.current = succeed;
1517
+ const begin = React.useCallback(
1466
1518
  (product, surface) => {
1467
1519
  setStatus("loading");
1468
1520
  setError(null);
1469
- tracker.track("checkout.start", { productId: product, surface });
1521
+ const { productId, amount, currency } = productFacts(product);
1522
+ tracker.track("checkout.start", {
1523
+ productId,
1524
+ amount,
1525
+ currency,
1526
+ surface,
1527
+ eventId: newEventId()
1528
+ });
1470
1529
  },
1471
- [tracker]
1530
+ [tracker, productFacts]
1531
+ );
1532
+ const paymentAdded = React.useCallback(
1533
+ (product) => {
1534
+ const { productId, amount, currency } = productFacts(product);
1535
+ tracker.track("checkout.payment_added", {
1536
+ productId,
1537
+ amount,
1538
+ currency,
1539
+ eventId: newEventId()
1540
+ });
1541
+ },
1542
+ [tracker, productFacts]
1472
1543
  );
1473
- const paymentAdded = react.useCallback(() => tracker.track("checkout.payment_added", {}), [tracker]);
1474
- const open = react.useCallback(
1544
+ const hostInSheet = (surface) => surface === "sheet" || chunk7JLOF6CJ_cjs.isInlineSurface(surface) && !!chunk7JLOF6CJ_cjs.PROVIDER_PROFILES[driver.provider]?.surfaces.sheet;
1545
+ const startRecovery = (product, surface) => {
1546
+ recovering.current = true;
1547
+ if (hostInSheet(surface)) {
1548
+ begin(product, "sheet");
1549
+ void showModal(CheckoutSheet, {
1550
+ product,
1551
+ surface: "sheet",
1552
+ intent,
1553
+ kind,
1554
+ onSuccess: (r) => succeedRef.current(r, product),
1555
+ onError: (e) => failRef.current(e, product),
1556
+ onPaymentAdded: () => paymentAdded(product)
1557
+ });
1558
+ } else {
1559
+ void attemptRef.current(product, surface);
1560
+ }
1561
+ };
1562
+ failRef.current = (err, product) => {
1563
+ setStatus(
1564
+ err.category === "authentication_required" ? "requires_action" : "error"
1565
+ );
1566
+ setError(err);
1567
+ tracker.track("checkout.failed", {
1568
+ category: err.category,
1569
+ declineCode: err.declineCode,
1570
+ productId: product,
1571
+ intent
1572
+ });
1573
+ opts.onError?.(err);
1574
+ if (err.category === "canceled" && !opts.onFailed?.canceled) return;
1575
+ if (product && !recovering.current && opts.recoverySurface && err.category !== "canceled") {
1576
+ startRecovery(product, opts.recoverySurface);
1577
+ return;
1578
+ }
1579
+ let route = opts.onFailed?.[err.category] ?? (err.category === "canceled" ? void 0 : opts.onFailed?.default);
1580
+ if (route && typeof route === "object" && "fallback" in route && recovering.current) {
1581
+ const dflt = opts.onFailed?.default;
1582
+ route = dflt && dflt !== route && !(typeof dflt === "object" && "fallback" in dflt) ? dflt : void 0;
1583
+ }
1584
+ if (!route) return;
1585
+ if (typeof route === "function") route(err);
1586
+ else if ("go" in route) nav.go(route.go);
1587
+ else if ("modal" in route) {
1588
+ const props = {
1589
+ error: err,
1590
+ product,
1591
+ go: (pageKey) => nav.go(pageKey),
1592
+ retry: (surface) => {
1593
+ if (!product) return;
1594
+ recovering.current = true;
1595
+ if (!surface) {
1596
+ if (intent === "upsell")
1597
+ void attemptRef.current(product);
1598
+ else
1599
+ startRecovery(
1600
+ product,
1601
+ chunk7JLOF6CJ_cjs.PROVIDER_PROFILES[driver.provider]?.surfaces.sheet ? "sheet" : "popup"
1602
+ );
1603
+ return;
1604
+ }
1605
+ startRecovery(product, surface);
1606
+ }
1607
+ };
1608
+ void showModal(
1609
+ route.modal,
1610
+ props
1611
+ );
1612
+ } else if ("fallback" in route) {
1613
+ if (product && !recovering.current)
1614
+ startRecovery(product, route.fallback);
1615
+ }
1616
+ };
1617
+ attemptRef.current = async (product, surface) => {
1618
+ begin(product, surface);
1619
+ try {
1620
+ const result = await driver.start({
1621
+ product,
1622
+ intent,
1623
+ surface,
1624
+ kind
1625
+ });
1626
+ if (result.ok) succeedRef.current(result, product);
1627
+ else
1628
+ failRef.current(
1629
+ result.error ?? chunk7JLOF6CJ_cjs.checkoutError("unknown", "Checkout failed"),
1630
+ product
1631
+ );
1632
+ return result;
1633
+ } catch (e) {
1634
+ const err = chunk7JLOF6CJ_cjs.checkoutError(
1635
+ "processing_error",
1636
+ e instanceof Error ? e.message : "Checkout failed"
1637
+ );
1638
+ failRef.current(err, product);
1639
+ return { ok: false, error: err };
1640
+ }
1641
+ };
1642
+ const open = React.useCallback(
1475
1643
  async (product, surface) => {
1476
- begin(product, surface);
1477
- try {
1478
- const result = await driver.start({ product, intent, surface, kind });
1479
- if (result.ok) succeed(result);
1480
- else fail(result.error ?? checkoutError("unknown", "Checkout failed"), product);
1481
- return result;
1482
- } catch (e) {
1483
- const err = checkoutError("processing_error", e instanceof Error ? e.message : "Checkout failed");
1484
- fail(err, product);
1485
- return { ok: false, error: err };
1486
- }
1644
+ recovering.current = false;
1645
+ return attemptRef.current(product, surface);
1487
1646
  },
1488
- [driver, intent, kind, begin, succeed, fail]
1647
+ []
1489
1648
  );
1490
- const callbacksFor = react.useCallback(
1649
+ const callbacksFor = React.useCallback(
1491
1650
  (product, surface) => ({
1492
- onStart: () => begin(product, surface),
1493
- onSuccess: (result) => succeed(result),
1494
- onError: (err) => fail(err, product),
1495
- onPaymentAdded: () => paymentAdded()
1651
+ onStart: () => {
1652
+ recovering.current = false;
1653
+ begin(product, surface);
1654
+ },
1655
+ onSuccess: (result) => succeedRef.current(result, product),
1656
+ onError: (err) => failRef.current(err, product),
1657
+ onPaymentAdded: () => paymentAdded(product)
1496
1658
  }),
1497
- [begin, succeed, fail, paymentAdded]
1659
+ [begin, paymentAdded]
1498
1660
  );
1499
- const reset = react.useCallback(() => {
1661
+ const reset = React.useCallback(() => {
1662
+ recovering.current = false;
1500
1663
  setStatus("idle");
1501
1664
  setError(null);
1502
1665
  }, []);
1503
- return { open, status, error, isLoading: status === "loading", reset, callbacksFor };
1666
+ return {
1667
+ open,
1668
+ status,
1669
+ error,
1670
+ isLoading: status === "loading",
1671
+ reset,
1672
+ callbacksFor
1673
+ };
1504
1674
  }
1505
- var CheckoutSheet = defineModal(({ product, surface, intent, onSuccess, onError }) => {
1675
+ var CheckoutSheet = defineModal(({ product, surface, intent, kind, onSuccess, onError, onPaymentAdded }) => {
1506
1676
  const driver = useDriver();
1507
1677
  const modal = useModal();
1508
- if (!driver.renderInline) return null;
1509
- return react.createElement(Sheet, {
1510
- children: driver.renderInline(
1511
- { product, intent, surface },
1678
+ const resolved = React.useRef(false);
1679
+ const onErrorRef = React.useRef(onError);
1680
+ onErrorRef.current = onError;
1681
+ const hostable = !!driver.renderInline;
1682
+ React.useEffect(() => {
1683
+ if (!hostable) {
1684
+ resolved.current = true;
1685
+ onErrorRef.current(
1686
+ chunk7JLOF6CJ_cjs.checkoutError(
1687
+ "processing_error",
1688
+ "This checkout driver has no inline surface to host in the sheet"
1689
+ )
1690
+ );
1691
+ void modal.hide();
1692
+ modal.remove();
1693
+ return;
1694
+ }
1695
+ return () => {
1696
+ if (!resolved.current) {
1697
+ onErrorRef.current(
1698
+ chunk7JLOF6CJ_cjs.checkoutError(
1699
+ "canceled",
1700
+ "Checkout was dismissed before completing"
1701
+ )
1702
+ );
1703
+ }
1704
+ };
1705
+ }, []);
1706
+ if (!driver.renderInline) return null;
1707
+ const close = () => {
1708
+ resolved.current = true;
1709
+ void modal.hide();
1710
+ modal.remove();
1711
+ };
1712
+ return React.createElement(Sheet, {
1713
+ children: driver.renderInline(
1714
+ { product, intent, surface, kind },
1512
1715
  {
1513
1716
  onSuccess: (r) => {
1514
- modal.hide();
1717
+ close();
1515
1718
  onSuccess(r);
1516
1719
  },
1517
1720
  onError: (e) => {
1518
- modal.hide();
1721
+ close();
1519
1722
  onError(e);
1520
- }
1723
+ },
1724
+ onPaymentAdded
1521
1725
  }
1522
1726
  )
1523
1727
  });
1524
1728
  });
1525
- function trigger(onClick, { asChild, children, className, disabled }) {
1526
- if (asChild && react.isValidElement(children)) {
1729
+ function trigger(onClick, {
1730
+ asChild,
1731
+ children,
1732
+ className,
1733
+ disabled
1734
+ }) {
1735
+ if (asChild && React.isValidElement(children)) {
1527
1736
  const child = children;
1528
- return react.cloneElement(child, { onClick });
1737
+ return React.cloneElement(child, { onClick });
1738
+ }
1739
+ return React.createElement(
1740
+ "button",
1741
+ { type: "button", className, disabled, onClick },
1742
+ children ?? "Continue"
1743
+ );
1744
+ }
1745
+ function detectDeviceClass() {
1746
+ if (typeof window === "undefined") return "desktop";
1747
+ const ua = navigator.userAgent?.toLowerCase() ?? "";
1748
+ if (/mobile|android(?!.*tablet)|iphone|ipod/.test(ua)) return "mobile";
1749
+ try {
1750
+ if (window.matchMedia?.("(pointer: coarse)").matches && window.innerWidth > 0 && window.innerWidth < 768) {
1751
+ return "mobile";
1752
+ }
1753
+ } catch {
1529
1754
  }
1530
- return react.createElement("button", { type: "button", className, disabled, onClick }, children ?? "Continue");
1755
+ return "desktop";
1531
1756
  }
1532
1757
  function Checkout({
1533
- product,
1534
- surface,
1758
+ offering,
1759
+ surface: surfacePreference,
1535
1760
  asChild,
1536
1761
  children,
1537
1762
  className,
1538
1763
  ...options
1539
1764
  }) {
1765
+ const product = offering;
1540
1766
  const driver = useDriver();
1767
+ const [surface] = React.useState(
1768
+ () => chunk7JLOF6CJ_cjs.resolveSurface(driver.provider, surfacePreference, detectDeviceClass())
1769
+ );
1541
1770
  const checkout = useCheckout(options);
1542
1771
  const intent = options.intent ?? "purchase";
1543
- if (isInlineSurface(surface)) {
1544
- return driver.renderInline ? react.createElement(
1772
+ if (chunk7JLOF6CJ_cjs.isInlineSurface(surface)) {
1773
+ return driver.renderInline ? React.createElement(
1545
1774
  "div",
1546
1775
  { key: product, className },
1547
- driver.renderInline({ product, intent, surface }, checkout.callbacksFor(product, surface))
1776
+ driver.renderInline(
1777
+ { product, intent, surface },
1778
+ checkout.callbacksFor(product, surface)
1779
+ )
1548
1780
  ) : null;
1549
1781
  }
1550
1782
  if (surface === "sheet") {
1551
1783
  const cb = checkout.callbacksFor(product, surface);
1552
1784
  const open = () => {
1553
1785
  cb.onStart();
1554
- showModal(CheckoutSheet, { product, surface, intent, onSuccess: cb.onSuccess, onError: cb.onError });
1786
+ showModal(CheckoutSheet, {
1787
+ product,
1788
+ surface,
1789
+ intent,
1790
+ kind: options.kind,
1791
+ onSuccess: cb.onSuccess,
1792
+ onError: cb.onError,
1793
+ onPaymentAdded: cb.onPaymentAdded
1794
+ });
1555
1795
  };
1556
- return trigger(open, { asChild, children, className, disabled: checkout.isLoading });
1796
+ return trigger(open, {
1797
+ asChild,
1798
+ children,
1799
+ className,
1800
+ disabled: checkout.isLoading
1801
+ });
1557
1802
  }
1558
1803
  return trigger(() => checkout.open(product, surface), {
1559
1804
  asChild,
@@ -1562,32 +1807,1150 @@ function Checkout({
1562
1807
  disabled: checkout.isLoading
1563
1808
  });
1564
1809
  }
1565
- function Upsell({ product, fallback, asChild, children, className, ...options }) {
1566
- const checkout = useCheckout({ ...options, intent: "upsell" });
1567
- const start = async () => {
1568
- const result = await checkout.open(product);
1569
- if (result.ok || !fallback) return;
1570
- const cb = checkout.callbacksFor(product, fallback);
1571
- if (fallback === "sheet") {
1572
- cb.onStart();
1573
- showModal(CheckoutSheet, { product, surface: "sheet", intent: "upsell", onSuccess: cb.onSuccess, onError: cb.onError });
1810
+ function Upsell({
1811
+ offering,
1812
+ fallback,
1813
+ asChild,
1814
+ children,
1815
+ className,
1816
+ ...options
1817
+ }) {
1818
+ const product = offering;
1819
+ const checkout = useCheckout({
1820
+ ...options,
1821
+ intent: "upsell",
1822
+ recoverySurface: fallback ?? options.recoverySurface
1823
+ });
1824
+ return trigger(
1825
+ () => void checkout.open(product),
1826
+ {
1827
+ asChild,
1828
+ children: children ?? "Add to my plan",
1829
+ className,
1830
+ disabled: checkout.isLoading
1831
+ }
1832
+ );
1833
+ }
1834
+ var consumedResumes = /* @__PURE__ */ new Set();
1835
+ function CheckoutResume(options = {}) {
1836
+ const driver = useDriver();
1837
+ const checkout = useCheckout(options);
1838
+ const callbacksRef = React.useRef(checkout.callbacksFor);
1839
+ callbacksRef.current = checkout.callbacksFor;
1840
+ React.useEffect(() => {
1841
+ if (typeof window === "undefined" || !driver.resume) return;
1842
+ const url = new URL(window.location.href);
1843
+ const correlationId = url.searchParams.get("af_checkout");
1844
+ if (!correlationId || consumedResumes.has(correlationId)) return;
1845
+ consumedResumes.add(correlationId);
1846
+ void driver.resume(correlationId).then((result) => {
1847
+ const cb = callbacksRef.current();
1848
+ if (result.ok) {
1849
+ url.searchParams.delete("af_checkout");
1850
+ window.history.replaceState(
1851
+ window.history.state,
1852
+ "",
1853
+ url.toString()
1854
+ );
1855
+ cb.onSuccess(result);
1856
+ } else {
1857
+ cb.onError(
1858
+ result.error ?? chunk7JLOF6CJ_cjs.checkoutError(
1859
+ "unknown",
1860
+ "The checkout could not be verified"
1861
+ )
1862
+ );
1863
+ }
1864
+ });
1865
+ }, []);
1866
+ return null;
1867
+ }
1868
+ function __insertCSS(code) {
1869
+ if (typeof document == "undefined") return;
1870
+ let head = document.head || document.getElementsByTagName("head")[0];
1871
+ let style = document.createElement("style");
1872
+ style.type = "text/css";
1873
+ head.appendChild(style);
1874
+ style.styleSheet ? style.styleSheet.cssText = code : style.appendChild(document.createTextNode(code));
1875
+ }
1876
+ var getAsset = (type) => {
1877
+ switch (type) {
1878
+ case "success":
1879
+ return SuccessIcon;
1880
+ case "info":
1881
+ return InfoIcon;
1882
+ case "warning":
1883
+ return WarningIcon;
1884
+ case "error":
1885
+ return ErrorIcon;
1886
+ default:
1887
+ return null;
1888
+ }
1889
+ };
1890
+ var bars = Array(12).fill(0);
1891
+ var Loader = ({ visible, className }) => {
1892
+ return /* @__PURE__ */ React__default.default.createElement("div", {
1893
+ className: [
1894
+ "sonner-loading-wrapper",
1895
+ className
1896
+ ].filter(Boolean).join(" "),
1897
+ "data-visible": visible
1898
+ }, /* @__PURE__ */ React__default.default.createElement("div", {
1899
+ className: "sonner-spinner"
1900
+ }, bars.map((_, i) => /* @__PURE__ */ React__default.default.createElement("div", {
1901
+ className: "sonner-loading-bar",
1902
+ key: `spinner-bar-${i}`
1903
+ }))));
1904
+ };
1905
+ var SuccessIcon = /* @__PURE__ */ React__default.default.createElement("svg", {
1906
+ xmlns: "http://www.w3.org/2000/svg",
1907
+ viewBox: "0 0 20 20",
1908
+ fill: "currentColor",
1909
+ height: "20",
1910
+ width: "20"
1911
+ }, /* @__PURE__ */ React__default.default.createElement("path", {
1912
+ fillRule: "evenodd",
1913
+ d: "M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z",
1914
+ clipRule: "evenodd"
1915
+ }));
1916
+ var WarningIcon = /* @__PURE__ */ React__default.default.createElement("svg", {
1917
+ xmlns: "http://www.w3.org/2000/svg",
1918
+ viewBox: "0 0 24 24",
1919
+ fill: "currentColor",
1920
+ height: "20",
1921
+ width: "20"
1922
+ }, /* @__PURE__ */ React__default.default.createElement("path", {
1923
+ fillRule: "evenodd",
1924
+ d: "M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003zM12 8.25a.75.75 0 01.75.75v3.75a.75.75 0 01-1.5 0V9a.75.75 0 01.75-.75zm0 8.25a.75.75 0 100-1.5.75.75 0 000 1.5z",
1925
+ clipRule: "evenodd"
1926
+ }));
1927
+ var InfoIcon = /* @__PURE__ */ React__default.default.createElement("svg", {
1928
+ xmlns: "http://www.w3.org/2000/svg",
1929
+ viewBox: "0 0 20 20",
1930
+ fill: "currentColor",
1931
+ height: "20",
1932
+ width: "20"
1933
+ }, /* @__PURE__ */ React__default.default.createElement("path", {
1934
+ fillRule: "evenodd",
1935
+ d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z",
1936
+ clipRule: "evenodd"
1937
+ }));
1938
+ var ErrorIcon = /* @__PURE__ */ React__default.default.createElement("svg", {
1939
+ xmlns: "http://www.w3.org/2000/svg",
1940
+ viewBox: "0 0 20 20",
1941
+ fill: "currentColor",
1942
+ height: "20",
1943
+ width: "20"
1944
+ }, /* @__PURE__ */ React__default.default.createElement("path", {
1945
+ fillRule: "evenodd",
1946
+ d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z",
1947
+ clipRule: "evenodd"
1948
+ }));
1949
+ var CloseIcon = /* @__PURE__ */ React__default.default.createElement("svg", {
1950
+ xmlns: "http://www.w3.org/2000/svg",
1951
+ width: "12",
1952
+ height: "12",
1953
+ viewBox: "0 0 24 24",
1954
+ fill: "none",
1955
+ stroke: "currentColor",
1956
+ strokeWidth: "1.5",
1957
+ strokeLinecap: "round",
1958
+ strokeLinejoin: "round"
1959
+ }, /* @__PURE__ */ React__default.default.createElement("line", {
1960
+ x1: "18",
1961
+ y1: "6",
1962
+ x2: "6",
1963
+ y2: "18"
1964
+ }), /* @__PURE__ */ React__default.default.createElement("line", {
1965
+ x1: "6",
1966
+ y1: "6",
1967
+ x2: "18",
1968
+ y2: "18"
1969
+ }));
1970
+ var useIsDocumentHidden = () => {
1971
+ const [isDocumentHidden, setIsDocumentHidden] = React__default.default.useState(document.hidden);
1972
+ React__default.default.useEffect(() => {
1973
+ const callback = () => {
1974
+ setIsDocumentHidden(document.hidden);
1975
+ };
1976
+ document.addEventListener("visibilitychange", callback);
1977
+ return () => window.removeEventListener("visibilitychange", callback);
1978
+ }, []);
1979
+ return isDocumentHidden;
1980
+ };
1981
+ var toastsCounter = 1;
1982
+ var Observer = class {
1983
+ constructor() {
1984
+ this.subscribe = (subscriber) => {
1985
+ this.subscribers.push(subscriber);
1986
+ return () => {
1987
+ const index = this.subscribers.indexOf(subscriber);
1988
+ this.subscribers.splice(index, 1);
1989
+ };
1990
+ };
1991
+ this.publish = (data) => {
1992
+ this.subscribers.forEach((subscriber) => subscriber(data));
1993
+ };
1994
+ this.addToast = (data) => {
1995
+ this.publish(data);
1996
+ this.toasts = [
1997
+ ...this.toasts,
1998
+ data
1999
+ ];
2000
+ };
2001
+ this.create = (data) => {
2002
+ var _data_id;
2003
+ const { message, ...rest } = data;
2004
+ const id = typeof (data == null ? void 0 : data.id) === "number" || ((_data_id = data.id) == null ? void 0 : _data_id.length) > 0 ? data.id : toastsCounter++;
2005
+ const alreadyExists = this.toasts.find((toast2) => {
2006
+ return toast2.id === id;
2007
+ });
2008
+ const dismissible = data.dismissible === void 0 ? true : data.dismissible;
2009
+ if (this.dismissedToasts.has(id)) {
2010
+ this.dismissedToasts.delete(id);
2011
+ }
2012
+ if (alreadyExists) {
2013
+ this.toasts = this.toasts.map((toast2) => {
2014
+ if (toast2.id === id) {
2015
+ this.publish({
2016
+ ...toast2,
2017
+ ...data,
2018
+ id,
2019
+ title: message
2020
+ });
2021
+ return {
2022
+ ...toast2,
2023
+ ...data,
2024
+ id,
2025
+ dismissible,
2026
+ title: message
2027
+ };
2028
+ }
2029
+ return toast2;
2030
+ });
2031
+ } else {
2032
+ this.addToast({
2033
+ title: message,
2034
+ ...rest,
2035
+ dismissible,
2036
+ id
2037
+ });
2038
+ }
2039
+ return id;
2040
+ };
2041
+ this.dismiss = (id) => {
2042
+ if (id) {
2043
+ this.dismissedToasts.add(id);
2044
+ requestAnimationFrame(() => this.subscribers.forEach((subscriber) => subscriber({
2045
+ id,
2046
+ dismiss: true
2047
+ })));
2048
+ } else {
2049
+ this.toasts.forEach((toast2) => {
2050
+ this.subscribers.forEach((subscriber) => subscriber({
2051
+ id: toast2.id,
2052
+ dismiss: true
2053
+ }));
2054
+ });
2055
+ }
2056
+ return id;
2057
+ };
2058
+ this.message = (message, data) => {
2059
+ return this.create({
2060
+ ...data,
2061
+ message
2062
+ });
2063
+ };
2064
+ this.error = (message, data) => {
2065
+ return this.create({
2066
+ ...data,
2067
+ message,
2068
+ type: "error"
2069
+ });
2070
+ };
2071
+ this.success = (message, data) => {
2072
+ return this.create({
2073
+ ...data,
2074
+ type: "success",
2075
+ message
2076
+ });
2077
+ };
2078
+ this.info = (message, data) => {
2079
+ return this.create({
2080
+ ...data,
2081
+ type: "info",
2082
+ message
2083
+ });
2084
+ };
2085
+ this.warning = (message, data) => {
2086
+ return this.create({
2087
+ ...data,
2088
+ type: "warning",
2089
+ message
2090
+ });
2091
+ };
2092
+ this.loading = (message, data) => {
2093
+ return this.create({
2094
+ ...data,
2095
+ type: "loading",
2096
+ message
2097
+ });
2098
+ };
2099
+ this.promise = (promise, data) => {
2100
+ if (!data) {
2101
+ return;
2102
+ }
2103
+ let id = void 0;
2104
+ if (data.loading !== void 0) {
2105
+ id = this.create({
2106
+ ...data,
2107
+ promise,
2108
+ type: "loading",
2109
+ message: data.loading,
2110
+ description: typeof data.description !== "function" ? data.description : void 0
2111
+ });
2112
+ }
2113
+ const p = Promise.resolve(promise instanceof Function ? promise() : promise);
2114
+ let shouldDismiss = id !== void 0;
2115
+ let result;
2116
+ const originalPromise = p.then(async (response) => {
2117
+ result = [
2118
+ "resolve",
2119
+ response
2120
+ ];
2121
+ const isReactElementResponse = React__default.default.isValidElement(response);
2122
+ if (isReactElementResponse) {
2123
+ shouldDismiss = false;
2124
+ this.create({
2125
+ id,
2126
+ type: "default",
2127
+ message: response
2128
+ });
2129
+ } else if (isHttpResponse(response) && !response.ok) {
2130
+ shouldDismiss = false;
2131
+ const promiseData = typeof data.error === "function" ? await data.error(`HTTP error! status: ${response.status}`) : data.error;
2132
+ const description = typeof data.description === "function" ? await data.description(`HTTP error! status: ${response.status}`) : data.description;
2133
+ const isExtendedResult = typeof promiseData === "object" && !React__default.default.isValidElement(promiseData);
2134
+ const toastSettings = isExtendedResult ? promiseData : {
2135
+ message: promiseData
2136
+ };
2137
+ this.create({
2138
+ id,
2139
+ type: "error",
2140
+ description,
2141
+ ...toastSettings
2142
+ });
2143
+ } else if (response instanceof Error) {
2144
+ shouldDismiss = false;
2145
+ const promiseData = typeof data.error === "function" ? await data.error(response) : data.error;
2146
+ const description = typeof data.description === "function" ? await data.description(response) : data.description;
2147
+ const isExtendedResult = typeof promiseData === "object" && !React__default.default.isValidElement(promiseData);
2148
+ const toastSettings = isExtendedResult ? promiseData : {
2149
+ message: promiseData
2150
+ };
2151
+ this.create({
2152
+ id,
2153
+ type: "error",
2154
+ description,
2155
+ ...toastSettings
2156
+ });
2157
+ } else if (data.success !== void 0) {
2158
+ shouldDismiss = false;
2159
+ const promiseData = typeof data.success === "function" ? await data.success(response) : data.success;
2160
+ const description = typeof data.description === "function" ? await data.description(response) : data.description;
2161
+ const isExtendedResult = typeof promiseData === "object" && !React__default.default.isValidElement(promiseData);
2162
+ const toastSettings = isExtendedResult ? promiseData : {
2163
+ message: promiseData
2164
+ };
2165
+ this.create({
2166
+ id,
2167
+ type: "success",
2168
+ description,
2169
+ ...toastSettings
2170
+ });
2171
+ }
2172
+ }).catch(async (error) => {
2173
+ result = [
2174
+ "reject",
2175
+ error
2176
+ ];
2177
+ if (data.error !== void 0) {
2178
+ shouldDismiss = false;
2179
+ const promiseData = typeof data.error === "function" ? await data.error(error) : data.error;
2180
+ const description = typeof data.description === "function" ? await data.description(error) : data.description;
2181
+ const isExtendedResult = typeof promiseData === "object" && !React__default.default.isValidElement(promiseData);
2182
+ const toastSettings = isExtendedResult ? promiseData : {
2183
+ message: promiseData
2184
+ };
2185
+ this.create({
2186
+ id,
2187
+ type: "error",
2188
+ description,
2189
+ ...toastSettings
2190
+ });
2191
+ }
2192
+ }).finally(() => {
2193
+ if (shouldDismiss) {
2194
+ this.dismiss(id);
2195
+ id = void 0;
2196
+ }
2197
+ data.finally == null ? void 0 : data.finally.call(data);
2198
+ });
2199
+ const unwrap = () => new Promise((resolve, reject) => originalPromise.then(() => result[0] === "reject" ? reject(result[1]) : resolve(result[1])).catch(reject));
2200
+ if (typeof id !== "string" && typeof id !== "number") {
2201
+ return {
2202
+ unwrap
2203
+ };
2204
+ } else {
2205
+ return Object.assign(id, {
2206
+ unwrap
2207
+ });
2208
+ }
2209
+ };
2210
+ this.custom = (jsx2, data) => {
2211
+ const id = (data == null ? void 0 : data.id) || toastsCounter++;
2212
+ this.create({
2213
+ jsx: jsx2(id),
2214
+ id,
2215
+ ...data
2216
+ });
2217
+ return id;
2218
+ };
2219
+ this.getActiveToasts = () => {
2220
+ return this.toasts.filter((toast2) => !this.dismissedToasts.has(toast2.id));
2221
+ };
2222
+ this.subscribers = [];
2223
+ this.toasts = [];
2224
+ this.dismissedToasts = /* @__PURE__ */ new Set();
2225
+ }
2226
+ };
2227
+ var ToastState = new Observer();
2228
+ var toastFunction = (message, data) => {
2229
+ const id = (data == null ? void 0 : data.id) || toastsCounter++;
2230
+ ToastState.addToast({
2231
+ title: message,
2232
+ ...data,
2233
+ id
2234
+ });
2235
+ return id;
2236
+ };
2237
+ var isHttpResponse = (data) => {
2238
+ return data && typeof data === "object" && "ok" in data && typeof data.ok === "boolean" && "status" in data && typeof data.status === "number";
2239
+ };
2240
+ var basicToast = toastFunction;
2241
+ var getHistory = () => ToastState.toasts;
2242
+ var getToasts = () => ToastState.getActiveToasts();
2243
+ var toast = Object.assign(basicToast, {
2244
+ success: ToastState.success,
2245
+ info: ToastState.info,
2246
+ warning: ToastState.warning,
2247
+ error: ToastState.error,
2248
+ custom: ToastState.custom,
2249
+ message: ToastState.message,
2250
+ promise: ToastState.promise,
2251
+ dismiss: ToastState.dismiss,
2252
+ loading: ToastState.loading
2253
+ }, {
2254
+ getHistory,
2255
+ getToasts
2256
+ });
2257
+ __insertCSS("[data-sonner-toaster][dir=ltr],html[dir=ltr]{--toast-icon-margin-start:-3px;--toast-icon-margin-end:4px;--toast-svg-margin-start:-1px;--toast-svg-margin-end:0px;--toast-button-margin-start:auto;--toast-button-margin-end:0;--toast-close-button-start:0;--toast-close-button-end:unset;--toast-close-button-transform:translate(-35%, -35%)}[data-sonner-toaster][dir=rtl],html[dir=rtl]{--toast-icon-margin-start:4px;--toast-icon-margin-end:-3px;--toast-svg-margin-start:0px;--toast-svg-margin-end:-1px;--toast-button-margin-start:0;--toast-button-margin-end:auto;--toast-close-button-start:unset;--toast-close-button-end:0;--toast-close-button-transform:translate(35%, -35%)}[data-sonner-toaster]{position:fixed;width:var(--width);font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;--gray1:hsl(0, 0%, 99%);--gray2:hsl(0, 0%, 97.3%);--gray3:hsl(0, 0%, 95.1%);--gray4:hsl(0, 0%, 93%);--gray5:hsl(0, 0%, 90.9%);--gray6:hsl(0, 0%, 88.7%);--gray7:hsl(0, 0%, 85.8%);--gray8:hsl(0, 0%, 78%);--gray9:hsl(0, 0%, 56.1%);--gray10:hsl(0, 0%, 52.3%);--gray11:hsl(0, 0%, 43.5%);--gray12:hsl(0, 0%, 9%);--border-radius:8px;box-sizing:border-box;padding:0;margin:0;list-style:none;outline:0;z-index:999999999;transition:transform .4s ease}@media (hover:none) and (pointer:coarse){[data-sonner-toaster][data-lifted=true]{transform:none}}[data-sonner-toaster][data-x-position=right]{right:var(--offset-right)}[data-sonner-toaster][data-x-position=left]{left:var(--offset-left)}[data-sonner-toaster][data-x-position=center]{left:50%;transform:translateX(-50%)}[data-sonner-toaster][data-y-position=top]{top:var(--offset-top)}[data-sonner-toaster][data-y-position=bottom]{bottom:var(--offset-bottom)}[data-sonner-toast]{--y:translateY(100%);--lift-amount:calc(var(--lift) * var(--gap));z-index:var(--z-index);position:absolute;opacity:0;transform:var(--y);touch-action:none;transition:transform .4s,opacity .4s,height .4s,box-shadow .2s;box-sizing:border-box;outline:0;overflow-wrap:anywhere}[data-sonner-toast][data-styled=true]{padding:16px;background:var(--normal-bg);border:1px solid var(--normal-border);color:var(--normal-text);border-radius:var(--border-radius);box-shadow:0 4px 12px rgba(0,0,0,.1);width:var(--width);font-size:13px;display:flex;align-items:center;gap:6px}[data-sonner-toast]:focus-visible{box-shadow:0 4px 12px rgba(0,0,0,.1),0 0 0 2px rgba(0,0,0,.2)}[data-sonner-toast][data-y-position=top]{top:0;--y:translateY(-100%);--lift:1;--lift-amount:calc(1 * var(--gap))}[data-sonner-toast][data-y-position=bottom]{bottom:0;--y:translateY(100%);--lift:-1;--lift-amount:calc(var(--lift) * var(--gap))}[data-sonner-toast][data-styled=true] [data-description]{font-weight:400;line-height:1.4;color:#3f3f3f}[data-rich-colors=true][data-sonner-toast][data-styled=true] [data-description]{color:inherit}[data-sonner-toaster][data-sonner-theme=dark] [data-description]{color:#e8e8e8}[data-sonner-toast][data-styled=true] [data-title]{font-weight:500;line-height:1.5;color:inherit}[data-sonner-toast][data-styled=true] [data-icon]{display:flex;height:16px;width:16px;position:relative;justify-content:flex-start;align-items:center;flex-shrink:0;margin-left:var(--toast-icon-margin-start);margin-right:var(--toast-icon-margin-end)}[data-sonner-toast][data-promise=true] [data-icon]>svg{opacity:0;transform:scale(.8);transform-origin:center;animation:sonner-fade-in .3s ease forwards}[data-sonner-toast][data-styled=true] [data-icon]>*{flex-shrink:0}[data-sonner-toast][data-styled=true] [data-icon] svg{margin-left:var(--toast-svg-margin-start);margin-right:var(--toast-svg-margin-end)}[data-sonner-toast][data-styled=true] [data-content]{display:flex;flex-direction:column;gap:2px}[data-sonner-toast][data-styled=true] [data-button]{border-radius:4px;padding-left:8px;padding-right:8px;height:24px;font-size:12px;color:var(--normal-bg);background:var(--normal-text);margin-left:var(--toast-button-margin-start);margin-right:var(--toast-button-margin-end);border:none;font-weight:500;cursor:pointer;outline:0;display:flex;align-items:center;flex-shrink:0;transition:opacity .4s,box-shadow .2s}[data-sonner-toast][data-styled=true] [data-button]:focus-visible{box-shadow:0 0 0 2px rgba(0,0,0,.4)}[data-sonner-toast][data-styled=true] [data-button]:first-of-type{margin-left:var(--toast-button-margin-start);margin-right:var(--toast-button-margin-end)}[data-sonner-toast][data-styled=true] [data-cancel]{color:var(--normal-text);background:rgba(0,0,0,.08)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast][data-styled=true] [data-cancel]{background:rgba(255,255,255,.3)}[data-sonner-toast][data-styled=true] [data-close-button]{position:absolute;left:var(--toast-close-button-start);right:var(--toast-close-button-end);top:0;height:20px;width:20px;display:flex;justify-content:center;align-items:center;padding:0;color:var(--gray12);background:var(--normal-bg);border:1px solid var(--gray4);transform:var(--toast-close-button-transform);border-radius:50%;cursor:pointer;z-index:1;transition:opacity .1s,background .2s,border-color .2s}[data-sonner-toast][data-styled=true] [data-close-button]:focus-visible{box-shadow:0 4px 12px rgba(0,0,0,.1),0 0 0 2px rgba(0,0,0,.2)}[data-sonner-toast][data-styled=true] [data-disabled=true]{cursor:not-allowed}[data-sonner-toast][data-styled=true]:hover [data-close-button]:hover{background:var(--gray2);border-color:var(--gray5)}[data-sonner-toast][data-swiping=true]::before{content:'';position:absolute;left:-100%;right:-100%;height:100%;z-index:-1}[data-sonner-toast][data-y-position=top][data-swiping=true]::before{bottom:50%;transform:scaleY(3) translateY(50%)}[data-sonner-toast][data-y-position=bottom][data-swiping=true]::before{top:50%;transform:scaleY(3) translateY(-50%)}[data-sonner-toast][data-swiping=false][data-removed=true]::before{content:'';position:absolute;inset:0;transform:scaleY(2)}[data-sonner-toast][data-expanded=true]::after{content:'';position:absolute;left:0;height:calc(var(--gap) + 1px);bottom:100%;width:100%}[data-sonner-toast][data-mounted=true]{--y:translateY(0);opacity:1}[data-sonner-toast][data-expanded=false][data-front=false]{--scale:var(--toasts-before) * 0.05 + 1;--y:translateY(calc(var(--lift-amount) * var(--toasts-before))) scale(calc(-1 * var(--scale)));height:var(--front-toast-height)}[data-sonner-toast]>*{transition:opacity .4s}[data-sonner-toast][data-x-position=right]{right:0}[data-sonner-toast][data-x-position=left]{left:0}[data-sonner-toast][data-expanded=false][data-front=false][data-styled=true]>*{opacity:0}[data-sonner-toast][data-visible=false]{opacity:0;pointer-events:none}[data-sonner-toast][data-mounted=true][data-expanded=true]{--y:translateY(calc(var(--lift) * var(--offset)));height:var(--initial-height)}[data-sonner-toast][data-removed=true][data-front=true][data-swipe-out=false]{--y:translateY(calc(var(--lift) * -100%));opacity:0}[data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=true]{--y:translateY(calc(var(--lift) * var(--offset) + var(--lift) * -100%));opacity:0}[data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=false]{--y:translateY(40%);opacity:0;transition:transform .5s,opacity .2s}[data-sonner-toast][data-removed=true][data-front=false]::before{height:calc(var(--initial-height) + 20%)}[data-sonner-toast][data-swiping=true]{transform:var(--y) translateY(var(--swipe-amount-y,0)) translateX(var(--swipe-amount-x,0));transition:none}[data-sonner-toast][data-swiped=true]{user-select:none}[data-sonner-toast][data-swipe-out=true][data-y-position=bottom],[data-sonner-toast][data-swipe-out=true][data-y-position=top]{animation-duration:.2s;animation-timing-function:ease-out;animation-fill-mode:forwards}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=left]{animation-name:swipe-out-left}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=right]{animation-name:swipe-out-right}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=up]{animation-name:swipe-out-up}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=down]{animation-name:swipe-out-down}@keyframes swipe-out-left{from{transform:var(--y) translateX(var(--swipe-amount-x));opacity:1}to{transform:var(--y) translateX(calc(var(--swipe-amount-x) - 100%));opacity:0}}@keyframes swipe-out-right{from{transform:var(--y) translateX(var(--swipe-amount-x));opacity:1}to{transform:var(--y) translateX(calc(var(--swipe-amount-x) + 100%));opacity:0}}@keyframes swipe-out-up{from{transform:var(--y) translateY(var(--swipe-amount-y));opacity:1}to{transform:var(--y) translateY(calc(var(--swipe-amount-y) - 100%));opacity:0}}@keyframes swipe-out-down{from{transform:var(--y) translateY(var(--swipe-amount-y));opacity:1}to{transform:var(--y) translateY(calc(var(--swipe-amount-y) + 100%));opacity:0}}@media (max-width:600px){[data-sonner-toaster]{position:fixed;right:var(--mobile-offset-right);left:var(--mobile-offset-left);width:100%}[data-sonner-toaster][dir=rtl]{left:calc(var(--mobile-offset-left) * -1)}[data-sonner-toaster] [data-sonner-toast]{left:0;right:0;width:calc(100% - var(--mobile-offset-left) * 2)}[data-sonner-toaster][data-x-position=left]{left:var(--mobile-offset-left)}[data-sonner-toaster][data-y-position=bottom]{bottom:var(--mobile-offset-bottom)}[data-sonner-toaster][data-y-position=top]{top:var(--mobile-offset-top)}[data-sonner-toaster][data-x-position=center]{left:var(--mobile-offset-left);right:var(--mobile-offset-right);transform:none}}[data-sonner-toaster][data-sonner-theme=light]{--normal-bg:#fff;--normal-border:var(--gray4);--normal-text:var(--gray12);--success-bg:hsl(143, 85%, 96%);--success-border:hsl(145, 92%, 87%);--success-text:hsl(140, 100%, 27%);--info-bg:hsl(208, 100%, 97%);--info-border:hsl(221, 91%, 93%);--info-text:hsl(210, 92%, 45%);--warning-bg:hsl(49, 100%, 97%);--warning-border:hsl(49, 91%, 84%);--warning-text:hsl(31, 92%, 45%);--error-bg:hsl(359, 100%, 97%);--error-border:hsl(359, 100%, 94%);--error-text:hsl(360, 100%, 45%)}[data-sonner-toaster][data-sonner-theme=light] [data-sonner-toast][data-invert=true]{--normal-bg:#000;--normal-border:hsl(0, 0%, 20%);--normal-text:var(--gray1)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast][data-invert=true]{--normal-bg:#fff;--normal-border:var(--gray3);--normal-text:var(--gray12)}[data-sonner-toaster][data-sonner-theme=dark]{--normal-bg:#000;--normal-bg-hover:hsl(0, 0%, 12%);--normal-border:hsl(0, 0%, 20%);--normal-border-hover:hsl(0, 0%, 25%);--normal-text:var(--gray1);--success-bg:hsl(150, 100%, 6%);--success-border:hsl(147, 100%, 12%);--success-text:hsl(150, 86%, 65%);--info-bg:hsl(215, 100%, 6%);--info-border:hsl(223, 43%, 17%);--info-text:hsl(216, 87%, 65%);--warning-bg:hsl(64, 100%, 6%);--warning-border:hsl(60, 100%, 9%);--warning-text:hsl(46, 87%, 65%);--error-bg:hsl(358, 76%, 10%);--error-border:hsl(357, 89%, 16%);--error-text:hsl(358, 100%, 81%)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button]{background:var(--normal-bg);border-color:var(--normal-border);color:var(--normal-text)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button]:hover{background:var(--normal-bg-hover);border-color:var(--normal-border-hover)}[data-rich-colors=true][data-sonner-toast][data-type=success]{background:var(--success-bg);border-color:var(--success-border);color:var(--success-text)}[data-rich-colors=true][data-sonner-toast][data-type=success] [data-close-button]{background:var(--success-bg);border-color:var(--success-border);color:var(--success-text)}[data-rich-colors=true][data-sonner-toast][data-type=info]{background:var(--info-bg);border-color:var(--info-border);color:var(--info-text)}[data-rich-colors=true][data-sonner-toast][data-type=info] [data-close-button]{background:var(--info-bg);border-color:var(--info-border);color:var(--info-text)}[data-rich-colors=true][data-sonner-toast][data-type=warning]{background:var(--warning-bg);border-color:var(--warning-border);color:var(--warning-text)}[data-rich-colors=true][data-sonner-toast][data-type=warning] [data-close-button]{background:var(--warning-bg);border-color:var(--warning-border);color:var(--warning-text)}[data-rich-colors=true][data-sonner-toast][data-type=error]{background:var(--error-bg);border-color:var(--error-border);color:var(--error-text)}[data-rich-colors=true][data-sonner-toast][data-type=error] [data-close-button]{background:var(--error-bg);border-color:var(--error-border);color:var(--error-text)}.sonner-loading-wrapper{--size:16px;height:var(--size);width:var(--size);position:absolute;inset:0;z-index:10}.sonner-loading-wrapper[data-visible=false]{transform-origin:center;animation:sonner-fade-out .2s ease forwards}.sonner-spinner{position:relative;top:50%;left:50%;height:var(--size);width:var(--size)}.sonner-loading-bar{animation:sonner-spin 1.2s linear infinite;background:var(--gray11);border-radius:6px;height:8%;left:-10%;position:absolute;top:-3.9%;width:24%}.sonner-loading-bar:first-child{animation-delay:-1.2s;transform:rotate(.0001deg) translate(146%)}.sonner-loading-bar:nth-child(2){animation-delay:-1.1s;transform:rotate(30deg) translate(146%)}.sonner-loading-bar:nth-child(3){animation-delay:-1s;transform:rotate(60deg) translate(146%)}.sonner-loading-bar:nth-child(4){animation-delay:-.9s;transform:rotate(90deg) translate(146%)}.sonner-loading-bar:nth-child(5){animation-delay:-.8s;transform:rotate(120deg) translate(146%)}.sonner-loading-bar:nth-child(6){animation-delay:-.7s;transform:rotate(150deg) translate(146%)}.sonner-loading-bar:nth-child(7){animation-delay:-.6s;transform:rotate(180deg) translate(146%)}.sonner-loading-bar:nth-child(8){animation-delay:-.5s;transform:rotate(210deg) translate(146%)}.sonner-loading-bar:nth-child(9){animation-delay:-.4s;transform:rotate(240deg) translate(146%)}.sonner-loading-bar:nth-child(10){animation-delay:-.3s;transform:rotate(270deg) translate(146%)}.sonner-loading-bar:nth-child(11){animation-delay:-.2s;transform:rotate(300deg) translate(146%)}.sonner-loading-bar:nth-child(12){animation-delay:-.1s;transform:rotate(330deg) translate(146%)}@keyframes sonner-fade-in{0%{opacity:0;transform:scale(.8)}100%{opacity:1;transform:scale(1)}}@keyframes sonner-fade-out{0%{opacity:1;transform:scale(1)}100%{opacity:0;transform:scale(.8)}}@keyframes sonner-spin{0%{opacity:1}100%{opacity:.15}}@media (prefers-reduced-motion){.sonner-loading-bar,[data-sonner-toast],[data-sonner-toast]>*{transition:none!important;animation:none!important}}.sonner-loader{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;transition:opacity .2s,transform .2s}.sonner-loader[data-visible=false]{opacity:0;transform:scale(.8) translate(-50%,-50%)}");
2258
+ function isAction(action) {
2259
+ return action.label !== void 0;
2260
+ }
2261
+ var VISIBLE_TOASTS_AMOUNT = 3;
2262
+ var VIEWPORT_OFFSET = "24px";
2263
+ var MOBILE_VIEWPORT_OFFSET = "16px";
2264
+ var TOAST_LIFETIME = 4e3;
2265
+ var TOAST_WIDTH = 356;
2266
+ var GAP = 14;
2267
+ var SWIPE_THRESHOLD = 45;
2268
+ var TIME_BEFORE_UNMOUNT = 200;
2269
+ function cn(...classes) {
2270
+ return classes.filter(Boolean).join(" ");
2271
+ }
2272
+ function getDefaultSwipeDirections(position) {
2273
+ const [y, x] = position.split("-");
2274
+ const directions = [];
2275
+ if (y) {
2276
+ directions.push(y);
2277
+ }
2278
+ if (x) {
2279
+ directions.push(x);
2280
+ }
2281
+ return directions;
2282
+ }
2283
+ var Toast = (props) => {
2284
+ var _toast_classNames, _toast_classNames1, _toast_classNames2, _toast_classNames3, _toast_classNames4, _toast_classNames5, _toast_classNames6, _toast_classNames7, _toast_classNames8;
2285
+ const { invert: ToasterInvert, toast: toast2, unstyled, interacting, setHeights, visibleToasts, heights, index, toasts, expanded, removeToast, defaultRichColors, closeButton: closeButtonFromToaster, style, cancelButtonStyle, actionButtonStyle, className = "", descriptionClassName = "", duration: durationFromToaster, position, gap, expandByDefault, classNames, icons, closeButtonAriaLabel = "Close toast" } = props;
2286
+ const [swipeDirection, setSwipeDirection] = React__default.default.useState(null);
2287
+ const [swipeOutDirection, setSwipeOutDirection] = React__default.default.useState(null);
2288
+ const [mounted, setMounted] = React__default.default.useState(false);
2289
+ const [removed, setRemoved] = React__default.default.useState(false);
2290
+ const [swiping, setSwiping] = React__default.default.useState(false);
2291
+ const [swipeOut, setSwipeOut] = React__default.default.useState(false);
2292
+ const [isSwiped, setIsSwiped] = React__default.default.useState(false);
2293
+ const [offsetBeforeRemove, setOffsetBeforeRemove] = React__default.default.useState(0);
2294
+ const [initialHeight, setInitialHeight] = React__default.default.useState(0);
2295
+ const remainingTime = React__default.default.useRef(toast2.duration || durationFromToaster || TOAST_LIFETIME);
2296
+ const dragStartTime = React__default.default.useRef(null);
2297
+ const toastRef = React__default.default.useRef(null);
2298
+ const isFront = index === 0;
2299
+ const isVisible = index + 1 <= visibleToasts;
2300
+ const toastType = toast2.type;
2301
+ const dismissible = toast2.dismissible !== false;
2302
+ const toastClassname = toast2.className || "";
2303
+ const toastDescriptionClassname = toast2.descriptionClassName || "";
2304
+ const heightIndex = React__default.default.useMemo(() => heights.findIndex((height) => height.toastId === toast2.id) || 0, [
2305
+ heights,
2306
+ toast2.id
2307
+ ]);
2308
+ const closeButton = React__default.default.useMemo(() => {
2309
+ var _toast_closeButton;
2310
+ return (_toast_closeButton = toast2.closeButton) != null ? _toast_closeButton : closeButtonFromToaster;
2311
+ }, [
2312
+ toast2.closeButton,
2313
+ closeButtonFromToaster
2314
+ ]);
2315
+ const duration = React__default.default.useMemo(() => toast2.duration || durationFromToaster || TOAST_LIFETIME, [
2316
+ toast2.duration,
2317
+ durationFromToaster
2318
+ ]);
2319
+ const closeTimerStartTimeRef = React__default.default.useRef(0);
2320
+ const offset = React__default.default.useRef(0);
2321
+ const lastCloseTimerStartTimeRef = React__default.default.useRef(0);
2322
+ const pointerStartRef = React__default.default.useRef(null);
2323
+ const [y, x] = position.split("-");
2324
+ const toastsHeightBefore = React__default.default.useMemo(() => {
2325
+ return heights.reduce((prev, curr, reducerIndex) => {
2326
+ if (reducerIndex >= heightIndex) {
2327
+ return prev;
2328
+ }
2329
+ return prev + curr.height;
2330
+ }, 0);
2331
+ }, [
2332
+ heights,
2333
+ heightIndex
2334
+ ]);
2335
+ const isDocumentHidden = useIsDocumentHidden();
2336
+ const invert = toast2.invert || ToasterInvert;
2337
+ const disabled = toastType === "loading";
2338
+ offset.current = React__default.default.useMemo(() => heightIndex * gap + toastsHeightBefore, [
2339
+ heightIndex,
2340
+ toastsHeightBefore
2341
+ ]);
2342
+ React__default.default.useEffect(() => {
2343
+ remainingTime.current = duration;
2344
+ }, [
2345
+ duration
2346
+ ]);
2347
+ React__default.default.useEffect(() => {
2348
+ setMounted(true);
2349
+ }, []);
2350
+ React__default.default.useEffect(() => {
2351
+ const toastNode = toastRef.current;
2352
+ if (toastNode) {
2353
+ const height = toastNode.getBoundingClientRect().height;
2354
+ setInitialHeight(height);
2355
+ setHeights((h) => [
2356
+ {
2357
+ toastId: toast2.id,
2358
+ height,
2359
+ position: toast2.position
2360
+ },
2361
+ ...h
2362
+ ]);
2363
+ return () => setHeights((h) => h.filter((height2) => height2.toastId !== toast2.id));
2364
+ }
2365
+ }, [
2366
+ setHeights,
2367
+ toast2.id
2368
+ ]);
2369
+ React__default.default.useLayoutEffect(() => {
2370
+ if (!mounted) return;
2371
+ const toastNode = toastRef.current;
2372
+ const originalHeight = toastNode.style.height;
2373
+ toastNode.style.height = "auto";
2374
+ const newHeight = toastNode.getBoundingClientRect().height;
2375
+ toastNode.style.height = originalHeight;
2376
+ setInitialHeight(newHeight);
2377
+ setHeights((heights2) => {
2378
+ const alreadyExists = heights2.find((height) => height.toastId === toast2.id);
2379
+ if (!alreadyExists) {
2380
+ return [
2381
+ {
2382
+ toastId: toast2.id,
2383
+ height: newHeight,
2384
+ position: toast2.position
2385
+ },
2386
+ ...heights2
2387
+ ];
2388
+ } else {
2389
+ return heights2.map((height) => height.toastId === toast2.id ? {
2390
+ ...height,
2391
+ height: newHeight
2392
+ } : height);
2393
+ }
2394
+ });
2395
+ }, [
2396
+ mounted,
2397
+ toast2.title,
2398
+ toast2.description,
2399
+ setHeights,
2400
+ toast2.id,
2401
+ toast2.jsx,
2402
+ toast2.action,
2403
+ toast2.cancel
2404
+ ]);
2405
+ const deleteToast = React__default.default.useCallback(() => {
2406
+ setRemoved(true);
2407
+ setOffsetBeforeRemove(offset.current);
2408
+ setHeights((h) => h.filter((height) => height.toastId !== toast2.id));
2409
+ setTimeout(() => {
2410
+ removeToast(toast2);
2411
+ }, TIME_BEFORE_UNMOUNT);
2412
+ }, [
2413
+ toast2,
2414
+ removeToast,
2415
+ setHeights,
2416
+ offset
2417
+ ]);
2418
+ React__default.default.useEffect(() => {
2419
+ if (toast2.promise && toastType === "loading" || toast2.duration === Infinity || toast2.type === "loading") return;
2420
+ let timeoutId;
2421
+ const pauseTimer = () => {
2422
+ if (lastCloseTimerStartTimeRef.current < closeTimerStartTimeRef.current) {
2423
+ const elapsedTime = (/* @__PURE__ */ new Date()).getTime() - closeTimerStartTimeRef.current;
2424
+ remainingTime.current = remainingTime.current - elapsedTime;
2425
+ }
2426
+ lastCloseTimerStartTimeRef.current = (/* @__PURE__ */ new Date()).getTime();
2427
+ };
2428
+ const startTimer = () => {
2429
+ if (remainingTime.current === Infinity) return;
2430
+ closeTimerStartTimeRef.current = (/* @__PURE__ */ new Date()).getTime();
2431
+ timeoutId = setTimeout(() => {
2432
+ toast2.onAutoClose == null ? void 0 : toast2.onAutoClose.call(toast2, toast2);
2433
+ deleteToast();
2434
+ }, remainingTime.current);
2435
+ };
2436
+ if (expanded || interacting || isDocumentHidden) {
2437
+ pauseTimer();
1574
2438
  } else {
1575
- await checkout.open(product, fallback);
2439
+ startTimer();
2440
+ }
2441
+ return () => clearTimeout(timeoutId);
2442
+ }, [
2443
+ expanded,
2444
+ interacting,
2445
+ toast2,
2446
+ toastType,
2447
+ isDocumentHidden,
2448
+ deleteToast
2449
+ ]);
2450
+ React__default.default.useEffect(() => {
2451
+ if (toast2.delete) {
2452
+ deleteToast();
2453
+ toast2.onDismiss == null ? void 0 : toast2.onDismiss.call(toast2, toast2);
2454
+ }
2455
+ }, [
2456
+ deleteToast,
2457
+ toast2.delete
2458
+ ]);
2459
+ function getLoadingIcon() {
2460
+ var _toast_classNames9;
2461
+ if (icons == null ? void 0 : icons.loading) {
2462
+ var _toast_classNames12;
2463
+ return /* @__PURE__ */ React__default.default.createElement("div", {
2464
+ className: cn(classNames == null ? void 0 : classNames.loader, toast2 == null ? void 0 : (_toast_classNames12 = toast2.classNames) == null ? void 0 : _toast_classNames12.loader, "sonner-loader"),
2465
+ "data-visible": toastType === "loading"
2466
+ }, icons.loading);
2467
+ }
2468
+ return /* @__PURE__ */ React__default.default.createElement(Loader, {
2469
+ className: cn(classNames == null ? void 0 : classNames.loader, toast2 == null ? void 0 : (_toast_classNames9 = toast2.classNames) == null ? void 0 : _toast_classNames9.loader),
2470
+ visible: toastType === "loading"
2471
+ });
2472
+ }
2473
+ const icon = toast2.icon || (icons == null ? void 0 : icons[toastType]) || getAsset(toastType);
2474
+ var _toast_richColors, _icons_close;
2475
+ return /* @__PURE__ */ React__default.default.createElement("li", {
2476
+ tabIndex: 0,
2477
+ ref: toastRef,
2478
+ className: cn(className, toastClassname, classNames == null ? void 0 : classNames.toast, toast2 == null ? void 0 : (_toast_classNames = toast2.classNames) == null ? void 0 : _toast_classNames.toast, classNames == null ? void 0 : classNames.default, classNames == null ? void 0 : classNames[toastType], toast2 == null ? void 0 : (_toast_classNames1 = toast2.classNames) == null ? void 0 : _toast_classNames1[toastType]),
2479
+ "data-sonner-toast": "",
2480
+ "data-rich-colors": (_toast_richColors = toast2.richColors) != null ? _toast_richColors : defaultRichColors,
2481
+ "data-styled": !Boolean(toast2.jsx || toast2.unstyled || unstyled),
2482
+ "data-mounted": mounted,
2483
+ "data-promise": Boolean(toast2.promise),
2484
+ "data-swiped": isSwiped,
2485
+ "data-removed": removed,
2486
+ "data-visible": isVisible,
2487
+ "data-y-position": y,
2488
+ "data-x-position": x,
2489
+ "data-index": index,
2490
+ "data-front": isFront,
2491
+ "data-swiping": swiping,
2492
+ "data-dismissible": dismissible,
2493
+ "data-type": toastType,
2494
+ "data-invert": invert,
2495
+ "data-swipe-out": swipeOut,
2496
+ "data-swipe-direction": swipeOutDirection,
2497
+ "data-expanded": Boolean(expanded || expandByDefault && mounted),
2498
+ "data-testid": toast2.testId,
2499
+ style: {
2500
+ "--index": index,
2501
+ "--toasts-before": index,
2502
+ "--z-index": toasts.length - index,
2503
+ "--offset": `${removed ? offsetBeforeRemove : offset.current}px`,
2504
+ "--initial-height": expandByDefault ? "auto" : `${initialHeight}px`,
2505
+ ...style,
2506
+ ...toast2.style
2507
+ },
2508
+ onDragEnd: () => {
2509
+ setSwiping(false);
2510
+ setSwipeDirection(null);
2511
+ pointerStartRef.current = null;
2512
+ },
2513
+ onPointerDown: (event) => {
2514
+ if (event.button === 2) return;
2515
+ if (disabled || !dismissible) return;
2516
+ dragStartTime.current = /* @__PURE__ */ new Date();
2517
+ setOffsetBeforeRemove(offset.current);
2518
+ event.target.setPointerCapture(event.pointerId);
2519
+ if (event.target.tagName === "BUTTON") return;
2520
+ setSwiping(true);
2521
+ pointerStartRef.current = {
2522
+ x: event.clientX,
2523
+ y: event.clientY
2524
+ };
2525
+ },
2526
+ onPointerUp: () => {
2527
+ var _toastRef_current, _toastRef_current1, _dragStartTime_current;
2528
+ if (swipeOut || !dismissible) return;
2529
+ pointerStartRef.current = null;
2530
+ const swipeAmountX = Number(((_toastRef_current = toastRef.current) == null ? void 0 : _toastRef_current.style.getPropertyValue("--swipe-amount-x").replace("px", "")) || 0);
2531
+ const swipeAmountY = Number(((_toastRef_current1 = toastRef.current) == null ? void 0 : _toastRef_current1.style.getPropertyValue("--swipe-amount-y").replace("px", "")) || 0);
2532
+ const timeTaken = (/* @__PURE__ */ new Date()).getTime() - ((_dragStartTime_current = dragStartTime.current) == null ? void 0 : _dragStartTime_current.getTime());
2533
+ const swipeAmount = swipeDirection === "x" ? swipeAmountX : swipeAmountY;
2534
+ const velocity = Math.abs(swipeAmount) / timeTaken;
2535
+ if (Math.abs(swipeAmount) >= SWIPE_THRESHOLD || velocity > 0.11) {
2536
+ setOffsetBeforeRemove(offset.current);
2537
+ toast2.onDismiss == null ? void 0 : toast2.onDismiss.call(toast2, toast2);
2538
+ if (swipeDirection === "x") {
2539
+ setSwipeOutDirection(swipeAmountX > 0 ? "right" : "left");
2540
+ } else {
2541
+ setSwipeOutDirection(swipeAmountY > 0 ? "down" : "up");
2542
+ }
2543
+ deleteToast();
2544
+ setSwipeOut(true);
2545
+ return;
2546
+ } else {
2547
+ var _toastRef_current2, _toastRef_current3;
2548
+ (_toastRef_current2 = toastRef.current) == null ? void 0 : _toastRef_current2.style.setProperty("--swipe-amount-x", `0px`);
2549
+ (_toastRef_current3 = toastRef.current) == null ? void 0 : _toastRef_current3.style.setProperty("--swipe-amount-y", `0px`);
2550
+ }
2551
+ setIsSwiped(false);
2552
+ setSwiping(false);
2553
+ setSwipeDirection(null);
2554
+ },
2555
+ onPointerMove: (event) => {
2556
+ var _window_getSelection, _toastRef_current, _toastRef_current1;
2557
+ if (!pointerStartRef.current || !dismissible) return;
2558
+ const isHighlighted = ((_window_getSelection = window.getSelection()) == null ? void 0 : _window_getSelection.toString().length) > 0;
2559
+ if (isHighlighted) return;
2560
+ const yDelta = event.clientY - pointerStartRef.current.y;
2561
+ const xDelta = event.clientX - pointerStartRef.current.x;
2562
+ var _props_swipeDirections;
2563
+ const swipeDirections = (_props_swipeDirections = props.swipeDirections) != null ? _props_swipeDirections : getDefaultSwipeDirections(position);
2564
+ if (!swipeDirection && (Math.abs(xDelta) > 1 || Math.abs(yDelta) > 1)) {
2565
+ setSwipeDirection(Math.abs(xDelta) > Math.abs(yDelta) ? "x" : "y");
2566
+ }
2567
+ let swipeAmount = {
2568
+ x: 0,
2569
+ y: 0
2570
+ };
2571
+ const getDampening = (delta) => {
2572
+ const factor = Math.abs(delta) / 20;
2573
+ return 1 / (1.5 + factor);
2574
+ };
2575
+ if (swipeDirection === "y") {
2576
+ if (swipeDirections.includes("top") || swipeDirections.includes("bottom")) {
2577
+ if (swipeDirections.includes("top") && yDelta < 0 || swipeDirections.includes("bottom") && yDelta > 0) {
2578
+ swipeAmount.y = yDelta;
2579
+ } else {
2580
+ const dampenedDelta = yDelta * getDampening(yDelta);
2581
+ swipeAmount.y = Math.abs(dampenedDelta) < Math.abs(yDelta) ? dampenedDelta : yDelta;
2582
+ }
2583
+ }
2584
+ } else if (swipeDirection === "x") {
2585
+ if (swipeDirections.includes("left") || swipeDirections.includes("right")) {
2586
+ if (swipeDirections.includes("left") && xDelta < 0 || swipeDirections.includes("right") && xDelta > 0) {
2587
+ swipeAmount.x = xDelta;
2588
+ } else {
2589
+ const dampenedDelta = xDelta * getDampening(xDelta);
2590
+ swipeAmount.x = Math.abs(dampenedDelta) < Math.abs(xDelta) ? dampenedDelta : xDelta;
2591
+ }
2592
+ }
2593
+ }
2594
+ if (Math.abs(swipeAmount.x) > 0 || Math.abs(swipeAmount.y) > 0) {
2595
+ setIsSwiped(true);
2596
+ }
2597
+ (_toastRef_current = toastRef.current) == null ? void 0 : _toastRef_current.style.setProperty("--swipe-amount-x", `${swipeAmount.x}px`);
2598
+ (_toastRef_current1 = toastRef.current) == null ? void 0 : _toastRef_current1.style.setProperty("--swipe-amount-y", `${swipeAmount.y}px`);
2599
+ }
2600
+ }, closeButton && !toast2.jsx && toastType !== "loading" ? /* @__PURE__ */ React__default.default.createElement("button", {
2601
+ "aria-label": closeButtonAriaLabel,
2602
+ "data-disabled": disabled,
2603
+ "data-close-button": true,
2604
+ onClick: disabled || !dismissible ? () => {
2605
+ } : () => {
2606
+ deleteToast();
2607
+ toast2.onDismiss == null ? void 0 : toast2.onDismiss.call(toast2, toast2);
2608
+ },
2609
+ className: cn(classNames == null ? void 0 : classNames.closeButton, toast2 == null ? void 0 : (_toast_classNames2 = toast2.classNames) == null ? void 0 : _toast_classNames2.closeButton)
2610
+ }, (_icons_close = icons == null ? void 0 : icons.close) != null ? _icons_close : CloseIcon) : null, (toastType || toast2.icon || toast2.promise) && toast2.icon !== null && ((icons == null ? void 0 : icons[toastType]) !== null || toast2.icon) ? /* @__PURE__ */ React__default.default.createElement("div", {
2611
+ "data-icon": "",
2612
+ className: cn(classNames == null ? void 0 : classNames.icon, toast2 == null ? void 0 : (_toast_classNames3 = toast2.classNames) == null ? void 0 : _toast_classNames3.icon)
2613
+ }, toast2.promise || toast2.type === "loading" && !toast2.icon ? toast2.icon || getLoadingIcon() : null, toast2.type !== "loading" ? icon : null) : null, /* @__PURE__ */ React__default.default.createElement("div", {
2614
+ "data-content": "",
2615
+ className: cn(classNames == null ? void 0 : classNames.content, toast2 == null ? void 0 : (_toast_classNames4 = toast2.classNames) == null ? void 0 : _toast_classNames4.content)
2616
+ }, /* @__PURE__ */ React__default.default.createElement("div", {
2617
+ "data-title": "",
2618
+ className: cn(classNames == null ? void 0 : classNames.title, toast2 == null ? void 0 : (_toast_classNames5 = toast2.classNames) == null ? void 0 : _toast_classNames5.title)
2619
+ }, toast2.jsx ? toast2.jsx : typeof toast2.title === "function" ? toast2.title() : toast2.title), toast2.description ? /* @__PURE__ */ React__default.default.createElement("div", {
2620
+ "data-description": "",
2621
+ className: cn(descriptionClassName, toastDescriptionClassname, classNames == null ? void 0 : classNames.description, toast2 == null ? void 0 : (_toast_classNames6 = toast2.classNames) == null ? void 0 : _toast_classNames6.description)
2622
+ }, typeof toast2.description === "function" ? toast2.description() : toast2.description) : null), /* @__PURE__ */ React__default.default.isValidElement(toast2.cancel) ? toast2.cancel : toast2.cancel && isAction(toast2.cancel) ? /* @__PURE__ */ React__default.default.createElement("button", {
2623
+ "data-button": true,
2624
+ "data-cancel": true,
2625
+ style: toast2.cancelButtonStyle || cancelButtonStyle,
2626
+ onClick: (event) => {
2627
+ if (!isAction(toast2.cancel)) return;
2628
+ if (!dismissible) return;
2629
+ toast2.cancel.onClick == null ? void 0 : toast2.cancel.onClick.call(toast2.cancel, event);
2630
+ deleteToast();
2631
+ },
2632
+ className: cn(classNames == null ? void 0 : classNames.cancelButton, toast2 == null ? void 0 : (_toast_classNames7 = toast2.classNames) == null ? void 0 : _toast_classNames7.cancelButton)
2633
+ }, toast2.cancel.label) : null, /* @__PURE__ */ React__default.default.isValidElement(toast2.action) ? toast2.action : toast2.action && isAction(toast2.action) ? /* @__PURE__ */ React__default.default.createElement("button", {
2634
+ "data-button": true,
2635
+ "data-action": true,
2636
+ style: toast2.actionButtonStyle || actionButtonStyle,
2637
+ onClick: (event) => {
2638
+ if (!isAction(toast2.action)) return;
2639
+ toast2.action.onClick == null ? void 0 : toast2.action.onClick.call(toast2.action, event);
2640
+ if (event.defaultPrevented) return;
2641
+ deleteToast();
2642
+ },
2643
+ className: cn(classNames == null ? void 0 : classNames.actionButton, toast2 == null ? void 0 : (_toast_classNames8 = toast2.classNames) == null ? void 0 : _toast_classNames8.actionButton)
2644
+ }, toast2.action.label) : null);
2645
+ };
2646
+ function getDocumentDirection() {
2647
+ if (typeof window === "undefined") return "ltr";
2648
+ if (typeof document === "undefined") return "ltr";
2649
+ const dirAttribute = document.documentElement.getAttribute("dir");
2650
+ if (dirAttribute === "auto" || !dirAttribute) {
2651
+ return window.getComputedStyle(document.documentElement).direction;
2652
+ }
2653
+ return dirAttribute;
2654
+ }
2655
+ function assignOffset(defaultOffset, mobileOffset) {
2656
+ const styles = {};
2657
+ [
2658
+ defaultOffset,
2659
+ mobileOffset
2660
+ ].forEach((offset, index) => {
2661
+ const isMobile = index === 1;
2662
+ const prefix = isMobile ? "--mobile-offset" : "--offset";
2663
+ const defaultValue = isMobile ? MOBILE_VIEWPORT_OFFSET : VIEWPORT_OFFSET;
2664
+ function assignAll(offset2) {
2665
+ [
2666
+ "top",
2667
+ "right",
2668
+ "bottom",
2669
+ "left"
2670
+ ].forEach((key) => {
2671
+ styles[`${prefix}-${key}`] = typeof offset2 === "number" ? `${offset2}px` : offset2;
2672
+ });
2673
+ }
2674
+ if (typeof offset === "number" || typeof offset === "string") {
2675
+ assignAll(offset);
2676
+ } else if (typeof offset === "object") {
2677
+ [
2678
+ "top",
2679
+ "right",
2680
+ "bottom",
2681
+ "left"
2682
+ ].forEach((key) => {
2683
+ if (offset[key] === void 0) {
2684
+ styles[`${prefix}-${key}`] = defaultValue;
2685
+ } else {
2686
+ styles[`${prefix}-${key}`] = typeof offset[key] === "number" ? `${offset[key]}px` : offset[key];
2687
+ }
2688
+ });
2689
+ } else {
2690
+ assignAll(defaultValue);
1576
2691
  }
1577
- };
1578
- return trigger(start, {
1579
- asChild,
1580
- children: children ?? "Add to my plan",
1581
- className,
1582
- disabled: checkout.isLoading
1583
2692
  });
1584
- }
2693
+ return styles;
2694
+ }
2695
+ var Toaster = /* @__PURE__ */ React__default.default.forwardRef(function Toaster2(props, ref) {
2696
+ const { id, invert, position = "bottom-right", hotkey = [
2697
+ "altKey",
2698
+ "KeyT"
2699
+ ], expand, closeButton, className, offset, mobileOffset, theme = "light", richColors, duration, style, visibleToasts = VISIBLE_TOASTS_AMOUNT, toastOptions, dir = getDocumentDirection(), gap = GAP, icons, containerAriaLabel = "Notifications" } = props;
2700
+ const [toasts, setToasts] = React__default.default.useState([]);
2701
+ const filteredToasts = React__default.default.useMemo(() => {
2702
+ if (id) {
2703
+ return toasts.filter((toast2) => toast2.toasterId === id);
2704
+ }
2705
+ return toasts.filter((toast2) => !toast2.toasterId);
2706
+ }, [
2707
+ toasts,
2708
+ id
2709
+ ]);
2710
+ const possiblePositions = React__default.default.useMemo(() => {
2711
+ return Array.from(new Set([
2712
+ position
2713
+ ].concat(filteredToasts.filter((toast2) => toast2.position).map((toast2) => toast2.position))));
2714
+ }, [
2715
+ filteredToasts,
2716
+ position
2717
+ ]);
2718
+ const [heights, setHeights] = React__default.default.useState([]);
2719
+ const [expanded, setExpanded] = React__default.default.useState(false);
2720
+ const [interacting, setInteracting] = React__default.default.useState(false);
2721
+ const [actualTheme, setActualTheme] = React__default.default.useState(theme !== "system" ? theme : typeof window !== "undefined" ? window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : "light");
2722
+ const listRef = React__default.default.useRef(null);
2723
+ const hotkeyLabel = hotkey.join("+").replace(/Key/g, "").replace(/Digit/g, "");
2724
+ const lastFocusedElementRef = React__default.default.useRef(null);
2725
+ const isFocusWithinRef = React__default.default.useRef(false);
2726
+ const removeToast = React__default.default.useCallback((toastToRemove) => {
2727
+ setToasts((toasts2) => {
2728
+ var _toasts_find;
2729
+ if (!((_toasts_find = toasts2.find((toast2) => toast2.id === toastToRemove.id)) == null ? void 0 : _toasts_find.delete)) {
2730
+ ToastState.dismiss(toastToRemove.id);
2731
+ }
2732
+ return toasts2.filter(({ id: id2 }) => id2 !== toastToRemove.id);
2733
+ });
2734
+ }, []);
2735
+ React__default.default.useEffect(() => {
2736
+ return ToastState.subscribe((toast2) => {
2737
+ if (toast2.dismiss) {
2738
+ requestAnimationFrame(() => {
2739
+ setToasts((toasts2) => toasts2.map((t) => t.id === toast2.id ? {
2740
+ ...t,
2741
+ delete: true
2742
+ } : t));
2743
+ });
2744
+ return;
2745
+ }
2746
+ setTimeout(() => {
2747
+ ReactDOM__default.default.flushSync(() => {
2748
+ setToasts((toasts2) => {
2749
+ const indexOfExistingToast = toasts2.findIndex((t) => t.id === toast2.id);
2750
+ if (indexOfExistingToast !== -1) {
2751
+ return [
2752
+ ...toasts2.slice(0, indexOfExistingToast),
2753
+ {
2754
+ ...toasts2[indexOfExistingToast],
2755
+ ...toast2
2756
+ },
2757
+ ...toasts2.slice(indexOfExistingToast + 1)
2758
+ ];
2759
+ }
2760
+ return [
2761
+ toast2,
2762
+ ...toasts2
2763
+ ];
2764
+ });
2765
+ });
2766
+ });
2767
+ });
2768
+ }, [
2769
+ toasts
2770
+ ]);
2771
+ React__default.default.useEffect(() => {
2772
+ if (theme !== "system") {
2773
+ setActualTheme(theme);
2774
+ return;
2775
+ }
2776
+ if (theme === "system") {
2777
+ if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
2778
+ setActualTheme("dark");
2779
+ } else {
2780
+ setActualTheme("light");
2781
+ }
2782
+ }
2783
+ if (typeof window === "undefined") return;
2784
+ const darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
2785
+ try {
2786
+ darkMediaQuery.addEventListener("change", ({ matches }) => {
2787
+ if (matches) {
2788
+ setActualTheme("dark");
2789
+ } else {
2790
+ setActualTheme("light");
2791
+ }
2792
+ });
2793
+ } catch (error) {
2794
+ darkMediaQuery.addListener(({ matches }) => {
2795
+ try {
2796
+ if (matches) {
2797
+ setActualTheme("dark");
2798
+ } else {
2799
+ setActualTheme("light");
2800
+ }
2801
+ } catch (e) {
2802
+ console.error(e);
2803
+ }
2804
+ });
2805
+ }
2806
+ }, [
2807
+ theme
2808
+ ]);
2809
+ React__default.default.useEffect(() => {
2810
+ if (toasts.length <= 1) {
2811
+ setExpanded(false);
2812
+ }
2813
+ }, [
2814
+ toasts
2815
+ ]);
2816
+ React__default.default.useEffect(() => {
2817
+ const handleKeyDown = (event) => {
2818
+ var _listRef_current;
2819
+ const isHotkeyPressed = hotkey.every((key) => event[key] || event.code === key);
2820
+ if (isHotkeyPressed) {
2821
+ var _listRef_current1;
2822
+ setExpanded(true);
2823
+ (_listRef_current1 = listRef.current) == null ? void 0 : _listRef_current1.focus();
2824
+ }
2825
+ if (event.code === "Escape" && (document.activeElement === listRef.current || ((_listRef_current = listRef.current) == null ? void 0 : _listRef_current.contains(document.activeElement)))) {
2826
+ setExpanded(false);
2827
+ }
2828
+ };
2829
+ document.addEventListener("keydown", handleKeyDown);
2830
+ return () => document.removeEventListener("keydown", handleKeyDown);
2831
+ }, [
2832
+ hotkey
2833
+ ]);
2834
+ React__default.default.useEffect(() => {
2835
+ if (listRef.current) {
2836
+ return () => {
2837
+ if (lastFocusedElementRef.current) {
2838
+ lastFocusedElementRef.current.focus({
2839
+ preventScroll: true
2840
+ });
2841
+ lastFocusedElementRef.current = null;
2842
+ isFocusWithinRef.current = false;
2843
+ }
2844
+ };
2845
+ }
2846
+ }, [
2847
+ listRef.current
2848
+ ]);
2849
+ return (
2850
+ // Remove item from normal navigation flow, only available via hotkey
2851
+ /* @__PURE__ */ React__default.default.createElement("section", {
2852
+ ref,
2853
+ "aria-label": `${containerAriaLabel} ${hotkeyLabel}`,
2854
+ tabIndex: -1,
2855
+ "aria-live": "polite",
2856
+ "aria-relevant": "additions text",
2857
+ "aria-atomic": "false",
2858
+ suppressHydrationWarning: true
2859
+ }, possiblePositions.map((position2, index) => {
2860
+ var _heights_;
2861
+ const [y, x] = position2.split("-");
2862
+ if (!filteredToasts.length) return null;
2863
+ return /* @__PURE__ */ React__default.default.createElement("ol", {
2864
+ key: position2,
2865
+ dir: dir === "auto" ? getDocumentDirection() : dir,
2866
+ tabIndex: -1,
2867
+ ref: listRef,
2868
+ className,
2869
+ "data-sonner-toaster": true,
2870
+ "data-sonner-theme": actualTheme,
2871
+ "data-y-position": y,
2872
+ "data-x-position": x,
2873
+ style: {
2874
+ "--front-toast-height": `${((_heights_ = heights[0]) == null ? void 0 : _heights_.height) || 0}px`,
2875
+ "--width": `${TOAST_WIDTH}px`,
2876
+ "--gap": `${gap}px`,
2877
+ ...style,
2878
+ ...assignOffset(offset, mobileOffset)
2879
+ },
2880
+ onBlur: (event) => {
2881
+ if (isFocusWithinRef.current && !event.currentTarget.contains(event.relatedTarget)) {
2882
+ isFocusWithinRef.current = false;
2883
+ if (lastFocusedElementRef.current) {
2884
+ lastFocusedElementRef.current.focus({
2885
+ preventScroll: true
2886
+ });
2887
+ lastFocusedElementRef.current = null;
2888
+ }
2889
+ }
2890
+ },
2891
+ onFocus: (event) => {
2892
+ const isNotDismissible = event.target instanceof HTMLElement && event.target.dataset.dismissible === "false";
2893
+ if (isNotDismissible) return;
2894
+ if (!isFocusWithinRef.current) {
2895
+ isFocusWithinRef.current = true;
2896
+ lastFocusedElementRef.current = event.relatedTarget;
2897
+ }
2898
+ },
2899
+ onMouseEnter: () => setExpanded(true),
2900
+ onMouseMove: () => setExpanded(true),
2901
+ onMouseLeave: () => {
2902
+ if (!interacting) {
2903
+ setExpanded(false);
2904
+ }
2905
+ },
2906
+ onDragEnd: () => setExpanded(false),
2907
+ onPointerDown: (event) => {
2908
+ const isNotDismissible = event.target instanceof HTMLElement && event.target.dataset.dismissible === "false";
2909
+ if (isNotDismissible) return;
2910
+ setInteracting(true);
2911
+ },
2912
+ onPointerUp: () => setInteracting(false)
2913
+ }, filteredToasts.filter((toast2) => !toast2.position && index === 0 || toast2.position === position2).map((toast2, index2) => {
2914
+ var _toastOptions_duration, _toastOptions_closeButton;
2915
+ return /* @__PURE__ */ React__default.default.createElement(Toast, {
2916
+ key: toast2.id,
2917
+ icons,
2918
+ index: index2,
2919
+ toast: toast2,
2920
+ defaultRichColors: richColors,
2921
+ duration: (_toastOptions_duration = toastOptions == null ? void 0 : toastOptions.duration) != null ? _toastOptions_duration : duration,
2922
+ className: toastOptions == null ? void 0 : toastOptions.className,
2923
+ descriptionClassName: toastOptions == null ? void 0 : toastOptions.descriptionClassName,
2924
+ invert,
2925
+ visibleToasts,
2926
+ closeButton: (_toastOptions_closeButton = toastOptions == null ? void 0 : toastOptions.closeButton) != null ? _toastOptions_closeButton : closeButton,
2927
+ interacting,
2928
+ position: position2,
2929
+ style: toastOptions == null ? void 0 : toastOptions.style,
2930
+ unstyled: toastOptions == null ? void 0 : toastOptions.unstyled,
2931
+ classNames: toastOptions == null ? void 0 : toastOptions.classNames,
2932
+ cancelButtonStyle: toastOptions == null ? void 0 : toastOptions.cancelButtonStyle,
2933
+ actionButtonStyle: toastOptions == null ? void 0 : toastOptions.actionButtonStyle,
2934
+ closeButtonAriaLabel: toastOptions == null ? void 0 : toastOptions.closeButtonAriaLabel,
2935
+ removeToast,
2936
+ toasts: filteredToasts.filter((t) => t.position == toast2.position),
2937
+ heights: heights.filter((h) => h.position == toast2.position),
2938
+ setHeights,
2939
+ expandByDefault: expand,
2940
+ gap,
2941
+ expanded,
2942
+ swipeDirections: props.swipeDirections
2943
+ });
2944
+ }));
2945
+ }))
2946
+ );
2947
+ });
1585
2948
  function FunnelToaster(props = {}) {
1586
- return react.createElement(sonner.Toaster, { position: "top-center", ...props });
2949
+ return React.createElement(Toaster, { position: "top-center", ...props });
1587
2950
  }
1588
2951
  function renderToaster(toaster) {
1589
2952
  if (toaster === false) return null;
1590
- return react.createElement(sonner.Toaster, { position: "top-center", ...toaster === true ? {} : toaster });
2953
+ return /* @__PURE__ */ jsxRuntime.jsx(FunnelToaster, { ...toaster === true ? {} : toaster });
1591
2954
  }
1592
2955
 
1593
2956
  // src/state/namespaces.ts
@@ -1611,7 +2974,8 @@ var Namespace = class {
1611
2974
  const out = {};
1612
2975
  const all = this.store.getState();
1613
2976
  for (const key in all) {
1614
- if (key.startsWith(this.dot)) out[key.slice(this.dot.length)] = all[key];
2977
+ if (key.startsWith(this.dot))
2978
+ out[key.slice(this.dot.length)] = all[key];
1615
2979
  }
1616
2980
  return out;
1617
2981
  }
@@ -1653,13 +3017,14 @@ function createFunnel(store, context, tracker, experiments = []) {
1653
3017
  })
1654
3018
  };
1655
3019
  }
1656
- var FunnelCtx = react.createContext(null);
3020
+ var FunnelCtx = React.createContext(null);
1657
3021
  function FunnelProvider({
1658
3022
  config,
1659
3023
  sessionValues,
3024
+ deferSessionValues = false,
1660
3025
  tracker,
1661
3026
  context: contextOpts,
1662
- products,
3027
+ offerings,
1663
3028
  experiments,
1664
3029
  checkout,
1665
3030
  messages,
@@ -1669,10 +3034,15 @@ function FunnelProvider({
1669
3034
  children
1670
3035
  }) {
1671
3036
  const resolvedTracker = tracker ?? FALLBACK_TRACKER;
1672
- const [value] = react.useState(() => {
3037
+ const [value] = React.useState(() => {
1673
3038
  const s = store ?? createFunnelStore(
1674
3039
  { responses: config?.responses, data: config?.data },
1675
- sessionValues
3040
+ // FLASH-FREE HYDRATION: with deferSessionValues the initial render is seeded with
3041
+ // DEFAULTS ONLY, so the client's first render is byte-identical to the build-time
3042
+ // DEFAULT-state prerender it hydrates against (clean match). The visitor's saved
3043
+ // answers arrive as a post-hydration setMany below. Without the flag, sessionValues
3044
+ // seed the store at construction (SSR + non-prerendered paths).
3045
+ deferSessionValues ? void 0 : sessionValues
1676
3046
  );
1677
3047
  const ctx = buildContext({
1678
3048
  funnelId: config?.id,
@@ -1686,65 +3056,90 @@ function FunnelProvider({
1686
3056
  getVisitorId: () => ctx.identity.visitorId
1687
3057
  });
1688
3058
  const busTracker = withBus(resolvedTracker, bus);
3059
+ const emailForCheckout = () => {
3060
+ const value2 = s.get("user.email");
3061
+ return typeof value2 === "string" && /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(value2) ? value2 : void 0;
3062
+ };
3063
+ const catalog = chunkRKPRMTSU_cjs.buildCatalog(offerings ?? []);
1689
3064
  return {
1690
3065
  funnel: createFunnel(s, ctx, busTracker, experiments ?? []),
1691
3066
  store: s,
1692
3067
  context: ctx,
1693
- catalog: buildCatalog(products ?? []),
1694
- driver: checkout ?? createMockDriver(),
3068
+ catalog,
3069
+ // Slot → catalog-key resolution wraps the email-enriching driver, so the wire charges the real
3070
+ // catalog key while pages + analytics keep the durable SLOT identity (req.product).
3071
+ driver: driverWithCatalog(
3072
+ driverWithEmail(
3073
+ checkout ?? createMockDriver(),
3074
+ emailForCheckout
3075
+ ),
3076
+ catalog
3077
+ ),
1695
3078
  tracker: busTracker,
1696
3079
  bus
1697
3080
  };
1698
3081
  });
1699
- react.useEffect(() => attachBus(value.bus), [value.bus]);
1700
- react.useEffect(() => {
3082
+ React.useEffect(() => attachBus(value.bus), [value.bus]);
3083
+ React.useEffect(() => {
3084
+ if (!deferSessionValues || !sessionValues) return;
3085
+ const updates = {};
3086
+ for (const [key, v] of Object.entries(sessionValues))
3087
+ if (v !== void 0) updates[key] = v;
3088
+ if (Object.keys(updates).length > 0) value.store.setMany(updates);
3089
+ }, []);
3090
+ React.useEffect(() => {
1701
3091
  value.tracker.setAcquisition?.(buildAcquisition(value.context));
1702
3092
  }, [value]);
1703
- const tree = react.createElement(TrackerProvider, {
3093
+ const tree = React.createElement(TrackerProvider, {
1704
3094
  tracker: value.tracker,
1705
- children: react.createElement(CheckoutDriverProvider, {
3095
+ children: React.createElement(CheckoutDriverProvider, {
1706
3096
  driver: value.driver,
1707
- children: react.createElement(CatalogProvider, {
3097
+ children: React.createElement(CatalogProvider, {
1708
3098
  catalog: value.catalog,
1709
- children: react.createElement(FunnelCtx.Provider, {
3099
+ children: React.createElement(FunnelCtx.Provider, {
1710
3100
  value,
1711
- children: react.createElement(LocaleProvider, {
3101
+ children: React.createElement(LocaleProvider, {
1712
3102
  config: config?.locales,
1713
3103
  catalog: messages,
1714
3104
  detected: value.context.locale.locale,
1715
3105
  override: localeOverride,
1716
- children: react.createElement(ModalRuntime, { children })
3106
+ children: React.createElement(ModalRuntime, { children })
1717
3107
  })
1718
3108
  })
1719
3109
  })
1720
3110
  })
1721
3111
  });
1722
- return react.createElement(react.Fragment, null, tree, renderToaster(toaster));
3112
+ return React.createElement(React.Fragment, null, tree, renderToaster(toaster));
1723
3113
  }
1724
3114
  var FALLBACK_TRACKER = createConsoleTracker({ silent: true });
1725
3115
  function useFunnelCtx() {
1726
- const ctx = react.useContext(FunnelCtx);
3116
+ const ctx = React.useContext(FunnelCtx);
1727
3117
  if (!ctx) {
1728
- throw new Error("useFunnel/useResponse/\u2026 must be used inside <FunnelProvider>");
3118
+ throw new Error(
3119
+ "useFunnel/useResponse/\u2026 must be used inside <FunnelProvider>"
3120
+ );
1729
3121
  }
1730
3122
  return ctx;
1731
3123
  }
1732
3124
  function useFunnel() {
1733
3125
  return useFunnelCtx().funnel;
1734
3126
  }
3127
+ function useBusInternal() {
3128
+ return React.useContext(FunnelCtx)?.bus ?? null;
3129
+ }
1735
3130
  function useStoreKey(store, key) {
1736
- const subscribe = react.useCallback(
3131
+ const subscribe = React.useCallback(
1737
3132
  (cb) => store.subscribe(cb, { keys: [key] }),
1738
3133
  [store, key]
1739
3134
  );
1740
- const getSnapshot = react.useCallback(() => store.get(key), [store, key]);
1741
- return react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
3135
+ const getSnapshot = React.useCallback(() => store.get(key), [store, key]);
3136
+ return React.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
1742
3137
  }
1743
3138
  function makeFieldHook(ns) {
1744
3139
  return function useNamespaceField(key) {
1745
3140
  const { funnel, store } = useFunnelCtx();
1746
3141
  const value = useStoreKey(store, `${NAMESPACE_PREFIX[ns]}.${key}`);
1747
- const set = react.useCallback(
3142
+ const set = React.useCallback(
1748
3143
  (v) => funnel[ns].set({ [key]: v }),
1749
3144
  [funnel, key]
1750
3145
  );
@@ -1760,10 +3155,12 @@ function useField(path) {
1760
3155
  const key = path.slice(dot + 1);
1761
3156
  const { funnel, store } = useFunnelCtx();
1762
3157
  if (!(ns in NAMESPACE_PREFIX)) {
1763
- throw new Error(`useField: "${path}" is not a writable namespace (user/responses/data)`);
3158
+ throw new Error(
3159
+ `useField: "${path}" is not a writable namespace (user/responses/data)`
3160
+ );
1764
3161
  }
1765
3162
  const value = useStoreKey(store, `${NAMESPACE_PREFIX[ns]}.${key}`);
1766
- const set = react.useCallback(
3163
+ const set = React.useCallback(
1767
3164
  (v) => funnel[ns].set({ [key]: v }),
1768
3165
  [funnel, ns, key]
1769
3166
  );
@@ -1789,11 +3186,404 @@ function useSystem() {
1789
3186
  }
1790
3187
  function useContextValue(path) {
1791
3188
  const ctx = useContextObject();
1792
- return react.useMemo(
1793
- () => path.split(".").reduce((acc, k) => acc?.[k], ctx),
3189
+ return React.useMemo(
3190
+ () => path.split(".").reduce(
3191
+ (acc, k) => acc?.[k],
3192
+ ctx
3193
+ ),
1794
3194
  [ctx, path]
1795
3195
  );
1796
3196
  }
3197
+
3198
+ // src/tracking/snapshotPersistence.ts
3199
+ function createSnapshotPersistence(opts) {
3200
+ const baseDelay = opts.baseDelayMs ?? 1e3;
3201
+ const maxDelay = opts.maxDelayMs ?? 3e4;
3202
+ const setT = opts.timers?.setTimeout ?? ((fn, ms) => setTimeout(fn, ms));
3203
+ const clearT = opts.timers?.clearTimeout ?? ((h) => clearTimeout(h));
3204
+ const rand = opts.random ?? Math.random;
3205
+ let pending = null;
3206
+ let seq = 0;
3207
+ let pendingSeq = 0;
3208
+ let inFlight = false;
3209
+ let attempt = 0;
3210
+ let retryTimer = null;
3211
+ let disposed = false;
3212
+ let fatalSeq = -1;
3213
+ const clearRetry = () => {
3214
+ if (retryTimer !== null) {
3215
+ clearT(retryTimer);
3216
+ retryTimer = null;
3217
+ }
3218
+ };
3219
+ const buildBody = (snapshot, s) => JSON.stringify({ sessionId: opts.getSessionId(), userData: snapshot, seq: s });
3220
+ const trySend = () => {
3221
+ if (disposed || inFlight || pending === null) return;
3222
+ if (retryTimer !== null) return;
3223
+ const sessionId = opts.getSessionId();
3224
+ if (!sessionId) return;
3225
+ if (typeof navigator !== "undefined" && navigator.onLine === false) return;
3226
+ if (pendingSeq === fatalSeq) return;
3227
+ const snapshot = pending;
3228
+ const sentSeq = pendingSeq;
3229
+ const body = buildBody(snapshot, sentSeq);
3230
+ inFlight = true;
3231
+ void fetch(opts.url, {
3232
+ method: "POST",
3233
+ headers: { "content-type": "application/json" },
3234
+ body,
3235
+ keepalive: true
3236
+ }).then((res) => {
3237
+ inFlight = false;
3238
+ if (disposed) return;
3239
+ if (res.ok) {
3240
+ attempt = 0;
3241
+ if (pendingSeq === sentSeq) pending = null;
3242
+ else trySend();
3243
+ return;
3244
+ }
3245
+ if (res.status >= 400 && res.status < 500) {
3246
+ if (process.env.NODE_ENV !== "production") {
3247
+ console.warn(
3248
+ `[appfunnel] snapshot persist rejected ${res.status} (dropping snapshot; check session/payload)`
3249
+ );
3250
+ }
3251
+ fatalSeq = sentSeq;
3252
+ if (pendingSeq === sentSeq) pending = null;
3253
+ else trySend();
3254
+ return;
3255
+ }
3256
+ scheduleRetry();
3257
+ }).catch(() => {
3258
+ inFlight = false;
3259
+ if (disposed) return;
3260
+ scheduleRetry();
3261
+ });
3262
+ };
3263
+ const scheduleRetry = () => {
3264
+ if (disposed || pending === null) return;
3265
+ const exp = Math.min(maxDelay, baseDelay * 2 ** attempt);
3266
+ const delay2 = Math.round(exp * (0.5 + rand() * 0.5));
3267
+ attempt++;
3268
+ clearRetry();
3269
+ retryTimer = setT(() => {
3270
+ retryTimer = null;
3271
+ trySend();
3272
+ }, delay2);
3273
+ };
3274
+ const push = (snapshot) => {
3275
+ if (disposed) return;
3276
+ pending = snapshot;
3277
+ pendingSeq = ++seq;
3278
+ if (fatalSeq !== -1 && pendingSeq !== fatalSeq) fatalSeq = -1;
3279
+ if (retryTimer === null) trySend();
3280
+ };
3281
+ const flushBeacon = () => {
3282
+ if (disposed || pending === null) return;
3283
+ const sessionId = opts.getSessionId();
3284
+ if (!sessionId) return;
3285
+ const body = buildBody(pending, pendingSeq);
3286
+ let delivered = false;
3287
+ if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
3288
+ try {
3289
+ delivered = navigator.sendBeacon(
3290
+ opts.url,
3291
+ new Blob([body], { type: "application/json" })
3292
+ );
3293
+ } catch {
3294
+ delivered = false;
3295
+ }
3296
+ }
3297
+ if (!delivered) {
3298
+ try {
3299
+ void fetch(opts.url, {
3300
+ method: "POST",
3301
+ headers: { "content-type": "application/json" },
3302
+ body,
3303
+ keepalive: true
3304
+ }).catch(() => {
3305
+ });
3306
+ delivered = true;
3307
+ } catch {
3308
+ delivered = false;
3309
+ }
3310
+ }
3311
+ if (delivered) pending = null;
3312
+ };
3313
+ const onHidden = () => {
3314
+ if (typeof document !== "undefined" && document.visibilityState === "hidden") {
3315
+ flushBeacon();
3316
+ }
3317
+ };
3318
+ const onPageHide = () => flushBeacon();
3319
+ const onOnline = () => trySend();
3320
+ if (typeof window !== "undefined" && typeof window.addEventListener === "function") {
3321
+ try {
3322
+ document.addEventListener("visibilitychange", onHidden);
3323
+ window.addEventListener("pagehide", onPageHide);
3324
+ window.addEventListener("online", onOnline);
3325
+ } catch {
3326
+ }
3327
+ }
3328
+ const dispose = () => {
3329
+ disposed = true;
3330
+ clearRetry();
3331
+ pending = null;
3332
+ if (typeof window !== "undefined" && typeof window.removeEventListener === "function") {
3333
+ try {
3334
+ document.removeEventListener("visibilitychange", onHidden);
3335
+ window.removeEventListener("pagehide", onPageHide);
3336
+ window.removeEventListener("online", onOnline);
3337
+ } catch {
3338
+ }
3339
+ }
3340
+ };
3341
+ return { push, flushBeacon, dispose };
3342
+ }
3343
+
3344
+ // src/tracking/platformTracker.ts
3345
+ function validClickIds(acq) {
3346
+ return Object.entries(acq.clickIds ?? {}).filter(
3347
+ (e) => typeof e[1] === "string" && e[1] !== ""
3348
+ );
3349
+ }
3350
+ function acquisitionQueryBag(acq) {
3351
+ const q = {};
3352
+ const utm = acq.utm ?? {};
3353
+ for (const [k, v] of Object.entries({
3354
+ utm_source: utm.source,
3355
+ utm_medium: utm.medium,
3356
+ utm_campaign: utm.campaign,
3357
+ utm_term: utm.term,
3358
+ utm_content: utm.content
3359
+ })) if (v) q[k] = v;
3360
+ for (const [k, v] of validClickIds(acq)) q[k] = v;
3361
+ if (acq.cookies?.fbp) q._fbp = acq.cookies.fbp;
3362
+ if (acq.cookies?.fbc) q._fbc = acq.cookies.fbc;
3363
+ return q;
3364
+ }
3365
+ function acquisitionAdAttribution(acq) {
3366
+ const a = {};
3367
+ for (const [k, v] of validClickIds(acq)) a[k] = v;
3368
+ if (acq.cookies?.fbp) a.fbp = acq.cookies.fbp;
3369
+ if (acq.cookies?.fbc) a.fbc = acq.cookies.fbc;
3370
+ else if (acq.clickIds?.fbclid) a.fbc = `fb.1.${Date.now()}.${acq.clickIds.fbclid}`;
3371
+ return a;
3372
+ }
3373
+ function createPlatformTracker(cfg) {
3374
+ if (typeof window === "undefined" || cfg.mode === "test") return createConsoleTracker();
3375
+ const base = cfg.apiBase.replace(/\/+$/, "");
3376
+ const url = `${base}/campaign/${encodeURIComponent(cfg.campaignId)}/event`;
3377
+ const landingUrl = window.location.href;
3378
+ const sidKey = `af_sid:${cfg.campaignId}`;
3379
+ let sessionId = null;
3380
+ try {
3381
+ sessionId = window.sessionStorage.getItem(sidKey);
3382
+ } catch {
3383
+ }
3384
+ if (!sessionId && cfg.sessionId) sessionId = cfg.sessionId;
3385
+ let acquisition = null;
3386
+ let acquisitionSent = false;
3387
+ let chain = Promise.resolve();
3388
+ const send = async (event, data, userData) => {
3389
+ let mergedUserData = landingUrl ? { landingUrl, ...userData } : userData;
3390
+ let adAttribution;
3391
+ let attachingAcquisition = false;
3392
+ if (acquisition && !acquisitionSent) {
3393
+ attachingAcquisition = true;
3394
+ const query = acquisitionQueryBag(acquisition);
3395
+ if (Object.keys(query).length) mergedUserData = { ...mergedUserData, query };
3396
+ const ad = acquisitionAdAttribution(acquisition);
3397
+ if (Object.keys(ad).length) adAttribution = ad;
3398
+ }
3399
+ const body = {
3400
+ campaignId: cfg.campaignId,
3401
+ funnelId: cfg.funnelId,
3402
+ event,
3403
+ sessionId: sessionId ?? void 0,
3404
+ visitorId: cfg.visitorId ?? void 0,
3405
+ // Campaign route stamp (campaign-routing pivot): ingestion reads this ONLY at session create
3406
+ // (WebFunnelSession.campaignRouteId, snapshot semantics) — carried on every event but ignored
3407
+ // once the session exists. Undefined ⇒ default funnel / no route matched.
3408
+ campaignRouteId: cfg.route?.id ?? void 0,
3409
+ eventId: typeof data?.eventId === "string" ? data.eventId : void 0,
3410
+ data: data ?? {},
3411
+ metadata: {
3412
+ language: typeof navigator !== "undefined" ? navigator.language : void 0,
3413
+ screen: typeof window.screen !== "undefined" ? `${window.screen.width}x${window.screen.height}` : void 0,
3414
+ timezone: (() => {
3415
+ try {
3416
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
3417
+ } catch {
3418
+ return void 0;
3419
+ }
3420
+ })(),
3421
+ referrer: typeof document !== "undefined" && document.referrer ? document.referrer : void 0
3422
+ },
3423
+ ...mergedUserData ? { userData: mergedUserData } : {},
3424
+ ...adAttribution ? { adAttribution } : {}
3425
+ };
3426
+ const res = await fetch(url, {
3427
+ method: "POST",
3428
+ headers: { "content-type": "application/json" },
3429
+ body: JSON.stringify(body),
3430
+ keepalive: true
3431
+ // survives page.exit / unload
3432
+ });
3433
+ if (attachingAcquisition) acquisitionSent = true;
3434
+ const json = await res.json().catch(() => null);
3435
+ if (json?.sessionId && json.sessionId !== sessionId) {
3436
+ sessionId = json.sessionId;
3437
+ try {
3438
+ window.sessionStorage.setItem(sidKey, sessionId);
3439
+ } catch {
3440
+ }
3441
+ }
3442
+ };
3443
+ const enqueue = (event, data, userData) => {
3444
+ const task = () => send(event, data, userData).catch((e) => {
3445
+ if (process.env.NODE_ENV !== "production") console.warn(`[appfunnel] track failed (${event}):`, e);
3446
+ });
3447
+ if (!sessionId) {
3448
+ chain = chain.then(task);
3449
+ } else {
3450
+ void task();
3451
+ }
3452
+ };
3453
+ const VARS_DEBOUNCE_MS = 800;
3454
+ const dataUrl = `${base}/campaign/${encodeURIComponent(cfg.campaignId)}/event/session/data`;
3455
+ let pendingVars = null;
3456
+ let varsTimer = null;
3457
+ const persistence = createSnapshotPersistence({
3458
+ url: dataUrl,
3459
+ getSessionId: () => sessionId
3460
+ });
3461
+ const flushVars = () => {
3462
+ if (varsTimer) {
3463
+ clearTimeout(varsTimer);
3464
+ varsTimer = null;
3465
+ }
3466
+ if (!pendingVars) return;
3467
+ const snapshot = pendingVars;
3468
+ pendingVars = null;
3469
+ persistence.push(snapshot);
3470
+ if (!sessionId) chain = chain.then(() => {
3471
+ persistence.push(snapshot);
3472
+ });
3473
+ };
3474
+ const drainOnExit = () => {
3475
+ if (pendingVars) {
3476
+ flushVars();
3477
+ persistence.flushBeacon();
3478
+ }
3479
+ };
3480
+ try {
3481
+ window.addEventListener("visibilitychange", drainOnExit);
3482
+ window.addEventListener("pagehide", drainOnExit);
3483
+ } catch {
3484
+ }
3485
+ if (cfg.experiment?.id && cfg.experiment.variant) {
3486
+ enqueue("experiment.exposure", exposurePayload(cfg.experiment.id, cfg.experiment.variant, cfg.experiment.version));
3487
+ }
3488
+ return {
3489
+ track: (event, data, userData) => enqueue(event, data, userData),
3490
+ identify: (email, eventId) => enqueue("user.registered", { email, ...eventId ? { eventId } : {} }),
3491
+ setVariables: (variables) => {
3492
+ pendingVars = variables;
3493
+ if (varsTimer) clearTimeout(varsTimer);
3494
+ varsTimer = setTimeout(flushVars, VARS_DEBOUNCE_MS);
3495
+ },
3496
+ setAcquisition: (acq) => {
3497
+ acquisition = acq;
3498
+ }
3499
+ };
3500
+ }
3501
+
3502
+ // src/flow/mount.tsx
3503
+ var AssetsContext = React.createContext({});
3504
+ function AssetsProvider({
3505
+ assets,
3506
+ children
3507
+ }) {
3508
+ return React.createElement(AssetsContext.Provider, { value: assets ?? {} }, children);
3509
+ }
3510
+ function useAssets() {
3511
+ return React.useContext(AssetsContext);
3512
+ }
3513
+ function useAsset(name) {
3514
+ return React.useContext(AssetsContext)[name];
3515
+ }
3516
+ function createFunnelTree({
3517
+ config,
3518
+ pages,
3519
+ layout,
3520
+ checkoutDriver,
3521
+ messages
3522
+ }) {
3523
+ return function tree(opts) {
3524
+ const mode = opts.mode ?? "live";
3525
+ const embedEnabled = opts.embed ?? false;
3526
+ const tracker = opts.tracking ? createPlatformTracker({
3527
+ ...opts.tracking,
3528
+ visitorId: opts.visitorId,
3529
+ sessionId: opts.sessionId,
3530
+ mode
3531
+ }) : void 0;
3532
+ const checkout = checkoutDriver && opts.tracking ? checkoutDriver({
3533
+ apiBase: opts.tracking.apiBase,
3534
+ campaignId: opts.tracking.campaignId,
3535
+ funnelId: opts.tracking.funnelId,
3536
+ mode,
3537
+ sessionId: opts.sessionId,
3538
+ visitorId: opts.visitorId
3539
+ }) : void 0;
3540
+ const layoutWithResume = function LayoutWithCheckoutResume({
3541
+ children
3542
+ }) {
3543
+ return React.createElement(
3544
+ React.Fragment,
3545
+ null,
3546
+ React.createElement(CheckoutResume, null),
3547
+ embedEnabled ? React.createElement(EmbedBridge, null) : null,
3548
+ layout ? React.createElement(layout, null, children) : children
3549
+ );
3550
+ };
3551
+ return React.createElement(AssetsProvider, {
3552
+ assets: opts.assets,
3553
+ children: React.createElement(FunnelProvider, {
3554
+ config,
3555
+ offerings: opts.offerings ?? [],
3556
+ messages: opts.messages ?? messages,
3557
+ locale: opts.locale,
3558
+ tracker,
3559
+ checkout,
3560
+ sessionValues: opts.sessionValues,
3561
+ deferSessionValues: opts.deferSessionValues,
3562
+ experiments: opts.experiments,
3563
+ context: {
3564
+ mode,
3565
+ visitorId: opts.visitorId ?? null,
3566
+ customerId: opts.customerId ?? null,
3567
+ sessionId: opts.sessionId ?? null
3568
+ },
3569
+ children: React.createElement(FunnelView, {
3570
+ pages,
3571
+ layout: layoutWithResume,
3572
+ initialKey: opts.initialKey,
3573
+ // FLASH-FREE HYDRATION (Option B, FIX 1): trust the SERVER-resolved initialKey instead of
3574
+ // re-running the entry guard. The CLIENT sets this via deferSessionValues (its deferred
3575
+ // snapshot is empty, so re-running the guard would bounce a qualified deep-link and
3576
+ // mismatch the markup). The build-time PRERENDER sets `trustInitialKey` explicitly so a
3577
+ // guarded page prerenders its OWN default-state markup (the renderer picks target-vs-bounce
3578
+ // at serve time). Live SSR fallback leaves both off → the guard runs against real values.
3579
+ trustInitialKey: opts.trustInitialKey ?? opts.deferSessionValues,
3580
+ prefetch: "off"
3581
+ // renderer background-modulepreloads all chunks
3582
+ })
3583
+ })
3584
+ });
3585
+ };
3586
+ }
1797
3587
  function Choice({
1798
3588
  bind,
1799
3589
  options,
@@ -1806,7 +3596,7 @@ function Choice({
1806
3596
  return options.map((opt) => {
1807
3597
  const o = typeof opt === "string" ? { value: opt } : opt;
1808
3598
  const selected = value === o.value;
1809
- return react.createElement(
3599
+ return React.createElement(
1810
3600
  "button",
1811
3601
  {
1812
3602
  key: o.value,
@@ -1825,7 +3615,7 @@ function Choice({
1825
3615
  }
1826
3616
  function Next({ children, onClick, ...rest }) {
1827
3617
  const { next } = useNavigation();
1828
- return react.createElement(
3618
+ return React.createElement(
1829
3619
  "button",
1830
3620
  {
1831
3621
  type: "button",
@@ -1841,7 +3631,7 @@ function Next({ children, onClick, ...rest }) {
1841
3631
  function Back({ children, onClick, ...rest }) {
1842
3632
  const { back, canGoBack } = useNavigation();
1843
3633
  if (!canGoBack) return null;
1844
- return react.createElement(
3634
+ return React.createElement(
1845
3635
  "button",
1846
3636
  {
1847
3637
  type: "button",
@@ -1894,7 +3684,7 @@ function Script({
1894
3684
  onLoad,
1895
3685
  onError
1896
3686
  }) {
1897
- react.useEffect(() => {
3687
+ React.useEffect(() => {
1898
3688
  if (typeof document === "undefined") return;
1899
3689
  const key = id ?? src;
1900
3690
  if (injected.has(key)) {
@@ -1933,159 +3723,202 @@ function Script({
1933
3723
  return null;
1934
3724
  }
1935
3725
 
1936
- // src/manifest/manifest.ts
1937
- function isVariantNode(page) {
1938
- return isVariantKey(page.key);
1939
- }
1940
- function serializeGate(when) {
1941
- if (!when) return void 0;
1942
- if (typeof when === "function") return { kind: "predicate" };
1943
- return { kind: "declarative", condition: when };
1944
- }
1945
- function compileManifest(input) {
1946
- const { funnel, pages } = input;
1947
- const keys = new Set(pages.map((p) => p.key));
1948
- const manifestPages = pages.map((p, index) => ({
1949
- id: p.key,
1950
- key: p.key,
1951
- type: p.meta?.type ?? "default",
1952
- slug: p.meta?.slug ?? p.key,
1953
- index,
1954
- variantOf: isVariantKey(p.key) ? parseSlotKey(p.key).slot : void 0
1955
- }));
1956
- const edges = [];
1957
- const badTargets = [];
1958
- const nextAnchor = (from) => {
1959
- for (let j = from + 1; j < pages.length; j++) {
1960
- if (!isVariantNode(pages[j])) return pages[j];
1961
- }
1962
- return void 0;
1963
- };
1964
- pages.forEach((p, i) => {
1965
- if (isVariantNode(p)) return;
1966
- const routes = p.meta?.next ?? [];
1967
- let hasUnconditional = false;
1968
- for (const route of routes) {
1969
- if (!keys.has(route.to)) {
1970
- badTargets.push({ from: p.key, to: route.to });
1971
- continue;
1972
- }
1973
- edges.push({ from: p.key, to: route.to, kind: "branch", condition: serializeGate(route.when) });
1974
- if (!route.when) hasUnconditional = true;
1975
- }
1976
- const next = nextAnchor(i);
1977
- if (!hasUnconditional && next && p.meta?.type !== "finish") {
1978
- edges.push({ from: p.key, to: next.key, kind: "linear" });
1979
- }
1980
- });
1981
- const startPage = pages.find((p) => !isVariantNode(p));
1982
- const start = startPage?.key ?? null;
1983
- const variantKeys = new Set(pages.filter(isVariantNode).map((p) => p.key));
1984
- const validation = validate(manifestPages, edges, badTargets, start, variantKeys);
1985
- return {
1986
- id: funnel.id,
1987
- pages: manifestPages,
1988
- flow: { start, nodes: manifestPages.map((p) => p.id), edges },
1989
- products: funnel.products ?? [],
1990
- locales: funnel.locales,
1991
- validation
1992
- };
1993
- }
1994
- function validate(pages, edges, badTargets, start, variantKeys) {
1995
- const outgoing = /* @__PURE__ */ new Map();
1996
- for (const e of edges) outgoing.set(e.from, (outgoing.get(e.from) ?? 0) + 1);
1997
- const deadEnds = pages.filter((p) => p.type !== "finish" && !variantKeys.has(p.id) && !(outgoing.get(p.id) > 0)).map((p) => p.id);
1998
- const reachable = /* @__PURE__ */ new Set();
1999
- if (start) {
2000
- const adj = /* @__PURE__ */ new Map();
2001
- for (const e of edges) {
2002
- const list = adj.get(e.from);
2003
- if (list) list.push(e.to);
2004
- else adj.set(e.from, [e.to]);
2005
- }
2006
- const stack = [start];
2007
- while (stack.length) {
2008
- const id = stack.pop();
2009
- if (reachable.has(id)) continue;
2010
- reachable.add(id);
2011
- for (const to of adj.get(id) ?? []) stack.push(to);
2012
- }
2013
- }
2014
- const unreachable = pages.filter((p) => !variantKeys.has(p.id) && !reachable.has(p.id)).map((p) => p.id);
2015
- const missingEmailCapture = !pages.some((p) => p.type === "email-capture");
2016
- return {
2017
- ok: deadEnds.length === 0 && unreachable.length === 0 && badTargets.length === 0 && !missingEmailCapture,
2018
- deadEnds,
2019
- unreachable,
2020
- badTargets,
2021
- missingEmailCapture
2022
- };
2023
- }
2024
-
2025
- Object.defineProperty(exports, "toast", {
3726
+ Object.defineProperty(exports, "assignVariant", {
3727
+ enumerable: true,
3728
+ get: function () { return chunkRKPRMTSU_cjs.assignVariant; }
3729
+ });
3730
+ Object.defineProperty(exports, "bucketingSeed", {
3731
+ enumerable: true,
3732
+ get: function () { return chunkRKPRMTSU_cjs.bucketingSeed; }
3733
+ });
3734
+ Object.defineProperty(exports, "buildCatalog", {
3735
+ enumerable: true,
3736
+ get: function () { return chunkRKPRMTSU_cjs.buildCatalog; }
3737
+ });
3738
+ Object.defineProperty(exports, "compileManifest", {
3739
+ enumerable: true,
3740
+ get: function () { return chunkRKPRMTSU_cjs.compileManifest; }
3741
+ });
3742
+ Object.defineProperty(exports, "currencyExponent", {
3743
+ enumerable: true,
3744
+ get: function () { return chunkRKPRMTSU_cjs.currencyExponent; }
3745
+ });
3746
+ Object.defineProperty(exports, "defineFunnel", {
3747
+ enumerable: true,
3748
+ get: function () { return chunkRKPRMTSU_cjs.defineFunnel; }
3749
+ });
3750
+ Object.defineProperty(exports, "definePage", {
3751
+ enumerable: true,
3752
+ get: function () { return chunkRKPRMTSU_cjs.definePage; }
3753
+ });
3754
+ Object.defineProperty(exports, "entryGuard", {
3755
+ enumerable: true,
3756
+ get: function () { return chunkRKPRMTSU_cjs.entryGuard; }
3757
+ });
3758
+ Object.defineProperty(exports, "evaluateCondition", {
3759
+ enumerable: true,
3760
+ get: function () { return chunkRKPRMTSU_cjs.evaluateCondition; }
3761
+ });
3762
+ Object.defineProperty(exports, "evaluateGate", {
3763
+ enumerable: true,
3764
+ get: function () { return chunkRKPRMTSU_cjs.evaluateGate; }
3765
+ });
3766
+ Object.defineProperty(exports, "expectedPathLength", {
3767
+ enumerable: true,
3768
+ get: function () { return chunkRKPRMTSU_cjs.expectedPathLength; }
3769
+ });
3770
+ Object.defineProperty(exports, "fnv1a", {
3771
+ enumerable: true,
3772
+ get: function () { return chunkRKPRMTSU_cjs.fnv1a; }
3773
+ });
3774
+ Object.defineProperty(exports, "formatMoney", {
3775
+ enumerable: true,
3776
+ get: function () { return chunkRKPRMTSU_cjs.formatMoney; }
3777
+ });
3778
+ Object.defineProperty(exports, "formatOffering", {
3779
+ enumerable: true,
3780
+ get: function () { return chunkRKPRMTSU_cjs.formatOffering; }
3781
+ });
3782
+ Object.defineProperty(exports, "groupProgress", {
3783
+ enumerable: true,
3784
+ get: function () { return chunkRKPRMTSU_cjs.groupProgress; }
3785
+ });
3786
+ Object.defineProperty(exports, "hashToUnit", {
3787
+ enumerable: true,
3788
+ get: function () { return chunkRKPRMTSU_cjs.hashToUnit; }
3789
+ });
3790
+ Object.defineProperty(exports, "isRtl", {
3791
+ enumerable: true,
3792
+ get: function () { return chunkRKPRMTSU_cjs.isRtl; }
3793
+ });
3794
+ Object.defineProperty(exports, "isVariantKey", {
3795
+ enumerable: true,
3796
+ get: function () { return chunkRKPRMTSU_cjs.isVariantKey; }
3797
+ });
3798
+ Object.defineProperty(exports, "nextPage", {
3799
+ enumerable: true,
3800
+ get: function () { return chunkRKPRMTSU_cjs.nextPage; }
3801
+ });
3802
+ Object.defineProperty(exports, "outgoingKeys", {
3803
+ enumerable: true,
3804
+ get: function () { return chunkRKPRMTSU_cjs.outgoingKeys; }
3805
+ });
3806
+ Object.defineProperty(exports, "pageMeta", {
3807
+ enumerable: true,
3808
+ get: function () { return chunkRKPRMTSU_cjs.pageMeta; }
3809
+ });
3810
+ Object.defineProperty(exports, "parseSlotKey", {
3811
+ enumerable: true,
3812
+ get: function () { return chunkRKPRMTSU_cjs.parseSlotKey; }
3813
+ });
3814
+ Object.defineProperty(exports, "pickByWeight", {
3815
+ enumerable: true,
3816
+ get: function () { return chunkRKPRMTSU_cjs.pickByWeight; }
3817
+ });
3818
+ Object.defineProperty(exports, "resolveExperiments", {
3819
+ enumerable: true,
3820
+ get: function () { return chunkRKPRMTSU_cjs.resolveExperiments; }
3821
+ });
3822
+ Object.defineProperty(exports, "resolveLocale", {
3823
+ enumerable: true,
3824
+ get: function () { return chunkRKPRMTSU_cjs.resolveLocale; }
3825
+ });
3826
+ Object.defineProperty(exports, "resolveOffering", {
3827
+ enumerable: true,
3828
+ get: function () { return chunkRKPRMTSU_cjs.resolveOffering; }
3829
+ });
3830
+ Object.defineProperty(exports, "resolveRoute", {
3831
+ enumerable: true,
3832
+ get: function () { return chunkRKPRMTSU_cjs.resolveRoute; }
3833
+ });
3834
+ Object.defineProperty(exports, "INLINE_SURFACES", {
3835
+ enumerable: true,
3836
+ get: function () { return chunk7JLOF6CJ_cjs.INLINE_SURFACES; }
3837
+ });
3838
+ Object.defineProperty(exports, "PROVIDER_PROFILES", {
3839
+ enumerable: true,
3840
+ get: function () { return chunk7JLOF6CJ_cjs.PROVIDER_PROFILES; }
3841
+ });
3842
+ Object.defineProperty(exports, "checkoutError", {
3843
+ enumerable: true,
3844
+ get: function () { return chunk7JLOF6CJ_cjs.checkoutError; }
3845
+ });
3846
+ Object.defineProperty(exports, "isInlineSurface", {
3847
+ enumerable: true,
3848
+ get: function () { return chunk7JLOF6CJ_cjs.isInlineSurface; }
3849
+ });
3850
+ Object.defineProperty(exports, "isMerchantOfRecord", {
3851
+ enumerable: true,
3852
+ get: function () { return chunk7JLOF6CJ_cjs.isMerchantOfRecord; }
3853
+ });
3854
+ Object.defineProperty(exports, "isOrchestrator", {
3855
+ enumerable: true,
3856
+ get: function () { return chunk7JLOF6CJ_cjs.isOrchestrator; }
3857
+ });
3858
+ Object.defineProperty(exports, "isRenderableSurface", {
3859
+ enumerable: true,
3860
+ get: function () { return chunk7JLOF6CJ_cjs.isRenderableSurface; }
3861
+ });
3862
+ Object.defineProperty(exports, "renderableSurfaces", {
3863
+ enumerable: true,
3864
+ get: function () { return chunk7JLOF6CJ_cjs.renderableSurfaces; }
3865
+ });
3866
+ Object.defineProperty(exports, "resolveSurface", {
3867
+ enumerable: true,
3868
+ get: function () { return chunk7JLOF6CJ_cjs.resolveSurface; }
3869
+ });
3870
+ Object.defineProperty(exports, "surfacesFor", {
3871
+ enumerable: true,
3872
+ get: function () { return chunk7JLOF6CJ_cjs.surfacesFor; }
3873
+ });
3874
+ Object.defineProperty(exports, "validateCheckout", {
3875
+ enumerable: true,
3876
+ get: function () { return chunk7JLOF6CJ_cjs.validateCheckout; }
3877
+ });
3878
+ Object.defineProperty(exports, "validateUpsell", {
2026
3879
  enumerable: true,
2027
- get: function () { return sonner.toast; }
3880
+ get: function () { return chunk7JLOF6CJ_cjs.validateUpsell; }
2028
3881
  });
3882
+ exports.AssetsContext = AssetsContext;
3883
+ exports.AssetsProvider = AssetsProvider;
2029
3884
  exports.Back = Back;
2030
3885
  exports.Checkout = Checkout;
3886
+ exports.CheckoutResume = CheckoutResume;
2031
3887
  exports.Choice = Choice;
2032
3888
  exports.EVENT_CATALOG = EVENT_CATALOG;
2033
3889
  exports.FunnelProvider = FunnelProvider;
2034
3890
  exports.FunnelToaster = FunnelToaster;
2035
3891
  exports.FunnelView = FunnelView;
2036
- exports.INLINE_SURFACES = INLINE_SURFACES;
2037
3892
  exports.Modal = Modal;
2038
3893
  exports.Next = Next;
2039
- exports.PROVIDER_PROFILES = PROVIDER_PROFILES;
2040
3894
  exports.Script = Script;
2041
3895
  exports.Sheet = Sheet;
2042
3896
  exports.Upsell = Upsell;
2043
3897
  exports.VariableStore = VariableStore;
2044
- exports.assignVariant = assignVariant;
2045
3898
  exports.attachBus = attachBus;
2046
- exports.bucketingSeed = bucketingSeed;
2047
3899
  exports.buildAcquisition = buildAcquisition;
2048
- exports.buildCatalog = buildCatalog;
2049
- exports.checkoutError = checkoutError;
2050
- exports.compileManifest = compileManifest;
3900
+ exports.buildContext = buildContext;
2051
3901
  exports.createBus = createBus;
2052
3902
  exports.createConsoleTracker = createConsoleTracker;
2053
3903
  exports.createFunnelStore = createFunnelStore;
3904
+ exports.createFunnelTree = createFunnelTree;
2054
3905
  exports.createMockDriver = createMockDriver;
2055
- exports.defineFunnel = defineFunnel;
3906
+ exports.createPlatformTracker = createPlatformTracker;
2056
3907
  exports.defineModal = defineModal;
2057
- exports.definePage = definePage;
2058
- exports.entryGuard = entryGuard;
2059
- exports.evaluateCondition = evaluateCondition;
2060
- exports.evaluateGate = evaluateGate;
2061
- exports.expectedPathLength = expectedPathLength;
2062
- exports.fnv1a = fnv1a;
2063
- exports.formatProduct = formatProduct;
2064
- exports.hashToUnit = hashToUnit;
3908
+ exports.driverWithEmail = driverWithEmail;
2065
3909
  exports.hideModal = hideModal;
2066
3910
  exports.integrationEvents = integrationEvents;
2067
- exports.isInlineSurface = isInlineSurface;
2068
3911
  exports.isIntegrationEvent = isIntegrationEvent;
2069
- exports.isMerchantOfRecord = isMerchantOfRecord;
2070
- exports.isOrchestrator = isOrchestrator;
2071
- exports.isRtl = isRtl;
2072
3912
  exports.isTrackableEvent = isTrackableEvent;
2073
- exports.isVariantKey = isVariantKey;
2074
3913
  exports.newEventId = newEventId;
2075
- exports.nextPage = nextPage;
2076
- exports.outgoingKeys = outgoingKeys;
2077
- exports.pageMeta = pageMeta;
2078
- exports.parseSlotKey = parseSlotKey;
2079
- exports.pickByWeight = pickByWeight;
2080
3914
  exports.registerModal = registerModal;
2081
3915
  exports.removeModal = removeModal;
2082
- exports.resolveExperiments = resolveExperiments;
2083
- exports.resolveLocale = resolveLocale;
2084
- exports.resolveProduct = resolveProduct;
2085
- exports.resolveRoute = resolveRoute;
2086
3916
  exports.showModal = showModal;
2087
- exports.surfacesFor = surfacesFor;
3917
+ exports.toast = toast;
2088
3918
  exports.unregisterModal = unregisterModal;
3919
+ exports.useActiveLocale = useActiveLocale;
3920
+ exports.useAsset = useAsset;
3921
+ exports.useAssets = useAssets;
2089
3922
  exports.useCheckout = useCheckout;
2090
3923
  exports.useClickIds = useClickIds;
2091
3924
  exports.useContextValue = useContextValue;
@@ -2094,21 +3927,19 @@ exports.useDevice = useDevice;
2094
3927
  exports.useExperiment = useExperiment;
2095
3928
  exports.useField = useField;
2096
3929
  exports.useFunnel = useFunnel;
3930
+ exports.useGroupProgress = useGroupProgress;
2097
3931
  exports.useLocale = useLocale;
2098
3932
  exports.useModal = useModal;
2099
3933
  exports.useNavigation = useNavigation;
3934
+ exports.useOffering = useOffering;
3935
+ exports.useOfferings = useOfferings;
2100
3936
  exports.usePage = usePage;
2101
- exports.useProduct = useProduct;
2102
- exports.useProducts = useProducts;
2103
3937
  exports.useResponse = useResponse;
2104
3938
  exports.useSystem = useSystem;
2105
3939
  exports.useTracker = useTracker;
2106
3940
  exports.useTranslation = useTranslation;
2107
3941
  exports.useUserAttribute = useUserAttribute;
2108
3942
  exports.useUtm = useUtm;
2109
- exports.validateCheckout = validateCheckout;
2110
- exports.validateExperiments = validateExperiments;
2111
- exports.validateUpsell = validateUpsell;
2112
3943
  exports.withBus = withBus;
2113
3944
  //# sourceMappingURL=index.cjs.map
2114
3945
  //# sourceMappingURL=index.cjs.map