@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/CHANGELOG.md +32 -0
- package/LICENSE +21 -0
- package/README.md +1683 -0
- package/dist/chunk-3OBZP7TN.mjs +238 -0
- package/dist/chunk-3OBZP7TN.mjs.map +1 -0
- package/dist/index.d.mts +279 -0
- package/dist/index.mjs +234 -0
- package/dist/index.mjs.map +1 -0
- package/dist/permissions.d.mts +99 -0
- package/dist/permissions.mjs +29 -0
- package/dist/permissions.mjs.map +1 -0
- package/dist/types-CxtChQdm.d.mts +1137 -0
- package/dist/types.d.mts +2 -0
- package/dist/types.mjs +1 -0
- package/dist/types.mjs.map +1 -0
- package/package.json +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,1683 @@
|
|
|
1
|
+
# @wealthfolio/addon-sdk
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@wealthfolio/addon-sdk)
|
|
4
|
+
[](https://www.npmjs.com/package/@wealthfolio/addon-sdk)
|
|
5
|
+
[](https://github.com/afadil/wealthfolio/blob/main/LICENSE)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](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
|