@hook-sdk/template 0.28.10 → 0.29.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.d.cts CHANGED
@@ -2,7 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
3
  import { ReactNode, ComponentType, Component } from 'react';
4
4
  import * as _hook_sdk_sdk from '@hook-sdk/sdk';
5
- import { HookContextValue, SubscribeAnonymousResult, CheckoutMethod as CheckoutMethod$1, CheckoutCycle, CheckoutCardData, CheckoutHolderInfo, CheckoutResult, PlanState, ReminderSlot } from '@hook-sdk/sdk';
5
+ import { HookContextValue, VideoConfig, SubscribeAnonymousResult, CheckoutMethod as CheckoutMethod$1, CheckoutCycle, CheckoutCardData, CheckoutHolderInfo, CheckoutResult, PlanState, ReminderSlot } from '@hook-sdk/sdk';
6
6
  export { PlanInfo, PlanState, useTrackOnboardingStep } from '@hook-sdk/sdk';
7
7
  import { z } from 'zod';
8
8
 
@@ -205,6 +205,14 @@ type AppRootSlots = {
205
205
  type AppRootProps = AppRootSlots & {
206
206
  config: AppConfig;
207
207
  children?: ReactNode;
208
+ /**
209
+ * P-Video — per-videoId config map. Bundles that use <HookVideo> pass
210
+ * `videos.videos` from `import videos from './videos.json'` (file is
211
+ * synced from `hook/creators/<slug>/videos.json` by the build step).
212
+ * Omit when the app has no VSL screens — empty map is fine.
213
+ * Spec: docs/superpowers/specs/2026-05-16-hook-video-player-design.md
214
+ */
215
+ videos?: Record<string, VideoConfig>;
208
216
  /** Test-only: skip BrowserRouter (which fights jsdom's document.location). */
209
217
  testRouter?: 'memory';
210
218
  /** Test-only: initial entries for MemoryRouter. */
@@ -524,6 +532,7 @@ interface UseCheckoutFormResult {
524
532
  cpfError: string | null;
525
533
  postalCodeError: string | null;
526
534
  addressNumberError: string | null;
535
+ cardExpiryError: string | null;
527
536
  markNameTouched: () => void;
528
537
  markEmailTouched: () => void;
529
538
  markEmailConfirmTouched: () => void;
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
3
  import { ReactNode, ComponentType, Component } from 'react';
4
4
  import * as _hook_sdk_sdk from '@hook-sdk/sdk';
5
- import { HookContextValue, SubscribeAnonymousResult, CheckoutMethod as CheckoutMethod$1, CheckoutCycle, CheckoutCardData, CheckoutHolderInfo, CheckoutResult, PlanState, ReminderSlot } from '@hook-sdk/sdk';
5
+ import { HookContextValue, VideoConfig, SubscribeAnonymousResult, CheckoutMethod as CheckoutMethod$1, CheckoutCycle, CheckoutCardData, CheckoutHolderInfo, CheckoutResult, PlanState, ReminderSlot } from '@hook-sdk/sdk';
6
6
  export { PlanInfo, PlanState, useTrackOnboardingStep } from '@hook-sdk/sdk';
7
7
  import { z } from 'zod';
8
8
 
@@ -205,6 +205,14 @@ type AppRootSlots = {
205
205
  type AppRootProps = AppRootSlots & {
206
206
  config: AppConfig;
207
207
  children?: ReactNode;
208
+ /**
209
+ * P-Video — per-videoId config map. Bundles that use <HookVideo> pass
210
+ * `videos.videos` from `import videos from './videos.json'` (file is
211
+ * synced from `hook/creators/<slug>/videos.json` by the build step).
212
+ * Omit when the app has no VSL screens — empty map is fine.
213
+ * Spec: docs/superpowers/specs/2026-05-16-hook-video-player-design.md
214
+ */
215
+ videos?: Record<string, VideoConfig>;
208
216
  /** Test-only: skip BrowserRouter (which fights jsdom's document.location). */
209
217
  testRouter?: 'memory';
210
218
  /** Test-only: initial entries for MemoryRouter. */
@@ -524,6 +532,7 @@ interface UseCheckoutFormResult {
524
532
  cpfError: string | null;
525
533
  postalCodeError: string | null;
526
534
  addressNumberError: string | null;
535
+ cardExpiryError: string | null;
527
536
  markNameTouched: () => void;
528
537
  markEmailTouched: () => void;
529
538
  markEmailConfirmTouched: () => void;
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/AppRoot.tsx
2
2
  import { useMemo as useMemo3 } from "react";
3
3
  import { BrowserRouter, MemoryRouter, Navigate, Route, Routes } from "react-router-dom";
4
- import { useHook as useHook8 } from "@hook-sdk/sdk";
4
+ import { useHook as useHook8, VideoProvider } from "@hook-sdk/sdk";
5
5
 
6
6
  // src/config/AppConfigContext.tsx
7
7
  import { createContext, useContext } from "react";
@@ -2504,6 +2504,7 @@ function AppRoot(props) {
2504
2504
  const {
2505
2505
  config: rawConfig,
2506
2506
  children,
2507
+ videos,
2507
2508
  testRouter,
2508
2509
  testInitialEntries,
2509
2510
  Login,
@@ -2573,7 +2574,7 @@ function AppRoot(props) {
2573
2574
  position === "pre-auth" ? /* @__PURE__ */ jsx23(InstallGate, { position: "pre-auth", children: authGated }) : authGated,
2574
2575
  isDevToolsEnabled() && devSkipOnboarding ? /* @__PURE__ */ jsx23(DevSkipOnboardingFab, { defaults: devSkipOnboarding.defaults }) : null
2575
2576
  ] });
2576
- return /* @__PURE__ */ jsx23(ErrorBoundary, { children: /* @__PURE__ */ jsx23(AppConfigProvider, { config, children: /* @__PURE__ */ jsx23(TemplateConfigProvider, { config: legacyShim, children: /* @__PURE__ */ jsx23(ThemeProvider, { children: /* @__PURE__ */ jsx23(PersistenceRegistry, { config: config.persistedKeys, children: config.i18n ? /* @__PURE__ */ jsx23(
2577
+ return /* @__PURE__ */ jsx23(ErrorBoundary, { children: /* @__PURE__ */ jsx23(AppConfigProvider, { config, children: /* @__PURE__ */ jsx23(TemplateConfigProvider, { config: legacyShim, children: /* @__PURE__ */ jsx23(ThemeProvider, { children: /* @__PURE__ */ jsx23(PersistenceRegistry, { config: config.persistedKeys, children: /* @__PURE__ */ jsx23(VideoProvider, { videos: videos ?? {}, children: config.i18n ? /* @__PURE__ */ jsx23(
2577
2578
  I18nProvider,
2578
2579
  {
2579
2580
  defaultLocale: config.i18n.defaultLocale,
@@ -2581,7 +2582,7 @@ function AppRoot(props) {
2581
2582
  resources: config.i18n.resources,
2582
2583
  children: routedTree
2583
2584
  }
2584
- ) : routedTree }) }) }) }) });
2585
+ ) : routedTree }) }) }) }) }) });
2585
2586
  }
2586
2587
  function AuthGated({
2587
2588
  children,
@@ -2966,9 +2967,26 @@ function useCheckoutForm(hookArgs) {
2966
2967
  const cpfError = touchedCpf || formSubmitAttempted ? validateCpf : null;
2967
2968
  const postalCodeError = touchedPostalCode || formSubmitAttempted ? validatePostalCode : null;
2968
2969
  const addressNumberError = touchedAddressNumber || formSubmitAttempted ? validateAddressNumber : null;
2970
+ const cardExpiryError = useMemo4(() => {
2971
+ if (method !== "card") return null;
2972
+ const mm = card.expiryMonth.replace(/\D/g, "");
2973
+ const yy = card.expiryYear.replace(/\D/g, "");
2974
+ if (mm.length < 2 || yy.length < 2) return null;
2975
+ const monthNum = parseInt(mm, 10);
2976
+ if (monthNum < 1 || monthNum > 12) return "M\xEAs inv\xE1lido (use 01-12).";
2977
+ const yearNum = 2e3 + parseInt(yy, 10);
2978
+ const now = /* @__PURE__ */ new Date();
2979
+ const currentMonth = now.getMonth() + 1;
2980
+ const currentYear = now.getFullYear();
2981
+ if (yearNum < currentYear || yearNum === currentYear && monthNum < currentMonth) {
2982
+ return "Cart\xE3o vencido.";
2983
+ }
2984
+ if (yearNum > currentYear + 20) return "Ano muito distante.";
2985
+ return null;
2986
+ }, [method, card.expiryMonth, card.expiryYear]);
2969
2987
  const phoneOk = method === "pix-auto" ? phone === "" || PHONE_RE.test(phone) : PHONE_RE.test(phone);
2970
2988
  const cepGateOk = !hookArgs.collectCep || method !== "card" || postalCode.replace(/\D/g, "").length === 8 && addressNumber.trim().length > 0;
2971
- const canSubmit = name.trim().length >= 2 && EMAIL_RE.test(email) && (emailConfirm === "" || emailConfirm === email) && phoneOk && validateCpf === null && cpf.replace(/\D/g, "").length === 11 && emailStatus !== "exists" && !submitting && cepGateOk && (method !== "card" || card.number.length >= 12 && card.ccv.length >= 3 && card.expiryMonth.length >= 1 && card.expiryYear.length >= 2 && card.holderName.length >= 1);
2989
+ const canSubmit = name.trim().length >= 2 && EMAIL_RE.test(email) && (emailConfirm === "" || emailConfirm === email) && phoneOk && validateCpf === null && cpf.replace(/\D/g, "").length === 11 && emailStatus !== "exists" && !submitting && cepGateOk && (method !== "card" || card.number.length >= 12 && card.ccv.length >= 3 && card.expiryMonth.length >= 1 && card.expiryYear.length >= 2 && card.holderName.length >= 1 && cardExpiryError === null);
2972
2990
  const submit = useCallback6(async () => {
2973
2991
  setFormSubmitAttempted(true);
2974
2992
  setError(null);
@@ -3061,6 +3079,7 @@ function useCheckoutForm(hookArgs) {
3061
3079
  cpfError,
3062
3080
  postalCodeError,
3063
3081
  addressNumberError,
3082
+ cardExpiryError,
3064
3083
  markNameTouched: () => setTouchedName(true),
3065
3084
  markEmailTouched: () => setTouchedEmail(true),
3066
3085
  markEmailConfirmTouched: () => setTouchedEmailConfirm(true),
@@ -4028,11 +4047,11 @@ function CheckoutPageDefault() {
4028
4047
  value: form.email,
4029
4048
  onChange: form.setEmail,
4030
4049
  onBlur: form.markEmailTouched,
4031
- error: form.emailError,
4050
+ error: form.emailError ?? (form.emailStatus === "exists" ? "Este email j\xE1 tem conta. Faz login pra continuar." : null),
4032
4051
  valid: form.emailStatus === "available"
4033
4052
  }
4034
4053
  ),
4035
- !form.emailError && /* @__PURE__ */ jsx48(FieldHint, { children: form.emailStatus === "checking" ? "Verificando\u2026" : form.emailStatus === "available" ? "\u2713 Dispon\xEDvel" : "Voc\xEA vai usar este email para entrar no app" })
4054
+ !form.emailError && form.emailStatus !== "exists" && /* @__PURE__ */ jsx48(FieldHint, { children: form.emailStatus === "checking" ? "Verificando\u2026" : form.emailStatus === "available" ? "\u2713 Dispon\xEDvel" : "Voc\xEA vai usar este email para entrar no app" })
4036
4055
  ] }),
4037
4056
  /* @__PURE__ */ jsxs29("div", { children: [
4038
4057
  /* @__PURE__ */ jsx48(FieldLabel, { children: "Nome completo" }),
@@ -4183,7 +4202,8 @@ function CheckoutPageDefault() {
4183
4202
  autoComplete: "cc-exp",
4184
4203
  placeholder: "MM/AA",
4185
4204
  value: expiryMmAa,
4186
- onChange: (v) => setExpiryMmAa(formatExpiryMmAa(v))
4205
+ onChange: (v) => setExpiryMmAa(formatExpiryMmAa(v)),
4206
+ error: form.cardExpiryError
4187
4207
  }
4188
4208
  )
4189
4209
  ] }),