@hook-sdk/template 0.5.0 → 0.6.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,8 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, ComponentType, Component } from 'react';
3
3
  import { z } from 'zod';
4
4
  import * as _hook_sdk_sdk from '@hook-sdk/sdk';
5
- import { ReminderSlot } from '@hook-sdk/sdk';
5
+ import { PlanState, ReminderSlot } from '@hook-sdk/sdk';
6
+ export { PlanInfo, PlanState } from '@hook-sdk/sdk';
6
7
 
7
8
  declare const AppConfigSchema: z.ZodObject<{
8
9
  $schema: z.ZodOptional<z.ZodString>;
@@ -288,6 +289,66 @@ declare function usePaywallState(): {
288
289
  error: Error | null;
289
290
  };
290
291
 
292
+ /**
293
+ * Thin wrapper sobre `useHook().plan`. Existe pra apps importarem "tudo
294
+ * que precisam" via `@hook-sdk/template` sem mudar de package pra cada
295
+ * primitive, e pra manter espaço pra evoluir a API (ex: expor campos
296
+ * derivados como `monthlyEquivalent()`) sem mexer no SDK.
297
+ *
298
+ * Retorna o PlanState bruto. Use junto com helpers de `@hook-sdk/template`:
299
+ *
300
+ * const { data, initialLoadComplete } = usePlan();
301
+ * if (!initialLoadComplete) return <Skeleton />;
302
+ * if (!data) return null;
303
+ * return <span>{formatBRL(data.priceCents)}</span>;
304
+ */
305
+ declare function usePlan(): PlanState;
306
+
307
+ /**
308
+ * Helpers de formatação de preço pro Hook.
309
+ *
310
+ * Preço vive no banco em CENTAVOS (int), sempre. Apps que renderizam preço
311
+ * leem do hook `usePlan()` e passam por `formatBRL(cents)` — nunca formatam
312
+ * inline. Isso evita os dois patterns errados que já vimos em conversões
313
+ * Lovable→Hook: (a) strings hardcoded "R$ 19,90" no JSX, (b) divisões ad-hoc
314
+ * por 100 + `toFixed(2)` que ignoram locale.
315
+ *
316
+ * Ver G72 em .claude/skills/lovable-to-hook/catalog/known-gotchas.md.
317
+ */
318
+ /**
319
+ * Formata centavos como BRL no locale pt-BR. Aceita null pra conveniência
320
+ * (callers que lidam com `yearlyPriceCents: number | null`).
321
+ *
322
+ * Examples:
323
+ * formatBRL(1990) → "R$ 19,90"
324
+ * formatBRL(4999) → "R$ 49,99"
325
+ * formatBRL(23988) → "R$ 239,88"
326
+ * formatBRL(0) → "R$ 0,00"
327
+ * formatBRL(null) → ""
328
+ */
329
+ declare function formatBRL(cents: number | null | undefined): string;
330
+ /**
331
+ * Deriva "preço mensal" a partir do preço anual (em centavos). Round half-up
332
+ * pra evitar R$ 19,899... → R$ 19,89 quando o usuário esperava R$ 19,90.
333
+ *
334
+ * Não assume que o creator configurou yearlyPriceCents "redondo": se ele
335
+ * setou 23988 (= R$ 239,88), 23988/12 = 1999 (= R$ 19,99). Se setou 23880
336
+ * (= R$ 238,80), 23880/12 = 1990 (= R$ 19,90).
337
+ *
338
+ * Example (papomaterno):
339
+ * monthlyFromYearly(23988) = 1999 → formatBRL = "R$ 19,99"
340
+ * monthlyFromYearly(23880) = 1990 → formatBRL = "R$ 19,90"
341
+ */
342
+ declare function monthlyFromYearly(yearlyCents: number | null | undefined): number;
343
+ /**
344
+ * Deriva "preço por dia" (365 dias) a partir do anual, em centavos. Útil
345
+ * pra copy tipo "apenas R$ 0,66 por dia, menos que um café".
346
+ *
347
+ * Example:
348
+ * dailyFromYearly(23988) = 66 → formatBRL = "R$ 0,66"
349
+ */
350
+ declare function dailyFromYearly(yearlyCents: number | null | undefined): number;
351
+
291
352
  /**
292
353
  * Escape hatch pra screens FORA do fluxo de auth (ex: settings/trocar-senha).
293
354
  * Pra LoginScreen/SignupScreen/ForgotScreen custom, use useLoginForm/useSignupForm/useForgotForm.
@@ -493,4 +554,4 @@ declare function shouldBlockInstall(state: InstallState, now?: number): boolean;
493
554
  */
494
555
  declare function shouldShowPermanentOption(state: InstallState): boolean;
495
556
 
496
- export { type AndroidBrowser, AppRoot, type AppRootProps, type AuthFormError, type AuthFormErrorCode, type AuthScreen, type AuthScreenProps, DefaultForgotScreen, DefaultLoginScreen, DefaultPaywall, DefaultResetScreen, DefaultSignupScreen, EmptyState, ErrorBoundary, type IOSBrowser, type InAppApp, type InstallActions, InstallGate, InstallSplash, type InstallState, type InstallVariant, LoadingState, type Platform, PushPrompt, type PushPromptProps, type PushPromptTexts, type PushUiState, type SubscriptionStatus, type ToastItem, type UseLoginFormResult, type UseResetFormResult, detectAndroidBrowser, detectIOSBrowser, detectInAppApp, detectPlatform, detectStandalone, shouldBlockInstall, shouldShowPermanentOption, useAuth, useAuthPrimitives, useForgotForm, useInstallPrompt, useLoginForm, usePaywallState, usePush, useReminders, useResetForm, useSignupForm, useSubscription, useToast };
557
+ export { type AndroidBrowser, AppRoot, type AppRootProps, type AuthFormError, type AuthFormErrorCode, type AuthScreen, type AuthScreenProps, DefaultForgotScreen, DefaultLoginScreen, DefaultPaywall, DefaultResetScreen, DefaultSignupScreen, EmptyState, ErrorBoundary, type IOSBrowser, type InAppApp, type InstallActions, InstallGate, InstallSplash, type InstallState, type InstallVariant, LoadingState, type Platform, PushPrompt, type PushPromptProps, type PushPromptTexts, type PushUiState, type SubscriptionStatus, type ToastItem, type UseLoginFormResult, type UseResetFormResult, dailyFromYearly, detectAndroidBrowser, detectIOSBrowser, detectInAppApp, detectPlatform, detectStandalone, formatBRL, monthlyFromYearly, shouldBlockInstall, shouldShowPermanentOption, useAuth, useAuthPrimitives, useForgotForm, useInstallPrompt, useLoginForm, usePaywallState, usePlan, usePush, useReminders, useResetForm, useSignupForm, useSubscription, useToast };
package/dist/index.d.ts CHANGED
@@ -2,7 +2,8 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, ComponentType, Component } from 'react';
3
3
  import { z } from 'zod';
4
4
  import * as _hook_sdk_sdk from '@hook-sdk/sdk';
5
- import { ReminderSlot } from '@hook-sdk/sdk';
5
+ import { PlanState, ReminderSlot } from '@hook-sdk/sdk';
6
+ export { PlanInfo, PlanState } from '@hook-sdk/sdk';
6
7
 
7
8
  declare const AppConfigSchema: z.ZodObject<{
8
9
  $schema: z.ZodOptional<z.ZodString>;
@@ -288,6 +289,66 @@ declare function usePaywallState(): {
288
289
  error: Error | null;
289
290
  };
290
291
 
292
+ /**
293
+ * Thin wrapper sobre `useHook().plan`. Existe pra apps importarem "tudo
294
+ * que precisam" via `@hook-sdk/template` sem mudar de package pra cada
295
+ * primitive, e pra manter espaço pra evoluir a API (ex: expor campos
296
+ * derivados como `monthlyEquivalent()`) sem mexer no SDK.
297
+ *
298
+ * Retorna o PlanState bruto. Use junto com helpers de `@hook-sdk/template`:
299
+ *
300
+ * const { data, initialLoadComplete } = usePlan();
301
+ * if (!initialLoadComplete) return <Skeleton />;
302
+ * if (!data) return null;
303
+ * return <span>{formatBRL(data.priceCents)}</span>;
304
+ */
305
+ declare function usePlan(): PlanState;
306
+
307
+ /**
308
+ * Helpers de formatação de preço pro Hook.
309
+ *
310
+ * Preço vive no banco em CENTAVOS (int), sempre. Apps que renderizam preço
311
+ * leem do hook `usePlan()` e passam por `formatBRL(cents)` — nunca formatam
312
+ * inline. Isso evita os dois patterns errados que já vimos em conversões
313
+ * Lovable→Hook: (a) strings hardcoded "R$ 19,90" no JSX, (b) divisões ad-hoc
314
+ * por 100 + `toFixed(2)` que ignoram locale.
315
+ *
316
+ * Ver G72 em .claude/skills/lovable-to-hook/catalog/known-gotchas.md.
317
+ */
318
+ /**
319
+ * Formata centavos como BRL no locale pt-BR. Aceita null pra conveniência
320
+ * (callers que lidam com `yearlyPriceCents: number | null`).
321
+ *
322
+ * Examples:
323
+ * formatBRL(1990) → "R$ 19,90"
324
+ * formatBRL(4999) → "R$ 49,99"
325
+ * formatBRL(23988) → "R$ 239,88"
326
+ * formatBRL(0) → "R$ 0,00"
327
+ * formatBRL(null) → ""
328
+ */
329
+ declare function formatBRL(cents: number | null | undefined): string;
330
+ /**
331
+ * Deriva "preço mensal" a partir do preço anual (em centavos). Round half-up
332
+ * pra evitar R$ 19,899... → R$ 19,89 quando o usuário esperava R$ 19,90.
333
+ *
334
+ * Não assume que o creator configurou yearlyPriceCents "redondo": se ele
335
+ * setou 23988 (= R$ 239,88), 23988/12 = 1999 (= R$ 19,99). Se setou 23880
336
+ * (= R$ 238,80), 23880/12 = 1990 (= R$ 19,90).
337
+ *
338
+ * Example (papomaterno):
339
+ * monthlyFromYearly(23988) = 1999 → formatBRL = "R$ 19,99"
340
+ * monthlyFromYearly(23880) = 1990 → formatBRL = "R$ 19,90"
341
+ */
342
+ declare function monthlyFromYearly(yearlyCents: number | null | undefined): number;
343
+ /**
344
+ * Deriva "preço por dia" (365 dias) a partir do anual, em centavos. Útil
345
+ * pra copy tipo "apenas R$ 0,66 por dia, menos que um café".
346
+ *
347
+ * Example:
348
+ * dailyFromYearly(23988) = 66 → formatBRL = "R$ 0,66"
349
+ */
350
+ declare function dailyFromYearly(yearlyCents: number | null | undefined): number;
351
+
291
352
  /**
292
353
  * Escape hatch pra screens FORA do fluxo de auth (ex: settings/trocar-senha).
293
354
  * Pra LoginScreen/SignupScreen/ForgotScreen custom, use useLoginForm/useSignupForm/useForgotForm.
@@ -493,4 +554,4 @@ declare function shouldBlockInstall(state: InstallState, now?: number): boolean;
493
554
  */
494
555
  declare function shouldShowPermanentOption(state: InstallState): boolean;
495
556
 
496
- export { type AndroidBrowser, AppRoot, type AppRootProps, type AuthFormError, type AuthFormErrorCode, type AuthScreen, type AuthScreenProps, DefaultForgotScreen, DefaultLoginScreen, DefaultPaywall, DefaultResetScreen, DefaultSignupScreen, EmptyState, ErrorBoundary, type IOSBrowser, type InAppApp, type InstallActions, InstallGate, InstallSplash, type InstallState, type InstallVariant, LoadingState, type Platform, PushPrompt, type PushPromptProps, type PushPromptTexts, type PushUiState, type SubscriptionStatus, type ToastItem, type UseLoginFormResult, type UseResetFormResult, detectAndroidBrowser, detectIOSBrowser, detectInAppApp, detectPlatform, detectStandalone, shouldBlockInstall, shouldShowPermanentOption, useAuth, useAuthPrimitives, useForgotForm, useInstallPrompt, useLoginForm, usePaywallState, usePush, useReminders, useResetForm, useSignupForm, useSubscription, useToast };
557
+ export { type AndroidBrowser, AppRoot, type AppRootProps, type AuthFormError, type AuthFormErrorCode, type AuthScreen, type AuthScreenProps, DefaultForgotScreen, DefaultLoginScreen, DefaultPaywall, DefaultResetScreen, DefaultSignupScreen, EmptyState, ErrorBoundary, type IOSBrowser, type InAppApp, type InstallActions, InstallGate, InstallSplash, type InstallState, type InstallVariant, LoadingState, type Platform, PushPrompt, type PushPromptProps, type PushPromptTexts, type PushUiState, type SubscriptionStatus, type ToastItem, type UseLoginFormResult, type UseResetFormResult, dailyFromYearly, detectAndroidBrowser, detectIOSBrowser, detectInAppApp, detectPlatform, detectStandalone, formatBRL, monthlyFromYearly, shouldBlockInstall, shouldShowPermanentOption, useAuth, useAuthPrimitives, useForgotForm, useInstallPrompt, useLoginForm, usePaywallState, usePlan, usePush, useReminders, useResetForm, useSignupForm, useSubscription, useToast };
package/dist/index.js CHANGED
@@ -2327,12 +2327,37 @@ function EmptyState({ title, description, action }) {
2327
2327
  ] });
2328
2328
  }
2329
2329
 
2330
+ // src/hooks/usePlan.ts
2331
+ import { useHook as useHook10 } from "@hook-sdk/sdk";
2332
+ function usePlan() {
2333
+ const { plan } = useHook10();
2334
+ return plan;
2335
+ }
2336
+
2337
+ // src/utils/price.ts
2338
+ function formatBRL(cents) {
2339
+ if (cents === null || cents === void 0) return "";
2340
+ const reais = cents / 100;
2341
+ return new Intl.NumberFormat("pt-BR", {
2342
+ style: "currency",
2343
+ currency: "BRL"
2344
+ }).format(reais);
2345
+ }
2346
+ function monthlyFromYearly(yearlyCents) {
2347
+ if (yearlyCents === null || yearlyCents === void 0) return 0;
2348
+ return Math.round(yearlyCents / 12);
2349
+ }
2350
+ function dailyFromYearly(yearlyCents) {
2351
+ if (yearlyCents === null || yearlyCents === void 0) return 0;
2352
+ return Math.round(yearlyCents / 365);
2353
+ }
2354
+
2330
2355
  // src/hooks/useAuthPrimitives.ts
2331
2356
  import { useEffect as useEffect9 } from "react";
2332
- import { useHook as useHook10 } from "@hook-sdk/sdk";
2357
+ import { useHook as useHook11 } from "@hook-sdk/sdk";
2333
2358
  var warned = false;
2334
2359
  function useAuthPrimitives() {
2335
- const { auth } = useHook10();
2360
+ const { auth } = useHook11();
2336
2361
  useEffect9(() => {
2337
2362
  if (!warned && process.env.NODE_ENV !== "production") {
2338
2363
  warned = true;
@@ -2355,9 +2380,9 @@ function useAuthPrimitives() {
2355
2380
  }
2356
2381
 
2357
2382
  // src/hooks/useSubscription.ts
2358
- import { useHook as useHook11 } from "@hook-sdk/sdk";
2383
+ import { useHook as useHook12 } from "@hook-sdk/sdk";
2359
2384
  function useSubscription() {
2360
- const { subscription } = useHook11();
2385
+ const { subscription } = useHook12();
2361
2386
  return {
2362
2387
  status: subscription.status()
2363
2388
  };
@@ -2365,9 +2390,9 @@ function useSubscription() {
2365
2390
 
2366
2391
  // src/hooks/useReminders.ts
2367
2392
  import { useCallback as useCallback9, useEffect as useEffect10, useState as useState13 } from "react";
2368
- import { useHook as useHook12 } from "@hook-sdk/sdk";
2393
+ import { useHook as useHook13 } from "@hook-sdk/sdk";
2369
2394
  function useReminders() {
2370
- const { push } = useHook12();
2395
+ const { push } = useHook13();
2371
2396
  const r = push.reminders;
2372
2397
  const [reminders, setReminders] = useState13([]);
2373
2398
  const [loading, setLoading] = useState13(true);
@@ -2429,11 +2454,14 @@ export {
2429
2454
  InstallSplash,
2430
2455
  LoadingState,
2431
2456
  PushPrompt2 as PushPrompt,
2457
+ dailyFromYearly,
2432
2458
  detectAndroidBrowser,
2433
2459
  detectIOSBrowser,
2434
2460
  detectInAppApp,
2435
2461
  detectPlatform,
2436
2462
  detectStandalone,
2463
+ formatBRL,
2464
+ monthlyFromYearly,
2437
2465
  shouldBlockInstall,
2438
2466
  shouldShowPermanentOption,
2439
2467
  useAuth,
@@ -2442,6 +2470,7 @@ export {
2442
2470
  useInstallPrompt,
2443
2471
  useLoginForm,
2444
2472
  usePaywallState,
2473
+ usePlan,
2445
2474
  usePush,
2446
2475
  useReminders,
2447
2476
  useResetForm,