@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.
Files changed (39) hide show
  1. package/README.md +278 -461
  2. package/dist/index.d.ts +55 -515
  3. package/dist/index.js +326 -456
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +327 -456
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/types/core/client/ApiClient.d.ts +12 -26
  8. package/dist/types/core/client/FinaticConnect.d.ts +20 -103
  9. package/dist/types/index.d.ts +1 -2
  10. package/dist/types/mocks/MockApiClient.d.ts +2 -4
  11. package/dist/types/mocks/utils.d.ts +0 -5
  12. package/dist/types/types/api/auth.d.ts +12 -30
  13. package/dist/types/types/api/broker.d.ts +1 -1
  14. package/package.json +7 -3
  15. package/src/core/client/ApiClient.ts +1721 -0
  16. package/src/core/client/FinaticConnect.ts +1476 -0
  17. package/src/core/portal/PortalUI.ts +300 -0
  18. package/src/index.d.ts +23 -0
  19. package/src/index.ts +87 -0
  20. package/src/mocks/MockApiClient.ts +1032 -0
  21. package/src/mocks/MockDataProvider.ts +986 -0
  22. package/src/mocks/MockFactory.ts +97 -0
  23. package/src/mocks/utils.ts +133 -0
  24. package/src/themes/portalPresets.ts +1307 -0
  25. package/src/types/api/auth.ts +112 -0
  26. package/src/types/api/broker.ts +330 -0
  27. package/src/types/api/core.ts +53 -0
  28. package/src/types/api/errors.ts +35 -0
  29. package/src/types/api/orders.ts +45 -0
  30. package/src/types/api/portfolio.ts +59 -0
  31. package/src/types/common/pagination.ts +138 -0
  32. package/src/types/connect.ts +56 -0
  33. package/src/types/index.ts +25 -0
  34. package/src/types/portal.ts +214 -0
  35. package/src/types/ui/theme.ts +105 -0
  36. package/src/utils/brokerUtils.ts +85 -0
  37. package/src/utils/errors.ts +104 -0
  38. package/src/utils/events.ts +54 -0
  39. package/src/utils/themeUtils.ts +146 -0
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Pagination-related types and classes
3
+ */
4
+
5
+ export interface ApiPaginationInfo {
6
+ has_more: boolean;
7
+ next_offset: number;
8
+ current_offset: number;
9
+ limit: number;
10
+ }
11
+
12
+ export interface PaginationMetadata {
13
+ hasMore: boolean;
14
+ nextOffset: number;
15
+ currentOffset: number;
16
+ limit: number;
17
+ currentPage: number;
18
+ hasNext: boolean;
19
+ hasPrevious: boolean;
20
+ }
21
+
22
+ export class PaginatedResult<T> {
23
+ public readonly data: T;
24
+ public readonly metadata: PaginationMetadata;
25
+ private navigationCallback?: (offset: number, limit: number) => Promise<PaginatedResult<T>>;
26
+
27
+ constructor(
28
+ data: T,
29
+ paginationInfo: ApiPaginationInfo,
30
+ navigationCallback?: (offset: number, limit: number) => Promise<PaginatedResult<T>>
31
+ ) {
32
+ this.data = data;
33
+ this.navigationCallback = navigationCallback;
34
+ this.metadata = {
35
+ hasMore: paginationInfo.has_more,
36
+ nextOffset: paginationInfo.next_offset,
37
+ currentOffset: paginationInfo.current_offset,
38
+ limit: paginationInfo.limit,
39
+ currentPage: Math.floor(paginationInfo.current_offset / paginationInfo.limit) + 1,
40
+ hasNext: paginationInfo.has_more,
41
+ hasPrevious: paginationInfo.current_offset > 0,
42
+ };
43
+ }
44
+
45
+ get hasNext(): boolean {
46
+ return this.metadata.hasNext;
47
+ }
48
+
49
+ get hasPrevious(): boolean {
50
+ return this.metadata.hasPrevious;
51
+ }
52
+
53
+ get currentPage(): number {
54
+ return this.metadata.currentPage;
55
+ }
56
+
57
+ async nextPage(): Promise<PaginatedResult<T> | null> {
58
+ if (!this.hasNext || !this.navigationCallback) {
59
+ return null;
60
+ }
61
+
62
+ try {
63
+ return await this.navigationCallback(this.metadata.nextOffset, this.metadata.limit);
64
+ } catch (error) {
65
+ console.error('Error fetching next page:', error);
66
+ return null;
67
+ }
68
+ }
69
+
70
+ async previousPage(): Promise<PaginatedResult<T> | null> {
71
+ if (!this.hasPrevious || !this.navigationCallback) {
72
+ return null;
73
+ }
74
+
75
+ const previousOffset = Math.max(0, this.metadata.currentOffset - this.metadata.limit);
76
+ try {
77
+ return await this.navigationCallback(previousOffset, this.metadata.limit);
78
+ } catch (error) {
79
+ console.error('Error fetching previous page:', error);
80
+ return null;
81
+ }
82
+ }
83
+
84
+ async goToPage(pageNumber: number): Promise<PaginatedResult<T> | null> {
85
+ if (!this.navigationCallback || pageNumber < 1) {
86
+ return null;
87
+ }
88
+
89
+ const offset = (pageNumber - 1) * this.metadata.limit;
90
+ try {
91
+ return await this.navigationCallback(offset, this.metadata.limit);
92
+ } catch (error) {
93
+ console.error('Error fetching page:', pageNumber, error);
94
+ return null;
95
+ }
96
+ }
97
+
98
+ async firstPage(): Promise<PaginatedResult<T> | null> {
99
+ if (!this.navigationCallback) {
100
+ return null;
101
+ }
102
+
103
+ try {
104
+ return await this.navigationCallback(0, this.metadata.limit);
105
+ } catch (error) {
106
+ console.error('Error fetching first page:', error);
107
+ return null;
108
+ }
109
+ }
110
+
111
+ async lastPage(): Promise<PaginatedResult<T> | null> {
112
+ if (!this.navigationCallback) {
113
+ return null;
114
+ }
115
+
116
+ const findLast = async (page: PaginatedResult<T>): Promise<PaginatedResult<T>> => {
117
+ if (!page.hasNext) {
118
+ return page;
119
+ }
120
+ const nextPage = await page.nextPage();
121
+ if (!nextPage) {
122
+ return page;
123
+ }
124
+ return findLast(nextPage);
125
+ };
126
+
127
+ try {
128
+ return await findLast(this);
129
+ } catch (error) {
130
+ console.error('Error fetching last page:', error);
131
+ return null;
132
+ }
133
+ }
134
+
135
+ getPaginationInfo(): string {
136
+ return `Page ${this.currentPage} (${this.metadata.currentOffset + 1}-${this.metadata.currentOffset + this.metadata.limit})`;
137
+ }
138
+ }
@@ -0,0 +1,56 @@
1
+ import { Theme } from './ui/theme';
2
+ import { UserToken } from './api/auth';
3
+ import { PortalTheme } from './portal';
4
+
5
+ export interface FinaticConnectOptions {
6
+ /** The portal token from your backend */
7
+ token: string;
8
+ /** Optional base URL for API requests */
9
+ baseUrl?: string;
10
+ /** Optional origin for the portal */
11
+ origin?: string;
12
+ /** Callback when user successfully connects */
13
+ onSuccess?: (tokens: UserToken) => void;
14
+ /** Callback when an error occurs */
15
+ onError?: (error: Error) => void;
16
+ /** Callback when the portal is closed */
17
+ onClose?: () => void;
18
+ /** Optional theme configuration */
19
+ theme?: Theme;
20
+ /** Callback when tokens are received */
21
+ onTokensReceived?: (tokens: { access_token?: string; refresh_token?: string }) => void;
22
+ }
23
+
24
+ export interface FinaticUserToken {
25
+ accessToken: string;
26
+ refreshToken: string;
27
+ userId: string;
28
+ companyId: string;
29
+ expiresAt: Date;
30
+ }
31
+
32
+ export interface PortalMessage {
33
+ type: 'success' | 'error' | 'close' | 'resize';
34
+ userId?: string;
35
+ error?: string;
36
+ height?: number;
37
+ access_token?: string;
38
+ refresh_token?: string;
39
+ }
40
+
41
+ export interface PortalOptions {
42
+ /** Callback when user successfully connects */
43
+ onSuccess?: (userId: string, tokens?: { access_token?: string; refresh_token?: string }) => void;
44
+ /** Callback when an error occurs */
45
+ onError?: (error: Error) => void;
46
+ /** Callback when the portal is closed */
47
+ onClose?: () => void;
48
+ /** Callback when portal events occur */
49
+ onEvent?: (type: string, data: any) => void;
50
+ /** Optional theme configuration for the portal */
51
+ theme?: PortalTheme;
52
+ /** Optional list of broker names to filter by (only these brokers will be shown) */
53
+ brokers?: string[];
54
+ /** Optional email address to prefill in the portal */
55
+ email?: string;
56
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Main types barrel export
3
+ */
4
+
5
+ // Core API types
6
+ export * from './api/core';
7
+ export * from './api/auth';
8
+ export * from './api/broker';
9
+ export * from './api/orders';
10
+ export * from './api/portfolio';
11
+
12
+ // UI types
13
+ export * from './ui/theme';
14
+
15
+ // Common types
16
+ export * from './common/pagination';
17
+
18
+ // Connect types
19
+ export * from './connect';
20
+
21
+ // Re-export DeviceInfo for backward compatibility
22
+ export type { DeviceInfo } from './api/auth';
23
+
24
+ // Explicit re-export of SessionResponse to ensure it's available
25
+ export type { SessionResponse } from './api/auth';
@@ -0,0 +1,214 @@
1
+ import { Theme } from './ui/theme';
2
+
3
+ export interface PortalConfig {
4
+ width?: string;
5
+ height?: string;
6
+ position?: 'center' | 'top' | 'bottom';
7
+ zIndex?: number;
8
+ }
9
+
10
+ export interface PortalThemeConfig {
11
+ mode?: 'dark' | 'light' | 'auto';
12
+ colors?: {
13
+ background?: {
14
+ primary?: string; // Main background color
15
+ secondary?: string; // Secondary background color
16
+ tertiary?: string; // Tertiary background color
17
+ accent?: string; // Accent background color
18
+ glass?: string; // Glassmorphism background
19
+ };
20
+ status?: {
21
+ connected?: string; // Connected status color
22
+ disconnected?: string; // Disconnected status color
23
+ warning?: string; // Warning status color
24
+ pending?: string; // Pending status color
25
+ error?: string; // Error status color
26
+ success?: string; // Success status color
27
+ };
28
+ text?: {
29
+ primary?: string; // Primary text color
30
+ secondary?: string; // Secondary text color
31
+ muted?: string; // Muted text color
32
+ inverse?: string; // Inverse text color
33
+ };
34
+ border?: {
35
+ primary?: string; // Primary border color
36
+ secondary?: string; // Secondary border color
37
+ hover?: string; // Hover border color
38
+ focus?: string; // Focus border color
39
+ accent?: string; // Accent border color
40
+ };
41
+ input?: {
42
+ background?: string; // Input background color
43
+ border?: string; // Input border color
44
+ borderFocus?: string; // Input focus border color
45
+ text?: string; // Input text color
46
+ placeholder?: string; // Input placeholder color
47
+ };
48
+ button?: {
49
+ primary?: {
50
+ background?: string; // Primary button background
51
+ text?: string; // Primary button text
52
+ hover?: string; // Primary button hover
53
+ active?: string; // Primary button active
54
+ };
55
+ secondary?: {
56
+ background?: string; // Secondary button background
57
+ text?: string; // Secondary button text
58
+ border?: string; // Secondary button border
59
+ hover?: string; // Secondary button hover
60
+ active?: string; // Secondary button active
61
+ };
62
+ };
63
+ };
64
+ typography?: {
65
+ fontFamily?: {
66
+ primary?: string;
67
+ secondary?: string;
68
+ };
69
+ fontSize?: {
70
+ xs?: string;
71
+ sm?: string;
72
+ base?: string;
73
+ lg?: string;
74
+ xl?: string;
75
+ '2xl'?: string;
76
+ '3xl'?: string;
77
+ '4xl'?: string;
78
+ };
79
+ fontWeight?: {
80
+ normal?: number;
81
+ medium?: number;
82
+ semibold?: number;
83
+ bold?: number;
84
+ extrabold?: number;
85
+ };
86
+ lineHeight?: {
87
+ tight?: string;
88
+ normal?: string;
89
+ relaxed?: string;
90
+ };
91
+ };
92
+ spacing?: {
93
+ xs?: string;
94
+ sm?: string;
95
+ md?: string;
96
+ lg?: string;
97
+ xl?: string;
98
+ '2xl'?: string;
99
+ '3xl'?: string;
100
+ };
101
+ layout?: {
102
+ containerMaxWidth?: string;
103
+ gridGap?: string;
104
+ cardPadding?: string;
105
+ borderRadius?: {
106
+ sm?: string;
107
+ md?: string;
108
+ lg?: string;
109
+ xl?: string;
110
+ '2xl'?: string;
111
+ full?: string;
112
+ };
113
+ };
114
+ components?: {
115
+ brokerCard?: {
116
+ width?: string;
117
+ height?: string;
118
+ logoSize?: string;
119
+ padding?: string;
120
+ };
121
+ statusIndicator?: {
122
+ size?: string;
123
+ glowIntensity?: number;
124
+ };
125
+ modal?: {
126
+ background?: string;
127
+ backdrop?: string;
128
+ };
129
+ brokerCardModern?: {
130
+ width?: string;
131
+ height?: string;
132
+ padding?: string;
133
+ logoSize?: string;
134
+ statusSize?: string;
135
+ };
136
+ connectButton?: {
137
+ width?: string;
138
+ height?: string;
139
+ };
140
+ themeSwitcher?: {
141
+ indicatorSize?: string;
142
+ };
143
+ };
144
+ effects?: {
145
+ glassmorphism?: {
146
+ enabled?: boolean;
147
+ blur?: string;
148
+ opacity?: number;
149
+ border?: string;
150
+ };
151
+ animations?: {
152
+ enabled?: boolean;
153
+ duration?: {
154
+ fast?: string;
155
+ normal?: string;
156
+ slow?: string;
157
+ };
158
+ easing?: {
159
+ default?: string;
160
+ smooth?: string;
161
+ bounce?: string;
162
+ };
163
+ };
164
+ shadows?: {
165
+ sm?: string;
166
+ md?: string;
167
+ lg?: string;
168
+ xl?: string;
169
+ card?: string;
170
+ cardHover?: string;
171
+ glow?: string;
172
+ focus?: string;
173
+ };
174
+ };
175
+ branding?: {
176
+ logo?: string;
177
+ companyName?: string;
178
+ favicon?: string;
179
+ primaryColor?: string;
180
+ };
181
+
182
+ // Glow effect customization
183
+ glow?: {
184
+ primary?: string; // Primary glow color
185
+ secondary?: string; // Secondary glow color
186
+ card?: string; // Card glow effect
187
+ cardHover?: string; // Card hover glow
188
+ button?: string; // Button glow effect
189
+ focus?: string; // Focus ring glow
190
+ scrollbar?: string; // Scrollbar glow
191
+ };
192
+
193
+ // Gradient customization
194
+ gradients?: {
195
+ start?: string; // Gradient start color
196
+ end?: string; // Gradient end color
197
+ hoverStart?: string; // Hover gradient start
198
+ hoverEnd?: string; // Hover gradient end
199
+ };
200
+ }
201
+
202
+ export type PortalThemePreset = 'dark' | 'light' | 'corporateBlue' | 'purple' | 'green' | 'orange';
203
+
204
+ export interface PortalTheme {
205
+ preset?: PortalThemePreset;
206
+ custom?: PortalThemeConfig;
207
+ }
208
+
209
+ export interface PortalProps {
210
+ config: PortalConfig;
211
+ onClose?: () => void;
212
+ onReady?: () => void;
213
+ onError?: (error: Error) => void;
214
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Theme-related types
3
+ */
4
+
5
+ export interface Theme {
6
+ mode: 'light' | 'dark';
7
+ primaryColor: string;
8
+ colors: {
9
+ primary: string;
10
+ secondary: string;
11
+ background: {
12
+ primary: string;
13
+ secondary: string;
14
+ tertiary: string;
15
+ };
16
+ text: {
17
+ primary: string;
18
+ secondary: string;
19
+ disabled: string;
20
+ error: string;
21
+ success: string;
22
+ warning: string;
23
+ };
24
+ border: {
25
+ light: string;
26
+ medium: string;
27
+ dark: string;
28
+ };
29
+ status: {
30
+ active: string;
31
+ inactive: string;
32
+ pending: string;
33
+ error: string;
34
+ success: string;
35
+ };
36
+ };
37
+ typography: {
38
+ fontFamily: string;
39
+ fontSize: {
40
+ xs: string;
41
+ sm: string;
42
+ base: string;
43
+ lg: string;
44
+ xl: string;
45
+ '2xl': string;
46
+ };
47
+ fontWeight: {
48
+ light: number;
49
+ normal: number;
50
+ medium: number;
51
+ semibold: number;
52
+ bold: number;
53
+ };
54
+ lineHeight: {
55
+ none: number;
56
+ tight: number;
57
+ normal: number;
58
+ relaxed: number;
59
+ };
60
+ };
61
+ spacing: {
62
+ xs: string;
63
+ sm: string;
64
+ md: string;
65
+ lg: string;
66
+ xl: string;
67
+ '2xl': string;
68
+ };
69
+ animation: {
70
+ duration: {
71
+ fast: string;
72
+ normal: string;
73
+ slow: string;
74
+ };
75
+ easing: {
76
+ linear: string;
77
+ easeIn: string;
78
+ easeOut: string;
79
+ easeInOut: string;
80
+ };
81
+ };
82
+ components: {
83
+ button: {
84
+ borderRadius: string;
85
+ padding: string;
86
+ fontSize: string;
87
+ fontWeight: number;
88
+ };
89
+ input: {
90
+ borderRadius: string;
91
+ padding: string;
92
+ fontSize: string;
93
+ };
94
+ card: {
95
+ borderRadius: string;
96
+ padding: string;
97
+ shadow: string;
98
+ };
99
+ modal: {
100
+ borderRadius: string;
101
+ padding: string;
102
+ backdrop: string;
103
+ };
104
+ };
105
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Broker filtering utility functions
3
+ */
4
+
5
+ // Supported broker names and their corresponding IDs (including aliases)
6
+ const SUPPORTED_BROKERS: Record<string, string> = {
7
+ 'alpaca': 'alpaca',
8
+ 'robinhood': 'robinhood',
9
+ 'tasty_trade': 'tasty_trade',
10
+ 'ninja_trader': 'ninja_trader',
11
+ 'tradovate': 'tradovate', // Alias for ninja_trader
12
+ 'interactive_brokers': 'interactive_brokers',
13
+ };
14
+
15
+ /**
16
+ * Convert broker names to broker IDs, filtering out unsupported ones
17
+ * @param brokerNames Array of broker names to convert
18
+ * @returns Object containing valid broker IDs and any warnings about unsupported names
19
+ */
20
+ export function convertBrokerNamesToIds(brokerNames: string[]): {
21
+ brokerIds: string[];
22
+ warnings: string[];
23
+ } {
24
+ const brokerIds: string[] = [];
25
+ const warnings: string[] = [];
26
+
27
+ for (const brokerName of brokerNames) {
28
+ const brokerId = SUPPORTED_BROKERS[brokerName.toLowerCase()];
29
+ if (brokerId) {
30
+ brokerIds.push(brokerId);
31
+ } else {
32
+ warnings.push(`Broker name '${brokerName}' is not supported. Supported brokers: ${Object.keys(SUPPORTED_BROKERS).join(', ')}`);
33
+ }
34
+ }
35
+
36
+ return { brokerIds, warnings };
37
+ }
38
+
39
+ /**
40
+ * Append broker filter parameters to a portal URL
41
+ * @param baseUrl The base portal URL (may already have query parameters)
42
+ * @param brokerNames Array of broker names to filter by
43
+ * @returns The portal URL with broker filter parameters appended
44
+ */
45
+ export function appendBrokerFilterToURL(baseUrl: string, brokerNames?: string[]): string {
46
+ if (!brokerNames || brokerNames.length === 0) {
47
+ return baseUrl;
48
+ }
49
+
50
+ try {
51
+ const url = new URL(baseUrl);
52
+ const { brokerIds, warnings } = convertBrokerNamesToIds(brokerNames);
53
+
54
+ // Log warnings for unsupported broker names
55
+ warnings.forEach(warning => console.warn(`[FinaticConnect] ${warning}`));
56
+
57
+ // Only add broker filter if we have valid broker IDs
58
+ if (brokerIds.length > 0) {
59
+ const encodedBrokers = btoa(JSON.stringify(brokerIds));
60
+ url.searchParams.set('brokers', encodedBrokers);
61
+ }
62
+
63
+ return url.toString();
64
+ } catch (error) {
65
+ console.error('Failed to append broker filter to URL:', error);
66
+ return baseUrl;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Get list of supported broker names
72
+ * @returns Array of supported broker names
73
+ */
74
+ export function getSupportedBrokerNames(): string[] {
75
+ return Object.keys(SUPPORTED_BROKERS);
76
+ }
77
+
78
+ /**
79
+ * Check if a broker name is supported
80
+ * @param brokerName The broker name to check
81
+ * @returns True if the broker is supported, false otherwise
82
+ */
83
+ export function isBrokerSupported(brokerName: string): boolean {
84
+ return brokerName.toLowerCase() in SUPPORTED_BROKERS;
85
+ }