gloomberb 0.16.1 → 0.16.2

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 (574) hide show
  1. package/README.md +2 -1
  2. package/package.json +2 -1
  3. package/src/api-client/beneficial-owners.ts +92 -0
  4. package/src/api-client/data.ts +26 -8
  5. package/src/api-client/errors.ts +12 -0
  6. package/src/api-client/geo.ts +363 -0
  7. package/src/api-client/index.ts +2 -0
  8. package/src/api-client/market-discovery.ts +10 -0
  9. package/src/api-client/paths.ts +30 -1
  10. package/src/api-client/perps.ts +37 -0
  11. package/src/api-client/request.ts +5 -2
  12. package/src/api-client/types.ts +2 -0
  13. package/src/brokers/account-cache.ts +24 -0
  14. package/src/cli/commands/command-utils.ts +8 -0
  15. package/src/cli/commands/crypto-hints.ts +75 -0
  16. package/src/cli/commands/market.ts +414 -106
  17. package/src/cli/commands/overview.ts +77 -32
  18. package/src/cli/commands/ticker.ts +101 -43
  19. package/src/cli/desktop-pane-shot.ts +34 -4
  20. package/src/cli/freshness.ts +61 -0
  21. package/src/cli/help.ts +1 -0
  22. package/src/cli/helpers.ts +4 -8
  23. package/src/cli/index.ts +12 -2
  24. package/src/cli/listing-arg.ts +321 -0
  25. package/src/cli/pane-functions/access-gate.ts +42 -0
  26. package/src/cli/pane-functions/catalog.ts +28 -1
  27. package/src/cli/pane-functions/data.ts +3 -1
  28. package/src/cli/pane-functions/dom.ts +20 -2
  29. package/src/cli/pane-functions/freshness.ts +400 -0
  30. package/src/cli/pane-functions/function-help.ts +27 -20
  31. package/src/cli/pane-functions/headless.ts +39 -11
  32. package/src/cli/pane-functions/index.ts +15 -4
  33. package/src/cli/pane-functions/options.ts +3 -0
  34. package/src/cli/pane-functions/report.ts +4 -0
  35. package/src/cli/pane-functions/resolver.ts +35 -7
  36. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  37. package/src/cli/pane-functions/screenshot.ts +7 -3
  38. package/src/cli/result.ts +41 -27
  39. package/src/cli/ticker-resolution.ts +7 -5
  40. package/src/components/chart/composite/composite-chart.tsx +88 -2536
  41. package/src/components/chart/composite/drawing-store.ts +59 -0
  42. package/src/components/chart/composite/legend.tsx +319 -0
  43. package/src/components/chart/composite/panel-bitmap.ts +139 -0
  44. package/src/components/chart/composite/panel-gestures.ts +59 -0
  45. package/src/components/chart/composite/panel-labels.ts +107 -0
  46. package/src/components/chart/composite/panel-surface.tsx +876 -0
  47. package/src/components/chart/composite/tool-catalog.ts +98 -0
  48. package/src/components/chart/composite/toolbar.tsx +186 -0
  49. package/src/components/chart/composite/tools.ts +8 -0
  50. package/src/components/chart/composite/use-composite-chart-keyboard.ts +703 -0
  51. package/src/components/chart/composite/use-composite-viewport.ts +143 -0
  52. package/src/components/command-bar/list/model.ts +5 -0
  53. package/src/components/command-bar/list/view.tsx +16 -3
  54. package/src/components/command-bar/pane-templates/workflow.ts +1 -0
  55. package/src/components/command-bar/routes/ticker-search/actions.ts +5 -0
  56. package/src/components/command-bar/workflow/ops.ts +1 -1
  57. package/src/components/command-bar/workflow/tickers.ts +6 -0
  58. package/src/components/data-table/view.tsx +29 -10
  59. package/src/components/feedback-dialog.tsx +2 -1
  60. package/src/components/form-modal/field-row.tsx +2 -2
  61. package/src/components/layout/detached-pane-shell.tsx +6 -3
  62. package/src/components/layout/pane/content.tsx +23 -10
  63. package/src/components/layout/pane/error-boundary.tsx +103 -0
  64. package/src/components/layout/pane/footer/registration.tsx +68 -23
  65. package/src/components/layout/pane/header.tsx +11 -11
  66. package/src/components/layout/shell/menu.ts +2 -1
  67. package/src/components/layout/shell/pane/actions.ts +2 -1
  68. package/src/components/layout/shell/pane/layers.tsx +14 -3
  69. package/src/components/metric-treemap/heat-surface.tsx +43 -16
  70. package/src/components/metric-treemap/heat-tiles.ts +18 -0
  71. package/src/components/metric-treemap/layout.ts +2 -0
  72. package/src/components/onboarding/onboarding-keyboard.ts +311 -0
  73. package/src/components/onboarding/onboarding-wizard.tsx +101 -788
  74. package/src/components/onboarding/use-onboarding-broker-form.ts +263 -0
  75. package/src/components/onboarding/use-onboarding-progress.ts +298 -0
  76. package/src/components/onboarding/use-onboarding-upgrade.ts +83 -0
  77. package/src/components/ticker/list-table-view.tsx +13 -1
  78. package/src/components/ui/fields.tsx +8 -0
  79. package/src/components/ui/range-track.tsx +10 -3
  80. package/src/components/ui/status.tsx +8 -2
  81. package/src/components/ui/tabs.tsx +7 -4
  82. package/src/components/ui/text-prompt-dialog.tsx +4 -2
  83. package/src/data/config/store/normalize.ts +17 -1
  84. package/src/i18n/es.ts +19 -1
  85. package/src/i18n/ja.ts +19 -1
  86. package/src/i18n/ko.ts +19 -1
  87. package/src/i18n/zh-cn.ts +19 -1
  88. package/src/i18n/zh-tw.ts +19 -1
  89. package/src/market-data/coordinator/chart.ts +1 -1
  90. package/src/market-data/coordinator/quotes.ts +23 -4
  91. package/src/market-data/fx-closes.ts +72 -0
  92. package/src/market-data/market/freshness.ts +78 -6
  93. package/src/market-data/market/session-close-note.ts +57 -0
  94. package/src/market-data/market/status.ts +151 -0
  95. package/src/market-data/published-apac-sessions.ts +66 -0
  96. package/src/market-data/quotes/contributions.ts +2 -0
  97. package/src/market-data/quotes/resolution.ts +2 -0
  98. package/src/market-data/quotes/screener-live-quotes.ts +49 -4
  99. package/src/plugins/builtin/account-management/pane.tsx +4 -0
  100. package/src/plugins/builtin/alerts/cli.ts +10 -5
  101. package/src/plugins/builtin/alerts/index.tsx +1 -0
  102. package/src/plugins/builtin/analytics/metrics.ts +8 -3
  103. package/src/plugins/builtin/analytics/risk-client.ts +191 -66
  104. package/src/plugins/builtin/analytics/risk-fx.ts +102 -0
  105. package/src/plugins/builtin/analytics/risk-headless.ts +19 -13
  106. package/src/plugins/builtin/analytics/risk-model.ts +45 -17
  107. package/src/plugins/builtin/analytics/risk-pane.tsx +1 -0
  108. package/src/plugins/builtin/analytics/risk-test-data.ts +25 -1
  109. package/src/plugins/builtin/attention/headless.ts +6 -4
  110. package/src/plugins/builtin/attention/index.tsx +1 -1
  111. package/src/plugins/builtin/attention/pane.tsx +1 -1
  112. package/src/plugins/builtin/awards/headless.ts +1 -0
  113. package/src/plugins/builtin/awards/pane.tsx +1 -1
  114. package/src/plugins/builtin/backtest/headless.ts +1 -0
  115. package/src/plugins/builtin/bond-calculator/headless.ts +1 -0
  116. package/src/plugins/builtin/buildout/index.tsx +1 -0
  117. package/src/plugins/builtin/catalysts/headless.ts +3 -1
  118. package/src/plugins/builtin/cds/headless.ts +2 -0
  119. package/src/plugins/builtin/central-bank-rates/headless.ts +1 -0
  120. package/src/plugins/builtin/chart-composer/headless.ts +38 -3
  121. package/src/plugins/builtin/chart-composer/index.tsx +3 -1
  122. package/src/plugins/builtin/chart-composer/series-expression.ts +18 -2
  123. package/src/plugins/builtin/chat/channels.ts +19 -3
  124. package/src/plugins/builtin/chat/content/composer-runtime.ts +28 -13
  125. package/src/plugins/builtin/chat/content/footer.tsx +15 -6
  126. package/src/plugins/builtin/chat/content/index.tsx +120 -21
  127. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +261 -113
  128. package/src/plugins/builtin/chat/content/transcript.tsx +20 -2
  129. package/src/plugins/builtin/chat/direct-messages.ts +200 -0
  130. package/src/plugins/builtin/chat/index.tsx +1 -0
  131. package/src/plugins/builtin/chat/message/desktop.tsx +3 -0
  132. package/src/plugins/builtin/chat/message/header.tsx +16 -6
  133. package/src/plugins/builtin/chat/message/inline-tokens.tsx +72 -32
  134. package/src/plugins/builtin/chat/message/profile-popover.tsx +96 -12
  135. package/src/plugins/builtin/chat/message/terminal.tsx +3 -0
  136. package/src/plugins/builtin/chat/message/types.ts +14 -2
  137. package/src/plugins/builtin/chat/profile-popover.ts +18 -13
  138. package/src/plugins/builtin/clinical-trials/index.tsx +1 -0
  139. package/src/plugins/builtin/cloud/askg/executor.ts +2 -1
  140. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  141. package/src/plugins/builtin/cloud/team/module.tsx +1 -0
  142. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -0
  143. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  144. package/src/plugins/builtin/comment-letters/index.tsx +1 -0
  145. package/src/plugins/builtin/company-attention/headless.ts +5 -1
  146. package/src/plugins/builtin/company-attention/pane.tsx +1 -1
  147. package/src/plugins/builtin/company-kpis/headless.ts +1 -0
  148. package/src/plugins/builtin/congress-trades/headless.ts +1 -0
  149. package/src/plugins/builtin/correlation/compute.ts +59 -16
  150. package/src/plugins/builtin/correlation/geo.ts +34 -0
  151. package/src/plugins/builtin/correlation/headless-schema.ts +4 -1
  152. package/src/plugins/builtin/correlation/headless.ts +42 -8
  153. package/src/plugins/builtin/correlation/history.ts +2 -0
  154. package/src/plugins/builtin/correlation/index.tsx +68 -14
  155. package/src/plugins/builtin/correlation/matrix/model.ts +17 -2
  156. package/src/plugins/builtin/correlation/matrix/selection.ts +5 -3
  157. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  158. package/src/plugins/builtin/correlation/relationship/pane.tsx +4 -2
  159. package/src/plugins/builtin/correlation/relationship/view-model.ts +16 -1
  160. package/src/plugins/builtin/correlation/settings.ts +1 -1
  161. package/src/plugins/builtin/cot/headless.ts +6 -3
  162. package/src/plugins/builtin/cot/index.tsx +3 -3
  163. package/src/plugins/builtin/cot/model.ts +36 -3
  164. package/src/plugins/builtin/cpi/headless.ts +4 -0
  165. package/src/plugins/builtin/credit-boards/headless.ts +2 -0
  166. package/src/plugins/builtin/credit-conditions/headless.ts +1 -0
  167. package/src/plugins/builtin/credit-documents/gauge.tsx +5 -8
  168. package/src/plugins/builtin/credit-documents/headless.ts +2 -0
  169. package/src/plugins/builtin/crypto-board/headless.ts +3 -0
  170. package/src/plugins/builtin/custom-view/loader.ts +1 -1
  171. package/src/plugins/builtin/debt-maturities/headless.ts +4 -0
  172. package/src/plugins/builtin/dividend-yield/headless.ts +1 -0
  173. package/src/plugins/builtin/doe/headless.ts +7 -0
  174. package/src/plugins/builtin/doe/range-bar.tsx +7 -37
  175. package/src/plugins/builtin/earnings/headless.ts +5 -0
  176. package/src/plugins/builtin/earnings-calls/headless.ts +1 -0
  177. package/src/plugins/builtin/earnings-ripple/client.ts +91 -18
  178. package/src/plugins/builtin/earnings-ripple/headless.ts +28 -8
  179. package/src/plugins/builtin/earnings-ripple/index.tsx +2 -2
  180. package/src/plugins/builtin/earnings-ripple/model.ts +97 -7
  181. package/src/plugins/builtin/earnings-ripple/pane.tsx +148 -23
  182. package/src/plugins/builtin/econ/index.tsx +1 -0
  183. package/src/plugins/builtin/econ-statistics/headless.ts +2 -0
  184. package/src/plugins/builtin/equity-screener/headless.ts +2 -0
  185. package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
  186. package/src/plugins/builtin/executives/index.tsx +1 -0
  187. package/src/plugins/builtin/exposure/headless.ts +3 -1
  188. package/src/plugins/builtin/fear-greed/headless.ts +2 -0
  189. package/src/plugins/builtin/filing-events/index.tsx +1 -0
  190. package/src/plugins/builtin/futures-curve/client.ts +12 -3
  191. package/src/plugins/builtin/futures-curve/headless.ts +39 -10
  192. package/src/plugins/builtin/futures-curve/index.tsx +3 -3
  193. package/src/plugins/builtin/futures-curve/model.ts +188 -4
  194. package/src/plugins/builtin/futures-curve/pane.tsx +51 -9
  195. package/src/plugins/builtin/gpu/headless.ts +7 -3
  196. package/src/plugins/builtin/gpu/ladder.tsx +26 -30
  197. package/src/plugins/builtin/gpu/model.ts +27 -1
  198. package/src/plugins/builtin/holders/beneficial-client.ts +209 -0
  199. package/src/plugins/builtin/holders/beneficial-model.ts +279 -0
  200. package/src/plugins/builtin/holders/beneficial-report.ts +126 -0
  201. package/src/plugins/builtin/holders/beneficial-view.tsx +241 -0
  202. package/src/plugins/builtin/holders/headless.ts +139 -5
  203. package/src/plugins/builtin/holders/index.tsx +10 -3
  204. package/src/plugins/builtin/holders/pane.tsx +20 -3
  205. package/src/plugins/builtin/holders/report-header.ts +95 -0
  206. package/src/plugins/builtin/holders/table-model.ts +20 -5
  207. package/src/plugins/builtin/holders/thirteenf-match.ts +29 -1
  208. package/src/plugins/builtin/holders/types.ts +2 -1
  209. package/src/plugins/builtin/insider/headless.ts +2 -0
  210. package/src/plugins/builtin/ipo-calendar/headless.ts +1 -0
  211. package/src/plugins/builtin/iv-history/headless.ts +4 -1
  212. package/src/plugins/builtin/jobs/headless.ts +1 -0
  213. package/src/plugins/builtin/kelly-sizer/index.tsx +1 -0
  214. package/src/plugins/builtin/macro-day/headless.ts +2 -0
  215. package/src/plugins/builtin/market-halts/index.tsx +1 -0
  216. package/src/plugins/builtin/market-heatmap/data.ts +2 -0
  217. package/src/plugins/builtin/market-heatmap/headless.ts +135 -0
  218. package/src/plugins/builtin/market-heatmap/index.tsx +39 -46
  219. package/src/plugins/builtin/market-heatmap/model.ts +146 -0
  220. package/src/plugins/builtin/market-heatmap/portfolio.ts +23 -4
  221. package/src/plugins/builtin/market-movers/headless.ts +2 -0
  222. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  223. package/src/plugins/builtin/members/client.ts +20 -3
  224. package/src/plugins/builtin/members/headless.ts +5 -3
  225. package/src/plugins/builtin/members/model.ts +10 -2
  226. package/src/plugins/builtin/members/pane.tsx +13 -6
  227. package/src/plugins/builtin/mna/distress/headless.ts +1 -0
  228. package/src/plugins/builtin/mna/headless.ts +1 -0
  229. package/src/plugins/builtin/money-markets/headless.ts +1 -0
  230. package/src/plugins/builtin/news/headless.ts +2 -0
  231. package/src/plugins/builtin/news/wire/index.tsx +8 -1
  232. package/src/plugins/builtin/notes/cli.ts +14 -6
  233. package/src/plugins/builtin/notes/index.tsx +1 -0
  234. package/src/plugins/builtin/openfda/index.tsx +1 -0
  235. package/src/plugins/builtin/options-calculator/headless.ts +2 -0
  236. package/src/plugins/builtin/options-positioning/headless.ts +3 -0
  237. package/src/plugins/builtin/options-scenario/headless.ts +5 -0
  238. package/src/plugins/builtin/pe-band/client.ts +48 -3
  239. package/src/plugins/builtin/pe-band/headless.ts +25 -7
  240. package/src/plugins/builtin/pe-band/model.ts +92 -16
  241. package/src/plugins/builtin/pe-band/pane.tsx +33 -16
  242. package/src/plugins/builtin/perps/client.ts +50 -3
  243. package/src/plugins/builtin/perps/headless.ts +144 -16
  244. package/src/plugins/builtin/perps/index.tsx +6 -6
  245. package/src/plugins/builtin/perps/model.ts +189 -12
  246. package/src/plugins/builtin/perps/pane.tsx +248 -47
  247. package/src/plugins/builtin/portfolio-list/allocation.ts +245 -0
  248. package/src/plugins/builtin/portfolio-list/cached-account.ts +33 -0
  249. package/src/plugins/builtin/portfolio-list/cell-version.ts +19 -1
  250. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +328 -29
  251. package/src/plugins/builtin/portfolio-list/cli/render.ts +230 -39
  252. package/src/plugins/builtin/portfolio-list/cli/shared.ts +67 -2
  253. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +35 -31
  254. package/src/plugins/builtin/portfolio-list/column-values.ts +124 -47
  255. package/src/plugins/builtin/portfolio-list/headless.ts +98 -16
  256. package/src/plugins/builtin/portfolio-list/index.tsx +14 -3
  257. package/src/plugins/builtin/portfolio-list/live-valuation.ts +7 -5
  258. package/src/plugins/builtin/portfolio-list/mutations.ts +55 -1
  259. package/src/plugins/builtin/portfolio-list/pane/data.ts +6 -0
  260. package/src/plugins/builtin/portfolio-list/pane/index.tsx +47 -13
  261. package/src/plugins/builtin/portfolio-list/settings.ts +33 -2
  262. package/src/plugins/builtin/portfolio-list/summary/index.tsx +24 -1
  263. package/src/plugins/builtin/portfolio-list/summary/totals.ts +26 -7
  264. package/src/plugins/builtin/power/headless.ts +3 -1
  265. package/src/plugins/builtin/rate-path/headless.ts +2 -0
  266. package/src/plugins/builtin/realized-vol/headless.ts +2 -0
  267. package/src/plugins/builtin/relative-rotation/headless.ts +2 -0
  268. package/src/plugins/builtin/research/analyst-headless.ts +3 -0
  269. package/src/plugins/builtin/research/events-headless.ts +2 -0
  270. package/src/plugins/builtin/research/headless.ts +2 -0
  271. package/src/plugins/builtin/research/index.tsx +1 -0
  272. package/src/plugins/builtin/research/relative-valuation-headless.ts +10 -5
  273. package/src/plugins/builtin/research/relative-valuation-model.ts +11 -3
  274. package/src/plugins/builtin/research/relative-valuation-pane.tsx +3 -3
  275. package/src/plugins/builtin/research-search/index.tsx +1 -0
  276. package/src/plugins/builtin/revenue-breakdown/headless.ts +2 -0
  277. package/src/plugins/builtin/reverse-dcf/headless.ts +2 -0
  278. package/src/plugins/builtin/risk-factors/headless.ts +2 -0
  279. package/src/plugins/builtin/scanner/flow-history.ts +2 -13
  280. package/src/plugins/builtin/scanner/headless.ts +14 -0
  281. package/src/plugins/builtin/seasonality/headless.ts +2 -0
  282. package/src/plugins/builtin/sec/command-bar-search.ts +14 -4
  283. package/src/plugins/builtin/sec/filing-content.ts +42 -17
  284. package/src/plugins/builtin/sec/forms.ts +12 -0
  285. package/src/plugins/builtin/sec/headless.ts +2 -0
  286. package/src/plugins/builtin/sectors/client.ts +1 -0
  287. package/src/plugins/builtin/sectors/headless.ts +9 -1
  288. package/src/plugins/builtin/sectors/sector-model.ts +2 -0
  289. package/src/plugins/builtin/shared/crypto-pair.ts +14 -0
  290. package/src/plugins/builtin/shared/locked-rows.tsx +1 -1
  291. package/src/plugins/builtin/shared/report-freshness.ts +57 -0
  292. package/src/plugins/builtin/shared/research-cells.tsx +4 -9
  293. package/src/plugins/builtin/short-interest/headless.ts +2 -0
  294. package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
  295. package/src/plugins/builtin/short-volume/headless.ts +1 -0
  296. package/src/plugins/builtin/social-mentions/headless.ts +1 -0
  297. package/src/plugins/builtin/supply-chain/headless.ts +7 -1
  298. package/src/plugins/builtin/supply-chain/index.tsx +10 -1
  299. package/src/plugins/builtin/supply-chain/model.ts +27 -2
  300. package/src/plugins/builtin/supply-chain/pane.tsx +71 -32
  301. package/src/plugins/builtin/themes/headless.ts +3 -0
  302. package/src/plugins/builtin/thirteenf/headless.ts +110 -25
  303. package/src/plugins/builtin/thirteenf/report-notes.ts +149 -0
  304. package/src/plugins/builtin/ticker-detail/headless.ts +7 -1
  305. package/src/plugins/builtin/ticker-detail/index.tsx +1 -0
  306. package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -2
  307. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +26 -26
  308. package/src/plugins/builtin/time-sales/headless.ts +5 -1
  309. package/src/plugins/builtin/treasury-auctions/headless.ts +1 -0
  310. package/src/plugins/builtin/volatility/headless.ts +6 -1
  311. package/src/plugins/builtin/world-indices/headless.ts +2 -0
  312. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +157 -0
  313. package/src/plugins/builtin/world-venue-map/basemap.ts +214 -0
  314. package/src/plugins/builtin/world-venue-map/client.ts +45 -0
  315. package/src/plugins/builtin/world-venue-map/entity-detail.tsx +147 -0
  316. package/src/plugins/builtin/world-venue-map/entity-table.ts +59 -0
  317. package/src/plugins/builtin/world-venue-map/evidence.ts +77 -0
  318. package/src/plugins/builtin/world-venue-map/feed.ts +293 -0
  319. package/src/plugins/builtin/world-venue-map/geo-draw.tsx +248 -0
  320. package/src/plugins/builtin/world-venue-map/geo-series.ts +142 -0
  321. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +447 -0
  322. package/src/plugins/builtin/world-venue-map/headless.ts +146 -0
  323. package/src/plugins/builtin/world-venue-map/ids.ts +1 -0
  324. package/src/plugins/builtin/world-venue-map/index.ts +67 -4
  325. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +552 -0
  326. package/src/plugins/builtin/world-venue-map/layers.ts +348 -0
  327. package/src/plugins/builtin/world-venue-map/map-symbols.ts +220 -0
  328. package/src/plugins/builtin/world-venue-map/map.tsx +381 -45
  329. package/src/plugins/builtin/world-venue-map/model.ts +7 -4
  330. package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +199 -0
  331. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n00.chunk.ts +4 -0
  332. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n15.chunk.ts +4 -0
  333. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n30.chunk.ts +4 -0
  334. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n45.chunk.ts +4 -0
  335. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n60.chunk.ts +4 -0
  336. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n75.chunk.ts +4 -0
  337. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s15.chunk.ts +4 -0
  338. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s30.chunk.ts +4 -0
  339. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s60.chunk.ts +4 -0
  340. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n00.chunk.ts +4 -0
  341. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n15.chunk.ts +4 -0
  342. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n30.chunk.ts +4 -0
  343. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n45.chunk.ts +4 -0
  344. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n60.chunk.ts +4 -0
  345. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n75.chunk.ts +4 -0
  346. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s15.chunk.ts +4 -0
  347. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s30.chunk.ts +4 -0
  348. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s45.chunk.ts +4 -0
  349. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n00.chunk.ts +4 -0
  350. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n15.chunk.ts +4 -0
  351. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n30.chunk.ts +4 -0
  352. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n45.chunk.ts +4 -0
  353. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n60.chunk.ts +4 -0
  354. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n75.chunk.ts +4 -0
  355. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s15.chunk.ts +4 -0
  356. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s30.chunk.ts +4 -0
  357. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s45.chunk.ts +4 -0
  358. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s60.chunk.ts +4 -0
  359. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n00.chunk.ts +4 -0
  360. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n15.chunk.ts +4 -0
  361. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n30.chunk.ts +4 -0
  362. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n45.chunk.ts +4 -0
  363. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n60.chunk.ts +4 -0
  364. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n75.chunk.ts +4 -0
  365. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s15.chunk.ts +4 -0
  366. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s30.chunk.ts +4 -0
  367. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s60.chunk.ts +4 -0
  368. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n00.chunk.ts +4 -0
  369. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n15.chunk.ts +4 -0
  370. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n30.chunk.ts +4 -0
  371. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n45.chunk.ts +4 -0
  372. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n60.chunk.ts +4 -0
  373. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n75.chunk.ts +4 -0
  374. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s15.chunk.ts +4 -0
  375. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s30.chunk.ts +4 -0
  376. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s60.chunk.ts +4 -0
  377. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n00.chunk.ts +4 -0
  378. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n15.chunk.ts +4 -0
  379. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n30.chunk.ts +4 -0
  380. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n45.chunk.ts +4 -0
  381. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n60.chunk.ts +4 -0
  382. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n75.chunk.ts +4 -0
  383. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075s45.chunk.ts +4 -0
  384. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n00.chunk.ts +4 -0
  385. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n15.chunk.ts +4 -0
  386. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n30.chunk.ts +4 -0
  387. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n45.chunk.ts +4 -0
  388. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n60.chunk.ts +4 -0
  389. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n75.chunk.ts +4 -0
  390. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090s15.chunk.ts +4 -0
  391. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n00.chunk.ts +4 -0
  392. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n15.chunk.ts +4 -0
  393. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n30.chunk.ts +4 -0
  394. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n45.chunk.ts +4 -0
  395. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n60.chunk.ts +4 -0
  396. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n75.chunk.ts +4 -0
  397. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s15.chunk.ts +4 -0
  398. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s30.chunk.ts +4 -0
  399. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s45.chunk.ts +4 -0
  400. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n00.chunk.ts +4 -0
  401. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n15.chunk.ts +4 -0
  402. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n30.chunk.ts +4 -0
  403. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n45.chunk.ts +4 -0
  404. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n60.chunk.ts +4 -0
  405. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s15.chunk.ts +4 -0
  406. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s30.chunk.ts +4 -0
  407. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s45.chunk.ts +4 -0
  408. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n00.chunk.ts +4 -0
  409. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n15.chunk.ts +4 -0
  410. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n30.chunk.ts +4 -0
  411. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n45.chunk.ts +4 -0
  412. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n60.chunk.ts +4 -0
  413. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n75.chunk.ts +4 -0
  414. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s15.chunk.ts +4 -0
  415. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s30.chunk.ts +4 -0
  416. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s45.chunk.ts +4 -0
  417. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n00.chunk.ts +4 -0
  418. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n15.chunk.ts +4 -0
  419. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n45.chunk.ts +4 -0
  420. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n60.chunk.ts +4 -0
  421. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n75.chunk.ts +4 -0
  422. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s15.chunk.ts +4 -0
  423. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s30.chunk.ts +4 -0
  424. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s45.chunk.ts +4 -0
  425. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s60.chunk.ts +4 -0
  426. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n00.chunk.ts +4 -0
  427. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n15.chunk.ts +4 -0
  428. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n45.chunk.ts +4 -0
  429. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n60.chunk.ts +4 -0
  430. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s15.chunk.ts +4 -0
  431. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s30.chunk.ts +4 -0
  432. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s45.chunk.ts +4 -0
  433. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s60.chunk.ts +4 -0
  434. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n00.chunk.ts +4 -0
  435. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n15.chunk.ts +4 -0
  436. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n30.chunk.ts +4 -0
  437. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n45.chunk.ts +4 -0
  438. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n60.chunk.ts +4 -0
  439. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n75.chunk.ts +4 -0
  440. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s15.chunk.ts +4 -0
  441. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s30.chunk.ts +4 -0
  442. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s45.chunk.ts +4 -0
  443. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n00.chunk.ts +4 -0
  444. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n15.chunk.ts +4 -0
  445. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n30.chunk.ts +4 -0
  446. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n60.chunk.ts +4 -0
  447. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n75.chunk.ts +4 -0
  448. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s30.chunk.ts +4 -0
  449. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s60.chunk.ts +4 -0
  450. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n30.chunk.ts +4 -0
  451. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n45.chunk.ts +4 -0
  452. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n60.chunk.ts +4 -0
  453. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n75.chunk.ts +4 -0
  454. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s15.chunk.ts +4 -0
  455. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s30.chunk.ts +4 -0
  456. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s60.chunk.ts +4 -0
  457. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n00.chunk.ts +4 -0
  458. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n30.chunk.ts +4 -0
  459. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n45.chunk.ts +4 -0
  460. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n60.chunk.ts +4 -0
  461. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n75.chunk.ts +4 -0
  462. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s15.chunk.ts +4 -0
  463. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s30.chunk.ts +4 -0
  464. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s45.chunk.ts +4 -0
  465. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s60.chunk.ts +4 -0
  466. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n00.chunk.ts +4 -0
  467. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n15.chunk.ts +4 -0
  468. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n30.chunk.ts +4 -0
  469. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n45.chunk.ts +4 -0
  470. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n60.chunk.ts +4 -0
  471. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n75.chunk.ts +4 -0
  472. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s15.chunk.ts +4 -0
  473. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s30.chunk.ts +4 -0
  474. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s45.chunk.ts +4 -0
  475. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s60.chunk.ts +4 -0
  476. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n00.chunk.ts +4 -0
  477. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n15.chunk.ts +4 -0
  478. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n30.chunk.ts +4 -0
  479. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n45.chunk.ts +4 -0
  480. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n60.chunk.ts +4 -0
  481. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n75.chunk.ts +4 -0
  482. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s15.chunk.ts +4 -0
  483. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s30.chunk.ts +4 -0
  484. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s45.chunk.ts +4 -0
  485. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s60.chunk.ts +4 -0
  486. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n00.chunk.ts +4 -0
  487. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n15.chunk.ts +4 -0
  488. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n30.chunk.ts +4 -0
  489. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n45.chunk.ts +4 -0
  490. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n60.chunk.ts +4 -0
  491. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n75.chunk.ts +4 -0
  492. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105s15.chunk.ts +4 -0
  493. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n00.chunk.ts +4 -0
  494. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n15.chunk.ts +4 -0
  495. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n30.chunk.ts +4 -0
  496. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n45.chunk.ts +4 -0
  497. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n60.chunk.ts +4 -0
  498. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n75.chunk.ts +4 -0
  499. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120s30.chunk.ts +4 -0
  500. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n30.chunk.ts +4 -0
  501. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n45.chunk.ts +4 -0
  502. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n60.chunk.ts +4 -0
  503. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n75.chunk.ts +4 -0
  504. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135s30.chunk.ts +4 -0
  505. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n45.chunk.ts +4 -0
  506. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n60.chunk.ts +4 -0
  507. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s15.chunk.ts +4 -0
  508. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s30.chunk.ts +4 -0
  509. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n00.chunk.ts +4 -0
  510. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n15.chunk.ts +4 -0
  511. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n45.chunk.ts +4 -0
  512. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n60.chunk.ts +4 -0
  513. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s15.chunk.ts +4 -0
  514. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s30.chunk.ts +4 -0
  515. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n00.chunk.ts +4 -0
  516. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n15.chunk.ts +4 -0
  517. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n45.chunk.ts +4 -0
  518. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n60.chunk.ts +4 -0
  519. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s15.chunk.ts +4 -0
  520. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s30.chunk.ts +4 -0
  521. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s45.chunk.ts +4 -0
  522. package/src/plugins/builtin/world-venue-map/natural-earth/world-50m.chunk.ts +4 -0
  523. package/src/plugins/builtin/world-venue-map/pane.tsx +28 -43
  524. package/src/plugins/builtin/world-venue-map/use-geo.ts +55 -0
  525. package/src/plugins/builtin/world-venue-map/venue-header.tsx +32 -0
  526. package/src/plugins/builtin/yield-curve/headless.ts +16 -0
  527. package/src/plugins/loader.ts +13 -1
  528. package/src/plugins/registry/pane-settings.ts +1 -0
  529. package/src/renderers/dom/build-assets.ts +81 -3
  530. package/src/renderers/dom/data-table/row.tsx +2 -0
  531. package/src/renderers/dom/desktop/controls.tsx +7 -3
  532. package/src/renderers/dom/desktop/list-view.tsx +35 -10
  533. package/src/renderers/dom/desktop/popover.tsx +73 -14
  534. package/src/renderers/dom/host/input.tsx +14 -3
  535. package/src/renderers/dom/styles.css +6 -7
  536. package/src/renderers/electrobun/view/backend-rpc.ts +10 -4
  537. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -0
  538. package/src/renderers/electrobun/view/rpc-timeout.ts +14 -3
  539. package/src/renderers/opentui/host.tsx +5 -0
  540. package/src/sources/gloomberb-cloud/index.ts +25 -4
  541. package/src/sources/gloomberb-cloud/normalizers.ts +27 -3
  542. package/src/sources/provider-router/cached-routes.ts +4 -1
  543. package/src/sources/provider-router/financials.ts +8 -7
  544. package/src/sources/provider-router/history.ts +15 -10
  545. package/src/sources/sec-registrant.ts +75 -0
  546. package/src/state/app/context/index.tsx +24 -8
  547. package/src/state/hooks/quote-streaming.ts +41 -26
  548. package/src/sync/core-contributors.ts +4 -0
  549. package/src/telemetry/crash-reports.ts +4 -1
  550. package/src/test-support/cli-context.ts +10 -2
  551. package/src/theme/heat-colors.ts +266 -84
  552. package/src/time-series/resolve.ts +1 -1
  553. package/src/time-series/use-chart-resolution.ts +1 -1
  554. package/src/types/context-menu.ts +6 -0
  555. package/src/types/data-provider.ts +2 -0
  556. package/src/types/financials.ts +3 -0
  557. package/src/types/headless.ts +79 -2
  558. package/src/types/plugin.ts +20 -1
  559. package/src/types/ticker.ts +15 -0
  560. package/src/ui/host.tsx +14 -0
  561. package/src/updater/index.ts +9 -11
  562. package/src/utils/async-deadline.ts +21 -0
  563. package/src/utils/cli-output.ts +5 -1
  564. package/src/utils/crypto-pair.ts +41 -0
  565. package/src/utils/currency-units.ts +14 -0
  566. package/src/utils/depositary-receipt.ts +26 -0
  567. package/src/utils/exchanges.ts +72 -0
  568. package/src/utils/fire-and-forget.ts +24 -0
  569. package/src/utils/format.ts +13 -0
  570. package/src/utils/price-earnings.ts +70 -3
  571. package/src/utils/price-history.ts +58 -13
  572. package/src/utils/rpc-timeout-error.ts +27 -0
  573. package/src/utils/sec.ts +13 -1
  574. package/src/utils/utc-time.ts +79 -0
@@ -78,6 +78,14 @@ export interface TextFieldProps {
78
78
  }) => void;
79
79
  }
80
80
 
81
+ /**
82
+ * Desktop and web: the `style` of a Textarea that sits in its own framed box (a
83
+ * prompt, a form's long field, a bio), so its text keeps off the frame as a
84
+ * TextField's does. The terminal's box border already leaves that cell; pass
85
+ * it only where `nativePaneChrome` is true.
86
+ */
87
+ export const FRAMED_TEXTAREA_DESKTOP_STYLE = { padding: "6px var(--cell-w)", lineHeight: "var(--cell-h)" } as const;
88
+
81
89
  const PASSWORD_MASK_CHAR = "*";
82
90
 
83
91
  function maskPassword(value: string): string {
@@ -7,6 +7,12 @@ export interface RangeTrackProps {
7
7
  /** Cells the track spans. */
8
8
  width: number;
9
9
  markerColor: string;
10
+ /**
11
+ * Mark a value past either end with an arrow at that end (a square on the
12
+ * desktop) instead of pinning the dot there, so it does not read as the low
13
+ * or the high itself.
14
+ */
15
+ outside?: boolean;
10
16
  }
11
17
 
12
18
  /**
@@ -14,9 +20,10 @@ export interface RangeTrackProps {
14
20
  * a rule with a dot on it. The caller draws the endpoints. The terminal draws
15
21
  * a box-drawing rule; the desktop draws a real line and dot, never glyphs.
16
22
  */
17
- export function RangeTrack({ position, width, markerColor }: RangeTrackProps) {
23
+ export function RangeTrack({ position, width, markerColor, outside = false }: RangeTrackProps) {
18
24
  const colors = useThemeColors();
19
25
  const isDesktopWeb = useUiCapabilities().nativePaneChrome === true;
26
+ const past = !outside ? null : position < 0 ? "below" : position > 1 ? "above" : null;
20
27
  const at = position > 0 ? Math.min(1, position) : 0;
21
28
  if (width <= 0) return null;
22
29
  if (isDesktopWeb) {
@@ -29,7 +36,7 @@ export function RangeTrack({ position, width, markerColor }: RangeTrackProps) {
29
36
  width: "8px",
30
37
  height: "8px",
31
38
  marginLeft: "-4px",
32
- borderRadius: "50%",
39
+ borderRadius: past ? "1px" : "50%",
33
40
  backgroundColor: markerColor,
34
41
  }} />
35
42
  </Box>
@@ -39,7 +46,7 @@ export function RangeTrack({ position, width, markerColor }: RangeTrackProps) {
39
46
  return (
40
47
  <Box width={width} height={1} flexShrink={0} flexDirection="row">
41
48
  <Text fg={colors.border}>{"─".repeat(marker)}</Text>
42
- <Text fg={markerColor}>●</Text>
49
+ <Text fg={markerColor}>{past === "below" ? "◂" : past === "above" ? "▸" : "●"}</Text>
43
50
  <Text fg={colors.border}>{"─".repeat(width - marker - 1)}</Text>
44
51
  </Box>
45
52
  );
@@ -1,7 +1,7 @@
1
1
  import type { ReactNode } from "react";
2
2
  import { t, tf } from "../../i18n";
3
3
  import { useThemeColors } from "../../theme/theme-context";
4
- import { Box, Text } from "../../ui";
4
+ import { Box, Text, useUiCapabilities } from "../../ui";
5
5
  import { Spinner } from "./loading";
6
6
  import { ButtonActionScope } from "./action-scope";
7
7
 
@@ -15,13 +15,19 @@ export interface EmptyStateProps {
15
15
 
16
16
  export function EmptyState({ title, message, hint, actions, status = "empty" }: EmptyStateProps) {
17
17
  const colors = useThemeColors();
18
+ const { nativePaneChrome } = useUiCapabilities();
18
19
  // Provider messages must wrap at narrow pane widths rather than lose their tail.
19
20
  return (
20
21
  <Box flexDirection="column" data-gloom-status={status} data-gloom-ui="empty-state">
21
22
  <Box><Text fg={status === "error" ? colors.negative : colors.textDim} wrapText>{t(title)}</Text></Box>
22
23
  {message && <Box><Text fg={colors.textMuted} wrapText>{t(message)}</Text></Box>}
23
24
  {hint && <Box><Text fg={colors.textMuted} wrapText>{t(hint)}</Text></Box>}
24
- {actions && <Box flexDirection="row" gap={1} marginTop={1}><ButtonActionScope>{actions}</ButtonActionScope></Box>}
25
+ {/* Wraps in a narrow pane rather than clipping the second action; a terminal wraps without a blank row. */}
26
+ {actions && (
27
+ <Box flexDirection="row" flexWrap="wrap" gap={1} rowGap={nativePaneChrome ? undefined : 0} marginTop={1}>
28
+ <ButtonActionScope>{actions}</ButtonActionScope>
29
+ </Box>
30
+ )}
25
31
  </Box>
26
32
  );
27
33
  }
@@ -1,5 +1,5 @@
1
1
  import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
2
- import { usePaneArrowClaim, usePaneMenuItems } from "../layout/pane/footer/registration";
2
+ import { usePaneMenuItems, usePaneTabStripKeys } from "../layout/pane/footer/registration";
3
3
  import type { ContextMenuItem } from "../../types/context-menu";
4
4
  import { useShortcut } from "../../react/input";
5
5
  import { Box, ScrollBox, Text, useNativeRenderer, useUiHost } from "../../ui";
@@ -70,12 +70,16 @@ export function Tabs({
70
70
  onAdd,
71
71
  onReorder,
72
72
  onDrag,
73
- focused = false,
73
+ focused: paneFocused = false,
74
74
  keyboardNavigation = true,
75
75
  paneMenu = true,
76
76
  scrollable = true,
77
77
  scrollId,
78
78
  }: TabsProps) {
79
+ // While a chart the user moved into holds the keys, the strip draws as
80
+ // unfocused, so it shows where the arrows went.
81
+ const stripKeys = usePaneTabStripKeys(keyboardNavigation, paneFocused);
82
+ const focused = paneFocused && !stripKeys.held;
79
83
  const language = useAppLanguage();
80
84
  const colors = useThemeColors();
81
85
  const tabs = useMemo(() => rawTabs.map((tab) => (
@@ -216,8 +220,7 @@ export function Tabs({
216
220
  event.stopPropagation();
217
221
  selectAdjacentTab(1);
218
222
  }
219
- }, { enabled: focused && keyboardNavigation });
220
- usePaneArrowClaim(focused && keyboardNavigation);
223
+ }, { enabled: stripKeys.active });
221
224
 
222
225
  if (NativeTabs) {
223
226
  return (
@@ -1,10 +1,10 @@
1
1
  import { useEffect, useRef, useState } from "react";
2
- import { Box, Text, Textarea, type InputRenderable, type TextareaRenderable } from "../../ui";
2
+ import { Box, Text, Textarea, useUiCapabilities, type InputRenderable, type TextareaRenderable } from "../../ui";
3
3
  import { t } from "../../i18n";
4
4
  import { useDialogKeyboard, type PromptContext } from "../../ui/dialog";
5
5
  import { useThemeColors } from "../../theme/theme-context";
6
6
  import { Button } from "./button";
7
- import { TextField } from "./fields";
7
+ import { FRAMED_TEXTAREA_DESKTOP_STYLE, TextField } from "./fields";
8
8
  import { DialogFrame } from "./frame";
9
9
 
10
10
  export interface TextPromptDialogProps extends PromptContext<string> {
@@ -48,6 +48,7 @@ export function TextPromptDialog({
48
48
  footer,
49
49
  }: TextPromptDialogProps) {
50
50
  const colors = useThemeColors();
51
+ const { nativePaneChrome } = useUiCapabilities();
51
52
  const inputRef = useRef<InputRenderable | null>(null);
52
53
  const textareaRef = useRef<TextareaRenderable | null>(null);
53
54
  const [value, setValue] = useState(initialValue);
@@ -97,6 +98,7 @@ export function TextPromptDialog({
97
98
  backgroundColor={colors.panel}
98
99
  flexGrow={1}
99
100
  wrapText
101
+ {...(nativePaneChrome ? { style: FRAMED_TEXTAREA_DESKTOP_STYLE } : {})}
100
102
  keyBindings={[
101
103
  { name: "return", action: "submit" },
102
104
  { name: "linefeed", action: "submit" },
@@ -311,7 +311,23 @@ function sanitizePortfolios(value: unknown, fallback: Portfolio[]): Portfolio[]
311
311
  && typeof (entry as Portfolio).name === "string"
312
312
  && typeof (entry as Portfolio).currency === "string",
313
313
  )
314
- .map((entry) => ({ ...entry }));
314
+ .map(sanitizePortfolioAllocation);
315
+ }
316
+
317
+ /** Cash and target weights the portfolio commands write; anything else is dropped. */
318
+ function sanitizePortfolioAllocation(entry: Portfolio): Portfolio {
319
+ const { cash, targetWeights, ...portfolio } = entry;
320
+ const next: Portfolio = { ...portfolio };
321
+ if (isRecord(cash) && typeof cash.amount === "number" && Number.isFinite(cash.amount)
322
+ && typeof cash.currency === "string" && /^[A-Z]{3}$/.test(cash.currency)) {
323
+ next.cash = { amount: cash.amount, currency: cash.currency };
324
+ }
325
+ if (isRecord(targetWeights)) {
326
+ const weights = Object.entries(targetWeights).filter((weight): weight is [string, number] =>
327
+ !!weight[0].trim() && typeof weight[1] === "number" && Number.isFinite(weight[1]) && weight[1] >= 0 && weight[1] <= 100);
328
+ if (weights.length > 0) next.targetWeights = Object.fromEntries(weights);
329
+ }
330
+ return next;
315
331
  }
316
332
 
317
333
  function sanitizeWatchlists(value: unknown, fallback: Watchlist[]): Watchlist[] {
package/src/i18n/es.ts CHANGED
@@ -823,7 +823,22 @@ export const es: Record<string, string> = {
823
823
  "Recent users": "Usuarios recientes",
824
824
  "No recent users": "Sin usuarios recientes",
825
825
  "Enter at least one @username.": "Ingresa al menos un @username.",
826
- "Could not start conversation.": "No se pudo iniciar la conversación.",
826
+ "@{username} only takes DMs from people they have written to first.": "@{username} solo acepta DM de personas a las que escribió primero.",
827
+ "@{username} does not take DMs.": "@{username} no acepta DM.",
828
+ "@{username} was not found.": "No se encontró a @{username}.",
829
+ "{usernames} were not found.": "No se encontró a {usernames}.",
830
+ "One of those users was not found.": "No se encontró a uno de esos usuarios.",
831
+ "Someone in that group only takes DMs from people they have written to first.": "Alguien de ese grupo solo acepta DM de personas a las que escribió primero.",
832
+ "Could not start the conversation (error {status}).": "No se pudo iniciar la conversación (error {status}).",
833
+ "Could not start the conversation. Check your connection and try again.": "No se pudo iniciar la conversación. Revisa tu conexión e inténtalo de nuevo.",
834
+ "no DMs": "sin DM",
835
+ "Only takes DMs from people they have written to first.": "Solo acepta DM de personas a las que escribió primero.",
836
+ "Does not take DMs.": "No acepta DM.",
837
+ "Message @{username}": "Enviar mensaje a @{username}",
838
+ "Open DM with @{username}": "Abrir DM con @{username}",
839
+ "Message": "Mensaje",
840
+ "Open DM": "Abrir DM",
841
+ "Show Profile": "Ver perfil",
827
842
  "Starting": "Iniciando",
828
843
  "Start": "Iniciar",
829
844
 
@@ -960,6 +975,9 @@ export const es: Record<string, string> = {
960
975
  "Minimize": "Minimizar",
961
976
  "Maximize": "Maximizar",
962
977
  "Pane unavailable.": "Panel no disponible.",
978
+ "{title} stopped working.": "{title} dejó de funcionar.",
979
+ "Reload pane": "Recargar panel",
980
+ "Close pane": "Cerrar panel",
963
981
  "Cash & Margin": "Efectivo y margen",
964
982
  "No non-zero cash balances.": "Sin saldos de efectivo distintos de cero.",
965
983
  "adding...": "agregando...",
package/src/i18n/ja.ts CHANGED
@@ -837,7 +837,22 @@ export const ja: Record<string, string> = {
837
837
  "Recent users": "最近のユーザー",
838
838
  "No recent users": "最近のユーザーはいません",
839
839
  "Enter at least one @username.": "@username を 1 件以上入力してください。",
840
- "Could not start conversation.": "会話を開始できませんでした。",
840
+ "@{username} only takes DMs from people they have written to first.": "@{username} は自分から先にメッセージを送った相手の DM だけを受け付けています。",
841
+ "@{username} does not take DMs.": "@{username} は DM を受け付けていません。",
842
+ "@{username} was not found.": "@{username} が見つかりません。",
843
+ "{usernames} were not found.": "{usernames} が見つかりません。",
844
+ "One of those users was not found.": "見つからないユーザーがいます。",
845
+ "Someone in that group only takes DMs from people they have written to first.": "このグループには、自分から先にメッセージを送った相手の DM だけを受け付けている人がいます。",
846
+ "Could not start the conversation (error {status}).": "会話を開始できませんでした (エラー {status})。",
847
+ "Could not start the conversation. Check your connection and try again.": "会話を開始できませんでした。接続を確認して、もう一度お試しください。",
848
+ "no DMs": "DM 不可",
849
+ "Only takes DMs from people they have written to first.": "自分から先にメッセージを送った相手の DM だけを受け付けています。",
850
+ "Does not take DMs.": "DM を受け付けていません。",
851
+ "Message @{username}": "@{username} にメッセージ",
852
+ "Open DM with @{username}": "@{username} との DM を開く",
853
+ "Message": "メッセージ",
854
+ "Open DM": "DM を開く",
855
+ "Show Profile": "プロフィールを表示",
841
856
  "Starting": "開始中",
842
857
  "Start": "開始",
843
858
 
@@ -974,6 +989,9 @@ export const ja: Record<string, string> = {
974
989
  "Minimize": "最小化",
975
990
  "Maximize": "最大化",
976
991
  "Pane unavailable.": "ペインが使用できません。",
992
+ "{title} stopped working.": "{title} が動作を停止しました。",
993
+ "Reload pane": "ペインを再読み込み",
994
+ "Close pane": "ペインを閉じる",
977
995
  "Cash & Margin": "現金・証拠金",
978
996
  "No non-zero cash balances.": "現金残高はありません。",
979
997
  "adding...": "追加中...",
package/src/i18n/ko.ts CHANGED
@@ -835,7 +835,22 @@ export const ko: Record<string, string> = {
835
835
  "Recent users": "최근 사용자",
836
836
  "No recent users": "최근 사용자 없음",
837
837
  "Enter at least one @username.": "@username을 하나 이상 입력하세요.",
838
- "Could not start conversation.": "대화를 시작할 수 없습니다.",
838
+ "@{username} only takes DMs from people they have written to first.": "@{username}님은 먼저 메시지를 보낸 상대의 DM만 받습니다.",
839
+ "@{username} does not take DMs.": "@{username}님은 DM을 받지 않습니다.",
840
+ "@{username} was not found.": "@{username}님을 찾을 수 없습니다.",
841
+ "{usernames} were not found.": "{usernames}님을 찾을 수 없습니다.",
842
+ "One of those users was not found.": "찾을 수 없는 사용자가 있습니다.",
843
+ "Someone in that group only takes DMs from people they have written to first.": "이 그룹에 먼저 메시지를 보낸 상대의 DM만 받는 사람이 있습니다.",
844
+ "Could not start the conversation (error {status}).": "대화를 시작할 수 없습니다 (오류 {status}).",
845
+ "Could not start the conversation. Check your connection and try again.": "대화를 시작할 수 없습니다. 연결을 확인하고 다시 시도하세요.",
846
+ "no DMs": "DM 불가",
847
+ "Only takes DMs from people they have written to first.": "먼저 메시지를 보낸 상대의 DM만 받습니다.",
848
+ "Does not take DMs.": "DM을 받지 않습니다.",
849
+ "Message @{username}": "@{username}님에게 메시지",
850
+ "Open DM with @{username}": "@{username}님과의 DM 열기",
851
+ "Message": "메시지",
852
+ "Open DM": "DM 열기",
853
+ "Show Profile": "프로필 보기",
839
854
  "Starting": "시작 중",
840
855
  "Start": "시작",
841
856
 
@@ -972,6 +987,9 @@ export const ko: Record<string, string> = {
972
987
  "Minimize": "최소화",
973
988
  "Maximize": "최대화",
974
989
  "Pane unavailable.": "패널을 사용할 수 없습니다.",
990
+ "{title} stopped working.": "{title}이(가) 작동을 멈췄습니다.",
991
+ "Reload pane": "패널 다시 불러오기",
992
+ "Close pane": "패널 닫기",
975
993
  "Cash & Margin": "현금·증거금",
976
994
  "No non-zero cash balances.": "표시할 현금 잔액이 없습니다.",
977
995
  "adding...": "추가 중...",
package/src/i18n/zh-cn.ts CHANGED
@@ -837,7 +837,22 @@ export const zhCN: Record<string, string> = {
837
837
  "Recent users": "最近联系人",
838
838
  "No recent users": "没有最近联系人",
839
839
  "Enter at least one @username.": "至少输入一个 @用户名。",
840
- "Could not start conversation.": "无法开始会话。",
840
+ "@{username} only takes DMs from people they have written to first.": "@{username} 只接收自己先联系过的人的私信。",
841
+ "@{username} does not take DMs.": "@{username} 不接收私信。",
842
+ "@{username} was not found.": "找不到 @{username}。",
843
+ "{usernames} were not found.": "找不到 {usernames}。",
844
+ "One of those users was not found.": "其中有用户不存在。",
845
+ "Someone in that group only takes DMs from people they have written to first.": "该群组中有人只接收自己先联系过的人的私信。",
846
+ "Could not start the conversation (error {status}).": "无法开始会话(错误 {status})。",
847
+ "Could not start the conversation. Check your connection and try again.": "无法开始会话。请检查网络连接后重试。",
848
+ "no DMs": "不收私信",
849
+ "Only takes DMs from people they have written to first.": "只接收自己先联系过的人的私信。",
850
+ "Does not take DMs.": "不接收私信。",
851
+ "Message @{username}": "私信 @{username}",
852
+ "Open DM with @{username}": "打开与 @{username} 的私信",
853
+ "Message": "发私信",
854
+ "Open DM": "打开私信",
855
+ "Show Profile": "查看资料",
841
856
  "Starting": "正在开始",
842
857
  "Start": "开始",
843
858
 
@@ -974,6 +989,9 @@ export const zhCN: Record<string, string> = {
974
989
  "Minimize": "最小化",
975
990
  "Maximize": "最大化",
976
991
  "Pane unavailable.": "面板不可用。",
992
+ "{title} stopped working.": "{title} 已停止工作。",
993
+ "Reload pane": "重新加载面板",
994
+ "Close pane": "关闭面板",
977
995
  "Cash & Margin": "现金与保证金",
978
996
  "No non-zero cash balances.": "没有非零现金余额。",
979
997
  "adding...": "添加中...",
package/src/i18n/zh-tw.ts CHANGED
@@ -837,7 +837,22 @@ export const zhTW: Record<string, string> = {
837
837
  "Recent users": "最近聯絡人",
838
838
  "No recent users": "沒有最近聯絡人",
839
839
  "Enter at least one @username.": "至少輸入一個 @username。",
840
- "Could not start conversation.": "無法開始對話。",
840
+ "@{username} only takes DMs from people they have written to first.": "@{username} 只接收自己先聯絡過的人的私訊。",
841
+ "@{username} does not take DMs.": "@{username} 不接收私訊。",
842
+ "@{username} was not found.": "找不到 @{username}。",
843
+ "{usernames} were not found.": "找不到 {usernames}。",
844
+ "One of those users was not found.": "其中有使用者不存在。",
845
+ "Someone in that group only takes DMs from people they have written to first.": "該群組中有人只接收自己先聯絡過的人的私訊。",
846
+ "Could not start the conversation (error {status}).": "無法開始對話(錯誤 {status})。",
847
+ "Could not start the conversation. Check your connection and try again.": "無法開始對話。請檢查網路連線後再試一次。",
848
+ "no DMs": "不收私訊",
849
+ "Only takes DMs from people they have written to first.": "只接收自己先聯絡過的人的私訊。",
850
+ "Does not take DMs.": "不接收私訊。",
851
+ "Message @{username}": "私訊 @{username}",
852
+ "Open DM with @{username}": "開啟與 @{username} 的私訊",
853
+ "Message": "傳私訊",
854
+ "Open DM": "開啟私訊",
855
+ "Show Profile": "查看個人資料",
841
856
  "Starting": "正在開始",
842
857
  "Start": "開始",
843
858
 
@@ -974,6 +989,9 @@ export const zhTW: Record<string, string> = {
974
989
  "Minimize": "最小化",
975
990
  "Maximize": "最大化",
976
991
  "Pane unavailable.": "面板不可用。",
992
+ "{title} stopped working.": "{title} 已停止運作。",
993
+ "Reload pane": "重新載入面板",
994
+ "Close pane": "關閉面板",
977
995
  "Cash & Margin": "現金與保證金",
978
996
  "No non-zero cash balances.": "沒有非零的現金餘額。",
979
997
  "adding...": "新增中...",
@@ -57,7 +57,7 @@ export function normalizeFreshChartData(
57
57
  interval: interval ?? undefined }) ?? undefined;
58
58
  if (
59
59
  isIntradayChartRequest(request, history)
60
- && isPriceHistoryStaleForCurrentWindow(normalized, Date.now(), { exchange: target.exchange,
60
+ && isPriceHistoryStaleForCurrentWindow(normalized, Date.now(), { symbol: target.symbol, exchange: target.exchange,
61
61
  intervalMs: interval ? priceHistoryIntervalMs(interval) : undefined, session })
62
62
  ) {
63
63
  return [];
@@ -113,13 +113,24 @@ export async function loadQuoteEntry({
113
113
  const attempts = [createAttempt(source, startedAt, "success")];
114
114
  return quoteStore.update(key, (current) => readyQuoteEntry(current, resolvedQuote, source, attempts, { keepNewerHeldQuote: true }));
115
115
  } catch (error) {
116
- const classified = classifyError(error);
117
- const attempt = createAttempt(dataProvider.id, startedAt, EXPECTED_EMPTY.test(classified.message) ? "empty" : "fatal_error", classified.reasonCode, classified.message);
118
- return quoteStore.update(key, (current) => errorEntry(current, attempt));
116
+ return storeQuoteFailure(quoteStore, key, dataProvider.id, startedAt, error);
119
117
  }
120
118
  });
121
119
  }
122
120
 
121
+ function storeQuoteFailure(
122
+ quoteStore: QueryStore<Quote>,
123
+ key: string,
124
+ providerId: string,
125
+ startedAt: number,
126
+ error: unknown,
127
+ ): QueryEntry<Quote> {
128
+ const classified = classifyError(error);
129
+ const status = EXPECTED_EMPTY.test(classified.message) ? "empty" : "fatal_error";
130
+ const attempt = createAttempt(providerId, startedAt, status, classified.reasonCode, classified.message);
131
+ return quoteStore.update(key, (current) => errorEntry(current, attempt));
132
+ }
133
+
123
134
  interface LoadQuoteBatchEntriesOptions {
124
135
  dataProvider: DataProvider;
125
136
  instruments: InstrumentRef[];
@@ -152,14 +163,22 @@ export async function loadQuoteBatchEntries({
152
163
  }
153
164
 
154
165
  if (misses.length > 0 && dataProvider.getQuotesBatch) {
166
+ const startedAt = Date.now();
155
167
  const batchResults = await dataProvider.getQuotesBatch(
156
168
  misses.map((instrument) => quoteTargetFromInstrument(instrument)),
157
169
  { forceRefresh: options.forceRefresh },
158
170
  );
159
171
  batchResults.forEach((item, index) => {
160
172
  const instrument = misses[index];
161
- if (!instrument || !item.quote) return;
173
+ if (!instrument) return;
162
174
  const key = buildQuoteKey(instrument);
175
+ if (!item.quote) {
176
+ // The batch already asked for this one, one by one where the batch
177
+ // answer fell short, and says why it failed. Asking again here would
178
+ // only send the same request a second time.
179
+ if (item.error != null) results.set(key, storeQuoteFailure(quoteStore, key, dataProvider.id, startedAt, item.error));
180
+ return;
181
+ }
163
182
  const quote = resolveQuote?.(instrument, item.quote) ?? item.quote;
164
183
  const source = quote.providerId ?? dataProvider.id;
165
184
  const attempts = [createAttempt(source, Date.now(), "success")];
@@ -0,0 +1,72 @@
1
+ import type { CloudMarketResponse, CloudPricePointPayload } from "../api-client/types";
2
+ import type { FxLeg } from "./coordinator/fx-legs";
3
+
4
+ /** USD per unit of one currency at each completed daily FX close, by date. */
5
+ export interface FxCloses {
6
+ currency: string;
7
+ closes: ReadonlyMap<string, number>;
8
+ first: string;
9
+ asOf: string;
10
+ }
11
+
12
+ /** Why a daily FX history cannot be used: no usable closes, or none in the last week. */
13
+ export class FxHistoryError extends Error {
14
+ constructor(readonly problem: "unavailable" | "stale") {
15
+ super(problem === "stale" ? "Daily FX history is stale" : "Daily FX history unavailable");
16
+ this.name = "FxHistoryError";
17
+ }
18
+ }
19
+
20
+ const DAY_MS = 86_400_000;
21
+ /** A pair's latest completed close older than this is stale; a weekend and a holiday fit well inside it. */
22
+ const FX_MAX_AGE_DAYS = 7;
23
+
24
+ const isDay = (value: string) => /^\d{4}-\d{2}-\d{2}$/.test(value) && Number.isFinite(Date.parse(value))
25
+ && new Date(value).toISOString().slice(0, 10) === value;
26
+
27
+ /**
28
+ * Completed daily closes of the pair as USD per unit, today's still-trading
29
+ * bar left out. A malformed row, two different closes on one date, or more
30
+ * rows than asked for rejects the whole history rather than a part of it.
31
+ */
32
+ export function dailyFxCloses(
33
+ response: CloudMarketResponse<CloudPricePointPayload[]>,
34
+ leg: FxLeg,
35
+ now = new Date(),
36
+ maxRows = 1500,
37
+ ): FxCloses {
38
+ if (response.status !== "success" || !Array.isArray(response.data) || response.data.length < 2) throw new FxHistoryError("unavailable");
39
+ if (response.stale || response.providerMeta?.stale) throw new FxHistoryError("stale");
40
+ if (response.data.length > maxRows) throw new FxHistoryError("unavailable");
41
+ const today = now.toISOString().slice(0, 10);
42
+ const closes = new Map<string, number>();
43
+ for (const row of response.data) {
44
+ if (!row || typeof row.date !== "string" || !Number.isFinite(Date.parse(row.date)) || !isDay(row.date.slice(0, 10))
45
+ || !Number.isFinite(row.close) || row.close <= 0) throw new FxHistoryError("unavailable");
46
+ const date = new Date(row.date).toISOString().slice(0, 10);
47
+ if (date >= today) continue;
48
+ const rate = leg.invert ? 1 / row.close : row.close;
49
+ const previous = closes.get(date);
50
+ if (previous != null && previous !== rate) throw new FxHistoryError("unavailable");
51
+ closes.set(date, rate);
52
+ }
53
+ const dates = [...closes.keys()].sort();
54
+ const asOf = dates.at(-1);
55
+ if (!asOf || Date.parse(today) - Date.parse(asOf) > FX_MAX_AGE_DAYS * DAY_MS) throw new FxHistoryError("stale");
56
+ return { currency: leg.currency, closes, first: dates[0]!, asOf };
57
+ }
58
+
59
+ /**
60
+ * The last completed close on or before `date` (YYYY-MM-DD), looking back at
61
+ * most a week so a weekend or holiday finds the close before it but a gap in
62
+ * the history is not bridged.
63
+ */
64
+ export function fxCloseOnOrBefore(fx: FxCloses, date: string): { date: string; rate: number } | null {
65
+ const end = Date.parse(`${(date < fx.asOf ? date : fx.asOf)}T00:00:00Z`);
66
+ for (let back = 0; back <= FX_MAX_AGE_DAYS; back++) {
67
+ const day = new Date(end - back * DAY_MS).toISOString().slice(0, 10);
68
+ const rate = fx.closes.get(day);
69
+ if (rate != null) return { date: day, rate };
70
+ }
71
+ return null;
72
+ }
@@ -1,5 +1,6 @@
1
1
  import type { MarketState } from "../../types/financials";
2
2
  import { canonicalExchange, EXCHANGE_TIME_ZONES, isUsListingExchange } from "../../utils/exchanges";
3
+ import { hasPublishedApacCalendar, isPublishedApacClosure } from "../published-apac-sessions";
3
4
  import { hasPublishedCnCalendar, isPublishedCnClosure } from "../published-cn-sessions";
4
5
  import { hasPublishedJpxCalendar, isPublishedJpxClosure } from "../published-jpx-sessions";
5
6
  import { hasPublishedNseCalendar, isPublishedNseClosure } from "../published-nse-sessions";
@@ -11,6 +12,9 @@ const ALWAYS_OPEN_EXCHANGES = new Set(["CCC"]);
11
12
  const MS_PER_DAY = 24 * 60 * 60 * 1000;
12
13
  const OVERNIGHT_CLOSE_MAX_AGE_MS = 20 * 60 * 60 * 1000;
13
14
  const ALWAYS_OPEN_MAX_AGE_MS = 2 * 60 * 60 * 1000;
15
+ // Spring Festival can put the previous session twelve calendar days back
16
+ // (TWSE, February 2026).
17
+ const SESSION_LOOKBACK_DAYS = 14;
14
18
  const REGULAR_OPEN_MINUTES: Record<string, number> = {
15
19
  NASDAQ: 9 * 60 + 30,
16
20
  NYSE: 9 * 60 + 30,
@@ -72,6 +76,28 @@ const REGULAR_CLOSE_MINUTES: Record<string, number> = {
72
76
  NZX: 17 * 60, SSE: 15 * 60 + 30, SZSE: 15 * 60 + 30,
73
77
  BMV: 15 * 60 + 10, B3: 18 * 60 + 30, BYMA: 17 * 60 + 10, JSE: 17 * 60 + 15, TASE: 17 * 60 + 40,
74
78
  };
79
+ // Venues that pause at midday, as local minutes [start, end), with a zone for
80
+ // those the session tables above do not cover. Jakarta pauses longer on Fridays.
81
+ const MIDDAY_BREAKS: Record<string, {
82
+ timeZone?: string;
83
+ weekdays: readonly [number, number];
84
+ friday?: readonly [number, number];
85
+ }> = {
86
+ HKEX: { weekdays: [12 * 60, 13 * 60] },
87
+ SGX: { weekdays: [12 * 60, 13 * 60] },
88
+ SSE: { weekdays: [11 * 60 + 30, 13 * 60] },
89
+ SZSE: { weekdays: [11 * 60 + 30, 13 * 60] },
90
+ JPX: { weekdays: [11 * 60 + 30, 12 * 60 + 30] },
91
+ BURSAMY: { timeZone: "Asia/Kuala_Lumpur", weekdays: [12 * 60 + 30, 14 * 60 + 30] },
92
+ JAKARTA: { timeZone: "Asia/Jakarta", weekdays: [12 * 60, 13 * 60 + 30], friday: [11 * 60 + 30, 14 * 60] },
93
+ };
94
+ // A morning print this close to the break is its last one, and afternoon
95
+ // prints are due this long after the feed's lag has passed the reopening.
96
+ const MIDDAY_BREAK_MARGIN_MS = 5 * 60_000;
97
+ // How far the delayed feed runs behind each venue's trades.
98
+ const DELAYED_FEED_LAG_MS = 15 * 60_000;
99
+ const SLOWER_DELAYED_FEED_LAG_MS = 20 * 60_000;
100
+ const SLOWER_DELAYED_FEEDS = new Set(["ASX", "KRX", "KOSDAQ", "TWSE", "TPEX", "SGX", "NZX"]);
75
101
  const exchangeLocalTimeFormatters = new Map<string, Intl.DateTimeFormat>();
76
102
  const usSessionFormatter = new Intl.DateTimeFormat("en-US", {
77
103
  timeZone: "America/New_York",
@@ -159,6 +185,7 @@ function isPublishedClosure(exchange: string, date: string): boolean {
159
185
  if (exchange === "JPX") return isPublishedJpxClosure(date);
160
186
  if (exchange === "NSE" || exchange === "BSE") return isPublishedNseClosure(date);
161
187
  if (exchange === "SSE" || exchange === "SZSE") return isPublishedCnClosure(date);
188
+ if (isPublishedApacClosure(exchange, date)) return true;
162
189
  return getPublishedUsEquityCalendarDay(exchange, date) === "closed";
163
190
  }
164
191
 
@@ -207,9 +234,7 @@ export function latestRegularSessionClose(
207
234
  if (!timeZone || ALWAYS_OPEN_EXCHANGES.has(canonical) || !Number.isFinite(time)) return null;
208
235
  const { year, month, day } = zonedDateTimeParts(time, timeZone);
209
236
  const today = Date.UTC(year, month - 1, day) / MS_PER_DAY;
210
- // Spring Festival can put the previous session eleven calendar days back.
211
- const lookback = (canonical === "SSE" || canonical === "SZSE") && hasPublishedCnCalendar(year) ? 14 : 10;
212
- for (let offset = 0; offset <= lookback; offset++) {
237
+ for (let offset = 0; offset <= SESSION_LOOKBACK_DAYS; offset++) {
213
238
  const date = new Date((today - offset) * MS_PER_DAY).toISOString().slice(0, 10);
214
239
  const published = getPublishedUsEquitySession(canonical, date);
215
240
  let close: number | null = null;
@@ -225,6 +250,20 @@ export function latestRegularSessionClose(
225
250
  return null;
226
251
  }
227
252
 
253
+ /**
254
+ * The UTC minute of the day the venue's regular session closed, for the latest
255
+ * session at or before `time` (20:00 UTC is 1200 while New York is on daylight
256
+ * time). Null for round-the-clock venues and for venues whose close hour is
257
+ * not listed, which `latestRegularSessionClose` would take to close at local
258
+ * midnight.
259
+ */
260
+ export function regularSessionCloseUtcMinute(exchange: string | undefined, time: number): number | null {
261
+ const canonical = canonicalExchange(exchange);
262
+ if (REGULAR_CLOSE_MINUTES[canonical] === undefined) return null;
263
+ const close = latestRegularSessionClose(canonical, time);
264
+ return close ? Math.floor(close.close / 60_000) % 1440 : null;
265
+ }
266
+
228
267
  /**
229
268
  * The open of the regular session `time` falls in, or of the latest one
230
269
  * before it: the published calendar for US venues, otherwise the venue's
@@ -238,8 +277,7 @@ export function latestRegularSessionOpen(exchange: string | undefined, time: num
238
277
  const minutes = REGULAR_OPEN_MINUTES[canonical];
239
278
  const { year, month, day } = zonedDateTimeParts(time, timeZone);
240
279
  const today = Date.UTC(year, month - 1, day) / MS_PER_DAY;
241
- const lookback = (canonical === "SSE" || canonical === "SZSE") && hasPublishedCnCalendar(year) ? 14 : 10;
242
- for (let offset = 0; offset <= lookback; offset++) {
280
+ for (let offset = 0; offset <= SESSION_LOOKBACK_DAYS; offset++) {
243
281
  const date = new Date((today - offset) * MS_PER_DAY).toISOString().slice(0, 10);
244
282
  const published = getPublishedUsEquitySession(canonical, date);
245
283
  let open: number | null = null;
@@ -270,7 +308,8 @@ export function isRegularSessionTime(exchange: string | undefined, time: number)
270
308
 
271
309
  /**
272
310
  * True when the venue's full-day closures for the year of `date` are
273
- * published: US venues, JPX, NSE, BSE, SSE and SZSE. Elsewhere a local holiday reads as a weekday.
311
+ * published: US venues, JPX, NSE, BSE, SSE, SZSE, KRX, KOSDAQ, TWSE, TPEX,
312
+ * HKEX, SGX and ASX. Elsewhere a local holiday reads as a weekday.
274
313
  */
275
314
  export function hasPublishedSessionCalendar(exchange: string | undefined, date: string): boolean {
276
315
  const canonical = canonicalExchange(exchange);
@@ -278,9 +317,42 @@ export function hasPublishedSessionCalendar(exchange: string | undefined, date:
278
317
  if (canonical === "JPX") return hasPublishedJpxCalendar(year);
279
318
  if (canonical === "NSE" || canonical === "BSE") return hasPublishedNseCalendar(year);
280
319
  if (canonical === "SSE" || canonical === "SZSE") return hasPublishedCnCalendar(year);
320
+ if (hasPublishedApacCalendar(canonical, year)) return true;
281
321
  return !!getPublishedUsEquityCalendarYears(canonical)?.includes(year);
282
322
  }
283
323
 
324
+ /** How far the delayed feed runs behind the venue's trades. */
325
+ export function delayedFeedLagMs(exchange: string | undefined): number {
326
+ return SLOWER_DELAYED_FEEDS.has(canonicalExchange(exchange)) ? SLOWER_DELAYED_FEED_LAG_MS : DELAYED_FEED_LAG_MS;
327
+ }
328
+
329
+ /**
330
+ * Whether a print is still the current price because the venue is paused at
331
+ * midday: `now` falls in the break, or within the feed's lag plus five
332
+ * minutes after it, and the print is the morning's last, no earlier than five
333
+ * minutes before the break started. An earlier morning print means the feed
334
+ * stopped before the break.
335
+ */
336
+ export function isMiddayBreakPrint(
337
+ timestampMs: number,
338
+ exchange: string | undefined,
339
+ now: number,
340
+ feedLagMs: number,
341
+ ): boolean {
342
+ const canonical = canonicalExchange(exchange);
343
+ const pause = MIDDAY_BREAKS[canonical];
344
+ const timeZone = pause?.timeZone ?? EXCHANGE_TIME_ZONES[canonical];
345
+ if (!pause || !timeZone || !Number.isFinite(timestampMs) || !Number.isFinite(now) || timestampMs > now) return false;
346
+ const { year, month, day } = zonedDateTimeParts(now, timeZone);
347
+ const weekday = new Date(Date.UTC(year, month - 1, day)).getUTCDay();
348
+ if (weekday === 0 || weekday === 6) return false;
349
+ const [start, end] = weekday === 5 && pause.friday ? pause.friday : pause.weekdays;
350
+ const at = (minutes: number) => zonedWallClockToUtcMs(timeZone, year, month, day, Math.floor(minutes / 60), minutes % 60, 0);
351
+ const breakStart = at(start);
352
+ return now >= breakStart && now <= at(end) + feedLagMs + MIDDAY_BREAK_MARGIN_MS
353
+ && timestampMs >= breakStart - MIDDAY_BREAK_MARGIN_MS;
354
+ }
355
+
284
356
  function usSessionState(timestampMs: number): UsSessionState {
285
357
  const minute = Math.floor(timestampMs / 60_000);
286
358
  return perMinute(usSessionCache, minute, () => formatUsSessionState(minute * 60_000));