@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
@@ -1,155 +1,155 @@
1
- # ADR-003: Use React Query for Data Fetching
2
-
3
- ## Status
4
-
5
- Accepted
6
-
7
- ## Date
8
-
9
- 2024-01-01
10
-
11
- ## Context
12
-
13
- We need a data fetching solution that handles:
14
-
15
- 1. Caching and cache invalidation
16
- 2. Loading and error states
17
- 3. Optimistic updates
18
- 4. Offline support
19
- 5. Request deduplication
20
- 6. Background refetching
21
-
22
- Options considered:
23
-
24
- - **fetch/axios only**: Manual state management
25
- - **SWR**: Lightweight, good for web
26
- - **React Query (TanStack Query)**: Full-featured
27
- - **RTK Query**: Redux-based
28
- - **Apollo Client**: GraphQL-focused
29
-
30
- ## Decision
31
-
32
- We chose **React Query (TanStack Query)** for server state management.
33
-
34
- ## Rationale
35
-
36
- 1. **Comprehensive Feature Set**
37
- - Automatic caching and cache invalidation
38
- - Background refetching
39
- - Optimistic updates
40
- - Infinite queries for pagination
41
- - Offline support with persistence
42
-
43
- 2. **Excellent Developer Experience**
44
-
45
- ```tsx
46
- const { data, isLoading, error } = useQuery({
47
- queryKey: ["users", userId],
48
- queryFn: () => api.get(`/users/${userId}`),
49
- });
50
- ```
51
-
52
- 3. **Offline Support**: Works great with AsyncStorage persister
53
-
54
- ```tsx
55
- const persister = createAsyncStoragePersister({
56
- storage: AsyncStorage,
57
- });
58
-
59
- <PersistQueryClientProvider
60
- client={queryClient}
61
- persistOptions={{ persister }}
62
- >
63
- ```
64
-
65
- 4. **Performance**: Smart refetching, request deduplication
66
-
67
- 5. **DevTools**: React Query DevTools for debugging
68
-
69
- ## Consequences
70
-
71
- ### Positive
72
-
73
- - No need for manual cache management
74
- - Automatic loading/error states
75
- - Works offline with persisted cache
76
- - Reduces boilerplate significantly
77
- - Battle-tested and well-documented
78
-
79
- ### Negative
80
-
81
- - Learning curve for query keys and cache invalidation
82
- - Can be overkill for simple apps
83
- - Adds bundle size (~12KB gzipped)
84
-
85
- ### Mitigation
86
-
87
- - Create query key factories for consistency
88
- - Document common patterns
89
- - Create custom hooks for reusable queries
90
-
91
- ## Implementation
92
-
93
- ### Query Client Configuration
94
-
95
- ```ts
96
- // services/queryClient.ts
97
- export const queryClient = new QueryClient({
98
- defaultOptions: {
99
- queries: {
100
- staleTime: 5 * 60 * 1000, // 5 minutes
101
- gcTime: 30 * 60 * 1000, // 30 minutes
102
- retry: 3,
103
- refetchOnReconnect: true,
104
- },
105
- },
106
- });
107
- ```
108
-
109
- ### Query Keys Factory
110
-
111
- ```ts
112
- // hooks/useApi.ts
113
- export const queryKeys = {
114
- users: {
115
- all: ["users"] as const,
116
- detail: (id: string) => ["users", id] as const,
117
- me: () => ["users", "me"] as const,
118
- },
119
- posts: {
120
- all: ["posts"] as const,
121
- list: (filters: PostFilters) => ["posts", filters] as const,
122
- detail: (id: string) => ["posts", id] as const,
123
- },
124
- };
125
- ```
126
-
127
- ### Custom Hook Pattern
128
-
129
- ```ts
130
- export function useUser(userId: string) {
131
- return useQuery({
132
- queryKey: queryKeys.users.detail(userId),
133
- queryFn: () => api.get<User>(`/users/${userId}`),
134
- enabled: !!userId,
135
- });
136
- }
137
-
138
- export function useUpdateUser() {
139
- const queryClient = useQueryClient();
140
-
141
- return useMutation({
142
- mutationFn: (data: UpdateUserInput) => api.patch<User>("/users/me", data),
143
- onSuccess: () => {
144
- queryClient.invalidateQueries({
145
- queryKey: queryKeys.users.me(),
146
- });
147
- },
148
- });
149
- }
150
- ```
151
-
152
- ## References
153
-
154
- - [TanStack Query Documentation](https://tanstack.com/query)
155
- - [React Query Offline Support](https://tanstack.com/query/latest/docs/react/plugins/persistQueryClient)
1
+ # ADR-003: Use React Query for Data Fetching
2
+
3
+ ## Status
4
+
5
+ Accepted
6
+
7
+ ## Date
8
+
9
+ 2024-01-01
10
+
11
+ ## Context
12
+
13
+ We need a data fetching solution that handles:
14
+
15
+ 1. Caching and cache invalidation
16
+ 2. Loading and error states
17
+ 3. Optimistic updates
18
+ 4. Offline support
19
+ 5. Request deduplication
20
+ 6. Background refetching
21
+
22
+ Options considered:
23
+
24
+ - **fetch/axios only**: Manual state management
25
+ - **SWR**: Lightweight, good for web
26
+ - **React Query (TanStack Query)**: Full-featured
27
+ - **RTK Query**: Redux-based
28
+ - **Apollo Client**: GraphQL-focused
29
+
30
+ ## Decision
31
+
32
+ We chose **React Query (TanStack Query)** for server state management.
33
+
34
+ ## Rationale
35
+
36
+ 1. **Comprehensive Feature Set**
37
+ - Automatic caching and cache invalidation
38
+ - Background refetching
39
+ - Optimistic updates
40
+ - Infinite queries for pagination
41
+ - Offline support with persistence
42
+
43
+ 2. **Excellent Developer Experience**
44
+
45
+ ```tsx
46
+ const { data, isLoading, error } = useQuery({
47
+ queryKey: ["users", userId],
48
+ queryFn: () => api.get(`/users/${userId}`),
49
+ });
50
+ ```
51
+
52
+ 3. **Offline Support**: Works great with AsyncStorage persister
53
+
54
+ ```tsx
55
+ const persister = createAsyncStoragePersister({
56
+ storage: AsyncStorage,
57
+ });
58
+
59
+ <PersistQueryClientProvider
60
+ client={queryClient}
61
+ persistOptions={{ persister }}
62
+ >
63
+ ```
64
+
65
+ 4. **Performance**: Smart refetching, request deduplication
66
+
67
+ 5. **DevTools**: React Query DevTools for debugging
68
+
69
+ ## Consequences
70
+
71
+ ### Positive
72
+
73
+ - No need for manual cache management
74
+ - Automatic loading/error states
75
+ - Works offline with persisted cache
76
+ - Reduces boilerplate significantly
77
+ - Battle-tested and well-documented
78
+
79
+ ### Negative
80
+
81
+ - Learning curve for query keys and cache invalidation
82
+ - Can be overkill for simple apps
83
+ - Adds bundle size (~12KB gzipped)
84
+
85
+ ### Mitigation
86
+
87
+ - Create query key factories for consistency
88
+ - Document common patterns
89
+ - Create custom hooks for reusable queries
90
+
91
+ ## Implementation
92
+
93
+ ### Query Client Configuration
94
+
95
+ ```ts
96
+ // services/queryClient.ts
97
+ export const queryClient = new QueryClient({
98
+ defaultOptions: {
99
+ queries: {
100
+ staleTime: 5 * 60 * 1000, // 5 minutes
101
+ gcTime: 30 * 60 * 1000, // 30 minutes
102
+ retry: 3,
103
+ refetchOnReconnect: true,
104
+ },
105
+ },
106
+ });
107
+ ```
108
+
109
+ ### Query Keys Factory
110
+
111
+ ```ts
112
+ // hooks/useApi.ts
113
+ export const queryKeys = {
114
+ users: {
115
+ all: ["users"] as const,
116
+ detail: (id: string) => ["users", id] as const,
117
+ me: () => ["users", "me"] as const,
118
+ },
119
+ posts: {
120
+ all: ["posts"] as const,
121
+ list: (filters: PostFilters) => ["posts", filters] as const,
122
+ detail: (id: string) => ["posts", id] as const,
123
+ },
124
+ };
125
+ ```
126
+
127
+ ### Custom Hook Pattern
128
+
129
+ ```ts
130
+ export function useUser(userId: string) {
131
+ return useQuery({
132
+ queryKey: queryKeys.users.detail(userId),
133
+ queryFn: () => api.get<User>(`/users/${userId}`),
134
+ enabled: !!userId,
135
+ });
136
+ }
137
+
138
+ export function useUpdateUser() {
139
+ const queryClient = useQueryClient();
140
+
141
+ return useMutation({
142
+ mutationFn: (data: UpdateUserInput) => api.patch<User>("/users/me", data),
143
+ onSuccess: () => {
144
+ queryClient.invalidateQueries({
145
+ queryKey: queryKeys.users.me(),
146
+ });
147
+ },
148
+ });
149
+ }
150
+ ```
151
+
152
+ ## References
153
+
154
+ - [TanStack Query Documentation](https://tanstack.com/query)
155
+ - [React Query Offline Support](https://tanstack.com/query/latest/docs/react/plugins/persistQueryClient)
@@ -1,144 +1,144 @@
1
- # ADR-004: Auth Adapter Pattern for Authentication
2
-
3
- ## Status
4
-
5
- Accepted
6
-
7
- ## Date
8
-
9
- 2024-01-15
10
-
11
- ## Context
12
-
13
- The template needs to support multiple authentication providers (Supabase, Firebase, Auth0, custom backends) without requiring significant code changes. We need:
14
-
15
- 1. Easy switching between auth providers
16
- 2. Consistent API regardless of provider
17
- 3. Type safety
18
- 4. Testability with mock implementations
19
-
20
- ## Decision
21
-
22
- Implement an **Adapter Pattern** for authentication that abstracts the auth provider behind a common interface.
23
-
24
- ## Rationale
25
-
26
- 1. **Flexibility**: Change auth providers without touching app code
27
- 2. **Testing**: Easy to mock for unit tests
28
- 3. **Consistency**: Same API for all providers
29
- 4. **Gradual Migration**: Can switch providers incrementally
30
-
31
- ## Implementation
32
-
33
- ### Interface Definition
34
-
35
- ```typescript
36
- // services/authAdapter.ts
37
- export interface AuthAdapter {
38
- signIn(email: string, password: string): Promise<AuthResult>;
39
- signUp(email: string, password: string, name: string): Promise<AuthResult>;
40
- signOut(): Promise<void>;
41
- refreshToken(refreshToken: string): Promise<AuthTokens>;
42
- forgotPassword(email: string): Promise<void>;
43
- resetPassword(token: string, newPassword: string): Promise<void>;
44
- getSession(): Promise<AuthResult | null>;
45
- onAuthStateChange?(callback: (user: User | null) => void): () => void;
46
- }
47
-
48
- export interface AuthResult {
49
- user: User;
50
- tokens: AuthTokens;
51
- }
52
- ```
53
-
54
- ### Mock Implementation
55
-
56
- ```typescript
57
- export const mockAuthAdapter: AuthAdapter = {
58
- async signIn(email, password) {
59
- await delay(1000); // Simulate network
60
- return {
61
- user: { id: '1', email, name: email.split('@')[0] },
62
- tokens: { accessToken: 'mock', refreshToken: 'mock', expiresAt: ... },
63
- };
64
- },
65
- // ... other methods
66
- };
67
- ```
68
-
69
- ### Supabase Implementation
70
-
71
- ```typescript
72
- export const supabaseAuthAdapter: AuthAdapter = {
73
- async signIn(email, password) {
74
- const { data, error } = await supabase.auth.signInWithPassword({
75
- email,
76
- password,
77
- });
78
- if (error) throw error;
79
- return transformSupabaseSession(data);
80
- },
81
- // ... other methods
82
- };
83
- ```
84
-
85
- ### Usage
86
-
87
- ```typescript
88
- // services/authAdapter.ts
89
- // Change this line to switch providers
90
- export const authAdapter: AuthAdapter = mockAuthAdapter;
91
- // export const authAdapter: AuthAdapter = supabaseAuthAdapter;
92
- // export const authAdapter: AuthAdapter = firebaseAuthAdapter;
93
- ```
94
-
95
- ## Consequences
96
-
97
- ### Positive
98
-
99
- - Provider-agnostic code
100
- - Easy to test with mocks
101
- - Clear contract for auth operations
102
- - Can support multiple providers simultaneously
103
-
104
- ### Negative
105
-
106
- - Some provider-specific features may not fit the interface
107
- - Additional abstraction layer
108
- - Need to maintain multiple implementations
109
-
110
- ### Mitigation
111
-
112
- - Allow optional methods in interface
113
- - Document provider-specific extensions
114
- - Keep interface focused on common operations
115
-
116
- ## Testing
117
-
118
- ```typescript
119
- // __tests__/auth.test.ts
120
- import { mockAuthAdapter } from "@/services/authAdapter";
121
-
122
- describe("Auth", () => {
123
- it("signs in successfully", async () => {
124
- const result = await mockAuthAdapter.signIn("test@example.com", "password");
125
- expect(result.user.email).toBe("test@example.com");
126
- expect(result.tokens.accessToken).toBeDefined();
127
- });
128
- });
129
- ```
130
-
131
- ## Migration Guide
132
-
133
- To switch from mock to Supabase:
134
-
135
- 1. Install Supabase: `npx expo install @supabase/supabase-js`
136
- 2. Configure environment variables
137
- 3. Implement `supabaseAuthAdapter`
138
- 4. Update export in `authAdapter.ts`
139
-
140
- ## References
141
-
142
- - [Adapter Pattern](https://refactoring.guru/design-patterns/adapter)
143
- - [Supabase Auth](https://supabase.com/docs/guides/auth)
144
- - [Firebase Auth](https://firebase.google.com/docs/auth)
1
+ # ADR-004: Auth Adapter Pattern for Authentication
2
+
3
+ ## Status
4
+
5
+ Accepted
6
+
7
+ ## Date
8
+
9
+ 2024-01-15
10
+
11
+ ## Context
12
+
13
+ The template needs to support multiple authentication providers (Supabase, Firebase, Auth0, custom backends) without requiring significant code changes. We need:
14
+
15
+ 1. Easy switching between auth providers
16
+ 2. Consistent API regardless of provider
17
+ 3. Type safety
18
+ 4. Testability with mock implementations
19
+
20
+ ## Decision
21
+
22
+ Implement an **Adapter Pattern** for authentication that abstracts the auth provider behind a common interface.
23
+
24
+ ## Rationale
25
+
26
+ 1. **Flexibility**: Change auth providers without touching app code
27
+ 2. **Testing**: Easy to mock for unit tests
28
+ 3. **Consistency**: Same API for all providers
29
+ 4. **Gradual Migration**: Can switch providers incrementally
30
+
31
+ ## Implementation
32
+
33
+ ### Interface Definition
34
+
35
+ ```typescript
36
+ // services/authAdapter.ts
37
+ export interface AuthAdapter {
38
+ signIn(email: string, password: string): Promise<AuthResult>;
39
+ signUp(email: string, password: string, name: string): Promise<AuthResult>;
40
+ signOut(): Promise<void>;
41
+ refreshToken(refreshToken: string): Promise<AuthTokens>;
42
+ forgotPassword(email: string): Promise<void>;
43
+ resetPassword(token: string, newPassword: string): Promise<void>;
44
+ getSession(): Promise<AuthResult | null>;
45
+ onAuthStateChange?(callback: (user: User | null) => void): () => void;
46
+ }
47
+
48
+ export interface AuthResult {
49
+ user: User;
50
+ tokens: AuthTokens;
51
+ }
52
+ ```
53
+
54
+ ### Mock Implementation
55
+
56
+ ```typescript
57
+ export const mockAuthAdapter: AuthAdapter = {
58
+ async signIn(email, password) {
59
+ await delay(1000); // Simulate network
60
+ return {
61
+ user: { id: '1', email, name: email.split('@')[0] },
62
+ tokens: { accessToken: 'mock', refreshToken: 'mock', expiresAt: ... },
63
+ };
64
+ },
65
+ // ... other methods
66
+ };
67
+ ```
68
+
69
+ ### Supabase Implementation
70
+
71
+ ```typescript
72
+ export const supabaseAuthAdapter: AuthAdapter = {
73
+ async signIn(email, password) {
74
+ const { data, error } = await supabase.auth.signInWithPassword({
75
+ email,
76
+ password,
77
+ });
78
+ if (error) throw error;
79
+ return transformSupabaseSession(data);
80
+ },
81
+ // ... other methods
82
+ };
83
+ ```
84
+
85
+ ### Usage
86
+
87
+ ```typescript
88
+ // services/authAdapter.ts
89
+ // Change this line to switch providers
90
+ export const authAdapter: AuthAdapter = mockAuthAdapter;
91
+ // export const authAdapter: AuthAdapter = supabaseAuthAdapter;
92
+ // export const authAdapter: AuthAdapter = firebaseAuthAdapter;
93
+ ```
94
+
95
+ ## Consequences
96
+
97
+ ### Positive
98
+
99
+ - Provider-agnostic code
100
+ - Easy to test with mocks
101
+ - Clear contract for auth operations
102
+ - Can support multiple providers simultaneously
103
+
104
+ ### Negative
105
+
106
+ - Some provider-specific features may not fit the interface
107
+ - Additional abstraction layer
108
+ - Need to maintain multiple implementations
109
+
110
+ ### Mitigation
111
+
112
+ - Allow optional methods in interface
113
+ - Document provider-specific extensions
114
+ - Keep interface focused on common operations
115
+
116
+ ## Testing
117
+
118
+ ```typescript
119
+ // __tests__/auth.test.ts
120
+ import { mockAuthAdapter } from "@/services/authAdapter";
121
+
122
+ describe("Auth", () => {
123
+ it("signs in successfully", async () => {
124
+ const result = await mockAuthAdapter.signIn("test@example.com", "password");
125
+ expect(result.user.email).toBe("test@example.com");
126
+ expect(result.tokens.accessToken).toBeDefined();
127
+ });
128
+ });
129
+ ```
130
+
131
+ ## Migration Guide
132
+
133
+ To switch from mock to Supabase:
134
+
135
+ 1. Install Supabase: `npx expo install @supabase/supabase-js`
136
+ 2. Configure environment variables
137
+ 3. Implement `supabaseAuthAdapter`
138
+ 4. Update export in `authAdapter.ts`
139
+
140
+ ## References
141
+
142
+ - [Adapter Pattern](https://refactoring.guru/design-patterns/adapter)
143
+ - [Supabase Auth](https://supabase.com/docs/guides/auth)
144
+ - [Firebase Auth](https://firebase.google.com/docs/auth)