@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/news/aggregator.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { CompanyFrom } from "../types/company-from";
|
|
1
2
|
import { logFailure } from "../utils/log-failure";
|
|
2
3
|
import type { NewsCapability } from "../capabilities";
|
|
3
4
|
import type { ConnectionHealthRegistry } from "../core/connection-health";
|
|
4
|
-
import type { NewsArticle, NewsQuery, NewsQueryState } from "./types";
|
|
5
|
+
import type { NewsAccess, NewsArticle, NewsQuery, NewsQueryState } from "./types";
|
|
5
6
|
import { isAppVisible, subscribeAppVisibility } from "../state/app/activity";
|
|
6
7
|
import {
|
|
7
8
|
DEFAULT_GLOBAL_QUERY,
|
|
@@ -28,6 +29,18 @@ export interface NewsServiceOptions {
|
|
|
28
29
|
connectionHealth?: ConnectionHealthRegistry;
|
|
29
30
|
/** Defaults to the app's visibility; injectable for tests. */
|
|
30
31
|
visibility?: { isVisible(): boolean; subscribe(listener: () => void): () => void };
|
|
32
|
+
/**
|
|
33
|
+
* The session the sources read news with. Its key changes when what the
|
|
34
|
+
* service answers changes: a session restored at launch, a sign-in or
|
|
35
|
+
* sign-out, an upgrade. Every open query is then read again and its stories
|
|
36
|
+
* replaced, so a feed read without the session is not left on screen.
|
|
37
|
+
*/
|
|
38
|
+
session?: NewsSessionSignal;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface NewsSessionSignal {
|
|
42
|
+
key(): string;
|
|
43
|
+
subscribe(listener: () => void): () => void;
|
|
31
44
|
}
|
|
32
45
|
|
|
33
46
|
export type NewsQueryListener = (state: NewsQueryState) => void;
|
|
@@ -50,17 +63,31 @@ interface SourceFetchResult {
|
|
|
50
63
|
failedSourceIds: string[];
|
|
51
64
|
errors: string[];
|
|
52
65
|
nextCursor: string | null;
|
|
66
|
+
access: NewsAccess | null;
|
|
67
|
+
companyFrom?: CompanyFrom | null;
|
|
53
68
|
}
|
|
54
69
|
|
|
55
70
|
interface NewsQueryEntry {
|
|
56
71
|
query: NewsQuery;
|
|
57
72
|
state: NewsQueryState;
|
|
58
73
|
inFlight: Promise<NewsQueryState> | null;
|
|
74
|
+
/** The session generation the read in flight went out under. */
|
|
75
|
+
inFlightGeneration: number;
|
|
59
76
|
loadMoreInFlight: Promise<void> | null;
|
|
77
|
+
loadMoreGeneration: number;
|
|
78
|
+
/** The session generation the stories on screen were read under. */
|
|
79
|
+
generation: number;
|
|
60
80
|
refs: number;
|
|
61
81
|
lastAccessedAt: number;
|
|
62
82
|
}
|
|
63
83
|
|
|
84
|
+
/** Held back beats live: one source the service held back is enough to say the list is not the live wire. */
|
|
85
|
+
function combineNewsAccess(values: ReadonlyArray<NewsAccess | null | undefined>): NewsAccess | null {
|
|
86
|
+
if (values.includes("delayed")) return "delayed";
|
|
87
|
+
if (values.includes("realtime")) return "realtime";
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
64
91
|
function newsCapabilityPriority(source: NewsCapability): number {
|
|
65
92
|
return source.priority ?? 1000;
|
|
66
93
|
}
|
|
@@ -85,6 +112,12 @@ export class NewsService {
|
|
|
85
112
|
private readonly visibility: NonNullable<NewsServiceOptions["visibility"]>;
|
|
86
113
|
private lastPollAt = 0;
|
|
87
114
|
private unsubscribeVisibility: (() => void) | null = null;
|
|
115
|
+
private readonly session?: NewsSessionSignal;
|
|
116
|
+
private sessionKey: string | null = null;
|
|
117
|
+
/** Bumped when the session changes; a read from an older generation is dropped. */
|
|
118
|
+
private sessionGeneration = 0;
|
|
119
|
+
private sessionRefetchQueued = false;
|
|
120
|
+
private unsubscribeSession: (() => void) | null = null;
|
|
88
121
|
|
|
89
122
|
constructor(options: NewsServiceOptions = {}) {
|
|
90
123
|
const pollInterval = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
@@ -94,6 +127,7 @@ export class NewsService {
|
|
|
94
127
|
this.now = options.now ?? Date.now;
|
|
95
128
|
this.connectionHealth = options.connectionHealth;
|
|
96
129
|
this.visibility = options.visibility ?? { isVisible: isAppVisible, subscribe: subscribeAppVisibility };
|
|
130
|
+
this.session = options.session;
|
|
97
131
|
}
|
|
98
132
|
|
|
99
133
|
register(source: NewsCapability): () => void {
|
|
@@ -125,6 +159,10 @@ export class NewsService {
|
|
|
125
159
|
this.clearPollTimer();
|
|
126
160
|
this.scheduleNextPoll();
|
|
127
161
|
});
|
|
162
|
+
if (this.session) {
|
|
163
|
+
this.sessionKey = this.session.key();
|
|
164
|
+
this.unsubscribeSession = this.session.subscribe(() => this.handleSessionChange());
|
|
165
|
+
}
|
|
128
166
|
this.scheduleNextPoll();
|
|
129
167
|
}
|
|
130
168
|
|
|
@@ -132,9 +170,46 @@ export class NewsService {
|
|
|
132
170
|
this.polling = false;
|
|
133
171
|
this.unsubscribeVisibility?.();
|
|
134
172
|
this.unsubscribeVisibility = null;
|
|
173
|
+
this.unsubscribeSession?.();
|
|
174
|
+
this.unsubscribeSession = null;
|
|
135
175
|
this.clearPollTimer();
|
|
136
176
|
}
|
|
137
177
|
|
|
178
|
+
/**
|
|
179
|
+
* A new session answers with other news: the live wire once a Pro session
|
|
180
|
+
* is in place, the held-back one after signing out. Reads still out under
|
|
181
|
+
* the old session are dropped, open queries are read again and replace
|
|
182
|
+
* their stories rather than merge into them, and closed ones are forgotten.
|
|
183
|
+
* A token and its user arrive as separate changes, so the reads wait for
|
|
184
|
+
* the end of the tick and go out once.
|
|
185
|
+
*/
|
|
186
|
+
private handleSessionChange(): void {
|
|
187
|
+
if (!this.session) return;
|
|
188
|
+
const key = this.session.key();
|
|
189
|
+
if (key === this.sessionKey) return;
|
|
190
|
+
this.sessionKey = key;
|
|
191
|
+
this.sessionGeneration++;
|
|
192
|
+
if (this.sessionRefetchQueued) return;
|
|
193
|
+
this.sessionRefetchQueued = true;
|
|
194
|
+
queueMicrotask(() => {
|
|
195
|
+
this.sessionRefetchQueued = false;
|
|
196
|
+
if (!this.polling) return;
|
|
197
|
+
let dropped = false;
|
|
198
|
+
for (const [queryKey, entry] of this.queries) {
|
|
199
|
+
if (entry.refs > 0) continue;
|
|
200
|
+
this.queries.delete(queryKey);
|
|
201
|
+
dropped = true;
|
|
202
|
+
}
|
|
203
|
+
if (dropped) {
|
|
204
|
+
this.rebuildArticlePool();
|
|
205
|
+
this.notify();
|
|
206
|
+
}
|
|
207
|
+
for (const entry of this.queries.values()) {
|
|
208
|
+
void this.refreshQuery(entry.query, true);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
138
213
|
private pollInFlight = false;
|
|
139
214
|
|
|
140
215
|
private clearPollTimer(): void {
|
|
@@ -226,6 +301,7 @@ export class NewsService {
|
|
|
226
301
|
return;
|
|
227
302
|
}
|
|
228
303
|
|
|
304
|
+
const generation = this.sessionGeneration;
|
|
229
305
|
const request = (async () => {
|
|
230
306
|
entry.state = { ...entry.state, loadingMore: true };
|
|
231
307
|
this.notify();
|
|
@@ -234,6 +310,12 @@ export class NewsService {
|
|
|
234
310
|
...normalized,
|
|
235
311
|
cursor: entry.state.nextCursor ?? undefined,
|
|
236
312
|
});
|
|
313
|
+
// An older page from another session must not join the list the new session read.
|
|
314
|
+
if (generation !== this.sessionGeneration || entry.generation !== generation) {
|
|
315
|
+
entry.state = { ...entry.state, loadingMore: false };
|
|
316
|
+
this.notify();
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
237
319
|
entry.state = {
|
|
238
320
|
...entry.state,
|
|
239
321
|
loadingMore: false,
|
|
@@ -253,6 +335,7 @@ export class NewsService {
|
|
|
253
335
|
}
|
|
254
336
|
})();
|
|
255
337
|
entry.loadMoreInFlight = request;
|
|
338
|
+
entry.loadMoreGeneration = generation;
|
|
256
339
|
await request;
|
|
257
340
|
}
|
|
258
341
|
|
|
@@ -318,7 +401,9 @@ export class NewsService {
|
|
|
318
401
|
showLoading: boolean,
|
|
319
402
|
): Promise<NewsQueryState> {
|
|
320
403
|
const entry = this.getOrCreateQueryEntry(query);
|
|
321
|
-
|
|
404
|
+
const generation = this.sessionGeneration;
|
|
405
|
+
// A read that went out under an older session answers for that session: a new one goes out beside it.
|
|
406
|
+
if (entry.inFlight && entry.inFlightGeneration === generation) return entry.inFlight;
|
|
322
407
|
|
|
323
408
|
const current = entry.state;
|
|
324
409
|
if (showLoading) {
|
|
@@ -333,14 +418,17 @@ export class NewsService {
|
|
|
333
418
|
const promise = (async () => {
|
|
334
419
|
try {
|
|
335
420
|
const result = await this.fetchFromSources(query);
|
|
421
|
+
if (generation !== this.sessionGeneration) return this.currentRead(entry);
|
|
336
422
|
if (result.sourceIds.length === 0 && result.failedSourceIds.length > 0) {
|
|
337
423
|
throw new Error("News sources unavailable.");
|
|
338
424
|
}
|
|
339
|
-
if (entry.loadMoreInFlight) return entry.state;
|
|
425
|
+
if (entry.loadMoreInFlight && entry.loadMoreGeneration === generation) return entry.state;
|
|
340
426
|
const incoming = filterNewsArticlesForQuery(dedupeNewsArticles(result.articles), query);
|
|
427
|
+
// Stories read under another session are another feed (held back, or live): the new read replaces them.
|
|
428
|
+
const sameSession = entry.generation === generation;
|
|
341
429
|
// The stories a coin's fallback found are not the pair's own: once stories are filed under it, they replace them.
|
|
342
|
-
const existing = incoming.length > 0 && entry.state.articles.length > 0
|
|
343
|
-
&& (incoming[0]!.searchedFor ?? null) !== (entry.state.articles[0]!.searchedFor ?? null)
|
|
430
|
+
const existing = !sameSession || (incoming.length > 0 && entry.state.articles.length > 0
|
|
431
|
+
&& (incoming[0]!.searchedFor ?? null) !== (entry.state.articles[0]!.searchedFor ?? null))
|
|
344
432
|
? []
|
|
345
433
|
: entry.state.articles;
|
|
346
434
|
const incomingIds = new Set(incoming.map((article) => article.id));
|
|
@@ -363,13 +451,18 @@ export class NewsService {
|
|
|
363
451
|
sourceIds: result.sourceIds,
|
|
364
452
|
nextCursor: hasOlderPages ? entry.state.nextCursor : result.nextCursor,
|
|
365
453
|
loadingMore: entry.state.loadingMore,
|
|
454
|
+
// A page that does not say keeps what this session's earlier answer said.
|
|
455
|
+
access: result.access ?? (sameSession ? entry.state.access ?? null : null),
|
|
456
|
+
companyFrom: result.companyFrom ?? null,
|
|
366
457
|
};
|
|
367
458
|
entry.state = state;
|
|
459
|
+
entry.generation = generation;
|
|
368
460
|
entry.lastAccessedAt = this.now();
|
|
369
461
|
this.rebuildArticlePool();
|
|
370
462
|
this.notify();
|
|
371
463
|
return state;
|
|
372
464
|
} catch (error) {
|
|
465
|
+
if (generation !== this.sessionGeneration) return this.currentRead(entry);
|
|
373
466
|
const state: NewsQueryState = {
|
|
374
467
|
...current,
|
|
375
468
|
phase: "error",
|
|
@@ -380,15 +473,21 @@ export class NewsService {
|
|
|
380
473
|
this.notify();
|
|
381
474
|
return state;
|
|
382
475
|
} finally {
|
|
383
|
-
entry.inFlight = null;
|
|
476
|
+
if (entry.inFlightGeneration === generation) entry.inFlight = null;
|
|
384
477
|
this.pruneInactiveQueries();
|
|
385
478
|
}
|
|
386
479
|
})();
|
|
387
480
|
|
|
388
481
|
entry.inFlight = promise;
|
|
482
|
+
entry.inFlightGeneration = generation;
|
|
389
483
|
return promise;
|
|
390
484
|
}
|
|
391
485
|
|
|
486
|
+
/** What a dropped read answers its caller: the current session's read when one is out, else the state as it is. */
|
|
487
|
+
private currentRead(entry: NewsQueryEntry): Promise<NewsQueryState> | NewsQueryState {
|
|
488
|
+
return entry.inFlight && entry.inFlightGeneration === this.sessionGeneration ? entry.inFlight : entry.state;
|
|
489
|
+
}
|
|
490
|
+
|
|
392
491
|
private getOrCreateQueryEntry(query: NewsQuery): NewsQueryEntry {
|
|
393
492
|
const key = buildNewsQueryKey(query);
|
|
394
493
|
const now = this.now();
|
|
@@ -402,7 +501,10 @@ export class NewsService {
|
|
|
402
501
|
query,
|
|
403
502
|
state: createIdleNewsQueryState(),
|
|
404
503
|
inFlight: null,
|
|
504
|
+
inFlightGeneration: this.sessionGeneration,
|
|
405
505
|
loadMoreInFlight: null,
|
|
506
|
+
loadMoreGeneration: this.sessionGeneration,
|
|
507
|
+
generation: this.sessionGeneration,
|
|
406
508
|
refs: 0,
|
|
407
509
|
lastAccessedAt: now,
|
|
408
510
|
};
|
|
@@ -451,7 +553,7 @@ export class NewsService {
|
|
|
451
553
|
private async readSourcePage(
|
|
452
554
|
source: NewsCapability,
|
|
453
555
|
query: NewsQuery,
|
|
454
|
-
): Promise<{ articles: NewsArticle[]; nextCursor: string | null; error?: string | null }> {
|
|
556
|
+
): Promise<{ articles: NewsArticle[]; nextCursor: string | null; error?: string | null; access?: NewsAccess | null; companyFrom?: CompanyFrom | null }> {
|
|
455
557
|
if (source.provider.fetchNewsPage) {
|
|
456
558
|
const page = await this.trackSourceRequest(
|
|
457
559
|
source,
|
|
@@ -462,6 +564,8 @@ export class NewsService {
|
|
|
462
564
|
articles: page.articles.map((article) => markDetailCapableArticle(source, article)),
|
|
463
565
|
nextCursor: page.nextCursor ?? null,
|
|
464
566
|
error: page.error,
|
|
567
|
+
access: page.access,
|
|
568
|
+
companyFrom: page.companyFrom ?? null,
|
|
465
569
|
};
|
|
466
570
|
}
|
|
467
571
|
const articles = (await this.trackSourceRequest(
|
|
@@ -484,6 +588,8 @@ export class NewsService {
|
|
|
484
588
|
failedSourceIds,
|
|
485
589
|
errors: page.error ? [page.error] : [],
|
|
486
590
|
nextCursor: page.nextCursor,
|
|
591
|
+
access: page.access ?? null,
|
|
592
|
+
companyFrom: page.companyFrom ?? null,
|
|
487
593
|
};
|
|
488
594
|
if (page.articles.length > 0) return result;
|
|
489
595
|
firstEmpty ??= result;
|
|
@@ -491,7 +597,7 @@ export class NewsService {
|
|
|
491
597
|
failedSourceIds.push(newsCapabilitySourceId(source));
|
|
492
598
|
}
|
|
493
599
|
}
|
|
494
|
-
return firstEmpty ?? { articles: [], sourceIds: [], failedSourceIds, errors: [], nextCursor: null };
|
|
600
|
+
return firstEmpty ?? { articles: [], sourceIds: [], failedSourceIds, errors: [], nextCursor: null, access: null };
|
|
495
601
|
}
|
|
496
602
|
|
|
497
603
|
private async fetchMergedNews(query: NewsQuery, sources: NewsCapability[]): Promise<SourceFetchResult> {
|
|
@@ -505,6 +611,7 @@ export class NewsService {
|
|
|
505
611
|
const errors: string[] = [];
|
|
506
612
|
const sourceIds: string[] = [];
|
|
507
613
|
const failedSourceIds: string[] = [];
|
|
614
|
+
const access: Array<NewsAccess | null | undefined> = [];
|
|
508
615
|
let nextCursor: string | null = null;
|
|
509
616
|
settled.forEach((result, index) => {
|
|
510
617
|
if (result.status !== "fulfilled") {
|
|
@@ -515,9 +622,10 @@ export class NewsService {
|
|
|
515
622
|
articles.push(...result.value.page.articles);
|
|
516
623
|
if (result.value.page.error) errors.push(result.value.page.error);
|
|
517
624
|
sourceIds.push(newsCapabilitySourceId(result.value.source));
|
|
625
|
+
access.push(result.value.page.access);
|
|
518
626
|
nextCursor ??= result.value.page.nextCursor;
|
|
519
627
|
});
|
|
520
|
-
return { articles, sourceIds, failedSourceIds, errors, nextCursor };
|
|
628
|
+
return { articles, sourceIds, failedSourceIds, errors, nextCursor, access: combineNewsAccess(access) };
|
|
521
629
|
}
|
|
522
630
|
|
|
523
631
|
private trackSourceRequest<T>(
|
|
@@ -550,6 +658,8 @@ export class NewsService {
|
|
|
550
658
|
sourceIds: [...new Set([...entry.state.sourceIds, newsCapabilitySourceId(source)])],
|
|
551
659
|
nextCursor: entry.state.nextCursor,
|
|
552
660
|
loadingMore: false,
|
|
661
|
+
access: entry.state.access ?? null,
|
|
662
|
+
companyFrom: entry.state.companyFrom ?? null,
|
|
553
663
|
};
|
|
554
664
|
changed = true;
|
|
555
665
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { apiClient } from "../api-client";
|
|
2
|
+
import { hasProAccess } from "../api-client/plan-rules";
|
|
3
|
+
import type { NewsSessionSignal } from "./aggregator";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* What decides which news Gloom Cloud answers with: whether requests carry a
|
|
7
|
+
* session, whose it is, and whether that account has real-time news. Changes
|
|
8
|
+
* when the session is restored at launch, on sign-in and sign-out, and when
|
|
9
|
+
* the plan changes.
|
|
10
|
+
*/
|
|
11
|
+
export const cloudNewsSession: NewsSessionSignal = {
|
|
12
|
+
key() {
|
|
13
|
+
const user = apiClient.getCurrentUser();
|
|
14
|
+
return [
|
|
15
|
+
apiClient.hasSessionCredential() ? "session" : "none",
|
|
16
|
+
user?.id ?? "",
|
|
17
|
+
hasProAccess(user) ? "realtime" : "delayed",
|
|
18
|
+
].join(":");
|
|
19
|
+
},
|
|
20
|
+
subscribe: (listener) => apiClient.subscribeCurrentUser(listener),
|
|
21
|
+
};
|
package/src/news/hooks.ts
CHANGED
package/src/news/news-model.ts
CHANGED
package/src/news/types.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { CompanyFrom } from "../types/company-from";
|
|
2
|
+
|
|
1
3
|
type NewsQueryScope = "global" | "ticker";
|
|
2
4
|
export type NewsFeed = "latest" | "top" | "breaking" | "ticker" | "sector" | "topic";
|
|
3
5
|
type NewsSentiment = "positive" | "neutral" | "negative";
|
|
@@ -81,11 +83,22 @@ export interface NewsQuery {
|
|
|
81
83
|
|
|
82
84
|
type NewsQueryPhase = "idle" | "loading" | "ready" | "refreshing" | "error";
|
|
83
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Which news a source answered with: the live wire, or the one held back for
|
|
88
|
+
* an account without real-time news. Read from the answer itself, never from
|
|
89
|
+
* the plan the app remembers.
|
|
90
|
+
*/
|
|
91
|
+
export type NewsAccess = "realtime" | "delayed";
|
|
92
|
+
|
|
84
93
|
export interface NewsPage {
|
|
85
94
|
/** Active source failure, including a partial failure with usable articles. */
|
|
86
95
|
error?: string | null;
|
|
87
96
|
articles: NewsArticle[];
|
|
88
97
|
nextCursor?: string | null;
|
|
98
|
+
/** Which news the source answered with; null or absent when the answer does not say. */
|
|
99
|
+
access?: NewsAccess | null;
|
|
100
|
+
/** The company's home line the stories are read from, when not the listing asked (a receipt, a foreign line). */
|
|
101
|
+
companyFrom?: CompanyFrom | null;
|
|
89
102
|
}
|
|
90
103
|
|
|
91
104
|
export interface NewsQueryState {
|
|
@@ -96,4 +109,8 @@ export interface NewsQueryState {
|
|
|
96
109
|
sourceIds: string[];
|
|
97
110
|
nextCursor: string | null;
|
|
98
111
|
loadingMore: boolean;
|
|
112
|
+
/** Which news the stories are, from the last answer; null or absent when it did not say. */
|
|
113
|
+
access?: NewsAccess | null;
|
|
114
|
+
/** The company's home line the stories are read from, when not the listing asked. */
|
|
115
|
+
companyFrom?: CompanyFrom | null;
|
|
99
116
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { metricDescription, readAlertMetric, validMetricRule } from "./metric-model";
|
|
2
2
|
import type { AlertCondition, AlertRule } from "./types";
|
|
3
|
+
import { alertListing } from "./format";
|
|
4
|
+
import { canonicalExchange } from "../../../utils/exchanges";
|
|
3
5
|
import { logFailure } from "../../../utils/log-failure";
|
|
4
6
|
|
|
5
7
|
export type { AlertRule };
|
|
@@ -33,10 +35,14 @@ export function editAlert(
|
|
|
33
35
|
targetPrice: number,
|
|
34
36
|
): AlertRule {
|
|
35
37
|
const nextSymbol = symbol.trim().toUpperCase();
|
|
38
|
+
const sameListing = nextSymbol === alert.symbol.trim().toUpperCase();
|
|
36
39
|
return {
|
|
37
40
|
id: alert.id,
|
|
38
41
|
symbol: nextSymbol,
|
|
39
|
-
exchange:
|
|
42
|
+
exchange: sameListing ? alert.exchange : undefined,
|
|
43
|
+
// The listing's currency and venue stay with it; a new symbol learns its own at its first check.
|
|
44
|
+
...(sameListing && alert.lastQuoteCurrency ? { lastQuoteCurrency: alert.lastQuoteCurrency } : {}),
|
|
45
|
+
...(sameListing && alert.lastQuoteExchange ? { lastQuoteExchange: alert.lastQuoteExchange } : {}),
|
|
40
46
|
condition,
|
|
41
47
|
targetPrice,
|
|
42
48
|
createdAt: alert.createdAt,
|
|
@@ -51,6 +57,19 @@ export function rearmAlert(alert: AlertRule): AlertRule {
|
|
|
51
57
|
return editAlert(alert, alert.symbol, alert.condition, alert.targetPrice);
|
|
52
58
|
}
|
|
53
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Whether two level alerts are one rule: the same measure, condition and
|
|
62
|
+
* target on the same listing, whether it was named with its exchange or read
|
|
63
|
+
* from the symbol alone. A second add of one names the first and adds nothing.
|
|
64
|
+
*/
|
|
65
|
+
export function sameLevelRule(a: AlertRule, b: AlertRule): boolean {
|
|
66
|
+
const venue = (alert: AlertRule) => canonicalExchange(alert.exchange ?? "");
|
|
67
|
+
return a.symbol.toUpperCase() === b.symbol.toUpperCase()
|
|
68
|
+
&& (venue(a) === venue(b) || alertListing(a) === alertListing(b))
|
|
69
|
+
&& a.condition === b.condition && a.targetPrice === b.targetPrice
|
|
70
|
+
&& JSON.stringify(readAlertMetric(a.metric)) === JSON.stringify(readAlertMetric(b.metric));
|
|
71
|
+
}
|
|
72
|
+
|
|
54
73
|
export function evaluateAlert(alert: AlertRule, currentPrice: number): boolean {
|
|
55
74
|
if (alert.metric || alert.status !== "active") return false;
|
|
56
75
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { metricDescription } from "./metric-model";
|
|
2
|
+
import { eventAlertTarget, type EventAlertRule } from "./events";
|
|
3
|
+
import { alertChangeEntry, appendFiredAlert, readFiredAlerts, type AlertLogAction, type FiredAlert } from "./fired-log";
|
|
4
|
+
import { alertListing } from "./format";
|
|
5
|
+
import { isResearchAlertKind, readResearchRule } from "./research-rules";
|
|
6
|
+
import { readScheduledRule, scheduledEventName } from "./scheduled";
|
|
7
|
+
import type { AlertRule } from "./types";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A rule as the alert log names it: its kind, the rule in words (`EWG:ARCA
|
|
11
|
+
* below 40`, as a fired price alert reads) and the symbol it follows.
|
|
12
|
+
*/
|
|
13
|
+
export function alertLogRule(rule: AlertRule | EventAlertRule) {
|
|
14
|
+
if ("symbol" in rule) {
|
|
15
|
+
return {
|
|
16
|
+
kind: rule.metric?.kind ?? "price", ruleId: rule.id,
|
|
17
|
+
rule: rule.metric ? metricDescription(rule) : `${alertListing(rule)} ${rule.condition} ${rule.targetPrice}`,
|
|
18
|
+
symbol: rule.symbol, ...rule.exchange ? { exchange: rule.exchange } : {},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const scheduled = readScheduledRule(rule);
|
|
22
|
+
const symbol = scheduled?.target.kind === "earnings" ? scheduled.target.symbol
|
|
23
|
+
: isResearchAlertKind(rule.kind) ? readResearchRule(rule.kind, rule.value)?.symbol : undefined;
|
|
24
|
+
return {
|
|
25
|
+
kind: rule.kind, ruleId: rule.id, rule: scheduled ? scheduledEventName(scheduled.target) : eventAlertTarget(rule),
|
|
26
|
+
...symbol ? { symbol } : {},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** The saved log with one change more: what the pane's `usePluginState(FIRED_LOG_KEY)` setter takes. */
|
|
31
|
+
export function withAlertChange(saved: unknown, action: AlertLogAction, rule: AlertRule | EventAlertRule, at = Date.now()): FiredAlert[] {
|
|
32
|
+
return appendFiredAlert(readFiredAlerts(saved), alertChangeEntry(action, alertLogRule(rule), at));
|
|
33
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// release's rows as the calendar's feeds name it from week to week, and the
|
|
4
4
|
// notifications sent before it and once its figures are out. CPI and FOMC are
|
|
5
5
|
// two cases of the same alert, whose dates come from their own calendars.
|
|
6
|
-
import { dateKey, eventCountry, matchesCountry, type CountryFilter } from "../econ/calendar-model";
|
|
6
|
+
import { dateKey, econEventName, eventCountry, matchesCountry, type CountryFilter } from "../econ/calendar-model";
|
|
7
7
|
import { isAllDayEvent, hasNoReleaseTime, type EconEvent, type EconImpact } from "../econ/types";
|
|
8
8
|
import { RENAMED_RELEASES } from "../shared/macro-releases";
|
|
9
9
|
import { MACRO_EVENT_NAMES } from "./research-builder";
|
|
@@ -126,7 +126,11 @@ export interface CalendarEventChoice {
|
|
|
126
126
|
|
|
127
127
|
const IMPACT_RANK: Record<EconImpact, number> = { high: 0, medium: 1, low: 2 };
|
|
128
128
|
|
|
129
|
-
type RankedChoice = CalendarEventChoice & {
|
|
129
|
+
type RankedChoice = CalendarEventChoice & {
|
|
130
|
+
words: string[];
|
|
131
|
+
/** The words of the name `econ` shows it under, when that is not the feed's (the Fed's rate as its upper bound). */
|
|
132
|
+
shownWords: string[];
|
|
133
|
+
};
|
|
130
134
|
|
|
131
135
|
/**
|
|
132
136
|
* The calendar's releases, one per country and name, each with its next row
|
|
@@ -150,6 +154,7 @@ function calendarChoices(events: readonly EconEvent[], now: number): RankedChoic
|
|
|
150
154
|
impact: rows.reduce<EconImpact>((best, event) => IMPACT_RANK[event.impact] < IMPACT_RANK[best] ? event.impact : best, "low"),
|
|
151
155
|
next,
|
|
152
156
|
words: calendarNameWords(named.event),
|
|
157
|
+
shownWords: calendarNameWords(econEventName(named)),
|
|
153
158
|
};
|
|
154
159
|
});
|
|
155
160
|
}
|
|
@@ -246,7 +251,8 @@ function matchCalendarWords(
|
|
|
246
251
|
now: number,
|
|
247
252
|
): CalendarEventMatch {
|
|
248
253
|
const choices = calendarChoices(country ? events.filter((event) => matchesCountry(event, country)) : events, now);
|
|
249
|
-
|
|
254
|
+
// The name as the feed gives it, or as `econ` shows it: the alert keeps the feed's, which the calendar's rows carry.
|
|
255
|
+
const exact = choices.filter((choice) => [choice.words, choice.shownWords].some((name) => name.join(" ") === words.join(" ")));
|
|
250
256
|
const naming = (typed: readonly string[], loose: boolean) => typed.length === 0 ? [] : choices.filter((choice) =>
|
|
251
257
|
typed.every((word) => loose ? wordFound(word, choice.words) : choice.words.includes(word)));
|
|
252
258
|
// CPI by the name one feed gives it everywhere but the US (China's Inflation Rate YoY).
|
|
@@ -278,7 +284,7 @@ function matchCalendarWords(
|
|
|
278
284
|
return { kind: "match", choice: strip(ranked[0]!), others: ranked.slice(1).map(strip) };
|
|
279
285
|
}
|
|
280
286
|
|
|
281
|
-
function strip({ words: _words, ...choice }: RankedChoice): CalendarEventChoice {
|
|
287
|
+
function strip({ words: _words, shownWords: _shown, ...choice }: RankedChoice): CalendarEventChoice {
|
|
282
288
|
return choice;
|
|
283
289
|
}
|
|
284
290
|
|