@onairos/react-native 3.1.15 → 3.1.17

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 (191) hide show
  1. package/README.md +404 -0
  2. package/lib/commonjs/assets/images/Checkbox.svg +3 -3
  3. package/lib/commonjs/assets/images/EnochE.svg +19 -19
  4. package/lib/commonjs/assets/images/Personalityprofile.svg +3 -3
  5. package/lib/commonjs/assets/images/Personalitytraits.svg +3 -3
  6. package/lib/commonjs/assets/images/Userpreferences.svg +3 -3
  7. package/lib/commonjs/assets/images/arrow.svg +20 -20
  8. package/lib/commonjs/assets/images/basicproficon.svg +43 -43
  9. package/lib/commonjs/assets/images/basicprofile.svg +3 -3
  10. package/lib/commonjs/assets/images/checkmark.svg +4 -4
  11. package/lib/commonjs/assets/images/contentanalysis.svg +3 -3
  12. package/lib/commonjs/assets/images/contenticon.svg +23 -23
  13. package/lib/commonjs/assets/images/personalityicon.svg +18 -18
  14. package/lib/commonjs/assets/images/x-close.svg +3 -3
  15. package/lib/commonjs/components/OnairosSignInButton.js +32 -74
  16. package/lib/commonjs/components/OnairosSignInButton.js.map +1 -1
  17. package/lib/commonjs/components/UniversalOnboarding.js +4 -4
  18. package/lib/commonjs/config/api.js +2 -2
  19. package/lib/commonjs/hooks/useConnections.js +6 -6
  20. package/lib/commonjs/hooks/useUserConnections.js +10 -10
  21. package/lib/commonjs/index.js +5 -12
  22. package/lib/commonjs/index.js.map +1 -1
  23. package/lib/commonjs/services/apiClient.js +35 -35
  24. package/lib/commonjs/services/apiKeyService.js +99 -99
  25. package/lib/commonjs/services/authService.js +82 -82
  26. package/lib/commonjs/services/biometricPinService.js +10 -10
  27. package/lib/commonjs/services/connectedAccountsService.js +32 -32
  28. package/lib/commonjs/services/googleAuthService.js +15 -15
  29. package/lib/commonjs/services/imageCompressionService.js +15 -15
  30. package/lib/commonjs/services/jwtStorageService.js +59 -59
  31. package/lib/commonjs/services/mobileTrainingService.js +14 -14
  32. package/lib/commonjs/services/pinEncryptionService.js +10 -10
  33. package/lib/commonjs/services/pinStorageUtils.js +15 -15
  34. package/lib/commonjs/services/platformAuthService.js +47 -47
  35. package/lib/commonjs/services/storageService.js +31 -31
  36. package/lib/commonjs/services/trainingApiHelpers.js +33 -33
  37. package/lib/commonjs/services/userConnectionsService.js +24 -24
  38. package/lib/commonjs/utils/Portal.js +4 -4
  39. package/lib/commonjs/utils/api.js +24 -24
  40. package/lib/commonjs/utils/auth.js +18 -18
  41. package/lib/commonjs/utils/crypto.js +13 -13
  42. package/lib/commonjs/utils/encryption.js +12 -12
  43. package/lib/commonjs/utils/eventUtils.js +52 -52
  44. package/lib/commonjs/utils/programmaticFlow.js +16 -16
  45. package/lib/commonjs/utils/retryHelper.js +27 -27
  46. package/lib/module/assets/images/Checkbox.svg +3 -3
  47. package/lib/module/assets/images/EnochE.svg +19 -19
  48. package/lib/module/assets/images/Personalityprofile.svg +3 -3
  49. package/lib/module/assets/images/Personalitytraits.svg +3 -3
  50. package/lib/module/assets/images/Userpreferences.svg +3 -3
  51. package/lib/module/assets/images/arrow.svg +20 -20
  52. package/lib/module/assets/images/basicproficon.svg +43 -43
  53. package/lib/module/assets/images/basicprofile.svg +3 -3
  54. package/lib/module/assets/images/checkmark.svg +4 -4
  55. package/lib/module/assets/images/contentanalysis.svg +3 -3
  56. package/lib/module/assets/images/contenticon.svg +23 -23
  57. package/lib/module/assets/images/personalityicon.svg +18 -18
  58. package/lib/module/assets/images/x-close.svg +3 -3
  59. package/lib/module/components/OnairosSignInButton.js +32 -74
  60. package/lib/module/components/OnairosSignInButton.js.map +1 -1
  61. package/lib/module/components/UniversalOnboarding.js +4 -4
  62. package/lib/module/config/api.js +2 -2
  63. package/lib/module/hooks/useConnections.js +6 -6
  64. package/lib/module/hooks/useUserConnections.js +10 -10
  65. package/lib/module/index.js +5 -6
  66. package/lib/module/index.js.map +1 -1
  67. package/lib/module/services/apiClient.js +35 -35
  68. package/lib/module/services/apiKeyService.js +99 -99
  69. package/lib/module/services/authService.js +82 -82
  70. package/lib/module/services/biometricPinService.js +10 -10
  71. package/lib/module/services/connectedAccountsService.js +32 -32
  72. package/lib/module/services/googleAuthService.js +15 -15
  73. package/lib/module/services/imageCompressionService.js +15 -15
  74. package/lib/module/services/jwtStorageService.js +59 -59
  75. package/lib/module/services/mobileTrainingService.js +14 -14
  76. package/lib/module/services/pinEncryptionService.js +10 -10
  77. package/lib/module/services/pinStorageUtils.js +15 -15
  78. package/lib/module/services/platformAuthService.js +47 -47
  79. package/lib/module/services/storageService.js +31 -31
  80. package/lib/module/services/trainingApiHelpers.js +33 -33
  81. package/lib/module/services/userConnectionsService.js +24 -24
  82. package/lib/module/utils/Portal.js +4 -4
  83. package/lib/module/utils/api.js +24 -24
  84. package/lib/module/utils/auth.js +18 -18
  85. package/lib/module/utils/crypto.js +13 -13
  86. package/lib/module/utils/encryption.js +12 -12
  87. package/lib/module/utils/eventUtils.js +52 -52
  88. package/lib/module/utils/programmaticFlow.js +16 -16
  89. package/lib/module/utils/retryHelper.js +27 -27
  90. package/lib/typescript/components/OnairosSignInButton.d.ts.map +1 -1
  91. package/lib/typescript/index.d.ts +0 -1
  92. package/lib/typescript/index.d.ts.map +1 -1
  93. package/package.json +163 -163
  94. package/src/api/index.ts +151 -151
  95. package/src/assets/images/Checkbox.svg +3 -3
  96. package/src/assets/images/EnochE.svg +19 -19
  97. package/src/assets/images/Personalityprofile.svg +3 -3
  98. package/src/assets/images/Personalitytraits.svg +3 -3
  99. package/src/assets/images/Userpreferences.svg +3 -3
  100. package/src/assets/images/arrow.svg +20 -20
  101. package/src/assets/images/basicproficon.svg +43 -43
  102. package/src/assets/images/basicprofile.svg +3 -3
  103. package/src/assets/images/checkmark.svg +4 -4
  104. package/src/assets/images/contentanalysis.svg +3 -3
  105. package/src/assets/images/contenticon.svg +23 -23
  106. package/src/assets/images/personalityicon.svg +18 -18
  107. package/src/assets/images/x-close.svg +3 -3
  108. package/src/components/BodyText.tsx +33 -33
  109. package/src/components/BrandMark.tsx +62 -62
  110. package/src/components/CodeInput.tsx +32 -32
  111. package/src/components/DataRequestScreen.tsx +355 -355
  112. package/src/components/EmailInput.tsx +31 -31
  113. package/src/components/EmailVerificationModal.tsx +363 -363
  114. package/src/components/ExistingUserDataConfirmation.tsx +506 -506
  115. package/src/components/GoogleButton.tsx +55 -55
  116. package/src/components/HeadingGroup.tsx +49 -49
  117. package/src/components/ModalHeader.tsx +125 -125
  118. package/src/components/ModalSheet.tsx +57 -57
  119. package/src/components/Onairos.tsx +422 -422
  120. package/src/components/OnairosButton.tsx +339 -339
  121. package/src/components/OnairosSignInButton.tsx +130 -166
  122. package/src/components/Overlay.tsx +506 -506
  123. package/src/components/PersonaImage.tsx +79 -79
  124. package/src/components/PersonaLoadingScreen.tsx +201 -201
  125. package/src/components/PersonalizationConsentScreen.tsx +410 -410
  126. package/src/components/PinCreationScreen.tsx +492 -492
  127. package/src/components/PinInput.tsx +555 -555
  128. package/src/components/PlatformConnectorsStep.tsx +891 -891
  129. package/src/components/PlatformList.tsx +144 -144
  130. package/src/components/PlatformToggle.tsx +226 -226
  131. package/src/components/PrimaryButton.tsx +213 -213
  132. package/src/components/SignInMatchAnimation.tsx +225 -225
  133. package/src/components/SignInStep.tsx +217 -217
  134. package/src/components/TrainingModal.tsx +1047 -1047
  135. package/src/components/UniversalOnboarding.tsx +2887 -2887
  136. package/src/components/VerificationStep.tsx +198 -198
  137. package/src/components/WelcomeScreen.tsx +473 -473
  138. package/src/components/icons/Basicproficon.tsx +30 -30
  139. package/src/components/icons/Basicprofile.tsx +17 -17
  140. package/src/components/icons/Checkbox.tsx +17 -17
  141. package/src/components/icons/Checkmark.tsx +24 -24
  142. package/src/components/icons/Contentanalysis.tsx +17 -17
  143. package/src/components/icons/Contenticon.tsx +30 -30
  144. package/src/components/icons/EnochE.tsx +39 -39
  145. package/src/components/icons/Personalityicon.tsx +22 -22
  146. package/src/components/icons/Personalityprofile.tsx +17 -17
  147. package/src/components/icons/Personalitytraits.tsx +17 -17
  148. package/src/components/icons/Userpreferences.tsx +17 -17
  149. package/src/components/icons/index.ts +12 -12
  150. package/src/components/onboarding/OAuthWebView.tsx +232 -232
  151. package/src/config/api.ts +25 -25
  152. package/src/context/AuthContext.tsx +393 -393
  153. package/src/hooks/useConnectedAccounts.ts +138 -138
  154. package/src/hooks/useConnections.ts +161 -161
  155. package/src/hooks/useCredentials.ts +174 -174
  156. package/src/hooks/useUserConnections.ts +165 -165
  157. package/src/index.js +14 -14
  158. package/src/index.ts +94 -95
  159. package/src/services/apiClient.ts +336 -336
  160. package/src/services/apiKeyService.ts +919 -919
  161. package/src/services/authService.ts +1008 -1008
  162. package/src/services/biometricPinService.ts +192 -192
  163. package/src/services/connectedAccountsService.ts +289 -289
  164. package/src/services/googleAuthService.ts +279 -279
  165. package/src/services/imageCompressionService.ts +302 -302
  166. package/src/services/jwtStorageService.ts +256 -256
  167. package/src/services/mobileTrainingService.ts +203 -203
  168. package/src/services/pinEncryptionService.ts +75 -75
  169. package/src/services/pinStorageUtils.ts +96 -96
  170. package/src/services/platformAuthService.ts +1346 -1346
  171. package/src/services/storageService.ts +451 -451
  172. package/src/services/trainingApiHelpers.ts +66 -66
  173. package/src/services/userConnectionsService.ts +556 -556
  174. package/src/services/youtubeMigrationService.ts +453 -453
  175. package/src/theme/index.ts +239 -239
  176. package/src/types/ambient.d.ts +28 -28
  177. package/src/types/index.ts +265 -265
  178. package/src/types/node-fix.d.ts +18 -18
  179. package/src/types/node-override.d.ts +23 -23
  180. package/src/types/opacity.d.ts +15 -15
  181. package/src/types/types.d.ts +17 -17
  182. package/src/utils/Portal.tsx +82 -82
  183. package/src/utils/api.js +111 -111
  184. package/src/utils/auth.js +103 -103
  185. package/src/utils/crypto.js +59 -59
  186. package/src/utils/encryption.ts +68 -68
  187. package/src/utils/eventUtils.ts +302 -302
  188. package/src/utils/haptics.ts +58 -58
  189. package/src/utils/imagePreloader.ts +2 -2
  190. package/src/utils/programmaticFlow.ts +112 -112
  191. package/src/utils/retryHelper.ts +274 -274
@@ -1,337 +1,337 @@
1
- import { getJWTForRoute, TokenType, getTokenTypeForRoute, clearJWT, getAllTokens } from './jwtStorageService';
2
- import { API_CONFIG } from '../config/api';
3
-
4
- /**
5
- * Centralized API Client with Route-Based JWT Token Management
6
- * CORRECTED APPROACH: Uses different JWT tokens for different route families
7
- *
8
- * TOKEN ROUTING:
9
- * - Enoch JWT: /enoch/*, /api/auth/*, /mobile-training/enoch
10
- * - Onairos JWT: /youtube/*, /gmail/*, social connections
11
- * - Auth Token: Context-specific auth flows
12
- */
13
-
14
- interface ApiRequestOptions {
15
- method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
16
- body?: any;
17
- headers?: Record<string, string>;
18
- requiresAuth?: boolean;
19
- }
20
-
21
- interface ApiResponse<T = any> {
22
- success: boolean;
23
- data?: T;
24
- error?: string;
25
- status: number;
26
- tokenType?: TokenType;
27
- }
28
-
29
- /**
30
- * Global request counter for tracking in-flight requests
31
- */
32
- let currentRequestId = 0;
33
- const activeRequests = new Map<number, AbortController>();
34
-
35
- /**
36
- * Route-specific authentication triggers
37
- */
38
- interface AuthTriggers {
39
- triggerEnochReAuth?: () => Promise<void>;
40
- triggerOnairosReAuth?: () => Promise<void>;
41
- triggerAuthTokenRefresh?: () => Promise<void>;
42
- }
43
-
44
- /**
45
- * Create API client with route-based token management
46
- */
47
- class ApiClient {
48
- private baseUrl: string;
49
- private authTriggers: AuthTriggers;
50
-
51
- constructor(baseUrl: string = API_CONFIG.BASE_URL, authTriggers: AuthTriggers = {}) {
52
- this.baseUrl = baseUrl;
53
- this.authTriggers = authTriggers;
54
- }
55
-
56
- /**
57
- * Make authenticated API request with route-based token selection
58
- * CRITICAL: Uses the correct token type based on route
59
- */
60
- async request<T = any>(
61
- endpoint: string,
62
- options: ApiRequestOptions = {}
63
- ): Promise<ApiResponse<T>> {
64
- const requestId = ++currentRequestId;
65
- const abortController = new AbortController();
66
- activeRequests.set(requestId, abortController);
67
-
68
- try {
69
- console.log(`🔗 [API Client] Starting request ${requestId} to: ${endpoint}`);
70
-
71
- // CRITICAL FIX: Determine which token type to use based on route
72
- const tokenType = getTokenTypeForRoute(endpoint);
73
- console.log(`🎯 [API Client] Route ${endpoint} requires ${tokenType} token`);
74
-
75
- // Get route-specific headers
76
- const headers = await this.getRequestHeaders(endpoint, options.requiresAuth !== false);
77
-
78
- const fetchOptions: RequestInit = {
79
- method: options.method || 'GET',
80
- headers: {
81
- 'Content-Type': 'application/json',
82
- 'User-Agent': 'OnairosSDK/1.0.0',
83
- ...headers,
84
- ...options.headers,
85
- },
86
- body: options.body ? JSON.stringify(options.body) : undefined,
87
- signal: abortController.signal,
88
- };
89
-
90
- console.log(`🔐 [API Client] Request ${requestId} using ${tokenType} token`);
91
-
92
- const response = await fetch(`${this.baseUrl}${endpoint}`, fetchOptions);
93
-
94
- console.log(`📡 [API Client] Response ${requestId} status:`, response.status);
95
-
96
- // CRITICAL FIX: Route-specific 401 handler
97
- if (response.status === 401) {
98
- return await this.handle401Error(requestId, endpoint, tokenType, options);
99
- }
100
-
101
- const result = await response.json();
102
-
103
- activeRequests.delete(requestId);
104
-
105
- if (!response.ok) {
106
- return {
107
- success: false,
108
- error: result.message || `Request failed with status ${response.status}`,
109
- status: response.status,
110
- tokenType,
111
- };
112
- }
113
-
114
- return {
115
- success: true,
116
- data: result,
117
- status: response.status,
118
- tokenType,
119
- };
120
-
121
- } catch (error) {
122
- activeRequests.delete(requestId);
123
-
124
- if (error instanceof Error && error.name === 'AbortError') {
125
- console.log(`🚫 [API Client] Request ${requestId} was cancelled`);
126
- return {
127
- success: false,
128
- error: 'Request cancelled',
129
- status: 0,
130
- };
131
- }
132
-
133
- console.error(`❌ [API Client] Request ${requestId} failed:`, error);
134
- return {
135
- success: false,
136
- error: error instanceof Error ? error.message : 'Unknown error',
137
- status: 0,
138
- };
139
- }
140
- }
141
-
142
- /**
143
- * Get request headers with route-appropriate token
144
- * CRITICAL: Selects correct token based on route
145
- */
146
- private async getRequestHeaders(endpoint: string, requiresAuth: boolean): Promise<Record<string, string>> {
147
- if (!requiresAuth) {
148
- return {};
149
- }
150
-
151
- // CRITICAL FIX: Get token specifically for this route
152
- const token = await getJWTForRoute(endpoint);
153
-
154
- if (!token) {
155
- const tokenType = getTokenTypeForRoute(endpoint);
156
- throw new Error(`No ${tokenType} token available for route ${endpoint}. Please authenticate first.`);
157
- }
158
-
159
- return {
160
- 'Authorization': token,
161
- };
162
- }
163
-
164
- /**
165
- * Handle 401 errors with route-specific re-authentication
166
- * CRITICAL FIX: Different auth flows for different token types
167
- */
168
- private async handle401Error<T>(
169
- requestId: number,
170
- endpoint: string,
171
- tokenType: TokenType,
172
- options: ApiRequestOptions
173
- ): Promise<ApiResponse<T>> {
174
- console.error(`🚫 [API Client] Request ${requestId} received 401 for ${tokenType} token on route: ${endpoint}`);
175
-
176
- // CRITICAL FIX: Clear only the specific token type that failed
177
- console.log(`🔄 [API Client] Clearing ${tokenType} token due to 401 error`);
178
- await clearJWT(tokenType);
179
-
180
- // Cancel requests using the same token type
181
- this.cancelRequestsForTokenType(tokenType, requestId);
182
-
183
- // CRITICAL FIX: Route-specific re-authentication
184
- await this.triggerReAuthForTokenType(tokenType, endpoint);
185
-
186
- console.log(`🔑 [API Client] ${tokenType} token cleared, user needs to re-authenticate`);
187
-
188
- return {
189
- success: false,
190
- error: `${tokenType} authentication expired for ${endpoint}. Please re-authenticate.`,
191
- status: 401,
192
- tokenType,
193
- };
194
- }
195
-
196
- /**
197
- * Trigger appropriate re-authentication based on token type
198
- */
199
- private async triggerReAuthForTokenType(tokenType: TokenType, endpoint: string): Promise<void> {
200
- console.log(`🔄 [API Client] Triggering re-auth for ${tokenType} token (endpoint: ${endpoint})`);
201
-
202
- try {
203
- switch (tokenType) {
204
- case TokenType.ENOCH:
205
- if (this.authTriggers.triggerEnochReAuth) {
206
- console.log('📧 [API Client] Triggering Enoch email re-verification');
207
- await this.authTriggers.triggerEnochReAuth();
208
- } else {
209
- console.warn('⚠️ [API Client] No Enoch re-auth trigger configured');
210
- }
211
- break;
212
-
213
- case TokenType.ONAIROS:
214
- if (this.authTriggers.triggerOnairosReAuth) {
215
- console.log('🔑 [API Client] Triggering Onairos re-authentication');
216
- await this.authTriggers.triggerOnairosReAuth();
217
- } else {
218
- console.warn('⚠️ [API Client] No Onairos re-auth trigger configured');
219
- }
220
- break;
221
-
222
- case TokenType.AUTH:
223
- if (this.authTriggers.triggerAuthTokenRefresh) {
224
- console.log('🔄 [API Client] Triggering auth token refresh');
225
- await this.authTriggers.triggerAuthTokenRefresh();
226
- } else {
227
- console.warn('⚠️ [API Client] No auth token refresh trigger configured');
228
- }
229
- break;
230
- }
231
- } catch (error) {
232
- console.error(`❌ [API Client] Failed to trigger re-auth for ${tokenType}:`, error);
233
- }
234
- }
235
-
236
- /**
237
- * Cancel requests using a specific token type
238
- */
239
- private cancelRequestsForTokenType(tokenType: TokenType, excludeRequestId?: number): void {
240
- let cancelledCount = 0;
241
-
242
- // Note: In a full implementation, we'd track which requests use which tokens
243
- // For now, we cancel all other requests as a safety measure
244
- for (const [requestId, controller] of activeRequests.entries()) {
245
- if (requestId !== excludeRequestId) {
246
- controller.abort();
247
- activeRequests.delete(requestId);
248
- cancelledCount++;
249
- }
250
- }
251
-
252
- if (cancelledCount > 0) {
253
- console.log(`🚫 [API Client] Cancelled ${cancelledCount} requests due to ${tokenType} token failure`);
254
- }
255
- }
256
-
257
- /**
258
- * Replace token for specific type and cancel related requests
259
- */
260
- async replaceTokenForType(tokenType: TokenType, newToken: string): Promise<boolean> {
261
- console.log(`🔄 [API Client] Replacing ${tokenType} token`);
262
-
263
- // Cancel all in-flight requests using this token type
264
- this.cancelRequestsForTokenType(tokenType);
265
-
266
- // Replace token using storage service
267
- const { replaceJWTAfterVerification } = await import('./jwtStorageService');
268
- const replaced = await replaceJWTAfterVerification(tokenType, newToken);
269
-
270
- if (replaced) {
271
- console.log(`✅ [API Client] ${tokenType} token replaced and related requests cancelled`);
272
- }
273
-
274
- return replaced;
275
- }
276
-
277
- /**
278
- * Debug: Get all current tokens
279
- */
280
- async debugTokens(): Promise<Record<TokenType, string | null>> {
281
- console.log('🔍 [API Client] Debug: Current token status');
282
- const tokens = await getAllTokens();
283
-
284
- for (const [tokenType, token] of Object.entries(tokens)) {
285
- if (token) {
286
- console.log(`🔐 ${tokenType}: ${token.substring(0, 20)}... (length: ${token.length})`);
287
- } else {
288
- console.log(`📭 ${tokenType}: null`);
289
- }
290
- }
291
-
292
- return tokens;
293
- }
294
-
295
- /**
296
- * Set authentication triggers
297
- */
298
- setAuthTriggers(triggers: AuthTriggers): void {
299
- this.authTriggers = { ...this.authTriggers, ...triggers };
300
- console.log('🔧 [API Client] Authentication triggers updated');
301
- }
302
-
303
- /**
304
- * Convenience methods for common HTTP verbs
305
- */
306
- async get<T = any>(endpoint: string, options: Omit<ApiRequestOptions, 'method'> = {}): Promise<ApiResponse<T>> {
307
- return this.request<T>(endpoint, { ...options, method: 'GET' });
308
- }
309
-
310
- async post<T = any>(endpoint: string, body?: any, options: Omit<ApiRequestOptions, 'method' | 'body'> = {}): Promise<ApiResponse<T>> {
311
- return this.request<T>(endpoint, { ...options, method: 'POST', body });
312
- }
313
-
314
- async put<T = any>(endpoint: string, body?: any, options: Omit<ApiRequestOptions, 'method' | 'body'> = {}): Promise<ApiResponse<T>> {
315
- return this.request<T>(endpoint, { ...options, method: 'PUT', body });
316
- }
317
-
318
- async delete<T = any>(endpoint: string, options: Omit<ApiRequestOptions, 'method'> = {}): Promise<ApiResponse<T>> {
319
- return this.request<T>(endpoint, { ...options, method: 'DELETE' });
320
- }
321
- }
322
-
323
- // SINGLE INSTANCE - Global API client
324
- export const apiClient = new ApiClient();
325
- export { ApiClient };
326
-
327
- // Export convenience functions
328
- export const authenticatedRequest = apiClient.request.bind(apiClient);
329
- export const apiGet = apiClient.get.bind(apiClient);
330
- export const apiPost = apiClient.post.bind(apiClient);
331
- export const apiPut = apiClient.put.bind(apiClient);
332
- export const apiDelete = apiClient.delete.bind(apiClient);
333
-
334
- // Export types for external use
335
- export type { TokenType, ApiResponse, AuthTriggers };
336
-
1
+ import { getJWTForRoute, TokenType, getTokenTypeForRoute, clearJWT, getAllTokens } from './jwtStorageService';
2
+ import { API_CONFIG } from '../config/api';
3
+
4
+ /**
5
+ * Centralized API Client with Route-Based JWT Token Management
6
+ * CORRECTED APPROACH: Uses different JWT tokens for different route families
7
+ *
8
+ * TOKEN ROUTING:
9
+ * - Enoch JWT: /enoch/*, /api/auth/*, /mobile-training/enoch
10
+ * - Onairos JWT: /youtube/*, /gmail/*, social connections
11
+ * - Auth Token: Context-specific auth flows
12
+ */
13
+
14
+ interface ApiRequestOptions {
15
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
16
+ body?: any;
17
+ headers?: Record<string, string>;
18
+ requiresAuth?: boolean;
19
+ }
20
+
21
+ interface ApiResponse<T = any> {
22
+ success: boolean;
23
+ data?: T;
24
+ error?: string;
25
+ status: number;
26
+ tokenType?: TokenType;
27
+ }
28
+
29
+ /**
30
+ * Global request counter for tracking in-flight requests
31
+ */
32
+ let currentRequestId = 0;
33
+ const activeRequests = new Map<number, AbortController>();
34
+
35
+ /**
36
+ * Route-specific authentication triggers
37
+ */
38
+ interface AuthTriggers {
39
+ triggerEnochReAuth?: () => Promise<void>;
40
+ triggerOnairosReAuth?: () => Promise<void>;
41
+ triggerAuthTokenRefresh?: () => Promise<void>;
42
+ }
43
+
44
+ /**
45
+ * Create API client with route-based token management
46
+ */
47
+ class ApiClient {
48
+ private baseUrl: string;
49
+ private authTriggers: AuthTriggers;
50
+
51
+ constructor(baseUrl: string = API_CONFIG.BASE_URL, authTriggers: AuthTriggers = {}) {
52
+ this.baseUrl = baseUrl;
53
+ this.authTriggers = authTriggers;
54
+ }
55
+
56
+ /**
57
+ * Make authenticated API request with route-based token selection
58
+ * CRITICAL: Uses the correct token type based on route
59
+ */
60
+ async request<T = any>(
61
+ endpoint: string,
62
+ options: ApiRequestOptions = {}
63
+ ): Promise<ApiResponse<T>> {
64
+ const requestId = ++currentRequestId;
65
+ const abortController = new AbortController();
66
+ activeRequests.set(requestId, abortController);
67
+
68
+ try {
69
+ console.log(`🔗 [API Client] Starting request ${requestId} to: ${endpoint}`);
70
+
71
+ // CRITICAL FIX: Determine which token type to use based on route
72
+ const tokenType = getTokenTypeForRoute(endpoint);
73
+ console.log(`🎯 [API Client] Route ${endpoint} requires ${tokenType} token`);
74
+
75
+ // Get route-specific headers
76
+ const headers = await this.getRequestHeaders(endpoint, options.requiresAuth !== false);
77
+
78
+ const fetchOptions: RequestInit = {
79
+ method: options.method || 'GET',
80
+ headers: {
81
+ 'Content-Type': 'application/json',
82
+ 'User-Agent': 'OnairosSDK/1.0.0',
83
+ ...headers,
84
+ ...options.headers,
85
+ },
86
+ body: options.body ? JSON.stringify(options.body) : undefined,
87
+ signal: abortController.signal,
88
+ };
89
+
90
+ console.log(`🔐 [API Client] Request ${requestId} using ${tokenType} token`);
91
+
92
+ const response = await fetch(`${this.baseUrl}${endpoint}`, fetchOptions);
93
+
94
+ console.log(`📡 [API Client] Response ${requestId} status:`, response.status);
95
+
96
+ // CRITICAL FIX: Route-specific 401 handler
97
+ if (response.status === 401) {
98
+ return await this.handle401Error(requestId, endpoint, tokenType, options);
99
+ }
100
+
101
+ const result = await response.json();
102
+
103
+ activeRequests.delete(requestId);
104
+
105
+ if (!response.ok) {
106
+ return {
107
+ success: false,
108
+ error: result.message || `Request failed with status ${response.status}`,
109
+ status: response.status,
110
+ tokenType,
111
+ };
112
+ }
113
+
114
+ return {
115
+ success: true,
116
+ data: result,
117
+ status: response.status,
118
+ tokenType,
119
+ };
120
+
121
+ } catch (error) {
122
+ activeRequests.delete(requestId);
123
+
124
+ if (error instanceof Error && error.name === 'AbortError') {
125
+ console.log(`🚫 [API Client] Request ${requestId} was cancelled`);
126
+ return {
127
+ success: false,
128
+ error: 'Request cancelled',
129
+ status: 0,
130
+ };
131
+ }
132
+
133
+ console.error(`❌ [API Client] Request ${requestId} failed:`, error);
134
+ return {
135
+ success: false,
136
+ error: error instanceof Error ? error.message : 'Unknown error',
137
+ status: 0,
138
+ };
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Get request headers with route-appropriate token
144
+ * CRITICAL: Selects correct token based on route
145
+ */
146
+ private async getRequestHeaders(endpoint: string, requiresAuth: boolean): Promise<Record<string, string>> {
147
+ if (!requiresAuth) {
148
+ return {};
149
+ }
150
+
151
+ // CRITICAL FIX: Get token specifically for this route
152
+ const token = await getJWTForRoute(endpoint);
153
+
154
+ if (!token) {
155
+ const tokenType = getTokenTypeForRoute(endpoint);
156
+ throw new Error(`No ${tokenType} token available for route ${endpoint}. Please authenticate first.`);
157
+ }
158
+
159
+ return {
160
+ 'Authorization': token,
161
+ };
162
+ }
163
+
164
+ /**
165
+ * Handle 401 errors with route-specific re-authentication
166
+ * CRITICAL FIX: Different auth flows for different token types
167
+ */
168
+ private async handle401Error<T>(
169
+ requestId: number,
170
+ endpoint: string,
171
+ tokenType: TokenType,
172
+ options: ApiRequestOptions
173
+ ): Promise<ApiResponse<T>> {
174
+ console.error(`🚫 [API Client] Request ${requestId} received 401 for ${tokenType} token on route: ${endpoint}`);
175
+
176
+ // CRITICAL FIX: Clear only the specific token type that failed
177
+ console.log(`🔄 [API Client] Clearing ${tokenType} token due to 401 error`);
178
+ await clearJWT(tokenType);
179
+
180
+ // Cancel requests using the same token type
181
+ this.cancelRequestsForTokenType(tokenType, requestId);
182
+
183
+ // CRITICAL FIX: Route-specific re-authentication
184
+ await this.triggerReAuthForTokenType(tokenType, endpoint);
185
+
186
+ console.log(`🔑 [API Client] ${tokenType} token cleared, user needs to re-authenticate`);
187
+
188
+ return {
189
+ success: false,
190
+ error: `${tokenType} authentication expired for ${endpoint}. Please re-authenticate.`,
191
+ status: 401,
192
+ tokenType,
193
+ };
194
+ }
195
+
196
+ /**
197
+ * Trigger appropriate re-authentication based on token type
198
+ */
199
+ private async triggerReAuthForTokenType(tokenType: TokenType, endpoint: string): Promise<void> {
200
+ console.log(`🔄 [API Client] Triggering re-auth for ${tokenType} token (endpoint: ${endpoint})`);
201
+
202
+ try {
203
+ switch (tokenType) {
204
+ case TokenType.ENOCH:
205
+ if (this.authTriggers.triggerEnochReAuth) {
206
+ console.log('📧 [API Client] Triggering Enoch email re-verification');
207
+ await this.authTriggers.triggerEnochReAuth();
208
+ } else {
209
+ console.warn('⚠️ [API Client] No Enoch re-auth trigger configured');
210
+ }
211
+ break;
212
+
213
+ case TokenType.ONAIROS:
214
+ if (this.authTriggers.triggerOnairosReAuth) {
215
+ console.log('🔑 [API Client] Triggering Onairos re-authentication');
216
+ await this.authTriggers.triggerOnairosReAuth();
217
+ } else {
218
+ console.warn('⚠️ [API Client] No Onairos re-auth trigger configured');
219
+ }
220
+ break;
221
+
222
+ case TokenType.AUTH:
223
+ if (this.authTriggers.triggerAuthTokenRefresh) {
224
+ console.log('🔄 [API Client] Triggering auth token refresh');
225
+ await this.authTriggers.triggerAuthTokenRefresh();
226
+ } else {
227
+ console.warn('⚠️ [API Client] No auth token refresh trigger configured');
228
+ }
229
+ break;
230
+ }
231
+ } catch (error) {
232
+ console.error(`❌ [API Client] Failed to trigger re-auth for ${tokenType}:`, error);
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Cancel requests using a specific token type
238
+ */
239
+ private cancelRequestsForTokenType(tokenType: TokenType, excludeRequestId?: number): void {
240
+ let cancelledCount = 0;
241
+
242
+ // Note: In a full implementation, we'd track which requests use which tokens
243
+ // For now, we cancel all other requests as a safety measure
244
+ for (const [requestId, controller] of activeRequests.entries()) {
245
+ if (requestId !== excludeRequestId) {
246
+ controller.abort();
247
+ activeRequests.delete(requestId);
248
+ cancelledCount++;
249
+ }
250
+ }
251
+
252
+ if (cancelledCount > 0) {
253
+ console.log(`🚫 [API Client] Cancelled ${cancelledCount} requests due to ${tokenType} token failure`);
254
+ }
255
+ }
256
+
257
+ /**
258
+ * Replace token for specific type and cancel related requests
259
+ */
260
+ async replaceTokenForType(tokenType: TokenType, newToken: string): Promise<boolean> {
261
+ console.log(`🔄 [API Client] Replacing ${tokenType} token`);
262
+
263
+ // Cancel all in-flight requests using this token type
264
+ this.cancelRequestsForTokenType(tokenType);
265
+
266
+ // Replace token using storage service
267
+ const { replaceJWTAfterVerification } = await import('./jwtStorageService');
268
+ const replaced = await replaceJWTAfterVerification(tokenType, newToken);
269
+
270
+ if (replaced) {
271
+ console.log(`✅ [API Client] ${tokenType} token replaced and related requests cancelled`);
272
+ }
273
+
274
+ return replaced;
275
+ }
276
+
277
+ /**
278
+ * Debug: Get all current tokens
279
+ */
280
+ async debugTokens(): Promise<Record<TokenType, string | null>> {
281
+ console.log('🔍 [API Client] Debug: Current token status');
282
+ const tokens = await getAllTokens();
283
+
284
+ for (const [tokenType, token] of Object.entries(tokens)) {
285
+ if (token) {
286
+ console.log(`🔐 ${tokenType}: ${token.substring(0, 20)}... (length: ${token.length})`);
287
+ } else {
288
+ console.log(`📭 ${tokenType}: null`);
289
+ }
290
+ }
291
+
292
+ return tokens;
293
+ }
294
+
295
+ /**
296
+ * Set authentication triggers
297
+ */
298
+ setAuthTriggers(triggers: AuthTriggers): void {
299
+ this.authTriggers = { ...this.authTriggers, ...triggers };
300
+ console.log('🔧 [API Client] Authentication triggers updated');
301
+ }
302
+
303
+ /**
304
+ * Convenience methods for common HTTP verbs
305
+ */
306
+ async get<T = any>(endpoint: string, options: Omit<ApiRequestOptions, 'method'> = {}): Promise<ApiResponse<T>> {
307
+ return this.request<T>(endpoint, { ...options, method: 'GET' });
308
+ }
309
+
310
+ async post<T = any>(endpoint: string, body?: any, options: Omit<ApiRequestOptions, 'method' | 'body'> = {}): Promise<ApiResponse<T>> {
311
+ return this.request<T>(endpoint, { ...options, method: 'POST', body });
312
+ }
313
+
314
+ async put<T = any>(endpoint: string, body?: any, options: Omit<ApiRequestOptions, 'method' | 'body'> = {}): Promise<ApiResponse<T>> {
315
+ return this.request<T>(endpoint, { ...options, method: 'PUT', body });
316
+ }
317
+
318
+ async delete<T = any>(endpoint: string, options: Omit<ApiRequestOptions, 'method'> = {}): Promise<ApiResponse<T>> {
319
+ return this.request<T>(endpoint, { ...options, method: 'DELETE' });
320
+ }
321
+ }
322
+
323
+ // SINGLE INSTANCE - Global API client
324
+ export const apiClient = new ApiClient();
325
+ export { ApiClient };
326
+
327
+ // Export convenience functions
328
+ export const authenticatedRequest = apiClient.request.bind(apiClient);
329
+ export const apiGet = apiClient.get.bind(apiClient);
330
+ export const apiPost = apiClient.post.bind(apiClient);
331
+ export const apiPut = apiClient.put.bind(apiClient);
332
+ export const apiDelete = apiClient.delete.bind(apiClient);
333
+
334
+ // Export types for external use
335
+ export type { TokenType, ApiResponse, AuthTriggers };
336
+
337
337
  export default apiClient;