gloomberb 0.19.2 → 0.19.3
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 +7 -1
- package/package.json +1 -1
- package/patches/electrobun@1.18.1.patch +108 -1
- package/src/account/upgrade-dialog.tsx +4 -23
- package/src/api-client/coalesce.ts +118 -0
- package/src/api-client/credit-boards.ts +13 -0
- package/src/api-client/crypto-etf-flows.ts +6 -0
- package/src/api-client/crypto-markets.ts +40 -0
- package/src/api-client/data.ts +201 -14
- package/src/api-client/equity-screener.ts +10 -0
- package/src/api-client/errors.ts +20 -0
- package/src/api-client/gpu.ts +29 -0
- package/src/api-client/index.ts +5 -2
- package/src/api-client/plan-access.ts +10 -1
- package/src/api-client/plan-rules.ts +35 -0
- package/src/api-client/research-activity.ts +1 -32
- package/src/api-client/types.ts +21 -1
- package/src/app/attention-notice.tsx +94 -0
- package/src/app.tsx +23 -1
- package/src/architecture/core-builtin-allowlist.json +2 -1
- package/src/capabilities/factories.ts +1 -0
- package/src/cli/ascii-output.ts +42 -0
- package/src/cli/base-converter.ts +10 -2
- package/src/cli/command-options.ts +16 -1
- package/src/cli/commands/crypto-hints.ts +23 -4
- package/src/cli/commands/market.ts +92 -23
- package/src/cli/commands/news-search.ts +24 -5
- package/src/cli/commands/overview.ts +9 -4
- package/src/cli/commands/search.ts +71 -12
- package/src/cli/commands/system.ts +22 -30
- package/src/cli/commands/ticker.ts +49 -9
- package/src/cli/commands/version.ts +33 -0
- package/src/cli/context.ts +5 -9
- package/src/cli/data-dir.ts +7 -0
- package/src/cli/desktop-pane-shot-api-policy.ts +85 -0
- package/src/cli/desktop-pane-shot-browser.ts +320 -0
- package/src/cli/desktop-pane-shot-routes.ts +26 -1
- package/src/cli/desktop-pane-shot.ts +196 -256
- package/src/cli/early-command.ts +42 -0
- package/src/cli/earnings-calendar.ts +9 -0
- package/src/cli/entry.ts +19 -13
- package/src/cli/errors.ts +12 -2
- package/src/cli/help.ts +1 -1
- package/src/cli/history-export.ts +22 -2
- package/src/cli/history-rows.ts +154 -4
- package/src/cli/index.ts +18 -1
- package/src/cli/known-gaps.ts +40 -5
- package/src/cli/listing-arg.ts +46 -9
- package/src/cli/options.ts +1 -1
- package/src/cli/output-file.ts +60 -13
- package/src/cli/pane-functions/catalog.ts +14 -3
- package/src/cli/pane-functions/data.ts +1 -1
- package/src/cli/pane-functions/dom.ts +45 -0
- package/src/cli/pane-functions/freshness.ts +46 -9
- package/src/cli/pane-functions/function-help.ts +17 -9
- package/src/cli/pane-functions/headless.ts +22 -14
- package/src/cli/pane-functions/index.ts +6 -2
- package/src/cli/pane-functions/options.ts +6 -2
- package/src/cli/pane-functions/resolver.ts +27 -5
- package/src/cli/pane-functions/screenshot-assessment.ts +33 -0
- package/src/cli/pane-functions/screenshot-bridge.ts +94 -13
- package/src/cli/pane-functions/screenshot-http.ts +70 -0
- package/src/cli/pane-functions/screenshot-render.ts +19 -5
- package/src/cli/quote-answer.ts +20 -0
- package/src/cli/registry.ts +41 -13
- package/src/cli/result.ts +12 -6
- package/src/cli/ticker-resolution.ts +2 -1
- package/src/cli/types.ts +2 -0
- package/src/components/chart/composite/format.ts +3 -0
- package/src/components/chart-table/header.tsx +1 -1
- package/src/components/chart-table/layout.ts +28 -11
- package/src/components/command-bar/action-runtime.ts +4 -0
- package/src/components/command-bar/commands/direct/index.ts +18 -33
- package/src/components/command-bar/commands/direct/runtime.ts +5 -0
- package/src/components/command-bar/commands/registry.ts +10 -1
- package/src/components/command-bar/routes/root/command-items.ts +9 -4
- package/src/components/command-bar/routes/root/runtime.ts +24 -1
- package/src/components/command-bar/surface/index.tsx +9 -3
- package/src/components/company-logo.tsx +2 -1
- package/src/components/layout/header.tsx +8 -3
- package/src/components/layout/market-summary.ts +25 -7
- package/src/components/layout/pane/footer/index.tsx +7 -2
- package/src/components/layout/pane/footer/model.ts +40 -5
- package/src/components/market-board/index.tsx +3 -2
- package/src/components/onboarding/desks-step.tsx +12 -4
- package/src/components/onboarding/first-run-workspace.ts +81 -4
- package/src/components/onboarding/onboarding-frame.tsx +2 -1
- package/src/components/onboarding/onboarding-keyboard.ts +8 -5
- package/src/components/onboarding/onboarding-wizard.tsx +54 -23
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +7 -2
- package/src/components/onboarding/run-setup-again.ts +24 -0
- package/src/components/onboarding/use-onboarding-progress.ts +33 -15
- package/src/components/onboarding/use-onboarding-upgrade.ts +2 -6
- package/src/components/onboarding/wizard-model.ts +57 -3
- package/src/components/onboarding/wizard-positions.ts +1 -1
- package/src/components/ui/delayed-mark.tsx +56 -0
- package/src/components/ui/external-link.tsx +3 -6
- package/src/components/ui/icon.tsx +8 -1
- package/src/components/ui/logo-mark.tsx +8 -5
- package/src/components/ui/stat-grid.tsx +263 -19
- package/src/data/config/store/node.ts +11 -5
- package/src/data/config/store/normalize.ts +16 -3
- package/src/data/profile-permissions.ts +47 -0
- package/src/data/resource-store.ts +34 -31
- package/src/data/sqlite/access.ts +19 -8
- package/src/data/sqlite/checkpointer.ts +95 -8
- package/src/data/sqlite/database.ts +40 -7
- package/src/docs/snapshot.json +191 -78
- package/src/i18n/es.ts +18 -2
- package/src/i18n/ja.ts +18 -2
- package/src/i18n/ko.ts +18 -2
- package/src/i18n/zh-cn.ts +18 -2
- package/src/i18n/zh-tw.ts +18 -2
- package/src/layout/desk-keys.ts +4 -1
- package/src/layout/desks.ts +38 -5
- package/src/layout-marketplace/gallery-desktop.tsx +45 -16
- package/src/market-data/batched-reads.ts +16 -0
- package/src/market-data/coordinator/auxiliary.ts +4 -2
- package/src/market-data/coordinator/index.ts +11 -2
- package/src/market-data/futures-board.ts +1 -1
- package/src/market-data/futures-contract.ts +3 -1
- package/src/market-data/market/index-level.ts +6 -4
- package/src/market-data/published-apac-sessions.ts +6 -0
- package/src/news/aggregator.ts +7 -4
- package/src/news/news-model.ts +11 -0
- package/src/news/story-id.ts +22 -0
- package/src/news/ticker-symbols.ts +17 -1
- package/src/notifications/app-notifier.ts +35 -14
- package/src/plugins/builtin/account-management/form-components.tsx +1 -1
- package/src/plugins/builtin/account-management/index.tsx +2 -3
- package/src/plugins/builtin/account-management/model.ts +1 -1
- package/src/plugins/builtin/alerts/cli-forms.ts +15 -1
- package/src/plugins/builtin/alerts/events-pane.tsx +7 -5
- package/src/plugins/builtin/alerts/events.ts +10 -2
- package/src/plugins/builtin/alerts/index.tsx +5 -5
- package/src/plugins/builtin/alerts/research-builder.ts +5 -1
- package/src/plugins/builtin/alerts/research-rules.ts +18 -0
- package/src/plugins/builtin/alerts/targets.ts +4 -1
- package/src/plugins/builtin/attention/headless.ts +1 -1
- package/src/plugins/builtin/attention/index.tsx +2 -4
- package/src/plugins/builtin/attention/listing.ts +1 -1
- package/src/plugins/builtin/attention/series.ts +1 -1
- package/src/plugins/builtin/awards/index.tsx +1 -2
- package/src/plugins/builtin/backtest/client.ts +1 -0
- package/src/plugins/builtin/backtest/index.tsx +1 -2
- package/src/plugins/builtin/backtest/rules.ts +3 -1
- package/src/plugins/builtin/backtest/window.ts +8 -7
- package/src/plugins/builtin/broker-manager/detail.tsx +16 -3
- package/src/plugins/builtin/broker-manager/index.tsx +1 -1
- package/src/plugins/builtin/buildout/cells.tsx +5 -0
- package/src/plugins/builtin/buildout/company/flow.tsx +367 -0
- package/src/plugins/builtin/buildout/company/id.ts +2 -0
- package/src/plugins/builtin/buildout/company/model.ts +247 -0
- package/src/plugins/builtin/buildout/company/pane.tsx +317 -0
- package/src/plugins/builtin/buildout/data-runtime.ts +31 -10
- package/src/plugins/builtin/buildout/facilities.ts +309 -0
- package/src/plugins/builtin/buildout/format.ts +9 -0
- package/src/plugins/builtin/buildout/index.tsx +51 -5
- package/src/plugins/builtin/buildout/model/index.ts +62 -10
- package/src/plugins/builtin/buildout/model/normalizers.ts +5 -0
- package/src/plugins/builtin/buildout/model/types.ts +11 -2
- package/src/plugins/builtin/buildout/pane/index.tsx +91 -20
- package/src/plugins/builtin/buildout/pane/status.tsx +1 -1
- package/src/plugins/builtin/buildout/pane/themes.tsx +110 -0
- package/src/plugins/builtin/buildout/table-model.ts +26 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +16 -0
- package/src/plugins/builtin/cds/headless.ts +10 -2
- package/src/plugins/builtin/cds/model.ts +7 -3
- package/src/plugins/builtin/central-bank-rates/index.tsx +1 -2
- package/src/plugins/builtin/central-bank-rates/pane.tsx +2 -1
- package/src/plugins/builtin/changelog/index.tsx +3 -2
- package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -2
- package/src/plugins/builtin/chart-composer/headless.ts +88 -21
- package/src/plugins/builtin/chart-composer/index.tsx +2 -3
- package/src/plugins/builtin/chart-composer/pane.tsx +26 -2
- package/src/plugins/builtin/chart-composer/series-expression.ts +3 -2
- package/src/plugins/builtin/chart-composer/studies.ts +1 -1
- package/src/plugins/builtin/chat/controller/index.ts +1 -1
- package/src/plugins/builtin/chat/controller/messages.ts +15 -4
- package/src/plugins/builtin/chat/ghost-user.ts +8 -0
- package/src/plugins/builtin/chat/index.tsx +5 -5
- package/src/plugins/builtin/clinical-trials/index.tsx +1 -2
- package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
- package/src/plugins/builtin/cloud/index.tsx +1 -2
- package/src/plugins/builtin/cloud/plugin.tsx +6 -4
- package/src/plugins/builtin/cloud/team/module.tsx +1 -1
- package/src/plugins/builtin/cloud/thesis/module.tsx +1 -1
- package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +24 -1
- package/src/plugins/builtin/cloud/upgrade-status-widget.tsx +39 -15
- package/src/plugins/builtin/comment-letters/index.tsx +1 -2
- package/src/plugins/builtin/commodity-balances/index.tsx +3 -3
- package/src/plugins/builtin/commodity-balances/pane.tsx +2 -2
- package/src/plugins/builtin/congress-trades/aggregates.ts +13 -2
- package/src/plugins/builtin/congress-trades/detail.tsx +20 -4
- package/src/plugins/builtin/congress-trades/headless.ts +2 -0
- package/src/plugins/builtin/congress-trades/model.ts +92 -64
- package/src/plugins/builtin/congress-trades/table.tsx +14 -2
- package/src/plugins/builtin/connections/index.ts +2 -3
- package/src/plugins/builtin/cot/index.tsx +1 -2
- package/src/plugins/builtin/cpi/index.tsx +1 -2
- package/src/plugins/builtin/credit-boards/cdx-pane.tsx +2 -1
- package/src/plugins/builtin/credit-boards/client.ts +7 -2
- package/src/plugins/builtin/credit-boards/headless.ts +25 -12
- package/src/plugins/builtin/credit-boards/model.ts +29 -0
- package/src/plugins/builtin/credit-boards/sovr-pane.tsx +9 -4
- package/src/plugins/builtin/crypto-board/bitcoin-network.ts +143 -0
- package/src/plugins/builtin/crypto-board/headless.ts +4 -3
- package/src/plugins/builtin/crypto-board/index.tsx +4 -2
- package/src/plugins/builtin/crypto-board/series.ts +2 -0
- package/src/plugins/builtin/crypto-etf-flows/client.ts +9 -2
- package/src/plugins/builtin/crypto-etf-flows/headless.ts +61 -6
- package/src/plugins/builtin/crypto-etf-flows/index.tsx +1 -2
- package/src/plugins/builtin/crypto-etf-flows/model.ts +14 -1
- package/src/plugins/builtin/crypto-etf-flows/pane.tsx +8 -5
- package/src/plugins/builtin/debt-maturities/headless.ts +4 -3
- package/src/plugins/builtin/debt-maturities/model.ts +17 -1
- package/src/plugins/builtin/debt-maturities/pane.tsx +18 -4
- package/src/plugins/builtin/debug/index.tsx +1 -1
- package/src/plugins/builtin/dividend-yield/client.ts +32 -2
- package/src/plugins/builtin/dividend-yield/headless.ts +58 -33
- package/src/plugins/builtin/dividend-yield/index.tsx +37 -2
- package/src/plugins/builtin/dividend-yield/pane.tsx +32 -13
- package/src/plugins/builtin/dividend-yield/ranking-reads.ts +73 -0
- package/src/plugins/builtin/dividend-yield/ranking-view.tsx +163 -0
- package/src/plugins/builtin/dividend-yield/ranking.ts +1 -1
- package/src/plugins/builtin/dividend-yield/view.ts +21 -0
- package/src/plugins/builtin/docs/index.tsx +2 -3
- package/src/plugins/builtin/doe/headless.ts +16 -3
- package/src/plugins/builtin/doe/index.tsx +5 -4
- package/src/plugins/builtin/earnings/board.tsx +2 -1
- package/src/plugins/builtin/earnings/history.tsx +2 -1
- package/src/plugins/builtin/earnings-ripple/client.ts +32 -7
- package/src/plugins/builtin/earnings-ripple/index.tsx +1 -2
- package/src/plugins/builtin/econ/calendar-model.ts +29 -6
- package/src/plugins/builtin/econ-statistics/index.tsx +1 -2
- package/src/plugins/builtin/equity-screener/client.ts +1 -1
- package/src/plugins/builtin/equity-screener/headless.ts +1 -0
- package/src/plugins/builtin/equity-screener/ideas.ts +30 -1
- package/src/plugins/builtin/equity-screener/index.tsx +1 -2
- package/src/plugins/builtin/equity-screener/model.ts +28 -4
- package/src/plugins/builtin/equity-screener/pane.tsx +5 -0
- package/src/plugins/builtin/equity-screener/screen-check.ts +23 -1
- package/src/plugins/builtin/estimate-revisions/index.tsx +1 -2
- package/src/plugins/builtin/exposure/holdings.ts +35 -13
- package/src/plugins/builtin/exposure/index.tsx +1 -2
- package/src/plugins/builtin/fear-greed/cli.ts +44 -0
- package/src/plugins/builtin/fear-greed/index.tsx +5 -53
- package/src/plugins/builtin/fear-greed/pane.tsx +2 -1
- package/src/plugins/builtin/fixed-income/index.tsx +1 -2
- package/src/plugins/builtin/futures/board-reads.ts +25 -0
- package/src/plugins/builtin/futures/coalesced-bump.ts +44 -0
- package/src/plugins/builtin/futures/index.tsx +15 -0
- package/src/plugins/builtin/futures/model.ts +14 -0
- package/src/plugins/builtin/futures/use-board-terms.ts +22 -15
- package/src/plugins/builtin/futures/use-front-returns.ts +20 -14
- package/src/plugins/builtin/futures-curve/headless.ts +6 -1
- package/src/plugins/builtin/futures-curve/index.tsx +1 -2
- package/src/plugins/builtin/fx-matrix/index.tsx +2 -2
- package/src/plugins/builtin/gpu/board.tsx +51 -12
- package/src/plugins/builtin/gpu/client.ts +15 -2
- package/src/plugins/builtin/gpu/equities.tsx +5 -2
- package/src/plugins/builtin/gpu/headless.ts +14 -2
- package/src/plugins/builtin/gpu/index.tsx +1 -2
- package/src/plugins/builtin/gpu/model.ts +20 -2
- package/src/plugins/builtin/gpu/pane.tsx +1 -1
- package/src/plugins/builtin/help/index.tsx +1 -1
- package/src/plugins/builtin/holders/index-holders.ts +24 -12
- package/src/plugins/builtin/holders/index.tsx +2 -3
- package/src/plugins/builtin/ipo-calendar/index.tsx +1 -2
- package/src/plugins/builtin/iv-history/index.tsx +1 -2
- package/src/plugins/builtin/jodi/index.tsx +1 -2
- package/src/plugins/builtin/kelly-sizer/index.tsx +1 -2
- package/src/plugins/builtin/layout-manager/index.tsx +9 -8
- package/src/plugins/builtin/macro-day/pane.tsx +4 -2
- package/src/plugins/builtin/market-halts/index.tsx +1 -2
- package/src/plugins/builtin/market-heatmap/index.tsx +11 -2
- package/src/plugins/builtin/market-movers/index.tsx +7 -1
- package/src/plugins/builtin/market-movers/session-body.tsx +4 -1
- package/src/plugins/builtin/market-valuation/index.tsx +1 -2
- package/src/plugins/builtin/members/index.tsx +1 -2
- package/src/plugins/builtin/money-markets/index.tsx +1 -2
- package/src/plugins/builtin/money-markets/pane.tsx +2 -1
- package/src/plugins/builtin/news/headless.ts +7 -0
- package/src/plugins/builtin/news/wire/index.tsx +1 -1
- package/src/plugins/builtin/news/wire/news/pop-out.tsx +9 -3
- package/src/plugins/builtin/notes/index.tsx +3 -6
- package/src/plugins/builtin/openfda/index.tsx +1 -2
- package/src/plugins/builtin/options/view.tsx +6 -5
- package/src/plugins/builtin/options-calculator/index.tsx +1 -2
- package/src/plugins/builtin/options-positioning/index.tsx +1 -2
- package/src/plugins/builtin/options-scenario/index.tsx +2 -4
- package/src/plugins/builtin/perps/index.tsx +1 -2
- package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -3
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +2 -1
- package/src/plugins/builtin/plugin-module.ts +16 -2
- package/src/plugins/builtin/portfolio-list/cli/render.ts +53 -3
- package/src/plugins/builtin/portfolio-list/index.tsx +1 -2
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +11 -0
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +2 -2
- package/src/plugins/builtin/power/index.tsx +1 -2
- package/src/plugins/builtin/rate-path/index.tsx +1 -2
- package/src/plugins/builtin/realized-vol/headless.ts +2 -0
- package/src/plugins/builtin/realized-vol/index.tsx +1 -2
- package/src/plugins/builtin/realized-vol/model.ts +16 -2
- package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +1 -2
- package/src/plugins/builtin/research/analyst-headless.ts +33 -6
- package/src/plugins/builtin/research/analyst-model.ts +37 -2
- package/src/plugins/builtin/research/analyst-pane.tsx +25 -5
- package/src/plugins/builtin/research/index.tsx +5 -8
- package/src/plugins/builtin/research-search/index.tsx +1 -2
- package/src/plugins/builtin/revenue-breakdown/headless.ts +22 -8
- package/src/plugins/builtin/revenue-breakdown/model.ts +23 -0
- package/src/plugins/builtin/reverse-dcf/pane.tsx +4 -2
- package/src/plugins/builtin/roadmap/index.tsx +2 -3
- package/src/plugins/builtin/roadmap/pane.tsx +49 -2
- package/src/plugins/builtin/scanner/hilo-model.ts +18 -1
- package/src/plugins/builtin/scanner/hilo-pane.tsx +12 -2
- package/src/plugins/builtin/scanner/live-pane.tsx +7 -4
- package/src/plugins/builtin/sec/filing-content.ts +38 -6
- package/src/plugins/builtin/sec/headless.ts +5 -1
- package/src/plugins/builtin/sec/index.tsx +4 -2
- package/src/plugins/builtin/sec/model.ts +2 -0
- package/src/plugins/builtin/sectors/index.tsx +11 -5
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +60 -0
- package/src/plugins/builtin/shared/cloud-upgrade.ts +24 -1
- package/src/plugins/builtin/shared/forming-bars.ts +29 -0
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +17 -0
- package/src/plugins/builtin/shared/series-detail-chart.tsx +4 -2
- package/src/plugins/builtin/shared/use-quote-board.ts +5 -0
- package/src/plugins/builtin/short-interest/publications-pane.tsx +2 -1
- package/src/plugins/builtin/short-volume/pane.tsx +2 -1
- package/src/plugins/builtin/social-mentions/index.tsx +1 -2
- package/src/plugins/builtin/social-mentions/pane.tsx +2 -1
- package/src/plugins/builtin/supply-chain/client.ts +22 -1
- package/src/plugins/builtin/supply-chain/graph-client.ts +22 -1
- package/src/plugins/builtin/supply-chain/index.tsx +1 -2
- package/src/plugins/builtin/thirteenf/api.ts +116 -13
- package/src/plugins/builtin/thirteenf/data.ts +132 -30
- package/src/plugins/builtin/thirteenf/index.tsx +2 -4
- package/src/plugins/builtin/thirteenf/model.ts +2 -0
- package/src/plugins/builtin/thirteenf/pane.tsx +20 -11
- package/src/plugins/builtin/thirteenf/types.ts +12 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +17 -4
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +81 -11
- package/src/plugins/builtin/ticker-detail/headless.ts +11 -3
- package/src/plugins/builtin/ticker-detail/index.tsx +5 -10
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +16 -6
- package/src/plugins/builtin/time-sales/index.tsx +1 -2
- package/src/plugins/builtin/time-sales/pane.tsx +3 -1
- package/src/plugins/builtin/trace-bonds/headless.ts +11 -2
- package/src/plugins/builtin/trace-bonds/model.ts +44 -10
- package/src/plugins/builtin/trace-bonds/pane.tsx +12 -7
- package/src/plugins/builtin/treasury-auctions/pane.tsx +2 -2
- package/src/plugins/builtin/vol-surface/client.ts +18 -3
- package/src/plugins/builtin/vol-surface/index.tsx +1 -2
- package/src/plugins/builtin/volatility/index.tsx +1 -2
- package/src/plugins/builtin/world-indices/footer.ts +6 -1
- package/src/plugins/builtin/world-venue-map/client.ts +10 -1
- package/src/plugins/builtin/world-venue-map/index.ts +6 -2
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +48 -7
- package/src/plugins/builtin/world-venue-map/layers.ts +17 -0
- package/src/plugins/builtin/world-venue-map/local-layers.ts +141 -0
- package/src/plugins/builtin/world-venue-map/map-symbols.ts +18 -0
- package/src/plugins/builtin/world-venue-map/map.tsx +4 -2
- package/src/plugins/builtin/world-venue-map/scope.ts +63 -0
- package/src/plugins/builtin/yield-curve/curves.ts +16 -2
- package/src/plugins/builtin/yield-curve/headless.ts +3 -3
- package/src/plugins/bundle.ts +52 -2
- package/src/plugins/catalog-browser.ts +2 -2
- package/src/plugins/catalog-cli.ts +20 -0
- package/src/plugins/host-link.ts +13 -1
- package/src/plugins/installer.ts +15 -4
- package/src/plugins/loader.ts +43 -11
- package/src/plugins/peer-imports.ts +201 -0
- package/src/plugins/peer-retry.ts +26 -29
- package/src/plugins/plugin-process.ts +128 -0
- package/src/plugins/redirection-guard.ts +45 -0
- package/src/remote/endpoint.ts +16 -3
- package/src/renderers/browser/main.tsx +3 -2
- package/src/renderers/cloudflare/http-proxy.ts +7 -123
- package/src/renderers/dom/desktop/icons.tsx +2 -0
- package/src/renderers/dom/host/image-surface.tsx +46 -21
- package/src/renderers/dom/styles.css +7 -0
- package/src/renderers/dom/toast-host.tsx +2 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +3 -2
- package/src/renderers/electrobun/bun/desktop/rpc-timing.ts +88 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +20 -4
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +8 -4
- package/src/renderers/electrobun/bun/external-plugins.ts +4 -0
- package/src/renderers/electrobun/bun/window/create.ts +4 -2
- package/src/renderers/electrobun/bun/window/native-timing.ts +48 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +82 -4
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +13 -5
- package/src/renderers/electrobun/view/desktop/window/snapshot.ts +3 -0
- package/src/renderers/electrobun/view/main.tsx +7 -2
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -0
- package/src/renderers/opentui/browser-open.ts +10 -4
- package/src/renderers/opentui/image/surface.tsx +1 -1
- package/src/renderers/opentui/start.tsx +12 -6
- package/src/renderers/opentui/toast-host.tsx +2 -1
- package/src/series/registry.ts +18 -0
- package/src/series/standalone.ts +10 -1
- package/src/series/types.ts +7 -0
- package/src/series/validate.ts +11 -2
- package/src/sources/gloomberb-cloud/index.ts +28 -5
- package/src/sources/gloomberb-cloud/news.ts +24 -3
- package/src/sources/gloomberb-cloud/normalizers.ts +17 -2
- package/src/sources/provider-errors.ts +26 -3
- package/src/sources/provider-router/history.ts +5 -0
- package/src/sources/provider-router/index.ts +13 -0
- package/src/sync/core-contributors.ts +32 -0
- package/src/telemetry/attention-counts.ts +64 -6
- package/src/telemetry/attention-setting.ts +33 -0
- package/src/telemetry/crash-reports-node.ts +3 -1
- package/src/telemetry/stall-clocks.ts +93 -0
- package/src/telemetry/stall-reports-node.ts +12 -7
- package/src/telemetry/stall-reports.ts +34 -5
- package/src/test-support/preload.ts +9 -0
- package/src/tickers/company-name.ts +19 -0
- package/src/tickers/listing-fate.ts +14 -3
- package/src/tickers/saved-instrument.ts +17 -0
- package/src/tickers/search/home-listing.ts +13 -2
- package/src/tickers/search/index.ts +4 -1
- package/src/tickers/search/types.ts +6 -1
- package/src/time-series/bar-aggregation.ts +7 -1
- package/src/time-series/base-series.ts +3 -3
- package/src/time-series/chart-bounds.ts +1 -1
- package/src/time-series/chart-data.ts +5 -2
- package/src/time-series/chart-seed.ts +1 -1
- package/src/time-series/coin-session-close.ts +211 -0
- package/src/time-series/live-bars.ts +13 -2
- package/src/time-series/live-quotes.ts +1 -1
- package/src/time-series/official-close.ts +159 -0
- package/src/time-series/price-comparison.ts +55 -6
- package/src/time-series/resolve.ts +12 -3
- package/src/time-series/series-presentation.ts +1 -1
- package/src/time-series/studies/formula.ts +205 -0
- package/src/time-series/studies/indicators.ts +299 -0
- package/src/time-series/studies/leverage.ts +75 -0
- package/src/time-series/studies/pair.ts +138 -0
- package/src/time-series/studies/resolve.ts +333 -0
- package/src/time-series/studies/shared.ts +141 -0
- package/src/time-series/studies/units.ts +243 -0
- package/src/time-series/studies/vwap.ts +274 -0
- package/src/types/config.ts +18 -2
- package/src/types/data-provider.ts +23 -0
- package/src/types/financials.ts +22 -0
- package/src/types/headless.ts +42 -3
- package/src/types/plugin.ts +32 -3
- package/src/ui/host.tsx +11 -0
- package/src/ui/toast.tsx +26 -1
- package/src/utils/ascii-text.ts +140 -0
- package/src/utils/cli-output.ts +91 -18
- package/src/utils/concurrency.ts +24 -0
- package/src/utils/exchanges.ts +6 -0
- package/src/utils/external-url.ts +20 -7
- package/src/utils/futures-generic.ts +4 -4
- package/src/utils/http-proxy-upstream.ts +124 -0
- package/src/utils/main-thread-stall.ts +75 -3
- package/src/utils/perf-marks.ts +36 -2
- package/src/utils/private-files.ts +141 -0
- package/src/utils/public-address.ts +99 -0
- package/src/account/upgrade-personalization.ts +0 -57
- package/src/account/upgrade-tickers.ts +0 -124
- package/src/api-client/upgrade-experiment.ts +0 -91
- package/src/plugins/builtin/cloud/browser.tsx +0 -3
- package/src/renderers/electrobun/launcher/preload.ts +0 -13
- package/src/renderers/electrobun/launcher/windows-emulation.ts +0 -50
- package/src/time-series/studies.ts +0 -1628
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { BrowserView, BrowserWindow } from "electrobun/bun";
|
|
2
|
+
import { measurePerf } from "../../../../utils/perf-marks";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Electrobun's window and webview calls that go to the native layer. On
|
|
6
|
+
* Windows each one is answered on the thread that runs the windows, and this
|
|
7
|
+
* process waits for it: while that thread is busy, so is this one.
|
|
8
|
+
*/
|
|
9
|
+
const WINDOW_CALLS = [
|
|
10
|
+
"activate", "close", "show", "showInactive", "hide", "minimize", "unminimize", "maximize", "unmaximize",
|
|
11
|
+
"isMinimized", "isMaximized", "isFullScreen", "setFullScreen", "isAlwaysOnTop", "setAlwaysOnTop",
|
|
12
|
+
"setTitle", "setPosition", "setSize", "setFrame", "getFrame",
|
|
13
|
+
];
|
|
14
|
+
const VIEW_CALLS = [
|
|
15
|
+
"executeJavascript", "loadURL", "loadHTML", "setNavigationRules", "openDevTools", "closeDevTools", "toggleDevTools",
|
|
16
|
+
"setPageZoom",
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
let measured = false;
|
|
20
|
+
|
|
21
|
+
/** `getFrame` as a stall report keeps it: `get-frame`. */
|
|
22
|
+
function sectionPart(name: string): string {
|
|
23
|
+
return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function measureCalls(prototype: object, prefix: string, names: readonly string[]): void {
|
|
27
|
+
const methods = prototype as Record<string, unknown>;
|
|
28
|
+
for (const name of names) {
|
|
29
|
+
const original = methods[name];
|
|
30
|
+
if (typeof original !== "function") continue;
|
|
31
|
+
const section = `${prefix}.${sectionPart(name)}`;
|
|
32
|
+
methods[name] = function measuredNativeCall(this: unknown, ...args: unknown[]) {
|
|
33
|
+
return measurePerf(section, () => original.apply(this, args));
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Names each native window and webview call (`native.window.get-frame`,
|
|
40
|
+
* `native.view.execute-javascript`), so a stall inside or right after one
|
|
41
|
+
* says so instead of naming whatever was measured last. Once per process.
|
|
42
|
+
*/
|
|
43
|
+
export function measureNativeWindowCalls(): void {
|
|
44
|
+
if (measured) return;
|
|
45
|
+
measured = true;
|
|
46
|
+
measureCalls(BrowserWindow.prototype, "native.window", WINDOW_CALLS);
|
|
47
|
+
measureCalls(BrowserView.prototype, "native.view", VIEW_CALLS);
|
|
48
|
+
}
|
|
@@ -16,8 +16,12 @@ import { JsonTickerRepository } from "../../../data/json-ticker-repository";
|
|
|
16
16
|
import type { DesktopPaneShotPayload } from "../../../cli/desktop-pane-shot";
|
|
17
17
|
import {
|
|
18
18
|
SHOT_API_PROXY_PREFIX,
|
|
19
|
+
SHOT_API_SOCKET_PATH,
|
|
19
20
|
SHOT_HTTP_BRIDGE_PATH,
|
|
20
21
|
SHOT_MARKET_BRIDGE_PATH,
|
|
22
|
+
shotRouteUrl,
|
|
23
|
+
type ShotApiSocketCall,
|
|
24
|
+
type ShotApiSocketReply,
|
|
21
25
|
} from "../../../cli/desktop-pane-shot-routes";
|
|
22
26
|
import { instrumentFromTicker } from "../../../market-data/request-types";
|
|
23
27
|
import type { RendererHost } from "../../../ui/host";
|
|
@@ -95,6 +99,12 @@ const TRACKED_RESPONSE_METHODS = new Set<PropertyKey>([
|
|
|
95
99
|
"text",
|
|
96
100
|
]);
|
|
97
101
|
|
|
102
|
+
/**
|
|
103
|
+
* The page's own address, read once before anything can change it: the
|
|
104
|
+
* server answers only inside the secret folder the page opened at.
|
|
105
|
+
*/
|
|
106
|
+
const SHOT_PAGE_URL = window.location.href;
|
|
107
|
+
|
|
98
108
|
let pendingShotWork = 0;
|
|
99
109
|
let didInstallShotFetchTracker = false;
|
|
100
110
|
const rendererHost: RendererHost = {
|
|
@@ -142,22 +152,90 @@ function installShotFetchTracker(): void {
|
|
|
142
152
|
)) as typeof fetch;
|
|
143
153
|
}
|
|
144
154
|
|
|
155
|
+
/** Sends a read over the socket; null once the socket has closed. */
|
|
156
|
+
type ShotApiSocketSend = (call: Omit<ShotApiSocketCall, "id">, signal: AbortSignal | null | undefined) => Promise<Response> | null;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Cloud API reads over one socket to the Bun server, answered as each lands.
|
|
160
|
+
* A page's fetches share six connections to its origin, so a board's seventy
|
|
161
|
+
* contract histories and terms waited on each other for seconds. Null when
|
|
162
|
+
* the socket does not open; the page's fetch carries the reads then, and
|
|
163
|
+
* once the socket closes.
|
|
164
|
+
*/
|
|
165
|
+
function openShotApiSocket(): Promise<ShotApiSocketSend | null> {
|
|
166
|
+
return new Promise((resolve) => {
|
|
167
|
+
const pending = new Map<number, (reply: ShotApiSocketReply | null) => void>();
|
|
168
|
+
let nextId = 1;
|
|
169
|
+
let open = false;
|
|
170
|
+
const url = shotRouteUrl(SHOT_API_SOCKET_PATH, SHOT_PAGE_URL);
|
|
171
|
+
url.protocol = "ws:";
|
|
172
|
+
const socket = new WebSocket(url);
|
|
173
|
+
socket.addEventListener("message", (event) => {
|
|
174
|
+
let reply: ShotApiSocketReply;
|
|
175
|
+
try {
|
|
176
|
+
reply = JSON.parse(String(event.data)) as ShotApiSocketReply;
|
|
177
|
+
} catch {
|
|
178
|
+
// Intentional ignore: every reply the server sends is JSON; anything else answers no call.
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
pending.get(reply.id)?.(reply);
|
|
182
|
+
pending.delete(reply.id);
|
|
183
|
+
});
|
|
184
|
+
socket.addEventListener("close", () => {
|
|
185
|
+
open = false;
|
|
186
|
+
for (const settle of pending.values()) settle(null);
|
|
187
|
+
pending.clear();
|
|
188
|
+
resolve(null);
|
|
189
|
+
});
|
|
190
|
+
socket.addEventListener("open", () => {
|
|
191
|
+
open = true;
|
|
192
|
+
resolve((call, signal) => !open ? null : new Promise<Response>((answer, fail) => {
|
|
193
|
+
const aborted = () => fail(signal?.reason ?? new DOMException("The operation was aborted.", "AbortError"));
|
|
194
|
+
if (signal?.aborted) return aborted();
|
|
195
|
+
const id = nextId++;
|
|
196
|
+
const onAbort = () => {
|
|
197
|
+
pending.delete(id);
|
|
198
|
+
aborted();
|
|
199
|
+
};
|
|
200
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
201
|
+
pending.set(id, (reply) => {
|
|
202
|
+
signal?.removeEventListener("abort", onAbort);
|
|
203
|
+
// A socket that closed mid-read is a request that never got an answer, as a dropped connection is to fetch.
|
|
204
|
+
if (!reply) return fail(new TypeError("Failed to fetch"));
|
|
205
|
+
if ("error" in reply) return fail(new TypeError(reply.error));
|
|
206
|
+
const bytes = Uint8Array.from(atob(reply.body), (char) => char.charCodeAt(0));
|
|
207
|
+
const empty = bytes.length === 0 || reply.status === 204 || reply.status === 304;
|
|
208
|
+
answer(new Response(empty ? null : bytes, { status: reply.status, statusText: reply.statusText, headers: reply.headers }));
|
|
209
|
+
});
|
|
210
|
+
socket.send(JSON.stringify({ id, ...call } satisfies ShotApiSocketCall));
|
|
211
|
+
}));
|
|
212
|
+
}, { once: true });
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
145
216
|
function installShotCloudApiTransport(): void {
|
|
146
217
|
apiClient.setCookieSessionMode(true);
|
|
218
|
+
let socket: Promise<ShotApiSocketSend | null> | null = null;
|
|
147
219
|
setCloudApiFetchTransport((url, init = {}) => {
|
|
148
220
|
const upstream = new URL(url);
|
|
149
|
-
const proxyUrl =
|
|
221
|
+
const proxyUrl = shotRouteUrl(`${SHOT_API_PROXY_PREFIX}${upstream.pathname}`, SHOT_PAGE_URL);
|
|
150
222
|
proxyUrl.search = upstream.search;
|
|
151
223
|
const headers = new Headers(init.headers);
|
|
152
224
|
// Bun owns the real session cookie. The browser only talks to the local
|
|
153
225
|
// same-origin proxy and never receives or serializes the credential.
|
|
154
226
|
headers.delete("Cookie");
|
|
155
227
|
headers.delete("Origin");
|
|
156
|
-
|
|
228
|
+
const viaFetch = () => window.fetch(proxyUrl, {
|
|
157
229
|
...init,
|
|
158
230
|
headers,
|
|
159
231
|
credentials: "same-origin",
|
|
160
232
|
});
|
|
233
|
+
if ((init.method ?? "GET").toUpperCase() !== "GET" || init.body != null) return viaFetch();
|
|
234
|
+
return trackShotWork((async () => {
|
|
235
|
+
socket ??= openShotApiSocket();
|
|
236
|
+
const send = await socket;
|
|
237
|
+
return await send?.({ path: `${upstream.pathname}${upstream.search}`, headers: [...headers] }, init.signal) ?? await viaFetch();
|
|
238
|
+
})());
|
|
161
239
|
});
|
|
162
240
|
}
|
|
163
241
|
|
|
@@ -168,7 +246,7 @@ function installShotCloudApiTransport(): void {
|
|
|
168
246
|
*/
|
|
169
247
|
function installShotHttpFetchTransport(): void {
|
|
170
248
|
setHttpFetchTransport(async (url, init) => {
|
|
171
|
-
const response = await window.fetch(SHOT_HTTP_BRIDGE_PATH, {
|
|
249
|
+
const response = await window.fetch(shotRouteUrl(SHOT_HTTP_BRIDGE_PATH, SHOT_PAGE_URL), {
|
|
172
250
|
method: "POST",
|
|
173
251
|
headers: { "Content-Type": "application/json" },
|
|
174
252
|
body: JSON.stringify(await toRequestEnvelope(url, init)),
|
|
@@ -190,7 +268,7 @@ async function restoreShotCloudSession(): Promise<void> {
|
|
|
190
268
|
* of the corporate action history come from providers the router merges in.
|
|
191
269
|
*/
|
|
192
270
|
async function requestShotMarketData<T>(operation: string, args: unknown[], signal?: AbortSignal): Promise<T> {
|
|
193
|
-
const response = await window.fetch(SHOT_MARKET_BRIDGE_PATH, {
|
|
271
|
+
const response = await window.fetch(shotRouteUrl(SHOT_MARKET_BRIDGE_PATH, SHOT_PAGE_URL), {
|
|
194
272
|
method: "POST",
|
|
195
273
|
headers: { "Content-Type": "application/json" },
|
|
196
274
|
body: JSON.stringify({ operation, args }),
|
|
@@ -14,7 +14,9 @@ declare global {
|
|
|
14
14
|
* Set by a capture that carries a status line: shows the first of the
|
|
15
15
|
* variants (longest first) that fits the footer, cut in the middle when
|
|
16
16
|
* none does, and resolves to the text shown. `dates` says what the line
|
|
17
|
-
* dates, so the pane's own text that says the same goes.
|
|
17
|
+
* dates, so the pane's own text that says the same goes. With no variants
|
|
18
|
+
* the pane has nothing to date: the footer row is given back to the body
|
|
19
|
+
* and the text shown is empty.
|
|
18
20
|
*/
|
|
19
21
|
__GLOOM_CLI_SHOT_FIT_STATUS__?: (variants: string[], options?: { dates?: ShotLineDates }) => Promise<string>;
|
|
20
22
|
}
|
|
@@ -90,22 +92,28 @@ function footerOverflows(): boolean {
|
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
interface ShotStatusLineState {
|
|
93
|
-
|
|
95
|
+
/** Null once the capture has no line to show: the footer row is gone. */
|
|
96
|
+
text: string | null;
|
|
94
97
|
dates: ShotLineDates;
|
|
95
98
|
/** No variant fit beside the pane's own `as of`, so it gave way. */
|
|
96
99
|
crowded: boolean;
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
function useShotStatusLine(enabled: boolean, hasAsOf: () => boolean): ShotStatusLineState {
|
|
100
|
-
const [text, setText] = useState(PENDING_STATUS);
|
|
103
|
+
const [text, setText] = useState<string | null>(PENDING_STATUS);
|
|
101
104
|
const [dates, setDates] = useState<ShotLineDates>(null);
|
|
102
105
|
const [crowded, setCrowded] = useState(false);
|
|
103
106
|
useEffect(() => {
|
|
104
107
|
if (!enabled) return;
|
|
105
108
|
// What the line says goes before it is fitted, so the line gets its room.
|
|
106
|
-
window.__GLOOM_CLI_SHOT_FIT_STATUS__ = (variants, options) => {
|
|
109
|
+
window.__GLOOM_CLI_SHOT_FIT_STATUS__ = async (variants, options) => {
|
|
107
110
|
setDates(options?.dates ?? null);
|
|
108
111
|
setCrowded(false);
|
|
112
|
+
if (variants.length === 0) {
|
|
113
|
+
setText(null);
|
|
114
|
+
await nextFrames(2);
|
|
115
|
+
return "";
|
|
116
|
+
}
|
|
109
117
|
const show = async (next: string) => {
|
|
110
118
|
setText(next);
|
|
111
119
|
await nextFrames(2);
|
|
@@ -148,7 +156,7 @@ export function PaneShotFrame({ paneId, title, width, height, preserveStatus = f
|
|
|
148
156
|
const { text: statusText, dates, crowded } = useShotStatusLine(statusLine, hasAsOf);
|
|
149
157
|
const datesData = statusLine && dates === "data";
|
|
150
158
|
return <PaneFooterProvider>{(registeredFooter) => {
|
|
151
|
-
const status: PaneFooterSegment[] = statusLine ? [{ id: "shot-status", parts: [{ text: statusText, tone: "label" }] }] : [];
|
|
159
|
+
const status: PaneFooterSegment[] = statusLine && statusText !== null ? [{ id: "shot-status", parts: [{ text: statusText, tone: "label" }] }] : [];
|
|
152
160
|
const info = registeredFooter.info.filter((segment) => (preserveStatus || segment.icon === "warning" || segment.capture)
|
|
153
161
|
&& !(datesPaneData(segment) && (datesData || crowded))
|
|
154
162
|
&& !(segment.quoteStatus && statusLine && dates !== null));
|
|
@@ -36,6 +36,9 @@ export function detachedSnapshotKey(snapshot: DesktopSharedStateSnapshot, paneId
|
|
|
36
36
|
pluginConfig: snapshot.config.pluginConfig,
|
|
37
37
|
disabledPlugins: snapshot.config.disabledPlugins,
|
|
38
38
|
chartPreferences: snapshot.config.chartPreferences,
|
|
39
|
+
// A popped-out window collects Attention Counts on its own, so turning
|
|
40
|
+
// them off in the main window has to reach it.
|
|
41
|
+
telemetry: snapshot.config.telemetry ?? null,
|
|
39
42
|
instances: paneStateIds.map((id) => findPaneInstance(snapshot.config.layout, id) ?? null),
|
|
40
43
|
paneState: Object.fromEntries(paneStateIds.map((id) => [id, snapshot.paneState[id] ?? {}])),
|
|
41
44
|
});
|
|
@@ -53,7 +53,7 @@ import { crashReportsEnabled, installCrashReporter, reportCrash } from "../../..
|
|
|
53
53
|
import { browserDoNotTrack, installWindowCrashListeners } from "../../../telemetry/crash-reports-dom";
|
|
54
54
|
import { forwardAttendedStalls } from "../../../telemetry/stall-reports-dom";
|
|
55
55
|
import { currentTelemetryConfig } from "../../../telemetry/live-config";
|
|
56
|
-
import {
|
|
56
|
+
import { installAttentionCounter } from "../../../telemetry/attention-counts";
|
|
57
57
|
import { installUsageCounter, usageCountsEnabled } from "../../../telemetry/usage-counts";
|
|
58
58
|
import { installWindowUsageFlush } from "../../../telemetry/usage-counts-dom";
|
|
59
59
|
import { cloudSyncController } from "../../../sync/controller";
|
|
@@ -160,7 +160,12 @@ async function boot() {
|
|
|
160
160
|
getInstallId: () => init.telemetry.installId,
|
|
161
161
|
officialPluginIds: loadOfficialPluginIds,
|
|
162
162
|
});
|
|
163
|
-
installAttentionCounter(
|
|
163
|
+
installAttentionCounter({
|
|
164
|
+
allowed: () => !init.telemetry.optedOut && !browserDoNotTrack(),
|
|
165
|
+
launchConfig: () => init.config,
|
|
166
|
+
// A popped-out window does not sync: the main window does, and sends it the settings it pulled.
|
|
167
|
+
...(init.windowKind === "detached" ? { accountSettingsPulled: () => true } : {}),
|
|
168
|
+
});
|
|
164
169
|
// Desktop has no Debug Log: this window's freezes go to the Bun process, which keeps the stall log.
|
|
165
170
|
setStallSink(forwardAttendedStalls(sendElectrobunStall).sink);
|
|
166
171
|
installElectrobunCapabilityStreamClient();
|
|
@@ -31,6 +31,7 @@ type PayloadBuilder = (...args: unknown[]) => Record<string, unknown>;
|
|
|
31
31
|
const assetDataPayloads: Record<string, PayloadBuilder> = {
|
|
32
32
|
canProvide: (ticker, exchange, context) => ({ ticker, exchange, context }),
|
|
33
33
|
primaryMarketSourceName: () => ({}),
|
|
34
|
+
batchesReads: (method) => ({ method }),
|
|
34
35
|
getCachedFinancialsForTargets: (targets, options) => ({ targets, options }),
|
|
35
36
|
// An abort signal does not cross the bridge; the board stops waiting instead.
|
|
36
37
|
getQuotesBatch: (targets, options) => {
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* nothing opened and can show the link instead.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { systemOpenCommand } from "../../utils/external-url";
|
|
10
|
+
|
|
9
11
|
type Env = Readonly<Record<string, string | undefined>>;
|
|
10
12
|
|
|
11
13
|
function present(value: string | undefined): boolean {
|
|
@@ -35,27 +37,31 @@ export function browserOpenCommands(url: string, env: Env, platform: NodeJS.Plat
|
|
|
35
37
|
const forwarded = present(env.BROWSER) ? envBrowserCommand(env.BROWSER!, url) : null;
|
|
36
38
|
|
|
37
39
|
if (platform === "darwin") {
|
|
38
|
-
|
|
40
|
+
const opener = systemOpenCommand(url, platform);
|
|
41
|
+
if (!remote) return opener ? [opener] : [];
|
|
39
42
|
return forwarded ? [forwarded] : [];
|
|
40
43
|
}
|
|
41
44
|
if (platform === "win32") {
|
|
42
|
-
|
|
45
|
+
const opener = systemOpenCommand(url, platform);
|
|
46
|
+
if (!remote) return opener ? [opener] : [];
|
|
43
47
|
return forwarded ? [forwarded] : [];
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
// Linux and the other unixes. WSL has no Linux browser, but Windows can
|
|
47
51
|
// open the link: `wslview` when wslu is installed, else Windows' own handler.
|
|
48
52
|
if (present(env.WSL_DISTRO_NAME) || present(env.WSL_INTEROP)) {
|
|
53
|
+
const windowsOpener = systemOpenCommand(url, "win32");
|
|
49
54
|
return [
|
|
50
55
|
...(forwarded ? [forwarded] : []),
|
|
51
56
|
["wslview", url],
|
|
52
|
-
[
|
|
57
|
+
...(windowsOpener ? [windowsOpener] : []),
|
|
53
58
|
];
|
|
54
59
|
}
|
|
55
60
|
const display = present(env.DISPLAY) || present(env.WAYLAND_DISPLAY);
|
|
56
61
|
// xdg-open reads $BROWSER itself, so a forwarding helper still applies.
|
|
57
62
|
if (remote && !display && !forwarded) return [];
|
|
58
|
-
|
|
63
|
+
const opener = systemOpenCommand(url, platform);
|
|
64
|
+
return opener ? [opener] : [];
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
/** How long an opener may run before it counts as having opened the browser. */
|
|
@@ -25,7 +25,7 @@ function resolveInsets(props: Record<string, unknown>): CellInsets {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export const OpenTuiImageSurface = forwardRef<unknown, ImageSurfaceProps>(function OpenTuiImageSurface(
|
|
28
|
-
{ children, src, alt: _alt, objectFit = "contain", ...props },
|
|
28
|
+
{ children, src, alt: _alt, objectFit = "contain", fallbackWhileLoading: _fallbackWhileLoading, imageBackground: _imageBackground, ...props },
|
|
29
29
|
forwardedRef,
|
|
30
30
|
) {
|
|
31
31
|
const renderer = useNativeRenderer();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { hostname } from "os";
|
|
2
2
|
import { getGloomberbDirs } from "../../data/config/home";
|
|
3
|
-
import { existsSync
|
|
3
|
+
import { existsSync } from "fs";
|
|
4
|
+
import { makePrivateDirSync } from "../../utils/private-files";
|
|
4
5
|
import { App } from "../../app";
|
|
5
6
|
import { getDataDir, initDataDir, loadConfig, setConfigStoreHost } from "../../data/config/store";
|
|
6
7
|
import { applyLanguageFromConfig } from "../../i18n";
|
|
@@ -28,7 +29,7 @@ import { startRemoteControlServer, type RemoteControlServer } from "../../remote
|
|
|
28
29
|
import { createAppServices } from "../../core/app-services";
|
|
29
30
|
import { flushPendingPersistence } from "../../state/persist-scheduler";
|
|
30
31
|
import { loadOfficialPluginIds } from "../../plugins/marketplace/feed";
|
|
31
|
-
import { flushCrashReports, installCrashReporter } from "../../telemetry/crash-reports";
|
|
32
|
+
import { flushCrashReports, installCrashReporter, telemetryOptedOut } from "../../telemetry/crash-reports";
|
|
32
33
|
import {
|
|
33
34
|
CRASH_REPORT_EXIT_FLUSH_MS,
|
|
34
35
|
createNodeCrashReporterHost,
|
|
@@ -38,13 +39,14 @@ import {
|
|
|
38
39
|
} from "../../telemetry/crash-reports-node";
|
|
39
40
|
import { currentTelemetryConfig } from "../../telemetry/live-config";
|
|
40
41
|
import { installUpdateFailureReporter } from "../../telemetry/update-failures";
|
|
42
|
+
import { createStallClocks } from "../../telemetry/stall-clocks";
|
|
41
43
|
import { installStallReporter, recordStall } from "../../telemetry/stall-reports";
|
|
42
44
|
import { createNodeStallReporterHost } from "../../telemetry/stall-reports-node";
|
|
43
|
-
import { setStallSink } from "../../utils/main-thread-stall";
|
|
45
|
+
import { setStallClocks, setStallSink } from "../../utils/main-thread-stall";
|
|
44
46
|
import { createNodeUpdateFailureHost } from "../../telemetry/update-failures-node";
|
|
45
47
|
import { terminalInstallKind } from "../../updater";
|
|
46
48
|
import { flushUsageCounts, installUsageCounter, usageCountsEnabled } from "../../telemetry/usage-counts";
|
|
47
|
-
import {
|
|
49
|
+
import { installAttentionCounter } from "../../telemetry/attention-counts";
|
|
48
50
|
import { starPromptEnvironmentAllows } from "../../app/star-prompt/model";
|
|
49
51
|
import { allowStarPrompt } from "../../app/star-prompt/runtime";
|
|
50
52
|
|
|
@@ -114,7 +116,7 @@ export async function startOpenTuiApp({ externalPlugins, cliLaunchRequest }: Sta
|
|
|
114
116
|
const dataDir = await getDataDir() ?? getGloomberbDirs().data;
|
|
115
117
|
|
|
116
118
|
if (!existsSync(dataDir)) {
|
|
117
|
-
|
|
119
|
+
makePrivateDirSync(dataDir);
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
const config = await measurePerfAsync("startup.opentui.init-data-dir", () => initDataDir(dataDir));
|
|
@@ -129,6 +131,7 @@ export async function startOpenTuiApp({ externalPlugins, cliLaunchRequest }: Sta
|
|
|
129
131
|
}));
|
|
130
132
|
installStallReporter(createNodeStallReporterHost({ getDataDir: () => config.dataDir }));
|
|
131
133
|
setStallSink((event) => recordStall("terminal", event));
|
|
134
|
+
setStallClocks(createStallClocks());
|
|
132
135
|
installUsageCounter({
|
|
133
136
|
surface: "terminal",
|
|
134
137
|
os: describeNodeOs(),
|
|
@@ -136,7 +139,10 @@ export async function startOpenTuiApp({ externalPlugins, cliLaunchRequest }: Sta
|
|
|
136
139
|
getInstallId: () => readOrCreateInstallId(config.dataDir),
|
|
137
140
|
officialPluginIds: loadOfficialPluginIds,
|
|
138
141
|
});
|
|
139
|
-
const stopAttention = installAttentionCounter(
|
|
142
|
+
const stopAttention = installAttentionCounter({
|
|
143
|
+
allowed: () => !telemetryOptedOut(process.env),
|
|
144
|
+
launchConfig: () => config,
|
|
145
|
+
});
|
|
140
146
|
allowStarPrompt(starPromptEnvironmentAllows({
|
|
141
147
|
env: process.env,
|
|
142
148
|
stdinIsTTY: process.stdin.isTTY,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { useTerminalDimensions } from "@opentui/react";
|
|
3
3
|
import { useSyncExternalStore } from "react";
|
|
4
4
|
import { colors } from "../../theme/colors";
|
|
5
|
-
import { createToastStore, type ToastHost, type ToastTone } from "../../ui/toast";
|
|
5
|
+
import { createToastStore, useToastsShown, type ToastHost, type ToastTone } from "../../ui/toast";
|
|
6
6
|
import { useActionShortcut } from "../../ui";
|
|
7
7
|
import { displayWidth } from "../../utils/format";
|
|
8
8
|
|
|
@@ -26,6 +26,7 @@ function toneIcon(tone: ToastTone): string {
|
|
|
26
26
|
function ToastViewport({ position = "bottom-right" }: { position?: string }) {
|
|
27
27
|
const dimensions = useTerminalDimensions();
|
|
28
28
|
const shownToasts = useSyncExternalStore(toastStore.subscribe, toastStore.getSnapshot, toastStore.getSnapshot);
|
|
29
|
+
useToastsShown(shownToasts);
|
|
29
30
|
const actionShortcut = useActionShortcut("notification-action");
|
|
30
31
|
const maxWidth = Math.max(1, Math.min(60, dimensions.width - 4));
|
|
31
32
|
const placement = position.startsWith("top") ? { top: 1 } : { bottom: 1 };
|
package/src/series/registry.ts
CHANGED
|
@@ -23,6 +23,24 @@ export function seriesExpression(definition: SeriesDefinition): string {
|
|
|
23
23
|
return definition.expression ?? loadedSeriesExpression(definition);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* The one catalog series a bare word is an alias of (`hashrate` is
|
|
28
|
+
* `bitcoin:hashrate`), for a chart typed with the word where a ticker goes.
|
|
29
|
+
* Only the words a series lists as its `aliases`, never any other id or
|
|
30
|
+
* name, and none that two series claim.
|
|
31
|
+
*/
|
|
32
|
+
export function seriesNamedBy(definitions: Iterable<SeriesDefinition>, word: string): SeriesDefinition | undefined {
|
|
33
|
+
const wanted = word.trim().toLowerCase();
|
|
34
|
+
if (!wanted) return undefined;
|
|
35
|
+
let found: SeriesDefinition | undefined;
|
|
36
|
+
for (const definition of definitions) {
|
|
37
|
+
if (definition.input || !definition.aliases?.includes(wanted)) continue;
|
|
38
|
+
if (found && found.id !== definition.id) return undefined;
|
|
39
|
+
found = definition;
|
|
40
|
+
}
|
|
41
|
+
return found;
|
|
42
|
+
}
|
|
43
|
+
|
|
26
44
|
/**
|
|
27
45
|
* The capability series id a loaded series charts under: its name, one of its
|
|
28
46
|
* lines after a tilde, and for a series with an input, the symbol after a
|
package/src/series/standalone.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { CapabilityInvoker } from "../capabilities/types";
|
|
|
4
4
|
import type { GloomPlugin } from "../types/plugin";
|
|
5
5
|
import { resolveRegisteredSeries } from "./capability";
|
|
6
6
|
import { readSeriesFolder } from "./files";
|
|
7
|
-
import { SeriesRegistry, seriesExpression } from "./registry";
|
|
7
|
+
import { SeriesRegistry, seriesExpression, seriesNamedBy } from "./registry";
|
|
8
8
|
import type { SeriesDefinition } from "./types";
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -75,3 +75,12 @@ export function standaloneSeriesCatalog(
|
|
|
75
75
|
return byExpression.get(expression);
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
|
+
|
|
79
|
+
/** The catalog series a bare word is an alias of (`hashrate`), where no plugin runtime runs; see `seriesNamedBy`. */
|
|
80
|
+
export function standaloneSeriesNamed(
|
|
81
|
+
plugins: readonly GloomPlugin[],
|
|
82
|
+
disabledPlugins: readonly string[] = [],
|
|
83
|
+
): (word: string) => SeriesDefinition | undefined {
|
|
84
|
+
const definitions = () => plugins.filter((plugin) => !disabledPlugins.includes(plugin.id)).flatMap((plugin) => plugin.series ?? []);
|
|
85
|
+
return (word) => seriesNamedBy(definitions(), word);
|
|
86
|
+
}
|
package/src/series/types.ts
CHANGED
|
@@ -128,6 +128,13 @@ interface SeriesDefinitionBase {
|
|
|
128
128
|
group?: string;
|
|
129
129
|
/** Extra words the catalog search matches, such as a ticker the series is often read against. */
|
|
130
130
|
tags?: readonly string[];
|
|
131
|
+
/**
|
|
132
|
+
* Words `fn G` charts the series by where a ticker goes (`hashrate`), each
|
|
133
|
+
* in lower case: only when no listing answers to the word, the quote source
|
|
134
|
+
* says it is not a ticker, and no other series has the word. A series with
|
|
135
|
+
* an `input` takes none.
|
|
136
|
+
*/
|
|
137
|
+
aliases?: readonly string[];
|
|
131
138
|
/** One sentence for the catalog search and for `gloomberb` automation. */
|
|
132
139
|
description?: string;
|
|
133
140
|
/**
|
package/src/series/validate.ts
CHANGED
|
@@ -23,11 +23,13 @@ const RATE_UNITS = ["%", "bp", "x", "ratio", "index", "score", "points"] as cons
|
|
|
23
23
|
const QUANTITY_UNITS = [
|
|
24
24
|
"number", "count", "units", "shares", "contracts", "days", "people", "jobs", "posts", "views", "hours",
|
|
25
25
|
"barrels", "barrels/day", "cubic feet", "MW", "MWh", "TWh", "GWh/day", "tons", "ounces",
|
|
26
|
+
// A hash rate: exahashes per second.
|
|
27
|
+
"EH/s",
|
|
26
28
|
// The instrument's own currency, for a field that applies to any ticker.
|
|
27
29
|
"currency", "currency/share",
|
|
28
30
|
] as const;
|
|
29
|
-
/** What a price is quoted per: `USD/barrel`, `USD/GPU-hour`. */
|
|
30
|
-
const PER_UNITS = ["share", "barrel", "gallon", "MMBtu", "MWh", "ounce", "ton", "tCO2", "hour", "GPU-hour"] as const;
|
|
31
|
+
/** What a price is quoted per: `USD/barrel`, `USD/GPU-hour`, a miner's `USD/PH/s/day`. */
|
|
32
|
+
const PER_UNITS = ["share", "barrel", "gallon", "MMBtu", "MWh", "ounce", "ton", "tCO2", "hour", "GPU-hour", "PH/s/day"] as const;
|
|
31
33
|
const SCALES = ["thousand", "million", "billion", "trillion"] as const;
|
|
32
34
|
|
|
33
35
|
const RATE_SET: ReadonlySet<string> = new Set(RATE_UNITS);
|
|
@@ -141,6 +143,13 @@ export function validateSeriesDefinition(definition: SeriesDefinition, context:
|
|
|
141
143
|
if (!Array.isArray(definition.tags) || definition.tags.length > 16) fail(id, "tags must be a list of at most 16 words.");
|
|
142
144
|
for (const tag of definition.tags) checkLabel(id, "tag", tag, 40, context, false);
|
|
143
145
|
}
|
|
146
|
+
if (definition.aliases !== undefined) {
|
|
147
|
+
if (!Array.isArray(definition.aliases) || definition.aliases.length > 4) fail(id, "aliases must be a list of at most 4 words.");
|
|
148
|
+
if (definition.input !== undefined) fail(id, "a series with an input takes no aliases: the word would stand for a ticker.");
|
|
149
|
+
for (const alias of definition.aliases) {
|
|
150
|
+
if (typeof alias !== "string" || !/^[a-z][a-z-]{3,31}$/.test(alias)) fail(id, `alias "${String(alias)}" must be one lower-case word of 4 to 32 letters.`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
144
153
|
if (definition.url !== undefined && (typeof definition.url !== "string" || !/^https?:\/\/\S+$/.test(definition.url))) {
|
|
145
154
|
fail(id, "url must be an http or https address.");
|
|
146
155
|
}
|
|
@@ -18,6 +18,7 @@ import type {
|
|
|
18
18
|
EarningsEvent,
|
|
19
19
|
SecFilingDocument,
|
|
20
20
|
SecFilingItem,
|
|
21
|
+
BatchedReadMethod,
|
|
21
22
|
MarketDataRequestContext,
|
|
22
23
|
QuoteBatchOptions,
|
|
23
24
|
QuoteBatchResult,
|
|
@@ -48,7 +49,7 @@ import { canonicalExchange, canonicalTickerKey, parsePublicTickerKey } from "../
|
|
|
48
49
|
import { normalizePriceHistory, priceHistoryIntervalMs, reachesLatestSettledSession } from "../../utils/price-history";
|
|
49
50
|
import { createProviderMiss, ProviderBusyError, providerMissReason, StaleHistoryError } from "../provider-errors";
|
|
50
51
|
import { parseListingDelisting } from "../../market-data/quotes/delisting";
|
|
51
|
-
import { isRetryableApiError } from "../../api-client/errors";
|
|
52
|
+
import { isRetryableApiError, markAskedTwice, sourceDidNotAnswer, wasAskedTwice } from "../../api-client/errors";
|
|
52
53
|
import { assertSecRegistrantMatches } from "../sec-registrant";
|
|
53
54
|
import { nonUsSecListingVenue } from "../../utils/sec";
|
|
54
55
|
import { publicListingTarget } from "../listing-target";
|
|
@@ -66,6 +67,7 @@ import {
|
|
|
66
67
|
GLOOMBERB_CLOUD_PROVIDER_ID,
|
|
67
68
|
formatCloudDateTime,
|
|
68
69
|
formatCloudHistoryEnd,
|
|
70
|
+
isCloudSourceFailure,
|
|
69
71
|
isEmptyCloudStatus,
|
|
70
72
|
mapBatchError,
|
|
71
73
|
NOT_FOUND_REASON,
|
|
@@ -86,6 +88,7 @@ import {
|
|
|
86
88
|
boardSearchesFrom,
|
|
87
89
|
} from "./perpetuals";
|
|
88
90
|
import { predictionMarketListing } from "../../tickers/prediction-listing";
|
|
91
|
+
import { cleanListingName } from "../../tickers/company-name";
|
|
89
92
|
import { settleWithin } from "../../utils/async-deadline";
|
|
90
93
|
import { cloudSearchBatch, cloudTickerChangesBatch } from "./batched-lookups";
|
|
91
94
|
|
|
@@ -127,6 +130,7 @@ function mapCloudSecFiling(item: {
|
|
|
127
130
|
primaryDocumentUrl?: string;
|
|
128
131
|
fileNumber?: string;
|
|
129
132
|
fundSeries?: boolean;
|
|
133
|
+
pressReleaseUrl?: string;
|
|
130
134
|
}): SecFilingItem {
|
|
131
135
|
return {
|
|
132
136
|
accessionNumber: item.accessionNumber,
|
|
@@ -144,9 +148,20 @@ function mapCloudSecFiling(item: {
|
|
|
144
148
|
// Absent from an older server: no file number is not the same as an empty one.
|
|
145
149
|
...(typeof item.fileNumber === "string" ? { fileNumber: item.fileNumber } : {}),
|
|
146
150
|
...(typeof item.fundSeries === "boolean" ? { fundSeries: item.fundSeries } : {}),
|
|
151
|
+
...(typeof item.pressReleaseUrl === "string" && /^https:\/\/www\.sec\.gov\//.test(item.pressReleaseUrl)
|
|
152
|
+
? { pressReleaseUrl: item.pressReleaseUrl } : {}),
|
|
147
153
|
};
|
|
148
154
|
}
|
|
149
155
|
|
|
156
|
+
/** Search rows with a venue's fixed-width name cleaned of the stray code after its spaces ("Americold Realty Trust Inc. R"). */
|
|
157
|
+
function withCleanNames<T extends { name: string }>(rows: T[]): T[] {
|
|
158
|
+
return rows.map((row) => {
|
|
159
|
+
if (typeof row.name !== "string") return row;
|
|
160
|
+
const name = cleanListingName(row.name);
|
|
161
|
+
return name === row.name ? row : { ...row, name };
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
150
165
|
function isCloudProviderMiss(error: unknown): boolean {
|
|
151
166
|
const message = error instanceof Error ? error.message : String(error);
|
|
152
167
|
return CLOUD_PROVIDER_MISS_PATTERNS.some((pattern) => pattern.test(message));
|
|
@@ -420,6 +435,8 @@ function unwrapRequiredCloudResponse<T>(response: CloudMarketResponse<T>, messag
|
|
|
420
435
|
if (isEmptyCloudStatus(response.status)) {
|
|
421
436
|
throw createProviderMiss(response.reasonCode ?? message, response.message, { notFound: response.reasonCode === NOT_FOUND_REASON });
|
|
422
437
|
}
|
|
438
|
+
// The service's own source failed on a listing read asked twice (`readListing`): not an answer that the listing has nothing.
|
|
439
|
+
if (isCloudSourceFailure(response.status) && wasAskedTwice(response)) throw markAskedTwice(sourceDidNotAnswer(response.reasonCode ?? message));
|
|
423
440
|
throw new Error(response.reasonCode ?? message);
|
|
424
441
|
}
|
|
425
442
|
|
|
@@ -647,7 +664,7 @@ export class GloomberbCloudProvider implements AssetDataProvider {
|
|
|
647
664
|
const pending = searchPerpetualMarkets(query).catch(logFailure("sources.gloomberb-cloud.index", "Searching perpetual markets failed", [] as InstrumentSearchResult[]));
|
|
648
665
|
// A crypto exchange's line of a coin pair, which no source quotes, is offered as the crypto venue's listing.
|
|
649
666
|
const listings = await withCloudFallback(
|
|
650
|
-
async () => withExchangeLinesAsCoinListings(await apiClient.searchInstruments(query, context?.limit ?? 10)),
|
|
667
|
+
async () => withExchangeLinesAsCoinListings(withCleanNames(await apiClient.searchInstruments(query, context?.limit ?? 10))),
|
|
651
668
|
"Cloud search is unavailable",
|
|
652
669
|
).catch((error: unknown) => {
|
|
653
670
|
// Rate limited or overloaded: the search was not run, which is not a search that found nothing.
|
|
@@ -764,7 +781,7 @@ export class GloomberbCloudProvider implements AssetDataProvider {
|
|
|
764
781
|
async searchBatch(queries: string[], options: { limit?: number; signal?: AbortSignal } = {}): Promise<Map<string, InstrumentSearchResult[]> | null> {
|
|
765
782
|
const answers = await cloudSearchBatch(queries, options);
|
|
766
783
|
if (!answers) return null;
|
|
767
|
-
for (const [query, rows] of answers) answers.set(query, withExchangeLinesAsCoinListings(rows));
|
|
784
|
+
for (const [query, rows] of answers) answers.set(query, withExchangeLinesAsCoinListings(withCleanNames(rows)));
|
|
768
785
|
// Perpetual markets lead each answer, as `search` gives them, so a symbol in a list resolves as it does alone:
|
|
769
786
|
// one read of the board answers every query's search of it. A query that found no listing is searched again
|
|
770
787
|
// on its own, which finds them then; a board that does not answer leaves them out, as a search does.
|
|
@@ -818,7 +835,8 @@ export class GloomberbCloudProvider implements AssetDataProvider {
|
|
|
818
835
|
exchange = target.exchange ?? "";
|
|
819
836
|
const request = toHistoryRequest(range);
|
|
820
837
|
const { response, extendedHours } = await withCloudFallback(
|
|
821
|
-
() => loadCloudHistory(request.interval, (session) => apiClient.getCloudHistory(target.symbol, exchange, { ...request, ...(session ? { session } : {}) }
|
|
838
|
+
() => loadCloudHistory(request.interval, (session) => apiClient.getCloudHistory(target.symbol, exchange, { ...request, ...(session ? { session } : {}) },
|
|
839
|
+
{ batch: _context?.batch }), _context),
|
|
822
840
|
`Cloud chart data is unavailable for ${ticker}`,
|
|
823
841
|
);
|
|
824
842
|
return mapCloudPriceHistory(response, target.symbol, exchange, request.interval, subtractTimeRange(new Date(), range), undefined, extendedHours);
|
|
@@ -905,11 +923,16 @@ export class GloomberbCloudProvider implements AssetDataProvider {
|
|
|
905
923
|
return mapCloudPriceHistory(response, target.symbol, exchange, interval, startDate, endDate, extendedHours);
|
|
906
924
|
}
|
|
907
925
|
|
|
926
|
+
/** Trailing history and dated expiries asked with others (`batch`) go out as batch requests while the service reads them so. */
|
|
927
|
+
batchesReads(method: BatchedReadMethod): boolean {
|
|
928
|
+
return apiClient.batchesReads(method === "getPriceHistory" ? "history" : "options");
|
|
929
|
+
}
|
|
930
|
+
|
|
908
931
|
async getOptionsChain(ticker: string, exchange?: string, expirationDate?: number, _context?: MarketDataRequestContext): Promise<OptionsChain> {
|
|
909
932
|
rejectPerpetual(ticker, exchange, "options chains");
|
|
910
933
|
const target = cloudInstrumentTarget(ticker, exchange);
|
|
911
934
|
return withCloudFallback(async () => {
|
|
912
|
-
const response = await apiClient.getCloudOptionsChain(target.symbol, target.exchange, expirationDate);
|
|
935
|
+
const response = await apiClient.getCloudOptionsChain(target.symbol, target.exchange, expirationDate, { batch: _context?.batch });
|
|
913
936
|
const chain = unwrapRequiredCloudResponse(
|
|
914
937
|
response,
|
|
915
938
|
`Cloud options chains are unavailable for ${ticker}`,
|