@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/CHANGELOG.md +19 -8
- package/README.md +226 -146
- package/dist/{chunk-3OBZP7TN.mjs → chunk-6MGUTH7T.js} +10 -40
- package/dist/chunk-6MGUTH7T.js.map +1 -0
- package/dist/{index.mjs → index.js} +70 -23
- package/dist/index.js.map +1 -0
- package/dist/{permissions.mjs → permissions.js} +2 -2
- package/dist/src/data-types.d.ts +713 -0
- package/dist/src/goal-progress.d.ts +7 -0
- package/dist/src/host-api.d.ts +566 -0
- package/dist/src/index.d.ts +29 -0
- package/dist/src/manifest.d.ts +176 -0
- package/dist/{permissions.d.mts → src/permissions.d.ts} +16 -18
- package/dist/src/query-keys.d.ts +44 -0
- package/dist/src/types.d.ts +88 -0
- package/dist/src/utils.d.ts +24 -0
- package/dist/src/version.d.ts +4 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types.js +1 -0
- package/package.json +28 -17
- package/dist/chunk-3OBZP7TN.mjs.map +0 -1
- package/dist/index.d.mts +0 -279
- package/dist/index.mjs.map +0 -1
- package/dist/types-CxtChQdm.d.mts +0 -1137
- package/dist/types.d.mts +0 -2
- package/dist/types.mjs +0 -1
- /package/dist/{permissions.mjs.map → permissions.js.map} +0 -0
- /package/dist/{types.mjs.map → types.js.map} +0 -0
package/README.md
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
[](https://www.typescriptlang.org/)
|
|
7
7
|
[](https://nodejs.org/)
|
|
8
8
|
|
|
9
|
-
A comprehensive TypeScript SDK for building secure, feature-rich addons for
|
|
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
|
|
34
|
-
|
|
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
|
|
206
|
-
|
|
207
|
-
| `id`
|
|
208
|
-
| `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
|
|
214
|
-
|
|
215
|
-
| `description`
|
|
216
|
-
| `author`
|
|
217
|
-
| `homepage`
|
|
218
|
-
| `license`
|
|
219
|
-
| `main`
|
|
220
|
-
| `sdkVersion`
|
|
221
|
-
| `permissions`
|
|
222
|
-
| `minWealthfolioVersion` | `string`
|
|
223
|
-
| `keywords`
|
|
224
|
-
| `icon`
|
|
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
|
|
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
|
|
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(
|
|
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>(
|
|
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(
|
|
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 =
|
|
510
|
-
|
|
511
|
-
|
|
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
|
|
534
|
-
|
|
535
|
-
| `ui`
|
|
536
|
-
| `market-data`
|
|
537
|
-
| `events`
|
|
538
|
-
| `currency`
|
|
539
|
-
| `portfolio`
|
|
540
|
-
| `files`
|
|
541
|
-
| `financial-planning` | Medium
|
|
542
|
-
| `activities`
|
|
543
|
-
| `accounts`
|
|
544
|
-
| `settings`
|
|
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
|
|
760
|
-
|
|
761
|
-
| `portfolio.getHoldings(accountId)`
|
|
762
|
-
| `portfolio.getHolding(accountId, assetId)`
|
|
763
|
-
| `portfolio.calculatePerformanceSummary(params)` | Calculate performance metrics
|
|
764
|
-
| `portfolio.getIncomeSummary()`
|
|
765
|
-
| `accounts.getAll()`
|
|
766
|
-
| `accounts.create(account)`
|
|
767
|
-
| `activities.getAll(
|
|
768
|
-
| `activities.create(activity)`
|
|
769
|
-
| `marketData.getQuoteHistory(symbol)`
|
|
770
|
-
| `marketData.getAssetProfile(assetId)`
|
|
771
|
-
| `marketData.searchTicker(query)`
|
|
772
|
-
| `goals.getAll()`
|
|
773
|
-
| `settings.get()`
|
|
774
|
-
| `query.getClient()`
|
|
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
|
|
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,
|
|
1017
|
-
clean: true,
|
|
1018
|
-
sourcemap: true,
|
|
1019
|
-
minify: false,
|
|
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
|
|
1271
|
-
|
|
1272
|
-
| **Package Name** | `@wealthfolio/addon-sdk`
|
|
1273
|
-
| **Scope**
|
|
1274
|
-
| **Registry**
|
|
1275
|
-
| **License**
|
|
1276
|
-
| **Repository**
|
|
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
|
|
1289
|
-
|
|
1290
|
-
| 1.0.x
|
|
1291
|
-
| 0.9.x
|
|
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
|
|
1384
|
-
|
|
1385
|
-
| `latest` | Stable releases
|
|
1386
|
-
| `beta`
|
|
1387
|
-
| `alpha`
|
|
1388
|
-
| `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
|
-
|
|
1394
|
-
- **
|
|
1395
|
-
|
|
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
|
|
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
|
|
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**:
|
|
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
|