gloomberb 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -11
- package/package.json +12 -5
- package/src/app.tsx +414 -191
- package/src/architecture/import-boundaries.test.ts +118 -0
- package/src/assets/gloomberb-logo.png +0 -0
- package/src/assets/gloomberb-logo.svg +73 -0
- package/src/brokers/account-cache.ts +157 -0
- package/src/brokers/profile-form.test.ts +73 -0
- package/src/brokers/profile-form.ts +109 -0
- package/src/brokers/remote-broker-adapter.ts +91 -0
- package/src/brokers/sync-broker-instance.test.ts +124 -1
- package/src/brokers/sync-broker-instance.ts +113 -17
- package/src/capabilities/factories.ts +100 -0
- package/src/capabilities/index.ts +4 -0
- package/src/capabilities/registry.test.ts +137 -0
- package/src/capabilities/registry.ts +131 -0
- package/src/capabilities/service-capabilities.ts +27 -0
- package/src/capabilities/types.ts +84 -0
- package/src/cli/commands/ticker.ts +11 -3
- package/src/cli/context.ts +19 -13
- package/src/cli/helpers.ts +1 -0
- package/src/cli/types.ts +2 -2
- package/src/components/chart/chart-controller.ts +4 -24
- package/src/components/chart/chart-data.ts +3 -14
- package/src/components/chart/chart-indicator-selector.tsx +28 -6
- package/src/components/chart/chart-pane-settings.ts +15 -28
- package/src/components/chart/chart-renderer.ts +74 -99
- package/src/components/chart/chart-resolution.ts +10 -10
- package/src/components/chart/chart-scroll.test.ts +6 -5
- package/src/components/chart/chart-scroll.ts +2 -2
- package/src/components/chart/chart-types.ts +0 -1
- package/src/components/chart/chart-viewport.ts +2 -1
- package/src/components/chart/chart.test.ts +37 -0
- package/src/components/chart/comparison-chart-data.ts +3 -17
- package/src/components/chart/comparison-chart-renderer.test.ts +27 -4
- package/src/components/chart/comparison-chart-renderer.ts +1 -1
- package/src/components/chart/comparison-stock-chart.tsx +304 -108
- package/src/components/chart/cursor-motion.ts +3 -2
- package/src/components/chart/indicators/bands.test.ts +0 -14
- package/src/components/chart/indicators/moving-averages.test.ts +0 -15
- package/src/components/chart/indicators/options.ts +3 -3
- package/src/components/chart/indicators/oscillators.test.ts +0 -21
- package/src/components/chart/indicators/types.ts +1 -2
- package/src/components/chart/native/chart-rasterizer.ts +37 -44
- package/src/components/chart/native/kitty-adapter.ts +2 -5
- package/src/components/chart/native/kitty-manager.ts +1 -1
- package/src/components/chart/native/kitty-protocol.ts +0 -1
- package/src/components/chart/native/kitty-support.ts +1 -1
- package/src/components/chart/native/kitty.test.ts +30 -13
- package/src/components/chart/native/renderer-selection.ts +1 -1
- package/src/components/chart/native/surface-manager.ts +1 -1
- package/src/components/chart/static-chart-surface.test.tsx +51 -0
- package/src/components/chart/static-chart-surface.tsx +191 -0
- package/src/components/chart/stock-chart-renderer-switch.test.tsx +242 -35
- package/src/components/chart/stock-chart.tsx +438 -141
- package/src/components/command-bar/__snapshots__/command-bar.test.tsx.snap +4 -4
- package/src/components/command-bar/command-bar.test.tsx +269 -20
- package/src/components/command-bar/command-bar.tsx +830 -353
- package/src/components/command-bar/command-registry.ts +10 -20
- package/src/components/command-bar/helpers.test.ts +1 -1
- package/src/components/command-bar/helpers.ts +9 -11
- package/src/components/command-bar/pane-template-display.ts +4 -8
- package/src/components/command-bar/root-shortcuts.ts +12 -11
- package/src/components/command-bar/view-model.test.ts +8 -42
- package/src/components/command-bar/view-model.ts +8 -36
- package/src/components/command-bar/workflow-ops.test.ts +76 -2
- package/src/components/command-bar/workflow-ops.ts +40 -9
- package/src/components/command-bar/workflow-types.ts +2 -1
- package/src/components/data-table-stack-view.test.tsx +206 -0
- package/src/components/data-table-stack-view.tsx +152 -0
- package/src/components/data-table-view.test.tsx +188 -0
- package/src/components/data-table-view.tsx +279 -0
- package/src/components/feed-data-table-stack-view.test.tsx +225 -0
- package/src/components/feed-data-table-stack-view.tsx +365 -0
- package/src/components/index.ts +26 -31
- package/src/components/layout/detached-pane-shell.tsx +185 -0
- package/src/components/layout/floating-pane.tsx +87 -13
- package/src/components/layout/header.tsx +139 -49
- package/src/components/layout/pane-content.tsx +40 -0
- package/src/components/layout/pane-footer.test.tsx +195 -0
- package/src/components/layout/pane-footer.tsx +410 -0
- package/src/components/layout/pane-header.test.tsx +39 -0
- package/src/components/layout/pane-header.tsx +192 -14
- package/src/components/layout/pane-sizing.ts +13 -4
- package/src/components/layout/pane-title.ts +35 -0
- package/src/components/layout/pane.tsx +56 -8
- package/src/components/layout/shell.test.tsx +322 -19
- package/src/components/layout/shell.tsx +1010 -366
- package/src/components/layout/status-bar.test.tsx +17 -10
- package/src/components/layout/status-bar.tsx +367 -100
- package/src/components/layout/titlebar-overlay.ts +2 -0
- package/src/components/markdown-editor.tsx +29 -14
- package/src/components/markdown-text.tsx +25 -24
- package/src/components/onboarding/onboarding-wizard.test.tsx +8 -3
- package/src/components/onboarding/onboarding-wizard.tsx +347 -473
- package/src/components/pane-settings-dialog.tsx +521 -89
- package/src/components/pane-template-wizard.test.tsx +1 -1
- package/src/components/pane-template-wizard.tsx +38 -37
- package/src/components/price-selector-dialog.tsx +22 -21
- package/src/components/ticker-badge-list.tsx +53 -0
- package/src/components/ticker-badge-text.test.tsx +44 -1
- package/src/components/ticker-badge-text.tsx +78 -38
- package/src/components/ticker-badge.tsx +28 -7
- package/src/components/ticker-list-table-view.tsx +248 -0
- package/src/components/ticker-list-table.test.tsx +47 -1
- package/src/components/ticker-list-table.tsx +212 -41
- package/src/components/toggle-list.tsx +114 -8
- package/src/components/ui/button.tsx +24 -16
- package/src/components/ui/choice-dialog.tsx +89 -0
- package/src/components/ui/data-table.tsx +313 -114
- package/src/components/ui/external-link.tsx +52 -13
- package/src/components/ui/fields.tsx +44 -30
- package/src/components/ui/frame.tsx +24 -57
- package/src/components/ui/index.ts +15 -50
- package/src/components/ui/list-view.tsx +65 -25
- package/src/components/ui/loading.tsx +5 -60
- package/src/components/ui/message-composer.test.tsx +81 -0
- package/src/components/ui/message-composer.tsx +139 -0
- package/src/components/ui/multi-select-dialog.tsx +100 -32
- package/src/components/ui/multi-select.ts +112 -0
- package/src/components/ui/native-select.tsx +120 -0
- package/src/components/ui/page-stack-view.tsx +49 -19
- package/src/components/ui/remote-image.tsx +75 -0
- package/src/components/ui/row-value-cache.test.ts +17 -0
- package/src/components/ui/row-value-cache.ts +41 -0
- package/src/components/ui/shortcut-hint.tsx +55 -0
- package/src/components/ui/status.tsx +12 -67
- package/src/components/ui/table-layout.ts +59 -0
- package/src/components/ui/tabs.tsx +249 -27
- package/src/components/ui/toggle.tsx +19 -127
- package/src/components/ui/ui.test.tsx +266 -53
- package/src/components/use-double-click-activation.ts +7 -2
- package/src/components/use-external-link-footer.ts +79 -0
- package/src/core/app-services.ts +95 -0
- package/src/core/state/app-state.ts +886 -0
- package/src/{state → core/state}/session-persistence.ts +7 -6
- package/src/data/config-layout.ts +225 -0
- package/src/data/config-store-node.ts +390 -0
- package/src/data/config-store.test.ts +72 -0
- package/src/data/config-store.ts +35 -478
- package/src/data/sqlite-json.ts +2 -5
- package/src/data/sqlite-retry.ts +1 -0
- package/src/index.tsx +2 -68
- package/src/market-data/coordinator-subscriptions.test.ts +125 -0
- package/src/market-data/coordinator.test.ts +73 -4
- package/src/market-data/coordinator.ts +190 -24
- package/src/market-data/hooks.test.tsx +1 -1
- package/src/market-data/hooks.tsx +109 -87
- package/src/market-data/query-store.ts +3 -3
- package/src/market-data/request-types.test.ts +0 -14
- package/src/market-data/result-types.ts +1 -0
- package/src/news/aggregator.test.ts +267 -0
- package/src/news/aggregator.ts +452 -0
- package/src/news/hooks.ts +39 -0
- package/src/news/ticker-symbols.ts +21 -0
- package/src/news/types.ts +77 -0
- package/src/notifications/app-notifier.ts +21 -10
- package/src/pane-settings.test.ts +0 -2
- package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +124 -134
- package/src/plugins/builtin/ai/gloomberb-cli.ts +14 -13
- package/src/plugins/builtin/ai/providers.ts +1 -9
- package/src/plugins/builtin/ai/runner.ts +41 -5
- package/src/plugins/builtin/ai/screener-pane.test.tsx +37 -55
- package/src/plugins/builtin/ai/screener-pane.tsx +231 -276
- package/src/plugins/builtin/ai/settings.ts +0 -1
- package/src/plugins/builtin/ai/utils.ts +1 -71
- package/src/plugins/builtin/alerts/alert-engine.test.ts +1 -38
- package/src/plugins/builtin/alerts/index.test.tsx +61 -82
- package/src/plugins/builtin/alerts/index.tsx +263 -155
- package/src/plugins/builtin/alerts/types.ts +7 -0
- package/src/plugins/builtin/analytics/index.test.tsx +13 -8
- package/src/plugins/builtin/analytics/index.tsx +78 -120
- package/src/plugins/builtin/analytics/metrics.test.ts +0 -4
- package/src/plugins/builtin/ask-ai.test.tsx +55 -54
- package/src/plugins/builtin/broker-manager/index.test.tsx +168 -0
- package/src/plugins/builtin/broker-manager/index.tsx +759 -0
- package/src/plugins/builtin/broker-manager/model.test.ts +107 -0
- package/src/plugins/builtin/broker-manager/model.ts +142 -0
- package/src/plugins/builtin/chat-controller.test.ts +150 -0
- package/src/plugins/builtin/chat-controller.ts +88 -16
- package/src/plugins/builtin/chat.test.tsx +283 -36
- package/src/plugins/builtin/chat.tsx +373 -172
- package/src/plugins/builtin/cloud-auth-actions.tsx +72 -0
- package/src/plugins/builtin/cloud-tweets.tsx +896 -0
- package/src/plugins/builtin/comparison-chart.test.tsx +46 -57
- package/src/plugins/builtin/comparison-chart.tsx +11 -11
- package/src/plugins/builtin/correlation/compute.test.ts +0 -17
- package/src/plugins/builtin/correlation/index.tsx +51 -46
- package/src/plugins/builtin/correlation/settings.ts +1 -2
- package/src/plugins/builtin/debug.tsx +99 -70
- package/src/plugins/builtin/earnings/earnings-cache.test.ts +2 -2
- package/src/plugins/builtin/earnings/earnings-cache.ts +12 -3
- package/src/plugins/builtin/earnings/earnings-data.test.ts +42 -11
- package/src/plugins/builtin/earnings/earnings-data.ts +70 -5
- package/src/plugins/builtin/earnings/index.tsx +187 -94
- package/src/plugins/builtin/econ/calendar-source.ts +9 -116
- package/src/plugins/builtin/econ/fred-client.test.ts +1 -14
- package/src/plugins/builtin/econ/fred-series-map.ts +1 -0
- package/src/plugins/builtin/econ/index.tsx +236 -421
- package/src/plugins/builtin/econ/types.ts +1 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +43 -45
- package/src/plugins/builtin/fx-matrix/pairs.ts +1 -0
- package/src/plugins/builtin/help.test.tsx +120 -60
- package/src/plugins/builtin/help.tsx +266 -82
- package/src/plugins/builtin/holders/index.test.ts +45 -0
- package/src/plugins/builtin/holders/index.tsx +949 -0
- package/src/plugins/builtin/insider/index.tsx +207 -253
- package/src/plugins/builtin/insider/insider-data.test.ts +1 -11
- package/src/plugins/builtin/insider/insider-data.ts +1 -0
- package/src/plugins/builtin/layout-manager.tsx +12 -40
- package/src/plugins/builtin/market-movers/index.tsx +70 -144
- package/src/plugins/builtin/market-movers/screener.test.ts +0 -18
- package/src/plugins/builtin/news-wire/breaking-notifications.test.ts +98 -0
- package/src/plugins/builtin/news-wire/breaking-notifications.ts +110 -0
- package/src/plugins/builtin/news-wire/breaking-pane.tsx +157 -0
- package/src/plugins/builtin/news-wire/categories.ts +114 -0
- package/src/plugins/builtin/news-wire/default-feeds.ts +3 -0
- package/src/plugins/builtin/news-wire/digest-store.ts +44 -0
- package/src/plugins/builtin/news-wire/feed-config.test.ts +90 -0
- package/src/plugins/builtin/news-wire/feed-config.ts +216 -0
- package/src/plugins/builtin/news-wire/feed-pane.tsx +21 -0
- package/src/plugins/builtin/news-wire/hash.ts +9 -0
- package/src/plugins/builtin/news-wire/index.tsx +92 -0
- package/src/plugins/builtin/news-wire/industry-pane.tsx +140 -0
- package/src/plugins/builtin/news-wire/news-detail-view.tsx +256 -0
- package/src/plugins/builtin/news-wire/news-footer.ts +28 -0
- package/src/plugins/builtin/news-wire/news-preset-pane.tsx +94 -0
- package/src/plugins/builtin/news-wire/news-query-presets.ts +40 -0
- package/src/plugins/builtin/news-wire/news-table.test.tsx +206 -0
- package/src/plugins/builtin/news-wire/news-table.tsx +296 -0
- package/src/plugins/builtin/news-wire/persisted-articles.ts +92 -0
- package/src/plugins/builtin/news-wire/read-state.test.ts +30 -0
- package/src/plugins/builtin/news-wire/read-state.ts +76 -0
- package/src/plugins/builtin/news-wire/rss-parser.test.ts +184 -0
- package/src/plugins/builtin/news-wire/rss-parser.ts +213 -0
- package/src/plugins/builtin/news-wire/rss-source.test.ts +159 -0
- package/src/plugins/builtin/news-wire/rss-source.ts +181 -0
- package/src/plugins/builtin/news-wire/top-pane.tsx +21 -0
- package/src/plugins/builtin/news.tsx +98 -25
- package/src/plugins/builtin/notes-files.ts +51 -10
- package/src/plugins/builtin/notes.tsx +332 -131
- package/src/plugins/builtin/options.test.tsx +185 -0
- package/src/plugins/builtin/options.tsx +346 -206
- package/src/plugins/builtin/plugin-groups.ts +92 -0
- package/src/plugins/builtin/portfolio-list/header.tsx +72 -37
- package/src/plugins/builtin/portfolio-list/index.tsx +25 -42
- package/src/plugins/builtin/portfolio-list/metrics.ts +1 -1
- package/src/plugins/builtin/portfolio-list/pane.tsx +246 -164
- package/src/plugins/builtin/portfolio-list/settings.ts +19 -51
- package/src/plugins/builtin/portfolio-list/summary.tsx +21 -16
- package/src/plugins/builtin/portfolio-list/table.tsx +66 -25
- package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.test.tsx +1 -1
- package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.ts +1 -1
- package/src/plugins/builtin/portfolio-list.test.ts +60 -1
- package/src/plugins/builtin/portfolio-list.test.tsx +122 -55
- package/src/plugins/builtin/quote-monitor.test.tsx +2 -27
- package/src/plugins/builtin/research/index.tsx +724 -0
- package/src/plugins/builtin/sec.tsx +67 -19
- package/src/plugins/builtin/sectors/index.tsx +42 -80
- package/src/plugins/builtin/sectors/sector-data.ts +1 -0
- package/src/plugins/builtin/ticker-detail/chart-tab.tsx +25 -39
- package/src/plugins/builtin/ticker-detail/financials-tab.tsx +108 -58
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +383 -271
- package/src/plugins/builtin/ticker-detail/pane.tsx +135 -49
- package/src/plugins/builtin/ticker-detail/quote-monitor.tsx +43 -27
- package/src/plugins/builtin/ticker-detail/settings.ts +3 -6
- package/src/plugins/builtin/ticker-detail-chart-tab-switch.test.tsx +16 -10
- package/src/plugins/builtin/ticker-detail.test.tsx +156 -33
- package/src/plugins/builtin/ticker-surface.ts +68 -0
- package/src/plugins/builtin/world-indices/index.tsx +44 -82
- package/src/plugins/builtin/world-indices/indices.ts +1 -0
- package/src/plugins/builtin/yahoo.ts +55 -1
- package/src/plugins/builtin/yield-curve/index.tsx +87 -145
- package/src/plugins/builtin/yield-curve/treasury-data.ts +6 -77
- package/src/plugins/catalog-ui.test.ts +24 -0
- package/src/plugins/catalog-ui.ts +44 -0
- package/src/plugins/catalog.ts +2 -50
- package/src/plugins/ibkr/__snapshots__/trade-tab.test.tsx.snap +5 -5
- package/src/plugins/ibkr/account-cache.test.ts +15 -14
- package/src/plugins/ibkr/account-cache.ts +15 -120
- package/src/plugins/ibkr/broker-adapter.ts +67 -0
- package/src/plugins/ibkr/config.test.ts +0 -18
- package/src/plugins/ibkr/dialogs.tsx +14 -56
- package/src/plugins/ibkr/flex.test.ts +57 -2
- package/src/plugins/ibkr/flex.ts +116 -12
- package/src/plugins/ibkr/gateway-helpers.ts +8 -3
- package/src/plugins/ibkr/gateway-service-native.ts +2381 -0
- package/src/plugins/ibkr/gateway-service.test.ts +47 -9
- package/src/plugins/ibkr/gateway-service.ts +264 -2235
- package/src/plugins/ibkr/gateway-types.ts +27 -0
- package/src/plugins/ibkr/index.test.tsx +3 -11
- package/src/plugins/ibkr/index.tsx +1 -0
- package/src/plugins/ibkr/price-normalization.test.ts +1 -6
- package/src/plugins/ibkr/price-normalization.ts +1 -0
- package/src/plugins/ibkr/trade-badge.tsx +6 -5
- package/src/plugins/ibkr/trade-tab.test.tsx +2 -2
- package/src/plugins/ibkr/trade-tab.tsx +164 -117
- package/src/plugins/ibkr/trading-pane.tsx +117 -83
- package/src/plugins/pane-manager.test.ts +3 -1
- package/src/plugins/pane-manager.ts +93 -19
- package/src/plugins/plugin-runtime.test.tsx +176 -74
- package/src/plugins/plugin-runtime.tsx +228 -10
- package/src/plugins/prediction-markets/cache.ts +49 -0
- package/src/plugins/prediction-markets/categories.ts +0 -10
- package/src/plugins/prediction-markets/chart.test.tsx +1 -1
- package/src/plugins/prediction-markets/chart.tsx +21 -20
- package/src/plugins/prediction-markets/cli.ts +1 -0
- package/src/plugins/prediction-markets/controller-data.ts +135 -89
- package/src/plugins/prediction-markets/controller.ts +37 -108
- package/src/plugins/prediction-markets/detail/book.tsx +23 -22
- package/src/plugins/prediction-markets/detail/outcomes.tsx +33 -32
- package/src/plugins/prediction-markets/detail/overview.tsx +17 -12
- package/src/plugins/prediction-markets/detail/pane.tsx +59 -45
- package/src/plugins/prediction-markets/detail/rules.tsx +22 -7
- package/src/plugins/prediction-markets/detail/shared.tsx +3 -2
- package/src/plugins/prediction-markets/detail/trades.tsx +12 -11
- package/src/plugins/prediction-markets/detail.test.tsx +1 -1
- package/src/plugins/prediction-markets/keyboard.ts +1 -0
- package/src/plugins/prediction-markets/launch.test.ts +1 -1
- package/src/plugins/prediction-markets/launch.ts +1 -1
- package/src/plugins/prediction-markets/metrics.ts +25 -12
- package/src/plugins/prediction-markets/pane.test.tsx +16 -7
- package/src/plugins/prediction-markets/pane.tsx +180 -82
- package/src/plugins/prediction-markets/plugin.test.tsx +37 -45
- package/src/plugins/prediction-markets/services/fetch.ts +27 -10
- package/src/plugins/prediction-markets/services/kalshi-adapter.ts +20 -3
- package/src/plugins/prediction-markets/services/polymarket-adapter.ts +29 -11
- package/src/plugins/prediction-markets/settings.ts +0 -5
- package/src/plugins/prediction-markets/test-helpers.tsx +19 -44
- package/src/plugins/registry.test.ts +307 -0
- package/src/plugins/registry.ts +293 -82
- package/src/react/input.ts +53 -0
- package/src/react/plugins/plugin-slot.tsx +21 -0
- package/src/react/use-raf-callback.ts +44 -0
- package/src/renderers/electrobun/bun/application-menu-click.test.ts +41 -0
- package/src/renderers/electrobun/bun/application-menu-click.ts +55 -0
- package/src/renderers/electrobun/bun/application-menu.ts +118 -0
- package/src/renderers/electrobun/bun/context-menu-click.test.ts +67 -0
- package/src/renderers/electrobun/bun/context-menu-click.ts +61 -0
- package/src/renderers/electrobun/bun/context-menu-normalize.test.ts +52 -0
- package/src/renderers/electrobun/bun/context-menu-normalize.ts +67 -0
- package/src/renderers/electrobun/bun/core-capabilities.ts +328 -0
- package/src/renderers/electrobun/bun/desktop-workspace.test.ts +63 -0
- package/src/renderers/electrobun/bun/desktop-workspace.ts +162 -0
- package/src/renderers/electrobun/bun/index.ts +1149 -0
- package/src/renderers/electrobun/bun/window-frame.test.ts +40 -0
- package/src/renderers/electrobun/bun/window-frame.ts +46 -0
- package/src/renderers/electrobun/shared/protocol.ts +72 -0
- package/src/renderers/electrobun/view/ai-host.ts +93 -0
- package/src/renderers/electrobun/view/app-services.ts +348 -0
- package/src/renderers/electrobun/view/application-menu-bridge.ts +12 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +173 -0
- package/src/renderers/electrobun/view/broker-remote-client.ts +154 -0
- package/src/renderers/electrobun/view/config-host.ts +35 -0
- package/src/renderers/electrobun/view/context-menu.test.ts +140 -0
- package/src/renderers/electrobun/view/context-menu.ts +164 -0
- package/src/renderers/electrobun/view/data-table.tsx +773 -0
- package/src/renderers/electrobun/view/desktop-controls.tsx +651 -0
- package/src/renderers/electrobun/view/desktop-window-bridge.ts +59 -0
- package/src/renderers/electrobun/view/desktop-window-snapshot.test.ts +57 -0
- package/src/renderers/electrobun/view/desktop-window-snapshot.ts +41 -0
- package/src/renderers/electrobun/view/dialog-host.tsx +62 -0
- package/src/renderers/electrobun/view/http-fetch.ts +113 -0
- package/src/renderers/electrobun/view/input-host.tsx +65 -0
- package/src/renderers/electrobun/view/key-event.test.ts +34 -0
- package/src/renderers/electrobun/view/key-event.ts +134 -0
- package/src/renderers/electrobun/view/main.tsx +104 -0
- package/src/renderers/electrobun/view/native-renderer.ts +110 -0
- package/src/renderers/electrobun/view/native-stubs/chart-kitty-support.ts +9 -0
- package/src/renderers/electrobun/view/native-stubs/chart-surface-manager.ts +25 -0
- package/src/renderers/electrobun/view/native-stubs/chart-surface-sync.ts +1 -0
- package/src/renderers/electrobun/view/notes-files.ts +56 -0
- package/src/renderers/electrobun/view/resource-store.test.ts +44 -0
- package/src/renderers/electrobun/view/resource-store.ts +106 -0
- package/src/renderers/electrobun/view/rpc-codec.ts +42 -0
- package/src/renderers/electrobun/view/scrollbar-activity.ts +23 -0
- package/src/renderers/electrobun/view/toast-host.tsx +65 -0
- package/src/renderers/electrobun/view/ui-host.tsx +1490 -0
- package/src/renderers/electrobun/view/update-host.ts +38 -0
- package/src/renderers/opentui/dialog-host.test.tsx +44 -0
- package/src/renderers/opentui/dialog-host.tsx +56 -0
- package/src/renderers/opentui/host.tsx +134 -0
- package/src/renderers/opentui/input-host.tsx +24 -0
- package/src/renderers/opentui/start.tsx +71 -0
- package/src/renderers/opentui/test-utils.tsx +76 -0
- package/src/renderers/opentui/toast-host.tsx +10 -0
- package/src/renderers/opentui/ui-host.tsx +97 -0
- package/src/sources/gloomberb-cloud.test.ts +288 -17
- package/src/sources/gloomberb-cloud.ts +176 -9
- package/src/sources/provider-errors.ts +1 -0
- package/src/sources/provider-router.test.ts +220 -81
- package/src/sources/provider-router.ts +294 -111
- package/src/sources/sec-edgar.ts +19 -6
- package/src/sources/yahoo-finance.test.ts +0 -5
- package/src/sources/yahoo-finance.ts +420 -11
- package/src/state/app-activity.ts +7 -7
- package/src/state/app-bootstrap.ts +114 -40
- package/src/state/app-context-selector.test.tsx +70 -1
- package/src/state/app-context.test.ts +71 -3
- package/src/state/app-context.test.tsx +14 -7
- package/src/state/app-context.tsx +205 -869
- package/src/state/config-save-scheduler.ts +35 -0
- package/src/state/persist-scheduler.test.ts +67 -0
- package/src/state/persist-scheduler.ts +69 -0
- package/src/state/selectors-ui.ts +55 -0
- package/src/state/selectors.ts +15 -71
- package/src/state/session-persistence.test.ts +1 -1
- package/src/state/ticker-refresh-queue.ts +52 -3
- package/src/state/use-inline-tickers.ts +18 -16
- package/src/state/use-quote-streaming.test.ts +0 -6
- package/src/state/use-quote-streaming.test.tsx +1 -1
- package/src/state/use-quote-streaming.ts +3 -2
- package/src/state/use-session-snapshot.ts +32 -17
- package/src/test-support/plugin-runtime.ts +149 -0
- package/src/theme/color-utils.ts +1 -0
- package/src/theme/colors.ts +16 -0
- package/src/types/broker.ts +16 -1
- package/src/types/capability-route-source.ts +23 -0
- package/src/types/config.test.ts +23 -0
- package/src/types/config.ts +145 -10
- package/src/types/context-menu.ts +95 -0
- package/src/types/data-provider.ts +34 -3
- package/src/types/desktop-menu.ts +13 -0
- package/src/types/desktop-window.ts +29 -0
- package/src/types/financials.ts +123 -2
- package/src/types/instrument.ts +1 -0
- package/src/types/news-source.ts +9 -0
- package/src/types/persistence.ts +1 -0
- package/src/types/plugin.ts +32 -8
- package/src/ui/ascii-font.ts +85 -0
- package/src/ui/context-menu.test.ts +94 -0
- package/src/ui/context-menu.tsx +433 -0
- package/src/ui/dialog.tsx +55 -0
- package/src/ui/host.tsx +361 -0
- package/src/ui/index.tsx +126 -0
- package/src/ui/toast.tsx +40 -0
- package/src/updater.test.ts +69 -0
- package/src/updater.ts +61 -18
- package/src/utils/api-client.test.ts +186 -6
- package/src/utils/api-client.ts +433 -33
- package/src/utils/back-navigation.ts +6 -0
- package/src/utils/cli-output.ts +1 -0
- package/src/utils/currency-units.test.ts +1 -6
- package/src/utils/currency-units.ts +1 -0
- package/src/utils/debug-log.ts +68 -0
- package/src/utils/dom-screenshot.ts +326 -0
- package/src/utils/exchange-rate-map.ts +1 -0
- package/src/utils/exchanges.ts +214 -0
- package/src/utils/format.test.ts +7 -0
- package/src/utils/format.ts +1 -0
- package/src/utils/fuzzy-search.ts +1 -0
- package/src/utils/html-entities.test.ts +8 -0
- package/src/utils/html-entities.ts +32 -0
- package/src/utils/http-transport.ts +11 -0
- package/src/utils/main-thread-monitor.ts +127 -0
- package/src/utils/perf-marks.ts +54 -0
- package/src/utils/quote-freshness.ts +27 -50
- package/src/utils/quote-resolution.test.ts +5 -5
- package/src/utils/quote-resolution.ts +0 -1
- package/src/utils/quote-time.ts +1 -5
- package/src/utils/sec.test.ts +0 -12
- package/src/utils/selection-clipboard.ts +14 -6
- package/src/utils/shortcut-labels.test.ts +20 -0
- package/src/utils/shortcut-labels.ts +67 -0
- package/src/utils/slugify.ts +1 -0
- package/src/utils/terminal-input-reset.ts +1 -0
- package/src/utils/text-wrap.ts +82 -0
- package/src/utils/throttled-fetch.test.ts +19 -0
- package/src/utils/throttled-fetch.ts +11 -9
- package/src/utils/ticker-list.test.ts +1 -7
- package/src/utils/ticker-list.ts +1 -0
- package/src/utils/timestamp.ts +29 -0
- package/src/utils/tweet-text.test.ts +13 -0
- package/src/utils/tweet-text.ts +18 -0
- package/src/utils/url.ts +11 -0
- package/src/version.ts +1 -1
- package/src/components/chart/chart-control-hint.tsx +0 -39
- package/src/components/detail-feed-view.test.tsx +0 -180
- package/src/components/detail-feed-view.tsx +0 -531
- package/src/components/layout/pixel-mouse-mode.test.ts +0 -122
- package/src/components/layout/pixel-mouse.test.ts +0 -40
- package/src/components/spinner.tsx +0 -2
- package/src/components/tab-bar.tsx +0 -17
- package/src/components/ui/multi-select-chips.tsx +0 -69
- package/src/plugins/builtin/ai/index.test.tsx +0 -139
- package/src/plugins/builtin/ask-ai.tsx +0 -9
- package/src/plugins/builtin/correlation/plugin.test.ts +0 -44
- package/src/plugins/builtin/correlation/settings.test.ts +0 -34
- package/src/plugins/builtin/econ/calendar-source.test.ts +0 -219
- package/src/plugins/builtin/econ/fred-client.ts +0 -79
- package/src/plugins/builtin/fred-settings.ts +0 -10
- package/src/plugins/builtin/options-availability.test.tsx +0 -179
- package/src/plugins/builtin/options-availability.ts +0 -158
- package/src/plugins/builtin/portfolio-list/metrics.test.ts +0 -61
- package/src/plugins/builtin/yield-curve/treasury-data.test.ts +0 -212
- package/src/plugins/prediction-markets/status.ts +0 -24
- package/src/plugins/prediction-markets/table.tsx +0 -87
- package/src/utils/quote-time.test.ts +0 -22
- package/src/utils/selection-clipboard.test.ts +0 -43
- package/src/utils/terminal-input-reset.test.ts +0 -50
|
@@ -3,9 +3,12 @@ import { existsSync, rmSync } from "fs";
|
|
|
3
3
|
import { tmpdir } from "os";
|
|
4
4
|
import { join } from "path";
|
|
5
5
|
import { AppPersistence } from "../data/app-persistence";
|
|
6
|
-
import {
|
|
6
|
+
import { AssetDataRouter } from "./provider-router";
|
|
7
|
+
import { assetDataProvider } from "../capabilities";
|
|
7
8
|
import type { BrokerAdapter } from "../types/broker";
|
|
8
9
|
import type { DataProvider, QuoteSubscriptionTarget } from "../types/data-provider";
|
|
10
|
+
import type { CapabilityRouteSource } from "../types/capability-route-source";
|
|
11
|
+
import type { NewsArticle } from "../news/types";
|
|
9
12
|
import { cloneLayout, CURRENT_CONFIG_VERSION, DEFAULT_LAYOUT } from "../types/config";
|
|
10
13
|
|
|
11
14
|
const originalConsoleError = console.error;
|
|
@@ -46,9 +49,6 @@ const fallbackProvider: DataProvider = {
|
|
|
46
49
|
async search() {
|
|
47
50
|
return [];
|
|
48
51
|
},
|
|
49
|
-
async getNews() {
|
|
50
|
-
return [];
|
|
51
|
-
},
|
|
52
52
|
async getArticleSummary() {
|
|
53
53
|
return null;
|
|
54
54
|
},
|
|
@@ -57,9 +57,146 @@ const fallbackProvider: DataProvider = {
|
|
|
57
57
|
},
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
function makeArticle(id: string): NewsArticle {
|
|
61
|
+
return {
|
|
62
|
+
id,
|
|
63
|
+
title: id,
|
|
64
|
+
url: `https://example.com/${id}`,
|
|
65
|
+
source: "Test",
|
|
66
|
+
publishedAt: new Date(),
|
|
67
|
+
topic: "general",
|
|
68
|
+
topics: ["general"],
|
|
69
|
+
sectors: [],
|
|
70
|
+
categories: [],
|
|
71
|
+
tickers: [],
|
|
72
|
+
scores: {
|
|
73
|
+
importance: 50,
|
|
74
|
+
urgency: 0,
|
|
75
|
+
marketImpact: 50,
|
|
76
|
+
novelty: 0,
|
|
77
|
+
confidence: 0,
|
|
78
|
+
},
|
|
79
|
+
importance: 50,
|
|
80
|
+
isBreaking: false,
|
|
81
|
+
isDeveloping: false,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
describe("AssetDataRouter", () => {
|
|
86
|
+
test("routes market calls only through sources with market capability", async () => {
|
|
87
|
+
const newsOnlySource: CapabilityRouteSource = {
|
|
88
|
+
id: "news-only",
|
|
89
|
+
name: "News Only",
|
|
90
|
+
priority: 1,
|
|
91
|
+
news: {
|
|
92
|
+
fetchNews: async () => {
|
|
93
|
+
throw new Error("news-only source should not handle market requests");
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
const marketSource: CapabilityRouteSource = {
|
|
98
|
+
id: "market-source",
|
|
99
|
+
name: "Market Source",
|
|
100
|
+
priority: 10,
|
|
101
|
+
market: {
|
|
102
|
+
...fallbackProvider,
|
|
103
|
+
id: "market-source",
|
|
104
|
+
name: "Market Source",
|
|
105
|
+
async getQuote(symbol) {
|
|
106
|
+
return {
|
|
107
|
+
symbol,
|
|
108
|
+
providerId: "market-source",
|
|
109
|
+
price: 250,
|
|
110
|
+
currency: "USD",
|
|
111
|
+
change: 1,
|
|
112
|
+
changePercent: 0.4,
|
|
113
|
+
lastUpdated: Date.now(),
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const router = new AssetDataRouter(fallbackProvider, [newsOnlySource, marketSource]);
|
|
120
|
+
|
|
121
|
+
const quote = await router.getQuote("AAPL", "NASDAQ");
|
|
122
|
+
|
|
123
|
+
expect(quote.providerId).toBe("market-source");
|
|
124
|
+
expect(quote.price).toBe(250);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("routes news calls only through sources with news capability", async () => {
|
|
128
|
+
const article = makeArticle("source-news");
|
|
129
|
+
const marketOnlySource: CapabilityRouteSource = {
|
|
130
|
+
id: "market-only",
|
|
131
|
+
name: "Market Only",
|
|
132
|
+
priority: 1,
|
|
133
|
+
market: {
|
|
134
|
+
...fallbackProvider,
|
|
135
|
+
id: "market-only",
|
|
136
|
+
name: "Market Only",
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
const newsSource: CapabilityRouteSource = {
|
|
140
|
+
id: "news-source",
|
|
141
|
+
name: "News Source",
|
|
142
|
+
priority: 10,
|
|
143
|
+
news: {
|
|
144
|
+
fetchNews: async () => [article],
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
const router = new AssetDataRouter(null, [marketOnlySource, newsSource]);
|
|
148
|
+
|
|
149
|
+
const articles = await router.getNews({ feed: "latest", limit: 10 });
|
|
150
|
+
|
|
151
|
+
expect(articles).toEqual([article]);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("falls back to later providers when holder data is empty", async () => {
|
|
155
|
+
const cloudProvider: DataProvider = {
|
|
156
|
+
...fallbackProvider,
|
|
157
|
+
id: "cloud",
|
|
158
|
+
name: "Cloud",
|
|
159
|
+
priority: 100,
|
|
160
|
+
async getHolders(symbol) {
|
|
161
|
+
return {
|
|
162
|
+
providerId: "cloud",
|
|
163
|
+
symbol,
|
|
164
|
+
holders: [],
|
|
165
|
+
};
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
const yahooProvider: DataProvider = {
|
|
169
|
+
...fallbackProvider,
|
|
170
|
+
id: "yahoo",
|
|
171
|
+
name: "Yahoo",
|
|
172
|
+
priority: 1000,
|
|
173
|
+
async getHolders(symbol) {
|
|
174
|
+
return {
|
|
175
|
+
providerId: "yahoo",
|
|
176
|
+
symbol,
|
|
177
|
+
currency: "USD",
|
|
178
|
+
holders: [{
|
|
179
|
+
providerId: "yahoo",
|
|
180
|
+
ownerType: "institution",
|
|
181
|
+
name: "Vanguard Group Inc",
|
|
182
|
+
shares: 1_200_000_000,
|
|
183
|
+
value: 250_000_000_000,
|
|
184
|
+
percentHeld: 0.08,
|
|
185
|
+
changePercent: 0.02,
|
|
186
|
+
}],
|
|
187
|
+
};
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const router = new AssetDataRouter(yahooProvider, [cloudProvider]);
|
|
192
|
+
const holders = await router.getHolders("AAPL", "NASDAQ");
|
|
193
|
+
|
|
194
|
+
expect(holders.providerId).toBe("yahoo");
|
|
195
|
+
expect(holders.holders[0]?.name).toBe("Vanguard Group Inc");
|
|
196
|
+
});
|
|
197
|
+
|
|
61
198
|
test("prefers broker quotes over fallback quotes", async () => {
|
|
62
|
-
const router = new
|
|
199
|
+
const router = new AssetDataRouter(fallbackProvider);
|
|
63
200
|
const broker: BrokerAdapter = {
|
|
64
201
|
id: "ibkr",
|
|
65
202
|
name: "IBKR",
|
|
@@ -84,7 +221,7 @@ describe("ProviderRouter", () => {
|
|
|
84
221
|
|
|
85
222
|
router.attachRegistry({
|
|
86
223
|
brokers: new Map([["ibkr", broker]]),
|
|
87
|
-
|
|
224
|
+
getEnabledCapabilities: () => [],
|
|
88
225
|
} as any);
|
|
89
226
|
router.setConfigAccessor(() => ({
|
|
90
227
|
dataDir: "",
|
|
@@ -120,7 +257,7 @@ describe("ProviderRouter", () => {
|
|
|
120
257
|
});
|
|
121
258
|
|
|
122
259
|
test("prefers the requested broker instance in search results", async () => {
|
|
123
|
-
const router = new
|
|
260
|
+
const router = new AssetDataRouter(fallbackProvider);
|
|
124
261
|
const broker: BrokerAdapter = {
|
|
125
262
|
id: "ibkr",
|
|
126
263
|
name: "IBKR",
|
|
@@ -149,7 +286,7 @@ describe("ProviderRouter", () => {
|
|
|
149
286
|
|
|
150
287
|
router.attachRegistry({
|
|
151
288
|
brokers: new Map([["ibkr", broker]]),
|
|
152
|
-
|
|
289
|
+
getEnabledCapabilities: () => [],
|
|
153
290
|
} as any);
|
|
154
291
|
router.setConfigAccessor(() => ({
|
|
155
292
|
dataDir: "",
|
|
@@ -239,11 +376,11 @@ describe("ProviderRouter", () => {
|
|
|
239
376
|
}];
|
|
240
377
|
},
|
|
241
378
|
};
|
|
242
|
-
const router = new
|
|
379
|
+
const router = new AssetDataRouter(fallbackProvider, [provider]);
|
|
243
380
|
|
|
244
381
|
router.attachRegistry({
|
|
245
382
|
brokers: new Map([["ibkr", broker]]),
|
|
246
|
-
|
|
383
|
+
getEnabledCapabilities: () => [],
|
|
247
384
|
} as any);
|
|
248
385
|
router.setConfigAccessor(() => ({
|
|
249
386
|
dataDir: "",
|
|
@@ -280,46 +417,47 @@ describe("ProviderRouter", () => {
|
|
|
280
417
|
expect(results[0]?.brokerContract?.localSymbol).toBe("AAPL");
|
|
281
418
|
});
|
|
282
419
|
|
|
283
|
-
test("ignores disabled plugin
|
|
284
|
-
const
|
|
285
|
-
|
|
420
|
+
test("ignores disabled plugin sources when the registry filters them out", async () => {
|
|
421
|
+
const router = new AssetDataRouter(fallbackProvider);
|
|
422
|
+
router.attachRegistry({
|
|
423
|
+
brokers: new Map(),
|
|
424
|
+
getEnabledCapabilities: () => [],
|
|
425
|
+
} as any);
|
|
426
|
+
|
|
427
|
+
const quote = await router.getQuote("AAPL", "NASDAQ");
|
|
428
|
+
expect(quote.price).toBe(100);
|
|
429
|
+
expect(quote.providerId).toBeUndefined();
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
test("routes through registered asset-data capabilities", async () => {
|
|
433
|
+
const cloudProvider: DataProvider = {
|
|
434
|
+
...fallbackProvider,
|
|
435
|
+
id: "cloud",
|
|
286
436
|
name: "Cloud",
|
|
287
437
|
priority: 10,
|
|
288
|
-
async getTickerFinancials() {
|
|
289
|
-
throw new Error("disabled provider should not be used");
|
|
290
|
-
},
|
|
291
438
|
async getQuote() {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
throw new Error("disabled provider should not be used");
|
|
302
|
-
},
|
|
303
|
-
async getArticleSummary() {
|
|
304
|
-
throw new Error("disabled provider should not be used");
|
|
305
|
-
},
|
|
306
|
-
async getPriceHistory() {
|
|
307
|
-
throw new Error("disabled provider should not be used");
|
|
439
|
+
return {
|
|
440
|
+
symbol: "AAPL",
|
|
441
|
+
providerId: "cloud",
|
|
442
|
+
price: 125,
|
|
443
|
+
currency: "USD",
|
|
444
|
+
change: 1,
|
|
445
|
+
changePercent: 0.8,
|
|
446
|
+
lastUpdated: Date.now(),
|
|
447
|
+
};
|
|
308
448
|
},
|
|
309
449
|
};
|
|
310
|
-
|
|
311
|
-
const router = new ProviderRouter(fallbackProvider);
|
|
450
|
+
const router = new AssetDataRouter(fallbackProvider);
|
|
312
451
|
router.attachRegistry({
|
|
313
452
|
brokers: new Map(),
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
},
|
|
453
|
+
getEnabledCapabilities: (kind?: string) => (
|
|
454
|
+
kind === "asset-data" ? [assetDataProvider(cloudProvider)] : []
|
|
455
|
+
),
|
|
318
456
|
} as any);
|
|
319
457
|
|
|
320
458
|
const quote = await router.getQuote("AAPL", "NASDAQ");
|
|
321
|
-
expect(quote.price).toBe(
|
|
322
|
-
expect(quote.providerId).
|
|
459
|
+
expect(quote.price).toBe(125);
|
|
460
|
+
expect(quote.providerId).toBe("cloud");
|
|
323
461
|
});
|
|
324
462
|
|
|
325
463
|
test("prefers provider quote streams when one is available", () => {
|
|
@@ -373,10 +511,10 @@ describe("ProviderRouter", () => {
|
|
|
373
511
|
},
|
|
374
512
|
};
|
|
375
513
|
|
|
376
|
-
const router = new
|
|
514
|
+
const router = new AssetDataRouter(fallbackProvider, [streamingProvider]);
|
|
377
515
|
router.attachRegistry({
|
|
378
516
|
brokers: new Map([["ibkr", broker]]),
|
|
379
|
-
|
|
517
|
+
getEnabledCapabilities: () => [],
|
|
380
518
|
} as any);
|
|
381
519
|
router.setConfigAccessor(() => ({
|
|
382
520
|
dataDir: "",
|
|
@@ -476,10 +614,10 @@ describe("ProviderRouter", () => {
|
|
|
476
614
|
},
|
|
477
615
|
};
|
|
478
616
|
|
|
479
|
-
const router = new
|
|
617
|
+
const router = new AssetDataRouter(fallbackProvider, [streamingProvider]);
|
|
480
618
|
router.attachRegistry({
|
|
481
619
|
brokers: new Map([["ibkr", broker]]),
|
|
482
|
-
|
|
620
|
+
getEnabledCapabilities: () => [],
|
|
483
621
|
} as any);
|
|
484
622
|
router.setConfigAccessor(() => ({
|
|
485
623
|
dataDir: "",
|
|
@@ -538,7 +676,7 @@ describe("ProviderRouter", () => {
|
|
|
538
676
|
const dbPath = createTempDbPath("cache-merge");
|
|
539
677
|
const persistence = new AppPersistence(dbPath);
|
|
540
678
|
const providerCalls = { broker: 0, fallback: 0 };
|
|
541
|
-
const router = new
|
|
679
|
+
const router = new AssetDataRouter({
|
|
542
680
|
...fallbackProvider,
|
|
543
681
|
async getTickerFinancials() {
|
|
544
682
|
providerCalls.fallback += 1;
|
|
@@ -581,7 +719,7 @@ describe("ProviderRouter", () => {
|
|
|
581
719
|
|
|
582
720
|
router.attachRegistry({
|
|
583
721
|
brokers: new Map([["ibkr", broker]]),
|
|
584
|
-
|
|
722
|
+
getEnabledCapabilities: () => [],
|
|
585
723
|
} as any);
|
|
586
724
|
router.setConfigAccessor(() => ({
|
|
587
725
|
dataDir: "",
|
|
@@ -633,7 +771,7 @@ describe("ProviderRouter", () => {
|
|
|
633
771
|
});
|
|
634
772
|
|
|
635
773
|
test("keeps broker price authority when a fallback quote is off by a likely 100x unit mismatch", async () => {
|
|
636
|
-
const router = new
|
|
774
|
+
const router = new AssetDataRouter({
|
|
637
775
|
...fallbackProvider,
|
|
638
776
|
async getTickerFinancials() {
|
|
639
777
|
return {
|
|
@@ -648,7 +786,7 @@ describe("ProviderRouter", () => {
|
|
|
648
786
|
change: -0.021,
|
|
649
787
|
changePercent: -7.89,
|
|
650
788
|
lastUpdated: Date.now(),
|
|
651
|
-
dataSource: "
|
|
789
|
+
dataSource: "delayed",
|
|
652
790
|
},
|
|
653
791
|
fundamentals: { revenue: 1000 },
|
|
654
792
|
};
|
|
@@ -686,7 +824,7 @@ describe("ProviderRouter", () => {
|
|
|
686
824
|
|
|
687
825
|
router.attachRegistry({
|
|
688
826
|
brokers: new Map([["ibkr", broker]]),
|
|
689
|
-
|
|
827
|
+
getEnabledCapabilities: () => [],
|
|
690
828
|
} as any);
|
|
691
829
|
router.setConfigAccessor(() => ({
|
|
692
830
|
dataDir: "",
|
|
@@ -729,7 +867,7 @@ describe("ProviderRouter", () => {
|
|
|
729
867
|
});
|
|
730
868
|
|
|
731
869
|
test("preserves fallback profile data when broker already has fundamentals", async () => {
|
|
732
|
-
const router = new
|
|
870
|
+
const router = new AssetDataRouter({
|
|
733
871
|
...fallbackProvider,
|
|
734
872
|
async getTickerFinancials() {
|
|
735
873
|
return {
|
|
@@ -766,7 +904,7 @@ describe("ProviderRouter", () => {
|
|
|
766
904
|
|
|
767
905
|
router.attachRegistry({
|
|
768
906
|
brokers: new Map([["ibkr", broker]]),
|
|
769
|
-
|
|
907
|
+
getEnabledCapabilities: () => [],
|
|
770
908
|
} as any);
|
|
771
909
|
router.setConfigAccessor(() => ({
|
|
772
910
|
dataDir: "",
|
|
@@ -857,7 +995,7 @@ describe("ProviderRouter", () => {
|
|
|
857
995
|
},
|
|
858
996
|
};
|
|
859
997
|
|
|
860
|
-
const router = new
|
|
998
|
+
const router = new AssetDataRouter(yahooProvider, [cloudProvider]);
|
|
861
999
|
const merged = await router.getTickerFinancials("AAPL", "NASDAQ");
|
|
862
1000
|
|
|
863
1001
|
expect(merged.quote?.price).toBe(125);
|
|
@@ -924,13 +1062,13 @@ describe("ProviderRouter", () => {
|
|
|
924
1062
|
},
|
|
925
1063
|
};
|
|
926
1064
|
|
|
927
|
-
const seedRouter = new
|
|
1065
|
+
const seedRouter = new AssetDataRouter(yahooProvider, [cloudProvider], persistence.resources);
|
|
928
1066
|
const seeded = await seedRouter.getTickerFinancials("AAPL", "NASDAQ");
|
|
929
1067
|
expect(seeded.quote?.marketCap).toBe(2_000_000_000);
|
|
930
1068
|
|
|
931
1069
|
let cloudCalls = 0;
|
|
932
1070
|
let yahooCalls = 0;
|
|
933
|
-
const cachedRouter = new
|
|
1071
|
+
const cachedRouter = new AssetDataRouter({
|
|
934
1072
|
...yahooProvider,
|
|
935
1073
|
async getTickerFinancials() {
|
|
936
1074
|
yahooCalls += 1;
|
|
@@ -1011,7 +1149,7 @@ describe("ProviderRouter", () => {
|
|
|
1011
1149
|
recentTickers: [],
|
|
1012
1150
|
};
|
|
1013
1151
|
|
|
1014
|
-
const seedRouter = new
|
|
1152
|
+
const seedRouter = new AssetDataRouter({
|
|
1015
1153
|
...fallbackProvider,
|
|
1016
1154
|
async getTickerFinancials() {
|
|
1017
1155
|
return {
|
|
@@ -1023,7 +1161,7 @@ describe("ProviderRouter", () => {
|
|
|
1023
1161
|
}, [], persistence.resources);
|
|
1024
1162
|
seedRouter.attachRegistry({
|
|
1025
1163
|
brokers: new Map([["ibkr", broker]]),
|
|
1026
|
-
|
|
1164
|
+
getEnabledCapabilities: () => [],
|
|
1027
1165
|
} as any);
|
|
1028
1166
|
seedRouter.setConfigAccessor(() => config);
|
|
1029
1167
|
|
|
@@ -1033,7 +1171,7 @@ describe("ProviderRouter", () => {
|
|
|
1033
1171
|
});
|
|
1034
1172
|
expect(seeded.profile).toBeUndefined();
|
|
1035
1173
|
|
|
1036
|
-
const refreshedRouter = new
|
|
1174
|
+
const refreshedRouter = new AssetDataRouter({
|
|
1037
1175
|
...fallbackProvider,
|
|
1038
1176
|
async getTickerFinancials() {
|
|
1039
1177
|
return {
|
|
@@ -1050,7 +1188,7 @@ describe("ProviderRouter", () => {
|
|
|
1050
1188
|
}, [], persistence.resources);
|
|
1051
1189
|
refreshedRouter.attachRegistry({
|
|
1052
1190
|
brokers: new Map([["ibkr", broker]]),
|
|
1053
|
-
|
|
1191
|
+
getEnabledCapabilities: () => [],
|
|
1054
1192
|
} as any);
|
|
1055
1193
|
refreshedRouter.setConfigAccessor(() => config);
|
|
1056
1194
|
|
|
@@ -1067,7 +1205,7 @@ describe("ProviderRouter", () => {
|
|
|
1067
1205
|
test("does not reuse another broker instance's financials when a specific instance is requested", async () => {
|
|
1068
1206
|
const dbPath = createTempDbPath("instance-scoped-financials");
|
|
1069
1207
|
const persistence = new AppPersistence(dbPath);
|
|
1070
|
-
const router = new
|
|
1208
|
+
const router = new AssetDataRouter({
|
|
1071
1209
|
...fallbackProvider,
|
|
1072
1210
|
async getTickerFinancials() {
|
|
1073
1211
|
return {
|
|
@@ -1082,7 +1220,7 @@ describe("ProviderRouter", () => {
|
|
|
1082
1220
|
change: 0,
|
|
1083
1221
|
changePercent: 0,
|
|
1084
1222
|
lastUpdated: Date.now(),
|
|
1085
|
-
dataSource: "
|
|
1223
|
+
dataSource: "delayed",
|
|
1086
1224
|
},
|
|
1087
1225
|
};
|
|
1088
1226
|
},
|
|
@@ -1121,7 +1259,7 @@ describe("ProviderRouter", () => {
|
|
|
1121
1259
|
|
|
1122
1260
|
router.attachRegistry({
|
|
1123
1261
|
brokers: new Map([["ibkr", broker]]),
|
|
1124
|
-
|
|
1262
|
+
getEnabledCapabilities: () => [],
|
|
1125
1263
|
} as any);
|
|
1126
1264
|
router.setConfigAccessor(() => ({
|
|
1127
1265
|
dataDir: "",
|
|
@@ -1235,13 +1373,14 @@ describe("ProviderRouter", () => {
|
|
|
1235
1373
|
priceHistory: [],
|
|
1236
1374
|
quote: {
|
|
1237
1375
|
symbol: "IQE.L",
|
|
1376
|
+
providerId: "yahoo",
|
|
1238
1377
|
price: 0.231,
|
|
1239
1378
|
currency: "GBP",
|
|
1240
1379
|
change: -0.014,
|
|
1241
1380
|
changePercent: -5.71,
|
|
1242
1381
|
previousClose: 0.245,
|
|
1243
1382
|
lastUpdated: now,
|
|
1244
|
-
dataSource: "
|
|
1383
|
+
dataSource: "delayed",
|
|
1245
1384
|
},
|
|
1246
1385
|
fundamentals: {
|
|
1247
1386
|
revenue: 1000,
|
|
@@ -1271,7 +1410,7 @@ describe("ProviderRouter", () => {
|
|
|
1271
1410
|
throw new Error("should not fetch cloud financials");
|
|
1272
1411
|
},
|
|
1273
1412
|
};
|
|
1274
|
-
const router = new
|
|
1413
|
+
const router = new AssetDataRouter(yahooProvider, [cloudProvider], persistence.resources);
|
|
1275
1414
|
|
|
1276
1415
|
const financials = await router.getTickerFinancials("IQE", "LSE", {
|
|
1277
1416
|
instrument: {
|
|
@@ -1356,7 +1495,7 @@ describe("ProviderRouter", () => {
|
|
|
1356
1495
|
exchangeName: "FWB2",
|
|
1357
1496
|
listingExchangeName: "FWB2",
|
|
1358
1497
|
providerId: "yahoo",
|
|
1359
|
-
dataSource: "
|
|
1498
|
+
dataSource: "delayed",
|
|
1360
1499
|
},
|
|
1361
1500
|
},
|
|
1362
1501
|
{
|
|
@@ -1365,7 +1504,7 @@ describe("ProviderRouter", () => {
|
|
|
1365
1504
|
},
|
|
1366
1505
|
);
|
|
1367
1506
|
|
|
1368
|
-
const router = new
|
|
1507
|
+
const router = new AssetDataRouter({
|
|
1369
1508
|
...fallbackProvider,
|
|
1370
1509
|
id: "yahoo",
|
|
1371
1510
|
name: "Yahoo",
|
|
@@ -1398,7 +1537,7 @@ describe("ProviderRouter", () => {
|
|
|
1398
1537
|
test("drops cached premarket cloud quotes that lack an active-session price", () => {
|
|
1399
1538
|
const dbPath = createTempDbPath("stale-cloud-premarket-cache");
|
|
1400
1539
|
const persistence = new AppPersistence(dbPath);
|
|
1401
|
-
const now = Date.
|
|
1540
|
+
const now = Date.now();
|
|
1402
1541
|
|
|
1403
1542
|
persistence.resources.set(
|
|
1404
1543
|
{
|
|
@@ -1457,7 +1596,7 @@ describe("ProviderRouter", () => {
|
|
|
1457
1596
|
exchangeName: "NASDAQ",
|
|
1458
1597
|
listingExchangeName: "NASDAQ",
|
|
1459
1598
|
providerId: "yahoo",
|
|
1460
|
-
dataSource: "
|
|
1599
|
+
dataSource: "delayed",
|
|
1461
1600
|
},
|
|
1462
1601
|
},
|
|
1463
1602
|
{
|
|
@@ -1466,7 +1605,7 @@ describe("ProviderRouter", () => {
|
|
|
1466
1605
|
},
|
|
1467
1606
|
);
|
|
1468
1607
|
|
|
1469
|
-
const router = new
|
|
1608
|
+
const router = new AssetDataRouter({
|
|
1470
1609
|
...fallbackProvider,
|
|
1471
1610
|
id: "yahoo",
|
|
1472
1611
|
name: "Yahoo",
|
|
@@ -1503,7 +1642,7 @@ describe("ProviderRouter", () => {
|
|
|
1503
1642
|
throw new Error('[404] {"chart":{"result":null,"error":{"code":"Not Found","description":"No data found, symbol may be delisted"}}}');
|
|
1504
1643
|
},
|
|
1505
1644
|
};
|
|
1506
|
-
const router = new
|
|
1645
|
+
const router = new AssetDataRouter(fallbackProvider, [noisyProvider]);
|
|
1507
1646
|
const logged: unknown[][] = [];
|
|
1508
1647
|
console.error = (...args: unknown[]) => {
|
|
1509
1648
|
logged.push(args);
|
|
@@ -1538,11 +1677,11 @@ describe("ProviderRouter", () => {
|
|
|
1538
1677
|
},
|
|
1539
1678
|
};
|
|
1540
1679
|
|
|
1541
|
-
const seedRouter = new
|
|
1680
|
+
const seedRouter = new AssetDataRouter(yahooProvider, [cloudProvider], persistence.resources);
|
|
1542
1681
|
const seeded = await seedRouter.getPriceHistory("AAPL", "NASDAQ", "1Y");
|
|
1543
1682
|
expect(seeded[0]?.close).toBe(101);
|
|
1544
1683
|
|
|
1545
|
-
const cachedRouter = new
|
|
1684
|
+
const cachedRouter = new AssetDataRouter(yahooProvider, [cloudProvider], persistence.resources);
|
|
1546
1685
|
const cached = await cachedRouter.getPriceHistory("AAPL", "NASDAQ", "1Y");
|
|
1547
1686
|
expect(cached[0]?.close).toBe(101);
|
|
1548
1687
|
|
|
@@ -1550,7 +1689,7 @@ describe("ProviderRouter", () => {
|
|
|
1550
1689
|
});
|
|
1551
1690
|
|
|
1552
1691
|
test("sorts reversed chart history into chronological order", async () => {
|
|
1553
|
-
const router = new
|
|
1692
|
+
const router = new AssetDataRouter({
|
|
1554
1693
|
...fallbackProvider,
|
|
1555
1694
|
id: "cloud",
|
|
1556
1695
|
name: "Cloud",
|
|
@@ -1590,7 +1729,7 @@ describe("ProviderRouter", () => {
|
|
|
1590
1729
|
);
|
|
1591
1730
|
|
|
1592
1731
|
let providerCalls = 0;
|
|
1593
|
-
const router = new
|
|
1732
|
+
const router = new AssetDataRouter({
|
|
1594
1733
|
...fallbackProvider,
|
|
1595
1734
|
id: "yahoo",
|
|
1596
1735
|
name: "Yahoo",
|
|
@@ -1615,7 +1754,7 @@ describe("ProviderRouter", () => {
|
|
|
1615
1754
|
const dbPath = createTempDbPath("forced-financial-refresh");
|
|
1616
1755
|
const persistence = new AppPersistence(dbPath);
|
|
1617
1756
|
|
|
1618
|
-
const seedRouter = new
|
|
1757
|
+
const seedRouter = new AssetDataRouter({
|
|
1619
1758
|
...fallbackProvider,
|
|
1620
1759
|
async getTickerFinancials() {
|
|
1621
1760
|
return {
|
|
@@ -1636,7 +1775,7 @@ describe("ProviderRouter", () => {
|
|
|
1636
1775
|
await seedRouter.getTickerFinancials("AAPL", "NASDAQ");
|
|
1637
1776
|
|
|
1638
1777
|
let providerCalls = 0;
|
|
1639
|
-
const refreshRouter = new
|
|
1778
|
+
const refreshRouter = new AssetDataRouter({
|
|
1640
1779
|
...fallbackProvider,
|
|
1641
1780
|
async getTickerFinancials() {
|
|
1642
1781
|
providerCalls += 1;
|
|
@@ -1669,7 +1808,7 @@ describe("ProviderRouter", () => {
|
|
|
1669
1808
|
const dbPath = createTempDbPath("stale-chart-refresh");
|
|
1670
1809
|
const persistence = new AppPersistence(dbPath);
|
|
1671
1810
|
|
|
1672
|
-
const seedRouter = new
|
|
1811
|
+
const seedRouter = new AssetDataRouter({
|
|
1673
1812
|
...fallbackProvider,
|
|
1674
1813
|
async getPriceHistory() {
|
|
1675
1814
|
return [{ date: new Date("2026-03-27T00:00:00Z"), close: 101 }];
|
|
@@ -1682,7 +1821,7 @@ describe("ProviderRouter", () => {
|
|
|
1682
1821
|
.run(Date.now() - 1, "market", "price-history", "AAPL");
|
|
1683
1822
|
|
|
1684
1823
|
let providerCalls = 0;
|
|
1685
|
-
const refreshRouter = new
|
|
1824
|
+
const refreshRouter = new AssetDataRouter({
|
|
1686
1825
|
...fallbackProvider,
|
|
1687
1826
|
async getPriceHistory() {
|
|
1688
1827
|
providerCalls += 1;
|
|
@@ -1718,7 +1857,7 @@ describe("ProviderRouter", () => {
|
|
|
1718
1857
|
},
|
|
1719
1858
|
};
|
|
1720
1859
|
|
|
1721
|
-
const router = new
|
|
1860
|
+
const router = new AssetDataRouter(yahooProvider, [cloudProvider]);
|
|
1722
1861
|
const history = await router.getPriceHistoryForResolution("AAPL", "NASDAQ", "1Y", "1d");
|
|
1723
1862
|
|
|
1724
1863
|
expect(history[0]?.close).toBe(102);
|
|
@@ -1744,7 +1883,7 @@ describe("ProviderRouter", () => {
|
|
|
1744
1883
|
},
|
|
1745
1884
|
};
|
|
1746
1885
|
|
|
1747
|
-
const router = new
|
|
1886
|
+
const router = new AssetDataRouter(yahooProvider, [cloudProvider]);
|
|
1748
1887
|
expect(await router.getChartResolutionCapabilities("AAPL", "NASDAQ")).toEqual(["1d", "1wk"]);
|
|
1749
1888
|
});
|
|
1750
1889
|
|
|
@@ -1768,7 +1907,7 @@ describe("ProviderRouter", () => {
|
|
|
1768
1907
|
},
|
|
1769
1908
|
};
|
|
1770
1909
|
|
|
1771
|
-
const router = new
|
|
1910
|
+
const router = new AssetDataRouter(yahooProvider, [cloudProvider]);
|
|
1772
1911
|
const history = await router.getDetailedPriceHistory(
|
|
1773
1912
|
"AAPL",
|
|
1774
1913
|
"NASDAQ",
|