@finatic/client 0.0.138 → 0.0.140

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 (40) hide show
  1. package/README.md +278 -461
  2. package/dist/index.d.ts +59 -516
  3. package/dist/index.js +337 -456
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +338 -456
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/types/core/client/ApiClient.d.ts +12 -26
  8. package/dist/types/core/client/FinaticConnect.d.ts +20 -103
  9. package/dist/types/index.d.ts +1 -2
  10. package/dist/types/mocks/MockApiClient.d.ts +2 -4
  11. package/dist/types/mocks/utils.d.ts +0 -5
  12. package/dist/types/types/api/auth.d.ts +12 -30
  13. package/dist/types/types/api/broker.d.ts +1 -1
  14. package/dist/types/types/connect.d.ts +4 -1
  15. package/package.json +7 -3
  16. package/src/core/client/ApiClient.ts +1721 -0
  17. package/src/core/client/FinaticConnect.ts +1476 -0
  18. package/src/core/portal/PortalUI.ts +300 -0
  19. package/src/index.d.ts +23 -0
  20. package/src/index.ts +87 -0
  21. package/src/mocks/MockApiClient.ts +1032 -0
  22. package/src/mocks/MockDataProvider.ts +986 -0
  23. package/src/mocks/MockFactory.ts +97 -0
  24. package/src/mocks/utils.ts +133 -0
  25. package/src/themes/portalPresets.ts +1307 -0
  26. package/src/types/api/auth.ts +112 -0
  27. package/src/types/api/broker.ts +330 -0
  28. package/src/types/api/core.ts +53 -0
  29. package/src/types/api/errors.ts +35 -0
  30. package/src/types/api/orders.ts +45 -0
  31. package/src/types/api/portfolio.ts +59 -0
  32. package/src/types/common/pagination.ts +138 -0
  33. package/src/types/connect.ts +56 -0
  34. package/src/types/index.ts +25 -0
  35. package/src/types/portal.ts +214 -0
  36. package/src/types/ui/theme.ts +105 -0
  37. package/src/utils/brokerUtils.ts +85 -0
  38. package/src/utils/errors.ts +104 -0
  39. package/src/utils/events.ts +54 -0
  40. package/src/utils/themeUtils.ts +146 -0
package/dist/index.js CHANGED
@@ -205,15 +205,13 @@ class TradingNotEnabledError extends ApiError {
205
205
  }
206
206
  }
207
207
 
208
+ // Supabase import removed - SDK no longer depends on Supabase
208
209
  class ApiClient {
209
210
  constructor(baseUrl, deviceInfo) {
210
211
  this.currentSessionState = null;
211
212
  this.currentSessionId = null;
212
213
  this.tradingContext = {};
213
- // Token management
214
- this.tokenInfo = null;
215
- this.refreshPromise = null;
216
- this.REFRESH_BUFFER_MINUTES = 5; // Refresh token 5 minutes before expiry
214
+ // Session management (no Supabase needed)
217
215
  // Session and company context
218
216
  this.companyId = null;
219
217
  this.csrfToken = null;
@@ -225,7 +223,9 @@ class ApiClient {
225
223
  if (!this.baseUrl.includes('/api/v1')) {
226
224
  this.baseUrl = `${this.baseUrl}/api/v1`;
227
225
  }
226
+ // No Supabase initialization needed - SDK is clean
228
227
  }
228
+ // Supabase initialization removed - SDK no longer depends on Supabase
229
229
  /**
230
230
  * Set session context (session ID, company ID, CSRF token)
231
231
  */
@@ -253,105 +253,30 @@ class ApiClient {
253
253
  return this.csrfToken;
254
254
  }
255
255
  /**
256
- * Store tokens after successful authentication
257
- */
258
- setTokens(accessToken, refreshToken, expiresAt, userId) {
259
- this.tokenInfo = {
260
- accessToken,
261
- refreshToken,
262
- expiresAt,
263
- userId,
264
- };
265
- }
266
- /**
267
- * Get the current access token, refreshing if necessary
256
+ * Get a valid access token (session-based auth - no tokens needed)
268
257
  */
269
258
  async getValidAccessToken() {
270
- if (!this.tokenInfo) {
271
- throw new AuthenticationError('No tokens available. Please authenticate first.');
272
- }
273
- // Check if token is expired or about to expire
274
- if (this.isTokenExpired()) {
275
- await this.refreshTokens();
276
- }
277
- return this.tokenInfo.accessToken;
259
+ // Session-based auth - return empty token as we use session headers
260
+ return '';
278
261
  }
262
+ // Token expiration check removed - session-based auth doesn't use expiring tokens
263
+ // Supabase refresh method removed - SDK no longer uses Supabase tokens
279
264
  /**
280
- * Check if the current token is expired or about to expire
265
+ * Perform the actual Supabase session refresh
281
266
  */
282
- isTokenExpired() {
283
- if (!this.tokenInfo)
284
- return true;
285
- const expiryTime = new Date(this.tokenInfo.expiresAt).getTime();
286
- const currentTime = Date.now();
287
- const bufferTime = this.REFRESH_BUFFER_MINUTES * 60 * 1000; // 5 minutes in milliseconds
288
- return currentTime >= expiryTime - bufferTime;
289
- }
290
- /**
291
- * Refresh the access token using the refresh token
292
- */
293
- async refreshTokens() {
294
- if (!this.tokenInfo) {
295
- throw new AuthenticationError('No refresh token available.');
296
- }
297
- // If a refresh is already in progress, wait for it
298
- if (this.refreshPromise) {
299
- await this.refreshPromise;
300
- return;
301
- }
302
- // Start a new refresh
303
- this.refreshPromise = this.performTokenRefresh();
304
- try {
305
- await this.refreshPromise;
306
- }
307
- finally {
308
- this.refreshPromise = null;
309
- }
310
- }
267
+ // Supabase refresh method removed - SDK no longer uses Supabase tokens
311
268
  /**
312
- * Perform the actual token refresh request
313
- */
314
- async performTokenRefresh() {
315
- if (!this.tokenInfo) {
316
- throw new AuthenticationError('No refresh token available.');
317
- }
318
- try {
319
- const response = await this.request('/company/auth/refresh', {
320
- method: 'POST',
321
- headers: {
322
- 'Content-Type': 'application/json',
323
- },
324
- body: {
325
- refresh_token: this.tokenInfo.refreshToken,
326
- },
327
- });
328
- // Update stored tokens
329
- this.tokenInfo = {
330
- accessToken: response.response_data.access_token,
331
- refreshToken: response.response_data.refresh_token,
332
- expiresAt: response.response_data.expires_at,
333
- userId: this.tokenInfo.userId,
334
- };
335
- return this.tokenInfo;
336
- }
337
- catch (error) {
338
- // Clear tokens on refresh failure
339
- this.tokenInfo = null;
340
- throw new AuthenticationError('Token refresh failed. Please re-authenticate.', error);
341
- }
342
- }
343
- /**
344
- * Clear stored tokens (useful for logout)
269
+ * Clear session tokens (useful for logout)
345
270
  */
346
271
  clearTokens() {
347
- this.tokenInfo = null;
348
- this.refreshPromise = null;
272
+ // Session-based auth - no tokens to clear
349
273
  }
350
274
  /**
351
- * Get current token info (for debugging/testing)
275
+ * Get current session info (for debugging/testing) - session-based auth
352
276
  */
353
277
  getTokenInfo() {
354
- return this.tokenInfo ? { ...this.tokenInfo } : null;
278
+ // Session-based auth - no tokens to return
279
+ return null;
355
280
  }
356
281
  /**
357
282
  * Make a request to the API.
@@ -365,9 +290,12 @@ class ApiClient {
365
290
  url.searchParams.append(key, value);
366
291
  });
367
292
  }
293
+ // Get Supabase JWT token
294
+ const accessToken = await this.getValidAccessToken();
368
295
  // Build comprehensive headers object with all available session data
369
296
  const comprehensiveHeaders = {
370
297
  'Content-Type': 'application/json',
298
+ Authorization: `Bearer ${accessToken}`,
371
299
  };
372
300
  // Add device info if available
373
301
  if (this.deviceInfo) {
@@ -535,7 +463,7 @@ class ApiClient {
535
463
  }
536
464
  // Session Management
537
465
  async startSession(token, userId) {
538
- const response = await this.request('/auth/session/start', {
466
+ const response = await this.request('/session/start', {
539
467
  method: 'POST',
540
468
  headers: {
541
469
  'Content-Type': 'application/json',
@@ -589,11 +517,8 @@ class ApiClient {
589
517
  otp,
590
518
  },
591
519
  });
592
- // Store tokens after successful OTP verification
593
- if (response.success && response.data) {
594
- const expiresAt = new Date(Date.now() + response.data.expires_in * 1000).toISOString();
595
- this.setTokens(response.data.access_token, response.data.refresh_token, expiresAt, response.data.user_id);
596
- }
520
+ // OTP verification successful - tokens are handled by Supabase client
521
+ if (response.success && response.data) ;
597
522
  return response;
598
523
  }
599
524
  // Direct Authentication
@@ -602,7 +527,7 @@ class ApiClient {
602
527
  if (this.currentSessionState !== SessionState.ACTIVE) {
603
528
  throw new SessionError('Session must be in ACTIVE state to authenticate');
604
529
  }
605
- const response = await this.request('/auth/session/authenticate', {
530
+ const response = await this.request('/session/authenticate', {
606
531
  method: 'POST',
607
532
  headers: {
608
533
  'Content-Type': 'application/json',
@@ -620,11 +545,7 @@ class ApiClient {
620
545
  },
621
546
  });
622
547
  // Store tokens after successful direct authentication
623
- if (response.success && response.data) {
624
- // For direct auth, we don't get expires_in, so we'll set a default 1-hour expiry
625
- const expiresAt = new Date(Date.now() + 60 * 60 * 1000).toISOString(); // 1 hour
626
- this.setTokens(response.data.access_token, response.data.refresh_token, expiresAt, userId);
627
- }
548
+ if (response.success && response.data) ;
628
549
  return response;
629
550
  }
630
551
  // Portal Management
@@ -638,7 +559,7 @@ class ApiClient {
638
559
  if (this.currentSessionState !== SessionState.ACTIVE) {
639
560
  throw new SessionError('Session must be in ACTIVE state to get portal URL');
640
561
  }
641
- return this.request('/auth/session/portal', {
562
+ return this.request('/session/portal', {
642
563
  method: 'GET',
643
564
  headers: {
644
565
  'Content-Type': 'application/json',
@@ -799,12 +720,6 @@ class ApiClient {
799
720
  this.tradingContext.accountNumber = accountNumber;
800
721
  this.tradingContext.accountId = accountId;
801
722
  }
802
- getTradingContext() {
803
- return { ...this.tradingContext };
804
- }
805
- clearTradingContext() {
806
- this.tradingContext = {};
807
- }
808
723
  // Stock convenience methods
809
724
  async placeStockMarketOrder(symbol, orderQty, action, broker, accountNumber, extras = {}, connection_id) {
810
725
  return this.placeBrokerOrder({
@@ -812,7 +727,7 @@ class ApiClient {
812
727
  orderQty,
813
728
  action,
814
729
  orderType: 'Market',
815
- assetType: 'Stock',
730
+ assetType: 'equity',
816
731
  timeInForce: 'day',
817
732
  broker,
818
733
  accountNumber,
@@ -824,7 +739,7 @@ class ApiClient {
824
739
  orderQty,
825
740
  action,
826
741
  orderType: 'Limit',
827
- assetType: 'Stock',
742
+ assetType: 'equity',
828
743
  price,
829
744
  timeInForce,
830
745
  broker,
@@ -837,7 +752,7 @@ class ApiClient {
837
752
  orderQty,
838
753
  action,
839
754
  orderType: 'Stop',
840
- assetType: 'Stock',
755
+ assetType: 'equity',
841
756
  stopPrice,
842
757
  timeInForce,
843
758
  broker,
@@ -851,7 +766,7 @@ class ApiClient {
851
766
  orderQty,
852
767
  action,
853
768
  orderType: 'Market',
854
- assetType: 'Crypto',
769
+ assetType: 'crypto',
855
770
  timeInForce: 'day',
856
771
  broker,
857
772
  accountNumber,
@@ -864,7 +779,7 @@ class ApiClient {
864
779
  orderQty,
865
780
  action,
866
781
  orderType: 'Limit',
867
- assetType: 'Crypto',
782
+ assetType: 'crypto',
868
783
  price,
869
784
  timeInForce,
870
785
  broker,
@@ -879,7 +794,7 @@ class ApiClient {
879
794
  orderQty,
880
795
  action,
881
796
  orderType: 'Market',
882
- assetType: 'Option',
797
+ assetType: 'equity_option',
883
798
  timeInForce: 'day',
884
799
  broker,
885
800
  accountNumber,
@@ -892,7 +807,7 @@ class ApiClient {
892
807
  orderQty,
893
808
  action,
894
809
  orderType: 'Limit',
895
- assetType: 'Option',
810
+ assetType: 'equity_option',
896
811
  price,
897
812
  timeInForce,
898
813
  broker,
@@ -907,7 +822,7 @@ class ApiClient {
907
822
  orderQty,
908
823
  action,
909
824
  orderType: 'Market',
910
- assetType: 'Future',
825
+ assetType: 'future',
911
826
  timeInForce: 'day',
912
827
  broker,
913
828
  accountNumber,
@@ -919,7 +834,7 @@ class ApiClient {
919
834
  orderQty,
920
835
  action,
921
836
  orderType: 'Limit',
922
- assetType: 'Future',
837
+ assetType: 'future',
923
838
  price,
924
839
  timeInForce,
925
840
  broker,
@@ -1023,7 +938,7 @@ class ApiClient {
1023
938
  }
1024
939
  async getUserToken(sessionId) {
1025
940
  const accessToken = await this.getValidAccessToken();
1026
- return this.request(`/auth/session/${sessionId}/user`, {
941
+ return this.request(`/session/${sessionId}/user`, {
1027
942
  method: 'GET',
1028
943
  headers: {
1029
944
  Authorization: `Bearer ${accessToken}`,
@@ -1038,18 +953,27 @@ class ApiClient {
1038
953
  }
1039
954
  /**
1040
955
  * Refresh the current session to extend its lifetime
956
+ * Note: This now uses Supabase session refresh instead of custom endpoint
1041
957
  */
1042
958
  async refreshSession() {
1043
959
  if (!this.currentSessionId || !this.companyId) {
1044
960
  throw new SessionError('No active session to refresh');
1045
961
  }
1046
- return this.request('/auth/session/refresh', {
1047
- method: 'POST',
1048
- headers: {
1049
- 'Session-ID': this.currentSessionId,
1050
- 'X-Company-ID': this.companyId,
962
+ // Session-based auth - no token refresh needed
963
+ // Return session info in expected format
964
+ return {
965
+ success: true,
966
+ response_data: {
967
+ session_id: this.currentSessionId,
968
+ company_id: this.companyId,
969
+ status: 'active',
970
+ expires_at: new Date(Date.now() + 60 * 60 * 1000).toISOString(), // 1 hour from now
971
+ user_id: '', // Session-based auth - user_id comes from session
972
+ auto_login: false,
1051
973
  },
1052
- });
974
+ message: 'Session refreshed successfully',
975
+ status_code: 200,
976
+ };
1053
977
  }
1054
978
  // Broker Data Management
1055
979
  async getBrokerList() {
@@ -1672,13 +1596,17 @@ class PortalUI {
1672
1596
  console.warn('[PortalUI] Received message from unauthorized origin:', event.origin, 'Expected:', this.portalOrigin);
1673
1597
  return;
1674
1598
  }
1675
- const { type, userId, error, height, data } = event.data;
1599
+ const { type, userId, access_token, refresh_token, error, height, data } = event.data;
1676
1600
  console.log('[PortalUI] Received message:', event.data);
1677
1601
  switch (type) {
1678
1602
  case 'portal-success': {
1679
1603
  // Handle both direct userId and data.userId formats
1680
1604
  const successUserId = userId || (data && data.userId);
1681
- this.handlePortalSuccess(successUserId);
1605
+ const tokens = {
1606
+ access_token: access_token || (data && data.access_token),
1607
+ refresh_token: refresh_token || (data && data.refresh_token)
1608
+ };
1609
+ this.handlePortalSuccess(successUserId, tokens);
1682
1610
  break;
1683
1611
  }
1684
1612
  case 'portal-error': {
@@ -1710,14 +1638,17 @@ class PortalUI {
1710
1638
  console.warn('[PortalUI] Received unhandled message type:', type);
1711
1639
  }
1712
1640
  }
1713
- handlePortalSuccess(userId) {
1641
+ handlePortalSuccess(userId, tokens) {
1714
1642
  if (!userId) {
1715
1643
  console.error('[PortalUI] Missing userId in portal-success message');
1716
1644
  return;
1717
1645
  }
1718
1646
  console.log('[PortalUI] Portal success - User connected:', userId);
1647
+ if (tokens?.access_token && tokens?.refresh_token) {
1648
+ console.log('[PortalUI] Tokens received for user:', userId);
1649
+ }
1719
1650
  // Pass userId to parent (SDK will handle tokens internally)
1720
- this.options?.onSuccess?.(userId);
1651
+ this.options?.onSuccess?.(userId, tokens);
1721
1652
  }
1722
1653
  handlePortalError(error) {
1723
1654
  console.error('[PortalUI] Portal error:', error);
@@ -1813,15 +1744,11 @@ class MockDataProvider {
1813
1744
  * Generate mock tokens
1814
1745
  */
1815
1746
  generateTokens(userId) {
1816
- const accessToken = `mock_access_${uuid.v4().replace(/-/g, '')}`;
1817
- const refreshToken = `mock_refresh_${uuid.v4().replace(/-/g, '')}`;
1747
+ `mock_access_${uuid.v4().replace(/-/g, '')}`;
1748
+ `mock_refresh_${uuid.v4().replace(/-/g, '')}`;
1818
1749
  return {
1819
- accessToken,
1820
- refreshToken,
1821
- expiresIn: 3600, // 1 hour
1822
1750
  user_id: userId,
1823
- tokenType: 'Bearer',
1824
- scope: 'read write',
1751
+ // Removed token fields - we no longer use Supabase tokens in the SDK
1825
1752
  };
1826
1753
  }
1827
1754
  // Authentication & Session Management Mocks
@@ -1841,6 +1768,7 @@ class MockDataProvider {
1841
1768
  };
1842
1769
  this.sessionData.set(sessionId, sessionData);
1843
1770
  return {
1771
+ success: true,
1844
1772
  data: sessionData,
1845
1773
  message: 'Session started successfully',
1846
1774
  };
@@ -1862,12 +1790,12 @@ class MockDataProvider {
1862
1790
  success: true,
1863
1791
  message: 'OTP verified successfully',
1864
1792
  data: {
1865
- access_token: tokens.accessToken,
1866
- refresh_token: tokens.refreshToken,
1793
+ access_token: '', // No longer using Supabase tokens
1794
+ refresh_token: '', // No longer using Supabase tokens
1867
1795
  user_id: userId,
1868
- expires_in: tokens.expiresIn,
1869
- scope: tokens.scope,
1870
- token_type: tokens.tokenType,
1796
+ expires_in: 0, // No token expiration for session-based auth
1797
+ scope: 'api:access',
1798
+ token_type: 'Bearer',
1871
1799
  },
1872
1800
  };
1873
1801
  }
@@ -1879,8 +1807,8 @@ class MockDataProvider {
1879
1807
  success: true,
1880
1808
  message: 'Authentication successful',
1881
1809
  data: {
1882
- access_token: tokens.accessToken,
1883
- refresh_token: tokens.refreshToken,
1810
+ access_token: '', // No longer using Supabase tokens
1811
+ refresh_token: '', // No longer using Supabase tokens
1884
1812
  },
1885
1813
  };
1886
1814
  }
@@ -1926,6 +1854,8 @@ class MockDataProvider {
1926
1854
  valid: true,
1927
1855
  company_id: this.generateCompanyId(),
1928
1856
  status: 'active',
1857
+ is_sandbox: false,
1858
+ environment: 'production',
1929
1859
  };
1930
1860
  }
1931
1861
  async mockCompletePortalSession(sessionId) {
@@ -2029,6 +1959,12 @@ class MockDataProvider {
2029
1959
  created_at: new Date().toISOString(),
2030
1960
  updated_at: new Date().toISOString(),
2031
1961
  last_synced_at: new Date().toISOString(),
1962
+ positions_synced_at: new Date().toISOString(),
1963
+ orders_synced_at: new Date().toISOString(),
1964
+ balances_synced_at: new Date().toISOString(),
1965
+ account_created_at: new Date(Date.now() - 365 * 24 * 60 * 60 * 1000).toISOString(), // 1 year ago
1966
+ account_updated_at: new Date().toISOString(),
1967
+ account_first_trade_at: new Date(Date.now() - 300 * 24 * 60 * 60 * 1000).toISOString(), // 300 days ago
2032
1968
  },
2033
1969
  {
2034
1970
  id: uuid.v4(),
@@ -2043,6 +1979,12 @@ class MockDataProvider {
2043
1979
  created_at: new Date().toISOString(),
2044
1980
  updated_at: new Date().toISOString(),
2045
1981
  last_synced_at: new Date().toISOString(),
1982
+ positions_synced_at: new Date().toISOString(),
1983
+ orders_synced_at: new Date().toISOString(),
1984
+ balances_synced_at: new Date().toISOString(),
1985
+ account_created_at: new Date(Date.now() - 730 * 24 * 60 * 60 * 1000).toISOString(), // 2 years ago
1986
+ account_updated_at: new Date().toISOString(),
1987
+ account_first_trade_at: new Date(Date.now() - 700 * 24 * 60 * 60 * 1000).toISOString(), // 700 days ago
2046
1988
  },
2047
1989
  ];
2048
1990
  return {
@@ -2528,6 +2470,12 @@ class MockDataProvider {
2528
2470
  created_at: new Date(Date.now() - Math.random() * 365 * 24 * 60 * 60 * 1000).toISOString(), // Random date within last year
2529
2471
  updated_at: new Date().toISOString(),
2530
2472
  last_synced_at: new Date().toISOString(),
2473
+ positions_synced_at: new Date().toISOString(),
2474
+ orders_synced_at: new Date().toISOString(),
2475
+ balances_synced_at: new Date().toISOString(),
2476
+ account_created_at: new Date(Date.now() - Math.random() * 730 * 24 * 60 * 60 * 1000).toISOString(), // Random date within last 2 years
2477
+ account_updated_at: new Date().toISOString(),
2478
+ account_first_trade_at: new Date(Date.now() - Math.random() * 500 * 24 * 60 * 60 * 1000).toISOString(), // Random date within last 500 days
2531
2479
  });
2532
2480
  }
2533
2481
  return accounts;
@@ -2864,12 +2812,6 @@ class MockApiClient {
2864
2812
  this.tradingContext.accountId = accountId;
2865
2813
  console.log('MockApiClient.setAccount Debug - Updated context:', this.tradingContext);
2866
2814
  }
2867
- getTradingContext() {
2868
- return { ...this.tradingContext };
2869
- }
2870
- clearTradingContext() {
2871
- this.tradingContext = {};
2872
- }
2873
2815
  // Stock convenience methods
2874
2816
  async placeStockMarketOrder(symbol, orderQty, action, broker, accountNumber, extras = {}) {
2875
2817
  return this.placeBrokerOrder({
@@ -2879,7 +2821,7 @@ class MockApiClient {
2879
2821
  orderQty,
2880
2822
  action,
2881
2823
  orderType: 'Market',
2882
- assetType: 'Stock',
2824
+ assetType: 'equity',
2883
2825
  timeInForce: 'day',
2884
2826
  }, extras);
2885
2827
  }
@@ -2891,7 +2833,7 @@ class MockApiClient {
2891
2833
  orderQty,
2892
2834
  action,
2893
2835
  orderType: 'Limit',
2894
- assetType: 'Stock',
2836
+ assetType: 'equity',
2895
2837
  timeInForce,
2896
2838
  price,
2897
2839
  }, extras);
@@ -2904,7 +2846,7 @@ class MockApiClient {
2904
2846
  orderQty,
2905
2847
  action,
2906
2848
  orderType: 'Stop',
2907
- assetType: 'Stock',
2849
+ assetType: 'equity',
2908
2850
  timeInForce,
2909
2851
  stopPrice,
2910
2852
  }, extras);
@@ -2918,7 +2860,7 @@ class MockApiClient {
2918
2860
  orderQty: options.quantity || orderQty,
2919
2861
  action,
2920
2862
  orderType: 'Market',
2921
- assetType: 'Crypto',
2863
+ assetType: 'crypto',
2922
2864
  timeInForce: 'gtc', // Crypto typically uses GTC
2923
2865
  };
2924
2866
  return this.placeBrokerOrder(orderParams, extras);
@@ -2931,7 +2873,7 @@ class MockApiClient {
2931
2873
  orderQty: options.quantity || orderQty,
2932
2874
  action,
2933
2875
  orderType: 'Limit',
2934
- assetType: 'Crypto',
2876
+ assetType: 'crypto',
2935
2877
  timeInForce,
2936
2878
  price,
2937
2879
  };
@@ -2946,7 +2888,7 @@ class MockApiClient {
2946
2888
  orderQty,
2947
2889
  action,
2948
2890
  orderType: 'Market',
2949
- assetType: 'Option',
2891
+ assetType: 'equity_option',
2950
2892
  timeInForce: 'day',
2951
2893
  };
2952
2894
  return this.placeBrokerOrder(orderParams, extras);
@@ -2959,7 +2901,7 @@ class MockApiClient {
2959
2901
  orderQty,
2960
2902
  action,
2961
2903
  orderType: 'Limit',
2962
- assetType: 'Option',
2904
+ assetType: 'equity_option',
2963
2905
  timeInForce,
2964
2906
  price,
2965
2907
  };
@@ -2974,7 +2916,7 @@ class MockApiClient {
2974
2916
  orderQty,
2975
2917
  action,
2976
2918
  orderType: 'Market',
2977
- assetType: 'Future',
2919
+ assetType: 'future',
2978
2920
  timeInForce: 'day',
2979
2921
  }, extras);
2980
2922
  }
@@ -2986,7 +2928,7 @@ class MockApiClient {
2986
2928
  orderQty,
2987
2929
  action,
2988
2930
  orderType: 'Limit',
2989
- assetType: 'Future',
2931
+ assetType: 'future',
2990
2932
  timeInForce,
2991
2933
  price,
2992
2934
  }, extras);
@@ -3200,6 +3142,8 @@ class MockApiClient {
3200
3142
  /**
3201
3143
  * Utility functions for mock system environment detection
3202
3144
  */
3145
+ // Type declarations for Node.js environment
3146
+ // Note: process is already declared globally in Node.js types
3203
3147
  /**
3204
3148
  * Check if mocks should be used based on environment variables
3205
3149
  * Supports both browser and Node.js environments
@@ -4902,95 +4846,161 @@ class FinaticConnect extends EventEmitter {
4902
4846
  // Register automatic session cleanup
4903
4847
  this.registerSessionCleanup();
4904
4848
  }
4905
- handleTokens(tokens) {
4906
- if (!tokens.access_token || !tokens.refresh_token) {
4907
- return;
4849
+ async linkUserToSession(userId) {
4850
+ try {
4851
+ if (!this.sessionId) {
4852
+ console.error('No session ID available for user linking');
4853
+ return false;
4854
+ }
4855
+ // Call API endpoint to authenticate user with session
4856
+ const response = await this.apiClient.request('/session/authenticate', {
4857
+ method: 'POST',
4858
+ body: {
4859
+ session_id: this.sessionId,
4860
+ user_id: userId,
4861
+ },
4862
+ });
4863
+ if (response.error) {
4864
+ console.error('Failed to link user to session:', response.error);
4865
+ return false;
4866
+ }
4867
+ console.log('User linked to session successfully');
4868
+ return true;
4869
+ }
4870
+ catch (error) {
4871
+ console.error('Error linking user to session:', error);
4872
+ return false;
4908
4873
  }
4909
- // Keep existing user_id or use empty string as fallback
4910
- const userId = this.userToken?.user_id || '';
4911
- this.userToken = {
4912
- accessToken: tokens.access_token,
4913
- refreshToken: tokens.refresh_token,
4914
- expiresIn: 3600, // Default to 1 hour if not provided
4915
- user_id: userId,
4916
- tokenType: 'Bearer',
4917
- scope: 'api:access',
4918
- };
4919
- // Store tokens in ApiClient for automatic refresh
4920
- const expiresAt = new Date(Date.now() + 3600 * 1000).toISOString(); // 1 hour from now
4921
- this.apiClient.setTokens(tokens.access_token, tokens.refresh_token, expiresAt, userId);
4922
4874
  }
4923
4875
  /**
4924
- * Check if the user is fully authenticated (has userId, access token, and refresh token)
4925
- * @returns True if the user is fully authenticated and ready for API calls
4876
+ * Store user ID for authentication state persistence
4877
+ * @param userId - The user ID to store
4926
4878
  */
4927
- isAuthed() {
4928
- return !!(this.userToken?.accessToken &&
4929
- this.userToken?.refreshToken &&
4930
- this.userToken?.user_id);
4879
+ storeUserId(userId) {
4880
+ // Initialize userToken if it doesn't exist
4881
+ if (!this.userToken) {
4882
+ this.userToken = {
4883
+ user_id: userId,
4884
+ };
4885
+ }
4886
+ else {
4887
+ // Update existing userToken with new userId
4888
+ this.userToken.user_id = userId;
4889
+ }
4890
+ // Set user ID in ApiClient for session context
4891
+ this.apiClient.setSessionContext(this.sessionId || '', this.companyId, undefined);
4931
4892
  }
4932
4893
  /**
4933
- * Check if the client is authenticated (alias for isAuthed for consistency)
4934
- * @returns True if authenticated, false otherwise
4894
+ * Check if the user is fully authenticated (has userId in session context)
4895
+ * @returns True if the user is fully authenticated and ready for API calls
4935
4896
  */
4936
- is_authenticated() {
4937
- return this.isAuthed();
4897
+ async isAuthenticated() {
4898
+ // Check internal session context only - no localStorage dependency
4899
+ return this.userToken?.user_id !== undefined && this.userToken?.user_id !== null;
4938
4900
  }
4939
4901
  /**
4940
4902
  * Get user's orders with pagination and optional filtering
4941
4903
  * @param params - Query parameters including page, perPage, and filters
4942
4904
  * @returns Promise with paginated result that supports navigation
4943
4905
  */
4944
- async getOrders(params) {
4945
- if (!this.isAuthed()) {
4906
+ async getOrders(page = 1, perPage = 100, options, filters) {
4907
+ if (!(await this.isAuthenticated())) {
4946
4908
  throw new AuthenticationError('User is not authenticated');
4947
4909
  }
4948
- const page = params?.page || 1;
4949
- const perPage = params?.perPage || 100;
4950
- const filter = params?.filter;
4951
- return this.getOrdersPage(page, perPage, filter);
4910
+ const result = await this.apiClient.getBrokerOrdersPage(page, perPage, filters);
4911
+ // Add navigation methods to the result
4912
+ const paginatedResult = result;
4913
+ paginatedResult.next_page = async () => {
4914
+ if (paginatedResult.hasNext) {
4915
+ return this.apiClient.getBrokerOrdersPage(page + 1, perPage, filters);
4916
+ }
4917
+ throw new Error('No next page available');
4918
+ };
4919
+ paginatedResult.previous_page = async () => {
4920
+ if (paginatedResult.has_previous) {
4921
+ return this.apiClient.getBrokerOrdersPage(page - 1, perPage, filters);
4922
+ }
4923
+ throw new Error('No previous page available');
4924
+ };
4925
+ return paginatedResult;
4952
4926
  }
4953
4927
  /**
4954
4928
  * Get user's positions with pagination and optional filtering
4955
4929
  * @param params - Query parameters including page, perPage, and filters
4956
4930
  * @returns Promise with paginated result that supports navigation
4957
4931
  */
4958
- async getPositions(params) {
4959
- if (!this.isAuthed()) {
4932
+ async getPositions(page = 1, perPage = 100, options, filters) {
4933
+ if (!(await this.isAuthenticated())) {
4960
4934
  throw new AuthenticationError('User is not authenticated');
4961
4935
  }
4962
- const page = params?.page || 1;
4963
- const perPage = params?.perPage || 100;
4964
- const filter = params?.filter;
4965
- return this.getPositionsPage(page, perPage, filter);
4936
+ const result = await this.apiClient.getBrokerPositionsPage(page, perPage, filters);
4937
+ // Add navigation methods to the result
4938
+ const paginatedResult = result;
4939
+ paginatedResult.next_page = async () => {
4940
+ if (paginatedResult.hasNext) {
4941
+ return this.apiClient.getBrokerPositionsPage(page + 1, perPage, filters);
4942
+ }
4943
+ throw new Error('No next page available');
4944
+ };
4945
+ paginatedResult.previous_page = async () => {
4946
+ if (paginatedResult.has_previous) {
4947
+ return this.apiClient.getBrokerPositionsPage(page - 1, perPage, filters);
4948
+ }
4949
+ throw new Error('No previous page available');
4950
+ };
4951
+ return paginatedResult;
4966
4952
  }
4967
4953
  /**
4968
4954
  * Get user's accounts with pagination and optional filtering
4969
4955
  * @param params - Query parameters including page, perPage, and filters
4970
4956
  * @returns Promise with paginated result that supports navigation
4971
4957
  */
4972
- async getAccounts(params) {
4973
- if (!this.isAuthed()) {
4958
+ async getAccounts(page = 1, perPage = 100, options, filters) {
4959
+ if (!(await this.isAuthenticated())) {
4974
4960
  throw new AuthenticationError('User is not authenticated');
4975
4961
  }
4976
- const page = params?.page || 1;
4977
- const perPage = params?.perPage || 100;
4978
- const filter = params?.filter;
4979
- return this.getAccountsPage(page, perPage, filter);
4962
+ const result = await this.apiClient.getBrokerAccountsPage(page, perPage, filters);
4963
+ // Add navigation methods to the result
4964
+ const paginatedResult = result;
4965
+ paginatedResult.next_page = async () => {
4966
+ if (paginatedResult.hasNext) {
4967
+ return this.apiClient.getBrokerAccountsPage(page + 1, perPage, filters);
4968
+ }
4969
+ throw new Error('No next page available');
4970
+ };
4971
+ paginatedResult.previous_page = async () => {
4972
+ if (paginatedResult.has_previous) {
4973
+ return this.apiClient.getBrokerAccountsPage(page - 1, perPage, filters);
4974
+ }
4975
+ throw new Error('No previous page available');
4976
+ };
4977
+ return paginatedResult;
4980
4978
  }
4981
4979
  /**
4982
4980
  * Get user's balances with pagination and optional filtering
4983
4981
  * @param params - Query parameters including page, perPage, and filters
4984
4982
  * @returns Promise with paginated result that supports navigation
4985
4983
  */
4986
- async getBalances(params) {
4987
- if (!this.isAuthed()) {
4984
+ async getBalances(page = 1, perPage = 100, options, filters) {
4985
+ if (!(await this.isAuthenticated())) {
4988
4986
  throw new AuthenticationError('User is not authenticated');
4989
4987
  }
4990
- const page = params?.page || 1;
4991
- const perPage = params?.perPage || 100;
4992
- const filter = params?.filter;
4993
- return this.getBalancesPage(page, perPage, filter);
4988
+ const result = await this.apiClient.getBrokerBalancesPage(page, perPage, filters);
4989
+ // Add navigation methods to the result
4990
+ const paginatedResult = result;
4991
+ paginatedResult.next_page = async () => {
4992
+ if (paginatedResult.hasNext) {
4993
+ return this.apiClient.getBrokerBalancesPage(page + 1, perPage, filters);
4994
+ }
4995
+ throw new Error('No next page available');
4996
+ };
4997
+ paginatedResult.previous_page = async () => {
4998
+ if (paginatedResult.has_previous) {
4999
+ return this.apiClient.getBrokerBalancesPage(page - 1, perPage, filters);
5000
+ }
5001
+ throw new Error('No previous page available');
5002
+ };
5003
+ return paginatedResult;
4994
5004
  }
4995
5005
  /**
4996
5006
  * Initialize the Finatic Connect SDK
@@ -5043,26 +5053,20 @@ class FinaticConnect extends EventEmitter {
5043
5053
  FinaticConnect.instance.apiClient.setSessionContext(FinaticConnect.instance.sessionId, FinaticConnect.instance.companyId, startResponse.data.csrf_token // If available in response
5044
5054
  );
5045
5055
  }
5046
- // If userId is provided, authenticate directly
5056
+ // If userId is provided, try to link user to session
5047
5057
  if (normalizedUserId) {
5048
5058
  try {
5049
- const authResponse = await FinaticConnect.instance.apiClient.authenticateDirectly(startResponse.data.session_id, normalizedUserId);
5050
- // Convert API response to UserToken format
5051
- const userToken = {
5052
- accessToken: authResponse.data.access_token,
5053
- refreshToken: authResponse.data.refresh_token,
5054
- expiresIn: 3600, // Default to 1 hour
5055
- user_id: normalizedUserId,
5056
- tokenType: 'Bearer',
5057
- scope: 'api:access',
5058
- };
5059
- // Set the tokens in both FinaticConnect and ApiClient
5060
- FinaticConnect.instance.userToken = userToken;
5061
- // Set tokens in ApiClient for automatic token management
5062
- const expiresAt = new Date(Date.now() + 3600 * 1000).toISOString(); // 1 hour from now
5063
- FinaticConnect.instance.apiClient.setTokens(authResponse.data.access_token, authResponse.data.refresh_token, expiresAt, normalizedUserId);
5064
- // Emit success event
5065
- FinaticConnect.instance.emit('success', normalizedUserId);
5059
+ // Try to link user to session via API
5060
+ const linked = await FinaticConnect.instance.linkUserToSession(normalizedUserId);
5061
+ if (linked) {
5062
+ // Store user ID for authentication state
5063
+ FinaticConnect.instance.storeUserId(normalizedUserId);
5064
+ // Emit success event
5065
+ FinaticConnect.instance.emit('success', normalizedUserId);
5066
+ }
5067
+ else {
5068
+ console.warn('Failed to link user to session during initialization');
5069
+ }
5066
5070
  }
5067
5071
  catch (error) {
5068
5072
  FinaticConnect.instance.emit('error', error);
@@ -5083,38 +5087,25 @@ class FinaticConnect extends EventEmitter {
5083
5087
  * Get the user and tokens for a completed session
5084
5088
  * @returns Promise with user information and tokens
5085
5089
  */
5086
- async getSessionUser() {
5087
- if (!this.isAuthed()) {
5088
- throw new AuthenticationError('User is not authenticated');
5089
- }
5090
- if (!this.userToken) {
5091
- throw new AuthenticationError('No user token available');
5092
- }
5093
- return {
5094
- user_id: this.userToken.userId,
5095
- access_token: this.userToken.accessToken,
5096
- refresh_token: this.userToken.refreshToken,
5097
- expires_in: this.userToken.expiresIn,
5098
- token_type: this.userToken.tokenType,
5099
- scope: this.userToken.scope,
5100
- company_id: this.companyId,
5101
- };
5102
- }
5103
5090
  async initializeWithUser(userId) {
5104
5091
  try {
5105
5092
  if (!this.sessionId) {
5106
5093
  throw new SessionError('Session not initialized');
5107
5094
  }
5108
- this.userToken = await this.apiClient.getUserToken(this.sessionId);
5109
- // Set tokens in ApiClient for automatic token management
5110
- if (this.userToken) {
5111
- const expiresAt = new Date(Date.now() + 3600 * 1000).toISOString(); // 1 hour from now
5112
- this.apiClient.setTokens(this.userToken.accessToken, this.userToken.refreshToken, expiresAt, userId);
5095
+ // Try to link user to session
5096
+ const linked = await this.linkUserToSession(userId);
5097
+ if (!linked) {
5098
+ console.warn('Failed to link user to session during initialization');
5099
+ // Don't throw error, just continue without authentication
5100
+ return;
5113
5101
  }
5102
+ // Store user ID for authentication state
5103
+ this.storeUserId(userId);
5114
5104
  this.emit('success', userId);
5115
5105
  }
5116
5106
  catch (error) {
5117
5107
  this.emit('error', error);
5108
+ throw error;
5118
5109
  }
5119
5110
  }
5120
5111
  /**
@@ -5167,6 +5158,15 @@ class FinaticConnect extends EventEmitter {
5167
5158
  url.searchParams.set('email', options.email);
5168
5159
  themedPortalUrl = url.toString();
5169
5160
  }
5161
+ // Add session ID to portal URL so the portal can use it
5162
+ const url = new URL(themedPortalUrl);
5163
+ if (this.sessionId) {
5164
+ url.searchParams.set('session_id', this.sessionId);
5165
+ }
5166
+ if (this.companyId) {
5167
+ url.searchParams.set('company_id', this.companyId);
5168
+ }
5169
+ themedPortalUrl = url.toString();
5170
5170
  // Create portal UI if not exists
5171
5171
  if (!this.portalUI) {
5172
5172
  this.portalUI = new PortalUI(this.baseUrl);
@@ -5178,6 +5178,13 @@ class FinaticConnect extends EventEmitter {
5178
5178
  if (!this.sessionId) {
5179
5179
  throw new SessionError('Session not initialized');
5180
5180
  }
5181
+ // Store the userId for authentication state
5182
+ this.storeUserId(userId);
5183
+ // Try to link user to session via API
5184
+ const linked = await this.linkUserToSession(userId);
5185
+ if (!linked) {
5186
+ console.warn('Failed to link user to session, but continuing with authentication');
5187
+ }
5181
5188
  // Emit portal success event
5182
5189
  this.emit('portal:success', userId);
5183
5190
  // Emit legacy success event
@@ -5267,32 +5274,16 @@ class FinaticConnect extends EventEmitter {
5267
5274
  * Place a new order using the broker order API
5268
5275
  * @param order - Order details with broker context
5269
5276
  */
5270
- async placeOrder(order) {
5271
- if (!this.userToken) {
5272
- throw new Error('Not initialized with user');
5277
+ async placeOrder(order, extras) {
5278
+ if (!(await this.isAuthenticated())) {
5279
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5280
+ }
5281
+ if (!this.userToken?.user_id) {
5282
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5273
5283
  }
5274
5284
  try {
5275
- // Convert order format to match broker API
5276
- const brokerOrder = {
5277
- symbol: order.symbol,
5278
- orderQty: order.quantity,
5279
- action: order.side === 'buy' ? 'Buy' : 'Sell',
5280
- orderType: order.orderType === 'market'
5281
- ? 'Market'
5282
- : order.orderType === 'limit'
5283
- ? 'Limit'
5284
- : order.orderType === 'stop'
5285
- ? 'Stop'
5286
- : 'StopLimit',
5287
- assetType: order.assetType || 'Stock',
5288
- timeInForce: order.timeInForce,
5289
- price: order.price,
5290
- stopPrice: order.stopPrice,
5291
- broker: order.broker,
5292
- accountNumber: order.accountNumber,
5293
- order_id: order.order_id,
5294
- };
5295
- return await this.apiClient.placeBrokerOrder(this.userToken.accessToken, brokerOrder, {}, order.connection_id);
5285
+ // Use the order parameter directly since it's already BrokerOrderParams
5286
+ return await this.apiClient.placeBrokerOrder(order, extras || {}, order.connection_id);
5296
5287
  }
5297
5288
  catch (error) {
5298
5289
  this.emit('error', error);
@@ -5306,8 +5297,11 @@ class FinaticConnect extends EventEmitter {
5306
5297
  * @param connection_id - Optional connection ID for testing bypass
5307
5298
  */
5308
5299
  async cancelOrder(orderId, broker, connection_id) {
5309
- if (!this.userToken) {
5310
- throw new Error('Not initialized with user');
5300
+ if (!(await this.isAuthenticated())) {
5301
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5302
+ }
5303
+ if (!this.userToken?.user_id) {
5304
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5311
5305
  }
5312
5306
  try {
5313
5307
  return await this.apiClient.cancelBrokerOrder(orderId, broker, {}, connection_id);
@@ -5325,8 +5319,11 @@ class FinaticConnect extends EventEmitter {
5325
5319
  * @param connection_id - Optional connection ID for testing bypass
5326
5320
  */
5327
5321
  async modifyOrder(orderId, modifications, broker, connection_id) {
5328
- if (!this.userToken) {
5329
- throw new Error('Not initialized with user');
5322
+ if (!(await this.isAuthenticated())) {
5323
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5324
+ }
5325
+ if (!this.userToken?.user_id) {
5326
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5330
5327
  }
5331
5328
  try {
5332
5329
  // Convert modifications to broker format
@@ -5354,39 +5351,15 @@ class FinaticConnect extends EventEmitter {
5354
5351
  throw error;
5355
5352
  }
5356
5353
  }
5357
- /**
5358
- * Set the broker context for trading
5359
- * @param broker - The broker to use for trading
5360
- */
5361
- setTradingContextBroker(broker) {
5362
- this.apiClient.setBroker(broker);
5363
- }
5364
- /**
5365
- * Set the account context for trading
5366
- * @param accountNumber - The account number to use for trading
5367
- * @param accountId - Optional account ID
5368
- */
5369
- setTradingContextAccount(accountNumber, accountId) {
5370
- this.apiClient.setAccount(accountNumber, accountId);
5371
- }
5372
- /**
5373
- * Get the current trading context
5374
- */
5375
- getTradingContext() {
5376
- return this.apiClient.getTradingContext();
5377
- }
5378
- /**
5379
- * Clear the trading context
5380
- */
5381
- clearTradingContext() {
5382
- this.apiClient.clearTradingContext();
5383
- }
5384
5354
  /**
5385
5355
  * Place a stock market order (convenience method)
5386
5356
  */
5387
5357
  async placeStockMarketOrder(symbol, quantity, side, broker, accountNumber) {
5388
- if (!this.userToken) {
5389
- throw new Error('Not initialized with user');
5358
+ if (!(await this.isAuthenticated())) {
5359
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5360
+ }
5361
+ if (!this.userToken?.user_id) {
5362
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5390
5363
  }
5391
5364
  try {
5392
5365
  return await this.apiClient.placeStockMarketOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', broker, accountNumber);
@@ -5400,8 +5373,11 @@ class FinaticConnect extends EventEmitter {
5400
5373
  * Place a stock limit order (convenience method)
5401
5374
  */
5402
5375
  async placeStockLimitOrder(symbol, quantity, side, price, timeInForce = 'gtc', broker, accountNumber) {
5403
- if (!this.userToken) {
5404
- throw new Error('Not initialized with user');
5376
+ if (!(await this.isAuthenticated())) {
5377
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5378
+ }
5379
+ if (!this.userToken?.user_id) {
5380
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5405
5381
  }
5406
5382
  try {
5407
5383
  return await this.apiClient.placeStockLimitOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', price, timeInForce, broker, accountNumber);
@@ -5415,8 +5391,11 @@ class FinaticConnect extends EventEmitter {
5415
5391
  * Place a stock stop order (convenience method)
5416
5392
  */
5417
5393
  async placeStockStopOrder(symbol, quantity, side, stopPrice, timeInForce = 'gtc', broker, accountNumber) {
5418
- if (!this.userToken) {
5419
- throw new Error('Not initialized with user');
5394
+ if (!(await this.isAuthenticated())) {
5395
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5396
+ }
5397
+ if (!this.userToken?.user_id) {
5398
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5420
5399
  }
5421
5400
  try {
5422
5401
  return await this.apiClient.placeStockStopOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', stopPrice, timeInForce, broker, accountNumber);
@@ -5430,8 +5409,11 @@ class FinaticConnect extends EventEmitter {
5430
5409
  * Place a crypto market order (convenience method)
5431
5410
  */
5432
5411
  async placeCryptoMarketOrder(symbol, quantity, side, broker, accountNumber) {
5433
- if (!this.userToken) {
5434
- throw new Error('Not initialized with user');
5412
+ if (!(await this.isAuthenticated())) {
5413
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5414
+ }
5415
+ if (!this.userToken?.user_id) {
5416
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5435
5417
  }
5436
5418
  try {
5437
5419
  return await this.apiClient.placeCryptoMarketOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', broker, accountNumber);
@@ -5445,8 +5427,11 @@ class FinaticConnect extends EventEmitter {
5445
5427
  * Place a crypto limit order (convenience method)
5446
5428
  */
5447
5429
  async placeCryptoLimitOrder(symbol, quantity, side, price, timeInForce = 'gtc', broker, accountNumber) {
5448
- if (!this.userToken) {
5449
- throw new Error('Not initialized with user');
5430
+ if (!(await this.isAuthenticated())) {
5431
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5432
+ }
5433
+ if (!this.userToken?.user_id) {
5434
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5450
5435
  }
5451
5436
  try {
5452
5437
  return await this.apiClient.placeCryptoLimitOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', price, timeInForce, broker, accountNumber);
@@ -5460,8 +5445,11 @@ class FinaticConnect extends EventEmitter {
5460
5445
  * Place an options market order (convenience method)
5461
5446
  */
5462
5447
  async placeOptionsMarketOrder(symbol, quantity, side, broker, accountNumber) {
5463
- if (!this.userToken) {
5464
- throw new Error('Not initialized with user');
5448
+ if (!(await this.isAuthenticated())) {
5449
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5450
+ }
5451
+ if (!this.userToken?.user_id) {
5452
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5465
5453
  }
5466
5454
  try {
5467
5455
  return await this.apiClient.placeOptionsMarketOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', broker, accountNumber);
@@ -5475,8 +5463,11 @@ class FinaticConnect extends EventEmitter {
5475
5463
  * Place an options limit order (convenience method)
5476
5464
  */
5477
5465
  async placeOptionsLimitOrder(symbol, quantity, side, price, timeInForce = 'gtc', broker, accountNumber) {
5478
- if (!this.userToken) {
5479
- throw new Error('Not initialized with user');
5466
+ if (!(await this.isAuthenticated())) {
5467
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5468
+ }
5469
+ if (!this.userToken?.user_id) {
5470
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5480
5471
  }
5481
5472
  try {
5482
5473
  return await this.apiClient.placeOptionsLimitOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', price, timeInForce, broker, accountNumber);
@@ -5490,8 +5481,11 @@ class FinaticConnect extends EventEmitter {
5490
5481
  * Place a futures market order (convenience method)
5491
5482
  */
5492
5483
  async placeFuturesMarketOrder(symbol, quantity, side, broker, accountNumber) {
5493
- if (!this.userToken) {
5494
- throw new Error('Not initialized with user');
5484
+ if (!(await this.isAuthenticated())) {
5485
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5486
+ }
5487
+ if (!this.userToken?.user_id) {
5488
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5495
5489
  }
5496
5490
  try {
5497
5491
  return await this.apiClient.placeFuturesMarketOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', broker, accountNumber);
@@ -5505,8 +5499,11 @@ class FinaticConnect extends EventEmitter {
5505
5499
  * Place a futures limit order (convenience method)
5506
5500
  */
5507
5501
  async placeFuturesLimitOrder(symbol, quantity, side, price, timeInForce = 'gtc', broker, accountNumber) {
5508
- if (!this.userToken) {
5509
- throw new Error('Not initialized with user');
5502
+ if (!(await this.isAuthenticated())) {
5503
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5504
+ }
5505
+ if (!this.userToken?.user_id) {
5506
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5510
5507
  }
5511
5508
  try {
5512
5509
  return await this.apiClient.placeFuturesLimitOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', price, timeInForce, broker, accountNumber);
@@ -5521,8 +5518,8 @@ class FinaticConnect extends EventEmitter {
5521
5518
  * @returns The current user ID or undefined if not authenticated
5522
5519
  * @throws AuthenticationError if user is not authenticated
5523
5520
  */
5524
- getUserId() {
5525
- if (!this.isAuthed()) {
5521
+ async getUserId() {
5522
+ if (!(await this.isAuthenticated())) {
5526
5523
  return null;
5527
5524
  }
5528
5525
  if (!this.userToken?.user_id) {
@@ -5535,7 +5532,7 @@ class FinaticConnect extends EventEmitter {
5535
5532
  * @returns Promise with array of broker information
5536
5533
  */
5537
5534
  async getBrokerList() {
5538
- // if (!this.isAuthed()) {
5535
+ // if (!this.isAuthenticated()) {
5539
5536
  // throw new AuthenticationError('Not authenticated');
5540
5537
  // }
5541
5538
  const response = await this.apiClient.getBrokerList();
@@ -5552,7 +5549,7 @@ class FinaticConnect extends EventEmitter {
5552
5549
  * @throws AuthenticationError if user is not authenticated
5553
5550
  */
5554
5551
  async getBrokerConnections() {
5555
- if (!this.isAuthed()) {
5552
+ if (!(await this.isAuthenticated())) {
5556
5553
  throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5557
5554
  }
5558
5555
  if (!this.userToken?.user_id) {
@@ -5626,118 +5623,22 @@ class FinaticConnect extends EventEmitter {
5626
5623
  return this.getAllPositions({ broker_id: brokerId });
5627
5624
  }
5628
5625
  // Pagination methods
5629
- /**
5630
- * Get a specific page of orders with pagination metadata
5631
- * @param page - Page number (default: 1)
5632
- * @param perPage - Items per page (default: 100)
5633
- * @param filter - Optional filter parameters
5634
- * @returns Promise with paginated orders result
5635
- */
5636
- async getOrdersPage(page = 1, perPage = 100, filter) {
5637
- if (!this.isAuthed()) {
5638
- throw new AuthenticationError('User is not authenticated');
5639
- }
5640
- return this.apiClient.getBrokerOrdersPage(page, perPage, filter);
5641
- }
5642
- /**
5643
- * Get a specific page of positions with pagination metadata
5644
- * @param page - Page number (default: 1)
5645
- * @param perPage - Items per page (default: 100)
5646
- * @param filter - Optional filter parameters
5647
- * @returns Promise with paginated positions result
5648
- */
5649
- async getPositionsPage(page = 1, perPage = 100, filter) {
5650
- if (!this.isAuthed()) {
5651
- throw new AuthenticationError('User is not authenticated');
5652
- }
5653
- return this.apiClient.getBrokerPositionsPage(page, perPage, filter);
5654
- }
5655
- /**
5656
- * Get a specific page of accounts with pagination metadata
5657
- * @param page - Page number (default: 1)
5658
- * @param perPage - Items per page (default: 100)
5659
- * @param filter - Optional filter parameters
5660
- * @returns Promise with paginated accounts result
5661
- */
5662
- async getAccountsPage(page = 1, perPage = 100, filter) {
5663
- if (!this.isAuthed()) {
5664
- throw new AuthenticationError('User is not authenticated');
5665
- }
5666
- return this.apiClient.getBrokerAccountsPage(page, perPage, filter);
5667
- }
5668
- async getBalancesPage(page = 1, perPage = 100, filter) {
5669
- if (!this.isAuthed()) {
5670
- throw new AuthenticationError('User is not authenticated');
5671
- }
5672
- return this.apiClient.getBrokerBalancesPage(page, perPage, filter);
5673
- }
5674
- /**
5675
- * Get the next page of orders
5676
- * @param previousResult - The previous paginated result
5677
- * @returns Promise with next page of orders or null if no more pages
5678
- */
5679
- async getNextOrdersPage(previousResult) {
5680
- if (!this.isAuthed()) {
5681
- throw new AuthenticationError('User is not authenticated');
5682
- }
5683
- return this.apiClient.getNextPage(previousResult, (offset, limit) => {
5684
- const page = Math.floor(offset / limit) + 1;
5685
- return this.apiClient.getBrokerOrdersPage(page, limit);
5686
- });
5687
- }
5688
- /**
5689
- * Get the next page of positions
5690
- * @param previousResult - The previous paginated result
5691
- * @returns Promise with next page of positions or null if no more pages
5692
- */
5693
- async getNextPositionsPage(previousResult) {
5694
- if (!this.isAuthed()) {
5695
- throw new AuthenticationError('User is not authenticated');
5696
- }
5697
- return this.apiClient.getNextPage(previousResult, (offset, limit) => {
5698
- const page = Math.floor(offset / limit) + 1;
5699
- return this.apiClient.getBrokerPositionsPage(page, limit);
5700
- });
5701
- }
5702
- /**
5703
- * Get the next page of accounts
5704
- * @param previousResult - The previous paginated result
5705
- * @returns Promise with next page of accounts or null if no more pages
5706
- */
5707
- async getNextAccountsPage(previousResult) {
5708
- if (!this.isAuthed()) {
5709
- throw new AuthenticationError('User is not authenticated');
5710
- }
5711
- return this.apiClient.getNextPage(previousResult, (offset, limit) => {
5712
- const page = Math.floor(offset / limit) + 1;
5713
- return this.apiClient.getBrokerAccountsPage(page, limit);
5714
- });
5715
- }
5716
- async getNextBalancesPage(previousResult) {
5717
- if (!this.isAuthed()) {
5718
- throw new AuthenticationError('User is not authenticated');
5719
- }
5720
- return this.apiClient.getNextPage(previousResult, (offset, limit) => {
5721
- const page = Math.floor(offset / limit) + 1;
5722
- return this.apiClient.getBrokerBalancesPage(page, limit);
5723
- });
5724
- }
5725
5626
  /**
5726
5627
  * Get all orders across all pages (convenience method)
5727
5628
  * @param filter - Optional filter parameters
5728
5629
  * @returns Promise with all orders
5729
5630
  */
5730
5631
  async getAllOrders(filter) {
5731
- if (!this.isAuthed()) {
5632
+ if (!(await this.isAuthenticated())) {
5732
5633
  throw new AuthenticationError('User is not authenticated');
5733
5634
  }
5734
5635
  const allData = [];
5735
- let currentResult = await this.getOrdersPage(1, 100, filter);
5636
+ let currentResult = await this.apiClient.getBrokerOrdersPage(1, 100, filter);
5736
5637
  while (currentResult) {
5737
5638
  allData.push(...currentResult.data);
5738
5639
  if (!currentResult.hasNext)
5739
5640
  break;
5740
- const nextResult = await this.getNextOrdersPage(currentResult);
5641
+ const nextResult = await currentResult.nextPage();
5741
5642
  if (!nextResult)
5742
5643
  break;
5743
5644
  currentResult = nextResult;
@@ -5750,16 +5651,16 @@ class FinaticConnect extends EventEmitter {
5750
5651
  * @returns Promise with all positions
5751
5652
  */
5752
5653
  async getAllPositions(filter) {
5753
- if (!this.isAuthed()) {
5654
+ if (!(await this.isAuthenticated())) {
5754
5655
  throw new AuthenticationError('User is not authenticated');
5755
5656
  }
5756
5657
  const allData = [];
5757
- let currentResult = await this.getPositionsPage(1, 100, filter);
5658
+ let currentResult = await this.apiClient.getBrokerPositionsPage(1, 100, filter);
5758
5659
  while (currentResult) {
5759
5660
  allData.push(...currentResult.data);
5760
5661
  if (!currentResult.hasNext)
5761
5662
  break;
5762
- const nextResult = await this.getNextPositionsPage(currentResult);
5663
+ const nextResult = await currentResult.nextPage();
5763
5664
  if (!nextResult)
5764
5665
  break;
5765
5666
  currentResult = nextResult;
@@ -5772,16 +5673,16 @@ class FinaticConnect extends EventEmitter {
5772
5673
  * @returns Promise with all accounts
5773
5674
  */
5774
5675
  async getAllAccounts(filter) {
5775
- if (!this.isAuthed()) {
5676
+ if (!(await this.isAuthenticated())) {
5776
5677
  throw new AuthenticationError('User is not authenticated');
5777
5678
  }
5778
5679
  const allData = [];
5779
- let currentResult = await this.getAccountsPage(1, 100, filter);
5680
+ let currentResult = await this.apiClient.getBrokerAccountsPage(1, 100, filter);
5780
5681
  while (currentResult) {
5781
5682
  allData.push(...currentResult.data);
5782
5683
  if (!currentResult.hasNext)
5783
5684
  break;
5784
- const nextResult = await this.getNextAccountsPage(currentResult);
5685
+ const nextResult = await currentResult.nextPage();
5785
5686
  if (!nextResult)
5786
5687
  break;
5787
5688
  currentResult = nextResult;
@@ -5789,16 +5690,16 @@ class FinaticConnect extends EventEmitter {
5789
5690
  return allData;
5790
5691
  }
5791
5692
  async getAllBalances(filter) {
5792
- if (!this.isAuthed()) {
5693
+ if (!(await this.isAuthenticated())) {
5793
5694
  throw new AuthenticationError('User is not authenticated');
5794
5695
  }
5795
5696
  const allData = [];
5796
- let currentResult = await this.getBalancesPage(1, 100, filter);
5697
+ let currentResult = await this.apiClient.getBrokerBalancesPage(1, 100, filter);
5797
5698
  while (currentResult) {
5798
5699
  allData.push(...currentResult.data);
5799
5700
  if (!currentResult.hasNext)
5800
5701
  break;
5801
- const nextResult = await this.getNextBalancesPage(currentResult);
5702
+ const nextResult = await currentResult.nextPage();
5802
5703
  if (!nextResult)
5803
5704
  break;
5804
5705
  currentResult = nextResult;
@@ -5843,7 +5744,7 @@ class FinaticConnect extends EventEmitter {
5843
5744
  * Validate session for keep-alive purposes and handle automatic refresh
5844
5745
  */
5845
5746
  async validateSessionKeepAlive() {
5846
- if (!this.sessionId || !this.isAuthed()) {
5747
+ if (!this.sessionId || !(await this.isAuthenticated())) {
5847
5748
  console.log('[FinaticConnect] Session keep-alive skipped - no active session');
5848
5749
  return;
5849
5750
  }
@@ -5954,7 +5855,6 @@ class FinaticConnect extends EventEmitter {
5954
5855
  const response = await fetch(`${this.baseUrl}/portal/${sessionId}/complete`, {
5955
5856
  method: 'POST',
5956
5857
  headers: {
5957
- Authorization: `Bearer ${this.userToken?.accessToken || ''}`,
5958
5858
  'Content-Type': 'application/json',
5959
5859
  },
5960
5860
  });
@@ -5977,7 +5877,7 @@ class FinaticConnect extends EventEmitter {
5977
5877
  * @throws AuthenticationError if user is not authenticated
5978
5878
  */
5979
5879
  async disconnectCompany(connectionId) {
5980
- if (!this.isAuthed()) {
5880
+ if (!(await this.isAuthenticated())) {
5981
5881
  throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5982
5882
  }
5983
5883
  if (!this.userToken?.user_id) {
@@ -5985,24 +5885,6 @@ class FinaticConnect extends EventEmitter {
5985
5885
  }
5986
5886
  return this.apiClient.disconnectCompany(connectionId);
5987
5887
  }
5988
- /**
5989
- * Get account balances for the authenticated user
5990
- * @param filters - Optional filters for balances
5991
- * @returns Promise with balance data
5992
- */
5993
- async getBalances(filters) {
5994
- if (!this.isAuthed()) {
5995
- throw new AuthenticationError('User is not authenticated');
5996
- }
5997
- try {
5998
- const response = await this.apiClient.getBalances(filters);
5999
- return response.response_data || [];
6000
- }
6001
- catch (error) {
6002
- this.emit('error', error);
6003
- throw error;
6004
- }
6005
- }
6006
5888
  }
6007
5889
  FinaticConnect.instance = null;
6008
5890
 
@@ -6014,7 +5896,6 @@ exports.BaseError = BaseError;
6014
5896
  exports.CompanyAccessError = CompanyAccessError;
6015
5897
  exports.EventEmitter = EventEmitter;
6016
5898
  exports.FinaticConnect = FinaticConnect;
6017
- exports.MockFactory = MockFactory;
6018
5899
  exports.NetworkError = NetworkError;
6019
5900
  exports.OrderError = OrderError;
6020
5901
  exports.OrderValidationError = OrderValidationError;