@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.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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2133
|
+
this.logger.error('Missing userId in portal-success message', this.buildLoggerExtra('handlePortalSuccess'));
|
|
1642
2134
|
return;
|
|
1643
2135
|
}
|
|
1644
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2161
|
+
this.logger.warn('Invalid event data received', this.buildLoggerExtra('handleGenericEvent', {
|
|
2162
|
+
has_type: Boolean(data?.type),
|
|
2163
|
+
}));
|
|
1663
2164
|
return;
|
|
1664
2165
|
}
|
|
1665
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2739
|
-
params,
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2805
|
-
accountNumber,
|
|
2806
|
-
accountId,
|
|
2807
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =>
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6334
|
+
this.logger.debug('Session keep-alive skipped - no active session', this.buildLoggerExtra('validateSessionKeepAlive'));
|
|
5747
6335
|
return;
|
|
5748
6336
|
}
|
|
5749
6337
|
try {
|
|
5750
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6381
|
+
this.logger.warn('Cannot refresh session - missing session ID', this.buildLoggerExtra('refreshSessionAutomatically'));
|
|
5790
6382
|
return;
|
|
5791
6383
|
}
|
|
5792
6384
|
try {
|
|
5793
|
-
|
|
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
|
-
|
|
5797
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6463
|
+
this.logger.info('Session completed successfully', this.buildLoggerExtra('completeSession', { session_id: sessionId }));
|
|
5861
6464
|
}
|
|
5862
6465
|
else {
|
|
5863
|
-
|
|
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
|
-
|
|
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
|