@wealthfolio/addon-sdk 3.5.1 → 3.6.1

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 CHANGED
@@ -4,6 +4,221 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
4
4
  and this project adheres to
5
5
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [3.6.1] - 2026-07-06
8
+
9
+ Follow-up to the v3.6 sandbox release: sidebar icons are now a typed, curated
10
+ set. See the
11
+ [v3.5 → v3.6 migration guide](../../docs/addons/addon-migration-guide-v3.5-to-v3.6.md).
12
+
13
+ ### Breaking
14
+
15
+ - **`SidebarItemConfig.icon`** is now a typed `AddonIconName` (was `string`).
16
+ Names outside the curated set fail type-checking; unknown or omitted names
17
+ render a neutral `caret-right` fallback at runtime.
18
+
19
+ ### Added
20
+
21
+ - `AddonIconName` type and `ADDON_ICON_NAMES` — the curated set of duotone
22
+ Phosphor sidebar icon names, exported from the package entry point.
23
+
24
+ ## [3.6.0] - 2026-07-04
25
+
26
+ Addons now run in an isolated sandbox iframe. This is a **breaking** release for
27
+ addons that register routes. See the
28
+ [v3.5 → v3.6 migration guide](../../docs/addons/addon-migration-guide-v3.5-to-v3.6.md).
29
+
30
+ ### Breaking
31
+
32
+ - **Route registration**: `RouteConfig.component` (a lazy React component) is
33
+ replaced by `RouteConfig.render(ctx)`, which mounts into a host-provided
34
+ `ctx.root: HTMLElement`. New types: `AddonRouteRenderer`,
35
+ `AddonRouteRenderContext`, `AddonRouteLocation`.
36
+ - **React exports removed**: the SDK no longer re-exports `React` / `ReactDOM`
37
+ from host globals. Import from `react` / `react-dom/client`; mark them
38
+ `external` in your build.
39
+ - **`SidebarItemConfig`**: `icon` is now a host-supported icon name (`string`)
40
+ instead of a `React.ReactNode`, and the `onClick` handler was removed — use
41
+ `route`.
42
+ - **React** guaranteed version bumped 19.1.1 → 19.2.4.
43
+
44
+ ### Added
45
+
46
+ - `HOST_DEPENDENCIES` export — the versioned packages the sandbox provides
47
+ (react, react-dom, @tanstack/react-query, @wealthfolio/ui, date-fns,
48
+ lucide-react, recharts).
49
+ - Brokered networking: `ctx.api.network.request()` with manifest-declared
50
+ `network.allowedHosts` and bearer auth resolved from scoped secret storage
51
+ (`NetworkAPI`, `NetworkRequest`, `NetworkResponse`, `NetworkAuth`).
52
+ - Manifest fields: `minWealthfolioVersion`, `hostDependencies`, `network`,
53
+ `sha256`. `AddonHostDependencies` type.
54
+ - Addon SDK tax fields on activity/data types (#1188).
55
+
56
+ ## [3.5.1] - 2026-06-09
57
+
58
+ Also includes the unpublished 3.4.0 and 3.5.0.
59
+
60
+ ### Added
61
+
62
+ - `PerformanceResult` — a comprehensive performance model replacing
63
+ `PerformanceMetrics`, with supporting types `PerformanceScopeDescriptor`,
64
+ `PerformancePeriod`, `ReturnMethod`, `PerformanceReturns`,
65
+ `PerformanceAttribution`, `PerformanceRisk`, `PerformanceDataQuality`.
66
+ - Goal enrichment: `GoalType`, `GoalLifecycle`, `GoalHealth` types; `Goal` gains
67
+ `goalType`, `statusHealth`, `priority`, `coverImageKey`, `currency`, dates,
68
+ valuation summaries, and timestamps. `GoalProgress` gains `goalId`,
69
+ `statusHealth`, `targetDate`.
70
+ - Asset resolution: `AssetResolutionInput` (`quoteCcy`, `instrumentType`,
71
+ `providerId`, `providerSymbol`); an `asset` field on `ActivityCreate` /
72
+ `ActivityUpdate`; `assetId` on `ActivityImport`.
73
+ - Provider-aware market data: `providerId` / `providerSymbol` plus canonical
74
+ `canonicalSymbol` / `canonicalExchangeMic` on `SymbolSearchResult`;
75
+ `DividendEvent` type and `FetchDividendsOptions`.
76
+ - `AccountType` gains `CREDIT_CARD`; `Settings.defaultReturnMetric`
77
+ (`'twr' | 'irr' | 'valueReturn'`).
78
+ - Base-currency fields on `AccountValuation` (`totalValueBase`, `costBasisBase`,
79
+ `netContributionBase`, …) and provider/quote fields on `SnapshotHoldingInput`
80
+ / `SnapshotPositionInput`.
81
+
82
+ ### Changed
83
+
84
+ - Performance API now returns the new `Result` types: `calculateHistory()` (with
85
+ `startDate` / `endDate` now optional) and `calculateSummary()` return
86
+ `PerformanceResult`; `calculateAccountsSimple()`, `AccountSummaryView`, and
87
+ `AccountGroup` use `SimplePerformanceResult`.
88
+ - `fetchDividends()` accepts an optional `FetchDividendsOptions` and returns
89
+ `DividendEvent[]` (was `YahooDividend[]`).
90
+ - `backupDatabase()` returns `{ filename: string }` (dropped the
91
+ `data: Uint8Array` payload).
92
+
93
+ ### Deprecated
94
+
95
+ - `PerformanceMetrics` / `SimplePerformanceMetrics` (aliases for their `Result`
96
+ counterparts), `SymbolInput` (use `AssetResolutionInput`), and the `symbol`
97
+ field on `ActivityCreate` / `ActivityUpdate` (use `asset`).
98
+
99
+ ### Removed
100
+
101
+ - `YahooDividend` (replaced by `DividendEvent`); `dayGainLossAmount` and
102
+ `dayReturnPercentModDietz` from `SimplePerformanceResult`.
103
+
104
+ ## [3.3.0] - 2026-05-01
105
+
106
+ ### Added
107
+
108
+ - `GoalsAPI.getFunding(goalId)` and `saveFunding(goalId, rules)`, with matching
109
+ `getFunding` / `saveFunding` functions under the renamed `financial-planning`
110
+ permission.
111
+ - `GoalAllocation.taxBucket`.
112
+
113
+ ### Changed
114
+
115
+ - `Goal.isAchieved` replaced by `Goal.statusLifecycle`
116
+ (`'active' | 'achieved' | 'archived'`).
117
+ - `GoalAllocation.percentAllocation` renamed to `sharePercent`.
118
+ - `goals` permission category renamed to `financial-planning`.
119
+
120
+ ### Deprecated
121
+
122
+ - `GoalsAPI.updateAllocations()` (use `saveFunding`) and `getAllocations()` (use
123
+ `getAll()` + `getFunding(goalId)`).
124
+
125
+ ## [3.2.0] - 2026-04-02
126
+
127
+ ### Changed
128
+
129
+ - `ActivitiesAPI.checkImport()` dropped its `accountId` parameter — now
130
+ `checkImport(activities)`, a read-only preview.
131
+ - `ActivitiesAPI.getImportMapping()` accepts an optional `contextKind` (default
132
+ `'ACTIVITY'`).
133
+ - `ImportMappingData.fieldMappings` widened to
134
+ `Record<string, string | string[]>`.
135
+ - `PerformanceMetrics.periodReturn` is now nullable (`number | null`).
136
+
137
+ ## [3.1.1] - 2026-03-14
138
+
139
+ A large release that overhauls the asset and activity data models (also includes
140
+ the unpublished 3.0.1–3.1.0). **Breaking** for addons that read or write assets,
141
+ activities, or quotes.
142
+
143
+ ### Breaking
144
+
145
+ - **Asset model** is now keyed by an opaque UUID instead of a symbol. `Asset` is
146
+ redesigned with `kind` (`AssetKind`), `displayCode`, `quoteMode`, `quoteCcy`,
147
+ `instrumentType`, `instrumentSymbol`, `instrumentKey`, `providerConfig`, …;
148
+ removed `symbol`, `isin`, `assetClass`, `countries`, `sectors`, `dataSource`,
149
+ and more.
150
+ - **Activity model** redesigned: `type` → `activityType`, `date` →
151
+ `activityDate`, `assetId` now optional, monetary/quantity fields are decimal
152
+ strings; new lifecycle fields (`subtype`, `status`, `settlementDate`,
153
+ `idempotencyKey`, `importRunId`, `metadata`, …). `ActivityType` is now a
154
+ closed set of 14 (removed `ADD_HOLDING` / `REMOVE_HOLDING`; added `SPLIT`,
155
+ `CREDIT`, `ADJUSTMENT`, `UNKNOWN`).
156
+ - **Quotes / assets APIs** now take `assetId` instead of `symbol`:
157
+ `QuotesAPI.update()` / `getHistory()`, `Quote` / `QuoteUpdate`,
158
+ `MarketDataAPI.sync(assetIds)`. `AssetsAPI.updateDataSource()` →
159
+ `updateQuoteMode(assetId, quoteMode)`. `searchTicker()` returns
160
+ `SymbolSearchResult` (renamed from `QuoteSummary`).
161
+ - `ActivitiesAPI.import()` returns `ImportActivitiesResult` (was
162
+ `ActivityImport[]`); `SettingsAPI.update()` takes `Partial<Settings>`;
163
+ `IncomeSummary.bySymbol` → `byAsset`.
164
+
165
+ ### Added
166
+
167
+ - New APIs: `SnapshotsAPI` (holdings snapshots) and `ToastAPI` (`success` /
168
+ `error` / `warning` / `info`), each with a matching permission category;
169
+ `MarketDataAPI.fetchDividends(symbol)`.
170
+ - Activity taxonomy: `ActivityStatus`, `ACTIVITY_SUBTYPES` / `ActivitySubtype`,
171
+ `AssetKind`, `QuoteMode` constants; helpers `getEffectiveType()` and
172
+ `hasUserOverride()`.
173
+ - `calculateGoalProgress()` helper (new `goal-progress` module).
174
+ - Import/sync types (`ImportRun*`, `BrokerSyncState`, `SyncStatus`), snapshot
175
+ types (`Snapshot*`), classification types (`AssetClassifications`,
176
+ `TaxonomyCategory`, `CategoryWithWeight`), and `QueryKeys.SNAPSHOTS`.
177
+ - React peer dependency bumped to `^19.2.4`.
178
+
179
+ ### Removed
180
+
181
+ - `AssetProfile`, `MarketData`, `Sector`, `Country`, `SettingsContextType`;
182
+ `ActivityType` values `ADD_HOLDING` / `REMOVE_HOLDING`.
183
+
184
+ ## [3.0.0] - 2026-02-24
185
+
186
+ ### Breaking
187
+
188
+ - React peer dependency updated to 19 (`ReactVersion` `18.3.1` → `19.1.1`).
189
+ - `ActivitiesAPI.search()` now takes typed `ActivitySearchFilters` and
190
+ `ActivitySort` (was `unknown`).
191
+ - `ActivitiesAPI.saveMany()` takes `ActivityBulkMutationRequest` and returns
192
+ `ActivityBulkMutationResult` (was `ActivityUpdate[]` → `Activity[]`).
193
+
194
+ ### Added
195
+
196
+ - Bulk-mutation types: `ActivityBulkMutationRequest`,
197
+ `ActivityBulkMutationResult`, `ActivityBulkMutationError`,
198
+ `ActivityBulkIdentifierMapping`; exported `ActivitySearchFilters` and
199
+ `ActivitySort`.
200
+ - `Activity.assetDataSource`, `ActivityCreate.id` / `assetDataSource`,
201
+ `UpdateAssetProfile.symbolMapping`.
202
+
203
+ ### Changed
204
+
205
+ - Build now emits `.js` / `.d.ts` (was `.mjs` / `.d.mts`); the types entry moved
206
+ to `dist/src/index.d.ts`.
207
+
208
+ ## [2.0.0] - 2025-11-22
209
+
210
+ ### Breaking
211
+
212
+ - Package now ships as an ES module (`"type": "module"`).
213
+
214
+ ### Changed
215
+
216
+ - Tightened types: loose `any` parameters/returns replaced with `unknown` across
217
+ `AccountsAPI.create()`, `ActivitiesAPI.search()`, `GoalsAPI.create()`,
218
+ `FilesAPI.openSaveDialog()`, `QueryAPI.getClient()`, `isAddonManifest()`, and
219
+ `RouteConfig.component`.
220
+ - `DateRange` and `TrackedItem` changed from type aliases to interfaces.
221
+
7
222
  ## [1.0.0] - 2024-12-19
8
223
 
9
224
  ### Added
package/README.md CHANGED
@@ -70,9 +70,13 @@ mkdir src && touch src/index.ts
70
70
 
71
71
  ```typescript
72
72
  // src/index.ts
73
+ import { createRoot, type Root } from 'react-dom/client';
73
74
  import { getAddonContext, type AddonContext } from '@wealthfolio/addon-sdk';
75
+ import { MyComponent } from './MyComponent';
74
76
 
75
77
  export default function enable(context: AddonContext) {
78
+ let root: Root | null = null;
79
+
76
80
  // Add navigation item
77
81
  const navItem = context.sidebar.addItem({
78
82
  id: 'my-addon',
@@ -84,7 +88,10 @@ export default function enable(context: AddonContext) {
84
88
  // Register route
85
89
  context.router.add({
86
90
  path: '/addons/my-addon',
87
- component: () => import('./MyComponent'),
91
+ render: ({ root: routeRoot }) => {
92
+ root ??= createRoot(routeRoot);
93
+ root.render(<MyComponent ctx={context} />);
94
+ },
88
95
  });
89
96
 
90
97
  // Log activation
@@ -92,6 +99,8 @@ export default function enable(context: AddonContext) {
92
99
 
93
100
  // Cleanup on disable
94
101
  context.onDisable(() => {
102
+ root?.unmount();
103
+ root = null;
95
104
  navItem.remove();
96
105
  context.api.logger.info('My addon deactivated');
97
106
  });
@@ -235,10 +244,9 @@ example:
235
244
 
236
245
  ```typescript
237
246
  // src/addon.tsx
238
- import React from 'react';
247
+ import { createRoot, type Root } from 'react-dom/client';
239
248
  import { QueryClientProvider } from '@tanstack/react-query';
240
249
  import type { AddonContext, AddonEnableFunction } from '@wealthfolio/addon-sdk';
241
- import { Icons } from '@wealthfolio/ui';
242
250
  import FeesPage from './pages/fees-page';
243
251
 
244
252
  // Main addon component
@@ -256,13 +264,14 @@ const enable: AddonEnableFunction = (context) => {
256
264
 
257
265
  // Store references to items for cleanup
258
266
  const addedItems: Array<{ remove: () => void }> = [];
267
+ let root: Root | null = null;
259
268
 
260
269
  try {
261
- // Add sidebar navigation item with icon from UI library
270
+ // Add sidebar navigation item with a host-supported icon token
262
271
  const sidebarItem = context.sidebar.addItem({
263
272
  id: 'investment-fees-tracker',
264
273
  label: 'Fee Tracker',
265
- icon: <Icons.Invoice className="h-5 w-5" />,
274
+ icon: 'receipt',
266
275
  route: '/addons/investment-fees-tracker',
267
276
  order: 200
268
277
  });
@@ -280,12 +289,13 @@ const enable: AddonEnableFunction = (context) => {
280
289
  );
281
290
  };
282
291
 
283
- // Register route with lazy loading
292
+ // Register route
284
293
  context.router.add({
285
294
  path: '/addons/investment-fees-tracker',
286
- component: React.lazy(() => Promise.resolve({
287
- default: InvestmentFeesTrackerWrapper
288
- }))
295
+ render: ({ root: routeRoot }) => {
296
+ root ??= createRoot(routeRoot);
297
+ root.render(<InvestmentFeesTrackerWrapper />);
298
+ },
289
299
  });
290
300
 
291
301
  context.api.logger.debug('Route registered successfully');
@@ -300,6 +310,10 @@ const enable: AddonEnableFunction = (context) => {
300
310
  context.onDisable(() => {
301
311
  context.api.logger.info('🛑 Investment Fees Tracker addon is being disabled');
302
312
 
313
+ // Unmount the addon's React root
314
+ root?.unmount();
315
+ root = null;
316
+
303
317
  // Remove all sidebar items
304
318
  addedItems.forEach(item => {
305
319
  try {
@@ -325,7 +339,8 @@ export default enable;
325
339
  3. **Error Handling**: Comprehensive error handling with logging
326
340
  4. **Resource Management**: Proper cleanup of sidebar items and event listeners
327
341
  5. **TypeScript**: Full type safety with proper imports
328
- 6. **Lazy Loading**: Efficient component loading with React.lazy
342
+ 6. **Sandbox Rendering**: Mounts into the route root with `createRoot` and
343
+ unmounts on disable
329
344
 
330
345
  ````
331
346
 
@@ -608,13 +623,18 @@ export default defineConfig({
608
623
  formats: ['es'],
609
624
  },
610
625
  rollupOptions: {
611
- external: ['react', 'react-dom'],
612
- output: {
613
- globals: {
614
- react: 'React',
615
- 'react-dom': 'ReactDOM',
616
- },
617
- },
626
+ external: [
627
+ '@tanstack/react-query',
628
+ '@wealthfolio/addon-sdk',
629
+ '@wealthfolio/ui',
630
+ 'date-fns',
631
+ 'lucide-react',
632
+ 'react',
633
+ 'react-dom',
634
+ 'react-dom/client',
635
+ 'react/jsx-runtime',
636
+ 'recharts',
637
+ ],
618
638
  },
619
639
  outDir: 'dist',
620
640
  minify: 'terser',
@@ -726,7 +746,8 @@ Add an item to the application sidebar.
726
746
 
727
747
  - `config.id` (string): Unique identifier
728
748
  - `config.label` (string): Display text
729
- - `config.icon` (string | ReactNode): Icon name or component
749
+ - `config.icon` (string): Host-supported icon token, such as `receipt`,
750
+ `chart-bar`, or `calendar-dots`
730
751
  - `config.route` (string): Navigation route
731
752
  - `config.order` (number): Display order (optional)
732
753
  - `config.onClick` (function): Click handler (optional)
@@ -740,7 +761,8 @@ Register a new route in the application.
740
761
  **Parameters:**
741
762
 
742
763
  - `route.path` (string): Route path pattern
743
- - `route.component` (LazyExoticComponent): Lazy-loaded component
764
+ - `route.render` (function): `({ root, location }) => void` — mount your React
765
+ root into the provided `root` element
744
766
 
745
767
  #### `onDisable(callback)`
746
768
 
@@ -993,13 +1015,18 @@ export default defineConfig({
993
1015
  formats: ['es'],
994
1016
  },
995
1017
  rollupOptions: {
996
- external: ['react', 'react-dom'],
997
- output: {
998
- globals: {
999
- react: 'React',
1000
- 'react-dom': 'ReactDOM',
1001
- },
1002
- },
1018
+ external: [
1019
+ '@tanstack/react-query',
1020
+ '@wealthfolio/addon-sdk',
1021
+ '@wealthfolio/ui',
1022
+ 'date-fns',
1023
+ 'lucide-react',
1024
+ 'react',
1025
+ 'react-dom',
1026
+ 'react-dom/client',
1027
+ 'react/jsx-runtime',
1028
+ 'recharts',
1029
+ ],
1003
1030
  },
1004
1031
  outDir: 'dist',
1005
1032
  minify: 'terser',
@@ -0,0 +1,61 @@
1
+ // src/goal-progress.ts
2
+ function isParticipatingGoal(goal) {
3
+ if (goal.statusLifecycle) {
4
+ return goal.statusLifecycle === "active";
5
+ }
6
+ return true;
7
+ }
8
+ function getFiniteAmount(value) {
9
+ if (value === null || value === void 0 || value === "") {
10
+ return void 0;
11
+ }
12
+ const amount = typeof value === "number" ? value : Number(value);
13
+ return Number.isFinite(amount) ? amount : void 0;
14
+ }
15
+ function toFiniteAmount(value) {
16
+ return getFiniteAmount(value) ?? 0;
17
+ }
18
+ function calculateGoalProgress(accountsValuations, goals, allocations) {
19
+ if (!goals) {
20
+ return [];
21
+ }
22
+ const baseCurrency = accountsValuations?.[0]?.baseCurrency ?? "USD";
23
+ const accountValueMap = /* @__PURE__ */ new Map();
24
+ accountsValuations?.forEach((account) => {
25
+ const valueInBaseCurrency = account.totalValueBase ?? 0;
26
+ accountValueMap.set(account.accountId, valueInBaseCurrency);
27
+ });
28
+ const allocationsByGoal = /* @__PURE__ */ new Map();
29
+ allocations?.forEach((alloc) => {
30
+ const existing = allocationsByGoal.get(alloc.goalId) ?? [];
31
+ allocationsByGoal.set(alloc.goalId, [...existing, alloc]);
32
+ });
33
+ const sortedGoals = [...goals].filter(isParticipatingGoal).sort(
34
+ (a, b) => toFiniteAmount(a.summaryTargetAmount ?? a.targetAmount) - toFiniteAmount(b.summaryTargetAmount ?? b.targetAmount)
35
+ );
36
+ return sortedGoals.map((goal) => {
37
+ const goalAllocations = allocationsByGoal.get(goal.id) ?? [];
38
+ const targetAmount = toFiniteAmount(goal.summaryTargetAmount ?? goal.targetAmount);
39
+ const totalAllocatedValue = goalAllocations.reduce((total, allocation) => {
40
+ const accountValueInBase = accountValueMap.get(allocation.accountId) ?? 0;
41
+ return total + accountValueInBase * allocation.sharePercent / 100;
42
+ }, 0);
43
+ const currentValue = getFiniteAmount(goal.summaryCurrentValue) ?? totalAllocatedValue;
44
+ const progress = getFiniteAmount(goal.summaryProgress) ?? (targetAmount > 0 ? currentValue / targetAmount : 0);
45
+ return {
46
+ goalId: goal.id,
47
+ name: goal.title,
48
+ targetValue: targetAmount,
49
+ currentValue,
50
+ progress,
51
+ currency: goal.currency ?? baseCurrency,
52
+ statusHealth: goal.statusHealth,
53
+ targetDate: goal.targetDate
54
+ };
55
+ });
56
+ }
57
+
58
+ export {
59
+ calculateGoalProgress
60
+ };
61
+ //# sourceMappingURL=chunk-465MB7HT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/goal-progress.ts"],"sourcesContent":["import type { Goal, GoalAllocation, AccountValuation, GoalProgress } from './data-types';\n\nfunction isParticipatingGoal(goal: Goal): boolean {\n if (goal.statusLifecycle) {\n return goal.statusLifecycle === 'active';\n }\n\n return true;\n}\n\nfunction getFiniteAmount(value: unknown): number | undefined {\n if (value === null || value === undefined || value === '') {\n return undefined;\n }\n\n const amount = typeof value === 'number' ? value : Number(value);\n return Number.isFinite(amount) ? amount : undefined;\n}\n\nfunction toFiniteAmount(value: unknown): number {\n return getFiniteAmount(value) ?? 0;\n}\n\n/**\n * Calculate goal progress from goal summaries, falling back to allocations.\n */\nexport function calculateGoalProgress(\n accountsValuations: AccountValuation[],\n goals: Goal[],\n allocations: GoalAllocation[],\n): GoalProgress[] {\n if (!goals) {\n return [];\n }\n\n const baseCurrency = accountsValuations?.[0]?.baseCurrency ?? 'USD';\n\n // accountId -> totalValue in base currency\n const accountValueMap = new Map<string, number>();\n accountsValuations?.forEach((account) => {\n const valueInBaseCurrency = account.totalValueBase ?? 0;\n accountValueMap.set(account.accountId, valueInBaseCurrency);\n });\n\n // goalId -> allocations\n const allocationsByGoal = new Map<string, GoalAllocation[]>();\n allocations?.forEach((alloc) => {\n const existing = allocationsByGoal.get(alloc.goalId) ?? [];\n allocationsByGoal.set(alloc.goalId, [...existing, alloc]);\n });\n\n const sortedGoals = [...goals]\n .filter(isParticipatingGoal)\n .sort(\n (a, b) =>\n toFiniteAmount(a.summaryTargetAmount ?? a.targetAmount) -\n toFiniteAmount(b.summaryTargetAmount ?? b.targetAmount),\n );\n\n return sortedGoals.map((goal) => {\n const goalAllocations = allocationsByGoal.get(goal.id) ?? [];\n const targetAmount = toFiniteAmount(goal.summaryTargetAmount ?? goal.targetAmount);\n\n const totalAllocatedValue = goalAllocations.reduce((total, allocation) => {\n const accountValueInBase = accountValueMap.get(allocation.accountId) ?? 0;\n return total + (accountValueInBase * allocation.sharePercent) / 100;\n }, 0);\n\n const currentValue = getFiniteAmount(goal.summaryCurrentValue) ?? totalAllocatedValue;\n const progress =\n getFiniteAmount(goal.summaryProgress) ??\n (targetAmount > 0 ? currentValue / targetAmount : 0);\n\n return {\n goalId: goal.id,\n name: goal.title,\n targetValue: targetAmount,\n currentValue,\n progress,\n currency: goal.currency ?? baseCurrency,\n statusHealth: goal.statusHealth,\n targetDate: goal.targetDate,\n };\n });\n}\n"],"mappings":";AAEA,SAAS,oBAAoB,MAAqB;AAChD,MAAI,KAAK,iBAAiB;AACxB,WAAO,KAAK,oBAAoB;AAAA,EAClC;AAEA,SAAO;AACT;AAEA,SAAS,gBAAgB,OAAoC;AAC3D,MAAI,UAAU,QAAQ,UAAU,UAAa,UAAU,IAAI;AACzD,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK;AAC/D,SAAO,OAAO,SAAS,MAAM,IAAI,SAAS;AAC5C;AAEA,SAAS,eAAe,OAAwB;AAC9C,SAAO,gBAAgB,KAAK,KAAK;AACnC;AAKO,SAAS,sBACd,oBACA,OACA,aACgB;AAChB,MAAI,CAAC,OAAO;AACV,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,eAAe,qBAAqB,CAAC,GAAG,gBAAgB;AAG9D,QAAM,kBAAkB,oBAAI,IAAoB;AAChD,sBAAoB,QAAQ,CAAC,YAAY;AACvC,UAAM,sBAAsB,QAAQ,kBAAkB;AACtD,oBAAgB,IAAI,QAAQ,WAAW,mBAAmB;AAAA,EAC5D,CAAC;AAGD,QAAM,oBAAoB,oBAAI,IAA8B;AAC5D,eAAa,QAAQ,CAAC,UAAU;AAC9B,UAAM,WAAW,kBAAkB,IAAI,MAAM,MAAM,KAAK,CAAC;AACzD,sBAAkB,IAAI,MAAM,QAAQ,CAAC,GAAG,UAAU,KAAK,CAAC;AAAA,EAC1D,CAAC;AAED,QAAM,cAAc,CAAC,GAAG,KAAK,EAC1B,OAAO,mBAAmB,EAC1B;AAAA,IACC,CAAC,GAAG,MACF,eAAe,EAAE,uBAAuB,EAAE,YAAY,IACtD,eAAe,EAAE,uBAAuB,EAAE,YAAY;AAAA,EAC1D;AAEF,SAAO,YAAY,IAAI,CAAC,SAAS;AAC/B,UAAM,kBAAkB,kBAAkB,IAAI,KAAK,EAAE,KAAK,CAAC;AAC3D,UAAM,eAAe,eAAe,KAAK,uBAAuB,KAAK,YAAY;AAEjF,UAAM,sBAAsB,gBAAgB,OAAO,CAAC,OAAO,eAAe;AACxE,YAAM,qBAAqB,gBAAgB,IAAI,WAAW,SAAS,KAAK;AACxE,aAAO,QAAS,qBAAqB,WAAW,eAAgB;AAAA,IAClE,GAAG,CAAC;AAEJ,UAAM,eAAe,gBAAgB,KAAK,mBAAmB,KAAK;AAClE,UAAM,WACJ,gBAAgB,KAAK,eAAe,MACnC,eAAe,IAAI,eAAe,eAAe;AAEpD,WAAO;AAAA,MACL,QAAQ,KAAK;AAAA,MACb,MAAM,KAAK;AAAA,MACX,aAAa;AAAA,MACb;AAAA,MACA;AAAA,MACA,UAAU,KAAK,YAAY;AAAA,MAC3B,cAAc,KAAK;AAAA,MACnB,YAAY,KAAK;AAAA,IACnB;AAAA,EACF,CAAC;AACH;","names":[]}
@@ -0,0 +1,16 @@
1
+ // src/host-dependencies.ts
2
+ var HOST_DEPENDENCIES = {
3
+ "@tanstack/react-query": "^5.90.0",
4
+ "@wealthfolio/addon-sdk": "^3.6.0",
5
+ "@wealthfolio/ui": "^3.6.0",
6
+ "date-fns": "^4.1.0",
7
+ "lucide-react": "^0.561.0",
8
+ react: "^19.2.0",
9
+ "react-dom": "^19.2.0",
10
+ recharts: "^3.7.0"
11
+ };
12
+
13
+ export {
14
+ HOST_DEPENDENCIES
15
+ };
16
+ //# sourceMappingURL=chunk-AAIYUZY5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/host-dependencies.ts"],"sourcesContent":["export const HOST_DEPENDENCIES = {\n '@tanstack/react-query': '^5.90.0',\n '@wealthfolio/addon-sdk': '^3.6.0',\n '@wealthfolio/ui': '^3.6.0',\n 'date-fns': '^4.1.0',\n 'lucide-react': '^0.561.0',\n react: '^19.2.0',\n 'react-dom': '^19.2.0',\n recharts: '^3.7.0',\n} as const;\n"],"mappings":";AAAO,IAAM,oBAAoB;AAAA,EAC/B,yBAAyB;AAAA,EACzB,0BAA0B;AAAA,EAC1B,mBAAmB;AAAA,EACnB,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,OAAO;AAAA,EACP,aAAa;AAAA,EACb,UAAU;AACZ;","names":[]}
@@ -0,0 +1,9 @@
1
+ // src/manifest.ts
2
+ function isInstalledManifest(manifest) {
3
+ return !!(manifest.installedAt && manifest.main !== void 0 && manifest.enabled !== void 0);
4
+ }
5
+
6
+ export {
7
+ isInstalledManifest
8
+ };
9
+ //# sourceMappingURL=chunk-O566EAIL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/manifest.ts"],"sourcesContent":["/**\n * Addon manifest and metadata types\n */\n\nimport type { Permission } from './permissions';\n\nexport interface AddonNetworkAccess {\n allowedHosts: string[];\n approvedHosts?: string[];\n}\n\nexport type AddonHostDependencies = Record<string, string>;\n\n/**\n * Unified addon manifest structure that handles both development and runtime scenarios\n * This represents both what developers write in their manifest.json and installed addon metadata\n */\nexport interface AddonManifest {\n // Core manifest fields (always present)\n /** Unique addon identifier (lowercase, no spaces, hyphens allowed) */\n id: string;\n /** Human-readable addon name */\n name: string;\n /** Semantic version (e.g., \"1.0.0\") */\n version: string;\n /** Brief description of the addon's functionality */\n description?: string;\n /** Author name or organization */\n author?: string;\n /** Compatible SDK version */\n sdkVersion?: string;\n /** Main entry point file (relative to addon root) */\n main?: string;\n /** Whether the addon is enabled by default */\n enabled?: boolean;\n /** Permission declarations for security review */\n permissions?: Permission[];\n /** Addon homepage or documentation URL */\n homepage?: string;\n /** Support or issues URL */\n repository?: string;\n /** License identifier (e.g., \"MIT\", \"Apache-2.0\") */\n license?: string;\n /** Minimum Wealthfolio version required */\n minWealthfolioVersion?: string;\n /** Keywords for discoverability */\n keywords?: string[];\n /** Addon icon (base64 or relative path) */\n icon?: string;\n /** Network hosts this addon may reach through the host broker */\n network?: AddonNetworkAccess;\n /** Host-provided packages this addon imports instead of bundling */\n hostDependencies?: AddonHostDependencies;\n\n // Runtime fields (only present after installation)\n /** Installation timestamp in ISO format */\n installedAt?: string;\n /** Last update timestamp */\n updatedAt?: string;\n /** Installation source */\n source?: 'local' | 'store' | 'sideload';\n /** File size in bytes */\n size?: number;\n}\n\n/**\n * Type guard to check if a manifest has been installed (has runtime fields)\n */\nexport function isInstalledManifest(\n manifest: AddonManifest,\n): manifest is Required<Pick<AddonManifest, 'main' | 'enabled' | 'installedAt'>> &\n AddonManifest {\n return !!(\n manifest.installedAt &&\n manifest.main !== undefined &&\n manifest.enabled !== undefined\n );\n}\n\n/**\n * Helper type for development manifests (without runtime fields)\n */\nexport type DevelopmentManifest = Omit<\n AddonManifest,\n 'installedAt' | 'updatedAt' | 'source' | 'size'\n>;\n\n/**\n * Helper type for installed manifests (with runtime fields)\n */\nexport type InstalledManifest = Required<\n Pick<AddonManifest, 'main' | 'enabled' | 'installedAt'>\n> &\n AddonManifest;\n\n/**\n * Addon file information\n */\nexport interface AddonFile {\n /** File name */\n name: string;\n /** File content */\n content: string;\n /** Whether this is the main entry point */\n is_main: boolean;\n /** File size in bytes */\n size?: number;\n}\n\n/**\n * Extracted addon package\n */\nexport interface ExtractedAddon {\n /** Addon metadata from manifest */\n metadata: AddonManifest;\n /** List of files in the addon package */\n files: AddonFile[];\n}\n\n/**\n * Installed addon information\n */\nexport interface InstalledAddon {\n /** Addon metadata */\n metadata: AddonManifest;\n /** Installation path */\n path?: string;\n /** Whether the addon is currently active */\n active?: boolean;\n}\n\n/**\n * Addon installation result\n */\nexport interface AddonInstallResult {\n /** Whether installation was successful */\n success: boolean;\n /** Error message if installation failed */\n error?: string;\n /** Installed addon metadata */\n addon?: AddonManifest;\n}\n\n/**\n * Addon validation result\n */\nexport interface AddonValidationResult {\n /** Whether the addon is valid */\n valid: boolean;\n /** List of validation errors */\n errors: string[];\n /** List of validation warnings */\n warnings: string[];\n}\n\n/**\n * Addon update information\n */\nexport interface AddonUpdateInfo {\n /** Current installed version */\n currentVersion: string;\n /** Latest available version */\n latestVersion: string;\n /** Whether an update is available */\n updateAvailable: boolean;\n /** Download URL for the update */\n downloadUrl?: string;\n /** Optional SHA-256 digest for the update package bytes */\n sha256?: string;\n /** Release notes for the latest version */\n releaseNotes?: string;\n /** Release date of the latest version */\n releaseDate?: string;\n /** Changelog URL */\n changelogUrl?: string;\n /** Whether this is a critical security update */\n isCritical?: boolean;\n /** Breaking changes in this update */\n hasBreakingChanges?: boolean;\n /** Minimum Wealthfolio version required for this update */\n minWealthfolioVersion?: string;\n}\n\n/**\n * Addon update check result\n */\nexport interface AddonUpdateCheckResult {\n /** Addon ID */\n addonId: string;\n /** Update information */\n updateInfo: AddonUpdateInfo;\n /** Any errors during update check */\n error?: string;\n}\n\n/**\n * Addon store listing\n */\nexport interface AddonStoreListing {\n /** Addon metadata */\n metadata: AddonManifest;\n /** Download URL */\n downloadUrl: string;\n /** Optional SHA-256 digest for the package bytes */\n sha256?: string;\n /** Number of downloads */\n downloads?: number;\n /** Average rating */\n rating?: number;\n /** Number of reviews */\n reviewCount?: number;\n /** Whether it's verified by Wealthfolio team */\n verified?: boolean;\n /** Last update date */\n lastUpdated?: string;\n /** Screenshots or images */\n images?: string[];\n /** Release notes for the latest version */\n releaseNotes?: string;\n /** Changelog URL */\n changelogUrl?: string;\n}\n"],"mappings":";AAoEO,SAAS,oBACd,UAEc;AACd,SAAO,CAAC,EACN,SAAS,eACT,SAAS,SAAS,UAClB,SAAS,YAAY;AAEzB;","names":[]}
@@ -0,0 +1,61 @@
1
+ // src/query-keys.ts
2
+ var QueryKeys = {
3
+ // Account related keys
4
+ ACCOUNTS: "accounts",
5
+ ACCOUNTS_SUMMARY: "accounts_summary",
6
+ // Activity related keys
7
+ ACTIVITY_DATA: "activity-data",
8
+ ACTIVITIES: "activities",
9
+ // Portfolio related keys
10
+ HOLDINGS: "holdings",
11
+ HOLDING: "holding",
12
+ INCOME_SUMMARY: "incomeSummary",
13
+ PORTFOLIO_SUMMARY: "portfolioSummary",
14
+ QUOTE_HISTORY: "quoteHistory",
15
+ // Goals related keys
16
+ GOALS: "goals",
17
+ GOALS_ALLOCATIONS: "goals_allocations",
18
+ // Settings related keys
19
+ SETTINGS: "settings",
20
+ EXCHANGE_RATES: "exchangeRates",
21
+ // New keys for exchange rates
22
+ EXCHANGE_RATE_SYMBOLS: "exchange_rate_symbols",
23
+ QUOTE: "quote",
24
+ CONTRIBUTION_LIMITS: "contributionLimits",
25
+ CONTRIBUTION_LIMIT_PROGRESS: "contributionLimitProgress",
26
+ ASSET_DATA: "asset_data",
27
+ IMPORT_MAPPING: "import_mapping",
28
+ PERFORMANCE_SUMMARY: "performanceSummary",
29
+ PERFORMANCE_HISTORY: "performanceHistory",
30
+ HISTORY_VALUATION: "historyValuation",
31
+ // Helper function to create account-specific keys
32
+ valuationHistory: (id) => [QueryKeys.HISTORY_VALUATION, id],
33
+ // Account simple performance
34
+ ACCOUNTS_SIMPLE_PERFORMANCE: "accountsSimplePerformance",
35
+ accountsSimplePerformance: (accountIds) => [
36
+ QueryKeys.ACCOUNTS_SIMPLE_PERFORMANCE,
37
+ [...accountIds].sort().join(",") || "none"
38
+ ],
39
+ // Market Data Providers
40
+ MARKET_DATA_PROVIDERS: "marketDataProviders",
41
+ MARKET_DATA_PROVIDER_SETTINGS: "marketDataProviderSettings",
42
+ transactions: "transactions",
43
+ latestValuations: "latest-valuations",
44
+ // Market Data
45
+ symbolSearch: "symbol-search",
46
+ ASSET_HISTORY: "asset-history",
47
+ // Addons
48
+ INSTALLED_ADDONS: "installedAddons",
49
+ ADDON_STORE_LISTINGS: "addonStoreListings",
50
+ ADDON_AUTO_UPDATE_CHECK: "addonAutoUpdateCheck",
51
+ SNAPSHOTS: "snapshots",
52
+ snapshots: (accountId) => [QueryKeys.SNAPSHOTS, accountId],
53
+ secrets: {
54
+ apiKey: (providerId) => ["secrets", "apiKey", providerId]
55
+ }
56
+ };
57
+
58
+ export {
59
+ QueryKeys
60
+ };
61
+ //# sourceMappingURL=chunk-PYPEFQCY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/query-keys.ts"],"sourcesContent":["/**\n * Shared Query Keys for React Query\n * These keys should match the main application's query keys to ensure cache consistency\n */\nexport const QueryKeys = {\n // Account related keys\n ACCOUNTS: 'accounts',\n ACCOUNTS_SUMMARY: 'accounts_summary',\n\n // Activity related keys\n ACTIVITY_DATA: 'activity-data',\n ACTIVITIES: 'activities',\n\n // Portfolio related keys\n HOLDINGS: 'holdings',\n HOLDING: 'holding',\n INCOME_SUMMARY: 'incomeSummary',\n PORTFOLIO_SUMMARY: 'portfolioSummary',\n QUOTE_HISTORY: 'quoteHistory',\n\n // Goals related keys\n GOALS: 'goals',\n GOALS_ALLOCATIONS: 'goals_allocations',\n\n // Settings related keys\n SETTINGS: 'settings',\n EXCHANGE_RATES: 'exchangeRates',\n\n // New keys for exchange rates\n EXCHANGE_RATE_SYMBOLS: 'exchange_rate_symbols',\n QUOTE: 'quote',\n\n CONTRIBUTION_LIMITS: 'contributionLimits',\n CONTRIBUTION_LIMIT_PROGRESS: 'contributionLimitProgress',\n\n ASSET_DATA: 'asset_data',\n IMPORT_MAPPING: 'import_mapping',\n\n PERFORMANCE_SUMMARY: 'performanceSummary',\n PERFORMANCE_HISTORY: 'performanceHistory',\n\n HISTORY_VALUATION: 'historyValuation',\n // Helper function to create account-specific keys\n valuationHistory: (id: string) => [QueryKeys.HISTORY_VALUATION, id],\n\n // Account simple performance\n ACCOUNTS_SIMPLE_PERFORMANCE: 'accountsSimplePerformance',\n accountsSimplePerformance: (accountIds: string[]) => [\n QueryKeys.ACCOUNTS_SIMPLE_PERFORMANCE,\n [...accountIds].sort().join(',') || 'none',\n ],\n\n // Market Data Providers\n MARKET_DATA_PROVIDERS: 'marketDataProviders',\n MARKET_DATA_PROVIDER_SETTINGS: 'marketDataProviderSettings',\n\n transactions: 'transactions',\n latestValuations: 'latest-valuations',\n\n // Market Data\n symbolSearch: 'symbol-search',\n\n ASSET_HISTORY: 'asset-history',\n\n // Addons\n INSTALLED_ADDONS: 'installedAddons',\n ADDON_STORE_LISTINGS: 'addonStoreListings',\n ADDON_AUTO_UPDATE_CHECK: 'addonAutoUpdateCheck',\n\n SNAPSHOTS: 'snapshots',\n snapshots: (accountId: string) => [QueryKeys.SNAPSHOTS, accountId],\n\n secrets: {\n apiKey: (providerId: string) => ['secrets', 'apiKey', providerId],\n },\n} as const;\n\nexport type QueryKeys = typeof QueryKeys;\n"],"mappings":";AAIO,IAAM,YAAY;AAAA;AAAA,EAEvB,UAAU;AAAA,EACV,kBAAkB;AAAA;AAAA,EAGlB,eAAe;AAAA,EACf,YAAY;AAAA;AAAA,EAGZ,UAAU;AAAA,EACV,SAAS;AAAA,EACT,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EACnB,eAAe;AAAA;AAAA,EAGf,OAAO;AAAA,EACP,mBAAmB;AAAA;AAAA,EAGnB,UAAU;AAAA,EACV,gBAAgB;AAAA;AAAA,EAGhB,uBAAuB;AAAA,EACvB,OAAO;AAAA,EAEP,qBAAqB;AAAA,EACrB,6BAA6B;AAAA,EAE7B,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAEhB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EAErB,mBAAmB;AAAA;AAAA,EAEnB,kBAAkB,CAAC,OAAe,CAAC,UAAU,mBAAmB,EAAE;AAAA;AAAA,EAGlE,6BAA6B;AAAA,EAC7B,2BAA2B,CAAC,eAAyB;AAAA,IACnD,UAAU;AAAA,IACV,CAAC,GAAG,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,KAAK;AAAA,EACtC;AAAA;AAAA,EAGA,uBAAuB;AAAA,EACvB,+BAA+B;AAAA,EAE/B,cAAc;AAAA,EACd,kBAAkB;AAAA;AAAA,EAGlB,cAAc;AAAA,EAEd,eAAe;AAAA;AAAA,EAGf,kBAAkB;AAAA,EAClB,sBAAsB;AAAA,EACtB,yBAAyB;AAAA,EAEzB,WAAW;AAAA,EACX,WAAW,CAAC,cAAsB,CAAC,UAAU,WAAW,SAAS;AAAA,EAEjE,SAAS;AAAA,IACP,QAAQ,CAAC,eAAuB,CAAC,WAAW,UAAU,UAAU;AAAA,EAClE;AACF;","names":[]}
@@ -114,7 +114,7 @@ var PERMISSION_CATEGORIES = [
114
114
  id: "secrets",
115
115
  name: "Secrets Management",
116
116
  description: "Access to secure storage for addon secrets",
117
- functions: ["set", "get", "delete"],
117
+ functions: ["set", "get", "use", "delete"],
118
118
  riskLevel: "high"
119
119
  },
120
120
  {
@@ -147,11 +147,25 @@ var PERMISSION_CATEGORIES = [
147
147
  ],
148
148
  riskLevel: "low"
149
149
  },
150
+ {
151
+ id: "query",
152
+ name: "Query Cache",
153
+ description: "Access to refresh host application data",
154
+ functions: ["invalidateQueries", "refetchQueries"],
155
+ riskLevel: "low"
156
+ },
157
+ {
158
+ id: "network",
159
+ name: "Network Access",
160
+ description: "Access to declared external HTTPS hosts through the host network broker",
161
+ functions: ["request"],
162
+ riskLevel: "high"
163
+ },
150
164
  {
151
165
  id: "ui",
152
166
  name: "User Interface",
153
167
  description: "Access to modify navigation and add UI components",
154
- functions: ["sidebar.addItem", "router.add"],
168
+ functions: ["sidebar.addItem", "router.add", "navigation.navigate", "onDisable"],
155
169
  riskLevel: "low"
156
170
  }
157
171
  ];
@@ -227,4 +241,4 @@ export {
227
241
  addDetectedFunction,
228
242
  markFunctionAsDeclared
229
243
  };
230
- //# sourceMappingURL=chunk-ISD4MMHY.js.map
244
+ //# sourceMappingURL=chunk-SEHD46ND.js.map