gloomberb 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,201 @@
|
|
|
1
|
+
import { readFileSync, realpathSync } from "fs";
|
|
2
|
+
import { dirname, extname, isAbsolute, join, relative } from "path";
|
|
3
|
+
import type { BunPlugin } from "bun";
|
|
4
|
+
|
|
5
|
+
import { installedPeerPlugins } from "./host-link";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A plugin's imports of a sibling plugin it declares as a peer (IBKR Gateway
|
|
9
|
+
* imports `gloom-ibkr/gateway-bridge`), read from the sibling's own folder
|
|
10
|
+
* instead of through the link in the plugin's `node_modules`.
|
|
11
|
+
*
|
|
12
|
+
* That link is a junction on Windows, and a process can be unable to follow
|
|
13
|
+
* a junction at all. On the first launch after an update the gateway failed
|
|
14
|
+
* with its link in place and pointing at Interactive Brokers: the loader wrote
|
|
15
|
+
* it again four times, never saw it resolve, and an import that looked again
|
|
16
|
+
* failed the same way, while Interactive Brokers itself loaded from its folder
|
|
17
|
+
* in the same process. The next launch worked.
|
|
18
|
+
*
|
|
19
|
+
* So the host resolves those imports itself and hands Bun the sibling's file
|
|
20
|
+
* by its absolute path: the file the link leads to, under the module key the
|
|
21
|
+
* sibling's own imports give it, so the plugin still shares the sibling's
|
|
22
|
+
* modules. The terminal and the desktop's Bun process rewrite the plugin's
|
|
23
|
+
* source as Bun loads it; the desktop's compile for the view resolves them in
|
|
24
|
+
* the bundler. The link stays, for editors, `tsc`, and older Gloom versions.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
type SourceLoader = "ts" | "tsx" | "js" | "jsx";
|
|
28
|
+
|
|
29
|
+
/** What Bun loads a plugin's own source files with, by extension. */
|
|
30
|
+
export const SOURCE_LOADERS: Readonly<Record<string, SourceLoader>> = {
|
|
31
|
+
".ts": "ts",
|
|
32
|
+
".mts": "ts",
|
|
33
|
+
".tsx": "tsx",
|
|
34
|
+
".js": "js",
|
|
35
|
+
".mjs": "js",
|
|
36
|
+
".jsx": "jsx",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export function sourceLoader(file: string): SourceLoader {
|
|
40
|
+
return SOURCE_LOADERS[extname(file).toLowerCase()] ?? "js";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function isInside(root: string, path: string): boolean {
|
|
44
|
+
const inside = relative(root, path);
|
|
45
|
+
return !!inside && !inside.startsWith("..") && !isAbsolute(inside);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function escapeRegExp(text: string): string {
|
|
49
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Where a module is named: after `from`, `import` or `import(`, and in `require(`. */
|
|
53
|
+
const IMPORTED_AT = String.raw`(\bfrom\s*|\bimport\s*(?:\(\s*)?|\brequire\s*\(\s*)`;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* `contents` with `specifier` replaced by `replacement` where it is imported.
|
|
57
|
+
* The same text in a string anywhere else stays.
|
|
58
|
+
*/
|
|
59
|
+
export function replaceSpecifier(contents: string, specifier: string, replacement: string): string {
|
|
60
|
+
const imported = new RegExp(`${IMPORTED_AT}(["'\`])${escapeRegExp(specifier)}\\2`, "g");
|
|
61
|
+
return contents.replace(imported, (_match, at: string) => `${at}${JSON.stringify(replacement)}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function realFolder(path: string): string {
|
|
65
|
+
try {
|
|
66
|
+
return realpathSync(path);
|
|
67
|
+
} catch {
|
|
68
|
+
// Intentional ignore: a folder that cannot be resolved is matched as spelled.
|
|
69
|
+
return path;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Each registered plugin's real folder, with its siblings' folders by package name. */
|
|
74
|
+
const siblingsByPlugin = new Map<string, ReadonlyMap<string, string>>();
|
|
75
|
+
/** Real plugin folders whose source Bun loads through the hook below. */
|
|
76
|
+
const hookedFolders = new Set<string>();
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The installed siblings `pluginDir` declares, by package name, each with its
|
|
80
|
+
* real folder. A sibling that maps its paths with `exports` is left out: only
|
|
81
|
+
* a lookup through `node_modules` reads that map.
|
|
82
|
+
*/
|
|
83
|
+
function siblingFolders(pluginDir: string, pluginsDir: string = dirname(pluginDir)): Map<string, string> {
|
|
84
|
+
const folders = new Map<string, string>();
|
|
85
|
+
for (const { peer, directory } of installedPeerPlugins(pluginDir, pluginsDir)) {
|
|
86
|
+
const folder = join(pluginsDir, directory);
|
|
87
|
+
try {
|
|
88
|
+
const pkg = JSON.parse(readFileSync(join(folder, "package.json"), "utf8")) as { exports?: unknown };
|
|
89
|
+
if (pkg.exports !== undefined) continue;
|
|
90
|
+
} catch {
|
|
91
|
+
// Intentional ignore: a sibling without a readable package.json has no export map.
|
|
92
|
+
}
|
|
93
|
+
folders.set(peer, realFolder(folder));
|
|
94
|
+
}
|
|
95
|
+
return folders;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** The file `specifier` names in the sibling `peer`, as Bun resolves it from inside its folder; null when there is none. */
|
|
99
|
+
function siblingModule(peer: string, peerDir: string, specifier: string): string | null {
|
|
100
|
+
const subpath = specifier.slice(peer.length + 1);
|
|
101
|
+
try {
|
|
102
|
+
return Bun.resolveSync(subpath ? `./${subpath}` : ".", peerDir);
|
|
103
|
+
} catch {
|
|
104
|
+
// Intentional ignore: left as written, the import fails the way it always did.
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* `contents` of `file` with its imports of the siblings its plugin declares
|
|
111
|
+
* naming the siblings' files by absolute path. Anything else is left as
|
|
112
|
+
* written: a file outside a registered plugin, and an import the sibling
|
|
113
|
+
* cannot satisfy, which then fails the way it always did.
|
|
114
|
+
*/
|
|
115
|
+
export function withSiblingImports(file: string, contents: string): string {
|
|
116
|
+
let siblings: ReadonlyMap<string, string> | undefined;
|
|
117
|
+
for (const [root, folders] of siblingsByPlugin) {
|
|
118
|
+
if (isInside(root, file)) siblings = folders;
|
|
119
|
+
}
|
|
120
|
+
if (!siblings) return contents;
|
|
121
|
+
let next = contents;
|
|
122
|
+
for (const [peer, peerDir] of siblings) {
|
|
123
|
+
if (!next.includes(peer)) continue;
|
|
124
|
+
const imported = new RegExp(`${IMPORTED_AT}(["'\`])(${escapeRegExp(peer)}(?:/[^"'\`\\s]*)?)\\2`, "g");
|
|
125
|
+
next = next.replace(imported, (match, at: string, _quote: string, specifier: string) => {
|
|
126
|
+
const target = siblingModule(peer, peerDir, specifier);
|
|
127
|
+
return target ? `${at}${JSON.stringify(target)}` : match;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return next;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function pathPattern(path: string): string {
|
|
134
|
+
return path.split(/[\\/]+/).map(escapeRegExp).join("[\\\\/]+");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* The plugin's own source files, with an entry imported fresh (`?reload=`,
|
|
139
|
+
* or the installer's `?validate=`); not its dependencies, and not a retried
|
|
140
|
+
* import's files, which the retry's own hook loads the same way
|
|
141
|
+
* (peer-retry.ts).
|
|
142
|
+
*/
|
|
143
|
+
function pluginSourceFilter(root: string): RegExp {
|
|
144
|
+
return new RegExp(
|
|
145
|
+
`^${pathPattern(root)}[\\\\/](?!(?:[^?]*[\\\\/])?node_modules[\\\\/])[^?]*\\.(?:tsx?|mts|jsx?|mjs)(?:\\?(?:reload|validate)=\\d+)?$`,
|
|
146
|
+
process.platform === "win32" ? "i" : "",
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function hookPluginSource(root: string): boolean {
|
|
151
|
+
if (hookedFolders.has(root)) return true;
|
|
152
|
+
if (typeof Bun === "undefined" || typeof Bun.plugin !== "function") return false;
|
|
153
|
+
Bun.plugin({
|
|
154
|
+
name: "gloomberb-sibling-imports",
|
|
155
|
+
setup(build) {
|
|
156
|
+
build.onLoad({ filter: pluginSourceFilter(root) }, (args) => {
|
|
157
|
+
const file = args.path.replace(/\?.*$/, "");
|
|
158
|
+
return { contents: withSiblingImports(file, readFileSync(file, "utf8")), loader: sourceLoader(file) };
|
|
159
|
+
});
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
hookedFolders.add(root);
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Makes this process import the siblings `pluginDir` declares from their own
|
|
168
|
+
* folders, from the next import of the plugin on. Returns whether it does: a
|
|
169
|
+
* plugin with no installed sibling, and a process without Bun's runtime
|
|
170
|
+
* plugins, go through the links as before.
|
|
171
|
+
*/
|
|
172
|
+
export function importSiblingsFromTheirFolders(pluginDir: string, pluginsDir?: string): boolean {
|
|
173
|
+
const siblings = siblingFolders(pluginDir, pluginsDir);
|
|
174
|
+
const root = realFolder(pluginDir);
|
|
175
|
+
if (siblings.size === 0) {
|
|
176
|
+
siblingsByPlugin.delete(root);
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
siblingsByPlugin.set(root, siblings);
|
|
180
|
+
return hookPluginSource(root);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* The same for a bundle of `pluginDir`: a bundler plugin that resolves the
|
|
185
|
+
* siblings it declares in their folders, or null when none is installed.
|
|
186
|
+
*/
|
|
187
|
+
export function siblingImportsBundlerPlugin(pluginDir: string, pluginsDir?: string): BunPlugin | null {
|
|
188
|
+
const siblings = siblingFolders(pluginDir, pluginsDir);
|
|
189
|
+
if (siblings.size === 0) return null;
|
|
190
|
+
const filter = new RegExp(`^(?:${[...siblings.keys()].map(escapeRegExp).join("|")})(?:/.*)?$`);
|
|
191
|
+
return {
|
|
192
|
+
name: "gloomberb-sibling-imports",
|
|
193
|
+
setup(build) {
|
|
194
|
+
build.onResolve({ filter }, (args) => {
|
|
195
|
+
const peer = [...siblings.keys()].find((name) => args.path === name || args.path.startsWith(`${name}/`));
|
|
196
|
+
const path = peer ? siblingModule(peer, siblings.get(peer)!, args.path) : null;
|
|
197
|
+
return path ? { path } : undefined;
|
|
198
|
+
});
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import { readdirSync, readFileSync, type Dirent } from "fs";
|
|
2
|
-
import { dirname, extname,
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, type Dirent } from "fs";
|
|
2
|
+
import { dirname, extname, join } from "path";
|
|
3
|
+
|
|
4
|
+
import { isInside, replaceSpecifier, SOURCE_LOADERS, sourceLoader, withSiblingImports } from "./peer-imports";
|
|
5
|
+
import { pluginDirectoryNames } from "./plugin-names";
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* A second import of a plugin whose first one failed on a sibling plugin it
|
|
@@ -19,19 +22,15 @@ import { dirname, extname, isAbsolute, join, relative } from "path";
|
|
|
19
22
|
* nothing from the failed attempt is reused. Imports that leave the plugin,
|
|
20
23
|
* the sibling's files included, resolve to the same modules as always, so the
|
|
21
24
|
* plugin shares the sibling's state as on a normal load.
|
|
25
|
+
*
|
|
26
|
+
* A plugin's imports of its siblings no longer go through the link at all
|
|
27
|
+
* (peer-imports.ts), so what is left for the retry is a plugin that Bun
|
|
28
|
+
* already failed in this process, imported before its sibling was there.
|
|
22
29
|
*/
|
|
23
30
|
|
|
24
31
|
/** The query that gives a plugin's files their new module keys. */
|
|
25
32
|
const RETRY_PARAM = "gloom-plugin-retry";
|
|
26
33
|
const RETRY_QUERY = new RegExp(`[?&]${RETRY_PARAM}=(\\d+)(?:&|$)`);
|
|
27
|
-
const SOURCE_LOADERS: Readonly<Record<string, "ts" | "tsx" | "js" | "jsx">> = {
|
|
28
|
-
".ts": "ts",
|
|
29
|
-
".mts": "ts",
|
|
30
|
-
".tsx": "tsx",
|
|
31
|
-
".js": "js",
|
|
32
|
-
".mjs": "js",
|
|
33
|
-
".jsx": "jsx",
|
|
34
|
-
};
|
|
35
34
|
/** A file name that is never there: looking it up is what clears a folder. */
|
|
36
35
|
const MISSING_FILE = "./.gloom-resolver-refresh";
|
|
37
36
|
const SKIPPED_FOLDERS = new Set(["node_modules", ".git", "dist"]);
|
|
@@ -49,11 +48,6 @@ const retryRoots = new Map<number, string>();
|
|
|
49
48
|
let retries = 0;
|
|
50
49
|
let installed = false;
|
|
51
50
|
|
|
52
|
-
function isInside(root: string, path: string): boolean {
|
|
53
|
-
const inside = relative(root, path);
|
|
54
|
-
return !!inside && !inside.startsWith("..") && !isAbsolute(inside);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
51
|
/** The plugin's own folders, without dependencies or build output. */
|
|
58
52
|
function sourceFolders(dir: string, out: string[] = [], depth = 0): string[] {
|
|
59
53
|
out.push(dir);
|
|
@@ -72,11 +66,11 @@ function sourceFolders(dir: string, out: string[] = [], depth = 0): string[] {
|
|
|
72
66
|
}
|
|
73
67
|
|
|
74
68
|
/**
|
|
75
|
-
* Makes Bun read the plugin's folders
|
|
76
|
-
* again. Bun drops a folder from its resolver cache
|
|
77
|
-
* misses, so this looks up a file that is not there in
|
|
78
|
-
* bundler shares the cache, which is why the desktop's compile
|
|
79
|
-
* plugin failed in the same session too.
|
|
69
|
+
* Makes Bun read the plugin's folders, its links to `peers` and the peers'
|
|
70
|
+
* own folders from disk again. Bun drops a folder from its resolver cache
|
|
71
|
+
* when a lookup in it misses, so this looks up a file that is not there in
|
|
72
|
+
* each one. The bundler shares the cache, which is why the desktop's compile
|
|
73
|
+
* of the same plugin failed in the same session too.
|
|
80
74
|
*/
|
|
81
75
|
export function refreshResolverFolders(pluginDir: string, peers: readonly string[]): void {
|
|
82
76
|
const modules = join(pluginDir, "node_modules");
|
|
@@ -84,6 +78,10 @@ export function refreshResolverFolders(pluginDir: string, peers: readonly string
|
|
|
84
78
|
for (const peer of peers) {
|
|
85
79
|
if (peer.startsWith("@")) folders.push(join(modules, peer.split("/")[0]!));
|
|
86
80
|
folders.push(join(modules, peer));
|
|
81
|
+
for (const name of pluginDirectoryNames(peer)) {
|
|
82
|
+
const peerDir = join(dirname(pluginDir), name);
|
|
83
|
+
if (existsSync(peerDir)) folders.push(...sourceFolders(peerDir));
|
|
84
|
+
}
|
|
87
85
|
}
|
|
88
86
|
for (const folder of folders) {
|
|
89
87
|
try {
|
|
@@ -97,15 +95,17 @@ export function refreshResolverFolders(pluginDir: string, peers: readonly string
|
|
|
97
95
|
/**
|
|
98
96
|
* The plugin's file at `path` (with the retry query), its relative imports
|
|
99
97
|
* of other source files in the plugin given the same query, so they load
|
|
100
|
-
* under new keys as well. Imports that leave the plugin keep their keys
|
|
98
|
+
* under new keys as well. Imports that leave the plugin keep their keys,
|
|
99
|
+
* and those of a sibling plugin name its file in the sibling's folder, as
|
|
100
|
+
* on a first import (peer-imports.ts).
|
|
101
101
|
*/
|
|
102
|
-
function rekeyedSource(path: string): { contents: string; loader:
|
|
102
|
+
function rekeyedSource(path: string): { contents: string; loader: ReturnType<typeof sourceLoader> } {
|
|
103
103
|
const match = RETRY_QUERY.exec(path);
|
|
104
104
|
const file = match ? path.slice(0, match.index) : path;
|
|
105
|
-
const loader =
|
|
105
|
+
const loader = sourceLoader(file);
|
|
106
106
|
let contents = readFileSync(file, "utf8");
|
|
107
107
|
const root = match ? retryRoots.get(Number(match[1])) : undefined;
|
|
108
|
-
if (!match || !root) return { contents, loader };
|
|
108
|
+
if (!match || !root) return { contents: withSiblingImports(file, contents), loader };
|
|
109
109
|
const retry = Number(match[1]);
|
|
110
110
|
const specifiers = new Set(
|
|
111
111
|
new Bun.Transpiler({ loader }).scanImports(contents)
|
|
@@ -121,12 +121,9 @@ function rekeyedSource(path: string): { contents: string; loader: "ts" | "tsx" |
|
|
|
121
121
|
continue;
|
|
122
122
|
}
|
|
123
123
|
if (!isInside(root, target) || !SOURCE_LOADERS[extname(target).toLowerCase()]) continue;
|
|
124
|
-
|
|
125
|
-
for (const quote of ["\"", "'", "`"]) {
|
|
126
|
-
contents = contents.split(`${quote}${specifier}${quote}`).join(`${quote}${rekeyed}${quote}`);
|
|
127
|
-
}
|
|
124
|
+
contents = replaceSpecifier(contents, specifier, `${specifier}?${RETRY_PARAM}=${retry}`);
|
|
128
125
|
}
|
|
129
|
-
return { contents, loader };
|
|
126
|
+
return { contents: withSiblingImports(file, contents), loader };
|
|
130
127
|
}
|
|
131
128
|
|
|
132
129
|
function installRekeyingLoader(): boolean {
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `process` a plugin sees in the desktop view and the web app.
|
|
3
|
+
*
|
|
4
|
+
* A plugin written for the terminal runs in Bun and reads `process.env`,
|
|
5
|
+
* `process.platform` and friends without a second thought. The desktop view
|
|
6
|
+
* and the browser are not Node: there is no `process`, so the same plugin
|
|
7
|
+
* died on load with `ReferenceError: process is not defined`. Defining a global
|
|
8
|
+
* `process` would be simpler, but the host itself looks at `globalThis.process`
|
|
9
|
+
* to tell a Node runtime from a browser (updater, platform labels, libc
|
|
10
|
+
* detection), so every plugin would change how the host behaves. The bundler
|
|
11
|
+
* (`bundle.ts`) gives each plugin bundle its own copy instead, as a module
|
|
12
|
+
* level constant that only that bundle's references to `process` read.
|
|
13
|
+
*
|
|
14
|
+
* What it holds is the harmless half of `process`: a copy of `env` with only
|
|
15
|
+
* `NODE_ENV` in it (never the host's real environment, which the view cannot
|
|
16
|
+
* read and a plugin should not receive by RPC), the platform of the window it
|
|
17
|
+
* runs in, empty `argv` and `versions`, and timers and listeners that work or
|
|
18
|
+
* do nothing. What cannot exist in a view (`exit`, `kill`, `chdir`, memory
|
|
19
|
+
* and CPU readings) throws an error naming the plugin, instead of "is not a
|
|
20
|
+
* function". Anything else is left undefined, so feature detection such as
|
|
21
|
+
* `process.stdin?.isTTY` keeps working.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/** The name the bundler gives the shim; no plugin source declares it. */
|
|
25
|
+
export const PLUGIN_PROCESS_IDENTIFIER = "__gloomPluginProcess";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Builds the shim. This function is serialised into the plugin bundle with
|
|
29
|
+
* `toString()`, so it has to stay self-contained: no imports and no module
|
|
30
|
+
* level names, only globals the view provides.
|
|
31
|
+
*/
|
|
32
|
+
function createPluginProcess(pluginName: string): Record<string, unknown> {
|
|
33
|
+
const nav = (globalThis as {
|
|
34
|
+
navigator?: { userAgentData?: { platform?: string }; platform?: string; userAgent?: string };
|
|
35
|
+
}).navigator;
|
|
36
|
+
const hint = String(nav?.userAgentData?.platform || nav?.platform || nav?.userAgent || "").toLowerCase();
|
|
37
|
+
// Mac is tested before Windows because "darwin" contains "win".
|
|
38
|
+
const platform = /android/.test(hint)
|
|
39
|
+
? "android"
|
|
40
|
+
: /mac|iphone|ipad|darwin/.test(hint)
|
|
41
|
+
? "darwin"
|
|
42
|
+
: /win/.test(hint)
|
|
43
|
+
? "win32"
|
|
44
|
+
: /linux|x11|cros/.test(hint)
|
|
45
|
+
? "linux"
|
|
46
|
+
: "browser";
|
|
47
|
+
|
|
48
|
+
const hrtime = (previous?: readonly [number, number]): [number, number] => {
|
|
49
|
+
const ms = performance.now();
|
|
50
|
+
let seconds = Math.floor(ms / 1000);
|
|
51
|
+
let nanos = Math.floor((ms % 1000) * 1e6);
|
|
52
|
+
if (previous) {
|
|
53
|
+
seconds -= previous[0];
|
|
54
|
+
nanos -= previous[1];
|
|
55
|
+
if (nanos < 0) {
|
|
56
|
+
seconds -= 1;
|
|
57
|
+
nanos += 1e9;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return [seconds, nanos];
|
|
61
|
+
};
|
|
62
|
+
const stream = (log: (line: string) => void) => ({
|
|
63
|
+
isTTY: false,
|
|
64
|
+
write: (chunk: unknown) => {
|
|
65
|
+
log(String(chunk).replace(/\n$/, ""));
|
|
66
|
+
return true;
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const proc: Record<string, unknown> = {
|
|
71
|
+
env: { NODE_ENV: "production" },
|
|
72
|
+
platform,
|
|
73
|
+
browser: true,
|
|
74
|
+
title: "browser",
|
|
75
|
+
argv: [],
|
|
76
|
+
execArgv: [],
|
|
77
|
+
execPath: "",
|
|
78
|
+
version: "",
|
|
79
|
+
versions: {},
|
|
80
|
+
pid: 0,
|
|
81
|
+
ppid: 0,
|
|
82
|
+
cwd: () => "/",
|
|
83
|
+
uptime: () => performance.now() / 1000,
|
|
84
|
+
hrtime: Object.assign(hrtime, { bigint: () => BigInt(Math.round(performance.now() * 1e6)) }),
|
|
85
|
+
nextTick: (callback: (...args: unknown[]) => void, ...args: unknown[]) => {
|
|
86
|
+
queueMicrotask(() => callback(...args));
|
|
87
|
+
},
|
|
88
|
+
emitWarning: (warning: unknown) => {
|
|
89
|
+
console.warn(String((warning as { message?: string } | null)?.message ?? warning));
|
|
90
|
+
},
|
|
91
|
+
stdout: stream((line) => console.log(line)),
|
|
92
|
+
stderr: stream((line) => console.error(line)),
|
|
93
|
+
emit: () => false,
|
|
94
|
+
listeners: () => [],
|
|
95
|
+
listenerCount: () => 0,
|
|
96
|
+
};
|
|
97
|
+
// Nothing exits or signals in a view, so a handler would never run.
|
|
98
|
+
for (const name of [
|
|
99
|
+
"on", "once", "off", "addListener", "removeListener", "removeAllListeners",
|
|
100
|
+
"prependListener", "prependOnceListener", "setMaxListeners",
|
|
101
|
+
]) {
|
|
102
|
+
proc[name] = () => proc;
|
|
103
|
+
}
|
|
104
|
+
for (const name of [
|
|
105
|
+
"exit", "abort", "kill", "chdir", "umask", "binding", "memoryUsage", "cpuUsage", "resourceUsage",
|
|
106
|
+
]) {
|
|
107
|
+
proc[name] = () => {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`Plugin "${pluginName}" called process.${name}(), which is only available in the terminal app, not in the desktop or web app.`,
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
return proc;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** The declaration the bundler puts at the top of a bundle that reads `process`. */
|
|
117
|
+
export function pluginProcessDeclaration(pluginName: string): string {
|
|
118
|
+
return `const ${PLUGIN_PROCESS_IDENTIFIER} = (${createPluginProcess.toString()})(${JSON.stringify(pluginName)});\n`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Source of `import process from "node:process"`, for a plugin that imports it. */
|
|
122
|
+
export function pluginProcessModuleSource(): string {
|
|
123
|
+
const names = Object.keys(createPluginProcess(""));
|
|
124
|
+
return [
|
|
125
|
+
"export default process;",
|
|
126
|
+
...names.map((name) => `export const ${name} = process.${name};`),
|
|
127
|
+
].join("\n");
|
|
128
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether Windows lets this process follow a junction a standard user made,
|
|
3
|
+
* which is what every link to a sibling plugin is (host-link.ts).
|
|
4
|
+
*
|
|
5
|
+
* Windows 11's RedirectionGuard stops a process that runs with it from
|
|
6
|
+
* following such a junction at all: the junction is there, points where it
|
|
7
|
+
* should, and every read through it fails. That is how the gateway's link
|
|
8
|
+
* looked on the first launch after an update. Some Windows services run with
|
|
9
|
+
* it; whether that launch did is not known, so the report of a link that does
|
|
10
|
+
* not resolve carries the answer.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** `ProcessRedirectionTrustPolicy` in Windows' PROCESS_MITIGATION_POLICY. */
|
|
14
|
+
const PROCESS_REDIRECTION_TRUST_POLICY = 16;
|
|
15
|
+
const CURRENT_PROCESS = -1;
|
|
16
|
+
const ENFORCE = 0x1;
|
|
17
|
+
const AUDIT = 0x2;
|
|
18
|
+
|
|
19
|
+
let answer: string | null | undefined;
|
|
20
|
+
|
|
21
|
+
/** `enforce`, `audit` (only logged), `off`, or `unknown` when Windows does not say. Null on other systems. */
|
|
22
|
+
export function redirectionGuard(): string | null {
|
|
23
|
+
if (process.platform !== "win32") return null;
|
|
24
|
+
if (answer !== undefined) return answer;
|
|
25
|
+
answer = "unknown";
|
|
26
|
+
try {
|
|
27
|
+
// Required when asked, not imported: the pane screenshot page bundles the
|
|
28
|
+
// plugin loader for a browser, which has no `bun:ffi`.
|
|
29
|
+
const { dlopen, ptr } = import.meta.require("bun:ffi") as typeof import("bun:ffi");
|
|
30
|
+
const kernel32 = dlopen("kernel32.dll", {
|
|
31
|
+
GetProcessMitigationPolicy: { args: ["i64", "i32", "ptr", "u64"], returns: "i32" },
|
|
32
|
+
});
|
|
33
|
+
try {
|
|
34
|
+
const flags = new Uint32Array(1);
|
|
35
|
+
if (kernel32.symbols.GetProcessMitigationPolicy(CURRENT_PROCESS, PROCESS_REDIRECTION_TRUST_POLICY, ptr(flags), 4)) {
|
|
36
|
+
answer = (flags[0]! & ENFORCE) ? "enforce" : (flags[0]! & AUDIT) ? "audit" : "off";
|
|
37
|
+
}
|
|
38
|
+
} finally {
|
|
39
|
+
kernel32.close();
|
|
40
|
+
}
|
|
41
|
+
} catch {
|
|
42
|
+
// Intentional ignore: a Windows without the policy leaves the answer unknown.
|
|
43
|
+
}
|
|
44
|
+
return answer;
|
|
45
|
+
}
|
package/src/remote/endpoint.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { readFile,
|
|
1
|
+
import { readFile, rename, rm } from "fs/promises";
|
|
2
2
|
import { dirname, join } from "path";
|
|
3
|
+
import { makePrivateDir, writePrivateFile } from "../utils/private-files";
|
|
3
4
|
import type { RemoteAppKind, RemoteEndpoint } from "./types";
|
|
4
5
|
|
|
5
6
|
const DEFAULT_ENDPOINT_FILE = "remote-control.json";
|
|
@@ -25,9 +26,21 @@ export async function removeRemoteEndpointFiles(dataDir: string, endpoint: Remot
|
|
|
25
26
|
]);
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Readable by the user alone, an older copy replaced: it holds the token that
|
|
31
|
+
* controls the app. Written beside it and moved into place, so a client that
|
|
32
|
+
* finds the file never reads it half written.
|
|
33
|
+
*/
|
|
28
34
|
async function writeRemoteEndpoint(filePath: string, endpoint: RemoteEndpoint): Promise<void> {
|
|
29
|
-
await
|
|
30
|
-
|
|
35
|
+
await makePrivateDir(dirname(filePath));
|
|
36
|
+
const temporary = `${filePath}.${process.pid}.tmp`;
|
|
37
|
+
try {
|
|
38
|
+
await writePrivateFile(temporary, JSON.stringify(endpoint, null, 2));
|
|
39
|
+
await rename(temporary, filePath);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
await rm(temporary, { force: true });
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
31
44
|
}
|
|
32
45
|
|
|
33
46
|
async function removeRemoteEndpointIfOwned(filePath: string, endpoint: RemoteEndpoint): Promise<void> {
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
readOrCreateBrowserInstallId,
|
|
32
32
|
} from "../../telemetry/crash-reports-dom";
|
|
33
33
|
import { currentTelemetryConfig } from "../../telemetry/live-config";
|
|
34
|
-
import {
|
|
34
|
+
import { installAttentionCounter } from "../../telemetry/attention-counts";
|
|
35
35
|
import { installUsageCounter, recordRestoredFunctions, usageCountsEnabled } from "../../telemetry/usage-counts";
|
|
36
36
|
import { installWindowUsageFlush } from "../../telemetry/usage-counts-dom";
|
|
37
37
|
import type { AppConfig } from "../../types/config";
|
|
@@ -71,7 +71,7 @@ async function boot(): Promise<void> {
|
|
|
71
71
|
getInstallId: () => readOrCreateBrowserInstallId(),
|
|
72
72
|
officialPluginIds: loadOfficialPluginIds,
|
|
73
73
|
});
|
|
74
|
-
installAttentionCounter(() => !browserDoNotTrack()
|
|
74
|
+
installAttentionCounter({ allowed: () => !browserDoNotTrack(), launchConfig: () => loadedConfig });
|
|
75
75
|
// A document reload does not unmount React. Flush both config and session
|
|
76
76
|
// timers while localStorage is still available, including background tabs.
|
|
77
77
|
window.addEventListener("pagehide", () => { void flushPendingPersistence(); });
|
|
@@ -114,6 +114,7 @@ async function boot(): Promise<void> {
|
|
|
114
114
|
plugins={getBrowserPlugins(externalPlugins)}
|
|
115
115
|
desktopDeepLinkBridge={deepLinkBridge}
|
|
116
116
|
updatesEnabled={false}
|
|
117
|
+
setupEnabled={false}
|
|
117
118
|
/>
|
|
118
119
|
</DomHostProviders>
|
|
119
120
|
</DomErrorBoundary>,
|