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