@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.
package/dist/index.mjs CHANGED
@@ -1,8 +1,285 @@
1
1
  import { v4 } from 'uuid';
2
2
 
3
+ const LOG_LEVEL_ORDER = {
4
+ error: 0,
5
+ warn: 1,
6
+ info: 2,
7
+ debug: 3,
8
+ };
9
+ const LEVEL_TO_CONSOLE = {
10
+ error: 'error',
11
+ warn: 'warn',
12
+ info: 'info',
13
+ debug: 'debug',
14
+ };
15
+ const DEFAULT_LOGGER_NAME = 'FinaticLogger';
16
+ const parseLogLevel = (value, fallback) => {
17
+ if (typeof value !== 'string') {
18
+ return fallback;
19
+ }
20
+ const normalized = value.toLowerCase().trim();
21
+ if (normalized === 'silent' || normalized === 'error' || normalized === 'warn' || normalized === 'info' || normalized === 'debug') {
22
+ return normalized;
23
+ }
24
+ return fallback;
25
+ };
26
+ const parseVerbosity = (value, fallback) => {
27
+ if (typeof value !== 'string' && typeof value !== 'number') {
28
+ return fallback;
29
+ }
30
+ const numeric = typeof value === 'number' ? value : Number.parseInt(value, 10);
31
+ if (Number.isNaN(numeric)) {
32
+ return fallback;
33
+ }
34
+ if (numeric <= 0) {
35
+ return 0;
36
+ }
37
+ if (numeric >= 3) {
38
+ return 3;
39
+ }
40
+ return numeric;
41
+ };
42
+ const resolveEnv = (key) => {
43
+ try {
44
+ if (typeof process !== 'undefined' && process.env && typeof process.env[key] === 'string') {
45
+ return process.env[key];
46
+ }
47
+ }
48
+ catch {
49
+ // ignore
50
+ }
51
+ try {
52
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
+ const metaEnv = typeof import.meta !== 'undefined' ? import.meta.env : undefined;
54
+ if (metaEnv && typeof metaEnv[key] === 'string') {
55
+ return metaEnv[key];
56
+ }
57
+ }
58
+ catch {
59
+ // ignore
60
+ }
61
+ try {
62
+ if (typeof globalThis !== 'undefined') {
63
+ const value = globalThis[key];
64
+ if (typeof value === 'string') {
65
+ return value;
66
+ }
67
+ }
68
+ }
69
+ catch {
70
+ // ignore
71
+ }
72
+ return undefined;
73
+ };
74
+ const resolveDefaultLogLevel = (explicitLevel) => {
75
+ if (explicitLevel) {
76
+ return explicitLevel;
77
+ }
78
+ const envLevel = resolveEnv('FINATIC_LOG_LEVEL') ||
79
+ resolveEnv('VITE_FINATIC_LOG_LEVEL') ||
80
+ resolveEnv('NEXT_PUBLIC_FINATIC_LOG_LEVEL') ||
81
+ resolveEnv('NEXT_FINATIC_LOG_LEVEL') ||
82
+ resolveEnv('REACT_APP_FINATIC_LOG_LEVEL') ||
83
+ resolveEnv('NUXT_PUBLIC_FINATIC_LOG_LEVEL') ||
84
+ resolveEnv('NX_FINATIC_LOG_LEVEL');
85
+ if (envLevel) {
86
+ return parseLogLevel(envLevel, 'silent');
87
+ }
88
+ return 'silent';
89
+ };
90
+ const resolveVerbosity = () => {
91
+ const envVerbosity = resolveEnv('FINATIC_LOG_VERBOSITY') ||
92
+ resolveEnv('VITE_FINATIC_LOG_VERBOSITY') ||
93
+ resolveEnv('NEXT_PUBLIC_FINATIC_LOG_VERBOSITY') ||
94
+ resolveEnv('NEXT_FINATIC_LOG_VERBOSITY') ||
95
+ resolveEnv('REACT_APP_FINATIC_LOG_VERBOSITY') ||
96
+ resolveEnv('NUXT_PUBLIC_FINATIC_LOG_VERBOSITY') ||
97
+ resolveEnv('NX_FINATIC_LOG_VERBOSITY');
98
+ if (envVerbosity) {
99
+ return parseVerbosity(envVerbosity, 1);
100
+ }
101
+ return 1;
102
+ };
103
+ const resolveBaseMetadata = () => {
104
+ const base = {
105
+ timestamp: new Date().toISOString(),
106
+ };
107
+ try {
108
+ if (typeof globalThis !== 'undefined') {
109
+ if (typeof globalThis.location !== 'undefined') {
110
+ base.host = globalThis.location.hostname;
111
+ }
112
+ if (typeof globalThis.navigator !== 'undefined') {
113
+ base.user_agent = globalThis.navigator.userAgent;
114
+ }
115
+ }
116
+ }
117
+ catch {
118
+ // ignore
119
+ }
120
+ try {
121
+ if (typeof process !== 'undefined') {
122
+ base.pid = process.pid;
123
+ }
124
+ }
125
+ catch {
126
+ // ignore
127
+ }
128
+ return base;
129
+ };
130
+ const normalizeError = (error) => {
131
+ if (!error) {
132
+ return undefined;
133
+ }
134
+ if (error instanceof Error) {
135
+ return {
136
+ type: error.name,
137
+ message: error.message,
138
+ stacktrace: error.stack,
139
+ };
140
+ }
141
+ if (typeof error === 'object') {
142
+ return { ...error };
143
+ }
144
+ return {
145
+ type: 'Error',
146
+ message: String(error),
147
+ };
148
+ };
149
+ const shouldLog = (requestedLevel, currentLevel) => {
150
+ if (currentLevel === 'silent') {
151
+ return false;
152
+ }
153
+ const currentOrder = LOG_LEVEL_ORDER[currentLevel];
154
+ const requestedOrder = LOG_LEVEL_ORDER[requestedLevel];
155
+ return requestedOrder <= currentOrder;
156
+ };
157
+ const buildPayload = (name, level, message, defaultMetadata, extra, verbosity) => {
158
+ const payload = {
159
+ timestamp: new Date().toISOString(),
160
+ message,
161
+ };
162
+ if (verbosity >= 1 && extra) {
163
+ if (extra.module) {
164
+ payload.module = extra.module;
165
+ }
166
+ if (extra.function) {
167
+ payload.function = extra.function;
168
+ }
169
+ }
170
+ if (verbosity >= 2) {
171
+ if (extra?.duration_ms !== undefined) {
172
+ payload.duration_ms = extra.duration_ms;
173
+ }
174
+ if (extra?.event) {
175
+ payload.event = extra.event;
176
+ }
177
+ if (extra?.error) {
178
+ payload.error = normalizeError(extra.error);
179
+ }
180
+ }
181
+ if (verbosity >= 3) {
182
+ payload.level = level.toUpperCase();
183
+ payload.name = name;
184
+ const baseMetadata = resolveBaseMetadata();
185
+ const mergedMetadata = {
186
+ ...baseMetadata,
187
+ ...(defaultMetadata || {}),
188
+ ...(extra?.metadata || {}),
189
+ };
190
+ if (Object.keys(mergedMetadata).length > 0) {
191
+ payload.metadata = mergedMetadata;
192
+ }
193
+ }
194
+ const restKeys = ['module', 'function', 'event', 'duration_ms', 'error', 'metadata'];
195
+ if (extra) {
196
+ Object.entries(extra).forEach(([key, value]) => {
197
+ if (!restKeys.includes(key) && value !== undefined) {
198
+ payload[key] = value;
199
+ }
200
+ });
201
+ }
202
+ return payload;
203
+ };
204
+ const consoleWrite = (consoleLevel, name, level, message, payload) => {
205
+ if (typeof console === 'undefined' || typeof console[consoleLevel] !== 'function') {
206
+ return;
207
+ }
208
+ const prefix = `${level.toUpperCase()}: ${name} || ${message}`;
209
+ console[consoleLevel](prefix, payload);
210
+ };
211
+ const setupLogger = (nameOrOptions, level, defaultMetadata) => {
212
+ const options = typeof nameOrOptions === 'string'
213
+ ? {
214
+ name: nameOrOptions,
215
+ level,
216
+ defaultMetadata,
217
+ }
218
+ : nameOrOptions;
219
+ const loggerName = options.name || DEFAULT_LOGGER_NAME;
220
+ let currentLevel = resolveDefaultLogLevel(options.level);
221
+ const loggerDefaultMetadata = options.defaultMetadata;
222
+ const verbosity = resolveVerbosity();
223
+ const log = (requestedLevel, message, extra) => {
224
+ if (!shouldLog(requestedLevel, currentLevel)) {
225
+ return;
226
+ }
227
+ const payload = buildPayload(loggerName, requestedLevel, message, loggerDefaultMetadata, extra, verbosity);
228
+ consoleWrite(LEVEL_TO_CONSOLE[requestedLevel], loggerName, requestedLevel, message, payload);
229
+ };
230
+ return {
231
+ getLevel: () => currentLevel,
232
+ setLevel: (nextLevel) => {
233
+ currentLevel = nextLevel;
234
+ },
235
+ debug: (message, extra) => log('debug', message, extra),
236
+ info: (message, extra) => log('info', message, extra),
237
+ warn: (message, extra) => log('warn', message, extra),
238
+ error: (message, extra) => log('error', message, extra),
239
+ exception: (message, error, extra) => {
240
+ log('error', message, {
241
+ ...extra,
242
+ error,
243
+ event: extra?.event || 'exception',
244
+ });
245
+ },
246
+ };
247
+ };
248
+ const buildLoggerExtra = (metadata) => ({
249
+ metadata,
250
+ });
251
+ const logStartEnd = (logger) => (fn) => async (...args) => {
252
+ const start = Date.now();
253
+ const functionName = fn.name || 'anonymous';
254
+ logger.debug('START', { module: 'logStartEnd', function: functionName, event: 'start' });
255
+ try {
256
+ const result = await fn(...args);
257
+ const duration = Date.now() - start;
258
+ logger.info('END', { module: 'logStartEnd', function: functionName, event: 'end', duration_ms: duration });
259
+ return result;
260
+ }
261
+ catch (error) {
262
+ const duration = Date.now() - start;
263
+ logger.exception('EXCEPTION', error, {
264
+ module: 'logStartEnd',
265
+ function: functionName,
266
+ duration_ms: duration,
267
+ });
268
+ throw error;
269
+ }
270
+ };
271
+
3
272
  /**
4
273
  * Pagination-related types and classes
5
274
  */
275
+ const paginationLogger = setupLogger('FinaticClientSDK.Pagination', undefined, {
276
+ codebase: 'FinaticClientSDK',
277
+ });
278
+ const buildPaginationExtra = (functionName, metadata) => ({
279
+ module: 'PaginatedResult',
280
+ function: functionName,
281
+ ...(metadata ? buildLoggerExtra(metadata) : {}),
282
+ });
6
283
  class PaginatedResult {
7
284
  constructor(data, paginationInfo, navigationCallback) {
8
285
  this.data = data;
@@ -34,7 +311,10 @@ class PaginatedResult {
34
311
  return await this.navigationCallback(this.metadata.nextOffset, this.metadata.limit);
35
312
  }
36
313
  catch (error) {
37
- console.error('Error fetching next page:', error);
314
+ paginationLogger.exception('Error fetching next page', error, buildPaginationExtra('nextPage', {
315
+ next_offset: this.metadata.nextOffset,
316
+ limit: this.metadata.limit,
317
+ }));
38
318
  return null;
39
319
  }
40
320
  }
@@ -47,7 +327,10 @@ class PaginatedResult {
47
327
  return await this.navigationCallback(previousOffset, this.metadata.limit);
48
328
  }
49
329
  catch (error) {
50
- console.error('Error fetching previous page:', error);
330
+ paginationLogger.exception('Error fetching previous page', error, buildPaginationExtra('previousPage', {
331
+ previous_offset: previousOffset,
332
+ limit: this.metadata.limit,
333
+ }));
51
334
  return null;
52
335
  }
53
336
  }
@@ -60,7 +343,11 @@ class PaginatedResult {
60
343
  return await this.navigationCallback(offset, this.metadata.limit);
61
344
  }
62
345
  catch (error) {
63
- console.error('Error fetching page:', pageNumber, error);
346
+ paginationLogger.exception('Error fetching page', error, buildPaginationExtra('goToPage', {
347
+ page_number: pageNumber,
348
+ offset,
349
+ limit: this.metadata.limit,
350
+ }));
64
351
  return null;
65
352
  }
66
353
  }
@@ -72,7 +359,9 @@ class PaginatedResult {
72
359
  return await this.navigationCallback(0, this.metadata.limit);
73
360
  }
74
361
  catch (error) {
75
- console.error('Error fetching first page:', error);
362
+ paginationLogger.exception('Error fetching first page', error, buildPaginationExtra('firstPage', {
363
+ limit: this.metadata.limit,
364
+ }));
76
365
  return null;
77
366
  }
78
367
  }
@@ -94,7 +383,9 @@ class PaginatedResult {
94
383
  return await findLast(this);
95
384
  }
96
385
  catch (error) {
97
- console.error('Error fetching last page:', error);
386
+ paginationLogger.exception('Error fetching last page', error, buildPaginationExtra('lastPage', {
387
+ limit: this.metadata.limit,
388
+ }));
98
389
  return null;
99
390
  }
100
391
  }
@@ -205,6 +496,13 @@ class TradingNotEnabledError extends ApiError {
205
496
 
206
497
  // Supabase import removed - SDK no longer depends on Supabase
207
498
  class ApiClient {
499
+ buildLoggerExtra(functionName, metadata) {
500
+ return {
501
+ module: 'ApiClient',
502
+ function: functionName,
503
+ ...(metadata ? buildLoggerExtra(metadata) : {}),
504
+ };
505
+ }
208
506
  constructor(baseUrl, deviceInfo) {
209
507
  this.currentSessionState = null;
210
508
  this.currentSessionId = null;
@@ -213,6 +511,9 @@ class ApiClient {
213
511
  // Session and company context
214
512
  this.companyId = null;
215
513
  this.csrfToken = null;
514
+ this.logger = setupLogger('FinaticClientSDK.ApiClient', undefined, {
515
+ codebase: 'FinaticClientSDK',
516
+ });
216
517
  this.baseUrl = baseUrl;
217
518
  this.deviceInfo = deviceInfo;
218
519
  // Ensure baseUrl doesn't end with a slash
@@ -333,29 +634,28 @@ class ApiClient {
333
634
  safariSafeHeaders[normalizedKey] = normalizedValue;
334
635
  }
335
636
  });
336
- // Debug logging for development
337
- if (typeof window !== 'undefined' && window.location.hostname === 'localhost') {
338
- console.log('Request to:', url.toString());
339
- console.log('Safari-safe headers:', safariSafeHeaders);
340
- console.log('Browser:', navigator.userAgent);
341
- console.log('Session ID:', this.currentSessionId);
342
- console.log('Company ID:', this.companyId);
343
- console.log('CSRF Token:', this.csrfToken);
344
- }
637
+ this.logger.debug('Dispatching API request', this.buildLoggerExtra('request', {
638
+ url: url.toString(),
639
+ method: options.method,
640
+ has_body: Boolean(options.body),
641
+ }));
345
642
  const response = await fetch(url.toString(), {
346
643
  method: options.method,
347
644
  headers: safariSafeHeaders,
348
645
  body: options.body ? JSON.stringify(options.body) : undefined,
349
646
  });
350
- // Debug logging for response
351
- if (typeof window !== 'undefined' && window.location.hostname === 'localhost') {
352
- console.log('Response status:', response.status);
353
- console.log('Response headers:', Object.fromEntries(response.headers.entries()));
354
- console.log('Request was made with headers:', safariSafeHeaders);
355
- }
647
+ this.logger.debug('Received API response', this.buildLoggerExtra('request', {
648
+ url: url.toString(),
649
+ status: response.status,
650
+ }));
356
651
  if (!response.ok) {
357
652
  const error = await response.json();
358
- throw this.handleError(response.status, error);
653
+ const apiError = this.handleError(response.status, error);
654
+ this.logger.exception('API request failed', apiError, this.buildLoggerExtra('request', {
655
+ url: url.toString(),
656
+ status: response.status,
657
+ }));
658
+ throw apiError;
359
659
  }
360
660
  const data = await response.json();
361
661
  // Check if the response has a success field and it's false
@@ -366,11 +666,21 @@ class ApiClient {
366
666
  // Add context that this is an order-related error
367
667
  data._isOrderError = true;
368
668
  }
369
- throw this.handleError(data.status_code || 500, data);
669
+ const apiError = this.handleError(data.status_code || 500, data);
670
+ this.logger.exception('API response indicated failure', apiError, this.buildLoggerExtra('request', {
671
+ url: url.toString(),
672
+ status: data.status_code || 500,
673
+ }));
674
+ throw apiError;
370
675
  }
371
676
  // Check if the response has a status_code field indicating an error (4xx or 5xx)
372
677
  if (data && typeof data === 'object' && 'status_code' in data && data.status_code >= 400) {
373
- throw this.handleError(data.status_code, data);
678
+ const apiError = this.handleError(data.status_code, data);
679
+ this.logger.exception('API status code error', apiError, this.buildLoggerExtra('request', {
680
+ url: url.toString(),
681
+ status: data.status_code,
682
+ }));
683
+ throw apiError;
374
684
  }
375
685
  // Check if the response has errors field with content
376
686
  if (data &&
@@ -379,7 +689,12 @@ class ApiClient {
379
689
  data.errors &&
380
690
  Array.isArray(data.errors) &&
381
691
  data.errors.length > 0) {
382
- throw this.handleError(data.status_code || 500, data);
692
+ const apiError = this.handleError(data.status_code || 500, data);
693
+ this.logger.exception('API response contained errors', apiError, this.buildLoggerExtra('request', {
694
+ url: url.toString(),
695
+ status: data.status_code || 500,
696
+ }));
697
+ throw apiError;
383
698
  }
384
699
  return data;
385
700
  }
@@ -1409,8 +1724,160 @@ class ApiClient {
1409
1724
  },
1410
1725
  });
1411
1726
  }
1727
+ /**
1728
+ * Get order fills for a specific order
1729
+ * @param orderId - The order ID
1730
+ * @param filter - Optional filter parameters
1731
+ * @returns Promise with order fills response
1732
+ */
1733
+ async getOrderFills(orderId, filter) {
1734
+ const accessToken = await this.getValidAccessToken();
1735
+ const params = {};
1736
+ if (filter?.connection_id) {
1737
+ params.connection_id = filter.connection_id;
1738
+ }
1739
+ if (filter?.limit) {
1740
+ params.limit = filter.limit.toString();
1741
+ }
1742
+ if (filter?.offset) {
1743
+ params.offset = filter.offset.toString();
1744
+ }
1745
+ return this.request(`/brokers/data/orders/${orderId}/fills`, {
1746
+ method: 'GET',
1747
+ headers: {
1748
+ Authorization: `Bearer ${accessToken}`,
1749
+ },
1750
+ params,
1751
+ });
1752
+ }
1753
+ /**
1754
+ * Get order events for a specific order
1755
+ * @param orderId - The order ID
1756
+ * @param filter - Optional filter parameters
1757
+ * @returns Promise with order events response
1758
+ */
1759
+ async getOrderEvents(orderId, filter) {
1760
+ const accessToken = await this.getValidAccessToken();
1761
+ const params = {};
1762
+ if (filter?.connection_id) {
1763
+ params.connection_id = filter.connection_id;
1764
+ }
1765
+ if (filter?.limit) {
1766
+ params.limit = filter.limit.toString();
1767
+ }
1768
+ if (filter?.offset) {
1769
+ params.offset = filter.offset.toString();
1770
+ }
1771
+ return this.request(`/brokers/data/orders/${orderId}/events`, {
1772
+ method: 'GET',
1773
+ headers: {
1774
+ Authorization: `Bearer ${accessToken}`,
1775
+ },
1776
+ params,
1777
+ });
1778
+ }
1779
+ /**
1780
+ * Get order groups
1781
+ * @param filter - Optional filter parameters
1782
+ * @returns Promise with order groups response
1783
+ */
1784
+ async getOrderGroups(filter) {
1785
+ const accessToken = await this.getValidAccessToken();
1786
+ const params = {};
1787
+ if (filter?.broker_id) {
1788
+ params.broker_id = filter.broker_id;
1789
+ }
1790
+ if (filter?.connection_id) {
1791
+ params.connection_id = filter.connection_id;
1792
+ }
1793
+ if (filter?.limit) {
1794
+ params.limit = filter.limit.toString();
1795
+ }
1796
+ if (filter?.offset) {
1797
+ params.offset = filter.offset.toString();
1798
+ }
1799
+ if (filter?.created_after) {
1800
+ params.created_after = filter.created_after;
1801
+ }
1802
+ if (filter?.created_before) {
1803
+ params.created_before = filter.created_before;
1804
+ }
1805
+ return this.request('/brokers/data/orders/groups', {
1806
+ method: 'GET',
1807
+ headers: {
1808
+ Authorization: `Bearer ${accessToken}`,
1809
+ },
1810
+ params,
1811
+ });
1812
+ }
1813
+ /**
1814
+ * Get position lots (tax lots for positions)
1815
+ * @param filter - Optional filter parameters
1816
+ * @returns Promise with position lots response
1817
+ */
1818
+ async getPositionLots(filter) {
1819
+ const accessToken = await this.getValidAccessToken();
1820
+ const params = {};
1821
+ if (filter?.broker_id) {
1822
+ params.broker_id = filter.broker_id;
1823
+ }
1824
+ if (filter?.connection_id) {
1825
+ params.connection_id = filter.connection_id;
1826
+ }
1827
+ if (filter?.account_id) {
1828
+ params.account_id = filter.account_id;
1829
+ }
1830
+ if (filter?.symbol) {
1831
+ params.symbol = filter.symbol;
1832
+ }
1833
+ if (filter?.position_id) {
1834
+ params.position_id = filter.position_id;
1835
+ }
1836
+ if (filter?.limit) {
1837
+ params.limit = filter.limit.toString();
1838
+ }
1839
+ if (filter?.offset) {
1840
+ params.offset = filter.offset.toString();
1841
+ }
1842
+ return this.request('/brokers/data/positions/lots', {
1843
+ method: 'GET',
1844
+ headers: {
1845
+ Authorization: `Bearer ${accessToken}`,
1846
+ },
1847
+ params,
1848
+ });
1849
+ }
1850
+ /**
1851
+ * Get position lot fills for a specific lot
1852
+ * @param lotId - The position lot ID
1853
+ * @param filter - Optional filter parameters
1854
+ * @returns Promise with position lot fills response
1855
+ */
1856
+ async getPositionLotFills(lotId, filter) {
1857
+ const accessToken = await this.getValidAccessToken();
1858
+ const params = {};
1859
+ if (filter?.connection_id) {
1860
+ params.connection_id = filter.connection_id;
1861
+ }
1862
+ if (filter?.limit) {
1863
+ params.limit = filter.limit.toString();
1864
+ }
1865
+ if (filter?.offset) {
1866
+ params.offset = filter.offset.toString();
1867
+ }
1868
+ return this.request(`/brokers/data/positions/lots/${lotId}/fills`, {
1869
+ method: 'GET',
1870
+ headers: {
1871
+ Authorization: `Bearer ${accessToken}`,
1872
+ },
1873
+ params,
1874
+ });
1875
+ }
1412
1876
  }
1413
1877
 
1878
+ const eventsLogger = setupLogger('FinaticClientSDK.Events', undefined, {
1879
+ codebase: 'FinaticClientSDK',
1880
+ });
1414
1881
  class EventEmitter {
1415
1882
  constructor() {
1416
1883
  this.events = new Map();
@@ -1440,7 +1907,13 @@ class EventEmitter {
1440
1907
  callback(...args);
1441
1908
  }
1442
1909
  catch (error) {
1443
- console.error(`Error in event handler for ${event}:`, error);
1910
+ eventsLogger.exception('Error in event handler', error, {
1911
+ module: 'EventEmitter',
1912
+ function: 'emit',
1913
+ ...buildLoggerExtra({
1914
+ event_name: event,
1915
+ }),
1916
+ });
1444
1917
  }
1445
1918
  });
1446
1919
  }
@@ -1461,6 +1934,9 @@ class EventEmitter {
1461
1934
  }
1462
1935
  }
1463
1936
 
1937
+ const portalLogger = setupLogger('FinaticClientSDK.PortalUI', undefined, {
1938
+ codebase: 'FinaticClientSDK',
1939
+ });
1464
1940
  class PortalUI {
1465
1941
  constructor(portalUrl) {
1466
1942
  this.iframe = null;
@@ -1469,10 +1945,18 @@ class PortalUI {
1469
1945
  this.sessionId = null;
1470
1946
  this.portalOrigin = null;
1471
1947
  this.originalBodyStyle = null;
1948
+ this.logger = portalLogger;
1472
1949
  this.createContainer();
1473
1950
  }
1951
+ buildLoggerExtra(functionName, metadata) {
1952
+ return {
1953
+ module: 'PortalUI',
1954
+ function: functionName,
1955
+ ...(metadata ? buildLoggerExtra(metadata) : {}),
1956
+ };
1957
+ }
1474
1958
  createContainer() {
1475
- console.debug('[PortalUI] Creating container and iframe');
1959
+ this.logger.debug('Creating portal container and iframe', this.buildLoggerExtra('createContainer'));
1476
1960
  this.container = document.createElement('div');
1477
1961
  this.container.style.cssText = `
1478
1962
  position: fixed;
@@ -1519,7 +2003,7 @@ class PortalUI {
1519
2003
  this.iframe.contentDocument?.head.appendChild(meta);
1520
2004
  this.container.appendChild(this.iframe);
1521
2005
  document.body.appendChild(this.container);
1522
- console.debug('[PortalUI] Container and iframe created successfully');
2006
+ this.logger.debug('Portal container and iframe created successfully', this.buildLoggerExtra('createContainer'));
1523
2007
  }
1524
2008
  /**
1525
2009
  * Lock background scrolling by setting overflow: hidden on body
@@ -1533,7 +2017,7 @@ class PortalUI {
1533
2017
  document.body.style.position = 'fixed';
1534
2018
  document.body.style.width = '100%';
1535
2019
  document.body.style.top = `-${window.scrollY}px`;
1536
- console.debug('[PortalUI] Background scroll locked');
2020
+ this.logger.debug('Background scroll locked', this.buildLoggerExtra('lockScroll'));
1537
2021
  }
1538
2022
  }
1539
2023
  /**
@@ -1549,7 +2033,7 @@ class PortalUI {
1549
2033
  document.body.style.top = '';
1550
2034
  window.scrollTo(0, parseInt(scrollY || '0') * -1);
1551
2035
  this.originalBodyStyle = null;
1552
- console.debug('[PortalUI] Background scroll unlocked');
2036
+ this.logger.debug('Background scroll unlocked', this.buildLoggerExtra('unlockScroll'));
1553
2037
  }
1554
2038
  }
1555
2039
  show(url, sessionId, options = {}) {
@@ -1591,11 +2075,17 @@ class PortalUI {
1591
2075
  handleMessage(event) {
1592
2076
  // Verify origin matches the portal URL
1593
2077
  if (!this.portalOrigin || event.origin !== this.portalOrigin) {
1594
- console.warn('[PortalUI] Received message from unauthorized origin:', event.origin, 'Expected:', this.portalOrigin);
2078
+ this.logger.warn('Received message from unauthorized origin', this.buildLoggerExtra('handleMessage', {
2079
+ received_origin: event.origin,
2080
+ expected_origin: this.portalOrigin || 'unknown',
2081
+ }));
1595
2082
  return;
1596
2083
  }
1597
2084
  const { type, userId, access_token, refresh_token, error, height, data } = event.data;
1598
- console.log('[PortalUI] Received message:', event.data);
2085
+ this.logger.debug('Received portal message', this.buildLoggerExtra('handleMessage', {
2086
+ message_type: type,
2087
+ has_data: Boolean(data),
2088
+ }));
1599
2089
  switch (type) {
1600
2090
  case 'portal-success': {
1601
2091
  // Handle both direct userId and data.userId formats
@@ -1633,36 +2123,50 @@ class PortalUI {
1633
2123
  this.handleClose();
1634
2124
  break;
1635
2125
  default:
1636
- console.warn('[PortalUI] Received unhandled message type:', type);
2126
+ this.logger.warn('Received unhandled message type', this.buildLoggerExtra('handleMessage', {
2127
+ message_type: type,
2128
+ }));
1637
2129
  }
1638
2130
  }
1639
2131
  handlePortalSuccess(userId, tokens) {
1640
2132
  if (!userId) {
1641
- console.error('[PortalUI] Missing userId in portal-success message');
2133
+ this.logger.error('Missing userId in portal-success message', this.buildLoggerExtra('handlePortalSuccess'));
1642
2134
  return;
1643
2135
  }
1644
- console.log('[PortalUI] Portal success - User connected:', userId);
2136
+ this.logger.info('Portal success - user connected', this.buildLoggerExtra('handlePortalSuccess', {
2137
+ user_id_present: Boolean(userId),
2138
+ tokens_provided: Boolean(tokens?.access_token && tokens?.refresh_token),
2139
+ }));
1645
2140
  if (tokens?.access_token && tokens?.refresh_token) {
1646
- console.log('[PortalUI] Tokens received for user:', userId);
2141
+ this.logger.debug('Tokens received for user', this.buildLoggerExtra('handlePortalSuccess', {
2142
+ tokens_provided: true,
2143
+ }));
1647
2144
  }
1648
2145
  // Pass userId to parent (SDK will handle tokens internally)
1649
2146
  this.options?.onSuccess?.(userId, tokens);
1650
2147
  }
1651
2148
  handlePortalError(error) {
1652
- console.error('[PortalUI] Portal error:', error);
2149
+ this.logger.error('Portal error received', this.buildLoggerExtra('handlePortalError', {
2150
+ error_message: error,
2151
+ }));
1653
2152
  this.options?.onError?.(new Error(error || 'Unknown portal error'));
1654
2153
  }
1655
2154
  handlePortalClose() {
1656
- console.log('[PortalUI] Portal closed by user');
2155
+ this.logger.info('Portal closed by user', this.buildLoggerExtra('handlePortalClose'));
1657
2156
  this.options?.onClose?.();
1658
2157
  this.hide();
1659
2158
  }
1660
2159
  handleGenericEvent(data) {
1661
2160
  if (!data || !data.type) {
1662
- console.warn('[PortalUI] Invalid event data:', data);
2161
+ this.logger.warn('Invalid event data received', this.buildLoggerExtra('handleGenericEvent', {
2162
+ has_type: Boolean(data?.type),
2163
+ }));
1663
2164
  return;
1664
2165
  }
1665
- console.log('[PortalUI] Generic event received:', data.type, data.data);
2166
+ this.logger.debug('Generic event received', this.buildLoggerExtra('handleGenericEvent', {
2167
+ event_type: data.type,
2168
+ payload_present: Boolean(data.data),
2169
+ }));
1666
2170
  // Emit the event to be handled by the SDK
1667
2171
  // This will be implemented in FinaticConnect
1668
2172
  if (this.options?.onEvent) {
@@ -1671,24 +2175,28 @@ class PortalUI {
1671
2175
  }
1672
2176
  handleSuccess(userId) {
1673
2177
  if (!userId) {
1674
- console.error('[PortalUI] Missing required fields in success message');
2178
+ this.logger.error('Missing required fields in success message', this.buildLoggerExtra('handleSuccess'));
1675
2179
  return;
1676
2180
  }
1677
2181
  // Pass userId to parent
1678
2182
  this.options?.onSuccess?.(userId);
1679
2183
  }
1680
2184
  handleError(error) {
1681
- console.error('[PortalUI] Received error:', error);
2185
+ this.logger.error('Received portal error message', this.buildLoggerExtra('handleError', {
2186
+ error_message: error,
2187
+ }));
1682
2188
  this.options?.onError?.(new Error(error || 'Unknown error'));
1683
2189
  }
1684
2190
  handleClose() {
1685
- console.log('[PortalUI] Received close message');
2191
+ this.logger.debug('Received close message', this.buildLoggerExtra('handleClose'));
1686
2192
  this.options?.onClose?.();
1687
2193
  this.hide();
1688
2194
  }
1689
2195
  handleResize(height) {
1690
2196
  if (height && this.iframe) {
1691
- console.log('[PortalUI] Received resize message:', height);
2197
+ this.logger.debug('Received resize message', this.buildLoggerExtra('handleResize', {
2198
+ height,
2199
+ }));
1692
2200
  this.iframe.style.height = `${height}px`;
1693
2201
  }
1694
2202
  }
@@ -2516,11 +3024,21 @@ class MockDataProvider {
2516
3024
  }
2517
3025
  }
2518
3026
 
3027
+ const mockApiLogger = setupLogger('FinaticClientSDK.MockApiClient', undefined, {
3028
+ codebase: 'FinaticClientSDK',
3029
+ });
2519
3030
  /**
2520
3031
  * Mock API Client that implements the same interface as the real ApiClient
2521
3032
  * but returns mock data instead of making HTTP requests
2522
3033
  */
2523
3034
  class MockApiClient {
3035
+ buildLoggerExtra(functionName, metadata) {
3036
+ return {
3037
+ module: 'MockApiClient',
3038
+ function: functionName,
3039
+ ...(metadata ? buildLoggerExtra(metadata) : {}),
3040
+ };
3041
+ }
2524
3042
  constructor(baseUrl, deviceInfo, mockConfig) {
2525
3043
  this.currentSessionState = null;
2526
3044
  this.currentSessionId = null;
@@ -2532,16 +3050,21 @@ class MockApiClient {
2532
3050
  // Session and company context
2533
3051
  this.companyId = null;
2534
3052
  this.csrfToken = null;
3053
+ this.logger = mockApiLogger;
2535
3054
  this.baseUrl = baseUrl;
2536
3055
  this.deviceInfo = deviceInfo;
2537
3056
  this.mockApiOnly = mockConfig?.mockApiOnly || false;
2538
3057
  this.mockDataProvider = new MockDataProvider(mockConfig);
2539
3058
  // Log that mocks are being used
2540
3059
  if (this.mockApiOnly) {
2541
- console.log('🔧 Finatic SDK: Using MOCK API Client (API only - real portal)');
3060
+ this.logger.info('Using mock API client (API only, real portal)', this.buildLoggerExtra('constructor', {
3061
+ mock_api_only: true,
3062
+ }));
2542
3063
  }
2543
3064
  else {
2544
- console.log('🔧 Finatic SDK: Using MOCK API Client');
3065
+ this.logger.info('Using mock API client', this.buildLoggerExtra('constructor', {
3066
+ mock_api_only: false,
3067
+ }));
2545
3068
  }
2546
3069
  }
2547
3070
  /**
@@ -2735,14 +3258,12 @@ class MockApiClient {
2735
3258
  async placeBrokerOrder(params, extras = {}, connection_id) {
2736
3259
  await this.getValidAccessToken();
2737
3260
  // Debug logging
2738
- console.log('MockApiClient.placeBrokerOrder Debug:', {
2739
- params,
2740
- tradingContext: this.tradingContext,
2741
- paramsBroker: params.broker,
2742
- contextBroker: this.tradingContext.broker,
2743
- paramsAccountNumber: params.accountNumber,
2744
- contextAccountNumber: this.tradingContext.accountNumber,
2745
- });
3261
+ this.logger.debug('placeBrokerOrder parameters', this.buildLoggerExtra('placeBrokerOrder', {
3262
+ has_params_broker: Boolean(params.broker),
3263
+ has_context_broker: Boolean(this.tradingContext.broker),
3264
+ has_params_account: Boolean(params.accountNumber),
3265
+ has_context_account: Boolean(this.tradingContext.accountNumber),
3266
+ }));
2746
3267
  const fullParams = {
2747
3268
  broker: (params.broker || this.tradingContext.broker) ||
2748
3269
  (() => {
@@ -2763,7 +3284,13 @@ class MockApiClient {
2763
3284
  stopPrice: params.stopPrice,
2764
3285
  order_id: params.order_id,
2765
3286
  };
2766
- console.log('MockApiClient.placeBrokerOrder Debug - Final params:', fullParams);
3287
+ this.logger.debug('placeBrokerOrder normalized parameters', this.buildLoggerExtra('placeBrokerOrder', {
3288
+ broker: fullParams.broker,
3289
+ account_number_present: Boolean(fullParams.accountNumber),
3290
+ symbol: fullParams.symbol,
3291
+ order_type: fullParams.orderType,
3292
+ asset_type: fullParams.assetType,
3293
+ }));
2767
3294
  return this.mockDataProvider.mockPlaceOrder(fullParams);
2768
3295
  }
2769
3296
  async cancelBrokerOrder(orderId, broker, extras = {}, connection_id) {
@@ -2801,14 +3328,18 @@ class MockApiClient {
2801
3328
  this.tradingContext.accountId = undefined;
2802
3329
  }
2803
3330
  setAccount(accountNumber, accountId) {
2804
- console.log('MockApiClient.setAccount Debug:', {
2805
- accountNumber,
2806
- accountId,
2807
- previousContext: { ...this.tradingContext },
2808
- });
3331
+ this.logger.debug('setAccount invoked', this.buildLoggerExtra('setAccount', {
3332
+ account_number: accountNumber,
3333
+ account_id_present: Boolean(accountId),
3334
+ had_existing_account: Boolean(this.tradingContext.accountNumber),
3335
+ }));
2809
3336
  this.tradingContext.accountNumber = accountNumber;
2810
3337
  this.tradingContext.accountId = accountId;
2811
- console.log('MockApiClient.setAccount Debug - Updated context:', this.tradingContext);
3338
+ this.logger.debug('setAccount updated context', this.buildLoggerExtra('setAccount', {
3339
+ broker: this.tradingContext.broker,
3340
+ account_number_present: Boolean(this.tradingContext.accountNumber),
3341
+ account_id_present: Boolean(this.tradingContext.accountId),
3342
+ }));
2812
3343
  }
2813
3344
  // Stock convenience methods
2814
3345
  async placeStockMarketOrder(symbol, orderQty, action, broker, accountNumber, extras = {}) {
@@ -4590,6 +5121,14 @@ const portalThemePresets = {
4590
5121
  stockAlgos: stockAlgosTheme,
4591
5122
  };
4592
5123
 
5124
+ const themeLogger = setupLogger('FinaticClientSDK.ThemeUtils', undefined, {
5125
+ codebase: 'FinaticClientSDK',
5126
+ });
5127
+ const buildThemeExtra = (functionName, metadata) => ({
5128
+ module: 'ThemeUtils',
5129
+ function: functionName,
5130
+ ...(metadata ? buildLoggerExtra(metadata) : {}),
5131
+ });
4593
5132
  /**
4594
5133
  * Generate a portal URL with theme parameters
4595
5134
  * @param baseUrl The base portal URL
@@ -4615,7 +5154,10 @@ function generatePortalThemeURL(baseUrl, theme) {
4615
5154
  return url.toString();
4616
5155
  }
4617
5156
  catch (error) {
4618
- console.error('Failed to generate theme URL:', error);
5157
+ themeLogger.exception('Failed to generate theme URL', error, buildThemeExtra('generatePortalThemeURL', {
5158
+ base_url: baseUrl,
5159
+ has_theme: Boolean(theme),
5160
+ }));
4619
5161
  return baseUrl;
4620
5162
  }
4621
5163
  }
@@ -4644,7 +5186,10 @@ function appendThemeToURL(baseUrl, theme) {
4644
5186
  return url.toString();
4645
5187
  }
4646
5188
  catch (error) {
4647
- console.error('Failed to append theme to URL:', error);
5189
+ themeLogger.exception('Failed to append theme to URL', error, buildThemeExtra('appendThemeToURL', {
5190
+ base_url: baseUrl,
5191
+ has_theme: Boolean(theme),
5192
+ }));
4648
5193
  return baseUrl;
4649
5194
  }
4650
5195
  }
@@ -4699,7 +5244,7 @@ function validateCustomTheme(theme) {
4699
5244
  return true;
4700
5245
  }
4701
5246
  catch (error) {
4702
- console.error('Theme validation error:', error);
5247
+ themeLogger.exception('Theme validation error', error, buildThemeExtra('validateCustomTheme'));
4703
5248
  return false;
4704
5249
  }
4705
5250
  }
@@ -4712,7 +5257,9 @@ function validateCustomTheme(theme) {
4712
5257
  function createCustomThemeFromPreset(preset, modifications) {
4713
5258
  const baseTheme = getThemePreset(preset);
4714
5259
  if (!baseTheme) {
4715
- console.error(`Preset theme '${preset}' not found`);
5260
+ themeLogger.warn('Preset theme not found', buildThemeExtra('createCustomThemeFromPreset', {
5261
+ preset,
5262
+ }));
4716
5263
  return null;
4717
5264
  }
4718
5265
  return {
@@ -4724,6 +5271,14 @@ function createCustomThemeFromPreset(preset, modifications) {
4724
5271
  /**
4725
5272
  * Broker filtering utility functions
4726
5273
  */
5274
+ const brokerLogger = setupLogger('FinaticClientSDK.BrokerUtils', undefined, {
5275
+ codebase: 'FinaticClientSDK',
5276
+ });
5277
+ const buildBrokerExtra = (functionName, metadata) => ({
5278
+ module: 'BrokerUtils',
5279
+ function: functionName,
5280
+ ...(metadata ? buildLoggerExtra(metadata) : {}),
5281
+ });
4727
5282
  // Supported broker names and their corresponding IDs (including aliases)
4728
5283
  const SUPPORTED_BROKERS = {
4729
5284
  'alpaca': 'alpaca',
@@ -4766,7 +5321,9 @@ function appendBrokerFilterToURL(baseUrl, brokerNames) {
4766
5321
  const url = new URL(baseUrl);
4767
5322
  const { brokerIds, warnings } = convertBrokerNamesToIds(brokerNames);
4768
5323
  // Log warnings for unsupported broker names
4769
- warnings.forEach(warning => console.warn(`[FinaticConnect] ${warning}`));
5324
+ warnings.forEach(warning => brokerLogger.warn('Unsupported broker name provided', buildBrokerExtra('appendBrokerFilterToURL', {
5325
+ warning,
5326
+ })));
4770
5327
  // Only add broker filter if we have valid broker IDs
4771
5328
  if (brokerIds.length > 0) {
4772
5329
  const encodedBrokers = btoa(JSON.stringify(brokerIds));
@@ -4775,12 +5332,30 @@ function appendBrokerFilterToURL(baseUrl, brokerNames) {
4775
5332
  return url.toString();
4776
5333
  }
4777
5334
  catch (error) {
4778
- console.error('Failed to append broker filter to URL:', error);
5335
+ brokerLogger.exception('Failed to append broker filter to URL', error, buildBrokerExtra('appendBrokerFilterToURL', {
5336
+ base_url: baseUrl,
5337
+ brokers_count: brokerNames?.length ?? 0,
5338
+ }));
4779
5339
  return baseUrl;
4780
5340
  }
4781
5341
  }
4782
5342
 
5343
+ const finaticConnectLogger = setupLogger('FinaticClientSDK.FinaticConnect', undefined, {
5344
+ codebase: 'FinaticClientSDK',
5345
+ });
5346
+ const makeFinaticConnectExtra = (functionName, metadata) => ({
5347
+ module: 'FinaticConnect',
5348
+ function: functionName,
5349
+ ...(metadata ? buildLoggerExtra(metadata) : {}),
5350
+ });
4783
5351
  class FinaticConnect extends EventEmitter {
5352
+ buildLoggerExtra(functionName, metadata) {
5353
+ return {
5354
+ module: 'FinaticConnect',
5355
+ function: functionName,
5356
+ ...(metadata ? buildLoggerExtra(metadata) : {}),
5357
+ };
5358
+ }
4784
5359
  constructor(options, deviceInfo) {
4785
5360
  super();
4786
5361
  this.userToken = null;
@@ -4795,6 +5370,7 @@ class FinaticConnect extends EventEmitter {
4795
5370
  this.SESSION_VALIDATION_TIMEOUT = 1000 * 30; // 30 seconds
4796
5371
  this.SESSION_REFRESH_BUFFER_HOURS = 16; // Refresh session at 16 hours
4797
5372
  this.sessionStartTime = null;
5373
+ this.logger = finaticConnectLogger;
4798
5374
  this.options = options;
4799
5375
  this.baseUrl = options.baseUrl || 'https://api.finatic.dev';
4800
5376
  this.apiClient = MockFactory.createApiClient(this.baseUrl, deviceInfo);
@@ -4847,7 +5423,7 @@ class FinaticConnect extends EventEmitter {
4847
5423
  async linkUserToSession(userId) {
4848
5424
  try {
4849
5425
  if (!this.sessionId) {
4850
- console.error('No session ID available for user linking');
5426
+ this.logger.error('No session ID available for user linking', this.buildLoggerExtra('linkUserToSession'));
4851
5427
  return false;
4852
5428
  }
4853
5429
  // Call API endpoint to authenticate user with session
@@ -4859,14 +5435,20 @@ class FinaticConnect extends EventEmitter {
4859
5435
  },
4860
5436
  });
4861
5437
  if (response.error) {
4862
- console.error('Failed to link user to session:', response.error);
5438
+ this.logger.error('Failed to link user to session', {
5439
+ ...this.buildLoggerExtra('linkUserToSession', {
5440
+ session_id: this.sessionId,
5441
+ user_id: userId,
5442
+ }),
5443
+ error: response.error,
5444
+ });
4863
5445
  return false;
4864
5446
  }
4865
- console.log('User linked to session successfully');
5447
+ this.logger.info('User linked to session successfully', this.buildLoggerExtra('linkUserToSession', { session_id: this.sessionId, user_id: userId }));
4866
5448
  return true;
4867
5449
  }
4868
5450
  catch (error) {
4869
- console.error('Error linking user to session:', error);
5451
+ this.logger.exception('Error linking user to session', error, this.buildLoggerExtra('linkUserToSession', { session_id: this.sessionId, user_id: userId }));
4870
5452
  return false;
4871
5453
  }
4872
5454
  }
@@ -5011,7 +5593,7 @@ class FinaticConnect extends EventEmitter {
5011
5593
  // Safari-specific fix: Clear instance if it exists but has no valid session
5012
5594
  // This prevents stale instances from interfering with new requests
5013
5595
  if (FinaticConnect.instance && !FinaticConnect.instance.sessionId) {
5014
- console.log('[FinaticConnect] Clearing stale instance for Safari compatibility');
5596
+ finaticConnectLogger.debug('Clearing stale instance for Safari compatibility', makeFinaticConnectExtra('init'));
5015
5597
  FinaticConnect.instance = null;
5016
5598
  }
5017
5599
  if (!FinaticConnect.instance) {
@@ -5063,7 +5645,7 @@ class FinaticConnect extends EventEmitter {
5063
5645
  FinaticConnect.instance.emit('success', normalizedUserId);
5064
5646
  }
5065
5647
  else {
5066
- console.warn('Failed to link user to session during initialization');
5648
+ finaticConnectLogger.warn('Failed to link user to session during initialization', makeFinaticConnectExtra('init', { user_id: normalizedUserId }));
5067
5649
  }
5068
5650
  }
5069
5651
  catch (error) {
@@ -5093,7 +5675,7 @@ class FinaticConnect extends EventEmitter {
5093
5675
  // Try to link user to session
5094
5676
  const linked = await this.linkUserToSession(userId);
5095
5677
  if (!linked) {
5096
- console.warn('Failed to link user to session during initialization');
5678
+ this.logger.warn('Failed to link user to session during initialization', this.buildLoggerExtra('initializeWithUser', { user_id: userId }));
5097
5679
  // Don't throw error, just continue without authentication
5098
5680
  return;
5099
5681
  }
@@ -5181,7 +5763,7 @@ class FinaticConnect extends EventEmitter {
5181
5763
  // Try to link user to session via API
5182
5764
  const linked = await this.linkUserToSession(userId);
5183
5765
  if (!linked) {
5184
- console.warn('Failed to link user to session, but continuing with authentication');
5766
+ this.logger.warn('Failed to link user to session, continuing with authentication', this.buildLoggerExtra('openPortal.onSuccess', { user_id: userId }));
5185
5767
  }
5186
5768
  // Emit portal success event
5187
5769
  this.emit('portal:success', userId);
@@ -5215,7 +5797,13 @@ class FinaticConnect extends EventEmitter {
5215
5797
  options?.onClose?.();
5216
5798
  },
5217
5799
  onEvent: (type, data) => {
5218
- console.log('[FinaticConnect] Portal event received:', type, data);
5800
+ this.logger.debug('Portal event received', {
5801
+ ...this.buildLoggerExtra('openPortal.onEvent', {
5802
+ event_type: type,
5803
+ payload_present: Boolean(data),
5804
+ }),
5805
+ event: 'portal-event',
5806
+ });
5219
5807
  // Emit generic event
5220
5808
  this.emit('event', type, data);
5221
5809
  // Call the event callback
@@ -5726,7 +6314,7 @@ class FinaticConnect extends EventEmitter {
5726
6314
  this.sessionKeepAliveInterval = setInterval(() => {
5727
6315
  this.validateSessionKeepAlive();
5728
6316
  }, this.SESSION_KEEP_ALIVE_INTERVAL);
5729
- console.log('[FinaticConnect] Session keep-alive started (5-minute intervals)');
6317
+ this.logger.debug('Session keep-alive started', this.buildLoggerExtra('startSessionKeepAlive', { interval_ms: this.SESSION_KEEP_ALIVE_INTERVAL }));
5730
6318
  }
5731
6319
  /**
5732
6320
  * Stop the session keep-alive mechanism
@@ -5735,7 +6323,7 @@ class FinaticConnect extends EventEmitter {
5735
6323
  if (this.sessionKeepAliveInterval) {
5736
6324
  clearInterval(this.sessionKeepAliveInterval);
5737
6325
  this.sessionKeepAliveInterval = null;
5738
- console.log('[FinaticConnect] Session keep-alive stopped');
6326
+ this.logger.debug('Session keep-alive stopped', this.buildLoggerExtra('stopSessionKeepAlive'));
5739
6327
  }
5740
6328
  }
5741
6329
  /**
@@ -5743,22 +6331,22 @@ class FinaticConnect extends EventEmitter {
5743
6331
  */
5744
6332
  async validateSessionKeepAlive() {
5745
6333
  if (!this.sessionId || !(await this.isAuthenticated())) {
5746
- console.log('[FinaticConnect] Session keep-alive skipped - no active session');
6334
+ this.logger.debug('Session keep-alive skipped - no active session', this.buildLoggerExtra('validateSessionKeepAlive'));
5747
6335
  return;
5748
6336
  }
5749
6337
  try {
5750
- console.log('[FinaticConnect] Validating session for keep-alive...');
6338
+ this.logger.debug('Validating session for keep-alive', this.buildLoggerExtra('validateSessionKeepAlive', { session_id: this.sessionId }));
5751
6339
  // Check if we need to refresh the session (at 16 hours)
5752
6340
  if (this.shouldRefreshSession()) {
5753
6341
  await this.refreshSessionAutomatically();
5754
6342
  return;
5755
6343
  }
5756
6344
  // Session keep-alive - assume session is active if we have a session ID
5757
- console.log('[FinaticConnect] Session keep-alive successful');
6345
+ this.logger.debug('Session keep-alive successful', this.buildLoggerExtra('validateSessionKeepAlive', { session_id: this.sessionId }));
5758
6346
  this.currentSessionState = 'active';
5759
6347
  }
5760
6348
  catch (error) {
5761
- console.warn('[FinaticConnect] Session keep-alive error:', error);
6349
+ this.logger.exception('Session keep-alive error', error, this.buildLoggerExtra('validateSessionKeepAlive', { session_id: this.sessionId }));
5762
6350
  // Don't throw errors during keep-alive - just log them
5763
6351
  }
5764
6352
  }
@@ -5772,12 +6360,16 @@ class FinaticConnect extends EventEmitter {
5772
6360
  const sessionAgeHours = (Date.now() - this.sessionStartTime) / (1000 * 60 * 60);
5773
6361
  const hoursUntilRefresh = this.SESSION_REFRESH_BUFFER_HOURS - sessionAgeHours;
5774
6362
  if (hoursUntilRefresh <= 0) {
5775
- console.log(`[FinaticConnect] Session is ${sessionAgeHours.toFixed(1)} hours old - triggering refresh`);
6363
+ this.logger.info('Session age threshold exceeded - triggering refresh', this.buildLoggerExtra('shouldRefreshSession', {
6364
+ session_age_hours: Number(sessionAgeHours.toFixed(1)),
6365
+ }));
5776
6366
  return true;
5777
6367
  }
5778
6368
  // Log when refresh will occur (every 5 minutes during keep-alive)
5779
6369
  if (hoursUntilRefresh <= 1) {
5780
- console.log(`[FinaticConnect] Session will refresh in ${hoursUntilRefresh.toFixed(1)} hours`);
6370
+ this.logger.debug('Session refresh scheduled', this.buildLoggerExtra('shouldRefreshSession', {
6371
+ hours_until_refresh: Number(hoursUntilRefresh.toFixed(1)),
6372
+ }));
5781
6373
  }
5782
6374
  return false;
5783
6375
  }
@@ -5786,25 +6378,34 @@ class FinaticConnect extends EventEmitter {
5786
6378
  */
5787
6379
  async refreshSessionAutomatically() {
5788
6380
  if (!this.sessionId) {
5789
- console.warn('[FinaticConnect] Cannot refresh session - no session ID');
6381
+ this.logger.warn('Cannot refresh session - missing session ID', this.buildLoggerExtra('refreshSessionAutomatically'));
5790
6382
  return;
5791
6383
  }
5792
6384
  try {
5793
- console.log('[FinaticConnect] Automatically refreshing session (16+ hours old)...');
6385
+ this.logger.info('Automatically refreshing session', this.buildLoggerExtra('refreshSessionAutomatically', {
6386
+ session_id: this.sessionId,
6387
+ }));
5794
6388
  const response = await this.apiClient.refreshSession();
5795
6389
  if (response.success) {
5796
- console.log('[FinaticConnect] Session automatically refreshed successfully');
5797
- console.log('[FinaticConnect] New session expires at:', response.response_data.expires_at);
6390
+ this.logger.info('Session automatically refreshed successfully', this.buildLoggerExtra('refreshSessionAutomatically', {
6391
+ session_id: this.sessionId,
6392
+ status: response.response_data.status,
6393
+ expires_at: response.response_data.expires_at,
6394
+ }));
5798
6395
  this.currentSessionState = response.response_data.status;
5799
6396
  // Update session start time to prevent immediate re-refresh
5800
6397
  this.sessionStartTime = Date.now();
5801
6398
  }
5802
6399
  else {
5803
- console.warn('[FinaticConnect] Automatic session refresh failed');
6400
+ this.logger.warn('Automatic session refresh failed', this.buildLoggerExtra('refreshSessionAutomatically', {
6401
+ session_id: this.sessionId,
6402
+ }));
5804
6403
  }
5805
6404
  }
5806
6405
  catch (error) {
5807
- console.warn('[FinaticConnect] Automatic session refresh error:', error);
6406
+ this.logger.exception('Automatic session refresh error', error, this.buildLoggerExtra('refreshSessionAutomatically', {
6407
+ session_id: this.sessionId,
6408
+ }));
5808
6409
  // Don't throw errors during automatic refresh - just log them
5809
6410
  }
5810
6411
  }
@@ -5824,7 +6425,9 @@ class FinaticConnect extends EventEmitter {
5824
6425
  async handleVisibilityChange() {
5825
6426
  // For 24-hour sessions, we don't want to complete sessions on visibility changes
5826
6427
  // This prevents sessions from being closed when users switch tabs or apps
5827
- console.log('[FinaticConnect] Page visibility changed to:', document.visibilityState);
6428
+ this.logger.debug('Page visibility changed', this.buildLoggerExtra('handleVisibilityChange', {
6429
+ visibility_state: document.visibilityState,
6430
+ }));
5828
6431
  // Only pause keep-alive when hidden, but don't complete the session
5829
6432
  if (document.visibilityState === 'hidden') {
5830
6433
  this.stopSessionKeepAlive();
@@ -5846,7 +6449,7 @@ class FinaticConnect extends EventEmitter {
5846
6449
  this.apiClient.isMockClient();
5847
6450
  if (isMockMode) {
5848
6451
  // Mock the completion response
5849
- console.log('[FinaticConnect] Mock session completion for session:', sessionId);
6452
+ this.logger.debug('Mock session completion', this.buildLoggerExtra('completeSession', { session_id: sessionId }));
5850
6453
  return;
5851
6454
  }
5852
6455
  // Real API call
@@ -5857,15 +6460,18 @@ class FinaticConnect extends EventEmitter {
5857
6460
  },
5858
6461
  });
5859
6462
  if (response.ok) {
5860
- console.log('[FinaticConnect] Session completed successfully');
6463
+ this.logger.info('Session completed successfully', this.buildLoggerExtra('completeSession', { session_id: sessionId }));
5861
6464
  }
5862
6465
  else {
5863
- console.warn('[FinaticConnect] Failed to complete session:', response.status);
6466
+ this.logger.warn('Failed to complete session', this.buildLoggerExtra('completeSession', {
6467
+ session_id: sessionId,
6468
+ response_status: response.status,
6469
+ }));
5864
6470
  }
5865
6471
  }
5866
6472
  catch (error) {
5867
6473
  // Silent failure - don't throw errors during cleanup
5868
- console.warn('[FinaticConnect] Session cleanup failed:', error);
6474
+ this.logger.exception('Session cleanup failed', error, this.buildLoggerExtra('completeSession', { session_id: sessionId }));
5869
6475
  }
5870
6476
  }
5871
6477
  /**
@@ -5883,8 +6489,71 @@ class FinaticConnect extends EventEmitter {
5883
6489
  }
5884
6490
  return this.apiClient.disconnectCompany(connectionId);
5885
6491
  }
6492
+ /**
6493
+ * Get order fills for a specific order
6494
+ * @param orderId - The order ID
6495
+ * @param filter - Optional filter parameters
6496
+ * @returns Promise with order fills response
6497
+ */
6498
+ async getOrderFills(orderId, filter) {
6499
+ if (!(await this.isAuthenticated())) {
6500
+ throw new AuthenticationError('User is not authenticated');
6501
+ }
6502
+ const response = await this.apiClient.getOrderFills(orderId, filter);
6503
+ return response.response_data;
6504
+ }
6505
+ /**
6506
+ * Get order events for a specific order
6507
+ * @param orderId - The order ID
6508
+ * @param filter - Optional filter parameters
6509
+ * @returns Promise with order events response
6510
+ */
6511
+ async getOrderEvents(orderId, filter) {
6512
+ if (!(await this.isAuthenticated())) {
6513
+ throw new AuthenticationError('User is not authenticated');
6514
+ }
6515
+ const response = await this.apiClient.getOrderEvents(orderId, filter);
6516
+ return response.response_data;
6517
+ }
6518
+ /**
6519
+ * Get order groups
6520
+ * @param filter - Optional filter parameters
6521
+ * @returns Promise with order groups response
6522
+ */
6523
+ async getOrderGroups(filter) {
6524
+ if (!(await this.isAuthenticated())) {
6525
+ throw new AuthenticationError('User is not authenticated');
6526
+ }
6527
+ const response = await this.apiClient.getOrderGroups(filter);
6528
+ return response.response_data;
6529
+ }
6530
+ /**
6531
+ * Get position lots (tax lots for positions)
6532
+ * @param filter - Optional filter parameters
6533
+ * @returns Promise with position lots response
6534
+ */
6535
+ async getPositionLots(filter) {
6536
+ if (!(await this.isAuthenticated())) {
6537
+ throw new AuthenticationError('User is not authenticated');
6538
+ }
6539
+ const response = await this.apiClient.getPositionLots(filter);
6540
+ return response.response_data;
6541
+ }
6542
+ /**
6543
+ * Get position lot fills for a specific lot
6544
+ * @param lotId - The position lot ID
6545
+ * @param filter - Optional filter parameters
6546
+ * @returns Promise with position lot fills response
6547
+ */
6548
+ async getPositionLotFills(lotId, filter) {
6549
+ if (!(await this.isAuthenticated())) {
6550
+ throw new AuthenticationError('User is not authenticated');
6551
+ }
6552
+ const response = await this.apiClient.getPositionLotFills(lotId, filter);
6553
+ return response.response_data;
6554
+ }
5886
6555
  }
5887
6556
  FinaticConnect.instance = null;
5888
6557
 
5889
- export { ApiClient, ApiError, AuthenticationError, AuthorizationError, BaseError, CompanyAccessError, EventEmitter, FinaticConnect, NetworkError, OrderError, OrderValidationError, PaginatedResult, RateLimitError, SecurityError, SessionError, TokenError, TradingNotEnabledError, ValidationError, appendThemeToURL, createCustomThemeFromPreset, generatePortalThemeURL, getThemePreset, portalThemePresets, validateCustomTheme };
6558
+ export { ApiClient, ApiError, AuthenticationError, AuthorizationError, BaseError, CompanyAccessError, EventEmitter, FinaticConnect, NetworkError, OrderError, OrderValidationError, PaginatedResult, RateLimitError, SecurityError, SessionError, TokenError, TradingNotEnabledError, ValidationError, appendThemeToURL, buildLoggerExtra, createCustomThemeFromPreset, generatePortalThemeURL, getThemePreset, logStartEnd, portalThemePresets, setupLogger, validateCustomTheme };
5890
6559
  //# sourceMappingURL=index.mjs.map