@wealthfolio/addon-sdk 1.0.0 → 2.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
 
@@ -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,22 @@ 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(params)` | Get activity history | `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 |
775
790
 
776
791
  ### Logger API
777
792
 
@@ -797,7 +812,8 @@ if (ctx.api.logger.isLevelEnabled('debug')) {
797
812
 
798
813
  ### Shared QueryClient Integration
799
814
 
800
- The SDK provides access to Wealthfolio's shared React Query client for consistent data fetching and caching:
815
+ The SDK provides access to Wealthfolio's shared React Query client for
816
+ consistent data fetching and caching:
801
817
 
802
818
  ```typescript
803
819
  // Access the shared QueryClient instance
@@ -830,6 +846,7 @@ function MyAddonComponent() {
830
846
  ```
831
847
 
832
848
  **Benefits of Shared QueryClient:**
849
+
833
850
  - **Consistent Caching**: Share cache with the main application
834
851
  - **Performance**: Avoid duplicate API calls across addons
835
852
  - **Synchronization**: Real-time updates when data changes
@@ -840,6 +857,7 @@ function MyAddonComponent() {
840
857
  ### From v1.0.0 to v1.1.0
841
858
 
842
859
  #### Context Access
860
+
843
861
  ```typescript
844
862
  // Before
845
863
  import ctx from '@wealthfolio/addon-sdk';
@@ -850,6 +868,7 @@ const ctx = getAddonContext();
850
868
  ```
851
869
 
852
870
  #### Type Imports
871
+
853
872
  ```typescript
854
873
  // Before
855
874
  import type { AddonContext, AddonManifest } from '@wealthfolio/addon-sdk';
@@ -887,6 +906,7 @@ npm install --save-dev @types/react-dom
887
906
  Create the essential configuration files:
888
907
 
889
908
  **tsconfig.json**
909
+
890
910
  ```json
891
911
  {
892
912
  "compilerOptions": {
@@ -916,6 +936,7 @@ Create the essential configuration files:
916
936
  ```
917
937
 
918
938
  **vite.config.ts**
939
+
919
940
  ```typescript
920
941
  import { defineConfig } from 'vite';
921
942
  import react from '@vitejs/plugin-react';
@@ -928,30 +949,31 @@ export default defineConfig({
928
949
  entry: resolve(__dirname, 'src/index.ts'),
929
950
  name: 'MyPortfolioAddon',
930
951
  fileName: 'addon',
931
- formats: ['es']
952
+ formats: ['es'],
932
953
  },
933
954
  rollupOptions: {
934
955
  external: ['react', 'react-dom'],
935
956
  output: {
936
957
  globals: {
937
958
  react: 'React',
938
- 'react-dom': 'ReactDOM'
939
- }
940
- }
959
+ 'react-dom': 'ReactDOM',
960
+ },
961
+ },
941
962
  },
942
963
  outDir: 'dist',
943
964
  minify: 'terser',
944
- sourcemap: true
965
+ sourcemap: true,
945
966
  },
946
967
  resolve: {
947
968
  alias: {
948
- '@': resolve(__dirname, 'src')
949
- }
950
- }
969
+ '@': resolve(__dirname, 'src'),
970
+ },
971
+ },
951
972
  });
952
973
  ```
953
974
 
954
975
  **package.json scripts**
976
+
955
977
  ```json
956
978
  {
957
979
  "scripts": {
@@ -1013,10 +1035,10 @@ export default defineConfig({
1013
1035
  permissions: 'src/permissions.ts',
1014
1036
  },
1015
1037
  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
1038
+ dts: true, // Generate TypeScript declarations
1039
+ clean: true, // Clean dist folder before build
1040
+ sourcemap: true, // Generate source maps
1041
+ minify: false, // Keep code readable for debugging
1020
1042
  target: 'es2020',
1021
1043
  external: ['react'], // Don't bundle React
1022
1044
  });
@@ -1071,6 +1093,7 @@ ctx.api.logger.debug('Debug information:', data);
1071
1093
  #### 2. Development Console
1072
1094
 
1073
1095
  Access the browser's developer console for debugging:
1096
+
1074
1097
  - Open Wealthfolio
1075
1098
  - Press F12 or right-click → Inspect
1076
1099
  - Check Console tab for addon logs
@@ -1099,24 +1122,24 @@ import { getAddonContext } from '@wealthfolio/addon-sdk';
1099
1122
 
1100
1123
  async function fetchPortfolioData() {
1101
1124
  const ctx = getAddonContext();
1102
-
1125
+
1103
1126
  try {
1104
1127
  // Get all accounts first, then holdings for each
1105
1128
  const accounts = await ctx.api.accounts.getAll();
1106
1129
  const holdings = await Promise.all(
1107
- accounts.map(account => ctx.api.portfolio.getHoldings(account.id))
1108
- ).then(results => results.flat());
1130
+ accounts.map((account) => ctx.api.portfolio.getHoldings(account.id)),
1131
+ ).then((results) => results.flat());
1109
1132
  return holdings;
1110
1133
  } catch (error) {
1111
1134
  ctx.api.logger.error('Failed to fetch holdings:', error);
1112
-
1135
+
1113
1136
  // Handle different error types
1114
1137
  if (error.code === 'PERMISSION_DENIED') {
1115
1138
  // Show permission error to user
1116
1139
  } else if (error.code === 'NETWORK_ERROR') {
1117
1140
  // Handle network issues
1118
1141
  }
1119
-
1142
+
1120
1143
  throw error;
1121
1144
  }
1122
1145
  }
@@ -1127,14 +1150,14 @@ async function fetchPortfolioData() {
1127
1150
  ```typescript
1128
1151
  export default function enable(context: AddonContext) {
1129
1152
  const subscriptions: (() => void)[] = [];
1130
-
1153
+
1131
1154
  // Add event listeners
1132
1155
  const unsubscribe = context.events.subscribe('portfolio.updated', handler);
1133
1156
  subscriptions.push(unsubscribe);
1134
-
1157
+
1135
1158
  // Cleanup on disable
1136
1159
  context.onDisable(() => {
1137
- subscriptions.forEach(unsub => unsub());
1160
+ subscriptions.forEach((unsub) => unsub());
1138
1161
  context.api.logger.info('Addon cleaned up successfully');
1139
1162
  });
1140
1163
  }
@@ -1152,7 +1175,7 @@ const AddonStateContext = createContext<AddonState | null>(null);
1152
1175
 
1153
1176
  export function AddonProvider({ children }: { children: ReactNode }) {
1154
1177
  const [state, setState] = useState<AddonState>(initialState);
1155
-
1178
+
1156
1179
  return (
1157
1180
  <AddonStateContext.Provider value={{ state, setState }}>
1158
1181
  {children}
@@ -1188,7 +1211,7 @@ function usePortfolioData(accountId: string) {
1188
1211
  {
1189
1212
  staleTime: 5 * 60 * 1000, // 5 minutes
1190
1213
  cacheTime: 10 * 60 * 1000, // 10 minutes
1191
- }
1214
+ },
1192
1215
  );
1193
1216
  }
1194
1217
  ```
@@ -1204,10 +1227,10 @@ export default defineConfig({
1204
1227
  manualChunks: {
1205
1228
  vendor: ['react', 'react-dom'],
1206
1229
  charts: ['chart.js', 'd3'],
1207
- }
1208
- }
1209
- }
1210
- }
1230
+ },
1231
+ },
1232
+ },
1233
+ },
1211
1234
  });
1212
1235
  ```
1213
1236
 
@@ -1218,33 +1241,37 @@ We welcome contributions to improve the addon SDK!
1218
1241
  ### Development Setup
1219
1242
 
1220
1243
  1. **Fork and Clone**
1244
+
1221
1245
  ```bash
1222
1246
  git clone https://github.com/yourusername/wealthfolio.git
1223
1247
  cd wealthfolio/packages/addon-sdk
1224
1248
  ```
1225
1249
 
1226
1250
  2. **Install Dependencies**
1251
+
1227
1252
  ```bash
1228
1253
  pnpm install
1229
1254
  ```
1230
1255
 
1231
1256
  3. **Make Changes**
1257
+
1232
1258
  ```bash
1233
1259
  # Start development mode
1234
1260
  pnpm dev
1235
-
1261
+
1236
1262
  # Run type checking
1237
1263
  pnpm lint
1238
-
1264
+
1239
1265
  # Build for testing
1240
1266
  pnpm build
1241
1267
  ```
1242
1268
 
1243
1269
  4. **Testing Your Changes**
1270
+
1244
1271
  ```bash
1245
1272
  # Link the SDK locally for testing
1246
1273
  npm link
1247
-
1274
+
1248
1275
  # In your test addon project
1249
1276
  npm link @wealthfolio/addon-sdk
1250
1277
  ```
@@ -1267,13 +1294,13 @@ We welcome contributions to improve the addon SDK!
1267
1294
 
1268
1295
  ### Package Details
1269
1296
 
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) |
1297
+ | Field | Value |
1298
+ | ---------------- | ----------------------------------------------------------------- |
1299
+ | **Package Name** | `@wealthfolio/addon-sdk` |
1300
+ | **Scope** | `@wealthfolio` |
1301
+ | **Registry** | [npmjs.com](https://www.npmjs.com/package/@wealthfolio/addon-sdk) |
1302
+ | **License** | MIT |
1303
+ | **Repository** | [GitHub](https://github.com/afadil/wealthfolio) |
1277
1304
 
1278
1305
  ### Version History
1279
1306
 
@@ -1285,14 +1312,15 @@ We follow [Semantic Versioning](https://semver.org/) (SemVer):
1285
1312
 
1286
1313
  #### Version Compatibility
1287
1314
 
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 |
1315
+ | SDK Version | Wealthfolio Version | Node.js | React |
1316
+ | ----------- | ------------------- | --------- | ------- |
1317
+ | 1.0.x | >= 1.0.0 | >= 18.0.0 | ^18.0.0 |
1318
+ | 0.9.x | >= 0.9.0 | >= 16.0.0 | ^17.0.0 |
1292
1319
 
1293
1320
  ### Installation from Registry
1294
1321
 
1295
1322
  #### Stable Release
1323
+
1296
1324
  ```bash
1297
1325
  # Latest stable version
1298
1326
  npm install @wealthfolio/addon-sdk
@@ -1305,6 +1333,7 @@ npm install @wealthfolio/addon-sdk@^1.0.0
1305
1333
  ```
1306
1334
 
1307
1335
  #### Beta/Preview Releases
1336
+
1308
1337
  ```bash
1309
1338
  # Latest beta version
1310
1339
  npm install @wealthfolio/addon-sdk@beta
@@ -1314,6 +1343,7 @@ npm install @wealthfolio/addon-sdk@1.1.0-beta.1
1314
1343
  ```
1315
1344
 
1316
1345
  #### Development Version
1346
+
1317
1347
  ```bash
1318
1348
  # Install directly from GitHub
1319
1349
  npm install github:afadil/wealthfolio#main
@@ -1344,6 +1374,7 @@ npm outdated @wealthfolio/addon-sdk
1344
1374
  ### Publishing Information (For Maintainers)
1345
1375
 
1346
1376
  #### Prerequisites
1377
+
1347
1378
  ```bash
1348
1379
  # Login to npm (maintainers only)
1349
1380
  npm login
@@ -1356,6 +1387,7 @@ npm access list packages @wealthfolio
1356
1387
  ```
1357
1388
 
1358
1389
  #### Release Process
1390
+
1359
1391
  ```bash
1360
1392
  # 1. Update version
1361
1393
  npm version patch # or minor/major
@@ -1380,23 +1412,28 @@ git push --tags
1380
1412
 
1381
1413
  #### Distribution Tags
1382
1414
 
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` |
1415
+ | Tag | Purpose | Command |
1416
+ | -------- | ------------------ | ------------------------- |
1417
+ | `latest` | Stable releases | `npm publish` |
1418
+ | `beta` | Beta releases | `npm publish --tag beta` |
1419
+ | `alpha` | Alpha releases | `npm publish --tag alpha` |
1420
+ | `next` | Next major version | `npm publish --tag next` |
1389
1421
 
1390
1422
  #### Package Metrics
1391
1423
 
1392
1424
  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)
1425
+
1426
+ - **Downloads**:
1427
+ [npm-stat.com](https://npm-stat.com/charts.html?package=@wealthfolio/addon-sdk)
1428
+ - **Bundle Size**:
1429
+ [bundlephobia.com](https://bundlephobia.com/package/@wealthfolio/addon-sdk)
1430
+ - **Dependencies**:
1431
+ [npm.anvaka.com](https://npm.anvaka.com/#/view/2d/@wealthfolio/addon-sdk)
1396
1432
 
1397
1433
  ### Security
1398
1434
 
1399
1435
  #### Vulnerability Scanning
1436
+
1400
1437
  ```bash
1401
1438
  # Check for vulnerabilities
1402
1439
  npm audit
@@ -1409,6 +1446,7 @@ npm audit --audit-level=moderate
1409
1446
  ```
1410
1447
 
1411
1448
  #### Package Integrity
1449
+
1412
1450
  ```bash
1413
1451
  # Verify package integrity
1414
1452
  npm pack --dry-run
@@ -1422,7 +1460,8 @@ npm pack && tar -tf *.tgz
1422
1460
  #### Package Support Policy
1423
1461
 
1424
1462
  - **Latest Major Version**: Full support with new features and bug fixes
1425
- - **Previous Major Version**: Security fixes and critical bug fixes for 12 months
1463
+ - **Previous Major Version**: Security fixes and critical bug fixes for 12
1464
+ months
1426
1465
  - **Older Versions**: Community support only
1427
1466
 
1428
1467
  #### Maintenance Schedule
@@ -1433,9 +1472,11 @@ npm pack && tar -tf *.tgz
1433
1472
 
1434
1473
  #### Getting Help
1435
1474
 
1436
- 1. **Documentation**: Check this README and [docs](https://docs.wealthfolio.app/addons)
1475
+ 1. **Documentation**: Check this README and
1476
+ [docs](https://docs.wealthfolio.app/addons)
1437
1477
  2. **Issues**: [GitHub Issues](https://github.com/afadil/wealthfolio/issues)
1438
- 3. **Discussions**: [GitHub Discussions](https://github.com/afadil/wealthfolio/discussions)
1478
+ 3. **Discussions**:
1479
+ [GitHub Discussions](https://github.com/afadil/wealthfolio/discussions)
1439
1480
  4. **Discord**: [Community Discord](https://discord.gg/wealthfolio)
1440
1481
  5. **Email**: [support@wealthfolio.app](mailto:support@wealthfolio.app)
1441
1482
 
@@ -1466,6 +1507,7 @@ MIT - see [LICENSE](LICENSE) for details.
1466
1507
  **Error**: `Cannot resolve module '@wealthfolio/addon-sdk'`
1467
1508
 
1468
1509
  **Solutions**:
1510
+
1469
1511
  ```bash
1470
1512
  # Clear npm cache
1471
1513
  npm cache clean --force
@@ -1483,6 +1525,7 @@ node --version
1483
1525
  **Error**: `Cannot find type definitions`
1484
1526
 
1485
1527
  **Solutions**:
1528
+
1486
1529
  ```typescript
1487
1530
  // Ensure proper TypeScript configuration
1488
1531
  {
@@ -1502,6 +1545,7 @@ import type { AddonContext } from '@wealthfolio/addon-sdk';
1502
1545
  **Error**: `React version mismatch`
1503
1546
 
1504
1547
  **Solutions**:
1548
+
1505
1549
  ```bash
1506
1550
  # Install correct React version
1507
1551
  npm install react@^18.0.0 react-dom@^18.0.0
@@ -1515,14 +1559,15 @@ npm list react react-dom
1515
1559
  **Error**: `Vite build fails with external dependencies`
1516
1560
 
1517
1561
  **Solutions**:
1562
+
1518
1563
  ```typescript
1519
1564
  // vite.config.ts
1520
1565
  export default defineConfig({
1521
1566
  build: {
1522
1567
  rollupOptions: {
1523
- external: ['react', 'react-dom', '@wealthfolio/addon-sdk']
1524
- }
1525
- }
1568
+ external: ['react', 'react-dom', '@wealthfolio/addon-sdk'],
1569
+ },
1570
+ },
1526
1571
  });
1527
1572
  ```
1528
1573
 
@@ -1531,6 +1576,7 @@ export default defineConfig({
1531
1576
  **Error**: `Permission denied for API call`
1532
1577
 
1533
1578
  **Solutions**:
1579
+
1534
1580
  ```json
1535
1581
  // Add required permissions to manifest.json
1536
1582
  {
@@ -1549,6 +1595,7 @@ export default defineConfig({
1549
1595
  **Error**: `getAddonContext() returns undefined`
1550
1596
 
1551
1597
  **Solutions**:
1598
+
1552
1599
  ```typescript
1553
1600
  // Ensure you're calling it within addon context
1554
1601
  function MyComponent() {
@@ -1578,6 +1625,7 @@ ls -la dist/ # Should update when you save files
1578
1625
  #### 2. Addon Not Loading in Wealthfolio
1579
1626
 
1580
1627
  1. Check the addon package structure:
1628
+
1581
1629
  ```
1582
1630
  addon.zip
1583
1631
  ├── manifest.json ✓
@@ -1587,6 +1635,7 @@ ls -la dist/ # Should update when you save files
1587
1635
  ```
1588
1636
 
1589
1637
  2. Validate manifest.json:
1638
+
1590
1639
  ```bash
1591
1640
  # Check JSON syntax
1592
1641
  cat manifest.json | jq .
@@ -1606,10 +1655,10 @@ try {
1606
1655
  const data = await ctx.api.portfolio.getHoldings(accounts[0]?.id);
1607
1656
  ctx.api.logger.info('Data loaded successfully', { count: data.length });
1608
1657
  } catch (error) {
1609
- ctx.api.logger.error('API call failed', {
1658
+ ctx.api.logger.error('API call failed', {
1610
1659
  error: error.message,
1611
1660
  stack: error.stack,
1612
- timestamp: new Date().toISOString()
1661
+ timestamp: new Date().toISOString(),
1613
1662
  });
1614
1663
  }
1615
1664
  ```
@@ -1630,11 +1679,11 @@ export default defineConfig({
1630
1679
  output: {
1631
1680
  manualChunks: {
1632
1681
  vendor: ['react', 'react-dom'],
1633
- utils: ['lodash', 'date-fns']
1634
- }
1635
- }
1636
- }
1637
- }
1682
+ utils: ['lodash', 'date-fns'],
1683
+ },
1684
+ },
1685
+ },
1686
+ },
1638
1687
  });
1639
1688
  ```
1640
1689
 
@@ -1644,7 +1693,7 @@ export default defineConfig({
1644
1693
  // Proper cleanup in useEffect
1645
1694
  useEffect(() => {
1646
1695
  const subscription = ctx.events.subscribe('update', handler);
1647
-
1696
+
1648
1697
  return () => {
1649
1698
  subscription.unsubscribe(); // ✓ Clean up
1650
1699
  };
@@ -1663,6 +1712,7 @@ context.onDisable(() => {
1663
1712
  If you're still experiencing issues:
1664
1713
 
1665
1714
  1. **Check Version Compatibility**:
1715
+
1666
1716
  ```bash
1667
1717
  npm list @wealthfolio/addon-sdk
1668
1718
  ```
@@ -1680,4 +1730,4 @@ If you're still experiencing issues:
1680
1730
  - Node.js version
1681
1731
  - Operating system
1682
1732
  - Error messages with stack traces
1683
- - Minimal reproduction steps
1733
+ - Minimal reproduction steps