gloomberb 0.17.6 → 0.18.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 +2 -0
- package/README.zh-CN.md +2 -0
- package/package.json +6 -1
- package/patches/electrobun@1.18.1.patch +74 -0
- package/src/account/auth-model.ts +3 -2
- package/src/account/device-signin-dialog.tsx +38 -13
- package/src/account/device-signin.ts +2 -1
- package/src/account/upgrade-dialog.tsx +6 -2
- package/src/api-client/askg.ts +5 -3
- package/src/api-client/bank-figures.ts +73 -0
- package/src/api-client/credit-boards.ts +21 -1
- package/src/api-client/crypto-markets.ts +88 -1
- package/src/api-client/data.ts +104 -4
- package/src/api-client/debt-maturities.ts +18 -0
- package/src/api-client/equity-screener.ts +3 -0
- package/src/api-client/errors.ts +13 -0
- package/src/api-client/fund-facts.ts +72 -0
- package/src/api-client/futures-curve.ts +26 -3
- package/src/api-client/geo.ts +11 -1
- package/src/api-client/index.ts +25 -2
- package/src/api-client/insider-signals.ts +95 -11
- package/src/api-client/normalizers.ts +1 -0
- package/src/api-client/paths.ts +20 -1
- package/src/api-client/request.ts +11 -3
- package/src/api-client/research-activity.ts +15 -8
- package/src/api-client/socket.ts +6 -3
- package/src/api-client/supply-chain-graph.ts +2 -2
- package/src/api-client/supply-chain.ts +8 -0
- package/src/api-client/tape.ts +12 -0
- package/src/api-client/types.ts +66 -2
- package/src/api-client/upgrade-experiment.ts +4 -3
- package/src/api-client/wall-experiments.ts +5 -3
- package/src/api-client/wall-summary.ts +3 -1
- package/src/api-client/web-experiments.ts +5 -2
- package/src/api-client/yield-curves.ts +17 -0
- package/src/app/command-bar-intro.ts +38 -0
- package/src/app/keybindings/actions.ts +1 -1
- package/src/app/pane-runtime/pane-template-runtime.ts +35 -7
- package/src/app/pane-runtime/plugin-bindings.ts +4 -0
- package/src/app/pane-runtime/ticker-open-runtime.ts +4 -14
- package/src/app/pane-runtime/ticker-open-target.ts +31 -0
- package/src/app/runtime/desktop-deeplink.ts +3 -4
- package/src/app/runtime/plugin-bindings.ts +3 -2
- package/src/app/runtime/startup.ts +3 -4
- package/src/app/runtime/ticker-refresh.ts +13 -12
- package/src/app/runtime/update.ts +3 -2
- package/src/architecture/core-builtin-allowlist.json +0 -3
- package/src/architecture/silent-catch-allowlist.json +3 -0
- package/src/architecture/ui-literals-allowlist.json +186 -0
- package/src/architecture/ui-tokens-allowlist.json +40 -0
- package/src/ask-gloom/pane-context.ts +3 -1
- package/src/brokers/signed-in/catalog.ts +2 -1
- package/src/brokers/signed-in/client.ts +7 -2
- package/src/brokers/signed-in/sign-in-dialog.tsx +120 -26
- package/src/brokers/signed-in/sign-in.ts +22 -20
- package/src/brokers/sync-broker-instance.ts +2 -1
- package/src/capabilities/chart-series.ts +2 -0
- package/src/capabilities/registry.ts +1 -3
- package/src/cli/bank-section.ts +147 -0
- package/src/cli/base-converter.ts +5 -2
- package/src/cli/board-coins.ts +70 -0
- package/src/cli/commands/fred-series.ts +90 -0
- package/src/cli/commands/market.ts +340 -69
- package/src/cli/commands/news-search.ts +35 -2
- package/src/cli/commands/options-chain.ts +7 -5
- package/src/cli/commands/options-leaps.ts +3 -3
- package/src/cli/commands/overview.ts +78 -29
- package/src/cli/commands/plugins.ts +1 -0
- package/src/cli/commands/search.ts +87 -24
- package/src/cli/commands/system.ts +5 -2
- package/src/cli/commands/ticker.ts +337 -60
- package/src/cli/context.ts +2 -1
- package/src/cli/desktop-pane-shot.ts +11 -3
- package/src/cli/earnings-calendar.ts +2 -1
- package/src/cli/errors.ts +10 -0
- package/src/cli/export-source.ts +18 -3
- package/src/cli/glossary.ts +196 -14
- package/src/cli/history-rows.ts +60 -9
- package/src/cli/index-roots.ts +26 -0
- package/src/cli/index.ts +34 -9
- package/src/cli/known-gaps.ts +156 -22
- package/src/cli/listing-arg.ts +156 -12
- package/src/cli/listing-suggestion.ts +4 -2
- package/src/cli/name-entries.ts +76 -0
- package/src/cli/not-a-ticker.ts +15 -6
- package/src/cli/pane-functions/access-gate.ts +22 -0
- package/src/cli/pane-functions/capabilities.ts +62 -4
- package/src/cli/pane-functions/catalog.ts +94 -39
- package/src/cli/pane-functions/data.ts +6 -5
- package/src/cli/pane-functions/discovery.ts +1 -1
- package/src/cli/pane-functions/dom.ts +3 -3
- package/src/cli/pane-functions/freshness.ts +49 -8
- package/src/cli/pane-functions/function-help.ts +13 -10
- package/src/cli/pane-functions/headless.ts +177 -42
- package/src/cli/pane-functions/index.ts +64 -8
- package/src/cli/pane-functions/options.ts +17 -3
- package/src/cli/pane-functions/report-notices.ts +17 -3
- package/src/cli/pane-functions/resolver.ts +48 -13
- package/src/cli/pane-functions/screenshot.ts +124 -23
- package/src/cli/pane-functions/shot-options.ts +68 -0
- package/src/cli/perpetual-contract.ts +3 -1
- package/src/cli/report-tables.ts +10 -2
- package/src/cli/staged-tickers.ts +5 -0
- package/src/cli/statement-filings.ts +261 -0
- package/src/cli/symbol-args.ts +47 -8
- package/src/cli/symbol-list.ts +9 -2
- package/src/cli/symbol-rows.ts +269 -29
- package/src/cli/ticker-resolution.ts +148 -34
- package/src/cli/treasury-yields.ts +86 -0
- package/src/cli/types.ts +3 -0
- package/src/cli/uncovered-instrument.ts +20 -0
- package/src/components/chart/composite/axis-overlays.tsx +16 -9
- package/src/components/chart/composite/composite-chart.tsx +85 -6
- package/src/components/chart/composite/downsample.ts +19 -3
- package/src/components/chart/composite/format.ts +3 -0
- package/src/components/chart/composite/legend.tsx +155 -76
- package/src/components/chart/composite/panel-bitmap.ts +8 -2
- package/src/components/chart/composite/panel-surface.tsx +3 -3
- package/src/components/chart/composite/toolbar.tsx +12 -7
- package/src/components/chart/composite/types.ts +6 -0
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +28 -1
- package/src/components/chart/curve/curve-surface.tsx +1 -1
- package/src/components/chart/static/series.ts +39 -0
- package/src/components/chart/static/trail-chart-surface.tsx +2 -1
- package/src/components/command-bar/commands/collection-list.ts +45 -15
- package/src/components/command-bar/commands/collection.ts +10 -3
- package/src/components/command-bar/commands/registry.ts +2 -2
- package/src/components/command-bar/list/badge.ts +4 -3
- package/src/components/command-bar/list/model.ts +5 -0
- package/src/components/command-bar/list/view.tsx +1 -1
- package/src/components/command-bar/panel/index.tsx +19 -8
- package/src/components/command-bar/panel/native-surface.ts +6 -4
- package/src/components/command-bar/panel/prompt-example.ts +22 -0
- package/src/components/command-bar/routes/root/provider-search.ts +3 -9
- package/src/components/command-bar/routes/root/results.ts +13 -0
- package/src/components/command-bar/routes/root/start-here.ts +90 -0
- package/src/components/command-bar/routes/ticker-search/results.ts +42 -3
- package/src/components/command-bar/routes/ticker-search/route.ts +3 -10
- package/src/components/command-bar/theme-picker.tsx +3 -1
- package/src/components/command-bar/workflow/builtin.ts +5 -1
- package/src/components/command-bar/workflow/collection-actions.ts +21 -12
- package/src/components/command-bar/workflow/ops.ts +21 -1
- package/src/{plugins/builtin/portfolio-list/command-bar.ts → components/command-bar/workflow/portfolio-position.ts} +42 -9
- package/src/components/command-bar/workflow/tickers.ts +6 -4
- package/src/components/feedback-dialog.tsx +1 -1
- package/src/components/form-modal/broker-step.tsx +7 -63
- package/src/components/form-modal/content.tsx +7 -2
- package/src/components/form-modal/field-row.tsx +6 -8
- package/src/components/form-modal/host.tsx +3 -3
- package/src/components/form-modal/model.ts +3 -0
- package/src/components/form-modal/routes.ts +17 -2
- package/src/components/index.ts +6 -6
- package/src/components/input-search-bar.tsx +43 -6
- package/src/components/layout/detached-pane-shell.tsx +0 -1
- package/src/components/layout/floating-pane.tsx +0 -1
- package/src/components/layout/header.tsx +8 -7
- package/src/components/layout/pane/footer/index.tsx +7 -4
- package/src/components/layout/pane/footer/model.ts +28 -2
- package/src/components/layout/pane/header.tsx +9 -19
- package/src/components/layout/pane/index.tsx +0 -1
- package/src/components/layout/pane/instrument-coverage-gate.tsx +57 -0
- package/src/components/layout/pane/pane-tabs.tsx +4 -3
- package/src/components/layout/pane/sizing.ts +9 -0
- package/src/components/layout/pane/ticker-fate-gate.tsx +8 -2
- package/src/components/layout/shell/pane/capture.ts +44 -20
- package/src/components/layout/status-bar.tsx +3 -6
- package/src/components/markdown-text.tsx +17 -2
- package/src/components/market-board/index.tsx +19 -8
- package/src/components/metric-treemap/heat-surface.tsx +2 -7
- package/src/components/onboarding/account-step/qr-panel.tsx +5 -2
- package/src/components/onboarding/onboarding-frame.tsx +24 -21
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +20 -10
- package/src/components/onboarding/wizard-positions.ts +40 -11
- package/src/components/pane-settings-dialog/desktop.tsx +12 -50
- package/src/components/pane-settings-dialog/field-dialogs.tsx +1 -1
- package/src/components/price-performance.tsx +18 -2
- package/src/components/reader/article-reader.tsx +11 -10
- package/src/components/reader/detail-reader.tsx +9 -5
- package/src/components/reader/reader-text.tsx +51 -24
- package/src/components/sign-in-code-panel.tsx +129 -75
- package/src/components/ticker/badge/index.tsx +2 -1
- package/src/components/ui/action-row.tsx +5 -2
- package/src/components/ui/audio-player.tsx +70 -0
- package/src/components/ui/card.tsx +75 -0
- package/src/components/ui/data-table/index.tsx +4 -2
- package/src/components/ui/data-table/types.ts +11 -0
- package/src/components/ui/detail-scroll-body.tsx +1 -1
- package/src/components/ui/dialog-actions.tsx +1 -21
- package/src/components/ui/display.tsx +13 -5
- package/src/components/ui/external-link.tsx +4 -2
- package/src/components/ui/fields.tsx +12 -0
- package/src/components/ui/figure.tsx +5 -3
- package/src/components/ui/frame.tsx +40 -6
- package/src/components/ui/icon.tsx +4 -0
- package/src/components/ui/index.ts +4 -1
- package/src/components/ui/list-view.tsx +8 -0
- package/src/components/ui/prose.tsx +97 -25
- package/src/components/ui/query-bar-keys.tsx +3 -1
- package/src/components/ui/query-bar.tsx +6 -0
- package/src/components/ui/quote.ts +42 -0
- package/src/components/ui/range-track.tsx +15 -2
- package/src/components/ui/reveal-in-scroll-box.ts +21 -3
- package/src/components/ui/select-button.tsx +38 -3
- package/src/components/ui/status.tsx +73 -4
- package/src/components/ui/table-layout.ts +20 -0
- package/src/components/use-pinned-moment.ts +19 -0
- package/src/core/app-runtime.ts +2 -2
- package/src/core/state/app/instrument.ts +18 -1
- package/src/core/state/app/layout.ts +6 -0
- package/src/core/state/app/state.ts +14 -2
- package/src/data/cached-query.ts +1 -1
- package/src/data/config/chart-settings.ts +3 -1
- package/src/data/config/home.ts +4 -1
- package/src/data/config/store/node.ts +9 -4
- package/src/data/config/store/normalize.ts +9 -0
- package/src/data/json-storage.ts +6 -3
- package/src/data/json-ticker-repository.ts +3 -1
- package/src/data/plugin-cache.ts +5 -1
- package/src/data/sqlite/json.ts +3 -1
- package/src/data/ticker-repository.ts +5 -2
- package/src/feedback/diagnostics.ts +2 -6
- package/src/i18n/es.ts +86 -6
- package/src/i18n/index.ts +3 -9
- package/src/i18n/ja.ts +86 -6
- package/src/i18n/ko.ts +86 -6
- package/src/i18n/zh-cn.ts +86 -6
- package/src/i18n/zh-tw.ts +86 -6
- package/src/layout-marketplace/gallery-desktop.tsx +8 -4
- package/src/market-data/bars-per-year.ts +43 -0
- package/src/market-data/bond-funds.ts +85 -0
- package/src/market-data/coordinator/auxiliary.ts +5 -2
- package/src/market-data/coordinator/index.ts +6 -5
- package/src/market-data/futures-contract.ts +102 -5
- package/src/market-data/hooks.tsx +20 -11
- package/src/market-data/market/bar-period.ts +36 -0
- package/src/market-data/market/format.ts +5 -13
- package/src/market-data/market/freshness.ts +1 -3
- package/src/market-data/market/index-level.ts +51 -0
- package/src/market-data/model-dividend-yield.ts +57 -16
- package/src/market-data/quotes/live-valuation.ts +48 -2
- package/src/market-data/realized-volatility.ts +16 -7
- package/src/market-data/tax-equivalent.ts +344 -0
- package/src/news/aggregator.ts +4 -2
- package/src/notifications/notification-log.ts +5 -3
- package/src/plugins/auto-update-node.ts +1 -0
- package/src/plugins/builtin/account-management/footer.ts +1 -1
- package/src/plugins/builtin/account-management/keyboard.ts +3 -0
- package/src/plugins/builtin/account-management/model.ts +1 -0
- package/src/plugins/builtin/account-management/pane.tsx +71 -19
- package/src/plugins/builtin/account-management/trial-cancel.ts +40 -0
- package/src/plugins/builtin/alerts/cli.ts +7 -18
- package/src/plugins/builtin/alerts/constants.ts +8 -0
- package/src/plugins/builtin/alerts/event-rows.ts +16 -0
- package/src/plugins/builtin/alerts/events-pane.tsx +2 -14
- package/src/plugins/builtin/alerts/headless.ts +182 -0
- package/src/plugins/builtin/alerts/index.tsx +3 -0
- package/src/plugins/builtin/alerts/pane.tsx +1 -7
- package/src/plugins/builtin/alerts/storage.ts +17 -1
- package/src/plugins/builtin/analytics/index.tsx +6 -1
- package/src/plugins/builtin/analytics/metrics.ts +47 -9
- package/src/plugins/builtin/analytics/risk-headless.ts +14 -2
- package/src/plugins/builtin/analytics/risk-math.ts +83 -0
- package/src/plugins/builtin/analytics/risk-model.ts +97 -4
- package/src/plugins/builtin/analytics/risk-pane.tsx +11 -6
- package/src/plugins/builtin/analytics/sector-model.ts +14 -3
- package/src/plugins/builtin/awards/headless.ts +6 -5
- package/src/plugins/builtin/awards/index.tsx +1 -1
- package/src/plugins/builtin/backtest/client.ts +38 -8
- package/src/plugins/builtin/backtest/engine.ts +25 -13
- package/src/plugins/builtin/backtest/export.ts +27 -0
- package/src/plugins/builtin/backtest/format.ts +19 -3
- package/src/plugins/builtin/backtest/headless.ts +42 -28
- package/src/plugins/builtin/backtest/index.tsx +3 -0
- package/src/plugins/builtin/backtest/pane.tsx +39 -10
- package/src/plugins/builtin/backtest/report.ts +160 -0
- package/src/plugins/builtin/backtest/rules.ts +2 -0
- package/src/plugins/builtin/backtest/window.ts +112 -0
- package/src/plugins/builtin/bond-calculator/headless.ts +3 -2
- package/src/plugins/builtin/broker-manager/add-flow.tsx +4 -4
- package/src/plugins/builtin/catalysts/headless.ts +24 -13
- package/src/plugins/builtin/catalysts/index.tsx +4 -2
- package/src/plugins/builtin/catalysts/model.ts +18 -1
- package/src/plugins/builtin/catalysts/pane.tsx +2 -2
- package/src/plugins/builtin/cds/client.ts +28 -5
- package/src/plugins/builtin/cds/headless.ts +183 -31
- package/src/plugins/builtin/cds/index.tsx +1 -1
- package/src/plugins/builtin/cds/model.ts +229 -53
- package/src/plugins/builtin/cds/pane.tsx +82 -26
- package/src/plugins/builtin/central-bank-rates/headless.ts +1 -1
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +39 -1
- package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +77 -0
- package/src/plugins/builtin/chart-composer/chart-spec.ts +15 -0
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +22 -2
- package/src/plugins/builtin/chart-composer/editor/desktop-view.tsx +2 -2
- package/src/plugins/builtin/chart-composer/headless-schema.ts +10 -2
- package/src/plugins/builtin/chart-composer/headless.ts +99 -21
- package/src/plugins/builtin/chart-composer/index.tsx +70 -13
- package/src/plugins/builtin/chart-composer/pane.tsx +65 -15
- package/src/plugins/builtin/chart-composer/presets.ts +79 -11
- package/src/plugins/builtin/chart-composer/series-catalog.ts +50 -14
- package/src/plugins/builtin/chart-composer/series-expression.ts +9 -7
- package/src/plugins/builtin/chart-composer/series-listings.ts +178 -0
- package/src/plugins/builtin/chart-composer/session-range.ts +87 -0
- package/src/plugins/builtin/chat/attachments/desktop.tsx +4 -1
- package/src/plugins/builtin/chat/content/composer-runtime.ts +49 -14
- package/src/plugins/builtin/chat/content/composer.tsx +22 -1
- package/src/plugins/builtin/chat/content/index.tsx +35 -5
- package/src/plugins/builtin/chat/content/layout-metrics.ts +3 -0
- package/src/plugins/builtin/chat/content/shortcuts.ts +32 -13
- package/src/plugins/builtin/chat/content/snapshot.ts +29 -6
- package/src/plugins/builtin/chat/ghost-user.ts +11 -3
- package/src/plugins/builtin/chat/layout.ts +2 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +13 -5
- package/src/plugins/builtin/chat/message/header.tsx +26 -4
- package/src/plugins/builtin/chat/message/pane-chip.tsx +131 -35
- package/src/plugins/builtin/chat/message/render-state.ts +28 -1
- package/src/plugins/builtin/chat/message/terminal.tsx +9 -3
- package/src/plugins/builtin/chat/pane-links/composer.ts +87 -0
- package/src/plugins/builtin/chat/pane-links/model.ts +1 -1
- package/src/plugins/builtin/chat/share/flow.tsx +9 -73
- package/src/plugins/builtin/cloud/askg/controller.ts +5 -3
- package/src/plugins/builtin/cloud/askg/executor.ts +3 -1
- package/src/plugins/builtin/cloud/askg/lock-note.ts +16 -0
- package/src/plugins/builtin/cloud/askg/pane.tsx +6 -2
- package/src/plugins/builtin/cloud/auth-actions.tsx +4 -2
- package/src/plugins/builtin/cloud/company-picker.tsx +21 -26
- package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +5 -3
- package/src/plugins/builtin/cloud/plugin.tsx +2 -0
- package/src/plugins/builtin/cloud/team/module.tsx +5 -1
- package/src/plugins/builtin/cloud/team/pane.tsx +11 -4
- package/src/plugins/builtin/cloud/team/views.ts +11 -4
- package/src/plugins/builtin/cloud/terminal-relay/engine.ts +9 -4
- package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +3 -2
- package/src/plugins/builtin/cloud/thesis/detail.tsx +2 -1
- package/src/plugins/builtin/cloud/thesis/exposure.ts +3 -0
- package/src/plugins/builtin/cloud/thesis/prompts.tsx +3 -2
- package/src/plugins/builtin/cloud/thesis/ticker-tab.tsx +2 -1
- package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +2 -1
- package/src/plugins/builtin/cloud/verification-status-widget.tsx +2 -1
- package/src/plugins/builtin/commodities-energy/index.tsx +8 -5
- package/src/plugins/builtin/commodities-energy/view-state.ts +11 -0
- package/src/plugins/builtin/company-attention/headless.ts +15 -7
- package/src/plugins/builtin/company-attention/index.tsx +1 -1
- package/src/plugins/builtin/company-attention/model.ts +49 -4
- package/src/plugins/builtin/company-attention/pane.tsx +11 -9
- package/src/plugins/builtin/company-kpis/headless.ts +4 -3
- package/src/plugins/builtin/company-kpis/index.tsx +2 -2
- package/src/plugins/builtin/company-kpis/pane.tsx +9 -3
- package/src/plugins/builtin/congress-trades/headless.ts +23 -2
- package/src/plugins/builtin/correlation/relationship/pane.tsx +18 -46
- package/src/plugins/builtin/cot/headless.ts +5 -2
- package/src/plugins/builtin/credit-boards/cdx-pane.tsx +41 -13
- package/src/plugins/builtin/credit-boards/client.ts +9 -2
- package/src/plugins/builtin/credit-boards/headless.ts +93 -27
- package/src/plugins/builtin/credit-boards/index.tsx +15 -4
- package/src/plugins/builtin/credit-boards/model.ts +105 -6
- package/src/plugins/builtin/credit-boards/sovr-pane.tsx +14 -3
- package/src/plugins/builtin/credit-documents/detail.tsx +2 -1
- package/src/plugins/builtin/credit-documents/index.tsx +3 -0
- package/src/plugins/builtin/credit-documents/model.ts +3 -10
- package/src/plugins/builtin/crypto-board/client.ts +152 -2
- package/src/plugins/builtin/crypto-board/headless.ts +138 -19
- package/src/plugins/builtin/crypto-board/index.tsx +5 -3
- package/src/plugins/builtin/crypto-board/model.ts +210 -12
- package/src/plugins/builtin/crypto-board/pane.tsx +148 -9
- package/src/plugins/builtin/crypto-board/series.ts +52 -1
- package/src/plugins/builtin/crypto-board/supply.ts +123 -0
- package/src/plugins/builtin/debt-maturities/client.ts +54 -1
- package/src/plugins/builtin/debt-maturities/headless.ts +27 -3
- package/src/plugins/builtin/debt-maturities/index.tsx +2 -0
- package/src/plugins/builtin/debt-maturities/model.ts +58 -13
- package/src/plugins/builtin/debt-maturities/pane.tsx +47 -7
- package/src/plugins/builtin/dividend-yield/client.ts +42 -36
- package/src/plugins/builtin/dividend-yield/headless.ts +113 -18
- package/src/plugins/builtin/dividend-yield/index.tsx +5 -2
- package/src/plugins/builtin/dividend-yield/pane.tsx +37 -13
- package/src/plugins/builtin/dividend-yield/provider-client.ts +7 -3
- package/src/plugins/builtin/dividend-yield/ranking.ts +187 -0
- package/src/plugins/builtin/dividend-yield/reference-price.ts +28 -5
- package/src/plugins/builtin/dividend-yield/types.ts +6 -1
- package/src/plugins/builtin/dividend-yield/view.ts +1 -1
- package/src/plugins/builtin/docs/blocks.tsx +54 -8
- package/src/plugins/builtin/docs/pane.tsx +8 -13
- package/src/plugins/builtin/earnings/headless.ts +3 -2
- package/src/plugins/builtin/earnings-calls/data.ts +51 -0
- package/src/plugins/builtin/earnings-calls/headless.ts +7 -0
- package/src/plugins/builtin/earnings-calls/index.tsx +1 -0
- package/src/plugins/builtin/earnings-calls/model.ts +97 -0
- package/src/plugins/builtin/earnings-calls/pane.tsx +107 -7
- package/src/plugins/builtin/earnings-calls/transcript-view.tsx +140 -22
- package/src/plugins/builtin/earnings-calls/use-call-playback.ts +194 -0
- package/src/plugins/builtin/econ/calendar-model.ts +22 -0
- package/src/plugins/builtin/econ/calendar-source.ts +35 -0
- package/src/plugins/builtin/econ/index.tsx +40 -8
- package/src/plugins/builtin/econ-statistics/defs.ts +7 -0
- package/src/plugins/builtin/econ-statistics/detail.tsx +8 -1
- package/src/plugins/builtin/econ-statistics/headless.ts +26 -5
- package/src/plugins/builtin/econ-statistics/pane.tsx +4 -2
- package/src/plugins/builtin/econ-statistics/stats.ts +10 -0
- package/src/plugins/builtin/econ-statistics/view.ts +19 -0
- package/src/plugins/builtin/equity-screener/client.ts +1 -1
- package/src/plugins/builtin/equity-screener/model.ts +46 -4
- package/src/plugins/builtin/equity-screener/pane.tsx +3 -0
- package/src/plugins/builtin/estimate-revisions/client.ts +32 -22
- package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
- package/src/plugins/builtin/estimate-revisions/index.tsx +1 -0
- package/src/plugins/builtin/estimate-revisions/pane.tsx +5 -3
- package/src/plugins/builtin/executives/index.tsx +1 -0
- package/src/plugins/builtin/exposure/editor.tsx +3 -3
- package/src/plugins/builtin/fear-greed/index.tsx +9 -0
- package/src/plugins/builtin/filing-events/index.tsx +1 -0
- package/src/plugins/builtin/futures/index.tsx +2 -2
- package/src/plugins/builtin/futures/model.ts +34 -21
- package/src/plugins/builtin/futures/table.tsx +1 -1
- package/src/plugins/builtin/futures/use-board-terms.ts +2 -2
- package/src/plugins/builtin/futures-curve/headless.ts +4 -2
- package/src/plugins/builtin/futures-curve/model.ts +5 -7
- package/src/plugins/builtin/help/index.tsx +55 -8
- package/src/plugins/builtin/help/shortcut-table.tsx +15 -2
- package/src/plugins/builtin/holders/headless.ts +12 -0
- package/src/plugins/builtin/insider/headless.ts +311 -41
- package/src/plugins/builtin/insider/index.tsx +280 -106
- package/src/plugins/builtin/insider/insiders.tsx +224 -0
- package/src/plugins/builtin/insider/signals-cells.tsx +52 -0
- package/src/plugins/builtin/insider/signals-client.ts +135 -11
- package/src/plugins/builtin/insider/signals-model.ts +628 -56
- package/src/plugins/builtin/insider/signals.tsx +283 -83
- package/src/plugins/builtin/ipo-calendar/headless.ts +17 -7
- package/src/plugins/builtin/ipo-calendar/index.tsx +23 -3
- package/src/plugins/builtin/ipo-calendar/model.ts +84 -36
- package/src/plugins/builtin/ipo-calendar/pane.tsx +20 -8
- package/src/plugins/builtin/iv-history/headless.ts +2 -0
- package/src/plugins/builtin/jobs/headless.ts +1 -0
- package/src/plugins/builtin/jobs/index.tsx +1 -0
- package/src/plugins/builtin/jodi/headless.ts +2 -0
- package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
- package/src/plugins/builtin/kelly-sizer/portfolio.ts +1 -1
- package/src/plugins/builtin/market-heatmap/headless.ts +1 -0
- package/src/plugins/builtin/market-heatmap/portfolio.ts +3 -1
- package/src/plugins/builtin/market-valuation/pane.tsx +1 -1
- package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +31 -57
- package/src/plugins/builtin/mna/distress/headless.ts +2 -1
- package/src/plugins/builtin/mna/headless.ts +14 -2
- package/src/plugins/builtin/mna/index.tsx +1 -0
- package/src/plugins/builtin/news/headless.ts +4 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +5 -3
- package/src/plugins/builtin/news/wire/news/table.tsx +57 -11
- package/src/plugins/builtin/notes/cli.ts +8 -4
- package/src/plugins/builtin/notes/files.ts +66 -20
- package/src/plugins/builtin/notes/migration.ts +18 -6
- package/src/plugins/builtin/notes/owner.tsx +3 -2
- package/src/plugins/builtin/notes/quick-notes-pane.tsx +3 -2
- package/src/plugins/builtin/options-scenario/client.ts +7 -4
- package/src/plugins/builtin/options-scenario/headless.ts +5 -3
- package/src/plugins/builtin/pe-band/index.tsx +1 -1
- package/src/plugins/builtin/pe-band/model.ts +1 -15
- package/src/plugins/builtin/perps/headless.ts +30 -13
- package/src/plugins/builtin/perps/model.ts +45 -3
- package/src/plugins/builtin/perps/pane.tsx +7 -7
- package/src/plugins/builtin/portfolio-list/cell-version.ts +10 -4
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +128 -14
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +108 -46
- package/src/plugins/builtin/portfolio-list/cli/render.ts +368 -45
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +8 -2
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +24 -34
- package/src/plugins/builtin/portfolio-list/grid.tsx +3 -1
- package/src/plugins/builtin/portfolio-list/headless.ts +151 -29
- package/src/plugins/builtin/portfolio-list/income.ts +296 -0
- package/src/plugins/builtin/portfolio-list/metrics.ts +2 -2
- package/src/plugins/builtin/portfolio-list/pane/futures-terms.ts +58 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +27 -10
- package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +47 -28
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +3 -2
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +10 -3
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +26 -4
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +31 -3
- package/src/plugins/builtin/portfolio-list/table.tsx +25 -5
- package/src/plugins/builtin/power/prices-headless.ts +2 -1
- package/src/plugins/builtin/realized-vol/chart-model.ts +29 -5
- package/src/plugins/builtin/realized-vol/charts.tsx +14 -8
- package/src/plugins/builtin/realized-vol/evidence.ts +2 -0
- package/src/plugins/builtin/realized-vol/headless.ts +6 -4
- package/src/plugins/builtin/realized-vol/model.ts +5 -1
- package/src/plugins/builtin/realized-vol/pane.tsx +31 -11
- package/src/plugins/builtin/realized-vol/settings.ts +1 -1
- package/src/plugins/builtin/research/analyst-headless.ts +4 -0
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -0
- package/src/plugins/builtin/research/event-model.ts +22 -2
- package/src/plugins/builtin/research/headless-schema.ts +1 -0
- package/src/plugins/builtin/research/headless.ts +3 -0
- package/src/plugins/builtin/research/index.tsx +3 -0
- package/src/plugins/builtin/research/relative-valuation-columns.ts +27 -5
- package/src/plugins/builtin/research/relative-valuation-headless.ts +2 -0
- package/src/plugins/builtin/revenue-breakdown/index.tsx +1 -0
- package/src/plugins/builtin/reverse-dcf/headless.ts +9 -6
- package/src/plugins/builtin/reverse-dcf/index.tsx +2 -2
- package/src/plugins/builtin/reverse-dcf/model.ts +51 -4
- package/src/plugins/builtin/reverse-dcf/pane.tsx +23 -10
- package/src/plugins/builtin/risk-factors/headless.ts +7 -0
- package/src/plugins/builtin/risk-factors/index.tsx +1 -0
- package/src/plugins/builtin/scanner/feed.ts +5 -3
- package/src/plugins/builtin/scanner/headless.ts +9 -57
- package/src/plugins/builtin/scanner/index.ts +1 -2
- package/src/plugins/builtin/sec/forms.ts +2 -6
- package/src/plugins/builtin/sec/fund-facts-view.tsx +53 -0
- package/src/plugins/builtin/sec/fund-facts.ts +328 -0
- package/src/plugins/builtin/sec/headless.ts +140 -44
- package/src/plugins/builtin/sec/index.tsx +95 -30
- package/src/plugins/builtin/sec/model.ts +2 -44
- package/src/plugins/builtin/shared/headless-options.ts +10 -0
- package/src/plugins/builtin/shared/locked-rows.tsx +3 -1
- package/src/plugins/builtin/shared/move-rank.ts +108 -0
- package/src/plugins/builtin/shared/report-freshness.ts +25 -9
- package/src/plugins/builtin/shared/scanner-capture.ts +95 -0
- package/src/plugins/builtin/shared/treasury-cutoffs.ts +15 -0
- package/src/plugins/builtin/shared/use-quote-board.ts +1 -0
- package/src/plugins/builtin/shared/volatility/live-session.ts +34 -0
- package/src/plugins/builtin/shared/volatility-indices.ts +32 -0
- package/src/plugins/builtin/short-interest/headless.ts +3 -0
- package/src/plugins/builtin/short-interest/watch-model.ts +2 -2
- package/src/plugins/builtin/social-mentions/client.ts +8 -1
- package/src/plugins/builtin/social-mentions/pane.tsx +8 -6
- package/src/plugins/builtin/supply-chain/client.ts +12 -3
- package/src/plugins/builtin/supply-chain/disclosure-quote.tsx +9 -5
- package/src/plugins/builtin/supply-chain/evidence.ts +3 -1
- package/src/plugins/builtin/supply-chain/flow-layout.ts +2 -1
- package/src/plugins/builtin/supply-chain/flow.tsx +23 -16
- package/src/plugins/builtin/supply-chain/graph-client.ts +8 -5
- package/src/plugins/builtin/supply-chain/graph-evidence.ts +1 -1
- package/src/plugins/builtin/supply-chain/graph-model.ts +10 -2
- package/src/plugins/builtin/supply-chain/graph-pane.tsx +9 -7
- package/src/plugins/builtin/supply-chain/graph.tsx +8 -6
- package/src/plugins/builtin/supply-chain/headless.ts +69 -27
- package/src/plugins/builtin/supply-chain/index.tsx +1 -1
- package/src/plugins/builtin/supply-chain/model.ts +76 -15
- package/src/plugins/builtin/supply-chain/pane.tsx +29 -15
- package/src/plugins/builtin/supply-chain/row-evidence.tsx +5 -4
- package/src/plugins/builtin/themes/model.ts +2 -5
- package/src/plugins/builtin/thirteenf/api.ts +50 -2
- package/src/plugins/builtin/thirteenf/data.ts +250 -11
- package/src/plugins/builtin/thirteenf/headless.ts +311 -28
- package/src/plugins/builtin/thirteenf/index.tsx +4 -1
- package/src/plugins/builtin/thirteenf/model.ts +244 -19
- package/src/plugins/builtin/thirteenf/overlap-pane.tsx +133 -33
- package/src/plugins/builtin/thirteenf/overlap.ts +1 -1
- package/src/plugins/builtin/thirteenf/pane.tsx +24 -5
- package/src/plugins/builtin/thirteenf/report-notes.ts +78 -0
- package/src/plugins/builtin/thirteenf/types.ts +49 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +22 -8
- package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +17 -1
- package/src/plugins/builtin/ticker-detail/financials/model.ts +2 -42
- package/src/plugins/builtin/ticker-detail/financials/tab.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/headless.ts +23 -10
- package/src/plugins/builtin/ticker-detail/overview/components.tsx +27 -3
- package/src/plugins/builtin/ticker-detail/overview/dividend.ts +60 -0
- package/src/plugins/builtin/ticker-detail/overview/model.ts +46 -9
- package/src/plugins/builtin/ticker-detail/overview/pane.tsx +3 -0
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +36 -3
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +18 -19
- package/src/plugins/builtin/time-sales/client.ts +7 -4
- package/src/plugins/builtin/time-sales/headless.ts +71 -22
- package/src/plugins/builtin/time-sales/model.ts +12 -0
- package/src/plugins/builtin/time-sales/pane.tsx +31 -7
- package/src/plugins/builtin/time-sales/window.ts +128 -0
- package/src/plugins/builtin/trace-bonds/client.ts +351 -33
- package/src/plugins/builtin/trace-bonds/headless.ts +60 -22
- package/src/plugins/builtin/trace-bonds/index.tsx +4 -2
- package/src/plugins/builtin/trace-bonds/model.ts +27 -25
- package/src/plugins/builtin/trace-bonds/pane.tsx +63 -45
- package/src/plugins/builtin/treasury-auctions/cache.ts +61 -15
- package/src/plugins/builtin/treasury-auctions/client.ts +45 -10
- package/src/plugins/builtin/treasury-auctions/headless.ts +35 -13
- package/src/plugins/builtin/treasury-auctions/index.tsx +5 -2
- package/src/plugins/builtin/treasury-auctions/model.ts +20 -16
- package/src/plugins/builtin/treasury-auctions/pane.tsx +29 -12
- package/src/plugins/builtin/vol-surface/charts.tsx +2 -1
- package/src/plugins/builtin/vol-surface/headless.ts +3 -2
- package/src/plugins/builtin/vol-surface/pane.tsx +2 -1
- package/src/plugins/builtin/vol-surface/stored.ts +4 -7
- package/src/plugins/builtin/volatility/chart-model.ts +16 -15
- package/src/plugins/builtin/volatility/charts.tsx +5 -5
- package/src/plugins/builtin/volatility/client.ts +73 -2
- package/src/plugins/builtin/volatility/evidence.ts +13 -14
- package/src/plugins/builtin/volatility/headless.ts +13 -7
- package/src/plugins/builtin/volatility/index.tsx +1 -1
- package/src/plugins/builtin/volatility/model.ts +104 -38
- package/src/plugins/builtin/volatility/pane.tsx +42 -24
- package/src/plugins/builtin/volatility/series.ts +22 -13
- package/src/plugins/builtin/world-venue-map/geo-series.ts +9 -0
- package/src/plugins/builtin/world-venue-map/headless.ts +8 -5
- package/src/plugins/builtin/world-venue-map/index.ts +16 -2
- package/src/plugins/builtin/world-venue-map/map.tsx +13 -17
- package/src/plugins/builtin/yield-curve/curves.ts +29 -5
- package/src/plugins/builtin/yield-curve/headless.ts +41 -8
- package/src/plugins/builtin/yield-curve/history.ts +5 -5
- package/src/plugins/builtin/yield-curve/index.tsx +30 -13
- package/src/plugins/compat.ts +3 -1
- package/src/plugins/dependencies.ts +8 -2
- package/src/plugins/host-link.ts +12 -5
- package/src/plugins/installer.ts +16 -7
- package/src/plugins/loader.ts +9 -2
- package/src/plugins/marketplace/activation.ts +10 -4
- package/src/plugins/registry/host-actions.ts +3 -0
- package/src/plugins/registry/index.ts +3 -1
- package/src/{plugins/builtin/portfolio-list → portfolio}/allocation.ts +74 -12
- package/src/{plugins/builtin/portfolio-list → portfolio}/column-values.ts +140 -53
- package/src/portfolio/futures-positions.ts +213 -0
- package/src/portfolio/futures-terms.ts +75 -0
- package/src/portfolio/manual-lots.ts +78 -0
- package/src/portfolio/mutations.ts +54 -10
- package/src/portfolio/position-metrics.ts +44 -10
- package/src/portfolio/valuation-basis.ts +31 -4
- package/src/public/__snapshots__/public-api.test.ts.snap +4 -0
- package/src/public/time-series.ts +2 -1
- package/src/remote/confirm-change.tsx +2 -1
- package/src/remote/server.ts +1 -1
- package/src/remote/watchlist.ts +9 -1
- package/src/renderers/dom/data-table/row.tsx +1 -1
- package/src/renderers/dom/desktop/audio-player.tsx +312 -0
- package/src/renderers/dom/desktop/control-styles.ts +17 -82
- package/src/renderers/dom/desktop/controls.tsx +39 -63
- package/src/renderers/dom/desktop/dialog-actions.tsx +13 -53
- package/src/renderers/dom/desktop/icons.tsx +8 -0
- package/src/renderers/dom/desktop/list-view.tsx +6 -6
- package/src/renderers/dom/desktop/popover.tsx +0 -3
- package/src/renderers/dom/desktop/query-bar.tsx +6 -4
- package/src/renderers/dom/desktop/select-field.tsx +9 -8
- package/src/renderers/dom/desktop/stack-header.ts +3 -0
- package/src/renderers/dom/dom-ui-host.tsx +3 -2
- package/src/renderers/dom/host/chart-surface.tsx +3 -3
- package/src/renderers/dom/host/size-observer.ts +33 -4
- package/src/renderers/dom/host/surface-3d.tsx +3 -2
- package/src/renderers/dom/host/surface-fit.ts +4 -1
- package/src/renderers/dom/host/tabs.tsx +22 -132
- package/src/renderers/dom/styles.css +358 -160
- package/src/renderers/electrobun/bun/core-capabilities.ts +1 -1
- package/src/renderers/electrobun/bun/desktop/backend.ts +1 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/state-broadcaster.ts +43 -1
- package/src/renderers/electrobun/bun/desktop/workspace/requests.ts +5 -1
- package/src/renderers/electrobun/shared/rpc-codec.ts +7 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +21 -17
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +3 -3
- package/src/renderers/electrobun/view/desktop/window/bridge.ts +6 -8
- package/src/renderers/electrobun/view/desktop/window/snapshot.ts +18 -0
- package/src/series/capability.ts +1 -0
- package/src/series/files.ts +9 -3
- package/src/series/registry.ts +40 -14
- package/src/series/resolve.ts +121 -18
- package/src/series/standalone.ts +3 -2
- package/src/series/types.ts +55 -1
- package/src/series/validate.ts +39 -0
- package/src/shares/api.ts +26 -7
- package/src/shares/bug-report-dialog.tsx +2 -2
- package/src/shares/bug-report-flow.tsx +4 -7
- package/src/shares/capture-flow.ts +5 -4
- package/src/shares/capture-preview.tsx +2 -2
- package/src/shares/chat.ts +7 -27
- package/src/shares/flow.tsx +86 -40
- package/src/shares/location.ts +1 -3
- package/src/shares/pane-image.ts +11 -9
- package/src/shares/pane.ts +5 -2
- package/src/shares/payload.ts +1 -1
- package/src/shares/personal-pane.ts +3 -3
- package/src/shares/portable-layout.ts +9 -2
- package/src/shares/ready.ts +42 -0
- package/src/shares/test-harness.ts +62 -0
- package/src/sources/gloomberb-cloud/batched-lookups.ts +73 -0
- package/src/sources/gloomberb-cloud/index.ts +64 -12
- package/src/sources/gloomberb-cloud/normalizers.ts +31 -2
- package/src/sources/gloomberb-cloud/perpetuals.ts +20 -1
- package/src/sources/history-omitted.ts +34 -0
- package/src/sources/history-result.ts +3 -0
- package/src/sources/history-retention.ts +1 -1
- package/src/sources/provider-errors.ts +43 -1
- package/src/sources/provider-router/batches.ts +3 -2
- package/src/sources/provider-router/cache.ts +2 -1
- package/src/sources/provider-router/cached-routes.ts +9 -3
- package/src/sources/provider-router/history.ts +9 -8
- package/src/sources/provider-router/index.ts +18 -3
- package/src/sources/provider-router/primary.ts +5 -4
- package/src/sources/provider-router/quote-route.ts +3 -1
- package/src/sources/provider-router/routing.ts +2 -1
- package/src/sources/provider-router/search.ts +47 -13
- package/src/sources/provider-router/streaming.ts +7 -4
- package/src/sources/sec-edgar.ts +1 -3
- package/src/state/app/context/index.tsx +9 -13
- package/src/state/app/tab-cursor.ts +149 -12
- package/src/state/hooks/quote-streaming.ts +3 -2
- package/src/state/hooks/session-snapshot.ts +3 -2
- package/src/state/persist-scheduler.ts +12 -4
- package/src/sync/controller.ts +22 -1
- package/src/sync/core-contributors.ts +58 -15
- package/src/team/collections-sync.ts +14 -4
- package/src/team/collections.ts +40 -11
- package/src/team/store.ts +5 -4
- package/src/telemetry/attention-counts.ts +8 -2
- package/src/telemetry/crash-reports-dom.ts +3 -0
- package/src/telemetry/crash-reports-node.ts +7 -4
- package/src/telemetry/crash-reports.ts +11 -11
- package/src/telemetry/usage-counts.ts +16 -12
- package/src/theme/colors.ts +2 -0
- package/src/theme/font-scale.ts +2 -0
- package/src/theme/ui-tokens.ts +71 -0
- package/src/tickers/batched-lookups.ts +186 -0
- package/src/tickers/company-name-listing.ts +97 -0
- package/src/tickers/company-name.ts +13 -0
- package/src/tickers/instrument-coverage.ts +112 -0
- package/src/tickers/listing-fate.ts +49 -9
- package/src/tickers/listing-venues.ts +24 -5
- package/src/tickers/open-target.ts +13 -5
- package/src/tickers/search/index.ts +112 -35
- package/src/tickers/search/ranking.ts +49 -1
- package/src/tickers/terminal-codes.ts +79 -0
- package/src/tickers/ticker-changes.ts +74 -3
- package/src/tickers/us-listing.ts +2 -1
- package/src/time-series/chart-data.ts +79 -8
- package/src/time-series/fundamentals.ts +55 -7
- package/src/time-series/ice-credit.ts +17 -2
- package/src/time-series/live-bars.ts +21 -8
- package/src/time-series/live-quotes.ts +2 -3
- package/src/time-series/price-comparison.ts +6 -1
- package/src/time-series/resolve.ts +65 -10
- package/src/time-series/spec.ts +2 -1
- package/src/time-series/studies.ts +3 -3
- package/src/time-series/types.ts +22 -0
- package/src/time-series/use-chart-resolution.ts +2 -1
- package/src/types/config.ts +7 -0
- package/src/types/data-provider.ts +20 -0
- package/src/types/headless.ts +142 -1
- package/src/types/instrument.ts +6 -0
- package/src/types/plugin.ts +32 -9
- package/src/types/price-history.ts +5 -0
- package/src/types/ticker-changes.ts +19 -0
- package/src/types/ticker.ts +10 -2
- package/src/ui/context-menu.tsx +2 -0
- package/src/ui/host.tsx +40 -4
- package/src/ui/measured-cells.ts +13 -7
- package/src/ui/qr.ts +7 -5
- package/src/updater/index.ts +5 -5
- package/src/utils/calendar-date.ts +86 -27
- package/src/utils/currency-units.ts +17 -0
- package/src/utils/debug-log.ts +1 -1
- package/src/utils/dividend-cash.ts +48 -0
- package/src/utils/dom-screenshot-rasters.ts +3 -1
- package/src/utils/dom-screenshot.ts +14 -10
- package/src/utils/exchange-rate-snapshot.ts +9 -4
- package/src/utils/external-url.ts +1 -0
- package/src/utils/fiscal-period.ts +47 -0
- package/src/utils/format.ts +5 -2
- package/src/utils/forward-dividend.ts +160 -11
- package/src/utils/futures-generic.ts +34 -8
- package/src/utils/http-proxy-response.ts +2 -1
- package/src/utils/local-utc-offset.ts +2 -0
- package/src/utils/log-failure.ts +31 -0
- package/src/utils/option-expiry.ts +12 -12
- package/src/utils/price-history-integrity.ts +56 -0
- package/src/utils/price-history.ts +13 -1
- package/src/utils/sec-filings.ts +245 -0
- package/src/utils/selection-clipboard.ts +5 -2
- package/src/utils/throttled-fetch.ts +1 -0
- package/src/{plugins/builtin/dividend-yield/trailing-cash.ts → utils/trailing-dividend.ts} +55 -8
- package/src/utils/url.ts +1 -0
- package/src/utils/utc-time.ts +11 -0
- package/src/plugins/builtin/chat/share/targets.ts +0 -27
- package/src/renderers/dom/host/top-surface.ts +0 -119
- package/src/shares/dialog.tsx +0 -221
- package/src/shares/live.ts +0 -19
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logFailure } from "../utils/log-failure";
|
|
1
2
|
import type { ResolvedPane } from "../layout/pane-manager";
|
|
2
3
|
import type { PluginRegistry } from "../plugins/registry";
|
|
3
4
|
import { panePersonalData } from "../shares/personal-pane";
|
|
@@ -122,7 +123,8 @@ export function buildAskGloomPaneAttachment({
|
|
|
122
123
|
function showsPersonalData(source: Parameters<typeof panePersonalData>[0]): boolean {
|
|
123
124
|
try {
|
|
124
125
|
return panePersonalData(source) !== null;
|
|
125
|
-
} catch {
|
|
126
|
+
} catch (error) {
|
|
127
|
+
logFailure("ask-gloom.pane-context", "Checking pane personal data failed")(error);
|
|
126
128
|
return true;
|
|
127
129
|
}
|
|
128
130
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* restart everywhere) so the directory still lists them offline. The desktop's
|
|
7
7
|
* Bun half never fetches; it picks up the list the view saved.
|
|
8
8
|
*/
|
|
9
|
+
import { logFailure } from "../../utils/log-failure";
|
|
9
10
|
import type { PluginPersistence } from "../../types/plugin";
|
|
10
11
|
import { listSignedInBrokers, type SignedInBroker } from "./client";
|
|
11
12
|
|
|
@@ -102,7 +103,7 @@ export function createSignedInBrokerCatalog(
|
|
|
102
103
|
persistence?.setState(STATE_KEY, { brokers: next, fetchedAt }, { schemaVersion: SCHEMA_VERSION });
|
|
103
104
|
})
|
|
104
105
|
// Offline or Cloud down: the last list stays, and the next open retries.
|
|
105
|
-
.catch((
|
|
106
|
+
.catch(logFailure("signed-in-brokers", "Broker catalog refresh failed"))
|
|
106
107
|
.finally(() => {
|
|
107
108
|
inFlight = null;
|
|
108
109
|
});
|
|
@@ -52,11 +52,16 @@ export function fetchSignedInBrokerConnection(brokerId: string): Promise<SignedI
|
|
|
52
52
|
return apiClient.brokerRequest(brokerId, "");
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Opens a connect ticket. This app shows no code: it asks for a ticket that
|
|
57
|
+
* only the account's own Gloom session in the browser can continue. A Cloud
|
|
58
|
+
* that predates that still answers with a code, which the dialog then shows.
|
|
59
|
+
*/
|
|
55
60
|
export function startSignedInBrokerConnect(
|
|
56
61
|
brokerId: string,
|
|
57
62
|
write: boolean,
|
|
58
|
-
): Promise<{ connectUrl: string; code
|
|
59
|
-
return apiClient.brokerRequest(brokerId, "/connect", { method: "POST", body: { write } });
|
|
63
|
+
): Promise<{ connectUrl: string; code?: string | null; expiresAt: string }> {
|
|
64
|
+
return apiClient.brokerRequest(brokerId, "/connect", { method: "POST", body: { write, code: false } });
|
|
60
65
|
}
|
|
61
66
|
|
|
62
67
|
export function disconnectSignedInBroker(brokerId: string): Promise<{ disconnected: boolean }> {
|
|
@@ -9,14 +9,18 @@
|
|
|
9
9
|
import { useEffect, useRef, useState } from "react";
|
|
10
10
|
import { apiClient, type AuthUser } from "../../api-client";
|
|
11
11
|
import { SignInCodePanel } from "../../components/sign-in-code-panel";
|
|
12
|
+
import { Button } from "../../components/ui/button";
|
|
13
|
+
import { DialogActions } from "../../components/ui/dialog-actions";
|
|
14
|
+
import { FORM_DIALOG_WIDTH, DialogFrame } from "../../components/ui/frame";
|
|
12
15
|
import { t, tf } from "../../i18n";
|
|
13
16
|
import { useAppLanguage } from "../../i18n/react";
|
|
14
17
|
import { useViewport } from "../../react/input";
|
|
15
18
|
import { colors } from "../../theme/colors";
|
|
19
|
+
import { useThemeColors } from "../../theme/theme-context";
|
|
16
20
|
import { Box, Text, TextAttributes, useUiCapabilities } from "../../ui";
|
|
17
|
-
import { DialogHints } from "../../components/ui/dialog-actions";
|
|
18
21
|
import { useDialog, useDialogKeyboard, type DialogApi, type PromptContext } from "../../ui/dialog";
|
|
19
22
|
import { isPlainKey } from "../../utils/keyboard";
|
|
23
|
+
import { wrapTextLines } from "../../utils/text-wrap";
|
|
20
24
|
import { DeviceSignInDialog } from "../../account/device-signin-dialog";
|
|
21
25
|
import type { SignedInBroker } from "./client";
|
|
22
26
|
import {
|
|
@@ -43,7 +47,13 @@ export function brokerSignInStatus(snapshot: BrokerSignInSnapshot, broker: Signe
|
|
|
43
47
|
}
|
|
44
48
|
}
|
|
45
49
|
|
|
46
|
-
/**
|
|
50
|
+
/** What every connect surface says under its title: the browser does the rest. */
|
|
51
|
+
export const BROKER_CONNECT_SUBTITLE = "Continue in your browser.";
|
|
52
|
+
|
|
53
|
+
/** The key every connect surface takes for a fresh link, as the desktop shows it. */
|
|
54
|
+
export const BROKER_CONNECT_NEW_LINK = { key: "r", label: "new link" };
|
|
55
|
+
|
|
56
|
+
/** Under the panel: the broker's own note, or what connecting a single-connection broker does to other apps. */
|
|
47
57
|
export function brokerSignInNote(broker: SignedInBroker): string | null {
|
|
48
58
|
return broker.capabilities.signupNote
|
|
49
59
|
?? (broker.capabilities.singleConnection
|
|
@@ -108,6 +118,18 @@ export async function promptGloomSignIn(dialog: DialogApi): Promise<boolean> {
|
|
|
108
118
|
});
|
|
109
119
|
}
|
|
110
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Rows the dialog's own lines take around the connect view: the terminal's
|
|
123
|
+
* margins, border and padding (7), title, subtitle and key line with the
|
|
124
|
+
* blank rows between them, and the desktop's frame, title block and Cancel.
|
|
125
|
+
*/
|
|
126
|
+
const TERMINAL_DIALOG_ROWS = 12;
|
|
127
|
+
const DESKTOP_DIALOG_ROWS = 10;
|
|
128
|
+
/** Columns the terminal's full dialog takes from the window: margins, border and padding. */
|
|
129
|
+
const TERMINAL_DIALOG_INSET = 8;
|
|
130
|
+
/** Cells the desktop dialog's padding takes from its width. */
|
|
131
|
+
const DESKTOP_DIALOG_INSET = 4;
|
|
132
|
+
|
|
111
133
|
function BrokerSignInDialog({
|
|
112
134
|
resolve,
|
|
113
135
|
dismiss,
|
|
@@ -117,7 +139,7 @@ function BrokerSignInDialog({
|
|
|
117
139
|
}: PromptContext<BrokerSignInOutcome> & { broker: SignedInBroker; write: boolean; renew: boolean }) {
|
|
118
140
|
useAppLanguage();
|
|
119
141
|
const { nativePaneChrome } = useUiCapabilities();
|
|
120
|
-
const { height: termHeight } = useViewport();
|
|
142
|
+
const { width: termWidth, height: termHeight } = useViewport();
|
|
121
143
|
// Close after a beat so "Connected" is visible; enter skips the wait. Signed
|
|
122
144
|
// out closes at once, for the host to sign in to Gloom.
|
|
123
145
|
const { snapshot, restart, finish } = useBrokerSignInAttempt(broker, write, resolve, { renew });
|
|
@@ -133,44 +155,116 @@ function BrokerSignInDialog({
|
|
|
133
155
|
}
|
|
134
156
|
});
|
|
135
157
|
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
158
|
+
const title = tf("Connect {broker}", { broker: broker.name });
|
|
159
|
+
const subtitle = t(BROKER_CONNECT_SUBTITLE);
|
|
160
|
+
|
|
161
|
+
// The desktop frames it as New Portfolio frames its connect step: as wide,
|
|
162
|
+
// with Cancel at the foot and the key for a new link beside it.
|
|
163
|
+
if (nativePaneChrome) {
|
|
164
|
+
return (
|
|
165
|
+
<Box width={FORM_DIALOG_WIDTH} maxWidth="calc(100vw - 72px)" flexDirection="column">
|
|
166
|
+
<DialogFrame title={title} subtitle={subtitle} onClose={dismiss} desktopFooter={null}>
|
|
167
|
+
<BrokerConnectView
|
|
168
|
+
broker={broker}
|
|
169
|
+
snapshot={snapshot}
|
|
170
|
+
height={Math.max(4, termHeight - DESKTOP_DIALOG_ROWS)}
|
|
171
|
+
width={FORM_DIALOG_WIDTH - DESKTOP_DIALOG_INSET}
|
|
172
|
+
/>
|
|
173
|
+
<Box style={{ marginTop: "var(--gloom-space-md)" }}>
|
|
174
|
+
<DialogActions alignEnd hints={{ keys: [BROKER_CONNECT_NEW_LINK] }}>
|
|
175
|
+
<Button label="Cancel" variant="secondary" desktopShortcut="Esc" onPress={dismiss} />
|
|
176
|
+
</DialogActions>
|
|
177
|
+
</Box>
|
|
178
|
+
</DialogFrame>
|
|
179
|
+
</Box>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
139
182
|
|
|
140
183
|
return (
|
|
141
184
|
<Box flexDirection="column" alignItems="center">
|
|
142
185
|
<Box height={1}>
|
|
143
|
-
<Text fg={colors.textBright} attributes={TextAttributes.BOLD}>
|
|
144
|
-
{tf("Connect {broker}", { broker: broker.name })}
|
|
145
|
-
</Text>
|
|
186
|
+
<Text fg={colors.textBright} attributes={TextAttributes.BOLD}>{title}</Text>
|
|
146
187
|
</Box>
|
|
147
188
|
<Box height={1}>
|
|
148
|
-
<Text fg={colors.textDim}>{
|
|
189
|
+
<Text fg={colors.textDim}>{subtitle}</Text>
|
|
149
190
|
</Box>
|
|
150
191
|
<Box height={1} />
|
|
192
|
+
<BrokerConnectView
|
|
193
|
+
broker={broker}
|
|
194
|
+
snapshot={snapshot}
|
|
195
|
+
height={Math.max(4, termHeight - TERMINAL_DIALOG_ROWS)}
|
|
196
|
+
width={Math.max(10, termWidth - TERMINAL_DIALOG_INSET)}
|
|
197
|
+
/>
|
|
198
|
+
<Box height={1} />
|
|
199
|
+
<Box height={1}>
|
|
200
|
+
<Text fg={colors.textMuted}>{t("r for a new link · esc to cancel")}</Text>
|
|
201
|
+
</Box>
|
|
202
|
+
</Box>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* On the desktop a connect's note, under the panel, is set to a reading
|
|
208
|
+
* measure rather than the width of the dialog or pane. The terminal wraps it
|
|
209
|
+
* to the width it has, since every row it takes is one the QR code may need.
|
|
210
|
+
*/
|
|
211
|
+
const NOTE_MEASURE = 56;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* One attempt at connecting a signed-in broker as the user sees it: the link,
|
|
215
|
+
* QR code and status, with the broker's note wrapped under them. The connect
|
|
216
|
+
* dialog, Add Broker's step in the Brokers pane and New Portfolio's show it.
|
|
217
|
+
*/
|
|
218
|
+
export function BrokerConnectView({
|
|
219
|
+
broker,
|
|
220
|
+
snapshot,
|
|
221
|
+
height,
|
|
222
|
+
width,
|
|
223
|
+
browserKey = true,
|
|
224
|
+
}: {
|
|
225
|
+
broker: SignedInBroker;
|
|
226
|
+
snapshot: BrokerSignInSnapshot;
|
|
227
|
+
/** Terminal rows for the panel and the note. */
|
|
228
|
+
height: number;
|
|
229
|
+
/** Terminal columns the note wraps to. */
|
|
230
|
+
width: number;
|
|
231
|
+
/** Whether `b` opens the link now. */
|
|
232
|
+
browserKey?: boolean;
|
|
233
|
+
}) {
|
|
234
|
+
useAppLanguage();
|
|
235
|
+
const colors = useThemeColors();
|
|
236
|
+
const desktop = useUiCapabilities().nativePaneChrome === true;
|
|
237
|
+
const note = brokerSignInNote(broker);
|
|
238
|
+
const noteLines = note ? wrapTextLines(note, Math.max(10, desktop ? Math.min(width, NOTE_MEASURE) : width)) : [];
|
|
239
|
+
// The note goes under the panel, which gets the rows left.
|
|
240
|
+
const noteRows = note ? 1 + noteLines.length : 0;
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
<Box flexDirection="column" alignItems="center">
|
|
151
244
|
<SignInCodePanel
|
|
152
245
|
url={snapshot.connectUrl}
|
|
153
246
|
code={snapshot.code}
|
|
154
247
|
status={brokerSignInStatus(snapshot, broker)}
|
|
155
|
-
height={
|
|
248
|
+
height={Math.max(4, height - noteRows)}
|
|
156
249
|
shortcutScope="broker-signin:browser"
|
|
250
|
+
browserKey={browserKey}
|
|
157
251
|
/>
|
|
158
|
-
{note && (
|
|
159
|
-
|
|
160
|
-
<Box
|
|
161
|
-
|
|
162
|
-
<Text fg={colors.textMuted}>{note}</Text>
|
|
252
|
+
{note && (desktop
|
|
253
|
+
? (
|
|
254
|
+
<Box marginTop={1} maxWidth={NOTE_MEASURE}>
|
|
255
|
+
<Text fg={colors.textMuted} wrapText style={{ textAlign: "center" }}>{note}</Text>
|
|
163
256
|
</Box>
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
257
|
+
)
|
|
258
|
+
: (
|
|
259
|
+
<>
|
|
260
|
+
<Box height={1} />
|
|
261
|
+
{noteLines.map((line, index) => (
|
|
262
|
+
<Box key={index} height={1}>
|
|
263
|
+
<Text fg={colors.textMuted}>{line}</Text>
|
|
264
|
+
</Box>
|
|
265
|
+
))}
|
|
266
|
+
</>
|
|
267
|
+
))}
|
|
174
268
|
</Box>
|
|
175
269
|
);
|
|
176
270
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Connecting a signed-in broker: Gloom Cloud hands out a link
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* Connecting a signed-in broker: Gloom Cloud hands out a link, the user opens
|
|
3
|
+
* it and signs in to the broker there. The link goes on only in a browser
|
|
4
|
+
* signed in to Gloom as this account, so a link someone else started cannot
|
|
5
|
+
* attach their brokerage to it. The connection itself lives in the Cloud
|
|
6
|
+
* account, shared by every device and by the user's agents.
|
|
7
7
|
*/
|
|
8
|
+
import { logFailure } from "../../utils/log-failure";
|
|
8
9
|
import { ApiRequestError } from "../../api-client/errors";
|
|
9
10
|
import {
|
|
10
11
|
fetchSignedInBrokerConnection,
|
|
@@ -19,6 +20,7 @@ type BrokerSignInPhase = "starting" | "waiting" | "connected" | "signed-out" | "
|
|
|
19
20
|
export interface BrokerSignInSnapshot {
|
|
20
21
|
phase: BrokerSignInPhase;
|
|
21
22
|
connectUrl: string | null;
|
|
23
|
+
/** Only from a Cloud that still asks the browser for a code; none otherwise. */
|
|
22
24
|
code: string | null;
|
|
23
25
|
/** A transient poll failure while waiting, or why starting failed. */
|
|
24
26
|
error: string | null;
|
|
@@ -37,7 +39,7 @@ export interface BrokerSignInOptions {
|
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
export interface BrokerSignInIo {
|
|
40
|
-
start(brokerId: string, write: boolean): Promise<{ connectUrl: string; code
|
|
42
|
+
start(brokerId: string, write: boolean): Promise<{ connectUrl: string; code?: string | null; expiresAt: string }>;
|
|
41
43
|
fetchConnection(brokerId: string): Promise<BrokerConnectionRead>;
|
|
42
44
|
now(): number;
|
|
43
45
|
delay(ms: number): Promise<void>;
|
|
@@ -46,7 +48,7 @@ export interface BrokerSignInIo {
|
|
|
46
48
|
const POLL_INTERVAL_MS = 2_000;
|
|
47
49
|
const RETRY_MAX_MS = 30_000;
|
|
48
50
|
/** Used when the expiry is missing or unreadable, so polling can never run forever. */
|
|
49
|
-
const
|
|
51
|
+
const DEFAULT_LINK_TTL_MS = 15 * 60_000;
|
|
50
52
|
|
|
51
53
|
const defaultIo: BrokerSignInIo = {
|
|
52
54
|
start: (brokerId, write) => startSignedInBrokerConnect(brokerId, write),
|
|
@@ -61,13 +63,13 @@ function describeStartError(error: unknown, broker: SignedInBroker): string {
|
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
/**
|
|
64
|
-
* When a
|
|
65
|
-
* past (the clock runs fast) counts as missing, or every
|
|
66
|
+
* When a link stops working. An expiry this device's clock already sees as
|
|
67
|
+
* past (the clock runs fast) counts as missing, or every link would expire
|
|
66
68
|
* the moment it arrived.
|
|
67
69
|
*/
|
|
68
|
-
function
|
|
70
|
+
function linkDeadline(expiresAt: string, now: number): number {
|
|
69
71
|
const parsed = Date.parse(expiresAt);
|
|
70
|
-
return Number.isFinite(parsed) && parsed > now ? parsed : now +
|
|
72
|
+
return Number.isFinite(parsed) && parsed > now ? parsed : now + DEFAULT_LINK_TTL_MS;
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
function stamp(value: string | null | undefined): number | null {
|
|
@@ -96,7 +98,7 @@ export class BrokerSignInController {
|
|
|
96
98
|
private generation = 0;
|
|
97
99
|
/**
|
|
98
100
|
* Renewing only: the account's connection as first seen. It outlives a
|
|
99
|
-
* restarted
|
|
101
|
+
* restarted link, so a sign-in finished while "r" asked for a new one still
|
|
100
102
|
* counts.
|
|
101
103
|
*/
|
|
102
104
|
private before: BrokerConnectionRead | null = null;
|
|
@@ -125,7 +127,7 @@ export class BrokerSignInController {
|
|
|
125
127
|
};
|
|
126
128
|
}
|
|
127
129
|
|
|
128
|
-
/** Starts, or restarts with a fresh
|
|
130
|
+
/** Starts, or restarts with a fresh link. Safe to call repeatedly. */
|
|
129
131
|
start(): void {
|
|
130
132
|
const generation = ++this.generation;
|
|
131
133
|
void this.run(generation);
|
|
@@ -143,16 +145,16 @@ export class BrokerSignInController {
|
|
|
143
145
|
private async run(generation: number): Promise<void> {
|
|
144
146
|
const stale = () => this.generation !== generation;
|
|
145
147
|
let retryMs = POLL_INTERVAL_MS;
|
|
146
|
-
// Renewing: read before a
|
|
148
|
+
// Renewing: read before a link exists, so nothing the user does can come
|
|
147
149
|
// first. A failed read leaves the first poll to stand in for it: no
|
|
148
150
|
// sign-in finishes within a poll interval.
|
|
149
151
|
if (this.renew && !this.before) {
|
|
150
|
-
const read = await this.io.fetchConnection(this.broker.id).catch((
|
|
152
|
+
const read = await this.io.fetchConnection(this.broker.id).catch(logFailure("broker-signin", "Broker connection refresh failed", null));
|
|
151
153
|
if (stale()) return;
|
|
152
154
|
this.before ??= read;
|
|
153
155
|
}
|
|
154
|
-
// Each iteration is one
|
|
155
|
-
// when the
|
|
156
|
+
// Each iteration is one link: start, poll until connected, and loop again
|
|
157
|
+
// when the link expired so it refreshes instead of dead-ending.
|
|
156
158
|
while (!stale()) {
|
|
157
159
|
this.update({ phase: "starting", connectUrl: null, code: null, error: null });
|
|
158
160
|
let started: Awaited<ReturnType<BrokerSignInIo["start"]>>;
|
|
@@ -174,9 +176,9 @@ export class BrokerSignInController {
|
|
|
174
176
|
}
|
|
175
177
|
if (stale()) return;
|
|
176
178
|
retryMs = POLL_INTERVAL_MS;
|
|
177
|
-
this.update({ phase: "waiting", connectUrl: started.connectUrl, code: started.code, error: null });
|
|
178
|
-
const deadline =
|
|
179
|
-
// At least one poll per
|
|
179
|
+
this.update({ phase: "waiting", connectUrl: started.connectUrl, code: started.code ?? null, error: null });
|
|
180
|
+
const deadline = linkDeadline(started.expiresAt, this.io.now());
|
|
181
|
+
// At least one poll per link, so the next link is never asked for at once.
|
|
180
182
|
do {
|
|
181
183
|
await this.io.delay(POLL_INTERVAL_MS);
|
|
182
184
|
if (stale()) return;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logFailure } from "../utils/log-failure";
|
|
1
2
|
import type { AppResourceStorePort, AppTickerRepositoryPort } from "../core/app-service-ports";
|
|
2
3
|
import { persistBrokerAccounts } from "./account-cache";
|
|
3
4
|
import type { BrokerAdapter, BrokerPosition } from "../types/broker";
|
|
@@ -218,7 +219,7 @@ export async function syncBrokerInstance({
|
|
|
218
219
|
throw new Error(`Broker "${instance.brokerType}" is not available.`);
|
|
219
220
|
}
|
|
220
221
|
|
|
221
|
-
const valid = await broker.validate(instance).catch((
|
|
222
|
+
const valid = await broker.validate(instance).catch(logFailure("broker-sync", "Broker validation failed", false));
|
|
222
223
|
throwIfBrokerImportCancelled(signal);
|
|
223
224
|
if (!valid) {
|
|
224
225
|
throw new Error(`${broker.name} setup is incomplete.`);
|
|
@@ -203,6 +203,7 @@ function resolvedOutput(value: unknown): ResolvedSeries {
|
|
|
203
203
|
onlyKeys(input, "resolve output", [
|
|
204
204
|
"id", "label", "color", "unit", "unitGroup", "nativeFrequency", "timestampMode", "dataShape", "style", "transform",
|
|
205
205
|
"axis", "panelId", "interpolation", "timeBasis", "latestChangePercent", "points", "warning", "hidden", "attribution",
|
|
206
|
+
"priceOf",
|
|
206
207
|
]);
|
|
207
208
|
if (!Array.isArray(input.points)) fail("resolve output points", "expected an array.");
|
|
208
209
|
if (input.points.length > MAX_CHART_SERIES_POINTS) {
|
|
@@ -252,6 +253,7 @@ function resolvedOutput(value: unknown): ResolvedSeries {
|
|
|
252
253
|
...(input.hidden !== undefined
|
|
253
254
|
? typeof input.hidden === "boolean" ? { hidden: input.hidden } : fail("resolve output hidden", "expected a boolean.")
|
|
254
255
|
: {}),
|
|
256
|
+
...(input.priceOf !== undefined ? { priceOf: boundedString(input.priceOf, "resolve output price ticker", 64) } : {}),
|
|
255
257
|
};
|
|
256
258
|
}
|
|
257
259
|
|
|
@@ -150,9 +150,7 @@ export class CapabilityRegistry {
|
|
|
150
150
|
for (const subscriptionId of [...this.subscriptions.keys()]) {
|
|
151
151
|
try {
|
|
152
152
|
this.unsubscribe(subscriptionId);
|
|
153
|
-
} catch {
|
|
154
|
-
// Continue releasing the remaining subscriptions.
|
|
155
|
-
}
|
|
153
|
+
} catch { /* Intentional ignore: Teardown must attempt every subscription disposer even if one has already closed. */ }
|
|
156
154
|
}
|
|
157
155
|
this.capabilities.clear();
|
|
158
156
|
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { BankFiguresPayload } from "../api-client/bank-figures";
|
|
2
|
+
import type { TickerFinancials } from "../types/financials";
|
|
3
|
+
import type { CliReportEntry } from "./report-tables";
|
|
4
|
+
import { isForeignIssuerForm, type StatementReportSources } from "./statement-filings";
|
|
5
|
+
import { formatCompact, formatPercent } from "../utils/format";
|
|
6
|
+
import { logFailure } from "../utils/log-failure";
|
|
7
|
+
import { nonUsSecListingVenue } from "../utils/sec";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The Bank section of `fundamentals` for a bank holding company: its deposits
|
|
11
|
+
* and their change on the quarter from its own filings, and its lead bank's
|
|
12
|
+
* uninsured share and CRE concentration from the FDIC call report. Every line
|
|
13
|
+
* names the quarter and the filing or report date it is from. Methodology is
|
|
14
|
+
* in docs/usage.md (Statement filings and bank figures).
|
|
15
|
+
*/
|
|
16
|
+
export interface BankSection {
|
|
17
|
+
/** Label and text, as the report prints them. */
|
|
18
|
+
lines: Array<[string, string]>;
|
|
19
|
+
/** The same figures for --csv and --ndjson. */
|
|
20
|
+
entries: CliReportEntry[];
|
|
21
|
+
/** Muted lines under the section: what is missing and why, and where NIM is. */
|
|
22
|
+
notes: string[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const money = (value: number) => `${formatCompact(value)} USD`;
|
|
26
|
+
const share = (value: number) => `${(value * 100).toFixed(1)}%`;
|
|
27
|
+
const day = (stamp: string | null) => stamp?.slice(0, 10) ?? null;
|
|
28
|
+
|
|
29
|
+
/** The screener on one company with its net interest margin, a Pro figure. */
|
|
30
|
+
export function nimCommand(symbol: string): string {
|
|
31
|
+
const definition = JSON.stringify({
|
|
32
|
+
version: 1, currency: null, criteria: [{ field: "symbol", op: "in", value: [symbol] }], sort: { field: "symbol", direction: "asc" },
|
|
33
|
+
});
|
|
34
|
+
return `gloomberb fn EQS --metric kpiNimPercent --definition '${definition}'`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Gloom Cloud's note for a company with no US GAAP deposits tag, which reads as if it filed 10-Qs; a foreign issuer files neither. */
|
|
38
|
+
const NO_DEPOSITS_TAG_NOTE = "The company's 10-Q and 10-K tag no total deposits.";
|
|
39
|
+
|
|
40
|
+
export function bankSection(bank: BankFiguresPayload, symbol: string, { latestStatementDate, annualReportForm }: {
|
|
41
|
+
/** The newest statement period, YYYY-MM-DD. */
|
|
42
|
+
latestStatementDate: string | null;
|
|
43
|
+
/** The company's annual report form: a 20-F or 40-F names a foreign group, whose FDIC-insured bank is only its US arm. */
|
|
44
|
+
annualReportForm: string | null;
|
|
45
|
+
}): BankSection | null {
|
|
46
|
+
if (!bank.bank) return null;
|
|
47
|
+
const foreignGroup = isForeignIssuerForm(annualReportForm);
|
|
48
|
+
const lines: Array<[string, string]> = [];
|
|
49
|
+
const entries: CliReportEntry[] = [];
|
|
50
|
+
const notes = foreignGroup ? bank.notes.filter((note) => note !== NO_DEPOSITS_TAG_NOTE) : [...bank.notes];
|
|
51
|
+
const { deposits, leadBank, callReport } = bank;
|
|
52
|
+
|
|
53
|
+
if (leadBank) {
|
|
54
|
+
const largest = leadBank.otherBanks > 0
|
|
55
|
+
? `largest of ${leadBank.otherBanks + 1} insured banks by assets${leadBank.reportDate ? ` at ${leadBank.reportDate}` : ""}`
|
|
56
|
+
: null;
|
|
57
|
+
const role = foreignGroup
|
|
58
|
+
? ["US bank subsidiary only", largest].filter(Boolean).join(", ")
|
|
59
|
+
: !leadBank.holdingCompany ? "the company is the bank" : largest ?? "its insured bank";
|
|
60
|
+
const asOf = day(leadBank.source.datasetAsOf);
|
|
61
|
+
lines.push(["Lead Bank", `${leadBank.name} (FDIC cert ${leadBank.cert}; ${role}${asOf ? `; FDIC data of ${asOf}` : ""})`]);
|
|
62
|
+
entries.push({ label: "Lead Bank", value: leadBank.name }, { label: "Lead Bank FDIC Cert", value: leadBank.cert });
|
|
63
|
+
if (foreignGroup) {
|
|
64
|
+
entries.push({ label: "Lead Bank Scope", value: "US bank subsidiary of a foreign group" });
|
|
65
|
+
notes.push(`${symbol} reports as a foreign issuer (${annualReportForm}): the FDIC figures are those of its US bank subsidiary, ${leadBank.name}, `
|
|
66
|
+
+ `not the group's. Group deposits are in its ${annualReportForm} and not read here.`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (deposits) {
|
|
71
|
+
const { latest, previous } = deposits;
|
|
72
|
+
const parts = latest.basis === "sum" ? "; interest-bearing plus noninterest-bearing" : "";
|
|
73
|
+
lines.push(["Deposits", `${money(latest.value)} at ${latest.date} (${latest.form} filed ${latest.filed}${parts})`]);
|
|
74
|
+
entries.push(
|
|
75
|
+
{ label: "Deposits", value: latest.value, unit: "USD" },
|
|
76
|
+
{ label: "Deposits Date", value: latest.date },
|
|
77
|
+
{ label: "Deposits Filing", value: `${latest.form} filed ${latest.filed}` },
|
|
78
|
+
);
|
|
79
|
+
if (previous && previous.value > 0) {
|
|
80
|
+
const depositsChange = latest.value / previous.value - 1;
|
|
81
|
+
lines.push(["Deposits QoQ", `${formatPercent(depositsChange)} from ${money(previous.value)} at ${previous.date} (${previous.form} filed ${previous.filed})`]);
|
|
82
|
+
entries.push({ label: "Deposits QoQ", value: depositsChange * 100, unit: "%" });
|
|
83
|
+
}
|
|
84
|
+
if (latestStatementDate && latest.date < latestStatementDate) {
|
|
85
|
+
notes.push(`Deposits are as of ${latest.date}: the company's later filings do not tag total deposits.`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (callReport) {
|
|
90
|
+
const asOf = day(callReport.source.datasetAsOf);
|
|
91
|
+
const report = `call report ${callReport.reportDate}${asOf ? `, FDIC data of ${asOf}` : ""}`;
|
|
92
|
+
// The FDIC's estimate covers deposits in domestic offices: its uninsured part over insured plus uninsured.
|
|
93
|
+
const { uninsuredShare, uninsuredDeposits, insuredDeposits } = callReport;
|
|
94
|
+
const estimated = uninsuredDeposits != null && insuredDeposits != null ? uninsuredDeposits + insuredDeposits : null;
|
|
95
|
+
lines.push(["Uninsured Deposits", uninsuredShare != null && uninsuredDeposits != null && estimated != null
|
|
96
|
+
? `${share(uninsuredShare)} of domestic deposits (${formatCompact(uninsuredDeposits)} uninsured of ${money(estimated)} estimated insured plus uninsured; ${report})`
|
|
97
|
+
: `not reported: banks under $1B in assets do not file the estimate (${report})`]);
|
|
98
|
+
if (uninsuredShare != null) entries.push({ label: "Uninsured Share of Domestic Deposits", value: uninsuredShare * 100, unit: "%" });
|
|
99
|
+
if (uninsuredDeposits != null) entries.push({ label: "Uninsured Deposits", value: uninsuredDeposits, unit: "USD" });
|
|
100
|
+
// Deposits in foreign offices are outside the estimate, and FDIC insurance does not cover them.
|
|
101
|
+
const foreignOffices = callReport.totalDeposits != null && callReport.domesticDeposits != null
|
|
102
|
+
? callReport.totalDeposits - callReport.domesticDeposits
|
|
103
|
+
: 0;
|
|
104
|
+
if (uninsuredShare != null && callReport.totalDeposits && foreignOffices / callReport.totalDeposits >= 0.005) {
|
|
105
|
+
entries.push({ label: "Foreign Office Deposits", value: foreignOffices, unit: "USD" });
|
|
106
|
+
notes.push(`The uninsured share covers domestic offices; another ${money(foreignOffices)} of deposits sits in foreign offices, which FDIC insurance does not cover.`);
|
|
107
|
+
}
|
|
108
|
+
if (callReport.cre && callReport.capital && callReport.creToCapital != null) {
|
|
109
|
+
lines.push(["CRE / Capital", `${share(callReport.creToCapital)}: CRE ${money(callReport.cre.total)} over ${callReport.capital.basis} ${money(callReport.capital.value)} (${report})`]);
|
|
110
|
+
entries.push(
|
|
111
|
+
{ label: "CRE / Capital", value: callReport.creToCapital * 100, unit: "%" },
|
|
112
|
+
{ label: "CRE Loans", value: callReport.cre.total, unit: "USD" },
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
entries.push({ label: "Call Report Date", value: callReport.reportDate });
|
|
116
|
+
if (asOf) entries.push({ label: "FDIC Data As Of", value: asOf });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
notes.push(`NIM is a Pro figure in EQS: ${nimCommand(symbol)}`);
|
|
120
|
+
return { lines, entries, notes };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** The line for a bank whose figures could not be read. */
|
|
124
|
+
const BANK_FIGURES_UNAVAILABLE = "Bank figures (deposits, uninsured share, CRE) are unavailable right now.";
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* A US-listed financial company's bank figures, or null: a company outside
|
|
128
|
+
* financial services is never asked about (Gloom Cloud answers a non-bank with
|
|
129
|
+
* `bank: false` anyway). A bank whose figures could not be read gets a one-line
|
|
130
|
+
* `note` in their place.
|
|
131
|
+
*/
|
|
132
|
+
export async function loadBankFigures(
|
|
133
|
+
sources: StatementReportSources | undefined,
|
|
134
|
+
listing: { symbol: string; exchange?: string },
|
|
135
|
+
financials: Pick<TickerFinancials, "profile">,
|
|
136
|
+
): Promise<{ bank: BankFiguresPayload | null; note: string | null }> {
|
|
137
|
+
const profile = `${financials.profile?.sector ?? ""} ${financials.profile?.industry ?? ""}`;
|
|
138
|
+
if (!sources || !/financ|bank/i.test(profile) || nonUsSecListingVenue(listing.symbol, listing.exchange)) return { bank: null, note: null };
|
|
139
|
+
try {
|
|
140
|
+
const bank = await sources.bankFigures(listing.symbol);
|
|
141
|
+
return { bank: bank.bank ? bank : null, note: null };
|
|
142
|
+
} catch (error) {
|
|
143
|
+
logFailure("cli.bank-section", "Reading bank figures failed")(error);
|
|
144
|
+
// Only a bank's report misses them; another financial company's says nothing.
|
|
145
|
+
return { bank: null, note: /bank/i.test(financials.profile?.industry ?? "") ? BANK_FIGURES_UNAVAILABLE : null };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import type { DataProvider } from "../types/data-provider";
|
|
2
|
+
import { fixedUsdRate } from "../utils/currency-units";
|
|
3
|
+
import { logFailure } from "../utils/log-failure";
|
|
2
4
|
|
|
3
5
|
export function createBaseConverter(dataProvider: Pick<DataProvider, "getExchangeRate">, baseCurrency: string) {
|
|
4
6
|
const rateCache = new Map<string, number>([["USD", 1]]);
|
|
5
7
|
|
|
6
8
|
const getRate = async (currency: string): Promise<number> => {
|
|
7
9
|
const normalizedCurrency = currency.toUpperCase();
|
|
8
|
-
const cached = rateCache.get(normalizedCurrency);
|
|
10
|
+
const cached = rateCache.get(normalizedCurrency) ?? fixedUsdRate(normalizedCurrency);
|
|
9
11
|
if (cached != null) return cached;
|
|
10
12
|
try {
|
|
11
13
|
const rate = await dataProvider.getExchangeRate(normalizedCurrency);
|
|
12
14
|
const validRate = Number.isFinite(rate) && rate > 0 ? rate : Number.NaN;
|
|
13
15
|
rateCache.set(normalizedCurrency, validRate);
|
|
14
16
|
return validRate;
|
|
15
|
-
} catch {
|
|
17
|
+
} catch (error) {
|
|
18
|
+
logFailure("cli-base-converter", "Could not load base conversion exchange rate")(error);
|
|
16
19
|
return Number.NaN;
|
|
17
20
|
}
|
|
18
21
|
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { apiClient } from "../api-client";
|
|
2
|
+
import type { CryptoMarketAsset } from "../api-client/crypto-markets";
|
|
3
|
+
import { logFailure } from "../utils/log-failure";
|
|
4
|
+
|
|
5
|
+
/** A coin on the crypto board as a hint names it: its one symbol and its name. */
|
|
6
|
+
export interface BoardCoin {
|
|
7
|
+
symbol: string;
|
|
8
|
+
name: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type BoardEntry = Pick<CryptoMarketAsset, "symbol" | "code" | "name" | "marketCap">;
|
|
12
|
+
|
|
13
|
+
/** How long a hint waits for the board; a slow board leaves the plain search hint. */
|
|
14
|
+
const BOARD_TIMEOUT_MS = 4_000;
|
|
15
|
+
const BARE_NOT_A_TICKER = /Not a ticker: ([A-Za-z0-9]+)\./;
|
|
16
|
+
|
|
17
|
+
let board: readonly BoardEntry[] | null = null;
|
|
18
|
+
let loading: Promise<void> | null = null;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Reads the crypto board once, when some text says a bare word is not a
|
|
22
|
+
* ticker, so `boardCoinFor` can name the coin it may be. Never throws; a
|
|
23
|
+
* board that fails or is slow leaves the hints as they were.
|
|
24
|
+
*/
|
|
25
|
+
export async function primeBoardCoins(
|
|
26
|
+
texts: ReadonlyArray<string | null | undefined>,
|
|
27
|
+
load: () => Promise<{ assets: readonly BoardEntry[] }> = () => apiClient.getCloudCryptoMarkets(),
|
|
28
|
+
): Promise<void> {
|
|
29
|
+
if (board || !texts.some((text) => !!text && BARE_NOT_A_TICKER.test(text))) return;
|
|
30
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
31
|
+
loading ??= Promise.race([
|
|
32
|
+
load().then((data) => { board = boardEntries(data.assets); }),
|
|
33
|
+
new Promise<void>((resolve) => { timer = setTimeout(resolve, BOARD_TIMEOUT_MS); }),
|
|
34
|
+
]).catch(logFailure("cli.board-coins", "Read the crypto board for a coin hint failed")).finally(() => clearTimeout(timer));
|
|
35
|
+
await loading;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The board's coins a hint can name: a symbol, a code and a name each. */
|
|
39
|
+
function boardEntries(assets: unknown): BoardEntry[] {
|
|
40
|
+
return (Array.isArray(assets) ? assets : []).filter((asset): asset is BoardEntry => !!asset
|
|
41
|
+
&& typeof asset.symbol === "string" && /^[A-Z0-9]+-USD$/.test(asset.symbol)
|
|
42
|
+
&& typeof asset.code === "string" && !!asset.code && typeof asset.name === "string");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const compact = (text: string) => text.toUpperCase().replace(/[^\p{L}\p{N}]/gu, "");
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The board coin a bare word that is no ticker most likely means: its code
|
|
49
|
+
* (USDT, USDS, USDE) or its name (TETHER for Tether USDt, BITCOIN), the
|
|
50
|
+
* largest by market cap when several answer. Null before the board is read,
|
|
51
|
+
* and for anything else.
|
|
52
|
+
*/
|
|
53
|
+
export function boardCoinFor(typed: string): BoardCoin | null {
|
|
54
|
+
const id = typed.trim().replace(/^\$/, "").toUpperCase();
|
|
55
|
+
if (!board || !/^[A-Z0-9]+$/.test(id)) return null;
|
|
56
|
+
const byCode = board.filter((coin) => coin.code.toUpperCase() === id);
|
|
57
|
+
const byName = byCode.length ? [] : board.filter((coin) => {
|
|
58
|
+
const name = compact(coin.name);
|
|
59
|
+
const first = compact(coin.name.split(/\s+/)[0] ?? "");
|
|
60
|
+
return name === id || (id.length >= 5 && first === id);
|
|
61
|
+
});
|
|
62
|
+
const best = [...byCode.length ? byCode : byName].sort((left, right) => (right.marketCap ?? 0) - (left.marketCap ?? 0))[0];
|
|
63
|
+
return best ? { symbol: best.symbol, name: best.name } : null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** For tests: forget the board read. */
|
|
67
|
+
export function resetBoardCoins(entries: readonly BoardEntry[] | null = null): void {
|
|
68
|
+
board = entries;
|
|
69
|
+
loading = null;
|
|
70
|
+
}
|