@wealthfolio/addon-sdk 1.0.0 → 3.0.0

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
@@ -6,7 +6,9 @@
6
6
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue?style=flat-square)](https://www.typescriptlang.org/)
7
7
  [![Node](https://img.shields.io/node/v/@wealthfolio/addon-sdk?style=flat-square)](https://nodejs.org/)
8
8
 
9
- A comprehensive TypeScript SDK for building secure, feature-rich addons for Wealthfolio. Extend your portfolio management experience with custom analytics, integrations, and visualizations.
9
+ A comprehensive TypeScript SDK for building secure, feature-rich addons for
10
+ Wealthfolio. Extend your portfolio management experience with custom analytics,
11
+ integrations, and visualizations.
10
12
 
11
13
  ## 📚 Table of Contents
12
14
 
@@ -30,8 +32,9 @@ A comprehensive TypeScript SDK for building secure, feature-rich addons for Weal
30
32
 
31
33
  ## 🚀 Features
32
34
 
33
- - **Type-Safe Development**: Full TypeScript support with comprehensive type definitions
34
- - **Security-First**: Built-in permission system with granular risk assessment
35
+ - **Type-Safe Development**: Full TypeScript support with comprehensive type
36
+ definitions
37
+ - **Security-First**: Built-in permission system with granular risk assessment
35
38
  - **Modular Architecture**: Clean separation of concerns with well-defined APIs
36
39
  - **React Integration**: Seamless integration with React components and hooks
37
40
  - **Hot Reloading**: Development-friendly with automatic reload capabilities
@@ -75,13 +78,13 @@ export default function enable(context: AddonContext) {
75
78
  id: 'my-addon',
76
79
  label: 'My Addon',
77
80
  icon: 'chart-line',
78
- route: '/addons/my-addon'
81
+ route: '/addons/my-addon',
79
82
  });
80
83
 
81
84
  // Register route
82
85
  context.router.add({
83
86
  path: '/addons/my-addon',
84
- component: () => import('./MyComponent')
87
+ component: () => import('./MyComponent'),
85
88
  });
86
89
 
87
90
  // Log activation
@@ -202,32 +205,33 @@ Create a `manifest.json` file in your addon root:
202
205
 
203
206
  ### Required Fields
204
207
 
205
- | Field | Type | Description |
206
- |-------|------|-------------|
207
- | `id` | `string` | Unique identifier (lowercase, hyphens allowed) |
208
- | `name` | `string` | Human-readable addon name |
209
- | `version` | `string` | Semantic version (e.g., "1.0.0") |
208
+ | Field | Type | Description |
209
+ | --------- | -------- | ---------------------------------------------- |
210
+ | `id` | `string` | Unique identifier (lowercase, hyphens allowed) |
211
+ | `name` | `string` | Human-readable addon name |
212
+ | `version` | `string` | Semantic version (e.g., "1.0.0") |
210
213
 
211
214
  ### Optional Fields
212
215
 
213
- | Field | Type | Description |
214
- |-------|------|-------------|
215
- | `description` | `string` | Brief description of functionality |
216
- | `author` | `string` | Author name or organization |
217
- | `homepage` | `string` | Project homepage URL |
218
- | `license` | `string` | License identifier |
219
- | `main` | `string` | Entry point file (default: "addon.js") |
220
- | `sdkVersion` | `string` | Compatible SDK version |
221
- | `permissions` | `Permission[]` | Security permissions required |
222
- | `minWealthfolioVersion` | `string` | Minimum Wealthfolio version required |
223
- | `keywords` | `string[]` | Keywords for discoverability |
224
- | `icon` | `string` | Addon icon (base64 or relative path) |
216
+ | Field | Type | Description |
217
+ | ----------------------- | -------------- | -------------------------------------- |
218
+ | `description` | `string` | Brief description of functionality |
219
+ | `author` | `string` | Author name or organization |
220
+ | `homepage` | `string` | Project homepage URL |
221
+ | `license` | `string` | License identifier |
222
+ | `main` | `string` | Entry point file (default: "addon.js") |
223
+ | `sdkVersion` | `string` | Compatible SDK version |
224
+ | `permissions` | `Permission[]` | Security permissions required |
225
+ | `minWealthfolioVersion` | `string` | Minimum Wealthfolio version required |
226
+ | `keywords` | `string[]` | Keywords for discoverability |
227
+ | `icon` | `string` | Addon icon (base64 or relative path) |
225
228
 
226
229
  ## 🔨 Development Guide
227
230
 
228
231
  ### Modern Addon Example
229
232
 
230
- Based on the current SDK architecture, here's a complete real-world addon example:
233
+ Based on the current SDK architecture, here's a complete real-world addon
234
+ example:
231
235
 
232
236
  ```typescript
233
237
  // src/addon.tsx
@@ -263,7 +267,7 @@ const enable: AddonEnableFunction = (context) => {
263
267
  order: 200
264
268
  });
265
269
  addedItems.push(sidebarItem);
266
-
270
+
267
271
  context.api.logger.debug('Sidebar navigation item added successfully');
268
272
 
269
273
  // Create wrapper component with shared QueryClient
@@ -279,11 +283,11 @@ const enable: AddonEnableFunction = (context) => {
279
283
  // Register route with lazy loading
280
284
  context.router.add({
281
285
  path: '/addons/investment-fees-tracker',
282
- component: React.lazy(() => Promise.resolve({
283
- default: InvestmentFeesTrackerWrapper
286
+ component: React.lazy(() => Promise.resolve({
287
+ default: InvestmentFeesTrackerWrapper
284
288
  }))
285
289
  });
286
-
290
+
287
291
  context.api.logger.debug('Route registered successfully');
288
292
  context.api.logger.info('Investment Fees Tracker addon enabled successfully');
289
293
 
@@ -295,7 +299,7 @@ const enable: AddonEnableFunction = (context) => {
295
299
  // Register cleanup callback
296
300
  context.onDisable(() => {
297
301
  context.api.logger.info('🛑 Investment Fees Tracker addon is being disabled');
298
-
302
+
299
303
  // Remove all sidebar items
300
304
  addedItems.forEach(item => {
301
305
  try {
@@ -304,7 +308,7 @@ const enable: AddonEnableFunction = (context) => {
304
308
  context.api.logger.error('Error removing sidebar item: ' + (error as Error).message);
305
309
  }
306
310
  });
307
-
311
+
308
312
  context.api.logger.info('Investment Fees Tracker addon disabled successfully');
309
313
  });
310
314
  };
@@ -315,13 +319,15 @@ export default enable;
315
319
 
316
320
  ### Key Features Demonstrated
317
321
 
318
- 1. **Shared Query Client**: Uses `context.api.query.getClient()` for consistent data fetching
322
+ 1. **Shared Query Client**: Uses `context.api.query.getClient()` for consistent
323
+ data fetching
319
324
  2. **UI Icons**: Leverages `@wealthfolio/ui` for consistent iconography
320
325
  3. **Error Handling**: Comprehensive error handling with logging
321
326
  4. **Resource Management**: Proper cleanup of sidebar items and event listeners
322
327
  5. **TypeScript**: Full type safety with proper imports
323
328
  6. **Lazy Loading**: Efficient component loading with React.lazy
324
- ```
329
+
330
+ ````
325
331
 
326
332
  ### Advanced Component Example
327
333
 
@@ -358,7 +364,7 @@ export function FeesPage({ ctx }: FeesPageProps) {
358
364
 
359
365
  const { data: activities, isLoading: activitiesLoading } = useQuery({
360
366
  queryKey: ['activities'],
361
- queryFn: () => ctx.api.activities.getAll({ page: 1, pageSize: 1000 })
367
+ queryFn: () => ctx.api.activities.getAll()
362
368
  });
363
369
 
364
370
  const isLoading = accountsLoading || holdingsLoading || activitiesLoading;
@@ -366,7 +372,7 @@ export function FeesPage({ ctx }: FeesPageProps) {
366
372
  // Calculate total fees from activities
367
373
  const totalFees = React.useMemo(() => {
368
374
  if (!activities?.data) return 0;
369
-
375
+
370
376
  return activities.data.reduce((total, activity) => {
371
377
  // Look for fee-related activities or transaction costs
372
378
  const fee = activity.fee || 0;
@@ -376,11 +382,11 @@ export function FeesPage({ ctx }: FeesPageProps) {
376
382
 
377
383
  useEffect(() => {
378
384
  if (!isLoading) {
379
- ctx.api.logger.info('Fees data loaded successfully', {
385
+ ctx.api.logger.info('Fees data loaded successfully', {
380
386
  accountsCount: accounts?.length,
381
387
  holdingsCount: holdings?.length,
382
388
  activitiesCount: activities?.data?.length,
383
- totalFees
389
+ totalFees
384
390
  });
385
391
  }
386
392
  }, [isLoading, accounts, holdings, activities, totalFees, ctx.api.logger]);
@@ -402,7 +408,7 @@ export function FeesPage({ ctx }: FeesPageProps) {
402
408
  <h1 className="text-3xl font-bold text-gray-900 mb-2">Investment Fees Tracker</h1>
403
409
  <p className="text-gray-600">Track and analyze fees across your investment portfolio</p>
404
410
  </div>
405
-
411
+
406
412
  <div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
407
413
  <div className="bg-white p-6 rounded-lg shadow border">
408
414
  <h3 className="text-lg font-semibold text-gray-900 mb-2">Total Fees Paid</h3>
@@ -478,7 +484,7 @@ export default FeesPage;
478
484
  }
479
485
 
480
486
  export default AnalyticsDashboard;
481
- ```
487
+ ````
482
488
 
483
489
  ### Using Hooks and State Management
484
490
 
@@ -490,7 +496,9 @@ import type { Holding, PerformanceMetrics } from '@wealthfolio/addon-sdk/types';
490
496
 
491
497
  export function usePortfolioData(accountId?: string) {
492
498
  const [holdings, setHoldings] = useState<Holding[]>([]);
493
- const [performance, setPerformance] = useState<PerformanceMetrics | null>(null);
499
+ const [performance, setPerformance] = useState<PerformanceMetrics | null>(
500
+ null,
501
+ );
494
502
  const [loading, setLoading] = useState(true);
495
503
  const [error, setError] = useState<string | null>(null);
496
504
 
@@ -501,15 +509,18 @@ export function usePortfolioData(accountId?: string) {
501
509
  setError(null);
502
510
 
503
511
  const ctx = getAddonContext();
504
-
505
- const holdingsData = await ctx.api.portfolio.getHoldings(accountId || '');
512
+
513
+ const holdingsData = await ctx.api.portfolio.getHoldings(
514
+ accountId || '',
515
+ );
506
516
  setHoldings(holdingsData);
507
517
 
508
518
  if (accountId) {
509
- const performanceData = await ctx.api.portfolio.calculatePerformanceSummary({
510
- itemType: 'account',
511
- itemId: accountId
512
- });
519
+ const performanceData =
520
+ await ctx.api.portfolio.calculatePerformanceSummary({
521
+ itemType: 'account',
522
+ itemId: accountId,
523
+ });
513
524
  setPerformance(performanceData);
514
525
  }
515
526
  } catch (err) {
@@ -530,18 +541,18 @@ export function usePortfolioData(accountId?: string) {
530
541
 
531
542
  ### Permission Categories
532
543
 
533
- | Category | Risk Level | Description |
534
- |----------|------------|-------------|
535
- | `ui` | Low | Add navigation items and routes |
536
- | `market-data` | Low | Access market prices and quotes |
537
- | `events` | Low | Listen to application events |
538
- | `currency` | Low | Access exchange rates |
539
- | `portfolio` | Medium | Access holdings and valuations |
540
- | `files` | Medium | File dialog operations |
541
- | `financial-planning` | Medium | Goals and contribution limits |
542
- | `activities` | High | Transaction history access |
543
- | `accounts` | High | Account management |
544
- | `settings` | High | Application configuration |
544
+ | Category | Risk Level | Description |
545
+ | -------------------- | ---------- | ------------------------------- |
546
+ | `ui` | Low | Add navigation items and routes |
547
+ | `market-data` | Low | Access market prices and quotes |
548
+ | `events` | Low | Listen to application events |
549
+ | `currency` | Low | Access exchange rates |
550
+ | `portfolio` | Medium | Access holdings and valuations |
551
+ | `files` | Medium | File dialog operations |
552
+ | `financial-planning` | Medium | Goals and contribution limits |
553
+ | `activities` | High | Transaction history access |
554
+ | `accounts` | High | Account management |
555
+ | `settings` | High | Application configuration |
545
556
 
546
557
  ### Declaring Permissions
547
558
 
@@ -585,26 +596,26 @@ export default defineConfig({
585
596
  entry: resolve(__dirname, 'src/index.ts'),
586
597
  name: 'MyPortfolioAddon',
587
598
  fileName: 'addon',
588
- formats: ['es']
599
+ formats: ['es'],
589
600
  },
590
601
  rollupOptions: {
591
602
  external: ['react', 'react-dom'],
592
603
  output: {
593
604
  globals: {
594
605
  react: 'React',
595
- 'react-dom': 'ReactDOM'
596
- }
597
- }
606
+ 'react-dom': 'ReactDOM',
607
+ },
608
+ },
598
609
  },
599
610
  outDir: 'dist',
600
611
  minify: 'terser',
601
- sourcemap: true
612
+ sourcemap: true,
602
613
  },
603
614
  resolve: {
604
615
  alias: {
605
- '@': resolve(__dirname, 'src')
606
- }
607
- }
616
+ '@': resolve(__dirname, 'src'),
617
+ },
618
+ },
608
619
  });
609
620
  ```
610
621
 
@@ -666,6 +677,7 @@ zip -r my-portfolio-addon.zip \
666
677
  ### Package Structure
667
678
 
668
679
  Your final package should contain:
680
+
669
681
  - `manifest.json` - Addon metadata
670
682
  - `dist/addon.js` - Compiled addon code
671
683
  - `assets/` - Static assets (optional)
@@ -702,6 +714,7 @@ npm run dev
702
714
  Add an item to the application sidebar.
703
715
 
704
716
  **Parameters:**
717
+
705
718
  - `config.id` (string): Unique identifier
706
719
  - `config.label` (string): Display text
707
720
  - `config.icon` (string | ReactNode): Icon name or component
@@ -716,6 +729,7 @@ Add an item to the application sidebar.
716
729
  Register a new route in the application.
717
730
 
718
731
  **Parameters:**
732
+
719
733
  - `route.path` (string): Route path pattern
720
734
  - `route.component` (LazyExoticComponent): Lazy-loaded component
721
735
 
@@ -724,6 +738,7 @@ Register a new route in the application.
724
738
  Register cleanup callback for addon disable.
725
739
 
726
740
  **Parameters:**
741
+
727
742
  - `callback` (function): Cleanup function
728
743
 
729
744
  ### Data Access APIs
@@ -737,7 +752,7 @@ const ctx = getAddonContext();
737
752
  const holdings = await ctx.api.portfolio.getHoldings(accountId);
738
753
  const accounts = await ctx.api.accounts.getAll();
739
754
 
740
- // Market data
755
+ // Market data
741
756
  const quotes = await ctx.api.marketData.getQuoteHistory(symbol);
742
757
  const profile = await ctx.api.marketData.getAssetProfile(assetId);
743
758
 
@@ -756,22 +771,52 @@ ctx.api.logger.debug('Debug info:', debugData);
756
771
 
757
772
  ### Available API Methods
758
773
 
759
- | Method | Description | Permission Required |
760
- |--------|-------------|-------------------|
761
- | `portfolio.getHoldings(accountId)` | Get portfolio holdings for account | `portfolio` |
762
- | `portfolio.getHolding(accountId, assetId)` | Get specific holding | `portfolio` |
763
- | `portfolio.calculatePerformanceSummary(params)` | Calculate performance metrics | `portfolio` |
764
- | `portfolio.getIncomeSummary()` | Get income summary data | `portfolio` |
765
- | `accounts.getAll()` | Get all account information | `accounts` |
766
- | `accounts.create(account)` | Create new account | `accounts` |
767
- | `activities.getAll(params)` | Get activity history | `activities` |
768
- | `activities.create(activity)` | Create new activity | `activities` |
769
- | `marketData.getQuoteHistory(symbol)` | Get historical quotes | `market-data` |
770
- | `marketData.getAssetProfile(assetId)` | Get asset profile | `market-data` |
771
- | `marketData.searchTicker(query)` | Search for tickers | `market-data` |
772
- | `goals.getAll()` | Get financial goals | `financial-planning` |
773
- | `settings.get()` | Get app settings | `settings` |
774
- | `query.getClient()` | Get shared QueryClient instance | None |
774
+ | Method | Description | Permission Required |
775
+ | ----------------------------------------------- | --------------------------------------------------------- | -------------------- |
776
+ | `portfolio.getHoldings(accountId)` | Get portfolio holdings for account | `portfolio` |
777
+ | `portfolio.getHolding(accountId, assetId)` | Get specific holding | `portfolio` |
778
+ | `portfolio.calculatePerformanceSummary(params)` | Calculate performance metrics | `portfolio` |
779
+ | `portfolio.getIncomeSummary()` | Get income summary data | `portfolio` |
780
+ | `accounts.getAll()` | Get all account information | `accounts` |
781
+ | `accounts.create(account)` | Create new account | `accounts` |
782
+ | `activities.getAll(accountId?)` | Get activity history (optionally filtered to one account) | `activities` |
783
+ | `activities.create(activity)` | Create new activity | `activities` |
784
+ | `marketData.getQuoteHistory(symbol)` | Get historical quotes | `market-data` |
785
+ | `marketData.getAssetProfile(assetId)` | Get asset profile | `market-data` |
786
+ | `marketData.searchTicker(query)` | Search for tickers | `market-data` |
787
+ | `goals.getAll()` | Get financial goals | `financial-planning` |
788
+ | `settings.get()` | Get app settings | `settings` |
789
+ | `query.getClient()` | Get shared QueryClient instance | None |
790
+
791
+ > Tip: `activities.getAll` accepts an optional account ID string to scope
792
+ > results to a single account. The SDK normalizes this for both desktop (Tauri)
793
+ > and web runtimes—no need to wrap it in an array.
794
+
795
+ ### Activity search filters
796
+
797
+ `activities.search` accepts either a single value or an array for `accountIds`
798
+ and `activityTypes`. The host normalizes these inputs for both desktop and web
799
+ runtime paths and will also accept an explicit `symbol` filter when you want to
800
+ target a single ticker without a free-form search query. Sorting takes a single
801
+ sort object and defaults to `{ id: "date", desc: true }` when none is provided.
802
+ The first two parameters are pagination controls: `page` is a zero-based index
803
+ (use `0` for the first page) and `pageSize` is the number of rows to return. For
804
+ exports you can pass a large `pageSize` (for example, 1000) alongside `page` = 0
805
+ to fetch a wide slice in one call.
806
+
807
+ ```typescript
808
+ const response = await ctx.api.activities.search(
809
+ 0,
810
+ 50,
811
+ {
812
+ accountIds: 'account-1', // single string or string[] both work
813
+ activityTypes: ['BUY', 'DIVIDEND'],
814
+ symbol: 'AAPL',
815
+ },
816
+ '', // optional keyword search (ignored when empty)
817
+ { id: 'date', desc: true },
818
+ );
819
+ ```
775
820
 
776
821
  ### Logger API
777
822
 
@@ -797,7 +842,8 @@ if (ctx.api.logger.isLevelEnabled('debug')) {
797
842
 
798
843
  ### Shared QueryClient Integration
799
844
 
800
- The SDK provides access to Wealthfolio's shared React Query client for consistent data fetching and caching:
845
+ The SDK provides access to Wealthfolio's shared React Query client for
846
+ consistent data fetching and caching:
801
847
 
802
848
  ```typescript
803
849
  // Access the shared QueryClient instance
@@ -830,6 +876,7 @@ function MyAddonComponent() {
830
876
  ```
831
877
 
832
878
  **Benefits of Shared QueryClient:**
879
+
833
880
  - **Consistent Caching**: Share cache with the main application
834
881
  - **Performance**: Avoid duplicate API calls across addons
835
882
  - **Synchronization**: Real-time updates when data changes
@@ -840,6 +887,7 @@ function MyAddonComponent() {
840
887
  ### From v1.0.0 to v1.1.0
841
888
 
842
889
  #### Context Access
890
+
843
891
  ```typescript
844
892
  // Before
845
893
  import ctx from '@wealthfolio/addon-sdk';
@@ -850,6 +898,7 @@ const ctx = getAddonContext();
850
898
  ```
851
899
 
852
900
  #### Type Imports
901
+
853
902
  ```typescript
854
903
  // Before
855
904
  import type { AddonContext, AddonManifest } from '@wealthfolio/addon-sdk';
@@ -887,6 +936,7 @@ npm install --save-dev @types/react-dom
887
936
  Create the essential configuration files:
888
937
 
889
938
  **tsconfig.json**
939
+
890
940
  ```json
891
941
  {
892
942
  "compilerOptions": {
@@ -916,6 +966,7 @@ Create the essential configuration files:
916
966
  ```
917
967
 
918
968
  **vite.config.ts**
969
+
919
970
  ```typescript
920
971
  import { defineConfig } from 'vite';
921
972
  import react from '@vitejs/plugin-react';
@@ -928,30 +979,31 @@ export default defineConfig({
928
979
  entry: resolve(__dirname, 'src/index.ts'),
929
980
  name: 'MyPortfolioAddon',
930
981
  fileName: 'addon',
931
- formats: ['es']
982
+ formats: ['es'],
932
983
  },
933
984
  rollupOptions: {
934
985
  external: ['react', 'react-dom'],
935
986
  output: {
936
987
  globals: {
937
988
  react: 'React',
938
- 'react-dom': 'ReactDOM'
939
- }
940
- }
989
+ 'react-dom': 'ReactDOM',
990
+ },
991
+ },
941
992
  },
942
993
  outDir: 'dist',
943
994
  minify: 'terser',
944
- sourcemap: true
995
+ sourcemap: true,
945
996
  },
946
997
  resolve: {
947
998
  alias: {
948
- '@': resolve(__dirname, 'src')
949
- }
950
- }
999
+ '@': resolve(__dirname, 'src'),
1000
+ },
1001
+ },
951
1002
  });
952
1003
  ```
953
1004
 
954
1005
  **package.json scripts**
1006
+
955
1007
  ```json
956
1008
  {
957
1009
  "scripts": {
@@ -1013,10 +1065,10 @@ export default defineConfig({
1013
1065
  permissions: 'src/permissions.ts',
1014
1066
  },
1015
1067
  format: ['esm'],
1016
- dts: true, // Generate TypeScript declarations
1017
- clean: true, // Clean dist folder before build
1018
- sourcemap: true, // Generate source maps
1019
- minify: false, // Keep code readable for debugging
1068
+ dts: true, // Generate TypeScript declarations
1069
+ clean: true, // Clean dist folder before build
1070
+ sourcemap: true, // Generate source maps
1071
+ minify: false, // Keep code readable for debugging
1020
1072
  target: 'es2020',
1021
1073
  external: ['react'], // Don't bundle React
1022
1074
  });
@@ -1071,6 +1123,7 @@ ctx.api.logger.debug('Debug information:', data);
1071
1123
  #### 2. Development Console
1072
1124
 
1073
1125
  Access the browser's developer console for debugging:
1126
+
1074
1127
  - Open Wealthfolio
1075
1128
  - Press F12 or right-click → Inspect
1076
1129
  - Check Console tab for addon logs
@@ -1099,24 +1152,24 @@ import { getAddonContext } from '@wealthfolio/addon-sdk';
1099
1152
 
1100
1153
  async function fetchPortfolioData() {
1101
1154
  const ctx = getAddonContext();
1102
-
1155
+
1103
1156
  try {
1104
1157
  // Get all accounts first, then holdings for each
1105
1158
  const accounts = await ctx.api.accounts.getAll();
1106
1159
  const holdings = await Promise.all(
1107
- accounts.map(account => ctx.api.portfolio.getHoldings(account.id))
1108
- ).then(results => results.flat());
1160
+ accounts.map((account) => ctx.api.portfolio.getHoldings(account.id)),
1161
+ ).then((results) => results.flat());
1109
1162
  return holdings;
1110
1163
  } catch (error) {
1111
1164
  ctx.api.logger.error('Failed to fetch holdings:', error);
1112
-
1165
+
1113
1166
  // Handle different error types
1114
1167
  if (error.code === 'PERMISSION_DENIED') {
1115
1168
  // Show permission error to user
1116
1169
  } else if (error.code === 'NETWORK_ERROR') {
1117
1170
  // Handle network issues
1118
1171
  }
1119
-
1172
+
1120
1173
  throw error;
1121
1174
  }
1122
1175
  }
@@ -1127,14 +1180,14 @@ async function fetchPortfolioData() {
1127
1180
  ```typescript
1128
1181
  export default function enable(context: AddonContext) {
1129
1182
  const subscriptions: (() => void)[] = [];
1130
-
1183
+
1131
1184
  // Add event listeners
1132
1185
  const unsubscribe = context.events.subscribe('portfolio.updated', handler);
1133
1186
  subscriptions.push(unsubscribe);
1134
-
1187
+
1135
1188
  // Cleanup on disable
1136
1189
  context.onDisable(() => {
1137
- subscriptions.forEach(unsub => unsub());
1190
+ subscriptions.forEach((unsub) => unsub());
1138
1191
  context.api.logger.info('Addon cleaned up successfully');
1139
1192
  });
1140
1193
  }
@@ -1152,7 +1205,7 @@ const AddonStateContext = createContext<AddonState | null>(null);
1152
1205
 
1153
1206
  export function AddonProvider({ children }: { children: ReactNode }) {
1154
1207
  const [state, setState] = useState<AddonState>(initialState);
1155
-
1208
+
1156
1209
  return (
1157
1210
  <AddonStateContext.Provider value={{ state, setState }}>
1158
1211
  {children}
@@ -1188,7 +1241,7 @@ function usePortfolioData(accountId: string) {
1188
1241
  {
1189
1242
  staleTime: 5 * 60 * 1000, // 5 minutes
1190
1243
  cacheTime: 10 * 60 * 1000, // 10 minutes
1191
- }
1244
+ },
1192
1245
  );
1193
1246
  }
1194
1247
  ```
@@ -1204,10 +1257,10 @@ export default defineConfig({
1204
1257
  manualChunks: {
1205
1258
  vendor: ['react', 'react-dom'],
1206
1259
  charts: ['chart.js', 'd3'],
1207
- }
1208
- }
1209
- }
1210
- }
1260
+ },
1261
+ },
1262
+ },
1263
+ },
1211
1264
  });
1212
1265
  ```
1213
1266
 
@@ -1218,33 +1271,37 @@ We welcome contributions to improve the addon SDK!
1218
1271
  ### Development Setup
1219
1272
 
1220
1273
  1. **Fork and Clone**
1274
+
1221
1275
  ```bash
1222
1276
  git clone https://github.com/yourusername/wealthfolio.git
1223
1277
  cd wealthfolio/packages/addon-sdk
1224
1278
  ```
1225
1279
 
1226
1280
  2. **Install Dependencies**
1281
+
1227
1282
  ```bash
1228
1283
  pnpm install
1229
1284
  ```
1230
1285
 
1231
1286
  3. **Make Changes**
1287
+
1232
1288
  ```bash
1233
1289
  # Start development mode
1234
1290
  pnpm dev
1235
-
1291
+
1236
1292
  # Run type checking
1237
1293
  pnpm lint
1238
-
1294
+
1239
1295
  # Build for testing
1240
1296
  pnpm build
1241
1297
  ```
1242
1298
 
1243
1299
  4. **Testing Your Changes**
1300
+
1244
1301
  ```bash
1245
1302
  # Link the SDK locally for testing
1246
1303
  npm link
1247
-
1304
+
1248
1305
  # In your test addon project
1249
1306
  npm link @wealthfolio/addon-sdk
1250
1307
  ```
@@ -1267,13 +1324,13 @@ We welcome contributions to improve the addon SDK!
1267
1324
 
1268
1325
  ### Package Details
1269
1326
 
1270
- | Field | Value |
1271
- |-------|--------|
1272
- | **Package Name** | `@wealthfolio/addon-sdk` |
1273
- | **Scope** | `@wealthfolio` |
1274
- | **Registry** | [npmjs.com](https://www.npmjs.com/package/@wealthfolio/addon-sdk) |
1275
- | **License** | MIT |
1276
- | **Repository** | [GitHub](https://github.com/afadil/wealthfolio) |
1327
+ | Field | Value |
1328
+ | ---------------- | ----------------------------------------------------------------- |
1329
+ | **Package Name** | `@wealthfolio/addon-sdk` |
1330
+ | **Scope** | `@wealthfolio` |
1331
+ | **Registry** | [npmjs.com](https://www.npmjs.com/package/@wealthfolio/addon-sdk) |
1332
+ | **License** | MIT |
1333
+ | **Repository** | [GitHub](https://github.com/afadil/wealthfolio) |
1277
1334
 
1278
1335
  ### Version History
1279
1336
 
@@ -1285,14 +1342,15 @@ We follow [Semantic Versioning](https://semver.org/) (SemVer):
1285
1342
 
1286
1343
  #### Version Compatibility
1287
1344
 
1288
- | SDK Version | Wealthfolio Version | Node.js | React |
1289
- |-------------|---------------------|---------|-------|
1290
- | 1.0.x | >= 1.0.0 | >= 18.0.0 | ^18.0.0 |
1291
- | 0.9.x | >= 0.9.0 | >= 16.0.0 | ^17.0.0 |
1345
+ | SDK Version | Wealthfolio Version | Node.js | React |
1346
+ | ----------- | ------------------- | --------- | ------- |
1347
+ | 1.0.x | >= 1.0.0 | >= 18.0.0 | ^18.0.0 |
1348
+ | 0.9.x | >= 0.9.0 | >= 16.0.0 | ^17.0.0 |
1292
1349
 
1293
1350
  ### Installation from Registry
1294
1351
 
1295
1352
  #### Stable Release
1353
+
1296
1354
  ```bash
1297
1355
  # Latest stable version
1298
1356
  npm install @wealthfolio/addon-sdk
@@ -1305,6 +1363,7 @@ npm install @wealthfolio/addon-sdk@^1.0.0
1305
1363
  ```
1306
1364
 
1307
1365
  #### Beta/Preview Releases
1366
+
1308
1367
  ```bash
1309
1368
  # Latest beta version
1310
1369
  npm install @wealthfolio/addon-sdk@beta
@@ -1314,6 +1373,7 @@ npm install @wealthfolio/addon-sdk@1.1.0-beta.1
1314
1373
  ```
1315
1374
 
1316
1375
  #### Development Version
1376
+
1317
1377
  ```bash
1318
1378
  # Install directly from GitHub
1319
1379
  npm install github:afadil/wealthfolio#main
@@ -1344,6 +1404,7 @@ npm outdated @wealthfolio/addon-sdk
1344
1404
  ### Publishing Information (For Maintainers)
1345
1405
 
1346
1406
  #### Prerequisites
1407
+
1347
1408
  ```bash
1348
1409
  # Login to npm (maintainers only)
1349
1410
  npm login
@@ -1356,6 +1417,7 @@ npm access list packages @wealthfolio
1356
1417
  ```
1357
1418
 
1358
1419
  #### Release Process
1420
+
1359
1421
  ```bash
1360
1422
  # 1. Update version
1361
1423
  npm version patch # or minor/major
@@ -1380,23 +1442,28 @@ git push --tags
1380
1442
 
1381
1443
  #### Distribution Tags
1382
1444
 
1383
- | Tag | Purpose | Command |
1384
- |-----|---------|---------|
1385
- | `latest` | Stable releases | `npm publish` |
1386
- | `beta` | Beta releases | `npm publish --tag beta` |
1387
- | `alpha` | Alpha releases | `npm publish --tag alpha` |
1388
- | `next` | Next major version | `npm publish --tag next` |
1445
+ | Tag | Purpose | Command |
1446
+ | -------- | ------------------ | ------------------------- |
1447
+ | `latest` | Stable releases | `npm publish` |
1448
+ | `beta` | Beta releases | `npm publish --tag beta` |
1449
+ | `alpha` | Alpha releases | `npm publish --tag alpha` |
1450
+ | `next` | Next major version | `npm publish --tag next` |
1389
1451
 
1390
1452
  #### Package Metrics
1391
1453
 
1392
1454
  View package statistics:
1393
- - **Downloads**: [npm-stat.com](https://npm-stat.com/charts.html?package=@wealthfolio/addon-sdk)
1394
- - **Bundle Size**: [bundlephobia.com](https://bundlephobia.com/package/@wealthfolio/addon-sdk)
1395
- - **Dependencies**: [npm.anvaka.com](https://npm.anvaka.com/#/view/2d/@wealthfolio/addon-sdk)
1455
+
1456
+ - **Downloads**:
1457
+ [npm-stat.com](https://npm-stat.com/charts.html?package=@wealthfolio/addon-sdk)
1458
+ - **Bundle Size**:
1459
+ [bundlephobia.com](https://bundlephobia.com/package/@wealthfolio/addon-sdk)
1460
+ - **Dependencies**:
1461
+ [npm.anvaka.com](https://npm.anvaka.com/#/view/2d/@wealthfolio/addon-sdk)
1396
1462
 
1397
1463
  ### Security
1398
1464
 
1399
1465
  #### Vulnerability Scanning
1466
+
1400
1467
  ```bash
1401
1468
  # Check for vulnerabilities
1402
1469
  npm audit
@@ -1409,6 +1476,7 @@ npm audit --audit-level=moderate
1409
1476
  ```
1410
1477
 
1411
1478
  #### Package Integrity
1479
+
1412
1480
  ```bash
1413
1481
  # Verify package integrity
1414
1482
  npm pack --dry-run
@@ -1422,7 +1490,8 @@ npm pack && tar -tf *.tgz
1422
1490
  #### Package Support Policy
1423
1491
 
1424
1492
  - **Latest Major Version**: Full support with new features and bug fixes
1425
- - **Previous Major Version**: Security fixes and critical bug fixes for 12 months
1493
+ - **Previous Major Version**: Security fixes and critical bug fixes for 12
1494
+ months
1426
1495
  - **Older Versions**: Community support only
1427
1496
 
1428
1497
  #### Maintenance Schedule
@@ -1433,9 +1502,11 @@ npm pack && tar -tf *.tgz
1433
1502
 
1434
1503
  #### Getting Help
1435
1504
 
1436
- 1. **Documentation**: Check this README and [docs](https://docs.wealthfolio.app/addons)
1505
+ 1. **Documentation**: Check this README and
1506
+ [docs](https://docs.wealthfolio.app/addons)
1437
1507
  2. **Issues**: [GitHub Issues](https://github.com/afadil/wealthfolio/issues)
1438
- 3. **Discussions**: [GitHub Discussions](https://github.com/afadil/wealthfolio/discussions)
1508
+ 3. **Discussions**:
1509
+ [GitHub Discussions](https://github.com/afadil/wealthfolio/discussions)
1439
1510
  4. **Discord**: [Community Discord](https://discord.gg/wealthfolio)
1440
1511
  5. **Email**: [support@wealthfolio.app](mailto:support@wealthfolio.app)
1441
1512
 
@@ -1466,6 +1537,7 @@ MIT - see [LICENSE](LICENSE) for details.
1466
1537
  **Error**: `Cannot resolve module '@wealthfolio/addon-sdk'`
1467
1538
 
1468
1539
  **Solutions**:
1540
+
1469
1541
  ```bash
1470
1542
  # Clear npm cache
1471
1543
  npm cache clean --force
@@ -1483,6 +1555,7 @@ node --version
1483
1555
  **Error**: `Cannot find type definitions`
1484
1556
 
1485
1557
  **Solutions**:
1558
+
1486
1559
  ```typescript
1487
1560
  // Ensure proper TypeScript configuration
1488
1561
  {
@@ -1502,6 +1575,7 @@ import type { AddonContext } from '@wealthfolio/addon-sdk';
1502
1575
  **Error**: `React version mismatch`
1503
1576
 
1504
1577
  **Solutions**:
1578
+
1505
1579
  ```bash
1506
1580
  # Install correct React version
1507
1581
  npm install react@^18.0.0 react-dom@^18.0.0
@@ -1515,14 +1589,15 @@ npm list react react-dom
1515
1589
  **Error**: `Vite build fails with external dependencies`
1516
1590
 
1517
1591
  **Solutions**:
1592
+
1518
1593
  ```typescript
1519
1594
  // vite.config.ts
1520
1595
  export default defineConfig({
1521
1596
  build: {
1522
1597
  rollupOptions: {
1523
- external: ['react', 'react-dom', '@wealthfolio/addon-sdk']
1524
- }
1525
- }
1598
+ external: ['react', 'react-dom', '@wealthfolio/addon-sdk'],
1599
+ },
1600
+ },
1526
1601
  });
1527
1602
  ```
1528
1603
 
@@ -1531,6 +1606,7 @@ export default defineConfig({
1531
1606
  **Error**: `Permission denied for API call`
1532
1607
 
1533
1608
  **Solutions**:
1609
+
1534
1610
  ```json
1535
1611
  // Add required permissions to manifest.json
1536
1612
  {
@@ -1549,6 +1625,7 @@ export default defineConfig({
1549
1625
  **Error**: `getAddonContext() returns undefined`
1550
1626
 
1551
1627
  **Solutions**:
1628
+
1552
1629
  ```typescript
1553
1630
  // Ensure you're calling it within addon context
1554
1631
  function MyComponent() {
@@ -1578,6 +1655,7 @@ ls -la dist/ # Should update when you save files
1578
1655
  #### 2. Addon Not Loading in Wealthfolio
1579
1656
 
1580
1657
  1. Check the addon package structure:
1658
+
1581
1659
  ```
1582
1660
  addon.zip
1583
1661
  ├── manifest.json ✓
@@ -1587,6 +1665,7 @@ ls -la dist/ # Should update when you save files
1587
1665
  ```
1588
1666
 
1589
1667
  2. Validate manifest.json:
1668
+
1590
1669
  ```bash
1591
1670
  # Check JSON syntax
1592
1671
  cat manifest.json | jq .
@@ -1606,10 +1685,10 @@ try {
1606
1685
  const data = await ctx.api.portfolio.getHoldings(accounts[0]?.id);
1607
1686
  ctx.api.logger.info('Data loaded successfully', { count: data.length });
1608
1687
  } catch (error) {
1609
- ctx.api.logger.error('API call failed', {
1688
+ ctx.api.logger.error('API call failed', {
1610
1689
  error: error.message,
1611
1690
  stack: error.stack,
1612
- timestamp: new Date().toISOString()
1691
+ timestamp: new Date().toISOString(),
1613
1692
  });
1614
1693
  }
1615
1694
  ```
@@ -1630,11 +1709,11 @@ export default defineConfig({
1630
1709
  output: {
1631
1710
  manualChunks: {
1632
1711
  vendor: ['react', 'react-dom'],
1633
- utils: ['lodash', 'date-fns']
1634
- }
1635
- }
1636
- }
1637
- }
1712
+ utils: ['lodash', 'date-fns'],
1713
+ },
1714
+ },
1715
+ },
1716
+ },
1638
1717
  });
1639
1718
  ```
1640
1719
 
@@ -1644,7 +1723,7 @@ export default defineConfig({
1644
1723
  // Proper cleanup in useEffect
1645
1724
  useEffect(() => {
1646
1725
  const subscription = ctx.events.subscribe('update', handler);
1647
-
1726
+
1648
1727
  return () => {
1649
1728
  subscription.unsubscribe(); // ✓ Clean up
1650
1729
  };
@@ -1663,6 +1742,7 @@ context.onDisable(() => {
1663
1742
  If you're still experiencing issues:
1664
1743
 
1665
1744
  1. **Check Version Compatibility**:
1745
+
1666
1746
  ```bash
1667
1747
  npm list @wealthfolio/addon-sdk
1668
1748
  ```
@@ -1680,4 +1760,4 @@ If you're still experiencing issues:
1680
1760
  - Node.js version
1681
1761
  - Operating system
1682
1762
  - Error messages with stack traces
1683
- - Minimal reproduction steps
1763
+ - Minimal reproduction steps