@finatic/client 0.0.139 → 0.0.141

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 (39) hide show
  1. package/README.md +335 -446
  2. package/dist/index.d.ts +272 -515
  3. package/dist/index.js +531 -449
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +532 -449
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/types/core/client/ApiClient.d.ts +81 -27
  8. package/dist/types/core/client/FinaticConnect.d.ts +53 -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 +117 -1
  14. package/package.json +7 -3
  15. package/src/core/client/ApiClient.ts +1978 -0
  16. package/src/core/client/FinaticConnect.ts +1557 -0
  17. package/src/core/portal/PortalUI.ts +300 -0
  18. package/src/index.d.ts +23 -0
  19. package/src/index.ts +99 -0
  20. package/src/mocks/MockApiClient.ts +1032 -0
  21. package/src/mocks/MockDataProvider.ts +986 -0
  22. package/src/mocks/MockFactory.ts +97 -0
  23. package/src/mocks/utils.ts +133 -0
  24. package/src/themes/portalPresets.ts +1307 -0
  25. package/src/types/api/auth.ts +112 -0
  26. package/src/types/api/broker.ts +461 -0
  27. package/src/types/api/core.ts +53 -0
  28. package/src/types/api/errors.ts +35 -0
  29. package/src/types/api/orders.ts +45 -0
  30. package/src/types/api/portfolio.ts +59 -0
  31. package/src/types/common/pagination.ts +138 -0
  32. package/src/types/connect.ts +56 -0
  33. package/src/types/index.ts +25 -0
  34. package/src/types/portal.ts +214 -0
  35. package/src/types/ui/theme.ts +105 -0
  36. package/src/utils/brokerUtils.ts +85 -0
  37. package/src/utils/errors.ts +104 -0
  38. package/src/utils/events.ts +54 -0
  39. 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
- }
267
+ // Supabase refresh method removed - SDK no longer uses Supabase tokens
290
268
  /**
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
- }
311
- /**
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() {
@@ -1487,6 +1411,155 @@ class ApiClient {
1487
1411
  },
1488
1412
  });
1489
1413
  }
1414
+ /**
1415
+ * Get order fills for a specific order
1416
+ * @param orderId - The order ID
1417
+ * @param filter - Optional filter parameters
1418
+ * @returns Promise with order fills response
1419
+ */
1420
+ async getOrderFills(orderId, filter) {
1421
+ const accessToken = await this.getValidAccessToken();
1422
+ const params = {};
1423
+ if (filter?.connection_id) {
1424
+ params.connection_id = filter.connection_id;
1425
+ }
1426
+ if (filter?.limit) {
1427
+ params.limit = filter.limit.toString();
1428
+ }
1429
+ if (filter?.offset) {
1430
+ params.offset = filter.offset.toString();
1431
+ }
1432
+ return this.request(`/brokers/data/orders/${orderId}/fills`, {
1433
+ method: 'GET',
1434
+ headers: {
1435
+ Authorization: `Bearer ${accessToken}`,
1436
+ },
1437
+ params,
1438
+ });
1439
+ }
1440
+ /**
1441
+ * Get order events for a specific order
1442
+ * @param orderId - The order ID
1443
+ * @param filter - Optional filter parameters
1444
+ * @returns Promise with order events response
1445
+ */
1446
+ async getOrderEvents(orderId, filter) {
1447
+ const accessToken = await this.getValidAccessToken();
1448
+ const params = {};
1449
+ if (filter?.connection_id) {
1450
+ params.connection_id = filter.connection_id;
1451
+ }
1452
+ if (filter?.limit) {
1453
+ params.limit = filter.limit.toString();
1454
+ }
1455
+ if (filter?.offset) {
1456
+ params.offset = filter.offset.toString();
1457
+ }
1458
+ return this.request(`/brokers/data/orders/${orderId}/events`, {
1459
+ method: 'GET',
1460
+ headers: {
1461
+ Authorization: `Bearer ${accessToken}`,
1462
+ },
1463
+ params,
1464
+ });
1465
+ }
1466
+ /**
1467
+ * Get order groups
1468
+ * @param filter - Optional filter parameters
1469
+ * @returns Promise with order groups response
1470
+ */
1471
+ async getOrderGroups(filter) {
1472
+ const accessToken = await this.getValidAccessToken();
1473
+ const params = {};
1474
+ if (filter?.broker_id) {
1475
+ params.broker_id = filter.broker_id;
1476
+ }
1477
+ if (filter?.connection_id) {
1478
+ params.connection_id = filter.connection_id;
1479
+ }
1480
+ if (filter?.limit) {
1481
+ params.limit = filter.limit.toString();
1482
+ }
1483
+ if (filter?.offset) {
1484
+ params.offset = filter.offset.toString();
1485
+ }
1486
+ if (filter?.created_after) {
1487
+ params.created_after = filter.created_after;
1488
+ }
1489
+ if (filter?.created_before) {
1490
+ params.created_before = filter.created_before;
1491
+ }
1492
+ return this.request('/brokers/data/orders/groups', {
1493
+ method: 'GET',
1494
+ headers: {
1495
+ Authorization: `Bearer ${accessToken}`,
1496
+ },
1497
+ params,
1498
+ });
1499
+ }
1500
+ /**
1501
+ * Get position lots (tax lots for positions)
1502
+ * @param filter - Optional filter parameters
1503
+ * @returns Promise with position lots response
1504
+ */
1505
+ async getPositionLots(filter) {
1506
+ const accessToken = await this.getValidAccessToken();
1507
+ const params = {};
1508
+ if (filter?.broker_id) {
1509
+ params.broker_id = filter.broker_id;
1510
+ }
1511
+ if (filter?.connection_id) {
1512
+ params.connection_id = filter.connection_id;
1513
+ }
1514
+ if (filter?.account_id) {
1515
+ params.account_id = filter.account_id;
1516
+ }
1517
+ if (filter?.symbol) {
1518
+ params.symbol = filter.symbol;
1519
+ }
1520
+ if (filter?.position_id) {
1521
+ params.position_id = filter.position_id;
1522
+ }
1523
+ if (filter?.limit) {
1524
+ params.limit = filter.limit.toString();
1525
+ }
1526
+ if (filter?.offset) {
1527
+ params.offset = filter.offset.toString();
1528
+ }
1529
+ return this.request('/brokers/data/positions/lots', {
1530
+ method: 'GET',
1531
+ headers: {
1532
+ Authorization: `Bearer ${accessToken}`,
1533
+ },
1534
+ params,
1535
+ });
1536
+ }
1537
+ /**
1538
+ * Get position lot fills for a specific lot
1539
+ * @param lotId - The position lot ID
1540
+ * @param filter - Optional filter parameters
1541
+ * @returns Promise with position lot fills response
1542
+ */
1543
+ async getPositionLotFills(lotId, filter) {
1544
+ const accessToken = await this.getValidAccessToken();
1545
+ const params = {};
1546
+ if (filter?.connection_id) {
1547
+ params.connection_id = filter.connection_id;
1548
+ }
1549
+ if (filter?.limit) {
1550
+ params.limit = filter.limit.toString();
1551
+ }
1552
+ if (filter?.offset) {
1553
+ params.offset = filter.offset.toString();
1554
+ }
1555
+ return this.request(`/brokers/data/positions/lots/${lotId}/fills`, {
1556
+ method: 'GET',
1557
+ headers: {
1558
+ Authorization: `Bearer ${accessToken}`,
1559
+ },
1560
+ params,
1561
+ });
1562
+ }
1490
1563
  }
1491
1564
 
1492
1565
  class EventEmitter {
@@ -1820,15 +1893,11 @@ class MockDataProvider {
1820
1893
  * Generate mock tokens
1821
1894
  */
1822
1895
  generateTokens(userId) {
1823
- const accessToken = `mock_access_${uuid.v4().replace(/-/g, '')}`;
1824
- const refreshToken = `mock_refresh_${uuid.v4().replace(/-/g, '')}`;
1896
+ `mock_access_${uuid.v4().replace(/-/g, '')}`;
1897
+ `mock_refresh_${uuid.v4().replace(/-/g, '')}`;
1825
1898
  return {
1826
- accessToken,
1827
- refreshToken,
1828
- expiresIn: 3600, // 1 hour
1829
1899
  user_id: userId,
1830
- tokenType: 'Bearer',
1831
- scope: 'read write',
1900
+ // Removed token fields - we no longer use Supabase tokens in the SDK
1832
1901
  };
1833
1902
  }
1834
1903
  // Authentication & Session Management Mocks
@@ -1848,6 +1917,7 @@ class MockDataProvider {
1848
1917
  };
1849
1918
  this.sessionData.set(sessionId, sessionData);
1850
1919
  return {
1920
+ success: true,
1851
1921
  data: sessionData,
1852
1922
  message: 'Session started successfully',
1853
1923
  };
@@ -1869,12 +1939,12 @@ class MockDataProvider {
1869
1939
  success: true,
1870
1940
  message: 'OTP verified successfully',
1871
1941
  data: {
1872
- access_token: tokens.accessToken,
1873
- refresh_token: tokens.refreshToken,
1942
+ access_token: '', // No longer using Supabase tokens
1943
+ refresh_token: '', // No longer using Supabase tokens
1874
1944
  user_id: userId,
1875
- expires_in: tokens.expiresIn,
1876
- scope: tokens.scope,
1877
- token_type: tokens.tokenType,
1945
+ expires_in: 0, // No token expiration for session-based auth
1946
+ scope: 'api:access',
1947
+ token_type: 'Bearer',
1878
1948
  },
1879
1949
  };
1880
1950
  }
@@ -1886,8 +1956,8 @@ class MockDataProvider {
1886
1956
  success: true,
1887
1957
  message: 'Authentication successful',
1888
1958
  data: {
1889
- access_token: tokens.accessToken,
1890
- refresh_token: tokens.refreshToken,
1959
+ access_token: '', // No longer using Supabase tokens
1960
+ refresh_token: '', // No longer using Supabase tokens
1891
1961
  },
1892
1962
  };
1893
1963
  }
@@ -1933,6 +2003,8 @@ class MockDataProvider {
1933
2003
  valid: true,
1934
2004
  company_id: this.generateCompanyId(),
1935
2005
  status: 'active',
2006
+ is_sandbox: false,
2007
+ environment: 'production',
1936
2008
  };
1937
2009
  }
1938
2010
  async mockCompletePortalSession(sessionId) {
@@ -2036,6 +2108,12 @@ class MockDataProvider {
2036
2108
  created_at: new Date().toISOString(),
2037
2109
  updated_at: new Date().toISOString(),
2038
2110
  last_synced_at: new Date().toISOString(),
2111
+ positions_synced_at: new Date().toISOString(),
2112
+ orders_synced_at: new Date().toISOString(),
2113
+ balances_synced_at: new Date().toISOString(),
2114
+ account_created_at: new Date(Date.now() - 365 * 24 * 60 * 60 * 1000).toISOString(), // 1 year ago
2115
+ account_updated_at: new Date().toISOString(),
2116
+ account_first_trade_at: new Date(Date.now() - 300 * 24 * 60 * 60 * 1000).toISOString(), // 300 days ago
2039
2117
  },
2040
2118
  {
2041
2119
  id: uuid.v4(),
@@ -2050,6 +2128,12 @@ class MockDataProvider {
2050
2128
  created_at: new Date().toISOString(),
2051
2129
  updated_at: new Date().toISOString(),
2052
2130
  last_synced_at: new Date().toISOString(),
2131
+ positions_synced_at: new Date().toISOString(),
2132
+ orders_synced_at: new Date().toISOString(),
2133
+ balances_synced_at: new Date().toISOString(),
2134
+ account_created_at: new Date(Date.now() - 730 * 24 * 60 * 60 * 1000).toISOString(), // 2 years ago
2135
+ account_updated_at: new Date().toISOString(),
2136
+ account_first_trade_at: new Date(Date.now() - 700 * 24 * 60 * 60 * 1000).toISOString(), // 700 days ago
2053
2137
  },
2054
2138
  ];
2055
2139
  return {
@@ -2535,6 +2619,12 @@ class MockDataProvider {
2535
2619
  created_at: new Date(Date.now() - Math.random() * 365 * 24 * 60 * 60 * 1000).toISOString(), // Random date within last year
2536
2620
  updated_at: new Date().toISOString(),
2537
2621
  last_synced_at: new Date().toISOString(),
2622
+ positions_synced_at: new Date().toISOString(),
2623
+ orders_synced_at: new Date().toISOString(),
2624
+ balances_synced_at: new Date().toISOString(),
2625
+ account_created_at: new Date(Date.now() - Math.random() * 730 * 24 * 60 * 60 * 1000).toISOString(), // Random date within last 2 years
2626
+ account_updated_at: new Date().toISOString(),
2627
+ account_first_trade_at: new Date(Date.now() - Math.random() * 500 * 24 * 60 * 60 * 1000).toISOString(), // Random date within last 500 days
2538
2628
  });
2539
2629
  }
2540
2630
  return accounts;
@@ -2871,12 +2961,6 @@ class MockApiClient {
2871
2961
  this.tradingContext.accountId = accountId;
2872
2962
  console.log('MockApiClient.setAccount Debug - Updated context:', this.tradingContext);
2873
2963
  }
2874
- getTradingContext() {
2875
- return { ...this.tradingContext };
2876
- }
2877
- clearTradingContext() {
2878
- this.tradingContext = {};
2879
- }
2880
2964
  // Stock convenience methods
2881
2965
  async placeStockMarketOrder(symbol, orderQty, action, broker, accountNumber, extras = {}) {
2882
2966
  return this.placeBrokerOrder({
@@ -2886,7 +2970,7 @@ class MockApiClient {
2886
2970
  orderQty,
2887
2971
  action,
2888
2972
  orderType: 'Market',
2889
- assetType: 'Stock',
2973
+ assetType: 'equity',
2890
2974
  timeInForce: 'day',
2891
2975
  }, extras);
2892
2976
  }
@@ -2898,7 +2982,7 @@ class MockApiClient {
2898
2982
  orderQty,
2899
2983
  action,
2900
2984
  orderType: 'Limit',
2901
- assetType: 'Stock',
2985
+ assetType: 'equity',
2902
2986
  timeInForce,
2903
2987
  price,
2904
2988
  }, extras);
@@ -2911,7 +2995,7 @@ class MockApiClient {
2911
2995
  orderQty,
2912
2996
  action,
2913
2997
  orderType: 'Stop',
2914
- assetType: 'Stock',
2998
+ assetType: 'equity',
2915
2999
  timeInForce,
2916
3000
  stopPrice,
2917
3001
  }, extras);
@@ -2925,7 +3009,7 @@ class MockApiClient {
2925
3009
  orderQty: options.quantity || orderQty,
2926
3010
  action,
2927
3011
  orderType: 'Market',
2928
- assetType: 'Crypto',
3012
+ assetType: 'crypto',
2929
3013
  timeInForce: 'gtc', // Crypto typically uses GTC
2930
3014
  };
2931
3015
  return this.placeBrokerOrder(orderParams, extras);
@@ -2938,7 +3022,7 @@ class MockApiClient {
2938
3022
  orderQty: options.quantity || orderQty,
2939
3023
  action,
2940
3024
  orderType: 'Limit',
2941
- assetType: 'Crypto',
3025
+ assetType: 'crypto',
2942
3026
  timeInForce,
2943
3027
  price,
2944
3028
  };
@@ -2953,7 +3037,7 @@ class MockApiClient {
2953
3037
  orderQty,
2954
3038
  action,
2955
3039
  orderType: 'Market',
2956
- assetType: 'Option',
3040
+ assetType: 'equity_option',
2957
3041
  timeInForce: 'day',
2958
3042
  };
2959
3043
  return this.placeBrokerOrder(orderParams, extras);
@@ -2966,7 +3050,7 @@ class MockApiClient {
2966
3050
  orderQty,
2967
3051
  action,
2968
3052
  orderType: 'Limit',
2969
- assetType: 'Option',
3053
+ assetType: 'equity_option',
2970
3054
  timeInForce,
2971
3055
  price,
2972
3056
  };
@@ -2981,7 +3065,7 @@ class MockApiClient {
2981
3065
  orderQty,
2982
3066
  action,
2983
3067
  orderType: 'Market',
2984
- assetType: 'Future',
3068
+ assetType: 'future',
2985
3069
  timeInForce: 'day',
2986
3070
  }, extras);
2987
3071
  }
@@ -2993,7 +3077,7 @@ class MockApiClient {
2993
3077
  orderQty,
2994
3078
  action,
2995
3079
  orderType: 'Limit',
2996
- assetType: 'Future',
3080
+ assetType: 'future',
2997
3081
  timeInForce,
2998
3082
  price,
2999
3083
  }, extras);
@@ -3207,6 +3291,8 @@ class MockApiClient {
3207
3291
  /**
3208
3292
  * Utility functions for mock system environment detection
3209
3293
  */
3294
+ // Type declarations for Node.js environment
3295
+ // Note: process is already declared globally in Node.js types
3210
3296
  /**
3211
3297
  * Check if mocks should be used based on environment variables
3212
3298
  * Supports both browser and Node.js environments
@@ -4909,95 +4995,161 @@ class FinaticConnect extends EventEmitter {
4909
4995
  // Register automatic session cleanup
4910
4996
  this.registerSessionCleanup();
4911
4997
  }
4912
- handleTokens(tokens) {
4913
- if (!tokens.access_token || !tokens.refresh_token) {
4914
- return;
4998
+ async linkUserToSession(userId) {
4999
+ try {
5000
+ if (!this.sessionId) {
5001
+ console.error('No session ID available for user linking');
5002
+ return false;
5003
+ }
5004
+ // Call API endpoint to authenticate user with session
5005
+ const response = await this.apiClient.request('/session/authenticate', {
5006
+ method: 'POST',
5007
+ body: {
5008
+ session_id: this.sessionId,
5009
+ user_id: userId,
5010
+ },
5011
+ });
5012
+ if (response.error) {
5013
+ console.error('Failed to link user to session:', response.error);
5014
+ return false;
5015
+ }
5016
+ console.log('User linked to session successfully');
5017
+ return true;
5018
+ }
5019
+ catch (error) {
5020
+ console.error('Error linking user to session:', error);
5021
+ return false;
4915
5022
  }
4916
- // Keep existing user_id or use empty string as fallback
4917
- const userId = this.userToken?.user_id || '';
4918
- this.userToken = {
4919
- accessToken: tokens.access_token,
4920
- refreshToken: tokens.refresh_token,
4921
- expiresIn: 3600, // Default to 1 hour if not provided
4922
- user_id: userId,
4923
- tokenType: 'Bearer',
4924
- scope: 'api:access',
4925
- };
4926
- // Store tokens in ApiClient for automatic refresh
4927
- const expiresAt = new Date(Date.now() + 3600 * 1000).toISOString(); // 1 hour from now
4928
- this.apiClient.setTokens(tokens.access_token, tokens.refresh_token, expiresAt, userId);
4929
5023
  }
4930
5024
  /**
4931
- * Check if the user is fully authenticated (has userId, access token, and refresh token)
4932
- * @returns True if the user is fully authenticated and ready for API calls
5025
+ * Store user ID for authentication state persistence
5026
+ * @param userId - The user ID to store
4933
5027
  */
4934
- isAuthed() {
4935
- return !!(this.userToken?.accessToken &&
4936
- this.userToken?.refreshToken &&
4937
- this.userToken?.user_id);
5028
+ storeUserId(userId) {
5029
+ // Initialize userToken if it doesn't exist
5030
+ if (!this.userToken) {
5031
+ this.userToken = {
5032
+ user_id: userId,
5033
+ };
5034
+ }
5035
+ else {
5036
+ // Update existing userToken with new userId
5037
+ this.userToken.user_id = userId;
5038
+ }
5039
+ // Set user ID in ApiClient for session context
5040
+ this.apiClient.setSessionContext(this.sessionId || '', this.companyId, undefined);
4938
5041
  }
4939
5042
  /**
4940
- * Check if the client is authenticated (alias for isAuthed for consistency)
4941
- * @returns True if authenticated, false otherwise
5043
+ * Check if the user is fully authenticated (has userId in session context)
5044
+ * @returns True if the user is fully authenticated and ready for API calls
4942
5045
  */
4943
- is_authenticated() {
4944
- return this.isAuthed();
5046
+ async isAuthenticated() {
5047
+ // Check internal session context only - no localStorage dependency
5048
+ return this.userToken?.user_id !== undefined && this.userToken?.user_id !== null;
4945
5049
  }
4946
5050
  /**
4947
5051
  * Get user's orders with pagination and optional filtering
4948
5052
  * @param params - Query parameters including page, perPage, and filters
4949
5053
  * @returns Promise with paginated result that supports navigation
4950
5054
  */
4951
- async getOrders(params) {
4952
- if (!this.isAuthed()) {
5055
+ async getOrders(page = 1, perPage = 100, options, filters) {
5056
+ if (!(await this.isAuthenticated())) {
4953
5057
  throw new AuthenticationError('User is not authenticated');
4954
5058
  }
4955
- const page = params?.page || 1;
4956
- const perPage = params?.perPage || 100;
4957
- const filter = params?.filter;
4958
- return this.getOrdersPage(page, perPage, filter);
5059
+ const result = await this.apiClient.getBrokerOrdersPage(page, perPage, filters);
5060
+ // Add navigation methods to the result
5061
+ const paginatedResult = result;
5062
+ paginatedResult.next_page = async () => {
5063
+ if (paginatedResult.hasNext) {
5064
+ return this.apiClient.getBrokerOrdersPage(page + 1, perPage, filters);
5065
+ }
5066
+ throw new Error('No next page available');
5067
+ };
5068
+ paginatedResult.previous_page = async () => {
5069
+ if (paginatedResult.has_previous) {
5070
+ return this.apiClient.getBrokerOrdersPage(page - 1, perPage, filters);
5071
+ }
5072
+ throw new Error('No previous page available');
5073
+ };
5074
+ return paginatedResult;
4959
5075
  }
4960
5076
  /**
4961
5077
  * Get user's positions with pagination and optional filtering
4962
5078
  * @param params - Query parameters including page, perPage, and filters
4963
5079
  * @returns Promise with paginated result that supports navigation
4964
5080
  */
4965
- async getPositions(params) {
4966
- if (!this.isAuthed()) {
5081
+ async getPositions(page = 1, perPage = 100, options, filters) {
5082
+ if (!(await this.isAuthenticated())) {
4967
5083
  throw new AuthenticationError('User is not authenticated');
4968
5084
  }
4969
- const page = params?.page || 1;
4970
- const perPage = params?.perPage || 100;
4971
- const filter = params?.filter;
4972
- return this.getPositionsPage(page, perPage, filter);
5085
+ const result = await this.apiClient.getBrokerPositionsPage(page, perPage, filters);
5086
+ // Add navigation methods to the result
5087
+ const paginatedResult = result;
5088
+ paginatedResult.next_page = async () => {
5089
+ if (paginatedResult.hasNext) {
5090
+ return this.apiClient.getBrokerPositionsPage(page + 1, perPage, filters);
5091
+ }
5092
+ throw new Error('No next page available');
5093
+ };
5094
+ paginatedResult.previous_page = async () => {
5095
+ if (paginatedResult.has_previous) {
5096
+ return this.apiClient.getBrokerPositionsPage(page - 1, perPage, filters);
5097
+ }
5098
+ throw new Error('No previous page available');
5099
+ };
5100
+ return paginatedResult;
4973
5101
  }
4974
5102
  /**
4975
5103
  * Get user's accounts with pagination and optional filtering
4976
5104
  * @param params - Query parameters including page, perPage, and filters
4977
5105
  * @returns Promise with paginated result that supports navigation
4978
5106
  */
4979
- async getAccounts(params) {
4980
- if (!this.isAuthed()) {
5107
+ async getAccounts(page = 1, perPage = 100, options, filters) {
5108
+ if (!(await this.isAuthenticated())) {
4981
5109
  throw new AuthenticationError('User is not authenticated');
4982
5110
  }
4983
- const page = params?.page || 1;
4984
- const perPage = params?.perPage || 100;
4985
- const filter = params?.filter;
4986
- return this.getAccountsPage(page, perPage, filter);
5111
+ const result = await this.apiClient.getBrokerAccountsPage(page, perPage, filters);
5112
+ // Add navigation methods to the result
5113
+ const paginatedResult = result;
5114
+ paginatedResult.next_page = async () => {
5115
+ if (paginatedResult.hasNext) {
5116
+ return this.apiClient.getBrokerAccountsPage(page + 1, perPage, filters);
5117
+ }
5118
+ throw new Error('No next page available');
5119
+ };
5120
+ paginatedResult.previous_page = async () => {
5121
+ if (paginatedResult.has_previous) {
5122
+ return this.apiClient.getBrokerAccountsPage(page - 1, perPage, filters);
5123
+ }
5124
+ throw new Error('No previous page available');
5125
+ };
5126
+ return paginatedResult;
4987
5127
  }
4988
5128
  /**
4989
5129
  * Get user's balances with pagination and optional filtering
4990
5130
  * @param params - Query parameters including page, perPage, and filters
4991
5131
  * @returns Promise with paginated result that supports navigation
4992
5132
  */
4993
- async getBalances(params) {
4994
- if (!this.isAuthed()) {
5133
+ async getBalances(page = 1, perPage = 100, options, filters) {
5134
+ if (!(await this.isAuthenticated())) {
4995
5135
  throw new AuthenticationError('User is not authenticated');
4996
5136
  }
4997
- const page = params?.page || 1;
4998
- const perPage = params?.perPage || 100;
4999
- const filter = params?.filter;
5000
- return this.getBalancesPage(page, perPage, filter);
5137
+ const result = await this.apiClient.getBrokerBalancesPage(page, perPage, filters);
5138
+ // Add navigation methods to the result
5139
+ const paginatedResult = result;
5140
+ paginatedResult.next_page = async () => {
5141
+ if (paginatedResult.hasNext) {
5142
+ return this.apiClient.getBrokerBalancesPage(page + 1, perPage, filters);
5143
+ }
5144
+ throw new Error('No next page available');
5145
+ };
5146
+ paginatedResult.previous_page = async () => {
5147
+ if (paginatedResult.has_previous) {
5148
+ return this.apiClient.getBrokerBalancesPage(page - 1, perPage, filters);
5149
+ }
5150
+ throw new Error('No previous page available');
5151
+ };
5152
+ return paginatedResult;
5001
5153
  }
5002
5154
  /**
5003
5155
  * Initialize the Finatic Connect SDK
@@ -5050,26 +5202,20 @@ class FinaticConnect extends EventEmitter {
5050
5202
  FinaticConnect.instance.apiClient.setSessionContext(FinaticConnect.instance.sessionId, FinaticConnect.instance.companyId, startResponse.data.csrf_token // If available in response
5051
5203
  );
5052
5204
  }
5053
- // If userId is provided, authenticate directly
5205
+ // If userId is provided, try to link user to session
5054
5206
  if (normalizedUserId) {
5055
5207
  try {
5056
- const authResponse = await FinaticConnect.instance.apiClient.authenticateDirectly(startResponse.data.session_id, normalizedUserId);
5057
- // Convert API response to UserToken format
5058
- const userToken = {
5059
- accessToken: authResponse.data.access_token,
5060
- refreshToken: authResponse.data.refresh_token,
5061
- expiresIn: 3600, // Default to 1 hour
5062
- user_id: normalizedUserId,
5063
- tokenType: 'Bearer',
5064
- scope: 'api:access',
5065
- };
5066
- // Set the tokens in both FinaticConnect and ApiClient
5067
- FinaticConnect.instance.userToken = userToken;
5068
- // Set tokens in ApiClient for automatic token management
5069
- const expiresAt = new Date(Date.now() + 3600 * 1000).toISOString(); // 1 hour from now
5070
- FinaticConnect.instance.apiClient.setTokens(authResponse.data.access_token, authResponse.data.refresh_token, expiresAt, normalizedUserId);
5071
- // Emit success event
5072
- FinaticConnect.instance.emit('success', normalizedUserId);
5208
+ // Try to link user to session via API
5209
+ const linked = await FinaticConnect.instance.linkUserToSession(normalizedUserId);
5210
+ if (linked) {
5211
+ // Store user ID for authentication state
5212
+ FinaticConnect.instance.storeUserId(normalizedUserId);
5213
+ // Emit success event
5214
+ FinaticConnect.instance.emit('success', normalizedUserId);
5215
+ }
5216
+ else {
5217
+ console.warn('Failed to link user to session during initialization');
5218
+ }
5073
5219
  }
5074
5220
  catch (error) {
5075
5221
  FinaticConnect.instance.emit('error', error);
@@ -5090,38 +5236,25 @@ class FinaticConnect extends EventEmitter {
5090
5236
  * Get the user and tokens for a completed session
5091
5237
  * @returns Promise with user information and tokens
5092
5238
  */
5093
- async getSessionUser() {
5094
- if (!this.isAuthed()) {
5095
- throw new AuthenticationError('User is not authenticated');
5096
- }
5097
- if (!this.userToken) {
5098
- throw new AuthenticationError('No user token available');
5099
- }
5100
- return {
5101
- user_id: this.userToken.userId,
5102
- access_token: this.userToken.accessToken,
5103
- refresh_token: this.userToken.refreshToken,
5104
- expires_in: this.userToken.expiresIn,
5105
- token_type: this.userToken.tokenType,
5106
- scope: this.userToken.scope,
5107
- company_id: this.companyId,
5108
- };
5109
- }
5110
5239
  async initializeWithUser(userId) {
5111
5240
  try {
5112
5241
  if (!this.sessionId) {
5113
5242
  throw new SessionError('Session not initialized');
5114
5243
  }
5115
- this.userToken = await this.apiClient.getUserToken(this.sessionId);
5116
- // Set tokens in ApiClient for automatic token management
5117
- if (this.userToken) {
5118
- const expiresAt = new Date(Date.now() + 3600 * 1000).toISOString(); // 1 hour from now
5119
- this.apiClient.setTokens(this.userToken.accessToken, this.userToken.refreshToken, expiresAt, userId);
5244
+ // Try to link user to session
5245
+ const linked = await this.linkUserToSession(userId);
5246
+ if (!linked) {
5247
+ console.warn('Failed to link user to session during initialization');
5248
+ // Don't throw error, just continue without authentication
5249
+ return;
5120
5250
  }
5251
+ // Store user ID for authentication state
5252
+ this.storeUserId(userId);
5121
5253
  this.emit('success', userId);
5122
5254
  }
5123
5255
  catch (error) {
5124
5256
  this.emit('error', error);
5257
+ throw error;
5125
5258
  }
5126
5259
  }
5127
5260
  /**
@@ -5174,20 +5307,32 @@ class FinaticConnect extends EventEmitter {
5174
5307
  url.searchParams.set('email', options.email);
5175
5308
  themedPortalUrl = url.toString();
5176
5309
  }
5310
+ // Add session ID to portal URL so the portal can use it
5311
+ const url = new URL(themedPortalUrl);
5312
+ if (this.sessionId) {
5313
+ url.searchParams.set('session_id', this.sessionId);
5314
+ }
5315
+ if (this.companyId) {
5316
+ url.searchParams.set('company_id', this.companyId);
5317
+ }
5318
+ themedPortalUrl = url.toString();
5177
5319
  // Create portal UI if not exists
5178
5320
  if (!this.portalUI) {
5179
5321
  this.portalUI = new PortalUI(this.baseUrl);
5180
5322
  }
5181
5323
  // Show portal
5182
5324
  this.portalUI.show(themedPortalUrl, this.sessionId || '', {
5183
- onSuccess: async (userId, tokens) => {
5325
+ onSuccess: async (userId) => {
5184
5326
  try {
5185
5327
  if (!this.sessionId) {
5186
5328
  throw new SessionError('Session not initialized');
5187
5329
  }
5188
- // Handle tokens if provided
5189
- if (tokens?.access_token && tokens?.refresh_token) {
5190
- this.handleTokens(tokens);
5330
+ // Store the userId for authentication state
5331
+ this.storeUserId(userId);
5332
+ // Try to link user to session via API
5333
+ const linked = await this.linkUserToSession(userId);
5334
+ if (!linked) {
5335
+ console.warn('Failed to link user to session, but continuing with authentication');
5191
5336
  }
5192
5337
  // Emit portal success event
5193
5338
  this.emit('portal:success', userId);
@@ -5278,32 +5423,16 @@ class FinaticConnect extends EventEmitter {
5278
5423
  * Place a new order using the broker order API
5279
5424
  * @param order - Order details with broker context
5280
5425
  */
5281
- async placeOrder(order) {
5282
- if (!this.userToken) {
5283
- throw new Error('Not initialized with user');
5426
+ async placeOrder(order, extras) {
5427
+ if (!(await this.isAuthenticated())) {
5428
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5429
+ }
5430
+ if (!this.userToken?.user_id) {
5431
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5284
5432
  }
5285
5433
  try {
5286
- // Convert order format to match broker API
5287
- const brokerOrder = {
5288
- symbol: order.symbol,
5289
- orderQty: order.quantity,
5290
- action: order.side === 'buy' ? 'Buy' : 'Sell',
5291
- orderType: order.orderType === 'market'
5292
- ? 'Market'
5293
- : order.orderType === 'limit'
5294
- ? 'Limit'
5295
- : order.orderType === 'stop'
5296
- ? 'Stop'
5297
- : 'StopLimit',
5298
- assetType: order.assetType || 'Stock',
5299
- timeInForce: order.timeInForce,
5300
- price: order.price,
5301
- stopPrice: order.stopPrice,
5302
- broker: order.broker,
5303
- accountNumber: order.accountNumber,
5304
- order_id: order.order_id,
5305
- };
5306
- return await this.apiClient.placeBrokerOrder(this.userToken.accessToken, brokerOrder, {}, order.connection_id);
5434
+ // Use the order parameter directly since it's already BrokerOrderParams
5435
+ return await this.apiClient.placeBrokerOrder(order, extras || {}, order.connection_id);
5307
5436
  }
5308
5437
  catch (error) {
5309
5438
  this.emit('error', error);
@@ -5317,8 +5446,11 @@ class FinaticConnect extends EventEmitter {
5317
5446
  * @param connection_id - Optional connection ID for testing bypass
5318
5447
  */
5319
5448
  async cancelOrder(orderId, broker, connection_id) {
5320
- if (!this.userToken) {
5321
- throw new Error('Not initialized with user');
5449
+ if (!(await this.isAuthenticated())) {
5450
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5451
+ }
5452
+ if (!this.userToken?.user_id) {
5453
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5322
5454
  }
5323
5455
  try {
5324
5456
  return await this.apiClient.cancelBrokerOrder(orderId, broker, {}, connection_id);
@@ -5336,8 +5468,11 @@ class FinaticConnect extends EventEmitter {
5336
5468
  * @param connection_id - Optional connection ID for testing bypass
5337
5469
  */
5338
5470
  async modifyOrder(orderId, modifications, broker, connection_id) {
5339
- if (!this.userToken) {
5340
- throw new Error('Not initialized with user');
5471
+ if (!(await this.isAuthenticated())) {
5472
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5473
+ }
5474
+ if (!this.userToken?.user_id) {
5475
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5341
5476
  }
5342
5477
  try {
5343
5478
  // Convert modifications to broker format
@@ -5365,39 +5500,15 @@ class FinaticConnect extends EventEmitter {
5365
5500
  throw error;
5366
5501
  }
5367
5502
  }
5368
- /**
5369
- * Set the broker context for trading
5370
- * @param broker - The broker to use for trading
5371
- */
5372
- setTradingContextBroker(broker) {
5373
- this.apiClient.setBroker(broker);
5374
- }
5375
- /**
5376
- * Set the account context for trading
5377
- * @param accountNumber - The account number to use for trading
5378
- * @param accountId - Optional account ID
5379
- */
5380
- setTradingContextAccount(accountNumber, accountId) {
5381
- this.apiClient.setAccount(accountNumber, accountId);
5382
- }
5383
- /**
5384
- * Get the current trading context
5385
- */
5386
- getTradingContext() {
5387
- return this.apiClient.getTradingContext();
5388
- }
5389
- /**
5390
- * Clear the trading context
5391
- */
5392
- clearTradingContext() {
5393
- this.apiClient.clearTradingContext();
5394
- }
5395
5503
  /**
5396
5504
  * Place a stock market order (convenience method)
5397
5505
  */
5398
5506
  async placeStockMarketOrder(symbol, quantity, side, broker, accountNumber) {
5399
- if (!this.userToken) {
5400
- throw new Error('Not initialized with user');
5507
+ if (!(await this.isAuthenticated())) {
5508
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5509
+ }
5510
+ if (!this.userToken?.user_id) {
5511
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5401
5512
  }
5402
5513
  try {
5403
5514
  return await this.apiClient.placeStockMarketOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', broker, accountNumber);
@@ -5411,8 +5522,11 @@ class FinaticConnect extends EventEmitter {
5411
5522
  * Place a stock limit order (convenience method)
5412
5523
  */
5413
5524
  async placeStockLimitOrder(symbol, quantity, side, price, timeInForce = 'gtc', broker, accountNumber) {
5414
- if (!this.userToken) {
5415
- throw new Error('Not initialized with user');
5525
+ if (!(await this.isAuthenticated())) {
5526
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5527
+ }
5528
+ if (!this.userToken?.user_id) {
5529
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5416
5530
  }
5417
5531
  try {
5418
5532
  return await this.apiClient.placeStockLimitOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', price, timeInForce, broker, accountNumber);
@@ -5426,8 +5540,11 @@ class FinaticConnect extends EventEmitter {
5426
5540
  * Place a stock stop order (convenience method)
5427
5541
  */
5428
5542
  async placeStockStopOrder(symbol, quantity, side, stopPrice, timeInForce = 'gtc', broker, accountNumber) {
5429
- if (!this.userToken) {
5430
- throw new Error('Not initialized with user');
5543
+ if (!(await this.isAuthenticated())) {
5544
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5545
+ }
5546
+ if (!this.userToken?.user_id) {
5547
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5431
5548
  }
5432
5549
  try {
5433
5550
  return await this.apiClient.placeStockStopOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', stopPrice, timeInForce, broker, accountNumber);
@@ -5441,8 +5558,11 @@ class FinaticConnect extends EventEmitter {
5441
5558
  * Place a crypto market order (convenience method)
5442
5559
  */
5443
5560
  async placeCryptoMarketOrder(symbol, quantity, side, broker, accountNumber) {
5444
- if (!this.userToken) {
5445
- throw new Error('Not initialized with user');
5561
+ if (!(await this.isAuthenticated())) {
5562
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5563
+ }
5564
+ if (!this.userToken?.user_id) {
5565
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5446
5566
  }
5447
5567
  try {
5448
5568
  return await this.apiClient.placeCryptoMarketOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', broker, accountNumber);
@@ -5456,8 +5576,11 @@ class FinaticConnect extends EventEmitter {
5456
5576
  * Place a crypto limit order (convenience method)
5457
5577
  */
5458
5578
  async placeCryptoLimitOrder(symbol, quantity, side, price, timeInForce = 'gtc', broker, accountNumber) {
5459
- if (!this.userToken) {
5460
- throw new Error('Not initialized with user');
5579
+ if (!(await this.isAuthenticated())) {
5580
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5581
+ }
5582
+ if (!this.userToken?.user_id) {
5583
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5461
5584
  }
5462
5585
  try {
5463
5586
  return await this.apiClient.placeCryptoLimitOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', price, timeInForce, broker, accountNumber);
@@ -5471,8 +5594,11 @@ class FinaticConnect extends EventEmitter {
5471
5594
  * Place an options market order (convenience method)
5472
5595
  */
5473
5596
  async placeOptionsMarketOrder(symbol, quantity, side, broker, accountNumber) {
5474
- if (!this.userToken) {
5475
- throw new Error('Not initialized with user');
5597
+ if (!(await this.isAuthenticated())) {
5598
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5599
+ }
5600
+ if (!this.userToken?.user_id) {
5601
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5476
5602
  }
5477
5603
  try {
5478
5604
  return await this.apiClient.placeOptionsMarketOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', broker, accountNumber);
@@ -5486,8 +5612,11 @@ class FinaticConnect extends EventEmitter {
5486
5612
  * Place an options limit order (convenience method)
5487
5613
  */
5488
5614
  async placeOptionsLimitOrder(symbol, quantity, side, price, timeInForce = 'gtc', broker, accountNumber) {
5489
- if (!this.userToken) {
5490
- throw new Error('Not initialized with user');
5615
+ if (!(await this.isAuthenticated())) {
5616
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5617
+ }
5618
+ if (!this.userToken?.user_id) {
5619
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5491
5620
  }
5492
5621
  try {
5493
5622
  return await this.apiClient.placeOptionsLimitOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', price, timeInForce, broker, accountNumber);
@@ -5501,8 +5630,11 @@ class FinaticConnect extends EventEmitter {
5501
5630
  * Place a futures market order (convenience method)
5502
5631
  */
5503
5632
  async placeFuturesMarketOrder(symbol, quantity, side, broker, accountNumber) {
5504
- if (!this.userToken) {
5505
- throw new Error('Not initialized with user');
5633
+ if (!(await this.isAuthenticated())) {
5634
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5635
+ }
5636
+ if (!this.userToken?.user_id) {
5637
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5506
5638
  }
5507
5639
  try {
5508
5640
  return await this.apiClient.placeFuturesMarketOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', broker, accountNumber);
@@ -5516,8 +5648,11 @@ class FinaticConnect extends EventEmitter {
5516
5648
  * Place a futures limit order (convenience method)
5517
5649
  */
5518
5650
  async placeFuturesLimitOrder(symbol, quantity, side, price, timeInForce = 'gtc', broker, accountNumber) {
5519
- if (!this.userToken) {
5520
- throw new Error('Not initialized with user');
5651
+ if (!(await this.isAuthenticated())) {
5652
+ throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5653
+ }
5654
+ if (!this.userToken?.user_id) {
5655
+ throw new AuthenticationError('No user ID available. Please connect a broker first.');
5521
5656
  }
5522
5657
  try {
5523
5658
  return await this.apiClient.placeFuturesLimitOrder(symbol, quantity, side === 'buy' ? 'Buy' : 'Sell', price, timeInForce, broker, accountNumber);
@@ -5532,8 +5667,8 @@ class FinaticConnect extends EventEmitter {
5532
5667
  * @returns The current user ID or undefined if not authenticated
5533
5668
  * @throws AuthenticationError if user is not authenticated
5534
5669
  */
5535
- getUserId() {
5536
- if (!this.isAuthed()) {
5670
+ async getUserId() {
5671
+ if (!(await this.isAuthenticated())) {
5537
5672
  return null;
5538
5673
  }
5539
5674
  if (!this.userToken?.user_id) {
@@ -5546,7 +5681,7 @@ class FinaticConnect extends EventEmitter {
5546
5681
  * @returns Promise with array of broker information
5547
5682
  */
5548
5683
  async getBrokerList() {
5549
- // if (!this.isAuthed()) {
5684
+ // if (!this.isAuthenticated()) {
5550
5685
  // throw new AuthenticationError('Not authenticated');
5551
5686
  // }
5552
5687
  const response = await this.apiClient.getBrokerList();
@@ -5563,7 +5698,7 @@ class FinaticConnect extends EventEmitter {
5563
5698
  * @throws AuthenticationError if user is not authenticated
5564
5699
  */
5565
5700
  async getBrokerConnections() {
5566
- if (!this.isAuthed()) {
5701
+ if (!(await this.isAuthenticated())) {
5567
5702
  throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5568
5703
  }
5569
5704
  if (!this.userToken?.user_id) {
@@ -5637,118 +5772,22 @@ class FinaticConnect extends EventEmitter {
5637
5772
  return this.getAllPositions({ broker_id: brokerId });
5638
5773
  }
5639
5774
  // Pagination methods
5640
- /**
5641
- * Get a specific page of orders 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 orders result
5646
- */
5647
- async getOrdersPage(page = 1, perPage = 100, filter) {
5648
- if (!this.isAuthed()) {
5649
- throw new AuthenticationError('User is not authenticated');
5650
- }
5651
- return this.apiClient.getBrokerOrdersPage(page, perPage, filter);
5652
- }
5653
- /**
5654
- * Get a specific page of positions 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 positions result
5659
- */
5660
- async getPositionsPage(page = 1, perPage = 100, filter) {
5661
- if (!this.isAuthed()) {
5662
- throw new AuthenticationError('User is not authenticated');
5663
- }
5664
- return this.apiClient.getBrokerPositionsPage(page, perPage, filter);
5665
- }
5666
- /**
5667
- * Get a specific page of accounts with pagination metadata
5668
- * @param page - Page number (default: 1)
5669
- * @param perPage - Items per page (default: 100)
5670
- * @param filter - Optional filter parameters
5671
- * @returns Promise with paginated accounts result
5672
- */
5673
- async getAccountsPage(page = 1, perPage = 100, filter) {
5674
- if (!this.isAuthed()) {
5675
- throw new AuthenticationError('User is not authenticated');
5676
- }
5677
- return this.apiClient.getBrokerAccountsPage(page, perPage, filter);
5678
- }
5679
- async getBalancesPage(page = 1, perPage = 100, filter) {
5680
- if (!this.isAuthed()) {
5681
- throw new AuthenticationError('User is not authenticated');
5682
- }
5683
- return this.apiClient.getBrokerBalancesPage(page, perPage, filter);
5684
- }
5685
- /**
5686
- * Get the next page of orders
5687
- * @param previousResult - The previous paginated result
5688
- * @returns Promise with next page of orders or null if no more pages
5689
- */
5690
- async getNextOrdersPage(previousResult) {
5691
- if (!this.isAuthed()) {
5692
- throw new AuthenticationError('User is not authenticated');
5693
- }
5694
- return this.apiClient.getNextPage(previousResult, (offset, limit) => {
5695
- const page = Math.floor(offset / limit) + 1;
5696
- return this.apiClient.getBrokerOrdersPage(page, limit);
5697
- });
5698
- }
5699
- /**
5700
- * Get the next page of positions
5701
- * @param previousResult - The previous paginated result
5702
- * @returns Promise with next page of positions or null if no more pages
5703
- */
5704
- async getNextPositionsPage(previousResult) {
5705
- if (!this.isAuthed()) {
5706
- throw new AuthenticationError('User is not authenticated');
5707
- }
5708
- return this.apiClient.getNextPage(previousResult, (offset, limit) => {
5709
- const page = Math.floor(offset / limit) + 1;
5710
- return this.apiClient.getBrokerPositionsPage(page, limit);
5711
- });
5712
- }
5713
- /**
5714
- * Get the next page of accounts
5715
- * @param previousResult - The previous paginated result
5716
- * @returns Promise with next page of accounts or null if no more pages
5717
- */
5718
- async getNextAccountsPage(previousResult) {
5719
- if (!this.isAuthed()) {
5720
- throw new AuthenticationError('User is not authenticated');
5721
- }
5722
- return this.apiClient.getNextPage(previousResult, (offset, limit) => {
5723
- const page = Math.floor(offset / limit) + 1;
5724
- return this.apiClient.getBrokerAccountsPage(page, limit);
5725
- });
5726
- }
5727
- async getNextBalancesPage(previousResult) {
5728
- if (!this.isAuthed()) {
5729
- throw new AuthenticationError('User is not authenticated');
5730
- }
5731
- return this.apiClient.getNextPage(previousResult, (offset, limit) => {
5732
- const page = Math.floor(offset / limit) + 1;
5733
- return this.apiClient.getBrokerBalancesPage(page, limit);
5734
- });
5735
- }
5736
5775
  /**
5737
5776
  * Get all orders across all pages (convenience method)
5738
5777
  * @param filter - Optional filter parameters
5739
5778
  * @returns Promise with all orders
5740
5779
  */
5741
5780
  async getAllOrders(filter) {
5742
- if (!this.isAuthed()) {
5781
+ if (!(await this.isAuthenticated())) {
5743
5782
  throw new AuthenticationError('User is not authenticated');
5744
5783
  }
5745
5784
  const allData = [];
5746
- let currentResult = await this.getOrdersPage(1, 100, filter);
5785
+ let currentResult = await this.apiClient.getBrokerOrdersPage(1, 100, filter);
5747
5786
  while (currentResult) {
5748
5787
  allData.push(...currentResult.data);
5749
5788
  if (!currentResult.hasNext)
5750
5789
  break;
5751
- const nextResult = await this.getNextOrdersPage(currentResult);
5790
+ const nextResult = await currentResult.nextPage();
5752
5791
  if (!nextResult)
5753
5792
  break;
5754
5793
  currentResult = nextResult;
@@ -5761,16 +5800,16 @@ class FinaticConnect extends EventEmitter {
5761
5800
  * @returns Promise with all positions
5762
5801
  */
5763
5802
  async getAllPositions(filter) {
5764
- if (!this.isAuthed()) {
5803
+ if (!(await this.isAuthenticated())) {
5765
5804
  throw new AuthenticationError('User is not authenticated');
5766
5805
  }
5767
5806
  const allData = [];
5768
- let currentResult = await this.getPositionsPage(1, 100, filter);
5807
+ let currentResult = await this.apiClient.getBrokerPositionsPage(1, 100, filter);
5769
5808
  while (currentResult) {
5770
5809
  allData.push(...currentResult.data);
5771
5810
  if (!currentResult.hasNext)
5772
5811
  break;
5773
- const nextResult = await this.getNextPositionsPage(currentResult);
5812
+ const nextResult = await currentResult.nextPage();
5774
5813
  if (!nextResult)
5775
5814
  break;
5776
5815
  currentResult = nextResult;
@@ -5783,16 +5822,16 @@ class FinaticConnect extends EventEmitter {
5783
5822
  * @returns Promise with all accounts
5784
5823
  */
5785
5824
  async getAllAccounts(filter) {
5786
- if (!this.isAuthed()) {
5825
+ if (!(await this.isAuthenticated())) {
5787
5826
  throw new AuthenticationError('User is not authenticated');
5788
5827
  }
5789
5828
  const allData = [];
5790
- let currentResult = await this.getAccountsPage(1, 100, filter);
5829
+ let currentResult = await this.apiClient.getBrokerAccountsPage(1, 100, filter);
5791
5830
  while (currentResult) {
5792
5831
  allData.push(...currentResult.data);
5793
5832
  if (!currentResult.hasNext)
5794
5833
  break;
5795
- const nextResult = await this.getNextAccountsPage(currentResult);
5834
+ const nextResult = await currentResult.nextPage();
5796
5835
  if (!nextResult)
5797
5836
  break;
5798
5837
  currentResult = nextResult;
@@ -5800,16 +5839,16 @@ class FinaticConnect extends EventEmitter {
5800
5839
  return allData;
5801
5840
  }
5802
5841
  async getAllBalances(filter) {
5803
- if (!this.isAuthed()) {
5842
+ if (!(await this.isAuthenticated())) {
5804
5843
  throw new AuthenticationError('User is not authenticated');
5805
5844
  }
5806
5845
  const allData = [];
5807
- let currentResult = await this.getBalancesPage(1, 100, filter);
5846
+ let currentResult = await this.apiClient.getBrokerBalancesPage(1, 100, filter);
5808
5847
  while (currentResult) {
5809
5848
  allData.push(...currentResult.data);
5810
5849
  if (!currentResult.hasNext)
5811
5850
  break;
5812
- const nextResult = await this.getNextBalancesPage(currentResult);
5851
+ const nextResult = await currentResult.nextPage();
5813
5852
  if (!nextResult)
5814
5853
  break;
5815
5854
  currentResult = nextResult;
@@ -5854,7 +5893,7 @@ class FinaticConnect extends EventEmitter {
5854
5893
  * Validate session for keep-alive purposes and handle automatic refresh
5855
5894
  */
5856
5895
  async validateSessionKeepAlive() {
5857
- if (!this.sessionId || !this.isAuthed()) {
5896
+ if (!this.sessionId || !(await this.isAuthenticated())) {
5858
5897
  console.log('[FinaticConnect] Session keep-alive skipped - no active session');
5859
5898
  return;
5860
5899
  }
@@ -5965,7 +6004,6 @@ class FinaticConnect extends EventEmitter {
5965
6004
  const response = await fetch(`${this.baseUrl}/portal/${sessionId}/complete`, {
5966
6005
  method: 'POST',
5967
6006
  headers: {
5968
- Authorization: `Bearer ${this.userToken?.accessToken || ''}`,
5969
6007
  'Content-Type': 'application/json',
5970
6008
  },
5971
6009
  });
@@ -5988,7 +6026,7 @@ class FinaticConnect extends EventEmitter {
5988
6026
  * @throws AuthenticationError if user is not authenticated
5989
6027
  */
5990
6028
  async disconnectCompany(connectionId) {
5991
- if (!this.isAuthed()) {
6029
+ if (!(await this.isAuthenticated())) {
5992
6030
  throw new AuthenticationError('User is not authenticated. Please connect a broker first.');
5993
6031
  }
5994
6032
  if (!this.userToken?.user_id) {
@@ -5997,22 +6035,67 @@ class FinaticConnect extends EventEmitter {
5997
6035
  return this.apiClient.disconnectCompany(connectionId);
5998
6036
  }
5999
6037
  /**
6000
- * Get account balances for the authenticated user
6001
- * @param filters - Optional filters for balances
6002
- * @returns Promise with balance data
6038
+ * Get order fills for a specific order
6039
+ * @param orderId - The order ID
6040
+ * @param filter - Optional filter parameters
6041
+ * @returns Promise with order fills response
6003
6042
  */
6004
- async getBalances(filters) {
6005
- if (!this.isAuthed()) {
6043
+ async getOrderFills(orderId, filter) {
6044
+ if (!(await this.isAuthenticated())) {
6006
6045
  throw new AuthenticationError('User is not authenticated');
6007
6046
  }
6008
- try {
6009
- const response = await this.apiClient.getBalances(filters);
6010
- return response.response_data || [];
6047
+ const response = await this.apiClient.getOrderFills(orderId, filter);
6048
+ return response.response_data;
6049
+ }
6050
+ /**
6051
+ * Get order events for a specific order
6052
+ * @param orderId - The order ID
6053
+ * @param filter - Optional filter parameters
6054
+ * @returns Promise with order events response
6055
+ */
6056
+ async getOrderEvents(orderId, filter) {
6057
+ if (!(await this.isAuthenticated())) {
6058
+ throw new AuthenticationError('User is not authenticated');
6011
6059
  }
6012
- catch (error) {
6013
- this.emit('error', error);
6014
- throw error;
6060
+ const response = await this.apiClient.getOrderEvents(orderId, filter);
6061
+ return response.response_data;
6062
+ }
6063
+ /**
6064
+ * Get order groups
6065
+ * @param filter - Optional filter parameters
6066
+ * @returns Promise with order groups response
6067
+ */
6068
+ async getOrderGroups(filter) {
6069
+ if (!(await this.isAuthenticated())) {
6070
+ throw new AuthenticationError('User is not authenticated');
6015
6071
  }
6072
+ const response = await this.apiClient.getOrderGroups(filter);
6073
+ return response.response_data;
6074
+ }
6075
+ /**
6076
+ * Get position lots (tax lots for positions)
6077
+ * @param filter - Optional filter parameters
6078
+ * @returns Promise with position lots response
6079
+ */
6080
+ async getPositionLots(filter) {
6081
+ if (!(await this.isAuthenticated())) {
6082
+ throw new AuthenticationError('User is not authenticated');
6083
+ }
6084
+ const response = await this.apiClient.getPositionLots(filter);
6085
+ return response.response_data;
6086
+ }
6087
+ /**
6088
+ * Get position lot fills for a specific lot
6089
+ * @param lotId - The position lot ID
6090
+ * @param filter - Optional filter parameters
6091
+ * @returns Promise with position lot fills response
6092
+ */
6093
+ async getPositionLotFills(lotId, filter) {
6094
+ if (!(await this.isAuthenticated())) {
6095
+ throw new AuthenticationError('User is not authenticated');
6096
+ }
6097
+ const response = await this.apiClient.getPositionLotFills(lotId, filter);
6098
+ return response.response_data;
6016
6099
  }
6017
6100
  }
6018
6101
  FinaticConnect.instance = null;
@@ -6025,7 +6108,6 @@ exports.BaseError = BaseError;
6025
6108
  exports.CompanyAccessError = CompanyAccessError;
6026
6109
  exports.EventEmitter = EventEmitter;
6027
6110
  exports.FinaticConnect = FinaticConnect;
6028
- exports.MockFactory = MockFactory;
6029
6111
  exports.NetworkError = NetworkError;
6030
6112
  exports.OrderError = OrderError;
6031
6113
  exports.OrderValidationError = OrderValidationError;