@onairos/react-native 3.1.16 → 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 (198) 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/OnairosButton.js +290 -0
  16. package/lib/commonjs/components/OnairosButton.js.map +1 -0
  17. package/lib/commonjs/components/OnairosSignInButton.js +30 -8
  18. package/lib/commonjs/components/OnairosSignInButton.js.map +1 -1
  19. package/lib/commonjs/components/UniversalOnboarding.js +4 -4
  20. package/lib/commonjs/config/api.js +2 -2
  21. package/lib/commonjs/hooks/useConnections.js +6 -6
  22. package/lib/commonjs/hooks/useUserConnections.js +10 -10
  23. package/lib/commonjs/index.js +9 -10
  24. package/lib/commonjs/index.js.map +1 -1
  25. package/lib/commonjs/services/apiClient.js +35 -35
  26. package/lib/commonjs/services/apiKeyService.js +99 -99
  27. package/lib/commonjs/services/authService.js +82 -82
  28. package/lib/commonjs/services/biometricPinService.js +10 -10
  29. package/lib/commonjs/services/connectedAccountsService.js +32 -32
  30. package/lib/commonjs/services/googleAuthService.js +15 -15
  31. package/lib/commonjs/services/imageCompressionService.js +15 -15
  32. package/lib/commonjs/services/jwtStorageService.js +59 -59
  33. package/lib/commonjs/services/mobileTrainingService.js +14 -14
  34. package/lib/commonjs/services/pinEncryptionService.js +10 -10
  35. package/lib/commonjs/services/pinStorageUtils.js +15 -15
  36. package/lib/commonjs/services/platformAuthService.js +47 -47
  37. package/lib/commonjs/services/storageService.js +31 -31
  38. package/lib/commonjs/services/trainingApiHelpers.js +33 -33
  39. package/lib/commonjs/services/userConnectionsService.js +24 -24
  40. package/lib/commonjs/utils/Portal.js +4 -4
  41. package/lib/commonjs/utils/api.js +24 -24
  42. package/lib/commonjs/utils/auth.js +18 -18
  43. package/lib/commonjs/utils/crypto.js +13 -13
  44. package/lib/commonjs/utils/encryption.js +12 -12
  45. package/lib/commonjs/utils/eventUtils.js +52 -52
  46. package/lib/commonjs/utils/programmaticFlow.js +16 -16
  47. package/lib/commonjs/utils/retryHelper.js +27 -27
  48. package/lib/module/assets/images/Checkbox.svg +3 -3
  49. package/lib/module/assets/images/EnochE.svg +19 -19
  50. package/lib/module/assets/images/Personalityprofile.svg +3 -3
  51. package/lib/module/assets/images/Personalitytraits.svg +3 -3
  52. package/lib/module/assets/images/Userpreferences.svg +3 -3
  53. package/lib/module/assets/images/arrow.svg +20 -20
  54. package/lib/module/assets/images/basicproficon.svg +43 -43
  55. package/lib/module/assets/images/basicprofile.svg +3 -3
  56. package/lib/module/assets/images/checkmark.svg +4 -4
  57. package/lib/module/assets/images/contentanalysis.svg +3 -3
  58. package/lib/module/assets/images/contenticon.svg +23 -23
  59. package/lib/module/assets/images/personalityicon.svg +18 -18
  60. package/lib/module/assets/images/x-close.svg +3 -3
  61. package/lib/module/components/OnairosButton.js +282 -0
  62. package/lib/module/components/OnairosButton.js.map +1 -0
  63. package/lib/module/components/OnairosSignInButton.js +30 -8
  64. package/lib/module/components/OnairosSignInButton.js.map +1 -1
  65. package/lib/module/components/UniversalOnboarding.js +4 -4
  66. package/lib/module/config/api.js +2 -2
  67. package/lib/module/hooks/useConnections.js +6 -6
  68. package/lib/module/hooks/useUserConnections.js +10 -10
  69. package/lib/module/index.js +8 -10
  70. package/lib/module/index.js.map +1 -1
  71. package/lib/module/services/apiClient.js +35 -35
  72. package/lib/module/services/apiKeyService.js +99 -99
  73. package/lib/module/services/authService.js +82 -82
  74. package/lib/module/services/biometricPinService.js +10 -10
  75. package/lib/module/services/connectedAccountsService.js +32 -32
  76. package/lib/module/services/googleAuthService.js +15 -15
  77. package/lib/module/services/imageCompressionService.js +15 -15
  78. package/lib/module/services/jwtStorageService.js +59 -59
  79. package/lib/module/services/mobileTrainingService.js +14 -14
  80. package/lib/module/services/pinEncryptionService.js +10 -10
  81. package/lib/module/services/pinStorageUtils.js +15 -15
  82. package/lib/module/services/platformAuthService.js +47 -47
  83. package/lib/module/services/storageService.js +31 -31
  84. package/lib/module/services/trainingApiHelpers.js +33 -33
  85. package/lib/module/services/userConnectionsService.js +24 -24
  86. package/lib/module/utils/Portal.js +4 -4
  87. package/lib/module/utils/api.js +24 -24
  88. package/lib/module/utils/auth.js +18 -18
  89. package/lib/module/utils/crypto.js +13 -13
  90. package/lib/module/utils/encryption.js +12 -12
  91. package/lib/module/utils/eventUtils.js +52 -52
  92. package/lib/module/utils/programmaticFlow.js +16 -16
  93. package/lib/module/utils/retryHelper.js +27 -27
  94. package/lib/typescript/components/OnairosButton.d.ts +37 -0
  95. package/lib/typescript/components/OnairosButton.d.ts.map +1 -0
  96. package/lib/typescript/components/OnairosSignInButton.d.ts +2 -1
  97. package/lib/typescript/components/OnairosSignInButton.d.ts.map +1 -1
  98. package/lib/typescript/index.d.ts +3 -4
  99. package/lib/typescript/index.d.ts.map +1 -1
  100. package/package.json +163 -163
  101. package/src/api/index.ts +151 -151
  102. package/src/assets/images/Checkbox.svg +3 -3
  103. package/src/assets/images/EnochE.svg +19 -19
  104. package/src/assets/images/Personalityprofile.svg +3 -3
  105. package/src/assets/images/Personalitytraits.svg +3 -3
  106. package/src/assets/images/Userpreferences.svg +3 -3
  107. package/src/assets/images/arrow.svg +20 -20
  108. package/src/assets/images/basicproficon.svg +43 -43
  109. package/src/assets/images/basicprofile.svg +3 -3
  110. package/src/assets/images/checkmark.svg +4 -4
  111. package/src/assets/images/contentanalysis.svg +3 -3
  112. package/src/assets/images/contenticon.svg +23 -23
  113. package/src/assets/images/personalityicon.svg +18 -18
  114. package/src/assets/images/x-close.svg +3 -3
  115. package/src/components/BodyText.tsx +33 -33
  116. package/src/components/BrandMark.tsx +62 -62
  117. package/src/components/CodeInput.tsx +32 -32
  118. package/src/components/DataRequestScreen.tsx +355 -355
  119. package/src/components/EmailInput.tsx +31 -31
  120. package/src/components/EmailVerificationModal.tsx +363 -363
  121. package/src/components/ExistingUserDataConfirmation.tsx +506 -506
  122. package/src/components/GoogleButton.tsx +55 -55
  123. package/src/components/HeadingGroup.tsx +49 -49
  124. package/src/components/ModalHeader.tsx +125 -125
  125. package/src/components/ModalSheet.tsx +57 -57
  126. package/src/components/Onairos.tsx +422 -422
  127. package/src/components/OnairosButton.tsx +339 -0
  128. package/src/components/OnairosSignInButton.tsx +30 -10
  129. package/src/components/Overlay.tsx +506 -506
  130. package/src/components/PersonaImage.tsx +79 -79
  131. package/src/components/PersonaLoadingScreen.tsx +201 -201
  132. package/src/components/PersonalizationConsentScreen.tsx +410 -410
  133. package/src/components/PinCreationScreen.tsx +492 -492
  134. package/src/components/PinInput.tsx +555 -555
  135. package/src/components/PlatformConnectorsStep.tsx +891 -891
  136. package/src/components/PlatformList.tsx +144 -144
  137. package/src/components/PlatformToggle.tsx +226 -226
  138. package/src/components/PrimaryButton.tsx +213 -213
  139. package/src/components/SignInMatchAnimation.tsx +225 -225
  140. package/src/components/SignInStep.tsx +217 -217
  141. package/src/components/TrainingModal.tsx +1047 -1047
  142. package/src/components/UniversalOnboarding.tsx +2887 -2887
  143. package/src/components/VerificationStep.tsx +198 -198
  144. package/src/components/WelcomeScreen.tsx +473 -473
  145. package/src/components/icons/Basicproficon.tsx +30 -30
  146. package/src/components/icons/Basicprofile.tsx +17 -17
  147. package/src/components/icons/Checkbox.tsx +17 -17
  148. package/src/components/icons/Checkmark.tsx +24 -24
  149. package/src/components/icons/Contentanalysis.tsx +17 -17
  150. package/src/components/icons/Contenticon.tsx +30 -30
  151. package/src/components/icons/EnochE.tsx +39 -39
  152. package/src/components/icons/Personalityicon.tsx +22 -22
  153. package/src/components/icons/Personalityprofile.tsx +17 -17
  154. package/src/components/icons/Personalitytraits.tsx +17 -17
  155. package/src/components/icons/Userpreferences.tsx +17 -17
  156. package/src/components/icons/index.ts +12 -12
  157. package/src/components/onboarding/OAuthWebView.tsx +232 -232
  158. package/src/config/api.ts +25 -25
  159. package/src/context/AuthContext.tsx +393 -393
  160. package/src/hooks/useConnectedAccounts.ts +138 -138
  161. package/src/hooks/useConnections.ts +161 -161
  162. package/src/hooks/useCredentials.ts +174 -174
  163. package/src/hooks/useUserConnections.ts +165 -165
  164. package/src/index.js +14 -0
  165. package/src/index.ts +94 -96
  166. package/src/services/apiClient.ts +336 -336
  167. package/src/services/apiKeyService.ts +919 -919
  168. package/src/services/authService.ts +1008 -1008
  169. package/src/services/biometricPinService.ts +192 -192
  170. package/src/services/connectedAccountsService.ts +289 -289
  171. package/src/services/googleAuthService.ts +279 -279
  172. package/src/services/imageCompressionService.ts +302 -302
  173. package/src/services/jwtStorageService.ts +256 -256
  174. package/src/services/mobileTrainingService.ts +203 -203
  175. package/src/services/pinEncryptionService.ts +75 -75
  176. package/src/services/pinStorageUtils.ts +96 -96
  177. package/src/services/platformAuthService.ts +1346 -1346
  178. package/src/services/storageService.ts +451 -451
  179. package/src/services/trainingApiHelpers.ts +66 -66
  180. package/src/services/userConnectionsService.ts +556 -556
  181. package/src/services/youtubeMigrationService.ts +453 -453
  182. package/src/theme/index.ts +239 -239
  183. package/src/types/ambient.d.ts +28 -28
  184. package/src/types/index.ts +265 -265
  185. package/src/types/node-fix.d.ts +18 -18
  186. package/src/types/node-override.d.ts +23 -23
  187. package/src/types/opacity.d.ts +15 -15
  188. package/src/types/types.d.ts +17 -17
  189. package/src/utils/Portal.tsx +82 -82
  190. package/src/utils/api.js +111 -111
  191. package/src/utils/auth.js +103 -103
  192. package/src/utils/crypto.js +59 -59
  193. package/src/utils/encryption.ts +68 -68
  194. package/src/utils/eventUtils.ts +302 -302
  195. package/src/utils/haptics.ts +58 -58
  196. package/src/utils/imagePreloader.ts +2 -2
  197. package/src/utils/programmaticFlow.ts +112 -112
  198. 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;