@wealthfolio/addon-sdk 1.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 ADDED
@@ -0,0 +1,1683 @@
1
+ # @wealthfolio/addon-sdk
2
+
3
+ [![Version](https://img.shields.io/npm/v/@wealthfolio/addon-sdk?style=flat-square)](https://www.npmjs.com/package/@wealthfolio/addon-sdk)
4
+ [![Downloads](https://img.shields.io/npm/dm/@wealthfolio/addon-sdk?style=flat-square)](https://www.npmjs.com/package/@wealthfolio/addon-sdk)
5
+ [![License](https://img.shields.io/npm/l/@wealthfolio/addon-sdk?style=flat-square)](https://github.com/afadil/wealthfolio/blob/main/LICENSE)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue?style=flat-square)](https://www.typescriptlang.org/)
7
+ [![Node](https://img.shields.io/node/v/@wealthfolio/addon-sdk?style=flat-square)](https://nodejs.org/)
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.
10
+
11
+ ## 📚 Table of Contents
12
+
13
+ - [Features](#-features)
14
+ - [Installation](#-installation)
15
+ - [Project Structure](#-project-structure)
16
+ - [Manifest Configuration](#-manifest-configuration)
17
+ - [Development Guide](#-development-guide)
18
+ - [Security & Permissions](#-security--permissions)
19
+ - [Build Configuration](#-build-configuration)
20
+ - [Building and Packaging](#-building-and-packaging)
21
+ - [Installation & Testing](#-installation--testing)
22
+ - [API Reference](#-api-reference)
23
+ - [Migration Guide](#-migration-guide)
24
+ - [Contributing](#-contributing)
25
+ - [NPM Registry Information](#-npm-registry-information)
26
+ - [Troubleshooting](#-troubleshooting)
27
+ - [License](#-license)
28
+ - [Links](#-links)
29
+ - [Support](#-support)
30
+
31
+ ## 🚀 Features
32
+
33
+ - **Type-Safe Development**: Full TypeScript support with comprehensive type definitions
34
+ - **Security-First**: Built-in permission system with granular risk assessment
35
+ - **Modular Architecture**: Clean separation of concerns with well-defined APIs
36
+ - **React Integration**: Seamless integration with React components and hooks
37
+ - **Hot Reloading**: Development-friendly with automatic reload capabilities
38
+ - **ZIP Packaging**: Simple distribution model with manifest-based configuration
39
+ - **ESM Support**: Modern ECMAScript modules with tree-shaking support
40
+ - **Comprehensive Logging**: Built-in logging system with multiple levels
41
+ - **Event System**: Subscribe to application events and state changes
42
+ - **Performance Optimized**: Lightweight bundle with minimal overhead
43
+ - **Developer Tools**: Built-in debugging and development utilities
44
+ - **Backwards Compatible**: Stable API with semantic versioning
45
+
46
+ ## ⚡ Quick Start
47
+
48
+ Get up and running with your first addon in minutes:
49
+
50
+ ```bash
51
+ # 1. Create a new project
52
+ mkdir my-portfolio-addon && cd my-portfolio-addon
53
+
54
+ # 2. Initialize and install dependencies
55
+ npm init -y
56
+ npm install @wealthfolio/addon-sdk react react-dom
57
+ npm install -D typescript @types/react vite @vitejs/plugin-react
58
+
59
+ # 3. Create basic files
60
+ echo '{"id": "my-addon", "name": "My Portfolio Addon", "version": "1.0.0"}' > manifest.json
61
+ mkdir src && touch src/index.ts
62
+
63
+ # 4. Start building your addon!
64
+ ```
65
+
66
+ ### Minimal Addon Example
67
+
68
+ ```typescript
69
+ // src/index.ts
70
+ import { getAddonContext, type AddonContext } from '@wealthfolio/addon-sdk';
71
+
72
+ export default function enable(context: AddonContext) {
73
+ // Add navigation item
74
+ const navItem = context.sidebar.addItem({
75
+ id: 'my-addon',
76
+ label: 'My Addon',
77
+ icon: 'chart-line',
78
+ route: '/addons/my-addon'
79
+ });
80
+
81
+ // Register route
82
+ context.router.add({
83
+ path: '/addons/my-addon',
84
+ component: () => import('./MyComponent')
85
+ });
86
+
87
+ // Log activation
88
+ context.api.logger.info('My addon activated!');
89
+
90
+ // Cleanup on disable
91
+ context.onDisable(() => {
92
+ navItem.remove();
93
+ context.api.logger.info('My addon deactivated');
94
+ });
95
+ }
96
+ ```
97
+
98
+ ## 📦 Installation
99
+
100
+ ```bash
101
+ # Using npm
102
+ npm install @wealthfolio/addon-sdk @tanstack/react-query
103
+
104
+ # Using yarn
105
+ yarn add @wealthfolio/addon-sdk @tanstack/react-query
106
+
107
+ # Using pnpm
108
+ pnpm add @wealthfolio/addon-sdk @tanstack/react-query
109
+ ```
110
+
111
+ ### Requirements
112
+
113
+ - **Node.js**: >= 18.0.0
114
+ - **React**: ^18.0.0 (peer dependency)
115
+ - **TypeScript**: ^5.0.0 (recommended for development)
116
+ - **React Query**: ^4.0.0 or ^5.0.0 (for data fetching)
117
+
118
+ ### Package Information
119
+
120
+ - **Package Name**: `@wealthfolio/addon-sdk`
121
+ - **Current Version**: 1.0.0
122
+ - **Bundle Format**: ESM (ECMAScript Modules)
123
+ - **Type Definitions**: Included (TypeScript ready)
124
+ - **License**: MIT
125
+ - **Bundle Size**: ~15KB (minified + gzipped)
126
+ - **Tree Shakeable**: Yes
127
+ - **Side Effects**: No
128
+
129
+ ### Import Methods
130
+
131
+ The SDK supports multiple import patterns:
132
+
133
+ ```typescript
134
+ // Default import (recommended)
135
+ import { getAddonContext } from '@wealthfolio/addon-sdk';
136
+
137
+ // Named imports
138
+ import { AddonContext, PermissionLevel } from '@wealthfolio/addon-sdk';
139
+
140
+ // Type-only imports
141
+ import type { AddonManifest, Permission } from '@wealthfolio/addon-sdk';
142
+
143
+ // Subpath imports
144
+ import type { PortfolioHolding } from '@wealthfolio/addon-sdk/types';
145
+ import { PERMISSION_CATEGORIES } from '@wealthfolio/addon-sdk/permissions';
146
+ ```
147
+
148
+ ## 🏗️ Project Structure
149
+
150
+ Create your addon with the following recommended structure:
151
+
152
+ ```
153
+ my-portfolio-addon/
154
+ ├── manifest.json # Addon metadata and permissions
155
+ ├── src/
156
+ │ ├── index.ts # Main entry point
157
+ │ ├── components/ # React components
158
+ │ │ └── Dashboard.tsx
159
+ │ ├── hooks/ # Custom hooks
160
+ │ ├── types/ # TypeScript types
161
+ │ └── utils/ # Utility functions
162
+ ├── dist/ # Built output
163
+ │ └── addon.js
164
+ ├── assets/ # Static assets
165
+ ├── package.json
166
+ ├── tsconfig.json
167
+ └── vite.config.ts # Build configuration
168
+ ```
169
+
170
+ ## 📋 Manifest Configuration
171
+
172
+ Create a `manifest.json` file in your addon root:
173
+
174
+ ```json
175
+ {
176
+ "id": "investment-fees-tracker",
177
+ "name": "Investment Fees Tracker",
178
+ "version": "1.0.0",
179
+ "description": "Track and analyze investment fees across your portfolio",
180
+ "author": "Your Name",
181
+ "homepage": "https://github.com/yourname/investment-fees-tracker",
182
+ "license": "MIT",
183
+ "main": "dist/addon.js",
184
+ "sdkVersion": "1.0.0",
185
+ "minWealthfolioVersion": "1.0.0",
186
+ "keywords": ["portfolio", "fees", "tracking", "analytics"],
187
+ "icon": "data:image/svg+xml;base64,...",
188
+ "permissions": [
189
+ {
190
+ "category": "portfolio",
191
+ "functions": ["getHoldings"],
192
+ "purpose": "Access portfolio data to calculate fee analytics"
193
+ },
194
+ {
195
+ "category": "activities",
196
+ "functions": ["getAll"],
197
+ "purpose": "Analyze transaction history for fee calculations"
198
+ }
199
+ ]
200
+ }
201
+ ```
202
+
203
+ ### Required Fields
204
+
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") |
210
+
211
+ ### Optional Fields
212
+
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) |
225
+
226
+ ## 🔨 Development Guide
227
+
228
+ ### Modern Addon Example
229
+
230
+ Based on the current SDK architecture, here's a complete real-world addon example:
231
+
232
+ ```typescript
233
+ // src/addon.tsx
234
+ import React from 'react';
235
+ import { QueryClientProvider } from '@tanstack/react-query';
236
+ import type { AddonContext, AddonEnableFunction } from '@wealthfolio/addon-sdk';
237
+ import { Icons } from '@wealthfolio/ui';
238
+ import FeesPage from './pages/fees-page';
239
+
240
+ // Main addon component
241
+ function InvestmentFeesTrackerAddon({ ctx }: { ctx: AddonContext }) {
242
+ return (
243
+ <div className="investment-fees-tracker-addon">
244
+ <FeesPage ctx={ctx} />
245
+ </div>
246
+ );
247
+ }
248
+
249
+ // Addon enable function - called when the addon is loaded
250
+ const enable: AddonEnableFunction = (context) => {
251
+ context.api.logger.info('💰 Investment Fees Tracker addon is being enabled!');
252
+
253
+ // Store references to items for cleanup
254
+ const addedItems: Array<{ remove: () => void }> = [];
255
+
256
+ try {
257
+ // Add sidebar navigation item with icon from UI library
258
+ const sidebarItem = context.sidebar.addItem({
259
+ id: 'investment-fees-tracker',
260
+ label: 'Fee Tracker',
261
+ icon: <Icons.Invoice className="h-5 w-5" />,
262
+ route: '/addons/investment-fees-tracker',
263
+ order: 200
264
+ });
265
+ addedItems.push(sidebarItem);
266
+
267
+ context.api.logger.debug('Sidebar navigation item added successfully');
268
+
269
+ // Create wrapper component with shared QueryClient
270
+ const InvestmentFeesTrackerWrapper = () => {
271
+ const sharedQueryClient = context.api.query.getClient();
272
+ return (
273
+ <QueryClientProvider client={sharedQueryClient}>
274
+ <InvestmentFeesTrackerAddon ctx={context} />
275
+ </QueryClientProvider>
276
+ );
277
+ };
278
+
279
+ // Register route with lazy loading
280
+ context.router.add({
281
+ path: '/addons/investment-fees-tracker',
282
+ component: React.lazy(() => Promise.resolve({
283
+ default: InvestmentFeesTrackerWrapper
284
+ }))
285
+ });
286
+
287
+ context.api.logger.debug('Route registered successfully');
288
+ context.api.logger.info('Investment Fees Tracker addon enabled successfully');
289
+
290
+ } catch (error) {
291
+ context.api.logger.error('Failed to initialize addon: ' + (error as Error).message);
292
+ throw error; // Re-throw so addon system can handle it
293
+ }
294
+
295
+ // Register cleanup callback
296
+ context.onDisable(() => {
297
+ context.api.logger.info('🛑 Investment Fees Tracker addon is being disabled');
298
+
299
+ // Remove all sidebar items
300
+ addedItems.forEach(item => {
301
+ try {
302
+ item.remove();
303
+ } catch (error) {
304
+ context.api.logger.error('Error removing sidebar item: ' + (error as Error).message);
305
+ }
306
+ });
307
+
308
+ context.api.logger.info('Investment Fees Tracker addon disabled successfully');
309
+ });
310
+ };
311
+
312
+ // Export the enable function as default
313
+ export default enable;
314
+ ```
315
+
316
+ ### Key Features Demonstrated
317
+
318
+ 1. **Shared Query Client**: Uses `context.api.query.getClient()` for consistent data fetching
319
+ 2. **UI Icons**: Leverages `@wealthfolio/ui` for consistent iconography
320
+ 3. **Error Handling**: Comprehensive error handling with logging
321
+ 4. **Resource Management**: Proper cleanup of sidebar items and event listeners
322
+ 5. **TypeScript**: Full type safety with proper imports
323
+ 6. **Lazy Loading**: Efficient component loading with React.lazy
324
+ ```
325
+
326
+ ### Advanced Component Example
327
+
328
+ ```typescript
329
+ // components/FeesPage.tsx
330
+ import React, { useEffect, useState } from 'react';
331
+ import { useQuery } from '@tanstack/react-query';
332
+ import type { AddonContext } from '@wealthfolio/addon-sdk';
333
+ import type { Holding, Account, Activity } from '@wealthfolio/addon-sdk/types';
334
+
335
+ interface FeesPageProps {
336
+ ctx: AddonContext;
337
+ }
338
+
339
+ export function FeesPage({ ctx }: FeesPageProps) {
340
+ // Use React Query for data fetching with the shared client
341
+ const { data: accounts, isLoading: accountsLoading } = useQuery({
342
+ queryKey: ['accounts'],
343
+ queryFn: () => ctx.api.accounts.getAll()
344
+ });
345
+
346
+ const { data: holdings, isLoading: holdingsLoading } = useQuery({
347
+ queryKey: ['holdings'],
348
+ queryFn: async () => {
349
+ if (!accounts || accounts.length === 0) return [];
350
+ // Get holdings for all accounts
351
+ const allHoldings = await Promise.all(
352
+ accounts.map(account => ctx.api.portfolio.getHoldings(account.id))
353
+ );
354
+ return allHoldings.flat();
355
+ },
356
+ enabled: !!accounts && accounts.length > 0
357
+ });
358
+
359
+ const { data: activities, isLoading: activitiesLoading } = useQuery({
360
+ queryKey: ['activities'],
361
+ queryFn: () => ctx.api.activities.getAll({ page: 1, pageSize: 1000 })
362
+ });
363
+
364
+ const isLoading = accountsLoading || holdingsLoading || activitiesLoading;
365
+
366
+ // Calculate total fees from activities
367
+ const totalFees = React.useMemo(() => {
368
+ if (!activities?.data) return 0;
369
+
370
+ return activities.data.reduce((total, activity) => {
371
+ // Look for fee-related activities or transaction costs
372
+ const fee = activity.fee || 0;
373
+ return total + fee;
374
+ }, 0);
375
+ }, [activities]);
376
+
377
+ useEffect(() => {
378
+ if (!isLoading) {
379
+ ctx.api.logger.info('Fees data loaded successfully', {
380
+ accountsCount: accounts?.length,
381
+ holdingsCount: holdings?.length,
382
+ activitiesCount: activities?.data?.length,
383
+ totalFees
384
+ });
385
+ }
386
+ }, [isLoading, accounts, holdings, activities, totalFees, ctx.api.logger]);
387
+
388
+ if (isLoading) {
389
+ return (
390
+ <div className="flex items-center justify-center p-8">
391
+ <div className="text-center">
392
+ <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto mb-4"></div>
393
+ <p>Loading fees data...</p>
394
+ </div>
395
+ </div>
396
+ );
397
+ }
398
+
399
+ return (
400
+ <div className="p-6 max-w-7xl mx-auto">
401
+ <div className="mb-8">
402
+ <h1 className="text-3xl font-bold text-gray-900 mb-2">Investment Fees Tracker</h1>
403
+ <p className="text-gray-600">Track and analyze fees across your investment portfolio</p>
404
+ </div>
405
+
406
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
407
+ <div className="bg-white p-6 rounded-lg shadow border">
408
+ <h3 className="text-lg font-semibold text-gray-900 mb-2">Total Fees Paid</h3>
409
+ <p className="text-3xl font-bold text-red-600">
410
+ ${totalFees.toLocaleString('en-US', { minimumFractionDigits: 2 })}
411
+ </p>
412
+ </div>
413
+
414
+ <div className="bg-white p-6 rounded-lg shadow border">
415
+ <h3 className="text-lg font-semibold text-gray-900 mb-2">Accounts Tracked</h3>
416
+ <p className="text-3xl font-bold text-blue-600">{accounts?.length || 0}</p>
417
+ </div>
418
+
419
+ <div className="bg-white p-6 rounded-lg shadow border">
420
+ <h3 className="text-lg font-semibold text-gray-900 mb-2">Holdings</h3>
421
+ <p className="text-3xl font-bold text-green-600">{holdings?.length || 0}</p>
422
+ </div>
423
+ </div>
424
+
425
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
426
+ <div className="bg-white p-6 rounded-lg shadow border">
427
+ <h2 className="text-xl font-semibold mb-4">Recent Fee Activities</h2>
428
+ <div className="space-y-3">
429
+ {activities?.data?.slice(0, 5).map((activity) => (
430
+ <div key={activity.id} className="flex justify-between items-center py-2 border-b">
431
+ <div>
432
+ <p className="font-medium">{activity.activityType}</p>
433
+ <p className="text-sm text-gray-600">{activity.date}</p>
434
+ </div>
435
+ <span className="text-red-600 font-medium">
436
+ ${(activity.fee || 0).toFixed(2)}
437
+ </span>
438
+ </div>
439
+ ))}
440
+ </div>
441
+ </div>
442
+
443
+ <div className="bg-white p-6 rounded-lg shadow border">
444
+ <h2 className="text-xl font-semibold mb-4">Account Summary</h2>
445
+ <div className="space-y-3">
446
+ {accounts?.map((account) => (
447
+ <div key={account.id} className="flex justify-between items-center py-2 border-b">
448
+ <div>
449
+ <p className="font-medium">{account.name}</p>
450
+ <p className="text-sm text-gray-600">{account.accountType}</p>
451
+ </div>
452
+ <span className="text-gray-900 font-medium">
453
+ ${account.balance?.toLocaleString('en-US', { minimumFractionDigits: 2 }) || '0.00'}
454
+ </span>
455
+ </div>
456
+ ))}
457
+ </div>
458
+ </div>
459
+ </div>
460
+ </div>
461
+ );
462
+ }
463
+
464
+ export default FeesPage;
465
+ <h2 className="text-lg font-semibold mb-4">Holdings Overview</h2>
466
+ <p>Total holdings: {holdings.length}</p>
467
+ {/* Add your custom analytics here */}
468
+ </div>
469
+
470
+ <div className="bg-white p-4 rounded-lg shadow">
471
+ <h2 className="text-lg font-semibold mb-4">Account Summary</h2>
472
+ <p>Total accounts: {accounts.length}</p>
473
+ {/* Add account analytics here */}
474
+ </div>
475
+ </div>
476
+ </div>
477
+ );
478
+ }
479
+
480
+ export default AnalyticsDashboard;
481
+ ```
482
+
483
+ ### Using Hooks and State Management
484
+
485
+ ```typescript
486
+ // hooks/usePortfolioData.ts
487
+ import { useState, useEffect } from 'react';
488
+ import { getAddonContext } from '@wealthfolio/addon-sdk';
489
+ import type { Holding, PerformanceMetrics } from '@wealthfolio/addon-sdk/types';
490
+
491
+ export function usePortfolioData(accountId?: string) {
492
+ const [holdings, setHoldings] = useState<Holding[]>([]);
493
+ const [performance, setPerformance] = useState<PerformanceMetrics | null>(null);
494
+ const [loading, setLoading] = useState(true);
495
+ const [error, setError] = useState<string | null>(null);
496
+
497
+ useEffect(() => {
498
+ async function fetchData() {
499
+ try {
500
+ setLoading(true);
501
+ setError(null);
502
+
503
+ const ctx = getAddonContext();
504
+
505
+ const holdingsData = await ctx.api.portfolio.getHoldings(accountId || '');
506
+ setHoldings(holdingsData);
507
+
508
+ if (accountId) {
509
+ const performanceData = await ctx.api.portfolio.calculatePerformanceSummary({
510
+ itemType: 'account',
511
+ itemId: accountId
512
+ });
513
+ setPerformance(performanceData);
514
+ }
515
+ } catch (err) {
516
+ setError(err instanceof Error ? err.message : 'Unknown error');
517
+ } finally {
518
+ setLoading(false);
519
+ }
520
+ }
521
+
522
+ fetchData();
523
+ }, [accountId]);
524
+
525
+ return { holdings, performance, loading, error };
526
+ }
527
+ ```
528
+
529
+ ## 🔐 Security & Permissions
530
+
531
+ ### Permission Categories
532
+
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 |
545
+
546
+ ### Declaring Permissions
547
+
548
+ ```json
549
+ {
550
+ "permissions": [
551
+ {
552
+ "category": "portfolio",
553
+ "functions": ["getHoldings", "getHolding", "calculatePerformanceSummary"],
554
+ "purpose": "Display detailed portfolio analytics and performance metrics"
555
+ },
556
+ {
557
+ "category": "activities",
558
+ "functions": ["getAll", "create"],
559
+ "purpose": "Access transaction history for fee calculations and analysis"
560
+ },
561
+ {
562
+ "category": "market-data",
563
+ "functions": ["searchTicker", "getQuoteHistory"],
564
+ "purpose": "Show price charts and enable ticker search functionality"
565
+ }
566
+ ]
567
+ }
568
+ ```
569
+
570
+ ## 🛠️ Build Configuration
571
+
572
+ ### Vite Configuration
573
+
574
+ Create a `vite.config.ts` for optimal bundling:
575
+
576
+ ```typescript
577
+ import { defineConfig } from 'vite';
578
+ import react from '@vitejs/plugin-react';
579
+ import { resolve } from 'path';
580
+
581
+ export default defineConfig({
582
+ plugins: [react()],
583
+ build: {
584
+ lib: {
585
+ entry: resolve(__dirname, 'src/index.ts'),
586
+ name: 'MyPortfolioAddon',
587
+ fileName: 'addon',
588
+ formats: ['es']
589
+ },
590
+ rollupOptions: {
591
+ external: ['react', 'react-dom'],
592
+ output: {
593
+ globals: {
594
+ react: 'React',
595
+ 'react-dom': 'ReactDOM'
596
+ }
597
+ }
598
+ },
599
+ outDir: 'dist',
600
+ minify: 'terser',
601
+ sourcemap: true
602
+ },
603
+ resolve: {
604
+ alias: {
605
+ '@': resolve(__dirname, 'src')
606
+ }
607
+ }
608
+ });
609
+ ```
610
+
611
+ ### TypeScript Configuration
612
+
613
+ ```json
614
+ {
615
+ "compilerOptions": {
616
+ "target": "ES2020",
617
+ "useDefineForClassFields": true,
618
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
619
+ "module": "ESNext",
620
+ "skipLibCheck": true,
621
+ "moduleResolution": "bundler",
622
+ "allowImportingTsExtensions": true,
623
+ "resolveJsonModule": true,
624
+ "isolatedModules": true,
625
+ "noEmit": true,
626
+ "jsx": "react-jsx",
627
+ "strict": true,
628
+ "noUnusedLocals": true,
629
+ "noUnusedParameters": true,
630
+ "noFallthroughCasesInSwitch": true,
631
+ "baseUrl": ".",
632
+ "paths": {
633
+ "@/*": ["src/*"]
634
+ }
635
+ },
636
+ "include": ["src"],
637
+ "references": [{ "path": "./tsconfig.node.json" }]
638
+ }
639
+ ```
640
+
641
+ ## 📦 Building and Packaging
642
+
643
+ ### Build Your Addon
644
+
645
+ ```bash
646
+ # Install dependencies
647
+ npm install
648
+
649
+ # Build for production
650
+ npm run build
651
+
652
+ # The built addon will be in dist/addon.js
653
+ ```
654
+
655
+ ### Create Distribution Package
656
+
657
+ ```bash
658
+ # Create a ZIP package with all necessary files
659
+ zip -r my-portfolio-addon.zip \
660
+ manifest.json \
661
+ dist/ \
662
+ assets/ \
663
+ README.md
664
+ ```
665
+
666
+ ### Package Structure
667
+
668
+ Your final package should contain:
669
+ - `manifest.json` - Addon metadata
670
+ - `dist/addon.js` - Compiled addon code
671
+ - `assets/` - Static assets (optional)
672
+ - `README.md` - Documentation (optional)
673
+
674
+ ## 🚀 Installation & Testing
675
+
676
+ ### Install in Wealthfolio
677
+
678
+ 1. Open Wealthfolio
679
+ 2. Navigate to Settings → Addons
680
+ 3. Click "Install Addon"
681
+ 4. Select your ZIP package
682
+ 5. Review permissions and approve
683
+ 6. Restart Wealthfolio to activate
684
+
685
+ ### Development Testing
686
+
687
+ For development, you can test addons locally:
688
+
689
+ ```bash
690
+ # Build in watch mode
691
+ npm run dev
692
+
693
+ # Your changes will be reflected after reloading addons in Wealthfolio
694
+ ```
695
+
696
+ ## 📚 API Reference
697
+
698
+ ### Context Methods
699
+
700
+ #### `sidebar.addItem(config)`
701
+
702
+ Add an item to the application sidebar.
703
+
704
+ **Parameters:**
705
+ - `config.id` (string): Unique identifier
706
+ - `config.label` (string): Display text
707
+ - `config.icon` (string | ReactNode): Icon name or component
708
+ - `config.route` (string): Navigation route
709
+ - `config.order` (number): Display order (optional)
710
+ - `config.onClick` (function): Click handler (optional)
711
+
712
+ **Returns:** `SidebarItemHandle` with `remove()` method
713
+
714
+ #### `router.add(route)`
715
+
716
+ Register a new route in the application.
717
+
718
+ **Parameters:**
719
+ - `route.path` (string): Route path pattern
720
+ - `route.component` (LazyExoticComponent): Lazy-loaded component
721
+
722
+ #### `onDisable(callback)`
723
+
724
+ Register cleanup callback for addon disable.
725
+
726
+ **Parameters:**
727
+ - `callback` (function): Cleanup function
728
+
729
+ ### Data Access APIs
730
+
731
+ All data access is performed through the context's `api` property:
732
+
733
+ ```typescript
734
+ const ctx = getAddonContext();
735
+
736
+ // Portfolio data
737
+ const holdings = await ctx.api.portfolio.getHoldings(accountId);
738
+ const accounts = await ctx.api.accounts.getAll();
739
+
740
+ // Market data
741
+ const quotes = await ctx.api.marketData.getQuoteHistory(symbol);
742
+ const profile = await ctx.api.marketData.getAssetProfile(assetId);
743
+
744
+ // Financial planning
745
+ const goals = await ctx.api.goals.getAll();
746
+ const limits = await ctx.api.financialPlanning.getContributionLimit();
747
+
748
+ // Settings
749
+ const settings = await ctx.api.getSettings();
750
+
751
+ // Logging and debugging
752
+ ctx.api.logger.info('Operation completed successfully');
753
+ ctx.api.logger.error('Error occurred:', error);
754
+ ctx.api.logger.debug('Debug info:', debugData);
755
+ ```
756
+
757
+ ### Available API Methods
758
+
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 |
775
+
776
+ ### Logger API
777
+
778
+ The SDK provides a comprehensive logging system:
779
+
780
+ ```typescript
781
+ const ctx = getAddonContext();
782
+
783
+ // Log levels: 'error', 'warn', 'info', 'debug'
784
+ ctx.api.logger.error('Critical error occurred', { error, context });
785
+ ctx.api.logger.warn('Warning message', additionalData);
786
+ ctx.api.logger.info('Information message');
787
+ ctx.api.logger.debug('Debug information', debugObject);
788
+
789
+ // Set log level (for development)
790
+ ctx.api.logger.setLevel('debug');
791
+
792
+ // Check if logging level is enabled
793
+ if (ctx.api.logger.isLevelEnabled('debug')) {
794
+ ctx.api.logger.debug('Expensive debug operation', expensiveData);
795
+ }
796
+ ```
797
+
798
+ ### Shared QueryClient Integration
799
+
800
+ The SDK provides access to Wealthfolio's shared React Query client for consistent data fetching and caching:
801
+
802
+ ```typescript
803
+ // Access the shared QueryClient instance
804
+ const sharedQueryClient = context.api.query.getClient();
805
+
806
+ // Wrap your components with QueryClientProvider
807
+ const MyAddonWrapper = () => {
808
+ return (
809
+ <QueryClientProvider client={sharedQueryClient}>
810
+ <MyAddonComponent />
811
+ </QueryClientProvider>
812
+ );
813
+ };
814
+
815
+ // Use React Query hooks in your components
816
+ function MyAddonComponent() {
817
+ const { data: accounts, isLoading } = useQuery({
818
+ queryKey: ['accounts'],
819
+ queryFn: () => ctx.api.accounts.getAll()
820
+ });
821
+
822
+ const { data: holdings } = useQuery({
823
+ queryKey: ['holdings', selectedAccountId],
824
+ queryFn: () => ctx.api.portfolio.getHoldings(selectedAccountId),
825
+ enabled: !!selectedAccountId
826
+ });
827
+
828
+ // Your component logic here
829
+ }
830
+ ```
831
+
832
+ **Benefits of Shared QueryClient:**
833
+ - **Consistent Caching**: Share cache with the main application
834
+ - **Performance**: Avoid duplicate API calls across addons
835
+ - **Synchronization**: Real-time updates when data changes
836
+ - **Memory Efficiency**: Single cache instance for all data
837
+
838
+ ## 🔄 Migration Guide
839
+
840
+ ### From v1.0.0 to v1.1.0
841
+
842
+ #### Context Access
843
+ ```typescript
844
+ // Before
845
+ import ctx from '@wealthfolio/addon-sdk';
846
+
847
+ // After (recommended)
848
+ import { getAddonContext } from '@wealthfolio/addon-sdk';
849
+ const ctx = getAddonContext();
850
+ ```
851
+
852
+ #### Type Imports
853
+ ```typescript
854
+ // Before
855
+ import type { AddonContext, AddonManifest } from '@wealthfolio/addon-sdk';
856
+
857
+ // After (more specific)
858
+ import type { AddonContext } from '@wealthfolio/addon-sdk';
859
+ import type { AddonManifest } from '@wealthfolio/addon-sdk/manifest';
860
+ ```
861
+
862
+ ## 👩‍💻 Development Guide
863
+
864
+ ### Setting Up Development Environment
865
+
866
+ #### 1. Create New Addon Project
867
+
868
+ ```bash
869
+ # Create a new directory for your addon
870
+ mkdir my-portfolio-addon
871
+ cd my-portfolio-addon
872
+
873
+ # Initialize package.json
874
+ npm init -y
875
+
876
+ # Install the SDK and peer dependencies
877
+ npm install @wealthfolio/addon-sdk
878
+ npm install --save-dev typescript @types/react vite @vitejs/plugin-react
879
+
880
+ # Install React (peer dependency)
881
+ npm install react react-dom
882
+ npm install --save-dev @types/react-dom
883
+ ```
884
+
885
+ #### 2. Project Setup
886
+
887
+ Create the essential configuration files:
888
+
889
+ **tsconfig.json**
890
+ ```json
891
+ {
892
+ "compilerOptions": {
893
+ "target": "ES2020",
894
+ "useDefineForClassFields": true,
895
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
896
+ "module": "ESNext",
897
+ "skipLibCheck": true,
898
+ "moduleResolution": "bundler",
899
+ "allowImportingTsExtensions": true,
900
+ "resolveJsonModule": true,
901
+ "isolatedModules": true,
902
+ "noEmit": true,
903
+ "jsx": "react-jsx",
904
+ "strict": true,
905
+ "noUnusedLocals": true,
906
+ "noUnusedParameters": true,
907
+ "noFallthroughCasesInSwitch": true,
908
+ "baseUrl": ".",
909
+ "paths": {
910
+ "@/*": ["src/*"]
911
+ }
912
+ },
913
+ "include": ["src"],
914
+ "references": [{ "path": "./tsconfig.node.json" }]
915
+ }
916
+ ```
917
+
918
+ **vite.config.ts**
919
+ ```typescript
920
+ import { defineConfig } from 'vite';
921
+ import react from '@vitejs/plugin-react';
922
+ import { resolve } from 'path';
923
+
924
+ export default defineConfig({
925
+ plugins: [react()],
926
+ build: {
927
+ lib: {
928
+ entry: resolve(__dirname, 'src/index.ts'),
929
+ name: 'MyPortfolioAddon',
930
+ fileName: 'addon',
931
+ formats: ['es']
932
+ },
933
+ rollupOptions: {
934
+ external: ['react', 'react-dom'],
935
+ output: {
936
+ globals: {
937
+ react: 'React',
938
+ 'react-dom': 'ReactDOM'
939
+ }
940
+ }
941
+ },
942
+ outDir: 'dist',
943
+ minify: 'terser',
944
+ sourcemap: true
945
+ },
946
+ resolve: {
947
+ alias: {
948
+ '@': resolve(__dirname, 'src')
949
+ }
950
+ }
951
+ });
952
+ ```
953
+
954
+ **package.json scripts**
955
+ ```json
956
+ {
957
+ "scripts": {
958
+ "dev": "vite build --watch",
959
+ "build": "vite build",
960
+ "type-check": "tsc --noEmit",
961
+ "package": "npm run build && zip -r addon.zip manifest.json dist/ assets/ README.md"
962
+ }
963
+ }
964
+ ```
965
+
966
+ #### 3. Development Workflow
967
+
968
+ ```bash
969
+ # Start development mode (watches for changes)
970
+ npm run dev
971
+
972
+ # Type checking
973
+ npm run type-check
974
+
975
+ # Build for production
976
+ npm run build
977
+
978
+ # Create distribution package
979
+ npm run package
980
+ ```
981
+
982
+ ### SDK Development (Contributing to the SDK)
983
+
984
+ If you want to contribute to the SDK itself:
985
+
986
+ #### 1. Clone and Setup
987
+
988
+ ```bash
989
+ # Clone the Wealthfolio repository
990
+ git clone https://github.com/afadil/wealthfolio.git
991
+ cd wealthfolio/packages/addon-sdk
992
+
993
+ # Install dependencies
994
+ pnpm install
995
+
996
+ # Build the SDK
997
+ pnpm build
998
+
999
+ # Watch for changes during development
1000
+ pnpm dev
1001
+ ```
1002
+
1003
+ #### 2. SDK Build Process
1004
+
1005
+ The SDK uses `tsup` for building with the following configuration:
1006
+
1007
+ ```typescript
1008
+ // tsup.config.ts
1009
+ export default defineConfig({
1010
+ entry: {
1011
+ index: 'src/index.ts',
1012
+ types: 'src/types.ts',
1013
+ permissions: 'src/permissions.ts',
1014
+ },
1015
+ 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
1020
+ target: 'es2020',
1021
+ external: ['react'], // Don't bundle React
1022
+ });
1023
+ ```
1024
+
1025
+ #### 3. Testing Your Changes
1026
+
1027
+ ```bash
1028
+ # Build the SDK
1029
+ pnpm build
1030
+
1031
+ # Link for local testing
1032
+ npm link
1033
+
1034
+ # In your addon project
1035
+ npm link @wealthfolio/addon-sdk
1036
+
1037
+ # Test your changes
1038
+ npm run dev
1039
+ ```
1040
+
1041
+ #### 4. Publishing to NPM
1042
+
1043
+ The SDK is published to the npm registry. For maintainers:
1044
+
1045
+ ```bash
1046
+ # Ensure you're logged in to npm
1047
+ npm login
1048
+
1049
+ # Update version in package.json
1050
+ npm version patch # or minor/major
1051
+
1052
+ # Build and publish
1053
+ npm run build
1054
+ npm publish
1055
+
1056
+ # Or for beta releases
1057
+ npm publish --tag beta
1058
+ ```
1059
+
1060
+ ### Debugging Tips
1061
+
1062
+ #### 1. Enable Debug Logging
1063
+
1064
+ ```typescript
1065
+ // In your addon
1066
+ const ctx = getAddonContext();
1067
+ ctx.api.logger.setLevel('debug');
1068
+ ctx.api.logger.debug('Debug information:', data);
1069
+ ```
1070
+
1071
+ #### 2. Development Console
1072
+
1073
+ Access the browser's developer console for debugging:
1074
+ - Open Wealthfolio
1075
+ - Press F12 or right-click → Inspect
1076
+ - Check Console tab for addon logs
1077
+ - Use Network tab to monitor API calls
1078
+
1079
+ #### 3. Hot Reloading
1080
+
1081
+ During development, enable hot reloading:
1082
+
1083
+ ```typescript
1084
+ // Add to your addon's main file
1085
+ if (process.env.NODE_ENV === 'development') {
1086
+ // Enable hot module replacement
1087
+ if (module.hot) {
1088
+ module.hot.accept();
1089
+ }
1090
+ }
1091
+ ```
1092
+
1093
+ ### Common Development Patterns
1094
+
1095
+ #### 1. Error Handling
1096
+
1097
+ ```typescript
1098
+ import { getAddonContext } from '@wealthfolio/addon-sdk';
1099
+
1100
+ async function fetchPortfolioData() {
1101
+ const ctx = getAddonContext();
1102
+
1103
+ try {
1104
+ // Get all accounts first, then holdings for each
1105
+ const accounts = await ctx.api.accounts.getAll();
1106
+ const holdings = await Promise.all(
1107
+ accounts.map(account => ctx.api.portfolio.getHoldings(account.id))
1108
+ ).then(results => results.flat());
1109
+ return holdings;
1110
+ } catch (error) {
1111
+ ctx.api.logger.error('Failed to fetch holdings:', error);
1112
+
1113
+ // Handle different error types
1114
+ if (error.code === 'PERMISSION_DENIED') {
1115
+ // Show permission error to user
1116
+ } else if (error.code === 'NETWORK_ERROR') {
1117
+ // Handle network issues
1118
+ }
1119
+
1120
+ throw error;
1121
+ }
1122
+ }
1123
+ ```
1124
+
1125
+ #### 2. Resource Cleanup
1126
+
1127
+ ```typescript
1128
+ export default function enable(context: AddonContext) {
1129
+ const subscriptions: (() => void)[] = [];
1130
+
1131
+ // Add event listeners
1132
+ const unsubscribe = context.events.subscribe('portfolio.updated', handler);
1133
+ subscriptions.push(unsubscribe);
1134
+
1135
+ // Cleanup on disable
1136
+ context.onDisable(() => {
1137
+ subscriptions.forEach(unsub => unsub());
1138
+ context.api.logger.info('Addon cleaned up successfully');
1139
+ });
1140
+ }
1141
+ ```
1142
+
1143
+ #### 3. State Management
1144
+
1145
+ ```typescript
1146
+ // Use React state for component-level state
1147
+ const [loading, setLoading] = useState(false);
1148
+ const [data, setData] = useState<PortfolioData | null>(null);
1149
+
1150
+ // Use context API for global addon state
1151
+ const AddonStateContext = createContext<AddonState | null>(null);
1152
+
1153
+ export function AddonProvider({ children }: { children: ReactNode }) {
1154
+ const [state, setState] = useState<AddonState>(initialState);
1155
+
1156
+ return (
1157
+ <AddonStateContext.Provider value={{ state, setState }}>
1158
+ {children}
1159
+ </AddonStateContext.Provider>
1160
+ );
1161
+ }
1162
+ ```
1163
+
1164
+ ### Performance Best Practices
1165
+
1166
+ #### 1. Lazy Loading
1167
+
1168
+ ```typescript
1169
+ // Lazy load heavy components
1170
+ const HeavyChart = lazy(() => import('./components/HeavyChart'));
1171
+
1172
+ // Use React.Suspense
1173
+ <Suspense fallback={<div>Loading chart...</div>}>
1174
+ <HeavyChart data={chartData} />
1175
+ </Suspense>
1176
+ ```
1177
+
1178
+ #### 2. Efficient Data Fetching
1179
+
1180
+ ```typescript
1181
+ // Use React Query or SWR for caching
1182
+ import { useQuery } from 'react-query';
1183
+
1184
+ function usePortfolioData(accountId: string) {
1185
+ return useQuery(
1186
+ ['portfolio', accountId],
1187
+ () => ctx.api.portfolio.getHoldings(accountId),
1188
+ {
1189
+ staleTime: 5 * 60 * 1000, // 5 minutes
1190
+ cacheTime: 10 * 60 * 1000, // 10 minutes
1191
+ }
1192
+ );
1193
+ }
1194
+ ```
1195
+
1196
+ #### 3. Bundle Optimization
1197
+
1198
+ ```typescript
1199
+ // vite.config.ts - optimize chunks
1200
+ export default defineConfig({
1201
+ build: {
1202
+ rollupOptions: {
1203
+ output: {
1204
+ manualChunks: {
1205
+ vendor: ['react', 'react-dom'],
1206
+ charts: ['chart.js', 'd3'],
1207
+ }
1208
+ }
1209
+ }
1210
+ }
1211
+ });
1212
+ ```
1213
+
1214
+ ## 🤝 Contributing
1215
+
1216
+ We welcome contributions to improve the addon SDK!
1217
+
1218
+ ### Development Setup
1219
+
1220
+ 1. **Fork and Clone**
1221
+ ```bash
1222
+ git clone https://github.com/yourusername/wealthfolio.git
1223
+ cd wealthfolio/packages/addon-sdk
1224
+ ```
1225
+
1226
+ 2. **Install Dependencies**
1227
+ ```bash
1228
+ pnpm install
1229
+ ```
1230
+
1231
+ 3. **Make Changes**
1232
+ ```bash
1233
+ # Start development mode
1234
+ pnpm dev
1235
+
1236
+ # Run type checking
1237
+ pnpm lint
1238
+
1239
+ # Build for testing
1240
+ pnpm build
1241
+ ```
1242
+
1243
+ 4. **Testing Your Changes**
1244
+ ```bash
1245
+ # Link the SDK locally for testing
1246
+ npm link
1247
+
1248
+ # In your test addon project
1249
+ npm link @wealthfolio/addon-sdk
1250
+ ```
1251
+
1252
+ 5. **Submit Changes**
1253
+ - Create a feature branch
1254
+ - Make your changes with tests
1255
+ - Update documentation
1256
+ - Submit a pull request
1257
+
1258
+ ### Contribution Guidelines
1259
+
1260
+ - **Code Style**: Follow TypeScript best practices
1261
+ - **Testing**: Add tests for new features
1262
+ - **Documentation**: Update README and JSDoc comments
1263
+ - **Versioning**: Follow semantic versioning
1264
+ - **Backwards Compatibility**: Maintain API compatibility when possible
1265
+
1266
+ ## 📋 NPM Registry Information
1267
+
1268
+ ### Package Details
1269
+
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) |
1277
+
1278
+ ### Version History
1279
+
1280
+ We follow [Semantic Versioning](https://semver.org/) (SemVer):
1281
+
1282
+ - **MAJOR**: Breaking changes to public API
1283
+ - **MINOR**: New features, backwards compatible
1284
+ - **PATCH**: Bug fixes, backwards compatible
1285
+
1286
+ #### Version Compatibility
1287
+
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 |
1292
+
1293
+ ### Installation from Registry
1294
+
1295
+ #### Stable Release
1296
+ ```bash
1297
+ # Latest stable version
1298
+ npm install @wealthfolio/addon-sdk
1299
+
1300
+ # Specific version
1301
+ npm install @wealthfolio/addon-sdk@1.0.0
1302
+
1303
+ # Version range
1304
+ npm install @wealthfolio/addon-sdk@^1.0.0
1305
+ ```
1306
+
1307
+ #### Beta/Preview Releases
1308
+ ```bash
1309
+ # Latest beta version
1310
+ npm install @wealthfolio/addon-sdk@beta
1311
+
1312
+ # Specific beta version
1313
+ npm install @wealthfolio/addon-sdk@1.1.0-beta.1
1314
+ ```
1315
+
1316
+ #### Development Version
1317
+ ```bash
1318
+ # Install directly from GitHub
1319
+ npm install github:afadil/wealthfolio#main
1320
+
1321
+ # Or from a specific branch/commit
1322
+ npm install github:afladil/wealthfolio#wealthfolio-addons
1323
+ ```
1324
+
1325
+ ### Package Information Commands
1326
+
1327
+ ```bash
1328
+ # View package information
1329
+ npm info @wealthfolio/addon-sdk
1330
+
1331
+ # View all available versions
1332
+ npm view @wealthfolio/addon-sdk versions --json
1333
+
1334
+ # View latest version
1335
+ npm view @wealthfolio/addon-sdk version
1336
+
1337
+ # View package dependencies
1338
+ npm view @wealthfolio/addon-sdk dependencies
1339
+
1340
+ # Check for outdated packages
1341
+ npm outdated @wealthfolio/addon-sdk
1342
+ ```
1343
+
1344
+ ### Publishing Information (For Maintainers)
1345
+
1346
+ #### Prerequisites
1347
+ ```bash
1348
+ # Login to npm (maintainers only)
1349
+ npm login
1350
+
1351
+ # Verify login
1352
+ npm whoami
1353
+
1354
+ # Check publishing permissions
1355
+ npm access list packages @wealthfolio
1356
+ ```
1357
+
1358
+ #### Release Process
1359
+ ```bash
1360
+ # 1. Update version
1361
+ npm version patch # or minor/major
1362
+
1363
+ # 2. Build the package
1364
+ npm run build
1365
+
1366
+ # 3. Test the build
1367
+ npm pack
1368
+ tar -tf wealthfolio-addon-sdk-*.tgz
1369
+
1370
+ # 4. Publish to npm
1371
+ npm publish
1372
+
1373
+ # 5. For beta releases
1374
+ npm publish --tag beta
1375
+
1376
+ # 6. Tag the release
1377
+ git tag v$(node -p "require('./package.json').version")
1378
+ git push --tags
1379
+ ```
1380
+
1381
+ #### Distribution Tags
1382
+
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` |
1389
+
1390
+ #### Package Metrics
1391
+
1392
+ 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)
1396
+
1397
+ ### Security
1398
+
1399
+ #### Vulnerability Scanning
1400
+ ```bash
1401
+ # Check for vulnerabilities
1402
+ npm audit
1403
+
1404
+ # Fix vulnerabilities
1405
+ npm audit fix
1406
+
1407
+ # View security advisories
1408
+ npm audit --audit-level=moderate
1409
+ ```
1410
+
1411
+ #### Package Integrity
1412
+ ```bash
1413
+ # Verify package integrity
1414
+ npm pack --dry-run
1415
+
1416
+ # Check package contents
1417
+ npm pack && tar -tf *.tgz
1418
+ ```
1419
+
1420
+ ### Support and Maintenance
1421
+
1422
+ #### Package Support Policy
1423
+
1424
+ - **Latest Major Version**: Full support with new features and bug fixes
1425
+ - **Previous Major Version**: Security fixes and critical bug fixes for 12 months
1426
+ - **Older Versions**: Community support only
1427
+
1428
+ #### Maintenance Schedule
1429
+
1430
+ - **Regular Updates**: Monthly minor releases
1431
+ - **Security Patches**: As needed (within 48 hours for critical issues)
1432
+ - **Major Releases**: Quarterly or as needed for breaking changes
1433
+
1434
+ #### Getting Help
1435
+
1436
+ 1. **Documentation**: Check this README and [docs](https://docs.wealthfolio.app/addons)
1437
+ 2. **Issues**: [GitHub Issues](https://github.com/afadil/wealthfolio/issues)
1438
+ 3. **Discussions**: [GitHub Discussions](https://github.com/afadil/wealthfolio/discussions)
1439
+ 4. **Discord**: [Community Discord](https://discord.gg/wealthfolio)
1440
+ 5. **Email**: [support@wealthfolio.app](mailto:support@wealthfolio.app)
1441
+
1442
+ ## 📄 License
1443
+
1444
+ MIT - see [LICENSE](LICENSE) for details.
1445
+
1446
+ ## 🔗 Links
1447
+
1448
+ - [Wealthfolio Homepage](https://wealthfolio.app)
1449
+ - [Addon Gallery](https://wealthfolio.app/addons)
1450
+ - [Documentation](https://docs.wealthfolio.app/addons)
1451
+ - [GitHub Repository](https://github.com/afadil/wealthfolio)
1452
+ - [Issue Tracker](https://github.com/afadil/wealthfolio/issues)
1453
+
1454
+ ## 💬 Support
1455
+
1456
+ - [Discord Community](https://discord.gg/wealthfolio)
1457
+ - [GitHub Discussions](https://github.com/afadil/wealthfolio/discussions)
1458
+ - [Email Support](mailto:support@wealthfolio.app)
1459
+
1460
+ ## 🔧 Troubleshooting
1461
+
1462
+ ### Common Issues
1463
+
1464
+ #### 1. Module Resolution Errors
1465
+
1466
+ **Error**: `Cannot resolve module '@wealthfolio/addon-sdk'`
1467
+
1468
+ **Solutions**:
1469
+ ```bash
1470
+ # Clear npm cache
1471
+ npm cache clean --force
1472
+
1473
+ # Delete node_modules and reinstall
1474
+ rm -rf node_modules package-lock.json
1475
+ npm install
1476
+
1477
+ # Check Node.js version (requires >= 18.0.0)
1478
+ node --version
1479
+ ```
1480
+
1481
+ #### 2. TypeScript Compilation Errors
1482
+
1483
+ **Error**: `Cannot find type definitions`
1484
+
1485
+ **Solutions**:
1486
+ ```typescript
1487
+ // Ensure proper TypeScript configuration
1488
+ {
1489
+ "compilerOptions": {
1490
+ "moduleResolution": "bundler", // or "node"
1491
+ "allowSyntheticDefaultImports": true,
1492
+ "esModuleInterop": true
1493
+ }
1494
+ }
1495
+
1496
+ // Use explicit type imports
1497
+ import type { AddonContext } from '@wealthfolio/addon-sdk';
1498
+ ```
1499
+
1500
+ #### 3. React Peer Dependency Warnings
1501
+
1502
+ **Error**: `React version mismatch`
1503
+
1504
+ **Solutions**:
1505
+ ```bash
1506
+ # Install correct React version
1507
+ npm install react@^18.0.0 react-dom@^18.0.0
1508
+
1509
+ # Check installed versions
1510
+ npm list react react-dom
1511
+ ```
1512
+
1513
+ #### 4. Build Errors
1514
+
1515
+ **Error**: `Vite build fails with external dependencies`
1516
+
1517
+ **Solutions**:
1518
+ ```typescript
1519
+ // vite.config.ts
1520
+ export default defineConfig({
1521
+ build: {
1522
+ rollupOptions: {
1523
+ external: ['react', 'react-dom', '@wealthfolio/addon-sdk']
1524
+ }
1525
+ }
1526
+ });
1527
+ ```
1528
+
1529
+ #### 5. Permission Denied Errors
1530
+
1531
+ **Error**: `Permission denied for API call`
1532
+
1533
+ **Solutions**:
1534
+ ```json
1535
+ // Add required permissions to manifest.json
1536
+ {
1537
+ "permissions": [
1538
+ {
1539
+ "category": "portfolio",
1540
+ "functions": ["holdings"],
1541
+ "purpose": "Access portfolio data for analytics"
1542
+ }
1543
+ ]
1544
+ }
1545
+ ```
1546
+
1547
+ #### 6. Context Not Available
1548
+
1549
+ **Error**: `getAddonContext() returns undefined`
1550
+
1551
+ **Solutions**:
1552
+ ```typescript
1553
+ // Ensure you're calling it within addon context
1554
+ function MyComponent() {
1555
+ useEffect(() => {
1556
+ // Call context inside useEffect or event handlers
1557
+ const ctx = getAddonContext();
1558
+ // ... use context
1559
+ }, []);
1560
+ }
1561
+
1562
+ // Don't call at module level
1563
+ // const ctx = getAddonContext(); // ❌ Wrong
1564
+ ```
1565
+
1566
+ ### Development Environment Issues
1567
+
1568
+ #### 1. Hot Reload Not Working
1569
+
1570
+ ```bash
1571
+ # Ensure dev mode is enabled
1572
+ npm run dev
1573
+
1574
+ # Check if files are being watched
1575
+ ls -la dist/ # Should update when you save files
1576
+ ```
1577
+
1578
+ #### 2. Addon Not Loading in Wealthfolio
1579
+
1580
+ 1. Check the addon package structure:
1581
+ ```
1582
+ addon.zip
1583
+ ├── manifest.json ✓
1584
+ ├── dist/
1585
+ │ └── addon.js ✓
1586
+ └── assets/ (optional)
1587
+ ```
1588
+
1589
+ 2. Validate manifest.json:
1590
+ ```bash
1591
+ # Check JSON syntax
1592
+ cat manifest.json | jq .
1593
+ ```
1594
+
1595
+ 3. Check Wealthfolio logs:
1596
+ - Open Developer Tools (F12)
1597
+ - Look for addon-related errors
1598
+ - Check Network tab for failed requests
1599
+
1600
+ #### 3. API Calls Failing
1601
+
1602
+ ```typescript
1603
+ // Add error handling and logging
1604
+ try {
1605
+ const accounts = await ctx.api.accounts.getAll();
1606
+ const data = await ctx.api.portfolio.getHoldings(accounts[0]?.id);
1607
+ ctx.api.logger.info('Data loaded successfully', { count: data.length });
1608
+ } catch (error) {
1609
+ ctx.api.logger.error('API call failed', {
1610
+ error: error.message,
1611
+ stack: error.stack,
1612
+ timestamp: new Date().toISOString()
1613
+ });
1614
+ }
1615
+ ```
1616
+
1617
+ ### Performance Issues
1618
+
1619
+ #### 1. Slow Addon Loading
1620
+
1621
+ ```typescript
1622
+ // Use code splitting and lazy loading
1623
+ const HeavyComponent = lazy(() => import('./HeavyComponent'));
1624
+
1625
+ // Reduce bundle size
1626
+ // vite.config.ts
1627
+ export default defineConfig({
1628
+ build: {
1629
+ rollupOptions: {
1630
+ output: {
1631
+ manualChunks: {
1632
+ vendor: ['react', 'react-dom'],
1633
+ utils: ['lodash', 'date-fns']
1634
+ }
1635
+ }
1636
+ }
1637
+ }
1638
+ });
1639
+ ```
1640
+
1641
+ #### 2. Memory Leaks
1642
+
1643
+ ```typescript
1644
+ // Proper cleanup in useEffect
1645
+ useEffect(() => {
1646
+ const subscription = ctx.events.subscribe('update', handler);
1647
+
1648
+ return () => {
1649
+ subscription.unsubscribe(); // ✓ Clean up
1650
+ };
1651
+ }, []);
1652
+
1653
+ // Cleanup on addon disable
1654
+ context.onDisable(() => {
1655
+ // Clean up all resources
1656
+ clearInterval(intervalId);
1657
+ subscription.unsubscribe();
1658
+ });
1659
+ ```
1660
+
1661
+ ### Getting Help
1662
+
1663
+ If you're still experiencing issues:
1664
+
1665
+ 1. **Check Version Compatibility**:
1666
+ ```bash
1667
+ npm list @wealthfolio/addon-sdk
1668
+ ```
1669
+
1670
+ 2. **Create Minimal Reproduction**:
1671
+ - Create a simple addon that reproduces the issue
1672
+ - Share the code and error logs
1673
+
1674
+ 3. **Search Existing Issues**:
1675
+ - Check [GitHub Issues](https://github.com/afadil/wealthfolio/issues)
1676
+ - Look for similar problems and solutions
1677
+
1678
+ 4. **Provide Complete Information**:
1679
+ - SDK version
1680
+ - Node.js version
1681
+ - Operating system
1682
+ - Error messages with stack traces
1683
+ - Minimal reproduction steps