@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/README.md +72 -0
- package/dist/index.d.ts +256 -2
- package/dist/index.js +767 -94
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +764 -95
- package/dist/index.mjs.map +1 -1
- package/dist/types/core/client/ApiClient.d.ts +71 -1
- package/dist/types/core/client/FinaticConnect.d.ts +35 -0
- package/dist/types/core/portal/PortalUI.d.ts +2 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/lib/logger/index.d.ts +2 -0
- package/dist/types/lib/logger/logger.d.ts +4 -0
- package/dist/types/lib/logger/logger.types.d.ts +28 -0
- package/dist/types/mocks/MockApiClient.d.ts +2 -0
- package/dist/types/types/api/broker.d.ts +116 -0
- package/package.json +1 -1
- package/src/core/client/ApiClient.ts +302 -19
- package/src/core/client/FinaticConnect.ts +160 -30
- package/src/core/portal/PortalUI.ts +58 -23
- package/src/index.ts +13 -0
- package/src/lib/logger/index.ts +3 -0
- package/src/lib/logger/logger.ts +332 -0
- package/src/lib/logger/logger.types.ts +34 -0
- package/src/mocks/MockApiClient.ts +43 -17
- package/src/types/api/broker.ts +131 -0
- package/src/types/common/pagination.ts +31 -5
- package/src/utils/brokerUtils.ts +21 -2
- package/src/utils/events.ts +13 -1
- package/src/utils/themeUtils.ts +23 -4
|
@@ -27,6 +27,17 @@ import {
|
|
|
27
27
|
import { FinaticConnectOptions, PortalOptions } from '../../types/connect';
|
|
28
28
|
import { appendThemeToURL } from '../../utils/themeUtils';
|
|
29
29
|
import { appendBrokerFilterToURL } from '../../utils/brokerUtils';
|
|
30
|
+
import { setupLogger, buildLoggerExtra, LoggerExtra } from '../../lib/logger';
|
|
31
|
+
|
|
32
|
+
const finaticConnectLogger = setupLogger('FinaticClientSDK.FinaticConnect', undefined, {
|
|
33
|
+
codebase: 'FinaticClientSDK',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const makeFinaticConnectExtra = (functionName: string, metadata?: Record<string, unknown>): LoggerExtra => ({
|
|
37
|
+
module: 'FinaticConnect',
|
|
38
|
+
function: functionName,
|
|
39
|
+
...(metadata ? buildLoggerExtra(metadata) : {}),
|
|
40
|
+
});
|
|
30
41
|
// Supabase import removed - SDK no longer depends on Supabase
|
|
31
42
|
|
|
32
43
|
interface DeviceInfo {
|
|
@@ -57,6 +68,16 @@ export class FinaticConnect extends EventEmitter {
|
|
|
57
68
|
private readonly SESSION_REFRESH_BUFFER_HOURS = 16; // Refresh session at 16 hours
|
|
58
69
|
private sessionStartTime: number | null = null;
|
|
59
70
|
|
|
71
|
+
private readonly logger = finaticConnectLogger;
|
|
72
|
+
|
|
73
|
+
private buildLoggerExtra(functionName: string, metadata?: Record<string, unknown>): LoggerExtra {
|
|
74
|
+
return {
|
|
75
|
+
module: 'FinaticConnect',
|
|
76
|
+
function: functionName,
|
|
77
|
+
...(metadata ? buildLoggerExtra(metadata) : {}),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
60
81
|
constructor(options: FinaticConnectOptions, deviceInfo?: DeviceInfo) {
|
|
61
82
|
super();
|
|
62
83
|
this.options = options;
|
|
@@ -112,7 +133,7 @@ export class FinaticConnect extends EventEmitter {
|
|
|
112
133
|
private async linkUserToSession(userId: string): Promise<boolean> {
|
|
113
134
|
try {
|
|
114
135
|
if (!this.sessionId) {
|
|
115
|
-
|
|
136
|
+
this.logger.error('No session ID available for user linking', this.buildLoggerExtra('linkUserToSession'));
|
|
116
137
|
return false;
|
|
117
138
|
}
|
|
118
139
|
|
|
@@ -126,14 +147,20 @@ export class FinaticConnect extends EventEmitter {
|
|
|
126
147
|
});
|
|
127
148
|
|
|
128
149
|
if (response.error) {
|
|
129
|
-
|
|
150
|
+
this.logger.error('Failed to link user to session', {
|
|
151
|
+
...this.buildLoggerExtra('linkUserToSession', {
|
|
152
|
+
session_id: this.sessionId,
|
|
153
|
+
user_id: userId,
|
|
154
|
+
}),
|
|
155
|
+
error: response.error,
|
|
156
|
+
});
|
|
130
157
|
return false;
|
|
131
158
|
}
|
|
132
159
|
|
|
133
|
-
|
|
160
|
+
this.logger.info('User linked to session successfully', this.buildLoggerExtra('linkUserToSession', { session_id: this.sessionId, user_id: userId }));
|
|
134
161
|
return true;
|
|
135
162
|
} catch (error) {
|
|
136
|
-
|
|
163
|
+
this.logger.exception('Error linking user to session', error, this.buildLoggerExtra('linkUserToSession', { session_id: this.sessionId, user_id: userId }));
|
|
137
164
|
return false;
|
|
138
165
|
}
|
|
139
166
|
}
|
|
@@ -322,7 +349,7 @@ export class FinaticConnect extends EventEmitter {
|
|
|
322
349
|
// Safari-specific fix: Clear instance if it exists but has no valid session
|
|
323
350
|
// This prevents stale instances from interfering with new requests
|
|
324
351
|
if (FinaticConnect.instance && !FinaticConnect.instance.sessionId) {
|
|
325
|
-
|
|
352
|
+
finaticConnectLogger.debug('Clearing stale instance for Safari compatibility', makeFinaticConnectExtra('init'));
|
|
326
353
|
FinaticConnect.instance = null;
|
|
327
354
|
}
|
|
328
355
|
|
|
@@ -390,7 +417,7 @@ export class FinaticConnect extends EventEmitter {
|
|
|
390
417
|
// Emit success event
|
|
391
418
|
FinaticConnect.instance.emit('success', normalizedUserId);
|
|
392
419
|
} else {
|
|
393
|
-
|
|
420
|
+
finaticConnectLogger.warn('Failed to link user to session during initialization', makeFinaticConnectExtra('init', { user_id: normalizedUserId }));
|
|
394
421
|
}
|
|
395
422
|
} catch (error) {
|
|
396
423
|
FinaticConnect.instance.emit('error', error as Error);
|
|
@@ -423,7 +450,7 @@ export class FinaticConnect extends EventEmitter {
|
|
|
423
450
|
// Try to link user to session
|
|
424
451
|
const linked = await this.linkUserToSession(userId);
|
|
425
452
|
if (!linked) {
|
|
426
|
-
|
|
453
|
+
this.logger.warn('Failed to link user to session during initialization', this.buildLoggerExtra('initializeWithUser', { user_id: userId }));
|
|
427
454
|
// Don't throw error, just continue without authentication
|
|
428
455
|
return;
|
|
429
456
|
}
|
|
@@ -537,7 +564,7 @@ export class FinaticConnect extends EventEmitter {
|
|
|
537
564
|
// Try to link user to session via API
|
|
538
565
|
const linked = await this.linkUserToSession(userId);
|
|
539
566
|
if (!linked) {
|
|
540
|
-
|
|
567
|
+
this.logger.warn('Failed to link user to session, continuing with authentication', this.buildLoggerExtra('openPortal.onSuccess', { user_id: userId }));
|
|
541
568
|
}
|
|
542
569
|
|
|
543
570
|
// Emit portal success event
|
|
@@ -573,7 +600,13 @@ export class FinaticConnect extends EventEmitter {
|
|
|
573
600
|
options?.onClose?.();
|
|
574
601
|
},
|
|
575
602
|
onEvent: (type: string, data: any) => {
|
|
576
|
-
|
|
603
|
+
this.logger.debug('Portal event received', {
|
|
604
|
+
...this.buildLoggerExtra('openPortal.onEvent', {
|
|
605
|
+
event_type: type,
|
|
606
|
+
payload_present: Boolean(data),
|
|
607
|
+
}),
|
|
608
|
+
event: 'portal-event',
|
|
609
|
+
});
|
|
577
610
|
|
|
578
611
|
// Emit generic event
|
|
579
612
|
this.emit('event', type, data);
|
|
@@ -1293,7 +1326,7 @@ export class FinaticConnect extends EventEmitter {
|
|
|
1293
1326
|
this.validateSessionKeepAlive();
|
|
1294
1327
|
}, this.SESSION_KEEP_ALIVE_INTERVAL);
|
|
1295
1328
|
|
|
1296
|
-
|
|
1329
|
+
this.logger.debug('Session keep-alive started', this.buildLoggerExtra('startSessionKeepAlive', { interval_ms: this.SESSION_KEEP_ALIVE_INTERVAL }));
|
|
1297
1330
|
}
|
|
1298
1331
|
|
|
1299
1332
|
/**
|
|
@@ -1303,7 +1336,7 @@ export class FinaticConnect extends EventEmitter {
|
|
|
1303
1336
|
if (this.sessionKeepAliveInterval) {
|
|
1304
1337
|
clearInterval(this.sessionKeepAliveInterval);
|
|
1305
1338
|
this.sessionKeepAliveInterval = null;
|
|
1306
|
-
|
|
1339
|
+
this.logger.debug('Session keep-alive stopped', this.buildLoggerExtra('stopSessionKeepAlive'));
|
|
1307
1340
|
}
|
|
1308
1341
|
}
|
|
1309
1342
|
|
|
@@ -1312,12 +1345,12 @@ export class FinaticConnect extends EventEmitter {
|
|
|
1312
1345
|
*/
|
|
1313
1346
|
private async validateSessionKeepAlive(): Promise<void> {
|
|
1314
1347
|
if (!this.sessionId || !(await this.isAuthenticated())) {
|
|
1315
|
-
|
|
1348
|
+
this.logger.debug('Session keep-alive skipped - no active session', this.buildLoggerExtra('validateSessionKeepAlive'));
|
|
1316
1349
|
return;
|
|
1317
1350
|
}
|
|
1318
1351
|
|
|
1319
1352
|
try {
|
|
1320
|
-
|
|
1353
|
+
this.logger.debug('Validating session for keep-alive', this.buildLoggerExtra('validateSessionKeepAlive', { session_id: this.sessionId }));
|
|
1321
1354
|
|
|
1322
1355
|
// Check if we need to refresh the session (at 16 hours)
|
|
1323
1356
|
if (this.shouldRefreshSession()) {
|
|
@@ -1326,10 +1359,10 @@ export class FinaticConnect extends EventEmitter {
|
|
|
1326
1359
|
}
|
|
1327
1360
|
|
|
1328
1361
|
// Session keep-alive - assume session is active if we have a session ID
|
|
1329
|
-
|
|
1362
|
+
this.logger.debug('Session keep-alive successful', this.buildLoggerExtra('validateSessionKeepAlive', { session_id: this.sessionId }));
|
|
1330
1363
|
this.currentSessionState = 'active';
|
|
1331
1364
|
} catch (error) {
|
|
1332
|
-
|
|
1365
|
+
this.logger.exception('Session keep-alive error', error, this.buildLoggerExtra('validateSessionKeepAlive', { session_id: this.sessionId }));
|
|
1333
1366
|
// Don't throw errors during keep-alive - just log them
|
|
1334
1367
|
}
|
|
1335
1368
|
}
|
|
@@ -1346,15 +1379,17 @@ export class FinaticConnect extends EventEmitter {
|
|
|
1346
1379
|
const hoursUntilRefresh = this.SESSION_REFRESH_BUFFER_HOURS - sessionAgeHours;
|
|
1347
1380
|
|
|
1348
1381
|
if (hoursUntilRefresh <= 0) {
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
);
|
|
1382
|
+
this.logger.info('Session age threshold exceeded - triggering refresh', this.buildLoggerExtra('shouldRefreshSession', {
|
|
1383
|
+
session_age_hours: Number(sessionAgeHours.toFixed(1)),
|
|
1384
|
+
}));
|
|
1352
1385
|
return true;
|
|
1353
1386
|
}
|
|
1354
1387
|
|
|
1355
1388
|
// Log when refresh will occur (every 5 minutes during keep-alive)
|
|
1356
1389
|
if (hoursUntilRefresh <= 1) {
|
|
1357
|
-
|
|
1390
|
+
this.logger.debug('Session refresh scheduled', this.buildLoggerExtra('shouldRefreshSession', {
|
|
1391
|
+
hours_until_refresh: Number(hoursUntilRefresh.toFixed(1)),
|
|
1392
|
+
}));
|
|
1358
1393
|
}
|
|
1359
1394
|
|
|
1360
1395
|
return false;
|
|
@@ -1365,26 +1400,35 @@ export class FinaticConnect extends EventEmitter {
|
|
|
1365
1400
|
*/
|
|
1366
1401
|
private async refreshSessionAutomatically(): Promise<void> {
|
|
1367
1402
|
if (!this.sessionId) {
|
|
1368
|
-
|
|
1403
|
+
this.logger.warn('Cannot refresh session - missing session ID', this.buildLoggerExtra('refreshSessionAutomatically'));
|
|
1369
1404
|
return;
|
|
1370
1405
|
}
|
|
1371
1406
|
|
|
1372
1407
|
try {
|
|
1373
|
-
|
|
1408
|
+
this.logger.info('Automatically refreshing session', this.buildLoggerExtra('refreshSessionAutomatically', {
|
|
1409
|
+
session_id: this.sessionId,
|
|
1410
|
+
}));
|
|
1374
1411
|
const response = await this.apiClient.refreshSession();
|
|
1375
1412
|
|
|
1376
1413
|
if (response.success) {
|
|
1377
|
-
|
|
1378
|
-
|
|
1414
|
+
this.logger.info('Session automatically refreshed successfully', this.buildLoggerExtra('refreshSessionAutomatically', {
|
|
1415
|
+
session_id: this.sessionId,
|
|
1416
|
+
status: response.response_data.status,
|
|
1417
|
+
expires_at: response.response_data.expires_at,
|
|
1418
|
+
}));
|
|
1379
1419
|
this.currentSessionState = response.response_data.status;
|
|
1380
1420
|
|
|
1381
1421
|
// Update session start time to prevent immediate re-refresh
|
|
1382
1422
|
this.sessionStartTime = Date.now();
|
|
1383
1423
|
} else {
|
|
1384
|
-
|
|
1424
|
+
this.logger.warn('Automatic session refresh failed', this.buildLoggerExtra('refreshSessionAutomatically', {
|
|
1425
|
+
session_id: this.sessionId,
|
|
1426
|
+
}));
|
|
1385
1427
|
}
|
|
1386
1428
|
} catch (error) {
|
|
1387
|
-
|
|
1429
|
+
this.logger.exception('Automatic session refresh error', error, this.buildLoggerExtra('refreshSessionAutomatically', {
|
|
1430
|
+
session_id: this.sessionId,
|
|
1431
|
+
}));
|
|
1388
1432
|
// Don't throw errors during automatic refresh - just log them
|
|
1389
1433
|
}
|
|
1390
1434
|
}
|
|
@@ -1406,7 +1450,9 @@ export class FinaticConnect extends EventEmitter {
|
|
|
1406
1450
|
private async handleVisibilityChange(): Promise<void> {
|
|
1407
1451
|
// For 24-hour sessions, we don't want to complete sessions on visibility changes
|
|
1408
1452
|
// This prevents sessions from being closed when users switch tabs or apps
|
|
1409
|
-
|
|
1453
|
+
this.logger.debug('Page visibility changed', this.buildLoggerExtra('handleVisibilityChange', {
|
|
1454
|
+
visibility_state: document.visibilityState,
|
|
1455
|
+
}));
|
|
1410
1456
|
|
|
1411
1457
|
// Only pause keep-alive when hidden, but don't complete the session
|
|
1412
1458
|
if (document.visibilityState === 'hidden') {
|
|
@@ -1431,7 +1477,7 @@ export class FinaticConnect extends EventEmitter {
|
|
|
1431
1477
|
|
|
1432
1478
|
if (isMockMode) {
|
|
1433
1479
|
// Mock the completion response
|
|
1434
|
-
|
|
1480
|
+
this.logger.debug('Mock session completion', this.buildLoggerExtra('completeSession', { session_id: sessionId }));
|
|
1435
1481
|
return;
|
|
1436
1482
|
}
|
|
1437
1483
|
|
|
@@ -1444,13 +1490,16 @@ export class FinaticConnect extends EventEmitter {
|
|
|
1444
1490
|
});
|
|
1445
1491
|
|
|
1446
1492
|
if (response.ok) {
|
|
1447
|
-
|
|
1493
|
+
this.logger.info('Session completed successfully', this.buildLoggerExtra('completeSession', { session_id: sessionId }));
|
|
1448
1494
|
} else {
|
|
1449
|
-
|
|
1495
|
+
this.logger.warn('Failed to complete session', this.buildLoggerExtra('completeSession', {
|
|
1496
|
+
session_id: sessionId,
|
|
1497
|
+
response_status: response.status,
|
|
1498
|
+
}));
|
|
1450
1499
|
}
|
|
1451
1500
|
} catch (error) {
|
|
1452
1501
|
// Silent failure - don't throw errors during cleanup
|
|
1453
|
-
|
|
1502
|
+
this.logger.exception('Session cleanup failed', error, this.buildLoggerExtra('completeSession', { session_id: sessionId }));
|
|
1454
1503
|
}
|
|
1455
1504
|
}
|
|
1456
1505
|
|
|
@@ -1471,6 +1520,87 @@ export class FinaticConnect extends EventEmitter {
|
|
|
1471
1520
|
return this.apiClient.disconnectCompany(connectionId);
|
|
1472
1521
|
}
|
|
1473
1522
|
|
|
1523
|
+
/**
|
|
1524
|
+
* Get order fills for a specific order
|
|
1525
|
+
* @param orderId - The order ID
|
|
1526
|
+
* @param filter - Optional filter parameters
|
|
1527
|
+
* @returns Promise with order fills response
|
|
1528
|
+
*/
|
|
1529
|
+
public async getOrderFills(
|
|
1530
|
+
orderId: string,
|
|
1531
|
+
filter?: import('../../types/api/broker').OrderFillsFilter
|
|
1532
|
+
): Promise<import('../../types/api/broker').OrderFill[]> {
|
|
1533
|
+
if (!(await this.isAuthenticated())) {
|
|
1534
|
+
throw new AuthenticationError('User is not authenticated');
|
|
1535
|
+
}
|
|
1536
|
+
const response = await this.apiClient.getOrderFills(orderId, filter);
|
|
1537
|
+
return response.response_data;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
/**
|
|
1541
|
+
* Get order events for a specific order
|
|
1542
|
+
* @param orderId - The order ID
|
|
1543
|
+
* @param filter - Optional filter parameters
|
|
1544
|
+
* @returns Promise with order events response
|
|
1545
|
+
*/
|
|
1546
|
+
public async getOrderEvents(
|
|
1547
|
+
orderId: string,
|
|
1548
|
+
filter?: import('../../types/api/broker').OrderEventsFilter
|
|
1549
|
+
): Promise<import('../../types/api/broker').OrderEvent[]> {
|
|
1550
|
+
if (!(await this.isAuthenticated())) {
|
|
1551
|
+
throw new AuthenticationError('User is not authenticated');
|
|
1552
|
+
}
|
|
1553
|
+
const response = await this.apiClient.getOrderEvents(orderId, filter);
|
|
1554
|
+
return response.response_data;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
/**
|
|
1558
|
+
* Get order groups
|
|
1559
|
+
* @param filter - Optional filter parameters
|
|
1560
|
+
* @returns Promise with order groups response
|
|
1561
|
+
*/
|
|
1562
|
+
public async getOrderGroups(
|
|
1563
|
+
filter?: import('../../types/api/broker').OrderGroupsFilter
|
|
1564
|
+
): Promise<import('../../types/api/broker').OrderGroup[]> {
|
|
1565
|
+
if (!(await this.isAuthenticated())) {
|
|
1566
|
+
throw new AuthenticationError('User is not authenticated');
|
|
1567
|
+
}
|
|
1568
|
+
const response = await this.apiClient.getOrderGroups(filter);
|
|
1569
|
+
return response.response_data;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
/**
|
|
1573
|
+
* Get position lots (tax lots for positions)
|
|
1574
|
+
* @param filter - Optional filter parameters
|
|
1575
|
+
* @returns Promise with position lots response
|
|
1576
|
+
*/
|
|
1577
|
+
public async getPositionLots(
|
|
1578
|
+
filter?: import('../../types/api/broker').PositionLotsFilter
|
|
1579
|
+
): Promise<import('../../types/api/broker').PositionLot[]> {
|
|
1580
|
+
if (!(await this.isAuthenticated())) {
|
|
1581
|
+
throw new AuthenticationError('User is not authenticated');
|
|
1582
|
+
}
|
|
1583
|
+
const response = await this.apiClient.getPositionLots(filter);
|
|
1584
|
+
return response.response_data;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
/**
|
|
1588
|
+
* Get position lot fills for a specific lot
|
|
1589
|
+
* @param lotId - The position lot ID
|
|
1590
|
+
* @param filter - Optional filter parameters
|
|
1591
|
+
* @returns Promise with position lot fills response
|
|
1592
|
+
*/
|
|
1593
|
+
public async getPositionLotFills(
|
|
1594
|
+
lotId: string,
|
|
1595
|
+
filter?: import('../../types/api/broker').PositionLotFillsFilter
|
|
1596
|
+
): Promise<import('../../types/api/broker').PositionLotFill[]> {
|
|
1597
|
+
if (!(await this.isAuthenticated())) {
|
|
1598
|
+
throw new AuthenticationError('User is not authenticated');
|
|
1599
|
+
}
|
|
1600
|
+
const response = await this.apiClient.getPositionLotFills(lotId, filter);
|
|
1601
|
+
return response.response_data;
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1474
1604
|
// Duplicate getBalances method removed - using the paginated version above
|
|
1475
1605
|
|
|
1476
1606
|
}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import { setupLogger, buildLoggerExtra, LoggerExtra } from '../../lib/logger';
|
|
2
|
+
|
|
3
|
+
const portalLogger = setupLogger('FinaticClientSDK.PortalUI', undefined, {
|
|
4
|
+
codebase: 'FinaticClientSDK',
|
|
5
|
+
});
|
|
6
|
+
|
|
1
7
|
export class PortalUI {
|
|
2
8
|
private iframe: HTMLIFrameElement | null = null;
|
|
3
9
|
private container: HTMLDivElement | null = null;
|
|
@@ -12,12 +18,22 @@ export class PortalUI {
|
|
|
12
18
|
};
|
|
13
19
|
private originalBodyStyle: string | null = null;
|
|
14
20
|
|
|
21
|
+
private readonly logger = portalLogger;
|
|
22
|
+
|
|
15
23
|
constructor(portalUrl: string) {
|
|
16
24
|
this.createContainer();
|
|
17
25
|
}
|
|
18
26
|
|
|
27
|
+
private buildLoggerExtra(functionName: string, metadata?: Record<string, unknown>): LoggerExtra {
|
|
28
|
+
return {
|
|
29
|
+
module: 'PortalUI',
|
|
30
|
+
function: functionName,
|
|
31
|
+
...(metadata ? buildLoggerExtra(metadata) : {}),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
19
35
|
private createContainer(): void {
|
|
20
|
-
|
|
36
|
+
this.logger.debug('Creating portal container and iframe', this.buildLoggerExtra('createContainer'));
|
|
21
37
|
this.container = document.createElement('div');
|
|
22
38
|
this.container.style.cssText = `
|
|
23
39
|
position: fixed;
|
|
@@ -74,7 +90,7 @@ export class PortalUI {
|
|
|
74
90
|
|
|
75
91
|
this.container.appendChild(this.iframe);
|
|
76
92
|
document.body.appendChild(this.container);
|
|
77
|
-
|
|
93
|
+
this.logger.debug('Portal container and iframe created successfully', this.buildLoggerExtra('createContainer'));
|
|
78
94
|
}
|
|
79
95
|
|
|
80
96
|
/**
|
|
@@ -91,7 +107,7 @@ export class PortalUI {
|
|
|
91
107
|
document.body.style.width = '100%';
|
|
92
108
|
document.body.style.top = `-${window.scrollY}px`;
|
|
93
109
|
|
|
94
|
-
|
|
110
|
+
this.logger.debug('Background scroll locked', this.buildLoggerExtra('lockScroll'));
|
|
95
111
|
}
|
|
96
112
|
}
|
|
97
113
|
|
|
@@ -110,7 +126,7 @@ export class PortalUI {
|
|
|
110
126
|
window.scrollTo(0, parseInt(scrollY || '0') * -1);
|
|
111
127
|
|
|
112
128
|
this.originalBodyStyle = null;
|
|
113
|
-
|
|
129
|
+
this.logger.debug('Background scroll unlocked', this.buildLoggerExtra('unlockScroll'));
|
|
114
130
|
}
|
|
115
131
|
}
|
|
116
132
|
|
|
@@ -168,17 +184,18 @@ export class PortalUI {
|
|
|
168
184
|
private handleMessage(event: MessageEvent): void {
|
|
169
185
|
// Verify origin matches the portal URL
|
|
170
186
|
if (!this.portalOrigin || event.origin !== this.portalOrigin) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
this.portalOrigin
|
|
176
|
-
);
|
|
187
|
+
this.logger.warn('Received message from unauthorized origin', this.buildLoggerExtra('handleMessage', {
|
|
188
|
+
received_origin: event.origin,
|
|
189
|
+
expected_origin: this.portalOrigin || 'unknown',
|
|
190
|
+
}));
|
|
177
191
|
return;
|
|
178
192
|
}
|
|
179
193
|
|
|
180
194
|
const { type, userId, access_token, refresh_token, error, height, data } = event.data;
|
|
181
|
-
|
|
195
|
+
this.logger.debug('Received portal message', this.buildLoggerExtra('handleMessage', {
|
|
196
|
+
message_type: type,
|
|
197
|
+
has_data: Boolean(data),
|
|
198
|
+
}));
|
|
182
199
|
|
|
183
200
|
switch (type) {
|
|
184
201
|
case 'portal-success': {
|
|
@@ -225,19 +242,26 @@ export class PortalUI {
|
|
|
225
242
|
break;
|
|
226
243
|
|
|
227
244
|
default:
|
|
228
|
-
|
|
245
|
+
this.logger.warn('Received unhandled message type', this.buildLoggerExtra('handleMessage', {
|
|
246
|
+
message_type: type,
|
|
247
|
+
}));
|
|
229
248
|
}
|
|
230
249
|
}
|
|
231
250
|
|
|
232
251
|
private handlePortalSuccess(userId: string, tokens?: { access_token?: string; refresh_token?: string }): void {
|
|
233
252
|
if (!userId) {
|
|
234
|
-
|
|
253
|
+
this.logger.error('Missing userId in portal-success message', this.buildLoggerExtra('handlePortalSuccess'));
|
|
235
254
|
return;
|
|
236
255
|
}
|
|
237
256
|
|
|
238
|
-
|
|
257
|
+
this.logger.info('Portal success - user connected', this.buildLoggerExtra('handlePortalSuccess', {
|
|
258
|
+
user_id_present: Boolean(userId),
|
|
259
|
+
tokens_provided: Boolean(tokens?.access_token && tokens?.refresh_token),
|
|
260
|
+
}));
|
|
239
261
|
if (tokens?.access_token && tokens?.refresh_token) {
|
|
240
|
-
|
|
262
|
+
this.logger.debug('Tokens received for user', this.buildLoggerExtra('handlePortalSuccess', {
|
|
263
|
+
tokens_provided: true,
|
|
264
|
+
}));
|
|
241
265
|
}
|
|
242
266
|
|
|
243
267
|
// Pass userId to parent (SDK will handle tokens internally)
|
|
@@ -245,23 +269,30 @@ export class PortalUI {
|
|
|
245
269
|
}
|
|
246
270
|
|
|
247
271
|
private handlePortalError(error: string): void {
|
|
248
|
-
|
|
272
|
+
this.logger.error('Portal error received', this.buildLoggerExtra('handlePortalError', {
|
|
273
|
+
error_message: error,
|
|
274
|
+
}));
|
|
249
275
|
this.options?.onError?.(new Error(error || 'Unknown portal error'));
|
|
250
276
|
}
|
|
251
277
|
|
|
252
278
|
private handlePortalClose(): void {
|
|
253
|
-
|
|
279
|
+
this.logger.info('Portal closed by user', this.buildLoggerExtra('handlePortalClose'));
|
|
254
280
|
this.options?.onClose?.();
|
|
255
281
|
this.hide();
|
|
256
282
|
}
|
|
257
283
|
|
|
258
284
|
private handleGenericEvent(data: any): void {
|
|
259
285
|
if (!data || !data.type) {
|
|
260
|
-
|
|
286
|
+
this.logger.warn('Invalid event data received', this.buildLoggerExtra('handleGenericEvent', {
|
|
287
|
+
has_type: Boolean(data?.type),
|
|
288
|
+
}));
|
|
261
289
|
return;
|
|
262
290
|
}
|
|
263
291
|
|
|
264
|
-
|
|
292
|
+
this.logger.debug('Generic event received', this.buildLoggerExtra('handleGenericEvent', {
|
|
293
|
+
event_type: data.type,
|
|
294
|
+
payload_present: Boolean(data.data),
|
|
295
|
+
}));
|
|
265
296
|
|
|
266
297
|
// Emit the event to be handled by the SDK
|
|
267
298
|
// This will be implemented in FinaticConnect
|
|
@@ -272,7 +303,7 @@ export class PortalUI {
|
|
|
272
303
|
|
|
273
304
|
private handleSuccess(userId: string): void {
|
|
274
305
|
if (!userId) {
|
|
275
|
-
|
|
306
|
+
this.logger.error('Missing required fields in success message', this.buildLoggerExtra('handleSuccess'));
|
|
276
307
|
return;
|
|
277
308
|
}
|
|
278
309
|
|
|
@@ -281,19 +312,23 @@ export class PortalUI {
|
|
|
281
312
|
}
|
|
282
313
|
|
|
283
314
|
private handleError(error: string): void {
|
|
284
|
-
|
|
315
|
+
this.logger.error('Received portal error message', this.buildLoggerExtra('handleError', {
|
|
316
|
+
error_message: error,
|
|
317
|
+
}));
|
|
285
318
|
this.options?.onError?.(new Error(error || 'Unknown error'));
|
|
286
319
|
}
|
|
287
320
|
|
|
288
321
|
private handleClose(): void {
|
|
289
|
-
|
|
322
|
+
this.logger.debug('Received close message', this.buildLoggerExtra('handleClose'));
|
|
290
323
|
this.options?.onClose?.();
|
|
291
324
|
this.hide();
|
|
292
325
|
}
|
|
293
326
|
|
|
294
327
|
private handleResize(height: number): void {
|
|
295
328
|
if (height && this.iframe) {
|
|
296
|
-
|
|
329
|
+
this.logger.debug('Received resize message', this.buildLoggerExtra('handleResize', {
|
|
330
|
+
height,
|
|
331
|
+
}));
|
|
297
332
|
this.iframe.style.height = `${height}px`;
|
|
298
333
|
}
|
|
299
334
|
}
|
package/src/index.ts
CHANGED
|
@@ -48,6 +48,18 @@ export type {
|
|
|
48
48
|
FilteredAccountsResponse,
|
|
49
49
|
FilteredBalancesResponse,
|
|
50
50
|
BrokerConnection,
|
|
51
|
+
OrderFill,
|
|
52
|
+
OrderEvent,
|
|
53
|
+
OrderLeg,
|
|
54
|
+
OrderGroup,
|
|
55
|
+
OrderGroupOrder,
|
|
56
|
+
PositionLot,
|
|
57
|
+
PositionLotFill,
|
|
58
|
+
OrderFillsFilter,
|
|
59
|
+
OrderEventsFilter,
|
|
60
|
+
OrderGroupsFilter,
|
|
61
|
+
PositionLotsFilter,
|
|
62
|
+
PositionLotFillsFilter,
|
|
51
63
|
} from './types';
|
|
52
64
|
|
|
53
65
|
export type { FinaticConnectOptions, FinaticUserToken, PortalMessage } from './types/connect';
|
|
@@ -79,6 +91,7 @@ export { PaginatedResult } from './types/common/pagination';
|
|
|
79
91
|
export * from './utils/errors';
|
|
80
92
|
export * from './utils/events';
|
|
81
93
|
export * from './utils/themeUtils';
|
|
94
|
+
export * from './lib/logger';
|
|
82
95
|
|
|
83
96
|
// Theme presets
|
|
84
97
|
export { portalThemePresets } from './themes/portalPresets';
|