@finatic/client 0.9.1 → 0.9.2

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 CHANGED
@@ -1,6 +1,6 @@
1
- # Client
1
+ # Finatic Client SDK
2
2
 
3
- A comprehensive browser SDK for integrating Finatic Client into your web applications. Connect your users to multiple brokerages through a unified, standardized interface.
3
+ A comprehensive browser SDK for integrating Finatic into your web applications. Connect your users to multiple brokerages through a unified, standardized interface.
4
4
 
5
5
  **Finatic is a brokerage first aggregator. We simplify, standardize and enhance broker data.**
6
6
 
@@ -10,80 +10,276 @@ A comprehensive browser SDK for integrating Finatic Client into your web applica
10
10
  npm install @finatic/client
11
11
  ```
12
12
 
13
- ## Quick Start
13
+ Or using yarn:
14
+
15
+ ```bash
16
+ yarn add @finatic/client
17
+ ```
18
+
19
+ ## Quick Start (5 Minutes)
20
+
21
+ ### 1. Initialize the SDK
14
22
 
15
23
  ```typescript
16
- import { FinaticClient from '@finatic/client';
24
+ import { FinaticConnect } from '@finatic/client';
17
25
 
18
- // TODO: Add example usage
26
+ // Initialize with a one-time token (obtained from your backend)
27
+ const finatic = await FinaticConnect.init('your-one-time-token', undefined, {
28
+ baseUrl: 'https://api.finatic.dev', // Optional, defaults to production
29
+ logLevel: 'info', // Optional: 'debug' | 'info' | 'warn' | 'error'
30
+ });
19
31
  ```
20
32
 
21
- ## Documentation
33
+ **Note:** The Client SDK requires a one-time token (not an API key). Get this token from your backend server using the Server SDK's `getToken()` method.
22
34
 
23
- Full documentation is available at [docs.finatic.com](/client/typescript).
35
+ ### 2. Open the Portal for Authentication
24
36
 
25
- ## Development
37
+ The portal allows users to connect their broker accounts. You can either:
26
38
 
27
- ```bash
28
- # Install dependencies
29
- npm install
39
+ **Option A: Open portal in iframe (recommended)**
30
40
 
31
- # Build
32
- npm run build
41
+ ```typescript
42
+ await finatic.openPortal(
43
+ 'dark', // Theme: 'light' | 'dark' | theme object
44
+ ['alpaca', 'tradier'], // Optional: Filter specific brokers
45
+ 'user@example.com', // Optional: Pre-fill email
46
+ 'modal', // Mode: 'modal' | 'fullscreen'
47
+ (userId) => {
48
+ // User successfully authenticated
49
+ console.log('User authenticated:', userId);
50
+ },
51
+ (error) => {
52
+ // Handle authentication error
53
+ console.error('Portal error:', error);
54
+ },
55
+ () => {
56
+ // Portal was closed
57
+ console.log('Portal closed');
58
+ }
59
+ );
60
+ ```
61
+
62
+ **Option B: Get portal URL and redirect**
33
63
 
34
- # Run tests
35
- npm test
64
+ ```typescript
65
+ const portalUrl = await finatic.getPortalUrl(
66
+ 'dark', // Theme
67
+ ['alpaca'], // Optional: Filter brokers
68
+ 'user@example.com', // Optional: Pre-fill email
69
+ 'dark' // Mode
70
+ );
71
+
72
+ // Redirect user to portal URL
73
+ window.location.href = portalUrl;
74
+ ```
36
75
 
37
- # Lint
38
- npm run lint
76
+ ### 3. Check Authentication Status
39
77
 
40
- # Format
41
- npm run format
78
+ ```typescript
79
+ // Check if user is authenticated
80
+ const isAuthenticated = finatic.isAuthed();
81
+
82
+ // Get current user ID
83
+ const userId = finatic.getUserId();
42
84
  ```
43
85
 
44
- ## Quality Checks
86
+ ### 4. Fetch Data
45
87
 
46
- Run all quality checks to ensure code quality:
88
+ Once authenticated, you can fetch broker data:
47
89
 
48
- ```bash
49
- # Run all quality checks (format, lint, type check, build)
50
- npm run quality:check
90
+ ```typescript
91
+ // Get all orders (automatically paginates through all pages)
92
+ const allOrders = await finatic.getAllOrders();
93
+
94
+ // Get orders with filters (single page)
95
+ const orders = await finatic.getOrders({
96
+ brokerId: 'alpaca',
97
+ accountId: '123456789',
98
+ orderStatus: 'filled',
99
+ limit: 100,
100
+ offset: 0,
101
+ });
102
+
103
+ // Get all positions
104
+ const allPositions = await finatic.getAllPositions();
105
+
106
+ // Get positions with filters
107
+ const positions = await finatic.getPositions({
108
+ brokerId: 'alpaca',
109
+ symbol: 'AAPL',
110
+ limit: 50,
111
+ });
112
+
113
+ // Get all accounts
114
+ const allAccounts = await finatic.getAllAccounts();
115
+
116
+ // Get balances
117
+ const balances = await finatic.getBalances({
118
+ brokerId: 'alpaca',
119
+ accountId: '123456789',
120
+ });
121
+ ```
122
+
123
+ ## Complete Example
51
124
 
52
- # Fix all auto-fixable issues (format, lint) and build
53
- npm run quality:fix
125
+ ```typescript
126
+ import { FinaticConnect } from '@finatic/client';
127
+
128
+ async function setupFinatic() {
129
+ // 1. Initialize SDK (get token from your backend)
130
+ const token = await fetch('/api/finatic/token').then((r) => r.text());
131
+ const finatic = await FinaticConnect.init(token);
132
+
133
+ // 2. Open portal for authentication
134
+ await finatic.openPortal(
135
+ 'dark',
136
+ undefined, // Show all brokers
137
+ undefined, // No pre-filled email
138
+ 'modal',
139
+ async (userId) => {
140
+ console.log('User authenticated:', userId);
141
+
142
+ // 3. Fetch data after authentication
143
+ const orders = await finatic.getAllOrders();
144
+ const positions = await finatic.getAllPositions();
145
+ const accounts = await finatic.getAllAccounts();
146
+
147
+ console.log('Orders:', orders);
148
+ console.log('Positions:', positions);
149
+ console.log('Accounts:', accounts);
150
+ },
151
+ (error) => {
152
+ console.error('Authentication failed:', error);
153
+ }
154
+ );
155
+ }
156
+
157
+ setupFinatic();
54
158
  ```
55
159
 
56
- Individual quality checks:
160
+ ## Available Data Methods
57
161
 
58
- ```bash
59
- # Format check (without modifying files)
60
- npm run format:check
61
- # Format fix (modifies files)
62
- npm run format:fix
162
+ ### Orders
163
+
164
+ - `getOrders(params?)` - Get single page of orders
165
+ - `getAllOrders(params?)` - Get all orders (auto-paginated)
166
+ - `getOrderFills({ orderId, ... })` - Get fills for a specific order
167
+ - `getAllOrderFills({ orderId, ... })` - Get all fills (auto-paginated)
168
+ - `getOrderEvents({ orderId, ... })` - Get events for a specific order
169
+ - `getAllOrderEvents({ orderId, ... })` - Get all events (auto-paginated)
170
+ - `getOrderGroups(params?)` - Get order groups
171
+ - `getAllOrderGroups(params?)` - Get all order groups (auto-paginated)
172
+
173
+ ### Positions
174
+
175
+ - `getPositions(params?)` - Get single page of positions
176
+ - `getAllPositions(params?)` - Get all positions (auto-paginated)
177
+ - `getPositionLots(params?)` - Get position lots
178
+ - `getAllPositionLots(params?)` - Get all position lots (auto-paginated)
179
+ - `getPositionLotFills({ lotId, ... })` - Get fills for a specific lot
180
+ - `getAllPositionLotFills({ lotId, ... })` - Get all fills (auto-paginated)
181
+
182
+ ### Accounts & Balances
183
+
184
+ - `getAccounts(params?)` - Get single page of accounts
185
+ - `getAllAccounts(params?)` - Get all accounts (auto-paginated)
186
+ - `getBalances(params?)` - Get balances
187
+ - `getAllBalances(params?)` - Get all balances (auto-paginated)
63
188
 
64
- # Lint check
65
- npm run lint
66
- # Lint fix (modifies files)
67
- npm run lint:fix
189
+ ### Broker Management
68
190
 
69
- # Type check
70
- npm run type:check
191
+ - `getBrokers()` - Get available brokers
192
+ - `getBrokerConnections()` - Get connected broker accounts
193
+ - `disconnectCompanyFromBroker({ connectionId })` - Disconnect a broker
71
194
 
72
- # Syntax check (strict linting)
73
- npm run syntax:check
74
- # Syntax fix (modifies files)
75
- npm run syntax:fix
195
+ ### Company
76
196
 
77
- # Import check
78
- npm run import:check
197
+ - `getCompany({ companyId })` - Get company information
79
198
 
80
- # Build check
81
- npm run build
199
+ ## Method Parameters
200
+
201
+ Most data methods accept optional parameters:
202
+
203
+ ```typescript
204
+ {
205
+ brokerId?: string; // Filter by broker (e.g., 'alpaca', 'tradier')
206
+ connectionId?: string; // Filter by connection ID
207
+ accountId?: string; // Filter by account ID
208
+ symbol?: string; // Filter by symbol (e.g., 'AAPL')
209
+ limit?: number; // Page size (default: varies by endpoint)
210
+ offset?: number; // Pagination offset (default: 0)
211
+ includeMetadata?: boolean; // Include metadata in response
212
+ // ... other filters specific to each method
213
+ }
214
+ ```
215
+
216
+ ## Event Handling
217
+
218
+ The SDK extends EventEmitter, so you can listen to events:
219
+
220
+ ```typescript
221
+ // Listen for portal events
222
+ finatic.on('portal:success', (userId: string) => {
223
+ console.log('Portal authentication successful:', userId);
224
+ });
225
+
226
+ finatic.on('portal:error', (error: Error) => {
227
+ console.error('Portal error:', error);
228
+ });
229
+
230
+ finatic.on('portal:close', () => {
231
+ console.log('Portal closed');
232
+ });
233
+ ```
234
+
235
+ ## Response Format
236
+
237
+ All data methods return a `FinaticResponse` object:
238
+
239
+ ```typescript
240
+ {
241
+ success: {
242
+ data: T[], // Array of data items
243
+ // ... other metadata
244
+ },
245
+ error?: {
246
+ message: string;
247
+ code?: string;
248
+ },
249
+ warning?: string[]
250
+ }
251
+ ```
252
+
253
+ Access the data:
254
+
255
+ ```typescript
256
+ const result = await finatic.getOrders();
257
+ if (result.success) {
258
+ const orders = result.success.data;
259
+ // Use orders...
260
+ } else {
261
+ console.error('Error:', result.error?.message);
262
+ }
263
+ ```
264
+
265
+ ## Configuration Options
266
+
267
+ ```typescript
268
+ const finatic = await FinaticConnect.init(token, userId, {
269
+ baseUrl: 'https://api.finatic.dev', // API base URL
270
+ logLevel: 'info', // 'debug' | 'info' | 'warn' | 'error'
271
+ structuredLogging: false, // Enable structured JSON logging
272
+ // ... other options
273
+ });
82
274
  ```
83
275
 
276
+ ## Documentation
277
+
278
+ Full API documentation is available at [https://finatic.dev/docs](https://finatic.dev/doc).
279
+
84
280
  ## License
85
281
 
86
- MIT License
282
+ PROPRIETARY
87
283
 
88
284
  ## Copyright
89
285
 
package/dist/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import * as axios from 'axios';
2
2
  import { AxiosInstance, RawAxiosRequestConfig, AxiosPromise, AxiosRequestConfig, AxiosResponse } from 'axios';
3
3
  import * as z from 'zod';
4
- import NodeCache from 'node-cache';
5
4
 
6
5
  /**
7
6
  * Finatic FastAPI Backend
@@ -4375,12 +4374,11 @@ declare const defaultConfig: SdkConfig;
4375
4374
  declare function getConfig(overrides?: Partial<SdkConfig>): SdkConfig;
4376
4375
 
4377
4376
  /**
4378
- * Structured logger utility with browser-safe fallback (Phase 2C).
4377
+ * Structured logger utility with browser-safe console logging (Phase 2C).
4379
4378
  *
4380
4379
  * Generated - do not edit directly.
4381
4380
  *
4382
- * This logger automatically falls back to console-based logging when pino fails
4383
- * in browser environments (Vite, Next.js, etc.).
4381
+ * This logger uses browser console APIs for all logging in browser environments.
4384
4382
  */
4385
4383
 
4386
4384
  type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
@@ -5700,15 +5698,23 @@ declare function numberSchema(min?: number, max?: number, defaultVal?: number):
5700
5698
  declare function stringSchema(min?: number, max?: number, defaultVal?: string): z.ZodString;
5701
5699
 
5702
5700
  /**
5703
- * Response caching utility with node-cache (Phase 2B).
5701
+ * Response caching utility with browser-compatible Map-based cache (Phase 2B).
5704
5702
  *
5705
5703
  * Generated - do not edit directly.
5706
5704
  */
5707
5705
 
5706
+ interface BrowserCache {
5707
+ get(key: string): any | undefined;
5708
+ set(key: string, value: any, ttl?: number): boolean;
5709
+ del(key: string): number;
5710
+ clear(): void;
5711
+ keys(): string[];
5712
+ has(key: string): boolean;
5713
+ }
5708
5714
  /**
5709
5715
  * Get or create cache instance.
5710
5716
  */
5711
- declare function getCache(config?: SdkConfig): NodeCache | null;
5717
+ declare function getCache(config?: SdkConfig): BrowserCache | null;
5712
5718
  /**
5713
5719
  * Generate cache key from method and parameters.
5714
5720
  */
package/dist/index.js CHANGED
@@ -1,8 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  var pRetry = require('p-retry');
4
- var pino = require('pino');
5
- var NodeCache = require('node-cache');
6
4
  var z = require('zod');
7
5
  var globalAxios = require('axios');
8
6
 
@@ -93,14 +91,12 @@ async function retryApiCall(fn, options = {}, config) {
93
91
  }
94
92
 
95
93
  /**
96
- * Structured logger utility with browser-safe fallback (Phase 2C).
94
+ * Structured logger utility with browser-safe console logging (Phase 2C).
97
95
  *
98
96
  * Generated - do not edit directly.
99
97
  *
100
- * This logger automatically falls back to console-based logging when pino fails
101
- * in browser environments (Vite, Next.js, etc.).
98
+ * This logger uses browser console APIs for all logging in browser environments.
102
99
  */
103
- // @ts-ignore - pino types available via @types/pino
104
100
  let _loggerInstance = null;
105
101
  /**
106
102
  * Get environment variable from various sources (browser and Node.js).
@@ -145,30 +141,6 @@ function isProduction() {
145
141
  return true;
146
142
  return false;
147
143
  }
148
- /**
149
- * Detect if we're in a browser environment.
150
- */
151
- function isBrowser() {
152
- // Check for window object (browser)
153
- if (typeof window !== 'undefined') {
154
- return true;
155
- }
156
- // Check for globalThis in browser-like environments
157
- if (typeof globalThis !== 'undefined') {
158
- // In browsers, process might be polyfilled but process.stdout won't exist
159
- try {
160
- const proc = globalThis.process;
161
- if (proc && typeof proc.stdout === 'undefined') {
162
- return true;
163
- }
164
- }
165
- catch {
166
- // If we can't check, assume browser if window exists
167
- return typeof window !== 'undefined';
168
- }
169
- }
170
- return false;
171
- }
172
144
  /**
173
145
  * Get or create a logger instance with browser-safe fallback.
174
146
  */
@@ -176,36 +148,13 @@ function getLogger(config) {
176
148
  if (_loggerInstance) {
177
149
  return _loggerInstance;
178
150
  }
179
- // In browser environments, skip pino entirely and use browser-safe logger
180
- if (isBrowser()) {
181
- return getBrowserSafeLogger(config);
182
- }
183
- // Try to use pino first (Node.js environments)
184
- try {
185
- const logLevel = (config?.logLevel || getEnvVar('FINATIC_LOG_LEVEL') || 'error');
186
- const pinoConfig = {
187
- level: logLevel === 'silent' ? 'silent' : logLevel,
188
- ...(config?.structuredLogging !== false && {
189
- formatters: {
190
- level: (label) => {
191
- return { level: label };
192
- },
193
- },
194
- timestamp: true,
195
- }),
196
- };
197
- _loggerInstance = pino(pinoConfig);
198
- return _loggerInstance;
199
- }
200
- catch (error) {
201
- // Fallback to browser-safe logger if pino fails
202
- return getBrowserSafeLogger(config, error);
203
- }
151
+ // Client SDK always uses browser-safe logger (no pino)
152
+ return getBrowserSafeLogger(config);
204
153
  }
205
154
  /**
206
- * Browser-safe logger fallback for environments where pino fails.
155
+ * Browser-safe logger for Client SDK.
207
156
  */
208
- function getBrowserSafeLogger(config, pinoError) {
157
+ function getBrowserSafeLogger(config) {
209
158
  // Log level hierarchy (matching pino's numeric levels)
210
159
  const LOG_LEVELS = {
211
160
  silent: 0,
@@ -236,7 +185,7 @@ function getBrowserSafeLogger(config, pinoError) {
236
185
  };
237
186
  const logLevel = getEffectiveLogLevel();
238
187
  const structuredLogging = config?.structuredLogging ?? false;
239
- const isProd = isProduction();
188
+ isProduction();
240
189
  /**
241
190
  * Check if we should log at this level.
242
191
  */
@@ -369,10 +318,6 @@ function getBrowserSafeLogger(config, pinoError) {
369
318
  }
370
319
  },
371
320
  };
372
- // Only warn about fallback logger if pino failed (not if we're in browser)
373
- if (pinoError && !isProd) {
374
- console.warn('[Finatic SDK] Using fallback logger due to pino initialization error:', pinoError);
375
- }
376
321
  _loggerInstance = fallbackLogger;
377
322
  return _loggerInstance;
378
323
  }
@@ -440,11 +385,96 @@ function handleError(error, requestId) {
440
385
  }
441
386
 
442
387
  /**
443
- * Response caching utility with node-cache (Phase 2B).
388
+ * Response caching utility with browser-compatible Map-based cache (Phase 2B).
444
389
  *
445
390
  * Generated - do not edit directly.
446
391
  */
447
- // @ts-ignore - node-cache types available via @types/node-cache
392
+ class MapBasedCache {
393
+ constructor(maxSize = 1000, defaultTtl = 300) {
394
+ this.cleanupInterval = null;
395
+ this.cache = new Map();
396
+ this.maxSize = maxSize;
397
+ this.defaultTtl = defaultTtl;
398
+ this.startCleanup();
399
+ }
400
+ startCleanup() {
401
+ // Clean up expired entries every minute
402
+ if (typeof window !== 'undefined') {
403
+ this.cleanupInterval = window.setInterval(() => {
404
+ this.cleanup();
405
+ }, 60000);
406
+ }
407
+ }
408
+ cleanup() {
409
+ const now = Date.now();
410
+ const keysToDelete = [];
411
+ for (const [key, entry] of this.cache.entries()) {
412
+ if (entry.expires < now) {
413
+ keysToDelete.push(key);
414
+ }
415
+ }
416
+ keysToDelete.forEach(key => this.cache.delete(key));
417
+ }
418
+ evictLRU() {
419
+ if (this.cache.size < this.maxSize) {
420
+ return;
421
+ }
422
+ // Simple LRU: remove oldest entry (first in Map)
423
+ const firstKey = this.cache.keys().next().value;
424
+ if (firstKey) {
425
+ this.cache.delete(firstKey);
426
+ }
427
+ }
428
+ get(key) {
429
+ const entry = this.cache.get(key);
430
+ if (!entry) {
431
+ return undefined;
432
+ }
433
+ // Check if expired
434
+ if (entry.expires < Date.now()) {
435
+ this.cache.delete(key);
436
+ return undefined;
437
+ }
438
+ return entry.value;
439
+ }
440
+ set(key, value, ttl) {
441
+ // Evict if at max size
442
+ if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
443
+ this.evictLRU();
444
+ }
445
+ const expires = Date.now() + (ttl || this.defaultTtl) * 1000;
446
+ this.cache.set(key, { value, expires });
447
+ return true;
448
+ }
449
+ del(key) {
450
+ return this.cache.delete(key) ? 1 : 0;
451
+ }
452
+ clear() {
453
+ this.cache.clear();
454
+ }
455
+ keys() {
456
+ return Array.from(this.cache.keys());
457
+ }
458
+ has(key) {
459
+ const entry = this.cache.get(key);
460
+ if (!entry) {
461
+ return false;
462
+ }
463
+ // Check if expired
464
+ if (entry.expires < Date.now()) {
465
+ this.cache.delete(key);
466
+ return false;
467
+ }
468
+ return true;
469
+ }
470
+ destroy() {
471
+ if (this.cleanupInterval !== null && typeof window !== 'undefined') {
472
+ window.clearInterval(this.cleanupInterval);
473
+ this.cleanupInterval = null;
474
+ }
475
+ this.cache.clear();
476
+ }
477
+ }
448
478
  let _cacheInstance = null;
449
479
  /**
450
480
  * Get or create cache instance.
@@ -456,11 +486,7 @@ function getCache(config) {
456
486
  if (_cacheInstance) {
457
487
  return _cacheInstance;
458
488
  }
459
- _cacheInstance = new NodeCache({
460
- stdTTL: config.cacheTtl || 300,
461
- maxKeys: config.cacheMaxSize || 1000,
462
- useClones: false,
463
- });
489
+ _cacheInstance = new MapBasedCache(config.cacheMaxSize || 1000, config.cacheTtl || 300);
464
490
  return _cacheInstance;
465
491
  }
466
492
  /**