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