@finatic/client 0.0.139 → 0.0.140
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 +278 -461
- package/dist/index.d.ts +55 -515
- package/dist/index.js +326 -456
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +327 -456
- package/dist/index.mjs.map +1 -1
- package/dist/types/core/client/ApiClient.d.ts +12 -26
- package/dist/types/core/client/FinaticConnect.d.ts +20 -103
- package/dist/types/index.d.ts +1 -2
- package/dist/types/mocks/MockApiClient.d.ts +2 -4
- package/dist/types/mocks/utils.d.ts +0 -5
- package/dist/types/types/api/auth.d.ts +12 -30
- package/dist/types/types/api/broker.d.ts +1 -1
- package/package.json +7 -3
- package/src/core/client/ApiClient.ts +1721 -0
- package/src/core/client/FinaticConnect.ts +1476 -0
- package/src/core/portal/PortalUI.ts +300 -0
- package/src/index.d.ts +23 -0
- package/src/index.ts +87 -0
- package/src/mocks/MockApiClient.ts +1032 -0
- package/src/mocks/MockDataProvider.ts +986 -0
- package/src/mocks/MockFactory.ts +97 -0
- package/src/mocks/utils.ts +133 -0
- package/src/themes/portalPresets.ts +1307 -0
- package/src/types/api/auth.ts +112 -0
- package/src/types/api/broker.ts +330 -0
- package/src/types/api/core.ts +53 -0
- package/src/types/api/errors.ts +35 -0
- package/src/types/api/orders.ts +45 -0
- package/src/types/api/portfolio.ts +59 -0
- package/src/types/common/pagination.ts +138 -0
- package/src/types/connect.ts +56 -0
- package/src/types/index.ts +25 -0
- package/src/types/portal.ts +214 -0
- package/src/types/ui/theme.ts +105 -0
- package/src/utils/brokerUtils.ts +85 -0
- package/src/utils/errors.ts +104 -0
- package/src/utils/events.ts +54 -0
- package/src/utils/themeUtils.ts +146 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export class BaseError extends Error {
|
|
2
|
+
constructor(
|
|
3
|
+
message: string,
|
|
4
|
+
public readonly code: string = 'UNKNOWN_ERROR'
|
|
5
|
+
) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = this.constructor.name;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class ApiError extends BaseError {
|
|
12
|
+
constructor(
|
|
13
|
+
public readonly status: number,
|
|
14
|
+
message: string,
|
|
15
|
+
public readonly details?: Record<string, any>
|
|
16
|
+
) {
|
|
17
|
+
super(message, `API_ERROR_${status}`);
|
|
18
|
+
this.name = 'ApiError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class SessionError extends ApiError {
|
|
23
|
+
constructor(message: string, details?: Record<string, any>) {
|
|
24
|
+
super(400, message, details);
|
|
25
|
+
this.name = 'SessionError';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class AuthenticationError extends ApiError {
|
|
30
|
+
constructor(message: string, details?: Record<string, any>) {
|
|
31
|
+
super(401, message, details);
|
|
32
|
+
this.name = 'AuthenticationError';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class AuthorizationError extends ApiError {
|
|
37
|
+
constructor(message: string, details?: Record<string, any>) {
|
|
38
|
+
super(403, message, details);
|
|
39
|
+
this.name = 'AuthorizationError';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class RateLimitError extends ApiError {
|
|
44
|
+
constructor(message: string, details?: Record<string, any>) {
|
|
45
|
+
super(429, message, details);
|
|
46
|
+
this.name = 'RateLimitError';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export class TokenError extends BaseError {
|
|
51
|
+
constructor(message: string) {
|
|
52
|
+
super(message, 'TOKEN_ERROR');
|
|
53
|
+
this.name = 'TokenError';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export class ValidationError extends BaseError {
|
|
58
|
+
constructor(message: string) {
|
|
59
|
+
super(message, 'VALIDATION_ERROR');
|
|
60
|
+
this.name = 'ValidationError';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export class NetworkError extends BaseError {
|
|
65
|
+
constructor(message: string) {
|
|
66
|
+
super(message, 'NETWORK_ERROR');
|
|
67
|
+
this.name = 'NetworkError';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export class SecurityError extends BaseError {
|
|
72
|
+
constructor(message: string) {
|
|
73
|
+
super(message, 'SECURITY_ERROR');
|
|
74
|
+
this.name = 'SecurityError';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export class CompanyAccessError extends ApiError {
|
|
79
|
+
constructor(message: string, details?: Record<string, any>) {
|
|
80
|
+
super(403, message, details);
|
|
81
|
+
this.name = 'CompanyAccessError';
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export class OrderError extends ApiError {
|
|
86
|
+
constructor(message: string, details?: Record<string, any>) {
|
|
87
|
+
super(500, message, details);
|
|
88
|
+
this.name = 'OrderError';
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export class OrderValidationError extends ApiError {
|
|
93
|
+
constructor(message: string, details?: Record<string, any>) {
|
|
94
|
+
super(400, message, details);
|
|
95
|
+
this.name = 'OrderValidationError';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export class TradingNotEnabledError extends ApiError {
|
|
100
|
+
constructor(message: string, details?: Record<string, any>) {
|
|
101
|
+
super(403, message, details);
|
|
102
|
+
this.name = 'TradingNotEnabledError';
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
type EventCallback = (...args: any[]) => void;
|
|
2
|
+
|
|
3
|
+
export class EventEmitter {
|
|
4
|
+
private events: Map<string, Set<EventCallback>> = new Map();
|
|
5
|
+
|
|
6
|
+
public on(event: string, callback: EventCallback): void {
|
|
7
|
+
if (!this.events.has(event)) {
|
|
8
|
+
this.events.set(event, new Set());
|
|
9
|
+
}
|
|
10
|
+
this.events.get(event)!.add(callback);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public off(event: string, callback: EventCallback): void {
|
|
14
|
+
if (this.events.has(event)) {
|
|
15
|
+
this.events.get(event)!.delete(callback);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public once(event: string, callback: EventCallback): void {
|
|
20
|
+
const onceCallback = (...args: any[]) => {
|
|
21
|
+
callback(...args);
|
|
22
|
+
this.off(event, onceCallback);
|
|
23
|
+
};
|
|
24
|
+
this.on(event, onceCallback);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public emit(event: string, ...args: any[]): void {
|
|
28
|
+
if (this.events.has(event)) {
|
|
29
|
+
this.events.get(event)!.forEach(callback => {
|
|
30
|
+
try {
|
|
31
|
+
callback(...args);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error(`Error in event handler for ${event}:`, error);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public removeAllListeners(event?: string): void {
|
|
40
|
+
if (event) {
|
|
41
|
+
this.events.delete(event);
|
|
42
|
+
} else {
|
|
43
|
+
this.events.clear();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public listenerCount(event: string): number {
|
|
48
|
+
return this.events.get(event)?.size || 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public listeners(event: string): EventCallback[] {
|
|
52
|
+
return Array.from(this.events.get(event) || []);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { PortalTheme, PortalThemeConfig } from '../types/portal';
|
|
2
|
+
import { portalThemePresets } from '../themes/portalPresets';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generate a portal URL with theme parameters
|
|
6
|
+
* @param baseUrl The base portal URL
|
|
7
|
+
* @param theme The theme configuration
|
|
8
|
+
* @returns The portal URL with theme parameters
|
|
9
|
+
*/
|
|
10
|
+
export function generatePortalThemeURL(baseUrl: string, theme?: PortalTheme): string {
|
|
11
|
+
if (!theme) {
|
|
12
|
+
return baseUrl;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
const url = new URL(baseUrl);
|
|
17
|
+
|
|
18
|
+
if (theme.preset) {
|
|
19
|
+
// Use preset theme
|
|
20
|
+
url.searchParams.set('theme', theme.preset);
|
|
21
|
+
} else if (theme.custom) {
|
|
22
|
+
// Use custom theme
|
|
23
|
+
const encodedTheme = btoa(JSON.stringify(theme.custom));
|
|
24
|
+
url.searchParams.set('theme', 'custom');
|
|
25
|
+
url.searchParams.set('themeObject', encodedTheme);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return url.toString();
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error('Failed to generate theme URL:', error);
|
|
31
|
+
return baseUrl;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Generate a portal URL with theme parameters, appending to existing query params
|
|
37
|
+
* @param baseUrl The base portal URL (may already have query parameters)
|
|
38
|
+
* @param theme The theme configuration
|
|
39
|
+
* @returns The portal URL with theme parameters appended
|
|
40
|
+
*/
|
|
41
|
+
export function appendThemeToURL(baseUrl: string, theme?: PortalTheme): string {
|
|
42
|
+
if (!theme) {
|
|
43
|
+
return baseUrl;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const url = new URL(baseUrl);
|
|
48
|
+
|
|
49
|
+
if (theme.preset) {
|
|
50
|
+
// Use preset theme
|
|
51
|
+
url.searchParams.set('theme', theme.preset);
|
|
52
|
+
} else if (theme.custom) {
|
|
53
|
+
// Use custom theme
|
|
54
|
+
const encodedTheme = btoa(JSON.stringify(theme.custom));
|
|
55
|
+
url.searchParams.set('theme', 'custom');
|
|
56
|
+
url.searchParams.set('themeObject', encodedTheme);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return url.toString();
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error('Failed to append theme to URL:', error);
|
|
62
|
+
return baseUrl;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get a theme configuration by preset name
|
|
68
|
+
* @param preset The preset theme name
|
|
69
|
+
* @returns The theme configuration or undefined if not found
|
|
70
|
+
*/
|
|
71
|
+
export function getThemePreset(preset: string): PortalThemeConfig | undefined {
|
|
72
|
+
return portalThemePresets[preset];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Validate a custom theme configuration
|
|
77
|
+
* @param theme The theme configuration to validate
|
|
78
|
+
* @returns True if valid, false otherwise
|
|
79
|
+
*/
|
|
80
|
+
export function validateCustomTheme(theme: PortalThemeConfig): boolean {
|
|
81
|
+
try {
|
|
82
|
+
// Only validate what's provided - everything else gets defaults
|
|
83
|
+
if (theme.mode && !['dark', 'light', 'auto'].includes(theme.mode)) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// If colors are provided, validate the structure
|
|
88
|
+
if (theme.colors) {
|
|
89
|
+
// Check that any provided color sections have valid structure
|
|
90
|
+
const colorSections = ['background', 'status', 'text', 'border', 'input', 'button'];
|
|
91
|
+
for (const section of colorSections) {
|
|
92
|
+
const colorSection = theme.colors[section as keyof typeof theme.colors];
|
|
93
|
+
if (colorSection && typeof colorSection !== 'object') {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// If typography is provided, validate structure
|
|
100
|
+
if (theme.typography) {
|
|
101
|
+
if (theme.typography.fontSize && typeof theme.typography.fontSize !== 'object') {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
if (theme.typography.fontWeight && typeof theme.typography.fontWeight !== 'object') {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// If effects are provided, validate structure
|
|
110
|
+
if (theme.effects) {
|
|
111
|
+
if (theme.effects.animations && typeof theme.effects.animations !== 'object') {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
if (theme.effects.shadows && typeof theme.effects.shadows !== 'object') {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return true;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error('Theme validation error:', error);
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Create a custom theme from a preset with modifications
|
|
128
|
+
* @param preset The base preset theme
|
|
129
|
+
* @param modifications Partial theme modifications
|
|
130
|
+
* @returns The modified theme configuration
|
|
131
|
+
*/
|
|
132
|
+
export function createCustomThemeFromPreset(
|
|
133
|
+
preset: string,
|
|
134
|
+
modifications: Partial<PortalThemeConfig>
|
|
135
|
+
): PortalThemeConfig | null {
|
|
136
|
+
const baseTheme = getThemePreset(preset);
|
|
137
|
+
if (!baseTheme) {
|
|
138
|
+
console.error(`Preset theme '${preset}' not found`);
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
...baseTheme,
|
|
144
|
+
...modifications,
|
|
145
|
+
};
|
|
146
|
+
}
|