@croacroa/react-native-template 2.0.1 → 3.2.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.
Files changed (172) hide show
  1. package/.env.example +5 -0
  2. package/.eslintrc.js +8 -0
  3. package/.github/workflows/ci.yml +187 -187
  4. package/.github/workflows/eas-build.yml +55 -55
  5. package/.github/workflows/eas-update.yml +50 -50
  6. package/.github/workflows/npm-publish.yml +57 -0
  7. package/CHANGELOG.md +195 -106
  8. package/CONTRIBUTING.md +377 -377
  9. package/LICENSE +21 -0
  10. package/README.md +446 -399
  11. package/__tests__/accessibility/components.test.tsx +285 -0
  12. package/__tests__/components/Button.test.tsx +2 -4
  13. package/__tests__/components/__snapshots__/snapshots.test.tsx.snap +512 -0
  14. package/__tests__/components/snapshots.test.tsx +131 -131
  15. package/__tests__/helpers/a11y.ts +54 -0
  16. package/__tests__/hooks/useAnalytics.test.ts +100 -0
  17. package/__tests__/hooks/useAnimations.test.ts +70 -0
  18. package/__tests__/hooks/useAuth.test.tsx +71 -28
  19. package/__tests__/hooks/useMedia.test.ts +318 -0
  20. package/__tests__/hooks/usePayments.test.tsx +307 -0
  21. package/__tests__/hooks/usePermission.test.ts +230 -0
  22. package/__tests__/hooks/useWebSocket.test.ts +329 -0
  23. package/__tests__/integration/auth-api.test.tsx +224 -227
  24. package/__tests__/performance/VirtualizedList.perf.test.tsx +385 -362
  25. package/__tests__/services/api.test.ts +24 -6
  26. package/app/(auth)/home.tsx +11 -9
  27. package/app/(auth)/profile.tsx +8 -6
  28. package/app/(auth)/settings.tsx +11 -9
  29. package/app/(public)/forgot-password.tsx +25 -15
  30. package/app/(public)/login.tsx +48 -12
  31. package/app/(public)/onboarding.tsx +5 -5
  32. package/app/(public)/register.tsx +24 -15
  33. package/app/_layout.tsx +6 -3
  34. package/app.config.ts +27 -2
  35. package/assets/images/.gitkeep +7 -7
  36. package/assets/images/adaptive-icon.png +0 -0
  37. package/assets/images/favicon.png +0 -0
  38. package/assets/images/icon.png +0 -0
  39. package/assets/images/notification-icon.png +0 -0
  40. package/assets/images/splash.png +0 -0
  41. package/components/ErrorBoundary.tsx +73 -28
  42. package/components/auth/SocialLoginButtons.tsx +168 -0
  43. package/components/forms/FormInput.tsx +5 -3
  44. package/components/onboarding/OnboardingScreen.tsx +370 -370
  45. package/components/onboarding/index.ts +2 -2
  46. package/components/providers/AnalyticsProvider.tsx +67 -0
  47. package/components/providers/SuspenseBoundary.tsx +359 -357
  48. package/components/providers/index.ts +24 -21
  49. package/components/ui/AnimatedButton.tsx +1 -9
  50. package/components/ui/AnimatedList.tsx +98 -0
  51. package/components/ui/AnimatedScreen.tsx +89 -0
  52. package/components/ui/Avatar.tsx +319 -316
  53. package/components/ui/Badge.tsx +416 -416
  54. package/components/ui/BottomSheet.tsx +307 -307
  55. package/components/ui/Button.tsx +11 -3
  56. package/components/ui/Checkbox.tsx +261 -261
  57. package/components/ui/FeatureGate.tsx +57 -0
  58. package/components/ui/ForceUpdateScreen.tsx +108 -0
  59. package/components/ui/ImagePickerButton.tsx +180 -0
  60. package/components/ui/Input.stories.tsx +2 -10
  61. package/components/ui/Input.tsx +2 -10
  62. package/components/ui/OptimizedImage.tsx +369 -369
  63. package/components/ui/Paywall.tsx +253 -0
  64. package/components/ui/PermissionGate.tsx +155 -0
  65. package/components/ui/PurchaseButton.tsx +84 -0
  66. package/components/ui/Select.tsx +240 -240
  67. package/components/ui/Skeleton.tsx +3 -1
  68. package/components/ui/Toast.tsx +427 -0
  69. package/components/ui/UploadProgress.tsx +189 -0
  70. package/components/ui/VirtualizedList.tsx +288 -285
  71. package/components/ui/index.ts +28 -23
  72. package/constants/config.ts +135 -97
  73. package/docs/adr/001-state-management.md +79 -79
  74. package/docs/adr/002-styling-approach.md +130 -130
  75. package/docs/adr/003-data-fetching.md +155 -155
  76. package/docs/adr/004-auth-adapter-pattern.md +144 -144
  77. package/docs/adr/README.md +78 -78
  78. package/docs/guides/analytics-posthog.md +121 -0
  79. package/docs/guides/auth-supabase.md +162 -0
  80. package/docs/guides/feature-flags-launchdarkly.md +150 -0
  81. package/docs/guides/payments-revenuecat.md +169 -0
  82. package/docs/plans/2026-02-22-phase6-implementation.md +3222 -0
  83. package/docs/plans/2026-02-22-phase6-template-completion-design.md +196 -0
  84. package/docs/plans/2026-02-23-npm-publish-design.md +31 -0
  85. package/docs/plans/2026-02-23-phase7-polish-documentation-design.md +79 -0
  86. package/docs/plans/2026-02-23-phase8-additional-features-design.md +136 -0
  87. package/eas.json +2 -1
  88. package/hooks/index.ts +70 -27
  89. package/hooks/useAnimatedEntry.ts +204 -0
  90. package/hooks/useApi.ts +64 -4
  91. package/hooks/useAuth.tsx +7 -3
  92. package/hooks/useBiometrics.ts +295 -295
  93. package/hooks/useChannel.ts +111 -0
  94. package/hooks/useDeepLinking.ts +256 -256
  95. package/hooks/useExperiment.ts +36 -0
  96. package/hooks/useFeatureFlag.ts +59 -0
  97. package/hooks/useForceUpdate.ts +91 -0
  98. package/hooks/useImagePicker.ts +281 -0
  99. package/hooks/useInAppReview.ts +64 -0
  100. package/hooks/useMFA.ts +509 -499
  101. package/hooks/useParallax.ts +142 -0
  102. package/hooks/usePerformance.ts +434 -434
  103. package/hooks/usePermission.ts +190 -0
  104. package/hooks/usePresence.ts +129 -0
  105. package/hooks/useProducts.ts +36 -0
  106. package/hooks/usePurchase.ts +103 -0
  107. package/hooks/useRateLimit.ts +70 -0
  108. package/hooks/useSubscription.ts +49 -0
  109. package/hooks/useTrackEvent.ts +52 -0
  110. package/hooks/useTrackScreen.ts +40 -0
  111. package/hooks/useUpdates.ts +358 -358
  112. package/hooks/useUpload.ts +165 -0
  113. package/hooks/useWebSocket.ts +111 -0
  114. package/i18n/index.ts +197 -194
  115. package/i18n/locales/ar.json +170 -101
  116. package/i18n/locales/de.json +170 -101
  117. package/i18n/locales/en.json +170 -101
  118. package/i18n/locales/es.json +170 -101
  119. package/i18n/locales/fr.json +170 -101
  120. package/jest.config.js +1 -1
  121. package/maestro/README.md +113 -113
  122. package/maestro/config.yaml +35 -35
  123. package/maestro/flows/login.yaml +62 -62
  124. package/maestro/flows/mfa-login.yaml +92 -92
  125. package/maestro/flows/mfa-setup.yaml +86 -86
  126. package/maestro/flows/navigation.yaml +68 -68
  127. package/maestro/flows/offline-conflict.yaml +101 -101
  128. package/maestro/flows/offline-sync.yaml +128 -128
  129. package/maestro/flows/offline.yaml +60 -60
  130. package/maestro/flows/register.yaml +94 -94
  131. package/package.json +188 -175
  132. package/scripts/generate-placeholders.js +38 -0
  133. package/services/analytics/adapters/console.ts +50 -0
  134. package/services/analytics/analytics-adapter.ts +94 -0
  135. package/services/analytics/types.ts +73 -0
  136. package/services/analytics.ts +428 -428
  137. package/services/api.ts +419 -340
  138. package/services/auth/social/apple.ts +110 -0
  139. package/services/auth/social/google.ts +159 -0
  140. package/services/auth/social/social-auth.ts +100 -0
  141. package/services/auth/social/types.ts +80 -0
  142. package/services/authAdapter.ts +333 -333
  143. package/services/backgroundSync.ts +652 -626
  144. package/services/feature-flags/adapters/mock.ts +108 -0
  145. package/services/feature-flags/feature-flag-adapter.ts +174 -0
  146. package/services/feature-flags/types.ts +79 -0
  147. package/services/force-update.ts +140 -0
  148. package/services/index.ts +116 -54
  149. package/services/media/compression.ts +91 -0
  150. package/services/media/media-picker.ts +151 -0
  151. package/services/media/media-upload.ts +160 -0
  152. package/services/payments/adapters/mock.ts +159 -0
  153. package/services/payments/payment-adapter.ts +118 -0
  154. package/services/payments/types.ts +131 -0
  155. package/services/permissions/permission-manager.ts +284 -0
  156. package/services/permissions/types.ts +104 -0
  157. package/services/realtime/types.ts +100 -0
  158. package/services/realtime/websocket-manager.ts +441 -0
  159. package/services/security.ts +289 -286
  160. package/services/sentry.ts +4 -4
  161. package/stores/appStore.ts +9 -0
  162. package/stores/notificationStore.ts +3 -1
  163. package/tailwind.config.js +47 -47
  164. package/tsconfig.json +37 -13
  165. package/types/user.ts +1 -1
  166. package/utils/accessibility.ts +446 -446
  167. package/utils/animations/presets.ts +182 -0
  168. package/utils/animations/transitions.ts +62 -0
  169. package/utils/index.ts +63 -52
  170. package/utils/toast.ts +9 -2
  171. package/utils/validation.ts +4 -1
  172. package/utils/withAccessibility.tsx +272 -272
@@ -0,0 +1,110 @@
1
+ /**
2
+ * @fileoverview Apple Sign-In adapter using expo-apple-authentication
3
+ * Provides native Apple Sign-In on iOS devices.
4
+ * @module services/auth/social/apple
5
+ */
6
+
7
+ import { Platform } from "react-native";
8
+ import * as AppleAuthentication from "expo-apple-authentication";
9
+
10
+ import type { SocialAuthResult } from "./types";
11
+
12
+ // ============================================================================
13
+ // Availability Check
14
+ // ============================================================================
15
+
16
+ /**
17
+ * Checks whether Apple Sign-In is available on the current device.
18
+ * Apple Sign-In is only available on iOS 13+.
19
+ *
20
+ * @returns true if the device supports Apple Sign-In
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * if (isAppleSignInAvailable()) {
25
+ * // Show Apple Sign-In button
26
+ * }
27
+ * ```
28
+ */
29
+ export function isAppleSignInAvailable(): boolean {
30
+ return Platform.OS === "ios";
31
+ }
32
+
33
+ // ============================================================================
34
+ // Apple Sign-In
35
+ // ============================================================================
36
+
37
+ /**
38
+ * Initiates the native Apple Sign-In flow.
39
+ *
40
+ * Requests the user's full name and email on first sign-in.
41
+ * Note that Apple only provides the user's name on the FIRST sign-in;
42
+ * subsequent sign-ins will not include the name.
43
+ *
44
+ * @returns SocialAuthResult on success, null if the user cancelled
45
+ * @throws Error if Apple Sign-In is not available or fails unexpectedly
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * if (isAppleSignInAvailable()) {
50
+ * const result = await signInWithApple();
51
+ * if (result) {
52
+ * console.log('Signed in as:', result.user.email);
53
+ * }
54
+ * }
55
+ * ```
56
+ */
57
+ export async function signInWithApple(): Promise<SocialAuthResult | null> {
58
+ if (!isAppleSignInAvailable()) {
59
+ throw new Error("Apple Sign-In is only available on iOS devices");
60
+ }
61
+
62
+ try {
63
+ const credential = await AppleAuthentication.signInAsync({
64
+ requestedScopes: [
65
+ AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
66
+ AppleAuthentication.AppleAuthenticationScope.EMAIL,
67
+ ],
68
+ });
69
+
70
+ const { identityToken, user, fullName, email } = credential;
71
+
72
+ if (!identityToken) {
73
+ throw new Error("Apple Sign-In did not return an identity token");
74
+ }
75
+
76
+ // Build display name from fullName components
77
+ // Apple only provides the name on the first sign-in
78
+ const nameParts: string[] = [];
79
+ if (fullName?.givenName) {
80
+ nameParts.push(fullName.givenName);
81
+ }
82
+ if (fullName?.familyName) {
83
+ nameParts.push(fullName.familyName);
84
+ }
85
+ const displayName = nameParts.length > 0 ? nameParts.join(" ") : "";
86
+
87
+ return {
88
+ provider: "apple",
89
+ idToken: identityToken,
90
+ user: {
91
+ id: user,
92
+ email: email ?? "",
93
+ name: displayName,
94
+ avatar: undefined,
95
+ },
96
+ };
97
+ } catch (error: unknown) {
98
+ // Handle user cancellation gracefully
99
+ if (
100
+ error &&
101
+ typeof error === "object" &&
102
+ "code" in error &&
103
+ (error as { code: string }).code === "ERR_REQUEST_CANCELED"
104
+ ) {
105
+ return null;
106
+ }
107
+
108
+ throw error;
109
+ }
110
+ }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * @fileoverview Google Sign-In adapter using expo-auth-session
3
+ * Implements OAuth 2.0 with PKCE for secure Google authentication.
4
+ * @module services/auth/social/google
5
+ */
6
+
7
+ import * as WebBrowser from "expo-web-browser";
8
+ import {
9
+ makeRedirectUri,
10
+ AuthRequest,
11
+ exchangeCodeAsync,
12
+ type AuthSessionResult,
13
+ } from "expo-auth-session";
14
+ import { Platform } from "react-native";
15
+
16
+ import type { SocialAuthResult, GoogleSignInOptions } from "./types";
17
+
18
+ // Required for web-based auth session completion
19
+ WebBrowser.maybeCompleteAuthSession();
20
+
21
+ // ============================================================================
22
+ // Google OAuth Discovery Document
23
+ // ============================================================================
24
+
25
+ /**
26
+ * Google OAuth 2.0 endpoint configuration.
27
+ * @see https://accounts.google.com/.well-known/openid-configuration
28
+ */
29
+ const GOOGLE_DISCOVERY = {
30
+ authorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth",
31
+ tokenEndpoint: "https://oauth2.googleapis.com/token",
32
+ revocationEndpoint: "https://oauth2.googleapis.com/revoke",
33
+ };
34
+
35
+ // ============================================================================
36
+ // JWT Decoding
37
+ // ============================================================================
38
+
39
+ /**
40
+ * Decodes a JWT token payload without verification.
41
+ * Used to extract user information from the Google ID token.
42
+ *
43
+ * NOTE: In production, you should verify the token on your backend.
44
+ * This client-side decode is only for extracting display information.
45
+ */
46
+ function decodeJwtPayload(token: string): Record<string, unknown> {
47
+ try {
48
+ const parts = token.split(".");
49
+ if (parts.length !== 3) {
50
+ throw new Error("Invalid JWT format");
51
+ }
52
+
53
+ const payload = parts[1];
54
+ // Handle base64url encoding (replace URL-safe chars and pad)
55
+ const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
56
+ const padded = base64 + "=".repeat((4 - (base64.length % 4)) % 4);
57
+ const decoded = atob(padded);
58
+
59
+ return JSON.parse(decoded);
60
+ } catch {
61
+ throw new Error("Failed to decode JWT token");
62
+ }
63
+ }
64
+
65
+ // ============================================================================
66
+ // Google Sign-In
67
+ // ============================================================================
68
+
69
+ /**
70
+ * Initiates a Google Sign-In flow using expo-auth-session with PKCE.
71
+ *
72
+ * @param options - Google sign-in configuration options
73
+ * @returns SocialAuthResult on success, null if the user cancelled
74
+ * @throws Error if the sign-in flow fails
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * const result = await signInWithGoogle({
79
+ * clientId: 'your-client-id.apps.googleusercontent.com',
80
+ * });
81
+ *
82
+ * if (result) {
83
+ * console.log('Signed in as:', result.user.name);
84
+ * }
85
+ * ```
86
+ */
87
+ export async function signInWithGoogle(
88
+ options: GoogleSignInOptions
89
+ ): Promise<SocialAuthResult | null> {
90
+ const clientId = Platform.select({
91
+ ios: options.iosClientId ?? options.clientId,
92
+ android: options.androidClientId ?? options.clientId,
93
+ default: options.clientId,
94
+ });
95
+
96
+ if (!clientId) {
97
+ throw new Error(
98
+ "Google Sign-In requires a clientId. " +
99
+ "Call SocialAuth.configure({ google: { clientId: '...' } }) first."
100
+ );
101
+ }
102
+
103
+ const redirectUri = makeRedirectUri({
104
+ scheme: undefined, // Uses Expo's default scheme
105
+ });
106
+
107
+ // Create auth request with PKCE enabled
108
+ const authRequest = new AuthRequest({
109
+ clientId,
110
+ redirectUri,
111
+ scopes: ["openid", "profile", "email"],
112
+ usePKCE: true,
113
+ });
114
+
115
+ // Prompt the user to sign in
116
+ const authResult: AuthSessionResult =
117
+ await authRequest.promptAsync(GOOGLE_DISCOVERY);
118
+
119
+ // User cancelled or dismissed the dialog
120
+ if (authResult.type !== "success") {
121
+ return null;
122
+ }
123
+
124
+ const { code } = authResult.params;
125
+
126
+ // Exchange authorization code for tokens
127
+ const tokenResult = await exchangeCodeAsync(
128
+ {
129
+ clientId,
130
+ code,
131
+ redirectUri,
132
+ extraParams: {
133
+ code_verifier: authRequest.codeVerifier ?? "",
134
+ },
135
+ },
136
+ GOOGLE_DISCOVERY
137
+ );
138
+
139
+ const { idToken, accessToken } = tokenResult;
140
+
141
+ if (!idToken) {
142
+ throw new Error("Google Sign-In did not return an ID token");
143
+ }
144
+
145
+ // Decode user info from the ID token
146
+ const payload = decodeJwtPayload(idToken);
147
+
148
+ return {
149
+ provider: "google",
150
+ idToken,
151
+ accessToken: accessToken ?? undefined,
152
+ user: {
153
+ id: (payload.sub as string) ?? "",
154
+ email: (payload.email as string) ?? "",
155
+ name: (payload.name as string) ?? "",
156
+ avatar: (payload.picture as string) ?? undefined,
157
+ },
158
+ };
159
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * @fileoverview Social authentication orchestrator
3
+ * Provides a unified API for Google and Apple sign-in flows.
4
+ * Configure once, then call SocialAuth.signIn(provider) from anywhere.
5
+ * @module services/auth/social/social-auth
6
+ */
7
+
8
+ import { signInWithGoogle } from "./google";
9
+ import { signInWithApple } from "./apple";
10
+
11
+ import type {
12
+ SocialProvider,
13
+ SocialAuthResult,
14
+ SocialAuthConfig,
15
+ } from "./types";
16
+
17
+ // Re-export commonly used types and utilities
18
+ export { isAppleSignInAvailable } from "./apple";
19
+ export type { SocialAuthResult, SocialProvider } from "./types";
20
+
21
+ // ============================================================================
22
+ // Configuration
23
+ // ============================================================================
24
+
25
+ /**
26
+ * Internal configuration state.
27
+ * Set via SocialAuth.configure() before calling signIn().
28
+ */
29
+ let config: SocialAuthConfig = {};
30
+
31
+ // ============================================================================
32
+ // Social Auth API
33
+ // ============================================================================
34
+
35
+ /**
36
+ * Unified social authentication API.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * // Configure (typically in app startup)
41
+ * SocialAuth.configure({
42
+ * google: {
43
+ * clientId: 'your-client-id.apps.googleusercontent.com',
44
+ * iosClientId: 'your-ios-client-id.apps.googleusercontent.com',
45
+ * },
46
+ * });
47
+ *
48
+ * // Sign in with a provider
49
+ * const result = await SocialAuth.signIn('google');
50
+ * if (result) {
51
+ * // Send result.idToken to your backend for verification
52
+ * }
53
+ * ```
54
+ */
55
+ export const SocialAuth = {
56
+ /**
57
+ * Configure social auth providers.
58
+ * Call this once during app initialization (e.g., in _layout.tsx).
59
+ *
60
+ * @param newConfig - Provider configuration
61
+ */
62
+ configure(newConfig: SocialAuthConfig): void {
63
+ config = { ...config, ...newConfig };
64
+ },
65
+
66
+ /**
67
+ * Initiate sign-in with the specified social provider.
68
+ *
69
+ * @param provider - The social provider to sign in with ('google' | 'apple')
70
+ * @returns SocialAuthResult on success, null if the user cancelled
71
+ * @throws Error if the provider is not configured or sign-in fails
72
+ */
73
+ async signIn(provider: SocialProvider): Promise<SocialAuthResult | null> {
74
+ switch (provider) {
75
+ case "google": {
76
+ if (!config.google?.clientId) {
77
+ throw new Error(
78
+ "Google Sign-In is not configured. " +
79
+ "Call SocialAuth.configure({ google: { clientId: '...' } }) first."
80
+ );
81
+ }
82
+
83
+ return signInWithGoogle({
84
+ clientId: config.google.clientId,
85
+ iosClientId: config.google.iosClientId,
86
+ androidClientId: config.google.androidClientId,
87
+ });
88
+ }
89
+
90
+ case "apple": {
91
+ return signInWithApple();
92
+ }
93
+
94
+ default: {
95
+ const _exhaustive: never = provider;
96
+ throw new Error(`Unknown social provider: ${_exhaustive}`);
97
+ }
98
+ }
99
+ },
100
+ };
@@ -0,0 +1,80 @@
1
+ /**
2
+ * @fileoverview Social authentication type definitions
3
+ * Provides shared types for Google and Apple sign-in adapters.
4
+ * @module services/auth/social/types
5
+ */
6
+
7
+ /**
8
+ * Supported social authentication providers.
9
+ */
10
+ export type SocialProvider = "google" | "apple";
11
+
12
+ /**
13
+ * User information returned from a social sign-in flow.
14
+ */
15
+ export interface SocialUser {
16
+ /** Unique user ID from the provider */
17
+ id: string;
18
+ /** User's email address */
19
+ email: string;
20
+ /** User's display name */
21
+ name: string;
22
+ /** URL to the user's avatar/profile picture */
23
+ avatar?: string;
24
+ }
25
+
26
+ /**
27
+ * Result of a successful social authentication.
28
+ * Contains the provider identifier, tokens, and user info.
29
+ */
30
+ export interface SocialAuthResult {
31
+ /** The social provider used for authentication */
32
+ provider: SocialProvider;
33
+ /** JWT ID token from the provider */
34
+ idToken: string;
35
+ /** OAuth access token (not always available, e.g., Apple) */
36
+ accessToken?: string;
37
+ /** Authenticated user information */
38
+ user: SocialUser;
39
+ }
40
+
41
+ /**
42
+ * Configuration for Google Sign-In.
43
+ */
44
+ export interface GoogleAuthConfig {
45
+ /** Web client ID (required for expo-auth-session) */
46
+ clientId: string;
47
+ /** iOS-specific client ID (optional, falls back to clientId) */
48
+ iosClientId?: string;
49
+ /** Android-specific client ID (optional, falls back to clientId) */
50
+ androidClientId?: string;
51
+ }
52
+
53
+ /**
54
+ * Configuration for Apple Sign-In.
55
+ * Apple Sign-In requires no additional configuration beyond platform support.
56
+ */
57
+ export type AppleAuthConfig = Record<string, never>;
58
+
59
+ /**
60
+ * Combined social auth configuration.
61
+ * Pass to SocialAuth.configure() to set up providers.
62
+ */
63
+ export interface SocialAuthConfig {
64
+ /** Google Sign-In configuration */
65
+ google?: GoogleAuthConfig;
66
+ /** Apple Sign-In configuration */
67
+ apple?: AppleAuthConfig;
68
+ }
69
+
70
+ /**
71
+ * Options passed to the Google sign-in function.
72
+ */
73
+ export interface GoogleSignInOptions {
74
+ /** Web client ID (overrides configured value) */
75
+ clientId?: string;
76
+ /** iOS-specific client ID (overrides configured value) */
77
+ iosClientId?: string;
78
+ /** Android-specific client ID (overrides configured value) */
79
+ androidClientId?: string;
80
+ }