gloomberb 0.18.3 → 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 (886) 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/src/account/checkout-handoff.tsx +225 -0
  6. package/src/account/device-signin-dialog.tsx +2 -2
  7. package/src/account/device-signin.ts +2 -2
  8. package/src/account/upgrade-dialog.tsx +1 -1
  9. package/src/api-client/askg.ts +2 -2
  10. package/src/api-client/auth.ts +2 -2
  11. package/src/api-client/central-bank-rates.ts +6 -0
  12. package/src/api-client/collections.ts +5 -1
  13. package/src/api-client/cot.ts +2 -0
  14. package/src/api-client/crypto-etf-flows.ts +37 -0
  15. package/src/api-client/crypto-markets.ts +60 -0
  16. package/src/api-client/data.ts +84 -5
  17. package/src/api-client/doe.ts +15 -0
  18. package/src/api-client/equity-screener.ts +28 -0
  19. package/src/api-client/exchange-announcements.ts +38 -0
  20. package/src/api-client/fund-facts.ts +39 -0
  21. package/src/api-client/futures-curve.ts +9 -1
  22. package/src/api-client/hiring.ts +301 -0
  23. package/src/api-client/index.ts +22 -5
  24. package/src/api-client/market-discovery.ts +14 -0
  25. package/src/api-client/members.ts +6 -0
  26. package/src/api-client/mna.ts +6 -0
  27. package/src/api-client/paths.ts +18 -1
  28. package/src/api-client/request.ts +21 -4
  29. package/src/api-client/short-side-publications.ts +37 -0
  30. package/src/api-client/socket.ts +64 -4
  31. package/src/api-client/swap-curve.ts +35 -0
  32. package/src/api-client/themes.ts +7 -1
  33. package/src/api-client/types.ts +90 -2
  34. package/src/app/pane-runtime/plugin-bindings.ts +9 -2
  35. package/src/app/runtime/desktop-deeplink.ts +5 -5
  36. package/src/app/runtime/plugin-bindings.ts +2 -2
  37. package/src/app/runtime/startup.ts +13 -0
  38. package/src/app/star-prompt/status-line.tsx +2 -2
  39. package/src/architecture/core-builtin-allowlist.json +5 -2
  40. package/src/architecture/ui-literals-allowlist.json +3 -3
  41. package/src/architecture/ui-tokens-allowlist.json +1 -1
  42. package/src/brokers/broker-portfolio-sync.ts +37 -0
  43. package/src/brokers/directory.ts +11 -0
  44. package/src/brokers/logos.ts +3 -1
  45. package/src/brokers/signed-in/adapter.ts +304 -26
  46. package/src/brokers/signed-in/client.ts +14 -2
  47. package/src/brokers/signed-in/connect.ts +10 -2
  48. package/src/brokers/sync-broker-instance.ts +9 -1
  49. package/src/capabilities/chart-series.ts +22 -6
  50. package/src/cli/bank-section.ts +1 -1
  51. package/src/cli/base-converter.ts +23 -14
  52. package/src/cli/central-bank-events.ts +1 -1
  53. package/src/cli/command-options.ts +59 -4
  54. package/src/cli/commands/api.ts +9 -6
  55. package/src/cli/commands/broker.ts +2 -2
  56. package/src/cli/commands/command-utils.ts +2 -2
  57. package/src/cli/commands/crypto-hints.ts +7 -20
  58. package/src/cli/commands/fred-series.ts +4 -2
  59. package/src/cli/commands/keybindings.ts +2 -2
  60. package/src/cli/commands/market.ts +1117 -264
  61. package/src/cli/commands/news-hints.ts +2 -2
  62. package/src/cli/commands/news-search.ts +59 -6
  63. package/src/cli/commands/options-leaps.ts +41 -26
  64. package/src/cli/commands/overview.ts +121 -48
  65. package/src/cli/commands/plugins.ts +9 -9
  66. package/src/cli/commands/remote.ts +13 -10
  67. package/src/cli/commands/search.ts +64 -44
  68. package/src/cli/commands/system.ts +27 -19
  69. package/src/cli/commands/themes.ts +1 -1
  70. package/src/cli/commands/ticker.ts +192 -47
  71. package/src/cli/context.ts +3 -2
  72. package/src/cli/desktop-pane-shot-view.ts +2 -2
  73. package/src/cli/desktop-pane-shot.ts +1 -1
  74. package/src/cli/earnings-calendar.ts +4 -4
  75. package/src/cli/entry.ts +18 -1
  76. package/src/cli/errors.ts +20 -2
  77. package/src/cli/exit-codes.ts +1 -1
  78. package/src/cli/export-source.ts +10 -4
  79. package/src/cli/freshness.ts +2 -8
  80. package/src/cli/fx-forward.ts +130 -0
  81. package/src/cli/fx-pair.ts +10 -9
  82. package/src/cli/glossary.ts +64 -20
  83. package/src/cli/help.ts +19 -13
  84. package/src/cli/history-export.ts +126 -0
  85. package/src/cli/history-rows.ts +196 -43
  86. package/src/cli/index-roots.ts +5 -45
  87. package/src/cli/index.ts +24 -18
  88. package/src/cli/known-gaps.ts +118 -25
  89. package/src/cli/listing-arg.ts +300 -41
  90. package/src/cli/name-entries.ts +30 -18
  91. package/src/cli/native-smoke.ts +2 -0
  92. package/src/cli/not-a-ticker.ts +19 -9
  93. package/src/cli/opentui-libc.ts +5 -0
  94. package/src/cli/options.ts +12 -0
  95. package/src/cli/pane-functions/capabilities.ts +75 -20
  96. package/src/cli/pane-functions/catalog.ts +24 -23
  97. package/src/cli/pane-functions/discovery.ts +1 -1
  98. package/src/cli/pane-functions/dom.ts +10 -7
  99. package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
  100. package/src/cli/pane-functions/freshness.ts +174 -23
  101. package/src/cli/pane-functions/function-help.ts +66 -27
  102. package/src/cli/pane-functions/headless.ts +24 -11
  103. package/src/cli/pane-functions/index.ts +18 -16
  104. package/src/cli/pane-functions/options.ts +22 -2
  105. package/src/cli/pane-functions/report-notices.ts +12 -4
  106. package/src/cli/pane-functions/report.ts +5 -5
  107. package/src/cli/pane-functions/resolver.ts +47 -10
  108. package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
  109. package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
  110. package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
  111. package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
  112. package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
  113. package/src/cli/pane-functions/screenshot-payload.ts +453 -0
  114. package/src/cli/pane-functions/screenshot-render.ts +316 -0
  115. package/src/cli/pane-functions/screenshot-types.ts +162 -0
  116. package/src/cli/pane-functions/shot-options.ts +8 -3
  117. package/src/cli/peso-dollar-rates.ts +79 -0
  118. package/src/cli/quote-answer.ts +38 -3
  119. package/src/cli/report-tables.ts +61 -16
  120. package/src/cli/restore-plugins.ts +29 -14
  121. package/src/cli/result.ts +111 -22
  122. package/src/cli/series-search.ts +5 -5
  123. package/src/cli/statement-filings.ts +19 -0
  124. package/src/cli/suffixed-listing-rows.ts +49 -0
  125. package/src/cli/symbol-args.ts +50 -15
  126. package/src/cli/symbol-cells.ts +120 -0
  127. package/src/cli/symbol-list.ts +198 -68
  128. package/src/cli/symbol-rows.ts +131 -25
  129. package/src/cli/ticker-resolution.ts +108 -11
  130. package/src/cli/treasury-yields.ts +5 -5
  131. package/src/cli/types.ts +2 -0
  132. package/src/cli/uncovered-instrument.ts +3 -3
  133. package/src/cli/unknown-command.ts +6 -6
  134. package/src/components/chart/composite/chart-layout.ts +131 -0
  135. package/src/components/chart/composite/composite-chart.tsx +373 -16
  136. package/src/components/chart/composite/interactions.ts +7 -2
  137. package/src/components/chart/composite/legend.tsx +75 -3
  138. package/src/components/chart/composite/levels.ts +5 -0
  139. package/src/components/chart/composite/panel-gestures.ts +41 -0
  140. package/src/components/chart/composite/panel-labels.ts +2 -1
  141. package/src/components/chart/composite/panel-surface.tsx +305 -11
  142. package/src/components/chart/composite/price-axis-labels.tsx +27 -3
  143. package/src/components/chart/composite/rasterizer.ts +44 -19
  144. package/src/components/chart/composite/scene.ts +16 -4
  145. package/src/components/chart/composite/text-renderer.ts +48 -21
  146. package/src/components/chart/composite/toolbar.tsx +52 -5
  147. package/src/components/chart/composite/types.ts +13 -0
  148. package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
  149. package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
  150. package/src/components/chart/composite/use-composite-viewport.ts +12 -3
  151. package/src/components/chart/composite/value-scale.ts +149 -0
  152. package/src/components/chart/composite/volume-profile-paint.ts +6 -3
  153. package/src/components/chart/core/pointer.ts +2 -0
  154. package/src/components/chart-table/index.ts +2 -0
  155. package/src/components/chart-table/read.tsx +74 -0
  156. package/src/components/command-bar/commands/collection-list.ts +41 -19
  157. package/src/components/command-bar/commands/collection.ts +9 -3
  158. package/src/components/command-bar/commands/direct/index.ts +1 -1
  159. package/src/components/command-bar/commands/direct/runtime.ts +3 -2
  160. package/src/components/command-bar/pane-templates/items.ts +59 -6
  161. package/src/components/command-bar/pane-templates/workflow.ts +10 -1
  162. package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
  163. package/src/components/command-bar/routes/root/provider-search.ts +42 -11
  164. package/src/components/command-bar/routes/root/results.ts +18 -4
  165. package/src/components/command-bar/routes/root/runtime.ts +15 -3
  166. package/src/components/command-bar/routes/root/search-providers.ts +25 -2
  167. package/src/components/command-bar/routes/root/selection.ts +6 -1
  168. package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
  169. package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
  170. package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
  171. package/src/components/command-bar/selection-runtime.ts +50 -10
  172. package/src/components/command-bar/surface/index.tsx +24 -2
  173. package/src/components/command-bar/view-model.ts +7 -0
  174. package/src/components/command-bar/workflow/broker.ts +2 -2
  175. package/src/components/command-bar/workflow/collection-actions.ts +46 -7
  176. package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
  177. package/src/components/command-bar/workflow/fields.ts +8 -2
  178. package/src/components/command-bar/workflow/ops.ts +18 -4
  179. package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
  180. package/src/components/command-bar/workflow/submit.ts +3 -2
  181. package/src/components/command-bar/workflow/tickers.ts +5 -1
  182. package/src/components/company-logo.tsx +39 -0
  183. package/src/components/data-table/sections.ts +4 -2
  184. package/src/components/form-modal/confirm.tsx +2 -1
  185. package/src/components/form-modal/request.ts +5 -0
  186. package/src/components/form-modal/routes.ts +4 -1
  187. package/src/components/index.ts +12 -6
  188. package/src/components/layout/header.tsx +7 -6
  189. package/src/components/layout/market-summary.ts +9 -6
  190. package/src/components/layout/pane/capture-status.ts +1 -1
  191. package/src/components/layout/pane/footer/model.ts +11 -5
  192. package/src/components/layout/shell/chrome.ts +19 -6
  193. package/src/components/layout/shell/index.tsx +2 -1
  194. package/src/components/layout/shell/pane/layers.tsx +19 -1
  195. package/src/components/layout/status-bar.tsx +2 -2
  196. package/src/components/market-board/index.tsx +11 -3
  197. package/src/components/onboarding/onboarding-wizard.tsx +1 -0
  198. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
  199. package/src/components/onboarding/wizard-broker-sync.ts +4 -1
  200. package/src/components/pane-settings-dialog/desktop.tsx +20 -7
  201. package/src/components/pane-settings-dialog/tui.tsx +17 -9
  202. package/src/components/pane-settings-dialog/value.ts +8 -1
  203. package/src/components/pane-settings-dialog.tsx +44 -13
  204. package/src/components/sign-in-code-panel.tsx +27 -19
  205. package/src/components/ui/column-grid.tsx +48 -4
  206. package/src/components/ui/country-flag-art.tsx +180 -0
  207. package/src/components/ui/country-flag.tsx +118 -12
  208. package/src/components/ui/data-table/opentui/index.tsx +61 -2
  209. package/src/components/ui/data-table/opentui/model.ts +109 -2
  210. package/src/components/ui/data-table/types.ts +1 -1
  211. package/src/components/ui/display.tsx +5 -1
  212. package/src/components/ui/index.ts +3 -1
  213. package/src/components/ui/logo-mark.tsx +11 -2
  214. package/src/components/ui/query-bar.tsx +1 -1
  215. package/src/components/ui/row-notes.ts +79 -0
  216. package/src/core/state/app/layout.ts +4 -4
  217. package/src/data/cached-query.ts +8 -1
  218. package/src/data/config/chart-settings.ts +3 -3
  219. package/src/data/config/home.ts +10 -2
  220. package/src/data/config/layout.ts +4 -1
  221. package/src/data/config/store/node.ts +4 -0
  222. package/src/data/config/store/normalize.ts +13 -3
  223. package/src/data/config/store/portfolio-as-of-column.ts +2 -2
  224. package/src/data/json-storage.ts +2 -0
  225. package/src/data/saved-copies.ts +84 -0
  226. package/src/docs/snapshot.json +65 -32
  227. package/src/i18n/es.ts +29 -12
  228. package/src/i18n/ja.ts +29 -12
  229. package/src/i18n/ko.ts +29 -12
  230. package/src/i18n/zh-cn.ts +29 -12
  231. package/src/i18n/zh-tw.ts +29 -12
  232. package/src/layout/desks.ts +1 -1
  233. package/src/layout/launch-layout.ts +1 -1
  234. package/src/layout/pane-manager/floating-actions.ts +5 -3
  235. package/src/layout/pane-manager/floating.ts +43 -1
  236. package/src/layout/pane-manager/index.ts +2 -0
  237. package/src/layout/selected-ticker.ts +74 -0
  238. package/src/market-data/depositary-pair.ts +251 -0
  239. package/src/market-data/futures-board.ts +1 -1
  240. package/src/market-data/futures-contract.ts +124 -40
  241. package/src/market-data/fx-closes.ts +17 -2
  242. package/src/market-data/hooks.tsx +19 -11
  243. package/src/market-data/market/change-basis.ts +49 -7
  244. package/src/market-data/market/freshness.ts +34 -5
  245. package/src/market-data/market/period-return.ts +163 -0
  246. package/src/market-data/market/trading-sessions.ts +13 -0
  247. package/src/market-data/market/year-to-date.ts +8 -13
  248. package/src/market-data/performance.ts +20 -14
  249. package/src/market-data/perpetual-premium.ts +32 -0
  250. package/src/market-data/published-apac-sessions.ts +2 -1
  251. package/src/market-data/published-futures-sessions.ts +69 -0
  252. package/src/market-data/quotes/contributions.ts +5 -1
  253. package/src/market-data/quotes/freshness.ts +42 -0
  254. package/src/market-data/quotes/intraday-bar.ts +37 -0
  255. package/src/market-data/quotes/regular-session-valuation.ts +13 -8
  256. package/src/market-data/quotes/resolution.ts +4 -2
  257. package/src/market-data/tax-equivalent.ts +1 -1
  258. package/src/market-data/ticker-price-returns.ts +3 -1
  259. package/src/news/aggregator.ts +5 -1
  260. package/src/news/news-model.ts +4 -0
  261. package/src/news/types.ts +11 -0
  262. package/src/notifications/app-notifier.ts +1 -1
  263. package/src/plugins/auto-update.ts +2 -2
  264. package/src/plugins/builtin/account-management/index.tsx +1 -0
  265. package/src/plugins/builtin/account-management/pane.tsx +9 -8
  266. package/src/plugins/builtin/alerts/alert-engine.ts +29 -19
  267. package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
  268. package/src/plugins/builtin/alerts/cli.ts +485 -76
  269. package/src/plugins/builtin/alerts/command.ts +5 -10
  270. package/src/plugins/builtin/alerts/event-rows.ts +9 -5
  271. package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
  272. package/src/plugins/builtin/alerts/events.ts +107 -5
  273. package/src/plugins/builtin/alerts/fired-log.ts +78 -0
  274. package/src/plugins/builtin/alerts/format.ts +2 -1
  275. package/src/plugins/builtin/alerts/headless.ts +100 -39
  276. package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
  277. package/src/plugins/builtin/alerts/history-rows.ts +89 -0
  278. package/src/plugins/builtin/alerts/index.tsx +45 -18
  279. package/src/plugins/builtin/alerts/iv-level.ts +365 -0
  280. package/src/plugins/builtin/alerts/levels.ts +3 -3
  281. package/src/plugins/builtin/alerts/pane.tsx +97 -29
  282. package/src/plugins/builtin/alerts/price.ts +35 -0
  283. package/src/plugins/builtin/alerts/quotes.ts +24 -0
  284. package/src/plugins/builtin/alerts/research-builder.ts +84 -11
  285. package/src/plugins/builtin/alerts/research-rules.ts +76 -3
  286. package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
  287. package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
  288. package/src/plugins/builtin/alerts/scheduled.ts +188 -29
  289. package/src/plugins/builtin/alerts/storage.ts +13 -10
  290. package/src/plugins/builtin/alerts/targets.ts +12 -3
  291. package/src/plugins/builtin/analytics/index.tsx +8 -2
  292. package/src/plugins/builtin/analytics/metrics.ts +1 -1
  293. package/src/plugins/builtin/analytics/risk-book.ts +215 -0
  294. package/src/plugins/builtin/analytics/risk-client.ts +13 -6
  295. package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
  296. package/src/plugins/builtin/analytics/risk-math.ts +6 -3
  297. package/src/plugins/builtin/analytics/risk-model.ts +297 -104
  298. package/src/plugins/builtin/analytics/risk-options.ts +17 -2
  299. package/src/plugins/builtin/analytics/risk-pane.tsx +97 -36
  300. package/src/plugins/builtin/attention/pane.tsx +6 -2
  301. package/src/plugins/builtin/awards/headless.ts +7 -3
  302. package/src/plugins/builtin/awards/model.ts +18 -0
  303. package/src/plugins/builtin/awards/pane.tsx +6 -3
  304. package/src/plugins/builtin/backtest/headless.ts +3 -3
  305. package/src/plugins/builtin/backtest/rules.ts +41 -5
  306. package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
  307. package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
  308. package/src/plugins/builtin/broker-manager/footer.ts +27 -11
  309. package/src/plugins/builtin/broker-manager/index.tsx +21 -12
  310. package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
  311. package/src/plugins/builtin/broker-manager/model.ts +12 -2
  312. package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
  313. package/src/plugins/builtin/broker-manager/table.ts +28 -12
  314. package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
  315. package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
  316. package/src/plugins/builtin/broker-trading/controller.ts +45 -13
  317. package/src/plugins/builtin/broker-trading/dock.tsx +22 -4
  318. package/src/plugins/builtin/broker-trading/headless.ts +2 -1
  319. package/src/plugins/builtin/broker-trading/index.tsx +7 -1
  320. package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
  321. package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
  322. package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
  323. package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
  324. package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
  325. package/src/plugins/builtin/broker-trading/pane.tsx +110 -23
  326. package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
  327. package/src/plugins/builtin/broker-trading/ticket.tsx +112 -31
  328. package/src/plugins/builtin/buildout/cells.tsx +4 -0
  329. package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
  330. package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
  331. package/src/plugins/builtin/buildout/pane/index.tsx +25 -12
  332. package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
  333. package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
  334. package/src/plugins/builtin/catalysts/detail.tsx +3 -2
  335. package/src/plugins/builtin/catalysts/index.tsx +1 -1
  336. package/src/plugins/builtin/catalysts/model.ts +49 -4
  337. package/src/plugins/builtin/catalysts/pane.tsx +6 -8
  338. package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
  339. package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
  340. package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
  341. package/src/plugins/builtin/changelog/index.tsx +1 -0
  342. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
  343. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
  344. package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
  345. package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
  346. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
  347. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
  348. package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
  349. package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
  350. package/src/plugins/builtin/chart-composer/headless.ts +253 -35
  351. package/src/plugins/builtin/chart-composer/index.tsx +12 -2
  352. package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
  353. package/src/plugins/builtin/chart-composer/pane.tsx +127 -28
  354. package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
  355. package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
  356. package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
  357. package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
  358. package/src/plugins/builtin/chart-composer/settings.ts +31 -19
  359. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
  360. package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
  361. package/src/plugins/builtin/chat/attachments/model.ts +3 -1
  362. package/src/plugins/builtin/chat/content/index.tsx +1 -1
  363. package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
  364. package/src/plugins/builtin/chat/people.ts +1 -1
  365. package/src/plugins/builtin/clinical-trials/client.ts +5 -2
  366. package/src/plugins/builtin/clinical-trials/model.ts +13 -4
  367. package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
  368. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
  369. package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
  370. package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
  371. package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
  372. package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
  373. package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
  374. package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
  375. package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
  376. package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
  377. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  378. package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
  379. package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
  380. package/src/plugins/builtin/commodity-balances/pane.tsx +11 -4
  381. package/src/plugins/builtin/company-attention/app-rank.tsx +7 -5
  382. package/src/plugins/builtin/company-attention/building-model.ts +222 -0
  383. package/src/plugins/builtin/company-attention/building.tsx +172 -0
  384. package/src/plugins/builtin/company-attention/client.ts +117 -1
  385. package/src/plugins/builtin/company-attention/evidence.ts +16 -3
  386. package/src/plugins/builtin/company-attention/headless.ts +101 -5
  387. package/src/plugins/builtin/company-attention/index.tsx +6 -3
  388. package/src/plugins/builtin/company-attention/model.ts +33 -7
  389. package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
  390. package/src/plugins/builtin/company-attention/moves.tsx +159 -0
  391. package/src/plugins/builtin/company-attention/pane.tsx +79 -23
  392. package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
  393. package/src/plugins/builtin/company-attention/signals.tsx +160 -0
  394. package/src/plugins/builtin/company-kpis/headless.ts +3 -2
  395. package/src/plugins/builtin/company-kpis/model.ts +2 -2
  396. package/src/plugins/builtin/company-kpis/pane.tsx +1 -1
  397. package/src/plugins/builtin/composite-plugins.ts +2 -1
  398. package/src/plugins/builtin/congress-trades/headless.ts +1 -1
  399. package/src/plugins/builtin/connections/index.ts +1 -0
  400. package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
  401. package/src/plugins/builtin/correlation/headless.ts +5 -3
  402. package/src/plugins/builtin/cot/headless.ts +28 -6
  403. package/src/plugins/builtin/cot/model.ts +12 -0
  404. package/src/plugins/builtin/cot/pane.tsx +3 -2
  405. package/src/plugins/builtin/cpi/headless.ts +5 -1
  406. package/src/plugins/builtin/cpi/pane.tsx +11 -9
  407. package/src/plugins/builtin/credit-boards/model.ts +2 -0
  408. package/src/plugins/builtin/credit-documents/headless.ts +1 -1
  409. package/src/plugins/builtin/crypto-board/client.ts +86 -0
  410. package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
  411. package/src/plugins/builtin/crypto-board/defi.ts +239 -0
  412. package/src/plugins/builtin/crypto-board/headless.ts +117 -6
  413. package/src/plugins/builtin/crypto-board/index.tsx +8 -3
  414. package/src/plugins/builtin/crypto-board/model.ts +40 -8
  415. package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
  416. package/src/plugins/builtin/crypto-board/series.ts +19 -1
  417. package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
  418. package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
  419. package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
  420. package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
  421. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
  422. package/src/plugins/builtin/debug/index.tsx +1 -0
  423. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  424. package/src/plugins/builtin/docs/index.tsx +1 -1
  425. package/src/plugins/builtin/docs/pane.tsx +2 -2
  426. package/src/plugins/builtin/docs/targets.ts +1 -1
  427. package/src/plugins/builtin/doe/client.ts +5 -1
  428. package/src/plugins/builtin/doe/headless.ts +12 -6
  429. package/src/plugins/builtin/doe/model.ts +43 -6
  430. package/src/plugins/builtin/doe/pane.tsx +18 -11
  431. package/src/plugins/builtin/earnings/board-model.ts +13 -6
  432. package/src/plugins/builtin/earnings/board-table.ts +4 -1
  433. package/src/plugins/builtin/earnings/board.tsx +253 -50
  434. package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
  435. package/src/plugins/builtin/earnings/calendar.tsx +338 -0
  436. package/src/plugins/builtin/earnings/client.ts +4 -3
  437. package/src/plugins/builtin/earnings/headless.ts +53 -15
  438. package/src/plugins/builtin/earnings/history-model.ts +53 -36
  439. package/src/plugins/builtin/earnings/index.tsx +15 -22
  440. package/src/plugins/builtin/earnings/model.ts +17 -12
  441. package/src/plugins/builtin/earnings/scope.ts +89 -0
  442. package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
  443. package/src/plugins/builtin/earnings-calls/data.ts +146 -2
  444. package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
  445. package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
  446. package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
  447. package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
  448. package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
  449. package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
  450. package/src/plugins/builtin/econ/calendar-model.ts +132 -20
  451. package/src/plugins/builtin/econ/detail-view.tsx +48 -10
  452. package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
  453. package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
  454. package/src/plugins/builtin/econ/index.tsx +112 -25
  455. package/src/plugins/builtin/econ/print-watch.ts +2 -1
  456. package/src/plugins/builtin/econ/types.ts +32 -1
  457. package/src/plugins/builtin/econ-statistics/client.ts +16 -3
  458. package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
  459. package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
  460. package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
  461. package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
  462. package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
  463. package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
  464. package/src/plugins/builtin/econ-statistics/view.ts +16 -1
  465. package/src/plugins/builtin/equity-screener/client.ts +21 -3
  466. package/src/plugins/builtin/equity-screener/headless.ts +68 -6
  467. package/src/plugins/builtin/equity-screener/live.ts +1 -1
  468. package/src/plugins/builtin/equity-screener/model.ts +6 -0
  469. package/src/plugins/builtin/equity-screener/pane.tsx +45 -10
  470. package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
  471. package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
  472. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
  473. package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
  474. package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
  475. package/src/plugins/builtin/exposure/headless.ts +51 -6
  476. package/src/plugins/builtin/exposure/index.tsx +28 -2
  477. package/src/plugins/builtin/exposure/model.ts +7 -0
  478. package/src/plugins/builtin/exposure/pane.tsx +6 -1
  479. package/src/plugins/builtin/fear-greed/index.tsx +1 -1
  480. package/src/plugins/builtin/filing-events/index.tsx +1 -1
  481. package/src/plugins/builtin/futures/model.ts +10 -9
  482. package/src/plugins/builtin/futures/returns.ts +8 -36
  483. package/src/plugins/builtin/futures/table.tsx +1 -1
  484. package/src/plugins/builtin/futures-curve/client.ts +40 -9
  485. package/src/plugins/builtin/futures-curve/headless.ts +39 -17
  486. package/src/plugins/builtin/futures-curve/model.ts +258 -69
  487. package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
  488. package/src/plugins/builtin/fx-matrix/client.ts +15 -8
  489. package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
  490. package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
  491. package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
  492. package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
  493. package/src/plugins/builtin/gpu/headless.ts +9 -4
  494. package/src/plugins/builtin/help/index.tsx +13 -3
  495. package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
  496. package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
  497. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  498. package/src/plugins/builtin/holders/client.ts +13 -1
  499. package/src/plugins/builtin/holders/format.ts +28 -17
  500. package/src/plugins/builtin/holders/headless.ts +46 -7
  501. package/src/plugins/builtin/holders/index-holders.ts +264 -0
  502. package/src/plugins/builtin/holders/pane.tsx +79 -15
  503. package/src/plugins/builtin/holders/report-rows.ts +35 -7
  504. package/src/plugins/builtin/holders/table-model.ts +2 -1
  505. package/src/plugins/builtin/holders/treemap.tsx +2 -2
  506. package/src/plugins/builtin/insider/headless.ts +1 -1
  507. package/src/plugins/builtin/insider/signals-client.ts +1 -1
  508. package/src/plugins/builtin/insider/signals.tsx +1 -1
  509. package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
  510. package/src/plugins/builtin/iv-history/client.ts +20 -0
  511. package/src/plugins/builtin/iv-history/headless.ts +4 -4
  512. package/src/plugins/builtin/iv-history/index.tsx +1 -1
  513. package/src/plugins/builtin/iv-history/model.ts +13 -4
  514. package/src/plugins/builtin/iv-history/series.ts +3 -1
  515. package/src/plugins/builtin/jobs/lock-note.ts +1 -1
  516. package/src/plugins/builtin/jobs/model.ts +4 -15
  517. package/src/plugins/builtin/jobs/pane.tsx +7 -5
  518. package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
  519. package/src/plugins/builtin/jodi/pane.tsx +10 -4
  520. package/src/plugins/builtin/layout-manager/index.tsx +1 -0
  521. package/src/plugins/builtin/macro-day/headless.ts +20 -7
  522. package/src/plugins/builtin/macro-day/index.tsx +3 -2
  523. package/src/plugins/builtin/macro-day/model.ts +29 -8
  524. package/src/plugins/builtin/macro-day/pane.tsx +46 -10
  525. package/src/plugins/builtin/macro-day/releases.ts +55 -13
  526. package/src/plugins/builtin/market-heatmap/data.ts +9 -2
  527. package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
  528. package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
  529. package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
  530. package/src/plugins/builtin/market-heatmap/model.ts +10 -7
  531. package/src/plugins/builtin/market-movers/client.ts +33 -19
  532. package/src/plugins/builtin/market-movers/headless.ts +8 -2
  533. package/src/plugins/builtin/market-movers/index.tsx +16 -10
  534. package/src/plugins/builtin/market-movers/screener.ts +29 -8
  535. package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
  536. package/src/plugins/builtin/market-movers/session-table.ts +4 -2
  537. package/src/plugins/builtin/market-movers/table.ts +4 -1
  538. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  539. package/src/plugins/builtin/market-valuation/view.ts +5 -0
  540. package/src/plugins/builtin/members/headless.ts +6 -5
  541. package/src/plugins/builtin/members/pane.tsx +11 -4
  542. package/src/plugins/builtin/mna/client.ts +1 -0
  543. package/src/plugins/builtin/mna/detail.tsx +20 -32
  544. package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
  545. package/src/plugins/builtin/mna/distress/model.ts +3 -2
  546. package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
  547. package/src/plugins/builtin/mna/headless.ts +6 -13
  548. package/src/plugins/builtin/mna/model.ts +93 -15
  549. package/src/plugins/builtin/mna/pane.tsx +5 -4
  550. package/src/plugins/builtin/news/headless.ts +12 -6
  551. package/src/plugins/builtin/news/index.tsx +10 -1
  552. package/src/plugins/builtin/news/wire/index.tsx +3 -2
  553. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
  554. package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
  555. package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
  556. package/src/plugins/builtin/notes/cli.ts +49 -14
  557. package/src/plugins/builtin/openfda/pane.tsx +1 -1
  558. package/src/plugins/builtin/options/carry.ts +2 -2
  559. package/src/plugins/builtin/options/index.tsx +1 -1
  560. package/src/plugins/builtin/options/view.tsx +10 -10
  561. package/src/plugins/builtin/options-calculator/headless.ts +46 -7
  562. package/src/plugins/builtin/options-scenario/client.ts +123 -28
  563. package/src/plugins/builtin/options-scenario/collar.ts +8 -6
  564. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
  565. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
  566. package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
  567. package/src/plugins/builtin/options-scenario/csp.ts +101 -21
  568. package/src/plugins/builtin/options-scenario/headless.ts +36 -15
  569. package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
  570. package/src/plugins/builtin/options-scenario/index.tsx +28 -3
  571. package/src/plugins/builtin/options-scenario/model.ts +13 -0
  572. package/src/plugins/builtin/options-scenario/pane.tsx +9 -5
  573. package/src/plugins/builtin/perps/equity-row.tsx +11 -3
  574. package/src/plugins/builtin/perps/freshness.ts +8 -5
  575. package/src/plugins/builtin/perps/headless.ts +3 -1
  576. package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
  577. package/src/plugins/builtin/perps/live-rows.ts +161 -0
  578. package/src/plugins/builtin/perps/markets-section.tsx +154 -0
  579. package/src/plugins/builtin/perps/pane.tsx +36 -17
  580. package/src/plugins/builtin/perps/venue-markets.ts +96 -0
  581. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
  582. package/src/plugins/builtin/plugin-marketplace/pane.tsx +143 -59
  583. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +72 -24
  584. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
  585. package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
  586. package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
  587. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
  588. package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
  589. package/src/plugins/builtin/portfolio-list/income.ts +13 -2
  590. package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
  591. package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
  592. package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
  593. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
  594. package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
  595. package/src/plugins/builtin/power/client.ts +4 -1
  596. package/src/plugins/builtin/power/evidence.ts +22 -7
  597. package/src/plugins/builtin/power/gas.tsx +5 -1
  598. package/src/plugins/builtin/power/pane.tsx +8 -1
  599. package/src/plugins/builtin/power/prices.tsx +10 -3
  600. package/src/plugins/builtin/research/analyst-headless.ts +25 -8
  601. package/src/plugins/builtin/research/analyst-model.ts +68 -3
  602. package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
  603. package/src/plugins/builtin/research/event-model.ts +3 -1
  604. package/src/plugins/builtin/research/event-sources.ts +12 -4
  605. package/src/plugins/builtin/research/index.tsx +2 -1
  606. package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
  607. package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
  608. package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
  609. package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
  610. package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
  611. package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
  612. package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
  613. package/src/plugins/builtin/reverse-dcf/model.ts +6 -2
  614. package/src/plugins/builtin/risk-factors/index.tsx +2 -2
  615. package/src/plugins/builtin/scanner/lock-note.ts +1 -1
  616. package/src/plugins/builtin/sec/forms.ts +13 -0
  617. package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
  618. package/src/plugins/builtin/sec/fund-facts.ts +68 -8
  619. package/src/plugins/builtin/sec/headless.ts +85 -8
  620. package/src/plugins/builtin/sec/index.tsx +58 -12
  621. package/src/plugins/builtin/sectors/client.ts +2 -1
  622. package/src/plugins/builtin/sectors/sector-model.ts +11 -15
  623. package/src/plugins/builtin/series-inventory.ts +2 -0
  624. package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
  625. package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
  626. package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
  627. package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
  628. package/src/plugins/builtin/shared/earnings-report.ts +3 -3
  629. package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
  630. package/src/plugins/builtin/shared/job-posting.ts +129 -0
  631. package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
  632. package/src/plugins/builtin/shared/report-freshness.ts +3 -0
  633. package/src/plugins/builtin/shared/research-trading.tsx +21 -0
  634. package/src/plugins/builtin/shared/series-cache.ts +2 -0
  635. package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
  636. package/src/plugins/builtin/short-interest/client.ts +2 -0
  637. package/src/plugins/builtin/short-interest/headless.ts +41 -12
  638. package/src/plugins/builtin/short-interest/index.tsx +23 -1
  639. package/src/plugins/builtin/short-interest/model.ts +70 -19
  640. package/src/plugins/builtin/short-interest/pane.tsx +19 -2
  641. package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
  642. package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
  643. package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
  644. package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
  645. package/src/plugins/builtin/short-interest/schedule.ts +35 -2
  646. package/src/plugins/builtin/short-interest/surface.tsx +11 -4
  647. package/src/plugins/builtin/short-interest/types.ts +9 -0
  648. package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
  649. package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
  650. package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
  651. package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
  652. package/src/plugins/builtin/supply-chain/model.ts +51 -3
  653. package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
  654. package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
  655. package/src/plugins/builtin/themes/headless.ts +8 -5
  656. package/src/plugins/builtin/themes/model.ts +14 -4
  657. package/src/plugins/builtin/themes/pane.tsx +24 -5
  658. package/src/plugins/builtin/thirteenf/headless.ts +5 -5
  659. package/src/plugins/builtin/thirteenf/signals.ts +3 -1
  660. package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
  661. package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
  662. package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
  663. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
  664. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
  665. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
  666. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
  667. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
  668. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
  669. package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
  670. package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
  671. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
  672. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
  673. package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
  674. package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
  675. package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
  676. package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
  677. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
  678. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
  679. package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
  680. package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
  681. package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
  682. package/src/plugins/builtin/vol-surface/client.ts +5 -4
  683. package/src/plugins/builtin/vol-surface/headless.ts +12 -3
  684. package/src/plugins/builtin/vol-surface/model.ts +4 -2
  685. package/src/plugins/builtin/volatility/model.ts +5 -10
  686. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
  687. package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
  688. package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
  689. package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
  690. package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
  691. package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
  692. package/src/plugins/builtin/world-venue-map/index.ts +3 -26
  693. package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
  694. package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
  695. package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
  696. package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
  697. package/src/plugins/builtin/yield-curve/client.ts +25 -0
  698. package/src/plugins/builtin/yield-curve/curves.ts +7 -4
  699. package/src/plugins/builtin/yield-curve/headless.ts +38 -4
  700. package/src/plugins/builtin/yield-curve/history.ts +4 -3
  701. package/src/plugins/builtin/yield-curve/index.tsx +12 -6
  702. package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
  703. package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
  704. package/src/plugins/bundle.ts +1 -1
  705. package/src/plugins/catalog-browser.ts +2 -1
  706. package/src/plugins/compat.ts +3 -3
  707. package/src/plugins/dependencies.ts +75 -14
  708. package/src/plugins/extracted-plugins.ts +132 -34
  709. package/src/plugins/host-link.ts +149 -34
  710. package/src/plugins/installer.ts +35 -10
  711. package/src/plugins/loader.ts +206 -38
  712. package/src/plugins/manager-node.ts +2 -2
  713. package/src/plugins/marketplace/deactivation.ts +22 -0
  714. package/src/plugins/marketplace/model.ts +2 -2
  715. package/src/plugins/marketplace/packs.ts +1 -1
  716. package/src/plugins/ownership.ts +1 -1
  717. package/src/plugins/peer-retry.ts +155 -0
  718. package/src/plugins/registry/contributions.ts +3 -1
  719. package/src/plugins/registry/index.ts +4 -3
  720. package/src/plugins/registry/plugin-state.ts +3 -0
  721. package/src/plugins/seed-notice.ts +20 -0
  722. package/src/plugins/shortcut-argument.ts +39 -0
  723. package/src/portfolio/allocation.ts +1 -1
  724. package/src/portfolio/collection-name.ts +31 -0
  725. package/src/portfolio/column-values.ts +10 -8
  726. package/src/portfolio/cost-fx.ts +296 -19
  727. package/src/portfolio/manual-lots.ts +75 -0
  728. package/src/portfolio/mutations.ts +60 -22
  729. package/src/portfolio/suffixed-listings.ts +209 -0
  730. package/src/portfolio/totals-currency.ts +24 -0
  731. package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
  732. package/src/public/time-series.ts +11 -8
  733. package/src/remote/schema.ts +1 -1
  734. package/src/renderers/browser/config-host.ts +3 -0
  735. package/src/renderers/browser/main.tsx +23 -3
  736. package/src/renderers/dom/boot-frame-page.ts +187 -0
  737. package/src/renderers/dom/boot-frame.ts +237 -0
  738. package/src/renderers/dom/build-assets.ts +46 -8
  739. package/src/renderers/dom/dom-ui-host.tsx +4 -29
  740. package/src/renderers/dom/host/ascii-text.ts +16 -2
  741. package/src/renderers/dom/host/box.tsx +43 -2
  742. package/src/renderers/dom/host/chart-surface.tsx +2 -0
  743. package/src/renderers/dom/host/hls.chunk.ts +3 -0
  744. package/src/renderers/dom/host/image-surface.tsx +49 -0
  745. package/src/renderers/dom/host/media-surface.tsx +24 -18
  746. package/src/renderers/dom/host/mouse.ts +30 -0
  747. package/src/renderers/dom/host/size-observer.ts +1 -1
  748. package/src/renderers/dom/host/style.ts +1 -1
  749. package/src/renderers/dom/styles.css +76 -0
  750. package/src/renderers/dom/test-utils.tsx +2 -1
  751. package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
  752. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
  753. package/src/renderers/electrobun/bun/desktop/backend.ts +46 -3
  754. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
  755. package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
  756. package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
  757. package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
  758. package/src/renderers/electrobun/bun/index.ts +11 -1
  759. package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
  760. package/src/renderers/electrobun/bun/window/create.ts +4 -1
  761. package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
  762. package/src/renderers/electrobun/shared/protocol.ts +6 -1
  763. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  764. package/src/renderers/electrobun/view/config-host.ts +3 -0
  765. package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
  766. package/src/renderers/electrobun/view/main.tsx +30 -4
  767. package/src/renderers/electrobun/view/news-source.ts +5 -5
  768. package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -1
  769. package/src/renderers/electrobun/view/ui-host.tsx +1 -1
  770. package/src/renderers/opentui/browser-open.ts +88 -0
  771. package/src/renderers/opentui/host.tsx +5 -14
  772. package/src/renderers/opentui/image/loader.ts +3 -2
  773. package/src/renderers/opentui/native-smoke.ts +10 -1
  774. package/src/renderers/opentui/pane-image/index.ts +2 -1
  775. package/src/renderers/opentui/render-lib.ts +253 -0
  776. package/src/renderers/opentui/render-timing-smoke.ts +1 -1
  777. package/src/renderers/opentui/start.tsx +1 -1
  778. package/src/renderers/share/layout-view.tsx +2 -2
  779. package/src/renderers/share/main.tsx +4 -4
  780. package/src/renderers/share/view.tsx +1 -1
  781. package/src/series/resolve.ts +22 -6
  782. package/src/series/standalone.ts +30 -2
  783. package/src/series/types.ts +8 -1
  784. package/src/shares/api.ts +4 -1
  785. package/src/shares/pane.ts +1 -1
  786. package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
  787. package/src/sources/gloomberb-cloud/index.ts +32 -16
  788. package/src/sources/gloomberb-cloud/news.ts +39 -0
  789. package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
  790. package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
  791. package/src/sources/listing-symbols.ts +37 -0
  792. package/src/sources/provider-errors.ts +16 -2
  793. package/src/sources/provider-router/financial-routes.ts +21 -3
  794. package/src/sources/provider-router/financials.ts +15 -2
  795. package/src/sources/provider-router/history.ts +30 -8
  796. package/src/sources/provider-router/primary.ts +1 -1
  797. package/src/state/app/bootstrap.ts +23 -0
  798. package/src/state/hooks/inline-tickers.ts +30 -5
  799. package/src/sync/controller.ts +1 -1
  800. package/src/sync/core-contributors.ts +46 -14
  801. package/src/team/collections-sync.ts +2 -1
  802. package/src/team/collections.ts +178 -17
  803. package/src/telemetry/crash-reports.ts +17 -4
  804. package/src/test-support/builtin-panes.ts +1 -1
  805. package/src/theme/colors.ts +3 -30
  806. package/src/theme/ui-tokens.ts +37 -1
  807. package/src/tickers/company-name-listing.ts +210 -33
  808. package/src/tickers/index-roots.ts +47 -0
  809. package/src/tickers/instrument-label.ts +5 -1
  810. package/src/tickers/metadata.ts +10 -0
  811. package/src/tickers/perpetual-listing.ts +5 -2
  812. package/src/tickers/search/home-listing.ts +75 -0
  813. package/src/tickers/search/index.ts +76 -27
  814. package/src/tickers/search/ranking.ts +54 -4
  815. package/src/tickers/search/upsert.ts +29 -0
  816. package/src/tickers/terminal-codes.ts +416 -40
  817. package/src/time-series/bar-aggregation.ts +243 -0
  818. package/src/time-series/base-series.ts +307 -0
  819. package/src/time-series/chart-bounds.ts +349 -0
  820. package/src/time-series/chart-seed.ts +142 -0
  821. package/src/time-series/economic.ts +5 -0
  822. package/src/time-series/fundamentals.ts +18 -3
  823. package/src/time-series/history-acquisition.ts +499 -0
  824. package/src/time-series/leverage.ts +100 -0
  825. package/src/time-series/market-resolution.ts +4 -2
  826. package/src/time-series/market.ts +2 -1
  827. package/src/time-series/price-comparison.ts +175 -30
  828. package/src/time-series/range.ts +9 -1
  829. package/src/time-series/resolution.ts +174 -16
  830. package/src/time-series/resolve-context.ts +157 -0
  831. package/src/time-series/resolve.ts +126 -1577
  832. package/src/time-series/series-name.ts +38 -0
  833. package/src/time-series/series-presentation.ts +208 -0
  834. package/src/time-series/spec.ts +11 -6
  835. package/src/time-series/studies.ts +109 -12
  836. package/src/time-series/tail-reconcile.ts +121 -0
  837. package/src/time-series/types.ts +29 -2
  838. package/src/time-series/use-chart-resolution.ts +4 -5
  839. package/src/types/broker.ts +10 -0
  840. package/src/types/config.ts +14 -0
  841. package/src/types/data-provider.ts +14 -0
  842. package/src/types/exchange-rate.ts +6 -0
  843. package/src/types/financials.ts +104 -7
  844. package/src/types/headless.ts +76 -6
  845. package/src/types/instrument.ts +3 -0
  846. package/src/types/plugin.ts +65 -8
  847. package/src/types/price-history.ts +2 -0
  848. package/src/types/ticker.ts +13 -0
  849. package/src/types/trading.ts +59 -1
  850. package/src/ui/ascii-font.ts +13 -1
  851. package/src/ui/host.tsx +6 -1
  852. package/src/updater/command-names.ts +175 -0
  853. package/src/updater/index.ts +10 -5
  854. package/src/utils/calendar-date.ts +32 -15
  855. package/src/utils/cash-runway.ts +241 -0
  856. package/src/utils/central-banks.ts +42 -0
  857. package/src/utils/cli-output.ts +259 -80
  858. package/src/utils/contract-month.ts +16 -0
  859. package/src/utils/crypto-pair.ts +61 -3
  860. package/src/utils/currency-units.ts +29 -0
  861. package/src/utils/datetime-format.ts +10 -0
  862. package/src/utils/debug-log.ts +1 -1
  863. package/src/utils/decimal-comma.ts +39 -6
  864. package/src/utils/exchange-announcements.ts +71 -0
  865. package/src/utils/exchange-rate-snapshot.ts +10 -0
  866. package/src/utils/financial-statements.ts +83 -4
  867. package/src/utils/format.ts +34 -16
  868. package/src/utils/fundamentals.ts +150 -34
  869. package/src/utils/fx-market-hours.ts +10 -1
  870. package/src/utils/latest-financial-period.ts +8 -0
  871. package/src/utils/linux-libc.ts +54 -0
  872. package/src/utils/market-capitalization.ts +111 -15
  873. package/src/utils/max-drawdown.ts +64 -0
  874. package/src/utils/network-failure.ts +83 -0
  875. package/src/utils/option-expiry.ts +139 -7
  876. package/src/utils/price-earnings.ts +11 -5
  877. package/src/utils/price-history.ts +18 -8
  878. package/src/utils/price-window.ts +131 -0
  879. package/src/utils/release-date.ts +47 -0
  880. package/src/utils/sec.ts +3 -0
  881. package/src/utils/throttled-fetch.ts +11 -4
  882. package/src/utils/trailing-eps.ts +67 -0
  883. package/src/utils/typed-text.ts +21 -0
  884. package/src/utils/utc-time.ts +24 -1
  885. package/src/version.ts +6 -0
  886. package/src/cli/pane-functions/screenshot.ts +0 -2122
@@ -12,8 +12,9 @@ import { getSharedRegistry } from "../../registry";
12
12
  import { requestAccountManagementTab } from "../account-management/navigation";
13
13
  import { resolvePlanAccess, usePlanAccess, type PlanAccess } from "../../../api-client/plan-access";
14
14
  import { useViewport } from "../../../react/input";
15
- import { useOptionalDialog } from "../../../ui/dialog";
15
+ import { type DialogApi, useOptionalDialog } from "../../../ui/dialog";
16
16
  import { promptCloudUpgrade } from "../../../account/upgrade-dialog";
17
+ import { type CheckoutHandoffTarget, promptCheckoutHandoff } from "../../../account/checkout-handoff";
17
18
 
18
19
  const CLOUD_UPGRADE_URL = "https://gloom.sh/cloud?upgrade=pro";
19
20
 
@@ -120,8 +121,12 @@ export function resetCloudUpgradeGuardForTests(): void {
120
121
  cloudUpgradeBlockedUntil.clear();
121
122
  }
122
123
 
124
+ function cloudUpgradeReturnTo(): string | undefined {
125
+ return getCurrentPluginTarget() === "web" ? window.location.href : undefined;
126
+ }
127
+
123
128
  async function resolveCloudUpgradeUrl(page: CloudUpgradePage, options: CloudUpgradeOptions): Promise<string> {
124
- const returnTo = getCurrentPluginTarget() === "web" ? window.location.href : undefined;
129
+ const returnTo = cloudUpgradeReturnTo();
125
130
  if (page === "cloud") return researchUpgradeUrl(returnTo);
126
131
  const { url } = page === "portal"
127
132
  ? await apiClient.createBillingPortal()
@@ -138,6 +143,28 @@ async function openCloudUpgrade(
138
143
  await rendererHost.openExternal(await resolveCloudUpgradeUrl(page, options));
139
144
  }
140
145
 
146
+ /**
147
+ * Opens checkout with the hand-off over the app: it says checkout is in the
148
+ * browser, reopens that same checkout, shows the short link where no browser
149
+ * opened, and waits for Pro. Settles once the browser was asked to open it;
150
+ * the dialog stays until Pro arrives or it is closed. Only a checkout that
151
+ * cannot be created rejects: a browser that fails to open leaves the link.
152
+ */
153
+ async function openCheckoutWithHandoff(
154
+ dialog: DialogApi,
155
+ rendererHost: Pick<RendererHost, "openExternal">,
156
+ options: CloudUpgradeOptions,
157
+ ): Promise<void> {
158
+ const target = apiClient.createCloudCheckout(cloudUpgradeReturnTo(), options.interval ?? "month")
159
+ .then(({ url, link }): CheckoutHandoffTarget => ({
160
+ url,
161
+ link: link ?? null,
162
+ opened: rendererHost.openExternal(url).then((result) => result !== false, () => false),
163
+ }));
164
+ void promptCheckoutHandoff(dialog, target);
165
+ await (await target).opened;
166
+ }
167
+
141
168
  /**
142
169
  * Opens the checkout page when any cloud UI has published an opener, counted
143
170
  * under `placement` rather than the UI that published it.
@@ -166,9 +193,9 @@ function isCloudUpgradeOptions(value: unknown): value is CloudUpgradeOptions {
166
193
  * a held Enter would otherwise open a checkout per keypress.
167
194
  *
168
195
  * A signed-in account without Pro sees the upgrade sheet first (what Pro adds,
169
- * what today costs), and checkout opens only once they choose to go on.
170
- * Signed-out users get the public Cloud page and Pro accounts the billing
171
- * portal, as before.
196
+ * what today costs), and checkout opens only once they choose to go on, with
197
+ * the checkout hand-off over the app until Pro arrives. Signed-out users get
198
+ * the public Cloud page and Pro accounts the billing portal, as before.
172
199
  */
173
200
  export function useCloudUpgradeAction(placement: string): (options?: unknown) => Promise<void> {
174
201
  const rendererHost = useRendererHost();
@@ -184,7 +211,8 @@ export function useCloudUpgradeAction(placement: string): (options?: unknown) =>
184
211
  const page = currentCloudUpgradePage();
185
212
  await openCloudUpgradePage(page, async () => {
186
213
  resolved.onOpening?.();
187
- await openCloudUpgrade(rendererHost, page, resolved);
214
+ if (page === "checkout" && dialog) await openCheckoutWithHandoff(dialog, rendererHost, resolved);
215
+ else await openCloudUpgrade(rendererHost, page, resolved);
188
216
  }).catch(() => (
189
217
  // Keep the Cloud page reachable when checkout cannot be created; a native
190
218
  // session still rides along on the server's one-time handoff URL. Under
@@ -0,0 +1,375 @@
1
+ import type { DataProvider, EarningsEvent } from "../../../types/data-provider";
2
+ import type { CorporateActionsData, EarningsAction, EarningsDateStatus, EarningsEpsBasis } from "../../../types/financials";
3
+ import { logFailure } from "../../../utils/log-failure";
4
+ import { zonedDateKey } from "../../../utils/zoned-date-time";
5
+ import { dateStatusFromEstimateFlag, earningsDateStatus, earningsEpsBasis } from "./earnings-report";
6
+ import { coherentEarningsValue, EARNINGS_ESTIMATE_FIELDS } from "./earnings-estimate-basis";
7
+
8
+ /**
9
+ * The one reading of a company's report dates that `earnings`, `events`, EE,
10
+ * ERN, OSA and the LEAPS next-earnings column share: the per-ticker
11
+ * calendar's date resolved against the quarters the company has on record
12
+ * (corporate actions, ERN's history), so a symbol shows the same next date,
13
+ * timing and status everywhere.
14
+ *
15
+ * - A confirmed date beats an estimated one; otherwise the calendar's stands
16
+ * (JPM: the record's 2027-01-13 gives way to the calendar's confirmed
17
+ * 2027-01-14).
18
+ * - A date that has passed is reported, with the actual EPS once it is known
19
+ * (PEP's 2026-10-08). A report out today is reported as soon as its figure
20
+ * is, as same-day actuals are (META after the close).
21
+ * - A next date only the calendar knows is added (GS's 2027-01-19).
22
+ */
23
+
24
+ /** Two dates this close are one report under two sources: quarterly reports are some ninety days apart. */
25
+ const SAME_QUARTER_DAYS = 45;
26
+ /** An actual this close to an upcoming date is that report, out already. */
27
+ const SAME_REPORT_DAYS = 3;
28
+ const DAY_MS = 86_400_000;
29
+
30
+ /** The trading calendar's day, which report dates are on. */
31
+ export function newYorkDay(now = Date.now()): string {
32
+ return zonedDateKey(now, "America/New_York");
33
+ }
34
+
35
+ /** The day the calendar puts a report on, YYYY-MM-DD. */
36
+ function calendarDay(event: Pick<EarningsEvent, "earningsDate">): string {
37
+ return event.earningsDate.toISOString().slice(0, 10);
38
+ }
39
+
40
+ /**
41
+ * A calendar event's status on `today`: reported once the report is out
42
+ * (Gloom Cloud says so, or has its figure) or its date has passed.
43
+ */
44
+ export function calendarStatus(
45
+ event: Pick<EarningsEvent, "earningsDate" | "epsActual" | "isDateEstimate" | "dateStatus">,
46
+ today: string,
47
+ ): EarningsDateStatus | null {
48
+ if (event.epsActual != null || earningsDateStatus(event.dateStatus) === "reported" || calendarDay(event) < today) return "reported";
49
+ return dateStatusFromEstimateFlag(event.isDateEstimate);
50
+ }
51
+
52
+ /** Whether the calendar's date for a report replaces another source's: a confirmed date beats an estimated one, else the calendar's stands. */
53
+ function calendarDateWins(calendar: EarningsDateStatus | null, other: EarningsDateStatus | null): boolean {
54
+ return calendar === "confirmed" || other !== "confirmed";
55
+ }
56
+
57
+ /** The calendar's report as a quarter on record takes it. */
58
+ interface CalendarReport {
59
+ date: string;
60
+ timing: "bmo" | "amc" | null;
61
+ dateStatus: EarningsDateStatus | null;
62
+ /** Whether the calendar's consensus is for this report; see `consensusIsReports`. */
63
+ ownConsensus: boolean;
64
+ /** Null when the calendar's consensus cannot be tied to the report. */
65
+ epsEstimate: number | null;
66
+ epsAnalysts: number | null;
67
+ epsActual: number | null;
68
+ epsBasis: EarningsEpsBasis | null;
69
+ revenueEstimate: number | null;
70
+ /** The quarter the estimate is for, YYYY-MM, when the calendar names it. */
71
+ fiscalPeriod: string | null;
72
+ currency: string | null;
73
+ }
74
+
75
+ /**
76
+ * Whether the calendar's consensus is for its own report. A report out keeps
77
+ * its date while the consensus rolls to the next quarter (PEP's 2026-10-08
78
+ * beside December's 2.24), so once a report is out its consensus counts only
79
+ * when something ties it to that report: the calendar's actual beside it,
80
+ * Gloom Cloud's stored report (which says its status, and keeps the report's
81
+ * own consensus), or a forecast quarter ending before the report date.
82
+ */
83
+ function consensusIsReports(event: EarningsEvent, dateStatus: EarningsDateStatus | null, date: string): boolean {
84
+ if (dateStatus !== "reported" || event.epsActual != null || event.dateStatus != null) return true;
85
+ const periodEnd = event.estimateBasis?.epsEstimate?.periodEndDate ?? null;
86
+ return periodEnd != null && periodEnd < date;
87
+ }
88
+
89
+ function calendarReport(event: EarningsEvent, today: string): CalendarReport {
90
+ const date = calendarDay(event);
91
+ const dateStatus = calendarStatus(event, today);
92
+ const ownConsensus = consensusIsReports(event, dateStatus, date);
93
+ const periodEnd = event.estimateBasis?.epsEstimate?.periodEndDate ?? null;
94
+ const epsEstimate = ownConsensus ? coherentEarningsValue(event, "epsEstimate") : null;
95
+ return {
96
+ date,
97
+ timing: event.timing === "BMO" ? "bmo" : event.timing === "AMC" ? "amc" : null,
98
+ dateStatus,
99
+ ownConsensus,
100
+ epsEstimate,
101
+ epsAnalysts: epsEstimate == null ? null : coherentEarningsValue(event, "epsAnalysts"),
102
+ epsActual: event.epsActual ?? null,
103
+ epsBasis: epsEstimate == null ? null : earningsEpsBasis(event.epsBasis),
104
+ revenueEstimate: ownConsensus ? coherentEarningsValue(event, "revenueEstimate") : null,
105
+ fiscalPeriod: ownConsensus && periodEnd ? periodEnd.slice(0, 7) : null,
106
+ currency: epsEstimate == null ? null : event.estimateBasis?.epsEstimate?.currency ?? null,
107
+ };
108
+ }
109
+
110
+ /** What the resolver reads of a quarter on record. */
111
+ interface ReportSlot {
112
+ date: string;
113
+ dateStatus: EarningsDateStatus | null | undefined;
114
+ epsActual: number | null | undefined;
115
+ }
116
+
117
+ /** How one source's rows are read and rebuilt: corporate actions for events and EE, history for ERN. */
118
+ export interface ReportRowAdapter<T> {
119
+ slot(row: T): ReportSlot;
120
+ /** The row labelled reported, with `epsActual` when it has no figure of its own. */
121
+ reported(row: T, epsActual: number | null): T;
122
+ /** The calendar's report as a row: over `row` when it is the same report, else a new one. */
123
+ fromCalendar(report: CalendarReport, row: T | null): T;
124
+ }
125
+
126
+ const isOut = (slot: ReportSlot, today: string) => slot.epsActual != null || slot.dateStatus === "reported" || slot.date < today;
127
+ const dayGap = (left: string, right: string) => Math.abs(Date.parse(`${left}T00:00:00Z`) - Date.parse(`${right}T00:00:00Z`)) / DAY_MS;
128
+
129
+ /**
130
+ * The row the calendar's report is: for a report out, the nearest quarter on
131
+ * record that is out, else an upcoming one days away; for an upcoming one,
132
+ * the nearest upcoming quarter, else one out days away (out early).
133
+ */
134
+ function calendarRowIndex(slots: readonly ReportSlot[], report: CalendarReport, today: string): number {
135
+ const nearest = (keep: (slot: ReportSlot) => boolean, days: number) => {
136
+ let best = -1;
137
+ for (const [index, slot] of slots.entries()) {
138
+ const gap = dayGap(slot.date, report.date);
139
+ if (gap <= days && keep(slot) && (best < 0 || gap < dayGap(slots[best]!.date, report.date))) best = index;
140
+ }
141
+ return best;
142
+ };
143
+ const out = (slot: ReportSlot) => isOut(slot, today);
144
+ const upcoming = (slot: ReportSlot) => !isOut(slot, today);
145
+ const [first, second] = report.dateStatus === "reported" ? [out, upcoming] : [upcoming, out];
146
+ const index = nearest(first, SAME_QUARTER_DAYS);
147
+ return index >= 0 ? index : nearest(second, SAME_REPORT_DAYS);
148
+ }
149
+
150
+ /**
151
+ * Quarters on record resolved against the calendar's event, with the row
152
+ * that is the calendar's report. Past quarters read reported; the calendar's
153
+ * report updates its row, or is added when the record has none.
154
+ */
155
+ function resolveReportRowsWithCalendar<T>(
156
+ rows: readonly T[],
157
+ event: EarningsEvent | null | undefined,
158
+ today: string,
159
+ adapter: ReportRowAdapter<T>,
160
+ ): { rows: T[]; calendarRow: T | null } {
161
+ const resolved = rows.map((row) => {
162
+ const slot = adapter.slot(row);
163
+ return isOut(slot, today) && slot.dateStatus !== "reported" ? adapter.reported(row, null) : row;
164
+ });
165
+ if (!event) return { rows: resolved, calendarRow: null };
166
+ const report = calendarReport(event, today);
167
+ const index = calendarRowIndex(resolved.map(adapter.slot), report, today);
168
+ if (index < 0) {
169
+ const added = adapter.fromCalendar(report, null);
170
+ return { rows: [...resolved, added], calendarRow: added };
171
+ }
172
+ const row = resolved[index]!;
173
+ const slot = adapter.slot(row);
174
+ if (isOut(slot, today)) {
175
+ if (slot.epsActual == null && report.epsActual != null) resolved[index] = adapter.reported(row, report.epsActual);
176
+ } else if (report.dateStatus === "reported" || calendarDateWins(report.dateStatus, earningsDateStatus(slot.dateStatus))) {
177
+ resolved[index] = adapter.fromCalendar(report, row);
178
+ }
179
+ return { rows: resolved, calendarRow: resolved[index]! };
180
+ }
181
+
182
+ export function resolveReportRows<T>(rows: readonly T[], event: EarningsEvent | null | undefined, today: string, adapter: ReportRowAdapter<T>): T[] {
183
+ return resolveReportRowsWithCalendar(rows, event, today, adapter).rows;
184
+ }
185
+
186
+ const TIME_TEXT = { bmo: "Pre Market", amc: "After Hours" } as const;
187
+
188
+ /** A row's beat, measured as Gloom Cloud measures it, once the calendar gives it a figure or an estimate it lacked. */
189
+ function withSurprise(row: EarningsAction, before: EarningsAction | null): EarningsAction {
190
+ const { epsActual: actual, epsEstimate: estimate } = row;
191
+ if (actual == null || estimate == null || (actual === before?.epsActual && estimate === before.epsEstimate)) return row;
192
+ const cents = (value: number) => Math.round(value * 100) / 100;
193
+ const { difference: _difference, surprisePercent: _surprise, ...rest } = row;
194
+ return { ...rest, difference: cents(actual - estimate), ...(estimate !== 0 ? { surprisePercent: cents((actual - estimate) / Math.abs(estimate) * 100) } : {}) };
195
+ }
196
+
197
+ function definedFields<T extends object>(row: T): T {
198
+ return Object.fromEntries(Object.entries(row).filter(([, value]) => value !== undefined)) as T;
199
+ }
200
+
201
+ /** Corporate-actions earnings rows, as events, EE and the `earnings` resolution read them. */
202
+ const ACTION_ROWS: ReportRowAdapter<EarningsAction> = {
203
+ slot: (row) => ({ date: row.date, dateStatus: earningsDateStatus(row.dateStatus), epsActual: row.epsActual }),
204
+ reported: (row, epsActual) => withSurprise({ ...row, dateStatus: "reported", ...(row.epsActual == null && epsActual != null ? { epsActual } : {}) }, row),
205
+ fromCalendar(report, row) {
206
+ // The calendar's consensus goes with its date; without one, the record's stays.
207
+ const estimate = report.epsEstimate != null;
208
+ return withSurprise(definedFields<EarningsAction>({
209
+ ...row,
210
+ date: report.date,
211
+ dateType: "announcement",
212
+ time: report.timing ? TIME_TEXT[report.timing] : row?.time,
213
+ currency: estimate ? report.currency ?? row?.currency : row?.currency,
214
+ epsEstimate: estimate ? report.epsEstimate! : row?.epsEstimate,
215
+ epsAnalysts: estimate ? report.epsAnalysts ?? undefined : row?.epsAnalysts,
216
+ epsActual: report.epsActual ?? row?.epsActual,
217
+ fiscalPeriod: row?.fiscalPeriod ?? report.fiscalPeriod ?? undefined,
218
+ epsBasis: estimate ? report.epsBasis : row?.epsBasis,
219
+ dateStatus: report.dateStatus ?? undefined,
220
+ }), row);
221
+ },
222
+ };
223
+
224
+ /** A company's corporate-actions earnings rows, newest first, resolved against its calendar event. */
225
+ export function resolveEarningsActions(earnings: readonly EarningsAction[], event: EarningsEvent | null | undefined, today: string): EarningsAction[] {
226
+ return resolveReportRows(earnings, event, today, ACTION_ROWS).sort((left, right) => right.date.localeCompare(left.date));
227
+ }
228
+
229
+ /** A company's corporate actions with their earnings rows resolved against its calendar event. */
230
+ export function withResolvedEarnings(actions: CorporateActionsData, event: EarningsEvent | null | undefined, today: string): CorporateActionsData {
231
+ // Without a calendar event the rows still resolve: a date gone by reads reported, as ERN reads it.
232
+ return { ...actions, earnings: resolveEarningsActions(actions.earnings, event, today) };
233
+ }
234
+
235
+ /** The calendar's event for a symbol, as it was asked for (SAN:EPA comes back as SAN:EPA). */
236
+ export function calendarEventOf(events: readonly EarningsEvent[] | null | undefined, symbol: string): EarningsEvent | null {
237
+ const key = symbol.trim().toUpperCase();
238
+ return events?.find((event) => event.symbol.trim().toUpperCase() === key) ?? null;
239
+ }
240
+
241
+ /**
242
+ * Whether the calendar event is read against its quarters on record: once
243
+ * its date has come or its report is out, for the actual and the next
244
+ * report, and while its date is not confirmed, which a confirmed date on
245
+ * record beats. A confirmed date to come stands alone.
246
+ */
247
+ function needsRecordedQuarters(event: EarningsEvent, today: string): boolean {
248
+ return event.epsActual == null && (calendarDay(event) <= today || calendarStatus(event, today) !== "confirmed");
249
+ }
250
+
251
+ export interface ResolvedEarningsEvent extends EarningsEvent {
252
+ /** Out, announced or projected, after resolution; null when no source says. */
253
+ dateStatus: EarningsDateStatus | null;
254
+ }
255
+
256
+ const TIMING_OF_TEXT: Record<string, EarningsEvent["timing"]> = { "Pre Market": "BMO", "After Hours": "AMC" };
257
+
258
+ /** The calendar's event without its consensus, which is another quarter's than the report's. */
259
+ function withoutConsensus(event: EarningsEvent): EarningsEvent {
260
+ const stripped: EarningsEvent = { ...event, epsEstimate: null, revenueEstimate: null, epsBasis: null };
261
+ delete stripped.estimateBasis;
262
+ for (const field of EARNINGS_ESTIMATE_FIELDS) if (stripped[field] != null) stripped[field] = null;
263
+ return stripped;
264
+ }
265
+
266
+ const statusFlag = (status: EarningsDateStatus | null) => status === "projected" ? true : status === "confirmed" ? false : null;
267
+
268
+ /**
269
+ * The resolved event for a row. The calendar's own report keeps every figure
270
+ * the calendar carries (revenue, ranges, bases), but its consensus only when
271
+ * it is the report's, and takes the row's status and, when the record has the
272
+ * actual the calendar lacks, the record's EPS figures, measured against the
273
+ * record's estimate. Another report on record is that row alone.
274
+ */
275
+ function eventOfRow(event: EarningsEvent, row: EarningsAction, report: CalendarReport | null): ResolvedEarningsEvent {
276
+ const ownDate = row.date === calendarDay(event);
277
+ const dateStatus = earningsDateStatus(row.dateStatus);
278
+ const earningsDate = ownDate ? event.earningsDate : new Date(`${row.date}T00:00:00.000Z`);
279
+ const rowTiming = TIMING_OF_TEXT[row.time ?? ""];
280
+ if (report) {
281
+ const base = report.ownConsensus ? event : withoutConsensus(event);
282
+ const recordFigures = row.epsActual != null && event.epsActual == null;
283
+ return {
284
+ ...base,
285
+ earningsDate,
286
+ earningsCallDate: ownDate ? event.earningsCallDate ?? null : null,
287
+ isDateEstimate: statusFlag(dateStatus) ?? (dateStatus === "reported" ? base.isDateEstimate ?? null : null),
288
+ epsEstimate: recordFigures ? row.epsEstimate ?? null : base.epsEstimate ?? row.epsEstimate ?? null,
289
+ epsAnalysts: recordFigures ? row.epsAnalysts ?? null : base.epsAnalysts ?? row.epsAnalysts ?? null,
290
+ epsActual: event.epsActual ?? row.epsActual ?? null,
291
+ epsBasis: recordFigures ? row.epsBasis ?? null : base.epsBasis ?? row.epsBasis ?? null,
292
+ surprise: recordFigures ? row.surprisePercent ?? null : base.surprise ?? row.surprisePercent ?? null,
293
+ timing: (ownDate && event.timing) || rowTiming || event.timing,
294
+ dateStatus,
295
+ };
296
+ }
297
+ return {
298
+ symbol: event.symbol,
299
+ name: event.name,
300
+ earningsDate,
301
+ earningsCallDate: null,
302
+ isDateEstimate: statusFlag(dateStatus),
303
+ epsEstimate: row.epsEstimate ?? null,
304
+ epsAnalysts: row.epsAnalysts ?? null,
305
+ epsActual: row.epsActual ?? null,
306
+ epsBasis: row.epsBasis ?? null,
307
+ revenueEstimate: null,
308
+ revenueActual: null,
309
+ surprise: row.surprisePercent ?? null,
310
+ timing: rowTiming ?? "",
311
+ dateStatus,
312
+ };
313
+ }
314
+
315
+ /**
316
+ * A company's next report: the calendar's, resolved against its quarters on
317
+ * record when they were read. Once the calendar's date has passed, the next
318
+ * upcoming quarter on record stands in, else the report that is out, with its
319
+ * actual and the estimate it was measured against. Without the record, a past
320
+ * date still reads reported.
321
+ */
322
+ export function resolveCalendarEvent(event: EarningsEvent, earnings: readonly EarningsAction[] | null | undefined, today: string): ResolvedEarningsEvent {
323
+ const status = calendarStatus(event, today);
324
+ if (!earnings && status !== "reported") return { ...event, dateStatus: status };
325
+ const { rows, calendarRow } = resolveReportRowsWithCalendar(earnings ?? [], event, today, ACTION_ROWS);
326
+ const next = rows
327
+ .filter((row) => !isOut(ACTION_ROWS.slot(row), today))
328
+ .sort((left, right) => left.date.localeCompare(right.date))[0];
329
+ const chosen = next ?? calendarRow!;
330
+ return eventOfRow(event, chosen, chosen === calendarRow ? calendarReport(event, today) : null);
331
+ }
332
+
333
+ /** The next report date, YYYY-MM-DD, of a resolved event; null once it is out. */
334
+ export function upcomingReportDate(event: ResolvedEarningsEvent | null | undefined): string | null {
335
+ return event && event.dateStatus !== "reported" ? calendarDay(event) : null;
336
+ }
337
+
338
+ /** A listing: the calendar's key for it (SAN:EPA) and the symbol and venue corporate actions are read by. */
339
+ export interface EarningsListing {
340
+ key: string;
341
+ symbol: string;
342
+ exchange: string;
343
+ }
344
+
345
+ /** Calendar events need their quarters on record read at most this many at a time. */
346
+ const RECORD_CONCURRENCY = 4;
347
+
348
+ /**
349
+ * Each listing's calendar event, resolved: the corporate actions of those
350
+ * whose date has come are read for their actual and the next quarter on
351
+ * record. A listing the calendar has nothing for is absent; one whose
352
+ * record cannot be read resolves on the calendar alone.
353
+ */
354
+ export async function resolveListingEvents(
355
+ provider: Pick<DataProvider, "getCorporateActions">,
356
+ listings: readonly EarningsListing[],
357
+ events: readonly EarningsEvent[],
358
+ today: string,
359
+ ): Promise<Map<string, ResolvedEarningsEvent>> {
360
+ const resolved = new Map<string, ResolvedEarningsEvent>();
361
+ const pending = listings.flatMap((listing) => {
362
+ const event = calendarEventOf(events, listing.key);
363
+ return event ? [{ listing, event }] : [];
364
+ });
365
+ for (let index = 0; index < pending.length; index += RECORD_CONCURRENCY) {
366
+ await Promise.all(pending.slice(index, index + RECORD_CONCURRENCY).map(async ({ listing, event }) => {
367
+ const earnings = needsRecordedQuarters(event, today) && provider.getCorporateActions
368
+ ? await provider.getCorporateActions(listing.symbol, listing.exchange)
369
+ .then((actions) => actions.earnings, logFailure("earnings-calendar", "Read corporate actions for a past report date failed", null))
370
+ : null;
371
+ resolved.set(listing.key.toUpperCase(), resolveCalendarEvent(event, earnings, today));
372
+ }));
373
+ }
374
+ return resolved;
375
+ }
@@ -1,6 +1,6 @@
1
1
  import type { EarningsEstimateBasis, EarningsEstimateField, EarningsEvent } from "../../../types/data-provider";
2
2
 
3
- const EARNINGS_ESTIMATE_FIELDS: readonly EarningsEstimateField[] = [
3
+ export const EARNINGS_ESTIMATE_FIELDS: readonly EarningsEstimateField[] = [
4
4
  "epsEstimate", "epsLow", "epsHigh", "epsYearAgo", "epsGrowth", "epsAnalysts",
5
5
  "epsTrend7dAgo", "epsTrend30dAgo", "epsRevisionUp7d", "epsRevisionUp30d", "epsRevisionDown7d", "epsRevisionDown30d",
6
6
  "revenueEstimate", "revenueLow", "revenueHigh", "revenueYearAgo", "revenueGrowth", "revenueAnalysts",
@@ -54,8 +54,8 @@ export function epsBasisShort(basis: EarningsEpsBasis | null): string {
54
54
  return basis === "gaap" ? "GAAP" : basis === "adjusted" ? "adj" : "?";
55
55
  }
56
56
 
57
- /** The basis as every earnings table names it: GAAP, adj, basis unknown. */
58
- const BASIS_TEXT: Record<EpsBasisName, string> = { GAAP: "GAAP", adjusted: "adj", unknown: "basis unknown" };
57
+ /** Known bases are named in row details; unknown stays in the structured data and drilldown. */
58
+ const BASIS_TEXT: Record<EpsBasisName, string> = { GAAP: "GAAP", adjusted: "adj", unknown: "" };
59
59
 
60
60
  type EarningsRecord = CorporateActionsData["earnings"][number];
61
61
 
@@ -80,7 +80,7 @@ export function earningsDetailParts(earning: EarningsRecord): string[] {
80
80
  export function earningsDetail(earning: EarningsRecord): string {
81
81
  const detail = earningsDetailParts(earning).join(", ");
82
82
  if (earning.epsActual == null) return detail || "Pending";
83
- return earning.dateType === "fiscal-period-end" ? `Period end; ${detail}` : detail;
83
+ return earning.dateType === "fiscal-period-end" ? ["Period end", detail].filter(Boolean).join("; ") : detail;
84
84
  }
85
85
 
86
86
  const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
@@ -1,4 +1,5 @@
1
- import type { Quote } from "../../../types/financials";
1
+ import { intradayBarText } from "../../../market-data/quotes/intraday-bar";
2
+ import type { Quote, QuoteIntradayBar } from "../../../types/financials";
2
3
  import { isFxMarketOpen } from "../../../utils/fx-market-hours";
3
4
  import { quoteFreshnessFields } from "./report-freshness";
4
5
 
@@ -32,6 +33,27 @@ export interface FxRateTiming {
32
33
  error?: unknown;
33
34
  /** How far the snapshot runs behind the market, as the data service states it; 0 is real-time. */
34
35
  delayMinutes?: number;
36
+ /** The pair's bar the rate is the close of, when the data service took one over a print that had stopped behind it. */
37
+ intradayBar?: QuoteIntradayBar;
38
+ /** Why a stale rate stands, as the data service states it. */
39
+ staleReason?: string;
40
+ }
41
+
42
+ /**
43
+ * What the loaded USD rates behind a pair say of themselves, one line per
44
+ * rate (`BRL/USD: ...`): the bar a rate is the close of, and, for a stale
45
+ * rate, why it stands (its bars had nothing newer, or did not load). A rate
46
+ * whose own refresh failed is stale for that reason, so it states none.
47
+ */
48
+ export function fxRateNotes(legs: ReadonlyMap<string, FxRateTiming>, now = Date.now()): string[] {
49
+ return [...legs].flatMap(([currency, timing]) => {
50
+ if (currency === "USD") return [];
51
+ const stale = timing.staleAt != null && timing.staleAt <= now;
52
+ return [
53
+ ...timing.intradayBar ? [`${currency}/USD: ${intradayBarText(timing.intradayBar)}`] : [],
54
+ ...stale && timing.error == null && timing.staleReason ? [`${currency}/USD: ${timing.staleReason}`] : [],
55
+ ];
56
+ });
35
57
  }
36
58
 
37
59
  function isoTime(time: number | undefined | null): string | undefined {
@@ -0,0 +1,129 @@
1
+ import type { CloudJobsPostingDetail } from "../../../api-client/types";
2
+ import { formatShortDate } from "../../../utils/datetime-format";
3
+ import { formatCompact, formatNumber } from "../../../utils/format";
4
+ import { humanLabel } from "./research-cells";
5
+
6
+ /**
7
+ * One job posting read by a person: its place, when it was posted, its pay
8
+ * and its hours, in words. Shared by JOBS and HIRE's posting detail. A value
9
+ * the careers system did not give is left out, never shown as a dash or
10
+ * "Unknown".
11
+ */
12
+
13
+ /** "$120k–150k", "$45–52/h", "£60k"; empty when the posting has no pay. */
14
+ export function formatSalaryRange(
15
+ min: number | null,
16
+ max: number | null,
17
+ currency: string | null,
18
+ period: string | null,
19
+ ): string {
20
+ if (min == null && max == null) return "";
21
+ const symbol = currency === "USD" ? "$" : currency === "EUR" ? "€" : currency === "GBP" ? "£" : currency ? `${currency} ` : "";
22
+ const fmt = (value: number) => (period === "hour" ? formatNumber(value, 0) : formatCompact(value));
23
+ const range = min != null && max != null && min !== max
24
+ ? `${symbol}${fmt(min)}–${fmt(max)}`
25
+ : `${symbol}${fmt((min ?? max)!)}`;
26
+ return period === "hour" ? `${range}/h` : period === "month" ? `${range}/mo` : range;
27
+ }
28
+
29
+ const countryNames = new Intl.DisplayNames(["en"], { type: "region" });
30
+
31
+ /** A country code as its name: "AU" as "Australia". */
32
+ export function countryName(code: string | null | undefined): string | null {
33
+ if (!code || !/^[A-Z]{2}$/.test(code)) return null;
34
+ try {
35
+ return countryNames.of(code) ?? null;
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Where the role is, in words: the posting's location with its country named
43
+ * ("Paris, FR" as "Paris, France"), the country when the location does not end
44
+ * with it, and "Remote" when the role is remote and the location does not say
45
+ * so. A list of places ("Austin, TX; Denver, CO") stays as written.
46
+ */
47
+ export function postingPlace(posting: { location: string | null; country: string | null; remote: boolean | null }): string | null {
48
+ const location = posting.location?.trim() || null;
49
+ const name = countryName(posting.country);
50
+ let place = location;
51
+ if (location && !location.includes(";")) {
52
+ const parts = location.split(/\s*,\s*/);
53
+ const last = parts.at(-1) ?? "";
54
+ const lastName = /^[A-Z]{2}$/.test(last) && parts.length > 1 ? countryName(last) : null;
55
+ // A trailing code names the country only when it is the posting's own ("…, CA" may be California).
56
+ if (lastName && last === posting.country) place = [...parts.slice(0, -1), lastName].join(", ");
57
+ else if (name && last.toLowerCase() !== name.toLowerCase() && last !== posting.country) place = `${location}, ${name}`;
58
+ } else if (!location) place = name;
59
+ const remote = posting.remote && !/remote/i.test(place ?? "") ? "Remote" : null;
60
+ return [place, remote].filter(Boolean).join(" · ") || null;
61
+ }
62
+
63
+ /** "Oct 3, 2026"; "about Oct 3, 2026" or "Oct 3, 2026 or earlier" when the careers system's date is not exact. */
64
+ function postedText(postedAt: string | null | undefined, precision?: string | null): string | null {
65
+ if (!postedAt) return null;
66
+ const date = formatShortDate(postedAt.slice(0, 10), { utc: true, fallback: "" });
67
+ if (!date) return null;
68
+ return precision === "floor" ? `${date} or earlier` : precision === "approximate" ? `about ${date}` : date;
69
+ }
70
+
71
+ /** schema.org's employment types, in words. */
72
+ const EMPLOYMENT: Record<string, string> = {
73
+ FULL_TIME: "Full time", PART_TIME: "Part time", CONTRACTOR: "Contract", TEMPORARY: "Temporary",
74
+ INTERN: "Internship", PER_DIEM: "Per diem", VOLUNTEER: "Volunteer",
75
+ };
76
+
77
+ /** "Full time" for FULL_TIME or full_time; a careers system's own word ("CDI", "Vollzeit") as written. Nothing for "other" or "unavailable". */
78
+ export function employmentText(value: string | null | undefined): string | null {
79
+ const text = value?.trim();
80
+ if (!text || /^(?:other|unknown|unavailable|n\/?a|none)$/i.test(text)) return null;
81
+ const known = EMPLOYMENT[text.toUpperCase().replace(/[\s-]+/g, "_")];
82
+ if (known) return known;
83
+ return /^[A-Z_]+$/.test(text) && text.includes("_") ? humanLabel(text) : text;
84
+ }
85
+
86
+ /**
87
+ * A title the careers system never gave: `Job <id>`, an id alone, or the
88
+ * words of the posting's address. A reader is shown "Untitled role" instead.
89
+ */
90
+ function isPlaceholderTitle(title: string | null | undefined, externalId?: string | null): boolean {
91
+ const text = title?.trim() ?? "";
92
+ if (!text) return true;
93
+ if (/^job\s+\S*\d\S*$/i.test(text) || /^[A-Z]{0,4}[-_]?\d{3,}$/i.test(text)) return true;
94
+ if (externalId && (text === externalId || text === `Job ${externalId}`)) return true;
95
+ // Bytes still percent-encoded: the address's words, not the role's title.
96
+ return /%[0-9A-F]{2}/i.test(text);
97
+ }
98
+
99
+ /** The posting's title as the careers system wrote it, or "Untitled role" for one it never gave. */
100
+ export function postingTitle(title: string | null | undefined, externalId?: string | null): string {
101
+ return isPlaceholderTitle(title, externalId) ? "Untitled role" : title!;
102
+ }
103
+
104
+ /**
105
+ * A posting's description as words: the markdown marks some careers systems
106
+ * write (Starbucks' "#### Basic Qualifications", "**Pay**") taken off, so a
107
+ * heading reads as a heading line and bold as its words.
108
+ */
109
+ export function descriptionText(text: string | null | undefined): string | null {
110
+ if (!text) return null;
111
+ return text.replace(/^[ \t]*#{1,6}[ \t]+(?=\S)/gm, "").replace(/\*\*([^*\n]+)\*\*/g, "$1").replace(/__([^_\n]+)__/g, "$1");
112
+ }
113
+
114
+ export interface PostingFact {
115
+ label: string;
116
+ value: string;
117
+ }
118
+
119
+ /** A posting's facts, in the order a reader wants them; only those the posting has. */
120
+ export function postingFacts(posting: CloudJobsPostingDetail): PostingFact[] {
121
+ const facts: Array<[string, string | null]> = [
122
+ ["Place", postingPlace(posting)],
123
+ ["Posted", postedText(posting.postedAt, posting.postedPrecision)],
124
+ ["Pay", formatSalaryRange(posting.salaryMin, posting.salaryMax, posting.salaryCurrency, posting.salaryPeriod) || null],
125
+ ["Hours", employmentText(posting.employmentType)],
126
+ ["Closed", posting.closedAt ? postedText(posting.closedAt) : null],
127
+ ];
128
+ return facts.filter((fact): fact is [string, string] => !!fact[1]).map(([label, value]) => ({ label, value }));
129
+ }
@@ -127,10 +127,10 @@ export async function proLockNote(
127
127
  ? "verify your email (Ctrl+P, Resend Verification Email)"
128
128
  : `log in or sign up${pro ? "" : " free"} from the command bar (Ctrl+P, Log In or Sign Up)`;
129
129
  const unlock = gate === "pro"
130
- ? `To read ${pane.shows} in ${token}, upgrade in the app (gloomberb, then Ctrl+P, UPGRADE)`
131
- : `To read ${pane.shows} in ${token}, open the app (gloomberb), ${account}${pro ? ", then upgrade (Ctrl+P, UPGRADE)" : ""}`;
130
+ ? `To read ${pane.shows} in ${token}, upgrade in the app (gloom, then Ctrl+P, UPGRADE)`
131
+ : `To read ${pane.shows} in ${token}, open the app (gloom), ${account}${pro ? ", then upgrade (Ctrl+P, UPGRADE)" : ""}`;
132
132
  const bare = pane.argument === "none" || (pane.argument === "optional" && !symbol);
133
- const command = bare ? `gloomberb fn ${token}` : `gloomberb fn ${token} ${ticker}`;
133
+ const command = bare ? `gloom fn ${token}` : `gloom fn ${token} ${ticker}`;
134
134
  const free = pane.free ? await answeringFreeRoute(lock, pane.free) : null;
135
135
  return [
136
136
  `${token} ${what}: ${pane.does}. ${without} it shows none of ${pane.shows}.`,