@hook-sdk/template 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,12 +1,20 @@
1
+ // src/AppRoot.tsx
2
+ import { useCallback as useCallback6, useEffect as useEffect4, useRef, useState as useState8 } from "react";
3
+ import { useHook as useHook8 } from "@hook-sdk/sdk";
4
+
1
5
  // src/internal/TemplateConfigContext.tsx
2
- import { createContext, useContext } from "react";
6
+ import { createContext, useContext, useMemo } from "react";
3
7
  import { jsx } from "react/jsx-runtime";
4
8
  var TemplateConfigContext = createContext(null);
5
9
  function TemplateConfigProvider({
6
10
  config,
7
11
  children
8
12
  }) {
9
- return /* @__PURE__ */ jsx(TemplateConfigContext.Provider, { value: config, children });
13
+ const value = useMemo(() => ({
14
+ ...config,
15
+ mode: config.subscription?.mode ?? "trial"
16
+ }), [config]);
17
+ return /* @__PURE__ */ jsx(TemplateConfigContext.Provider, { value, children });
10
18
  }
11
19
  function useTemplateConfig() {
12
20
  const ctx = useContext(TemplateConfigContext);
@@ -122,14 +130,35 @@ function usePaywallState() {
122
130
 
123
131
  // src/internal/SubscriptionGate.tsx
124
132
  import { Fragment as Fragment2, jsx as jsx5 } from "react/jsx-runtime";
133
+ var BLOCKING = /* @__PURE__ */ new Set([
134
+ "pending",
135
+ "expired",
136
+ "canceled",
137
+ "none"
138
+ ]);
125
139
  function SubscriptionGate({ Paywall, children }) {
140
+ const { mode } = useTemplateConfig();
126
141
  const { status } = usePaywallState();
127
- if (status === "expired" || status === "canceled" || status === "past_due") {
128
- return /* @__PURE__ */ jsx5(Paywall, {});
129
- }
142
+ if (mode === "free") return /* @__PURE__ */ jsx5(Fragment2, { children });
143
+ if (BLOCKING.has(status)) return /* @__PURE__ */ jsx5(Paywall, {});
130
144
  return /* @__PURE__ */ jsx5(Fragment2, { children });
131
145
  }
132
146
 
147
+ // src/internal/PersistedKeysPrefetch.tsx
148
+ import { useEffect as useEffect2 } from "react";
149
+ import { useHook as useHook3 } from "@hook-sdk/sdk";
150
+ import { Fragment as Fragment3, jsx as jsx6 } from "react/jsx-runtime";
151
+ function PersistedKeysPrefetch({ children }) {
152
+ const { appData } = useHook3();
153
+ const config = useTemplateConfig();
154
+ useEffect2(() => {
155
+ const keys = config.persistedKeys;
156
+ if (!keys || keys.length === 0) return;
157
+ appData.cache.startPrefetch(keys, (ks) => appData.bulkRead(ks));
158
+ }, [appData, config.persistedKeys]);
159
+ return /* @__PURE__ */ jsx6(Fragment3, { children });
160
+ }
161
+
133
162
  // src/internal/PushPrompt.tsx
134
163
  function PushPrompt() {
135
164
  return null;
@@ -137,7 +166,7 @@ function PushPrompt() {
137
166
 
138
167
  // src/defaults/ErrorBoundary.tsx
139
168
  import { Component } from "react";
140
- import { Fragment as Fragment3, jsx as jsx6, jsxs } from "react/jsx-runtime";
169
+ import { Fragment as Fragment4, jsx as jsx7, jsxs } from "react/jsx-runtime";
141
170
  var ErrorBoundary = class extends Component {
142
171
  state = { error: null };
143
172
  static getDerivedStateFromError(error) {
@@ -149,17 +178,17 @@ var ErrorBoundary = class extends Component {
149
178
  render() {
150
179
  if (this.state.error) {
151
180
  return this.props.fallback ?? /* @__PURE__ */ jsxs("div", { role: "alert", style: { padding: 24, textAlign: "center" }, children: [
152
- /* @__PURE__ */ jsx6("h2", { children: "Algo deu errado" }),
153
- /* @__PURE__ */ jsx6("p", { style: { opacity: 0.7 }, children: "Recarregue a p\xE1gina pra tentar de novo." })
181
+ /* @__PURE__ */ jsx7("h2", { children: "Algo deu errado" }),
182
+ /* @__PURE__ */ jsx7("p", { style: { opacity: 0.7 }, children: "Recarregue a p\xE1gina pra tentar de novo." })
154
183
  ] });
155
184
  }
156
- return /* @__PURE__ */ jsx6(Fragment3, { children: this.props.children });
185
+ return /* @__PURE__ */ jsx7(Fragment4, { children: this.props.children });
157
186
  }
158
187
  };
159
188
 
160
189
  // src/hooks/useLoginForm.ts
161
- import { useCallback as useCallback2, useMemo, useState as useState3 } from "react";
162
- import { useHook as useHook3 } from "@hook-sdk/sdk";
190
+ import { useCallback as useCallback2, useMemo as useMemo2, useState as useState3 } from "react";
191
+ import { useHook as useHook4 } from "@hook-sdk/sdk";
163
192
 
164
193
  // src/errors.ts
165
194
  import { SdkError, SdkAuthError, SdkRateLimitError } from "@hook-sdk/sdk";
@@ -194,30 +223,32 @@ function mapSdkError(err) {
194
223
  var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
195
224
  var MIN_PASSWORD = 8;
196
225
  function useLoginForm() {
197
- const { auth } = useHook3();
226
+ const { auth } = useHook4();
198
227
  const [email, setEmail] = useState3("");
199
228
  const [password, setPassword] = useState3("");
200
229
  const [submitting, setSubmitting] = useState3(false);
201
230
  const [error, setError] = useState3(null);
202
- const emailError = useMemo(() => {
231
+ const emailError = useMemo2(() => {
203
232
  if (email.length === 0) return null;
204
233
  if (!EMAIL_RE.test(email)) return "Formato de e-mail inv\xE1lido.";
205
234
  return null;
206
235
  }, [email]);
207
- const passwordError = useMemo(() => {
236
+ const passwordError = useMemo2(() => {
208
237
  if (password.length === 0) return null;
209
238
  if (password.length < MIN_PASSWORD) return `M\xEDnimo de ${MIN_PASSWORD} caracteres.`;
210
239
  return null;
211
240
  }, [password]);
212
241
  const canSubmit = email.length > 0 && password.length >= MIN_PASSWORD && emailError === null && passwordError === null && !submitting;
213
242
  const submit = useCallback2(async () => {
214
- if (!canSubmit) return;
243
+ if (!canSubmit) return false;
215
244
  setSubmitting(true);
216
245
  setError(null);
217
246
  try {
218
247
  await auth.login({ email, password });
248
+ return true;
219
249
  } catch (err) {
220
250
  setError(mapSdkError(err));
251
+ return false;
221
252
  } finally {
222
253
  setSubmitting(false);
223
254
  }
@@ -237,20 +268,20 @@ function useLoginForm() {
237
268
  }
238
269
 
239
270
  // src/defaults/DefaultLoginScreen.tsx
240
- import { jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
271
+ import { jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
241
272
  function DefaultLoginScreen({ onNavigate }) {
242
273
  const { name } = useTemplateConfig();
243
274
  const f = useLoginForm();
244
275
  return /* @__PURE__ */ jsxs2("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
245
- /* @__PURE__ */ jsx7("h1", { style: { marginBottom: 8 }, children: name }),
246
- /* @__PURE__ */ jsx7("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Entre na sua conta" }),
276
+ /* @__PURE__ */ jsx8("h1", { style: { marginBottom: 8 }, children: name }),
277
+ /* @__PURE__ */ jsx8("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Entre na sua conta" }),
247
278
  /* @__PURE__ */ jsxs2("form", { onSubmit: (e) => {
248
279
  e.preventDefault();
249
280
  void f.submit();
250
281
  }, children: [
251
282
  /* @__PURE__ */ jsxs2("label", { style: { display: "block", marginBottom: 12 }, children: [
252
283
  "E-mail",
253
- /* @__PURE__ */ jsx7(
284
+ /* @__PURE__ */ jsx8(
254
285
  "input",
255
286
  {
256
287
  "data-testid": "login-email",
@@ -260,11 +291,11 @@ function DefaultLoginScreen({ onNavigate }) {
260
291
  style: { display: "block", width: "100%" }
261
292
  }
262
293
  ),
263
- f.emailError && /* @__PURE__ */ jsx7("small", { style: { color: "#c00" }, children: f.emailError })
294
+ f.emailError && /* @__PURE__ */ jsx8("small", { style: { color: "#c00" }, children: f.emailError })
264
295
  ] }),
265
296
  /* @__PURE__ */ jsxs2("label", { style: { display: "block", marginBottom: 12 }, children: [
266
297
  "Senha",
267
- /* @__PURE__ */ jsx7(
298
+ /* @__PURE__ */ jsx8(
268
299
  "input",
269
300
  {
270
301
  "data-testid": "login-password",
@@ -274,10 +305,10 @@ function DefaultLoginScreen({ onNavigate }) {
274
305
  style: { display: "block", width: "100%" }
275
306
  }
276
307
  ),
277
- f.passwordError && /* @__PURE__ */ jsx7("small", { style: { color: "#c00" }, children: f.passwordError })
308
+ f.passwordError && /* @__PURE__ */ jsx8("small", { style: { color: "#c00" }, children: f.passwordError })
278
309
  ] }),
279
- f.error && /* @__PURE__ */ jsx7("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
280
- /* @__PURE__ */ jsx7(
310
+ f.error && /* @__PURE__ */ jsx8("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
311
+ /* @__PURE__ */ jsx8(
281
312
  "button",
282
313
  {
283
314
  "data-testid": "login-submit",
@@ -297,48 +328,50 @@ function DefaultLoginScreen({ onNavigate }) {
297
328
  )
298
329
  ] }),
299
330
  /* @__PURE__ */ jsxs2("div", { style: { marginTop: 16, display: "flex", justifyContent: "space-between" }, children: [
300
- /* @__PURE__ */ jsx7("button", { "data-testid": "login-goto-signup", type: "button", onClick: () => onNavigate("signup"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Criar conta" }),
301
- /* @__PURE__ */ jsx7("button", { "data-testid": "login-goto-forgot", type: "button", onClick: () => onNavigate("forgot"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Esqueci senha" })
331
+ /* @__PURE__ */ jsx8("button", { "data-testid": "login-goto-signup", type: "button", onClick: () => onNavigate("signup"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Criar conta" }),
332
+ /* @__PURE__ */ jsx8("button", { "data-testid": "login-goto-forgot", type: "button", onClick: () => onNavigate("forgot"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Esqueci senha" })
302
333
  ] })
303
334
  ] });
304
335
  }
305
336
 
306
337
  // src/hooks/useSignupForm.ts
307
- import { useCallback as useCallback3, useMemo as useMemo2, useState as useState4 } from "react";
308
- import { useHook as useHook4 } from "@hook-sdk/sdk";
338
+ import { useCallback as useCallback3, useMemo as useMemo3, useState as useState4 } from "react";
339
+ import { useHook as useHook5 } from "@hook-sdk/sdk";
309
340
  var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
310
341
  var MIN_PASSWORD2 = 8;
311
342
  function useSignupForm() {
312
- const { auth } = useHook4();
343
+ const { auth } = useHook5();
313
344
  const [name, setName] = useState4("");
314
345
  const [email, setEmail] = useState4("");
315
346
  const [password, setPassword] = useState4("");
316
347
  const [submitting, setSubmitting] = useState4(false);
317
348
  const [error, setError] = useState4(null);
318
- const nameError = useMemo2(() => {
349
+ const nameError = useMemo3(() => {
319
350
  if (name.length === 0) return null;
320
351
  if (name.trim().length < 2) return "Nome muito curto.";
321
352
  return null;
322
353
  }, [name]);
323
- const emailError = useMemo2(() => {
354
+ const emailError = useMemo3(() => {
324
355
  if (email.length === 0) return null;
325
356
  if (!EMAIL_RE2.test(email)) return "Formato de e-mail inv\xE1lido.";
326
357
  return null;
327
358
  }, [email]);
328
- const passwordError = useMemo2(() => {
359
+ const passwordError = useMemo3(() => {
329
360
  if (password.length === 0) return null;
330
361
  if (password.length < MIN_PASSWORD2) return `M\xEDnimo de ${MIN_PASSWORD2} caracteres.`;
331
362
  return null;
332
363
  }, [password]);
333
364
  const canSubmit = name.trim().length >= 2 && email.length > 0 && password.length >= MIN_PASSWORD2 && nameError === null && emailError === null && passwordError === null && !submitting;
334
365
  const submit = useCallback3(async () => {
335
- if (!canSubmit) return;
366
+ if (!canSubmit) return false;
336
367
  setSubmitting(true);
337
368
  setError(null);
338
369
  try {
339
370
  await auth.signup({ name, email, password });
371
+ return true;
340
372
  } catch (err) {
341
373
  setError(mapSdkError(err));
374
+ return false;
342
375
  } finally {
343
376
  setSubmitting(false);
344
377
  }
@@ -361,64 +394,66 @@ function useSignupForm() {
361
394
  }
362
395
 
363
396
  // src/defaults/DefaultSignupScreen.tsx
364
- import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
397
+ import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
365
398
  function DefaultSignupScreen({ onNavigate }) {
366
399
  const { name } = useTemplateConfig();
367
400
  const f = useSignupForm();
368
401
  return /* @__PURE__ */ jsxs3("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
369
- /* @__PURE__ */ jsx8("h1", { style: { marginBottom: 8 }, children: name }),
370
- /* @__PURE__ */ jsx8("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Criar sua conta" }),
402
+ /* @__PURE__ */ jsx9("h1", { style: { marginBottom: 8 }, children: name }),
403
+ /* @__PURE__ */ jsx9("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Criar sua conta" }),
371
404
  /* @__PURE__ */ jsxs3("form", { onSubmit: (e) => {
372
405
  e.preventDefault();
373
406
  void f.submit();
374
407
  }, children: [
375
408
  /* @__PURE__ */ jsxs3("label", { style: { display: "block", marginBottom: 12 }, children: [
376
409
  "Nome",
377
- /* @__PURE__ */ jsx8("input", { "data-testid": "signup-name", value: f.name, onChange: (e) => f.setName(e.target.value), style: { display: "block", width: "100%" } }),
378
- f.nameError && /* @__PURE__ */ jsx8("small", { style: { color: "#c00" }, children: f.nameError })
410
+ /* @__PURE__ */ jsx9("input", { "data-testid": "signup-name", value: f.name, onChange: (e) => f.setName(e.target.value), style: { display: "block", width: "100%" } }),
411
+ f.nameError && /* @__PURE__ */ jsx9("small", { style: { color: "#c00" }, children: f.nameError })
379
412
  ] }),
380
413
  /* @__PURE__ */ jsxs3("label", { style: { display: "block", marginBottom: 12 }, children: [
381
414
  "E-mail",
382
- /* @__PURE__ */ jsx8("input", { "data-testid": "signup-email", type: "email", value: f.email, onChange: (e) => f.setEmail(e.target.value), style: { display: "block", width: "100%" } }),
383
- f.emailError && /* @__PURE__ */ jsx8("small", { style: { color: "#c00" }, children: f.emailError })
415
+ /* @__PURE__ */ jsx9("input", { "data-testid": "signup-email", type: "email", value: f.email, onChange: (e) => f.setEmail(e.target.value), style: { display: "block", width: "100%" } }),
416
+ f.emailError && /* @__PURE__ */ jsx9("small", { style: { color: "#c00" }, children: f.emailError })
384
417
  ] }),
385
418
  /* @__PURE__ */ jsxs3("label", { style: { display: "block", marginBottom: 12 }, children: [
386
419
  "Senha",
387
- /* @__PURE__ */ jsx8("input", { "data-testid": "signup-password", type: "password", value: f.password, onChange: (e) => f.setPassword(e.target.value), style: { display: "block", width: "100%" } }),
388
- f.passwordError && /* @__PURE__ */ jsx8("small", { style: { color: "#c00" }, children: f.passwordError })
420
+ /* @__PURE__ */ jsx9("input", { "data-testid": "signup-password", type: "password", value: f.password, onChange: (e) => f.setPassword(e.target.value), style: { display: "block", width: "100%" } }),
421
+ f.passwordError && /* @__PURE__ */ jsx9("small", { style: { color: "#c00" }, children: f.passwordError })
389
422
  ] }),
390
- f.error && /* @__PURE__ */ jsx8("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
391
- /* @__PURE__ */ jsx8("button", { "data-testid": "signup-submit", type: "submit", disabled: !f.canSubmit, style: { width: "100%", padding: 12, background: "var(--hook-color-primary)", color: "#fff", border: "none", borderRadius: 8, opacity: f.canSubmit ? 1 : 0.5 }, children: f.submitting ? "Criando..." : "Criar conta" })
423
+ f.error && /* @__PURE__ */ jsx9("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
424
+ /* @__PURE__ */ jsx9("button", { "data-testid": "signup-submit", type: "submit", disabled: !f.canSubmit, style: { width: "100%", padding: 12, background: "var(--hook-color-primary)", color: "#fff", border: "none", borderRadius: 8, opacity: f.canSubmit ? 1 : 0.5 }, children: f.submitting ? "Criando..." : "Criar conta" })
392
425
  ] }),
393
- /* @__PURE__ */ jsx8("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsx8("button", { "data-testid": "signup-goto-login", type: "button", onClick: () => onNavigate("login"), style: { background: "none", border: "none", cursor: "pointer" }, children: "J\xE1 tem conta? Entre" }) })
426
+ /* @__PURE__ */ jsx9("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsx9("button", { "data-testid": "signup-goto-login", type: "button", onClick: () => onNavigate("login"), style: { background: "none", border: "none", cursor: "pointer" }, children: "J\xE1 tem conta? Entre" }) })
394
427
  ] });
395
428
  }
396
429
 
397
430
  // src/hooks/useForgotForm.ts
398
- import { useCallback as useCallback4, useMemo as useMemo3, useState as useState5 } from "react";
399
- import { useHook as useHook5 } from "@hook-sdk/sdk";
431
+ import { useCallback as useCallback4, useMemo as useMemo4, useState as useState5 } from "react";
432
+ import { useHook as useHook6 } from "@hook-sdk/sdk";
400
433
  var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
401
434
  function useForgotForm() {
402
- const { auth } = useHook5();
435
+ const { auth } = useHook6();
403
436
  const [email, setEmail] = useState5("");
404
437
  const [submitting, setSubmitting] = useState5(false);
405
438
  const [sent, setSent] = useState5(false);
406
439
  const [error, setError] = useState5(null);
407
- const emailError = useMemo3(() => {
440
+ const emailError = useMemo4(() => {
408
441
  if (email.length === 0) return null;
409
442
  if (!EMAIL_RE3.test(email)) return "Formato de e-mail inv\xE1lido.";
410
443
  return null;
411
444
  }, [email]);
412
445
  const canSubmit = email.length > 0 && emailError === null && !submitting;
413
446
  const submit = useCallback4(async () => {
414
- if (!canSubmit) return;
447
+ if (!canSubmit) return false;
415
448
  setSubmitting(true);
416
449
  setError(null);
417
450
  try {
418
451
  await auth.forgot({ email });
419
452
  setSent(true);
453
+ return true;
420
454
  } catch (err) {
421
455
  setError(mapSdkError(err));
456
+ return false;
422
457
  } finally {
423
458
  setSubmitting(false);
424
459
  }
@@ -436,60 +471,60 @@ function useForgotForm() {
436
471
  }
437
472
 
438
473
  // src/defaults/DefaultForgotScreen.tsx
439
- import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
474
+ import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
440
475
  function DefaultForgotScreen({ onNavigate }) {
441
476
  const { name } = useTemplateConfig();
442
477
  const f = useForgotForm();
443
478
  if (f.sent) {
444
479
  return /* @__PURE__ */ jsxs4("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
445
- /* @__PURE__ */ jsx9("h1", { children: "Verifique seu e-mail" }),
446
- /* @__PURE__ */ jsx9("p", { style: { opacity: 0.7 }, children: "Enviamos um link pra redefinir sua senha." }),
447
- /* @__PURE__ */ jsx9("button", { "data-testid": "forgot-back-login", type: "button", onClick: () => onNavigate("login"), children: "Voltar pro login" })
480
+ /* @__PURE__ */ jsx10("h1", { children: "Verifique seu e-mail" }),
481
+ /* @__PURE__ */ jsx10("p", { style: { opacity: 0.7 }, children: "Enviamos um link pra redefinir sua senha." }),
482
+ /* @__PURE__ */ jsx10("button", { "data-testid": "forgot-back-login", type: "button", onClick: () => onNavigate("login"), children: "Voltar pro login" })
448
483
  ] });
449
484
  }
450
485
  return /* @__PURE__ */ jsxs4("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
451
- /* @__PURE__ */ jsx9("h1", { style: { marginBottom: 8 }, children: name }),
452
- /* @__PURE__ */ jsx9("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Redefinir senha" }),
486
+ /* @__PURE__ */ jsx10("h1", { style: { marginBottom: 8 }, children: name }),
487
+ /* @__PURE__ */ jsx10("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Redefinir senha" }),
453
488
  /* @__PURE__ */ jsxs4("form", { onSubmit: (e) => {
454
489
  e.preventDefault();
455
490
  void f.submit();
456
491
  }, children: [
457
492
  /* @__PURE__ */ jsxs4("label", { style: { display: "block", marginBottom: 12 }, children: [
458
493
  "E-mail",
459
- /* @__PURE__ */ jsx9("input", { "data-testid": "forgot-email", type: "email", value: f.email, onChange: (e) => f.setEmail(e.target.value), style: { display: "block", width: "100%" } }),
460
- f.emailError && /* @__PURE__ */ jsx9("small", { style: { color: "#c00" }, children: f.emailError })
494
+ /* @__PURE__ */ jsx10("input", { "data-testid": "forgot-email", type: "email", value: f.email, onChange: (e) => f.setEmail(e.target.value), style: { display: "block", width: "100%" } }),
495
+ f.emailError && /* @__PURE__ */ jsx10("small", { style: { color: "#c00" }, children: f.emailError })
461
496
  ] }),
462
- f.error && /* @__PURE__ */ jsx9("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
463
- /* @__PURE__ */ jsx9("button", { "data-testid": "forgot-submit", type: "submit", disabled: !f.canSubmit, style: { width: "100%", padding: 12, background: "var(--hook-color-primary)", color: "#fff", border: "none", borderRadius: 8, opacity: f.canSubmit ? 1 : 0.5 }, children: f.submitting ? "Enviando..." : "Enviar link" })
497
+ f.error && /* @__PURE__ */ jsx10("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
498
+ /* @__PURE__ */ jsx10("button", { "data-testid": "forgot-submit", type: "submit", disabled: !f.canSubmit, style: { width: "100%", padding: 12, background: "var(--hook-color-primary)", color: "#fff", border: "none", borderRadius: 8, opacity: f.canSubmit ? 1 : 0.5 }, children: f.submitting ? "Enviando..." : "Enviar link" })
464
499
  ] }),
465
- /* @__PURE__ */ jsx9("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsx9("button", { "data-testid": "forgot-goto-login", type: "button", onClick: () => onNavigate("login"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Voltar pro login" }) })
500
+ /* @__PURE__ */ jsx10("div", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsx10("button", { "data-testid": "forgot-goto-login", type: "button", onClick: () => onNavigate("login"), style: { background: "none", border: "none", cursor: "pointer" }, children: "Voltar pro login" }) })
466
501
  ] });
467
502
  }
468
503
 
469
504
  // src/hooks/useResetForm.ts
470
- import { useCallback as useCallback5, useEffect as useEffect2, useMemo as useMemo4, useState as useState6 } from "react";
471
- import { useHook as useHook6 } from "@hook-sdk/sdk";
505
+ import { useCallback as useCallback5, useEffect as useEffect3, useMemo as useMemo5, useState as useState6 } from "react";
506
+ import { useHook as useHook7 } from "@hook-sdk/sdk";
472
507
  var MIN_PASSWORD3 = 12;
473
508
  function useResetForm() {
474
- const { auth } = useHook6();
509
+ const { auth } = useHook7();
475
510
  const [token, setToken] = useState6(null);
476
511
  const [password, setPassword] = useState6("");
477
512
  const [confirm, setConfirm] = useState6("");
478
513
  const [submitting, setSubmitting] = useState6(false);
479
514
  const [done, setDone] = useState6(false);
480
515
  const [error, setError] = useState6(null);
481
- useEffect2(() => {
516
+ useEffect3(() => {
482
517
  if (typeof window === "undefined") return;
483
518
  const params = new URLSearchParams(window.location.search);
484
519
  const t = params.get("token");
485
520
  setToken(t && t.length > 0 ? t : null);
486
521
  }, []);
487
- const passwordError = useMemo4(() => {
522
+ const passwordError = useMemo5(() => {
488
523
  if (password.length === 0) return null;
489
524
  if (password.length < MIN_PASSWORD3) return `M\xEDnimo de ${MIN_PASSWORD3} caracteres.`;
490
525
  return null;
491
526
  }, [password]);
492
- const confirmError = useMemo4(() => {
527
+ const confirmError = useMemo5(() => {
493
528
  if (confirm.length === 0) return null;
494
529
  if (confirm !== password) return "Senhas n\xE3o coincidem.";
495
530
  return null;
@@ -531,50 +566,50 @@ function useResetForm() {
531
566
  }
532
567
 
533
568
  // src/defaults/DefaultResetScreen.tsx
534
- import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
569
+ import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
535
570
  function DefaultResetScreen({ onNavigate }) {
536
571
  const { name } = useTemplateConfig();
537
572
  const f = useResetForm();
538
573
  if (f.done) {
539
574
  return /* @__PURE__ */ jsxs5("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
540
- /* @__PURE__ */ jsx10("h1", { children: "Senha alterada" }),
541
- /* @__PURE__ */ jsx10("p", { style: { opacity: 0.7 }, children: "Agora \xE9 s\xF3 fazer login com a nova senha." }),
542
- /* @__PURE__ */ jsx10("button", { "data-testid": "reset-back-login", type: "button", onClick: () => onNavigate("login"), children: "Ir pro login" })
575
+ /* @__PURE__ */ jsx11("h1", { children: "Senha alterada" }),
576
+ /* @__PURE__ */ jsx11("p", { style: { opacity: 0.7 }, children: "Agora \xE9 s\xF3 fazer login com a nova senha." }),
577
+ /* @__PURE__ */ jsx11("button", { "data-testid": "reset-back-login", type: "button", onClick: () => onNavigate("login"), children: "Ir pro login" })
543
578
  ] });
544
579
  }
545
580
  if (f.token === null) {
546
581
  return /* @__PURE__ */ jsxs5("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto", textAlign: "center" }, children: [
547
- /* @__PURE__ */ jsx10("h1", { children: "Link inv\xE1lido" }),
548
- /* @__PURE__ */ jsx10("p", { style: { opacity: 0.7 }, children: "Pe\xE7a um novo link de reset." }),
549
- /* @__PURE__ */ jsx10("button", { "data-testid": "reset-goto-forgot", type: "button", onClick: () => onNavigate("forgot"), children: "Pedir novo link" })
582
+ /* @__PURE__ */ jsx11("h1", { children: "Link inv\xE1lido" }),
583
+ /* @__PURE__ */ jsx11("p", { style: { opacity: 0.7 }, children: "Pe\xE7a um novo link de reset." }),
584
+ /* @__PURE__ */ jsx11("button", { "data-testid": "reset-goto-forgot", type: "button", onClick: () => onNavigate("forgot"), children: "Pedir novo link" })
550
585
  ] });
551
586
  }
552
587
  return /* @__PURE__ */ jsxs5("main", { style: { padding: 24, maxWidth: 360, margin: "0 auto" }, children: [
553
- /* @__PURE__ */ jsx10("h1", { style: { marginBottom: 8 }, children: name }),
554
- /* @__PURE__ */ jsx10("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Escolha uma nova senha" }),
588
+ /* @__PURE__ */ jsx11("h1", { style: { marginBottom: 8 }, children: name }),
589
+ /* @__PURE__ */ jsx11("p", { style: { opacity: 0.7, marginBottom: 24 }, children: "Escolha uma nova senha" }),
555
590
  /* @__PURE__ */ jsxs5("form", { onSubmit: (e) => {
556
591
  e.preventDefault();
557
592
  void f.submit();
558
593
  }, children: [
559
594
  /* @__PURE__ */ jsxs5("label", { style: { display: "block", marginBottom: 12 }, children: [
560
595
  "Nova senha",
561
- /* @__PURE__ */ jsx10("input", { "data-testid": "reset-password", type: "password", value: f.password, onChange: (e) => f.setPassword(e.target.value), style: { display: "block", width: "100%" }, autoComplete: "new-password" }),
562
- f.passwordError && /* @__PURE__ */ jsx10("small", { style: { color: "#c00" }, children: f.passwordError })
596
+ /* @__PURE__ */ jsx11("input", { "data-testid": "reset-password", type: "password", value: f.password, onChange: (e) => f.setPassword(e.target.value), style: { display: "block", width: "100%" }, autoComplete: "new-password" }),
597
+ f.passwordError && /* @__PURE__ */ jsx11("small", { style: { color: "#c00" }, children: f.passwordError })
563
598
  ] }),
564
599
  /* @__PURE__ */ jsxs5("label", { style: { display: "block", marginBottom: 12 }, children: [
565
600
  "Confirmar senha",
566
- /* @__PURE__ */ jsx10("input", { "data-testid": "reset-confirm", type: "password", value: f.confirm, onChange: (e) => f.setConfirm(e.target.value), style: { display: "block", width: "100%" }, autoComplete: "new-password" }),
567
- f.confirmError && /* @__PURE__ */ jsx10("small", { style: { color: "#c00" }, children: f.confirmError })
601
+ /* @__PURE__ */ jsx11("input", { "data-testid": "reset-confirm", type: "password", value: f.confirm, onChange: (e) => f.setConfirm(e.target.value), style: { display: "block", width: "100%" }, autoComplete: "new-password" }),
602
+ f.confirmError && /* @__PURE__ */ jsx11("small", { style: { color: "#c00" }, children: f.confirmError })
568
603
  ] }),
569
- f.error && /* @__PURE__ */ jsx10("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
570
- /* @__PURE__ */ jsx10("button", { "data-testid": "reset-submit", type: "submit", disabled: !f.canSubmit, style: { width: "100%", padding: 12, background: "var(--hook-color-primary)", color: "#fff", border: "none", borderRadius: 8, opacity: f.canSubmit ? 1 : 0.5 }, children: f.submitting ? "Alterando..." : "Alterar senha" })
604
+ f.error && /* @__PURE__ */ jsx11("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: f.error.message }),
605
+ /* @__PURE__ */ jsx11("button", { "data-testid": "reset-submit", type: "submit", disabled: !f.canSubmit, style: { width: "100%", padding: 12, background: "var(--hook-color-primary)", color: "#fff", border: "none", borderRadius: 8, opacity: f.canSubmit ? 1 : 0.5 }, children: f.submitting ? "Alterando..." : "Alterar senha" })
571
606
  ] })
572
607
  ] });
573
608
  }
574
609
 
575
610
  // src/defaults/DefaultPaywall.tsx
576
611
  import { useState as useState7 } from "react";
577
- import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
612
+ import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
578
613
  function DefaultPaywall() {
579
614
  const config = useTemplateConfig();
580
615
  const { checkout, opening, error } = usePaywallState();
@@ -583,15 +618,15 @@ function DefaultPaywall() {
583
618
  const cpfDigits = cpf.replace(/\D/g, "");
584
619
  const canCheckout = cpfDigits.length === 11 && !opening;
585
620
  return /* @__PURE__ */ jsxs6("main", { style: { padding: 24, maxWidth: 440, margin: "0 auto", textAlign: "center" }, children: [
586
- /* @__PURE__ */ jsx11("h1", { style: { marginBottom: 8 }, children: p.title }),
587
- p.subtitle && /* @__PURE__ */ jsx11("p", { style: { opacity: 0.7, marginBottom: 24 }, children: p.subtitle }),
588
- /* @__PURE__ */ jsx11("ul", { style: { listStyle: "none", padding: 0, textAlign: "left", marginBottom: 24 }, children: p.benefits.map((b) => /* @__PURE__ */ jsxs6("li", { style: { padding: "8px 0", display: "flex", alignItems: "center" }, children: [
589
- /* @__PURE__ */ jsx11("span", { "aria-hidden": true, style: { marginRight: 8 }, children: "\u2713" }),
590
- /* @__PURE__ */ jsx11("span", { children: b })
621
+ /* @__PURE__ */ jsx12("h1", { style: { marginBottom: 8 }, children: p.title }),
622
+ p.subtitle && /* @__PURE__ */ jsx12("p", { style: { opacity: 0.7, marginBottom: 24 }, children: p.subtitle }),
623
+ /* @__PURE__ */ jsx12("ul", { style: { listStyle: "none", padding: 0, textAlign: "left", marginBottom: 24 }, children: p.benefits.map((b) => /* @__PURE__ */ jsxs6("li", { style: { padding: "8px 0", display: "flex", alignItems: "center" }, children: [
624
+ /* @__PURE__ */ jsx12("span", { "aria-hidden": true, style: { marginRight: 8 }, children: "\u2713" }),
625
+ /* @__PURE__ */ jsx12("span", { children: b })
591
626
  ] }, b)) }),
592
627
  /* @__PURE__ */ jsxs6("div", { style: { textAlign: "left", marginBottom: 16 }, children: [
593
- /* @__PURE__ */ jsx11("label", { style: { display: "block", fontSize: 14, opacity: 0.7, marginBottom: 4 }, children: "Seu CPF (pra emiss\xE3o de recibo)" }),
594
- /* @__PURE__ */ jsx11(
628
+ /* @__PURE__ */ jsx12("label", { style: { display: "block", fontSize: 14, opacity: 0.7, marginBottom: 4 }, children: "Seu CPF (pra emiss\xE3o de recibo)" }),
629
+ /* @__PURE__ */ jsx12(
595
630
  "input",
596
631
  {
597
632
  "data-testid": "paywall-cpf",
@@ -604,8 +639,8 @@ function DefaultPaywall() {
604
639
  }
605
640
  )
606
641
  ] }),
607
- error && /* @__PURE__ */ jsx11("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: error.message }),
608
- /* @__PURE__ */ jsx11(
642
+ error && /* @__PURE__ */ jsx12("div", { role: "alert", style: { color: "#c00", marginBottom: 12 }, children: error.message }),
643
+ /* @__PURE__ */ jsx12(
609
644
  "button",
610
645
  {
611
646
  "data-testid": "paywall-cta",
@@ -626,13 +661,107 @@ function DefaultPaywall() {
626
661
  children: opening ? "Abrindo..." : p.cta
627
662
  }
628
663
  ),
629
- p.priceHint && /* @__PURE__ */ jsx11("p", { style: { opacity: 0.6, marginTop: 12 }, children: p.priceHint }),
630
- p.footerNote && /* @__PURE__ */ jsx11("p", { style: { opacity: 0.5, marginTop: 16, fontSize: 12 }, children: p.footerNote })
664
+ p.priceHint && /* @__PURE__ */ jsx12("p", { style: { opacity: 0.6, marginTop: 12 }, children: p.priceHint }),
665
+ p.footerNote && /* @__PURE__ */ jsx12("p", { style: { opacity: 0.5, marginTop: 16, fontSize: 12 }, children: p.footerNote })
631
666
  ] });
632
667
  }
633
668
 
634
669
  // src/AppRoot.tsx
635
- import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
670
+ import { Fragment as Fragment5, jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
671
+ var BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 4e4];
672
+ function PaymentReturnHandler({ children }) {
673
+ const { subscription } = useHook8();
674
+ const subRef = useRef(subscription);
675
+ subRef.current = subscription;
676
+ const runIdRef = useRef(0);
677
+ const [state, setState] = useState8("idle");
678
+ const runPoll = useCallback6(() => {
679
+ const runId = ++runIdRef.current;
680
+ setState("confirming");
681
+ let attempts = 0;
682
+ const tick = async () => {
683
+ if (runIdRef.current !== runId) return;
684
+ attempts++;
685
+ try {
686
+ await subRef.current.refresh();
687
+ } catch {
688
+ }
689
+ if (runIdRef.current !== runId) return;
690
+ const status = subRef.current.status();
691
+ if (status === "active" || status === "trialing") {
692
+ const cleanUrl = new URL(window.location.href);
693
+ cleanUrl.searchParams.delete("paymentReturn");
694
+ window.history.replaceState({}, "", cleanUrl.toString());
695
+ setState("idle");
696
+ return;
697
+ }
698
+ const delay = BACKOFF_MS[attempts - 1];
699
+ if (delay === void 0) {
700
+ setState("waiting");
701
+ return;
702
+ }
703
+ setTimeout(tick, delay);
704
+ };
705
+ void tick();
706
+ }, []);
707
+ useEffect4(() => {
708
+ if (typeof window === "undefined") return;
709
+ const url = new URL(window.location.href);
710
+ if (url.searchParams.get("paymentReturn") !== "1") return;
711
+ runPoll();
712
+ return () => {
713
+ runIdRef.current++;
714
+ };
715
+ }, [runPoll]);
716
+ if (state === "confirming") {
717
+ return /* @__PURE__ */ jsx13(
718
+ "div",
719
+ {
720
+ role: "status",
721
+ "aria-live": "polite",
722
+ style: overlayStyle,
723
+ children: "Confirmando pagamento\u2026"
724
+ }
725
+ );
726
+ }
727
+ if (state === "waiting") {
728
+ return /* @__PURE__ */ jsx13("div", { role: "status", "aria-live": "polite", style: overlayStyle, children: /* @__PURE__ */ jsxs7("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
729
+ /* @__PURE__ */ jsx13("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
730
+ /* @__PURE__ */ jsx13(
731
+ "button",
732
+ {
733
+ type: "button",
734
+ onClick: runPoll,
735
+ style: buttonStyle,
736
+ children: "Atualizar"
737
+ }
738
+ )
739
+ ] }) });
740
+ }
741
+ return /* @__PURE__ */ jsx13(Fragment5, { children });
742
+ }
743
+ var overlayStyle = {
744
+ position: "fixed",
745
+ inset: 0,
746
+ display: "flex",
747
+ alignItems: "center",
748
+ justifyContent: "center",
749
+ background: "rgba(0,0,0,0.4)",
750
+ zIndex: 9999,
751
+ color: "white",
752
+ fontSize: "1rem",
753
+ padding: 24
754
+ };
755
+ var buttonStyle = {
756
+ background: "white",
757
+ color: "black",
758
+ border: "none",
759
+ borderRadius: 8,
760
+ padding: "10px 24px",
761
+ fontSize: "1rem",
762
+ fontWeight: 600,
763
+ cursor: "pointer"
764
+ };
636
765
  function AppRoot({
637
766
  config,
638
767
  children,
@@ -642,29 +771,29 @@ function AppRoot({
642
771
  Reset = DefaultResetScreen,
643
772
  Paywall = DefaultPaywall
644
773
  }) {
645
- return /* @__PURE__ */ jsx12(TemplateConfigProvider, { config, children: /* @__PURE__ */ jsx12(ErrorBoundary, { children: /* @__PURE__ */ jsx12(ThemeProvider, { children: /* @__PURE__ */ jsx12(AuthGate, { Login, Signup, Forgot, Reset, children: /* @__PURE__ */ jsxs7(SubscriptionGate, { Paywall, children: [
774
+ return /* @__PURE__ */ jsx13(PaymentReturnHandler, { children: /* @__PURE__ */ jsx13(TemplateConfigProvider, { config, children: /* @__PURE__ */ jsx13(ErrorBoundary, { children: /* @__PURE__ */ jsx13(ThemeProvider, { children: /* @__PURE__ */ jsx13(AuthGate, { Login, Signup, Forgot, Reset, children: /* @__PURE__ */ jsx13(PersistedKeysPrefetch, { children: /* @__PURE__ */ jsxs7(SubscriptionGate, { Paywall, children: [
646
775
  children,
647
- /* @__PURE__ */ jsx12(PushPrompt, {})
648
- ] }) }) }) }) });
776
+ /* @__PURE__ */ jsx13(PushPrompt, {})
777
+ ] }) }) }) }) }) }) });
649
778
  }
650
779
 
651
780
  // src/defaults/EmptyState.tsx
652
- import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
781
+ import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
653
782
  function EmptyState({ title, description, action }) {
654
783
  return /* @__PURE__ */ jsxs8("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
655
- /* @__PURE__ */ jsx13("h2", { style: { marginBottom: 8 }, children: title }),
656
- description && /* @__PURE__ */ jsx13("p", { style: { opacity: 0.7 }, children: description }),
657
- action && /* @__PURE__ */ jsx13("div", { style: { marginTop: 16 }, children: action })
784
+ /* @__PURE__ */ jsx14("h2", { style: { marginBottom: 8 }, children: title }),
785
+ description && /* @__PURE__ */ jsx14("p", { style: { opacity: 0.7 }, children: description }),
786
+ action && /* @__PURE__ */ jsx14("div", { style: { marginTop: 16 }, children: action })
658
787
  ] });
659
788
  }
660
789
 
661
790
  // src/hooks/useAuthPrimitives.ts
662
- import { useEffect as useEffect3 } from "react";
663
- import { useHook as useHook7 } from "@hook-sdk/sdk";
791
+ import { useEffect as useEffect5 } from "react";
792
+ import { useHook as useHook9 } from "@hook-sdk/sdk";
664
793
  var warned = false;
665
794
  function useAuthPrimitives() {
666
- const { auth } = useHook7();
667
- useEffect3(() => {
795
+ const { auth } = useHook9();
796
+ useEffect5(() => {
668
797
  if (!warned && process.env.NODE_ENV !== "production") {
669
798
  warned = true;
670
799
  console.warn(
@@ -686,18 +815,18 @@ function useAuthPrimitives() {
686
815
  }
687
816
 
688
817
  // src/hooks/useSubscription.ts
689
- import { useHook as useHook8 } from "@hook-sdk/sdk";
818
+ import { useHook as useHook10 } from "@hook-sdk/sdk";
690
819
  function useSubscription() {
691
- const { subscription } = useHook8();
820
+ const { subscription } = useHook10();
692
821
  return {
693
822
  status: subscription.status()
694
823
  };
695
824
  }
696
825
 
697
826
  // src/hooks/usePush.ts
698
- import { useHook as useHook9 } from "@hook-sdk/sdk";
827
+ import { useHook as useHook11 } from "@hook-sdk/sdk";
699
828
  function usePush() {
700
- const { push } = useHook9();
829
+ const { push } = useHook11();
701
830
  return {
702
831
  status: push.status(),
703
832
  subscribe: push.subscribe,
@@ -706,17 +835,17 @@ function usePush() {
706
835
  }
707
836
 
708
837
  // src/hooks/useToast.ts
709
- import { useCallback as useCallback6, useState as useState8 } from "react";
838
+ import { useCallback as useCallback7, useState as useState9 } from "react";
710
839
  function useToast() {
711
- const [items, setItems] = useState8([]);
712
- const show = useCallback6((message, kind = "info") => {
840
+ const [items, setItems] = useState9([]);
841
+ const show = useCallback7((message, kind = "info") => {
713
842
  const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
714
843
  setItems((prev) => [...prev, { id, message, kind }]);
715
844
  setTimeout(() => {
716
845
  setItems((prev) => prev.filter((t) => t.id !== id));
717
846
  }, 4e3);
718
847
  }, []);
719
- const dismiss = useCallback6((id) => {
848
+ const dismiss = useCallback7((id) => {
720
849
  setItems((prev) => prev.filter((t) => t.id !== id));
721
850
  }, []);
722
851
  return { items, show, dismiss };