@gloom-sh/terminal 0.19.1 → 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 +2 -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 +58 -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/runtime/startup.ts +3 -1
- package/src/app.tsx +23 -1
- package/src/architecture/core-builtin-allowlist.json +2 -1
- package/src/capabilities/chart-series.ts +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 +101 -27
- package/src/cli/commands/news-search.ts +24 -5
- package/src/cli/commands/overview.ts +14 -10
- package/src/cli/commands/search.ts +84 -25
- 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 +6 -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/glossary.ts +2 -1
- package/src/cli/help.ts +1 -1
- package/src/cli/history-export.ts +22 -2
- package/src/cli/history-rows.ts +189 -11
- package/src/cli/index.ts +18 -1
- package/src/cli/known-gaps.ts +51 -5
- package/src/cli/listing-arg.ts +63 -10
- 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 +49 -6
- 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 +51 -6
- package/src/components/command-bar/selection-runtime.ts +12 -29
- package/src/components/command-bar/surface/index.tsx +9 -3
- package/src/components/company-logo.tsx +2 -1
- package/src/components/layout/detached-pane-shell.tsx +2 -2
- package/src/components/layout/header.tsx +10 -5
- package/src/components/layout/market-summary.ts +25 -7
- package/src/components/layout/pane/footer/index.tsx +8 -2
- package/src/components/layout/pane/footer/model.ts +73 -6
- package/src/components/layout/pane/header.tsx +2 -2
- package/src/components/layout/pane/status-footer.ts +2 -1
- 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 +4 -2
- package/src/components/onboarding/onboarding-keyboard.ts +8 -5
- package/src/components/onboarding/onboarding-wizard.tsx +60 -26
- 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/display.tsx +16 -5
- package/src/components/ui/external-link.tsx +3 -6
- package/src/components/ui/icon.tsx +16 -3
- package/src/components/ui/logo-mark.tsx +8 -5
- package/src/components/ui/stat-grid.tsx +263 -19
- package/src/core/app-services.ts +11 -2
- package/src/data/app-persistence.ts +9 -7
- package/src/data/config/store/node.ts +11 -5
- package/src/data/config/store/normalize.ts +16 -3
- package/src/data/plugin-cache.ts +15 -0
- package/src/data/plugin-state-store.ts +81 -24
- package/src/data/profile-permissions.ts +47 -0
- package/src/data/resource-store.ts +165 -90
- package/src/data/session-store.ts +31 -8
- package/src/data/sqlite/access.ts +324 -0
- package/src/data/sqlite/checkpointer.ts +285 -0
- package/src/data/sqlite/database.ts +80 -4
- package/src/data/sqlite/retry.ts +50 -9
- package/src/data/ticker-store.ts +57 -23
- 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/market-data/realized-volatility.ts +4 -2
- 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/auto-update-node.ts +10 -6
- 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/central-bank-rates/series.ts +2 -3
- package/src/plugins/builtin/changelog/index.tsx +3 -2
- package/src/plugins/builtin/chart-composer/cli-options.ts +16 -0
- package/src/plugins/builtin/chart-composer/headless-schema.ts +10 -0
- 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 +30 -2
- package/src/plugins/builtin/chart-composer/series-expression.ts +3 -2
- package/src/plugins/builtin/chart-composer/session-range.ts +5 -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/client.ts +4 -1
- 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/client.ts +28 -2
- package/src/plugins/builtin/cot/headless.ts +4 -1
- package/src/plugins/builtin/cot/index.tsx +5 -4
- package/src/plugins/builtin/cot/model.ts +58 -7
- package/src/plugins/builtin/cot/pane.tsx +6 -2
- package/src/plugins/builtin/cpi/client.ts +6 -5
- 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/series.ts +3 -4
- package/src/plugins/builtin/credit-boards/sovr-pane.tsx +9 -4
- package/src/plugins/builtin/credit-conditions/client.ts +22 -14
- package/src/plugins/builtin/crypto-board/bitcoin-network.ts +143 -0
- package/src/plugins/builtin/crypto-board/client.ts +5 -5
- 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 +6 -4
- 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/debt-maturities/series.ts +3 -3
- 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/client.ts +6 -5
- package/src/plugins/builtin/doe/headless.ts +16 -3
- package/src/plugins/builtin/doe/index.tsx +5 -4
- package/src/plugins/builtin/doe/prices.ts +2 -3
- package/src/plugins/builtin/doe/series.ts +4 -4
- package/src/plugins/builtin/earnings/board.tsx +2 -1
- package/src/plugins/builtin/earnings/history.tsx +2 -1
- package/src/plugins/builtin/earnings-calls/calendar.tsx +10 -2
- package/src/plugins/builtin/earnings-calls/data.ts +19 -11
- package/src/plugins/builtin/earnings-calls/pane.tsx +27 -14
- 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/detail-view.tsx +16 -2
- 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/executives/data.ts +6 -1
- package/src/plugins/builtin/executives/pane.tsx +9 -21
- package/src/plugins/builtin/exposure/holdings.ts +35 -13
- package/src/plugins/builtin/exposure/index.tsx +1 -2
- package/src/plugins/builtin/fear-greed/cache.ts +4 -0
- 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/fear-greed/series.ts +6 -4
- 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 +19 -3
- 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/gpu/series.ts +2 -3
- package/src/plugins/builtin/help/index.tsx +1 -1
- package/src/plugins/builtin/holders/beneficial-view.tsx +1 -2
- 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/client.ts +4 -1
- 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/client.ts +1 -0
- package/src/plugins/builtin/market-movers/index.tsx +16 -5
- package/src/plugins/builtin/market-movers/screener.ts +23 -6
- package/src/plugins/builtin/market-movers/session-body.tsx +6 -2
- 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/money-markets/series.ts +3 -5
- 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/gas-client.ts +2 -1
- package/src/plugins/builtin/power/index.tsx +1 -2
- package/src/plugins/builtin/power/prices-client.ts +3 -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/risk-factors/data.ts +8 -4
- package/src/plugins/builtin/risk-factors/index.tsx +3 -1
- package/src/plugins/builtin/risk-factors/pane.tsx +5 -8
- 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/scanner/lock-note.ts +26 -3
- 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 +73 -8
- 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/fred-points.ts +6 -1
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +17 -0
- package/src/plugins/builtin/shared/pro-lock-note.ts +23 -8
- package/src/plugins/builtin/shared/series-detail-chart.tsx +4 -2
- package/src/plugins/builtin/shared/series-warning.ts +13 -4
- 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/short-volume/series.ts +2 -3
- package/src/plugins/builtin/social-mentions/client.ts +15 -10
- package/src/plugins/builtin/social-mentions/index.tsx +1 -2
- package/src/plugins/builtin/social-mentions/pane.tsx +2 -1
- package/src/plugins/builtin/social-mentions/series.ts +2 -3
- 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 +127 -29
- package/src/plugins/builtin/thirteenf/data.ts +165 -35
- package/src/plugins/builtin/thirteenf/index.tsx +2 -4
- package/src/plugins/builtin/thirteenf/model.ts +2 -0
- package/src/plugins/builtin/thirteenf/overlap-pane.tsx +5 -1
- package/src/plugins/builtin/thirteenf/pane.tsx +41 -16
- package/src/plugins/builtin/thirteenf/types.ts +14 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +22 -6
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +81 -11
- package/src/plugins/builtin/ticker-detail/financials/model.ts +35 -2
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +53 -18
- package/src/plugins/builtin/ticker-detail/headless.ts +70 -11
- 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/cache.ts +20 -8
- package/src/plugins/builtin/treasury-auctions/client.ts +3 -3
- package/src/plugins/builtin/treasury-auctions/headless.ts +3 -1
- package/src/plugins/builtin/treasury-auctions/pane.tsx +10 -4
- 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/client.ts +43 -16
- package/src/plugins/builtin/volatility/headless.ts +5 -2
- package/src/plugins/builtin/volatility/index.tsx +1 -2
- package/src/plugins/builtin/volatility/model.ts +17 -6
- package/src/plugins/builtin/volatility/pane.tsx +6 -2
- package/src/plugins/builtin/volatility/series.ts +2 -1
- 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/client.ts +4 -6
- 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 +64 -42
- 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 +5 -2
- 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/detached-windows.ts +23 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +12 -3
- 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/attention.ts +54 -0
- package/src/renderers/electrobun/bun/window/create.ts +6 -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/electrobun/view/ui-host.tsx +9 -2
- 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/resolve.ts +2 -1
- package/src/series/standalone.ts +10 -1
- package/src/series/types.ts +12 -0
- package/src/series/validate.ts +11 -2
- package/src/sources/gloomberb-cloud/fred-series.ts +5 -3
- 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 +25 -9
- package/src/telemetry/stall-reports.ts +218 -13
- package/src/test-support/outage.ts +8 -0
- package/src/test-support/preload.ts +9 -0
- package/src/tickers/company-name-listing.ts +59 -29
- package/src/tickers/company-name.ts +84 -1
- package/src/tickers/listing-fate.ts +14 -3
- package/src/tickers/saved-instrument.ts +17 -0
- package/src/tickers/search/home-listing.ts +104 -8
- package/src/tickers/search/index.ts +5 -2
- 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/market.ts +5 -3
- package/src/time-series/official-close.ts +159 -0
- package/src/time-series/price-comparison.ts +75 -8
- package/src/time-series/resolve.ts +32 -4
- 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/time-series/types.ts +7 -0
- package/src/types/config.ts +18 -2
- package/src/types/data-provider.ts +23 -0
- package/src/types/financials.ts +40 -3
- package/src/types/headless.ts +42 -3
- package/src/types/plugin.ts +32 -3
- package/src/ui/host.tsx +17 -1
- 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/financial-statements.ts +73 -16
- package/src/utils/fundamentals.ts +17 -5
- package/src/utils/futures-generic.ts +4 -4
- package/src/utils/http-proxy-upstream.ts +124 -0
- package/src/utils/lease-liabilities.ts +54 -0
- package/src/utils/main-thread-stall.ts +122 -5
- package/src/utils/perf-marks.ts +58 -12
- package/src/utils/price-history-integrity.ts +47 -1
- 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/time-series/studies.ts +0 -1628
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TheBuildout's facilities as a layer of the world map: data centers, fabs,
|
|
3
|
+
* power plants, mines and the plants around them, read from the dataset's own
|
|
4
|
+
* map of located sites. A map linked to TheBuildout (or to any list) shows the
|
|
5
|
+
* selected company's sites, owned or not, or every site owned by a company in
|
|
6
|
+
* the selected theme. The dataset answers with what this account may see, so
|
|
7
|
+
* a theme's sites follow the same free and Pro split as the theme's companies.
|
|
8
|
+
*/
|
|
9
|
+
import type {
|
|
10
|
+
GeoBbox,
|
|
11
|
+
GeoColumn,
|
|
12
|
+
GeoEntitiesPayload,
|
|
13
|
+
GeoEntityPayload,
|
|
14
|
+
GeoEntityRow,
|
|
15
|
+
GeoFeature,
|
|
16
|
+
GeoFeaturesPayload,
|
|
17
|
+
GeoLayerInfo,
|
|
18
|
+
GeoPropValue,
|
|
19
|
+
} from "../../../api-client/geo";
|
|
20
|
+
import type { LocalEntityQuery, LocalFeatureQuery, LocalGeoLayer } from "../world-venue-map/local-layers";
|
|
21
|
+
import { createTtlCache } from "../../../data/ttl-cache";
|
|
22
|
+
import { type BuildoutReader, buildoutApi, getBuildoutReader } from "./model";
|
|
23
|
+
|
|
24
|
+
const FACILITIES_LAYER_ID = "facilities";
|
|
25
|
+
|
|
26
|
+
interface WorldMapPoint {
|
|
27
|
+
id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
type: string | null;
|
|
30
|
+
siteRoles?: string[] | null;
|
|
31
|
+
physicalForms?: string[] | null;
|
|
32
|
+
capabilities?: string[] | null;
|
|
33
|
+
countryIso?: string | null;
|
|
34
|
+
ownerCompanyId?: string | null;
|
|
35
|
+
ownerTicker?: string | null;
|
|
36
|
+
ownerName?: string | null;
|
|
37
|
+
powerCapacity?: string | null;
|
|
38
|
+
constructionActivity?: number | null;
|
|
39
|
+
relevance?: { category?: string | null; score?: number | null; tier?: string | null; reasons?: string[] | null } | null;
|
|
40
|
+
lat: number;
|
|
41
|
+
lng: number;
|
|
42
|
+
coordinateSource?: string | null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface WorldMapPayload {
|
|
46
|
+
generatedAt?: string | null;
|
|
47
|
+
points?: WorldMapPoint[];
|
|
48
|
+
scope?: { kind?: string; id?: string | null; ticker?: string | null } | null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface FacilitySet {
|
|
52
|
+
asOf: string | null;
|
|
53
|
+
rows: GeoEntityRow[];
|
|
54
|
+
byId: Map<string, { row: GeoEntityRow; point: WorldMapPoint }>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const COLUMNS: GeoColumn[] = [
|
|
58
|
+
{ key: "label", label: "Facility" },
|
|
59
|
+
{ key: "type", label: "Type" },
|
|
60
|
+
{ key: "mw", label: "MW", align: "right", format: "int" },
|
|
61
|
+
{ key: "owner", label: "Owner" },
|
|
62
|
+
{ key: "country", label: "Country" },
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
const INFO: GeoLayerInfo = {
|
|
66
|
+
id: FACILITIES_LAYER_ID,
|
|
67
|
+
name: "Facilities",
|
|
68
|
+
group: "infrastructure",
|
|
69
|
+
geometry: "point",
|
|
70
|
+
cadence: "daily",
|
|
71
|
+
refreshSeconds: null,
|
|
72
|
+
asOf: null,
|
|
73
|
+
count: null,
|
|
74
|
+
status: "ok",
|
|
75
|
+
access: "free",
|
|
76
|
+
columns: COLUMNS,
|
|
77
|
+
series: [],
|
|
78
|
+
defaultVisible: false,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/** Sites with no use to a map of the build-out: head offices and rows the dataset has not typed. */
|
|
82
|
+
const LEFT_OUT = new Set(["office", ""]);
|
|
83
|
+
const ALL_TTL_MS = 30 * 60_000;
|
|
84
|
+
const SCOPED_TTL_MS = 10 * 60_000;
|
|
85
|
+
const THEME_PAGE = 200;
|
|
86
|
+
|
|
87
|
+
const TYPE_WORDS: Record<string, string> = {
|
|
88
|
+
"Manufacturing Plant": "Plant",
|
|
89
|
+
"Semiconductor Fab": "Fab",
|
|
90
|
+
"Power Station": "Power plant",
|
|
91
|
+
"Advanced Packaging": "Packaging",
|
|
92
|
+
"Energy Storage Facility": "Storage",
|
|
93
|
+
"R&D Center": "R&D",
|
|
94
|
+
"Logistics Hub": "Logistics",
|
|
95
|
+
"Transmission Line": "Transmission",
|
|
96
|
+
"Utility Network": "Utility",
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
function entityKind(type: string | null): string {
|
|
100
|
+
switch (type) {
|
|
101
|
+
case "Data Center": return "data-center";
|
|
102
|
+
case "Power Station":
|
|
103
|
+
case "Substation":
|
|
104
|
+
case "Energy Storage Facility": return "power-plant";
|
|
105
|
+
case "Semiconductor Fab":
|
|
106
|
+
case "Advanced Packaging": return "fab";
|
|
107
|
+
default: return "facility";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** "300.00 MW" reads 300; a site the dataset has no figure for has none. */
|
|
112
|
+
function megawatts(value: string | null | undefined): number | null {
|
|
113
|
+
const match = value?.replace(/,/g, "").match(/([\d.]+)\s*(GW|MW|KW)/i);
|
|
114
|
+
if (!match) return null;
|
|
115
|
+
const number = Number(match[1]);
|
|
116
|
+
if (!Number.isFinite(number) || number <= 0) return null;
|
|
117
|
+
const unit = match[2]!.toUpperCase();
|
|
118
|
+
const mw = unit === "GW" ? number * 1000 : unit === "KW" ? number / 1000 : number;
|
|
119
|
+
return mw >= 10 ? Math.round(mw) : Math.round(mw * 10) / 10;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const PLACED_BY: Record<string, string> = { geocode: "Address", metadata: "Site record", boundary: "Traced boundary" };
|
|
123
|
+
|
|
124
|
+
function toRow(point: WorldMapPoint): GeoEntityRow {
|
|
125
|
+
const ticker = point.ownerTicker?.trim();
|
|
126
|
+
return {
|
|
127
|
+
id: point.id,
|
|
128
|
+
label: point.name,
|
|
129
|
+
lon: point.lng,
|
|
130
|
+
lat: point.lat,
|
|
131
|
+
props: {
|
|
132
|
+
type: point.type ? TYPE_WORDS[point.type] ?? point.type : null,
|
|
133
|
+
mw: megawatts(point.powerCapacity),
|
|
134
|
+
owner: point.ownerName ?? null,
|
|
135
|
+
country: point.countryIso ?? null,
|
|
136
|
+
},
|
|
137
|
+
...(ticker ? { tickers: [{ symbol: ticker, via: "owner" as const, confidence: "reviewed" as const }] } : {}),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** The score the dataset gives a site for the build-out, plus a little for its size, so a capped answer keeps what matters. */
|
|
142
|
+
function rank(point: WorldMapPoint): number {
|
|
143
|
+
const mw = megawatts(point.powerCapacity) ?? 0;
|
|
144
|
+
return (point.relevance?.score ?? 0) + Math.min(20, Math.log10(1 + mw) * 6);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function facilitySet(payload: WorldMapPayload, keep: (point: WorldMapPoint) => boolean = () => true): FacilitySet {
|
|
148
|
+
const points = (payload.points ?? []).filter((point) => (
|
|
149
|
+
Number.isFinite(point.lat) && Number.isFinite(point.lng) && !LEFT_OUT.has((point.type ?? "").toLowerCase()) && keep(point)
|
|
150
|
+
)).sort((left, right) => rank(right) - rank(left));
|
|
151
|
+
const byId = new Map<string, { row: GeoEntityRow; point: WorldMapPoint }>();
|
|
152
|
+
const rows = points.map((point) => {
|
|
153
|
+
const row = toRow(point);
|
|
154
|
+
byId.set(row.id, { row, point });
|
|
155
|
+
return row;
|
|
156
|
+
});
|
|
157
|
+
return { asOf: payload.generatedAt ?? null, rows, byId };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Every entry is one reader's: the free set is the same for everyone signed
|
|
161
|
+
// out or without Buildout, and a token's answer belongs to its account. A
|
|
162
|
+
// sign-out, a plan that ends or one that starts reads under a new key.
|
|
163
|
+
const allSites = createTtlCache<FacilitySet>({ ttlMs: ALL_TTL_MS, maxEntries: 4 });
|
|
164
|
+
const scopedSites = createTtlCache<FacilitySet>({ ttlMs: SCOPED_TTL_MS, maxEntries: 64 });
|
|
165
|
+
const themeCompanies = createTtlCache<Set<string>>({ ttlMs: ALL_TTL_MS, maxEntries: 64 });
|
|
166
|
+
|
|
167
|
+
function readerKey(reader: BuildoutReader): string {
|
|
168
|
+
return reader.token ? `pro:${reader.account}` : "free";
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function loadAll(reader: BuildoutReader): Promise<FacilitySet> {
|
|
172
|
+
return allSites.load(readerKey(reader), async () => facilitySet(await buildoutApi<WorldMapPayload>("/sites/world-map", reader.token)));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** The company's own sites and the ones it is named at. An unknown ticker has none. */
|
|
176
|
+
function loadCompany(reader: BuildoutReader, symbol: string): Promise<FacilitySet> {
|
|
177
|
+
return scopedSites.load(`${readerKey(reader)}|company:${symbol}`, async () => {
|
|
178
|
+
try {
|
|
179
|
+
return facilitySet(await buildoutApi<WorldMapPayload>(`/sites/world-map?company=${encodeURIComponent(symbol)}`, reader.token));
|
|
180
|
+
} catch (error) {
|
|
181
|
+
if (error instanceof Error && /not found|404/i.test(error.message)) return { asOf: null, rows: [], byId: new Map() };
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** The ids of the theme's companies this account can see. */
|
|
188
|
+
function loadThemeCompanies(reader: BuildoutReader, slug: string): Promise<Set<string>> {
|
|
189
|
+
return themeCompanies.load(`${readerKey(reader)}|${slug}`, async () => {
|
|
190
|
+
const ids = new Set<string>();
|
|
191
|
+
for (let offset = 0; offset < 2000; offset += THEME_PAGE) {
|
|
192
|
+
const page = await buildoutApi<{ companies?: Array<{ id?: string }> } | Array<{ id?: string }>>(
|
|
193
|
+
`/companies?list=${encodeURIComponent(slug)}&limit=${THEME_PAGE}&offset=${offset}&sort=marketCap&order=desc`,
|
|
194
|
+
reader.token,
|
|
195
|
+
);
|
|
196
|
+
const companies = Array.isArray(page) ? page : page.companies ?? [];
|
|
197
|
+
for (const company of companies) if (company.id) ids.add(company.id);
|
|
198
|
+
if (companies.length < THEME_PAGE) break;
|
|
199
|
+
}
|
|
200
|
+
return ids;
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function loadScope(scope: string | null): Promise<FacilitySet> {
|
|
205
|
+
const reader = await getBuildoutReader();
|
|
206
|
+
if (scope?.startsWith("company:")) return loadCompany(reader, scope.slice("company:".length));
|
|
207
|
+
if (scope?.startsWith("theme:")) {
|
|
208
|
+
const slug = scope.slice("theme:".length);
|
|
209
|
+
return scopedSites.load(`${readerKey(reader)}|${scope}`, async () => {
|
|
210
|
+
const [all, ids] = await Promise.all([loadAll(reader), loadThemeCompanies(reader, slug)]);
|
|
211
|
+
return {
|
|
212
|
+
asOf: all.asOf,
|
|
213
|
+
rows: all.rows.filter((row) => ids.has(all.byId.get(row.id)?.point.ownerCompanyId ?? "")),
|
|
214
|
+
byId: all.byId,
|
|
215
|
+
};
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
return loadAll(reader);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function inBbox(row: GeoEntityRow, bbox: GeoBbox | null): boolean {
|
|
222
|
+
if (!bbox) return true;
|
|
223
|
+
const [west, south, east, north] = bbox;
|
|
224
|
+
if (row.lat < south || row.lat > north) return false;
|
|
225
|
+
return west <= east ? row.lon >= west && row.lon <= east : row.lon >= west || row.lon <= east;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function feature(row: GeoEntityRow, point: WorldMapPoint): GeoFeature {
|
|
229
|
+
return {
|
|
230
|
+
id: row.id,
|
|
231
|
+
layer: FACILITIES_LAYER_ID,
|
|
232
|
+
entityKind: entityKind(point.type),
|
|
233
|
+
label: row.label,
|
|
234
|
+
geometry: { type: "Point", coordinates: [row.lon, row.lat] },
|
|
235
|
+
ts: null,
|
|
236
|
+
props: row.props,
|
|
237
|
+
...(row.tickers ? { tickers: row.tickers } : {}),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function matches(row: GeoEntityRow, needle: string): boolean {
|
|
242
|
+
if (!needle) return true;
|
|
243
|
+
const symbol = needle.replace(/^\$/, "").toUpperCase();
|
|
244
|
+
if (row.tickers?.some((link) => link.symbol.toUpperCase() === symbol)) return true;
|
|
245
|
+
return [row.label, ...Object.values(row.props)].some((value) => typeof value === "string" && value.toLowerCase().includes(needle));
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function compare(key: string | null, dir: "asc" | "desc") {
|
|
249
|
+
const sign = dir === "asc" ? 1 : -1;
|
|
250
|
+
return (left: GeoEntityRow, right: GeoEntityRow) => {
|
|
251
|
+
if (!key) return 0;
|
|
252
|
+
const a: GeoPropValue = key === "label" ? left.label : left.props[key] ?? null;
|
|
253
|
+
const b: GeoPropValue = key === "label" ? right.label : right.props[key] ?? null;
|
|
254
|
+
// Missing values sink in either direction.
|
|
255
|
+
if (a === null || b === null) return a === b ? 0 : a === null ? 1 : -1;
|
|
256
|
+
if (typeof a === "number" && typeof b === "number") return (a - b) * sign;
|
|
257
|
+
return String(a).localeCompare(String(b)) * sign;
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const PLAIN_LIST = (values: string[] | null | undefined, count = 6) => (values?.length ? values.slice(0, count).join(", ") : null);
|
|
262
|
+
|
|
263
|
+
export const facilitiesLayer: LocalGeoLayer = {
|
|
264
|
+
info: INFO,
|
|
265
|
+
async features(query: LocalFeatureQuery): Promise<GeoFeaturesPayload> {
|
|
266
|
+
const set = await loadScope(query.scope);
|
|
267
|
+
const inView = set.rows.filter((row) => inBbox(row, query.bbox));
|
|
268
|
+
const kept = inView.slice(0, query.limit);
|
|
269
|
+
return {
|
|
270
|
+
layer: FACILITIES_LAYER_ID,
|
|
271
|
+
asOf: set.asOf,
|
|
272
|
+
features: kept.map((row) => feature(row, set.byId.get(row.id)!.point)),
|
|
273
|
+
total: inView.length,
|
|
274
|
+
truncated: kept.length < inView.length,
|
|
275
|
+
};
|
|
276
|
+
},
|
|
277
|
+
async entities(query: LocalEntityQuery): Promise<GeoEntitiesPayload> {
|
|
278
|
+
const set = await loadScope(query.scope);
|
|
279
|
+
const needle = query.q.trim().toLowerCase();
|
|
280
|
+
const rows = set.rows.filter((row) => inBbox(row, query.bbox) && matches(row, needle));
|
|
281
|
+
const sorted = query.sort ? [...rows].sort(compare(query.sort, query.dir)) : rows;
|
|
282
|
+
return {
|
|
283
|
+
layer: FACILITIES_LAYER_ID,
|
|
284
|
+
asOf: set.asOf,
|
|
285
|
+
columns: COLUMNS,
|
|
286
|
+
rows: sorted.slice(query.offset, query.offset + query.limit),
|
|
287
|
+
total: rows.length,
|
|
288
|
+
};
|
|
289
|
+
},
|
|
290
|
+
async entity(id: string, scope: string | null): Promise<GeoEntityPayload> {
|
|
291
|
+
const set = await loadScope(scope);
|
|
292
|
+
const found = set.byId.get(id) ?? (await loadAll(await getBuildoutReader())).byId.get(id);
|
|
293
|
+
if (!found) throw new Error("Unknown facility");
|
|
294
|
+
const { row, point } = found;
|
|
295
|
+
const relevance = point.relevance?.tier
|
|
296
|
+
? `${point.relevance.tier.charAt(0).toUpperCase()}${point.relevance.tier.slice(1)}${point.relevance.reasons?.length ? `: ${point.relevance.reasons.join(", ")}` : ""}`
|
|
297
|
+
: null;
|
|
298
|
+
return {
|
|
299
|
+
feature: feature(row, point),
|
|
300
|
+
detail: {
|
|
301
|
+
roles: PLAIN_LIST(point.siteRoles),
|
|
302
|
+
forms: PLAIN_LIST(point.physicalForms),
|
|
303
|
+
capabilities: PLAIN_LIST(point.capabilities),
|
|
304
|
+
relevance,
|
|
305
|
+
placedBy: point.coordinateSource ? PLACED_BY[point.coordinateSource] ?? null : null,
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
},
|
|
309
|
+
};
|
|
@@ -127,6 +127,15 @@ export function criticalityColor(value: string | null | undefined) {
|
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
/** A theme's tiers, most important first. */
|
|
131
|
+
export const LIST_TIER_ORDER: readonly string[] = ["leader", "major", "notable", "watchlist"];
|
|
132
|
+
|
|
133
|
+
/** `core_vendor` reads "Core vendor". */
|
|
134
|
+
export function listWord(value: string | null | undefined): string | null {
|
|
135
|
+
const words = value?.trim().replace(/[_-]+/g, " ").toLowerCase();
|
|
136
|
+
return words ? words.charAt(0).toUpperCase() + words.slice(1) : null;
|
|
137
|
+
}
|
|
138
|
+
|
|
130
139
|
export function tickerSymbol(value: string | null | undefined): string | null {
|
|
131
140
|
const symbol = value?.trim();
|
|
132
141
|
return symbol ? symbol.toUpperCase() : null;
|
|
@@ -1,24 +1,70 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BUILDOUT_COMPANY_PANE_ID } from "./company/id";
|
|
2
2
|
import type { PluginModule } from "../plugin-module";
|
|
3
|
+
import type { PaneReportOptionDef } from "../../../types/plugin";
|
|
4
|
+
import { createTickerSurfacePaneTemplate } from "../shared/ticker-surface";
|
|
5
|
+
|
|
6
|
+
let removeFacilities: (() => void) | null = null;
|
|
7
|
+
|
|
8
|
+
/** Which view the company pane opens on: a desk opens one on its signals. */
|
|
9
|
+
const COMPANY_TAB_OPTION: PaneReportOptionDef = {
|
|
10
|
+
key: "tab",
|
|
11
|
+
type: "enum",
|
|
12
|
+
description: "The view to open on: the flow of deals, suppliers and customers, the sites, hiring by site or the signals.",
|
|
13
|
+
values: [{ value: "flow" }, { value: "sites" }, { value: "hiring" }, { value: "signals" }],
|
|
14
|
+
example: "--tab signals",
|
|
15
|
+
};
|
|
3
16
|
|
|
4
17
|
export const buildoutModule: PluginModule = {
|
|
18
|
+
// Facilities join the world map's layers for as long as the plugin is on.
|
|
19
|
+
setup() {
|
|
20
|
+
const { facilitiesLayer } = require("./facilities") as typeof import("./facilities");
|
|
21
|
+
const { registerLocalGeoLayer } = require("../world-venue-map/local-layers") as typeof import("../world-venue-map/local-layers");
|
|
22
|
+
removeFacilities?.();
|
|
23
|
+
removeFacilities = registerLocalGeoLayer(facilitiesLayer);
|
|
24
|
+
},
|
|
25
|
+
dispose() {
|
|
26
|
+
removeFacilities?.();
|
|
27
|
+
removeFacilities = null;
|
|
28
|
+
},
|
|
5
29
|
panes: [{
|
|
6
30
|
id: "buildout",
|
|
7
31
|
reportFreshness: { status: "not-a-feed", basis: "infrastructure research" },
|
|
8
32
|
name: "TheBuildout",
|
|
9
33
|
icon: "T",
|
|
10
|
-
component: BuildoutPane,
|
|
34
|
+
get component(): typeof import("./pane").BuildoutPane { const { BuildoutPane } = require("./pane"); return BuildoutPane; },
|
|
11
35
|
defaultPosition: "right",
|
|
12
36
|
defaultMode: "floating",
|
|
13
37
|
defaultFloatingSize: { width: 110, height: 34 },
|
|
38
|
+
// The company under the cursor, for linked panes: its flow, a chart, a map of its sites.
|
|
39
|
+
tickerSource: true,
|
|
40
|
+
}, {
|
|
41
|
+
id: BUILDOUT_COMPANY_PANE_ID,
|
|
42
|
+
reportFreshness: { status: "not-a-feed", basis: "disclosed relationships" },
|
|
43
|
+
// Short, since a linked pane's title adds the ticker and what it follows.
|
|
44
|
+
name: "Buildout",
|
|
45
|
+
icon: "T",
|
|
46
|
+
get component(): typeof import("./company/pane").BuildoutCompanyPane { const { BuildoutCompanyPane } = require("./company/pane"); return BuildoutCompanyPane; },
|
|
47
|
+
defaultPosition: "right",
|
|
48
|
+
defaultMode: "floating",
|
|
49
|
+
defaultFloatingSize: { width: 120, height: 32 },
|
|
50
|
+
tickerFollower: true,
|
|
51
|
+
instruments: ["equity"],
|
|
52
|
+
reportOptions: [COMPANY_TAB_OPTION],
|
|
14
53
|
}],
|
|
15
54
|
paneTemplates: [{
|
|
16
55
|
id: "buildout-pane",
|
|
17
56
|
paneId: "buildout",
|
|
18
57
|
label: "TheBuildout",
|
|
19
|
-
description: "Open TheBuildout infrastructure intelligence.",
|
|
20
|
-
keywords: ["tbo", "buildout", "thebuildout", "infrastructure", "sites", "intel"],
|
|
58
|
+
description: "Open TheBuildout infrastructure intelligence: themes, companies, sites and intel.",
|
|
59
|
+
keywords: ["tbo", "buildout", "thebuildout", "infrastructure", "themes", "sites", "intel"],
|
|
21
60
|
shortcut: { prefix: "TBO" },
|
|
22
61
|
createInstance: () => ({ placement: "floating" }),
|
|
23
|
-
}
|
|
62
|
+
}, createTickerSurfacePaneTemplate({
|
|
63
|
+
id: "buildout-company-pane",
|
|
64
|
+
paneId: BUILDOUT_COMPANY_PANE_ID,
|
|
65
|
+
label: "TheBuildout Company",
|
|
66
|
+
description: "One company in TheBuildout: its power and hosting deals, suppliers and customers as a flow, its sites, hiring by site and the signals that name it.",
|
|
67
|
+
keywords: ["tboc", "buildout", "thebuildout", "power deals", "data centers", "sites", "suppliers", "customers", "signals"],
|
|
68
|
+
shortcut: "TBOC",
|
|
69
|
+
})],
|
|
24
70
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { apiClient } from "../../../../api-client";
|
|
2
|
+
import { createTtlCache } from "../../../../data/ttl-cache";
|
|
2
3
|
import { httpFetch } from "../../../../utils/http-transport";
|
|
3
4
|
import { logFailure } from "../../../../utils/log-failure";
|
|
4
5
|
import type {
|
|
@@ -119,26 +120,77 @@ export async function loadBuildoutData(token: string | null) {
|
|
|
119
120
|
access,
|
|
120
121
|
token,
|
|
121
122
|
lists,
|
|
122
|
-
companies: { ...emptyPage<BuildoutCompany>(), blurredCompanyCount: 0 },
|
|
123
|
+
companies: { ...emptyPage<BuildoutCompany>(), blurredCompanyCount: 0, listSlug: null },
|
|
123
124
|
sites: pageFromItems(sites),
|
|
124
125
|
intel: pageFromItems(intel),
|
|
125
126
|
};
|
|
126
127
|
}
|
|
127
128
|
|
|
128
|
-
|
|
129
|
-
|
|
129
|
+
/** What the dataset is read with: a Buildout token, or none for the free set. */
|
|
130
|
+
interface BuildoutGrant {
|
|
131
|
+
token: string | null;
|
|
132
|
+
expiresAtMs: number | null;
|
|
133
|
+
}
|
|
130
134
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
135
|
+
/** Whose reads these are: the signed-in account, and its token when it has Buildout. */
|
|
136
|
+
export interface BuildoutReader {
|
|
137
|
+
account: string | null;
|
|
138
|
+
token: string | null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const NO_GRANT: BuildoutGrant = { token: null, expiresAtMs: null };
|
|
142
|
+
/** A token is asked for again this long before it lapses. */
|
|
143
|
+
const TOKEN_RENEW_EARLY_MS = 60_000;
|
|
144
|
+
/** A token whose answer carries no expiry is kept this long. */
|
|
145
|
+
const TOKEN_FALLBACK_MS = 5 * 60_000;
|
|
146
|
+
/** An account without Buildout is asked again after this long, so a new plan shows within a minute. */
|
|
147
|
+
const NO_PLAN_MS = 60_000;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* One account's grant, shared by every pane and the facilities layer: asking
|
|
151
|
+
* costs three account requests (one issues a token), and a linked desk asks
|
|
152
|
+
* on every company the cursor passes.
|
|
153
|
+
*/
|
|
154
|
+
const grants = createTtlCache<BuildoutGrant>({
|
|
155
|
+
ttlMs: (grant) => {
|
|
156
|
+
if (!grant.token) return NO_PLAN_MS;
|
|
157
|
+
if (grant.expiresAtMs === null) return TOKEN_FALLBACK_MS;
|
|
158
|
+
return Math.max(0, grant.expiresAtMs - Date.now() - TOKEN_RENEW_EARLY_MS);
|
|
159
|
+
},
|
|
160
|
+
maxEntries: 4,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
async function issueGrant(checkSession: boolean): Promise<BuildoutGrant> {
|
|
164
|
+
if (checkSession && !(await apiClient.getSession())) return NO_GRANT;
|
|
165
|
+
const account = await apiClient.getBuildoutAccount();
|
|
166
|
+
if (!account?.subscription.active) return NO_GRANT;
|
|
167
|
+
const issued = await apiClient.getBuildoutToken();
|
|
168
|
+
if (!issued?.token) return NO_GRANT;
|
|
169
|
+
const expiresAtMs = Date.parse(issued.expiresAt);
|
|
170
|
+
return { token: issued.token, expiresAtMs: Number.isFinite(expiresAtMs) ? expiresAtMs : null };
|
|
171
|
+
}
|
|
134
172
|
|
|
135
|
-
|
|
136
|
-
|
|
173
|
+
/**
|
|
174
|
+
* The account reading the dataset and its token. A token is reused until a
|
|
175
|
+
* minute before it lapses; `fresh` asks again, for a refresh the user pressed.
|
|
176
|
+
* Signed out there is no account and no token, at once.
|
|
177
|
+
*/
|
|
178
|
+
export async function getBuildoutReader({ fresh = false }: { fresh?: boolean } = {}): Promise<BuildoutReader> {
|
|
179
|
+
if (!apiClient.isSignedIn()) return { account: null, token: null };
|
|
137
180
|
|
|
138
|
-
|
|
139
|
-
|
|
181
|
+
try {
|
|
182
|
+
// A user restored from the last run still has its session checked before a token is issued.
|
|
183
|
+
const restored = apiClient.getCurrentUser();
|
|
184
|
+
const user = restored ?? await apiClient.getSession();
|
|
185
|
+
if (!user) return { account: null, token: null };
|
|
186
|
+
const grant = await grants.load(user.id, () => issueGrant(restored !== null), { force: fresh });
|
|
187
|
+
return { account: user.id, token: grant.token };
|
|
140
188
|
} catch (error) {
|
|
141
189
|
logFailure("buildout.model", "Check access failed")(error);
|
|
142
190
|
throw new Error("Access check failed. Access is unknown.");
|
|
143
191
|
}
|
|
144
192
|
}
|
|
193
|
+
|
|
194
|
+
export async function getBuildoutProToken(options?: { fresh?: boolean }) {
|
|
195
|
+
return (await getBuildoutReader(options)).token;
|
|
196
|
+
}
|
|
@@ -121,6 +121,9 @@ export function normalizeList(raw: RawObject): BuildoutList {
|
|
|
121
121
|
avgSectorGrowth: stringField(raw, "avgSectorGrowth", "avg_sector_growth"),
|
|
122
122
|
avgReturn1y: stringField(raw, "avgReturn1y", "avg_return_1y"),
|
|
123
123
|
avgMargin: stringField(raw, "avgMargin", "avg_margin"),
|
|
124
|
+
leaders: raw.importanceCounts && typeof raw.importanceCounts === "object"
|
|
125
|
+
? numberField(raw.importanceCounts as RawObject, "leader")
|
|
126
|
+
: null,
|
|
124
127
|
};
|
|
125
128
|
}
|
|
126
129
|
|
|
@@ -256,6 +259,8 @@ function readCompany(raw: RawObject): BuildoutCompany {
|
|
|
256
259
|
logoUrl: stringField(raw, "logoUrl", "logo_url"),
|
|
257
260
|
researchReport: stringField(raw, "researchReport", "research_report"),
|
|
258
261
|
listReason: stringField(raw, "listReason", "list_reason"),
|
|
262
|
+
listRole: stringField(raw, "listCategoryRole", "list_category_role"),
|
|
263
|
+
listTier: stringField(raw, "listCategoryImportance", "list_category_importance"),
|
|
259
264
|
intelligence: arrayField(raw, "intelligence").map((item) => ({
|
|
260
265
|
publishedAt: stringField(item, "publishedAt", "published_at"),
|
|
261
266
|
headline: stringField(item, "headline", "title"),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DataTableColumn } from "../../../../components";
|
|
2
2
|
|
|
3
|
-
export type BuildoutTabId = "companies" | "sites" | "intel";
|
|
3
|
+
export type BuildoutTabId = "themes" | "companies" | "sites" | "intel";
|
|
4
4
|
export type BuildoutAccess = "free" | "pro";
|
|
5
5
|
|
|
6
6
|
export type RawObject = Record<string, unknown>;
|
|
@@ -40,6 +40,8 @@ export type BuildoutList = {
|
|
|
40
40
|
avgSectorGrowth?: string | null;
|
|
41
41
|
avgReturn1y?: string | null;
|
|
42
42
|
avgMargin?: string | null;
|
|
43
|
+
/** Companies the theme ranks as its leaders. */
|
|
44
|
+
leaders?: number | null;
|
|
43
45
|
};
|
|
44
46
|
|
|
45
47
|
export type BuildoutCompany = {
|
|
@@ -135,6 +137,10 @@ export type BuildoutCompany = {
|
|
|
135
137
|
logoUrl?: string | null;
|
|
136
138
|
researchReport?: string | null;
|
|
137
139
|
listReason?: string | null;
|
|
140
|
+
/** Its part in the open theme: core vendor, integrator... */
|
|
141
|
+
listRole?: string | null;
|
|
142
|
+
/** How much it matters to the open theme: leader, major, notable, watchlist. */
|
|
143
|
+
listTier?: string | null;
|
|
138
144
|
intelligence?: Array<{
|
|
139
145
|
publishedAt?: string | null;
|
|
140
146
|
headline?: string | null;
|
|
@@ -267,7 +273,8 @@ export type BuildoutLoadState =
|
|
|
267
273
|
access: BuildoutAccess;
|
|
268
274
|
token: string | null;
|
|
269
275
|
lists: BuildoutList[];
|
|
270
|
-
companies:
|
|
276
|
+
/** The companies of one list: `listSlug`, or none yet. */
|
|
277
|
+
companies: BuildoutPagedState<BuildoutCompany> & { blurredCompanyCount: number; listSlug: string | null };
|
|
271
278
|
sites: BuildoutPagedState<BuildoutSite>;
|
|
272
279
|
intel: BuildoutPagedState<BuildoutUpdate>;
|
|
273
280
|
loadedAt: number;
|
|
@@ -299,6 +306,8 @@ export type BuildoutColumnId =
|
|
|
299
306
|
| "dividendYield"
|
|
300
307
|
| "return1y"
|
|
301
308
|
| "employees"
|
|
309
|
+
| "listRole"
|
|
310
|
+
| "listTier"
|
|
302
311
|
| "site"
|
|
303
312
|
| "type"
|
|
304
313
|
| "owner"
|