@hook-sdk/template 0.18.1 → 0.19.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
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -26,8 +36,10 @@ __export(index_exports, {
26
36
  DeepLinkHandler: () => DeepLinkHandler,
27
37
  EmptyState: () => EmptyState,
28
38
  ErrorBoundary: () => ErrorBoundary,
39
+ I18nProvider: () => I18nProvider,
29
40
  InstallGate: () => InstallGate,
30
41
  InstallSplash: () => InstallSplash,
42
+ LanguageSwitcher: () => LanguageSwitcher,
31
43
  LoadingState: () => LoadingState,
32
44
  OnboardingFlow: () => OnboardingFlow,
33
45
  PaymentReturnHandler: () => PaymentReturnHandler,
@@ -65,14 +77,14 @@ __export(index_exports, {
65
77
  useSignupForm: () => useSignupForm,
66
78
  useSubscription: () => useSubscription,
67
79
  useToast: () => useToast,
68
- useTrackOnboardingStep: () => import_sdk19.useTrackOnboardingStep
80
+ useTrackOnboardingStep: () => import_sdk21.useTrackOnboardingStep
69
81
  });
70
82
  module.exports = __toCommonJS(index_exports);
71
83
 
72
84
  // src/AppRoot.tsx
73
- var import_react13 = require("react");
85
+ var import_react14 = require("react");
74
86
  var import_react_router_dom2 = require("react-router-dom");
75
- var import_sdk6 = require("@hook-sdk/sdk");
87
+ var import_sdk7 = require("@hook-sdk/sdk");
76
88
 
77
89
  // src/config/AppConfigContext.tsx
78
90
  var import_react = require("react");
@@ -145,6 +157,17 @@ var DeepLinksSchema = import_zod.z.object({
145
157
  passwordReset: import_zod.z.string().startsWith("/").optional(),
146
158
  emailVerify: import_zod.z.string().startsWith("/").optional()
147
159
  }).strict();
160
+ var I18nConfigSchema = import_zod.z.object({
161
+ defaultLocale: import_zod.z.string().min(2),
162
+ supportedLocales: import_zod.z.array(import_zod.z.string().min(2)).min(1),
163
+ resources: import_zod.z.record(import_zod.z.string(), import_zod.z.record(import_zod.z.string(), import_zod.z.string()))
164
+ }).strict().refine((v) => v.supportedLocales.includes(v.defaultLocale), {
165
+ message: "i18n.defaultLocale must be a member of i18n.supportedLocales",
166
+ path: ["defaultLocale"]
167
+ });
168
+ var InstallPromptSchema = import_zod.z.object({
169
+ position: import_zod.z.enum(["pre-auth", "post-paywall"]).optional()
170
+ }).strict();
148
171
  var AppConfigSchema = import_zod.z.object({
149
172
  slug: import_zod.z.string().regex(/^[a-z0-9-]+$/),
150
173
  name: import_zod.z.string().min(1),
@@ -158,12 +181,18 @@ var AppConfigSchema = import_zod.z.object({
158
181
  persistedKeys: import_zod.z.array(PersistedKeySchema),
159
182
  onboarding: OnboardingSchema.optional(),
160
183
  deepLinks: DeepLinksSchema.optional(),
184
+ i18n: I18nConfigSchema.optional(),
161
185
  features_enabled: import_zod.z.array(import_zod.z.string()).optional(),
186
+ install_prompt: InstallPromptSchema.optional(),
162
187
  // Build-time injected theme metadata (e.g. icon_url for InstallSplash).
163
188
  // Apps don't author this directly; deploy workflows fill it from
164
189
  // env-resolved bundle host. Permissive shape so apps/workflows can
165
190
  // extend without re-bumping the template schema.
166
- theme: import_zod.z.object({}).passthrough().optional()
191
+ theme: import_zod.z.object({}).passthrough().optional(),
192
+ // G133 — per-tenant public app-data keys allowlist. Optional; default
193
+ // backfill em migration 0044 = canonical 6. Apps com keys próprias
194
+ // declaram aqui pra evitar 402 spam pós-signup no SubscriptionGate.
195
+ publicKeys: import_zod.z.array(import_zod.z.string().regex(SnakeKeyRE)).optional()
167
196
  }).strict();
168
197
  function parseAppConfig(input) {
169
198
  const r = AppConfigSchema.safeParse(input);
@@ -1882,9 +1911,9 @@ var bannerStyle = {
1882
1911
 
1883
1912
  // src/components/InstallGate/InstallGate.tsx
1884
1913
  var import_jsx_runtime16 = require("react/jsx-runtime");
1885
- function InstallGate({ children }) {
1914
+ function InstallGate({ children, position }) {
1886
1915
  const { slug, features_enabled } = useTemplateConfig();
1887
- const enabled = features_enabled.includes("install_prompt");
1916
+ const enabled = features_enabled.includes("pwa-install");
1888
1917
  const installState = useInstallPrompt(slug);
1889
1918
  const shouldBlock = enabled && shouldBlockInstall(installState);
1890
1919
  const trackedRef = (0, import_react9.useRef)(null);
@@ -1899,9 +1928,10 @@ function InstallGate({ children }) {
1899
1928
  platform: installState.platform,
1900
1929
  browser: installState.iosBrowser ?? installState.androidBrowser ?? null,
1901
1930
  in_app_app: installState.inAppApp,
1902
- variant: installState.variant
1931
+ variant: installState.variant,
1932
+ ...position !== void 0 ? { position } : {}
1903
1933
  });
1904
- }, [shouldBlock, slug, installState.variant, installState.platform, installState.iosBrowser, installState.androidBrowser, installState.inAppApp]);
1934
+ }, [shouldBlock, slug, installState.variant, installState.platform, installState.iosBrowser, installState.androidBrowser, installState.inAppApp, position]);
1905
1935
  if (!enabled) return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_jsx_runtime16.Fragment, { children });
1906
1936
  if (installState.isInstalled) return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_jsx_runtime16.Fragment, { children });
1907
1937
  if (installState.variant === "desktop") {
@@ -2024,21 +2054,58 @@ var ErrorBoundary = class extends import_react11.Component {
2024
2054
  }
2025
2055
  };
2026
2056
 
2027
- // src/internal/PaymentReturnHandler.tsx
2057
+ // src/i18n/I18nProvider.tsx
2028
2058
  var import_react12 = require("react");
2059
+ var import_i18next = __toESM(require("i18next"), 1);
2060
+ var import_react_i18next = require("react-i18next");
2029
2061
  var import_sdk5 = require("@hook-sdk/sdk");
2030
2062
  var import_jsx_runtime19 = require("react/jsx-runtime");
2063
+ function ensureInitialized(defaultLocale, supportedLocales, resources, initialLocale) {
2064
+ if (import_i18next.default.isInitialized) return;
2065
+ import_i18next.default.use(import_react_i18next.initReactI18next).init({
2066
+ resources: Object.fromEntries(
2067
+ supportedLocales.map((l) => [l, { translation: resources[l] ?? {} }])
2068
+ ),
2069
+ lng: initialLocale,
2070
+ fallbackLng: defaultLocale,
2071
+ interpolation: { escapeValue: false },
2072
+ // useTranslation suspends by default until i18next is "ready". Inline
2073
+ // resources are sync, so suspending creates a guaranteed empty render
2074
+ // tick — confusing in apps and breaks tests that don't use Suspense.
2075
+ react: { useSuspense: false }
2076
+ });
2077
+ }
2078
+ function I18nProvider({
2079
+ defaultLocale,
2080
+ supportedLocales,
2081
+ resources,
2082
+ children
2083
+ }) {
2084
+ const [userLocale] = (0, import_sdk5.usePersistedState)("user-locale", defaultLocale);
2085
+ ensureInitialized(defaultLocale, supportedLocales, resources, userLocale);
2086
+ (0, import_react12.useEffect)(() => {
2087
+ if (import_i18next.default.isInitialized && import_i18next.default.language !== userLocale) {
2088
+ import_i18next.default.changeLanguage(userLocale);
2089
+ }
2090
+ }, [userLocale]);
2091
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_i18next.I18nextProvider, { i18n: import_i18next.default, children });
2092
+ }
2093
+
2094
+ // src/internal/PaymentReturnHandler.tsx
2095
+ var import_react13 = require("react");
2096
+ var import_sdk6 = require("@hook-sdk/sdk");
2097
+ var import_jsx_runtime20 = require("react/jsx-runtime");
2031
2098
  var BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 4e4];
2032
2099
  var MAX_CYCLES = 3;
2033
2100
  var SUPPORT_MAILTO = "mailto:suporte@usehook.net?subject=Pagamento%20pendente";
2034
2101
  function PaymentReturnHandler({ children }) {
2035
- const { subscription } = (0, import_sdk5.useHook)();
2036
- const subRef = (0, import_react12.useRef)(subscription);
2102
+ const { subscription } = (0, import_sdk6.useHook)();
2103
+ const subRef = (0, import_react13.useRef)(subscription);
2037
2104
  subRef.current = subscription;
2038
- const runIdRef = (0, import_react12.useRef)(0);
2039
- const cyclesRef = (0, import_react12.useRef)(0);
2040
- const [state, setState] = (0, import_react12.useState)("idle");
2041
- const runPoll = (0, import_react12.useCallback)(() => {
2105
+ const runIdRef = (0, import_react13.useRef)(0);
2106
+ const cyclesRef = (0, import_react13.useRef)(0);
2107
+ const [state, setState] = (0, import_react13.useState)("idle");
2108
+ const runPoll = (0, import_react13.useCallback)(() => {
2042
2109
  const runId = ++runIdRef.current;
2043
2110
  cyclesRef.current += 1;
2044
2111
  setState("confirming");
@@ -2073,7 +2140,7 @@ function PaymentReturnHandler({ children }) {
2073
2140
  };
2074
2141
  void tick();
2075
2142
  }, []);
2076
- (0, import_react12.useEffect)(() => {
2143
+ (0, import_react13.useEffect)(() => {
2077
2144
  if (typeof window === "undefined") return;
2078
2145
  const url = new URL(window.location.href);
2079
2146
  if (url.searchParams.get("paymentReturn") !== "1") return;
@@ -2083,26 +2150,26 @@ function PaymentReturnHandler({ children }) {
2083
2150
  runIdRef.current++;
2084
2151
  };
2085
2152
  }, [runPoll]);
2086
- const goHome = (0, import_react12.useCallback)(() => {
2153
+ const goHome = (0, import_react13.useCallback)(() => {
2087
2154
  const cleanUrl = new URL(window.location.href);
2088
2155
  cleanUrl.searchParams.delete("paymentReturn");
2089
2156
  cleanUrl.pathname = "/app/home";
2090
2157
  window.location.href = cleanUrl.toString();
2091
2158
  }, []);
2092
2159
  if (state === "confirming") {
2093
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: "Confirmando pagamento\u2026" });
2160
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: "Confirmando pagamento\u2026" });
2094
2161
  }
2095
2162
  if (state === "waiting") {
2096
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
2097
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
2098
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", onClick: runPoll, style: buttonStyle, children: "Atualizar" })
2163
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
2164
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
2165
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { type: "button", onClick: runPoll, style: buttonStyle, children: "Atualizar" })
2099
2166
  ] }) });
2100
2167
  }
2101
2168
  if (state === "timeout") {
2102
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { role: "alert", "aria-live": "assertive", style: overlayStyle2, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { maxWidth: 360, textAlign: "center", lineHeight: 1.5 }, children: [
2103
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: { marginBottom: 16 }, children: "Ainda n\xE3o conseguimos confirmar seu pagamento com o banco. Voc\xEA pode tentar de novo, voltar pro app, ou falar com a gente." }),
2104
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
2105
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2169
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { role: "alert", "aria-live": "assertive", style: overlayStyle2, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { maxWidth: 360, textAlign: "center", lineHeight: 1.5 }, children: [
2170
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { marginBottom: 16 }, children: "Ainda n\xE3o conseguimos confirmar seu pagamento com o banco. Voc\xEA pode tentar de novo, voltar pro app, ou falar com a gente." }),
2171
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
2172
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2106
2173
  "button",
2107
2174
  {
2108
2175
  type: "button",
@@ -2115,7 +2182,7 @@ function PaymentReturnHandler({ children }) {
2115
2182
  children: "Tentar de novo"
2116
2183
  }
2117
2184
  ),
2118
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2185
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2119
2186
  "button",
2120
2187
  {
2121
2188
  type: "button",
@@ -2125,7 +2192,7 @@ function PaymentReturnHandler({ children }) {
2125
2192
  children: "Voltar pro app"
2126
2193
  }
2127
2194
  ),
2128
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2195
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2129
2196
  "a",
2130
2197
  {
2131
2198
  href: SUPPORT_MAILTO,
@@ -2137,7 +2204,7 @@ function PaymentReturnHandler({ children }) {
2137
2204
  ] })
2138
2205
  ] }) });
2139
2206
  }
2140
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_jsx_runtime19.Fragment, { children });
2207
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_jsx_runtime20.Fragment, { children });
2141
2208
  }
2142
2209
  var overlayStyle2 = {
2143
2210
  position: "fixed",
@@ -2176,7 +2243,7 @@ var linkStyle = {
2176
2243
  };
2177
2244
 
2178
2245
  // src/AppRoot.tsx
2179
- var import_jsx_runtime20 = require("react/jsx-runtime");
2246
+ var import_jsx_runtime21 = require("react/jsx-runtime");
2180
2247
  function buildLegacyConfigShim(config) {
2181
2248
  const paywall = config.paywall;
2182
2249
  const isFree = paywall.mode === "free";
@@ -2249,32 +2316,47 @@ function AppRoot(props) {
2249
2316
  "[hook-template] <AppRoot>: PreAuthFlow slot prop is required when config.onboarding.trigger === 'pre_signup_custom'."
2250
2317
  );
2251
2318
  }
2252
- const legacyShim = (0, import_react13.useMemo)(() => buildLegacyConfigShim(config), [config]);
2319
+ const legacyShim = (0, import_react14.useMemo)(() => buildLegacyConfigShim(config), [config]);
2253
2320
  const Router = testRouter === "memory" ? import_react_router_dom2.MemoryRouter : import_react_router_dom2.BrowserRouter;
2254
2321
  const basename = `/app/${config.slug}`;
2255
2322
  const routerProps = testRouter === "memory" ? { basename, initialEntries: testInitialEntries } : { basename };
2256
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ErrorBoundary, { children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(AppConfigProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TemplateConfigProvider, { config: legacyShim, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ThemeProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(PersistenceRegistry, { config: config.persistedKeys, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Router, { ...routerProps, children: [
2257
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DeepLinkHandler, { deepLinks: config.deepLinks }),
2258
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(SessionExpiredBanner, {}),
2259
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(InstallGate, { children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2260
- AuthGated,
2261
- {
2262
- config,
2263
- Login,
2264
- Signup,
2265
- Forgot,
2266
- Reset,
2267
- EmailVerify,
2268
- Paywall,
2269
- Onboarding,
2270
- PreAuthFlow,
2271
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(SubscriptionGate, { Paywall: Paywall ?? FallbackPaywall, children: [
2272
- children,
2273
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(PushPrompt, {})
2274
- ] })
2275
- }
2276
- ) })
2277
- ] }) }) }) }) }) });
2323
+ const position = config.install_prompt?.position ?? "post-paywall";
2324
+ const subscriptionGated = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(SubscriptionGate, { Paywall: Paywall ?? FallbackPaywall, children: position === "post-paywall" ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(InstallGate, { position: "post-paywall", children: [
2325
+ children,
2326
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PushPrompt, {})
2327
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
2328
+ children,
2329
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PushPrompt, {})
2330
+ ] }) });
2331
+ const authGated = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2332
+ AuthGated,
2333
+ {
2334
+ config,
2335
+ Login,
2336
+ Signup,
2337
+ Forgot,
2338
+ Reset,
2339
+ EmailVerify,
2340
+ Paywall,
2341
+ Onboarding,
2342
+ PreAuthFlow,
2343
+ children: subscriptionGated
2344
+ }
2345
+ );
2346
+ const routedTree = /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Router, { ...routerProps, children: [
2347
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DeepLinkHandler, { deepLinks: config.deepLinks }),
2348
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(SessionExpiredBanner, {}),
2349
+ position === "pre-auth" ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(InstallGate, { position: "pre-auth", children: authGated }) : authGated
2350
+ ] });
2351
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ErrorBoundary, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(AppConfigProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TemplateConfigProvider, { config: legacyShim, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ThemeProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PersistenceRegistry, { config: config.persistedKeys, children: config.i18n ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2352
+ I18nProvider,
2353
+ {
2354
+ defaultLocale: config.i18n.defaultLocale,
2355
+ supportedLocales: config.i18n.supportedLocales,
2356
+ resources: config.i18n.resources,
2357
+ children: routedTree
2358
+ }
2359
+ ) : routedTree }) }) }) }) });
2278
2360
  }
2279
2361
  function AuthGated({
2280
2362
  children,
@@ -2286,37 +2368,37 @@ function AuthGated({
2286
2368
  EmailVerify,
2287
2369
  PreAuthFlow
2288
2370
  }) {
2289
- const { authStatus } = (0, import_sdk6.useHook)();
2371
+ const { authStatus } = (0, import_sdk7.useHook)();
2290
2372
  if (authStatus === "loading") return null;
2291
2373
  if (authStatus !== "authenticated") {
2292
2374
  if (config.onboarding?.trigger === "pre_signup_custom" && PreAuthFlow) {
2293
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_react_router_dom2.Routes, { children: [
2294
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_router_dom2.Route, { path: "/signin", element: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Login, {}) }),
2295
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_router_dom2.Route, { path: "/signup", element: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Signup, {}) }),
2296
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_router_dom2.Route, { path: "/forgot", element: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Forgot, {}) }),
2297
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_router_dom2.Route, { path: "/reset", element: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Reset, {}) }),
2298
- EmailVerify ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_router_dom2.Route, { path: "/verify", element: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(EmailVerify, {}) }) : null,
2299
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_router_dom2.Route, { path: "/*", element: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(PreAuthFlow, {}) })
2375
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_react_router_dom2.Routes, { children: [
2376
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_router_dom2.Route, { path: "/signin", element: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Login, {}) }),
2377
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_router_dom2.Route, { path: "/signup", element: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Signup, {}) }),
2378
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_router_dom2.Route, { path: "/forgot", element: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Forgot, {}) }),
2379
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_router_dom2.Route, { path: "/reset", element: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Reset, {}) }),
2380
+ EmailVerify ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_router_dom2.Route, { path: "/verify", element: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(EmailVerify, {}) }) : null,
2381
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_router_dom2.Route, { path: "/*", element: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PreAuthFlow, {}) })
2300
2382
  ] });
2301
2383
  }
2302
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_react_router_dom2.Routes, { children: [
2303
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_router_dom2.Route, { path: "/", element: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Login, {}) }),
2304
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_router_dom2.Route, { path: "/signup", element: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Signup, {}) }),
2305
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_router_dom2.Route, { path: "/forgot", element: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Forgot, {}) }),
2306
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_router_dom2.Route, { path: "/reset", element: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Reset, {}) }),
2307
- EmailVerify ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_router_dom2.Route, { path: "/verify", element: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(EmailVerify, {}) }) : null,
2308
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_router_dom2.Route, { path: "*", element: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_router_dom2.Navigate, { to: "/", replace: true }) })
2384
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_react_router_dom2.Routes, { children: [
2385
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_router_dom2.Route, { path: "/", element: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Login, {}) }),
2386
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_router_dom2.Route, { path: "/signup", element: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Signup, {}) }),
2387
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_router_dom2.Route, { path: "/forgot", element: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Forgot, {}) }),
2388
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_router_dom2.Route, { path: "/reset", element: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Reset, {}) }),
2389
+ EmailVerify ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_router_dom2.Route, { path: "/verify", element: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(EmailVerify, {}) }) : null,
2390
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_router_dom2.Route, { path: "*", element: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_router_dom2.Navigate, { to: "/", replace: true }) })
2309
2391
  ] });
2310
2392
  }
2311
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_jsx_runtime20.Fragment, { children });
2393
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_jsx_runtime21.Fragment, { children });
2312
2394
  }
2313
2395
  function FallbackPaywall() {
2314
2396
  return null;
2315
2397
  }
2316
2398
 
2317
2399
  // src/hooks/usePush.ts
2318
- var import_react14 = require("react");
2319
- var import_sdk7 = require("@hook-sdk/sdk");
2400
+ var import_react15 = require("react");
2401
+ var import_sdk8 = require("@hook-sdk/sdk");
2320
2402
  var DISMISS_STORAGE_KEY = "push:dismissed-until";
2321
2403
  var DISMISS_TTL_MS2 = 7 * 24 * 60 * 60 * 1e3;
2322
2404
  function detectIosNeedsInstall() {
@@ -2360,12 +2442,12 @@ function deriveState(push) {
2360
2442
  return { kind: "prompt" };
2361
2443
  }
2362
2444
  function usePush() {
2363
- const { push } = (0, import_sdk7.useHook)();
2364
- const [state, setState] = (0, import_react14.useState)(() => deriveState(push));
2365
- (0, import_react14.useEffect)(() => {
2445
+ const { push } = (0, import_sdk8.useHook)();
2446
+ const [state, setState] = (0, import_react15.useState)(() => deriveState(push));
2447
+ (0, import_react15.useEffect)(() => {
2366
2448
  setState(deriveState(push));
2367
2449
  }, [push]);
2368
- const subscribe = (0, import_react14.useCallback)(async () => {
2450
+ const subscribe = (0, import_react15.useCallback)(async () => {
2369
2451
  try {
2370
2452
  await push.subscribe();
2371
2453
  setState({ kind: "subscribed" });
@@ -2377,7 +2459,7 @@ function usePush() {
2377
2459
  throw e;
2378
2460
  }
2379
2461
  }, [push]);
2380
- const unsubscribe = (0, import_react14.useCallback)(async () => {
2462
+ const unsubscribe = (0, import_react15.useCallback)(async () => {
2381
2463
  try {
2382
2464
  await push.unsubscribe();
2383
2465
  setState({ kind: "prompt" });
@@ -2386,7 +2468,7 @@ function usePush() {
2386
2468
  throw e;
2387
2469
  }
2388
2470
  }, [push]);
2389
- const dismiss = (0, import_react14.useCallback)(() => {
2471
+ const dismiss = (0, import_react15.useCallback)(() => {
2390
2472
  if (typeof localStorage !== "undefined") {
2391
2473
  try {
2392
2474
  localStorage.setItem(DISMISS_STORAGE_KEY, String(Date.now() + DISMISS_TTL_MS2));
@@ -2399,7 +2481,7 @@ function usePush() {
2399
2481
  }
2400
2482
 
2401
2483
  // src/components/PushPrompt.tsx
2402
- var import_jsx_runtime21 = require("react/jsx-runtime");
2484
+ var import_jsx_runtime22 = require("react/jsx-runtime");
2403
2485
  function platformRecoveryCopy(texts) {
2404
2486
  if (typeof navigator === "undefined") return null;
2405
2487
  const ua = navigator.userAgent || "";
@@ -2422,28 +2504,28 @@ function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, clas
2422
2504
  const { state, subscribe } = usePush();
2423
2505
  if (state.kind === "subscribed" || state.kind === "dismissed") return null;
2424
2506
  if (state.kind === "ios_needs_install") {
2425
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
2426
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("h3", { children: texts.iosInstallTitle }),
2427
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { children: texts.iosInstallBody }),
2428
- onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
2507
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
2508
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("h3", { children: texts.iosInstallTitle }),
2509
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { children: texts.iosInstallBody }),
2510
+ onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
2429
2511
  ] });
2430
2512
  }
2431
2513
  if (state.kind === "denied") {
2432
2514
  const recovery = platformRecoveryCopy(texts);
2433
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className, role: "region", "aria-label": texts.deniedTitle, children: [
2434
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("h3", { children: texts.deniedTitle }),
2435
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { children: texts.deniedBody }),
2436
- recovery && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { "data-testid": "denied-recovery", children: recovery })
2515
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className, role: "region", "aria-label": texts.deniedTitle, children: [
2516
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("h3", { children: texts.deniedTitle }),
2517
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { children: texts.deniedBody }),
2518
+ recovery && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { "data-testid": "denied-recovery", children: recovery })
2437
2519
  ] });
2438
2520
  }
2439
2521
  if (state.kind === "unsupported") {
2440
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className, role: "region", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { children: texts.unsupportedBody }) });
2522
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className, role: "region", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { children: texts.unsupportedBody }) });
2441
2523
  }
2442
2524
  if (state.kind === "error") {
2443
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { children: state.message }) });
2525
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { children: state.message }) });
2444
2526
  }
2445
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className, role: "region", children: [
2446
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2527
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className, role: "region", children: [
2528
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2447
2529
  "button",
2448
2530
  {
2449
2531
  type: "button",
@@ -2457,41 +2539,67 @@ function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, clas
2457
2539
  children: texts.cta
2458
2540
  }
2459
2541
  ),
2460
- onDeclined && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
2542
+ onDeclined && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
2543
+ ] });
2544
+ }
2545
+
2546
+ // src/components/LanguageSwitcher.tsx
2547
+ var import_sdk9 = require("@hook-sdk/sdk");
2548
+ var import_jsx_runtime23 = require("react/jsx-runtime");
2549
+ function LanguageSwitcher({ id, className, label = "Language" }) {
2550
+ const config = useAppConfig();
2551
+ const i18nConfig = config.i18n;
2552
+ const [userLocale, setUserLocale] = (0, import_sdk9.usePersistedState)(
2553
+ "user-locale",
2554
+ i18nConfig?.defaultLocale ?? "en-US"
2555
+ );
2556
+ if (!i18nConfig) return null;
2557
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { className, children: [
2558
+ label ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { children: label }) : null,
2559
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2560
+ "select",
2561
+ {
2562
+ id,
2563
+ value: userLocale,
2564
+ onChange: (e) => setUserLocale(e.target.value),
2565
+ "data-testid": "language-switcher",
2566
+ children: i18nConfig.supportedLocales.map((loc) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("option", { value: loc, children: loc }, loc))
2567
+ }
2568
+ )
2461
2569
  ] });
2462
2570
  }
2463
2571
 
2464
2572
  // src/defaults/LoadingState.tsx
2465
- var import_jsx_runtime22 = require("react/jsx-runtime");
2573
+ var import_jsx_runtime24 = require("react/jsx-runtime");
2466
2574
  function LoadingState({ message }) {
2467
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { role: "status", "aria-live": "polite", style: { padding: 24, textAlign: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: message ?? "Carregando..." }) });
2575
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { role: "status", "aria-live": "polite", style: { padding: 24, textAlign: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: message ?? "Carregando..." }) });
2468
2576
  }
2469
2577
 
2470
2578
  // src/defaults/EmptyState.tsx
2471
- var import_jsx_runtime23 = require("react/jsx-runtime");
2579
+ var import_jsx_runtime25 = require("react/jsx-runtime");
2472
2580
  function EmptyState({ title, description, action }) {
2473
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
2474
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h2", { style: { marginBottom: 8 }, children: title }),
2475
- description && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { style: { opacity: 0.7 }, children: description }),
2476
- action && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { marginTop: 16 }, children: action })
2581
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
2582
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("h2", { style: { marginBottom: 8 }, children: title }),
2583
+ description && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { style: { opacity: 0.7 }, children: description }),
2584
+ action && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { style: { marginTop: 16 }, children: action })
2477
2585
  ] });
2478
2586
  }
2479
2587
 
2480
2588
  // src/hooks/useLoginForm.ts
2481
- var import_react15 = require("react");
2482
- var import_sdk9 = require("@hook-sdk/sdk");
2589
+ var import_react16 = require("react");
2590
+ var import_sdk11 = require("@hook-sdk/sdk");
2483
2591
 
2484
2592
  // src/errors.ts
2485
- var import_sdk8 = require("@hook-sdk/sdk");
2593
+ var import_sdk10 = require("@hook-sdk/sdk");
2486
2594
  function mapSdkError(err) {
2487
- if (err instanceof import_sdk8.SdkRateLimitError) {
2595
+ if (err instanceof import_sdk10.SdkRateLimitError) {
2488
2596
  return {
2489
2597
  code: "rate_limited",
2490
2598
  message: `Aguarde ${err.retryAfter}s e tente novamente.`,
2491
2599
  retryAfter: err.retryAfter
2492
2600
  };
2493
2601
  }
2494
- if (err instanceof import_sdk8.SdkAuthError) {
2602
+ if (err instanceof import_sdk10.SdkAuthError) {
2495
2603
  const detail = err.detail;
2496
2604
  if (detail === "email_unverified") {
2497
2605
  return { code: "email_unverified", message: "Confirme seu e-mail antes de entrar." };
@@ -2501,7 +2609,7 @@ function mapSdkError(err) {
2501
2609
  }
2502
2610
  return { code: "invalid_credentials", message: "E-mail ou senha inv\xE1lidos." };
2503
2611
  }
2504
- if (err instanceof import_sdk8.SdkError && err.httpStatus === 0) {
2612
+ if (err instanceof import_sdk10.SdkError && err.httpStatus === 0) {
2505
2613
  return { code: "network", message: "Sem conex\xE3o com o servidor. Verifique sua internet." };
2506
2614
  }
2507
2615
  if (err instanceof TypeError) {
@@ -2514,20 +2622,20 @@ function mapSdkError(err) {
2514
2622
  var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2515
2623
  var MIN_PASSWORD = 8;
2516
2624
  function useLoginForm() {
2517
- const { auth } = (0, import_sdk9.useHook)();
2518
- const [email, setEmail] = (0, import_react15.useState)("");
2519
- const [password, setPassword] = (0, import_react15.useState)("");
2520
- const [submitting, setSubmitting] = (0, import_react15.useState)(false);
2521
- const [error, setError] = (0, import_react15.useState)(null);
2522
- const [touchedEmail, setTouchedEmail] = (0, import_react15.useState)(false);
2523
- const [touchedPassword, setTouchedPassword] = (0, import_react15.useState)(false);
2524
- const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react15.useState)(false);
2525
- const validateEmail = (0, import_react15.useMemo)(() => {
2625
+ const { auth } = (0, import_sdk11.useHook)();
2626
+ const [email, setEmail] = (0, import_react16.useState)("");
2627
+ const [password, setPassword] = (0, import_react16.useState)("");
2628
+ const [submitting, setSubmitting] = (0, import_react16.useState)(false);
2629
+ const [error, setError] = (0, import_react16.useState)(null);
2630
+ const [touchedEmail, setTouchedEmail] = (0, import_react16.useState)(false);
2631
+ const [touchedPassword, setTouchedPassword] = (0, import_react16.useState)(false);
2632
+ const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react16.useState)(false);
2633
+ const validateEmail = (0, import_react16.useMemo)(() => {
2526
2634
  if (email.length === 0) return null;
2527
2635
  if (!EMAIL_RE.test(email)) return "Formato de e-mail inv\xE1lido.";
2528
2636
  return null;
2529
2637
  }, [email]);
2530
- const validatePassword = (0, import_react15.useMemo)(() => {
2638
+ const validatePassword = (0, import_react16.useMemo)(() => {
2531
2639
  if (password.length === 0) return null;
2532
2640
  if (password.length < MIN_PASSWORD) return `M\xEDnimo de ${MIN_PASSWORD} caracteres.`;
2533
2641
  return null;
@@ -2535,7 +2643,7 @@ function useLoginForm() {
2535
2643
  const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
2536
2644
  const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
2537
2645
  const canSubmit = email.length > 0 && password.length >= MIN_PASSWORD && validateEmail === null && validatePassword === null && !submitting;
2538
- const submit = (0, import_react15.useCallback)(async () => {
2646
+ const submit = (0, import_react16.useCallback)(async () => {
2539
2647
  setFormSubmitAttempted(true);
2540
2648
  if (!canSubmit) return false;
2541
2649
  setSubmitting(true);
@@ -2569,32 +2677,32 @@ function useLoginForm() {
2569
2677
  }
2570
2678
 
2571
2679
  // src/hooks/useSignupForm.ts
2572
- var import_react16 = require("react");
2573
- var import_sdk10 = require("@hook-sdk/sdk");
2680
+ var import_react17 = require("react");
2681
+ var import_sdk12 = require("@hook-sdk/sdk");
2574
2682
  var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2575
2683
  var MIN_PASSWORD2 = 8;
2576
2684
  function useSignupForm() {
2577
- const { auth } = (0, import_sdk10.useHook)();
2578
- const [name, setName] = (0, import_react16.useState)("");
2579
- const [email, setEmail] = (0, import_react16.useState)("");
2580
- const [password, setPassword] = (0, import_react16.useState)("");
2581
- const [submitting, setSubmitting] = (0, import_react16.useState)(false);
2582
- const [error, setError] = (0, import_react16.useState)(null);
2583
- const [touchedName, setTouchedName] = (0, import_react16.useState)(false);
2584
- const [touchedEmail, setTouchedEmail] = (0, import_react16.useState)(false);
2585
- const [touchedPassword, setTouchedPassword] = (0, import_react16.useState)(false);
2586
- const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react16.useState)(false);
2587
- const validateName = (0, import_react16.useMemo)(() => {
2685
+ const { auth } = (0, import_sdk12.useHook)();
2686
+ const [name, setName] = (0, import_react17.useState)("");
2687
+ const [email, setEmail] = (0, import_react17.useState)("");
2688
+ const [password, setPassword] = (0, import_react17.useState)("");
2689
+ const [submitting, setSubmitting] = (0, import_react17.useState)(false);
2690
+ const [error, setError] = (0, import_react17.useState)(null);
2691
+ const [touchedName, setTouchedName] = (0, import_react17.useState)(false);
2692
+ const [touchedEmail, setTouchedEmail] = (0, import_react17.useState)(false);
2693
+ const [touchedPassword, setTouchedPassword] = (0, import_react17.useState)(false);
2694
+ const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react17.useState)(false);
2695
+ const validateName = (0, import_react17.useMemo)(() => {
2588
2696
  if (name.length === 0) return null;
2589
2697
  if (name.trim().length < 2) return "Nome muito curto.";
2590
2698
  return null;
2591
2699
  }, [name]);
2592
- const validateEmail = (0, import_react16.useMemo)(() => {
2700
+ const validateEmail = (0, import_react17.useMemo)(() => {
2593
2701
  if (email.length === 0) return null;
2594
2702
  if (!EMAIL_RE2.test(email)) return "Formato de e-mail inv\xE1lido.";
2595
2703
  return null;
2596
2704
  }, [email]);
2597
- const validatePassword = (0, import_react16.useMemo)(() => {
2705
+ const validatePassword = (0, import_react17.useMemo)(() => {
2598
2706
  if (password.length === 0) return null;
2599
2707
  if (password.length < MIN_PASSWORD2) return `M\xEDnimo de ${MIN_PASSWORD2} caracteres.`;
2600
2708
  return null;
@@ -2603,7 +2711,7 @@ function useSignupForm() {
2603
2711
  const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
2604
2712
  const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
2605
2713
  const canSubmit = name.trim().length >= 2 && email.length > 0 && password.length >= MIN_PASSWORD2 && validateName === null && validateEmail === null && validatePassword === null && !submitting;
2606
- const submit = (0, import_react16.useCallback)(async () => {
2714
+ const submit = (0, import_react17.useCallback)(async () => {
2607
2715
  setFormSubmitAttempted(true);
2608
2716
  if (!canSubmit) return false;
2609
2717
  setSubmitting(true);
@@ -2641,25 +2749,25 @@ function useSignupForm() {
2641
2749
  }
2642
2750
 
2643
2751
  // src/hooks/useForgotForm.ts
2644
- var import_react17 = require("react");
2645
- var import_sdk11 = require("@hook-sdk/sdk");
2752
+ var import_react18 = require("react");
2753
+ var import_sdk13 = require("@hook-sdk/sdk");
2646
2754
  var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2647
2755
  function useForgotForm() {
2648
- const { auth } = (0, import_sdk11.useHook)();
2649
- const [email, setEmail] = (0, import_react17.useState)("");
2650
- const [submitting, setSubmitting] = (0, import_react17.useState)(false);
2651
- const [sent, setSent] = (0, import_react17.useState)(false);
2652
- const [error, setError] = (0, import_react17.useState)(null);
2653
- const [touchedEmail, setTouchedEmail] = (0, import_react17.useState)(false);
2654
- const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react17.useState)(false);
2655
- const validateEmail = (0, import_react17.useMemo)(() => {
2756
+ const { auth } = (0, import_sdk13.useHook)();
2757
+ const [email, setEmail] = (0, import_react18.useState)("");
2758
+ const [submitting, setSubmitting] = (0, import_react18.useState)(false);
2759
+ const [sent, setSent] = (0, import_react18.useState)(false);
2760
+ const [error, setError] = (0, import_react18.useState)(null);
2761
+ const [touchedEmail, setTouchedEmail] = (0, import_react18.useState)(false);
2762
+ const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react18.useState)(false);
2763
+ const validateEmail = (0, import_react18.useMemo)(() => {
2656
2764
  if (email.length === 0) return null;
2657
2765
  if (!EMAIL_RE3.test(email)) return "Formato de e-mail inv\xE1lido.";
2658
2766
  return null;
2659
2767
  }, [email]);
2660
2768
  const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
2661
2769
  const canSubmit = email.length > 0 && validateEmail === null && !submitting;
2662
- const submit = (0, import_react17.useCallback)(async () => {
2770
+ const submit = (0, import_react18.useCallback)(async () => {
2663
2771
  setFormSubmitAttempted(true);
2664
2772
  if (!canSubmit) return false;
2665
2773
  setSubmitting(true);
@@ -2690,32 +2798,32 @@ function useForgotForm() {
2690
2798
  }
2691
2799
 
2692
2800
  // src/hooks/useResetForm.ts
2693
- var import_react18 = require("react");
2694
- var import_sdk12 = require("@hook-sdk/sdk");
2801
+ var import_react19 = require("react");
2802
+ var import_sdk14 = require("@hook-sdk/sdk");
2695
2803
  var MIN_PASSWORD3 = 12;
2696
2804
  function useResetForm() {
2697
- const { auth } = (0, import_sdk12.useHook)();
2698
- const [token, setToken] = (0, import_react18.useState)(null);
2699
- const [password, setPassword] = (0, import_react18.useState)("");
2700
- const [confirm, setConfirm] = (0, import_react18.useState)("");
2701
- const [submitting, setSubmitting] = (0, import_react18.useState)(false);
2702
- const [done, setDone] = (0, import_react18.useState)(false);
2703
- const [error, setError] = (0, import_react18.useState)(null);
2704
- const [touchedPassword, setTouchedPassword] = (0, import_react18.useState)(false);
2705
- const [touchedConfirm, setTouchedConfirm] = (0, import_react18.useState)(false);
2706
- const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react18.useState)(false);
2707
- (0, import_react18.useEffect)(() => {
2805
+ const { auth } = (0, import_sdk14.useHook)();
2806
+ const [token, setToken] = (0, import_react19.useState)(null);
2807
+ const [password, setPassword] = (0, import_react19.useState)("");
2808
+ const [confirm, setConfirm] = (0, import_react19.useState)("");
2809
+ const [submitting, setSubmitting] = (0, import_react19.useState)(false);
2810
+ const [done, setDone] = (0, import_react19.useState)(false);
2811
+ const [error, setError] = (0, import_react19.useState)(null);
2812
+ const [touchedPassword, setTouchedPassword] = (0, import_react19.useState)(false);
2813
+ const [touchedConfirm, setTouchedConfirm] = (0, import_react19.useState)(false);
2814
+ const [formSubmitAttempted, setFormSubmitAttempted] = (0, import_react19.useState)(false);
2815
+ (0, import_react19.useEffect)(() => {
2708
2816
  if (typeof window === "undefined") return;
2709
2817
  const params = new URLSearchParams(window.location.search);
2710
2818
  const t = params.get("token");
2711
2819
  setToken(t && t.length > 0 ? t : null);
2712
2820
  }, []);
2713
- const validatePassword = (0, import_react18.useMemo)(() => {
2821
+ const validatePassword = (0, import_react19.useMemo)(() => {
2714
2822
  if (password.length === 0) return null;
2715
2823
  if (password.length < MIN_PASSWORD3) return `M\xEDnimo de ${MIN_PASSWORD3} caracteres.`;
2716
2824
  return null;
2717
2825
  }, [password]);
2718
- const validateConfirm = (0, import_react18.useMemo)(() => {
2826
+ const validateConfirm = (0, import_react19.useMemo)(() => {
2719
2827
  if (confirm.length === 0) return null;
2720
2828
  if (confirm !== password) return "Senhas n\xE3o coincidem.";
2721
2829
  return null;
@@ -2723,7 +2831,7 @@ function useResetForm() {
2723
2831
  const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
2724
2832
  const confirmError = touchedConfirm || formSubmitAttempted ? validateConfirm : null;
2725
2833
  const canSubmit = token !== null && password.length >= MIN_PASSWORD3 && confirm === password && validatePassword === null && validateConfirm === null && !submitting && !done;
2726
- const submit = (0, import_react18.useCallback)(async () => {
2834
+ const submit = (0, import_react19.useCallback)(async () => {
2727
2835
  setFormSubmitAttempted(true);
2728
2836
  if (!canSubmit || token === null) return;
2729
2837
  setSubmitting(true);
@@ -2763,9 +2871,9 @@ function useResetForm() {
2763
2871
  }
2764
2872
 
2765
2873
  // src/hooks/usePlan.ts
2766
- var import_sdk13 = require("@hook-sdk/sdk");
2874
+ var import_sdk15 = require("@hook-sdk/sdk");
2767
2875
  function usePlan() {
2768
- const { plan } = (0, import_sdk13.useHook)();
2876
+ const { plan } = (0, import_sdk15.useHook)();
2769
2877
  return plan;
2770
2878
  }
2771
2879
 
@@ -2798,12 +2906,12 @@ function discountPercent(anchorCents, realCents) {
2798
2906
  }
2799
2907
 
2800
2908
  // src/hooks/useAuthPrimitives.ts
2801
- var import_react19 = require("react");
2802
- var import_sdk14 = require("@hook-sdk/sdk");
2909
+ var import_react20 = require("react");
2910
+ var import_sdk16 = require("@hook-sdk/sdk");
2803
2911
  var warned = false;
2804
2912
  function useAuthPrimitives() {
2805
- const { auth } = (0, import_sdk14.useHook)();
2806
- (0, import_react19.useEffect)(() => {
2913
+ const { auth } = (0, import_sdk16.useHook)();
2914
+ (0, import_react20.useEffect)(() => {
2807
2915
  if (!warned && process.env.NODE_ENV !== "production") {
2808
2916
  warned = true;
2809
2917
  console.warn(
@@ -2825,9 +2933,9 @@ function useAuthPrimitives() {
2825
2933
  }
2826
2934
 
2827
2935
  // src/hooks/useAuth.ts
2828
- var import_sdk15 = require("@hook-sdk/sdk");
2936
+ var import_sdk17 = require("@hook-sdk/sdk");
2829
2937
  function useAuth() {
2830
- const { user, authStatus, auth } = (0, import_sdk15.useHook)();
2938
+ const { user, authStatus, auth } = (0, import_sdk17.useHook)();
2831
2939
  return {
2832
2940
  user,
2833
2941
  authStatus,
@@ -2836,26 +2944,26 @@ function useAuth() {
2836
2944
  }
2837
2945
 
2838
2946
  // src/index.ts
2839
- var import_sdk19 = require("@hook-sdk/sdk");
2947
+ var import_sdk21 = require("@hook-sdk/sdk");
2840
2948
 
2841
2949
  // src/hooks/useSubscription.ts
2842
- var import_sdk16 = require("@hook-sdk/sdk");
2950
+ var import_sdk18 = require("@hook-sdk/sdk");
2843
2951
  function useSubscription() {
2844
- const { subscription } = (0, import_sdk16.useHook)();
2952
+ const { subscription } = (0, import_sdk18.useHook)();
2845
2953
  return {
2846
2954
  status: subscription.status()
2847
2955
  };
2848
2956
  }
2849
2957
 
2850
2958
  // src/hooks/useReminders.ts
2851
- var import_react20 = require("react");
2852
- var import_sdk17 = require("@hook-sdk/sdk");
2959
+ var import_react21 = require("react");
2960
+ var import_sdk19 = require("@hook-sdk/sdk");
2853
2961
  function useReminders() {
2854
- const { push } = (0, import_sdk17.useHook)();
2962
+ const { push } = (0, import_sdk19.useHook)();
2855
2963
  const r = push.reminders;
2856
- const [reminders, setReminders] = (0, import_react20.useState)([]);
2857
- const [loading, setLoading] = (0, import_react20.useState)(true);
2858
- const reload = (0, import_react20.useCallback)(async () => {
2964
+ const [reminders, setReminders] = (0, import_react21.useState)([]);
2965
+ const [loading, setLoading] = (0, import_react21.useState)(true);
2966
+ const reload = (0, import_react21.useCallback)(async () => {
2859
2967
  setLoading(true);
2860
2968
  try {
2861
2969
  const next = await r.list();
@@ -2864,38 +2972,38 @@ function useReminders() {
2864
2972
  setLoading(false);
2865
2973
  }
2866
2974
  }, [r]);
2867
- (0, import_react20.useEffect)(() => {
2975
+ (0, import_react21.useEffect)(() => {
2868
2976
  void reload();
2869
2977
  }, [reload]);
2870
- const setReminder = (0, import_react20.useCallback)(async (input) => {
2978
+ const setReminder = (0, import_react21.useCallback)(async (input) => {
2871
2979
  await r.set(input);
2872
2980
  await reload();
2873
2981
  }, [r, reload]);
2874
- const deleteReminder = (0, import_react20.useCallback)(async (slot) => {
2982
+ const deleteReminder = (0, import_react21.useCallback)(async (slot) => {
2875
2983
  await r.delete(slot);
2876
2984
  await reload();
2877
2985
  }, [r, reload]);
2878
- const schedule = (0, import_react20.useCallback)(async (items) => {
2986
+ const schedule = (0, import_react21.useCallback)(async (items) => {
2879
2987
  return r.schedule(items);
2880
2988
  }, [r]);
2881
- const setFallbacks = (0, import_react20.useCallback)(async (items) => {
2989
+ const setFallbacks = (0, import_react21.useCallback)(async (items) => {
2882
2990
  return r.setFallbacks(items);
2883
2991
  }, [r]);
2884
2992
  return { reminders, loading, setReminder, deleteReminder, schedule, setFallbacks };
2885
2993
  }
2886
2994
 
2887
2995
  // src/hooks/useToast.ts
2888
- var import_react21 = require("react");
2996
+ var import_react22 = require("react");
2889
2997
  function useToast() {
2890
- const [items, setItems] = (0, import_react21.useState)([]);
2891
- const show = (0, import_react21.useCallback)((message, kind = "info") => {
2998
+ const [items, setItems] = (0, import_react22.useState)([]);
2999
+ const show = (0, import_react22.useCallback)((message, kind = "info") => {
2892
3000
  const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
2893
3001
  setItems((prev) => [...prev, { id, message, kind }]);
2894
3002
  setTimeout(() => {
2895
3003
  setItems((prev) => prev.filter((t) => t.id !== id));
2896
3004
  }, 4e3);
2897
3005
  }, []);
2898
- const dismiss = (0, import_react21.useCallback)((id) => {
3006
+ const dismiss = (0, import_react22.useCallback)((id) => {
2899
3007
  setItems((prev) => prev.filter((t) => t.id !== id));
2900
3008
  }, []);
2901
3009
  return { items, show, dismiss };
@@ -2903,20 +3011,20 @@ function useToast() {
2903
3011
 
2904
3012
  // src/RouteBoundary.tsx
2905
3013
  var import_react_router_dom3 = require("react-router-dom");
2906
- var import_jsx_runtime24 = require("react/jsx-runtime");
3014
+ var import_jsx_runtime26 = require("react/jsx-runtime");
2907
3015
  function RouteBoundary({ children }) {
2908
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_react_router_dom3.Routes, { children: [
3016
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_react_router_dom3.Routes, { children: [
2909
3017
  children,
2910
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_react_router_dom3.Route, { path: "*", element: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(DefaultNotFound, {}) })
3018
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_router_dom3.Route, { path: "*", element: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(DefaultNotFound, {}) })
2911
3019
  ] });
2912
3020
  }
2913
3021
  function DefaultNotFound() {
2914
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { role: "alert", children: "P\xE1gina n\xE3o encontrada" });
3022
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { role: "alert", children: "P\xE1gina n\xE3o encontrada" });
2915
3023
  }
2916
3024
 
2917
3025
  // src/PreAuthShell.tsx
2918
3026
  var import_react_router_dom4 = require("react-router-dom");
2919
- var import_jsx_runtime25 = require("react/jsx-runtime");
3027
+ var import_jsx_runtime27 = require("react/jsx-runtime");
2920
3028
  function PreAuthShell({
2921
3029
  basename,
2922
3030
  testRouter,
@@ -2924,20 +3032,20 @@ function PreAuthShell({
2924
3032
  children
2925
3033
  }) {
2926
3034
  if (testRouter === "memory") {
2927
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_router_dom4.MemoryRouter, { basename, initialEntries: testInitialEntries, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_router_dom4.Routes, { children }) });
3035
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_router_dom4.MemoryRouter, { basename, initialEntries: testInitialEntries, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_router_dom4.Routes, { children }) });
2928
3036
  }
2929
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_router_dom4.BrowserRouter, { basename, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_router_dom4.Routes, { children }) });
3037
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_router_dom4.BrowserRouter, { basename, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_router_dom4.Routes, { children }) });
2930
3038
  }
2931
3039
 
2932
3040
  // src/OnboardingFlow.tsx
2933
- var import_react23 = require("react");
2934
- var import_sdk18 = require("@hook-sdk/sdk");
3041
+ var import_react24 = require("react");
3042
+ var import_sdk20 = require("@hook-sdk/sdk");
2935
3043
 
2936
3044
  // src/hooks/useOnboardingStep.ts
2937
- var import_react22 = require("react");
2938
- var OnboardingStepContext = (0, import_react22.createContext)(null);
3045
+ var import_react23 = require("react");
3046
+ var OnboardingStepContext = (0, import_react23.createContext)(null);
2939
3047
  function useOnboardingStep() {
2940
- const ctx = (0, import_react22.useContext)(OnboardingStepContext);
3048
+ const ctx = (0, import_react23.useContext)(OnboardingStepContext);
2941
3049
  if (!ctx) {
2942
3050
  throw new Error(
2943
3051
  "[hook-template] useOnboardingStep must be used inside <OnboardingFlow>. (G75)"
@@ -2947,7 +3055,7 @@ function useOnboardingStep() {
2947
3055
  }
2948
3056
 
2949
3057
  // src/OnboardingFlow.tsx
2950
- var import_jsx_runtime26 = require("react/jsx-runtime");
3058
+ var import_jsx_runtime28 = require("react/jsx-runtime");
2951
3059
  var isFilled = (v) => v != null && v !== "";
2952
3060
  var CURRENT_STEP_FIELD = "currentStep";
2953
3061
  function readPersistedStepIdx(draft) {
@@ -2960,12 +3068,12 @@ function OnboardingFlow({
2960
3068
  onComplete,
2961
3069
  persistKey
2962
3070
  }) {
2963
- const [draft, setDraft, status] = (0, import_sdk18.usePersistedState)(persistKey, {});
2964
- const draftRef = (0, import_react23.useRef)(draft);
3071
+ const [draft, setDraft, status] = (0, import_sdk20.usePersistedState)(persistKey, {});
3072
+ const draftRef = (0, import_react24.useRef)(draft);
2965
3073
  draftRef.current = draft;
2966
3074
  const idx = readPersistedStepIdx(draft);
2967
3075
  const clampedIdx = Math.min(Math.max(idx, 0), Math.max(steps.length - 1, 0));
2968
- const setIdx = (0, import_react23.useCallback)(
3076
+ const setIdx = (0, import_react24.useCallback)(
2969
3077
  (n) => {
2970
3078
  setDraft((prev) => {
2971
3079
  const prevIdx = readPersistedStepIdx(prev);
@@ -2975,7 +3083,7 @@ function OnboardingFlow({
2975
3083
  },
2976
3084
  [setDraft]
2977
3085
  );
2978
- const setValue = (0, import_react23.useCallback)(
3086
+ const setValue = (0, import_react24.useCallback)(
2979
3087
  (patch) => {
2980
3088
  draftRef.current = { ...draftRef.current, ...patch };
2981
3089
  setDraft((prev) => ({ ...prev, ...patch }));
@@ -2983,9 +3091,9 @@ function OnboardingFlow({
2983
3091
  [setDraft]
2984
3092
  );
2985
3093
  const step = steps[clampedIdx];
2986
- const hookCtx = (0, import_sdk18.useHook)();
3094
+ const hookCtx = (0, import_sdk20.useHook)();
2987
3095
  const track2 = typeof hookCtx.track === "function" ? hookCtx.track : void 0;
2988
- (0, import_react23.useEffect)(() => {
3096
+ (0, import_react24.useEffect)(() => {
2989
3097
  if (status.loading) return;
2990
3098
  if (!step) return;
2991
3099
  if (!track2) return;
@@ -2995,11 +3103,11 @@ function OnboardingFlow({
2995
3103
  total_steps: steps.length
2996
3104
  });
2997
3105
  }, [step?.id, clampedIdx, steps.length, status.loading, track2]);
2998
- const valid = (0, import_react23.useMemo)(
3106
+ const valid = (0, import_react24.useMemo)(
2999
3107
  () => step ? (step.validates ?? []).every((field) => isFilled(draft[field])) : false,
3000
3108
  [draft, step]
3001
3109
  );
3002
- const next = (0, import_react23.useCallback)(() => {
3110
+ const next = (0, import_react24.useCallback)(() => {
3003
3111
  if (!step) return;
3004
3112
  const current = draftRef.current;
3005
3113
  const validNow = (step.validates ?? []).every((field) => isFilled(current[field]));
@@ -3010,8 +3118,8 @@ function OnboardingFlow({
3010
3118
  setIdx(clampedIdx + 1);
3011
3119
  }
3012
3120
  }, [clampedIdx, onComplete, step, steps.length, setIdx]);
3013
- const prevStep = (0, import_react23.useCallback)(() => setIdx((i) => Math.max(0, i - 1)), [setIdx]);
3014
- const ctx = (0, import_react23.useMemo)(
3121
+ const prevStep = (0, import_react24.useCallback)(() => setIdx((i) => Math.max(0, i - 1)), [setIdx]);
3122
+ const ctx = (0, import_react24.useMemo)(
3015
3123
  () => ({
3016
3124
  stepIndex: clampedIdx,
3017
3125
  totalSteps: steps.length,
@@ -3037,7 +3145,7 @@ function OnboardingFlow({
3037
3145
  `[hook-template] OnboardingFlow: missing screen component for step '${step.id}' (expected key '${step.screen}' in screens prop)`
3038
3146
  );
3039
3147
  }
3040
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(OnboardingStepContext.Provider, { value: ctx, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Screen, {}) });
3148
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(OnboardingStepContext.Provider, { value: ctx, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Screen, {}) });
3041
3149
  }
3042
3150
 
3043
3151
  // src/hooks/useFeature.ts
@@ -3053,8 +3161,10 @@ function useFeature(name) {
3053
3161
  DeepLinkHandler,
3054
3162
  EmptyState,
3055
3163
  ErrorBoundary,
3164
+ I18nProvider,
3056
3165
  InstallGate,
3057
3166
  InstallSplash,
3167
+ LanguageSwitcher,
3058
3168
  LoadingState,
3059
3169
  OnboardingFlow,
3060
3170
  PaymentReturnHandler,