@finatic/client 0.0.141 → 0.0.142
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +39 -2
- package/dist/index.js +555 -94
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +552 -95
- package/dist/index.mjs.map +1 -1
- package/dist/types/core/client/ApiClient.d.ts +2 -0
- package/dist/types/core/client/FinaticConnect.d.ts +2 -0
- package/dist/types/core/portal/PortalUI.d.ts +2 -0
- package/dist/types/index.d.ts +1 -0
- 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/package.json +1 -1
- package/src/core/client/ApiClient.ts +45 -19
- package/src/core/client/FinaticConnect.ts +79 -30
- package/src/core/portal/PortalUI.ts +58 -23
- package/src/index.ts +1 -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/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/src/index.ts
CHANGED
|
@@ -91,6 +91,7 @@ export { PaginatedResult } from './types/common/pagination';
|
|
|
91
91
|
export * from './utils/errors';
|
|
92
92
|
export * from './utils/events';
|
|
93
93
|
export * from './utils/themeUtils';
|
|
94
|
+
export * from './lib/logger';
|
|
94
95
|
|
|
95
96
|
// Theme presets
|
|
96
97
|
export { portalThemePresets } from './themes/portalPresets';
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { Logger, LoggerExtra, LoggerMetadata, LoggerOptions, LogLevel, LogVerbosity } from './logger.types';
|
|
2
|
+
|
|
3
|
+
const LOG_LEVEL_ORDER: Record<Exclude<LogLevel, 'silent'>, number> = {
|
|
4
|
+
error: 0,
|
|
5
|
+
warn: 1,
|
|
6
|
+
info: 2,
|
|
7
|
+
debug: 3,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const LEVEL_TO_CONSOLE: Record<Exclude<LogLevel, 'silent'>, keyof Console> = {
|
|
11
|
+
error: 'error',
|
|
12
|
+
warn: 'warn',
|
|
13
|
+
info: 'info',
|
|
14
|
+
debug: 'debug',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const DEFAULT_LOGGER_NAME = 'FinaticLogger';
|
|
18
|
+
|
|
19
|
+
const parseLogLevel = (value: unknown, fallback: LogLevel): LogLevel => {
|
|
20
|
+
if (typeof value !== 'string') {
|
|
21
|
+
return fallback;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const normalized = value.toLowerCase().trim() as LogLevel;
|
|
25
|
+
if (normalized === 'silent' || normalized === 'error' || normalized === 'warn' || normalized === 'info' || normalized === 'debug') {
|
|
26
|
+
return normalized;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return fallback;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const parseVerbosity = (value: unknown, fallback: LogVerbosity): LogVerbosity => {
|
|
33
|
+
if (typeof value !== 'string' && typeof value !== 'number') {
|
|
34
|
+
return fallback;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const numeric = typeof value === 'number' ? value : Number.parseInt(value, 10);
|
|
38
|
+
if (Number.isNaN(numeric)) {
|
|
39
|
+
return fallback;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (numeric <= 0) {
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (numeric >= 3) {
|
|
47
|
+
return 3;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return numeric as LogVerbosity;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const resolveEnv = (key: string): string | undefined => {
|
|
54
|
+
try {
|
|
55
|
+
if (typeof process !== 'undefined' && process.env && typeof process.env[key] === 'string') {
|
|
56
|
+
return process.env[key];
|
|
57
|
+
}
|
|
58
|
+
} catch {
|
|
59
|
+
// ignore
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
64
|
+
const metaEnv = typeof import.meta !== 'undefined' ? (import.meta as any).env : undefined;
|
|
65
|
+
if (metaEnv && typeof metaEnv[key] === 'string') {
|
|
66
|
+
return metaEnv[key];
|
|
67
|
+
}
|
|
68
|
+
} catch {
|
|
69
|
+
// ignore
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
if (typeof globalThis !== 'undefined') {
|
|
74
|
+
const value = (globalThis as Record<string, unknown>)[key];
|
|
75
|
+
if (typeof value === 'string') {
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
} catch {
|
|
80
|
+
// ignore
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return undefined;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const resolveDefaultLogLevel = (explicitLevel?: LogLevel): LogLevel => {
|
|
87
|
+
if (explicitLevel) {
|
|
88
|
+
return explicitLevel;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const envLevel =
|
|
92
|
+
resolveEnv('FINATIC_LOG_LEVEL') ||
|
|
93
|
+
resolveEnv('VITE_FINATIC_LOG_LEVEL') ||
|
|
94
|
+
resolveEnv('NEXT_PUBLIC_FINATIC_LOG_LEVEL') ||
|
|
95
|
+
resolveEnv('NEXT_FINATIC_LOG_LEVEL') ||
|
|
96
|
+
resolveEnv('REACT_APP_FINATIC_LOG_LEVEL') ||
|
|
97
|
+
resolveEnv('NUXT_PUBLIC_FINATIC_LOG_LEVEL') ||
|
|
98
|
+
resolveEnv('NX_FINATIC_LOG_LEVEL');
|
|
99
|
+
|
|
100
|
+
if (envLevel) {
|
|
101
|
+
return parseLogLevel(envLevel, 'silent');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return 'silent';
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const resolveVerbosity = (): LogVerbosity => {
|
|
108
|
+
const envVerbosity =
|
|
109
|
+
resolveEnv('FINATIC_LOG_VERBOSITY') ||
|
|
110
|
+
resolveEnv('VITE_FINATIC_LOG_VERBOSITY') ||
|
|
111
|
+
resolveEnv('NEXT_PUBLIC_FINATIC_LOG_VERBOSITY') ||
|
|
112
|
+
resolveEnv('NEXT_FINATIC_LOG_VERBOSITY') ||
|
|
113
|
+
resolveEnv('REACT_APP_FINATIC_LOG_VERBOSITY') ||
|
|
114
|
+
resolveEnv('NUXT_PUBLIC_FINATIC_LOG_VERBOSITY') ||
|
|
115
|
+
resolveEnv('NX_FINATIC_LOG_VERBOSITY');
|
|
116
|
+
|
|
117
|
+
if (envVerbosity) {
|
|
118
|
+
return parseVerbosity(envVerbosity, 1);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return 1;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const resolveBaseMetadata = (): LoggerMetadata => {
|
|
125
|
+
const base: LoggerMetadata = {
|
|
126
|
+
timestamp: new Date().toISOString(),
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
if (typeof globalThis !== 'undefined') {
|
|
131
|
+
if (typeof (globalThis as Window).location !== 'undefined') {
|
|
132
|
+
base.host = (globalThis as Window).location.hostname;
|
|
133
|
+
}
|
|
134
|
+
if (typeof (globalThis as Window).navigator !== 'undefined') {
|
|
135
|
+
base.user_agent = (globalThis as Window).navigator.userAgent;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
} catch {
|
|
139
|
+
// ignore
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
if (typeof process !== 'undefined') {
|
|
144
|
+
base.pid = process.pid;
|
|
145
|
+
}
|
|
146
|
+
} catch {
|
|
147
|
+
// ignore
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return base;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const normalizeError = (error: unknown): Record<string, unknown> | undefined => {
|
|
154
|
+
if (!error) {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (error instanceof Error) {
|
|
159
|
+
return {
|
|
160
|
+
type: error.name,
|
|
161
|
+
message: error.message,
|
|
162
|
+
stacktrace: error.stack,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (typeof error === 'object') {
|
|
167
|
+
return { ...(error as Record<string, unknown>) };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
type: 'Error',
|
|
172
|
+
message: String(error),
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const shouldLog = (requestedLevel: Exclude<LogLevel, 'silent'>, currentLevel: LogLevel): boolean => {
|
|
177
|
+
if (currentLevel === 'silent') {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const currentOrder = LOG_LEVEL_ORDER[currentLevel];
|
|
182
|
+
const requestedOrder = LOG_LEVEL_ORDER[requestedLevel];
|
|
183
|
+
|
|
184
|
+
return requestedOrder <= currentOrder;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const buildPayload = (
|
|
188
|
+
name: string,
|
|
189
|
+
level: Exclude<LogLevel, 'silent'>,
|
|
190
|
+
message: string,
|
|
191
|
+
defaultMetadata: LoggerMetadata | undefined,
|
|
192
|
+
extra: LoggerExtra | undefined,
|
|
193
|
+
verbosity: LogVerbosity,
|
|
194
|
+
): Record<string, unknown> => {
|
|
195
|
+
const payload: Record<string, unknown> = {
|
|
196
|
+
timestamp: new Date().toISOString(),
|
|
197
|
+
message,
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
if (verbosity >= 1 && extra) {
|
|
201
|
+
if (extra.module) {
|
|
202
|
+
payload.module = extra.module;
|
|
203
|
+
}
|
|
204
|
+
if (extra.function) {
|
|
205
|
+
payload.function = extra.function;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (verbosity >= 2) {
|
|
210
|
+
if (extra?.duration_ms !== undefined) {
|
|
211
|
+
payload.duration_ms = extra.duration_ms;
|
|
212
|
+
}
|
|
213
|
+
if (extra?.event) {
|
|
214
|
+
payload.event = extra.event;
|
|
215
|
+
}
|
|
216
|
+
if (extra?.error) {
|
|
217
|
+
payload.error = normalizeError(extra.error);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (verbosity >= 3) {
|
|
222
|
+
payload.level = level.toUpperCase();
|
|
223
|
+
payload.name = name;
|
|
224
|
+
const baseMetadata = resolveBaseMetadata();
|
|
225
|
+
const mergedMetadata: LoggerMetadata = {
|
|
226
|
+
...baseMetadata,
|
|
227
|
+
...(defaultMetadata || {}),
|
|
228
|
+
...(extra?.metadata || {}),
|
|
229
|
+
};
|
|
230
|
+
if (Object.keys(mergedMetadata).length > 0) {
|
|
231
|
+
payload.metadata = mergedMetadata;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const restKeys = ['module', 'function', 'event', 'duration_ms', 'error', 'metadata'];
|
|
236
|
+
if (extra) {
|
|
237
|
+
Object.entries(extra).forEach(([key, value]) => {
|
|
238
|
+
if (!restKeys.includes(key) && value !== undefined) {
|
|
239
|
+
payload[key] = value;
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return payload;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const consoleWrite = (
|
|
248
|
+
consoleLevel: keyof Console,
|
|
249
|
+
name: string,
|
|
250
|
+
level: Exclude<LogLevel, 'silent'>,
|
|
251
|
+
message: string,
|
|
252
|
+
payload: Record<string, unknown>,
|
|
253
|
+
): void => {
|
|
254
|
+
if (typeof console === 'undefined' || typeof console[consoleLevel] !== 'function') {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const prefix = `${level.toUpperCase()}: ${name} || ${message}`;
|
|
259
|
+
console[consoleLevel](prefix, payload);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
export const setupLogger = (nameOrOptions: string | LoggerOptions, level?: LogLevel, defaultMetadata?: LoggerMetadata): Logger => {
|
|
263
|
+
const options: LoggerOptions =
|
|
264
|
+
typeof nameOrOptions === 'string'
|
|
265
|
+
? {
|
|
266
|
+
name: nameOrOptions,
|
|
267
|
+
level,
|
|
268
|
+
defaultMetadata,
|
|
269
|
+
}
|
|
270
|
+
: nameOrOptions;
|
|
271
|
+
|
|
272
|
+
const loggerName = options.name || DEFAULT_LOGGER_NAME;
|
|
273
|
+
let currentLevel: LogLevel = resolveDefaultLogLevel(options.level);
|
|
274
|
+
const loggerDefaultMetadata = options.defaultMetadata;
|
|
275
|
+
const verbosity = resolveVerbosity();
|
|
276
|
+
|
|
277
|
+
const log = (requestedLevel: Exclude<LogLevel, 'silent'>, message: string, extra?: LoggerExtra) => {
|
|
278
|
+
if (!shouldLog(requestedLevel, currentLevel)) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const payload = buildPayload(loggerName, requestedLevel, message, loggerDefaultMetadata, extra, verbosity);
|
|
283
|
+
consoleWrite(LEVEL_TO_CONSOLE[requestedLevel], loggerName, requestedLevel, message, payload);
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
return {
|
|
287
|
+
getLevel: () => currentLevel,
|
|
288
|
+
setLevel: (nextLevel: LogLevel) => {
|
|
289
|
+
currentLevel = nextLevel;
|
|
290
|
+
},
|
|
291
|
+
debug: (message: string, extra?: LoggerExtra) => log('debug', message, extra),
|
|
292
|
+
info: (message: string, extra?: LoggerExtra) => log('info', message, extra),
|
|
293
|
+
warn: (message: string, extra?: LoggerExtra) => log('warn', message, extra),
|
|
294
|
+
error: (message: string, extra?: LoggerExtra) => log('error', message, extra),
|
|
295
|
+
exception: (message: string, error: unknown, extra?: LoggerExtra) => {
|
|
296
|
+
log('error', message, {
|
|
297
|
+
...extra,
|
|
298
|
+
error,
|
|
299
|
+
event: extra?.event || 'exception',
|
|
300
|
+
});
|
|
301
|
+
},
|
|
302
|
+
};
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
export const buildLoggerExtra = (metadata: LoggerMetadata): LoggerExtra => ({
|
|
306
|
+
metadata,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
export const logStartEnd =
|
|
310
|
+
(logger: Logger) =>
|
|
311
|
+
<Args extends unknown[], ReturnType>(fn: (...args: Args) => ReturnType | Promise<ReturnType>) =>
|
|
312
|
+
async (...args: Args): Promise<ReturnType> => {
|
|
313
|
+
const start = Date.now();
|
|
314
|
+
const functionName = fn.name || 'anonymous';
|
|
315
|
+
logger.debug('START', { module: 'logStartEnd', function: functionName, event: 'start' });
|
|
316
|
+
|
|
317
|
+
try {
|
|
318
|
+
const result = await fn(...args);
|
|
319
|
+
const duration = Date.now() - start;
|
|
320
|
+
logger.info('END', { module: 'logStartEnd', function: functionName, event: 'end', duration_ms: duration });
|
|
321
|
+
return result;
|
|
322
|
+
} catch (error) {
|
|
323
|
+
const duration = Date.now() - start;
|
|
324
|
+
logger.exception('EXCEPTION', error, {
|
|
325
|
+
module: 'logStartEnd',
|
|
326
|
+
function: functionName,
|
|
327
|
+
duration_ms: duration,
|
|
328
|
+
});
|
|
329
|
+
throw error;
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type LogLevel = 'silent' | 'error' | 'warn' | 'info' | 'debug';
|
|
2
|
+
|
|
3
|
+
export interface LoggerMetadata {
|
|
4
|
+
[key: string]: unknown;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface LoggerExtra {
|
|
8
|
+
metadata?: LoggerMetadata;
|
|
9
|
+
module?: string;
|
|
10
|
+
function?: string;
|
|
11
|
+
event?: string;
|
|
12
|
+
duration_ms?: number;
|
|
13
|
+
error?: unknown;
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface LoggerOptions {
|
|
18
|
+
name: string;
|
|
19
|
+
level?: LogLevel;
|
|
20
|
+
defaultMetadata?: LoggerMetadata;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface Logger {
|
|
24
|
+
getLevel: () => LogLevel;
|
|
25
|
+
setLevel: (level: LogLevel) => void;
|
|
26
|
+
debug: (message: string, extra?: LoggerExtra) => void;
|
|
27
|
+
info: (message: string, extra?: LoggerExtra) => void;
|
|
28
|
+
warn: (message: string, extra?: LoggerExtra) => void;
|
|
29
|
+
error: (message: string, extra?: LoggerExtra) => void;
|
|
30
|
+
exception: (message: string, error: unknown, extra?: LoggerExtra) => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type LogVerbosity = 0 | 1 | 2 | 3;
|
|
34
|
+
|
|
@@ -47,6 +47,11 @@ import {
|
|
|
47
47
|
OrderValidationError,
|
|
48
48
|
} from '../utils/errors';
|
|
49
49
|
import { MockDataProvider, MockConfig } from './MockDataProvider';
|
|
50
|
+
import { setupLogger, buildLoggerExtra, LoggerExtra } from '../lib/logger';
|
|
51
|
+
|
|
52
|
+
const mockApiLogger = setupLogger('FinaticClientSDK.MockApiClient', undefined, {
|
|
53
|
+
codebase: 'FinaticClientSDK',
|
|
54
|
+
});
|
|
50
55
|
|
|
51
56
|
/**
|
|
52
57
|
* Mock API Client that implements the same interface as the real ApiClient
|
|
@@ -71,6 +76,15 @@ export class MockApiClient {
|
|
|
71
76
|
// Mock data provider
|
|
72
77
|
private mockDataProvider: MockDataProvider;
|
|
73
78
|
private readonly mockApiOnly: boolean;
|
|
79
|
+
private readonly logger = mockApiLogger;
|
|
80
|
+
|
|
81
|
+
private buildLoggerExtra(functionName: string, metadata?: Record<string, unknown>): LoggerExtra {
|
|
82
|
+
return {
|
|
83
|
+
module: 'MockApiClient',
|
|
84
|
+
function: functionName,
|
|
85
|
+
...(metadata ? buildLoggerExtra(metadata) : {}),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
74
88
|
|
|
75
89
|
constructor(baseUrl: string, deviceInfo?: DeviceInfo, mockConfig?: MockConfig) {
|
|
76
90
|
this.baseUrl = baseUrl;
|
|
@@ -80,9 +94,13 @@ export class MockApiClient {
|
|
|
80
94
|
|
|
81
95
|
// Log that mocks are being used
|
|
82
96
|
if (this.mockApiOnly) {
|
|
83
|
-
|
|
97
|
+
this.logger.info('Using mock API client (API only, real portal)', this.buildLoggerExtra('constructor', {
|
|
98
|
+
mock_api_only: true,
|
|
99
|
+
}));
|
|
84
100
|
} else {
|
|
85
|
-
|
|
101
|
+
this.logger.info('Using mock API client', this.buildLoggerExtra('constructor', {
|
|
102
|
+
mock_api_only: false,
|
|
103
|
+
}));
|
|
86
104
|
}
|
|
87
105
|
}
|
|
88
106
|
|
|
@@ -339,14 +357,12 @@ export class MockApiClient {
|
|
|
339
357
|
const accessToken = await this.getValidAccessToken();
|
|
340
358
|
|
|
341
359
|
// Debug logging
|
|
342
|
-
|
|
343
|
-
params,
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
contextAccountNumber: this.tradingContext.accountNumber,
|
|
349
|
-
});
|
|
360
|
+
this.logger.debug('placeBrokerOrder parameters', this.buildLoggerExtra('placeBrokerOrder', {
|
|
361
|
+
has_params_broker: Boolean(params.broker),
|
|
362
|
+
has_context_broker: Boolean(this.tradingContext.broker),
|
|
363
|
+
has_params_account: Boolean(params.accountNumber),
|
|
364
|
+
has_context_account: Boolean(this.tradingContext.accountNumber),
|
|
365
|
+
}));
|
|
350
366
|
|
|
351
367
|
const fullParams: BrokerOrderParams = {
|
|
352
368
|
broker:
|
|
@@ -374,7 +390,13 @@ export class MockApiClient {
|
|
|
374
390
|
order_id: params.order_id,
|
|
375
391
|
};
|
|
376
392
|
|
|
377
|
-
|
|
393
|
+
this.logger.debug('placeBrokerOrder normalized parameters', this.buildLoggerExtra('placeBrokerOrder', {
|
|
394
|
+
broker: fullParams.broker,
|
|
395
|
+
account_number_present: Boolean(fullParams.accountNumber),
|
|
396
|
+
symbol: fullParams.symbol,
|
|
397
|
+
order_type: fullParams.orderType,
|
|
398
|
+
asset_type: fullParams.assetType,
|
|
399
|
+
}));
|
|
378
400
|
return this.mockDataProvider.mockPlaceOrder(fullParams);
|
|
379
401
|
}
|
|
380
402
|
|
|
@@ -427,14 +449,18 @@ export class MockApiClient {
|
|
|
427
449
|
}
|
|
428
450
|
|
|
429
451
|
setAccount(accountNumber: string, accountId?: string): void {
|
|
430
|
-
|
|
431
|
-
accountNumber,
|
|
432
|
-
accountId,
|
|
433
|
-
|
|
434
|
-
});
|
|
452
|
+
this.logger.debug('setAccount invoked', this.buildLoggerExtra('setAccount', {
|
|
453
|
+
account_number: accountNumber,
|
|
454
|
+
account_id_present: Boolean(accountId),
|
|
455
|
+
had_existing_account: Boolean(this.tradingContext.accountNumber),
|
|
456
|
+
}));
|
|
435
457
|
this.tradingContext.accountNumber = accountNumber;
|
|
436
458
|
this.tradingContext.accountId = accountId;
|
|
437
|
-
|
|
459
|
+
this.logger.debug('setAccount updated context', this.buildLoggerExtra('setAccount', {
|
|
460
|
+
broker: this.tradingContext.broker,
|
|
461
|
+
account_number_present: Boolean(this.tradingContext.accountNumber),
|
|
462
|
+
account_id_present: Boolean(this.tradingContext.accountId),
|
|
463
|
+
}));
|
|
438
464
|
}
|
|
439
465
|
|
|
440
466
|
|
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
* Pagination-related types and classes
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { setupLogger, buildLoggerExtra, LoggerExtra } from '../../lib/logger';
|
|
6
|
+
|
|
7
|
+
const paginationLogger = setupLogger('FinaticClientSDK.Pagination', undefined, {
|
|
8
|
+
codebase: 'FinaticClientSDK',
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const buildPaginationExtra = (functionName: string, metadata?: Record<string, unknown>): LoggerExtra => ({
|
|
12
|
+
module: 'PaginatedResult',
|
|
13
|
+
function: functionName,
|
|
14
|
+
...(metadata ? buildLoggerExtra(metadata) : {}),
|
|
15
|
+
});
|
|
16
|
+
|
|
5
17
|
export interface ApiPaginationInfo {
|
|
6
18
|
has_more: boolean;
|
|
7
19
|
next_offset: number;
|
|
@@ -62,7 +74,10 @@ export class PaginatedResult<T> {
|
|
|
62
74
|
try {
|
|
63
75
|
return await this.navigationCallback(this.metadata.nextOffset, this.metadata.limit);
|
|
64
76
|
} catch (error) {
|
|
65
|
-
|
|
77
|
+
paginationLogger.exception('Error fetching next page', error, buildPaginationExtra('nextPage', {
|
|
78
|
+
next_offset: this.metadata.nextOffset,
|
|
79
|
+
limit: this.metadata.limit,
|
|
80
|
+
}));
|
|
66
81
|
return null;
|
|
67
82
|
}
|
|
68
83
|
}
|
|
@@ -76,7 +91,10 @@ export class PaginatedResult<T> {
|
|
|
76
91
|
try {
|
|
77
92
|
return await this.navigationCallback(previousOffset, this.metadata.limit);
|
|
78
93
|
} catch (error) {
|
|
79
|
-
|
|
94
|
+
paginationLogger.exception('Error fetching previous page', error, buildPaginationExtra('previousPage', {
|
|
95
|
+
previous_offset: previousOffset,
|
|
96
|
+
limit: this.metadata.limit,
|
|
97
|
+
}));
|
|
80
98
|
return null;
|
|
81
99
|
}
|
|
82
100
|
}
|
|
@@ -90,7 +108,11 @@ export class PaginatedResult<T> {
|
|
|
90
108
|
try {
|
|
91
109
|
return await this.navigationCallback(offset, this.metadata.limit);
|
|
92
110
|
} catch (error) {
|
|
93
|
-
|
|
111
|
+
paginationLogger.exception('Error fetching page', error, buildPaginationExtra('goToPage', {
|
|
112
|
+
page_number: pageNumber,
|
|
113
|
+
offset,
|
|
114
|
+
limit: this.metadata.limit,
|
|
115
|
+
}));
|
|
94
116
|
return null;
|
|
95
117
|
}
|
|
96
118
|
}
|
|
@@ -103,7 +125,9 @@ export class PaginatedResult<T> {
|
|
|
103
125
|
try {
|
|
104
126
|
return await this.navigationCallback(0, this.metadata.limit);
|
|
105
127
|
} catch (error) {
|
|
106
|
-
|
|
128
|
+
paginationLogger.exception('Error fetching first page', error, buildPaginationExtra('firstPage', {
|
|
129
|
+
limit: this.metadata.limit,
|
|
130
|
+
}));
|
|
107
131
|
return null;
|
|
108
132
|
}
|
|
109
133
|
}
|
|
@@ -127,7 +151,9 @@ export class PaginatedResult<T> {
|
|
|
127
151
|
try {
|
|
128
152
|
return await findLast(this);
|
|
129
153
|
} catch (error) {
|
|
130
|
-
|
|
154
|
+
paginationLogger.exception('Error fetching last page', error, buildPaginationExtra('lastPage', {
|
|
155
|
+
limit: this.metadata.limit,
|
|
156
|
+
}));
|
|
131
157
|
return null;
|
|
132
158
|
}
|
|
133
159
|
}
|
package/src/utils/brokerUtils.ts
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
* Broker filtering utility functions
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { setupLogger, buildLoggerExtra, LoggerExtra } from '../lib/logger';
|
|
6
|
+
|
|
7
|
+
const brokerLogger = setupLogger('FinaticClientSDK.BrokerUtils', undefined, {
|
|
8
|
+
codebase: 'FinaticClientSDK',
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const buildBrokerExtra = (functionName: string, metadata?: Record<string, unknown>): LoggerExtra => ({
|
|
12
|
+
module: 'BrokerUtils',
|
|
13
|
+
function: functionName,
|
|
14
|
+
...(metadata ? buildLoggerExtra(metadata) : {}),
|
|
15
|
+
});
|
|
16
|
+
|
|
5
17
|
// Supported broker names and their corresponding IDs (including aliases)
|
|
6
18
|
const SUPPORTED_BROKERS: Record<string, string> = {
|
|
7
19
|
'alpaca': 'alpaca',
|
|
@@ -52,7 +64,11 @@ export function appendBrokerFilterToURL(baseUrl: string, brokerNames?: string[])
|
|
|
52
64
|
const { brokerIds, warnings } = convertBrokerNamesToIds(brokerNames);
|
|
53
65
|
|
|
54
66
|
// Log warnings for unsupported broker names
|
|
55
|
-
warnings.forEach(warning =>
|
|
67
|
+
warnings.forEach(warning =>
|
|
68
|
+
brokerLogger.warn('Unsupported broker name provided', buildBrokerExtra('appendBrokerFilterToURL', {
|
|
69
|
+
warning,
|
|
70
|
+
})),
|
|
71
|
+
);
|
|
56
72
|
|
|
57
73
|
// Only add broker filter if we have valid broker IDs
|
|
58
74
|
if (brokerIds.length > 0) {
|
|
@@ -62,7 +78,10 @@ export function appendBrokerFilterToURL(baseUrl: string, brokerNames?: string[])
|
|
|
62
78
|
|
|
63
79
|
return url.toString();
|
|
64
80
|
} catch (error) {
|
|
65
|
-
|
|
81
|
+
brokerLogger.exception('Failed to append broker filter to URL', error, buildBrokerExtra('appendBrokerFilterToURL', {
|
|
82
|
+
base_url: baseUrl,
|
|
83
|
+
brokers_count: brokerNames?.length ?? 0,
|
|
84
|
+
}));
|
|
66
85
|
return baseUrl;
|
|
67
86
|
}
|
|
68
87
|
}
|
package/src/utils/events.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
import { setupLogger, buildLoggerExtra } from '../lib/logger';
|
|
2
|
+
|
|
1
3
|
type EventCallback = (...args: any[]) => void;
|
|
2
4
|
|
|
5
|
+
const eventsLogger = setupLogger('FinaticClientSDK.Events', undefined, {
|
|
6
|
+
codebase: 'FinaticClientSDK',
|
|
7
|
+
});
|
|
8
|
+
|
|
3
9
|
export class EventEmitter {
|
|
4
10
|
private events: Map<string, Set<EventCallback>> = new Map();
|
|
5
11
|
|
|
@@ -30,7 +36,13 @@ export class EventEmitter {
|
|
|
30
36
|
try {
|
|
31
37
|
callback(...args);
|
|
32
38
|
} catch (error) {
|
|
33
|
-
|
|
39
|
+
eventsLogger.exception('Error in event handler', error, {
|
|
40
|
+
module: 'EventEmitter',
|
|
41
|
+
function: 'emit',
|
|
42
|
+
...buildLoggerExtra({
|
|
43
|
+
event_name: event,
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
34
46
|
}
|
|
35
47
|
});
|
|
36
48
|
}
|
package/src/utils/themeUtils.ts
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { PortalTheme, PortalThemeConfig } from '../types/portal';
|
|
2
2
|
import { portalThemePresets } from '../themes/portalPresets';
|
|
3
|
+
import { setupLogger, buildLoggerExtra, LoggerExtra } from '../lib/logger';
|
|
4
|
+
|
|
5
|
+
const themeLogger = setupLogger('FinaticClientSDK.ThemeUtils', undefined, {
|
|
6
|
+
codebase: 'FinaticClientSDK',
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const buildThemeExtra = (functionName: string, metadata?: Record<string, unknown>): LoggerExtra => ({
|
|
10
|
+
module: 'ThemeUtils',
|
|
11
|
+
function: functionName,
|
|
12
|
+
...(metadata ? buildLoggerExtra(metadata) : {}),
|
|
13
|
+
});
|
|
3
14
|
|
|
4
15
|
/**
|
|
5
16
|
* Generate a portal URL with theme parameters
|
|
@@ -27,7 +38,10 @@ export function generatePortalThemeURL(baseUrl: string, theme?: PortalTheme): st
|
|
|
27
38
|
|
|
28
39
|
return url.toString();
|
|
29
40
|
} catch (error) {
|
|
30
|
-
|
|
41
|
+
themeLogger.exception('Failed to generate theme URL', error, buildThemeExtra('generatePortalThemeURL', {
|
|
42
|
+
base_url: baseUrl,
|
|
43
|
+
has_theme: Boolean(theme),
|
|
44
|
+
}));
|
|
31
45
|
return baseUrl;
|
|
32
46
|
}
|
|
33
47
|
}
|
|
@@ -58,7 +72,10 @@ export function appendThemeToURL(baseUrl: string, theme?: PortalTheme): string {
|
|
|
58
72
|
|
|
59
73
|
return url.toString();
|
|
60
74
|
} catch (error) {
|
|
61
|
-
|
|
75
|
+
themeLogger.exception('Failed to append theme to URL', error, buildThemeExtra('appendThemeToURL', {
|
|
76
|
+
base_url: baseUrl,
|
|
77
|
+
has_theme: Boolean(theme),
|
|
78
|
+
}));
|
|
62
79
|
return baseUrl;
|
|
63
80
|
}
|
|
64
81
|
}
|
|
@@ -118,7 +135,7 @@ export function validateCustomTheme(theme: PortalThemeConfig): boolean {
|
|
|
118
135
|
|
|
119
136
|
return true;
|
|
120
137
|
} catch (error) {
|
|
121
|
-
|
|
138
|
+
themeLogger.exception('Theme validation error', error, buildThemeExtra('validateCustomTheme'));
|
|
122
139
|
return false;
|
|
123
140
|
}
|
|
124
141
|
}
|
|
@@ -135,7 +152,9 @@ export function createCustomThemeFromPreset(
|
|
|
135
152
|
): PortalThemeConfig | null {
|
|
136
153
|
const baseTheme = getThemePreset(preset);
|
|
137
154
|
if (!baseTheme) {
|
|
138
|
-
|
|
155
|
+
themeLogger.warn('Preset theme not found', buildThemeExtra('createCustomThemeFromPreset', {
|
|
156
|
+
preset,
|
|
157
|
+
}));
|
|
139
158
|
return null;
|
|
140
159
|
}
|
|
141
160
|
|