@hook-sdk/template 0.14.1 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -64,14 +64,15 @@ __export(index_exports, {
64
64
  useResetForm: () => useResetForm,
65
65
  useSignupForm: () => useSignupForm,
66
66
  useSubscription: () => useSubscription,
67
- useToast: () => useToast
67
+ useToast: () => useToast,
68
+ useTrackOnboardingStep: () => import_sdk19.useTrackOnboardingStep
68
69
  });
69
70
  module.exports = __toCommonJS(index_exports);
70
71
 
71
72
  // src/AppRoot.tsx
72
- var import_react12 = require("react");
73
+ var import_react13 = require("react");
73
74
  var import_react_router_dom2 = require("react-router-dom");
74
- var import_sdk5 = require("@hook-sdk/sdk");
75
+ var import_sdk6 = require("@hook-sdk/sdk");
75
76
 
76
77
  // src/config/AppConfigContext.tsx
77
78
  var import_react = require("react");
@@ -248,8 +249,8 @@ function ThemeProvider({ children }) {
248
249
  }
249
250
 
250
251
  // src/hooks/usePaywallState.ts
251
- var import_react5 = require("react");
252
- var import_sdk2 = require("@hook-sdk/sdk");
252
+ var import_react6 = require("react");
253
+ var import_sdk3 = require("@hook-sdk/sdk");
253
254
 
254
255
  // src/errors/asaas-pt-br.ts
255
256
  var MAP = {
@@ -270,6 +271,35 @@ function asaasErrorMessage(code) {
270
271
  return MAP[code] ?? "Ocorreu um erro inesperado. Tente novamente em instantes.";
271
272
  }
272
273
 
274
+ // src/hooks/usePaywallTracker.ts
275
+ var import_react5 = require("react");
276
+ var import_sdk2 = require("@hook-sdk/sdk");
277
+ function deriveStep(s) {
278
+ if (s.pixPaid) return "success";
279
+ if (s.hasError) return "error";
280
+ if (s.pixPendingShown) return "pix_qr_shown";
281
+ if (s.selectedMethod === "card" && s.cpfValid) return "card_form";
282
+ if (s.cpfRequired && !s.cpfValid) return "cpf_input";
283
+ if (s.selectedMethod) return "method_select";
284
+ return "plan_select";
285
+ }
286
+ function usePaywallTracker(snapshot) {
287
+ const ctx = (0, import_sdk2.useHook)();
288
+ const track2 = typeof ctx.track === "function" ? ctx.track : void 0;
289
+ const lastStepRef = (0, import_react5.useRef)(null);
290
+ const step = deriveStep(snapshot);
291
+ (0, import_react5.useEffect)(() => {
292
+ if (lastStepRef.current === step) return;
293
+ lastStepRef.current = step;
294
+ if (!track2) return;
295
+ track2("paywall_step_viewed", {
296
+ step,
297
+ method: snapshot.selectedMethod,
298
+ cycle: snapshot.cycle
299
+ });
300
+ }, [step]);
301
+ }
302
+
273
303
  // src/hooks/usePaywallState.ts
274
304
  function isCheckoutFailure(r) {
275
305
  return "ok" in r && r.ok === false;
@@ -284,11 +314,11 @@ var FALLBACK_PAYWALL = {
284
314
  };
285
315
  var isMethodAvailable = (availability, method) => availability[method] !== false;
286
316
  function usePaywallState() {
287
- const { subscription, plan } = (0, import_sdk2.useHook)();
288
- const configFromCtx = (0, import_react5.useContext)(AppConfigContext);
317
+ const { subscription, plan } = (0, import_sdk3.useHook)();
318
+ const configFromCtx = (0, import_react6.useContext)(AppConfigContext);
289
319
  const paywall = configFromCtx?.paywall ?? FALLBACK_PAYWALL;
290
320
  const isFree = paywall.mode === "free";
291
- const declaredMethods = (0, import_react5.useMemo)(
321
+ const declaredMethods = (0, import_react6.useMemo)(
292
322
  () => isFree ? [] : paywall.checkoutMethods,
293
323
  [isFree, paywall]
294
324
  );
@@ -297,33 +327,33 @@ function usePaywallState() {
297
327
  "pix-auto": null,
298
328
  "pix-once": null
299
329
  };
300
- const methods = (0, import_react5.useMemo)(
330
+ const methods = (0, import_react6.useMemo)(
301
331
  () => declaredMethods.filter((m) => isMethodAvailable(availability, m)),
302
332
  [declaredMethods, availability]
303
333
  );
304
334
  const defaultMethod = methods[0] ?? declaredMethods[0] ?? "card";
305
- const [selectedMethodRaw, setSelectedMethod] = (0, import_react5.useState)(defaultMethod);
335
+ const [selectedMethodRaw, setSelectedMethod] = (0, import_react6.useState)(defaultMethod);
306
336
  const selectedMethod = methods.includes(selectedMethodRaw) ? selectedMethodRaw : methods[0] ?? selectedMethodRaw;
307
337
  const initialCycle = isFree ? "MONTHLY" : paywall.cycles[0] ?? "MONTHLY";
308
- const [cycle, setCycle] = (0, import_react5.useState)(initialCycle);
338
+ const [cycle, setCycle] = (0, import_react6.useState)(initialCycle);
309
339
  const cpfRequired = !isFree && paywall.requiresCpf;
310
- const [cpf, setCpf] = (0, import_react5.useState)("");
311
- const cpfValid = (0, import_react5.useMemo)(() => /^[0-9]{11}$/.test(cpf), [cpf]);
312
- const [card, setCardState] = (0, import_react5.useState)({
340
+ const [cpf, setCpf] = (0, import_react6.useState)("");
341
+ const cpfValid = (0, import_react6.useMemo)(() => /^[0-9]{11}$/.test(cpf), [cpf]);
342
+ const [card, setCardState] = (0, import_react6.useState)({
313
343
  number: "",
314
344
  cvv: "",
315
345
  expiry: "",
316
346
  holder: ""
317
347
  });
318
- const setCard = (0, import_react5.useCallback)((patch) => {
348
+ const setCard = (0, import_react6.useCallback)((patch) => {
319
349
  setCardState((prev) => ({ ...prev, ...patch }));
320
350
  }, []);
321
- const [error, setError] = (0, import_react5.useState)(null);
322
- const [submitting, setSubmitting] = (0, import_react5.useState)(false);
351
+ const [error, setError] = (0, import_react6.useState)(null);
352
+ const [submitting, setSubmitting] = (0, import_react6.useState)(false);
323
353
  const status = subscription.status();
324
354
  const daysLeftInTrial = subscription.daysLeftInTrial();
325
355
  const initialLoadComplete = subscription.initialLoadComplete;
326
- const pixPending = (0, import_react5.useMemo)(() => {
356
+ const pixPending = (0, import_react6.useMemo)(() => {
327
357
  const sdkPix = subscription.pixPending;
328
358
  if (!sdkPix) return null;
329
359
  const liveStatus = subscription.current?.status;
@@ -336,7 +366,7 @@ function usePaywallState() {
336
366
  paid
337
367
  };
338
368
  }, [subscription.pixPending, subscription.current]);
339
- const monthlyEquivalent = (0, import_react5.useCallback)(
369
+ const monthlyEquivalent = (0, import_react6.useCallback)(
340
370
  (c) => {
341
371
  const monthlyCents = plan.data?.priceCents ?? (isFree ? 0 : paywall.prices.monthlyCents);
342
372
  const yearlyCents = plan.data?.yearlyPriceCents ?? (isFree ? null : paywall.prices.yearlyCents);
@@ -345,7 +375,7 @@ function usePaywallState() {
345
375
  },
346
376
  [plan, paywall, isFree]
347
377
  );
348
- const planDerived = (0, import_react5.useMemo)(() => {
378
+ const planDerived = (0, import_react6.useMemo)(() => {
349
379
  if (isFree) return null;
350
380
  const monthlyCents = paywall.prices.monthlyCents;
351
381
  const yearlyCents = paywall.prices.yearlyCents;
@@ -361,7 +391,7 @@ function usePaywallState() {
361
391
  };
362
392
  }, [paywall, cycle, isFree]);
363
393
  const useDefaultMessages = paywall.mode !== "free" && paywall.errorMessages === "default";
364
- const buildError = (0, import_react5.useCallback)(
394
+ const buildError = (0, import_react6.useCallback)(
365
395
  (code, fallbackMessage) => ({
366
396
  code,
367
397
  message: fallbackMessage,
@@ -369,7 +399,7 @@ function usePaywallState() {
369
399
  }),
370
400
  [useDefaultMessages]
371
401
  );
372
- const submit = (0, import_react5.useCallback)(async () => {
402
+ const submit = (0, import_react6.useCallback)(async () => {
373
403
  setSubmitting(true);
374
404
  setError(null);
375
405
  const methodToUse = selectedMethod;
@@ -431,7 +461,7 @@ function usePaywallState() {
431
461
  return void 0;
432
462
  }
433
463
  }, [selectedMethod, availability, subscription, cycle, cpf, card, buildError]);
434
- const checkout = (0, import_react5.useCallback)(
464
+ const checkout = (0, import_react6.useCallback)(
435
465
  async (args) => {
436
466
  setSubmitting(true);
437
467
  setError(null);
@@ -495,7 +525,7 @@ function usePaywallState() {
495
525
  },
496
526
  [subscription, buildError]
497
527
  );
498
- const cancel = (0, import_react5.useCallback)(async () => {
528
+ const cancel = (0, import_react6.useCallback)(async () => {
499
529
  try {
500
530
  await subscription.cancel();
501
531
  await subscription.refresh();
@@ -505,14 +535,24 @@ function usePaywallState() {
505
535
  setError(buildError(code, message));
506
536
  }
507
537
  }, [subscription, buildError]);
508
- const cardState = (0, import_react5.useMemo)(
538
+ const cardState = (0, import_react6.useMemo)(
509
539
  () => ({ ...card, set: setCard }),
510
540
  [card, setCard]
511
541
  );
512
- const cpfState = (0, import_react5.useMemo)(
542
+ const cpfState = (0, import_react6.useMemo)(
513
543
  () => ({ required: cpfRequired, value: cpf, set: setCpf, valid: cpfValid }),
514
544
  [cpfRequired, cpf, cpfValid]
515
545
  );
546
+ usePaywallTracker({
547
+ selectedMethod,
548
+ cycle,
549
+ cpfRequired,
550
+ cpfValid,
551
+ pixPendingShown: pixPending !== null,
552
+ pixPaid: pixPending?.paid === true,
553
+ hasError: error !== null,
554
+ submitting
555
+ });
516
556
  return {
517
557
  // Subscription status (reactive, proxied from SDK)
518
558
  status,
@@ -567,10 +607,10 @@ function SubscriptionGate({ Paywall, children }) {
567
607
  }
568
608
 
569
609
  // src/components/InstallGate/InstallGate.tsx
570
- var import_react8 = require("react");
610
+ var import_react9 = require("react");
571
611
 
572
612
  // src/hooks/useInstallPrompt.ts
573
- var import_react6 = require("react");
613
+ var import_react7 = require("react");
574
614
  var ANDROID_PROMPT_WAIT_MS = 3e3;
575
615
  var IOS_RE = /iPad|iPhone|iPod/;
576
616
  var IOS_NON_SAFARI_RE = /CriOS|FxiOS|EdgiOS/;
@@ -705,27 +745,27 @@ function useInstallPrompt(slug) {
705
745
  const iosBrowser = detectIOSBrowser(ua);
706
746
  const androidBrowser = detectAndroidBrowser(ua);
707
747
  const inAppApp = detectInAppApp(ua);
708
- const [isInstallable, setIsInstallable] = (0, import_react6.useState)(() => {
748
+ const [isInstallable, setIsInstallable] = (0, import_react7.useState)(() => {
709
749
  if (typeof window === "undefined") return false;
710
750
  return window.__pwaInstallPrompt != null;
711
751
  });
712
- const [isInstalled, setIsInstalled] = (0, import_react6.useState)(() => {
752
+ const [isInstalled, setIsInstalled] = (0, import_react7.useState)(() => {
713
753
  const { installed } = detectStandalone();
714
754
  return installed || readInstalledMarker(slug);
715
755
  });
716
- const [isDismissedSession, setIsDismissedSession] = (0, import_react6.useState)(() => readSessionSkip(slug));
717
- const [isDismissedPermanent, setIsDismissedPermanent] = (0, import_react6.useState)(() => readPermanentDismiss(slug).dismissed);
718
- const [skipCount, setSkipCount] = (0, import_react6.useState)(() => readSkipCount(slug));
719
- const [promptWaitElapsed, setPromptWaitElapsed] = (0, import_react6.useState)(() => {
756
+ const [isDismissedSession, setIsDismissedSession] = (0, import_react7.useState)(() => readSessionSkip(slug));
757
+ const [isDismissedPermanent, setIsDismissedPermanent] = (0, import_react7.useState)(() => readPermanentDismiss(slug).dismissed);
758
+ const [skipCount, setSkipCount] = (0, import_react7.useState)(() => readSkipCount(slug));
759
+ const [promptWaitElapsed, setPromptWaitElapsed] = (0, import_react7.useState)(() => {
720
760
  if (typeof window === "undefined") return true;
721
761
  return window.__pwaInstallPrompt != null;
722
762
  });
723
- (0, import_react6.useEffect)(() => {
763
+ (0, import_react7.useEffect)(() => {
724
764
  if (promptWaitElapsed) return;
725
765
  const id = setTimeout(() => setPromptWaitElapsed(true), ANDROID_PROMPT_WAIT_MS);
726
766
  return () => clearTimeout(id);
727
767
  }, [promptWaitElapsed]);
728
- (0, import_react6.useEffect)(() => {
768
+ (0, import_react7.useEffect)(() => {
729
769
  if (typeof window === "undefined") return;
730
770
  if (window.__pwaInstallPrompt) {
731
771
  setIsInstallable(true);
@@ -749,7 +789,7 @@ function useInstallPrompt(slug) {
749
789
  window.removeEventListener("appinstalled", onInstalled);
750
790
  };
751
791
  }, [slug]);
752
- (0, import_react6.useEffect)(() => {
792
+ (0, import_react7.useEffect)(() => {
753
793
  if (typeof window === "undefined") return;
754
794
  const mq = window.matchMedia?.("(display-mode: standalone)");
755
795
  if (!mq) return;
@@ -776,7 +816,7 @@ function useInstallPrompt(slug) {
776
816
  },
777
817
  promptWaitElapsed
778
818
  );
779
- const promptInstall = (0, import_react6.useCallback)(async () => {
819
+ const promptInstall = (0, import_react7.useCallback)(async () => {
780
820
  if (typeof window === "undefined") return false;
781
821
  const prompt = window.__pwaInstallPrompt;
782
822
  if (!prompt) return false;
@@ -798,7 +838,7 @@ function useInstallPrompt(slug) {
798
838
  return false;
799
839
  }
800
840
  }, [slug]);
801
- const dismissSession = (0, import_react6.useCallback)(() => {
841
+ const dismissSession = (0, import_react7.useCallback)(() => {
802
842
  if (typeof sessionStorage !== "undefined") {
803
843
  try {
804
844
  sessionStorage.setItem(storageKey.sessionSkip(slug), "true");
@@ -812,20 +852,20 @@ function useInstallPrompt(slug) {
812
852
  setIsDismissedSession(true);
813
853
  track("pwa_install_session_skip", { slug, platform, skip_count: newCount });
814
854
  }, [slug, skipCount, platform]);
815
- const dismissPermanent = (0, import_react6.useCallback)(() => {
855
+ const dismissPermanent = (0, import_react7.useCallback)(() => {
816
856
  const storage = safeStorage();
817
857
  if (storage) storage.setItem(storageKey.dismissedAt(slug), (/* @__PURE__ */ new Date()).toISOString());
818
858
  setIsDismissedPermanent(true);
819
859
  track("pwa_install_permanent_dismiss", { slug, platform, prior_skip_count: skipCount });
820
860
  }, [slug, platform, skipCount]);
821
- const copyLink = (0, import_react6.useCallback)(async () => {
861
+ const copyLink = (0, import_react7.useCallback)(async () => {
822
862
  if (typeof navigator === "undefined" || typeof location === "undefined") return;
823
863
  try {
824
864
  await navigator.clipboard?.writeText?.(location.href);
825
865
  } catch {
826
866
  }
827
867
  }, []);
828
- const reset = (0, import_react6.useCallback)(() => {
868
+ const reset = (0, import_react7.useCallback)(() => {
829
869
  const storage = safeStorage();
830
870
  if (storage) {
831
871
  storage.removeItem(storageKey.dismissedAt(slug));
@@ -1618,7 +1658,7 @@ function IOSOtherVariant({
1618
1658
  }
1619
1659
 
1620
1660
  // src/components/InstallGate/variants/InAppBrowserVariant.tsx
1621
- var import_react7 = require("react");
1661
+ var import_react8 = require("react");
1622
1662
  var import_jsx_runtime14 = require("react/jsx-runtime");
1623
1663
  function InAppBrowserVariant({
1624
1664
  state,
@@ -1628,7 +1668,7 @@ function InAppBrowserVariant({
1628
1668
  const appCopy = INSTALL_COPY.inApp[app] ?? INSTALL_COPY.inApp.other;
1629
1669
  const copy = INSTALL_COPY.inApp;
1630
1670
  const showPermanent = shouldShowPermanentOption(state);
1631
- const [copied, setCopied] = (0, import_react7.useState)(false);
1671
+ const [copied, setCopied] = (0, import_react8.useState)(false);
1632
1672
  const handleCopy = async () => {
1633
1673
  await actions.copyLink();
1634
1674
  setCopied(true);
@@ -1833,8 +1873,8 @@ function InstallGate({ children }) {
1833
1873
  const enabled = features_enabled.includes("install_prompt");
1834
1874
  const installState = useInstallPrompt(slug);
1835
1875
  const shouldBlock = enabled && shouldBlockInstall(installState);
1836
- const trackedRef = (0, import_react8.useRef)(null);
1837
- (0, import_react8.useEffect)(() => {
1876
+ const trackedRef = (0, import_react9.useRef)(null);
1877
+ (0, import_react9.useEffect)(() => {
1838
1878
  if (!shouldBlock) return;
1839
1879
  if (typeof window === "undefined") return;
1840
1880
  const variantKey = `${slug}:${installState.variant}`;
@@ -1883,16 +1923,16 @@ function PushPrompt() {
1883
1923
  }
1884
1924
 
1885
1925
  // src/internal/SessionExpiredBanner.tsx
1886
- var import_react9 = require("react");
1887
- var import_sdk3 = require("@hook-sdk/sdk");
1926
+ var import_react10 = require("react");
1927
+ var import_sdk4 = require("@hook-sdk/sdk");
1888
1928
  var import_jsx_runtime17 = require("react/jsx-runtime");
1889
1929
  var DISMISS_KEY = "hook:session-expired-dismissed-until";
1890
1930
  var DISMISS_TTL_MS = 60 * 60 * 1e3;
1891
1931
  function SessionExpiredBanner() {
1892
- const { authStatus } = (0, import_sdk3.useHook)();
1893
- const wasAuthRef = (0, import_react9.useRef)(false);
1894
- const [show, setShow] = (0, import_react9.useState)(false);
1895
- (0, import_react9.useEffect)(() => {
1932
+ const { authStatus } = (0, import_sdk4.useHook)();
1933
+ const wasAuthRef = (0, import_react10.useRef)(false);
1934
+ const [show, setShow] = (0, import_react10.useState)(false);
1935
+ (0, import_react10.useEffect)(() => {
1896
1936
  if (authStatus === "authenticated") {
1897
1937
  wasAuthRef.current = true;
1898
1938
  setShow(false);
@@ -1942,9 +1982,9 @@ function SessionExpiredBanner() {
1942
1982
  }
1943
1983
 
1944
1984
  // src/defaults/ErrorBoundary.tsx
1945
- var import_react10 = require("react");
1985
+ var import_react11 = require("react");
1946
1986
  var import_jsx_runtime18 = require("react/jsx-runtime");
1947
- var ErrorBoundary = class extends import_react10.Component {
1987
+ var ErrorBoundary = class extends import_react11.Component {
1948
1988
  state = { error: null };
1949
1989
  static getDerivedStateFromError(error) {
1950
1990
  return { error };
@@ -1971,20 +2011,20 @@ var ErrorBoundary = class extends import_react10.Component {
1971
2011
  };
1972
2012
 
1973
2013
  // src/internal/PaymentReturnHandler.tsx
1974
- var import_react11 = require("react");
1975
- var import_sdk4 = require("@hook-sdk/sdk");
2014
+ var import_react12 = require("react");
2015
+ var import_sdk5 = require("@hook-sdk/sdk");
1976
2016
  var import_jsx_runtime19 = require("react/jsx-runtime");
1977
2017
  var BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 4e4];
1978
2018
  var MAX_CYCLES = 3;
1979
2019
  var SUPPORT_MAILTO = "mailto:suporte@usehook.net?subject=Pagamento%20pendente";
1980
2020
  function PaymentReturnHandler({ children }) {
1981
- const { subscription } = (0, import_sdk4.useHook)();
1982
- const subRef = (0, import_react11.useRef)(subscription);
2021
+ const { subscription } = (0, import_sdk5.useHook)();
2022
+ const subRef = (0, import_react12.useRef)(subscription);
1983
2023
  subRef.current = subscription;
1984
- const runIdRef = (0, import_react11.useRef)(0);
1985
- const cyclesRef = (0, import_react11.useRef)(0);
1986
- const [state, setState] = (0, import_react11.useState)("idle");
1987
- const runPoll = (0, import_react11.useCallback)(() => {
2024
+ const runIdRef = (0, import_react12.useRef)(0);
2025
+ const cyclesRef = (0, import_react12.useRef)(0);
2026
+ const [state, setState] = (0, import_react12.useState)("idle");
2027
+ const runPoll = (0, import_react12.useCallback)(() => {
1988
2028
  const runId = ++runIdRef.current;
1989
2029
  cyclesRef.current += 1;
1990
2030
  setState("confirming");
@@ -2019,7 +2059,7 @@ function PaymentReturnHandler({ children }) {
2019
2059
  };
2020
2060
  void tick();
2021
2061
  }, []);
2022
- (0, import_react11.useEffect)(() => {
2062
+ (0, import_react12.useEffect)(() => {
2023
2063
  if (typeof window === "undefined") return;
2024
2064
  const url = new URL(window.location.href);
2025
2065
  if (url.searchParams.get("paymentReturn") !== "1") return;
@@ -2029,7 +2069,7 @@ function PaymentReturnHandler({ children }) {
2029
2069
  runIdRef.current++;
2030
2070
  };
2031
2071
  }, [runPoll]);
2032
- const goHome = (0, import_react11.useCallback)(() => {
2072
+ const goHome = (0, import_react12.useCallback)(() => {
2033
2073
  const cleanUrl = new URL(window.location.href);
2034
2074
  cleanUrl.searchParams.delete("paymentReturn");
2035
2075
  cleanUrl.pathname = "/app/home";
@@ -2195,7 +2235,7 @@ function AppRoot(props) {
2195
2235
  "[hook-template] <AppRoot>: PreAuthFlow slot prop is required when config.onboarding.trigger === 'pre_signup_custom'."
2196
2236
  );
2197
2237
  }
2198
- const legacyShim = (0, import_react12.useMemo)(() => buildLegacyConfigShim(config), [config]);
2238
+ const legacyShim = (0, import_react13.useMemo)(() => buildLegacyConfigShim(config), [config]);
2199
2239
  const Router = testRouter === "memory" ? import_react_router_dom2.MemoryRouter : import_react_router_dom2.BrowserRouter;
2200
2240
  const basename = `/app/${config.slug}`;
2201
2241
  const routerProps = testRouter === "memory" ? { basename, initialEntries: testInitialEntries } : { basename };
@@ -2232,7 +2272,7 @@ function AuthGated({
2232
2272
  EmailVerify,
2233
2273
  PreAuthFlow
2234
2274
  }) {
2235
- const { authStatus } = (0, import_sdk5.useHook)();
2275
+ const { authStatus } = (0, import_sdk6.useHook)();
2236
2276
  if (authStatus === "loading") return null;
2237
2277
  if (authStatus !== "authenticated") {
2238
2278
  if (config.onboarding?.trigger === "pre_signup_custom" && PreAuthFlow) {
@@ -2261,8 +2301,8 @@ function FallbackPaywall() {
2261
2301
  }
2262
2302
 
2263
2303
  // src/hooks/usePush.ts
2264
- var import_react13 = require("react");
2265
- var import_sdk6 = require("@hook-sdk/sdk");
2304
+ var import_react14 = require("react");
2305
+ var import_sdk7 = require("@hook-sdk/sdk");
2266
2306
  var DISMISS_STORAGE_KEY = "push:dismissed-until";
2267
2307
  var DISMISS_TTL_MS2 = 7 * 24 * 60 * 60 * 1e3;
2268
2308
  function detectIosNeedsInstall() {
@@ -2306,12 +2346,12 @@ function deriveState(push) {
2306
2346
  return { kind: "prompt" };
2307
2347
  }
2308
2348
  function usePush() {
2309
- const { push } = (0, import_sdk6.useHook)();
2310
- const [state, setState] = (0, import_react13.useState)(() => deriveState(push));
2311
- (0, import_react13.useEffect)(() => {
2349
+ const { push } = (0, import_sdk7.useHook)();
2350
+ const [state, setState] = (0, import_react14.useState)(() => deriveState(push));
2351
+ (0, import_react14.useEffect)(() => {
2312
2352
  setState(deriveState(push));
2313
2353
  }, [push]);
2314
- const subscribe = (0, import_react13.useCallback)(async () => {
2354
+ const subscribe = (0, import_react14.useCallback)(async () => {
2315
2355
  try {
2316
2356
  await push.subscribe();
2317
2357
  setState({ kind: "subscribed" });
@@ -2323,7 +2363,7 @@ function usePush() {
2323
2363
  throw e;
2324
2364
  }
2325
2365
  }, [push]);
2326
- const unsubscribe = (0, import_react13.useCallback)(async () => {
2366
+ const unsubscribe = (0, import_react14.useCallback)(async () => {
2327
2367
  try {
2328
2368
  await push.unsubscribe();
2329
2369
  setState({ kind: "prompt" });
@@ -2332,7 +2372,7 @@ function usePush() {
2332
2372
  throw e;
2333
2373
  }
2334
2374
  }, [push]);
2335
- const dismiss = (0, import_react13.useCallback)(() => {
2375
+ const dismiss = (0, import_react14.useCallback)(() => {
2336
2376
  if (typeof localStorage !== "undefined") {
2337
2377
  try {
2338
2378
  localStorage.setItem(DISMISS_STORAGE_KEY, String(Date.now() + DISMISS_TTL_MS2));
@@ -2424,20 +2464,20 @@ function EmptyState({ title, description, action }) {
2424
2464
  }
2425
2465
 
2426
2466
  // src/hooks/useLoginForm.ts
2427
- var import_react14 = require("react");
2428
- var import_sdk8 = require("@hook-sdk/sdk");
2467
+ var import_react15 = require("react");
2468
+ var import_sdk9 = require("@hook-sdk/sdk");
2429
2469
 
2430
2470
  // src/errors.ts
2431
- var import_sdk7 = require("@hook-sdk/sdk");
2471
+ var import_sdk8 = require("@hook-sdk/sdk");
2432
2472
  function mapSdkError(err) {
2433
- if (err instanceof import_sdk7.SdkRateLimitError) {
2473
+ if (err instanceof import_sdk8.SdkRateLimitError) {
2434
2474
  return {
2435
2475
  code: "rate_limited",
2436
2476
  message: `Aguarde ${err.retryAfter}s e tente novamente.`,
2437
2477
  retryAfter: err.retryAfter
2438
2478
  };
2439
2479
  }
2440
- if (err instanceof import_sdk7.SdkAuthError) {
2480
+ if (err instanceof import_sdk8.SdkAuthError) {
2441
2481
  const detail = err.detail;
2442
2482
  if (detail === "email_unverified") {
2443
2483
  return { code: "email_unverified", message: "Confirme seu e-mail antes de entrar." };
@@ -2447,7 +2487,7 @@ function mapSdkError(err) {
2447
2487
  }
2448
2488
  return { code: "invalid_credentials", message: "E-mail ou senha inv\xE1lidos." };
2449
2489
  }
2450
- if (err instanceof import_sdk7.SdkError && err.httpStatus === 0) {
2490
+ if (err instanceof import_sdk8.SdkError && err.httpStatus === 0) {
2451
2491
  return { code: "network", message: "Sem conex\xE3o com o servidor. Verifique sua internet." };
2452
2492
  }
2453
2493
  if (err instanceof TypeError) {
@@ -2460,20 +2500,20 @@ function mapSdkError(err) {
2460
2500
  var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2461
2501
  var MIN_PASSWORD = 8;
2462
2502
  function useLoginForm() {
2463
- const { auth } = (0, import_sdk8.useHook)();
2464
- const [email, setEmail] = (0, import_react14.useState)("");
2465
- const [password, setPassword] = (0, import_react14.useState)("");
2466
- const [submitting, setSubmitting] = (0, import_react14.useState)(false);
2467
- const [error, setError] = (0, import_react14.useState)(null);
2468
- const [touchedEmail, setTouchedEmail] = (0, import_react14.useState)(false);
2469
- const [touchedPassword, setTouchedPassword] = (0, import_react14.useState)(false);
2470
- const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react14.useState)(false);
2471
- const validateEmail = (0, import_react14.useMemo)(() => {
2503
+ const { auth } = (0, import_sdk9.useHook)();
2504
+ const [email, setEmail] = (0, import_react15.useState)("");
2505
+ const [password, setPassword] = (0, import_react15.useState)("");
2506
+ const [submitting, setSubmitting] = (0, import_react15.useState)(false);
2507
+ const [error, setError] = (0, import_react15.useState)(null);
2508
+ const [touchedEmail, setTouchedEmail] = (0, import_react15.useState)(false);
2509
+ const [touchedPassword, setTouchedPassword] = (0, import_react15.useState)(false);
2510
+ const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react15.useState)(false);
2511
+ const validateEmail = (0, import_react15.useMemo)(() => {
2472
2512
  if (email.length === 0) return null;
2473
2513
  if (!EMAIL_RE.test(email)) return "Formato de e-mail inv\xE1lido.";
2474
2514
  return null;
2475
2515
  }, [email]);
2476
- const validatePassword = (0, import_react14.useMemo)(() => {
2516
+ const validatePassword = (0, import_react15.useMemo)(() => {
2477
2517
  if (password.length === 0) return null;
2478
2518
  if (password.length < MIN_PASSWORD) return `M\xEDnimo de ${MIN_PASSWORD} caracteres.`;
2479
2519
  return null;
@@ -2481,7 +2521,7 @@ function useLoginForm() {
2481
2521
  const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
2482
2522
  const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
2483
2523
  const canSubmit = email.length > 0 && password.length >= MIN_PASSWORD && validateEmail === null && validatePassword === null && !submitting;
2484
- const submit = (0, import_react14.useCallback)(async () => {
2524
+ const submit = (0, import_react15.useCallback)(async () => {
2485
2525
  setFormSubmitAttempted(true);
2486
2526
  if (!canSubmit) return false;
2487
2527
  setSubmitting(true);
@@ -2515,32 +2555,32 @@ function useLoginForm() {
2515
2555
  }
2516
2556
 
2517
2557
  // src/hooks/useSignupForm.ts
2518
- var import_react15 = require("react");
2519
- var import_sdk9 = require("@hook-sdk/sdk");
2558
+ var import_react16 = require("react");
2559
+ var import_sdk10 = require("@hook-sdk/sdk");
2520
2560
  var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2521
2561
  var MIN_PASSWORD2 = 8;
2522
2562
  function useSignupForm() {
2523
- const { auth } = (0, import_sdk9.useHook)();
2524
- const [name, setName] = (0, import_react15.useState)("");
2525
- const [email, setEmail] = (0, import_react15.useState)("");
2526
- const [password, setPassword] = (0, import_react15.useState)("");
2527
- const [submitting, setSubmitting] = (0, import_react15.useState)(false);
2528
- const [error, setError] = (0, import_react15.useState)(null);
2529
- const [touchedName, setTouchedName] = (0, import_react15.useState)(false);
2530
- const [touchedEmail, setTouchedEmail] = (0, import_react15.useState)(false);
2531
- const [touchedPassword, setTouchedPassword] = (0, import_react15.useState)(false);
2532
- const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react15.useState)(false);
2533
- const validateName = (0, import_react15.useMemo)(() => {
2563
+ const { auth } = (0, import_sdk10.useHook)();
2564
+ const [name, setName] = (0, import_react16.useState)("");
2565
+ const [email, setEmail] = (0, import_react16.useState)("");
2566
+ const [password, setPassword] = (0, import_react16.useState)("");
2567
+ const [submitting, setSubmitting] = (0, import_react16.useState)(false);
2568
+ const [error, setError] = (0, import_react16.useState)(null);
2569
+ const [touchedName, setTouchedName] = (0, import_react16.useState)(false);
2570
+ const [touchedEmail, setTouchedEmail] = (0, import_react16.useState)(false);
2571
+ const [touchedPassword, setTouchedPassword] = (0, import_react16.useState)(false);
2572
+ const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react16.useState)(false);
2573
+ const validateName = (0, import_react16.useMemo)(() => {
2534
2574
  if (name.length === 0) return null;
2535
2575
  if (name.trim().length < 2) return "Nome muito curto.";
2536
2576
  return null;
2537
2577
  }, [name]);
2538
- const validateEmail = (0, import_react15.useMemo)(() => {
2578
+ const validateEmail = (0, import_react16.useMemo)(() => {
2539
2579
  if (email.length === 0) return null;
2540
2580
  if (!EMAIL_RE2.test(email)) return "Formato de e-mail inv\xE1lido.";
2541
2581
  return null;
2542
2582
  }, [email]);
2543
- const validatePassword = (0, import_react15.useMemo)(() => {
2583
+ const validatePassword = (0, import_react16.useMemo)(() => {
2544
2584
  if (password.length === 0) return null;
2545
2585
  if (password.length < MIN_PASSWORD2) return `M\xEDnimo de ${MIN_PASSWORD2} caracteres.`;
2546
2586
  return null;
@@ -2549,7 +2589,7 @@ function useSignupForm() {
2549
2589
  const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
2550
2590
  const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
2551
2591
  const canSubmit = name.trim().length >= 2 && email.length > 0 && password.length >= MIN_PASSWORD2 && validateName === null && validateEmail === null && validatePassword === null && !submitting;
2552
- const submit = (0, import_react15.useCallback)(async () => {
2592
+ const submit = (0, import_react16.useCallback)(async () => {
2553
2593
  setFormSubmitAttempted(true);
2554
2594
  if (!canSubmit) return false;
2555
2595
  setSubmitting(true);
@@ -2587,25 +2627,25 @@ function useSignupForm() {
2587
2627
  }
2588
2628
 
2589
2629
  // src/hooks/useForgotForm.ts
2590
- var import_react16 = require("react");
2591
- var import_sdk10 = require("@hook-sdk/sdk");
2630
+ var import_react17 = require("react");
2631
+ var import_sdk11 = require("@hook-sdk/sdk");
2592
2632
  var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2593
2633
  function useForgotForm() {
2594
- const { auth } = (0, import_sdk10.useHook)();
2595
- const [email, setEmail] = (0, import_react16.useState)("");
2596
- const [submitting, setSubmitting] = (0, import_react16.useState)(false);
2597
- const [sent, setSent] = (0, import_react16.useState)(false);
2598
- const [error, setError] = (0, import_react16.useState)(null);
2599
- const [touchedEmail, setTouchedEmail] = (0, import_react16.useState)(false);
2600
- const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react16.useState)(false);
2601
- const validateEmail = (0, import_react16.useMemo)(() => {
2634
+ const { auth } = (0, import_sdk11.useHook)();
2635
+ const [email, setEmail] = (0, import_react17.useState)("");
2636
+ const [submitting, setSubmitting] = (0, import_react17.useState)(false);
2637
+ const [sent, setSent] = (0, import_react17.useState)(false);
2638
+ const [error, setError] = (0, import_react17.useState)(null);
2639
+ const [touchedEmail, setTouchedEmail] = (0, import_react17.useState)(false);
2640
+ const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react17.useState)(false);
2641
+ const validateEmail = (0, import_react17.useMemo)(() => {
2602
2642
  if (email.length === 0) return null;
2603
2643
  if (!EMAIL_RE3.test(email)) return "Formato de e-mail inv\xE1lido.";
2604
2644
  return null;
2605
2645
  }, [email]);
2606
2646
  const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
2607
2647
  const canSubmit = email.length > 0 && validateEmail === null && !submitting;
2608
- const submit = (0, import_react16.useCallback)(async () => {
2648
+ const submit = (0, import_react17.useCallback)(async () => {
2609
2649
  setFormSubmitAttempted(true);
2610
2650
  if (!canSubmit) return false;
2611
2651
  setSubmitting(true);
@@ -2636,32 +2676,32 @@ function useForgotForm() {
2636
2676
  }
2637
2677
 
2638
2678
  // src/hooks/useResetForm.ts
2639
- var import_react17 = require("react");
2640
- var import_sdk11 = require("@hook-sdk/sdk");
2679
+ var import_react18 = require("react");
2680
+ var import_sdk12 = require("@hook-sdk/sdk");
2641
2681
  var MIN_PASSWORD3 = 12;
2642
2682
  function useResetForm() {
2643
- const { auth } = (0, import_sdk11.useHook)();
2644
- const [token, setToken] = (0, import_react17.useState)(null);
2645
- const [password, setPassword] = (0, import_react17.useState)("");
2646
- const [confirm, setConfirm] = (0, import_react17.useState)("");
2647
- const [submitting, setSubmitting] = (0, import_react17.useState)(false);
2648
- const [done, setDone] = (0, import_react17.useState)(false);
2649
- const [error, setError] = (0, import_react17.useState)(null);
2650
- const [touchedPassword, setTouchedPassword] = (0, import_react17.useState)(false);
2651
- const [touchedConfirm, setTouchedConfirm] = (0, import_react17.useState)(false);
2652
- const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react17.useState)(false);
2653
- (0, import_react17.useEffect)(() => {
2683
+ const { auth } = (0, import_sdk12.useHook)();
2684
+ const [token, setToken] = (0, import_react18.useState)(null);
2685
+ const [password, setPassword] = (0, import_react18.useState)("");
2686
+ const [confirm, setConfirm] = (0, import_react18.useState)("");
2687
+ const [submitting, setSubmitting] = (0, import_react18.useState)(false);
2688
+ const [done, setDone] = (0, import_react18.useState)(false);
2689
+ const [error, setError] = (0, import_react18.useState)(null);
2690
+ const [touchedPassword, setTouchedPassword] = (0, import_react18.useState)(false);
2691
+ const [touchedConfirm, setTouchedConfirm] = (0, import_react18.useState)(false);
2692
+ const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react18.useState)(false);
2693
+ (0, import_react18.useEffect)(() => {
2654
2694
  if (typeof window === "undefined") return;
2655
2695
  const params = new URLSearchParams(window.location.search);
2656
2696
  const t = params.get("token");
2657
2697
  setToken(t && t.length > 0 ? t : null);
2658
2698
  }, []);
2659
- const validatePassword = (0, import_react17.useMemo)(() => {
2699
+ const validatePassword = (0, import_react18.useMemo)(() => {
2660
2700
  if (password.length === 0) return null;
2661
2701
  if (password.length < MIN_PASSWORD3) return `M\xEDnimo de ${MIN_PASSWORD3} caracteres.`;
2662
2702
  return null;
2663
2703
  }, [password]);
2664
- const validateConfirm = (0, import_react17.useMemo)(() => {
2704
+ const validateConfirm = (0, import_react18.useMemo)(() => {
2665
2705
  if (confirm.length === 0) return null;
2666
2706
  if (confirm !== password) return "Senhas n\xE3o coincidem.";
2667
2707
  return null;
@@ -2669,7 +2709,7 @@ function useResetForm() {
2669
2709
  const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
2670
2710
  const confirmError = touchedConfirm || formSubmitAttempted ? validateConfirm : null;
2671
2711
  const canSubmit = token !== null && password.length >= MIN_PASSWORD3 && confirm === password && validatePassword === null && validateConfirm === null && !submitting && !done;
2672
- const submit = (0, import_react17.useCallback)(async () => {
2712
+ const submit = (0, import_react18.useCallback)(async () => {
2673
2713
  setFormSubmitAttempted(true);
2674
2714
  if (!canSubmit || token === null) return;
2675
2715
  setSubmitting(true);
@@ -2709,9 +2749,9 @@ function useResetForm() {
2709
2749
  }
2710
2750
 
2711
2751
  // src/hooks/usePlan.ts
2712
- var import_sdk12 = require("@hook-sdk/sdk");
2752
+ var import_sdk13 = require("@hook-sdk/sdk");
2713
2753
  function usePlan() {
2714
- const { plan } = (0, import_sdk12.useHook)();
2754
+ const { plan } = (0, import_sdk13.useHook)();
2715
2755
  return plan;
2716
2756
  }
2717
2757
 
@@ -2744,12 +2784,12 @@ function discountPercent(anchorCents, realCents) {
2744
2784
  }
2745
2785
 
2746
2786
  // src/hooks/useAuthPrimitives.ts
2747
- var import_react18 = require("react");
2748
- var import_sdk13 = require("@hook-sdk/sdk");
2787
+ var import_react19 = require("react");
2788
+ var import_sdk14 = require("@hook-sdk/sdk");
2749
2789
  var warned = false;
2750
2790
  function useAuthPrimitives() {
2751
- const { auth } = (0, import_sdk13.useHook)();
2752
- (0, import_react18.useEffect)(() => {
2791
+ const { auth } = (0, import_sdk14.useHook)();
2792
+ (0, import_react19.useEffect)(() => {
2753
2793
  if (!warned && process.env.NODE_ENV !== "production") {
2754
2794
  warned = true;
2755
2795
  console.warn(
@@ -2771,9 +2811,9 @@ function useAuthPrimitives() {
2771
2811
  }
2772
2812
 
2773
2813
  // src/hooks/useAuth.ts
2774
- var import_sdk14 = require("@hook-sdk/sdk");
2814
+ var import_sdk15 = require("@hook-sdk/sdk");
2775
2815
  function useAuth() {
2776
- const { user, authStatus, auth } = (0, import_sdk14.useHook)();
2816
+ const { user, authStatus, auth } = (0, import_sdk15.useHook)();
2777
2817
  return {
2778
2818
  user,
2779
2819
  authStatus,
@@ -2781,24 +2821,27 @@ function useAuth() {
2781
2821
  };
2782
2822
  }
2783
2823
 
2824
+ // src/index.ts
2825
+ var import_sdk19 = require("@hook-sdk/sdk");
2826
+
2784
2827
  // src/hooks/useSubscription.ts
2785
- var import_sdk15 = require("@hook-sdk/sdk");
2828
+ var import_sdk16 = require("@hook-sdk/sdk");
2786
2829
  function useSubscription() {
2787
- const { subscription } = (0, import_sdk15.useHook)();
2830
+ const { subscription } = (0, import_sdk16.useHook)();
2788
2831
  return {
2789
2832
  status: subscription.status()
2790
2833
  };
2791
2834
  }
2792
2835
 
2793
2836
  // src/hooks/useReminders.ts
2794
- var import_react19 = require("react");
2795
- var import_sdk16 = require("@hook-sdk/sdk");
2837
+ var import_react20 = require("react");
2838
+ var import_sdk17 = require("@hook-sdk/sdk");
2796
2839
  function useReminders() {
2797
- const { push } = (0, import_sdk16.useHook)();
2840
+ const { push } = (0, import_sdk17.useHook)();
2798
2841
  const r = push.reminders;
2799
- const [reminders, setReminders] = (0, import_react19.useState)([]);
2800
- const [loading, setLoading] = (0, import_react19.useState)(true);
2801
- const reload = (0, import_react19.useCallback)(async () => {
2842
+ const [reminders, setReminders] = (0, import_react20.useState)([]);
2843
+ const [loading, setLoading] = (0, import_react20.useState)(true);
2844
+ const reload = (0, import_react20.useCallback)(async () => {
2802
2845
  setLoading(true);
2803
2846
  try {
2804
2847
  const next = await r.list();
@@ -2807,38 +2850,38 @@ function useReminders() {
2807
2850
  setLoading(false);
2808
2851
  }
2809
2852
  }, [r]);
2810
- (0, import_react19.useEffect)(() => {
2853
+ (0, import_react20.useEffect)(() => {
2811
2854
  void reload();
2812
2855
  }, [reload]);
2813
- const setReminder = (0, import_react19.useCallback)(async (input) => {
2856
+ const setReminder = (0, import_react20.useCallback)(async (input) => {
2814
2857
  await r.set(input);
2815
2858
  await reload();
2816
2859
  }, [r, reload]);
2817
- const deleteReminder = (0, import_react19.useCallback)(async (slot) => {
2860
+ const deleteReminder = (0, import_react20.useCallback)(async (slot) => {
2818
2861
  await r.delete(slot);
2819
2862
  await reload();
2820
2863
  }, [r, reload]);
2821
- const schedule = (0, import_react19.useCallback)(async (items) => {
2864
+ const schedule = (0, import_react20.useCallback)(async (items) => {
2822
2865
  return r.schedule(items);
2823
2866
  }, [r]);
2824
- const setFallbacks = (0, import_react19.useCallback)(async (items) => {
2867
+ const setFallbacks = (0, import_react20.useCallback)(async (items) => {
2825
2868
  return r.setFallbacks(items);
2826
2869
  }, [r]);
2827
2870
  return { reminders, loading, setReminder, deleteReminder, schedule, setFallbacks };
2828
2871
  }
2829
2872
 
2830
2873
  // src/hooks/useToast.ts
2831
- var import_react20 = require("react");
2874
+ var import_react21 = require("react");
2832
2875
  function useToast() {
2833
- const [items, setItems] = (0, import_react20.useState)([]);
2834
- const show = (0, import_react20.useCallback)((message, kind = "info") => {
2876
+ const [items, setItems] = (0, import_react21.useState)([]);
2877
+ const show = (0, import_react21.useCallback)((message, kind = "info") => {
2835
2878
  const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
2836
2879
  setItems((prev) => [...prev, { id, message, kind }]);
2837
2880
  setTimeout(() => {
2838
2881
  setItems((prev) => prev.filter((t) => t.id !== id));
2839
2882
  }, 4e3);
2840
2883
  }, []);
2841
- const dismiss = (0, import_react20.useCallback)((id) => {
2884
+ const dismiss = (0, import_react21.useCallback)((id) => {
2842
2885
  setItems((prev) => prev.filter((t) => t.id !== id));
2843
2886
  }, []);
2844
2887
  return { items, show, dismiss };
@@ -2873,14 +2916,14 @@ function PreAuthShell({
2873
2916
  }
2874
2917
 
2875
2918
  // src/OnboardingFlow.tsx
2876
- var import_react22 = require("react");
2877
- var import_sdk17 = require("@hook-sdk/sdk");
2919
+ var import_react23 = require("react");
2920
+ var import_sdk18 = require("@hook-sdk/sdk");
2878
2921
 
2879
2922
  // src/hooks/useOnboardingStep.ts
2880
- var import_react21 = require("react");
2881
- var OnboardingStepContext = (0, import_react21.createContext)(null);
2923
+ var import_react22 = require("react");
2924
+ var OnboardingStepContext = (0, import_react22.createContext)(null);
2882
2925
  function useOnboardingStep() {
2883
- const ctx = (0, import_react21.useContext)(OnboardingStepContext);
2926
+ const ctx = (0, import_react22.useContext)(OnboardingStepContext);
2884
2927
  if (!ctx) {
2885
2928
  throw new Error(
2886
2929
  "[hook-template] useOnboardingStep must be used inside <OnboardingFlow>. (G75)"
@@ -2903,12 +2946,12 @@ function OnboardingFlow({
2903
2946
  onComplete,
2904
2947
  persistKey
2905
2948
  }) {
2906
- const [draft, setDraft, status] = (0, import_sdk17.usePersistedState)(persistKey, {});
2907
- const draftRef = (0, import_react22.useRef)(draft);
2949
+ const [draft, setDraft, status] = (0, import_sdk18.usePersistedState)(persistKey, {});
2950
+ const draftRef = (0, import_react23.useRef)(draft);
2908
2951
  draftRef.current = draft;
2909
2952
  const idx = readPersistedStepIdx(draft);
2910
2953
  const clampedIdx = Math.min(Math.max(idx, 0), Math.max(steps.length - 1, 0));
2911
- const setIdx = (0, import_react22.useCallback)(
2954
+ const setIdx = (0, import_react23.useCallback)(
2912
2955
  (n) => {
2913
2956
  setDraft((prev) => {
2914
2957
  const prevIdx = readPersistedStepIdx(prev);
@@ -2918,7 +2961,7 @@ function OnboardingFlow({
2918
2961
  },
2919
2962
  [setDraft]
2920
2963
  );
2921
- const setValue = (0, import_react22.useCallback)(
2964
+ const setValue = (0, import_react23.useCallback)(
2922
2965
  (patch) => {
2923
2966
  draftRef.current = { ...draftRef.current, ...patch };
2924
2967
  setDraft((prev) => ({ ...prev, ...patch }));
@@ -2926,11 +2969,23 @@ function OnboardingFlow({
2926
2969
  [setDraft]
2927
2970
  );
2928
2971
  const step = steps[clampedIdx];
2929
- const valid = (0, import_react22.useMemo)(
2972
+ const hookCtx = (0, import_sdk18.useHook)();
2973
+ const track2 = typeof hookCtx.track === "function" ? hookCtx.track : void 0;
2974
+ (0, import_react23.useEffect)(() => {
2975
+ if (status.loading) return;
2976
+ if (!step) return;
2977
+ if (!track2) return;
2978
+ track2("onboarding_step_viewed", {
2979
+ step: step.id,
2980
+ step_index: clampedIdx,
2981
+ total_steps: steps.length
2982
+ });
2983
+ }, [step?.id, clampedIdx, steps.length, status.loading, track2]);
2984
+ const valid = (0, import_react23.useMemo)(
2930
2985
  () => step ? (step.validates ?? []).every((field) => isFilled(draft[field])) : false,
2931
2986
  [draft, step]
2932
2987
  );
2933
- const next = (0, import_react22.useCallback)(() => {
2988
+ const next = (0, import_react23.useCallback)(() => {
2934
2989
  if (!step) return;
2935
2990
  const current = draftRef.current;
2936
2991
  const validNow = (step.validates ?? []).every((field) => isFilled(current[field]));
@@ -2941,8 +2996,8 @@ function OnboardingFlow({
2941
2996
  setIdx(clampedIdx + 1);
2942
2997
  }
2943
2998
  }, [clampedIdx, onComplete, step, steps.length, setIdx]);
2944
- const prevStep = (0, import_react22.useCallback)(() => setIdx((i) => Math.max(0, i - 1)), [setIdx]);
2945
- const ctx = (0, import_react22.useMemo)(
2999
+ const prevStep = (0, import_react23.useCallback)(() => setIdx((i) => Math.max(0, i - 1)), [setIdx]);
3000
+ const ctx = (0, import_react23.useMemo)(
2946
3001
  () => ({
2947
3002
  stepIndex: clampedIdx,
2948
3003
  totalSteps: steps.length,
@@ -3022,6 +3077,7 @@ function useFeature(name) {
3022
3077
  useResetForm,
3023
3078
  useSignupForm,
3024
3079
  useSubscription,
3025
- useToast
3080
+ useToast,
3081
+ useTrackOnboardingStep
3026
3082
  });
3027
3083
  //# sourceMappingURL=index.cjs.map