@hook-sdk/template 0.2.0 → 0.4.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
@@ -23,6 +23,7 @@ declare const AppConfigSchema: z.ZodObject<{
23
23
  leaderboard: "leaderboard";
24
24
  push: "push";
25
25
  subscription: "subscription";
26
+ install_prompt: "install_prompt";
26
27
  }>>;
27
28
  persistedKeys: z.ZodOptional<z.ZodArray<z.ZodString>>;
28
29
  dependencies_allowlist: z.ZodArray<z.ZodString>;
@@ -105,6 +106,18 @@ interface PushPromptProps {
105
106
  }
106
107
  declare function PushPrompt({ texts, onSubscribed, onDeclined, onInstallRequested, className }: PushPromptProps): react_jsx_runtime.JSX.Element | null;
107
108
 
109
+ interface InstallGateProps {
110
+ children: ReactNode;
111
+ }
112
+ declare function InstallGate({ children }: InstallGateProps): react_jsx_runtime.JSX.Element;
113
+
114
+ interface InstallSplashProps {
115
+ children: ReactNode;
116
+ title: string;
117
+ subtitle?: string;
118
+ }
119
+ declare function InstallSplash({ children, title, subtitle }: InstallSplashProps): react_jsx_runtime.JSX.Element;
120
+
108
121
  declare function DefaultSignupScreen({ onNavigate }: AuthScreenProps): react_jsx_runtime.JSX.Element;
109
122
 
110
123
  declare function DefaultForgotScreen({ onNavigate }: AuthScreenProps): react_jsx_runtime.JSX.Element;
@@ -168,6 +181,12 @@ interface UseLoginFormResult {
168
181
  submitting: boolean;
169
182
  canSubmit: boolean;
170
183
  error: AuthFormError | null;
184
+ /**
185
+ * Dispara OAuth Google: redireciona pro backend `/api/auth/oauth/google/start`.
186
+ * Fire-and-forget — a página é descarregada antes de qualquer retorno.
187
+ * Volta pro app logado (ou com `?oauth_error=...` em falha).
188
+ */
189
+ loginWithGoogle: () => void;
171
190
  }
172
191
  declare function useLoginForm(): UseLoginFormResult;
173
192
 
@@ -190,6 +209,13 @@ interface UseSignupFormResult {
190
209
  submitting: boolean;
191
210
  canSubmit: boolean;
192
211
  error: AuthFormError | null;
212
+ /**
213
+ * Dispara OAuth Google: mesmo endpoint que `useLoginForm.loginWithGoogle`.
214
+ * Backend decide entre `signup_new`, `auto_linked`, ou `login_existing`
215
+ * via `linkPolicy` — do ponto de vista do usuário "criar conta com Google"
216
+ * e "entrar com Google" são o mesmo botão.
217
+ */
218
+ loginWithGoogle: () => void;
193
219
  }
194
220
  declare function useSignupForm(): UseSignupFormResult;
195
221
 
@@ -380,4 +406,91 @@ declare function useToast(): {
380
406
  dismiss: (id: string) => void;
381
407
  };
382
408
 
383
- export { AppRoot, type AppRootProps, type AuthFormError, type AuthFormErrorCode, type AuthScreen, type AuthScreenProps, DefaultForgotScreen, DefaultLoginScreen, DefaultPaywall, DefaultResetScreen, DefaultSignupScreen, EmptyState, ErrorBoundary, LoadingState, PushPrompt, type PushPromptProps, type PushPromptTexts, type PushUiState, type SubscriptionStatus, type ToastItem, type UseLoginFormResult, type UseResetFormResult, useAuth, useAuthPrimitives, useForgotForm, useLoginForm, usePaywallState, usePush, useReminders, useResetForm, useSignupForm, useSubscription, useToast };
409
+ /**
410
+ * useInstallPrompt — headless hook pra PWA install prompt.
411
+ *
412
+ * Responsabilidades:
413
+ * - Detectar plataforma (Android / iOS Safari / iOS outros / in-app / desktop)
414
+ * - Detectar browser específico (Chrome, Firefox, Samsung, Instagram, ...)
415
+ * - Detectar standalone mode (PWA já instalado)
416
+ * - Gerenciar dismissal graduada (session → permanente 14d)
417
+ * - Expor `promptInstall()` que dispara `window.__pwaInstallPrompt.prompt()`
418
+ *
419
+ * Não renderiza UI. Componente `<InstallGate>` consome o hook e renderiza.
420
+ *
421
+ * Porta regexes + lógica de `hook-old/src/hooks/usePWAInstall.ts` com
422
+ * divergências deliberadas (ver docs/adr/017 + P20 plan):
423
+ * - enum InAppApp granular pra copy por rede social (G60 amendment)
424
+ * - dismissal graduada em vez de permanente direto
425
+ * - TTL de 14d no permanent dismiss
426
+ * - tracking `skipCount` separado
427
+ *
428
+ * Gotchas de referência:
429
+ * - G61: beforeinstallprompt capturado pré-React (shell main.tsx faz)
430
+ * - G62: distinção ios-safari vs ios-other (Chrome iOS não instala)
431
+ * - G63: in-app browsers checados ANTES de Android/iOS
432
+ */
433
+ type Platform = 'android' | 'ios-safari' | 'ios-other' | 'in-app' | 'desktop' | 'unknown';
434
+ type IOSBrowser = 'safari' | 'chrome' | 'firefox' | 'edge' | 'other';
435
+ type AndroidBrowser = 'chrome' | 'firefox' | 'opera' | 'samsung' | 'edge' | 'other';
436
+ type InAppApp = 'instagram' | 'facebook' | 'tiktok' | 'whatsapp' | 'twitter' | 'linkedin' | 'telegram' | 'line' | 'snapchat' | 'pinterest' | 'wechat' | 'other';
437
+ type InstallVariant = 'android-native' | 'android-manual' | 'ios-safari' | 'ios-other' | 'in-app' | 'desktop' | 'none';
438
+ interface BeforeInstallPromptEvent extends Event {
439
+ prompt: () => Promise<void>;
440
+ userChoice: Promise<{
441
+ outcome: 'accepted' | 'dismissed';
442
+ platform: string;
443
+ }>;
444
+ }
445
+ declare global {
446
+ interface Window {
447
+ __pwaInstallPrompt?: BeforeInstallPromptEvent | null;
448
+ posthog?: {
449
+ capture?: (event: string, props?: Record<string, unknown>) => void;
450
+ };
451
+ }
452
+ }
453
+ interface InstallState {
454
+ platform: Platform;
455
+ iosBrowser: IOSBrowser | null;
456
+ androidBrowser: AndroidBrowser | null;
457
+ inAppApp: InAppApp | null;
458
+ isInstallable: boolean;
459
+ isInstalled: boolean;
460
+ isDismissedSession: boolean;
461
+ isDismissedPermanent: boolean;
462
+ skipCount: number;
463
+ variant: InstallVariant;
464
+ }
465
+ interface InstallActions {
466
+ promptInstall: () => Promise<boolean>;
467
+ dismissSession: () => void;
468
+ dismissPermanent: () => void;
469
+ redirectToSafari: () => void;
470
+ copyLink: () => Promise<void>;
471
+ reset: () => void;
472
+ }
473
+ declare function detectPlatform(ua: string): Platform;
474
+ declare function detectIOSBrowser(ua: string): IOSBrowser | null;
475
+ declare function detectAndroidBrowser(ua: string): AndroidBrowser | null;
476
+ declare function detectInAppApp(ua: string): InAppApp | null;
477
+ declare function detectStandalone(): {
478
+ installed: boolean;
479
+ source: 'match_media' | 'navigator_standalone' | 'storage_marker' | null;
480
+ };
481
+ /**
482
+ * Hook principal. Passar o slug do app (usado pra namespacing de storage +
483
+ * analytics). Componentes do template pegam via useTemplateConfig().slug.
484
+ */
485
+ declare function useInstallPrompt(slug: string): InstallState & InstallActions;
486
+ /**
487
+ * Exposto pra InstallGate decidir se renderiza splash (não faz parte do hook
488
+ * retornado pra manter surface pequena — componente usa).
489
+ */
490
+ declare function shouldBlockInstall(state: InstallState, now?: number): boolean;
491
+ /**
492
+ * Exposto pra decidir se mostra "Não me pergunte mais".
493
+ */
494
+ declare function shouldShowPermanentOption(state: InstallState): boolean;
495
+
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 };
package/dist/index.d.ts CHANGED
@@ -23,6 +23,7 @@ declare const AppConfigSchema: z.ZodObject<{
23
23
  leaderboard: "leaderboard";
24
24
  push: "push";
25
25
  subscription: "subscription";
26
+ install_prompt: "install_prompt";
26
27
  }>>;
27
28
  persistedKeys: z.ZodOptional<z.ZodArray<z.ZodString>>;
28
29
  dependencies_allowlist: z.ZodArray<z.ZodString>;
@@ -105,6 +106,18 @@ interface PushPromptProps {
105
106
  }
106
107
  declare function PushPrompt({ texts, onSubscribed, onDeclined, onInstallRequested, className }: PushPromptProps): react_jsx_runtime.JSX.Element | null;
107
108
 
109
+ interface InstallGateProps {
110
+ children: ReactNode;
111
+ }
112
+ declare function InstallGate({ children }: InstallGateProps): react_jsx_runtime.JSX.Element;
113
+
114
+ interface InstallSplashProps {
115
+ children: ReactNode;
116
+ title: string;
117
+ subtitle?: string;
118
+ }
119
+ declare function InstallSplash({ children, title, subtitle }: InstallSplashProps): react_jsx_runtime.JSX.Element;
120
+
108
121
  declare function DefaultSignupScreen({ onNavigate }: AuthScreenProps): react_jsx_runtime.JSX.Element;
109
122
 
110
123
  declare function DefaultForgotScreen({ onNavigate }: AuthScreenProps): react_jsx_runtime.JSX.Element;
@@ -168,6 +181,12 @@ interface UseLoginFormResult {
168
181
  submitting: boolean;
169
182
  canSubmit: boolean;
170
183
  error: AuthFormError | null;
184
+ /**
185
+ * Dispara OAuth Google: redireciona pro backend `/api/auth/oauth/google/start`.
186
+ * Fire-and-forget — a página é descarregada antes de qualquer retorno.
187
+ * Volta pro app logado (ou com `?oauth_error=...` em falha).
188
+ */
189
+ loginWithGoogle: () => void;
171
190
  }
172
191
  declare function useLoginForm(): UseLoginFormResult;
173
192
 
@@ -190,6 +209,13 @@ interface UseSignupFormResult {
190
209
  submitting: boolean;
191
210
  canSubmit: boolean;
192
211
  error: AuthFormError | null;
212
+ /**
213
+ * Dispara OAuth Google: mesmo endpoint que `useLoginForm.loginWithGoogle`.
214
+ * Backend decide entre `signup_new`, `auto_linked`, ou `login_existing`
215
+ * via `linkPolicy` — do ponto de vista do usuário "criar conta com Google"
216
+ * e "entrar com Google" são o mesmo botão.
217
+ */
218
+ loginWithGoogle: () => void;
193
219
  }
194
220
  declare function useSignupForm(): UseSignupFormResult;
195
221
 
@@ -380,4 +406,91 @@ declare function useToast(): {
380
406
  dismiss: (id: string) => void;
381
407
  };
382
408
 
383
- export { AppRoot, type AppRootProps, type AuthFormError, type AuthFormErrorCode, type AuthScreen, type AuthScreenProps, DefaultForgotScreen, DefaultLoginScreen, DefaultPaywall, DefaultResetScreen, DefaultSignupScreen, EmptyState, ErrorBoundary, LoadingState, PushPrompt, type PushPromptProps, type PushPromptTexts, type PushUiState, type SubscriptionStatus, type ToastItem, type UseLoginFormResult, type UseResetFormResult, useAuth, useAuthPrimitives, useForgotForm, useLoginForm, usePaywallState, usePush, useReminders, useResetForm, useSignupForm, useSubscription, useToast };
409
+ /**
410
+ * useInstallPrompt — headless hook pra PWA install prompt.
411
+ *
412
+ * Responsabilidades:
413
+ * - Detectar plataforma (Android / iOS Safari / iOS outros / in-app / desktop)
414
+ * - Detectar browser específico (Chrome, Firefox, Samsung, Instagram, ...)
415
+ * - Detectar standalone mode (PWA já instalado)
416
+ * - Gerenciar dismissal graduada (session → permanente 14d)
417
+ * - Expor `promptInstall()` que dispara `window.__pwaInstallPrompt.prompt()`
418
+ *
419
+ * Não renderiza UI. Componente `<InstallGate>` consome o hook e renderiza.
420
+ *
421
+ * Porta regexes + lógica de `hook-old/src/hooks/usePWAInstall.ts` com
422
+ * divergências deliberadas (ver docs/adr/017 + P20 plan):
423
+ * - enum InAppApp granular pra copy por rede social (G60 amendment)
424
+ * - dismissal graduada em vez de permanente direto
425
+ * - TTL de 14d no permanent dismiss
426
+ * - tracking `skipCount` separado
427
+ *
428
+ * Gotchas de referência:
429
+ * - G61: beforeinstallprompt capturado pré-React (shell main.tsx faz)
430
+ * - G62: distinção ios-safari vs ios-other (Chrome iOS não instala)
431
+ * - G63: in-app browsers checados ANTES de Android/iOS
432
+ */
433
+ type Platform = 'android' | 'ios-safari' | 'ios-other' | 'in-app' | 'desktop' | 'unknown';
434
+ type IOSBrowser = 'safari' | 'chrome' | 'firefox' | 'edge' | 'other';
435
+ type AndroidBrowser = 'chrome' | 'firefox' | 'opera' | 'samsung' | 'edge' | 'other';
436
+ type InAppApp = 'instagram' | 'facebook' | 'tiktok' | 'whatsapp' | 'twitter' | 'linkedin' | 'telegram' | 'line' | 'snapchat' | 'pinterest' | 'wechat' | 'other';
437
+ type InstallVariant = 'android-native' | 'android-manual' | 'ios-safari' | 'ios-other' | 'in-app' | 'desktop' | 'none';
438
+ interface BeforeInstallPromptEvent extends Event {
439
+ prompt: () => Promise<void>;
440
+ userChoice: Promise<{
441
+ outcome: 'accepted' | 'dismissed';
442
+ platform: string;
443
+ }>;
444
+ }
445
+ declare global {
446
+ interface Window {
447
+ __pwaInstallPrompt?: BeforeInstallPromptEvent | null;
448
+ posthog?: {
449
+ capture?: (event: string, props?: Record<string, unknown>) => void;
450
+ };
451
+ }
452
+ }
453
+ interface InstallState {
454
+ platform: Platform;
455
+ iosBrowser: IOSBrowser | null;
456
+ androidBrowser: AndroidBrowser | null;
457
+ inAppApp: InAppApp | null;
458
+ isInstallable: boolean;
459
+ isInstalled: boolean;
460
+ isDismissedSession: boolean;
461
+ isDismissedPermanent: boolean;
462
+ skipCount: number;
463
+ variant: InstallVariant;
464
+ }
465
+ interface InstallActions {
466
+ promptInstall: () => Promise<boolean>;
467
+ dismissSession: () => void;
468
+ dismissPermanent: () => void;
469
+ redirectToSafari: () => void;
470
+ copyLink: () => Promise<void>;
471
+ reset: () => void;
472
+ }
473
+ declare function detectPlatform(ua: string): Platform;
474
+ declare function detectIOSBrowser(ua: string): IOSBrowser | null;
475
+ declare function detectAndroidBrowser(ua: string): AndroidBrowser | null;
476
+ declare function detectInAppApp(ua: string): InAppApp | null;
477
+ declare function detectStandalone(): {
478
+ installed: boolean;
479
+ source: 'match_media' | 'navigator_standalone' | 'storage_marker' | null;
480
+ };
481
+ /**
482
+ * Hook principal. Passar o slug do app (usado pra namespacing de storage +
483
+ * analytics). Componentes do template pegam via useTemplateConfig().slug.
484
+ */
485
+ declare function useInstallPrompt(slug: string): InstallState & InstallActions;
486
+ /**
487
+ * Exposto pra InstallGate decidir se renderiza splash (não faz parte do hook
488
+ * retornado pra manter surface pequena — componente usa).
489
+ */
490
+ declare function shouldBlockInstall(state: InstallState, now?: number): boolean;
491
+ /**
492
+ * Exposto pra decidir se mostra "Não me pergunte mais".
493
+ */
494
+ declare function shouldShowPermanentOption(state: InstallState): boolean;
495
+
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 };