gloomberb 0.15.7 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -2
- package/package.json +1 -1
- package/src/api-client/app-attention.ts +147 -0
- package/src/api-client/attention.ts +26 -0
- package/src/api-client/awards.ts +215 -0
- package/src/api-client/catalysts.ts +160 -0
- package/src/api-client/chat.ts +137 -14
- package/src/api-client/company-kpis.ts +153 -0
- package/src/api-client/cpi.ts +52 -0
- package/src/api-client/credit-documents.ts +107 -0
- package/src/api-client/data.ts +192 -3
- package/src/api-client/distress.ts +164 -0
- package/src/api-client/doe.ts +75 -0
- package/src/api-client/equity-screener.ts +11 -0
- package/src/api-client/exposure.ts +202 -0
- package/src/api-client/gpu.ts +93 -0
- package/src/api-client/hiring.ts +110 -0
- package/src/api-client/index.ts +78 -3
- package/src/api-client/normalizers.ts +31 -1
- package/src/api-client/paths.ts +3 -0
- package/src/api-client/perps.ts +127 -0
- package/src/api-client/power.ts +148 -0
- package/src/api-client/request.ts +58 -1
- package/src/api-client/research-activity.ts +172 -13
- package/src/api-client/socket.ts +75 -27
- package/src/api-client/supply-chain-graph.ts +40 -0
- package/src/api-client/supply-chain.ts +98 -0
- package/src/api-client/telemetry.ts +29 -0
- package/src/api-client/themes.ts +28 -0
- package/src/api-client/types.ts +33 -1
- package/src/api-client/wall-experiments.ts +100 -0
- package/src/api-client/wall-summary.ts +49 -0
- package/src/api-client/web-experiments.ts +136 -0
- package/src/app/pane-runtime/plugin-bindings.ts +5 -0
- package/src/app/pane-runtime/ticker-open-runtime.ts +8 -2
- package/src/app/runtime/update.ts +13 -1
- package/src/cli/commands/system.ts +2 -0
- package/src/cli/desktop-pane-shot.ts +3 -4
- package/src/cli/pane-functions/data.ts +78 -0
- package/src/cli/pane-functions/financial-ratio-shot.ts +160 -0
- package/src/cli/pane-functions/function-help.ts +220 -8
- package/src/cli/pane-functions/headless.ts +4 -0
- package/src/cli/pane-functions/screenshot-evidence.ts +21 -0
- package/src/cli/pane-functions/screenshot.ts +49 -5
- package/src/components/chart/composite/composite-chart.tsx +15 -2
- package/src/components/chart/composite/format.ts +24 -11
- package/src/components/chart/composite/rasterizer.ts +67 -16
- package/src/components/chart/composite/scene.ts +53 -2
- package/src/components/chart/composite/session-shading.ts +3 -2
- package/src/components/chart/composite/text-renderer.ts +52 -1
- package/src/components/chart/composite/time-axis.ts +100 -30
- package/src/components/chart/composite/tools.ts +2 -1
- package/src/components/chart/composite/types.ts +4 -0
- package/src/components/chart-table/index.ts +1 -0
- package/src/components/chart-table/layout.ts +15 -0
- package/src/components/command-bar/action-runtime.ts +1 -0
- package/src/components/command-bar/commands/direct/index.ts +32 -0
- package/src/components/command-bar/commands/registry.ts +7 -0
- package/src/components/command-bar/pane-templates/items.ts +11 -0
- package/src/components/command-bar/pane-templates/workflow.ts +19 -4
- package/src/components/command-bar/routes/root/command-items.ts +6 -0
- package/src/components/command-bar/routes/root/provider-search.ts +2 -0
- package/src/components/command-bar/routes/root/results.ts +64 -0
- package/src/components/command-bar/routes/root/runtime.ts +8 -0
- package/src/components/command-bar/routes/ticker-search/results.ts +89 -24
- package/src/components/command-bar/routes/ticker-search/route.ts +2 -0
- package/src/components/command-bar/surface/index.tsx +15 -0
- package/src/components/command-bar/view-model.ts +3 -0
- package/src/components/command-bar/workflow/collection-actions.ts +9 -7
- package/src/components/command-bar/workflow/ops.ts +19 -0
- package/src/components/command-bar/workflow/tickers.ts +5 -0
- package/src/components/company-logo.tsx +15 -2
- package/src/components/data-table/table-pane.ts +2 -1
- package/src/components/index.ts +1 -0
- package/src/components/layout/detached-pane-shell.tsx +12 -1
- package/src/components/layout/missing-pane.tsx +87 -23
- package/src/components/layout/pane/footer/index.tsx +64 -18
- package/src/components/layout/pane/footer/model.ts +85 -0
- package/src/components/layout/pane/sidebar.tsx +19 -1
- package/src/components/layout/shell/index.tsx +38 -28
- package/src/components/layout/shell/pane/management-shortcuts.ts +13 -1
- package/src/components/layout/status-bar.tsx +93 -28
- package/src/components/layout/status-widget-space.ts +72 -0
- package/src/components/onboarding/first-run-workspace.ts +8 -2
- package/src/components/onboarding/onboarding-wizard.tsx +35 -16
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +14 -5
- package/src/components/onboarding/wizard-model.ts +13 -5
- package/src/components/onboarding/wizard-positions.ts +56 -7
- package/src/components/price-performance.tsx +31 -34
- package/src/components/ui/data-table/index.tsx +11 -8
- package/src/components/ui/display.tsx +15 -6
- package/src/components/ui/fields.tsx +5 -0
- package/src/components/ui/figure.tsx +94 -0
- package/src/components/ui/header-case.ts +25 -3
- package/src/components/ui/icon.tsx +3 -1
- package/src/components/ui/message-composer.tsx +8 -1
- package/src/components/ui/query-bar.tsx +8 -1
- package/src/components/ui/ratio-bar.tsx +84 -0
- package/src/components/ui/toggle.tsx +3 -1
- package/src/components/use-scrollbox-layout.ts +17 -0
- package/src/core/state/app/layout.ts +35 -1
- package/src/core/state/app/state.ts +15 -0
- package/src/core/state/app/types.ts +1 -0
- package/src/data/config/store/normalize.ts +24 -0
- package/src/i18n/es.ts +2 -0
- package/src/i18n/ja.ts +2 -0
- package/src/i18n/ko.ts +2 -0
- package/src/i18n/zh-cn.ts +2 -0
- package/src/i18n/zh-tw.ts +2 -0
- package/src/layout-marketplace/pane-imagery.ts +1 -0
- package/src/market-data/coordinator/entries.ts +13 -0
- package/src/market-data/coordinator/financials.ts +33 -8
- package/src/market-data/market/freshness.ts +16 -5
- package/src/market-data/market/trading-sessions.ts +65 -1
- package/src/market-data/published-cn-sessions.ts +25 -0
- package/src/market-data/published-nse-sessions.ts +20 -0
- package/src/news/aggregator.ts +4 -0
- package/src/plugins/builtin/account-management/model.ts +6 -1
- package/src/plugins/builtin/account-management/pane.tsx +36 -25
- package/src/plugins/builtin/alerts/catalysts.ts +77 -0
- package/src/plugins/builtin/alerts/events-pane.tsx +3 -0
- package/src/plugins/builtin/alerts/events.ts +10 -4
- package/src/plugins/builtin/alerts/history-pane.tsx +1 -1
- package/src/plugins/builtin/alerts/history.ts +1 -0
- package/src/plugins/builtin/alerts/index.tsx +6 -0
- package/src/plugins/builtin/alerts/research-builder.ts +10 -3
- package/src/plugins/builtin/alerts/research-rules.ts +15 -1
- package/src/plugins/builtin/analytics/risk-client.ts +15 -3
- package/src/plugins/builtin/analytics/risk-headless.ts +123 -18
- package/src/plugins/builtin/attention/client.ts +80 -0
- package/src/plugins/builtin/attention/evidence.ts +30 -0
- package/src/plugins/builtin/attention/headless.ts +33 -0
- package/src/plugins/builtin/attention/index.tsx +18 -0
- package/src/plugins/builtin/attention/model.ts +59 -0
- package/src/plugins/builtin/attention/pane.tsx +211 -0
- package/src/plugins/builtin/attention/trending.tsx +59 -0
- package/src/plugins/builtin/awards/alerts.ts +32 -0
- package/src/plugins/builtin/awards/client.ts +82 -0
- package/src/plugins/builtin/awards/detail.tsx +107 -0
- package/src/plugins/builtin/awards/evidence.ts +36 -0
- package/src/plugins/builtin/awards/headless.ts +55 -0
- package/src/plugins/builtin/awards/index.tsx +20 -0
- package/src/plugins/builtin/awards/model.ts +42 -0
- package/src/plugins/builtin/awards/pane.tsx +263 -0
- package/src/plugins/builtin/catalysts/client.ts +28 -0
- package/src/plugins/builtin/catalysts/detail.tsx +88 -0
- package/src/plugins/builtin/catalysts/evidence.ts +47 -0
- package/src/plugins/builtin/catalysts/headless.ts +42 -0
- package/src/plugins/builtin/catalysts/history.ts +26 -0
- package/src/plugins/builtin/catalysts/index.tsx +29 -0
- package/src/plugins/builtin/catalysts/model.ts +135 -0
- package/src/plugins/builtin/catalysts/pane.tsx +159 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
- package/src/plugins/builtin/chart-composer/headless-schema.ts +6 -0
- package/src/plugins/builtin/chart-composer/index.tsx +8 -1
- package/src/plugins/builtin/chart-composer/pane.tsx +28 -1
- package/src/plugins/builtin/chat/attachments/desktop.tsx +610 -0
- package/src/plugins/builtin/chat/attachments/files.ts +45 -0
- package/src/plugins/builtin/chat/attachments/model.ts +164 -0
- package/src/plugins/builtin/chat/attachments/terminal.tsx +72 -0
- package/src/plugins/builtin/chat/attachments/transfer.ts +165 -0
- package/src/plugins/builtin/chat/content/channel-navigation.ts +13 -12
- package/src/plugins/builtin/chat/content/composer-runtime.ts +25 -4
- package/src/plugins/builtin/chat/content/composer.tsx +46 -4
- package/src/plugins/builtin/chat/content/footer.tsx +46 -8
- package/src/plugins/builtin/chat/content/index.tsx +263 -62
- package/src/plugins/builtin/chat/content/layout-metrics.ts +10 -2
- package/src/plugins/builtin/chat/content/open-targets.ts +17 -3
- package/src/plugins/builtin/chat/content/scroll.ts +7 -1
- package/src/plugins/builtin/chat/content/shortcuts.ts +36 -11
- package/src/plugins/builtin/chat/content/snapshot.ts +3 -0
- package/src/plugins/builtin/chat/content/transcript.tsx +17 -1
- package/src/plugins/builtin/chat/content/types.ts +7 -0
- package/src/plugins/builtin/chat/controller/attachments.ts +213 -0
- package/src/plugins/builtin/chat/controller/connections.ts +3 -1
- package/src/plugins/builtin/chat/controller/index.ts +114 -2
- package/src/plugins/builtin/chat/controller/lifecycle.ts +9 -0
- package/src/plugins/builtin/chat/controller/messages.ts +35 -2
- package/src/plugins/builtin/chat/controller/send.ts +128 -25
- package/src/plugins/builtin/chat/controller/state.ts +12 -1
- package/src/plugins/builtin/chat/controller/utils.ts +7 -5
- package/src/plugins/builtin/chat/controller/view.ts +2 -0
- package/src/plugins/builtin/chat/index.tsx +21 -0
- package/src/plugins/builtin/chat/layout.ts +9 -4
- package/src/plugins/builtin/chat/message/desktop.tsx +55 -20
- package/src/plugins/builtin/chat/message/header.tsx +30 -5
- package/src/plugins/builtin/chat/message/inline-tokens.tsx +9 -0
- package/src/plugins/builtin/chat/message/pending-send.ts +35 -0
- package/src/plugins/builtin/chat/message/profile-popover.tsx +5 -1
- package/src/plugins/builtin/chat/message/render-state.ts +12 -2
- package/src/plugins/builtin/chat/message/terminal.tsx +51 -4
- package/src/plugins/builtin/chat/message/types.ts +6 -1
- package/src/plugins/builtin/chat/pane-state.ts +59 -1
- package/src/plugins/builtin/chat/profile-popover.ts +50 -1
- package/src/plugins/builtin/chat/sidebar.tsx +8 -3
- package/src/plugins/builtin/chat/status-widget.tsx +29 -12
- package/src/plugins/builtin/chat/unread-inbox-pane.tsx +143 -0
- package/src/plugins/builtin/chat/unread-inbox.ts +91 -0
- package/src/plugins/builtin/clinical-trials/client.ts +237 -0
- package/src/plugins/builtin/clinical-trials/index.tsx +74 -0
- package/src/plugins/builtin/clinical-trials/pane.tsx +225 -0
- package/src/plugins/builtin/clinical-trials/types.ts +2 -0
- package/src/plugins/builtin/cloud/askg/controller.ts +37 -6
- package/src/plugins/builtin/cloud/askg/executor.ts +59 -44
- package/src/plugins/builtin/cloud/askg/host.ts +8 -1
- package/src/plugins/builtin/cloud/askg/manifest.ts +29 -2
- package/src/plugins/builtin/cloud/askg/model.ts +47 -13
- package/src/plugins/builtin/cloud/askg/notes.ts +100 -0
- package/src/plugins/builtin/cloud/askg/pane.tsx +50 -14
- package/src/plugins/builtin/cloud/askg/protocol.ts +39 -1
- package/src/plugins/builtin/cloud/askg/result-budget.ts +156 -0
- package/src/plugins/builtin/cloud/askg/user-data.ts +89 -0
- package/src/plugins/builtin/cloud/auth-actions.tsx +55 -31
- package/src/plugins/builtin/cloud/plugin.tsx +4 -1
- package/src/plugins/builtin/cloud/team/pane-sections.tsx +10 -1
- package/src/plugins/builtin/cloud/team/pane.tsx +3 -2
- package/src/plugins/builtin/cloud/thesis/board-pane.tsx +2 -2
- package/src/plugins/builtin/cloud/thesis/detail.tsx +1 -1
- package/src/plugins/builtin/cloud/thesis/ticker-tab.tsx +2 -2
- package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +202 -0
- package/src/plugins/builtin/cloud/upgrade-command.ts +1 -1
- package/src/plugins/builtin/cloud/upgrade-dialog.tsx +29 -3
- package/src/plugins/builtin/cloud/upgrade-status-widget.tsx +1 -1
- package/src/plugins/builtin/cloud-tweets/table.tsx +1 -1
- package/src/plugins/builtin/comment-letters/client.ts +342 -0
- package/src/plugins/builtin/comment-letters/index.tsx +74 -0
- package/src/plugins/builtin/comment-letters/pane.tsx +295 -0
- package/src/plugins/builtin/comment-letters/types.ts +43 -0
- package/src/plugins/builtin/company-attention/app-rank.tsx +79 -0
- package/src/plugins/builtin/company-attention/client.ts +69 -0
- package/src/plugins/builtin/company-attention/evidence.ts +45 -0
- package/src/plugins/builtin/company-attention/headless.ts +47 -0
- package/src/plugins/builtin/company-attention/index.tsx +30 -0
- package/src/plugins/builtin/company-attention/model.ts +129 -0
- package/src/plugins/builtin/company-attention/pane.tsx +183 -0
- package/src/plugins/builtin/company-kpis/client.ts +92 -0
- package/src/plugins/builtin/company-kpis/headless.ts +32 -0
- package/src/plugins/builtin/company-kpis/index.tsx +26 -0
- package/src/plugins/builtin/company-kpis/model.ts +124 -0
- package/src/plugins/builtin/company-kpis/pane.tsx +226 -0
- package/src/plugins/builtin/company-kpis/related.ts +11 -0
- package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +44 -0
- package/src/plugins/builtin/composite-plugins.ts +34 -0
- package/src/plugins/builtin/correlation/history.ts +4 -1
- package/src/plugins/builtin/correlation/index.tsx +139 -80
- package/src/plugins/builtin/correlation/matrix/pair-chart.tsx +48 -0
- package/src/plugins/builtin/correlation/matrix/selection.ts +77 -0
- package/src/plugins/builtin/correlation/relationship/model.ts +2 -2
- package/src/plugins/builtin/cpi/client.ts +66 -0
- package/src/plugins/builtin/cpi/headless.ts +78 -0
- package/src/plugins/builtin/cpi/index.tsx +31 -0
- package/src/plugins/builtin/cpi/model.ts +198 -0
- package/src/plugins/builtin/cpi/pane.tsx +157 -0
- package/src/plugins/builtin/credit-documents/client.ts +99 -0
- package/src/plugins/builtin/credit-documents/detail.tsx +109 -0
- package/src/plugins/builtin/credit-documents/evidence.ts +64 -0
- package/src/plugins/builtin/credit-documents/gauge.tsx +55 -0
- package/src/plugins/builtin/credit-documents/headless.ts +40 -0
- package/src/plugins/builtin/credit-documents/index.tsx +19 -0
- package/src/plugins/builtin/credit-documents/model.ts +336 -0
- package/src/plugins/builtin/credit-documents/pane.tsx +185 -0
- package/src/plugins/builtin/custom-view/view-spec.ts +14 -2
- package/src/plugins/builtin/debt-maturities/pane.tsx +1 -0
- package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
- package/src/plugins/builtin/doe/client.ts +73 -0
- package/src/plugins/builtin/doe/headless.ts +93 -0
- package/src/plugins/builtin/doe/index.tsx +41 -0
- package/src/plugins/builtin/doe/model.ts +215 -0
- package/src/plugins/builtin/doe/pane.tsx +148 -0
- package/src/plugins/builtin/doe/range-bar.tsx +45 -0
- package/src/plugins/builtin/earnings/board.tsx +3 -0
- package/src/plugins/builtin/earnings/index.tsx +2 -0
- package/src/plugins/builtin/earnings-calls/pane.tsx +7 -1
- package/src/plugins/builtin/earnings-calls/transcript-view.tsx +7 -7
- package/src/plugins/builtin/earnings-ripple/client.ts +60 -0
- package/src/plugins/builtin/earnings-ripple/headless.ts +35 -0
- package/src/plugins/builtin/earnings-ripple/index.tsx +35 -0
- package/src/plugins/builtin/earnings-ripple/model.ts +98 -0
- package/src/plugins/builtin/earnings-ripple/pane.tsx +103 -0
- package/src/plugins/builtin/equity-screener/client.ts +1 -1
- package/src/plugins/builtin/equity-screener/criterion-editor.tsx +1 -1
- package/src/plugins/builtin/equity-screener/model.ts +74 -9
- package/src/plugins/builtin/equity-screener/pane.tsx +2 -0
- package/src/plugins/builtin/estimate-revisions/pane.tsx +3 -0
- package/src/plugins/builtin/executives/pane.tsx +7 -5
- package/src/plugins/builtin/exposure/charts.tsx +66 -0
- package/src/plugins/builtin/exposure/client.ts +53 -0
- package/src/plugins/builtin/exposure/drivers.ts +29 -0
- package/src/plugins/builtin/exposure/editor.tsx +61 -0
- package/src/plugins/builtin/exposure/evidence.ts +39 -0
- package/src/plugins/builtin/exposure/headless.ts +51 -0
- package/src/plugins/builtin/exposure/holdings.ts +56 -0
- package/src/plugins/builtin/exposure/index.tsx +13 -0
- package/src/plugins/builtin/exposure/model.ts +104 -0
- package/src/plugins/builtin/exposure/pane.tsx +247 -0
- package/src/plugins/builtin/fear-greed/charts.tsx +1 -1
- package/src/plugins/builtin/fear-greed/pane.tsx +2 -9
- package/src/plugins/builtin/filing-events/pane.tsx +2 -1
- package/src/plugins/builtin/gpu/board.tsx +163 -0
- package/src/plugins/builtin/gpu/changes.tsx +94 -0
- package/src/plugins/builtin/gpu/client.ts +130 -0
- package/src/plugins/builtin/gpu/equities.tsx +112 -0
- package/src/plugins/builtin/gpu/headless.ts +67 -0
- package/src/plugins/builtin/gpu/history.tsx +140 -0
- package/src/plugins/builtin/gpu/index.tsx +18 -0
- package/src/plugins/builtin/gpu/ladder.tsx +127 -0
- package/src/plugins/builtin/gpu/model.ts +317 -0
- package/src/plugins/builtin/gpu/pane.tsx +63 -0
- package/src/plugins/builtin/help/index.tsx +19 -2
- package/src/plugins/builtin/holders/pane.tsx +5 -9
- package/src/plugins/builtin/insider/detail.tsx +119 -0
- package/src/plugins/builtin/insider/display.ts +221 -0
- package/src/plugins/builtin/insider/headless.ts +24 -3
- package/src/plugins/builtin/insider/index.tsx +206 -108
- package/src/plugins/builtin/insider/insider-data.ts +31 -3
- package/src/plugins/builtin/insider/model.ts +53 -4
- package/src/plugins/builtin/insider/table-model.ts +226 -0
- package/src/plugins/builtin/insider/table.tsx +79 -0
- package/src/plugins/builtin/iv-history/screen-pane.tsx +1 -1
- package/src/plugins/builtin/jobs/pane.tsx +14 -7
- package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
- package/src/plugins/builtin/macro-day/client.ts +50 -0
- package/src/plugins/builtin/macro-day/headless.ts +51 -0
- package/src/plugins/builtin/macro-day/index.tsx +21 -0
- package/src/plugins/builtin/macro-day/model.ts +137 -0
- package/src/plugins/builtin/macro-day/pane.tsx +118 -0
- package/src/plugins/builtin/macro-day/releases.ts +58 -0
- package/src/plugins/builtin/market-halts/model.ts +30 -17
- package/src/plugins/builtin/market-halts/pane.tsx +15 -21
- package/src/plugins/builtin/market-heatmap/data.ts +1 -1
- package/src/plugins/builtin/market-heatmap/index.tsx +198 -50
- package/src/plugins/builtin/market-heatmap/portfolio.ts +220 -0
- package/src/plugins/builtin/market-movers/index.tsx +1 -0
- package/src/plugins/builtin/market-movers/session-body.tsx +5 -2
- package/src/plugins/builtin/mna/distress/client.ts +326 -0
- package/src/plugins/builtin/mna/distress/detail.tsx +247 -0
- package/src/plugins/builtin/mna/distress/headless.ts +257 -0
- package/src/plugins/builtin/mna/distress/model.ts +427 -0
- package/src/plugins/builtin/mna/distress/pane.tsx +606 -0
- package/src/plugins/builtin/mna/index.tsx +15 -0
- package/src/plugins/builtin/mna/pane.tsx +42 -1
- package/src/plugins/builtin/news/index.tsx +62 -16
- package/src/plugins/builtin/news/wire/filter-articles.ts +54 -0
- package/src/plugins/builtin/news/wire/index.tsx +37 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +29 -13
- package/src/plugins/builtin/news/wire/mutes.ts +253 -0
- package/src/plugins/builtin/news/wire/news/command-bar-search.ts +117 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +4 -0
- package/src/plugins/builtin/news/wire/news/list-search.ts +34 -0
- package/src/plugins/builtin/news/wire/news/pop-out.tsx +158 -0
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +64 -19
- package/src/plugins/builtin/openfda/client.ts +300 -0
- package/src/plugins/builtin/openfda/index.tsx +71 -0
- package/src/plugins/builtin/openfda/pane.tsx +382 -0
- package/src/plugins/builtin/openfda/types.ts +76 -0
- package/src/plugins/builtin/options/footer.ts +1 -0
- package/src/plugins/builtin/options/view.tsx +1 -1
- package/src/plugins/builtin/options-calculator/pane.tsx +0 -1
- package/src/plugins/builtin/options-positioning/index.tsx +4 -2
- package/src/plugins/builtin/options-positioning/pane.tsx +1 -1
- package/src/plugins/builtin/pe-band/client.ts +61 -0
- package/src/plugins/builtin/pe-band/headless.ts +51 -0
- package/src/plugins/builtin/pe-band/index.tsx +20 -0
- package/src/plugins/builtin/pe-band/model.ts +267 -0
- package/src/plugins/builtin/pe-band/pane.tsx +159 -0
- package/src/plugins/builtin/perps/client.ts +66 -0
- package/src/plugins/builtin/perps/equity-row.tsx +28 -0
- package/src/plugins/builtin/perps/headless.ts +34 -0
- package/src/plugins/builtin/perps/index.tsx +14 -0
- package/src/plugins/builtin/perps/model.ts +58 -0
- package/src/plugins/builtin/perps/pane.tsx +185 -0
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +0 -1
- package/src/plugins/builtin/portfolio-list/cli/render.ts +177 -87
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +6 -20
- package/src/plugins/builtin/portfolio-list/command-bar.ts +6 -2
- package/src/plugins/builtin/portfolio-list/headless.ts +237 -0
- package/src/plugins/builtin/portfolio-list/index.tsx +2 -0
- package/src/plugins/builtin/portfolio-list/mutations.ts +70 -1
- package/src/plugins/builtin/portfolio-list/pane/data.ts +17 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +88 -5
- package/src/plugins/builtin/portfolio-list/pane/remove-ticker.ts +85 -0
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +18 -12
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +6 -0
- package/src/plugins/builtin/power/client.ts +100 -0
- package/src/plugins/builtin/power/detail.tsx +82 -0
- package/src/plugins/builtin/power/evidence.ts +56 -0
- package/src/plugins/builtin/power/generation.tsx +37 -0
- package/src/plugins/builtin/power/headless.ts +43 -0
- package/src/plugins/builtin/power/index.tsx +16 -0
- package/src/plugins/builtin/power/model.ts +89 -0
- package/src/plugins/builtin/power/pane.tsx +209 -0
- package/src/plugins/builtin/power/query.ts +25 -0
- package/src/plugins/builtin/relative-rotation/pane.tsx +3 -3
- package/src/plugins/builtin/research/analyst-pane.tsx +1 -1
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +13 -4
- package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +11 -9
- package/src/plugins/builtin/research-search/pane.tsx +21 -7
- package/src/plugins/builtin/revenue-breakdown/pane.tsx +1 -1
- package/src/plugins/builtin/reverse-dcf/client.ts +35 -0
- package/src/plugins/builtin/reverse-dcf/headless.ts +44 -0
- package/src/plugins/builtin/reverse-dcf/index.tsx +20 -0
- package/src/plugins/builtin/reverse-dcf/model.ts +87 -0
- package/src/plugins/builtin/reverse-dcf/pane.tsx +107 -0
- package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
- package/src/plugins/builtin/scanner/denied.tsx +4 -2
- package/src/plugins/builtin/scanner/feed.ts +1 -0
- package/src/plugins/builtin/scanner/flow-pane.tsx +3 -2
- package/src/plugins/builtin/scanner/hilo-pane.tsx +1 -1
- package/src/plugins/builtin/seasonality/client.ts +4 -1
- package/src/plugins/builtin/sec/command-bar-search.ts +217 -0
- package/src/plugins/builtin/sec/forms.ts +54 -0
- package/src/plugins/builtin/sec/headless.ts +31 -5
- package/src/plugins/builtin/sec/index.tsx +67 -11
- package/src/plugins/builtin/sec/model.ts +35 -5
- package/src/plugins/builtin/sectors/headless.ts +1 -0
- package/src/plugins/builtin/sectors/index.tsx +72 -26
- package/src/plugins/builtin/shared/cloud-upgrade.ts +143 -39
- package/src/plugins/builtin/shared/pro-feature-wall.tsx +8 -4
- package/src/plugins/builtin/shared/research-cells.tsx +107 -0
- package/src/plugins/builtin/shared/series-list-detail.tsx +5 -1
- package/src/plugins/builtin/shared/ticker-request.ts +1 -1
- package/src/plugins/builtin/shared/wall-teaser-catalog.ts +80 -0
- package/src/plugins/builtin/shared/wall-teaser.tsx +192 -0
- package/src/plugins/builtin/shared/wall-view-lifecycle.ts +43 -0
- package/src/plugins/builtin/short-interest/index.tsx +31 -0
- package/src/plugins/builtin/short-interest/watch-client.ts +70 -0
- package/src/plugins/builtin/short-interest/watch-headless.ts +49 -0
- package/src/plugins/builtin/short-interest/watch-model.ts +185 -0
- package/src/plugins/builtin/short-interest/watch-pane.tsx +169 -0
- package/src/plugins/builtin/short-volume/pane.tsx +1 -1
- package/src/plugins/builtin/social-mentions/pane.tsx +1 -1
- package/src/plugins/builtin/supply-chain/client.ts +104 -0
- package/src/plugins/builtin/supply-chain/disclosure-quote.tsx +17 -0
- package/src/plugins/builtin/supply-chain/evidence.ts +88 -0
- package/src/plugins/builtin/supply-chain/flow-layout.ts +133 -0
- package/src/plugins/builtin/supply-chain/flow.tsx +323 -0
- package/src/plugins/builtin/supply-chain/graph-client.ts +83 -0
- package/src/plugins/builtin/supply-chain/graph-evidence.ts +25 -0
- package/src/plugins/builtin/supply-chain/graph-model.ts +45 -0
- package/src/plugins/builtin/supply-chain/graph-pane.tsx +218 -0
- package/src/plugins/builtin/supply-chain/graph.tsx +163 -0
- package/src/plugins/builtin/supply-chain/headless.ts +105 -0
- package/src/plugins/builtin/supply-chain/index.tsx +17 -0
- package/src/plugins/builtin/supply-chain/model.ts +145 -0
- package/src/plugins/builtin/supply-chain/pane.tsx +276 -0
- package/src/plugins/builtin/supply-chain/row-evidence.tsx +96 -0
- package/src/plugins/builtin/supply-chain/trust.ts +34 -0
- package/src/plugins/builtin/themes/client.ts +39 -0
- package/src/plugins/builtin/themes/headless.ts +38 -0
- package/src/plugins/builtin/themes/model.ts +92 -0
- package/src/plugins/builtin/themes/pane.tsx +125 -0
- package/src/plugins/builtin/thirteenf/pane.tsx +6 -15
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +5 -6
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +201 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +327 -0
- package/src/plugins/builtin/ticker-detail/financials/period-end-history.ts +5 -2
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +60 -12
- package/src/plugins/builtin/ticker-detail/financials/tab.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/headless.ts +7 -2
- package/src/plugins/builtin/ticker-detail/index.tsx +22 -0
- package/src/plugins/builtin/ticker-detail/overview/components.tsx +53 -30
- package/src/plugins/builtin/ticker-detail/overview/pane.tsx +2 -0
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +112 -47
- package/src/plugins/builtin/ticker-detail/pane.tsx +50 -6
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +21 -42
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +3 -0
- package/src/plugins/builtin/time-sales/pane.tsx +1 -1
- package/src/plugins/builtin/world-indices/indices.ts +1 -0
- package/src/plugins/catalog-browser.ts +40 -0
- package/src/plugins/catalog-manifest.ts +3 -0
- package/src/plugins/catalog-ui.ts +6 -0
- package/src/plugins/current-target.ts +0 -9
- package/src/public/__snapshots__/public-api.test.ts.snap +1 -0
- package/src/renderers/browser/cloud-transport.ts +66 -1
- package/src/renderers/browser/main.tsx +2 -0
- package/src/renderers/dom/data-table/row.tsx +2 -0
- package/src/renderers/dom/desktop/controls.tsx +38 -25
- package/src/renderers/dom/desktop/icons.tsx +8 -0
- package/src/renderers/dom/host/chart-surface.tsx +36 -2
- package/src/renderers/dom/host/focus-scope.ts +11 -1
- package/src/renderers/dom/styles.css +28 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +3 -2
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
- package/src/renderers/electrobun/view/http-fetch.ts +2 -0
- package/src/renderers/electrobun/view/main.tsx +3 -1
- package/src/renderers/electrobun/view/rpc-timeout.ts +59 -0
- package/src/renderers/opentui/start.tsx +3 -0
- package/src/shares/api.ts +3 -1
- package/src/sources/gloomberb-cloud/index.ts +47 -13
- package/src/sources/history-result.ts +1 -0
- package/src/sources/history-retention.ts +16 -0
- package/src/sources/provider-router/history.ts +4 -2
- package/src/sources/sec-edgar/identity.ts +36 -0
- package/src/sources/sec-edgar.ts +1 -34
- package/src/state/app/context/index.tsx +8 -0
- package/src/sync/core-contributors.ts +20 -2
- package/src/telemetry/attention-counts.ts +164 -0
- package/src/telemetry/live-config.ts +7 -0
- package/src/test-support/rpc-loopback.ts +7 -3
- package/src/test-support/shell-pane-keys.tsx +34 -0
- package/src/tickers/instrument-kind.ts +12 -6
- package/src/tickers/open-target.ts +1 -1
- package/src/tickers/search/asset-classes.ts +91 -0
- package/src/tickers/search/index.ts +62 -15
- package/src/tickers/search/ranking.ts +22 -3
- package/src/tickers/search/upsert.ts +4 -1
- package/src/time-series/live-bars.ts +8 -3
- package/src/time-series/resolve.ts +224 -17
- package/src/time-series/spec.ts +1 -0
- package/src/time-series/types.ts +9 -1
- package/src/types/config.ts +23 -1
- package/src/types/data-provider.ts +2 -0
- package/src/types/headless.ts +17 -0
- package/src/types/price-history.ts +2 -0
- package/src/ui/host.tsx +6 -0
- package/src/utils/double-escape-close.ts +20 -0
- package/src/utils/exchanges.ts +1 -0
- package/src/utils/html-entities.ts +15 -0
- package/src/utils/http-proxy-response.ts +44 -7
- package/src/utils/plugin-proxy-hosts.json +3 -0
- package/src/utils/price-history.ts +35 -1
- package/src/utils/sec.ts +21 -2
- package/src/utils/text-wrap.ts +13 -8
- package/src/utils/zoned-date-time.ts +67 -0
|
@@ -172,6 +172,34 @@ body.gloom-dragging {
|
|
|
172
172
|
opacity: 1;
|
|
173
173
|
pointer-events: auto;
|
|
174
174
|
}
|
|
175
|
+
/* Message text can be selected and copied, like any chat. */
|
|
176
|
+
[data-gloom-role="chat-message-body"] {
|
|
177
|
+
-webkit-user-select: text;
|
|
178
|
+
user-select: text;
|
|
179
|
+
}
|
|
180
|
+
[data-gloom-role="chat-message-image"]:focus-visible {
|
|
181
|
+
outline: 2px solid var(--gloom-border-focused, currentColor);
|
|
182
|
+
outline-offset: 1px;
|
|
183
|
+
}
|
|
184
|
+
/* An upload whose progress the transport cannot report. */
|
|
185
|
+
.gloom-chat-upload-indeterminate {
|
|
186
|
+
animation: gloom-chat-upload-slide 1.2s ease-in-out infinite;
|
|
187
|
+
}
|
|
188
|
+
@keyframes gloom-chat-upload-slide {
|
|
189
|
+
from { transform: translateX(-100%); }
|
|
190
|
+
to { transform: translateX(250%); }
|
|
191
|
+
}
|
|
192
|
+
@media (prefers-reduced-motion: reduce) {
|
|
193
|
+
.gloom-chat-upload-indeterminate {
|
|
194
|
+
animation: none;
|
|
195
|
+
width: 100% !important;
|
|
196
|
+
opacity: 0.5;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/* The image viewer may use the whole window, not a dialog's usual width. */
|
|
200
|
+
.gloom-dialog:has([data-gloom-role="chat-image-lightbox"]) {
|
|
201
|
+
max-width: calc(100vw - 48px);
|
|
202
|
+
}
|
|
175
203
|
[data-gloom-role="status-bar"] {
|
|
176
204
|
flex: 0 0 var(--cell-h);
|
|
177
205
|
height: var(--cell-h);
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from "../shared/protocol";
|
|
15
15
|
import { decodeRpcResponse, decodeRpcValue, encodeRpcValue } from "../shared/rpc-codec";
|
|
16
16
|
import { subscribeCapability, type CapabilitySubscriptionOptions } from "./capability-subscription";
|
|
17
|
+
import { nameRpcTimeout } from "./rpc-timeout";
|
|
17
18
|
import type { RemoteControlRequest, RemoteControlResponse } from "../../../remote/types";
|
|
18
19
|
|
|
19
20
|
type BackendMessages = ElectrobunDesktopRpcSchema["webview"]["messages"];
|
|
@@ -130,10 +131,10 @@ export async function backendRequest(
|
|
|
130
131
|
payload: unknown = null,
|
|
131
132
|
): Promise<unknown> {
|
|
132
133
|
await waitForBridgeReady();
|
|
133
|
-
const result = await rpc.request["backend.request"]({
|
|
134
|
+
const result = await nameRpcTimeout(method, payload, () => rpc.request["backend.request"]({
|
|
134
135
|
method,
|
|
135
136
|
payload: encodeRpcValue(payload),
|
|
136
|
-
});
|
|
137
|
+
}));
|
|
137
138
|
return decodeRpcResponse(result);
|
|
138
139
|
}
|
|
139
140
|
|
|
@@ -437,7 +437,7 @@ function ShotPane({ payload, registry }: { payload: DesktopPaneShotPayload; regi
|
|
|
437
437
|
const width = payload.widthCells;
|
|
438
438
|
const height = payload.heightCells;
|
|
439
439
|
return (
|
|
440
|
-
<PaneShotFrame paneId={instance.instanceId} title={title} width={width} height={height} preserveStatus={
|
|
440
|
+
<PaneShotFrame paneId={instance.instanceId} title={title} width={width} height={height} preserveStatus={["time-sales", "gpu", "hiring", "apps", "catalysts", "litigation", "perps"].includes(pane.id)}>
|
|
441
441
|
{(bodyFrame) => <PaneContent
|
|
442
442
|
component={pane.component}
|
|
443
443
|
paneId={instance.instanceId}
|
|
@@ -57,6 +57,8 @@ async function electrobunCloudApiFetch(url: string, init?: RequestInit): Promise
|
|
|
57
57
|
const path = new URL(url).pathname;
|
|
58
58
|
const timeoutMs = path.startsWith("/market/")
|
|
59
59
|
? CLOUD_MARKET_HTTP_TIMEOUT_MS
|
|
60
|
+
: path === "/telemetry/attention"
|
|
61
|
+
? 5_000
|
|
60
62
|
: path.endsWith("/feedback") && init?.method === "POST"
|
|
61
63
|
? FEEDBACK_SUBMIT_BUN_TIMEOUT_MS
|
|
62
64
|
: undefined;
|
|
@@ -43,8 +43,9 @@ import { remoteNotesFilesIO, setNotesFilesIO } from "../../../plugins/builtin/no
|
|
|
43
43
|
import { NOTES_FILES_CAPABILITY_ID } from "../../../capabilities";
|
|
44
44
|
import { loadOfficialPluginIds } from "../../../plugins/builtin/plugin-marketplace/feed";
|
|
45
45
|
import { crashReportsEnabled, installCrashReporter, reportCrash } from "../../../telemetry/crash-reports";
|
|
46
|
-
import { installWindowCrashListeners } from "../../../telemetry/crash-reports-dom";
|
|
46
|
+
import { browserDoNotTrack, installWindowCrashListeners } from "../../../telemetry/crash-reports-dom";
|
|
47
47
|
import { currentTelemetryConfig } from "../../../telemetry/live-config";
|
|
48
|
+
import { attentionCountsEnabled, installAttentionCounter } from "../../../telemetry/attention-counts";
|
|
48
49
|
import { installUsageCounter, usageCountsEnabled } from "../../../telemetry/usage-counts";
|
|
49
50
|
import { installWindowUsageFlush } from "../../../telemetry/usage-counts-dom";
|
|
50
51
|
|
|
@@ -135,6 +136,7 @@ async function boot() {
|
|
|
135
136
|
getInstallId: () => init.telemetry.installId,
|
|
136
137
|
officialPluginIds: loadOfficialPluginIds,
|
|
137
138
|
});
|
|
139
|
+
installAttentionCounter(() => !init.telemetry.optedOut && !browserDoNotTrack() && attentionCountsEnabled(currentTelemetryConfig(init.config)));
|
|
138
140
|
installElectrobunCapabilityStreamClient();
|
|
139
141
|
installFocusScopeRelease();
|
|
140
142
|
installElectrobunWindowFullscreenTracking();
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { getCloudApiBaseUrl } from "../../../api-client/request";
|
|
2
|
+
import type { DesktopBackendRequestMethod } from "../shared/protocol";
|
|
3
|
+
|
|
4
|
+
/** What Electrobun's request timer rejects with: no method, request id or caller. */
|
|
5
|
+
const ELECTROBUN_REQUEST_TIMEOUT_MESSAGE = "RPC request timed out.";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Runs one `backend.request` and, when Electrobun's request timer gives up on
|
|
9
|
+
* it, rethrows the timeout with the request named. Every other failure passes
|
|
10
|
+
* through untouched.
|
|
11
|
+
*
|
|
12
|
+
* The library's error is the same for every method, so crash reports could not
|
|
13
|
+
* tell a stalled update check from a stalled fetch. The name is the method,
|
|
14
|
+
* plus the capability operation for `capability.invoke`. An HTTP request to
|
|
15
|
+
* the Gloom API adds its host and first path segment; any other URL can come
|
|
16
|
+
* from the user's config (a broker gateway, a feed, a plugin endpoint) and is
|
|
17
|
+
* only called external. Never the payload, rest of the path, query or headers.
|
|
18
|
+
* Elapsed time is rounded to 10 s because crash reports are deduplicated per
|
|
19
|
+
* session on the message.
|
|
20
|
+
*/
|
|
21
|
+
export async function nameRpcTimeout<T>(
|
|
22
|
+
method: DesktopBackendRequestMethod,
|
|
23
|
+
payload: unknown,
|
|
24
|
+
request: () => Promise<T>,
|
|
25
|
+
): Promise<T> {
|
|
26
|
+
const startedAt = performance.now();
|
|
27
|
+
try {
|
|
28
|
+
return await request();
|
|
29
|
+
} catch (error) {
|
|
30
|
+
if (!(error instanceof Error) || error.message !== ELECTROBUN_REQUEST_TIMEOUT_MESSAGE) throw error;
|
|
31
|
+
const seconds = Math.round((performance.now() - startedAt) / 10_000) * 10;
|
|
32
|
+
throw new Error(`RPC request timed out: ${describeRequest(method, payload)} after ~${seconds}s`, { cause: error });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function describeRequest(method: DesktopBackendRequestMethod, payload: unknown): string {
|
|
37
|
+
if (!payload || typeof payload !== "object") return method;
|
|
38
|
+
const { capabilityId, operationId, url } = payload as Record<string, unknown>;
|
|
39
|
+
if (method === "capability.invoke" && typeof capabilityId === "string" && typeof operationId === "string") {
|
|
40
|
+
return `${method} ${capabilityId}.${operationId}`;
|
|
41
|
+
}
|
|
42
|
+
if (method === "http.fetch" || method === "http.stream.open") {
|
|
43
|
+
return `${method} ${gloomApiArea(url) ?? "external"}`;
|
|
44
|
+
}
|
|
45
|
+
return method;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** `api.gloom.sh/market` for a Gloom API URL, null for anything else. */
|
|
49
|
+
function gloomApiArea(url: unknown): string | null {
|
|
50
|
+
if (typeof url !== "string") return null;
|
|
51
|
+
try {
|
|
52
|
+
const parsed = new URL(url);
|
|
53
|
+
if (parsed.origin !== new URL(getCloudApiBaseUrl()).origin) return null;
|
|
54
|
+
const segment = parsed.pathname.split("/")[1];
|
|
55
|
+
return segment ? `${parsed.host}/${segment}` : parsed.host;
|
|
56
|
+
} catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
} from "../../telemetry/crash-reports-node";
|
|
38
38
|
import { currentTelemetryConfig } from "../../telemetry/live-config";
|
|
39
39
|
import { flushUsageCounts, installUsageCounter, usageCountsEnabled } from "../../telemetry/usage-counts";
|
|
40
|
+
import { attentionCountsEnabled, installAttentionCounter } from "../../telemetry/attention-counts";
|
|
40
41
|
|
|
41
42
|
// Declared here rather than sniffed: the desktop view and the hosted browser
|
|
42
43
|
// app are both browser contexts but differ in what plugins may do.
|
|
@@ -119,8 +120,10 @@ export async function startOpenTuiApp({ externalPlugins, cliLaunchRequest }: Sta
|
|
|
119
120
|
getInstallId: () => readOrCreateInstallId(config.dataDir),
|
|
120
121
|
officialPluginIds: loadOfficialPluginIds,
|
|
121
122
|
});
|
|
123
|
+
const stopAttention = installAttentionCounter(() => attentionCountsEnabled(currentTelemetryConfig(config), process.env));
|
|
122
124
|
host = await measurePerfAsync("startup.opentui.create-host", () => createOpenTuiHost());
|
|
123
125
|
const renderer = host.renderer;
|
|
126
|
+
renderer.once("destroy", stopAttention);
|
|
124
127
|
// The renderer already listens for uncaught errors and keeps the process
|
|
125
128
|
// running; these listeners live exactly as long as it does, so nothing
|
|
126
129
|
// changes about when the process exits.
|
package/src/shares/api.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { apiClient } from "../api-client";
|
|
2
1
|
import { ApiRequestError } from "../api-client/errors";
|
|
3
2
|
import { parseSharePayload, type SharePayload } from "./payload";
|
|
4
3
|
|
|
@@ -52,6 +51,9 @@ export async function createShare(payload: SharePayload, fetchImpl?: ShareFetch)
|
|
|
52
51
|
body = await readJson(response);
|
|
53
52
|
} else {
|
|
54
53
|
try {
|
|
54
|
+
// Public share pages only read/delete through fetch. Load the signed-in
|
|
55
|
+
// client when an interactive app creates a share, not on every public view.
|
|
56
|
+
const { apiClient } = await import("../api-client");
|
|
55
57
|
body = await apiClient.createTerminalShare(validated);
|
|
56
58
|
} catch (error) {
|
|
57
59
|
if (error instanceof ApiRequestError && error.status === 401) throw new Error("Sign in to Gloom Cloud to share.");
|
|
@@ -38,7 +38,7 @@ import type { NewsArticle, NewsQuery } from "../../types/news-source";
|
|
|
38
38
|
import { normalizeNewsFeed } from "../../news/news-model";
|
|
39
39
|
import { resolveCurrencyUnit } from "../../utils/currency-units";
|
|
40
40
|
import { canonicalExchange, canonicalTickerKey, parsePublicTickerKey } from "../../utils/exchanges";
|
|
41
|
-
import { normalizePriceHistory } from "../../utils/price-history";
|
|
41
|
+
import { normalizePriceHistory, priceHistoryIntervalMs, reachesLatestSettledSession } from "../../utils/price-history";
|
|
42
42
|
import { createProviderMiss } from "../provider-errors";
|
|
43
43
|
import { publicListingTarget } from "../listing-target";
|
|
44
44
|
import { canonicalHistoryInterval, HistoryRetentionError, parseHistoryRecoveryCandidate, parseHistoryRetention, type HistoryRetention } from "../history-retention";
|
|
@@ -132,6 +132,28 @@ function isStaleCloudResponse(response: CloudMarketResponse<unknown>): boolean {
|
|
|
132
132
|
return response.stale === true || response.providerMeta?.stale === true;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
/**
|
|
136
|
+
* History with the pre-market and after-hours bars when the chart asks for
|
|
137
|
+
* them. A backend that cannot serve them answers the regular session, and one
|
|
138
|
+
* that ignores the request answers it without saying it served them.
|
|
139
|
+
*/
|
|
140
|
+
async function loadCloudHistory(
|
|
141
|
+
interval: string,
|
|
142
|
+
load: (session?: "extended") => Promise<CloudMarketResponse<CloudPricePointPayload[]>>,
|
|
143
|
+
context: MarketDataRequestContext | undefined,
|
|
144
|
+
): Promise<{ response: CloudMarketResponse<CloudPricePointPayload[]>; extendedHours: boolean }> {
|
|
145
|
+
if (context?.historySession !== "extended" || !/^\d+(min|h)$/i.test(interval)) return { response: await load(), extendedHours: false };
|
|
146
|
+
try {
|
|
147
|
+
const response = await load("extended");
|
|
148
|
+
if (response.status === "success" || response.status === "partial" || response.status === "empty") {
|
|
149
|
+
return { response, extendedHours: response.session === "extended" };
|
|
150
|
+
}
|
|
151
|
+
} catch {
|
|
152
|
+
// The regular session below.
|
|
153
|
+
}
|
|
154
|
+
return { response: await load(), extendedHours: false };
|
|
155
|
+
}
|
|
156
|
+
|
|
135
157
|
function mapCloudPriceHistory(
|
|
136
158
|
response: CloudMarketResponse<CloudPricePointPayload[]>,
|
|
137
159
|
ticker: string,
|
|
@@ -139,11 +161,17 @@ function mapCloudPriceHistory(
|
|
|
139
161
|
interval: string,
|
|
140
162
|
requestedStart: Date,
|
|
141
163
|
requestedEnd?: Date,
|
|
164
|
+
extendedHours = false,
|
|
142
165
|
): PriceHistoryResult {
|
|
143
166
|
const coverageStart = parseHistoryCoverageStart(response.coverage);
|
|
144
167
|
if (response.status === "empty" && coverageStart
|
|
145
168
|
&& requestedStart.getTime() < Date.parse(coverageStart)) throw new HistoryCoverageError(coverageStart);
|
|
146
|
-
|
|
169
|
+
// The server marks a cached copy it could not refresh in time. Bars that
|
|
170
|
+
// reach the venue's latest settled session still answer, and the router's
|
|
171
|
+
// own current-window check applies to them as to any other copy. Older bars,
|
|
172
|
+
// or a stale answer without bars, are a miss.
|
|
173
|
+
const stale = isStaleCloudResponse(response);
|
|
174
|
+
if (stale && response.status !== "success" && response.status !== "partial") {
|
|
147
175
|
throw createProviderMiss(`Cloud chart data is stale for ${ticker}`);
|
|
148
176
|
}
|
|
149
177
|
if (response.status === "unsupported" && response.reasonCode === "HISTORY_RETENTION" && requestedEnd) {
|
|
@@ -167,6 +195,9 @@ function mapCloudPriceHistory(
|
|
|
167
195
|
`Cloud chart data is unavailable for ${ticker}`,
|
|
168
196
|
).map((point) => mapPricePoint(point, divisor, exchange)),
|
|
169
197
|
);
|
|
198
|
+
if (stale && !reachesLatestSettledSession(points, Date.now(), { exchange, intervalMs: priceHistoryIntervalMs(interval) })) {
|
|
199
|
+
throw createProviderMiss(`Cloud chart data is stale for ${ticker}`);
|
|
200
|
+
}
|
|
170
201
|
if (
|
|
171
202
|
/^\d+(min|h)$/i.test(interval)
|
|
172
203
|
&& hasMalformedIntradayHistory(points)
|
|
@@ -193,6 +224,7 @@ function mapCloudPriceHistory(
|
|
|
193
224
|
resolution,
|
|
194
225
|
...(session && matchingSource && matchingIdentity ? { session } : {}),
|
|
195
226
|
...(coverageStart ? { coverageStart } : {}),
|
|
227
|
+
...(extendedHours ? { extendedHours: true } : {}),
|
|
196
228
|
};
|
|
197
229
|
}
|
|
198
230
|
|
|
@@ -542,11 +574,11 @@ export class GloomberbCloudProvider implements AssetDataProvider {
|
|
|
542
574
|
const target = cloudInstrumentTarget(ticker, exchange);
|
|
543
575
|
exchange = target.exchange ?? "";
|
|
544
576
|
const request = toHistoryRequest(range);
|
|
545
|
-
const response = await withCloudFallback(
|
|
546
|
-
() => apiClient.getCloudHistory(target.symbol, exchange, request),
|
|
577
|
+
const { response, extendedHours } = await withCloudFallback(
|
|
578
|
+
() => loadCloudHistory(request.interval, (session) => apiClient.getCloudHistory(target.symbol, exchange, { ...request, ...(session ? { session } : {}) }), _context),
|
|
547
579
|
`Cloud chart data is unavailable for ${ticker}`,
|
|
548
580
|
);
|
|
549
|
-
return mapCloudPriceHistory(response, target.symbol, exchange, request.interval, subtractTimeRange(new Date(), range));
|
|
581
|
+
return mapCloudPriceHistory(response, target.symbol, exchange, request.interval, subtractTimeRange(new Date(), range), undefined, extendedHours);
|
|
550
582
|
}
|
|
551
583
|
|
|
552
584
|
async getPriceHistoryForResolution(
|
|
@@ -570,15 +602,16 @@ export class GloomberbCloudProvider implements AssetDataProvider {
|
|
|
570
602
|
const endDate = new Date();
|
|
571
603
|
const startDate = subtractTimeRange(endDate, bufferRange);
|
|
572
604
|
const includeTime = /^\d+(min|h)$/i.test(interval);
|
|
573
|
-
const response = await withCloudFallback(
|
|
574
|
-
() => apiClient.getCloudHistory(target.symbol, exchange, {
|
|
605
|
+
const { response, extendedHours } = await withCloudFallback(
|
|
606
|
+
() => loadCloudHistory(interval, (session) => apiClient.getCloudHistory(target.symbol, exchange, {
|
|
575
607
|
interval,
|
|
576
608
|
startDate: formatCloudDateTime(startDate, includeTime, exchange),
|
|
577
609
|
endDate: formatCloudDateTime(endDate, includeTime, exchange),
|
|
578
|
-
|
|
610
|
+
...(session ? { session } : {}),
|
|
611
|
+
}), _context),
|
|
579
612
|
`Cloud chart data is unavailable for ${ticker}`,
|
|
580
613
|
);
|
|
581
|
-
return mapCloudPriceHistory(response, target.symbol, exchange, interval, startDate, endDate);
|
|
614
|
+
return mapCloudPriceHistory(response, target.symbol, exchange, interval, startDate, endDate, extendedHours);
|
|
582
615
|
}
|
|
583
616
|
|
|
584
617
|
async getDetailedPriceHistory(
|
|
@@ -601,16 +634,17 @@ export class GloomberbCloudProvider implements AssetDataProvider {
|
|
|
601
634
|
const interval = toCloudInterval(barSize);
|
|
602
635
|
const includeTime = /^\d+(min|h)$/i.test(interval);
|
|
603
636
|
const historyRecovery = cloudHistoryRecovery(_context, ticker, target.symbol, exchange, interval, startDate, endDate);
|
|
604
|
-
const response = await withCloudFallback(
|
|
605
|
-
() => apiClient.getCloudHistory(target.symbol, exchange, {
|
|
637
|
+
const { response, extendedHours } = await withCloudFallback(
|
|
638
|
+
() => loadCloudHistory(interval, (session) => apiClient.getCloudHistory(target.symbol, exchange, {
|
|
606
639
|
interval,
|
|
607
640
|
startDate: formatCloudDateTime(startDate, includeTime, exchange),
|
|
608
641
|
endDate: formatCloudDateTime(endDate, includeTime, exchange),
|
|
609
642
|
...(historyRecovery ? { historyRecovery } : {}),
|
|
610
|
-
|
|
643
|
+
...(session ? { session } : {}),
|
|
644
|
+
}), historyRecovery ? undefined : _context),
|
|
611
645
|
`Cloud detailed chart history is unavailable for ${ticker}`,
|
|
612
646
|
);
|
|
613
|
-
return mapCloudPriceHistory(response, target.symbol, exchange, interval, startDate, endDate);
|
|
647
|
+
return mapCloudPriceHistory(response, target.symbol, exchange, interval, startDate, endDate, extendedHours);
|
|
614
648
|
}
|
|
615
649
|
|
|
616
650
|
async getOptionsChain(ticker: string, exchange?: string, expirationDate?: number, _context?: MarketDataRequestContext): Promise<OptionsChain> {
|
|
@@ -40,6 +40,7 @@ export function normalizeHistoryResult(
|
|
|
40
40
|
...(session ? { session } : {}),
|
|
41
41
|
...(typeof result.sourceKey === "string" && /^(provider|broker):[^\r\n\0]+$/.test(result.sourceKey) ? { sourceKey: result.sourceKey } : {}),
|
|
42
42
|
...(isHistoryCoverageDate(result.coverageStart) ? { coverageStart: result.coverageStart } : {}),
|
|
43
|
+
...(result.extendedHours === true ? { extendedHours: true } : {}),
|
|
43
44
|
};
|
|
44
45
|
}
|
|
45
46
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { formatShortDate } from "../utils/datetime-format";
|
|
1
2
|
import { canonicalExchange, parsePublicTickerKey } from "../utils/exchanges";
|
|
2
3
|
import { isRecord } from "../utils/guards";
|
|
4
|
+
import { zonedDateKey } from "../utils/zoned-date-time";
|
|
3
5
|
import { parseBarInterval } from "../time-series/resolution";
|
|
4
6
|
|
|
5
7
|
export const HISTORY_RETENTION_MAX_AGE_MS = 5 * 60_000;
|
|
@@ -132,6 +134,20 @@ export class HistoryRetentionError extends Error {
|
|
|
132
134
|
}
|
|
133
135
|
}
|
|
134
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Shown while a chart at an explicit interval reaches back past what the
|
|
139
|
+
* source keeps at it: the first retained day, read in the listing's zone
|
|
140
|
+
* (UTC when unknown), and the length of the window the source keeps.
|
|
141
|
+
*/
|
|
142
|
+
export function historyRetentionNotice(retention: HistoryRetention, interval: string, timeZone: string | null): string {
|
|
143
|
+
const zone = timeZone ?? "UTC";
|
|
144
|
+
const day = zonedDateKey(retention.availableStart, zone);
|
|
145
|
+
const sameYear = day.slice(0, 4) === zonedDateKey(retention.observedAt, zone).slice(0, 4);
|
|
146
|
+
const days = Math.round((retention.observedAt - retention.availableStart) / 86_400_000);
|
|
147
|
+
return `${interval} history starts ${formatShortDate(day, { utc: true, year: sameYear ? false : "numeric" })}; `
|
|
148
|
+
+ `the source keeps the last ${days === 1 ? "day" : `${days} days`}.`;
|
|
149
|
+
}
|
|
150
|
+
|
|
135
151
|
export function isHistoryRetentionError(value: unknown): value is HistoryRetentionError {
|
|
136
152
|
return value instanceof HistoryRetentionError && parseHistoryRetention(value.retention) !== null;
|
|
137
153
|
}
|
|
@@ -143,15 +143,17 @@ function makeHistoryRequestIdentity(
|
|
|
143
143
|
fallbackVariantParts: Array<[string, string | number | undefined | null]>;
|
|
144
144
|
},
|
|
145
145
|
): Pick<HistoryRequestDescriptor, "identity" | "cacheVariantKeys" | "exactCacheVariantKeys" | "target"> {
|
|
146
|
+
// Extended-hours bars are another history; they never answer for the regular session.
|
|
147
|
+
const session: [string, string | undefined] = ["session", input.context?.historySession];
|
|
146
148
|
const identity = makeRouterRequestIdentity(deps, {
|
|
147
149
|
kind: input.kind,
|
|
148
150
|
ticker: input.ticker,
|
|
149
151
|
context: input.context,
|
|
150
|
-
variantParts: priceHistoryVariantParts(input.variantParts, input.exchange),
|
|
152
|
+
variantParts: priceHistoryVariantParts([...input.variantParts, session], input.exchange),
|
|
151
153
|
});
|
|
152
154
|
const cacheVariantKeys = [
|
|
153
155
|
identity.variantKey,
|
|
154
|
-
buildVariantKey(priceHistoryVariantParts(input.fallbackVariantParts, input.exchange)),
|
|
156
|
+
buildVariantKey(priceHistoryVariantParts([...input.fallbackVariantParts, session], input.exchange)),
|
|
155
157
|
];
|
|
156
158
|
return {
|
|
157
159
|
target: { symbol: input.ticker, exchange: input.exchange },
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// SEC's fair-access policy asks every client to name itself and a contact in
|
|
2
|
+
// the User-Agent; EDGAR blocks requests that do not.
|
|
3
|
+
|
|
4
|
+
function sanitizeIdentityPart(value: string, fallback: string): string {
|
|
5
|
+
const sanitized = value.trim().toLowerCase().replace(/[^a-z0-9.-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
6
|
+
return sanitized || fallback;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function extractEmail(value: string | undefined): string | null {
|
|
10
|
+
if (!value) return null;
|
|
11
|
+
const match = value.match(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/i);
|
|
12
|
+
return match?.[0] ?? null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getEnv(name: string): string | undefined {
|
|
16
|
+
return typeof process !== "undefined" ? process.env[name] : undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function getRuntimeHostName(): string {
|
|
20
|
+
const maybeLocation = (globalThis as { location?: { hostname?: string } }).location;
|
|
21
|
+
if (maybeLocation?.hostname) {
|
|
22
|
+
return maybeLocation.hostname;
|
|
23
|
+
}
|
|
24
|
+
return "localhost";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const runtimeHostName = getRuntimeHostName();
|
|
28
|
+
|
|
29
|
+
export const DEFAULT_SEC_FROM =
|
|
30
|
+
getEnv("SEC_FROM_EMAIL")?.trim()
|
|
31
|
+
|| extractEmail(getEnv("SEC_USER_AGENT"))
|
|
32
|
+
|| `${sanitizeIdentityPart(getEnv("USER") ?? "gloomberb", "gloomberb")}@${sanitizeIdentityPart(`${runtimeHostName}.local`, "localhost.localdomain")}`;
|
|
33
|
+
|
|
34
|
+
export const DEFAULT_SEC_USER_AGENT =
|
|
35
|
+
getEnv("SEC_USER_AGENT")?.trim()
|
|
36
|
+
|| `Gloomberb/0.1 (${sanitizeIdentityPart(runtimeHostName, "localhost")}; contact=${DEFAULT_SEC_FROM})`;
|
package/src/sources/sec-edgar.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
isPdfDocument,
|
|
12
12
|
} from "./sec-edgar/content";
|
|
13
13
|
import { parseSecAcceptanceTime } from "./sec-edgar/acceptance-time";
|
|
14
|
+
import { DEFAULT_SEC_FROM, DEFAULT_SEC_USER_AGENT } from "./sec-edgar/identity";
|
|
14
15
|
import { secFourthQuarters, withGuardedFourthQuarters, type SecFourthQuarter } from "./sec-edgar/fourth-quarter";
|
|
15
16
|
|
|
16
17
|
export { extractFilingContent } from "./sec-edgar/content";
|
|
@@ -20,40 +21,6 @@ const SUBMISSIONS_URL = "https://data.sec.gov/submissions";
|
|
|
20
21
|
const COMPANY_FACTS_URL = "https://data.sec.gov/api/xbrl/companyfacts";
|
|
21
22
|
const FETCH_TIMEOUT_MS = 15_000;
|
|
22
23
|
|
|
23
|
-
function sanitizeIdentityPart(value: string, fallback: string): string {
|
|
24
|
-
const sanitized = value.trim().toLowerCase().replace(/[^a-z0-9.-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
25
|
-
return sanitized || fallback;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function extractEmail(value: string | undefined): string | null {
|
|
29
|
-
if (!value) return null;
|
|
30
|
-
const match = value.match(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/i);
|
|
31
|
-
return match?.[0] ?? null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function getEnv(name: string): string | undefined {
|
|
35
|
-
return typeof process !== "undefined" ? process.env[name] : undefined;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function getRuntimeHostName(): string {
|
|
39
|
-
const maybeLocation = (globalThis as { location?: { hostname?: string } }).location;
|
|
40
|
-
if (maybeLocation?.hostname) {
|
|
41
|
-
return maybeLocation.hostname;
|
|
42
|
-
}
|
|
43
|
-
return "localhost";
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const runtimeHostName = getRuntimeHostName();
|
|
47
|
-
|
|
48
|
-
const DEFAULT_SEC_FROM =
|
|
49
|
-
getEnv("SEC_FROM_EMAIL")?.trim()
|
|
50
|
-
|| extractEmail(getEnv("SEC_USER_AGENT"))
|
|
51
|
-
|| `${sanitizeIdentityPart(getEnv("USER") ?? "gloomberb", "gloomberb")}@${sanitizeIdentityPart(`${runtimeHostName}.local`, "localhost.localdomain")}`;
|
|
52
|
-
|
|
53
|
-
const DEFAULT_SEC_USER_AGENT =
|
|
54
|
-
getEnv("SEC_USER_AGENT")?.trim()
|
|
55
|
-
|| `Gloomberb/0.1 (${sanitizeIdentityPart(runtimeHostName, "localhost")}; contact=${DEFAULT_SEC_FROM})`;
|
|
56
|
-
|
|
57
24
|
type LookupEntry = {
|
|
58
25
|
cik: string;
|
|
59
26
|
exchange?: string;
|
|
@@ -455,6 +455,7 @@ export function AppProvider({
|
|
|
455
455
|
);
|
|
456
456
|
const effectiveThemeId = getEffectiveThemeId(state);
|
|
457
457
|
const previousRecentTickers = useRef(state.recentTickers);
|
|
458
|
+
const previousRecentCommands = useRef(state.config.recentCommands);
|
|
458
459
|
const stateRef = useRef(state);
|
|
459
460
|
const listenersRef = useRef(new Set<() => void>());
|
|
460
461
|
const storeRef = useRef<AppContextStoreValue | null>(null);
|
|
@@ -528,6 +529,13 @@ export function AppProvider({
|
|
|
528
529
|
);
|
|
529
530
|
}, [state.config, state.recentTickers]);
|
|
530
531
|
|
|
532
|
+
// A recent pane run changes only config.recentCommands; nothing else saves it.
|
|
533
|
+
useEffect(() => {
|
|
534
|
+
if (previousRecentCommands.current === state.config.recentCommands) return;
|
|
535
|
+
previousRecentCommands.current = state.config.recentCommands;
|
|
536
|
+
scheduleConfigSave(() => ({ ...stateRef.current.config, recentTickers: stateRef.current.recentTickers }));
|
|
537
|
+
}, [state.config.recentCommands]);
|
|
538
|
+
|
|
531
539
|
useEffect(() => {
|
|
532
540
|
if (!desktopBridge) return;
|
|
533
541
|
return desktopBridge.subscribeState((snapshot) => {
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
normalizeBuiltinPluginStateMap,
|
|
29
29
|
} from "../plugins/ownership";
|
|
30
30
|
import { isRecord } from "../utils/guards";
|
|
31
|
+
import { withoutMissingWatchlists } from "../plugins/builtin/portfolio-list/mutations";
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* What a saved layout mirrors from the live session rather than from the
|
|
@@ -365,6 +366,7 @@ function collectCoreConfigPayload(config: AppConfig) {
|
|
|
365
366
|
chartPreferences: config.chartPreferences,
|
|
366
367
|
valueFlashingEnabled: config.valueFlashingEnabled,
|
|
367
368
|
recentTickers: config.recentTickers,
|
|
369
|
+
recentCommands: config.recentCommands,
|
|
368
370
|
// Completion is monotonic in synced state. A device only advertises the
|
|
369
371
|
// completed state, while resumable progress and incomplete state stay local.
|
|
370
372
|
onboardingComplete: config.onboardingComplete === true ? true : undefined,
|
|
@@ -655,6 +657,7 @@ function mergeConfigPayload(
|
|
|
655
657
|
assign("chartPreferences");
|
|
656
658
|
assign("valueFlashingEnabled");
|
|
657
659
|
assign("recentTickers");
|
|
660
|
+
assign("recentCommands");
|
|
658
661
|
// Sync can complete onboarding on another device, but never reopen it.
|
|
659
662
|
// Resumable progress remains local until this installation completes it.
|
|
660
663
|
if (
|
|
@@ -920,6 +923,11 @@ export const coreCollectionsSyncContributor: SyncContributor = {
|
|
|
920
923
|
if (!isRecord(payload)) return;
|
|
921
924
|
hydrateProfileAnalytics(payload);
|
|
922
925
|
const lastSyncedTickers = lastSyncedTickersById(baselinePayload);
|
|
926
|
+
// The config contributor applies first, so these are the watchlists this
|
|
927
|
+
// pull left. Tickers here and pulled ones drop the ids of any list that is
|
|
928
|
+
// gone (removed elsewhere, or deleted by an older build that left its id on
|
|
929
|
+
// the tickers), so this device never pushes them back.
|
|
930
|
+
const watchlistIds = new Set(getState().config.watchlists.map((watchlist) => watchlist.id));
|
|
923
931
|
const incomingRecords: TickerRecord[] = [];
|
|
924
932
|
const rawTickers = Array.isArray(payload.tickers) ? payload.tickers : [];
|
|
925
933
|
for (const rawTicker of rawTickers) {
|
|
@@ -943,16 +951,26 @@ export const coreCollectionsSyncContributor: SyncContributor = {
|
|
|
943
951
|
positions: syncedPositions,
|
|
944
952
|
broker_contracts: current?.metadata.broker_contracts ?? [],
|
|
945
953
|
});
|
|
946
|
-
const record
|
|
954
|
+
const record = withoutMissingWatchlists({ metadata }, watchlistIds);
|
|
947
955
|
await tickerRepository.saveTicker(record);
|
|
948
956
|
incomingRecords.push(record);
|
|
949
957
|
}
|
|
950
958
|
|
|
951
|
-
if (!isCurrent()
|
|
959
|
+
if (!isCurrent()) return;
|
|
952
960
|
const nextTickers = new Map(getState().tickers);
|
|
953
961
|
for (const record of incomingRecords) {
|
|
954
962
|
nextTickers.set(record.metadata.ticker, record);
|
|
955
963
|
}
|
|
964
|
+
let pruned = false;
|
|
965
|
+
for (const ticker of nextTickers.values()) {
|
|
966
|
+
const next = withoutMissingWatchlists(ticker, watchlistIds);
|
|
967
|
+
if (next === ticker) continue;
|
|
968
|
+
if (!isCurrent()) return;
|
|
969
|
+
await tickerRepository.saveTicker(next);
|
|
970
|
+
nextTickers.set(next.metadata.ticker, next);
|
|
971
|
+
pruned = true;
|
|
972
|
+
}
|
|
973
|
+
if (!isCurrent() || (incomingRecords.length === 0 && !pruned)) return;
|
|
956
974
|
dispatch({ type: "SET_TICKERS", tickers: nextTickers });
|
|
957
975
|
},
|
|
958
976
|
};
|