gloomberb 0.18.2 → 0.18.4

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.
Files changed (954) hide show
  1. package/README.md +8 -8
  2. package/README.zh-CN.md +11 -11
  3. package/bin/{gloomberb → gloom} +3 -1
  4. package/package.json +6 -2
  5. package/patches/electrobun@1.18.1.patch +106 -5
  6. package/src/account/checkout-handoff.tsx +225 -0
  7. package/src/account/device-signin-dialog.tsx +2 -2
  8. package/src/account/device-signin.ts +2 -2
  9. package/src/account/upgrade-dialog.tsx +1 -1
  10. package/src/api-client/askg.ts +2 -2
  11. package/src/api-client/auth.ts +2 -2
  12. package/src/api-client/central-bank-rates.ts +6 -0
  13. package/src/api-client/collections.ts +5 -1
  14. package/src/api-client/cot.ts +2 -0
  15. package/src/api-client/crypto-etf-flows.ts +37 -0
  16. package/src/api-client/crypto-markets.ts +60 -0
  17. package/src/api-client/data.ts +99 -5
  18. package/src/api-client/doe.ts +15 -0
  19. package/src/api-client/equity-screener.ts +28 -0
  20. package/src/api-client/exchange-announcements.ts +38 -0
  21. package/src/api-client/fund-facts.ts +39 -0
  22. package/src/api-client/futures-curve.ts +9 -1
  23. package/src/api-client/hiring.ts +301 -0
  24. package/src/api-client/index.ts +24 -5
  25. package/src/api-client/market-discovery.ts +14 -0
  26. package/src/api-client/members.ts +6 -0
  27. package/src/api-client/mna.ts +6 -0
  28. package/src/api-client/paths.ts +18 -1
  29. package/src/api-client/request.ts +26 -7
  30. package/src/api-client/short-side-publications.ts +37 -0
  31. package/src/api-client/socket.ts +64 -4
  32. package/src/api-client/swap-curve.ts +35 -0
  33. package/src/api-client/telemetry.ts +17 -0
  34. package/src/api-client/themes.ts +7 -1
  35. package/src/api-client/types.ts +90 -2
  36. package/src/app/global-shortcuts.ts +1 -16
  37. package/src/app/pane-runtime/plugin-bindings.ts +9 -2
  38. package/src/app/runtime/desktop-deeplink.ts +5 -5
  39. package/src/app/runtime/plugin-bindings.ts +2 -2
  40. package/src/app/runtime/startup.ts +13 -0
  41. package/src/app/runtime/update.ts +28 -8
  42. package/src/app/star-prompt/status-line.tsx +2 -2
  43. package/src/architecture/core-builtin-allowlist.json +5 -2
  44. package/src/architecture/pane-tabs-allowlist.json +65 -0
  45. package/src/architecture/ui-literals-allowlist.json +3 -3
  46. package/src/architecture/ui-tokens-allowlist.json +1 -1
  47. package/src/brokers/broker-portfolio-sync.ts +37 -0
  48. package/src/brokers/directory.ts +11 -0
  49. package/src/brokers/logos.ts +3 -1
  50. package/src/brokers/signed-in/adapter.ts +304 -26
  51. package/src/brokers/signed-in/client.ts +14 -2
  52. package/src/brokers/signed-in/connect.ts +10 -2
  53. package/src/brokers/sync-broker-instance.ts +9 -1
  54. package/src/capabilities/chart-series.ts +22 -6
  55. package/src/cli/bank-section.ts +1 -1
  56. package/src/cli/base-converter.ts +23 -14
  57. package/src/cli/central-bank-events.ts +1 -1
  58. package/src/cli/command-options.ts +59 -4
  59. package/src/cli/commands/api.ts +9 -6
  60. package/src/cli/commands/broker.ts +2 -2
  61. package/src/cli/commands/command-utils.ts +2 -2
  62. package/src/cli/commands/crypto-hints.ts +7 -20
  63. package/src/cli/commands/fred-series.ts +4 -2
  64. package/src/cli/commands/keybindings.ts +2 -2
  65. package/src/cli/commands/market.ts +1189 -296
  66. package/src/cli/commands/news-hints.ts +2 -2
  67. package/src/cli/commands/news-search.ts +59 -6
  68. package/src/cli/commands/options-leaps.ts +41 -26
  69. package/src/cli/commands/overview.ts +121 -48
  70. package/src/cli/commands/plugins.ts +9 -9
  71. package/src/cli/commands/remote.ts +13 -10
  72. package/src/cli/commands/search.ts +64 -44
  73. package/src/cli/commands/system.ts +27 -19
  74. package/src/cli/commands/themes.ts +1 -1
  75. package/src/cli/commands/ticker.ts +272 -70
  76. package/src/cli/context.ts +3 -2
  77. package/src/cli/desktop-pane-shot-view.ts +2 -2
  78. package/src/cli/desktop-pane-shot.ts +1 -1
  79. package/src/cli/earnings-calendar.ts +4 -4
  80. package/src/cli/entry.ts +18 -1
  81. package/src/cli/errors.ts +20 -2
  82. package/src/cli/exit-codes.ts +1 -1
  83. package/src/cli/export-source.ts +10 -4
  84. package/src/cli/freshness.ts +2 -8
  85. package/src/cli/fx-forward.ts +130 -0
  86. package/src/cli/fx-pair.ts +10 -9
  87. package/src/cli/glossary.ts +64 -20
  88. package/src/cli/help.ts +20 -14
  89. package/src/cli/history-export.ts +126 -0
  90. package/src/cli/history-rows.ts +196 -43
  91. package/src/cli/index-roots.ts +5 -45
  92. package/src/cli/index.ts +32 -18
  93. package/src/cli/issuer-membership.ts +103 -0
  94. package/src/cli/known-gaps.ts +118 -25
  95. package/src/cli/listing-arg.ts +315 -43
  96. package/src/cli/name-entries.ts +73 -22
  97. package/src/cli/native-smoke.ts +2 -0
  98. package/src/cli/not-a-ticker.ts +19 -9
  99. package/src/cli/opentui-libc.ts +5 -0
  100. package/src/cli/options.ts +12 -0
  101. package/src/cli/pane-functions/capabilities.ts +77 -20
  102. package/src/cli/pane-functions/catalog.ts +40 -23
  103. package/src/cli/pane-functions/discovery.ts +1 -1
  104. package/src/cli/pane-functions/dom.ts +12 -8
  105. package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
  106. package/src/cli/pane-functions/freshness.ts +174 -23
  107. package/src/cli/pane-functions/function-help.ts +66 -27
  108. package/src/cli/pane-functions/headless.ts +48 -16
  109. package/src/cli/pane-functions/index.ts +82 -21
  110. package/src/cli/pane-functions/options.ts +22 -2
  111. package/src/cli/pane-functions/report-notices.ts +12 -4
  112. package/src/cli/pane-functions/report.ts +5 -5
  113. package/src/cli/pane-functions/resolver.ts +65 -14
  114. package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
  115. package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
  116. package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
  117. package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
  118. package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
  119. package/src/cli/pane-functions/screenshot-payload.ts +453 -0
  120. package/src/cli/pane-functions/screenshot-render.ts +316 -0
  121. package/src/cli/pane-functions/screenshot-types.ts +162 -0
  122. package/src/cli/pane-functions/shot-options.ts +8 -3
  123. package/src/cli/peso-dollar-rates.ts +79 -0
  124. package/src/cli/quote-answer.ts +38 -3
  125. package/src/cli/report-tables.ts +190 -27
  126. package/src/cli/restore-plugins.ts +29 -14
  127. package/src/cli/result.ts +122 -25
  128. package/src/cli/series-search.ts +5 -5
  129. package/src/cli/statement-filings.ts +19 -0
  130. package/src/cli/suffixed-listing-rows.ts +49 -0
  131. package/src/cli/symbol-args.ts +50 -15
  132. package/src/cli/symbol-cells.ts +120 -0
  133. package/src/cli/symbol-list.ts +226 -60
  134. package/src/cli/symbol-rows.ts +131 -25
  135. package/src/cli/ticker-resolution.ts +108 -11
  136. package/src/cli/treasury-yields.ts +5 -5
  137. package/src/cli/types.ts +2 -0
  138. package/src/cli/uncovered-instrument.ts +3 -3
  139. package/src/cli/unknown-command.ts +6 -6
  140. package/src/components/chart/composite/chart-layout.ts +131 -0
  141. package/src/components/chart/composite/composite-chart.tsx +387 -21
  142. package/src/components/chart/composite/drawing-store.ts +26 -6
  143. package/src/components/chart/composite/interactions.ts +9 -3
  144. package/src/components/chart/composite/legend.tsx +75 -3
  145. package/src/components/chart/composite/levels.ts +5 -0
  146. package/src/components/chart/composite/panel-gestures.ts +41 -0
  147. package/src/components/chart/composite/panel-labels.ts +2 -1
  148. package/src/components/chart/composite/panel-surface.tsx +316 -11
  149. package/src/components/chart/composite/price-axis-labels.tsx +27 -3
  150. package/src/components/chart/composite/rasterizer.ts +44 -19
  151. package/src/components/chart/composite/scene.ts +16 -4
  152. package/src/components/chart/composite/text-renderer.ts +48 -21
  153. package/src/components/chart/composite/toolbar.tsx +52 -5
  154. package/src/components/chart/composite/types.ts +19 -0
  155. package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
  156. package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
  157. package/src/components/chart/composite/use-composite-viewport.ts +12 -3
  158. package/src/components/chart/composite/value-scale.ts +149 -0
  159. package/src/components/chart/composite/volume-profile-paint.ts +6 -3
  160. package/src/components/chart/core/pointer.ts +2 -0
  161. package/src/components/chart-table/index.ts +2 -0
  162. package/src/components/chart-table/read.tsx +74 -0
  163. package/src/components/command-bar/commands/collection-list.ts +41 -19
  164. package/src/components/command-bar/commands/collection.ts +9 -3
  165. package/src/components/command-bar/commands/direct/index.ts +1 -1
  166. package/src/components/command-bar/commands/direct/runtime.ts +3 -2
  167. package/src/components/command-bar/pane-templates/items.ts +59 -6
  168. package/src/components/command-bar/pane-templates/workflow.ts +10 -1
  169. package/src/components/command-bar/routes/root/command-items.ts +32 -6
  170. package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
  171. package/src/components/command-bar/routes/root/provider-search.ts +42 -11
  172. package/src/components/command-bar/routes/root/results.ts +18 -4
  173. package/src/components/command-bar/routes/root/runtime.ts +15 -3
  174. package/src/components/command-bar/routes/root/search-providers.ts +25 -2
  175. package/src/components/command-bar/routes/root/selection.ts +6 -1
  176. package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
  177. package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
  178. package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
  179. package/src/components/command-bar/selection-runtime.ts +50 -10
  180. package/src/components/command-bar/surface/index.tsx +24 -2
  181. package/src/components/command-bar/view-model.ts +7 -0
  182. package/src/components/command-bar/workflow/broker.ts +2 -2
  183. package/src/components/command-bar/workflow/collection-actions.ts +46 -7
  184. package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
  185. package/src/components/command-bar/workflow/fields.ts +8 -2
  186. package/src/components/command-bar/workflow/ops.ts +18 -4
  187. package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
  188. package/src/components/command-bar/workflow/submit.ts +3 -2
  189. package/src/components/command-bar/workflow/tickers.ts +5 -1
  190. package/src/components/company-logo.tsx +39 -0
  191. package/src/components/data-table/sections.ts +4 -2
  192. package/src/components/data-table/view.tsx +23 -3
  193. package/src/components/form-modal/confirm.tsx +2 -1
  194. package/src/components/form-modal/request.ts +5 -0
  195. package/src/components/form-modal/routes.ts +4 -1
  196. package/src/components/index.ts +13 -6
  197. package/src/components/layout/detached-pane-shell.tsx +1 -0
  198. package/src/components/layout/floating-pane.tsx +1 -0
  199. package/src/components/layout/header.tsx +234 -60
  200. package/src/components/layout/market-summary.ts +9 -6
  201. package/src/components/layout/pane/capture-status.ts +1 -1
  202. package/src/components/layout/pane/footer/model.ts +11 -5
  203. package/src/components/layout/pane/header.tsx +10 -3
  204. package/src/components/layout/pane/index.tsx +1 -0
  205. package/src/components/layout/pane/pane-tabs.tsx +9 -1
  206. package/src/components/layout/shell/chrome.ts +19 -6
  207. package/src/components/layout/shell/index.tsx +2 -1
  208. package/src/components/layout/shell/pane/layers.tsx +19 -1
  209. package/src/components/layout/status-bar.tsx +2 -2
  210. package/src/components/market-board/index.tsx +11 -3
  211. package/src/components/onboarding/onboarding-wizard.tsx +1 -0
  212. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
  213. package/src/components/onboarding/wizard-broker-sync.ts +4 -1
  214. package/src/components/pane-settings-dialog/desktop.tsx +20 -7
  215. package/src/components/pane-settings-dialog/tui.tsx +17 -9
  216. package/src/components/pane-settings-dialog/value.ts +8 -1
  217. package/src/components/pane-settings-dialog.tsx +44 -13
  218. package/src/components/sign-in-code-panel.tsx +27 -19
  219. package/src/components/ui/column-grid.tsx +48 -4
  220. package/src/components/ui/country-flag-art.tsx +180 -0
  221. package/src/components/ui/country-flag.tsx +118 -12
  222. package/src/components/ui/data-table/opentui/index.tsx +61 -2
  223. package/src/components/ui/data-table/opentui/model.ts +109 -2
  224. package/src/components/ui/data-table/types.ts +1 -1
  225. package/src/components/ui/display.tsx +5 -1
  226. package/src/components/ui/index.ts +3 -1
  227. package/src/components/ui/logo-mark.tsx +11 -2
  228. package/src/components/ui/query-bar.tsx +1 -1
  229. package/src/components/ui/row-notes.ts +79 -0
  230. package/src/components/ui/stat-grid.tsx +28 -3
  231. package/src/components/ui/status.tsx +3 -1
  232. package/src/core/app-services.ts +4 -0
  233. package/src/core/state/app/layout.ts +4 -4
  234. package/src/data/cached-query.ts +8 -1
  235. package/src/data/config/chart-settings.ts +3 -3
  236. package/src/data/config/home.ts +10 -2
  237. package/src/data/config/layout.ts +4 -1
  238. package/src/data/config/store/node.ts +4 -0
  239. package/src/data/config/store/normalize.ts +13 -3
  240. package/src/data/config/store/portfolio-as-of-column.ts +2 -2
  241. package/src/data/json-storage.ts +2 -0
  242. package/src/data/saved-copies.ts +84 -0
  243. package/src/docs/snapshot.json +65 -32
  244. package/src/i18n/es.ts +51 -12
  245. package/src/i18n/ja.ts +51 -12
  246. package/src/i18n/ko.ts +51 -12
  247. package/src/i18n/zh-cn.ts +51 -12
  248. package/src/i18n/zh-tw.ts +51 -12
  249. package/src/layout/desks.ts +1 -1
  250. package/src/layout/launch-layout.ts +1 -1
  251. package/src/layout/pane-manager/floating-actions.ts +5 -3
  252. package/src/layout/pane-manager/floating.ts +43 -1
  253. package/src/layout/pane-manager/index.ts +2 -0
  254. package/src/layout/selected-ticker.ts +74 -0
  255. package/src/market-data/depositary-pair.ts +251 -0
  256. package/src/market-data/futures-board.ts +1 -1
  257. package/src/market-data/futures-contract.ts +124 -40
  258. package/src/market-data/fx-closes.ts +17 -2
  259. package/src/market-data/hooks.tsx +19 -11
  260. package/src/market-data/market/change-basis.ts +49 -7
  261. package/src/market-data/market/freshness.ts +34 -5
  262. package/src/market-data/market/period-return.ts +163 -0
  263. package/src/market-data/market/trading-sessions.ts +13 -0
  264. package/src/market-data/market/year-to-date.ts +8 -13
  265. package/src/market-data/performance.ts +20 -14
  266. package/src/market-data/perpetual-premium.ts +32 -0
  267. package/src/market-data/published-apac-sessions.ts +2 -1
  268. package/src/market-data/published-futures-sessions.ts +69 -0
  269. package/src/market-data/quotes/contributions.ts +5 -1
  270. package/src/market-data/quotes/freshness.ts +42 -0
  271. package/src/market-data/quotes/intraday-bar.ts +37 -0
  272. package/src/market-data/quotes/regular-session-valuation.ts +13 -8
  273. package/src/market-data/quotes/resolution.ts +4 -2
  274. package/src/market-data/tax-equivalent.ts +1 -1
  275. package/src/market-data/ticker-price-returns.ts +3 -1
  276. package/src/news/aggregator.ts +5 -1
  277. package/src/news/news-model.ts +4 -0
  278. package/src/news/types.ts +11 -0
  279. package/src/notifications/app-notifier.ts +1 -1
  280. package/src/plugins/auto-update.ts +2 -2
  281. package/src/plugins/builtin/account-management/index.tsx +1 -0
  282. package/src/plugins/builtin/account-management/pane.tsx +9 -8
  283. package/src/plugins/builtin/alerts/alert-engine.ts +30 -18
  284. package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
  285. package/src/plugins/builtin/alerts/cli.ts +485 -76
  286. package/src/plugins/builtin/alerts/command.ts +5 -10
  287. package/src/plugins/builtin/alerts/event-rows.ts +9 -5
  288. package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
  289. package/src/plugins/builtin/alerts/events.ts +108 -5
  290. package/src/plugins/builtin/alerts/fired-log.ts +78 -0
  291. package/src/plugins/builtin/alerts/format.ts +2 -1
  292. package/src/plugins/builtin/alerts/headless.ts +100 -39
  293. package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
  294. package/src/plugins/builtin/alerts/history-rows.ts +89 -0
  295. package/src/plugins/builtin/alerts/index.tsx +45 -18
  296. package/src/plugins/builtin/alerts/iv-level.ts +365 -0
  297. package/src/plugins/builtin/alerts/levels.ts +3 -3
  298. package/src/plugins/builtin/alerts/pane.tsx +97 -29
  299. package/src/plugins/builtin/alerts/price.ts +35 -0
  300. package/src/plugins/builtin/alerts/quotes.ts +24 -0
  301. package/src/plugins/builtin/alerts/research-builder.ts +84 -11
  302. package/src/plugins/builtin/alerts/research-rules.ts +77 -3
  303. package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
  304. package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
  305. package/src/plugins/builtin/alerts/scheduled.ts +188 -29
  306. package/src/plugins/builtin/alerts/storage.ts +13 -10
  307. package/src/plugins/builtin/alerts/targets.ts +12 -3
  308. package/src/plugins/builtin/analytics/index.tsx +8 -2
  309. package/src/plugins/builtin/analytics/metrics.ts +1 -1
  310. package/src/plugins/builtin/analytics/risk-book.ts +215 -0
  311. package/src/plugins/builtin/analytics/risk-client.ts +13 -6
  312. package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
  313. package/src/plugins/builtin/analytics/risk-math.ts +6 -3
  314. package/src/plugins/builtin/analytics/risk-model.ts +297 -104
  315. package/src/plugins/builtin/analytics/risk-options.ts +17 -2
  316. package/src/plugins/builtin/analytics/risk-pane.tsx +107 -43
  317. package/src/plugins/builtin/attention/pane.tsx +22 -16
  318. package/src/plugins/builtin/awards/headless.ts +7 -3
  319. package/src/plugins/builtin/awards/model.ts +18 -0
  320. package/src/plugins/builtin/awards/pane.tsx +12 -9
  321. package/src/plugins/builtin/backtest/export.ts +1 -1
  322. package/src/plugins/builtin/backtest/headless.ts +3 -3
  323. package/src/plugins/builtin/backtest/rules.ts +41 -5
  324. package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
  325. package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
  326. package/src/plugins/builtin/broker-manager/footer.ts +27 -11
  327. package/src/plugins/builtin/broker-manager/index.tsx +21 -12
  328. package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
  329. package/src/plugins/builtin/broker-manager/model.ts +12 -2
  330. package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
  331. package/src/plugins/builtin/broker-manager/table.ts +28 -12
  332. package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
  333. package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
  334. package/src/plugins/builtin/broker-trading/controller.ts +45 -13
  335. package/src/plugins/builtin/broker-trading/dock.tsx +24 -4
  336. package/src/plugins/builtin/broker-trading/headless.ts +2 -1
  337. package/src/plugins/builtin/broker-trading/index.tsx +7 -1
  338. package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
  339. package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
  340. package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
  341. package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
  342. package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
  343. package/src/plugins/builtin/broker-trading/pane.tsx +115 -26
  344. package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
  345. package/src/plugins/builtin/broker-trading/ticket.tsx +122 -36
  346. package/src/plugins/builtin/buildout/cells.tsx +4 -0
  347. package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
  348. package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
  349. package/src/plugins/builtin/buildout/pane/index.tsx +34 -18
  350. package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
  351. package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
  352. package/src/plugins/builtin/catalysts/client.ts +2 -2
  353. package/src/plugins/builtin/catalysts/detail.tsx +3 -2
  354. package/src/plugins/builtin/catalysts/index.tsx +1 -1
  355. package/src/plugins/builtin/catalysts/model.ts +49 -4
  356. package/src/plugins/builtin/catalysts/pane.tsx +9 -11
  357. package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
  358. package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
  359. package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
  360. package/src/plugins/builtin/changelog/index.tsx +1 -0
  361. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
  362. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
  363. package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
  364. package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
  365. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
  366. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
  367. package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
  368. package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
  369. package/src/plugins/builtin/chart-composer/headless.ts +253 -35
  370. package/src/plugins/builtin/chart-composer/index.tsx +12 -2
  371. package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
  372. package/src/plugins/builtin/chart-composer/pane.tsx +128 -28
  373. package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
  374. package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
  375. package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
  376. package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
  377. package/src/plugins/builtin/chart-composer/settings.ts +31 -19
  378. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
  379. package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
  380. package/src/plugins/builtin/chat/attachments/model.ts +3 -1
  381. package/src/plugins/builtin/chat/content/index.tsx +1 -1
  382. package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
  383. package/src/plugins/builtin/chat/people.ts +1 -1
  384. package/src/plugins/builtin/clinical-trials/client.ts +5 -2
  385. package/src/plugins/builtin/clinical-trials/model.ts +13 -4
  386. package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
  387. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
  388. package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
  389. package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
  390. package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
  391. package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
  392. package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
  393. package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
  394. package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
  395. package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
  396. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  397. package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
  398. package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
  399. package/src/plugins/builtin/commodity-balances/pane.tsx +14 -8
  400. package/src/plugins/builtin/company-attention/app-rank.tsx +11 -10
  401. package/src/plugins/builtin/company-attention/building-model.ts +222 -0
  402. package/src/plugins/builtin/company-attention/building.tsx +172 -0
  403. package/src/plugins/builtin/company-attention/client.ts +117 -1
  404. package/src/plugins/builtin/company-attention/evidence.ts +16 -3
  405. package/src/plugins/builtin/company-attention/headless.ts +101 -5
  406. package/src/plugins/builtin/company-attention/index.tsx +6 -3
  407. package/src/plugins/builtin/company-attention/model.ts +33 -7
  408. package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
  409. package/src/plugins/builtin/company-attention/moves.tsx +159 -0
  410. package/src/plugins/builtin/company-attention/pane.tsx +89 -31
  411. package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
  412. package/src/plugins/builtin/company-attention/signals.tsx +160 -0
  413. package/src/plugins/builtin/company-kpis/headless.ts +3 -2
  414. package/src/plugins/builtin/company-kpis/model.ts +2 -2
  415. package/src/plugins/builtin/company-kpis/pane.tsx +5 -4
  416. package/src/plugins/builtin/composite-plugins.ts +2 -1
  417. package/src/plugins/builtin/congress-trades/headless.ts +1 -1
  418. package/src/plugins/builtin/connections/index.ts +1 -0
  419. package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
  420. package/src/plugins/builtin/correlation/headless.ts +7 -4
  421. package/src/plugins/builtin/correlation/relationship/model.ts +1 -0
  422. package/src/plugins/builtin/correlation/settings.ts +1 -0
  423. package/src/plugins/builtin/cot/headless.ts +28 -6
  424. package/src/plugins/builtin/cot/model.ts +12 -0
  425. package/src/plugins/builtin/cot/pane.tsx +7 -5
  426. package/src/plugins/builtin/cpi/headless.ts +5 -1
  427. package/src/plugins/builtin/cpi/pane.tsx +11 -9
  428. package/src/plugins/builtin/credit-boards/model.ts +2 -0
  429. package/src/plugins/builtin/credit-documents/headless.ts +1 -1
  430. package/src/plugins/builtin/credit-documents/pane.tsx +8 -7
  431. package/src/plugins/builtin/crypto-board/client.ts +86 -0
  432. package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
  433. package/src/plugins/builtin/crypto-board/defi.ts +239 -0
  434. package/src/plugins/builtin/crypto-board/headless.ts +117 -6
  435. package/src/plugins/builtin/crypto-board/index.tsx +8 -3
  436. package/src/plugins/builtin/crypto-board/model.ts +40 -8
  437. package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
  438. package/src/plugins/builtin/crypto-board/series.ts +19 -1
  439. package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
  440. package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
  441. package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
  442. package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
  443. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
  444. package/src/plugins/builtin/debt-maturities/pane.tsx +4 -3
  445. package/src/plugins/builtin/debug/index.tsx +1 -0
  446. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  447. package/src/plugins/builtin/docs/index.tsx +1 -1
  448. package/src/plugins/builtin/docs/pane.tsx +2 -2
  449. package/src/plugins/builtin/docs/targets.ts +1 -1
  450. package/src/plugins/builtin/doe/client.ts +5 -1
  451. package/src/plugins/builtin/doe/headless.ts +12 -6
  452. package/src/plugins/builtin/doe/model.ts +43 -6
  453. package/src/plugins/builtin/doe/pane.tsx +19 -12
  454. package/src/plugins/builtin/earnings/board-model.ts +13 -6
  455. package/src/plugins/builtin/earnings/board-table.ts +4 -1
  456. package/src/plugins/builtin/earnings/board.tsx +253 -50
  457. package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
  458. package/src/plugins/builtin/earnings/calendar.tsx +338 -0
  459. package/src/plugins/builtin/earnings/client.ts +4 -3
  460. package/src/plugins/builtin/earnings/headless.ts +53 -15
  461. package/src/plugins/builtin/earnings/history-model.ts +53 -36
  462. package/src/plugins/builtin/earnings/index.tsx +15 -22
  463. package/src/plugins/builtin/earnings/model.ts +17 -12
  464. package/src/plugins/builtin/earnings/scope.ts +89 -0
  465. package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
  466. package/src/plugins/builtin/earnings-calls/data.ts +146 -2
  467. package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
  468. package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
  469. package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
  470. package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
  471. package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
  472. package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
  473. package/src/plugins/builtin/econ/calendar-model.ts +132 -20
  474. package/src/plugins/builtin/econ/detail-view.tsx +48 -10
  475. package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
  476. package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
  477. package/src/plugins/builtin/econ/index.tsx +112 -25
  478. package/src/plugins/builtin/econ/print-watch.ts +2 -1
  479. package/src/plugins/builtin/econ/types.ts +32 -1
  480. package/src/plugins/builtin/econ-statistics/client.ts +16 -3
  481. package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
  482. package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
  483. package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
  484. package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
  485. package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
  486. package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
  487. package/src/plugins/builtin/econ-statistics/view.ts +16 -1
  488. package/src/plugins/builtin/equity-screener/client.ts +21 -3
  489. package/src/plugins/builtin/equity-screener/headless.ts +77 -5
  490. package/src/plugins/builtin/equity-screener/live.ts +1 -1
  491. package/src/plugins/builtin/equity-screener/model.ts +34 -0
  492. package/src/plugins/builtin/equity-screener/pane.tsx +148 -106
  493. package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
  494. package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
  495. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
  496. package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
  497. package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
  498. package/src/plugins/builtin/exposure/headless.ts +51 -6
  499. package/src/plugins/builtin/exposure/index.tsx +28 -2
  500. package/src/plugins/builtin/exposure/model.ts +7 -0
  501. package/src/plugins/builtin/exposure/pane.tsx +16 -10
  502. package/src/plugins/builtin/fear-greed/index.tsx +1 -1
  503. package/src/plugins/builtin/filing-events/index.tsx +1 -1
  504. package/src/plugins/builtin/futures/model.ts +10 -9
  505. package/src/plugins/builtin/futures/returns.ts +8 -36
  506. package/src/plugins/builtin/futures/table.tsx +1 -1
  507. package/src/plugins/builtin/futures-curve/client.ts +40 -9
  508. package/src/plugins/builtin/futures-curve/headless.ts +39 -17
  509. package/src/plugins/builtin/futures-curve/model.ts +258 -69
  510. package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
  511. package/src/plugins/builtin/fx-matrix/client.ts +15 -8
  512. package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
  513. package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
  514. package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
  515. package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
  516. package/src/plugins/builtin/gpu/board.tsx +4 -1
  517. package/src/plugins/builtin/gpu/changes.tsx +10 -10
  518. package/src/plugins/builtin/gpu/equities.tsx +4 -1
  519. package/src/plugins/builtin/gpu/headless.ts +9 -4
  520. package/src/plugins/builtin/gpu/history.tsx +4 -2
  521. package/src/plugins/builtin/gpu/pane.tsx +5 -3
  522. package/src/plugins/builtin/help/index.tsx +13 -3
  523. package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
  524. package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
  525. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  526. package/src/plugins/builtin/holders/client.ts +15 -2
  527. package/src/plugins/builtin/holders/format.ts +28 -17
  528. package/src/plugins/builtin/holders/headless.ts +48 -8
  529. package/src/plugins/builtin/holders/index-holders.ts +264 -0
  530. package/src/plugins/builtin/holders/pane.tsx +79 -15
  531. package/src/plugins/builtin/holders/report-rows.ts +35 -7
  532. package/src/plugins/builtin/holders/table-model.ts +2 -1
  533. package/src/plugins/builtin/holders/thirteenf-match.ts +3 -1
  534. package/src/plugins/builtin/holders/treemap.tsx +2 -2
  535. package/src/plugins/builtin/insider/base-values.ts +11 -7
  536. package/src/plugins/builtin/insider/client.ts +3 -0
  537. package/src/plugins/builtin/insider/headless.ts +20 -7
  538. package/src/plugins/builtin/insider/index.tsx +17 -8
  539. package/src/plugins/builtin/insider/issuer.ts +34 -4
  540. package/src/plugins/builtin/insider/signals-client.ts +3 -3
  541. package/src/plugins/builtin/insider/signals.tsx +1 -1
  542. package/src/plugins/builtin/insider/table-model.ts +35 -13
  543. package/src/plugins/builtin/insider/table.tsx +5 -2
  544. package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
  545. package/src/plugins/builtin/iv-history/client.ts +20 -0
  546. package/src/plugins/builtin/iv-history/events.ts +145 -0
  547. package/src/plugins/builtin/iv-history/format.ts +3 -0
  548. package/src/plugins/builtin/iv-history/headless.ts +91 -7
  549. package/src/plugins/builtin/iv-history/index.tsx +1 -1
  550. package/src/plugins/builtin/iv-history/model.ts +13 -4
  551. package/src/plugins/builtin/iv-history/series.ts +3 -1
  552. package/src/plugins/builtin/jobs/lock-note.ts +1 -1
  553. package/src/plugins/builtin/jobs/model.ts +4 -15
  554. package/src/plugins/builtin/jobs/pane.tsx +7 -5
  555. package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
  556. package/src/plugins/builtin/jodi/pane.tsx +14 -8
  557. package/src/plugins/builtin/layout-manager/index.tsx +1 -0
  558. package/src/plugins/builtin/macro-day/headless.ts +20 -7
  559. package/src/plugins/builtin/macro-day/index.tsx +3 -2
  560. package/src/plugins/builtin/macro-day/model.ts +29 -8
  561. package/src/plugins/builtin/macro-day/pane.tsx +46 -10
  562. package/src/plugins/builtin/macro-day/releases.ts +55 -13
  563. package/src/plugins/builtin/market-heatmap/data.ts +9 -2
  564. package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
  565. package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
  566. package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
  567. package/src/plugins/builtin/market-heatmap/model.ts +10 -7
  568. package/src/plugins/builtin/market-movers/client.ts +36 -21
  569. package/src/plugins/builtin/market-movers/headless.ts +8 -2
  570. package/src/plugins/builtin/market-movers/index.tsx +16 -10
  571. package/src/plugins/builtin/market-movers/screener.ts +33 -11
  572. package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
  573. package/src/plugins/builtin/market-movers/session-table.ts +4 -2
  574. package/src/plugins/builtin/market-movers/table.ts +4 -1
  575. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  576. package/src/plugins/builtin/market-valuation/view.ts +5 -0
  577. package/src/plugins/builtin/members/headless.ts +6 -5
  578. package/src/plugins/builtin/members/pane.tsx +11 -4
  579. package/src/plugins/builtin/mna/client.ts +1 -0
  580. package/src/plugins/builtin/mna/detail.tsx +20 -32
  581. package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
  582. package/src/plugins/builtin/mna/distress/model.ts +3 -2
  583. package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
  584. package/src/plugins/builtin/mna/headless.ts +6 -13
  585. package/src/plugins/builtin/mna/model.ts +93 -15
  586. package/src/plugins/builtin/mna/pane.tsx +5 -4
  587. package/src/plugins/builtin/news/headless.ts +12 -6
  588. package/src/plugins/builtin/news/index.tsx +10 -1
  589. package/src/plugins/builtin/news/wire/index.tsx +3 -2
  590. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
  591. package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
  592. package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
  593. package/src/plugins/builtin/notes/cli.ts +50 -15
  594. package/src/plugins/builtin/notes/export.ts +17 -6
  595. package/src/plugins/builtin/notes/files.ts +16 -5
  596. package/src/plugins/builtin/openfda/pane.tsx +1 -1
  597. package/src/plugins/builtin/options/carry.ts +2 -2
  598. package/src/plugins/builtin/options/enrichment-model.ts +10 -9
  599. package/src/plugins/builtin/options/index.tsx +1 -1
  600. package/src/plugins/builtin/options/model-spot.ts +70 -4
  601. package/src/plugins/builtin/options/view.tsx +51 -23
  602. package/src/plugins/builtin/options-calculator/headless.ts +46 -7
  603. package/src/plugins/builtin/options-calculator/pane.tsx +3 -2
  604. package/src/plugins/builtin/options-positioning/headless.ts +11 -3
  605. package/src/plugins/builtin/options-positioning/model.ts +24 -0
  606. package/src/plugins/builtin/options-positioning/pane.tsx +10 -12
  607. package/src/plugins/builtin/options-scenario/client.ts +142 -29
  608. package/src/plugins/builtin/options-scenario/collar.ts +8 -6
  609. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
  610. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
  611. package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
  612. package/src/plugins/builtin/options-scenario/csp.ts +101 -21
  613. package/src/plugins/builtin/options-scenario/headless.ts +49 -21
  614. package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
  615. package/src/plugins/builtin/options-scenario/index.tsx +28 -3
  616. package/src/plugins/builtin/options-scenario/model.ts +13 -0
  617. package/src/plugins/builtin/options-scenario/pane.tsx +22 -10
  618. package/src/plugins/builtin/perps/equity-row.tsx +11 -3
  619. package/src/plugins/builtin/perps/freshness.ts +8 -5
  620. package/src/plugins/builtin/perps/headless.ts +3 -1
  621. package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
  622. package/src/plugins/builtin/perps/live-rows.ts +161 -0
  623. package/src/plugins/builtin/perps/markets-section.tsx +154 -0
  624. package/src/plugins/builtin/perps/pane.tsx +36 -17
  625. package/src/plugins/builtin/perps/venue-markets.ts +96 -0
  626. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
  627. package/src/plugins/builtin/plugin-marketplace/pane.tsx +145 -60
  628. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +78 -27
  629. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
  630. package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
  631. package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
  632. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
  633. package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
  634. package/src/plugins/builtin/portfolio-list/income.ts +13 -2
  635. package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
  636. package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
  637. package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
  638. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
  639. package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
  640. package/src/plugins/builtin/power/client.ts +6 -3
  641. package/src/plugins/builtin/power/evidence.ts +22 -7
  642. package/src/plugins/builtin/power/gas.tsx +5 -1
  643. package/src/plugins/builtin/power/pane.tsx +44 -32
  644. package/src/plugins/builtin/power/prices.tsx +10 -3
  645. package/src/plugins/builtin/research/analyst-headless.ts +25 -8
  646. package/src/plugins/builtin/research/analyst-model.ts +68 -3
  647. package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
  648. package/src/plugins/builtin/research/event-model.ts +3 -1
  649. package/src/plugins/builtin/research/event-sources.ts +12 -4
  650. package/src/plugins/builtin/research/index.tsx +2 -1
  651. package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
  652. package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
  653. package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
  654. package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
  655. package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
  656. package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
  657. package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
  658. package/src/plugins/builtin/reverse-dcf/client.ts +2 -1
  659. package/src/plugins/builtin/reverse-dcf/model.ts +10 -5
  660. package/src/plugins/builtin/risk-factors/index.tsx +2 -2
  661. package/src/plugins/builtin/scanner/lock-note.ts +1 -1
  662. package/src/plugins/builtin/seasonality/client.ts +2 -1
  663. package/src/plugins/builtin/sec/forms.ts +13 -0
  664. package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
  665. package/src/plugins/builtin/sec/fund-facts.ts +68 -8
  666. package/src/plugins/builtin/sec/headless.ts +85 -8
  667. package/src/plugins/builtin/sec/index.tsx +58 -12
  668. package/src/plugins/builtin/sectors/client.ts +6 -4
  669. package/src/plugins/builtin/sectors/sector-model.ts +11 -15
  670. package/src/plugins/builtin/series-inventory.ts +2 -0
  671. package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
  672. package/src/plugins/builtin/shared/cloud-resource.ts +15 -2
  673. package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
  674. package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
  675. package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
  676. package/src/plugins/builtin/shared/earnings-report.ts +3 -3
  677. package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
  678. package/src/plugins/builtin/shared/job-posting.ts +129 -0
  679. package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
  680. package/src/plugins/builtin/shared/report-freshness.ts +3 -0
  681. package/src/plugins/builtin/shared/research-trading.tsx +23 -0
  682. package/src/plugins/builtin/shared/series-cache.ts +2 -0
  683. package/src/plugins/builtin/shared/series-list-detail.tsx +3 -1
  684. package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
  685. package/src/plugins/builtin/shared/volatility/options.ts +2 -1
  686. package/src/plugins/builtin/short-interest/client.ts +2 -0
  687. package/src/plugins/builtin/short-interest/headless.ts +41 -12
  688. package/src/plugins/builtin/short-interest/index.tsx +23 -1
  689. package/src/plugins/builtin/short-interest/model.ts +70 -19
  690. package/src/plugins/builtin/short-interest/pane.tsx +19 -2
  691. package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
  692. package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
  693. package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
  694. package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
  695. package/src/plugins/builtin/short-interest/schedule.ts +35 -2
  696. package/src/plugins/builtin/short-interest/surface.tsx +11 -4
  697. package/src/plugins/builtin/short-interest/types.ts +9 -0
  698. package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
  699. package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
  700. package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
  701. package/src/plugins/builtin/social-mentions/client.ts +2 -1
  702. package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
  703. package/src/plugins/builtin/supply-chain/model.ts +51 -3
  704. package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
  705. package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
  706. package/src/plugins/builtin/themes/headless.ts +8 -5
  707. package/src/plugins/builtin/themes/model.ts +14 -4
  708. package/src/plugins/builtin/themes/pane.tsx +24 -5
  709. package/src/plugins/builtin/thirteenf/headless.ts +5 -5
  710. package/src/plugins/builtin/thirteenf/signals.ts +3 -1
  711. package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
  712. package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
  713. package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
  714. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
  715. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
  716. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
  717. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
  718. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
  719. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
  720. package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
  721. package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
  722. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
  723. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
  724. package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
  725. package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
  726. package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
  727. package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
  728. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
  729. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
  730. package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
  731. package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
  732. package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
  733. package/src/plugins/builtin/vol-surface/client.ts +28 -7
  734. package/src/plugins/builtin/vol-surface/headless.ts +43 -15
  735. package/src/plugins/builtin/vol-surface/model.ts +34 -12
  736. package/src/plugins/builtin/vol-surface/pane.tsx +33 -13
  737. package/src/plugins/builtin/vol-surface/stored.ts +34 -3
  738. package/src/plugins/builtin/vol-surface/tables.ts +14 -0
  739. package/src/plugins/builtin/volatility/model.ts +5 -10
  740. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
  741. package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
  742. package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
  743. package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
  744. package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
  745. package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
  746. package/src/plugins/builtin/world-venue-map/index.ts +3 -26
  747. package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
  748. package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
  749. package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
  750. package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
  751. package/src/plugins/builtin/yield-curve/client.ts +25 -0
  752. package/src/plugins/builtin/yield-curve/curves.ts +7 -4
  753. package/src/plugins/builtin/yield-curve/headless.ts +38 -4
  754. package/src/plugins/builtin/yield-curve/history.ts +4 -3
  755. package/src/plugins/builtin/yield-curve/index.tsx +12 -6
  756. package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
  757. package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
  758. package/src/plugins/bundle.ts +1 -1
  759. package/src/plugins/catalog-browser.ts +2 -1
  760. package/src/plugins/compat.ts +3 -3
  761. package/src/plugins/dependencies.ts +75 -14
  762. package/src/plugins/extracted-plugins.ts +132 -34
  763. package/src/plugins/host-link.ts +149 -34
  764. package/src/plugins/installer.ts +35 -10
  765. package/src/plugins/loader.ts +206 -38
  766. package/src/plugins/manager-node.ts +2 -2
  767. package/src/plugins/marketplace/deactivation.ts +22 -0
  768. package/src/plugins/marketplace/model.ts +2 -2
  769. package/src/plugins/marketplace/packs.ts +1 -1
  770. package/src/plugins/ownership.ts +1 -1
  771. package/src/plugins/peer-retry.ts +155 -0
  772. package/src/plugins/registry/contributions.ts +3 -1
  773. package/src/plugins/registry/index.ts +4 -3
  774. package/src/plugins/registry/plugin-state.ts +3 -0
  775. package/src/plugins/seed-notice.ts +20 -0
  776. package/src/plugins/shortcut-argument.ts +39 -0
  777. package/src/portfolio/allocation.ts +1 -1
  778. package/src/portfolio/collection-name.ts +31 -0
  779. package/src/portfolio/column-values.ts +10 -8
  780. package/src/portfolio/cost-fx.ts +296 -19
  781. package/src/portfolio/manual-lots.ts +75 -0
  782. package/src/portfolio/mutations.ts +60 -22
  783. package/src/portfolio/suffixed-listings.ts +209 -0
  784. package/src/portfolio/totals-currency.ts +24 -0
  785. package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
  786. package/src/public/time-series.ts +11 -8
  787. package/src/remote/schema.ts +1 -1
  788. package/src/renderers/browser/config-host.ts +3 -0
  789. package/src/renderers/browser/main.tsx +23 -3
  790. package/src/renderers/dom/boot-frame-page.ts +187 -0
  791. package/src/renderers/dom/boot-frame.ts +237 -0
  792. package/src/renderers/dom/build-assets.ts +46 -8
  793. package/src/renderers/dom/desktop/controls.tsx +1 -1
  794. package/src/renderers/dom/desktop/query-bar.tsx +2 -0
  795. package/src/renderers/dom/dom-ui-host.tsx +4 -29
  796. package/src/renderers/dom/host/ascii-text.ts +16 -2
  797. package/src/renderers/dom/host/box.tsx +43 -2
  798. package/src/renderers/dom/host/chart-surface.tsx +2 -0
  799. package/src/renderers/dom/host/hls.chunk.ts +3 -0
  800. package/src/renderers/dom/host/image-surface.tsx +49 -0
  801. package/src/renderers/dom/host/media-surface.tsx +24 -18
  802. package/src/renderers/dom/host/mouse.ts +30 -0
  803. package/src/renderers/dom/host/size-observer.ts +1 -1
  804. package/src/renderers/dom/host/style.ts +1 -1
  805. package/src/renderers/dom/host/tabs.tsx +16 -5
  806. package/src/renderers/dom/host/top-surface.ts +127 -0
  807. package/src/renderers/dom/styles.css +115 -0
  808. package/src/renderers/dom/test-utils.tsx +2 -1
  809. package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
  810. package/src/renderers/electrobun/bun/core-capabilities.ts +9 -1
  811. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
  812. package/src/renderers/electrobun/bun/desktop/backend.ts +56 -3
  813. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
  814. package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
  815. package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
  816. package/src/renderers/electrobun/bun/desktop/update.ts +210 -30
  817. package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
  818. package/src/renderers/electrobun/bun/index.ts +11 -1
  819. package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
  820. package/src/renderers/electrobun/bun/window/create.ts +4 -1
  821. package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
  822. package/src/renderers/electrobun/shared/protocol.ts +6 -1
  823. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  824. package/src/renderers/electrobun/view/config-host.ts +3 -0
  825. package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
  826. package/src/renderers/electrobun/view/main.tsx +30 -4
  827. package/src/renderers/electrobun/view/news-source.ts +9 -7
  828. package/src/renderers/electrobun/view/remote/asset-data-client.ts +9 -2
  829. package/src/renderers/electrobun/view/ui-host.tsx +1 -1
  830. package/src/renderers/opentui/browser-open.ts +88 -0
  831. package/src/renderers/opentui/host.tsx +5 -14
  832. package/src/renderers/opentui/image/loader.ts +3 -2
  833. package/src/renderers/opentui/native-smoke.ts +10 -1
  834. package/src/renderers/opentui/pane-image/index.ts +2 -1
  835. package/src/renderers/opentui/render-lib.ts +253 -0
  836. package/src/renderers/opentui/render-timing-smoke.ts +1 -1
  837. package/src/renderers/opentui/start.tsx +8 -1
  838. package/src/renderers/share/layout-view.tsx +2 -2
  839. package/src/renderers/share/main.tsx +4 -4
  840. package/src/renderers/share/view.tsx +1 -1
  841. package/src/series/resolve.ts +22 -6
  842. package/src/series/standalone.ts +30 -2
  843. package/src/series/types.ts +8 -1
  844. package/src/shares/api.ts +4 -1
  845. package/src/shares/pane.ts +1 -1
  846. package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
  847. package/src/sources/gloomberb-cloud/index.ts +39 -16
  848. package/src/sources/gloomberb-cloud/news.ts +39 -0
  849. package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
  850. package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
  851. package/src/sources/listing-symbols.ts +37 -0
  852. package/src/sources/provider-errors.ts +16 -2
  853. package/src/sources/provider-router/financial-routes.ts +21 -3
  854. package/src/sources/provider-router/financials.ts +15 -2
  855. package/src/sources/provider-router/history.ts +30 -8
  856. package/src/sources/provider-router/index.ts +16 -0
  857. package/src/sources/provider-router/primary.ts +1 -1
  858. package/src/state/app/bootstrap.ts +23 -0
  859. package/src/state/hooks/inline-tickers.ts +30 -5
  860. package/src/sync/controller.ts +1 -1
  861. package/src/sync/core-contributors.ts +46 -14
  862. package/src/team/collections-sync.ts +2 -1
  863. package/src/team/collections.ts +178 -17
  864. package/src/telemetry/crash-reports.ts +21 -4
  865. package/src/telemetry/update-failures-node.ts +59 -0
  866. package/src/telemetry/update-failures.ts +307 -0
  867. package/src/test-support/builtin-panes.ts +1 -1
  868. package/src/theme/colors.ts +3 -30
  869. package/src/theme/ui-tokens.ts +37 -1
  870. package/src/tickers/company-name-listing.ts +215 -36
  871. package/src/tickers/company-name.ts +43 -0
  872. package/src/tickers/index-roots.ts +47 -0
  873. package/src/tickers/instrument-label.ts +5 -1
  874. package/src/tickers/listing-fate.ts +30 -0
  875. package/src/tickers/metadata.ts +10 -0
  876. package/src/tickers/perpetual-listing.ts +5 -2
  877. package/src/tickers/search/home-listing.ts +75 -0
  878. package/src/tickers/search/index.ts +76 -27
  879. package/src/tickers/search/ranking.ts +54 -4
  880. package/src/tickers/search/upsert.ts +29 -0
  881. package/src/tickers/terminal-codes.ts +416 -40
  882. package/src/time-series/bar-aggregation.ts +243 -0
  883. package/src/time-series/base-series.ts +307 -0
  884. package/src/time-series/chart-bounds.ts +349 -0
  885. package/src/time-series/chart-seed.ts +142 -0
  886. package/src/time-series/economic.ts +5 -0
  887. package/src/time-series/fundamentals.ts +18 -3
  888. package/src/time-series/history-acquisition.ts +499 -0
  889. package/src/time-series/leverage.ts +100 -0
  890. package/src/time-series/market-resolution.ts +4 -2
  891. package/src/time-series/market.ts +2 -1
  892. package/src/time-series/price-comparison.ts +175 -30
  893. package/src/time-series/range.ts +9 -1
  894. package/src/time-series/resolution.ts +174 -16
  895. package/src/time-series/resolve-context.ts +157 -0
  896. package/src/time-series/resolve.ts +126 -1577
  897. package/src/time-series/series-name.ts +38 -0
  898. package/src/time-series/series-presentation.ts +208 -0
  899. package/src/time-series/spec.ts +11 -6
  900. package/src/time-series/studies.ts +109 -12
  901. package/src/time-series/tail-reconcile.ts +121 -0
  902. package/src/time-series/types.ts +29 -2
  903. package/src/time-series/use-chart-resolution.ts +4 -5
  904. package/src/types/broker.ts +10 -0
  905. package/src/types/config.ts +14 -0
  906. package/src/types/data-provider.ts +20 -0
  907. package/src/types/exchange-rate.ts +6 -0
  908. package/src/types/financials.ts +104 -7
  909. package/src/types/headless.ts +83 -6
  910. package/src/types/instrument.ts +3 -0
  911. package/src/types/issuer-listings.ts +28 -0
  912. package/src/types/plugin.ts +65 -8
  913. package/src/types/price-history.ts +2 -0
  914. package/src/types/ticker.ts +13 -0
  915. package/src/types/trading.ts +59 -1
  916. package/src/ui/ascii-font.ts +13 -1
  917. package/src/ui/host.tsx +6 -1
  918. package/src/updater/command-names.ts +175 -0
  919. package/src/updater/failure.ts +163 -0
  920. package/src/updater/index.ts +97 -28
  921. package/src/utils/calendar-date.ts +32 -15
  922. package/src/utils/cash-runway.ts +241 -0
  923. package/src/utils/central-banks.ts +42 -0
  924. package/src/utils/cli-output.ts +259 -80
  925. package/src/utils/contract-month.ts +16 -0
  926. package/src/utils/crypto-pair.ts +61 -3
  927. package/src/utils/currency-pair.ts +26 -0
  928. package/src/utils/currency-units.ts +29 -0
  929. package/src/utils/datetime-format.ts +10 -0
  930. package/src/utils/debug-log.ts +1 -1
  931. package/src/utils/decimal-comma.ts +39 -6
  932. package/src/utils/exchange-announcements.ts +71 -0
  933. package/src/utils/exchange-rate-snapshot.ts +10 -0
  934. package/src/utils/financial-statements.ts +83 -4
  935. package/src/utils/format.ts +34 -16
  936. package/src/utils/fundamentals.ts +150 -34
  937. package/src/utils/fx-market-hours.ts +10 -1
  938. package/src/utils/latest-financial-period.ts +8 -0
  939. package/src/utils/linux-libc.ts +54 -0
  940. package/src/utils/market-capitalization.ts +111 -15
  941. package/src/utils/max-drawdown.ts +64 -0
  942. package/src/utils/network-failure.ts +83 -0
  943. package/src/utils/option-expiry.ts +139 -7
  944. package/src/utils/price-earnings.ts +11 -5
  945. package/src/utils/price-history.ts +18 -8
  946. package/src/utils/price-window.ts +131 -0
  947. package/src/utils/release-date.ts +47 -0
  948. package/src/utils/sec.ts +71 -0
  949. package/src/utils/throttled-fetch.ts +11 -4
  950. package/src/utils/trailing-eps.ts +67 -0
  951. package/src/utils/typed-text.ts +21 -0
  952. package/src/utils/utc-time.ts +24 -1
  953. package/src/version.ts +6 -0
  954. package/src/cli/pane-functions/screenshot.ts +0 -2122
@@ -57,6 +57,12 @@ export interface CliReportColumn<Row = Record<string, unknown>> {
57
57
  format?: (value: unknown, row: Row) => string;
58
58
  /** The row field naming the unit of a number in this column; the number then exports as it is, with that unit. */
59
59
  unitKey?: string;
60
+ /**
61
+ * With `unitKey`: the unit always goes to a `<header> unit` column beside
62
+ * the figures, never into the header, so the header is the same whichever
63
+ * rows loaded (a market cap in one currency for some peers, in another for others).
64
+ */
65
+ unitColumn?: boolean;
60
66
  }
61
67
 
62
68
  /** A label/value line of a report. `unit` names the unit of a numeric `value` when `formatted` cannot say it. */
@@ -69,7 +75,8 @@ export interface CliReportEntry {
69
75
  }
70
76
 
71
77
  type Cell =
72
- | { kind: "blank" }
78
+ /** `unit`: what the column draws a number of this row in, so a missing figure still names its unit. */
79
+ | { kind: "blank"; unit?: string }
73
80
  | { kind: "text"; text: string; time?: boolean }
74
81
  | { kind: "number"; value: number; unit: string }
75
82
  /** Text that may read as a number, once the whole column is known. */
@@ -80,6 +87,10 @@ type ResolvedCell = Exclude<Cell, { kind: "textual" }>;
80
87
  interface ResolvedColumn {
81
88
  header: string;
82
89
  cells: ResolvedCell[];
90
+ /** The unit the column draws its numbers in, for a table with no rows to say it. */
91
+ unit?: string;
92
+ /** The unit always has a column of its own (`unitColumn`). */
93
+ unitColumn?: boolean;
83
94
  }
84
95
 
85
96
  const BLANK = { kind: "blank" } as const;
@@ -265,8 +276,9 @@ function resolveColumn(header: string, cells: Cell[], parseText: boolean): Resol
265
276
  };
266
277
  }
267
278
 
279
+ /** The units a column's numbers are in, a missing figure's own among them, so the header does not change with which figures are missing. */
268
280
  function numberUnits(cells: readonly ResolvedCell[]): Set<string> {
269
- return new Set(cells.flatMap((cell) => cell.kind === "number" ? [cell.unit] : []));
281
+ return new Set(cells.flatMap((cell) => cell.kind === "number" ? [cell.unit] : cell.kind === "blank" && cell.unit != null ? [cell.unit] : []));
270
282
  }
271
283
 
272
284
  function cellValue(cell: ResolvedCell): CliReportValue {
@@ -312,9 +324,9 @@ function rowUnits(columns: readonly ResolvedColumn[], mixed: readonly boolean[],
312
324
  * metrics whose rows each have one, and otherwise a `<header> unit` column
313
325
  * beside a column that mixes them (a rate in % and a spread in bp).
314
326
  */
315
- function assembleTable(title: string, columns: readonly ResolvedColumn[], rowCount: number): CliReportTable {
316
- const units = columns.map((column) => numberUnits(column.cells));
317
- const mixed = units.map((columnUnits) => columnUnits.size > 1);
327
+ function assembleTable(title: string, columns: readonly ResolvedColumn[], rowCount: number, status?: StatusColumn): CliReportTable {
328
+ const units = columns.map((column) => rowCount === 0 && column.unit != null ? new Set([column.unit]) : numberUnits(column.cells));
329
+ const mixed = units.map((columnUnits, index) => columnUnits.size > 1 || columns[index]!.unitColumn === true);
318
330
  const byRow = mixed.some(Boolean) ? rowUnits(columns, mixed, rowCount) : null;
319
331
  const layout = columns.flatMap((column, index) => {
320
332
  const columnUnits = units[index]!;
@@ -338,6 +350,7 @@ function assembleTable(title: string, columns: readonly ResolvedColumn[], rowCou
338
350
  },
339
351
  ];
340
352
  });
353
+ if (status) layout.push({ header: status.header, value: (row: number) => status.values[row] ?? "" });
341
354
  return {
342
355
  title,
343
356
  columns: layout.map((column) => column.header),
@@ -345,28 +358,151 @@ function assembleTable(title: string, columns: readonly ResolvedColumn[], rowCou
345
358
  };
346
359
  }
347
360
 
361
+ /** The `status` column a table of rows ends its own columns with: why a figure is missing, by row. */
362
+ interface StatusColumn {
363
+ header: string;
364
+ values: string[];
365
+ }
366
+
367
+ /**
368
+ * A number a probe draws through a column's format, to read the unit the
369
+ * column draws numbers in. A figure format rounds it; a format that prints
370
+ * it whole passes values through as text and draws no figures of its own.
371
+ */
372
+ const UNIT_PROBE = 0.1234567891;
373
+
374
+ /**
375
+ * A row with nothing in it that a format can still read through: every
376
+ * field, call and conversion of it is empty. For a format that reads fields of
377
+ * the row beyond its value and would throw on an empty one.
378
+ */
379
+ const EMPTY_ROW: unknown = (() => {
380
+ const empty: unknown = new Proxy(function empty() {}, {
381
+ get: (_target, key) => key === Symbol.toPrimitive ? (hint: string) => (hint === "number" ? Number.NaN : "") : empty,
382
+ apply: () => empty,
383
+ });
384
+ return empty;
385
+ })();
386
+
387
+ /**
388
+ * The unit a column draws a number of this row in (`%` for an IV, `""` for a
389
+ * plain figure), read from what its format draws for one; null when it draws
390
+ * no figure for one, passes the value through as it is, or throws.
391
+ */
392
+ function drawnUnit<Row>(column: CliReportColumn<Row>, row: Row, rowUnitKey?: string): string | null {
393
+ // A figure in the row's own unit is bare: the row's `currency_or_unit` names it.
394
+ if (rowUnitKey && column.unitKey === rowUnitKey) return "";
395
+ const unit = column.unitKey ? (row as Record<string, unknown> | undefined)?.[column.unitKey] : undefined;
396
+ if (typeof unit === "string" && unit) return unit;
397
+ if (!column.format) return null;
398
+ try {
399
+ const drawn = column.format(UNIT_PROBE, row);
400
+ if (typeof drawn !== "string" || drawn.trim() === String(UNIT_PROBE)) return null;
401
+ const read = readDisplay(drawn);
402
+ return read && read !== BLANK_DISPLAY ? read.unit : null;
403
+ } catch {
404
+ // Intentional ignore: a format that cannot draw a number for this row draws no unit for it.
405
+ return null;
406
+ }
407
+ }
408
+
409
+ /**
410
+ * The unit a column draws its figures in whatever the rows, read without one:
411
+ * on an empty row, or through a row of nothing when the format throws on an
412
+ * empty one. The same with rows and without, so a header and the table's
413
+ * status column do not depend on what was loaded. Null for a column of text.
414
+ */
415
+ function columnUnit<Row>(column: CliReportColumn<Row>): string | null {
416
+ if (column.unitKey) return "";
417
+ if (!column.format) return null;
418
+ try {
419
+ column.format(UNIT_PROBE, {} as Row);
420
+ return drawnUnit(column, {} as Row);
421
+ } catch {
422
+ // Intentional ignore: the format reads a field an empty row lacks; read it through a row of nothing.
423
+ return drawnUnit(column, EMPTY_ROW as Row);
424
+ }
425
+ }
426
+
427
+ /** What a status cell says: each reason once, after the columns it is the reason for. */
428
+ function statusText(reasons: ReadonlyArray<{ header: string; text: string }>): string {
429
+ const byText = new Map<string, string[]>();
430
+ for (const { header, text } of reasons) byText.set(text, [...byText.get(text) ?? [], header]);
431
+ return [...byText].map(([text, headers]) => `${headers.join(", ")}: ${text}`).join("; ");
432
+ }
433
+
434
+ const isMissing = (value: unknown) => value == null || (typeof value === "number" && !Number.isFinite(value));
435
+
348
436
  // Codes a spreadsheet must not turn into numbers, even when they are digits.
349
437
  const IDENTIFIER_KEY = /^(?:id|cik|code|cusip|figi|isin|symbol|ticker)$|(?:Id|Cik|Code|Cusip|Figi|Isin|Symbol|Ticker)$/;
350
438
 
351
- /** A table of rows under the columns the text view draws. */
439
+ /**
440
+ * A table of rows under the columns the text view draws. A figure that is
441
+ * missing still names the unit its column draws it in, so a header is the same
442
+ * whichever figures a symbol has. With `status`, a reason the text draws in
443
+ * place of a missing figure (`not quoted`, `under 7d`) leaves the cell empty
444
+ * and goes to the `status` column the table ends its own columns with, so a
445
+ * column of figures holds only numbers. Only a row whose own value is missing,
446
+ * in a column that draws that row's figures, has a reason: text a format draws
447
+ * from the rest of the row (a range, N/M, ATM) stays. A table has the column
448
+ * when one of its columns draws figures through a format, read from the
449
+ * columns alone so it is there with or without rows, and a table of text has
450
+ * none; beside a column of its own named Status it is `value_status`.
451
+ * `rowUnitKey` is the row field its `currency_or_unit` cell is read from: a
452
+ * column whose `unitKey` is that field exports its figures bare under its own
453
+ * header (`Last`, never `Last ($)` beside a franc), each row's unit named there.
454
+ */
352
455
  export function exportRowsTable<Row extends Record<string, unknown>>(
353
456
  title: string,
354
457
  columns: readonly CliReportColumn<Row>[],
355
458
  rows: readonly Row[],
459
+ { status = false, rowUnitKey }: { status?: boolean; rowUnitKey?: string } = {},
356
460
  ): CliReportTable {
357
- return assembleTable(title, columns.map((column) => resolveColumn(
358
- column.header,
359
- rows.map((row): Cell => {
360
- const record = row as Record<string, unknown> | undefined;
361
- const value = record?.[column.key];
362
- const unit = column.unitKey ? record?.[column.unitKey] : undefined;
363
- if (typeof unit === "string" && unit && typeof value === "number" && Number.isFinite(value)) {
364
- return { kind: "number", value: exportNumber(value), unit };
365
- }
366
- return exportCell(value, column.format ? column.format(value, row) : null, column.key);
367
- }),
368
- !IDENTIFIER_KEY.test(column.key),
369
- )), rows.length);
461
+ const reasons = rows.map((): Array<{ header: string; text: string }> => []);
462
+ const units = columns.map(columnUnit);
463
+ // Decided by the columns alone, so a table has the column, or not, whatever rows it has.
464
+ const withStatus = status && units.some((unit) => unit != null);
465
+ const resolved = columns.map((column, columnIndex): ResolvedColumn => {
466
+ const rowUnits = rows.map((row) => drawnUnit(column, row, rowUnitKey));
467
+ const values = rows.map((row) => (row as Record<string, unknown> | undefined)?.[column.key]);
468
+ const inRowUnit = !!rowUnitKey && column.unitKey === rowUnitKey;
469
+ const { header, cells } = resolveColumn(
470
+ column.header,
471
+ rows.map((row, index): Cell => {
472
+ const value = values[index];
473
+ const unit = column.unitKey ? (row as Record<string, unknown> | undefined)?.[column.unitKey] : undefined;
474
+ const drawn = column.format ? column.format(value, row) : null;
475
+ // A figure the text leaves out of this row (another session's print) is not one of its figures.
476
+ const left = drawn != null && readDisplay(drawn) === BLANK_DISPLAY;
477
+ if (!left && typeof value === "number" && Number.isFinite(value) && (inRowUnit || (typeof unit === "string" && unit))) {
478
+ return { kind: "number", value: exportNumber(value), unit: inRowUnit ? "" : unit as string };
479
+ }
480
+ return exportCell(value, drawn, column.key);
481
+ }),
482
+ !IDENTIFIER_KEY.test(column.key),
483
+ );
484
+ const table: ResolvedColumn = {
485
+ header,
486
+ cells: cells.map((cell, index): ResolvedCell => {
487
+ const unit = rowUnits[index];
488
+ const blank: ResolvedCell = unit != null ? { kind: "blank", unit } : BLANK;
489
+ if (cell.kind === "blank") return cell.unit == null ? blank : cell;
490
+ // Text drawn in place of a figure this row does not have, where the column draws this row's figures: a reason.
491
+ // Text drawn from the rest of the row (a range, N/M, ATM) is no figure's absence and stays.
492
+ if (!withStatus || cell.kind !== "text" || cell.time || unit == null || !isMissing(values[index])) return cell;
493
+ reasons[index]!.push({ header: column.header, text: cell.text });
494
+ return blank;
495
+ }),
496
+ };
497
+ const unit = units[columnIndex];
498
+ const withUnit = unit == null ? table : { ...table, unit };
499
+ return column.unitColumn && column.unitKey && !inRowUnit ? { ...withUnit, unitColumn: true } : withUnit;
500
+ });
501
+ const statusColumn = withStatus ? {
502
+ header: columns.some((column) => column.header.trim().toLowerCase() === "status") ? "value_status" : "status",
503
+ values: reasons.map(statusText),
504
+ } : undefined;
505
+ return assembleTable(title, resolved, rows.length, statusColumn);
370
506
  }
371
507
 
372
508
  /** Label/value lines as `Metric,Value`; a number's unit moves into its label. */
@@ -434,18 +570,33 @@ function sectionList(tables: readonly CliReportTable[]): string {
434
570
  }
435
571
 
436
572
  /**
437
- * `--section`: one table, by its title (any case) or its 1-based position.
438
- * Without a selector the report keeps every table.
573
+ * The table `--section` names by title, in any case: the one titled so, else
574
+ * the one whose title starts with it, as a title that carries its date does
575
+ * (`Strikes` for `Strikes · Oct 30 '26`). A start several titles share picks
576
+ * none.
577
+ */
578
+ function sectionByTitle(tables: readonly CliReportTable[], wanted: string): CliReportTable | undefined {
579
+ const name = wanted.toLowerCase();
580
+ const titles = tables.map((table) => table.title.trim().toLowerCase());
581
+ const exact = titles.indexOf(name);
582
+ if (exact >= 0) return tables[exact];
583
+ const starting = tables.filter((_, index) => titles[index]!.startsWith(name));
584
+ if (starting.length > 1) {
585
+ throw new Error(`"${wanted}" starts more than one section: ${starting.map((table) => table.title).join(", ")}. Give more of the title, or its number.`);
586
+ }
587
+ return starting[0];
588
+ }
589
+
590
+ /**
591
+ * `--section`: one table, by its title or the start of it (any case), or its
592
+ * 1-based position. Without a selector the report keeps every table.
439
593
  */
440
594
  export function selectReportTables(report: CliReportTables, selector: string | true | undefined): CliReportTables {
441
595
  if (selector === undefined) return report;
442
596
  if (selector === true || !selector.trim()) throw new Error("--section needs a section title or number.");
443
597
  const wanted = selector.trim();
444
598
  if (report.tables.length === 0) throw new Error(`No section "${wanted}": this report has no tables.`);
445
- const index = /^\d+$/.test(wanted)
446
- ? Number(wanted) - 1
447
- : report.tables.findIndex((table) => table.title.trim().toLowerCase() === wanted.toLowerCase());
448
- const table = report.tables[index];
599
+ const table = /^\d+$/.test(wanted) ? report.tables[Number(wanted) - 1] : sectionByTitle(report.tables, wanted);
449
600
  if (!table) throw new Error(`No section "${wanted}". Sections: ${sectionList(report.tables)}.`);
450
601
  return { ...report, tables: [table] };
451
602
  }
@@ -464,7 +615,10 @@ function csvLine(row: readonly unknown[]): string {
464
615
 
465
616
  /**
466
617
  * A table with the fixed columns after its own, every row naming what it is
467
- * and the source and as-of the footer cites. A table without columns stays empty.
618
+ * and the source and as-of the footer cites. A fixed column the table already
619
+ * has under the same name is left out, so its own per-row values stay the
620
+ * only ones (CBR's `source` and `as_of`) and a reader keyed by name finds one
621
+ * column. A table without columns stays empty.
468
622
  */
469
623
  function identifiedTable(table: CliReportTable, report: CliReportTables, freshness: ReportFreshness | undefined): CliReportTable {
470
624
  if (!freshness || table.columns.length === 0) return table;
@@ -474,7 +628,16 @@ function identifiedTable(table: CliReportTable, report: CliReportTables, freshne
474
628
  const own = table.rowIdentities?.[index];
475
629
  return own ? exportIdentityCells(freshness, { ...identity, ...own }) : cells;
476
630
  };
477
- return { ...table, columns: [...table.columns, ...EXPORT_IDENTITY_COLUMNS], rows: table.rows.map((row, index) => [...row, ...rowCells(index)]) };
631
+ const added = EXPORT_IDENTITY_COLUMNS.flatMap((column, index) => table.columns.includes(column) ? [] : [index]);
632
+ if (added.length === 0) return table;
633
+ return {
634
+ ...table,
635
+ columns: [...table.columns, ...added.map((index) => EXPORT_IDENTITY_COLUMNS[index]!)],
636
+ rows: table.rows.map((row, index) => {
637
+ const identityCells = rowCells(index);
638
+ return [...row, ...added.map((column) => identityCells[column]!)];
639
+ }),
640
+ };
478
641
  }
479
642
 
480
643
  /**
@@ -1,8 +1,10 @@
1
1
  import { saveConfig } from "../data/config/store";
2
2
  import { loadCliConfigIfAvailable } from "./context";
3
3
  import { getPluginsDir } from "../plugins/loader";
4
- import { seedExtractedPlugins } from "../plugins/extracted-plugins";
4
+ import { pluginSeedFailure, pluginSeedNotice, samePluginSeedFailure, seedExtractedPlugins } from "../plugins/extracted-plugins";
5
+ import { setPluginSeedNotice } from "../plugins/seed-notice";
5
6
  import { debugLog } from "../utils/debug-log";
7
+ import type { AppConfig } from "../types/config";
6
8
 
7
9
  const log = debugLog.createLogger("plugin-seed");
8
10
 
@@ -10,39 +12,52 @@ const log = debugLog.createLogger("plugin-seed");
10
12
  * Reinstalls plugins that moved out of this repository into their own.
11
13
  *
12
14
  * Runs before the plugin catalog is read so a restored plugin is available in
13
- * the same session, and stays quiet on failure: an offline launch should start
14
- * normally and try again next time, not block on a network request.
15
+ * the same session, and never blocks or prints: an offline launch should
16
+ * start normally and try again next time. What it could not install becomes
17
+ * one notice the app shows once it is up (`setPluginSeedNotice`), saying why
18
+ * when git is missing, and `doctor` names the plugins.
15
19
  *
16
- * Returns the seeded ids so a caller that already holds the config in memory
17
- * can adopt them. The desktop loads its config before restoring, and without
18
- * this it would keep an object whose `seededPlugins` is one launch stale and
19
- * overwrite the record on the next save.
20
+ * Returns what it recorded (the seeded ids, and what failed) so a caller that
21
+ * already holds the config in memory can adopt it. The desktop loads its
22
+ * config before restoring, and without this it would keep an object one
23
+ * launch stale and overwrite the record on the next save.
20
24
  */
21
- export async function restoreExtractedPlugins(): Promise<string[] | null> {
25
+ export async function restoreExtractedPlugins(): Promise<Pick<AppConfig, "seededPlugins" | "pluginSeedFailure"> | null> {
22
26
  try {
23
27
  const config = await loadCliConfigIfAvailable();
24
28
  // No data directory yet means a first run: there is nothing to restore.
25
29
  if (!config) return null;
26
- // Through the registry like `gloomberb install`: the reviewed commit, and
30
+ // Through the registry like `gloom install`: the reviewed commit, and
27
31
  // nothing on a Gloomberb too old for it. That failure is retried at the
28
32
  // next launch, by when this Gloomberb has usually updated itself. The
29
33
  // registry is read once, and only when something is left to install.
30
- const { installListedPlugin, loadRegistryListings } = await import("../plugins/installer");
34
+ // Quiet: the terminal app is about to own the screen, and git's or bun's
35
+ // output would be left behind in the scrollback.
36
+ const { gitInstalled, installListedPlugin, loadRegistryListings } = await import("../plugins/installer");
31
37
  let listings: ReturnType<typeof loadRegistryListings> | undefined;
38
+ let gitMissing: boolean | undefined;
32
39
  const result = await seedExtractedPlugins(config, async (repo) => {
40
+ // Without git every clone fails the same way: no registry read, no attempt.
41
+ gitMissing ??= !gitInstalled();
42
+ if (gitMissing) throw new Error("git is not installed");
33
43
  listings ??= loadRegistryListings();
34
- return installListedPlugin(repo, {}, await listings);
44
+ return installListedPlugin(repo, { quiet: true }, await listings);
35
45
  });
46
+ // Recorded, so doctor knows a launch tried, and a failure the next launch
47
+ // retries is not announced again while it lasts.
48
+ const failure = pluginSeedFailure(result.failed, gitMissing === true);
49
+ setPluginSeedNotice(pluginSeedNotice(failure, config.pluginSeedFailure));
36
50
 
37
51
  const seeded = [...new Set(result.seeded)].sort();
38
52
  const existing = [...new Set(config.seededPlugins ?? [])].sort();
39
- if (seeded.join() === existing.join()) return seeded;
53
+ const record = { seededPlugins: seeded, pluginSeedFailure: failure };
54
+ if (seeded.join() === existing.join() && samePluginSeedFailure(failure, config.pluginSeedFailure)) return record;
40
55
 
41
- await saveConfig({ ...config, seededPlugins: seeded });
56
+ await saveConfig({ ...config, ...record });
42
57
  if (result.installed.length > 0) {
43
58
  log.info(`Restored ${result.installed.join(", ")} into ${getPluginsDir()}`);
44
59
  }
45
- return seeded;
60
+ return record;
46
61
  } catch (error) {
47
62
  log.error(`Plugin restore skipped: ${error}`);
48
63
  return null;
package/src/cli/result.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { CliGlobalOptions } from "./options";
2
2
  import {
3
+ cliPlainEnabled,
3
4
  cliStyles,
4
5
  cliTerminalWidth,
5
6
  renderStats,
@@ -13,7 +14,7 @@ import {
13
14
  } from "../utils/cli-output";
14
15
  import { serializeCsv } from "../utils/csv";
15
16
  import { formatUtcTime, isEpochMilliseconds, isZonedIsoDateTime } from "../utils/utc-time";
16
- import { formatFreshnessLine, type ReportFreshness } from "./pane-functions/freshness";
17
+ import { formatFreshnessLine, withSavedCopy, type ReportFreshness } from "./pane-functions/freshness";
17
18
  import { withSearchHints, type FredSeriesCheck } from "./not-a-ticker";
18
19
  import { renderReportCsv, renderReportNdjson, withCsvFooter, type CliReportTables } from "./report-tables";
19
20
  import {
@@ -46,6 +47,12 @@ export interface CliResult<T = unknown> {
46
47
  * `fear-greed` prints its row alone.
47
48
  */
48
49
  exportFreshness?: ReportFreshness;
50
+ /**
51
+ * Why a result that still prints counts as a failure (a report with no
52
+ * data): the command exits 1, and JSON says `ok: false` with this as
53
+ * `error` beside the data, so the envelope and the exit status agree.
54
+ */
55
+ failure?: CliErrorObject;
49
56
  }
50
57
 
51
58
  export interface CliErrorObject {
@@ -92,6 +99,11 @@ export interface CliResultRenderOptions<T = unknown, Row = Record<string, unknow
92
99
  * `metadata.rows` in JSON.
93
100
  */
94
101
  dateKey?: string;
102
+ /**
103
+ * The row key that splits the rows into series of their own, each windowed
104
+ * apart: with `symbol`, `--tail 5` keeps every symbol's newest five rows.
105
+ */
106
+ seriesKey?: string;
95
107
  /** How many rows to show when neither `--limit` nor `--tail` is given; every row when absent. */
96
108
  defaultLimit?: number;
97
109
  /**
@@ -108,10 +120,20 @@ export interface CliResultRenderOptions<T = unknown, Row = Record<string, unknow
108
120
  exportIdentity?: ExportIdentity | ((row: Row) => ExportIdentity);
109
121
  /** What CSV says after the source lines as `# note:` lines: what the rows do not show, such as where a series begins. */
110
122
  exportNotes?: readonly string[];
123
+ /**
124
+ * Why a row's figures are empty (`Not a ticker: ZZZZQQ.`, `Chg%: no previous
125
+ * close`), for the `status` column CSV writes after the row's own columns and
126
+ * the `status` field of an NDJSON row; empty for a whole row. The column is
127
+ * there whenever this is, with rows or without, so the header never depends
128
+ * on what loaded. A figure that is no number (NaN) is written empty and,
129
+ * without a reason here, named as `<header>: not available`.
130
+ */
131
+ exportStatus?: (row: Row) => string | null | undefined;
111
132
  }
112
133
 
113
- interface CliResultJsonEnvelope<T> extends Omit<CliResult<T>, "freshness" | "exportFreshness"> {
114
- ok: true;
134
+ interface CliResultJsonEnvelope<T> extends Omit<CliResult<T>, "freshness" | "exportFreshness" | "failure"> {
135
+ ok: boolean;
136
+ error?: CliErrorObject;
115
137
  /** Where the data comes from, as CSV's closing `#` lines say it. */
116
138
  source?: ExportSource;
117
139
  columns?: Array<Pick<CliResultColumn, "key" | "header" | "align" | "width">>;
@@ -142,11 +164,28 @@ function rowWindowOptions(options: CliGlobalOptions, renderOptions: Pick<CliResu
142
164
  function windowed<Row>(
143
165
  rows: readonly Row[],
144
166
  options: CliGlobalOptions,
145
- renderOptions: Pick<CliResultRenderOptions, "dateKey" | "defaultLimit">,
167
+ renderOptions: Pick<CliResultRenderOptions, "dateKey" | "defaultLimit" | "seriesKey">,
146
168
  ): RowWindow<Row> {
147
- const { dateKey } = renderOptions;
169
+ const { dateKey, seriesKey } = renderOptions;
148
170
  const dateOf = dateKey ? (row: Row) => (isPlainObject(row) ? row[dateKey] : undefined) : undefined;
149
- return windowRows(rows, rowWindowOptions(options, renderOptions), dateOf);
171
+ const windowOptions = rowWindowOptions(options, renderOptions);
172
+ if (!seriesKey) return windowRows(rows, windowOptions, dateOf);
173
+ const series = new Map<unknown, Row[]>();
174
+ for (const row of rows) {
175
+ const key = isPlainObject(row) ? row[seriesKey] : undefined;
176
+ const group = series.get(key);
177
+ if (group) group.push(row);
178
+ else series.set(key, [row]);
179
+ }
180
+ const windows = [...series.values()].map((group) => windowRows(group, windowOptions, dateOf));
181
+ const count = windowOptions.tail ?? windowOptions.limit;
182
+ return {
183
+ rows: windows.flatMap((window) => window.rows),
184
+ total: rows.length,
185
+ kept: windows.find((window) => window.kept)?.kept ?? null,
186
+ note: windows.some((window) => window.note)
187
+ ? `showing the oldest ${count} ${count === 1 ? "row" : "rows"} of each ${seriesKey}; --tail ${count} for the latest` : null,
188
+ };
150
189
  }
151
190
 
152
191
  function asRows<T, Row>(data: T, rows?: (data: T) => Row[]): Row[] {
@@ -199,7 +238,7 @@ function formatTextValue(
199
238
  depth = 0,
200
239
  width: number | null = null,
201
240
  ): string {
202
- const missing = context === "record" ? cliStyles.muted("-") : "";
241
+ const missing = context === "record" ? cliPlainEnabled() ? "—" : cliStyles.muted("-") : "";
203
242
  if (value == null || value === "") return missing;
204
243
  // Times print in UTC with the zone named, never in the host's zone; JSON and CSV keep the raw value.
205
244
  if (value instanceof Date) return formatUtcTime(value);
@@ -284,23 +323,69 @@ function serializeColumns<Row>(columns?: CliResultColumn<Row>[]) {
284
323
  }));
285
324
  }
286
325
 
326
+ /** A number a spreadsheet cannot read: NaN or an infinity, which exports write empty, never as the word. */
327
+ function isNonFiniteNumber(value: unknown): boolean {
328
+ return typeof value === "number" && !Number.isFinite(value);
329
+ }
330
+
331
+ /**
332
+ * What a row's status cell says: the command's reason, else the figures
333
+ * written empty for being no number. Empty when nothing is missing.
334
+ */
335
+ function exportStatusText(reason: string | null | undefined, blanked: readonly string[]): string {
336
+ const own = reason?.trim();
337
+ if (own) return own;
338
+ return blanked.length > 0 ? `${blanked.join(", ")}: not available` : "";
339
+ }
340
+
341
+ /** `status`, or `value_status` beside a column of the table's own named Status. */
342
+ function statusHeader(headers: readonly string[]): string {
343
+ return headers.some((header) => header.trim().toLowerCase() === "status") ? "value_status" : "status";
344
+ }
345
+
287
346
  function renderCsv<Row extends Record<string, unknown>>(
288
347
  rows: Row[],
289
348
  columns?: CliResultColumn<Row>[],
290
349
  identity?: (row: Row) => string[],
350
+ status?: (row: Row) => string | null | undefined,
291
351
  ): string {
292
352
  const resolvedColumns = columns && columns.length > 0
293
353
  ? columns
294
354
  : inferColumns(rows as Record<string, unknown>[]) as CliResultColumn<Row>[];
355
+ const headers = resolvedColumns.map((column) => column.header);
295
356
  return serializeCsv([
296
- [...resolvedColumns.map((column) => column.header), ...identity ? EXPORT_IDENTITY_COLUMNS : []],
297
- ...rows.map((row) => [
298
- ...resolvedColumns.map((column) => (column.value ? column.value(row) : row[column.key])),
299
- ...identity?.(row) ?? [],
300
- ]),
357
+ [...headers, ...status ? [statusHeader(headers)] : [], ...identity ? EXPORT_IDENTITY_COLUMNS : []],
358
+ ...rows.map((row) => {
359
+ const values = resolvedColumns.map((column) => (column.value ? column.value(row) : row[column.key]));
360
+ const blanked = resolvedColumns.flatMap((column, index) => isNonFiniteNumber(values[index]) ? [column.header] : []);
361
+ return [
362
+ ...values.map((value) => isNonFiniteNumber(value) ? "" : value),
363
+ ...status ? [exportStatusText(status(row), blanked)] : [],
364
+ ...identity?.(row) ?? [],
365
+ ];
366
+ }),
301
367
  ]);
302
368
  }
303
369
 
370
+ /**
371
+ * The key an NDJSON row says why its figures are empty under: `status`, or
372
+ * `value_status` beside a field of the row's own that the export writes under
373
+ * that name (an order's status), as CSV's header does.
374
+ */
375
+ function ndjsonStatusKey(rows: readonly unknown[], columns?: readonly CliResultColumn<never>[]): string {
376
+ const own = columns && columns.length > 0
377
+ ? columns.some((column) => column.key === "status")
378
+ : rows.some((row) => isPlainObject(row) && "status" in row);
379
+ return own ? "value_status" : "status";
380
+ }
381
+
382
+ /** An NDJSON row: a figure that is no number is null, and the row says why its figures are empty when the command does. */
383
+ function ndjsonRow<Row extends Record<string, unknown>>(row: Row, status: ((row: Row) => string | null | undefined) | undefined, statusKey: string): Record<string, unknown> {
384
+ const blanked = Object.entries(row).flatMap(([key, value]) => isNonFiniteNumber(value) ? [key] : []);
385
+ const own = blanked.length > 0 ? { ...row, ...Object.fromEntries(blanked.map((key) => [key, null])) } : row;
386
+ return status ? { ...own, [statusKey]: exportStatusText(status(row), blanked) || null } : own;
387
+ }
388
+
304
389
  function stringField(row: Record<string, unknown>, key: string): string | null {
305
390
  const value = row?.[key];
306
391
  return typeof value === "string" && value.trim() ? value : null;
@@ -341,10 +426,11 @@ function renderTextTable<Row extends Record<string, unknown>>(
341
426
  ): string {
342
427
  const resolvedColumns = textColumns(rows, columns);
343
428
  const cells = rows.map((row) => resolvedColumns.map((column) => formatTextCell(column, row, "table")));
344
- // A column no row fills is noise in a table; exports keep it.
429
+ // A column no row fills is noise in a table; exports keep it, and so does plain output, whose
430
+ // tables keep one shape from query to query with "—" in the empty cells.
345
431
  const filled = resolvedColumns
346
432
  .map((column, index) => ({ column, index }))
347
- .filter(({ column, index }) => column.width != null || cells.some((row) => row[index] !== ""));
433
+ .filter(({ column, index }) => cliPlainEnabled() || column.width != null || cells.some((row) => row[index] !== ""));
348
434
  const shown = filled.length > 0 ? filled : resolvedColumns.map((column, index) => ({ column, index }));
349
435
  return renderTable(
350
436
  shown.map(({ column }) => ({
@@ -388,9 +474,13 @@ function overflowsTerminal<Row extends Record<string, unknown>>(rows: Row[]): bo
388
474
  return width > terminalWidth;
389
475
  }
390
476
 
391
- /** The closing source, as-of and status line of a text result, for output printed without `printResult`. */
392
- export function cliFreshnessFooter(freshness: ReportFreshness): string {
393
- return cliStyles.muted(wrapToTerminal(formatFreshnessLine(freshness)));
477
+ /**
478
+ * The closing source, as-of and status line of a text result, for output
479
+ * printed without `printResult`; `shown` is the data the line is drawn from,
480
+ * which says whether it came from a saved copy.
481
+ */
482
+ export function cliFreshnessFooter(freshness: ReportFreshness, shown?: unknown): string {
483
+ return cliStyles.muted(wrapToTerminal(formatFreshnessLine(withSavedCopy(freshness, shown))));
394
484
  }
395
485
 
396
486
  export function serializeCliResult<T, Row extends Record<string, unknown> = Record<string, unknown>>(
@@ -398,14 +488,17 @@ export function serializeCliResult<T, Row extends Record<string, unknown> = Reco
398
488
  options: CliGlobalOptions,
399
489
  renderOptions: CliResultRenderOptions<T, Row> = {},
400
490
  ): string {
401
- const { freshness, exportFreshness, ...rest } = result;
402
- const cited = freshness ?? exportFreshness;
491
+ const { freshness: reported, exportFreshness, failure, ...rest } = result;
492
+ // Rows shown from a saved copy, its refresh having failed, say so in place of live or delayed.
493
+ const freshness = reported && withSavedCopy(reported, result.data);
494
+ const cited = freshness ?? (exportFreshness && withSavedCopy(exportFreshness, result.data));
403
495
  if (options.format === "json") {
404
496
  const columns = serializeColumns(renderOptions.columns);
405
497
  const window = Array.isArray(result.data) ? windowed(result.data as unknown[], options, renderOptions) : null;
406
498
  const cut = window ? cutMetadata(window) : null;
407
499
  const envelope: CliResultJsonEnvelope<T> = {
408
- ok: true,
500
+ ok: !failure,
501
+ ...(failure ? { error: failure } : {}),
409
502
  ...rest,
410
503
  ...(freshness || cut ? { metadata: { ...rest.metadata, ...(freshness ? { freshness } : {}), ...(cut ? { rows: cut } : {}) } } : {}),
411
504
  data: window ? window.rows as T : result.data,
@@ -435,14 +528,18 @@ function serializeCliRows<T, Row extends Record<string, unknown>>(
435
528
  }
436
529
  const window = windowed(asRows(result.data, renderOptions.rows), options, renderOptions);
437
530
  const rows = window.rows;
531
+ const status = renderOptions.exportStatus;
438
532
  if (options.format === "ndjson") {
439
- return rows.map((row) => JSON.stringify(cited && isPlainObject(row)
440
- ? { ...row, ...exportIdentityFields(cited, rowIdentity(row as Row, renderOptions.exportIdentity)) }
441
- : row)).join("\n");
533
+ const statusKey = ndjsonStatusKey(rows, renderOptions.columns as readonly CliResultColumn<never>[] | undefined);
534
+ return rows.map((row) => {
535
+ if (!isPlainObject(row)) return JSON.stringify(row);
536
+ const fields = ndjsonRow(row as Row, status, statusKey);
537
+ return JSON.stringify(cited ? { ...fields, ...exportIdentityFields(cited, rowIdentity(row as Row, renderOptions.exportIdentity)) } : fields);
538
+ }).join("\n");
442
539
  }
443
540
  if (options.format === "csv") {
444
- if (!cited) return renderCsv(rows as Row[], renderOptions.columns);
445
- const csv = renderCsv(rows as Row[], renderOptions.columns, (row) => exportIdentityCells(cited, rowIdentity(row, renderOptions.exportIdentity)));
541
+ if (!cited) return renderCsv(rows as Row[], renderOptions.columns, undefined, status);
542
+ const csv = renderCsv(rows as Row[], renderOptions.columns, (row) => exportIdentityCells(cited, rowIdentity(row, renderOptions.exportIdentity)), status);
446
543
  return withCsvFooter(csv, [...exportSourceLines(cited), ...(renderOptions.exportNotes ?? []).map((note) => `note: ${note}`)]);
447
544
  }
448
545
  if (renderOptions.text) {