@gloom-sh/terminal 0.18.5 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/patches/electrobun@1.18.1.patch +163 -8
- package/src/api-client/app-attention.ts +3 -0
- package/src/api-client/beneficial-owners.ts +3 -0
- package/src/api-client/company-kpis.ts +3 -0
- package/src/api-client/cot.ts +6 -2
- package/src/api-client/credit-documents.ts +6 -0
- package/src/api-client/data.ts +19 -7
- package/src/api-client/equity-screener.ts +24 -0
- package/src/api-client/fund-facts.ts +45 -4
- package/src/api-client/hiring.ts +3 -0
- package/src/api-client/index.ts +15 -0
- package/src/api-client/ipo.ts +24 -1
- package/src/api-client/market-discovery.ts +10 -0
- package/src/api-client/market-movers.ts +7 -0
- package/src/api-client/members.ts +1 -1
- package/src/api-client/mna.ts +3 -0
- package/src/api-client/paths.ts +9 -1
- package/src/api-client/perps.ts +43 -0
- package/src/api-client/research-activity.ts +3 -2
- package/src/api-client/roadmap.ts +85 -0
- package/src/api-client/types.ts +82 -2
- package/src/app/pane-runtime/plugin-bindings.ts +9 -5
- package/src/app/runtime/broker-import.ts +0 -2
- package/src/app/runtime/plugin-bindings.ts +73 -56
- package/src/architecture/core-builtin-allowlist.json +2 -2
- package/src/architecture/pane-tabs-allowlist.json +3 -0
- package/src/architecture/ttl-caches-allowlist.json +19 -0
- package/src/brokers/sync-broker-instance.ts +2 -8
- package/src/capabilities/chart-series.ts +1 -1
- package/src/cli/commands/crypto-hints.ts +40 -24
- package/src/cli/commands/market.ts +107 -38
- package/src/cli/commands/news-search.ts +22 -0
- package/src/cli/commands/options-chain.ts +48 -7
- package/src/cli/commands/options-leaps.ts +9 -3
- package/src/cli/commands/overview.ts +147 -32
- package/src/cli/commands/search.ts +21 -1
- package/src/cli/commands/themes.ts +11 -2
- package/src/cli/commands/ticker.ts +87 -36
- package/src/cli/company-name-hint.ts +41 -0
- package/src/cli/earnings-calendar.ts +1 -1
- package/src/cli/errors.ts +25 -2
- package/src/cli/glossary.ts +31 -2
- package/src/cli/history-rows.ts +57 -9
- package/src/cli/index-roots.ts +0 -7
- package/src/cli/index.ts +2 -0
- package/src/cli/listing-arg.ts +235 -60
- package/src/cli/output-file.ts +68 -0
- package/src/cli/pane-functions/capabilities.ts +4 -0
- package/src/cli/pane-functions/catalog.ts +80 -7
- package/src/cli/pane-functions/discovery.ts +21 -1
- package/src/cli/pane-functions/dom.ts +13 -3
- package/src/cli/pane-functions/freshness.ts +93 -4
- package/src/cli/pane-functions/function-help.ts +20 -10
- package/src/cli/pane-functions/headless.ts +17 -7
- package/src/cli/pane-functions/index.ts +54 -16
- package/src/cli/pane-functions/market-session.ts +66 -4
- package/src/cli/pane-functions/options.ts +19 -1
- package/src/cli/pane-functions/resolver.ts +19 -8
- package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
- package/src/cli/pane-functions/screenshot-types.ts +5 -0
- package/src/cli/quote-answer.ts +22 -11
- package/src/cli/saved-copy-persistence.ts +88 -0
- package/src/cli/ticker-resolution.ts +3 -2
- package/src/components/chart/composite/composite-chart.tsx +4 -0
- package/src/components/chart/composite/interactions.ts +6 -0
- package/src/components/chart/composite/legend.tsx +45 -7
- package/src/components/chart/composite/panel-gestures.ts +33 -0
- package/src/components/chart/composite/panel-surface.tsx +52 -3
- package/src/components/chart/composite/rasterizer.ts +24 -13
- package/src/components/chart/composite/scene.ts +39 -4
- package/src/components/chart/composite/series-cues.ts +14 -0
- package/src/components/chart/composite/text-renderer.ts +9 -2
- package/src/components/chart/composite/tool-catalog.ts +36 -0
- package/src/components/chart/composite/tools.ts +203 -44
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
- package/src/components/command-bar/assist/inventory.ts +2 -1
- package/src/components/command-bar/commands/direct/index.ts +28 -13
- package/src/components/command-bar/list/model.ts +5 -0
- package/src/components/command-bar/pane-templates/items.ts +3 -2
- package/src/components/command-bar/routes/root/runtime.ts +6 -7
- package/src/components/command-bar/selection-runtime.ts +52 -3
- package/src/components/command-bar/surface/index.tsx +2 -0
- package/src/components/command-bar/workflow/collection-actions.ts +3 -3
- package/src/components/layout/pane/capture-stamp.ts +2 -2
- package/src/components/layout/pane/footer/index.tsx +22 -6
- package/src/components/layout/pane/footer/model.ts +83 -15
- package/src/components/layout/pane/status-footer.ts +35 -2
- package/src/components/layout/status-widget-space.ts +9 -0
- package/src/components/onboarding/onboarding-wizard.tsx +4 -1
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +12 -7
- package/src/components/onboarding/wizard-broker-sync.ts +0 -1
- package/src/components/onboarding/wizard-positions.ts +3 -3
- package/src/components/ticker/list-table-view.tsx +8 -21
- package/src/components/ui/choice-dialog.tsx +23 -18
- package/src/components/ui/choice-groups.ts +18 -0
- package/src/components/ui/data-table/opentui/index.tsx +9 -7
- package/src/components/ui/data-table/opentui/model.ts +26 -1
- package/src/components/ui/data-table/types.ts +2 -0
- package/src/components/ui/display.tsx +18 -3
- package/src/components/ui/icon.tsx +4 -1
- package/src/components/ui/list-view.tsx +8 -4
- package/src/components/ui/query-bar-keys.tsx +2 -0
- package/src/components/ui/query-bar.tsx +1 -0
- package/src/components/ui/select-button.tsx +5 -1
- package/src/components/ui/select-field.tsx +1 -0
- package/src/components/ui/table-layout.ts +8 -5
- package/src/core/app-runtime.ts +2 -0
- package/src/core/app-services.ts +2 -2
- package/src/data/config/chart-settings.ts +1 -1
- package/src/data/json-ticker-repository.ts +17 -8
- package/src/data/plugin-cache.ts +5 -3
- package/src/data/saved-copies.ts +8 -0
- package/src/data/ticker-repository.ts +16 -9
- package/src/data/ticker-writes.ts +81 -0
- package/src/data/ttl-cache.ts +165 -0
- package/src/docs/snapshot.json +103 -142
- package/src/i18n/es.ts +20 -6
- package/src/i18n/ja.ts +20 -6
- package/src/i18n/ko.ts +20 -6
- package/src/i18n/zh-cn.ts +20 -6
- package/src/i18n/zh-tw.ts +20 -6
- package/src/market-data/depositary-pair.ts +27 -3
- package/src/market-data/fund-facts.ts +50 -0
- package/src/market-data/market/change-basis.ts +16 -3
- package/src/market-data/market/format.ts +25 -0
- package/src/market-data/market/freshness.ts +2 -1
- package/src/market-data/options-alternatives.ts +81 -4
- package/src/market-data/quotes/metadata.ts +7 -0
- package/src/news/aggregator.ts +119 -9
- package/src/news/cloud-session.ts +21 -0
- package/src/news/hooks.ts +1 -0
- package/src/news/news-model.ts +1 -0
- package/src/news/types.ts +17 -0
- package/src/plugins/builtin/alerts/alert-engine.ts +20 -1
- package/src/plugins/builtin/alerts/alert-log.ts +33 -0
- package/src/plugins/builtin/alerts/calendar-events.ts +10 -4
- package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
- package/src/plugins/builtin/alerts/cli.ts +362 -80
- package/src/plugins/builtin/alerts/command.ts +25 -1
- package/src/plugins/builtin/alerts/event-rows.ts +24 -3
- package/src/plugins/builtin/alerts/events-pane.tsx +34 -10
- package/src/plugins/builtin/alerts/fired-log.ts +73 -9
- package/src/plugins/builtin/alerts/format.ts +75 -5
- package/src/plugins/builtin/alerts/headless.ts +34 -12
- package/src/plugins/builtin/alerts/history-rows.ts +3 -2
- package/src/plugins/builtin/alerts/history.ts +1 -1
- package/src/plugins/builtin/alerts/index.tsx +79 -16
- package/src/plugins/builtin/alerts/metric-command.ts +77 -4
- package/src/plugins/builtin/alerts/metric-data.ts +54 -13
- package/src/plugins/builtin/alerts/metric-model.ts +163 -14
- package/src/plugins/builtin/alerts/metric-parity-fixture.json +1 -1
- package/src/plugins/builtin/alerts/pane.tsx +61 -23
- package/src/plugins/builtin/alerts/quotes.ts +6 -0
- package/src/plugins/builtin/alerts/storage.ts +12 -8
- package/src/plugins/builtin/alerts/types.ts +4 -0
- package/src/plugins/builtin/analytics/index.tsx +2 -1
- package/src/plugins/builtin/analytics/risk-client.ts +16 -5
- package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
- package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
- package/src/plugins/builtin/analytics/risk-model.ts +55 -9
- package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
- package/src/plugins/builtin/attention/index.tsx +2 -1
- package/src/plugins/builtin/attention/listing.ts +14 -0
- package/src/plugins/builtin/broker-manager/index.tsx +7 -3
- package/src/plugins/builtin/buildout/cells.tsx +12 -4
- package/src/plugins/builtin/buildout/format.ts +38 -0
- package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
- package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
- package/src/plugins/builtin/buildout/table-model.ts +23 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +6 -2
- package/src/plugins/builtin/cds/client.ts +142 -30
- package/src/plugins/builtin/cds/headless.ts +35 -8
- package/src/plugins/builtin/cds/model.ts +145 -18
- package/src/plugins/builtin/cds/pane.tsx +16 -5
- package/src/plugins/builtin/changelog/index.tsx +9 -6
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +3 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +7 -0
- package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -2
- package/src/plugins/builtin/chart-composer/follow-binding.ts +14 -2
- package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -1
- package/src/plugins/builtin/chart-composer/headless.ts +6 -1
- package/src/plugins/builtin/chart-composer/index.tsx +5 -6
- package/src/plugins/builtin/chart-composer/pane.tsx +37 -8
- package/src/plugins/builtin/chart-composer/settings.ts +27 -0
- package/src/plugins/builtin/chat/content/command-suggestions.ts +1 -0
- package/src/plugins/builtin/chat/controller/index.ts +5 -1
- package/src/plugins/builtin/chat/controller/notifications.ts +33 -3
- package/src/plugins/builtin/chat/controller/state.ts +25 -2
- package/src/plugins/builtin/chat/controller/unread-people.ts +89 -23
- package/src/plugins/builtin/chat/controller/utils.ts +7 -2
- package/src/plugins/builtin/chat/pane-links/commands.ts +7 -1
- package/src/plugins/builtin/chat/status-preview.ts +79 -0
- package/src/plugins/builtin/chat/status-widget.tsx +147 -10
- package/src/plugins/builtin/chat/unread-badge.ts +51 -10
- package/src/plugins/builtin/clinical-trials/client.ts +49 -4
- package/src/plugins/builtin/clinical-trials/model.ts +86 -31
- package/src/plugins/builtin/clinical-trials/pane.tsx +61 -49
- package/src/plugins/builtin/clinical-trials/sponsor-listing.ts +90 -0
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +95 -15
- package/src/plugins/builtin/clinical-trials/trial-pages.ts +50 -0
- package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
- package/src/plugins/builtin/company-attention/building.tsx +5 -1
- package/src/plugins/builtin/company-attention/client.ts +8 -9
- package/src/plugins/builtin/company-attention/evidence.ts +4 -1
- package/src/plugins/builtin/company-attention/headless.ts +2 -1
- package/src/plugins/builtin/company-attention/moves.tsx +5 -1
- package/src/plugins/builtin/company-attention/pane.tsx +12 -2
- package/src/plugins/builtin/company-attention/signals.tsx +5 -1
- package/src/plugins/builtin/company-kpis/headless.ts +8 -3
- package/src/plugins/builtin/company-kpis/model.ts +73 -5
- package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
- package/src/plugins/builtin/composite-plugins.ts +2 -1
- package/src/plugins/builtin/correlation/headless.ts +13 -5
- package/src/plugins/builtin/correlation/history.ts +22 -0
- package/src/plugins/builtin/cot/client.ts +22 -18
- package/src/plugins/builtin/cot/headless.ts +15 -10
- package/src/plugins/builtin/cot/model.ts +14 -1
- package/src/plugins/builtin/cot/pane.tsx +22 -14
- package/src/plugins/builtin/credit-boards/client.ts +6 -10
- package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
- package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
- package/src/plugins/builtin/credit-conditions/model.ts +4 -0
- package/src/plugins/builtin/credit-conditions/series.ts +3 -1
- package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
- package/src/plugins/builtin/credit-documents/export.ts +173 -0
- package/src/plugins/builtin/credit-documents/headless.ts +10 -10
- package/src/plugins/builtin/credit-documents/model.ts +104 -14
- package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
- package/src/plugins/builtin/custom-view/sources.ts +2 -0
- package/src/plugins/builtin/dividend-yield/client.ts +131 -23
- package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
- package/src/plugins/builtin/dividend-yield/model.ts +8 -5
- package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
- package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
- package/src/plugins/builtin/dividend-yield/types.ts +20 -2
- package/src/plugins/builtin/dividend-yield/view.ts +30 -2
- package/src/plugins/builtin/docs/targets.ts +5 -1
- package/src/plugins/builtin/doe/headless.ts +3 -2
- package/src/plugins/builtin/doe/pane.tsx +13 -3
- package/src/plugins/builtin/earnings/board-model.ts +16 -15
- package/src/plugins/builtin/earnings/board.tsx +62 -71
- package/src/plugins/builtin/earnings/headless.ts +40 -4
- package/src/plugins/builtin/earnings/scope.ts +31 -43
- package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
- package/src/plugins/builtin/earnings-calls/data.ts +18 -6
- package/src/plugins/builtin/earnings-calls/format.ts +31 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +20 -5
- package/src/plugins/builtin/earnings-calls/model.ts +6 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +12 -4
- package/src/plugins/builtin/econ/calendar-model.ts +48 -1
- package/src/plugins/builtin/econ/calendar-source.ts +4 -2
- package/src/plugins/builtin/econ/detail-view.tsx +2 -2
- package/src/plugins/builtin/econ/figures.ts +48 -0
- package/src/plugins/builtin/econ/index.tsx +14 -5
- package/src/plugins/builtin/econ/print-watch.ts +3 -1
- package/src/plugins/builtin/econ/types.ts +2 -0
- package/src/plugins/builtin/econ-statistics/client.ts +11 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +16 -6
- package/src/plugins/builtin/econ-statistics/pane.tsx +8 -4
- package/src/plugins/builtin/econ-statistics/stats.ts +39 -9
- package/src/plugins/builtin/equity-screener/categories.ts +179 -0
- package/src/plugins/builtin/equity-screener/client.ts +4 -2
- package/src/plugins/builtin/equity-screener/headless.ts +144 -13
- package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
- package/src/plugins/builtin/equity-screener/model.ts +99 -10
- package/src/plugins/builtin/equity-screener/pane.tsx +68 -13
- package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
- package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
- package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
- package/src/plugins/builtin/fx-matrix/client.ts +6 -3
- package/src/plugins/builtin/fx-matrix/headless.ts +1 -1
- package/src/plugins/builtin/fx-matrix/index.tsx +4 -2
- package/src/plugins/builtin/fx-matrix/live-legs.ts +42 -3
- package/src/plugins/builtin/gpu/headless.ts +8 -2
- package/src/plugins/builtin/help/function-index.ts +4 -1
- package/src/plugins/builtin/help/index.tsx +62 -59
- package/src/plugins/builtin/help/shortcut-model.ts +1 -1
- package/src/plugins/builtin/help/shortcut-table.tsx +5 -6
- package/src/plugins/builtin/help/table-cells.tsx +13 -2
- package/src/plugins/builtin/holders/beneficial-client.ts +5 -1
- package/src/plugins/builtin/holders/beneficial-view.tsx +3 -0
- package/src/plugins/builtin/holders/fund-holders.ts +51 -0
- package/src/plugins/builtin/holders/headless.ts +22 -3
- package/src/plugins/builtin/holders/pane.tsx +3 -0
- package/src/plugins/builtin/holders/report-rows.ts +4 -1
- package/src/plugins/builtin/insider/headless.ts +2 -2
- package/src/plugins/builtin/ipo-calendar/client.ts +18 -2
- package/src/plugins/builtin/ipo-calendar/headless.ts +75 -11
- package/src/plugins/builtin/ipo-calendar/listing-ipo.ts +160 -0
- package/src/plugins/builtin/ipo-calendar/model.ts +98 -2
- package/src/plugins/builtin/iv-history/client.ts +52 -11
- package/src/plugins/builtin/iv-history/format.ts +36 -23
- package/src/plugins/builtin/iv-history/headless.ts +33 -18
- package/src/plugins/builtin/iv-history/index.tsx +2 -2
- package/src/plugins/builtin/iv-history/model.ts +163 -27
- package/src/plugins/builtin/iv-history/pane.tsx +10 -6
- package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
- package/src/plugins/builtin/iv-history/universe.ts +5 -1
- package/src/plugins/builtin/jobs/client.ts +25 -29
- package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
- package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
- package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
- package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
- package/src/plugins/builtin/macro-day/headless.ts +12 -5
- package/src/plugins/builtin/macro-day/index.tsx +1 -1
- package/src/plugins/builtin/macro-day/model.ts +39 -5
- package/src/plugins/builtin/macro-day/pane.tsx +27 -19
- package/src/plugins/builtin/macro-day/release-table.ts +2 -2
- package/src/plugins/builtin/market-heatmap/data.ts +8 -33
- package/src/plugins/builtin/market-movers/client.ts +27 -7
- package/src/plugins/builtin/market-movers/headless.ts +34 -12
- package/src/plugins/builtin/market-movers/index.tsx +26 -14
- package/src/plugins/builtin/market-movers/min-float.ts +74 -0
- package/src/plugins/builtin/market-movers/model.ts +4 -1
- package/src/plugins/builtin/market-movers/screener.ts +22 -4
- package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
- package/src/plugins/builtin/market-movers/session-table.ts +4 -3
- package/src/plugins/builtin/market-movers/table.ts +17 -5
- package/src/plugins/builtin/members/breadth-pane.tsx +2 -1
- package/src/plugins/builtin/mna/headless.ts +2 -0
- package/src/plugins/builtin/mna/pane.tsx +3 -1
- package/src/plugins/builtin/news/headless.ts +3 -1
- package/src/plugins/builtin/news/index.tsx +4 -0
- package/src/plugins/builtin/news/wire/categories.ts +19 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
- package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
- package/src/plugins/builtin/openfda/client.ts +14 -2
- package/src/plugins/builtin/openfda/index.tsx +3 -2
- package/src/plugins/builtin/openfda/model.ts +9 -0
- package/src/plugins/builtin/openfda/pane.tsx +42 -6
- package/src/plugins/builtin/options/analytics.ts +29 -5
- package/src/plugins/builtin/options/enrichment.ts +17 -5
- package/src/plugins/builtin/options/table.ts +5 -2
- package/src/plugins/builtin/options/types.ts +2 -0
- package/src/plugins/builtin/options/view.tsx +17 -4
- package/src/plugins/builtin/options-positioning/client.ts +11 -0
- package/src/plugins/builtin/options-positioning/headless.ts +21 -8
- package/src/plugins/builtin/options-positioning/index.tsx +1 -1
- package/src/plugins/builtin/options-positioning/model.ts +78 -9
- package/src/plugins/builtin/options-positioning/pane.tsx +21 -6
- package/src/plugins/builtin/options-scenario/client.ts +116 -47
- package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
- package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
- package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
- package/src/plugins/builtin/options-scenario/csp.ts +8 -2
- package/src/plugins/builtin/options-scenario/index.tsx +9 -7
- package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
- package/src/plugins/builtin/pe-band/model.ts +12 -49
- package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
- package/src/plugins/builtin/perps/cascade.ts +236 -0
- package/src/plugins/builtin/perps/client.ts +56 -2
- package/src/plugins/builtin/perps/headless.ts +65 -9
- package/src/plugins/builtin/perps/index.tsx +7 -5
- package/src/plugins/builtin/perps/model.ts +18 -4
- package/src/plugins/builtin/perps/pane.tsx +19 -22
- package/src/plugins/builtin/perps/status-line.ts +25 -0
- package/src/plugins/builtin/plugin-module.ts +5 -1
- package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
- package/src/plugins/builtin/portfolio-list/cli/render.ts +7 -5
- package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -7
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
- package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +0 -14
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +5 -2
- package/src/plugins/builtin/portfolio-list/table.tsx +15 -9
- package/src/plugins/builtin/primary-dealers/client.ts +13 -38
- package/src/plugins/builtin/realized-vol/client.ts +13 -5
- package/src/plugins/builtin/realized-vol/evidence.ts +29 -4
- package/src/plugins/builtin/realized-vol/headless.ts +8 -4
- package/src/plugins/builtin/realized-vol/index.tsx +2 -2
- package/src/plugins/builtin/realized-vol/model.ts +37 -6
- package/src/plugins/builtin/realized-vol/pane.tsx +11 -9
- package/src/plugins/builtin/relative-rotation/client.ts +18 -4
- package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
- package/src/plugins/builtin/relative-rotation/model.ts +16 -40
- package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
- package/src/plugins/builtin/research/event-model.ts +40 -12
- package/src/plugins/builtin/research/headless.ts +1 -1
- package/src/plugins/builtin/research/index.tsx +7 -3
- package/src/plugins/builtin/research/relative-valuation-headless.ts +13 -26
- package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +59 -20
- package/src/plugins/builtin/research/relative-valuation-peers.ts +217 -20
- package/src/plugins/builtin/research/relative-valuation-settings.ts +44 -0
- package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
- package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
- package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
- package/src/plugins/builtin/risk-factors/data.ts +4 -2
- package/src/plugins/builtin/risk-factors/headless.ts +5 -2
- package/src/plugins/builtin/roadmap/index.tsx +28 -0
- package/src/plugins/builtin/roadmap/model.ts +67 -0
- package/src/plugins/builtin/roadmap/pane.tsx +244 -0
- package/src/plugins/builtin/scanner/flow-model.ts +50 -4
- package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
- package/src/plugins/builtin/scanner/flow-session.ts +146 -0
- package/src/plugins/builtin/scanner/headless.ts +204 -35
- package/src/plugins/builtin/scanner/index.ts +16 -5
- package/src/plugins/builtin/scanner/live-headless.ts +48 -5
- package/src/plugins/builtin/scanner/live-model.ts +11 -0
- package/src/plugins/builtin/scanner/live-pane.tsx +3 -3
- package/src/plugins/builtin/scanner/lock-note.ts +3 -2
- package/src/plugins/builtin/seasonality/headless.ts +10 -1
- package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
- package/src/plugins/builtin/sec/fund-facts.ts +116 -34
- package/src/plugins/builtin/sec/headless.ts +4 -2
- package/src/plugins/builtin/sec/index.tsx +3 -0
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +16 -7
- package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
- package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
- package/src/plugins/builtin/shared/earnings-report.ts +2 -2
- package/src/plugins/builtin/shared/headless-market-data.ts +2 -2
- package/src/plugins/builtin/shared/job-posting.ts +1 -0
- package/src/plugins/builtin/shared/members-menu.tsx +3 -2
- package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
- package/src/plugins/builtin/shared/report-freshness.ts +11 -2
- package/src/plugins/builtin/shared/research-cells.tsx +1 -1
- package/src/plugins/builtin/shared/series-cache.ts +4 -4
- package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
- package/src/plugins/builtin/shared/ticker-company.ts +77 -0
- package/src/plugins/builtin/shared/use-quote-board.ts +2 -1
- package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
- package/src/plugins/builtin/shared/volatility/pricing.ts +19 -0
- package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
- package/src/plugins/builtin/themes/model.ts +16 -7
- package/src/plugins/builtin/themes/pane.tsx +5 -5
- package/src/plugins/builtin/thirteenf/api.ts +12 -1
- package/src/plugins/builtin/thirteenf/data.ts +7 -7
- package/src/plugins/builtin/thirteenf/headless.ts +31 -4
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
- package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
- package/src/plugins/builtin/ticker-detail/overview/model.ts +11 -3
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +14 -8
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
- package/src/plugins/builtin/time-sales/pane.tsx +31 -19
- package/src/plugins/builtin/treasury-auctions/headless.ts +21 -12
- package/src/plugins/builtin/treasury-auctions/pane.tsx +9 -3
- package/src/plugins/builtin/treasury-daily/client.ts +41 -5
- package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
- package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
- package/src/plugins/builtin/treasury-daily/model.ts +50 -3
- package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
- package/src/plugins/builtin/vol-surface/client.ts +3 -13
- package/src/plugins/builtin/vol-surface/headless.ts +3 -3
- package/src/plugins/builtin/vol-surface/index.tsx +1 -1
- package/src/plugins/builtin/vol-surface/pane.tsx +7 -5
- package/src/plugins/builtin/volatility/index.tsx +2 -2
- package/src/plugins/builtin/volatility/pane.tsx +7 -4
- package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
- package/src/plugins/builtin/yield-curve/client.ts +61 -1
- package/src/plugins/builtin/yield-curve/headless.ts +9 -6
- package/src/plugins/builtin/yield-curve/index.tsx +34 -10
- package/src/plugins/builtin/yield-curve/swaps.tsx +9 -5
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/catalog-manifest.ts +2 -1
- package/src/plugins/host-link.ts +1 -1
- package/src/plugins/marketplace/model.ts +18 -5
- package/src/portfolio/column-values.ts +11 -7
- package/src/portfolio/futures-positions.ts +6 -3
- package/src/portfolio/position-quantity.ts +15 -9
- package/src/remote/resources.ts +2 -1
- package/src/renderers/dom/data-table/row.tsx +26 -0
- package/src/renderers/dom/desktop/icons.tsx +7 -0
- package/src/renderers/dom/desktop/list-view.tsx +13 -10
- package/src/renderers/dom/desktop/query-bar.tsx +4 -2
- package/src/renderers/dom/desktop/select-field.tsx +4 -2
- package/src/renderers/dom/host/chart-surface.tsx +21 -0
- package/src/renderers/electrobun/bun/desktop/app-name.ts +3 -3
- package/src/renderers/electrobun/bun/desktop/backend.ts +21 -10
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +15 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
- package/src/renderers/electrobun/bun/desktop/installer-folder.ts +731 -0
- package/src/renderers/electrobun/bun/desktop/relaunch.ts +102 -10
- package/src/renderers/electrobun/bun/desktop/update.ts +115 -14
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
- package/src/renderers/electrobun/bun/desktop/windows-script.ts +92 -0
- package/src/renderers/electrobun/bun/index.ts +26 -2
- package/src/renderers/electrobun/bun/window/create.ts +43 -4
- package/src/renderers/electrobun/bun/window/events.ts +20 -0
- package/src/renderers/electrobun/bun/window/windows-custom-chrome.ts +9 -2
- package/src/renderers/electrobun/bun/window/windows-native.ts +78 -0
- package/src/renderers/electrobun/bun/window/windows-quit.ts +57 -0
- package/src/renderers/electrobun/shared/app-name.ts +13 -0
- package/src/renderers/electrobun/shared/protocol.ts +12 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
- package/src/renderers/electrobun/view/main.tsx +24 -1
- package/src/renderers/electrobun/view/news-source.ts +5 -1
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
- package/src/renderers/electrobun/view/ui-host.tsx +8 -0
- package/src/renderers/electrobun/view/view-ids.ts +85 -0
- package/src/renderers/opentui/start.tsx +5 -0
- package/src/series/registry.ts +1 -0
- package/src/series/types.ts +6 -0
- package/src/series/validate.ts +1 -0
- package/src/sources/gloomberb-cloud/fred-series.ts +3 -1
- package/src/sources/gloomberb-cloud/index.ts +43 -8
- package/src/sources/gloomberb-cloud/news.ts +47 -7
- package/src/sources/provider-router/financials.ts +15 -3
- package/src/sources/provider-router/index.ts +8 -5
- package/src/state/ticker-cleanup.ts +50 -0
- package/src/sync/controller.ts +4 -3
- package/src/sync/core-contributors.ts +90 -31
- package/src/telemetry/stall-reports-dom.ts +38 -0
- package/src/telemetry/stall-reports-node.ts +75 -0
- package/src/telemetry/stall-reports.ts +214 -0
- package/src/test-support/preload.ts +14 -0
- package/src/theme/themes.ts +10 -0
- package/src/tickers/company-name-listing.ts +53 -13
- package/src/tickers/company-name.ts +50 -4
- package/src/tickers/index-roots.ts +7 -0
- package/src/tickers/listing-fate.ts +2 -2
- package/src/tickers/listing-venues.ts +15 -3
- package/src/tickers/search/index.ts +6 -2
- package/src/tickers/search/ranking.ts +5 -1
- package/src/tickers/search/upsert.ts +13 -0
- package/src/tickers/terminal-codes.ts +19 -0
- package/src/tickers/ticker-changes.ts +8 -4
- package/src/time-series/alignment.ts +1 -1
- package/src/time-series/economic.ts +18 -4
- package/src/time-series/field-catalog.ts +1 -1
- package/src/time-series/filed-trailing-eps.ts +197 -0
- package/src/time-series/fundamentals.ts +44 -1
- package/src/time-series/ice-credit.ts +3 -0
- package/src/time-series/price-comparison.ts +30 -2
- package/src/time-series/resolve-context.ts +3 -1
- package/src/time-series/resolve.ts +56 -4
- package/src/time-series/session-bar.ts +11 -0
- package/src/time-series/spec.ts +3 -2
- package/src/time-series/studies.ts +8 -2
- package/src/time-series/total-return.ts +160 -0
- package/src/time-series/transforms.ts +2 -3
- package/src/time-series/types.ts +22 -1
- package/src/types/company-from.ts +40 -0
- package/src/types/data-provider.ts +7 -0
- package/src/types/financials.ts +59 -1
- package/src/types/headless.ts +27 -3
- package/src/types/news-source.ts +1 -0
- package/src/types/plugin.ts +9 -0
- package/src/ui/host.tsx +3 -0
- package/src/updater/failure.ts +4 -1
- package/src/utils/cli-output.ts +42 -1
- package/src/utils/crypto-pair.ts +165 -7
- package/src/utils/declared-dividends.ts +1 -1
- package/src/utils/forward-dividend.ts +2 -0
- package/src/utils/fundamentals.ts +60 -8
- package/src/utils/fx-market-hours.ts +26 -3
- package/src/utils/main-thread-stall.ts +35 -2
- package/src/utils/market-capitalization.ts +24 -1
- package/src/utils/price-history.ts +7 -1
- package/src/utils/shell-word.ts +8 -0
- package/src/utils/trailing-dividend.ts +53 -4
- package/src/version.ts +2 -2
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
package/src/cli/listing-arg.ts
CHANGED
|
@@ -15,12 +15,12 @@ import {
|
|
|
15
15
|
type ListingVenue,
|
|
16
16
|
} from "../tickers/listing-venues";
|
|
17
17
|
import { findUsListingOfCompany, type UsListing } from "../tickers/us-listing";
|
|
18
|
-
import { findListingOfCompanyName, isCompanyNameInput, readTerminalCodeArg } from "../tickers/company-name-listing";
|
|
18
|
+
import { findListingOfCompanyName, isCompanyNameInput, readTerminalCodeArg, type CompanyNameListing } from "../tickers/company-name-listing";
|
|
19
19
|
import { movedAwayChange, startTickerChangeLookup, type MovedTickerChange } from "../tickers/ticker-changes";
|
|
20
|
-
import { loadListingFate, MovedSymbolError, type ListingFate } from "../tickers/listing-fate";
|
|
20
|
+
import { lineMayHaveMoved, loadListingFate, MovedSymbolError, type ListingFate } from "../tickers/listing-fate";
|
|
21
21
|
import { quotedListingName } from "../tickers/listing-name";
|
|
22
22
|
import { cliStyles } from "../utils/cli-output";
|
|
23
|
-
import { groupByCompany, sameCompany, shortCompanyName } from "../tickers/company-name";
|
|
23
|
+
import { groupByCompany, namesDepositaryReceipt, sameCompany, sameListedName, shortCompanyName } from "../tickers/company-name";
|
|
24
24
|
import {
|
|
25
25
|
canonicalExchange,
|
|
26
26
|
exchangeCodeVenues,
|
|
@@ -35,8 +35,8 @@ import {
|
|
|
35
35
|
import { withMarketData } from "./scoped-context";
|
|
36
36
|
import { failWithFields } from "./errors";
|
|
37
37
|
import { stripStrayEdges } from "./symbol-args";
|
|
38
|
-
import { isNoProviderMessage, isProviderBusy } from "../sources/provider-errors";
|
|
39
|
-
import { dashedCoinPair, MAJOR_COIN_NAMES } from "../utils/crypto-pair";
|
|
38
|
+
import { isNoProviderMessage, isNoSourceAnswer, isProviderBusy } from "../sources/provider-errors";
|
|
39
|
+
import { dashedCoinPair, exchangeLineCoinListing, isSpotCommodityOrCurrencyPair, MAJOR_COIN_NAMES } from "../utils/crypto-pair";
|
|
40
40
|
import { boardCoinFor, primeBoardCoins } from "./board-coins";
|
|
41
41
|
import { knownGapMessage } from "./known-gaps";
|
|
42
42
|
import { currencyPairSpelling } from "../utils/currency-pair";
|
|
@@ -49,10 +49,12 @@ import {
|
|
|
49
49
|
bloombergMarketHint,
|
|
50
50
|
endsInLegalForm,
|
|
51
51
|
isSearchOnlyVenue,
|
|
52
|
+
readLondonDottedSymbol,
|
|
52
53
|
readNearMissListing,
|
|
53
54
|
readTerminalListingCode,
|
|
54
55
|
terminalCodeReadAs,
|
|
55
56
|
unknownBloombergMarket,
|
|
57
|
+
type NearMissListing,
|
|
56
58
|
type TerminalListingCode,
|
|
57
59
|
} from "../tickers/terminal-codes";
|
|
58
60
|
import { listingAbroadVenue } from "../utils/sec";
|
|
@@ -105,6 +107,12 @@ export interface ListingArg {
|
|
|
105
107
|
* `readAs` asks about it, and a command the listing fails asks too.
|
|
106
108
|
*/
|
|
107
109
|
meant?: { symbol: string; name: string };
|
|
110
|
+
/**
|
|
111
|
+
* A bare symbol a company's name was read as still says what it was read
|
|
112
|
+
* as (`listingChoiceNotes`): GETYQ for "getty images", whose NYSE line
|
|
113
|
+
* became GETYQ. Other bare symbols say where the data service chose instead.
|
|
114
|
+
*/
|
|
115
|
+
readFromName?: true;
|
|
108
116
|
}
|
|
109
117
|
|
|
110
118
|
/** "Did you mean JPM (JPMorgan Chase & Co.)?", for a name read as a fund or an index; empty for any other listing. */
|
|
@@ -174,8 +182,13 @@ function parseCleanListingArg(
|
|
|
174
182
|
// `SAN:` asks which listing; with --exchange it has its answer.
|
|
175
183
|
if (option && typed.endsWith(":")) typed = typed.slice(0, -1);
|
|
176
184
|
const venueKey = parsePublicTickerKey(typed);
|
|
177
|
-
//
|
|
178
|
-
const
|
|
185
|
+
// A crypto exchange's line of a pair (BTC/GBP:COINBASE PRO, as search offered it) is the crypto venue's BTC-GBP:CCC.
|
|
186
|
+
const line = venueKey.exchange || optionCode ? exchangeLineCoinListing(venueKey.symbol, venueKey.exchange ?? optionCode) : null;
|
|
187
|
+
if (line) return { symbol: line.symbol, exchange: line.exchange, key: `${line.symbol}:${line.exchange}` };
|
|
188
|
+
// BTC/USD, as crypto venues write a pair, is the coin's own BTC-USD; spot crude (WTI/USD) and gold by the gram
|
|
189
|
+
// (GAU/USD:COMMODITY) are not coins, though tokens share their letters.
|
|
190
|
+
const coinPair = isSpotCommodityOrCurrencyPair(venueKey.symbol, venueKey.exchange ?? optionCode) ? null : dashedCoinPair(venueKey.symbol);
|
|
191
|
+
const parsed = { ...venueKey, symbol: coinPair ?? venueKey.symbol };
|
|
179
192
|
const ownCode = parsed.exchange ? typedExchangeCode(typed) : "";
|
|
180
193
|
let exchange = parsed.exchange ?? option;
|
|
181
194
|
let code = ownCode || optionCode;
|
|
@@ -338,6 +351,15 @@ export function isNoProviderError(error: unknown): boolean {
|
|
|
338
351
|
return isNoProviderMessage(error instanceof Error ? error.message : String(error ?? ""));
|
|
339
352
|
}
|
|
340
353
|
|
|
354
|
+
/**
|
|
355
|
+
* The router's answer that no source served a request because one failed (a
|
|
356
|
+
* timeout, a rate limit, a refused session) rather than missed: it says
|
|
357
|
+
* nothing about whether the symbol exists, unlike "Not a ticker".
|
|
358
|
+
*/
|
|
359
|
+
export function isUnsettledNoProvider(error: unknown): boolean {
|
|
360
|
+
return isNoProviderError(error) && !isNoSourceAnswer(error);
|
|
361
|
+
}
|
|
362
|
+
|
|
341
363
|
const MAX_OTHER_LISTINGS = 5;
|
|
342
364
|
|
|
343
365
|
type BareListing = Pick<CliListing, "symbol" | "exchange"> & Partial<Pick<CliListing, "saved">>;
|
|
@@ -359,33 +381,42 @@ function listsSeveralCompanies(venues: readonly ListingVenue[]): boolean {
|
|
|
359
381
|
return groupByCompany(venues, (venue) => venue.name).length > 1;
|
|
360
382
|
}
|
|
361
383
|
|
|
362
|
-
/**
|
|
384
|
+
/**
|
|
385
|
+
* Every other company or fund that lists the symbol, once, at its first venue
|
|
386
|
+
* in search order. A venue is another's only when its name says so: one with
|
|
387
|
+
* no name, or a depositary receipt named unlike the company (a CEDEAR's short
|
|
388
|
+
* form of its fund's name), is left out rather than guessed at, and one
|
|
389
|
+
* fund's venues are one fund however each words it (`sameListedName`).
|
|
390
|
+
*/
|
|
363
391
|
function otherCompanies(venues: readonly ListingVenue[], company: string): ListingVenue[] {
|
|
364
|
-
|
|
365
|
-
|
|
392
|
+
if (!company.trim()) return [];
|
|
393
|
+
const known = venues.filter((venue) => !namesDepositaryReceipt(venue.name) || sameListedName(venue.name, company));
|
|
394
|
+
return groupByCompany(known, (venue) => venue.name, sameListedName)
|
|
395
|
+
.filter((group) => !group.some((venue) => sameListedName(venue.name, company)))
|
|
366
396
|
.map((group) => group[0]!);
|
|
367
397
|
}
|
|
368
398
|
|
|
369
399
|
/**
|
|
370
|
-
* "CAR = Avis Budget Group, NASDAQ (
|
|
371
|
-
* symbol several companies list was read on, and where the
|
|
372
|
-
* Null when
|
|
373
|
-
* nothing.
|
|
400
|
+
* "CAR = Avis Budget Group, NASDAQ (different companies on XETRA, ASX)": the
|
|
401
|
+
* listing a bare symbol several companies list was read on, and where the
|
|
402
|
+
* others list it. Null when no other company is known to list it, so a symbol
|
|
403
|
+
* that means one thing (XLK, and its receipt in Buenos Aires) says nothing.
|
|
374
404
|
*/
|
|
375
405
|
export function listingChoiceNote(
|
|
376
406
|
symbol: string,
|
|
377
407
|
chosen: { exchange: string; name?: string | null },
|
|
378
408
|
venues: readonly ListingVenue[],
|
|
379
409
|
): string | null {
|
|
380
|
-
if (!chosen.exchange
|
|
410
|
+
if (!chosen.exchange) return null;
|
|
381
411
|
const chosenVenue = venues.find((venue) => venue.exchange === chosen.exchange);
|
|
382
412
|
const name = chosen.name?.trim() || chosenVenue?.name || "";
|
|
383
413
|
const others = otherCompanies(venues, chosenVenue?.name || name).map((venue) => venue.exchange)
|
|
384
414
|
.filter((exchange) => exchange !== chosen.exchange);
|
|
385
415
|
if (others.length === 0) return null;
|
|
386
416
|
const more = others.length - MAX_OTHER_LISTINGS;
|
|
387
|
-
const
|
|
388
|
-
|
|
417
|
+
const named = `${others.slice(0, MAX_OTHER_LISTINGS).join(", ")}${more > 0 ? ` and ${more} more: gloom search ${symbol}` : ""}`;
|
|
418
|
+
const which = others.length === 1 ? "a different company" : "different companies";
|
|
419
|
+
return `${symbol} = ${name ? `${shortCompanyName(name)}, ` : ""}${chosen.exchange} (${which} on ${named})`;
|
|
389
420
|
}
|
|
390
421
|
|
|
391
422
|
// Quotes of these never name a company's listing, so they are never a choice between companies.
|
|
@@ -409,44 +440,60 @@ export function listingReadNotes(listing: Pick<ListingArg, "readAs">): string[]
|
|
|
409
440
|
return listing.readAs ? [listing.readAs] : [];
|
|
410
441
|
}
|
|
411
442
|
|
|
443
|
+
/**
|
|
444
|
+
* The coin line for a bare coin ticker (SOL, ETH) a command read as a listing that is
|
|
445
|
+
* not the coin, from the quote it was read on (`notTheCoinNote`). A list, so a command can
|
|
446
|
+
* spread it into its warnings. A named listing (`ETH:ARCA`, `--exchange`) asked for that
|
|
447
|
+
* listing on purpose and gets none.
|
|
448
|
+
*/
|
|
449
|
+
export function coinListingNotes(
|
|
450
|
+
listing: Pick<CliListing, "symbol" | "exchange">,
|
|
451
|
+
quote: Pick<Quote, "name" | "instrumentType" | "stale" | "delisting"> | null | undefined,
|
|
452
|
+
): string[] {
|
|
453
|
+
const note = listing.exchange ? null : notTheCoinNote(listing.symbol, quote);
|
|
454
|
+
return note ? [note] : [];
|
|
455
|
+
}
|
|
456
|
+
|
|
412
457
|
/**
|
|
413
458
|
* `listingChoiceNote` for a listing a command resolved, from the quote it was
|
|
414
459
|
* read on: only a bare symbol went where the data service chose. A named
|
|
415
460
|
* listing whose code names several venues says which it was read as
|
|
416
|
-
* (`listingReadNotes`). A
|
|
461
|
+
* (`listingReadNotes`). A bare coin ticker read as another listing ends with
|
|
462
|
+
* the coin's pair (`coinListingNotes`). A list, so a command can spread it into
|
|
463
|
+
* its warnings.
|
|
417
464
|
*/
|
|
418
465
|
export async function listingChoiceNotes(
|
|
419
|
-
listing: Pick<CliListing, "symbol" | "exchange" | "saved" | "readAs">,
|
|
466
|
+
listing: Pick<CliListing, "symbol" | "exchange" | "saved" | "readAs" | "readFromName">,
|
|
420
467
|
deps: ListingDeps,
|
|
421
|
-
quote: Pick<Quote, "name" | "listingExchangeName" | "exchangeName" | "instrumentType"> | null | undefined,
|
|
468
|
+
quote: Pick<Quote, "name" | "listingExchangeName" | "exchangeName" | "instrumentType" | "stale" | "delisting"> | null | undefined,
|
|
422
469
|
/** The venues `loadBareVenues` already started; looked up here when not given. */
|
|
423
470
|
venues?: Promise<ListingVenue[]> | null,
|
|
424
471
|
): Promise<string[]> {
|
|
425
|
-
|
|
426
|
-
if (!
|
|
472
|
+
const coin = coinListingNotes(listing, quote);
|
|
473
|
+
if (!isBareListing(listing)) return [...listingReadNotes(listing), ...coin];
|
|
474
|
+
// A bare symbol a name was read as says so: "Read "getty images" as GETYQ (...). GETY became GETYQ on 2026-10-22."
|
|
475
|
+
if (listing.readFromName) return [...listingReadNotes(listing), ...coin];
|
|
476
|
+
if (!quote || UNLISTED_TYPES.has(quote.instrumentType?.trim().toUpperCase() ?? "")) return coin;
|
|
427
477
|
const exchange = canonicalExchange(quote.listingExchangeName || quote.exchangeName);
|
|
428
|
-
if (!exchange) return
|
|
478
|
+
if (!exchange) return coin;
|
|
429
479
|
const listed = await (venues ?? loadBareVenues(listing, deps) ?? []);
|
|
430
480
|
const note = listingChoiceNote(listing.symbol, { exchange, name: quote.name }, listed);
|
|
431
|
-
return note ? [note] : [];
|
|
481
|
+
return [...note ? [note] : [], ...coin];
|
|
432
482
|
}
|
|
433
483
|
|
|
434
484
|
/**
|
|
435
485
|
* `listingChoiceNotes` for a command with no quote of its own to read the
|
|
436
486
|
* listing from (`fn`): it loads the listing's quote, and only for a bare
|
|
437
487
|
* symbol, so a named or saved listing costs nothing. A bare coin ticker read
|
|
438
|
-
* as
|
|
439
|
-
* coin's pair.
|
|
488
|
+
* as another listing (BTC, the Grayscale trust) ends with the coin's pair.
|
|
440
489
|
*/
|
|
441
490
|
export async function quotedListingChoiceNotes(
|
|
442
|
-
listing: Pick<CliListing, "symbol" | "exchange" | "saved" | "request" | "readAs">,
|
|
491
|
+
listing: Pick<CliListing, "symbol" | "exchange" | "saved" | "request" | "readAs" | "readFromName">,
|
|
443
492
|
deps: ListingDeps,
|
|
444
493
|
): Promise<string[]> {
|
|
445
494
|
const venues = loadBareVenues(listing, deps);
|
|
446
495
|
if (!venues) return listingReadNotes(listing);
|
|
447
|
-
|
|
448
|
-
const coin = notTheCoinNote(listing.symbol, quote);
|
|
449
|
-
return [...await listingChoiceNotes(listing, deps, quote, venues), ...coin ? [coin] : []];
|
|
496
|
+
return listingChoiceNotes(listing, deps, await loadListingQuote(deps.dataProvider, listing), venues);
|
|
450
497
|
}
|
|
451
498
|
|
|
452
499
|
/**
|
|
@@ -563,31 +610,43 @@ export function futuresTryLine(symbol: string, request: ListingDataRequest): str
|
|
|
563
610
|
return `${future.symbol} ${future.name} future${retry ? ` (try: ${retry})` : ""}`;
|
|
564
611
|
}
|
|
565
612
|
|
|
613
|
+
/** A venue's listing as its key names it: under its own symbol there (BP:LSE for `BP.`), else the one asked for. */
|
|
614
|
+
function venueKey(symbol: string, venue: Pick<ListingVenue, "exchange" | "symbol">): string {
|
|
615
|
+
return `${venue.symbol ?? symbol}:${venue.exchange}`;
|
|
616
|
+
}
|
|
617
|
+
|
|
566
618
|
function describeOtherListings(
|
|
567
619
|
listing: BareListing,
|
|
568
620
|
{ exchange, resolved, others, choose }: BareListingVenues,
|
|
569
621
|
request: ListingDataRequest,
|
|
622
|
+
/** Listings just asked that had none: named as having none, never offered to try. */
|
|
623
|
+
without: readonly ListingVenue[] = [],
|
|
570
624
|
): OtherListingsMessage | null {
|
|
571
625
|
const future = futuresTryLine(listing.symbol, request);
|
|
572
|
-
|
|
626
|
+
const offered = others.filter((venue) => !without.includes(venue));
|
|
627
|
+
if (offered.length === 0 && !future && without.length === 0) return null;
|
|
573
628
|
const name = resolved?.name || listing.saved?.metadata.name?.trim() || "";
|
|
574
|
-
const lines =
|
|
575
|
-
const key =
|
|
629
|
+
const lines = offered.slice(0, MAX_OTHER_LISTINGS).map((venue) => {
|
|
630
|
+
const key = venueKey(listing.symbol, venue);
|
|
576
631
|
const retry = retryCommand(request, key);
|
|
577
632
|
return `${key}${venue.name ? ` ${venue.name}` : ""}${retry ? ` (try: ${retry})` : ""}`;
|
|
578
633
|
});
|
|
579
|
-
if (
|
|
634
|
+
if (offered.length > MAX_OTHER_LISTINGS) lines.push(`and ${offered.length - MAX_OTHER_LISTINGS} more: gloom search ${listing.symbol}`);
|
|
580
635
|
if (future) lines.push(future);
|
|
581
|
-
|
|
636
|
+
// Every other listing was asked and had nothing: they are named as such, with nothing to try.
|
|
637
|
+
const heading = choose ? "Listings" : lines.length === 0 ? `No ${request.noun} on its other listings either` : "Other listings";
|
|
638
|
+
const named = lines.length === 0 ? without : offered;
|
|
639
|
+
const details = lines.length === 0 ? `${heading}: ${without.map((venue) => venueKey(listing.symbol, venue)).join(", ")}.`
|
|
640
|
+
: lines.length === 1 ? `${heading}: ${lines[0]}` : [`${heading}:`, ...lines.map((line) => ` ${line}`)].join("\n");
|
|
582
641
|
return {
|
|
583
642
|
// The symbol went to no venue, so none of them is said to lack the data.
|
|
584
643
|
message: choose
|
|
585
644
|
? `Choose an exchange for ${listing.symbol}.`
|
|
586
645
|
: `${listing.symbol} resolved to ${name ? `${name} (${exchange})` : exchange}, which has no ${request.noun}.`,
|
|
587
|
-
details
|
|
646
|
+
details,
|
|
588
647
|
heading,
|
|
589
|
-
listings: [...
|
|
590
|
-
choices:
|
|
648
|
+
listings: [...named.map((venue) => venueKey(listing.symbol, venue)), ...future ? [futuresContractByCode(listing.symbol)!.symbol] : []],
|
|
649
|
+
choices: offered.slice(0, MAX_OTHER_LISTINGS).map((venue) => ({ key: venueKey(listing.symbol, venue), name: venue.name || null })),
|
|
591
650
|
choose,
|
|
592
651
|
};
|
|
593
652
|
}
|
|
@@ -612,30 +671,44 @@ async function otherListingsMessage(
|
|
|
612
671
|
return venues && describeOtherListings(listing, venues, request);
|
|
613
672
|
}
|
|
614
673
|
|
|
615
|
-
/** The listing on one venue, as `SYM:EXCH` names it. */
|
|
616
|
-
function namedListing(listing: CliListing,
|
|
617
|
-
const
|
|
674
|
+
/** The listing on one venue, as `SYM:EXCH` names it, under the venue's own symbol when search lists one. */
|
|
675
|
+
function namedListing(listing: CliListing, venue: Pick<ListingVenue, "exchange" | "symbol">): CliListing {
|
|
676
|
+
const { exchange } = venue;
|
|
677
|
+
const symbol = venue.symbol ?? listing.symbol;
|
|
678
|
+
const key = `${symbol}:${exchange}`;
|
|
618
679
|
const saved = canonicalExchange(listing.saved?.metadata.exchange) === exchange ? listing.saved : null;
|
|
619
|
-
return { symbol
|
|
680
|
+
return { symbol, exchange, key, saved, request: { symbol: key, exchange } };
|
|
620
681
|
}
|
|
621
682
|
|
|
683
|
+
/**
|
|
684
|
+
* Each listing's quote, null where it has none; `undefined` where the request
|
|
685
|
+
* itself failed, which says nothing of whether it has one.
|
|
686
|
+
*/
|
|
622
687
|
async function quoteListings(
|
|
623
688
|
dataProvider: DataProvider,
|
|
624
689
|
listings: readonly CliListing[],
|
|
625
690
|
refresh: boolean,
|
|
626
691
|
datesChangeBasis = false,
|
|
627
|
-
): Promise<Array<Quote | null>> {
|
|
692
|
+
): Promise<Array<Quote | null | undefined>> {
|
|
628
693
|
if (listings.length === 0) return [];
|
|
629
694
|
const targets = listings.map((listing) => listing.request);
|
|
630
695
|
if (dataProvider.getQuotesBatch) {
|
|
631
696
|
const results = await dataProvider.getQuotesBatch(targets, { forceRefresh: refresh, ...(datesChangeBasis ? { datesChangeBasis } : {}) })
|
|
632
|
-
.catch(logFailure("cli.listing-arg", "Read listing quote batch failed",
|
|
633
|
-
|
|
697
|
+
.catch(logFailure("cli.listing-arg", "Read listing quote batch failed", null));
|
|
698
|
+
if (!results) return targets.map(() => undefined);
|
|
699
|
+
return targets.map((target, index) => {
|
|
700
|
+
const result = results.find((entry) => entry.target === target) ?? results[index];
|
|
701
|
+
// No source quotes it, which is an answer; any other failure is none.
|
|
702
|
+
return result?.quote ?? (result && (result.error == null || isNoProviderError(result.error)) ? null : undefined);
|
|
703
|
+
});
|
|
634
704
|
}
|
|
635
705
|
return Promise.all(targets.map((target) => dataProvider.getQuote(target.symbol, target.exchange, {
|
|
636
706
|
cacheMode: refresh ? "refresh" : "default",
|
|
637
707
|
...(datesChangeBasis ? { datesChangeBasis } : {}),
|
|
638
|
-
}).catch(
|
|
708
|
+
}).catch((error: unknown) => {
|
|
709
|
+
logFailure("cli.listing-arg", "Read listing quote failed")(error);
|
|
710
|
+
return isNoProviderError(error) ? null : undefined;
|
|
711
|
+
})));
|
|
639
712
|
}
|
|
640
713
|
|
|
641
714
|
export type BareListingQuote =
|
|
@@ -674,8 +747,8 @@ export async function quoteBareListing(
|
|
|
674
747
|
const other = describeOtherListings(listing, venues, request);
|
|
675
748
|
return other && { kind: "missing", ...other };
|
|
676
749
|
}
|
|
677
|
-
const first = [namedListing(listing, venues.exchange)];
|
|
678
|
-
const rest = venues.others.map((venue) => namedListing(listing, venue
|
|
750
|
+
const first = [namedListing(listing, venues.resolved ?? { exchange: venues.exchange })];
|
|
751
|
+
const rest = venues.others.map((venue) => namedListing(listing, venue));
|
|
679
752
|
let tried = first;
|
|
680
753
|
let quotes = await quoteListings(deps.dataProvider, first, refresh, datesChangeBasis);
|
|
681
754
|
if (!quotes[0] && rest.length > 0) {
|
|
@@ -684,7 +757,9 @@ export async function quoteBareListing(
|
|
|
684
757
|
}
|
|
685
758
|
const found = quotes.findIndex(Boolean);
|
|
686
759
|
if (found < 0) {
|
|
687
|
-
|
|
760
|
+
// A listing just asked that had no quote is no listing to try.
|
|
761
|
+
const without = venues.others.filter((_venue, index) => quotes[index + 1] === null);
|
|
762
|
+
const other = describeOtherListings(listing, venues, request, without);
|
|
688
763
|
return other && { kind: "missing", ...other };
|
|
689
764
|
}
|
|
690
765
|
const answer = tried[found]!;
|
|
@@ -697,7 +772,8 @@ export async function quoteBareListing(
|
|
|
697
772
|
kind: "quoted",
|
|
698
773
|
listing: answer,
|
|
699
774
|
quote: quotes[found]!,
|
|
700
|
-
|
|
775
|
+
// Under the venue's own symbol (BP:LSE for `BP.`), the note names the listing, not only its venue.
|
|
776
|
+
note: chosenListingNote(listing.symbol, answer.symbol === listing.symbol ? answer.exchange : answer.key, others, without),
|
|
701
777
|
};
|
|
702
778
|
}
|
|
703
779
|
|
|
@@ -708,6 +784,8 @@ export interface ReadListing {
|
|
|
708
784
|
exchange: string;
|
|
709
785
|
/** The listing's company, from its quote. */
|
|
710
786
|
name: string | null;
|
|
787
|
+
/** A bare coin ticker read as a listing that is not the coin (`coinListingNotes`); null otherwise. */
|
|
788
|
+
coinNote: string | null;
|
|
711
789
|
}
|
|
712
790
|
|
|
713
791
|
/**
|
|
@@ -728,10 +806,14 @@ export async function readBareListing(
|
|
|
728
806
|
const listing = await resolveCliListing(symbol, null, deps);
|
|
729
807
|
if (listing.exchange) return null;
|
|
730
808
|
const answer = await loadListingQuoteAnswer(deps.dataProvider, listing);
|
|
731
|
-
if (answer.quote)
|
|
809
|
+
if (answer.quote) {
|
|
810
|
+
return { symbol: listing.symbol, exchange: listing.request.exchange, name: answer.quote.name?.trim() || null, coinNote: coinListingNotes(listing, answer.quote)[0] ?? null };
|
|
811
|
+
}
|
|
732
812
|
if (!isNoProviderError(answer.error)) return null;
|
|
733
813
|
const other = await quoteBareListing(listing, deps, request, { refresh });
|
|
734
|
-
if (other?.kind === "quoted")
|
|
814
|
+
if (other?.kind === "quoted") {
|
|
815
|
+
return { symbol: listing.symbol, exchange: other.listing.exchange, name: other.quote.name?.trim() || null, coinNote: coinListingNotes(listing, other.quote)[0] ?? null };
|
|
816
|
+
}
|
|
735
817
|
if (other?.choose) throw new ListingArgError(other.message, other.details, other.choices);
|
|
736
818
|
return null;
|
|
737
819
|
}
|
|
@@ -1096,7 +1178,9 @@ export async function resolveCliListing(
|
|
|
1096
1178
|
const terminal = named ? null : read;
|
|
1097
1179
|
// "AAPL GP" is written as a code, with a market code no venue answers to and no company's name: one entry, never AAPL and GP.
|
|
1098
1180
|
if (!terminal && !named) await refuseUnknownMarketCode(typed, deps);
|
|
1099
|
-
|
|
1181
|
+
// `BP.` is London's code for BP: its London line where London lists it, and `GE.` a US ticker with a stray dot.
|
|
1182
|
+
const dotted = terminal || named || exchangeOption?.trim() ? null : readLondonDottedSymbol(typed);
|
|
1183
|
+
let parsed = named ?? (dotted ? await londonDottedListing(dotted, deps) : parseListingArg(raw, exchangeOption, { ...options, terminalCode: terminal }));
|
|
1100
1184
|
if (!parsed.symbol) throw new ListingArgError("Ticker symbol is required.");
|
|
1101
1185
|
// $VIX is Schwab's code for ^VIX, unless a security trades as VIX: $AAPL is a cashtag.
|
|
1102
1186
|
const index = named || parsed.exchange ? null : await readDollarIndexCode(raw, deps);
|
|
@@ -1140,6 +1224,21 @@ export async function resolveCliListing(
|
|
|
1140
1224
|
};
|
|
1141
1225
|
}
|
|
1142
1226
|
|
|
1227
|
+
/**
|
|
1228
|
+
* A two-letter symbol written with London's trailing dot (`BP.`, `RR.`) as its
|
|
1229
|
+
* London line, said as "Read BP. as BP:LSE.", where search lists it on LSE or
|
|
1230
|
+
* its quote there says it trades. Elsewhere the dot was a slip after the
|
|
1231
|
+
* ticker, dropped as a stray edge is (`GE.` is GE).
|
|
1232
|
+
*/
|
|
1233
|
+
async function londonDottedListing(dotted: NearMissListing, deps: ListingDeps): Promise<ListingArg> {
|
|
1234
|
+
const london = { symbol: dotted.symbol, exchange: "LSE" };
|
|
1235
|
+
const venues = await listingVenues(dotted.symbol, deps).catch(logFailure("cli.listing-arg", "Read listing venues failed", []));
|
|
1236
|
+
const listed = venues.some((venue) => venue.exchange === london.exchange) || await quotedOnVenue(london, deps);
|
|
1237
|
+
if (!listed) return parseListingArg(dotted.symbol);
|
|
1238
|
+
const key = `${london.symbol}:${london.exchange}`;
|
|
1239
|
+
return { ...parseListingArg(key), readAs: `Read ${dotted.typed} as ${key}.` };
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1143
1242
|
/**
|
|
1144
1243
|
* Fails a line written as a Bloomberg code whose market code no venue answers
|
|
1145
1244
|
* to (`AAPL GP`), suggesting the code of the symbol's own listing (`AAPL
|
|
@@ -1235,17 +1334,23 @@ const ONE_WORD_NAME = /^\p{L}[\p{L}\p{N}&'’.-]*$/u;
|
|
|
1235
1334
|
* for anything else, and when search names no company or is too busy to say.
|
|
1236
1335
|
*/
|
|
1237
1336
|
export async function oneWordCompanyName(typed: string, deps: Pick<ListingDeps, "dataProvider">): Promise<ListingArg | null> {
|
|
1337
|
+
const found = await findOneWordCompany(typed, deps);
|
|
1338
|
+
return found ? companyListingArg(typed, found) : null;
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
/** The company `oneWordCompanyName` reads a word as, before it is read as a listing. */
|
|
1342
|
+
async function findOneWordCompany(typed: string, deps: Pick<ListingDeps, "dataProvider">): Promise<CompanyNameListing | null> {
|
|
1238
1343
|
const word = typed.trim();
|
|
1239
1344
|
if (!ONE_WORD_NAME.test(word) || knownGapMessage(word) || isMajorCoinName(word)) return null;
|
|
1240
|
-
const
|
|
1345
|
+
const found = await findCompanyName(word, deps, { oneWord: true }).catch((error: unknown) => {
|
|
1241
1346
|
if (!isProviderBusy(error)) throw error;
|
|
1242
1347
|
// Intentional ignore: a busy search says nothing about the name, so the word keeps its own "Not a ticker".
|
|
1243
1348
|
return null;
|
|
1244
1349
|
});
|
|
1245
|
-
if (!
|
|
1350
|
+
if (!found) return null;
|
|
1246
1351
|
// The board is read only for a word that names a company, and once for the run (`primeBoardCoins`).
|
|
1247
1352
|
if (/^[A-Za-z0-9]+$/.test(word)) await primeBoardCoins([`Not a ticker: ${word.toUpperCase()}.`]);
|
|
1248
|
-
return boardCoinFor(word) ? null :
|
|
1353
|
+
return boardCoinFor(word) ? null : found;
|
|
1249
1354
|
}
|
|
1250
1355
|
|
|
1251
1356
|
function isMajorCoinName(word: string): boolean {
|
|
@@ -1253,14 +1358,84 @@ function isMajorCoinName(word: string): boolean {
|
|
|
1253
1358
|
return [...MAJOR_COIN_NAMES.values()].some((name) => name.toLowerCase() === wanted);
|
|
1254
1359
|
}
|
|
1255
1360
|
|
|
1361
|
+
function findCompanyName(typed: string, deps: Pick<ListingDeps, "dataProvider">, options: { oneWord?: boolean } = {}): Promise<CompanyNameListing | null> {
|
|
1362
|
+
return findListingOfCompanyName(deps.dataProvider, typed, { meant: true, quoted: true, movedLine: (listing) => movedLineOf(listing, deps), ...options });
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
/**
|
|
1366
|
+
* Where the company of a US line that stopped trading, or has no quote, trades
|
|
1367
|
+
* now when it moved off the symbol (`loadListingFate`): GETY became GETYQ on
|
|
1368
|
+
* 2026-10-22, so "getty images" is GETYQ rather than its delisted NYSE line or
|
|
1369
|
+
* the Munich line still quoting the old shares. A line that trades is not
|
|
1370
|
+
* looked up.
|
|
1371
|
+
*/
|
|
1372
|
+
async function movedLineOf(
|
|
1373
|
+
listing: { symbol: string; exchange: string; quote: Quote | null; name: string | null },
|
|
1374
|
+
deps: Pick<ListingDeps, "dataProvider">,
|
|
1375
|
+
): Promise<{ symbol: string; note: string } | null> {
|
|
1376
|
+
const { quote, name, ...line } = listing;
|
|
1377
|
+
if (quote && !lineMayHaveMoved(quote)) return null;
|
|
1378
|
+
const fate = await loadListingFate(line, deps.dataProvider, { quote });
|
|
1379
|
+
const moved = fate.moved ?? (quote ? null : movedWithoutQuote(fate.change, name));
|
|
1380
|
+
return moved ? { symbol: moved.tradesAs, note: moved.note } : null;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
/**
|
|
1384
|
+
* The move of a line with no quote, when it is the move of the company search
|
|
1385
|
+
* names on the line. With no quote nothing tells the company's change from an
|
|
1386
|
+
* older one's: a reused symbol's change data is the company that held it
|
|
1387
|
+
* before, so it is followed only when that company is this one.
|
|
1388
|
+
*/
|
|
1389
|
+
function movedWithoutQuote(change: ListingFate["change"], name: string | null): MovedTickerChange | null {
|
|
1390
|
+
const moved = movedAwayChange(change);
|
|
1391
|
+
return moved && sameCompany(name, moved.issuer) ? moved : null;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1256
1394
|
async function readCompanyName(typed: string, deps: Pick<ListingDeps, "dataProvider">, options: { oneWord?: boolean } = {}): Promise<ListingArg | null> {
|
|
1257
|
-
const found = await
|
|
1258
|
-
|
|
1259
|
-
|
|
1395
|
+
const found = await findCompanyName(typed, deps, options);
|
|
1396
|
+
return found ? companyListingArg(typed, found) : null;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
function companyListingArg(typed: string, found: CompanyNameListing): ListingArg {
|
|
1400
|
+
const key = found.exchange ? `${found.symbol}:${found.exchange}` : found.symbol;
|
|
1260
1401
|
// A fund or an index carrying the name asks about the company's stock: "Did you mean JPM (JPMorgan Chase & Co.)?".
|
|
1261
1402
|
const meant = found.meant ? { symbol: found.meant.symbol, name: found.meant.name } : null;
|
|
1262
1403
|
const question = meant ? ` ${meantQuestion({ meant })}` : "";
|
|
1263
|
-
|
|
1404
|
+
// A company that moved off the line search lists says so: "GETY became GETYQ on 2026-10-22."
|
|
1405
|
+
const moved = found.moved ? ` ${found.moved}.` : "";
|
|
1406
|
+
return {
|
|
1407
|
+
...parseListingArg(key), readAs: `Read "${typed.trim()}" as ${key} (${found.name}).${moved}${question}`,
|
|
1408
|
+
...meant ? { meant } : {}, ...found.exchange ? {} : { readFromName: true as const },
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
/**
|
|
1413
|
+
* The listing search names for a company's name typed where a ticker goes,
|
|
1414
|
+
* as a sentence offers it: `PLTR (Palantir Technologies Inc.)`, a listing
|
|
1415
|
+
* abroad with its venue (`SIE:XETRA (Siemens AG)`), a fund or an index that
|
|
1416
|
+
* carries the name as the company's stock it means. Null when the words name
|
|
1417
|
+
* no company, search is busy, or the listing found is the word itself: a
|
|
1418
|
+
* real ticker is never a company's name. Never throws.
|
|
1419
|
+
*/
|
|
1420
|
+
export async function companyNameSuggestion(typed: string, deps: Pick<ListingDeps, "dataProvider">): Promise<{ symbol: string; name: string } | null> {
|
|
1421
|
+
const words = typed.trim().replace(/^\$/, "");
|
|
1422
|
+
if (!words) return null;
|
|
1423
|
+
try {
|
|
1424
|
+
const found = isCompanyNameInput(words) ? await findCompanyName(words, deps).catch(busyAsMiss) : await findOneWordCompany(words, deps);
|
|
1425
|
+
const listing = found?.meant ?? found;
|
|
1426
|
+
if (!listing || listing.symbol.toUpperCase() === words.toUpperCase()) return null;
|
|
1427
|
+
return { symbol: !listing.exchange || isUsListingExchange(listing.exchange) ? listing.symbol : `${listing.symbol}:${listing.exchange}`, name: listing.name };
|
|
1428
|
+
} catch (error) {
|
|
1429
|
+
logFailure("cli.listing-arg", "Search a company name for a suggestion failed")(error);
|
|
1430
|
+
return null;
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
/** A search too busy to answer is no company, as a name that matches none. */
|
|
1435
|
+
function busyAsMiss(error: unknown): null {
|
|
1436
|
+
if (!isProviderBusy(error)) throw error;
|
|
1437
|
+
// Intentional ignore: a busy search says nothing about the name, so no company is suggested.
|
|
1438
|
+
return null;
|
|
1264
1439
|
}
|
|
1265
1440
|
|
|
1266
1441
|
/** `resolveCliListing` for a command: a bad exchange fails the command with its message. */
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { mkdir, stat, writeFile } from "fs/promises";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { posix, win32 } from "path";
|
|
4
|
+
|
|
5
|
+
interface OutputPathContext {
|
|
6
|
+
cwd?: string;
|
|
7
|
+
home?: string;
|
|
8
|
+
platform?: NodeJS.Platform;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** `C:\Users\me\out.csv`, `C:/out.csv`, `\\server\share\out.csv`: a path only Windows reads. */
|
|
12
|
+
const WINDOWS_PATH = /^(?:[A-Za-z]:[\\/]|\\\\)/;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The absolute path `--output <path>` names, in any form a shell passes it:
|
|
16
|
+
* relative to the folder the command runs in, absolute, or under the home
|
|
17
|
+
* folder (`~/Desktop/rv.csv`, which a shell leaves as typed after
|
|
18
|
+
* `--output=`). A Windows path is read as one on Windows; anywhere else it
|
|
19
|
+
* is refused, since it would write a file named after the whole path.
|
|
20
|
+
*/
|
|
21
|
+
export function resolveOutputPath(raw: string, context: OutputPathContext = {}): string {
|
|
22
|
+
const platform = context.platform ?? process.platform;
|
|
23
|
+
const paths = platform === "win32" ? win32 : posix;
|
|
24
|
+
const typed = raw.trim();
|
|
25
|
+
if (!typed) throw new Error("--output needs a file to write, such as --output report.csv.");
|
|
26
|
+
if (platform !== "win32" && WINDOWS_PATH.test(typed)) {
|
|
27
|
+
throw new Error(`${typed} is a Windows path. Write a path on this computer, such as --output ~/Desktop/report.csv.`);
|
|
28
|
+
}
|
|
29
|
+
const home = context.home ?? homedir();
|
|
30
|
+
const homeRelative = /^~(?:$|[\\/])/.test(typed);
|
|
31
|
+
const expanded = homeRelative ? paths.join(home, typed.slice(1)) : typed;
|
|
32
|
+
return paths.resolve(context.cwd ?? process.cwd(), expanded);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function errorCode(error: unknown): string | undefined {
|
|
36
|
+
return error && typeof error === "object" && "code" in error ? String((error as { code: unknown }).code) : undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function isFolder(path: string, paths: typeof posix): Error {
|
|
40
|
+
return new Error(`Cannot write ${path}: it is a folder. Name a file in it, such as ${paths.join(path, "report.csv")}.`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Fails when `path` names a folder: checked before a report loads, so it stops at once. */
|
|
44
|
+
export async function checkOutputPath(path: string, platform: NodeJS.Platform = process.platform): Promise<void> {
|
|
45
|
+
const existing = await stat(path).catch(() => {
|
|
46
|
+
// Intentional ignore: a path that does not exist yet is the usual case, and writing reports any real failure.
|
|
47
|
+
return null;
|
|
48
|
+
});
|
|
49
|
+
if (existing?.isDirectory()) throw isFolder(path, platform === "win32" ? win32 : posix);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Writes a command's output to `path`, creating the folders it names as
|
|
54
|
+
* `shot --output` does, and failing with what to do when it cannot.
|
|
55
|
+
*/
|
|
56
|
+
export async function writeOutputFile(path: string, contents: string, platform: NodeJS.Platform = process.platform): Promise<void> {
|
|
57
|
+
const paths = platform === "win32" ? win32 : posix;
|
|
58
|
+
try {
|
|
59
|
+
await mkdir(paths.dirname(path), { recursive: true });
|
|
60
|
+
await writeFile(path, contents);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
const code = errorCode(error);
|
|
63
|
+
if (code === "EISDIR") throw isFolder(path, paths);
|
|
64
|
+
if (code === "EACCES" || code === "EPERM") throw new Error(`Cannot write ${path}: permission denied.`);
|
|
65
|
+
if (code === "ENOTDIR" || code === "EEXIST") throw new Error(`Cannot write ${path}: a file stands where one of its folders would be.`);
|
|
66
|
+
throw new Error(`Cannot write ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
HeadlessPaneArgumentDef,
|
|
3
3
|
HeadlessPaneDefinition,
|
|
4
4
|
HeadlessPaneOptionDef,
|
|
5
|
+
HeadlessPaneReference,
|
|
5
6
|
PaneDef,
|
|
6
7
|
PaneTemplateDef,
|
|
7
8
|
} from "../../types/plugin";
|
|
@@ -26,6 +27,8 @@ export interface PaneFunctionCapability {
|
|
|
26
27
|
screenshotReadiness: PaneFunctionScreenshotReadiness;
|
|
27
28
|
dataRequirements: string[];
|
|
28
29
|
limitations: string[];
|
|
30
|
+
/** Lists the catalog prints under the options (EQS's sector and industry names). */
|
|
31
|
+
reference?: HeadlessPaneReference[];
|
|
29
32
|
options: PaneFunctionOptionDef[];
|
|
30
33
|
/** A rendered view that refuses any option it does not declare (PaneDef.reportOptionsOnly), as a headless report does. */
|
|
31
34
|
strictOptions?: boolean;
|
|
@@ -45,6 +48,7 @@ const NON_DATA_PANE_IDS = new Set([
|
|
|
45
48
|
"local-agent-workspace",
|
|
46
49
|
"macro-tv",
|
|
47
50
|
"plugin-marketplace",
|
|
51
|
+
"roadmap",
|
|
48
52
|
"world-venue-map",
|
|
49
53
|
]);
|
|
50
54
|
|