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