@finatic/client 0.0.140 → 0.0.142

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.
@@ -16,11 +16,22 @@ import {
16
16
  PositionsFilter,
17
17
  AccountsFilter,
18
18
  BalancesFilter,
19
+ OrderFill,
20
+ OrderEvent,
21
+ OrderGroup,
22
+ PositionLot,
23
+ PositionLotFill,
24
+ OrderFillsFilter,
25
+ OrderEventsFilter,
26
+ OrderGroupsFilter,
27
+ PositionLotsFilter,
28
+ PositionLotFillsFilter,
19
29
  } from '../../types/api/broker';
20
30
  import { TradingContext } from '../../types/api/orders';
21
31
  import { ApiPaginationInfo, PaginatedResult } from '../../types/common/pagination';
22
32
  import { ApiResponse } from '../../types/api/core';
23
33
  import { PortalUrlResponse } from '../../types/api/core';
34
+ import { setupLogger, buildLoggerExtra, LoggerExtra } from '../../lib/logger';
24
35
  import {
25
36
  DeviceInfo,
26
37
  SessionState,
@@ -60,6 +71,17 @@ export class ApiClient {
60
71
  // Session and company context
61
72
  private companyId: string | null = null;
62
73
  private csrfToken: string | null = null;
74
+ private readonly logger = setupLogger('FinaticClientSDK.ApiClient', undefined, {
75
+ codebase: 'FinaticClientSDK',
76
+ });
77
+
78
+ private buildLoggerExtra(functionName: string, metadata?: Record<string, unknown>): LoggerExtra {
79
+ return {
80
+ module: 'ApiClient',
81
+ function: functionName,
82
+ ...(metadata ? buildLoggerExtra(metadata) : {}),
83
+ };
84
+ }
63
85
 
64
86
  constructor(baseUrl: string, deviceInfo?: DeviceInfo) {
65
87
  this.baseUrl = baseUrl;
@@ -213,15 +235,11 @@ export class ApiClient {
213
235
  }
214
236
  });
215
237
 
216
- // Debug logging for development
217
- if (typeof window !== 'undefined' && window.location.hostname === 'localhost') {
218
- console.log('Request to:', url.toString());
219
- console.log('Safari-safe headers:', safariSafeHeaders);
220
- console.log('Browser:', navigator.userAgent);
221
- console.log('Session ID:', this.currentSessionId);
222
- console.log('Company ID:', this.companyId);
223
- console.log('CSRF Token:', this.csrfToken);
224
- }
238
+ this.logger.debug('Dispatching API request', this.buildLoggerExtra('request', {
239
+ url: url.toString(),
240
+ method: options.method,
241
+ has_body: Boolean(options.body),
242
+ }));
225
243
 
226
244
  const response = await fetch(url.toString(), {
227
245
  method: options.method,
@@ -229,16 +247,19 @@ export class ApiClient {
229
247
  body: options.body ? JSON.stringify(options.body) : undefined,
230
248
  });
231
249
 
232
- // Debug logging for response
233
- if (typeof window !== 'undefined' && window.location.hostname === 'localhost') {
234
- console.log('Response status:', response.status);
235
- console.log('Response headers:', Object.fromEntries(response.headers.entries()));
236
- console.log('Request was made with headers:', safariSafeHeaders);
237
- }
250
+ this.logger.debug('Received API response', this.buildLoggerExtra('request', {
251
+ url: url.toString(),
252
+ status: response.status,
253
+ }));
238
254
 
239
255
  if (!response.ok) {
240
256
  const error = await response.json();
241
- throw this.handleError(response.status, error);
257
+ const apiError = this.handleError(response.status, error);
258
+ this.logger.exception('API request failed', apiError, this.buildLoggerExtra('request', {
259
+ url: url.toString(),
260
+ status: response.status,
261
+ }));
262
+ throw apiError;
242
263
  }
243
264
 
244
265
  const data = await response.json();
@@ -251,12 +272,22 @@ export class ApiClient {
251
272
  // Add context that this is an order-related error
252
273
  data._isOrderError = true;
253
274
  }
254
- throw this.handleError(data.status_code || 500, data);
275
+ const apiError = this.handleError(data.status_code || 500, data);
276
+ this.logger.exception('API response indicated failure', apiError, this.buildLoggerExtra('request', {
277
+ url: url.toString(),
278
+ status: data.status_code || 500,
279
+ }));
280
+ throw apiError;
255
281
  }
256
282
 
257
283
  // Check if the response has a status_code field indicating an error (4xx or 5xx)
258
284
  if (data && typeof data === 'object' && 'status_code' in data && data.status_code >= 400) {
259
- throw this.handleError(data.status_code, data);
285
+ const apiError = this.handleError(data.status_code, data);
286
+ this.logger.exception('API status code error', apiError, this.buildLoggerExtra('request', {
287
+ url: url.toString(),
288
+ status: data.status_code,
289
+ }));
290
+ throw apiError;
260
291
  }
261
292
 
262
293
  // Check if the response has errors field with content
@@ -268,7 +299,12 @@ export class ApiClient {
268
299
  Array.isArray(data.errors) &&
269
300
  data.errors.length > 0
270
301
  ) {
271
- throw this.handleError(data.status_code || 500, data);
302
+ const apiError = this.handleError(data.status_code || 500, data);
303
+ this.logger.exception('API response contained errors', apiError, this.buildLoggerExtra('request', {
304
+ url: url.toString(),
305
+ status: data.status_code || 500,
306
+ }));
307
+ throw apiError;
272
308
  }
273
309
 
274
310
  return data;
@@ -1718,4 +1754,251 @@ export class ApiClient {
1718
1754
  },
1719
1755
  });
1720
1756
  }
1757
+
1758
+ /**
1759
+ * Get order fills for a specific order
1760
+ * @param orderId - The order ID
1761
+ * @param filter - Optional filter parameters
1762
+ * @returns Promise with order fills response
1763
+ */
1764
+ async getOrderFills(
1765
+ orderId: string,
1766
+ filter?: OrderFillsFilter
1767
+ ): Promise<{
1768
+ _id: string;
1769
+ response_data: OrderFill[];
1770
+ message: string;
1771
+ status_code: number;
1772
+ warnings: null;
1773
+ errors: null;
1774
+ }> {
1775
+ const accessToken = await this.getValidAccessToken();
1776
+ const params: Record<string, string> = {};
1777
+
1778
+ if (filter?.connection_id) {
1779
+ params.connection_id = filter.connection_id;
1780
+ }
1781
+ if (filter?.limit) {
1782
+ params.limit = filter.limit.toString();
1783
+ }
1784
+ if (filter?.offset) {
1785
+ params.offset = filter.offset.toString();
1786
+ }
1787
+
1788
+ return this.request<{
1789
+ _id: string;
1790
+ response_data: OrderFill[];
1791
+ message: string;
1792
+ status_code: number;
1793
+ warnings: null;
1794
+ errors: null;
1795
+ }>(`/brokers/data/orders/${orderId}/fills`, {
1796
+ method: 'GET',
1797
+ headers: {
1798
+ Authorization: `Bearer ${accessToken}`,
1799
+ },
1800
+ params,
1801
+ });
1802
+ }
1803
+
1804
+ /**
1805
+ * Get order events for a specific order
1806
+ * @param orderId - The order ID
1807
+ * @param filter - Optional filter parameters
1808
+ * @returns Promise with order events response
1809
+ */
1810
+ async getOrderEvents(
1811
+ orderId: string,
1812
+ filter?: OrderEventsFilter
1813
+ ): Promise<{
1814
+ _id: string;
1815
+ response_data: OrderEvent[];
1816
+ message: string;
1817
+ status_code: number;
1818
+ warnings: null;
1819
+ errors: null;
1820
+ }> {
1821
+ const accessToken = await this.getValidAccessToken();
1822
+ const params: Record<string, string> = {};
1823
+
1824
+ if (filter?.connection_id) {
1825
+ params.connection_id = filter.connection_id;
1826
+ }
1827
+ if (filter?.limit) {
1828
+ params.limit = filter.limit.toString();
1829
+ }
1830
+ if (filter?.offset) {
1831
+ params.offset = filter.offset.toString();
1832
+ }
1833
+
1834
+ return this.request<{
1835
+ _id: string;
1836
+ response_data: OrderEvent[];
1837
+ message: string;
1838
+ status_code: number;
1839
+ warnings: null;
1840
+ errors: null;
1841
+ }>(`/brokers/data/orders/${orderId}/events`, {
1842
+ method: 'GET',
1843
+ headers: {
1844
+ Authorization: `Bearer ${accessToken}`,
1845
+ },
1846
+ params,
1847
+ });
1848
+ }
1849
+
1850
+ /**
1851
+ * Get order groups
1852
+ * @param filter - Optional filter parameters
1853
+ * @returns Promise with order groups response
1854
+ */
1855
+ async getOrderGroups(
1856
+ filter?: OrderGroupsFilter
1857
+ ): Promise<{
1858
+ _id: string;
1859
+ response_data: OrderGroup[];
1860
+ message: string;
1861
+ status_code: number;
1862
+ warnings: null;
1863
+ errors: null;
1864
+ }> {
1865
+ const accessToken = await this.getValidAccessToken();
1866
+ const params: Record<string, string> = {};
1867
+
1868
+ if (filter?.broker_id) {
1869
+ params.broker_id = filter.broker_id;
1870
+ }
1871
+ if (filter?.connection_id) {
1872
+ params.connection_id = filter.connection_id;
1873
+ }
1874
+ if (filter?.limit) {
1875
+ params.limit = filter.limit.toString();
1876
+ }
1877
+ if (filter?.offset) {
1878
+ params.offset = filter.offset.toString();
1879
+ }
1880
+ if (filter?.created_after) {
1881
+ params.created_after = filter.created_after;
1882
+ }
1883
+ if (filter?.created_before) {
1884
+ params.created_before = filter.created_before;
1885
+ }
1886
+
1887
+ return this.request<{
1888
+ _id: string;
1889
+ response_data: OrderGroup[];
1890
+ message: string;
1891
+ status_code: number;
1892
+ warnings: null;
1893
+ errors: null;
1894
+ }>('/brokers/data/orders/groups', {
1895
+ method: 'GET',
1896
+ headers: {
1897
+ Authorization: `Bearer ${accessToken}`,
1898
+ },
1899
+ params,
1900
+ });
1901
+ }
1902
+
1903
+ /**
1904
+ * Get position lots (tax lots for positions)
1905
+ * @param filter - Optional filter parameters
1906
+ * @returns Promise with position lots response
1907
+ */
1908
+ async getPositionLots(
1909
+ filter?: PositionLotsFilter
1910
+ ): Promise<{
1911
+ _id: string;
1912
+ response_data: PositionLot[];
1913
+ message: string;
1914
+ status_code: number;
1915
+ warnings: null;
1916
+ errors: null;
1917
+ }> {
1918
+ const accessToken = await this.getValidAccessToken();
1919
+ const params: Record<string, string> = {};
1920
+
1921
+ if (filter?.broker_id) {
1922
+ params.broker_id = filter.broker_id;
1923
+ }
1924
+ if (filter?.connection_id) {
1925
+ params.connection_id = filter.connection_id;
1926
+ }
1927
+ if (filter?.account_id) {
1928
+ params.account_id = filter.account_id;
1929
+ }
1930
+ if (filter?.symbol) {
1931
+ params.symbol = filter.symbol;
1932
+ }
1933
+ if (filter?.position_id) {
1934
+ params.position_id = filter.position_id;
1935
+ }
1936
+ if (filter?.limit) {
1937
+ params.limit = filter.limit.toString();
1938
+ }
1939
+ if (filter?.offset) {
1940
+ params.offset = filter.offset.toString();
1941
+ }
1942
+
1943
+ return this.request<{
1944
+ _id: string;
1945
+ response_data: PositionLot[];
1946
+ message: string;
1947
+ status_code: number;
1948
+ warnings: null;
1949
+ errors: null;
1950
+ }>('/brokers/data/positions/lots', {
1951
+ method: 'GET',
1952
+ headers: {
1953
+ Authorization: `Bearer ${accessToken}`,
1954
+ },
1955
+ params,
1956
+ });
1957
+ }
1958
+
1959
+ /**
1960
+ * Get position lot fills for a specific lot
1961
+ * @param lotId - The position lot ID
1962
+ * @param filter - Optional filter parameters
1963
+ * @returns Promise with position lot fills response
1964
+ */
1965
+ async getPositionLotFills(
1966
+ lotId: string,
1967
+ filter?: PositionLotFillsFilter
1968
+ ): Promise<{
1969
+ _id: string;
1970
+ response_data: PositionLotFill[];
1971
+ message: string;
1972
+ status_code: number;
1973
+ warnings: null;
1974
+ errors: null;
1975
+ }> {
1976
+ const accessToken = await this.getValidAccessToken();
1977
+ const params: Record<string, string> = {};
1978
+
1979
+ if (filter?.connection_id) {
1980
+ params.connection_id = filter.connection_id;
1981
+ }
1982
+ if (filter?.limit) {
1983
+ params.limit = filter.limit.toString();
1984
+ }
1985
+ if (filter?.offset) {
1986
+ params.offset = filter.offset.toString();
1987
+ }
1988
+
1989
+ return this.request<{
1990
+ _id: string;
1991
+ response_data: PositionLotFill[];
1992
+ message: string;
1993
+ status_code: number;
1994
+ warnings: null;
1995
+ errors: null;
1996
+ }>(`/brokers/data/positions/lots/${lotId}/fills`, {
1997
+ method: 'GET',
1998
+ headers: {
1999
+ Authorization: `Bearer ${accessToken}`,
2000
+ },
2001
+ params,
2002
+ });
2003
+ }
1721
2004
  }