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

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 (43) hide show
  1. package/dist/capabilities-7_hy5f5G.d.cts +114 -0
  2. package/dist/capabilities-7_hy5f5G.d.ts +114 -0
  3. package/dist/checkout-7Dy6IedP.d.ts +320 -0
  4. package/dist/checkout-Dz8cGkB_.d.cts +320 -0
  5. package/dist/chunk-AKO6XKXP.js +466 -0
  6. package/dist/chunk-AKO6XKXP.js.map +1 -0
  7. package/dist/chunk-CY4VBSMX.cjs +106 -0
  8. package/dist/chunk-CY4VBSMX.cjs.map +1 -0
  9. package/dist/chunk-JSRKA375.cjs +497 -0
  10. package/dist/chunk-JSRKA375.cjs.map +1 -0
  11. package/dist/chunk-LJYLGLFS.cjs +153 -0
  12. package/dist/chunk-LJYLGLFS.cjs.map +1 -0
  13. package/dist/chunk-M6U3FNRW.js +99 -0
  14. package/dist/chunk-M6U3FNRW.js.map +1 -0
  15. package/dist/chunk-YY375F2B.js +140 -0
  16. package/dist/chunk-YY375F2B.js.map +1 -0
  17. package/dist/driver-paddle.cjs +814 -0
  18. package/dist/driver-paddle.cjs.map +1 -0
  19. package/dist/driver-paddle.d.cts +10 -0
  20. package/dist/driver-paddle.d.ts +10 -0
  21. package/dist/driver-paddle.js +811 -0
  22. package/dist/driver-paddle.js.map +1 -0
  23. package/dist/driver-stripe.cjs +2253 -0
  24. package/dist/driver-stripe.cjs.map +1 -0
  25. package/dist/driver-stripe.d.cts +8 -0
  26. package/dist/driver-stripe.d.ts +8 -0
  27. package/dist/driver-stripe.js +2247 -0
  28. package/dist/driver-stripe.js.map +1 -0
  29. package/dist/index.cjs +1962 -811
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +183 -933
  32. package/dist/index.d.ts +183 -933
  33. package/dist/index.js +1683 -653
  34. package/dist/index.js.map +1 -1
  35. package/dist/manifest-Cr2y1op6.d.cts +814 -0
  36. package/dist/manifest-Cr2y1op6.d.ts +814 -0
  37. package/dist/manifest-entry.cjs +312 -0
  38. package/dist/manifest-entry.cjs.map +1 -0
  39. package/dist/manifest-entry.d.cts +209 -0
  40. package/dist/manifest-entry.d.ts +209 -0
  41. package/dist/manifest-entry.js +198 -0
  42. package/dist/manifest-entry.js.map +1 -0
  43. package/package.json +37 -4
package/dist/index.js CHANGED
@@ -1,9 +1,10 @@
1
- import { createContext, createElement, useCallback, useContext, useMemo, lazy, useState, useRef, useEffect, useTransition, Suspense, Fragment, isValidElement, cloneElement, useReducer, useSyncExternalStore } from 'react';
2
- import { createPortal } from 'react-dom';
3
- import { Toaster } from 'sonner';
4
- export { toast } from 'sonner';
5
-
6
- // src/state/namespaces.ts
1
+ import { isRtl, formatProduct, bucketingSeed, resolveExperiments, entryGuard, evaluateGate, expectedPathLength, nextPage, outgoingKeys, buildCatalog, resolveLocale } from './chunk-AKO6XKXP.js';
2
+ export { assignVariant, bucketingSeed, buildCatalog, compileManifest, currencyExponent, defineFunnel, definePage, entryGuard, evaluateCondition, evaluateGate, expectedPathLength, fnv1a, formatMoney, formatProduct, hashToUnit, isRtl, isVariantKey, nextPage, outgoingKeys, pageMeta, parseSlotKey, pickByWeight, resolveExperiments, resolveLocale, resolveProduct, resolveRoute, validateExperiments } from './chunk-AKO6XKXP.js';
3
+ import { checkoutError, isInlineSurface, PROVIDER_PROFILES } from './chunk-YY375F2B.js';
4
+ export { INLINE_SURFACES, PROVIDER_PROFILES, checkoutError, isInlineSurface, isMerchantOfRecord, isOrchestrator, surfacesFor, validateCheckout, validateUpsell } from './chunk-YY375F2B.js';
5
+ import React, { createContext, createElement, useCallback, useContext, useMemo, lazy, useRef, useState, useEffect, useTransition, Suspense, Fragment, isValidElement, cloneElement, useReducer, useSyncExternalStore } from 'react';
6
+ import ReactDOM, { createPortal } from 'react-dom';
7
+ import { jsx } from 'react/jsx-runtime';
7
8
 
8
9
  // src/state/variableStore.ts
9
10
  var VariableStore = class {
@@ -257,8 +258,12 @@ function collectClickIds() {
257
258
  }
258
259
  function readCookie(name) {
259
260
  if (typeof document === "undefined") return void 0;
260
- const m = document.cookie.match(new RegExp("(?:^|; )" + name + "=([^;]*)"));
261
- return m ? decodeURIComponent(m[1]) : void 0;
261
+ try {
262
+ const m = document.cookie.match(new RegExp("(?:^|; )" + name + "=([^;]*)"));
263
+ return m ? decodeURIComponent(m[1]) : void 0;
264
+ } catch {
265
+ return void 0;
266
+ }
262
267
  }
263
268
  function buildAcquisition(context) {
264
269
  return {
@@ -271,41 +276,44 @@ function buildAcquisition(context) {
271
276
  }
272
277
  function buildContext(opts = {}) {
273
278
  const raw = detect();
274
- const now = opts.now ?? 0;
279
+ const now2 = opts.now ?? 0;
275
280
  const referrer = typeof document !== "undefined" ? document.referrer : "";
276
- const base2 = {
281
+ const base = {
277
282
  device: raw.device,
278
283
  browser: raw.browser,
279
284
  os: raw.os,
280
285
  locale: raw.locale,
281
286
  utm: collectUtm(),
282
287
  clickIds: collectClickIds(),
283
- page: { key: "", slug: "", index: 0, total: 0, progressPercentage: 0, startedAt: now },
284
- session: { id: opts.sessionId ?? null, startedAt: now, referrer },
288
+ page: { key: "", slug: "", index: 0, total: 0, progressPercentage: 0, startedAt: now2 },
289
+ session: { id: opts.sessionId ?? null, startedAt: now2, referrer },
285
290
  identity: { customerId: opts.customerId ?? null, visitorId: opts.visitorId ?? null },
286
291
  system: {
287
292
  funnelId: opts.funnelId ?? "",
288
293
  campaignId: opts.campaignId ?? "",
289
294
  mode: opts.mode ?? "live",
290
- now
295
+ now: now2
291
296
  }
292
297
  };
293
- if (!opts.overrides) return base2;
298
+ if (!opts.overrides) return base;
294
299
  return {
295
- ...base2,
300
+ ...base,
296
301
  ...opts.overrides,
297
- device: { ...base2.device, ...opts.overrides.device },
298
- browser: { ...base2.browser, ...opts.overrides.browser },
299
- os: { ...base2.os, ...opts.overrides.os },
300
- locale: { ...base2.locale, ...opts.overrides.locale },
301
- utm: { ...base2.utm, ...opts.overrides.utm },
302
- clickIds: { ...base2.clickIds, ...opts.overrides.clickIds },
303
- page: { ...base2.page, ...opts.overrides.page },
304
- session: { ...base2.session, ...opts.overrides.session },
305
- identity: { ...base2.identity, ...opts.overrides.identity },
306
- system: { ...base2.system, ...opts.overrides.system }
302
+ device: { ...base.device, ...opts.overrides.device },
303
+ browser: { ...base.browser, ...opts.overrides.browser },
304
+ os: { ...base.os, ...opts.overrides.os },
305
+ locale: { ...base.locale, ...opts.overrides.locale },
306
+ utm: { ...base.utm, ...opts.overrides.utm },
307
+ clickIds: { ...base.clickIds, ...opts.overrides.clickIds },
308
+ page: { ...base.page, ...opts.overrides.page },
309
+ session: { ...base.session, ...opts.overrides.session },
310
+ identity: { ...base.identity, ...opts.overrides.identity },
311
+ system: { ...base.system, ...opts.overrides.system }
307
312
  };
308
313
  }
314
+ function exposurePayload(experimentId, variant, version) {
315
+ return { experimentId, variant, ...version !== void 0 ? { version } : {} };
316
+ }
309
317
  var _eventIdCounter = 0;
310
318
  function newEventId() {
311
319
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
@@ -344,7 +352,7 @@ function useTracker() {
344
352
 
345
353
  // src/tracking/bus.ts
346
354
  var MAX_HISTORY = 250;
347
- function createBus(accessors, now = () => Date.now()) {
355
+ function createBus(accessors, now2 = () => Date.now()) {
348
356
  const listeners = /* @__PURE__ */ new Map();
349
357
  const events = [];
350
358
  const on = (event, cb) => {
@@ -357,7 +365,7 @@ function createBus(accessors, now = () => Date.now()) {
357
365
  listeners.get(event)?.delete(cb);
358
366
  };
359
367
  const emit = (event, data) => {
360
- events.push({ event, data, ts: now() });
368
+ events.push({ event, data, ts: now2() });
361
369
  if (events.length > MAX_HISTORY) events.shift();
362
370
  for (const cb of listeners.get(event) ?? []) {
363
371
  try {
@@ -412,21 +420,6 @@ function attachBus(bus) {
412
420
  if (window.appfunnel === api) delete window.appfunnel;
413
421
  };
414
422
  }
415
- var RTL_LANGS = /* @__PURE__ */ new Set(["ar", "he", "fa", "ur", "ps", "sd", "dv", "yi"]);
416
- function isRtl(locale) {
417
- return RTL_LANGS.has(locale.split("-")[0]);
418
- }
419
- var base = (l) => l?.split("-")[0];
420
- function resolveLocale(config, detected, override) {
421
- const supported = config?.supported ?? (config?.default ? [config.default] : ["en"]);
422
- const def = config?.default ?? supported[0] ?? "en";
423
- const pick = (l) => {
424
- if (!l) return void 0;
425
- if (supported.includes(l)) return l;
426
- return supported.find((s) => base(s) === base(l));
427
- };
428
- return pick(override) ?? pick(detected) ?? def;
429
- }
430
423
  function interpolate(str, params) {
431
424
  if (!params) return str;
432
425
  return str.replace(/\{\{(\w+)\}\}/g, (_, k) => k in params ? String(params[k]) : `{{${k}}}`);
@@ -455,6 +448,9 @@ function LocaleProvider({
455
448
  children
456
449
  }) {
457
450
  const [locale, setLocale] = useState(() => resolveLocale(config, detected, override));
451
+ useEffect(() => {
452
+ setLocale(resolveLocale(config, detected, override));
453
+ }, [config, detected, override]);
458
454
  const value = useMemo(
459
455
  () => ({ locale, setLocale, config, catalog }),
460
456
  [locale, config, catalog]
@@ -483,7 +479,12 @@ function useTranslation() {
483
479
  return interpolate(raw, params);
484
480
  });
485
481
  fn.plural = (n, forms, params) => {
486
- const rule = new Intl.PluralRules(locale).select(n);
482
+ let rule;
483
+ try {
484
+ rule = new Intl.PluralRules(locale).select(n);
485
+ } catch {
486
+ rule = "other";
487
+ }
487
488
  const form = forms[rule] ?? forms.other ?? "";
488
489
  return interpolate(form.replace(/#/g, String(n)), params);
489
490
  };
@@ -491,9 +492,27 @@ function useTranslation() {
491
492
  }, [catalog, chain, locale]);
492
493
  const fmt = useMemo(
493
494
  () => ({
494
- number: (n, opts) => new Intl.NumberFormat(locale, opts).format(n),
495
- date: (d, opts) => new Intl.DateTimeFormat(locale, opts).format(d),
496
- percent: (n, opts) => new Intl.NumberFormat(locale, { style: "percent", ...opts }).format(n)
495
+ number: (n, opts) => {
496
+ try {
497
+ return new Intl.NumberFormat(locale, opts).format(n);
498
+ } catch {
499
+ return String(n);
500
+ }
501
+ },
502
+ date: (d, opts) => {
503
+ try {
504
+ return new Intl.DateTimeFormat(locale, opts).format(d);
505
+ } catch {
506
+ return String(d instanceof Date ? d.toISOString() : d);
507
+ }
508
+ },
509
+ percent: (n, opts) => {
510
+ try {
511
+ return new Intl.NumberFormat(locale, { style: "percent", ...opts }).format(n);
512
+ } catch {
513
+ return `${(n * 100).toFixed(0)}%`;
514
+ }
515
+ }
497
516
  }),
498
517
  [locale]
499
518
  );
@@ -508,84 +527,6 @@ function useTranslation() {
508
527
  }
509
528
 
510
529
  // src/commerce/catalog.ts
511
- var PERIOD_DAYS = {
512
- day: 1,
513
- week: 7,
514
- month: 30,
515
- year: 365,
516
- one_time: 0
517
- };
518
- function periodDays(interval, count) {
519
- return PERIOD_DAYS[interval] * count;
520
- }
521
- function intervalLabel(interval, count) {
522
- if (interval === "one_time") return { period: "one-time", periodly: "one-time" };
523
- if (interval === "month" && count === 3) return { period: "quarter", periodly: "quarterly" };
524
- if (interval === "month" && count === 6) return { period: "6 months", periodly: "semiannually" };
525
- if (interval === "week" && count === 2) return { period: "2 weeks", periodly: "biweekly" };
526
- if (count === 1) {
527
- const periodly = { day: "daily", week: "weekly", month: "monthly", year: "yearly" }[interval];
528
- return { period: interval, periodly };
529
- }
530
- return { period: `${count} ${interval}s`, periodly: `every ${count} ${interval}s` };
531
- }
532
- function resolveProduct(input) {
533
- const interval = input.interval ?? "one_time";
534
- const count = input.intervalCount ?? 1;
535
- const days = periodDays(interval, count);
536
- const perDayMinor = days > 0 ? input.amount / days : input.amount;
537
- const label = intervalLabel(interval, count);
538
- return {
539
- id: input.id,
540
- name: input.name ?? input.id,
541
- displayName: input.displayName ?? input.name ?? input.id,
542
- provider: input.provider ?? "stripe",
543
- currency: input.currency,
544
- priceMinor: input.amount,
545
- perDayMinor,
546
- perWeekMinor: perDayMinor * 7,
547
- perMonthMinor: perDayMinor * 30,
548
- perYearMinor: perDayMinor * 365,
549
- period: label.period,
550
- periodly: label.periodly,
551
- hasTrial: (input.trialDays ?? 0) > 0,
552
- trialDays: input.trialDays ?? 0,
553
- trialMinor: input.trialAmount ?? 0
554
- };
555
- }
556
- function buildCatalog(inputs) {
557
- return new Map(inputs.map((i) => [i.id, resolveProduct(i)]));
558
- }
559
- function formatMoney(minor, currency, locale) {
560
- const amount = minor / 100;
561
- const code = currency.toUpperCase();
562
- let formatted;
563
- try {
564
- formatted = new Intl.NumberFormat(locale, { style: "currency", currency: code }).format(amount);
565
- } catch {
566
- formatted = `${code} ${amount.toFixed(2)}`;
567
- }
568
- return { amount, minor, currency: code, formatted };
569
- }
570
- function formatProduct(r, locale) {
571
- const m = (minor) => formatMoney(minor, r.currency, locale);
572
- return {
573
- id: r.id,
574
- name: r.name,
575
- displayName: r.displayName,
576
- provider: r.provider,
577
- price: m(r.priceMinor),
578
- period: r.period,
579
- periodly: r.periodly,
580
- perDay: m(r.perDayMinor),
581
- perWeek: m(r.perWeekMinor),
582
- perMonth: m(r.perMonthMinor),
583
- perYear: m(r.perYearMinor),
584
- hasTrial: r.hasTrial,
585
- trialDays: r.trialDays,
586
- trialPrice: r.hasTrial ? m(r.trialMinor) : null
587
- };
588
- }
589
530
  var CatalogContext = createContext(null);
590
531
  function CatalogProvider({
591
532
  catalog,
@@ -611,223 +552,6 @@ function useProducts() {
611
552
  [catalog, locale]
612
553
  );
613
554
  }
614
-
615
- // src/flow/spine.ts
616
- function readField(s, field) {
617
- let cur = s;
618
- for (const part of field.split(".")) {
619
- if (cur == null) return void 0;
620
- cur = cur[part];
621
- }
622
- return cur;
623
- }
624
- function evaluateCondition(cond, s) {
625
- const val = readField(s, cond.field);
626
- const { op, value } = cond;
627
- switch (op) {
628
- case "eq":
629
- return val === value;
630
- case "neq":
631
- return val !== value;
632
- case "in":
633
- return Array.isArray(value) && value.includes(val);
634
- case "gt":
635
- return typeof val === "number" && typeof value === "number" && val > value;
636
- case "gte":
637
- return typeof val === "number" && typeof value === "number" && val >= value;
638
- case "lt":
639
- return typeof val === "number" && typeof value === "number" && val < value;
640
- case "lte":
641
- return typeof val === "number" && typeof value === "number" && val <= value;
642
- case "contains":
643
- if (typeof val === "string") return val.includes(String(value));
644
- if (Array.isArray(val)) return val.includes(value);
645
- return false;
646
- case "exists":
647
- return val !== void 0 && val !== null && val !== "";
648
- case "empty":
649
- return val === void 0 || val === null || val === "" || Array.isArray(val) && val.length === 0;
650
- default:
651
- return false;
652
- }
653
- }
654
- function evaluateGate(gate, s) {
655
- return typeof gate === "function" ? gate(s) : evaluateCondition(gate, s);
656
- }
657
- function resolveRoute(routes, s) {
658
- if (!routes) return void 0;
659
- for (const route of routes) {
660
- if (!route.when || evaluateGate(route.when, s)) return route.to;
661
- }
662
- return void 0;
663
- }
664
- function pageMeta(meta) {
665
- return meta;
666
- }
667
- var TYPE_GUARDS = {
668
- paywall: { field: "user.email", op: "exists" },
669
- upsell: { field: "user.email", op: "exists" }
670
- };
671
- function entryGuard(meta) {
672
- return meta?.guard ?? (meta?.type ? TYPE_GUARDS[meta.type] : void 0);
673
- }
674
- function definePage(component) {
675
- return component;
676
- }
677
- function nextPage(pages, currentKey, s) {
678
- const idx = pages.findIndex((p) => p.key === currentKey);
679
- if (idx === -1) return void 0;
680
- const routed = resolveRoute(pages[idx].meta?.next, s);
681
- if (routed) return routed;
682
- return pages[idx + 1]?.key;
683
- }
684
- function outgoingKeys(pages, currentKey) {
685
- const idx = pages.findIndex((p) => p.key === currentKey);
686
- if (idx === -1) return [];
687
- const out = /* @__PURE__ */ new Set();
688
- for (const route of pages[idx].meta?.next ?? []) out.add(route.to);
689
- const linear = pages[idx + 1]?.key;
690
- if (linear) out.add(linear);
691
- return [...out];
692
- }
693
- function expectedPathLength(pages, startKey, s) {
694
- const seen = /* @__PURE__ */ new Set();
695
- let key = startKey;
696
- let count = 0;
697
- while (key && !seen.has(key)) {
698
- seen.add(key);
699
- count++;
700
- if (pages.find((p) => p.key === key)?.meta?.type === "finish") break;
701
- key = nextPage(pages, key, s);
702
- }
703
- return count;
704
- }
705
- function defineFunnel(def) {
706
- return def;
707
- }
708
-
709
- // src/flow/experiments.ts
710
- function fnv1a(input) {
711
- let h = 2166136261;
712
- for (let i = 0; i < input.length; i++) {
713
- h ^= input.charCodeAt(i);
714
- h = h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24)) >>> 0;
715
- }
716
- return h >>> 0;
717
- }
718
- function hashToUnit(seed) {
719
- return fnv1a(seed) / 4294967296;
720
- }
721
- function pickByWeight(weights, u) {
722
- const entries = Object.entries(weights).filter(([, w]) => w > 0);
723
- if (entries.length === 0) return Object.keys(weights)[0] ?? "";
724
- const total = entries.reduce((sum, [, w]) => sum + w, 0);
725
- const target = u * total;
726
- let acc = 0;
727
- for (const [key, w] of entries) {
728
- acc += w;
729
- if (target < acc) return key;
730
- }
731
- return entries[entries.length - 1][0];
732
- }
733
- function assignVariant(experimentId, seed, weights) {
734
- return pickByWeight(weights, hashToUnit(`${experimentId}:${seed}`));
735
- }
736
- function bucketingSeed(context) {
737
- return context.identity.customerId ?? context.identity.visitorId ?? null;
738
- }
739
- function parseSlotKey(key) {
740
- const at = key.indexOf("@");
741
- return at === -1 ? { slot: key } : { slot: key.slice(0, at), variant: key.slice(at + 1) };
742
- }
743
- function isVariantKey(key) {
744
- return parseSlotKey(key).variant !== void 0;
745
- }
746
- function weightsOf(exp) {
747
- const out = {};
748
- for (const [label, v] of Object.entries(exp.variants)) out[label] = v.weight;
749
- return out;
750
- }
751
- function resolveExperiments(pages, experiments, seed) {
752
- const assignments = {};
753
- const render = {};
754
- const experimentOf = {};
755
- for (const exp of experiments) {
756
- const status = exp.status ?? "running";
757
- let label;
758
- if (status === "stopped" && exp.winner) label = exp.winner;
759
- else if (status === "running" && seed) label = assignVariant(exp.id, seed, weightsOf(exp));
760
- else continue;
761
- const arm = exp.variants[label];
762
- if (!arm) continue;
763
- assignments[exp.id] = label;
764
- experimentOf[exp.slot] = exp.id;
765
- if (arm.page !== exp.slot) render[exp.slot] = arm.page;
766
- }
767
- const slotOf = {};
768
- const activeKeys = [];
769
- for (const p of pages) {
770
- const { slot, variant } = parseSlotKey(p.key);
771
- if (variant !== void 0) slotOf[p.key] = slot;
772
- else activeKeys.push(p.key);
773
- }
774
- return { assignments, activeKeys, render, slotOf, experimentOf };
775
- }
776
- function validateExperiments(experiments, pages) {
777
- const errors = [];
778
- const warnings = [];
779
- const pageKeys = new Set(pages.map((p) => p.key));
780
- const seenIds = /* @__PURE__ */ new Set();
781
- const slotOwner = /* @__PURE__ */ new Map();
782
- const renderedVariantPages = /* @__PURE__ */ new Set();
783
- for (const exp of experiments) {
784
- const err = (code, message) => errors.push({ experimentId: exp.id, code, message });
785
- const warn = (code, message) => warnings.push({ experimentId: exp.id, code, message });
786
- if (seenIds.has(exp.id)) err("duplicate_id", `Experiment id "${exp.id}" is used more than once.`);
787
- seenIds.add(exp.id);
788
- if (!pageKeys.has(exp.slot)) err("slot_missing", `Slot "${exp.slot}" is not a page in the funnel.`);
789
- if (isVariantKey(exp.slot)) err("slot_is_variant", `Slot "${exp.slot}" is a variant page; a slot must be a real flow page.`);
790
- if (slotOwner.has(exp.slot)) {
791
- err("slot_conflict", `Slot "${exp.slot}" is already targeted by experiment "${slotOwner.get(exp.slot)}".`);
792
- } else {
793
- slotOwner.set(exp.slot, exp.id);
794
- }
795
- const arms = Object.entries(exp.variants);
796
- if (arms.length < 2) warn("single_arm", `Experiment "${exp.id}" has fewer than two variants \u2014 nothing to test.`);
797
- let hasControl = false;
798
- let totalWeight = 0;
799
- for (const [label, arm] of arms) {
800
- if (!pageKeys.has(arm.page)) {
801
- err("variant_page_missing", `Variant "${label}" references page "${arm.page}", which doesn't exist.`);
802
- }
803
- if (arm.page === exp.slot) hasControl = true;
804
- else if (isVariantKey(arm.page)) {
805
- renderedVariantPages.add(arm.page);
806
- if (parseSlotKey(arm.page).slot !== exp.slot) {
807
- err("variant_slot_mismatch", `Variant "${label}" page "${arm.page}" belongs to a different slot than "${exp.slot}".`);
808
- }
809
- }
810
- if (arm.weight < 0) err("negative_weight", `Variant "${label}" has a negative weight (${arm.weight}).`);
811
- totalWeight += arm.weight;
812
- }
813
- if (!hasControl) err("no_control", `No variant of "${exp.id}" renders the slot "${exp.slot}" itself (the control/baseline).`);
814
- if (totalWeight <= 0) err("no_traffic", `Experiment "${exp.id}" has no positive weight \u2014 no traffic would enter it.`);
815
- if (exp.status === "stopped" && exp.winner && !exp.variants[exp.winner]) {
816
- err("bad_winner", `Stopped experiment "${exp.id}" names winner "${exp.winner}", which isn't one of its variants.`);
817
- }
818
- }
819
- const running = new Set(experiments.filter((e) => (e.status ?? "running") === "running").flatMap(
820
- (e) => Object.values(e.variants).map((v) => v.page)
821
- ));
822
- for (const p of pages) {
823
- if (isVariantKey(p.key) && !running.has(p.key)) {
824
- warnings.push({ experimentId: "*", code: "orphan_variant", message: `Variant page "${p.key}" is rendered by no running experiment.` });
825
- }
826
- }
827
- return { ok: errors.length === 0, errors, warnings };
828
- }
829
-
830
- // src/flow/flow.tsx
831
555
  var NavContext = createContext(null);
832
556
  var ExperimentContext = createContext({});
833
557
  function useExperiment(id) {
@@ -844,6 +568,39 @@ function pageContextFor(currentKey, slug, index, total, startedAt) {
844
568
  startedAt
845
569
  };
846
570
  }
571
+ var now = () => typeof performance !== "undefined" ? performance.now() : Date.now();
572
+ function usePageExitTimer(tracker, currentKey) {
573
+ const timer = useRef(null);
574
+ const emitExit = useCallback(() => {
575
+ const t = timer.current;
576
+ if (!t) return;
577
+ const at = now();
578
+ const hidden = t.hidden + (t.hiddenSince != null ? at - t.hiddenSince : 0);
579
+ const durationMs = Math.round(at - t.enteredAt);
580
+ tracker.track("page.exit", { pageId: t.key, durationMs, activeMs: Math.max(0, Math.round(durationMs - hidden)) });
581
+ }, [tracker]);
582
+ useEffect(() => {
583
+ if (typeof document === "undefined") return;
584
+ const onVis = () => {
585
+ const t = timer.current;
586
+ if (!t) return;
587
+ if (document.visibilityState === "hidden") t.hiddenSince = now();
588
+ else if (t.hiddenSince != null) {
589
+ t.hidden += now() - t.hiddenSince;
590
+ t.hiddenSince = null;
591
+ }
592
+ };
593
+ document.addEventListener("visibilitychange", onVis);
594
+ return () => document.removeEventListener("visibilitychange", onVis);
595
+ }, []);
596
+ useEffect(() => {
597
+ if (!currentKey) return;
598
+ if (timer.current && timer.current.key !== currentKey) emitExit();
599
+ const hidden = typeof document !== "undefined" && document.visibilityState === "hidden";
600
+ timer.current = { key: currentKey, enteredAt: now(), hidden: 0, hiddenSince: hidden ? now() : null };
601
+ }, [currentKey, emitExit]);
602
+ useEffect(() => () => emitExit(), [emitExit]);
603
+ }
847
604
  function FunnelView({
848
605
  pages,
849
606
  initialKey,
@@ -871,14 +628,18 @@ function FunnelView({
871
628
  () => new Map(pages.filter((p) => p.load).map((p) => [p.key, p.load])),
872
629
  [pages]
873
630
  );
874
- const seed = bucketingSeed(funnel.context);
631
+ const seedRef = useRef(null);
632
+ if (seedRef.current === null) seedRef.current = bucketingSeed(funnel.context);
633
+ const seed = seedRef.current;
875
634
  const experiments = useMemo(
876
635
  () => resolveExperiments(pages, funnel.experiments, seed),
877
636
  [pages, funnel.experiments, seed]
878
637
  );
638
+ const toSlot = useCallback((k) => experiments.slotOf[k] ?? k, [experiments]);
639
+ const toRenderKey = useCallback((k) => experiments.render[k] ?? k, [experiments]);
879
640
  const pageByKey = useMemo(() => new Map(pages.map((p) => [p.key, p])), [pages]);
880
641
  const flowPages = useMemo(
881
- () => experiments.activeKeys.map((k) => pageByKey.get(k)).filter(Boolean),
642
+ () => experiments.activeKeys.map((k) => pageByKey.get(k)).filter((p) => p !== void 0),
882
643
  [experiments, pageByKey]
883
644
  );
884
645
  const flow = useMemo(
@@ -887,12 +648,13 @@ function FunnelView({
887
648
  );
888
649
  const [history, setHistory] = useState(() => {
889
650
  const start = flowPages[0]?.key;
890
- if (!initialKey || initialKey === start) return [start].filter(Boolean);
891
- const target = experiments.slotOf[initialKey] ?? initialKey;
651
+ if (!initialKey || initialKey === start) return start ? [start] : [];
652
+ const target = toSlot(initialKey);
892
653
  const page2 = flowPages.find((p) => p.key === target);
893
654
  const guard = entryGuard(page2?.meta);
894
655
  const ok = page2 && (!guard || evaluateGate(guard, funnel.snapshot()));
895
- return [ok ? target : start].filter(Boolean);
656
+ const first = ok ? target : start;
657
+ return first ? [first] : [];
896
658
  });
897
659
  const currentKey = history[history.length - 1];
898
660
  const index = history.length - 1;
@@ -907,7 +669,9 @@ function FunnelView({
907
669
  () => pageContextFor(currentKey, slug, index, total, funnel.context.system.now),
908
670
  [currentKey, slug, index, total, funnel.context.system.now]
909
671
  );
910
- funnel.context.page = page;
672
+ useEffect(() => {
673
+ funnel.context.page = page;
674
+ }, [funnel, page]);
911
675
  const started = useRef(false);
912
676
  useEffect(() => {
913
677
  if (started.current) return;
@@ -926,67 +690,37 @@ function FunnelView({
926
690
  const expId = experiments.experimentOf[currentKey];
927
691
  if (!expId || exposed.current.has(expId)) return;
928
692
  exposed.current.add(expId);
929
- tracker.track("experiment.exposure", { experimentId: expId, variant: experiments.assignments[expId] });
693
+ tracker.track("experiment.exposure", exposurePayload(expId, experiments.assignments[expId], experiments.versionOf[expId]));
930
694
  }, [tracker, currentKey, experiments]);
931
- const timer = useRef(null);
932
- const now = () => typeof performance !== "undefined" ? performance.now() : Date.now();
933
- const emitExit = useCallback(() => {
934
- const t = timer.current;
935
- if (!t) return;
936
- const at = now();
937
- const hidden = t.hidden + (t.hiddenSince != null ? at - t.hiddenSince : 0);
938
- const durationMs = Math.round(at - t.enteredAt);
939
- tracker.track("page.exit", { pageId: t.key, durationMs, activeMs: Math.max(0, Math.round(durationMs - hidden)) });
940
- }, [tracker]);
941
- useEffect(() => {
942
- if (typeof document === "undefined") return;
943
- const onVis = () => {
944
- const t = timer.current;
945
- if (!t) return;
946
- if (document.visibilityState === "hidden") t.hiddenSince = now();
947
- else if (t.hiddenSince != null) {
948
- t.hidden += now() - t.hiddenSince;
949
- t.hiddenSince = null;
950
- }
951
- };
952
- document.addEventListener("visibilitychange", onVis);
953
- return () => document.removeEventListener("visibilitychange", onVis);
954
- }, []);
955
- useEffect(() => {
956
- if (!currentKey) return;
957
- if (timer.current && timer.current.key !== currentKey) emitExit();
958
- const hidden = typeof document !== "undefined" && document.visibilityState === "hidden";
959
- timer.current = { key: currentKey, enteredAt: now(), hidden: 0, hiddenSince: hidden ? now() : null };
960
- }, [currentKey, emitExit]);
961
- useEffect(() => () => emitExit(), [emitExit]);
695
+ usePageExitTimer(tracker, currentKey);
962
696
  const [isNavigating, startNav] = useTransition();
963
697
  const next = useCallback(() => {
964
698
  const target = nextPage(flow, currentKey, funnel.snapshot());
965
- const resolved = target ? experiments.slotOf[target] ?? target : target;
699
+ const resolved = target ? toSlot(target) : target;
966
700
  if (resolved) startNav(() => setHistory((h) => [...h, resolved]));
967
- }, [flow, currentKey, funnel, experiments]);
701
+ }, [flow, currentKey, funnel, toSlot]);
968
702
  const back = useCallback(
969
703
  () => startNav(() => setHistory((h) => h.length > 1 ? h.slice(0, -1) : h)),
970
704
  []
971
705
  );
972
706
  const go = useCallback(
973
- (key) => startNav(() => setHistory((h) => [...h, experiments.slotOf[key] ?? key])),
974
- [experiments]
707
+ (key) => startNav(() => setHistory((h) => [...h, toSlot(key)])),
708
+ [toSlot]
975
709
  );
976
710
  const prefetched = useRef(/* @__PURE__ */ new Set());
977
711
  const prefetch = useCallback(
978
712
  (key) => {
979
- const target = experiments.render[key] ?? key;
713
+ const target = toRenderKey(key);
980
714
  const load = loaders.get(target);
981
715
  if (!load || prefetched.current.has(target)) return;
982
716
  prefetched.current.add(target);
983
717
  load().catch(() => prefetched.current.delete(target));
984
718
  },
985
- [loaders, experiments]
719
+ [loaders, toRenderKey]
986
720
  );
987
721
  const nextCandidates = useMemo(
988
- () => [...new Set(outgoingKeys(flow, currentKey).map((k) => experiments.render[k] ?? k))],
989
- [flow, currentKey, experiments]
722
+ () => [...new Set(outgoingKeys(flow, currentKey).map(toRenderKey))],
723
+ [flow, currentKey, toRenderKey]
990
724
  );
991
725
  useEffect(() => {
992
726
  if (prefetchMode !== "auto") return;
@@ -1002,7 +736,7 @@ function FunnelView({
1002
736
  canGoBack: history.length > 1,
1003
737
  isNavigating
1004
738
  };
1005
- const renderKey = experiments.render[currentKey] ?? currentKey;
739
+ const renderKey = toRenderKey(currentKey);
1006
740
  const Current = components.get(renderKey);
1007
741
  const pageEl = createElement(Suspense, { fallback: fallback ?? null }, Current ? createElement(Current) : null);
1008
742
  const content = layout ? createElement(layout, null, pageEl) : pageEl;
@@ -1024,14 +758,20 @@ var symModalId = /* @__PURE__ */ Symbol("AppFunnelModalId");
1024
758
  var initialState = {};
1025
759
  var ModalStateContext = createContext(initialState);
1026
760
  var ModalIdContext = createContext(null);
761
+ var ModalDispatchContext = createContext(null);
1027
762
  var REGISTRY = {};
1028
763
  var ALREADY_MOUNTED = {};
1029
764
  var modalCallbacks = {};
1030
765
  var hideCallbacks = {};
1031
766
  var uid = 0;
1032
767
  var getUid = () => `_af_modal_${uid++}`;
1033
- var dispatch = () => {
1034
- throw new Error("No modal dispatch \u2014 render inside <FunnelProvider> (which mounts the modal runtime).");
768
+ var runtimeStack = [];
769
+ var dispatch = (action) => {
770
+ const top = runtimeStack[runtimeStack.length - 1];
771
+ if (!top) {
772
+ throw new Error("No modal dispatch \u2014 render inside <FunnelProvider> (which mounts the modal runtime).");
773
+ }
774
+ top(action);
1035
775
  };
1036
776
  var modalReducer = (state = initialState, action) => {
1037
777
  const { modalId, args, flags } = action.payload;
@@ -1074,10 +814,10 @@ function registerModal(id, comp, props) {
1074
814
  function unregisterModal(id) {
1075
815
  delete REGISTRY[id];
1076
816
  }
1077
- function showModal(modal, args) {
817
+ function showModalVia(d, modal, args) {
1078
818
  const id = idOf(modal);
1079
819
  if (typeof modal !== "string" && !REGISTRY[id]) registerModal(id, modal);
1080
- dispatch({ type: "show", payload: { modalId: id, args } });
820
+ d({ type: "show", payload: { modalId: id, args } });
1081
821
  if (!modalCallbacks[id]) {
1082
822
  let res;
1083
823
  let rej;
@@ -1089,9 +829,12 @@ function showModal(modal, args) {
1089
829
  }
1090
830
  return modalCallbacks[id].promise;
1091
831
  }
1092
- function hideModal(modal) {
832
+ function showModal(modal, args) {
833
+ return showModalVia(dispatch, modal, args);
834
+ }
835
+ function hideModalVia(d, modal) {
1093
836
  const id = idOf(modal);
1094
- dispatch({ type: "hide", payload: { modalId: id } });
837
+ d({ type: "hide", payload: { modalId: id } });
1095
838
  delete modalCallbacks[id];
1096
839
  if (!hideCallbacks[id]) {
1097
840
  let res;
@@ -1104,18 +847,25 @@ function hideModal(modal) {
1104
847
  }
1105
848
  return hideCallbacks[id].promise;
1106
849
  }
1107
- function removeModal(modal) {
850
+ function hideModal(modal) {
851
+ return hideModalVia(dispatch, modal);
852
+ }
853
+ function removeModalVia(d, modal) {
1108
854
  const id = idOf(modal);
1109
- dispatch({ type: "remove", payload: { modalId: id } });
855
+ d({ type: "remove", payload: { modalId: id } });
1110
856
  delete modalCallbacks[id];
1111
857
  delete hideCallbacks[id];
1112
858
  }
1113
- function setFlags(id, flags) {
1114
- dispatch({ type: "set-flags", payload: { modalId: id, flags } });
859
+ function removeModal(modal) {
860
+ removeModalVia(dispatch, modal);
861
+ }
862
+ function setFlags(d, id, flags) {
863
+ d({ type: "set-flags", payload: { modalId: id, flags } });
1115
864
  }
1116
865
  function useModal(modal, args) {
1117
866
  const modals = useContext(ModalStateContext);
1118
867
  const contextId = useContext(ModalIdContext);
868
+ const d = useContext(ModalDispatchContext) ?? dispatch;
1119
869
  const isComponent = !!modal && typeof modal !== "string";
1120
870
  const id = modal ? idOf(modal) : contextId;
1121
871
  if (!id) throw new Error("useModal: no modal id (call inside a defineModal component, or pass an id/component).");
@@ -1123,9 +873,9 @@ function useModal(modal, args) {
1123
873
  if (isComponent && !REGISTRY[id]) registerModal(id, modal, args);
1124
874
  }, [isComponent, id, modal, args]);
1125
875
  const info = modals[id];
1126
- const show = useCallback((a) => showModal(id, a), [id]);
1127
- const hide = useCallback(() => hideModal(id), [id]);
1128
- const remove = useCallback(() => removeModal(id), [id]);
876
+ const show = useCallback((a) => showModalVia(d, id, a), [d, id]);
877
+ const hide = useCallback(() => hideModalVia(d, id), [d, id]);
878
+ const remove = useCallback(() => removeModalVia(d, id), [d, id]);
1129
879
  const resolve = useCallback((v) => {
1130
880
  modalCallbacks[id]?.resolve(v);
1131
881
  delete modalCallbacks[id];
@@ -1158,6 +908,7 @@ function defineModal(Comp) {
1158
908
  return function Wrapped({ id, defaultVisible, keepMounted, ...props }) {
1159
909
  const { args, show } = useModal(id);
1160
910
  const modals = useContext(ModalStateContext);
911
+ const d = useContext(ModalDispatchContext) ?? dispatch;
1161
912
  const shouldMount = !!modals[id];
1162
913
  useEffect(() => {
1163
914
  if (defaultVisible) show();
@@ -1167,8 +918,8 @@ function defineModal(Comp) {
1167
918
  };
1168
919
  }, [id, show, defaultVisible]);
1169
920
  useEffect(() => {
1170
- if (keepMounted) setFlags(id, { keepMounted: true });
1171
- }, [id, keepMounted]);
921
+ if (keepMounted) setFlags(d, id, { keepMounted: true });
922
+ }, [d, id, keepMounted]);
1172
923
  const delayVisible = modals[id]?.delayVisible;
1173
924
  useEffect(() => {
1174
925
  if (delayVisible) show(args);
@@ -1192,12 +943,30 @@ function ModalRoot() {
1192
943
  }
1193
944
  function ModalRuntime({ children }) {
1194
945
  const [state, localDispatch] = useReducer(modalReducer, initialState);
1195
- dispatch = localDispatch;
946
+ useEffect(() => {
947
+ runtimeStack.push(localDispatch);
948
+ if (runtimeStack.length > 1) {
949
+ try {
950
+ console.warn(
951
+ `[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.`
952
+ );
953
+ } catch {
954
+ }
955
+ }
956
+ return () => {
957
+ const i = runtimeStack.indexOf(localDispatch);
958
+ if (i !== -1) runtimeStack.splice(i, 1);
959
+ };
960
+ }, []);
1196
961
  return createElement(
1197
- ModalStateContext.Provider,
1198
- { value: state },
1199
- children,
1200
- createElement(ModalRoot, null)
962
+ ModalDispatchContext.Provider,
963
+ { value: localDispatch },
964
+ createElement(
965
+ ModalStateContext.Provider,
966
+ { value: state },
967
+ children,
968
+ createElement(ModalRoot, null)
969
+ )
1201
970
  );
1202
971
  }
1203
972
 
@@ -1296,103 +1065,6 @@ function Modal(props) {
1296
1065
  }
1297
1066
 
1298
1067
  // src/commerce/checkout.tsx
1299
- var INLINE_SURFACES = /* @__PURE__ */ new Set([
1300
- "express",
1301
- "element",
1302
- "embedded"
1303
- ]);
1304
- function isInlineSurface(surface) {
1305
- return INLINE_SURFACES.has(surface);
1306
- }
1307
- var ON = { purchase: true, wallets: true, offSession: "reliable" };
1308
- var MANAGED = { purchase: true, wallets: true, offSession: "conditional" };
1309
- var ALL_UPSELLS = ["subscription", "one-time", "upgrade"];
1310
- var PROVIDER_PROFILES = {
1311
- // PSP; you hold the PaymentMethod and fire your own off-session PaymentIntent on
1312
- // the Elements surfaces (reliable). **Managed** Checkout (`embedded`/`redirect`)
1313
- // upsells too, but as a direct charge against Stripe's vaulted card → conditional.
1314
- // No provider "popup" surface.
1315
- stripe: {
1316
- isMoR: false,
1317
- offSessionUpsells: ALL_UPSELLS,
1318
- tokenModel: "merchant",
1319
- orchestrator: false,
1320
- surfaces: { express: ON, element: ON, sheet: ON, embedded: MANAGED, redirect: MANAGED }
1321
- },
1322
- // Merchant-of-Record; card entry is always Paddle's frame → no own element/sheet/
1323
- // express. Off-session via subscription one-time charge / collection_mode, but
1324
- // **no 2nd concurrent subscription** (upgrade + one-time only), provider-initiated.
1325
- paddle: {
1326
- isMoR: true,
1327
- offSessionUpsells: ["one-time", "upgrade"],
1328
- tokenModel: "provider",
1329
- orchestrator: false,
1330
- surfaces: { embedded: ON, popup: ON, redirect: ON }
1331
- },
1332
- // Merchant-of-Record; iframe-only (no raw card field). Off-session works
1333
- // (provider-initiated, async via webhooks). `sheet` = our chrome hosting their embed.
1334
- whop: {
1335
- isMoR: true,
1336
- offSessionUpsells: ALL_UPSELLS,
1337
- tokenModel: "provider",
1338
- orchestrator: false,
1339
- surfaces: { embedded: ON, redirect: ON, sheet: ON }
1340
- },
1341
- // Pure orchestrator (PSP-side); Headless renders express/element/sheet/embedded.
1342
- // No hosted redirect page, no popup checkout. Off-session is PSP-conditional.
1343
- primer: {
1344
- isMoR: false,
1345
- offSessionUpsells: ALL_UPSELLS,
1346
- tokenModel: "merchant",
1347
- orchestrator: true,
1348
- surfaces: { express: ON, element: ON, sheet: ON, embedded: ON }
1349
- },
1350
- // PSP **and** orchestrator (own acquiring + connectors routing across Stripe/Adyen/
1351
- // PayPal). One Payment Form (element/sheet/embedded) + hosted page + wallet button.
1352
- // Strong native off-session (Recurring API). No popup.
1353
- solidgate: {
1354
- isMoR: false,
1355
- offSessionUpsells: ALL_UPSELLS,
1356
- tokenModel: "merchant",
1357
- orchestrator: true,
1358
- surfaces: { express: ON, element: ON, sheet: ON, embedded: ON, redirect: ON }
1359
- }
1360
- };
1361
- function surfacesFor(provider) {
1362
- return Object.keys(PROVIDER_PROFILES[provider]?.surfaces ?? {});
1363
- }
1364
- function isMerchantOfRecord(provider) {
1365
- return PROVIDER_PROFILES[provider]?.isMoR ?? false;
1366
- }
1367
- function isOrchestrator(provider) {
1368
- return PROVIDER_PROFILES[provider]?.orchestrator ?? false;
1369
- }
1370
- function validateCheckout(provider, surface) {
1371
- const cap = PROVIDER_PROFILES[provider]?.surfaces[surface];
1372
- if (!cap) return { ok: false, reason: `${provider} has no '${surface}' surface` };
1373
- return cap.purchase ? { ok: true } : { ok: false, reason: `'${surface}' can't take a purchase on ${provider}` };
1374
- }
1375
- function validateUpsell(provider, paywallSurface, kind = "subscription") {
1376
- const profile = PROVIDER_PROFILES[provider];
1377
- if (!profile) return { ok: false, reason: `unknown provider '${provider}'` };
1378
- if (!profile.offSessionUpsells.includes(kind)) {
1379
- const can = profile.offSessionUpsells.length ? ` (it supports: ${profile.offSessionUpsells.join(", ")})` : "";
1380
- return { ok: false, reason: `${provider} can't charge a '${kind}' upsell off-session${can}` };
1381
- }
1382
- const cap = profile.surfaces[paywallSurface];
1383
- if (!cap) return { ok: false, reason: `${provider} has no '${paywallSurface}' surface to upsell after` };
1384
- if (cap.offSession === "none") {
1385
- return { ok: false, reason: `a purchase on '${paywallSurface}' retains no chargeable method for an upsell` };
1386
- }
1387
- if (cap.offSession === "conditional") {
1388
- return { ok: true, note: `off-session upsell after ${provider} '${paywallSurface}' (managed checkout) may decline \u2014 prefer element/sheet/express` };
1389
- }
1390
- return { ok: true };
1391
- }
1392
- function checkoutError(category, message, extra = {}) {
1393
- const retryable = extra.retryable ?? (category === "authentication_required" || category === "requires_payment_method" || category === "processing_error");
1394
- return { category, message, declineCode: extra.declineCode, retryable };
1395
- }
1396
1068
  function createMockDriver(provider = "stripe", options = {}) {
1397
1069
  const success = options.result ?? { ok: true, amountMinor: 0, currency: "USD" };
1398
1070
  const offError = typeof options.failOffSession === "object" ? options.failOffSession : checkoutError("authentication_required", "off-session charge declined (mock)");
@@ -1407,10 +1079,31 @@ function createMockDriver(provider = "stripe", options = {}) {
1407
1079
  {
1408
1080
  type: "button",
1409
1081
  "data-mock-surface": req.surface,
1410
- onClick: () => success.ok ? cb.onSuccess(success) : cb.onError(success.error ?? checkoutError("processing_error", "failed (mock)"))
1082
+ onClick: () => {
1083
+ cb.onStart?.();
1084
+ if (success.ok) cb.onSuccess(success);
1085
+ else cb.onError(success.error ?? checkoutError("processing_error", "failed (mock)"));
1086
+ }
1411
1087
  },
1412
1088
  `Pay (mock ${req.surface})`
1413
- )
1089
+ ),
1090
+ resume: () => Promise.resolve(success)
1091
+ };
1092
+ }
1093
+ function driverWithEmail(driver, getEmail) {
1094
+ const enrich = (req) => req.email !== void 0 ? req : { ...req, email: getEmail() };
1095
+ return {
1096
+ ...driver,
1097
+ start: (req) => driver.start(enrich(req)),
1098
+ renderInline: driver.renderInline ? (req, cb) => driver.renderInline(enrich(req), cb) : void 0
1099
+ };
1100
+ }
1101
+ function driverWithCatalog(driver, catalog) {
1102
+ const enrich = (req) => req.catalogKey !== void 0 ? req : { ...req, catalogKey: catalog.get(req.product)?.catalogKey };
1103
+ return {
1104
+ ...driver,
1105
+ start: (req) => driver.start(enrich(req)),
1106
+ renderInline: driver.renderInline ? (req, cb) => driver.renderInline(enrich(req), cb) : void 0
1414
1107
  };
1415
1108
  }
1416
1109
  var DriverContext = createContext(null);
@@ -1433,6 +1126,12 @@ function useCheckout(opts = {}) {
1433
1126
  const intent = opts.intent ?? "purchase";
1434
1127
  const kind = opts.kind;
1435
1128
  const advance = opts.advanceOnSuccess ?? true;
1129
+ const recovering = useRef(false);
1130
+ const failRef = useRef(() => {
1131
+ });
1132
+ const attemptRef = useRef(
1133
+ async () => ({ ok: false })
1134
+ );
1436
1135
  const succeed = useCallback(
1437
1136
  (result) => {
1438
1137
  setStatus("success");
@@ -1447,20 +1146,8 @@ function useCheckout(opts = {}) {
1447
1146
  },
1448
1147
  [tracker, nav, advance, opts]
1449
1148
  );
1450
- const fail = useCallback(
1451
- (err, product) => {
1452
- setStatus(err.category === "authentication_required" ? "requires_action" : "error");
1453
- setError(err);
1454
- tracker.track("checkout.failed", {
1455
- category: err.category,
1456
- declineCode: err.declineCode,
1457
- productId: product,
1458
- intent
1459
- });
1460
- opts.onError?.(err);
1461
- },
1462
- [tracker, intent, opts]
1463
- );
1149
+ const succeedRef = useRef(succeed);
1150
+ succeedRef.current = succeed;
1464
1151
  const begin = useCallback(
1465
1152
  (product, surface) => {
1466
1153
  setStatus("loading");
@@ -1470,53 +1157,146 @@ function useCheckout(opts = {}) {
1470
1157
  [tracker]
1471
1158
  );
1472
1159
  const paymentAdded = useCallback(() => tracker.track("checkout.payment_added", {}), [tracker]);
1473
- const open = useCallback(
1474
- async (product, surface) => {
1475
- begin(product, surface);
1476
- try {
1477
- const result = await driver.start({ product, intent, surface, kind });
1478
- if (result.ok) succeed(result);
1479
- else fail(result.error ?? checkoutError("unknown", "Checkout failed"), product);
1480
- return result;
1481
- } catch (e) {
1482
- const err = checkoutError("processing_error", e instanceof Error ? e.message : "Checkout failed");
1483
- fail(err, product);
1484
- return { ok: false, error: err };
1485
- }
1486
- },
1487
- [driver, intent, kind, begin, succeed, fail]
1488
- );
1160
+ const hostInSheet = (surface) => surface === "sheet" || isInlineSurface(surface) && !!PROVIDER_PROFILES[driver.provider]?.surfaces.sheet;
1161
+ const startRecovery = (product, surface) => {
1162
+ recovering.current = true;
1163
+ if (hostInSheet(surface)) {
1164
+ begin(product, "sheet");
1165
+ void showModal(CheckoutSheet, {
1166
+ product,
1167
+ surface: "sheet",
1168
+ intent,
1169
+ kind,
1170
+ onSuccess: (r) => succeedRef.current(r),
1171
+ onError: (e) => failRef.current(e, product),
1172
+ onPaymentAdded: () => paymentAdded()
1173
+ });
1174
+ } else {
1175
+ void attemptRef.current(product, surface);
1176
+ }
1177
+ };
1178
+ failRef.current = (err, product) => {
1179
+ setStatus(err.category === "authentication_required" ? "requires_action" : "error");
1180
+ setError(err);
1181
+ tracker.track("checkout.failed", {
1182
+ category: err.category,
1183
+ declineCode: err.declineCode,
1184
+ productId: product,
1185
+ intent
1186
+ });
1187
+ opts.onError?.(err);
1188
+ if (err.category === "canceled" && !opts.onFailed?.canceled) return;
1189
+ if (product && !recovering.current && opts.recoverySurface && err.category !== "canceled") {
1190
+ startRecovery(product, opts.recoverySurface);
1191
+ return;
1192
+ }
1193
+ let route = opts.onFailed?.[err.category] ?? (err.category === "canceled" ? void 0 : opts.onFailed?.default);
1194
+ if (route && typeof route === "object" && "fallback" in route && recovering.current) {
1195
+ const dflt = opts.onFailed?.default;
1196
+ route = dflt && dflt !== route && !(typeof dflt === "object" && "fallback" in dflt) ? dflt : void 0;
1197
+ }
1198
+ if (!route) return;
1199
+ if (typeof route === "function") route(err);
1200
+ else if ("go" in route) nav.go(route.go);
1201
+ else if ("modal" in route) {
1202
+ const props = {
1203
+ error: err,
1204
+ product,
1205
+ go: (pageKey) => nav.go(pageKey),
1206
+ retry: (surface) => {
1207
+ if (!product) return;
1208
+ recovering.current = true;
1209
+ if (!surface) {
1210
+ if (intent === "upsell") void attemptRef.current(product);
1211
+ else startRecovery(product, PROVIDER_PROFILES[driver.provider]?.surfaces.sheet ? "sheet" : "popup");
1212
+ return;
1213
+ }
1214
+ startRecovery(product, surface);
1215
+ }
1216
+ };
1217
+ void showModal(route.modal, props);
1218
+ } else if ("fallback" in route) {
1219
+ if (product && !recovering.current) startRecovery(product, route.fallback);
1220
+ }
1221
+ };
1222
+ attemptRef.current = async (product, surface) => {
1223
+ begin(product, surface);
1224
+ try {
1225
+ const result = await driver.start({ product, intent, surface, kind });
1226
+ if (result.ok) succeedRef.current(result);
1227
+ else failRef.current(result.error ?? checkoutError("unknown", "Checkout failed"), product);
1228
+ return result;
1229
+ } catch (e) {
1230
+ const err = checkoutError("processing_error", e instanceof Error ? e.message : "Checkout failed");
1231
+ failRef.current(err, product);
1232
+ return { ok: false, error: err };
1233
+ }
1234
+ };
1235
+ const open = useCallback(async (product, surface) => {
1236
+ recovering.current = false;
1237
+ return attemptRef.current(product, surface);
1238
+ }, []);
1489
1239
  const callbacksFor = useCallback(
1490
1240
  (product, surface) => ({
1491
- onStart: () => begin(product, surface),
1492
- onSuccess: (result) => succeed(result),
1493
- onError: (err) => fail(err, product),
1241
+ onStart: () => {
1242
+ recovering.current = false;
1243
+ begin(product, surface);
1244
+ },
1245
+ onSuccess: (result) => succeedRef.current(result),
1246
+ onError: (err) => failRef.current(err, product),
1494
1247
  onPaymentAdded: () => paymentAdded()
1495
1248
  }),
1496
- [begin, succeed, fail, paymentAdded]
1249
+ [begin, paymentAdded]
1497
1250
  );
1498
1251
  const reset = useCallback(() => {
1252
+ recovering.current = false;
1499
1253
  setStatus("idle");
1500
1254
  setError(null);
1501
1255
  }, []);
1502
1256
  return { open, status, error, isLoading: status === "loading", reset, callbacksFor };
1503
1257
  }
1504
- var CheckoutSheet = defineModal(({ product, surface, intent, onSuccess, onError }) => {
1258
+ var CheckoutSheet = defineModal(({ product, surface, intent, kind, onSuccess, onError, onPaymentAdded }) => {
1505
1259
  const driver = useDriver();
1506
1260
  const modal = useModal();
1261
+ const resolved = useRef(false);
1262
+ const onErrorRef = useRef(onError);
1263
+ onErrorRef.current = onError;
1264
+ const hostable = !!driver.renderInline;
1265
+ useEffect(() => {
1266
+ if (!hostable) {
1267
+ resolved.current = true;
1268
+ onErrorRef.current(
1269
+ checkoutError("processing_error", "This checkout driver has no inline surface to host in the sheet")
1270
+ );
1271
+ void modal.hide();
1272
+ modal.remove();
1273
+ return;
1274
+ }
1275
+ return () => {
1276
+ if (!resolved.current) {
1277
+ onErrorRef.current(checkoutError("canceled", "Checkout was dismissed before completing"));
1278
+ }
1279
+ };
1280
+ }, []);
1507
1281
  if (!driver.renderInline) return null;
1282
+ const close = () => {
1283
+ resolved.current = true;
1284
+ void modal.hide();
1285
+ modal.remove();
1286
+ };
1508
1287
  return createElement(Sheet, {
1509
1288
  children: driver.renderInline(
1510
- { product, intent, surface },
1289
+ { product, intent, surface, kind },
1511
1290
  {
1512
1291
  onSuccess: (r) => {
1513
- modal.hide();
1292
+ close();
1514
1293
  onSuccess(r);
1515
1294
  },
1516
1295
  onError: (e) => {
1517
- modal.hide();
1296
+ close();
1518
1297
  onError(e);
1519
- }
1298
+ },
1299
+ onPaymentAdded
1520
1300
  }
1521
1301
  )
1522
1302
  });
@@ -1550,7 +1330,15 @@ function Checkout({
1550
1330
  const cb = checkout.callbacksFor(product, surface);
1551
1331
  const open = () => {
1552
1332
  cb.onStart();
1553
- showModal(CheckoutSheet, { product, surface, intent, onSuccess: cb.onSuccess, onError: cb.onError });
1333
+ showModal(CheckoutSheet, {
1334
+ product,
1335
+ surface,
1336
+ intent,
1337
+ kind: options.kind,
1338
+ onSuccess: cb.onSuccess,
1339
+ onError: cb.onError,
1340
+ onPaymentAdded: cb.onPaymentAdded
1341
+ });
1554
1342
  };
1555
1343
  return trigger(open, { asChild, children, className, disabled: checkout.isLoading });
1556
1344
  }
@@ -1562,31 +1350,1129 @@ function Checkout({
1562
1350
  });
1563
1351
  }
1564
1352
  function Upsell({ product, fallback, asChild, children, className, ...options }) {
1565
- const checkout = useCheckout({ ...options, intent: "upsell" });
1566
- const start = async () => {
1567
- const result = await checkout.open(product);
1568
- if (result.ok || !fallback) return;
1569
- const cb = checkout.callbacksFor(product, fallback);
1570
- if (fallback === "sheet") {
1571
- cb.onStart();
1572
- showModal(CheckoutSheet, { product, surface: "sheet", intent: "upsell", onSuccess: cb.onSuccess, onError: cb.onError });
1573
- } else {
1574
- await checkout.open(product, fallback);
1575
- }
1576
- };
1577
- return trigger(start, {
1353
+ const checkout = useCheckout({
1354
+ ...options,
1355
+ intent: "upsell",
1356
+ recoverySurface: fallback ?? options.recoverySurface
1357
+ });
1358
+ return trigger(() => void checkout.open(product), {
1578
1359
  asChild,
1579
1360
  children: children ?? "Add to my plan",
1580
1361
  className,
1581
1362
  disabled: checkout.isLoading
1582
1363
  });
1583
1364
  }
1365
+ var consumedResumes = /* @__PURE__ */ new Set();
1366
+ function CheckoutResume(options = {}) {
1367
+ const driver = useDriver();
1368
+ const checkout = useCheckout(options);
1369
+ const callbacksRef = useRef(checkout.callbacksFor);
1370
+ callbacksRef.current = checkout.callbacksFor;
1371
+ useEffect(() => {
1372
+ if (typeof window === "undefined" || !driver.resume) return;
1373
+ const url = new URL(window.location.href);
1374
+ const correlationId = url.searchParams.get("af_checkout");
1375
+ if (!correlationId || consumedResumes.has(correlationId)) return;
1376
+ consumedResumes.add(correlationId);
1377
+ void driver.resume(correlationId).then((result) => {
1378
+ const cb = callbacksRef.current();
1379
+ if (result.ok) {
1380
+ url.searchParams.delete("af_checkout");
1381
+ window.history.replaceState(window.history.state, "", url.toString());
1382
+ cb.onSuccess(result);
1383
+ } else {
1384
+ cb.onError(result.error ?? checkoutError("unknown", "The checkout could not be verified"));
1385
+ }
1386
+ });
1387
+ }, []);
1388
+ return null;
1389
+ }
1390
+ function __insertCSS(code) {
1391
+ if (typeof document == "undefined") return;
1392
+ let head = document.head || document.getElementsByTagName("head")[0];
1393
+ let style = document.createElement("style");
1394
+ style.type = "text/css";
1395
+ head.appendChild(style);
1396
+ style.styleSheet ? style.styleSheet.cssText = code : style.appendChild(document.createTextNode(code));
1397
+ }
1398
+ var getAsset = (type) => {
1399
+ switch (type) {
1400
+ case "success":
1401
+ return SuccessIcon;
1402
+ case "info":
1403
+ return InfoIcon;
1404
+ case "warning":
1405
+ return WarningIcon;
1406
+ case "error":
1407
+ return ErrorIcon;
1408
+ default:
1409
+ return null;
1410
+ }
1411
+ };
1412
+ var bars = Array(12).fill(0);
1413
+ var Loader = ({ visible, className }) => {
1414
+ return /* @__PURE__ */ React.createElement("div", {
1415
+ className: [
1416
+ "sonner-loading-wrapper",
1417
+ className
1418
+ ].filter(Boolean).join(" "),
1419
+ "data-visible": visible
1420
+ }, /* @__PURE__ */ React.createElement("div", {
1421
+ className: "sonner-spinner"
1422
+ }, bars.map((_, i) => /* @__PURE__ */ React.createElement("div", {
1423
+ className: "sonner-loading-bar",
1424
+ key: `spinner-bar-${i}`
1425
+ }))));
1426
+ };
1427
+ var SuccessIcon = /* @__PURE__ */ React.createElement("svg", {
1428
+ xmlns: "http://www.w3.org/2000/svg",
1429
+ viewBox: "0 0 20 20",
1430
+ fill: "currentColor",
1431
+ height: "20",
1432
+ width: "20"
1433
+ }, /* @__PURE__ */ React.createElement("path", {
1434
+ fillRule: "evenodd",
1435
+ 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",
1436
+ clipRule: "evenodd"
1437
+ }));
1438
+ var WarningIcon = /* @__PURE__ */ React.createElement("svg", {
1439
+ xmlns: "http://www.w3.org/2000/svg",
1440
+ viewBox: "0 0 24 24",
1441
+ fill: "currentColor",
1442
+ height: "20",
1443
+ width: "20"
1444
+ }, /* @__PURE__ */ React.createElement("path", {
1445
+ fillRule: "evenodd",
1446
+ 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",
1447
+ clipRule: "evenodd"
1448
+ }));
1449
+ var InfoIcon = /* @__PURE__ */ React.createElement("svg", {
1450
+ xmlns: "http://www.w3.org/2000/svg",
1451
+ viewBox: "0 0 20 20",
1452
+ fill: "currentColor",
1453
+ height: "20",
1454
+ width: "20"
1455
+ }, /* @__PURE__ */ React.createElement("path", {
1456
+ fillRule: "evenodd",
1457
+ 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",
1458
+ clipRule: "evenodd"
1459
+ }));
1460
+ var ErrorIcon = /* @__PURE__ */ React.createElement("svg", {
1461
+ xmlns: "http://www.w3.org/2000/svg",
1462
+ viewBox: "0 0 20 20",
1463
+ fill: "currentColor",
1464
+ height: "20",
1465
+ width: "20"
1466
+ }, /* @__PURE__ */ React.createElement("path", {
1467
+ fillRule: "evenodd",
1468
+ 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",
1469
+ clipRule: "evenodd"
1470
+ }));
1471
+ var CloseIcon = /* @__PURE__ */ React.createElement("svg", {
1472
+ xmlns: "http://www.w3.org/2000/svg",
1473
+ width: "12",
1474
+ height: "12",
1475
+ viewBox: "0 0 24 24",
1476
+ fill: "none",
1477
+ stroke: "currentColor",
1478
+ strokeWidth: "1.5",
1479
+ strokeLinecap: "round",
1480
+ strokeLinejoin: "round"
1481
+ }, /* @__PURE__ */ React.createElement("line", {
1482
+ x1: "18",
1483
+ y1: "6",
1484
+ x2: "6",
1485
+ y2: "18"
1486
+ }), /* @__PURE__ */ React.createElement("line", {
1487
+ x1: "6",
1488
+ y1: "6",
1489
+ x2: "18",
1490
+ y2: "18"
1491
+ }));
1492
+ var useIsDocumentHidden = () => {
1493
+ const [isDocumentHidden, setIsDocumentHidden] = React.useState(document.hidden);
1494
+ React.useEffect(() => {
1495
+ const callback = () => {
1496
+ setIsDocumentHidden(document.hidden);
1497
+ };
1498
+ document.addEventListener("visibilitychange", callback);
1499
+ return () => window.removeEventListener("visibilitychange", callback);
1500
+ }, []);
1501
+ return isDocumentHidden;
1502
+ };
1503
+ var toastsCounter = 1;
1504
+ var Observer = class {
1505
+ constructor() {
1506
+ this.subscribe = (subscriber) => {
1507
+ this.subscribers.push(subscriber);
1508
+ return () => {
1509
+ const index = this.subscribers.indexOf(subscriber);
1510
+ this.subscribers.splice(index, 1);
1511
+ };
1512
+ };
1513
+ this.publish = (data) => {
1514
+ this.subscribers.forEach((subscriber) => subscriber(data));
1515
+ };
1516
+ this.addToast = (data) => {
1517
+ this.publish(data);
1518
+ this.toasts = [
1519
+ ...this.toasts,
1520
+ data
1521
+ ];
1522
+ };
1523
+ this.create = (data) => {
1524
+ var _data_id;
1525
+ const { message, ...rest } = data;
1526
+ const id = typeof (data == null ? void 0 : data.id) === "number" || ((_data_id = data.id) == null ? void 0 : _data_id.length) > 0 ? data.id : toastsCounter++;
1527
+ const alreadyExists = this.toasts.find((toast2) => {
1528
+ return toast2.id === id;
1529
+ });
1530
+ const dismissible = data.dismissible === void 0 ? true : data.dismissible;
1531
+ if (this.dismissedToasts.has(id)) {
1532
+ this.dismissedToasts.delete(id);
1533
+ }
1534
+ if (alreadyExists) {
1535
+ this.toasts = this.toasts.map((toast2) => {
1536
+ if (toast2.id === id) {
1537
+ this.publish({
1538
+ ...toast2,
1539
+ ...data,
1540
+ id,
1541
+ title: message
1542
+ });
1543
+ return {
1544
+ ...toast2,
1545
+ ...data,
1546
+ id,
1547
+ dismissible,
1548
+ title: message
1549
+ };
1550
+ }
1551
+ return toast2;
1552
+ });
1553
+ } else {
1554
+ this.addToast({
1555
+ title: message,
1556
+ ...rest,
1557
+ dismissible,
1558
+ id
1559
+ });
1560
+ }
1561
+ return id;
1562
+ };
1563
+ this.dismiss = (id) => {
1564
+ if (id) {
1565
+ this.dismissedToasts.add(id);
1566
+ requestAnimationFrame(() => this.subscribers.forEach((subscriber) => subscriber({
1567
+ id,
1568
+ dismiss: true
1569
+ })));
1570
+ } else {
1571
+ this.toasts.forEach((toast2) => {
1572
+ this.subscribers.forEach((subscriber) => subscriber({
1573
+ id: toast2.id,
1574
+ dismiss: true
1575
+ }));
1576
+ });
1577
+ }
1578
+ return id;
1579
+ };
1580
+ this.message = (message, data) => {
1581
+ return this.create({
1582
+ ...data,
1583
+ message
1584
+ });
1585
+ };
1586
+ this.error = (message, data) => {
1587
+ return this.create({
1588
+ ...data,
1589
+ message,
1590
+ type: "error"
1591
+ });
1592
+ };
1593
+ this.success = (message, data) => {
1594
+ return this.create({
1595
+ ...data,
1596
+ type: "success",
1597
+ message
1598
+ });
1599
+ };
1600
+ this.info = (message, data) => {
1601
+ return this.create({
1602
+ ...data,
1603
+ type: "info",
1604
+ message
1605
+ });
1606
+ };
1607
+ this.warning = (message, data) => {
1608
+ return this.create({
1609
+ ...data,
1610
+ type: "warning",
1611
+ message
1612
+ });
1613
+ };
1614
+ this.loading = (message, data) => {
1615
+ return this.create({
1616
+ ...data,
1617
+ type: "loading",
1618
+ message
1619
+ });
1620
+ };
1621
+ this.promise = (promise, data) => {
1622
+ if (!data) {
1623
+ return;
1624
+ }
1625
+ let id = void 0;
1626
+ if (data.loading !== void 0) {
1627
+ id = this.create({
1628
+ ...data,
1629
+ promise,
1630
+ type: "loading",
1631
+ message: data.loading,
1632
+ description: typeof data.description !== "function" ? data.description : void 0
1633
+ });
1634
+ }
1635
+ const p = Promise.resolve(promise instanceof Function ? promise() : promise);
1636
+ let shouldDismiss = id !== void 0;
1637
+ let result;
1638
+ const originalPromise = p.then(async (response) => {
1639
+ result = [
1640
+ "resolve",
1641
+ response
1642
+ ];
1643
+ const isReactElementResponse = React.isValidElement(response);
1644
+ if (isReactElementResponse) {
1645
+ shouldDismiss = false;
1646
+ this.create({
1647
+ id,
1648
+ type: "default",
1649
+ message: response
1650
+ });
1651
+ } else if (isHttpResponse(response) && !response.ok) {
1652
+ shouldDismiss = false;
1653
+ const promiseData = typeof data.error === "function" ? await data.error(`HTTP error! status: ${response.status}`) : data.error;
1654
+ const description = typeof data.description === "function" ? await data.description(`HTTP error! status: ${response.status}`) : data.description;
1655
+ const isExtendedResult = typeof promiseData === "object" && !React.isValidElement(promiseData);
1656
+ const toastSettings = isExtendedResult ? promiseData : {
1657
+ message: promiseData
1658
+ };
1659
+ this.create({
1660
+ id,
1661
+ type: "error",
1662
+ description,
1663
+ ...toastSettings
1664
+ });
1665
+ } else if (response instanceof Error) {
1666
+ shouldDismiss = false;
1667
+ const promiseData = typeof data.error === "function" ? await data.error(response) : data.error;
1668
+ const description = typeof data.description === "function" ? await data.description(response) : data.description;
1669
+ const isExtendedResult = typeof promiseData === "object" && !React.isValidElement(promiseData);
1670
+ const toastSettings = isExtendedResult ? promiseData : {
1671
+ message: promiseData
1672
+ };
1673
+ this.create({
1674
+ id,
1675
+ type: "error",
1676
+ description,
1677
+ ...toastSettings
1678
+ });
1679
+ } else if (data.success !== void 0) {
1680
+ shouldDismiss = false;
1681
+ const promiseData = typeof data.success === "function" ? await data.success(response) : data.success;
1682
+ const description = typeof data.description === "function" ? await data.description(response) : data.description;
1683
+ const isExtendedResult = typeof promiseData === "object" && !React.isValidElement(promiseData);
1684
+ const toastSettings = isExtendedResult ? promiseData : {
1685
+ message: promiseData
1686
+ };
1687
+ this.create({
1688
+ id,
1689
+ type: "success",
1690
+ description,
1691
+ ...toastSettings
1692
+ });
1693
+ }
1694
+ }).catch(async (error) => {
1695
+ result = [
1696
+ "reject",
1697
+ error
1698
+ ];
1699
+ if (data.error !== void 0) {
1700
+ shouldDismiss = false;
1701
+ const promiseData = typeof data.error === "function" ? await data.error(error) : data.error;
1702
+ const description = typeof data.description === "function" ? await data.description(error) : data.description;
1703
+ const isExtendedResult = typeof promiseData === "object" && !React.isValidElement(promiseData);
1704
+ const toastSettings = isExtendedResult ? promiseData : {
1705
+ message: promiseData
1706
+ };
1707
+ this.create({
1708
+ id,
1709
+ type: "error",
1710
+ description,
1711
+ ...toastSettings
1712
+ });
1713
+ }
1714
+ }).finally(() => {
1715
+ if (shouldDismiss) {
1716
+ this.dismiss(id);
1717
+ id = void 0;
1718
+ }
1719
+ data.finally == null ? void 0 : data.finally.call(data);
1720
+ });
1721
+ const unwrap = () => new Promise((resolve, reject) => originalPromise.then(() => result[0] === "reject" ? reject(result[1]) : resolve(result[1])).catch(reject));
1722
+ if (typeof id !== "string" && typeof id !== "number") {
1723
+ return {
1724
+ unwrap
1725
+ };
1726
+ } else {
1727
+ return Object.assign(id, {
1728
+ unwrap
1729
+ });
1730
+ }
1731
+ };
1732
+ this.custom = (jsx2, data) => {
1733
+ const id = (data == null ? void 0 : data.id) || toastsCounter++;
1734
+ this.create({
1735
+ jsx: jsx2(id),
1736
+ id,
1737
+ ...data
1738
+ });
1739
+ return id;
1740
+ };
1741
+ this.getActiveToasts = () => {
1742
+ return this.toasts.filter((toast2) => !this.dismissedToasts.has(toast2.id));
1743
+ };
1744
+ this.subscribers = [];
1745
+ this.toasts = [];
1746
+ this.dismissedToasts = /* @__PURE__ */ new Set();
1747
+ }
1748
+ };
1749
+ var ToastState = new Observer();
1750
+ var toastFunction = (message, data) => {
1751
+ const id = (data == null ? void 0 : data.id) || toastsCounter++;
1752
+ ToastState.addToast({
1753
+ title: message,
1754
+ ...data,
1755
+ id
1756
+ });
1757
+ return id;
1758
+ };
1759
+ var isHttpResponse = (data) => {
1760
+ return data && typeof data === "object" && "ok" in data && typeof data.ok === "boolean" && "status" in data && typeof data.status === "number";
1761
+ };
1762
+ var basicToast = toastFunction;
1763
+ var getHistory = () => ToastState.toasts;
1764
+ var getToasts = () => ToastState.getActiveToasts();
1765
+ var toast = Object.assign(basicToast, {
1766
+ success: ToastState.success,
1767
+ info: ToastState.info,
1768
+ warning: ToastState.warning,
1769
+ error: ToastState.error,
1770
+ custom: ToastState.custom,
1771
+ message: ToastState.message,
1772
+ promise: ToastState.promise,
1773
+ dismiss: ToastState.dismiss,
1774
+ loading: ToastState.loading
1775
+ }, {
1776
+ getHistory,
1777
+ getToasts
1778
+ });
1779
+ __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%)}");
1780
+ function isAction(action) {
1781
+ return action.label !== void 0;
1782
+ }
1783
+ var VISIBLE_TOASTS_AMOUNT = 3;
1784
+ var VIEWPORT_OFFSET = "24px";
1785
+ var MOBILE_VIEWPORT_OFFSET = "16px";
1786
+ var TOAST_LIFETIME = 4e3;
1787
+ var TOAST_WIDTH = 356;
1788
+ var GAP = 14;
1789
+ var SWIPE_THRESHOLD = 45;
1790
+ var TIME_BEFORE_UNMOUNT = 200;
1791
+ function cn(...classes) {
1792
+ return classes.filter(Boolean).join(" ");
1793
+ }
1794
+ function getDefaultSwipeDirections(position) {
1795
+ const [y, x] = position.split("-");
1796
+ const directions = [];
1797
+ if (y) {
1798
+ directions.push(y);
1799
+ }
1800
+ if (x) {
1801
+ directions.push(x);
1802
+ }
1803
+ return directions;
1804
+ }
1805
+ var Toast = (props) => {
1806
+ var _toast_classNames, _toast_classNames1, _toast_classNames2, _toast_classNames3, _toast_classNames4, _toast_classNames5, _toast_classNames6, _toast_classNames7, _toast_classNames8;
1807
+ 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;
1808
+ const [swipeDirection, setSwipeDirection] = React.useState(null);
1809
+ const [swipeOutDirection, setSwipeOutDirection] = React.useState(null);
1810
+ const [mounted, setMounted] = React.useState(false);
1811
+ const [removed, setRemoved] = React.useState(false);
1812
+ const [swiping, setSwiping] = React.useState(false);
1813
+ const [swipeOut, setSwipeOut] = React.useState(false);
1814
+ const [isSwiped, setIsSwiped] = React.useState(false);
1815
+ const [offsetBeforeRemove, setOffsetBeforeRemove] = React.useState(0);
1816
+ const [initialHeight, setInitialHeight] = React.useState(0);
1817
+ const remainingTime = React.useRef(toast2.duration || durationFromToaster || TOAST_LIFETIME);
1818
+ const dragStartTime = React.useRef(null);
1819
+ const toastRef = React.useRef(null);
1820
+ const isFront = index === 0;
1821
+ const isVisible = index + 1 <= visibleToasts;
1822
+ const toastType = toast2.type;
1823
+ const dismissible = toast2.dismissible !== false;
1824
+ const toastClassname = toast2.className || "";
1825
+ const toastDescriptionClassname = toast2.descriptionClassName || "";
1826
+ const heightIndex = React.useMemo(() => heights.findIndex((height) => height.toastId === toast2.id) || 0, [
1827
+ heights,
1828
+ toast2.id
1829
+ ]);
1830
+ const closeButton = React.useMemo(() => {
1831
+ var _toast_closeButton;
1832
+ return (_toast_closeButton = toast2.closeButton) != null ? _toast_closeButton : closeButtonFromToaster;
1833
+ }, [
1834
+ toast2.closeButton,
1835
+ closeButtonFromToaster
1836
+ ]);
1837
+ const duration = React.useMemo(() => toast2.duration || durationFromToaster || TOAST_LIFETIME, [
1838
+ toast2.duration,
1839
+ durationFromToaster
1840
+ ]);
1841
+ const closeTimerStartTimeRef = React.useRef(0);
1842
+ const offset = React.useRef(0);
1843
+ const lastCloseTimerStartTimeRef = React.useRef(0);
1844
+ const pointerStartRef = React.useRef(null);
1845
+ const [y, x] = position.split("-");
1846
+ const toastsHeightBefore = React.useMemo(() => {
1847
+ return heights.reduce((prev, curr, reducerIndex) => {
1848
+ if (reducerIndex >= heightIndex) {
1849
+ return prev;
1850
+ }
1851
+ return prev + curr.height;
1852
+ }, 0);
1853
+ }, [
1854
+ heights,
1855
+ heightIndex
1856
+ ]);
1857
+ const isDocumentHidden = useIsDocumentHidden();
1858
+ const invert = toast2.invert || ToasterInvert;
1859
+ const disabled = toastType === "loading";
1860
+ offset.current = React.useMemo(() => heightIndex * gap + toastsHeightBefore, [
1861
+ heightIndex,
1862
+ toastsHeightBefore
1863
+ ]);
1864
+ React.useEffect(() => {
1865
+ remainingTime.current = duration;
1866
+ }, [
1867
+ duration
1868
+ ]);
1869
+ React.useEffect(() => {
1870
+ setMounted(true);
1871
+ }, []);
1872
+ React.useEffect(() => {
1873
+ const toastNode = toastRef.current;
1874
+ if (toastNode) {
1875
+ const height = toastNode.getBoundingClientRect().height;
1876
+ setInitialHeight(height);
1877
+ setHeights((h) => [
1878
+ {
1879
+ toastId: toast2.id,
1880
+ height,
1881
+ position: toast2.position
1882
+ },
1883
+ ...h
1884
+ ]);
1885
+ return () => setHeights((h) => h.filter((height2) => height2.toastId !== toast2.id));
1886
+ }
1887
+ }, [
1888
+ setHeights,
1889
+ toast2.id
1890
+ ]);
1891
+ React.useLayoutEffect(() => {
1892
+ if (!mounted) return;
1893
+ const toastNode = toastRef.current;
1894
+ const originalHeight = toastNode.style.height;
1895
+ toastNode.style.height = "auto";
1896
+ const newHeight = toastNode.getBoundingClientRect().height;
1897
+ toastNode.style.height = originalHeight;
1898
+ setInitialHeight(newHeight);
1899
+ setHeights((heights2) => {
1900
+ const alreadyExists = heights2.find((height) => height.toastId === toast2.id);
1901
+ if (!alreadyExists) {
1902
+ return [
1903
+ {
1904
+ toastId: toast2.id,
1905
+ height: newHeight,
1906
+ position: toast2.position
1907
+ },
1908
+ ...heights2
1909
+ ];
1910
+ } else {
1911
+ return heights2.map((height) => height.toastId === toast2.id ? {
1912
+ ...height,
1913
+ height: newHeight
1914
+ } : height);
1915
+ }
1916
+ });
1917
+ }, [
1918
+ mounted,
1919
+ toast2.title,
1920
+ toast2.description,
1921
+ setHeights,
1922
+ toast2.id,
1923
+ toast2.jsx,
1924
+ toast2.action,
1925
+ toast2.cancel
1926
+ ]);
1927
+ const deleteToast = React.useCallback(() => {
1928
+ setRemoved(true);
1929
+ setOffsetBeforeRemove(offset.current);
1930
+ setHeights((h) => h.filter((height) => height.toastId !== toast2.id));
1931
+ setTimeout(() => {
1932
+ removeToast(toast2);
1933
+ }, TIME_BEFORE_UNMOUNT);
1934
+ }, [
1935
+ toast2,
1936
+ removeToast,
1937
+ setHeights,
1938
+ offset
1939
+ ]);
1940
+ React.useEffect(() => {
1941
+ if (toast2.promise && toastType === "loading" || toast2.duration === Infinity || toast2.type === "loading") return;
1942
+ let timeoutId;
1943
+ const pauseTimer = () => {
1944
+ if (lastCloseTimerStartTimeRef.current < closeTimerStartTimeRef.current) {
1945
+ const elapsedTime = (/* @__PURE__ */ new Date()).getTime() - closeTimerStartTimeRef.current;
1946
+ remainingTime.current = remainingTime.current - elapsedTime;
1947
+ }
1948
+ lastCloseTimerStartTimeRef.current = (/* @__PURE__ */ new Date()).getTime();
1949
+ };
1950
+ const startTimer = () => {
1951
+ if (remainingTime.current === Infinity) return;
1952
+ closeTimerStartTimeRef.current = (/* @__PURE__ */ new Date()).getTime();
1953
+ timeoutId = setTimeout(() => {
1954
+ toast2.onAutoClose == null ? void 0 : toast2.onAutoClose.call(toast2, toast2);
1955
+ deleteToast();
1956
+ }, remainingTime.current);
1957
+ };
1958
+ if (expanded || interacting || isDocumentHidden) {
1959
+ pauseTimer();
1960
+ } else {
1961
+ startTimer();
1962
+ }
1963
+ return () => clearTimeout(timeoutId);
1964
+ }, [
1965
+ expanded,
1966
+ interacting,
1967
+ toast2,
1968
+ toastType,
1969
+ isDocumentHidden,
1970
+ deleteToast
1971
+ ]);
1972
+ React.useEffect(() => {
1973
+ if (toast2.delete) {
1974
+ deleteToast();
1975
+ toast2.onDismiss == null ? void 0 : toast2.onDismiss.call(toast2, toast2);
1976
+ }
1977
+ }, [
1978
+ deleteToast,
1979
+ toast2.delete
1980
+ ]);
1981
+ function getLoadingIcon() {
1982
+ var _toast_classNames9;
1983
+ if (icons == null ? void 0 : icons.loading) {
1984
+ var _toast_classNames12;
1985
+ return /* @__PURE__ */ React.createElement("div", {
1986
+ className: cn(classNames == null ? void 0 : classNames.loader, toast2 == null ? void 0 : (_toast_classNames12 = toast2.classNames) == null ? void 0 : _toast_classNames12.loader, "sonner-loader"),
1987
+ "data-visible": toastType === "loading"
1988
+ }, icons.loading);
1989
+ }
1990
+ return /* @__PURE__ */ React.createElement(Loader, {
1991
+ className: cn(classNames == null ? void 0 : classNames.loader, toast2 == null ? void 0 : (_toast_classNames9 = toast2.classNames) == null ? void 0 : _toast_classNames9.loader),
1992
+ visible: toastType === "loading"
1993
+ });
1994
+ }
1995
+ const icon = toast2.icon || (icons == null ? void 0 : icons[toastType]) || getAsset(toastType);
1996
+ var _toast_richColors, _icons_close;
1997
+ return /* @__PURE__ */ React.createElement("li", {
1998
+ tabIndex: 0,
1999
+ ref: toastRef,
2000
+ 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]),
2001
+ "data-sonner-toast": "",
2002
+ "data-rich-colors": (_toast_richColors = toast2.richColors) != null ? _toast_richColors : defaultRichColors,
2003
+ "data-styled": !Boolean(toast2.jsx || toast2.unstyled || unstyled),
2004
+ "data-mounted": mounted,
2005
+ "data-promise": Boolean(toast2.promise),
2006
+ "data-swiped": isSwiped,
2007
+ "data-removed": removed,
2008
+ "data-visible": isVisible,
2009
+ "data-y-position": y,
2010
+ "data-x-position": x,
2011
+ "data-index": index,
2012
+ "data-front": isFront,
2013
+ "data-swiping": swiping,
2014
+ "data-dismissible": dismissible,
2015
+ "data-type": toastType,
2016
+ "data-invert": invert,
2017
+ "data-swipe-out": swipeOut,
2018
+ "data-swipe-direction": swipeOutDirection,
2019
+ "data-expanded": Boolean(expanded || expandByDefault && mounted),
2020
+ "data-testid": toast2.testId,
2021
+ style: {
2022
+ "--index": index,
2023
+ "--toasts-before": index,
2024
+ "--z-index": toasts.length - index,
2025
+ "--offset": `${removed ? offsetBeforeRemove : offset.current}px`,
2026
+ "--initial-height": expandByDefault ? "auto" : `${initialHeight}px`,
2027
+ ...style,
2028
+ ...toast2.style
2029
+ },
2030
+ onDragEnd: () => {
2031
+ setSwiping(false);
2032
+ setSwipeDirection(null);
2033
+ pointerStartRef.current = null;
2034
+ },
2035
+ onPointerDown: (event) => {
2036
+ if (event.button === 2) return;
2037
+ if (disabled || !dismissible) return;
2038
+ dragStartTime.current = /* @__PURE__ */ new Date();
2039
+ setOffsetBeforeRemove(offset.current);
2040
+ event.target.setPointerCapture(event.pointerId);
2041
+ if (event.target.tagName === "BUTTON") return;
2042
+ setSwiping(true);
2043
+ pointerStartRef.current = {
2044
+ x: event.clientX,
2045
+ y: event.clientY
2046
+ };
2047
+ },
2048
+ onPointerUp: () => {
2049
+ var _toastRef_current, _toastRef_current1, _dragStartTime_current;
2050
+ if (swipeOut || !dismissible) return;
2051
+ pointerStartRef.current = null;
2052
+ const swipeAmountX = Number(((_toastRef_current = toastRef.current) == null ? void 0 : _toastRef_current.style.getPropertyValue("--swipe-amount-x").replace("px", "")) || 0);
2053
+ const swipeAmountY = Number(((_toastRef_current1 = toastRef.current) == null ? void 0 : _toastRef_current1.style.getPropertyValue("--swipe-amount-y").replace("px", "")) || 0);
2054
+ const timeTaken = (/* @__PURE__ */ new Date()).getTime() - ((_dragStartTime_current = dragStartTime.current) == null ? void 0 : _dragStartTime_current.getTime());
2055
+ const swipeAmount = swipeDirection === "x" ? swipeAmountX : swipeAmountY;
2056
+ const velocity = Math.abs(swipeAmount) / timeTaken;
2057
+ if (Math.abs(swipeAmount) >= SWIPE_THRESHOLD || velocity > 0.11) {
2058
+ setOffsetBeforeRemove(offset.current);
2059
+ toast2.onDismiss == null ? void 0 : toast2.onDismiss.call(toast2, toast2);
2060
+ if (swipeDirection === "x") {
2061
+ setSwipeOutDirection(swipeAmountX > 0 ? "right" : "left");
2062
+ } else {
2063
+ setSwipeOutDirection(swipeAmountY > 0 ? "down" : "up");
2064
+ }
2065
+ deleteToast();
2066
+ setSwipeOut(true);
2067
+ return;
2068
+ } else {
2069
+ var _toastRef_current2, _toastRef_current3;
2070
+ (_toastRef_current2 = toastRef.current) == null ? void 0 : _toastRef_current2.style.setProperty("--swipe-amount-x", `0px`);
2071
+ (_toastRef_current3 = toastRef.current) == null ? void 0 : _toastRef_current3.style.setProperty("--swipe-amount-y", `0px`);
2072
+ }
2073
+ setIsSwiped(false);
2074
+ setSwiping(false);
2075
+ setSwipeDirection(null);
2076
+ },
2077
+ onPointerMove: (event) => {
2078
+ var _window_getSelection, _toastRef_current, _toastRef_current1;
2079
+ if (!pointerStartRef.current || !dismissible) return;
2080
+ const isHighlighted = ((_window_getSelection = window.getSelection()) == null ? void 0 : _window_getSelection.toString().length) > 0;
2081
+ if (isHighlighted) return;
2082
+ const yDelta = event.clientY - pointerStartRef.current.y;
2083
+ const xDelta = event.clientX - pointerStartRef.current.x;
2084
+ var _props_swipeDirections;
2085
+ const swipeDirections = (_props_swipeDirections = props.swipeDirections) != null ? _props_swipeDirections : getDefaultSwipeDirections(position);
2086
+ if (!swipeDirection && (Math.abs(xDelta) > 1 || Math.abs(yDelta) > 1)) {
2087
+ setSwipeDirection(Math.abs(xDelta) > Math.abs(yDelta) ? "x" : "y");
2088
+ }
2089
+ let swipeAmount = {
2090
+ x: 0,
2091
+ y: 0
2092
+ };
2093
+ const getDampening = (delta) => {
2094
+ const factor = Math.abs(delta) / 20;
2095
+ return 1 / (1.5 + factor);
2096
+ };
2097
+ if (swipeDirection === "y") {
2098
+ if (swipeDirections.includes("top") || swipeDirections.includes("bottom")) {
2099
+ if (swipeDirections.includes("top") && yDelta < 0 || swipeDirections.includes("bottom") && yDelta > 0) {
2100
+ swipeAmount.y = yDelta;
2101
+ } else {
2102
+ const dampenedDelta = yDelta * getDampening(yDelta);
2103
+ swipeAmount.y = Math.abs(dampenedDelta) < Math.abs(yDelta) ? dampenedDelta : yDelta;
2104
+ }
2105
+ }
2106
+ } else if (swipeDirection === "x") {
2107
+ if (swipeDirections.includes("left") || swipeDirections.includes("right")) {
2108
+ if (swipeDirections.includes("left") && xDelta < 0 || swipeDirections.includes("right") && xDelta > 0) {
2109
+ swipeAmount.x = xDelta;
2110
+ } else {
2111
+ const dampenedDelta = xDelta * getDampening(xDelta);
2112
+ swipeAmount.x = Math.abs(dampenedDelta) < Math.abs(xDelta) ? dampenedDelta : xDelta;
2113
+ }
2114
+ }
2115
+ }
2116
+ if (Math.abs(swipeAmount.x) > 0 || Math.abs(swipeAmount.y) > 0) {
2117
+ setIsSwiped(true);
2118
+ }
2119
+ (_toastRef_current = toastRef.current) == null ? void 0 : _toastRef_current.style.setProperty("--swipe-amount-x", `${swipeAmount.x}px`);
2120
+ (_toastRef_current1 = toastRef.current) == null ? void 0 : _toastRef_current1.style.setProperty("--swipe-amount-y", `${swipeAmount.y}px`);
2121
+ }
2122
+ }, closeButton && !toast2.jsx && toastType !== "loading" ? /* @__PURE__ */ React.createElement("button", {
2123
+ "aria-label": closeButtonAriaLabel,
2124
+ "data-disabled": disabled,
2125
+ "data-close-button": true,
2126
+ onClick: disabled || !dismissible ? () => {
2127
+ } : () => {
2128
+ deleteToast();
2129
+ toast2.onDismiss == null ? void 0 : toast2.onDismiss.call(toast2, toast2);
2130
+ },
2131
+ className: cn(classNames == null ? void 0 : classNames.closeButton, toast2 == null ? void 0 : (_toast_classNames2 = toast2.classNames) == null ? void 0 : _toast_classNames2.closeButton)
2132
+ }, (_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.createElement("div", {
2133
+ "data-icon": "",
2134
+ className: cn(classNames == null ? void 0 : classNames.icon, toast2 == null ? void 0 : (_toast_classNames3 = toast2.classNames) == null ? void 0 : _toast_classNames3.icon)
2135
+ }, toast2.promise || toast2.type === "loading" && !toast2.icon ? toast2.icon || getLoadingIcon() : null, toast2.type !== "loading" ? icon : null) : null, /* @__PURE__ */ React.createElement("div", {
2136
+ "data-content": "",
2137
+ className: cn(classNames == null ? void 0 : classNames.content, toast2 == null ? void 0 : (_toast_classNames4 = toast2.classNames) == null ? void 0 : _toast_classNames4.content)
2138
+ }, /* @__PURE__ */ React.createElement("div", {
2139
+ "data-title": "",
2140
+ className: cn(classNames == null ? void 0 : classNames.title, toast2 == null ? void 0 : (_toast_classNames5 = toast2.classNames) == null ? void 0 : _toast_classNames5.title)
2141
+ }, toast2.jsx ? toast2.jsx : typeof toast2.title === "function" ? toast2.title() : toast2.title), toast2.description ? /* @__PURE__ */ React.createElement("div", {
2142
+ "data-description": "",
2143
+ className: cn(descriptionClassName, toastDescriptionClassname, classNames == null ? void 0 : classNames.description, toast2 == null ? void 0 : (_toast_classNames6 = toast2.classNames) == null ? void 0 : _toast_classNames6.description)
2144
+ }, typeof toast2.description === "function" ? toast2.description() : toast2.description) : null), /* @__PURE__ */ React.isValidElement(toast2.cancel) ? toast2.cancel : toast2.cancel && isAction(toast2.cancel) ? /* @__PURE__ */ React.createElement("button", {
2145
+ "data-button": true,
2146
+ "data-cancel": true,
2147
+ style: toast2.cancelButtonStyle || cancelButtonStyle,
2148
+ onClick: (event) => {
2149
+ if (!isAction(toast2.cancel)) return;
2150
+ if (!dismissible) return;
2151
+ toast2.cancel.onClick == null ? void 0 : toast2.cancel.onClick.call(toast2.cancel, event);
2152
+ deleteToast();
2153
+ },
2154
+ className: cn(classNames == null ? void 0 : classNames.cancelButton, toast2 == null ? void 0 : (_toast_classNames7 = toast2.classNames) == null ? void 0 : _toast_classNames7.cancelButton)
2155
+ }, toast2.cancel.label) : null, /* @__PURE__ */ React.isValidElement(toast2.action) ? toast2.action : toast2.action && isAction(toast2.action) ? /* @__PURE__ */ React.createElement("button", {
2156
+ "data-button": true,
2157
+ "data-action": true,
2158
+ style: toast2.actionButtonStyle || actionButtonStyle,
2159
+ onClick: (event) => {
2160
+ if (!isAction(toast2.action)) return;
2161
+ toast2.action.onClick == null ? void 0 : toast2.action.onClick.call(toast2.action, event);
2162
+ if (event.defaultPrevented) return;
2163
+ deleteToast();
2164
+ },
2165
+ className: cn(classNames == null ? void 0 : classNames.actionButton, toast2 == null ? void 0 : (_toast_classNames8 = toast2.classNames) == null ? void 0 : _toast_classNames8.actionButton)
2166
+ }, toast2.action.label) : null);
2167
+ };
2168
+ function getDocumentDirection() {
2169
+ if (typeof window === "undefined") return "ltr";
2170
+ if (typeof document === "undefined") return "ltr";
2171
+ const dirAttribute = document.documentElement.getAttribute("dir");
2172
+ if (dirAttribute === "auto" || !dirAttribute) {
2173
+ return window.getComputedStyle(document.documentElement).direction;
2174
+ }
2175
+ return dirAttribute;
2176
+ }
2177
+ function assignOffset(defaultOffset, mobileOffset) {
2178
+ const styles = {};
2179
+ [
2180
+ defaultOffset,
2181
+ mobileOffset
2182
+ ].forEach((offset, index) => {
2183
+ const isMobile = index === 1;
2184
+ const prefix = isMobile ? "--mobile-offset" : "--offset";
2185
+ const defaultValue = isMobile ? MOBILE_VIEWPORT_OFFSET : VIEWPORT_OFFSET;
2186
+ function assignAll(offset2) {
2187
+ [
2188
+ "top",
2189
+ "right",
2190
+ "bottom",
2191
+ "left"
2192
+ ].forEach((key) => {
2193
+ styles[`${prefix}-${key}`] = typeof offset2 === "number" ? `${offset2}px` : offset2;
2194
+ });
2195
+ }
2196
+ if (typeof offset === "number" || typeof offset === "string") {
2197
+ assignAll(offset);
2198
+ } else if (typeof offset === "object") {
2199
+ [
2200
+ "top",
2201
+ "right",
2202
+ "bottom",
2203
+ "left"
2204
+ ].forEach((key) => {
2205
+ if (offset[key] === void 0) {
2206
+ styles[`${prefix}-${key}`] = defaultValue;
2207
+ } else {
2208
+ styles[`${prefix}-${key}`] = typeof offset[key] === "number" ? `${offset[key]}px` : offset[key];
2209
+ }
2210
+ });
2211
+ } else {
2212
+ assignAll(defaultValue);
2213
+ }
2214
+ });
2215
+ return styles;
2216
+ }
2217
+ var Toaster = /* @__PURE__ */ React.forwardRef(function Toaster2(props, ref) {
2218
+ const { id, invert, position = "bottom-right", hotkey = [
2219
+ "altKey",
2220
+ "KeyT"
2221
+ ], expand, closeButton, className, offset, mobileOffset, theme = "light", richColors, duration, style, visibleToasts = VISIBLE_TOASTS_AMOUNT, toastOptions, dir = getDocumentDirection(), gap = GAP, icons, containerAriaLabel = "Notifications" } = props;
2222
+ const [toasts, setToasts] = React.useState([]);
2223
+ const filteredToasts = React.useMemo(() => {
2224
+ if (id) {
2225
+ return toasts.filter((toast2) => toast2.toasterId === id);
2226
+ }
2227
+ return toasts.filter((toast2) => !toast2.toasterId);
2228
+ }, [
2229
+ toasts,
2230
+ id
2231
+ ]);
2232
+ const possiblePositions = React.useMemo(() => {
2233
+ return Array.from(new Set([
2234
+ position
2235
+ ].concat(filteredToasts.filter((toast2) => toast2.position).map((toast2) => toast2.position))));
2236
+ }, [
2237
+ filteredToasts,
2238
+ position
2239
+ ]);
2240
+ const [heights, setHeights] = React.useState([]);
2241
+ const [expanded, setExpanded] = React.useState(false);
2242
+ const [interacting, setInteracting] = React.useState(false);
2243
+ const [actualTheme, setActualTheme] = React.useState(theme !== "system" ? theme : typeof window !== "undefined" ? window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : "light");
2244
+ const listRef = React.useRef(null);
2245
+ const hotkeyLabel = hotkey.join("+").replace(/Key/g, "").replace(/Digit/g, "");
2246
+ const lastFocusedElementRef = React.useRef(null);
2247
+ const isFocusWithinRef = React.useRef(false);
2248
+ const removeToast = React.useCallback((toastToRemove) => {
2249
+ setToasts((toasts2) => {
2250
+ var _toasts_find;
2251
+ if (!((_toasts_find = toasts2.find((toast2) => toast2.id === toastToRemove.id)) == null ? void 0 : _toasts_find.delete)) {
2252
+ ToastState.dismiss(toastToRemove.id);
2253
+ }
2254
+ return toasts2.filter(({ id: id2 }) => id2 !== toastToRemove.id);
2255
+ });
2256
+ }, []);
2257
+ React.useEffect(() => {
2258
+ return ToastState.subscribe((toast2) => {
2259
+ if (toast2.dismiss) {
2260
+ requestAnimationFrame(() => {
2261
+ setToasts((toasts2) => toasts2.map((t) => t.id === toast2.id ? {
2262
+ ...t,
2263
+ delete: true
2264
+ } : t));
2265
+ });
2266
+ return;
2267
+ }
2268
+ setTimeout(() => {
2269
+ ReactDOM.flushSync(() => {
2270
+ setToasts((toasts2) => {
2271
+ const indexOfExistingToast = toasts2.findIndex((t) => t.id === toast2.id);
2272
+ if (indexOfExistingToast !== -1) {
2273
+ return [
2274
+ ...toasts2.slice(0, indexOfExistingToast),
2275
+ {
2276
+ ...toasts2[indexOfExistingToast],
2277
+ ...toast2
2278
+ },
2279
+ ...toasts2.slice(indexOfExistingToast + 1)
2280
+ ];
2281
+ }
2282
+ return [
2283
+ toast2,
2284
+ ...toasts2
2285
+ ];
2286
+ });
2287
+ });
2288
+ });
2289
+ });
2290
+ }, [
2291
+ toasts
2292
+ ]);
2293
+ React.useEffect(() => {
2294
+ if (theme !== "system") {
2295
+ setActualTheme(theme);
2296
+ return;
2297
+ }
2298
+ if (theme === "system") {
2299
+ if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
2300
+ setActualTheme("dark");
2301
+ } else {
2302
+ setActualTheme("light");
2303
+ }
2304
+ }
2305
+ if (typeof window === "undefined") return;
2306
+ const darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
2307
+ try {
2308
+ darkMediaQuery.addEventListener("change", ({ matches }) => {
2309
+ if (matches) {
2310
+ setActualTheme("dark");
2311
+ } else {
2312
+ setActualTheme("light");
2313
+ }
2314
+ });
2315
+ } catch (error) {
2316
+ darkMediaQuery.addListener(({ matches }) => {
2317
+ try {
2318
+ if (matches) {
2319
+ setActualTheme("dark");
2320
+ } else {
2321
+ setActualTheme("light");
2322
+ }
2323
+ } catch (e) {
2324
+ console.error(e);
2325
+ }
2326
+ });
2327
+ }
2328
+ }, [
2329
+ theme
2330
+ ]);
2331
+ React.useEffect(() => {
2332
+ if (toasts.length <= 1) {
2333
+ setExpanded(false);
2334
+ }
2335
+ }, [
2336
+ toasts
2337
+ ]);
2338
+ React.useEffect(() => {
2339
+ const handleKeyDown = (event) => {
2340
+ var _listRef_current;
2341
+ const isHotkeyPressed = hotkey.every((key) => event[key] || event.code === key);
2342
+ if (isHotkeyPressed) {
2343
+ var _listRef_current1;
2344
+ setExpanded(true);
2345
+ (_listRef_current1 = listRef.current) == null ? void 0 : _listRef_current1.focus();
2346
+ }
2347
+ if (event.code === "Escape" && (document.activeElement === listRef.current || ((_listRef_current = listRef.current) == null ? void 0 : _listRef_current.contains(document.activeElement)))) {
2348
+ setExpanded(false);
2349
+ }
2350
+ };
2351
+ document.addEventListener("keydown", handleKeyDown);
2352
+ return () => document.removeEventListener("keydown", handleKeyDown);
2353
+ }, [
2354
+ hotkey
2355
+ ]);
2356
+ React.useEffect(() => {
2357
+ if (listRef.current) {
2358
+ return () => {
2359
+ if (lastFocusedElementRef.current) {
2360
+ lastFocusedElementRef.current.focus({
2361
+ preventScroll: true
2362
+ });
2363
+ lastFocusedElementRef.current = null;
2364
+ isFocusWithinRef.current = false;
2365
+ }
2366
+ };
2367
+ }
2368
+ }, [
2369
+ listRef.current
2370
+ ]);
2371
+ return (
2372
+ // Remove item from normal navigation flow, only available via hotkey
2373
+ /* @__PURE__ */ React.createElement("section", {
2374
+ ref,
2375
+ "aria-label": `${containerAriaLabel} ${hotkeyLabel}`,
2376
+ tabIndex: -1,
2377
+ "aria-live": "polite",
2378
+ "aria-relevant": "additions text",
2379
+ "aria-atomic": "false",
2380
+ suppressHydrationWarning: true
2381
+ }, possiblePositions.map((position2, index) => {
2382
+ var _heights_;
2383
+ const [y, x] = position2.split("-");
2384
+ if (!filteredToasts.length) return null;
2385
+ return /* @__PURE__ */ React.createElement("ol", {
2386
+ key: position2,
2387
+ dir: dir === "auto" ? getDocumentDirection() : dir,
2388
+ tabIndex: -1,
2389
+ ref: listRef,
2390
+ className,
2391
+ "data-sonner-toaster": true,
2392
+ "data-sonner-theme": actualTheme,
2393
+ "data-y-position": y,
2394
+ "data-x-position": x,
2395
+ style: {
2396
+ "--front-toast-height": `${((_heights_ = heights[0]) == null ? void 0 : _heights_.height) || 0}px`,
2397
+ "--width": `${TOAST_WIDTH}px`,
2398
+ "--gap": `${gap}px`,
2399
+ ...style,
2400
+ ...assignOffset(offset, mobileOffset)
2401
+ },
2402
+ onBlur: (event) => {
2403
+ if (isFocusWithinRef.current && !event.currentTarget.contains(event.relatedTarget)) {
2404
+ isFocusWithinRef.current = false;
2405
+ if (lastFocusedElementRef.current) {
2406
+ lastFocusedElementRef.current.focus({
2407
+ preventScroll: true
2408
+ });
2409
+ lastFocusedElementRef.current = null;
2410
+ }
2411
+ }
2412
+ },
2413
+ onFocus: (event) => {
2414
+ const isNotDismissible = event.target instanceof HTMLElement && event.target.dataset.dismissible === "false";
2415
+ if (isNotDismissible) return;
2416
+ if (!isFocusWithinRef.current) {
2417
+ isFocusWithinRef.current = true;
2418
+ lastFocusedElementRef.current = event.relatedTarget;
2419
+ }
2420
+ },
2421
+ onMouseEnter: () => setExpanded(true),
2422
+ onMouseMove: () => setExpanded(true),
2423
+ onMouseLeave: () => {
2424
+ if (!interacting) {
2425
+ setExpanded(false);
2426
+ }
2427
+ },
2428
+ onDragEnd: () => setExpanded(false),
2429
+ onPointerDown: (event) => {
2430
+ const isNotDismissible = event.target instanceof HTMLElement && event.target.dataset.dismissible === "false";
2431
+ if (isNotDismissible) return;
2432
+ setInteracting(true);
2433
+ },
2434
+ onPointerUp: () => setInteracting(false)
2435
+ }, filteredToasts.filter((toast2) => !toast2.position && index === 0 || toast2.position === position2).map((toast2, index2) => {
2436
+ var _toastOptions_duration, _toastOptions_closeButton;
2437
+ return /* @__PURE__ */ React.createElement(Toast, {
2438
+ key: toast2.id,
2439
+ icons,
2440
+ index: index2,
2441
+ toast: toast2,
2442
+ defaultRichColors: richColors,
2443
+ duration: (_toastOptions_duration = toastOptions == null ? void 0 : toastOptions.duration) != null ? _toastOptions_duration : duration,
2444
+ className: toastOptions == null ? void 0 : toastOptions.className,
2445
+ descriptionClassName: toastOptions == null ? void 0 : toastOptions.descriptionClassName,
2446
+ invert,
2447
+ visibleToasts,
2448
+ closeButton: (_toastOptions_closeButton = toastOptions == null ? void 0 : toastOptions.closeButton) != null ? _toastOptions_closeButton : closeButton,
2449
+ interacting,
2450
+ position: position2,
2451
+ style: toastOptions == null ? void 0 : toastOptions.style,
2452
+ unstyled: toastOptions == null ? void 0 : toastOptions.unstyled,
2453
+ classNames: toastOptions == null ? void 0 : toastOptions.classNames,
2454
+ cancelButtonStyle: toastOptions == null ? void 0 : toastOptions.cancelButtonStyle,
2455
+ actionButtonStyle: toastOptions == null ? void 0 : toastOptions.actionButtonStyle,
2456
+ closeButtonAriaLabel: toastOptions == null ? void 0 : toastOptions.closeButtonAriaLabel,
2457
+ removeToast,
2458
+ toasts: filteredToasts.filter((t) => t.position == toast2.position),
2459
+ heights: heights.filter((h) => h.position == toast2.position),
2460
+ setHeights,
2461
+ expandByDefault: expand,
2462
+ gap,
2463
+ expanded,
2464
+ swipeDirections: props.swipeDirections
2465
+ });
2466
+ }));
2467
+ }))
2468
+ );
2469
+ });
1584
2470
  function FunnelToaster(props = {}) {
1585
2471
  return createElement(Toaster, { position: "top-center", ...props });
1586
2472
  }
1587
2473
  function renderToaster(toaster) {
1588
2474
  if (toaster === false) return null;
1589
- return createElement(Toaster, { position: "top-center", ...toaster === true ? {} : toaster });
2475
+ return /* @__PURE__ */ jsx(FunnelToaster, { ...toaster === true ? {} : toaster });
1590
2476
  }
1591
2477
 
1592
2478
  // src/state/namespaces.ts
@@ -1685,12 +2571,19 @@ function FunnelProvider({
1685
2571
  getVisitorId: () => ctx.identity.visitorId
1686
2572
  });
1687
2573
  const busTracker = withBus(resolvedTracker, bus);
2574
+ const emailForCheckout = () => {
2575
+ const value2 = s.get("user.email");
2576
+ return typeof value2 === "string" && /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(value2) ? value2 : void 0;
2577
+ };
2578
+ const catalog = buildCatalog(products ?? []);
1688
2579
  return {
1689
2580
  funnel: createFunnel(s, ctx, busTracker, experiments ?? []),
1690
2581
  store: s,
1691
2582
  context: ctx,
1692
- catalog: buildCatalog(products ?? []),
1693
- driver: checkout ?? createMockDriver(),
2583
+ catalog,
2584
+ // Slot → catalog-key resolution wraps the email-enriching driver, so the wire charges the real
2585
+ // catalog key while pages + analytics keep the durable SLOT identity (req.product).
2586
+ driver: driverWithCatalog(driverWithEmail(checkout ?? createMockDriver(), emailForCheckout), catalog),
1694
2587
  tracker: busTracker,
1695
2588
  bus
1696
2589
  };
@@ -1793,6 +2686,232 @@ function useContextValue(path) {
1793
2686
  [ctx, path]
1794
2687
  );
1795
2688
  }
2689
+
2690
+ // src/tracking/platformTracker.ts
2691
+ function validClickIds(acq) {
2692
+ return Object.entries(acq.clickIds ?? {}).filter(
2693
+ (e) => typeof e[1] === "string" && e[1] !== ""
2694
+ );
2695
+ }
2696
+ function acquisitionQueryBag(acq) {
2697
+ const q = {};
2698
+ const utm = acq.utm ?? {};
2699
+ for (const [k, v] of Object.entries({
2700
+ utm_source: utm.source,
2701
+ utm_medium: utm.medium,
2702
+ utm_campaign: utm.campaign,
2703
+ utm_term: utm.term,
2704
+ utm_content: utm.content
2705
+ })) if (v) q[k] = v;
2706
+ for (const [k, v] of validClickIds(acq)) q[k] = v;
2707
+ if (acq.cookies?.fbp) q._fbp = acq.cookies.fbp;
2708
+ if (acq.cookies?.fbc) q._fbc = acq.cookies.fbc;
2709
+ return q;
2710
+ }
2711
+ function acquisitionAdAttribution(acq) {
2712
+ const a = {};
2713
+ for (const [k, v] of validClickIds(acq)) a[k] = v;
2714
+ if (acq.cookies?.fbp) a.fbp = acq.cookies.fbp;
2715
+ if (acq.cookies?.fbc) a.fbc = acq.cookies.fbc;
2716
+ else if (acq.clickIds?.fbclid) a.fbc = `fb.1.${Date.now()}.${acq.clickIds.fbclid}`;
2717
+ return a;
2718
+ }
2719
+ function createPlatformTracker(cfg) {
2720
+ if (typeof window === "undefined" || cfg.mode === "test") return createConsoleTracker();
2721
+ const base = cfg.apiBase.replace(/\/+$/, "");
2722
+ const url = `${base}/campaign/${encodeURIComponent(cfg.campaignId)}/event`;
2723
+ const sidKey = `af_sid:${cfg.campaignId}`;
2724
+ let sessionId = null;
2725
+ try {
2726
+ sessionId = window.sessionStorage.getItem(sidKey);
2727
+ } catch {
2728
+ }
2729
+ if (!sessionId && cfg.sessionId) sessionId = cfg.sessionId;
2730
+ let acquisition = null;
2731
+ let acquisitionSent = false;
2732
+ let chain = Promise.resolve();
2733
+ const send = async (event, data, userData) => {
2734
+ let mergedUserData = userData;
2735
+ let adAttribution;
2736
+ let attachingAcquisition = false;
2737
+ if (acquisition && !acquisitionSent) {
2738
+ attachingAcquisition = true;
2739
+ const query = acquisitionQueryBag(acquisition);
2740
+ mergedUserData = Object.keys(query).length ? { ...userData, query } : userData;
2741
+ const ad = acquisitionAdAttribution(acquisition);
2742
+ if (Object.keys(ad).length) adAttribution = ad;
2743
+ }
2744
+ const body = {
2745
+ campaignId: cfg.campaignId,
2746
+ funnelId: cfg.funnelId,
2747
+ event,
2748
+ sessionId: sessionId ?? void 0,
2749
+ visitorId: cfg.visitorId ?? void 0,
2750
+ eventId: typeof data?.eventId === "string" ? data.eventId : void 0,
2751
+ data: data ?? {},
2752
+ metadata: {
2753
+ language: typeof navigator !== "undefined" ? navigator.language : void 0,
2754
+ screen: typeof window.screen !== "undefined" ? `${window.screen.width}x${window.screen.height}` : void 0,
2755
+ timezone: (() => {
2756
+ try {
2757
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
2758
+ } catch {
2759
+ return void 0;
2760
+ }
2761
+ })(),
2762
+ referrer: typeof document !== "undefined" && document.referrer ? document.referrer : void 0
2763
+ },
2764
+ ...mergedUserData ? { userData: mergedUserData } : {},
2765
+ ...adAttribution ? { adAttribution } : {}
2766
+ };
2767
+ const res = await fetch(url, {
2768
+ method: "POST",
2769
+ headers: { "content-type": "application/json" },
2770
+ body: JSON.stringify(body),
2771
+ keepalive: true
2772
+ // survives page.exit / unload
2773
+ });
2774
+ if (attachingAcquisition) acquisitionSent = true;
2775
+ const json = await res.json().catch(() => null);
2776
+ if (json?.sessionId && json.sessionId !== sessionId) {
2777
+ sessionId = json.sessionId;
2778
+ try {
2779
+ window.sessionStorage.setItem(sidKey, sessionId);
2780
+ } catch {
2781
+ }
2782
+ }
2783
+ };
2784
+ const enqueue = (event, data, userData) => {
2785
+ const task = () => send(event, data, userData).catch((e) => {
2786
+ if (process.env.NODE_ENV !== "production") console.warn(`[appfunnel] track failed (${event}):`, e);
2787
+ });
2788
+ if (!sessionId) {
2789
+ chain = chain.then(task);
2790
+ } else {
2791
+ void task();
2792
+ }
2793
+ };
2794
+ const VARS_DEBOUNCE_MS = 800;
2795
+ let pendingVars = null;
2796
+ let varsTimer = null;
2797
+ const flushVars = () => {
2798
+ if (varsTimer) {
2799
+ clearTimeout(varsTimer);
2800
+ varsTimer = null;
2801
+ }
2802
+ if (!pendingVars) return;
2803
+ const doPost = () => {
2804
+ if (!pendingVars || !sessionId) return;
2805
+ const userData = pendingVars;
2806
+ pendingVars = null;
2807
+ void fetch(`${base}/campaign/${encodeURIComponent(cfg.campaignId)}/event/session/data`, {
2808
+ method: "POST",
2809
+ headers: { "content-type": "application/json" },
2810
+ body: JSON.stringify({ sessionId, userData }),
2811
+ keepalive: true
2812
+ }).catch((e) => {
2813
+ if (process.env.NODE_ENV !== "production") console.warn("[appfunnel] variable persist failed:", e);
2814
+ });
2815
+ };
2816
+ if (sessionId) doPost();
2817
+ else chain = chain.then(doPost);
2818
+ };
2819
+ try {
2820
+ window.addEventListener("pagehide", flushVars);
2821
+ } catch {
2822
+ }
2823
+ if (cfg.experiment?.id && cfg.experiment.variant) {
2824
+ enqueue("experiment.exposure", exposurePayload(cfg.experiment.id, cfg.experiment.variant, cfg.experiment.version));
2825
+ }
2826
+ return {
2827
+ track: (event, data, userData) => enqueue(event, data, userData),
2828
+ identify: (email, eventId) => enqueue("user.registered", { email, ...eventId ? { eventId } : {} }),
2829
+ setVariables: (variables) => {
2830
+ pendingVars = variables;
2831
+ if (varsTimer) clearTimeout(varsTimer);
2832
+ varsTimer = setTimeout(flushVars, VARS_DEBOUNCE_MS);
2833
+ },
2834
+ setAcquisition: (acq) => {
2835
+ acquisition = acq;
2836
+ }
2837
+ };
2838
+ }
2839
+
2840
+ // src/flow/mount.tsx
2841
+ var AssetsContext = createContext({});
2842
+ function AssetsProvider({
2843
+ assets,
2844
+ children
2845
+ }) {
2846
+ return createElement(AssetsContext.Provider, { value: assets ?? {} }, children);
2847
+ }
2848
+ function useAssets() {
2849
+ return useContext(AssetsContext);
2850
+ }
2851
+ function useAsset(name) {
2852
+ return useContext(AssetsContext)[name];
2853
+ }
2854
+ function createFunnelTree({
2855
+ config,
2856
+ pages,
2857
+ layout,
2858
+ checkoutDriver,
2859
+ messages
2860
+ }) {
2861
+ return function tree(opts) {
2862
+ const mode = opts.mode ?? "live";
2863
+ const tracker = opts.tracking ? createPlatformTracker({
2864
+ ...opts.tracking,
2865
+ visitorId: opts.visitorId,
2866
+ sessionId: opts.sessionId,
2867
+ mode
2868
+ }) : void 0;
2869
+ const checkout = checkoutDriver && opts.tracking ? checkoutDriver({
2870
+ apiBase: opts.tracking.apiBase,
2871
+ campaignId: opts.tracking.campaignId,
2872
+ funnelId: opts.tracking.funnelId,
2873
+ mode,
2874
+ sessionId: opts.sessionId,
2875
+ visitorId: opts.visitorId
2876
+ }) : void 0;
2877
+ const layoutWithResume = function LayoutWithCheckoutResume({
2878
+ children
2879
+ }) {
2880
+ return createElement(
2881
+ Fragment,
2882
+ null,
2883
+ createElement(CheckoutResume, null),
2884
+ layout ? createElement(layout, null, children) : children
2885
+ );
2886
+ };
2887
+ return createElement(AssetsProvider, {
2888
+ assets: opts.assets,
2889
+ children: createElement(FunnelProvider, {
2890
+ config,
2891
+ products: opts.products ?? [],
2892
+ messages: opts.messages ?? messages,
2893
+ locale: opts.locale,
2894
+ tracker,
2895
+ checkout,
2896
+ sessionValues: opts.sessionValues,
2897
+ experiments: opts.experiments,
2898
+ context: {
2899
+ mode,
2900
+ visitorId: opts.visitorId ?? null,
2901
+ customerId: opts.customerId ?? null,
2902
+ sessionId: opts.sessionId ?? null
2903
+ },
2904
+ children: createElement(FunnelView, {
2905
+ pages,
2906
+ layout: layoutWithResume,
2907
+ initialKey: opts.initialKey,
2908
+ prefetch: "off"
2909
+ // renderer background-modulepreloads all chunks
2910
+ })
2911
+ })
2912
+ });
2913
+ };
2914
+ }
1796
2915
  function Choice({
1797
2916
  bind,
1798
2917
  options,
@@ -1932,95 +3051,6 @@ function Script({
1932
3051
  return null;
1933
3052
  }
1934
3053
 
1935
- // src/manifest/manifest.ts
1936
- function isVariantNode(page) {
1937
- return isVariantKey(page.key);
1938
- }
1939
- function serializeGate(when) {
1940
- if (!when) return void 0;
1941
- if (typeof when === "function") return { kind: "predicate" };
1942
- return { kind: "declarative", condition: when };
1943
- }
1944
- function compileManifest(input) {
1945
- const { funnel, pages } = input;
1946
- const keys = new Set(pages.map((p) => p.key));
1947
- const manifestPages = pages.map((p, index) => ({
1948
- id: p.key,
1949
- key: p.key,
1950
- type: p.meta?.type ?? "default",
1951
- slug: p.meta?.slug ?? p.key,
1952
- index,
1953
- variantOf: isVariantKey(p.key) ? parseSlotKey(p.key).slot : void 0
1954
- }));
1955
- const edges = [];
1956
- const badTargets = [];
1957
- const nextAnchor = (from) => {
1958
- for (let j = from + 1; j < pages.length; j++) {
1959
- if (!isVariantNode(pages[j])) return pages[j];
1960
- }
1961
- return void 0;
1962
- };
1963
- pages.forEach((p, i) => {
1964
- if (isVariantNode(p)) return;
1965
- const routes = p.meta?.next ?? [];
1966
- let hasUnconditional = false;
1967
- for (const route of routes) {
1968
- if (!keys.has(route.to)) {
1969
- badTargets.push({ from: p.key, to: route.to });
1970
- continue;
1971
- }
1972
- edges.push({ from: p.key, to: route.to, kind: "branch", condition: serializeGate(route.when) });
1973
- if (!route.when) hasUnconditional = true;
1974
- }
1975
- const next = nextAnchor(i);
1976
- if (!hasUnconditional && next && p.meta?.type !== "finish") {
1977
- edges.push({ from: p.key, to: next.key, kind: "linear" });
1978
- }
1979
- });
1980
- const startPage = pages.find((p) => !isVariantNode(p));
1981
- const start = startPage?.key ?? null;
1982
- const variantKeys = new Set(pages.filter(isVariantNode).map((p) => p.key));
1983
- const validation = validate(manifestPages, edges, badTargets, start, variantKeys);
1984
- return {
1985
- id: funnel.id,
1986
- pages: manifestPages,
1987
- flow: { start, nodes: manifestPages.map((p) => p.id), edges },
1988
- products: funnel.products ?? [],
1989
- locales: funnel.locales,
1990
- validation
1991
- };
1992
- }
1993
- function validate(pages, edges, badTargets, start, variantKeys) {
1994
- const outgoing = /* @__PURE__ */ new Map();
1995
- for (const e of edges) outgoing.set(e.from, (outgoing.get(e.from) ?? 0) + 1);
1996
- const deadEnds = pages.filter((p) => p.type !== "finish" && !variantKeys.has(p.id) && !(outgoing.get(p.id) > 0)).map((p) => p.id);
1997
- const reachable = /* @__PURE__ */ new Set();
1998
- if (start) {
1999
- const adj = /* @__PURE__ */ new Map();
2000
- for (const e of edges) {
2001
- const list = adj.get(e.from);
2002
- if (list) list.push(e.to);
2003
- else adj.set(e.from, [e.to]);
2004
- }
2005
- const stack = [start];
2006
- while (stack.length) {
2007
- const id = stack.pop();
2008
- if (reachable.has(id)) continue;
2009
- reachable.add(id);
2010
- for (const to of adj.get(id) ?? []) stack.push(to);
2011
- }
2012
- }
2013
- const unreachable = pages.filter((p) => !variantKeys.has(p.id) && !reachable.has(p.id)).map((p) => p.id);
2014
- const missingEmailCapture = !pages.some((p) => p.type === "email-capture");
2015
- return {
2016
- ok: deadEnds.length === 0 && unreachable.length === 0 && badTargets.length === 0 && !missingEmailCapture,
2017
- deadEnds,
2018
- unreachable,
2019
- badTargets,
2020
- missingEmailCapture
2021
- };
2022
- }
2023
-
2024
- export { Back, Checkout, Choice, EVENT_CATALOG, FunnelProvider, FunnelToaster, FunnelView, INLINE_SURFACES, Modal, Next, PROVIDER_PROFILES, Script, Sheet, Upsell, VariableStore, assignVariant, attachBus, bucketingSeed, buildAcquisition, buildCatalog, checkoutError, compileManifest, createBus, createConsoleTracker, createFunnelStore, createMockDriver, defineFunnel, defineModal, definePage, entryGuard, evaluateCondition, evaluateGate, expectedPathLength, fnv1a, formatProduct, hashToUnit, hideModal, integrationEvents, isInlineSurface, isIntegrationEvent, isMerchantOfRecord, isOrchestrator, isRtl, isTrackableEvent, isVariantKey, newEventId, nextPage, outgoingKeys, pageMeta, parseSlotKey, pickByWeight, registerModal, removeModal, resolveExperiments, resolveLocale, resolveProduct, resolveRoute, showModal, surfacesFor, unregisterModal, useCheckout, useClickIds, useContextValue, useData, useDevice, useExperiment, useField, useFunnel, useLocale, useModal, useNavigation, usePage, useProduct, useProducts, useResponse, useSystem, useTracker, useTranslation, useUserAttribute, useUtm, validateCheckout, validateExperiments, validateUpsell, withBus };
3054
+ export { AssetsContext, AssetsProvider, Back, Checkout, CheckoutResume, Choice, EVENT_CATALOG, FunnelProvider, FunnelToaster, FunnelView, Modal, Next, Script, Sheet, Upsell, VariableStore, attachBus, buildAcquisition, buildContext, createBus, createConsoleTracker, createFunnelStore, createFunnelTree, createMockDriver, createPlatformTracker, defineModal, driverWithEmail, hideModal, integrationEvents, isIntegrationEvent, isTrackableEvent, newEventId, registerModal, removeModal, showModal, toast, unregisterModal, useActiveLocale, useAsset, useAssets, useCheckout, useClickIds, useContextValue, useData, useDevice, useExperiment, useField, useFunnel, useLocale, useModal, useNavigation, usePage, useProduct, useProducts, useResponse, useSystem, useTracker, useTranslation, useUserAttribute, useUtm, withBus };
2025
3055
  //# sourceMappingURL=index.js.map
2026
3056
  //# sourceMappingURL=index.js.map