@onairos/react-native 3.1.16 → 3.1.18

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