gloomberb 0.16.0 → 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 (691) 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 +44 -11
  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 +5 -0
  8. package/src/api-client/market-discovery.ts +10 -0
  9. package/src/api-client/members.ts +26 -0
  10. package/src/api-client/paths.ts +30 -1
  11. package/src/api-client/perps.ts +41 -0
  12. package/src/api-client/request.ts +5 -2
  13. package/src/api-client/types.ts +9 -1
  14. package/src/app/global-shortcuts.ts +14 -6
  15. package/src/app/keybindings/actions.ts +1 -1
  16. package/src/app/runtime/update.ts +38 -2
  17. package/src/app.tsx +10 -6
  18. package/src/brokers/account-cache.ts +24 -0
  19. package/src/cli/commands/command-utils.ts +8 -0
  20. package/src/cli/commands/crypto-hints.ts +75 -0
  21. package/src/cli/commands/market.ts +414 -106
  22. package/src/cli/commands/overview.ts +77 -32
  23. package/src/cli/commands/ticker.ts +101 -43
  24. package/src/cli/desktop-pane-shot.ts +34 -4
  25. package/src/cli/freshness.ts +61 -0
  26. package/src/cli/help.ts +1 -0
  27. package/src/cli/helpers.ts +4 -8
  28. package/src/cli/index.ts +12 -2
  29. package/src/cli/listing-arg.ts +321 -0
  30. package/src/cli/pane-functions/access-gate.ts +42 -0
  31. package/src/cli/pane-functions/catalog.ts +28 -1
  32. package/src/cli/pane-functions/data.ts +3 -1
  33. package/src/cli/pane-functions/dom.ts +20 -2
  34. package/src/cli/pane-functions/freshness.ts +400 -0
  35. package/src/cli/pane-functions/function-help.ts +36 -22
  36. package/src/cli/pane-functions/headless.ts +39 -11
  37. package/src/cli/pane-functions/index.ts +15 -4
  38. package/src/cli/pane-functions/options.ts +3 -0
  39. package/src/cli/pane-functions/report.ts +4 -0
  40. package/src/cli/pane-functions/resolver.ts +35 -7
  41. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  42. package/src/cli/pane-functions/screenshot.ts +7 -3
  43. package/src/cli/result.ts +41 -27
  44. package/src/cli/ticker-resolution.ts +7 -5
  45. package/src/components/chart/composite/composite-chart.tsx +88 -2536
  46. package/src/components/chart/composite/drawing-store.ts +59 -0
  47. package/src/components/chart/composite/legend.tsx +319 -0
  48. package/src/components/chart/composite/panel-bitmap.ts +139 -0
  49. package/src/components/chart/composite/panel-gestures.ts +59 -0
  50. package/src/components/chart/composite/panel-labels.ts +107 -0
  51. package/src/components/chart/composite/panel-surface.tsx +876 -0
  52. package/src/components/chart/composite/tool-catalog.ts +98 -0
  53. package/src/components/chart/composite/toolbar.tsx +186 -0
  54. package/src/components/chart/composite/tools.ts +8 -0
  55. package/src/components/chart/composite/use-composite-chart-keyboard.ts +703 -0
  56. package/src/components/chart/composite/use-composite-viewport.ts +143 -0
  57. package/src/components/command-bar/commands/collection.ts +5 -1
  58. package/src/components/command-bar/list/model.ts +5 -0
  59. package/src/components/command-bar/list/view.tsx +16 -3
  60. package/src/components/command-bar/pane-templates/workflow.ts +1 -0
  61. package/src/components/command-bar/routes/root/command-items.ts +9 -2
  62. package/src/components/command-bar/routes/root/plugin-install.ts +2 -2
  63. package/src/components/command-bar/routes/root/provider-search.ts +19 -5
  64. package/src/components/command-bar/routes/root/runtime.ts +14 -5
  65. package/src/components/command-bar/routes/ticker-search/actions.ts +5 -0
  66. package/src/components/command-bar/routes/ticker-search/results.ts +31 -5
  67. package/src/components/command-bar/routes/ticker-search/route.ts +2 -2
  68. package/src/components/command-bar/routing/actions.ts +2 -0
  69. package/src/components/command-bar/selection-runtime.ts +38 -0
  70. package/src/components/command-bar/surface/index.tsx +7 -2
  71. package/src/components/command-bar/workflow/ops.ts +1 -1
  72. package/src/components/command-bar/workflow/tickers.ts +6 -0
  73. package/src/components/data-table/view.tsx +29 -10
  74. package/src/components/feedback-dialog.tsx +2 -1
  75. package/src/components/form-modal/field-row.tsx +2 -2
  76. package/src/components/index.ts +2 -2
  77. package/src/components/layout/detached-pane-shell.tsx +6 -3
  78. package/src/components/layout/floating-pane.tsx +6 -0
  79. package/src/components/layout/header.tsx +24 -7
  80. package/src/components/layout/missing-pane.tsx +2 -2
  81. package/src/components/layout/pane/content.tsx +23 -10
  82. package/src/components/layout/pane/error-boundary.tsx +103 -0
  83. package/src/components/layout/pane/footer/registration.tsx +68 -23
  84. package/src/components/layout/pane/header.tsx +61 -28
  85. package/src/components/layout/pane/index.tsx +6 -0
  86. package/src/components/layout/pane/title.ts +31 -7
  87. package/src/components/layout/shell/drag/index.ts +6 -5
  88. package/src/components/layout/shell/drag/runtime.ts +3 -0
  89. package/src/components/layout/shell/index.tsx +6 -0
  90. package/src/components/layout/shell/menu.ts +2 -1
  91. package/src/components/layout/shell/pane/actions.ts +2 -1
  92. package/src/components/layout/shell/pane/layers.tsx +29 -10
  93. package/src/components/layout/shell/terminal-pointer-runtime.ts +24 -3
  94. package/src/components/layout/shell/ticker-link-menu.ts +23 -9
  95. package/src/components/metric-treemap/heat-surface.tsx +745 -0
  96. package/src/components/metric-treemap/heat-tiles.ts +228 -0
  97. package/src/components/metric-treemap/index.tsx +2 -0
  98. package/src/components/metric-treemap/layout.ts +8 -0
  99. package/src/components/metric-treemap/scene.ts +432 -0
  100. package/src/components/onboarding/onboarding-keyboard.ts +311 -0
  101. package/src/components/onboarding/onboarding-wizard.tsx +101 -788
  102. package/src/components/onboarding/use-onboarding-broker-form.ts +263 -0
  103. package/src/components/onboarding/use-onboarding-progress.ts +298 -0
  104. package/src/components/onboarding/use-onboarding-upgrade.ts +83 -0
  105. package/src/components/pane-settings-dialog/field-dialogs.tsx +1 -0
  106. package/src/components/ticker/list-table-view.tsx +13 -1
  107. package/src/components/toggle-list.tsx +38 -12
  108. package/src/components/ui/country-flag-art.tsx +449 -0
  109. package/src/components/ui/country-flag.tsx +41 -0
  110. package/src/components/ui/data-table/types.ts +13 -0
  111. package/src/components/ui/fields.tsx +8 -0
  112. package/src/components/ui/icon.tsx +2 -0
  113. package/src/components/ui/index.ts +4 -0
  114. package/src/components/ui/multi-select/dialog.tsx +108 -22
  115. package/src/components/ui/range-track.tsx +53 -0
  116. package/src/components/ui/status.tsx +8 -2
  117. package/src/components/ui/tabs.tsx +11 -4
  118. package/src/components/ui/text-prompt-dialog.tsx +4 -2
  119. package/src/core/state/app/types.ts +10 -1
  120. package/src/data/config/store/normalize.ts +17 -1
  121. package/src/i18n/es.ts +24 -4
  122. package/src/i18n/ja.ts +24 -4
  123. package/src/i18n/ko.ts +24 -4
  124. package/src/i18n/zh-cn.ts +24 -4
  125. package/src/i18n/zh-tw.ts +24 -4
  126. package/src/layout/ticker-navigation.ts +28 -2
  127. package/src/market-data/coordinator/chart.ts +1 -1
  128. package/src/market-data/coordinator/quotes.ts +23 -4
  129. package/src/market-data/fx-closes.ts +72 -0
  130. package/src/market-data/market/freshness.ts +78 -6
  131. package/src/market-data/market/session-close-note.ts +57 -0
  132. package/src/market-data/market/status.ts +151 -0
  133. package/src/market-data/published-apac-sessions.ts +66 -0
  134. package/src/market-data/quotes/contributions.ts +8 -0
  135. package/src/market-data/quotes/freshness.ts +36 -0
  136. package/src/market-data/quotes/resolution.ts +24 -8
  137. package/src/market-data/quotes/screener-live-quotes.ts +49 -4
  138. package/src/market-data/quotes/time.ts +16 -0
  139. package/src/plugins/builtin/account-management/pane.tsx +4 -0
  140. package/src/plugins/builtin/alerts/cli.ts +10 -5
  141. package/src/plugins/builtin/alerts/index.tsx +1 -0
  142. package/src/plugins/builtin/analytics/metrics.ts +8 -3
  143. package/src/plugins/builtin/analytics/risk-client.ts +330 -79
  144. package/src/plugins/builtin/analytics/risk-fx.ts +102 -0
  145. package/src/plugins/builtin/analytics/risk-headless.ts +61 -49
  146. package/src/plugins/builtin/analytics/risk-math.ts +33 -13
  147. package/src/plugins/builtin/analytics/risk-model.ts +282 -64
  148. package/src/plugins/builtin/analytics/risk-pane.tsx +62 -30
  149. package/src/plugins/builtin/analytics/risk-test-data.ts +206 -0
  150. package/src/plugins/builtin/attention/headless.ts +6 -4
  151. package/src/plugins/builtin/attention/index.tsx +1 -1
  152. package/src/plugins/builtin/attention/pane.tsx +1 -1
  153. package/src/plugins/builtin/awards/headless.ts +1 -0
  154. package/src/plugins/builtin/awards/pane.tsx +1 -1
  155. package/src/plugins/builtin/backtest/headless.ts +1 -0
  156. package/src/plugins/builtin/bond-calculator/headless.ts +1 -0
  157. package/src/plugins/builtin/buildout/cells.tsx +15 -12
  158. package/src/plugins/builtin/buildout/detail/company.tsx +3 -3
  159. package/src/plugins/builtin/buildout/detail/index.tsx +0 -1
  160. package/src/plugins/builtin/buildout/detail/intel.tsx +6 -6
  161. package/src/plugins/builtin/buildout/detail/site.tsx +4 -7
  162. package/src/plugins/builtin/buildout/detail/ui.tsx +0 -19
  163. package/src/plugins/builtin/buildout/index.tsx +1 -0
  164. package/src/plugins/builtin/catalysts/headless.ts +3 -1
  165. package/src/plugins/builtin/catalysts/index.tsx +2 -2
  166. package/src/plugins/builtin/catalysts/pane.tsx +3 -1
  167. package/src/plugins/builtin/cds/headless.ts +2 -0
  168. package/src/plugins/builtin/cds/index.tsx +1 -0
  169. package/src/plugins/builtin/central-bank-rates/headless.ts +1 -0
  170. package/src/plugins/builtin/chart-composer/headless.ts +38 -3
  171. package/src/plugins/builtin/chart-composer/index.tsx +3 -1
  172. package/src/plugins/builtin/chart-composer/series-expression.ts +18 -2
  173. package/src/plugins/builtin/chat/channels.ts +19 -3
  174. package/src/plugins/builtin/chat/content/composer-runtime.ts +28 -13
  175. package/src/plugins/builtin/chat/content/footer.tsx +15 -6
  176. package/src/plugins/builtin/chat/content/index.tsx +120 -21
  177. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +261 -113
  178. package/src/plugins/builtin/chat/content/transcript.tsx +20 -2
  179. package/src/plugins/builtin/chat/direct-messages.ts +200 -0
  180. package/src/plugins/builtin/chat/index.tsx +1 -0
  181. package/src/plugins/builtin/chat/message/desktop.tsx +3 -0
  182. package/src/plugins/builtin/chat/message/header.tsx +16 -6
  183. package/src/plugins/builtin/chat/message/inline-tokens.tsx +72 -32
  184. package/src/plugins/builtin/chat/message/profile-popover.tsx +96 -12
  185. package/src/plugins/builtin/chat/message/terminal.tsx +3 -0
  186. package/src/plugins/builtin/chat/message/types.ts +14 -2
  187. package/src/plugins/builtin/chat/profile-popover.ts +18 -13
  188. package/src/plugins/builtin/clinical-trials/index.tsx +1 -0
  189. package/src/plugins/builtin/cloud/askg/executor.ts +2 -1
  190. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  191. package/src/plugins/builtin/cloud/team/module.tsx +1 -0
  192. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -0
  193. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  194. package/src/plugins/builtin/comment-letters/index.tsx +1 -0
  195. package/src/plugins/builtin/company-attention/headless.ts +5 -1
  196. package/src/plugins/builtin/company-attention/pane.tsx +1 -1
  197. package/src/plugins/builtin/company-kpis/headless.ts +1 -0
  198. package/src/plugins/builtin/congress-trades/headless.ts +1 -0
  199. package/src/plugins/builtin/correlation/compute.ts +59 -16
  200. package/src/plugins/builtin/correlation/geo.ts +34 -0
  201. package/src/plugins/builtin/correlation/headless-schema.ts +4 -1
  202. package/src/plugins/builtin/correlation/headless.ts +42 -8
  203. package/src/plugins/builtin/correlation/history.ts +2 -0
  204. package/src/plugins/builtin/correlation/index.tsx +68 -14
  205. package/src/plugins/builtin/correlation/matrix/model.ts +17 -2
  206. package/src/plugins/builtin/correlation/matrix/selection.ts +5 -3
  207. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  208. package/src/plugins/builtin/correlation/relationship/pane.tsx +4 -2
  209. package/src/plugins/builtin/correlation/relationship/view-model.ts +16 -1
  210. package/src/plugins/builtin/correlation/settings.ts +1 -1
  211. package/src/plugins/builtin/cot/headless.ts +6 -3
  212. package/src/plugins/builtin/cot/index.tsx +3 -3
  213. package/src/plugins/builtin/cot/model.ts +36 -3
  214. package/src/plugins/builtin/cpi/headless.ts +4 -0
  215. package/src/plugins/builtin/credit-boards/headless.ts +2 -0
  216. package/src/plugins/builtin/credit-conditions/headless.ts +1 -0
  217. package/src/plugins/builtin/credit-documents/gauge.tsx +5 -8
  218. package/src/plugins/builtin/credit-documents/headless.ts +2 -0
  219. package/src/plugins/builtin/crypto-board/headless.ts +3 -0
  220. package/src/plugins/builtin/custom-view/loader.ts +1 -1
  221. package/src/plugins/builtin/debt-maturities/headless.ts +4 -0
  222. package/src/plugins/builtin/debt-maturities/wall-bar.tsx +14 -41
  223. package/src/plugins/builtin/dividend-yield/client.ts +4 -2
  224. package/src/plugins/builtin/dividend-yield/headless.ts +1 -0
  225. package/src/plugins/builtin/doe/headless.ts +7 -0
  226. package/src/plugins/builtin/doe/range-bar.tsx +7 -37
  227. package/src/plugins/builtin/earnings/client.ts +2 -2
  228. package/src/plugins/builtin/earnings/headless.ts +5 -0
  229. package/src/plugins/builtin/earnings-calls/headless.ts +1 -0
  230. package/src/plugins/builtin/earnings-calls/index.tsx +1 -0
  231. package/src/plugins/builtin/earnings-ripple/client.ts +91 -18
  232. package/src/plugins/builtin/earnings-ripple/headless.ts +28 -8
  233. package/src/plugins/builtin/earnings-ripple/index.tsx +2 -2
  234. package/src/plugins/builtin/earnings-ripple/model.ts +97 -7
  235. package/src/plugins/builtin/earnings-ripple/pane.tsx +148 -23
  236. package/src/plugins/builtin/econ/index.tsx +1 -0
  237. package/src/plugins/builtin/econ-statistics/headless.ts +2 -0
  238. package/src/plugins/builtin/equity-screener/headless.ts +2 -0
  239. package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
  240. package/src/plugins/builtin/executives/index.tsx +1 -0
  241. package/src/plugins/builtin/exposure/headless.ts +3 -1
  242. package/src/plugins/builtin/fear-greed/headless.ts +2 -0
  243. package/src/plugins/builtin/filing-events/index.tsx +1 -0
  244. package/src/plugins/builtin/futures-curve/client.ts +12 -3
  245. package/src/plugins/builtin/futures-curve/headless.ts +39 -10
  246. package/src/plugins/builtin/futures-curve/index.tsx +3 -3
  247. package/src/plugins/builtin/futures-curve/model.ts +188 -4
  248. package/src/plugins/builtin/futures-curve/pane.tsx +51 -9
  249. package/src/plugins/builtin/fx-matrix/direction.ts +81 -0
  250. package/src/plugins/builtin/fx-matrix/index.tsx +71 -33
  251. package/src/plugins/builtin/fx-matrix/live-legs.ts +17 -0
  252. package/src/plugins/builtin/fx-matrix/pairs.ts +68 -14
  253. package/src/plugins/builtin/fx-matrix/settings.ts +62 -0
  254. package/src/plugins/builtin/gpu/headless.ts +7 -3
  255. package/src/plugins/builtin/gpu/ladder.tsx +26 -30
  256. package/src/plugins/builtin/gpu/model.ts +27 -1
  257. package/src/plugins/builtin/holders/beneficial-client.ts +209 -0
  258. package/src/plugins/builtin/holders/beneficial-model.ts +279 -0
  259. package/src/plugins/builtin/holders/beneficial-report.ts +126 -0
  260. package/src/plugins/builtin/holders/beneficial-view.tsx +241 -0
  261. package/src/plugins/builtin/holders/headless.ts +139 -5
  262. package/src/plugins/builtin/holders/index.tsx +10 -3
  263. package/src/plugins/builtin/holders/pane.tsx +20 -3
  264. package/src/plugins/builtin/holders/report-header.ts +95 -0
  265. package/src/plugins/builtin/holders/table-model.ts +20 -5
  266. package/src/plugins/builtin/holders/thirteenf-match.ts +29 -1
  267. package/src/plugins/builtin/holders/types.ts +2 -1
  268. package/src/plugins/builtin/insider/headless.ts +2 -0
  269. package/src/plugins/builtin/ipo-calendar/headless.ts +1 -0
  270. package/src/plugins/builtin/iv-history/headless.ts +4 -1
  271. package/src/plugins/builtin/jobs/headless.ts +1 -0
  272. package/src/plugins/builtin/jobs/index.tsx +1 -0
  273. package/src/plugins/builtin/jobs/share-bars.tsx +5 -28
  274. package/src/plugins/builtin/kelly-sizer/index.tsx +1 -0
  275. package/src/plugins/builtin/macro-day/headless.ts +2 -0
  276. package/src/plugins/builtin/market-halts/index.tsx +1 -0
  277. package/src/plugins/builtin/market-heatmap/data.ts +30 -2
  278. package/src/plugins/builtin/market-heatmap/headless.ts +135 -0
  279. package/src/plugins/builtin/market-heatmap/index.tsx +245 -81
  280. package/src/plugins/builtin/market-heatmap/live.ts +88 -0
  281. package/src/plugins/builtin/market-heatmap/model.ts +146 -0
  282. package/src/plugins/builtin/market-heatmap/portfolio.ts +50 -11
  283. package/src/plugins/builtin/market-movers/client.ts +3 -2
  284. package/src/plugins/builtin/market-movers/headless.ts +10 -1
  285. package/src/plugins/builtin/market-movers/screener.ts +18 -5
  286. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  287. package/src/plugins/builtin/members/client.ts +53 -0
  288. package/src/plugins/builtin/members/headless.ts +35 -0
  289. package/src/plugins/builtin/members/index.tsx +20 -0
  290. package/src/plugins/builtin/members/model.ts +68 -0
  291. package/src/plugins/builtin/members/pane.tsx +175 -0
  292. package/src/plugins/builtin/mna/distress/headless.ts +1 -0
  293. package/src/plugins/builtin/mna/headless.ts +1 -0
  294. package/src/plugins/builtin/mna/index.tsx +2 -1
  295. package/src/plugins/builtin/mna/pane.tsx +5 -2
  296. package/src/plugins/builtin/money-markets/headless.ts +1 -0
  297. package/src/plugins/builtin/news/headless.ts +2 -0
  298. package/src/plugins/builtin/news/wire/index.tsx +8 -1
  299. package/src/plugins/builtin/notes/cli.ts +14 -6
  300. package/src/plugins/builtin/notes/index.tsx +1 -0
  301. package/src/plugins/builtin/openfda/index.tsx +1 -0
  302. package/src/plugins/builtin/options/index.tsx +2 -1
  303. package/src/plugins/builtin/options-calculator/headless.ts +2 -0
  304. package/src/plugins/builtin/options-positioning/headless.ts +3 -0
  305. package/src/plugins/builtin/options-scenario/headless.ts +5 -0
  306. package/src/plugins/builtin/pe-band/client.ts +74 -2
  307. package/src/plugins/builtin/pe-band/headless.ts +26 -8
  308. package/src/plugins/builtin/pe-band/model.ts +102 -22
  309. package/src/plugins/builtin/pe-band/pane.tsx +34 -16
  310. package/src/plugins/builtin/pe-band/report-dates.ts +78 -0
  311. package/src/plugins/builtin/perps/client.ts +66 -5
  312. package/src/plugins/builtin/perps/equity-identity.ts +16 -0
  313. package/src/plugins/builtin/perps/equity-row.tsx +6 -4
  314. package/src/plugins/builtin/perps/headless.ts +144 -16
  315. package/src/plugins/builtin/perps/index.tsx +6 -6
  316. package/src/plugins/builtin/perps/model.ts +189 -12
  317. package/src/plugins/builtin/perps/pane.tsx +248 -47
  318. package/src/plugins/builtin/plugin-marketplace/activation.ts +19 -0
  319. package/src/plugins/builtin/plugin-marketplace/pane.tsx +2 -2
  320. package/src/plugins/builtin/plugin-marketplace/store.ts +2 -0
  321. package/src/plugins/builtin/portfolio-list/allocation.ts +245 -0
  322. package/src/plugins/builtin/portfolio-list/cached-account.ts +33 -0
  323. package/src/plugins/builtin/portfolio-list/cell-version.ts +19 -1
  324. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +328 -29
  325. package/src/plugins/builtin/portfolio-list/cli/render.ts +230 -39
  326. package/src/plugins/builtin/portfolio-list/cli/shared.ts +67 -2
  327. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +35 -31
  328. package/src/plugins/builtin/portfolio-list/column-values.ts +124 -47
  329. package/src/plugins/builtin/portfolio-list/headless.ts +98 -16
  330. package/src/plugins/builtin/portfolio-list/index.tsx +14 -3
  331. package/src/plugins/builtin/portfolio-list/live-valuation.ts +7 -5
  332. package/src/plugins/builtin/portfolio-list/mutations.ts +55 -1
  333. package/src/plugins/builtin/portfolio-list/pane/data.ts +6 -0
  334. package/src/plugins/builtin/portfolio-list/pane/index.tsx +47 -13
  335. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +43 -2
  336. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +48 -5
  337. package/src/plugins/builtin/portfolio-list/settings.ts +33 -2
  338. package/src/plugins/builtin/portfolio-list/summary/index.tsx +24 -1
  339. package/src/plugins/builtin/portfolio-list/summary/totals.ts +26 -7
  340. package/src/plugins/builtin/power/headless.ts +3 -1
  341. package/src/plugins/builtin/rate-path/headless.ts +2 -0
  342. package/src/plugins/builtin/realized-vol/headless.ts +2 -0
  343. package/src/plugins/builtin/relative-rotation/headless.ts +2 -0
  344. package/src/plugins/builtin/research/analyst-headless.ts +3 -0
  345. package/src/plugins/builtin/research/events-headless.ts +2 -0
  346. package/src/plugins/builtin/research/headless.ts +2 -0
  347. package/src/plugins/builtin/research/index.tsx +1 -0
  348. package/src/plugins/builtin/research/relative-valuation-headless.ts +10 -5
  349. package/src/plugins/builtin/research/relative-valuation-model.ts +11 -3
  350. package/src/plugins/builtin/research/relative-valuation-pane.tsx +3 -3
  351. package/src/plugins/builtin/research-search/index.tsx +1 -0
  352. package/src/plugins/builtin/revenue-breakdown/headless.ts +2 -0
  353. package/src/plugins/builtin/reverse-dcf/headless.ts +2 -0
  354. package/src/plugins/builtin/risk-factors/headless.ts +2 -0
  355. package/src/plugins/builtin/scanner/flow-history.ts +2 -13
  356. package/src/plugins/builtin/scanner/headless.ts +14 -0
  357. package/src/plugins/builtin/scanner/hilo-bars.tsx +12 -70
  358. package/src/plugins/builtin/scanner/hilo-model.ts +0 -18
  359. package/src/plugins/builtin/seasonality/headless.ts +2 -0
  360. package/src/plugins/builtin/sec/command-bar-search.ts +14 -4
  361. package/src/plugins/builtin/sec/filing-content.ts +42 -17
  362. package/src/plugins/builtin/sec/forms.ts +12 -0
  363. package/src/plugins/builtin/sec/headless.ts +2 -0
  364. package/src/plugins/builtin/sectors/client.ts +1 -0
  365. package/src/plugins/builtin/sectors/headless.ts +9 -1
  366. package/src/plugins/builtin/sectors/move-bar.tsx +10 -32
  367. package/src/plugins/builtin/sectors/sector-model.ts +2 -0
  368. package/src/plugins/builtin/shared/crypto-pair.ts +14 -0
  369. package/src/plugins/builtin/shared/locked-rows.tsx +1 -1
  370. package/src/plugins/builtin/shared/report-freshness.ts +57 -0
  371. package/src/plugins/builtin/shared/research-cells.tsx +4 -9
  372. package/src/plugins/builtin/short-interest/headless.ts +2 -0
  373. package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
  374. package/src/plugins/builtin/short-volume/headless.ts +1 -0
  375. package/src/plugins/builtin/social-mentions/headless.ts +1 -0
  376. package/src/plugins/builtin/social-mentions/index.tsx +1 -1
  377. package/src/plugins/builtin/supply-chain/headless.ts +7 -1
  378. package/src/plugins/builtin/supply-chain/index.tsx +10 -1
  379. package/src/plugins/builtin/supply-chain/model.ts +27 -2
  380. package/src/plugins/builtin/supply-chain/pane.tsx +71 -32
  381. package/src/plugins/builtin/themes/headless.ts +3 -0
  382. package/src/plugins/builtin/thirteenf/headless.ts +110 -25
  383. package/src/plugins/builtin/thirteenf/index.tsx +2 -1
  384. package/src/plugins/builtin/thirteenf/report-notes.ts +149 -0
  385. package/src/plugins/builtin/ticker-detail/headless.ts +7 -1
  386. package/src/plugins/builtin/ticker-detail/index.tsx +1 -0
  387. package/src/plugins/builtin/ticker-detail/overview/components.tsx +5 -60
  388. package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -2
  389. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +1 -1
  390. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +26 -26
  391. package/src/plugins/builtin/ticker-detail/pane.tsx +1 -1
  392. package/src/plugins/builtin/ticker-detail/quote-footer.ts +7 -1
  393. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +20 -4
  394. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -2
  395. package/src/plugins/builtin/time-sales/headless.ts +5 -1
  396. package/src/plugins/builtin/treasury-auctions/headless.ts +1 -0
  397. package/src/plugins/builtin/vol-surface/index.tsx +1 -1
  398. package/src/plugins/builtin/volatility/headless.ts +6 -1
  399. package/src/plugins/builtin/world-indices/headless.ts +2 -0
  400. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +157 -0
  401. package/src/plugins/builtin/world-venue-map/basemap.ts +214 -0
  402. package/src/plugins/builtin/world-venue-map/client.ts +45 -0
  403. package/src/plugins/builtin/world-venue-map/entity-detail.tsx +147 -0
  404. package/src/plugins/builtin/world-venue-map/entity-table.ts +59 -0
  405. package/src/plugins/builtin/world-venue-map/evidence.ts +77 -0
  406. package/src/plugins/builtin/world-venue-map/feed.ts +293 -0
  407. package/src/plugins/builtin/world-venue-map/geo-draw.tsx +248 -0
  408. package/src/plugins/builtin/world-venue-map/geo-series.ts +142 -0
  409. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +447 -0
  410. package/src/plugins/builtin/world-venue-map/headless.ts +146 -0
  411. package/src/plugins/builtin/world-venue-map/ids.ts +1 -0
  412. package/src/plugins/builtin/world-venue-map/index.ts +67 -4
  413. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +552 -0
  414. package/src/plugins/builtin/world-venue-map/layers.ts +348 -0
  415. package/src/plugins/builtin/world-venue-map/map-symbols.ts +220 -0
  416. package/src/plugins/builtin/world-venue-map/map.tsx +381 -45
  417. package/src/plugins/builtin/world-venue-map/model.ts +7 -4
  418. package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +199 -0
  419. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n00.chunk.ts +4 -0
  420. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n15.chunk.ts +4 -0
  421. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n30.chunk.ts +4 -0
  422. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n45.chunk.ts +4 -0
  423. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n60.chunk.ts +4 -0
  424. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n75.chunk.ts +4 -0
  425. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s15.chunk.ts +4 -0
  426. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s30.chunk.ts +4 -0
  427. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s60.chunk.ts +4 -0
  428. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n00.chunk.ts +4 -0
  429. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n15.chunk.ts +4 -0
  430. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n30.chunk.ts +4 -0
  431. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n45.chunk.ts +4 -0
  432. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n60.chunk.ts +4 -0
  433. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n75.chunk.ts +4 -0
  434. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s15.chunk.ts +4 -0
  435. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s30.chunk.ts +4 -0
  436. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s45.chunk.ts +4 -0
  437. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n00.chunk.ts +4 -0
  438. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n15.chunk.ts +4 -0
  439. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n30.chunk.ts +4 -0
  440. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n45.chunk.ts +4 -0
  441. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n60.chunk.ts +4 -0
  442. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n75.chunk.ts +4 -0
  443. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s15.chunk.ts +4 -0
  444. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s30.chunk.ts +4 -0
  445. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s45.chunk.ts +4 -0
  446. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s60.chunk.ts +4 -0
  447. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n00.chunk.ts +4 -0
  448. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n15.chunk.ts +4 -0
  449. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n30.chunk.ts +4 -0
  450. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n45.chunk.ts +4 -0
  451. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n60.chunk.ts +4 -0
  452. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n75.chunk.ts +4 -0
  453. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s15.chunk.ts +4 -0
  454. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s30.chunk.ts +4 -0
  455. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s60.chunk.ts +4 -0
  456. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n00.chunk.ts +4 -0
  457. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n15.chunk.ts +4 -0
  458. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n30.chunk.ts +4 -0
  459. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n45.chunk.ts +4 -0
  460. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n60.chunk.ts +4 -0
  461. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n75.chunk.ts +4 -0
  462. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s15.chunk.ts +4 -0
  463. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s30.chunk.ts +4 -0
  464. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s60.chunk.ts +4 -0
  465. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n00.chunk.ts +4 -0
  466. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n15.chunk.ts +4 -0
  467. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n30.chunk.ts +4 -0
  468. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n45.chunk.ts +4 -0
  469. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n60.chunk.ts +4 -0
  470. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n75.chunk.ts +4 -0
  471. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075s45.chunk.ts +4 -0
  472. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n00.chunk.ts +4 -0
  473. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n15.chunk.ts +4 -0
  474. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n30.chunk.ts +4 -0
  475. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n45.chunk.ts +4 -0
  476. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n60.chunk.ts +4 -0
  477. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n75.chunk.ts +4 -0
  478. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090s15.chunk.ts +4 -0
  479. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n00.chunk.ts +4 -0
  480. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n15.chunk.ts +4 -0
  481. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n30.chunk.ts +4 -0
  482. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n45.chunk.ts +4 -0
  483. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n60.chunk.ts +4 -0
  484. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n75.chunk.ts +4 -0
  485. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s15.chunk.ts +4 -0
  486. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s30.chunk.ts +4 -0
  487. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s45.chunk.ts +4 -0
  488. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n00.chunk.ts +4 -0
  489. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n15.chunk.ts +4 -0
  490. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n30.chunk.ts +4 -0
  491. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n45.chunk.ts +4 -0
  492. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n60.chunk.ts +4 -0
  493. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s15.chunk.ts +4 -0
  494. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s30.chunk.ts +4 -0
  495. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s45.chunk.ts +4 -0
  496. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n00.chunk.ts +4 -0
  497. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n15.chunk.ts +4 -0
  498. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n30.chunk.ts +4 -0
  499. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n45.chunk.ts +4 -0
  500. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n60.chunk.ts +4 -0
  501. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n75.chunk.ts +4 -0
  502. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s15.chunk.ts +4 -0
  503. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s30.chunk.ts +4 -0
  504. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s45.chunk.ts +4 -0
  505. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n00.chunk.ts +4 -0
  506. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n15.chunk.ts +4 -0
  507. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n45.chunk.ts +4 -0
  508. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n60.chunk.ts +4 -0
  509. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n75.chunk.ts +4 -0
  510. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s15.chunk.ts +4 -0
  511. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s30.chunk.ts +4 -0
  512. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s45.chunk.ts +4 -0
  513. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s60.chunk.ts +4 -0
  514. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n00.chunk.ts +4 -0
  515. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n15.chunk.ts +4 -0
  516. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n45.chunk.ts +4 -0
  517. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n60.chunk.ts +4 -0
  518. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s15.chunk.ts +4 -0
  519. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s30.chunk.ts +4 -0
  520. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s45.chunk.ts +4 -0
  521. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s60.chunk.ts +4 -0
  522. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n00.chunk.ts +4 -0
  523. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n15.chunk.ts +4 -0
  524. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n30.chunk.ts +4 -0
  525. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n45.chunk.ts +4 -0
  526. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n60.chunk.ts +4 -0
  527. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n75.chunk.ts +4 -0
  528. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s15.chunk.ts +4 -0
  529. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s30.chunk.ts +4 -0
  530. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s45.chunk.ts +4 -0
  531. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n00.chunk.ts +4 -0
  532. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n15.chunk.ts +4 -0
  533. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n30.chunk.ts +4 -0
  534. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n60.chunk.ts +4 -0
  535. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n75.chunk.ts +4 -0
  536. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s30.chunk.ts +4 -0
  537. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s60.chunk.ts +4 -0
  538. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n30.chunk.ts +4 -0
  539. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n45.chunk.ts +4 -0
  540. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n60.chunk.ts +4 -0
  541. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n75.chunk.ts +4 -0
  542. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s15.chunk.ts +4 -0
  543. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s30.chunk.ts +4 -0
  544. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s60.chunk.ts +4 -0
  545. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n00.chunk.ts +4 -0
  546. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n30.chunk.ts +4 -0
  547. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n45.chunk.ts +4 -0
  548. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n60.chunk.ts +4 -0
  549. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n75.chunk.ts +4 -0
  550. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s15.chunk.ts +4 -0
  551. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s30.chunk.ts +4 -0
  552. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s45.chunk.ts +4 -0
  553. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s60.chunk.ts +4 -0
  554. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n00.chunk.ts +4 -0
  555. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n15.chunk.ts +4 -0
  556. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n30.chunk.ts +4 -0
  557. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n45.chunk.ts +4 -0
  558. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n60.chunk.ts +4 -0
  559. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n75.chunk.ts +4 -0
  560. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s15.chunk.ts +4 -0
  561. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s30.chunk.ts +4 -0
  562. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s45.chunk.ts +4 -0
  563. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s60.chunk.ts +4 -0
  564. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n00.chunk.ts +4 -0
  565. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n15.chunk.ts +4 -0
  566. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n30.chunk.ts +4 -0
  567. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n45.chunk.ts +4 -0
  568. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n60.chunk.ts +4 -0
  569. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n75.chunk.ts +4 -0
  570. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s15.chunk.ts +4 -0
  571. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s30.chunk.ts +4 -0
  572. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s45.chunk.ts +4 -0
  573. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s60.chunk.ts +4 -0
  574. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n00.chunk.ts +4 -0
  575. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n15.chunk.ts +4 -0
  576. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n30.chunk.ts +4 -0
  577. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n45.chunk.ts +4 -0
  578. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n60.chunk.ts +4 -0
  579. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n75.chunk.ts +4 -0
  580. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105s15.chunk.ts +4 -0
  581. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n00.chunk.ts +4 -0
  582. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n15.chunk.ts +4 -0
  583. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n30.chunk.ts +4 -0
  584. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n45.chunk.ts +4 -0
  585. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n60.chunk.ts +4 -0
  586. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n75.chunk.ts +4 -0
  587. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120s30.chunk.ts +4 -0
  588. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n30.chunk.ts +4 -0
  589. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n45.chunk.ts +4 -0
  590. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n60.chunk.ts +4 -0
  591. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n75.chunk.ts +4 -0
  592. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135s30.chunk.ts +4 -0
  593. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n45.chunk.ts +4 -0
  594. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n60.chunk.ts +4 -0
  595. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s15.chunk.ts +4 -0
  596. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s30.chunk.ts +4 -0
  597. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n00.chunk.ts +4 -0
  598. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n15.chunk.ts +4 -0
  599. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n45.chunk.ts +4 -0
  600. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n60.chunk.ts +4 -0
  601. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s15.chunk.ts +4 -0
  602. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s30.chunk.ts +4 -0
  603. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n00.chunk.ts +4 -0
  604. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n15.chunk.ts +4 -0
  605. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n45.chunk.ts +4 -0
  606. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n60.chunk.ts +4 -0
  607. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s15.chunk.ts +4 -0
  608. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s30.chunk.ts +4 -0
  609. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s45.chunk.ts +4 -0
  610. package/src/plugins/builtin/world-venue-map/natural-earth/world-50m.chunk.ts +4 -0
  611. package/src/plugins/builtin/world-venue-map/pane.tsx +28 -43
  612. package/src/plugins/builtin/world-venue-map/use-geo.ts +55 -0
  613. package/src/plugins/builtin/world-venue-map/venue-header.tsx +32 -0
  614. package/src/plugins/builtin/yield-curve/headless.ts +16 -0
  615. package/src/plugins/catalog-browser.ts +2 -0
  616. package/src/plugins/catalog-manifest.ts +1 -0
  617. package/src/plugins/catalog-ui.ts +3 -1
  618. package/src/plugins/catalog.ts +1 -1
  619. package/src/plugins/host-link.ts +33 -1
  620. package/src/plugins/installer.ts +48 -12
  621. package/src/plugins/loader.ts +30 -3
  622. package/src/plugins/manager-node.ts +15 -5
  623. package/src/plugins/registry/index.ts +1 -1
  624. package/src/plugins/registry/pane-settings.ts +26 -7
  625. package/src/public/__snapshots__/public-api.test.ts.snap +2 -0
  626. package/src/renderers/dom/build-assets.ts +81 -3
  627. package/src/renderers/dom/data-table/row.tsx +52 -16
  628. package/src/renderers/dom/desktop/controls.tsx +7 -3
  629. package/src/renderers/dom/desktop/icons.tsx +1 -0
  630. package/src/renderers/dom/desktop/list-view.tsx +35 -10
  631. package/src/renderers/dom/desktop/popover.tsx +73 -14
  632. package/src/renderers/dom/host/box.tsx +69 -26
  633. package/src/renderers/dom/host/input.tsx +14 -3
  634. package/src/renderers/dom/host/tabs.tsx +37 -1
  635. package/src/renderers/dom/styles.css +41 -7
  636. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +11 -6
  637. package/src/renderers/electrobun/bun/desktop/backend.ts +1 -0
  638. package/src/renderers/electrobun/bun/desktop/update.ts +181 -87
  639. package/src/renderers/electrobun/bun/index.ts +73 -6
  640. package/src/renderers/electrobun/bun/window/remembered-frame.ts +122 -0
  641. package/src/renderers/electrobun/shared/protocol.ts +3 -0
  642. package/src/renderers/electrobun/view/backend-rpc.ts +10 -4
  643. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +3 -1
  644. package/src/renderers/electrobun/view/main.tsx +7 -1
  645. package/src/renderers/electrobun/view/renderer-window-error.ts +18 -0
  646. package/src/renderers/electrobun/view/rpc-timeout.ts +14 -3
  647. package/src/renderers/electrobun/view/update-host.ts +45 -21
  648. package/src/renderers/opentui/host.tsx +5 -0
  649. package/src/sources/gloomberb-cloud/index.ts +25 -4
  650. package/src/sources/gloomberb-cloud/normalizers.ts +36 -5
  651. package/src/sources/provider-router/cached-routes.ts +4 -1
  652. package/src/sources/provider-router/financials.ts +11 -8
  653. package/src/sources/provider-router/history.ts +15 -10
  654. package/src/sources/sec-registrant.ts +75 -0
  655. package/src/state/app/context/index.tsx +24 -8
  656. package/src/state/hooks/quote-streaming.ts +41 -26
  657. package/src/sync/core-contributors.ts +4 -0
  658. package/src/telemetry/benign-window-error.ts +7 -0
  659. package/src/telemetry/crash-reports-dom.ts +2 -0
  660. package/src/telemetry/crash-reports.ts +4 -1
  661. package/src/test-support/cli-context.ts +10 -2
  662. package/src/test-support/daily-nav.ts +25 -0
  663. package/src/theme/heat-colors.ts +357 -2
  664. package/src/tickers/instrument-kind.ts +34 -7
  665. package/src/tickers/search/asset-classes.ts +24 -9
  666. package/src/tickers/search/index.ts +44 -11
  667. package/src/tickers/search/ranking.ts +23 -3
  668. package/src/time-series/resolve.ts +1 -1
  669. package/src/time-series/use-chart-resolution.ts +1 -1
  670. package/src/types/context-menu.ts +6 -0
  671. package/src/types/data-provider.ts +2 -0
  672. package/src/types/financials.ts +5 -0
  673. package/src/types/headless.ts +79 -2
  674. package/src/types/plugin.ts +38 -6
  675. package/src/types/ticker.ts +15 -0
  676. package/src/ui/context-menu.tsx +1 -0
  677. package/src/ui/host.tsx +16 -0
  678. package/src/updater/index.ts +38 -13
  679. package/src/utils/async-deadline.ts +21 -0
  680. package/src/utils/cli-output.ts +5 -1
  681. package/src/utils/crypto-pair.ts +41 -0
  682. package/src/utils/currency-units.ts +14 -0
  683. package/src/utils/depositary-receipt.ts +26 -0
  684. package/src/utils/exchanges.ts +72 -0
  685. package/src/utils/fire-and-forget.ts +24 -0
  686. package/src/utils/format.ts +13 -0
  687. package/src/utils/price-earnings.ts +70 -3
  688. package/src/utils/price-history.ts +58 -13
  689. package/src/utils/rpc-timeout-error.ts +27 -0
  690. package/src/utils/sec.ts +13 -1
  691. package/src/utils/utc-time.ts +79 -0
@@ -1,6 +1,10 @@
1
1
  import { getCloudApiBaseUrl } from "../../../api-client/request";
2
+ import { RpcTimeoutError } from "../../../utils/rpc-timeout-error";
2
3
  import type { DesktopBackendRequestMethod } from "../shared/protocol";
3
4
 
5
+ /** How long the view waits for the Bun process to answer one request. */
6
+ export const RPC_MAX_REQUEST_TIME_MS = 120_000;
7
+
4
8
  /** What Electrobun's request timer rejects with: no method, request id or caller. */
5
9
  const ELECTROBUN_REQUEST_TIMEOUT_MESSAGE = "RPC request timed out.";
6
10
 
@@ -16,7 +20,9 @@ const ELECTROBUN_REQUEST_TIMEOUT_MESSAGE = "RPC request timed out.";
16
20
  * from the user's config (a broker gateway, a feed, a plugin endpoint) and is
17
21
  * only called external. Never the payload, rest of the path, query or headers.
18
22
  * Elapsed time is rounded to 10 s because crash reports are deduplicated per
19
- * session on the message.
23
+ * session on the message. The error keeps its message and `Error` type, and
24
+ * carries the elapsed time so a timer that fired long after its limit (the
25
+ * machine slept) can be told from a request that really stalled.
20
26
  */
21
27
  export async function nameRpcTimeout<T>(
22
28
  method: DesktopBackendRequestMethod,
@@ -28,8 +34,13 @@ export async function nameRpcTimeout<T>(
28
34
  return await request();
29
35
  } catch (error) {
30
36
  if (!(error instanceof Error) || error.message !== ELECTROBUN_REQUEST_TIMEOUT_MESSAGE) throw error;
31
- const seconds = Math.round((performance.now() - startedAt) / 10_000) * 10;
32
- throw new Error(`RPC request timed out: ${describeRequest(method, payload)} after ~${seconds}s`, { cause: error });
37
+ const elapsedMs = performance.now() - startedAt;
38
+ const seconds = Math.round(elapsedMs / 10_000) * 10;
39
+ throw new RpcTimeoutError(`RPC request timed out: ${describeRequest(method, payload)} after ~${seconds}s`, {
40
+ elapsedMs,
41
+ limitMs: RPC_MAX_REQUEST_TIME_MS,
42
+ cause: error,
43
+ });
33
44
  }
34
45
  }
35
46
 
@@ -6,33 +6,57 @@ import {
6
6
  } from "../../../updater";
7
7
  import { backendRequest, onBackendMessage } from "./backend-rpc";
8
8
 
9
+ /**
10
+ * Starts a backend update request and follows its `update.progress` messages
11
+ * until one ends the flow: `ready` ends a download, and `done` or `error` end
12
+ * either flow. A request that itself fails is reported as an error.
13
+ */
14
+ function followUpdateFlow(
15
+ start: () => Promise<unknown>,
16
+ onProgress: (progress: UpdateProgress) => void,
17
+ endsFlow: (progress: UpdateProgress) => boolean,
18
+ ): Promise<void> {
19
+ return new Promise((resolve) => {
20
+ let settled = false;
21
+ const unsubscribe = onBackendMessage("update.progress", ({ progress }) => {
22
+ onProgress(progress);
23
+ if (endsFlow(progress)) {
24
+ settled = true;
25
+ unsubscribe();
26
+ resolve();
27
+ }
28
+ });
29
+
30
+ start().catch((error) => {
31
+ if (settled) return;
32
+ unsubscribe();
33
+ onProgress({
34
+ phase: "error",
35
+ error: error instanceof Error ? error.message : String(error),
36
+ });
37
+ resolve();
38
+ });
39
+ });
40
+ }
41
+
9
42
  export function installElectrobunUpdateHost(): void {
10
43
  setUpdateHost({
11
44
  checkForUpdateDetailed(currentVersion: string): Promise<UpdateCheckResult> {
12
45
  return backendRequest("update.check", { currentVersion });
13
46
  },
14
47
  performUpdate(release: ReleaseInfo, onProgress: (progress: UpdateProgress) => void): Promise<void> {
15
- return new Promise((resolve) => {
16
- let settled = false;
17
- const unsubscribe = onBackendMessage("update.progress", ({ progress }) => {
18
- onProgress(progress);
19
- if (progress.phase === "done" || progress.phase === "error") {
20
- settled = true;
21
- unsubscribe();
22
- resolve();
23
- }
24
- });
25
-
26
- backendRequest("update.start", { release }).catch((error) => {
27
- if (settled) return;
28
- unsubscribe();
29
- onProgress({
30
- phase: "error",
31
- error: error instanceof Error ? error.message : String(error),
32
- });
33
- resolve();
34
- });
35
- });
48
+ return followUpdateFlow(
49
+ () => backendRequest("update.start", { release }),
50
+ onProgress,
51
+ (progress) => progress.phase === "ready" || progress.phase === "done" || progress.phase === "error",
52
+ );
53
+ },
54
+ applyUpdate(onProgress: (progress: UpdateProgress) => void): Promise<void> {
55
+ return followUpdateFlow(
56
+ () => backendRequest("update.apply"),
57
+ onProgress,
58
+ (progress) => progress.phase === "done" || progress.phase === "error",
59
+ );
36
60
  },
37
61
  });
38
62
  }
@@ -108,6 +108,11 @@ export async function createOpenTuiHost(): Promise<OpenTuiHost> {
108
108
  enableMouseMovement: true,
109
109
  });
110
110
  const root = createRoot(renderer);
111
+ // Every mounted ScrollBox listens for `selection` and many components for
112
+ // `resize`, so the counts grow with the panes on screen (14 and 18 with one
113
+ // DES open at 80x24) and fall back when they close. Node's default warning
114
+ // at 11 is not a leak here, and it prints over the panes.
115
+ renderer.setMaxListeners(0);
111
116
  installResolutionEventBridge(renderer);
112
117
  // Streamed quotes apply and notify on one clock of up to 10 Hz: every
113
118
  // visible row stays live while a burst of ticks costs one terminal redraw,
@@ -40,6 +40,8 @@ import { resolveCurrencyUnit } from "../../utils/currency-units";
40
40
  import { canonicalExchange, canonicalTickerKey, parsePublicTickerKey } from "../../utils/exchanges";
41
41
  import { normalizePriceHistory, priceHistoryIntervalMs, reachesLatestSettledSession } from "../../utils/price-history";
42
42
  import { createProviderMiss } from "../provider-errors";
43
+ import { assertSecRegistrantMatches } from "../sec-registrant";
44
+ import { nonUsSecListingVenue } from "../../utils/sec";
43
45
  import { publicListingTarget } from "../listing-target";
44
46
  import { canonicalHistoryInterval, HistoryRetentionError, parseHistoryRecoveryCandidate, parseHistoryRetention, type HistoryRetention } from "../history-retention";
45
47
  import { getRouterEntityKey } from "../provider-router/cache";
@@ -195,7 +197,7 @@ function mapCloudPriceHistory(
195
197
  `Cloud chart data is unavailable for ${ticker}`,
196
198
  ).map((point) => mapPricePoint(point, divisor, exchange)),
197
199
  );
198
- if (stale && !reachesLatestSettledSession(points, Date.now(), { exchange, intervalMs: priceHistoryIntervalMs(interval) })) {
200
+ if (stale && !reachesLatestSettledSession(points, Date.now(), { symbol: ticker, exchange, intervalMs: priceHistoryIntervalMs(interval) })) {
199
201
  throw createProviderMiss(`Cloud chart data is stale for ${ticker}`);
200
202
  }
201
203
  if (
@@ -486,11 +488,30 @@ export class GloomberbCloudProvider implements AssetDataProvider {
486
488
  );
487
489
  }
488
490
 
489
- async getSecFilings(ticker: string, count = 15): Promise<SecFilingItem[]> {
490
- return withCloudFallback(async () => {
491
- const response = await apiClient.getCloudSecFilings({ ticker, limit: count, offset: 0 });
491
+ async getSecFilings(ticker: string, count = 15, exchange?: string, context?: MarketDataRequestContext): Promise<SecFilingItem[]> {
492
+ const venue = nonUsSecListingVenue(ticker, exchange);
493
+ const name = venue ? context?.listingName?.trim() || await this.listingName(ticker, venue) || undefined : undefined;
494
+ const filings = await withCloudFallback(async () => {
495
+ const response = await apiClient.getCloudSecFilings({ ticker, exchange, name, limit: count, offset: 0 });
492
496
  return response.filings.map(mapCloudSecFiling);
493
497
  }, `Cloud SEC filings are unavailable for ${ticker}`);
498
+ if (!venue || filings.length === 0) return filings;
499
+ const symbol = parsePublicTickerKey(ticker).symbol;
500
+ // Without the listing's company the filings cannot be told apart from another company's.
501
+ if (!name) throw createProviderMiss(`SEC filings for ${symbol} could not be matched to its ${venue} listing`);
502
+ assertSecRegistrantMatches(filings, { symbol, exchange: venue, name });
503
+ return filings;
504
+ }
505
+
506
+ /** The company a listing belongs to, from its own quote; null when the quote prices another venue. */
507
+ private async listingName(ticker: string, venue: string): Promise<string | null> {
508
+ try {
509
+ const quote = await this.getQuote(ticker, venue);
510
+ const quoteVenue = canonicalExchange(quote.listingExchangeName || quote.exchangeName);
511
+ return quoteVenue === venue ? quote.name?.trim() || null : null;
512
+ } catch {
513
+ return null;
514
+ }
494
515
  }
495
516
 
496
517
  async getSecFilingDocuments(filing: SecFilingItem): Promise<SecFilingDocument[]> {
@@ -36,10 +36,31 @@ function cloudInternalProviderId(providerMeta?: CloudProviderMeta): string | nul
36
36
  return upstream ? `${GLOOMBERB_CLOUD_PROVIDER_ID}:${upstream}` : null;
37
37
  }
38
38
 
39
+ /**
40
+ * A previous close is a price from inside the trailing 52 weeks, so it cannot
41
+ * sit far below the 52-week low. The service has sent 0.000205 as the
42
+ * Shanghai Composite's close (52-week low 3,741), which drew a +1.86e9% move.
43
+ * Today's price plays no part, so a real gap of any size still shows; two
44
+ * orders of magnitude leave room for a 52-week low that lags a crash or a split.
45
+ */
46
+ const IMPOSSIBLE_CLOSE_FACTOR = 100;
47
+
48
+ function isImpossiblePreviousClose(
49
+ previousClose: number | null | undefined,
50
+ low52w: number | null | undefined,
51
+ ): boolean {
52
+ return typeof previousClose === "number" && Number.isFinite(previousClose) && previousClose > 0
53
+ && typeof low52w === "number" && Number.isFinite(low52w) && low52w > 0
54
+ && previousClose * IMPOSSIBLE_CLOSE_FACTOR < low52w;
55
+ }
56
+
39
57
  export function mapQuote(
40
58
  quote: CloudQuotePayload,
41
59
  providerMeta?: CloudProviderMeta,
42
60
  ): Quote {
61
+ if (quote.priceBasis != null && !["nav", "per-unit", "percent-of-par", "points", "thirty-seconds"].includes(quote.priceBasis)) {
62
+ throw createProviderMiss("Unsupported quote price basis");
63
+ }
43
64
  const { currency, divisor } = resolveCurrencyUnit(quote.currency);
44
65
  const listingExchangeName = quote.listingExchangeName ?? quote.exchangeName;
45
66
  const listingExchangeFullName =
@@ -47,24 +68,34 @@ export function mapQuote(
47
68
  quote.fullExchangeName ??
48
69
  listingExchangeName;
49
70
  const internalProviderId = cloudInternalProviderId(providerMeta);
50
- const change = typeof quote.change === "number" && Number.isFinite(quote.change)
71
+ const impossibleClose = isImpossiblePreviousClose(quote.previousClose, quote.low52w);
72
+ const change = !impossibleClose && typeof quote.change === "number" && Number.isFinite(quote.change)
51
73
  ? quote.change / divisor
52
74
  : Number.NaN;
53
- const changePercent = typeof quote.changePercent === "number" && Number.isFinite(quote.changePercent)
75
+ const changePercent = !impossibleClose && typeof quote.changePercent === "number" && Number.isFinite(quote.changePercent)
54
76
  ? quote.changePercent
55
77
  : Number.NaN;
56
78
  // A generic future (VX1, TY1) reads in points or 32nds, not dollars.
57
79
  const generic = futuresGenericListing(quote.symbol, quote.listingExchangeName ?? quote.exchangeName);
58
- const priceBasis = generic ? futuresGenericPriceBasis(generic) : null;
80
+ const priceObservation = quote.priceBasis === "nav" ? "nav" : undefined;
81
+ const priceBasis = quote.priceBasis === "nav"
82
+ ? "per-unit"
83
+ : (generic ? futuresGenericPriceBasis(generic) : null) ?? quote.priceBasis;
59
84
  return withoutUndefinedFields(reconcileQuoteDayRange({
60
85
  ...quote,
61
- ...priceBasis ? { priceBasis } : {},
86
+ priceBasis,
87
+ priceObservation,
62
88
  currency: currency || quote.currency,
63
89
  price: normalizePriceValueByDivisor(quote.price, divisor) ?? quote.price,
64
90
  change,
65
91
  changePercent,
66
- previousClose: normalizePriceValueByDivisor(quote.previousClose, divisor),
92
+ previousClose: impossibleClose ? undefined : normalizePriceValueByDivisor(quote.previousClose, divisor),
67
93
  regularClose: normalizePriceValueByDivisor(quote.regularClose, divisor),
94
+ // JSON carries an unavailable move as null; the percent is unitless.
95
+ regularChange: normalizePriceValueByDivisor(quote.regularChange ?? undefined, divisor),
96
+ regularChangePercent: typeof quote.regularChangePercent === "number" && Number.isFinite(quote.regularChangePercent)
97
+ ? quote.regularChangePercent
98
+ : undefined,
68
99
  high52w: normalizePriceValueByDivisor(quote.high52w, divisor),
69
100
  low52w: normalizePriceValueByDivisor(quote.low52w, divisor),
70
101
  bid: normalizePriceValueByDivisor(quote.bid, divisor),
@@ -3,6 +3,7 @@ import { CachedQuery, type CachedValue } from "../../data/cached-query";
3
3
  import type { CachedAssetArgs, CachedAssetMethod, CachedAssetValue, DataProvider, MarketDataRequestContext, SecFilingItem } from "../../types/data-provider";
4
4
  import type { AnalystResearchData, CorporateActionsData, HolderData } from "../../types/financials";
5
5
  import { canonicalExchange } from "../../utils/exchanges";
6
+ import { nonUsSecListingVenue } from "../../utils/sec";
6
7
  import { shouldLogProviderError } from "../provider-errors";
7
8
  import { hasBrokerContext, withBrokerTimeout } from "./brokers";
8
9
  import { publicListingExchange } from "../listing-target";
@@ -220,9 +221,11 @@ export class ProviderRouterCachedRoutes {
220
221
  };
221
222
  case "getSecFilings": {
222
223
  const [symbol, count = 15, listingExchange, requestContext] = args as CachedAssetArgs<"getSecFilings">;
224
+ const exact = buildVariantKey([["exchange", canonicalExchange(listingExchange)], ["count", count]]);
223
225
  return {
224
226
  kind: "sec-filings", policy: "secFilings", entityKey: this.deps.getEntityKey(symbol, requestContext?.instrument),
225
- variants: [...new Set([buildVariantKey([["exchange", canonicalExchange(listingExchange)], ["count", count]]), buildVariantKey([["count", count]]), ""])],
227
+ // A non-US listing never reads the bare symbol's filings: SAN in Paris is Sanofi, SAN at the SEC is Santander.
228
+ variants: nonUsSecListingVenue(symbol, listingExchange) ? [exact] : [...new Set([exact, buildVariantKey([["count", count]]), ""])],
226
229
  request: (provider, force) => provider.getSecFilings?.(symbol, count, listingExchange, force ? { ...requestContext, cacheMode: "refresh" } : requestContext),
227
230
  throwLastError: true, error: `No SEC filings provider available for ${symbol}`,
228
231
  };
@@ -7,7 +7,7 @@ import { redactUnavailableFundamentals, RETRACTABLE_VALUATION_FIELDS } from "../
7
7
  import { isExtendedHoursExchange, isQuoteStaleForCurrentSession } from "../../market-data/quotes/freshness";
8
8
  import { mergeQuoteMetadata, quoteMetadataFromQuote, quoteMetadataMatchesTarget } from "../../market-data/quotes/metadata";
9
9
  import { parsePublicTickerKey } from "../../utils/exchanges";
10
- import { activeUsMarketSession, isUsPriorSessionPremarketQuote } from "../../market-data/market/freshness";
10
+ import { activeUsMarketSession, delayedFeedLagMs, isMiddayBreakPrint, isUsPriorSessionPremarketQuote } from "../../market-data/market/freshness";
11
11
  import {
12
12
  mergeQuoteContributionMaps,
13
13
  isQuoteContributionStaleForCurrentSession,
@@ -92,7 +92,7 @@ export function sanitizeCachedFinancials(
92
92
  }
93
93
 
94
94
  export function quoteWithFreshnessExchange(quote: Quote, exchange?: string): Quote {
95
- if (!exchange || quote.listingExchangeName || quote.exchangeName) return quote;
95
+ if (!exchange || quote.priceObservation != null || quote.listingExchangeName || quote.exchangeName) return quote;
96
96
  return {
97
97
  ...quote,
98
98
  listingExchangeName: exchange,
@@ -123,18 +123,21 @@ function isQuoteInActiveSession(quote: Quote, now: number): boolean {
123
123
  }
124
124
 
125
125
  function isActiveProviderQuoteTooOld(quote: Quote, now = Date.now()): boolean {
126
+ // The shared freshness predicate already validates and dates a daily NAV.
127
+ if (quote.priceObservation === "nav") return false;
126
128
  if (!isQuoteInActiveSession(quote, now)) return false;
127
129
  // A prior-session close before any pre-market trade has no in-session print
128
130
  // to age; the session-date rules bound it instead.
129
- if (isUsPriorSessionPremarketQuote(quote.lastUpdated, quote.listingExchangeName || quote.exchangeName, quote.marketState, now)) {
131
+ const exchange = quote.listingExchangeName || quote.exchangeName;
132
+ if (isUsPriorSessionPremarketQuote(quote.lastUpdated, exchange, quote.marketState, now)) {
130
133
  return false;
131
134
  }
132
135
  if (!Number.isFinite(quote.lastUpdated)) return false;
133
- const maxAge =
134
- quote.dataSource === "delayed"
135
- ? ACTIVE_DELAYED_PROVIDER_QUOTE_MAX_AGE_MS
136
- : ACTIVE_PROVIDER_QUOTE_MAX_AGE_MS;
137
- return now - quote.lastUpdated > maxAge;
136
+ const delayed = quote.dataSource === "delayed";
137
+ const maxAge = delayed ? ACTIVE_DELAYED_PROVIDER_QUOTE_MAX_AGE_MS : ACTIVE_PROVIDER_QUOTE_MAX_AGE_MS;
138
+ if (now - quote.lastUpdated <= maxAge) return false;
139
+ // While the venue pauses at midday, the morning's last print is the current price.
140
+ return !isMiddayBreakPrint(quote.lastUpdated, exchange, now, delayed ? delayedFeedLagMs(exchange) : 0);
138
141
  }
139
142
 
140
143
  export function providerQuoteMatchesTarget(quote: Quote | null | undefined, symbol?: string, exchange?: string): quote is Quote {
@@ -19,6 +19,7 @@ import { subtractTimeRange } from "../../time-series/date-window";
19
19
  import { clipPriceHistoryToRange } from "../../time-series/history-window";
20
20
  import { repairIsolatedIntradayOhlcOutliers } from "../../time-series/history-quality";
21
21
  import { canonicalExchange, parsePublicTickerKey, resolveExchangeTimeZone } from "../../utils/exchanges";
22
+ import { isRoundTheClockCoin } from "../../utils/crypto-pair";
22
23
  import { zonedDateKey } from "../../utils/zoned-date-time";
23
24
  import { resolvePriceHistoryCurrencyUnit } from "../../utils/currency-units";
24
25
  import { calendarHistoryFetchState, calendarHistoryLastBarDate, dropLeadingPlaceholderBars, getPricePointTimestamp, hasUsablePriceHistory, preservePriceHistoryGaps, isPriceHistoryStaleForCurrentWindow, normalizePriceHistory, priceHistoryIntervalMs, type CalendarHistoryFetchState } from "../../utils/price-history";
@@ -245,9 +246,9 @@ function normalizeRequestResult(value: PriceHistoryResult, request: HistoryReque
245
246
  };
246
247
  }
247
248
 
248
- function resultIsStale(value: PriceHistoryResult, exchange: string, intervalMs?: number | null): boolean {
249
+ function resultIsStale(value: PriceHistoryResult, symbol: string, exchange: string, intervalMs?: number | null): boolean {
249
250
  return isPriceHistoryStaleForCurrentWindow(value.points, Date.now(), {
250
- exchange, intervalMs: intervalMs ?? (value.resolution ? priceHistoryIntervalMs(value.resolution) : undefined), session: value.session,
251
+ symbol, exchange, intervalMs: intervalMs ?? (value.resolution ? priceHistoryIntervalMs(value.resolution) : undefined), session: value.session,
251
252
  });
252
253
  }
253
254
 
@@ -342,8 +343,8 @@ export class ProviderRouterHistoryRoutes {
342
343
  context,
343
344
  cachePolicyKey: intraday ? "priceHistoryIntraday" : "priceHistoryDaily",
344
345
  missingProviderError: `No history provider available for ${ticker}`,
345
- isCachedValueStale: (value) => intraday && resultIsStale(value, exchange),
346
- isFetchedValueStale: (value) => intraday && resultIsStale(value, exchange),
346
+ isCachedValueStale: (value) => intraday && resultIsStale(value, ticker, exchange),
347
+ isFetchedValueStale: (value) => intraday && resultIsStale(value, ticker, exchange),
347
348
  fetchBroker: async (candidate) => candidate.broker.getPriceHistory
348
349
  ? candidate.broker.getPriceHistory(
349
350
  ticker,
@@ -399,8 +400,8 @@ export class ProviderRouterHistoryRoutes {
399
400
  context,
400
401
  cachePolicyKey: intraday ? "priceHistoryIntraday" : "priceHistoryDaily",
401
402
  missingProviderError: `No resolution-aware history provider available for ${ticker}`,
402
- isCachedValueStale: (value) => intraday && resultIsStale(value, exchange, intervalMs),
403
- isFetchedValueStale: (value) => intraday && resultIsStale(value, exchange, intervalMs),
403
+ isCachedValueStale: (value) => intraday && resultIsStale(value, ticker, exchange, intervalMs),
404
+ isFetchedValueStale: (value) => intraday && resultIsStale(value, ticker, exchange, intervalMs),
404
405
  fetchBroker: async (candidate) => candidate.broker.getPriceHistoryForResolution
405
406
  ? candidate.broker.getPriceHistoryForResolution(
406
407
  ticker,
@@ -515,9 +516,9 @@ export class ProviderRouterHistoryRoutes {
515
516
  context,
516
517
  cachePolicyKey: intervalMs != null && intervalMs >= 24 * 60 * 60 * 1000 ? "priceHistoryDaily" : "priceHistoryIntraday",
517
518
  isCachedValueStale: (value) => currentWindowAtLookup
518
- && resultIsStale(value, exchange, intervalMs),
519
+ && resultIsStale(value, ticker, exchange, intervalMs),
519
520
  isFetchedValueStale: (value) => isCurrentHistoryWindow(endDate)
520
- && resultIsStale(value, exchange, intervalMs),
521
+ && resultIsStale(value, ticker, exchange, intervalMs),
521
522
  fetchBroker: async (candidate) => candidate.broker.getDetailedPriceHistory
522
523
  ? candidate.broker.getDetailedPriceHistory(
523
524
  ticker,
@@ -568,6 +569,7 @@ export class ProviderRouterHistoryRoutes {
568
569
  const now = Date.now();
569
570
  const fetchState = (record: { fetchedAt: number; value: PriceHistoryResult }, checkedAt?: number): CalendarHistoryFetchState => currentWindow
570
571
  ? calendarHistoryFetchState(record.value.points, record.fetchedAt, now, {
572
+ symbol: target.symbol,
571
573
  exchange: target.exchange || request.target.exchange,
572
574
  checkedAt,
573
575
  // Bar size comes from the request: a cached weekly or monthly series
@@ -580,7 +582,7 @@ export class ProviderRouterHistoryRoutes {
580
582
  const ranked = cachedRecords.filter((record) => hasUsablePriceHistory(record.value.points)).map((record) => {
581
583
  const tier = fetchTier[fetchState(record)];
582
584
  return { record, tier, exact: request.exactCacheVariantKeys.includes(record.variantKey),
583
- lastBar: tier === 0 ? "" : calendarHistoryLastBarDate(record.value.points, target.exchange || request.target.exchange) ?? "" };
585
+ lastBar: tier === 0 ? "" : calendarHistoryLastBarDate(record.value.points, target.exchange || request.target.exchange, target.symbol) ?? "" };
584
586
  });
585
587
  // Current copies keep their listed order. Otherwise ties go to this key's
586
588
  // own copy, then the latest fetch, rather than to row order.
@@ -657,7 +659,10 @@ export class ProviderRouterHistoryRoutes {
657
659
  throw new HistoryRetentionError(candidates[0]?.retention ?? attempts.retention, { candidates, outcomes: [...attempts.outcomes.values()] });
658
660
  }
659
661
  if (!providerResult && request.missingProviderError && !reportedGaps.some((value) => value.points.length > 0)) {
660
- throw new Error(request.missingProviderError);
662
+ // A source answered a coin's history but it stops short of the present: say so, not that nothing answered.
663
+ const behind = isRoundTheClockCoin(target.symbol, target.exchange || request.target.exchange)
664
+ && [...attempts.outcomes.values()].some((outcome) => outcome.outcome === "stale");
665
+ throw new Error(behind ? `Latest history for ${target.symbol} is behind` : request.missingProviderError);
661
666
  }
662
667
  return withReportedGaps(providerResult?.value ?? { points: [], resolution: historyResolutionForInterval(request.interval) });
663
668
  }
@@ -0,0 +1,75 @@
1
+ import type { SecFilingItem } from "../types/data-provider";
2
+ import { ProviderMissError } from "./provider-errors";
3
+
4
+ /** Legal forms and grouping words, which say nothing about which company a name is. */
5
+ const COMPANY_NAME_FILLER = new Set([
6
+ "THE", "SA", "PLC", "LTD", "LIMITED", "CORP", "CORPORATION", "INC", "INCORPORATED", "NV", "AG", "SE",
7
+ "HOLDING", "HOLDINGS", "GROUP", "COMPANY", "CO",
8
+ ]);
9
+
10
+ /**
11
+ * The first word of a company name that tells it apart, in capitals without
12
+ * accents: "BANCO" for "Banco Santander, S.A.", "BP" for "BP p.l.c.". Null
13
+ * when the name is only legal forms.
14
+ */
15
+ function companyNameToken(name: string | null | undefined): string | null {
16
+ const words = (name ?? "")
17
+ .normalize("NFKD")
18
+ .replace(/[\u0300-\u036f]/g, "")
19
+ .toUpperCase()
20
+ // An initialism (S.A., N.V., p.l.c.) is one word; any other dot separates two (Amazon.com).
21
+ .replace(/\b(?:[A-Z]\.){2,}/g, (initialism) => initialism.replaceAll(".", ""))
22
+ .replace(/['\u2019]/g, "")
23
+ .split(/[^A-Z0-9]+/);
24
+ return words.find((word) => word && !COMPANY_NAME_FILLER.has(word)) ?? null;
25
+ }
26
+
27
+ /**
28
+ * Whether two names are clearly different companies. Names that cannot be
29
+ * compared count as the same, so only a plain conflict hides anything.
30
+ */
31
+ export function areDifferentCompanies(left: string | null | undefined, right: string | null | undefined): boolean {
32
+ const leftToken = companyNameToken(left);
33
+ const rightToken = companyNameToken(right);
34
+ return !!leftToken && !!rightToken && leftToken !== rightToken;
35
+ }
36
+
37
+ export interface SecListing {
38
+ /** The bare symbol, such as SAN. */
39
+ symbol: string;
40
+ /** The listing's canonical venue, such as EPA. */
41
+ exchange: string;
42
+ /** The listing's company, from its own quote. */
43
+ name: string;
44
+ }
45
+
46
+ /** "No SEC filings found for Sanofi (EPA). The SEC lists SAN as Banco Santander, S.A. (NYSE)." */
47
+ export function secRegistrantMismatchMessage(listing: SecListing, registrantName: string, registrantExchange?: string | null): string {
48
+ const registrant = registrantExchange ? `${registrantName} (${registrantExchange})` : registrantName;
49
+ return `No SEC filings found for ${listing.name} (${listing.exchange}). The SEC lists ${listing.symbol} as ${registrant}${registrant.endsWith(".") ? "" : "."}`;
50
+ }
51
+
52
+ /** The SEC knows the listing's symbol as another company: SAN on EPA is Sanofi, SAN at the SEC is Banco Santander. */
53
+ export class SecRegistrantMismatchError extends ProviderMissError {
54
+ constructor(readonly listing: SecListing, readonly registrantName: string) {
55
+ super(secRegistrantMismatchMessage(listing, registrantName));
56
+ this.name = "SecRegistrantMismatchError";
57
+ }
58
+ }
59
+
60
+ /** The company the filings were filed by. */
61
+ function registrantName(filings: readonly SecFilingItem[]): string | null {
62
+ return filings.map((filing) => filing.companyName?.trim()).find((name): name is string => !!name) ?? null;
63
+ }
64
+
65
+ /**
66
+ * A lookup for a listing outside the US can answer with the US registrant of
67
+ * the same symbol: a backend that predates venue-aware lookups ignores the
68
+ * venue. Those filings are another company's, so they are refused.
69
+ */
70
+ export function assertSecRegistrantMatches(filings: readonly SecFilingItem[], listing: SecListing): void {
71
+ const registrant = registrantName(filings);
72
+ if (registrant && areDifferentCompanies(listing.name, registrant)) {
73
+ throw new SecRegistrantMismatchError(listing, registrant);
74
+ }
75
+ }
@@ -44,6 +44,7 @@ import {
44
44
  import type { AppAction, AppState } from "../../../core/state/app/state";
45
45
  import { scheduleConfigSave } from "../../config-save-scheduler";
46
46
  import { LOW_PRIORITY_CONFIG_SAVE_DEBOUNCE_MS } from "../../persist-scheduler";
47
+ import { fireAndForget } from "../../../utils/fire-and-forget";
47
48
 
48
49
  export {
49
50
  appReducer,
@@ -565,10 +566,16 @@ export function AppProvider({
565
566
  if (signature === lastMainSyncRef.current) return;
566
567
  lastMainSyncRef.current = signature;
567
568
  latestMainStateRevisionRef.current += 1;
568
- void desktopBridge.syncMainState({
569
- ...snapshot,
570
- mainStateRevision: latestMainStateRevisionRef.current,
571
- });
569
+ // A snapshot that did not arrive must not count as sent. Forget it, unless
570
+ // a newer one has replaced it, so the next effect run sends it again. The
571
+ // two syncs below dedupe the same way.
572
+ fireAndForget(
573
+ desktopBridge.syncMainState({ ...snapshot, mainStateRevision: latestMainStateRevisionRef.current }),
574
+ "desktop.syncMainState",
575
+ () => {
576
+ if (lastMainSyncRef.current === signature) lastMainSyncRef.current = null;
577
+ },
578
+ );
572
579
  }, [
573
580
  buildDesktopSnapshot,
574
581
  desktopBridge,
@@ -581,9 +588,12 @@ export function AppProvider({
581
588
 
582
589
  useEffect(() => {
583
590
  if (desktopBridge?.kind !== "main" || !desktopBridge.syncThemePreview) return;
584
- if (lastThemePreviewSyncRef.current === state.themePreview) return;
585
- lastThemePreviewSyncRef.current = state.themePreview;
586
- void desktopBridge.syncThemePreview({ theme: state.themePreview });
591
+ const theme = state.themePreview;
592
+ if (lastThemePreviewSyncRef.current === theme) return;
593
+ lastThemePreviewSyncRef.current = theme;
594
+ fireAndForget(desktopBridge.syncThemePreview({ theme }), "desktop.setThemePreview", () => {
595
+ if (lastThemePreviewSyncRef.current === theme) lastThemePreviewSyncRef.current = undefined;
596
+ });
587
597
  }, [desktopBridge, state.themePreview]);
588
598
 
589
599
  useEffect(() => {
@@ -598,7 +608,13 @@ export function AppProvider({
598
608
  const paneSignature = serializePaneState(state.paneState[desktopBridge.paneId]);
599
609
  if (paneSignature === lastDetachedPaneSyncRef.current) return;
600
610
  lastDetachedPaneSyncRef.current = paneSignature;
601
- void desktopBridge.replaceDetachedPaneState(desktopBridge.paneId, state.paneState[desktopBridge.paneId] ?? {});
611
+ fireAndForget(
612
+ desktopBridge.replaceDetachedPaneState(desktopBridge.paneId, state.paneState[desktopBridge.paneId] ?? {}),
613
+ "desktop.replaceDetachedPaneState",
614
+ () => {
615
+ if (lastDetachedPaneSyncRef.current === paneSignature) lastDetachedPaneSyncRef.current = null;
616
+ },
617
+ );
602
618
  }, [desktopBridge, serializePaneState, state.paneState]);
603
619
 
604
620
  useLayoutEffect(() => {
@@ -106,21 +106,28 @@ export function useQuoteStreaming(
106
106
  const paneInView = usePaneInView();
107
107
  const coordinator = getSharedMarketDataCoordinator();
108
108
 
109
- const normalizedEntries = new Map<string, QuoteSubscriptionTarget>();
110
- for (const target of targets) {
111
- const resolved = normalizeQuoteStreamSubscriptionTarget(target);
112
- if (!resolved) continue;
113
- const normalized = paneInView ? resolved : downgradeOffscreenQuoteTarget(resolved);
114
- const key = buildQuoteStreamSubscriptionKey(normalized);
115
- normalizedEntries.set(key, normalized);
116
- }
117
- const sortedEntries = [...normalizedEntries.entries()].sort(([left], [right]) => left.localeCompare(right));
118
- const normalizedTargets = sortedEntries.map(([, target]) => target);
119
- const subscriptionKey = sortedEntries.map(([key]) => key).join("|");
120
- const identityKey = [...new Set(normalizedTargets.map(buildQuoteStreamSubscriptionIdentityKey))]
121
- .sort()
122
- .join("\u001f");
123
- const coordinatorTargets = toCoordinatorQuoteTargets(normalizedTargets);
109
+ // Keyed on the array, not its contents: callers memoize their targets, and a
110
+ // portfolio re-renders on every data frame, where re-keying and sorting a
111
+ // thousand targets cost a few milliseconds each time.
112
+ const { coordinatorTargets, identityKey, subscriptionKey } = useMemo(() => {
113
+ const normalizedEntries = new Map<string, QuoteSubscriptionTarget>();
114
+ for (const target of targets) {
115
+ const resolved = normalizeQuoteStreamSubscriptionTarget(target);
116
+ if (!resolved) continue;
117
+ const normalized = paneInView ? resolved : downgradeOffscreenQuoteTarget(resolved);
118
+ const key = buildQuoteStreamSubscriptionKey(normalized);
119
+ normalizedEntries.set(key, normalized);
120
+ }
121
+ const sortedEntries = [...normalizedEntries.entries()].sort(([left], [right]) => left.localeCompare(right));
122
+ const normalizedTargets = sortedEntries.map(([, target]) => target);
123
+ return {
124
+ coordinatorTargets: toCoordinatorQuoteTargets(normalizedTargets),
125
+ identityKey: [...new Set(normalizedTargets.map(buildQuoteStreamSubscriptionIdentityKey))]
126
+ .sort()
127
+ .join("\u001f"),
128
+ subscriptionKey: sortedEntries.map(([key]) => key).join("|"),
129
+ };
130
+ }, [paneInView, targets]);
124
131
  const latestSubscriptionRef = useRef({
125
132
  identityKey,
126
133
  subscriptionKey,
@@ -208,11 +215,16 @@ export function useQuoteUpdates(
208
215
  ): void {
209
216
  const appActive = useAppVisible();
210
217
  const coordinator = getSharedMarketDataCoordinator();
211
- const normalizedTargets = normalizeQuoteStreamSubscriptionTargets(targets);
212
- const instrumentKey = normalizedTargets
213
- .map((target) => buildQuoteKey(instrumentFromQuoteTarget(target)))
214
- .sort()
215
- .join("\u001f");
218
+ const { instrumentKey, normalizedTargets } = useMemo(() => {
219
+ const normalized = normalizeQuoteStreamSubscriptionTargets(targets);
220
+ return {
221
+ instrumentKey: normalized
222
+ .map((target) => buildQuoteKey(instrumentFromQuoteTarget(target)))
223
+ .sort()
224
+ .join("\u001f"),
225
+ normalizedTargets: normalized,
226
+ };
227
+ }, [targets]);
216
228
  const instruments = useMemo(() => uniqueQuoteInstruments(normalizedTargets), [instrumentKey]);
217
229
 
218
230
  useQuoteStreaming(targets, { enabled: liveStreaming });
@@ -270,12 +282,15 @@ export function useLiveQuoteEntries(
270
282
  subscriptionStartedAt: number;
271
283
  } {
272
284
  const appActive = useAppVisible();
273
- const normalizedTargets = normalizeQuoteStreamSubscriptionTargets(targets);
274
- const targetKey = [...new Set(
275
- normalizedTargets.map((target) => buildQuoteStreamSubscriptionIdentityKey(target)),
276
- )]
277
- .sort()
278
- .join("\u001f");
285
+ const { normalizedTargets, targetKey } = useMemo(() => {
286
+ const normalized = normalizeQuoteStreamSubscriptionTargets(targets);
287
+ return {
288
+ normalizedTargets: normalized,
289
+ targetKey: [...new Set(normalized.map((target) => buildQuoteStreamSubscriptionIdentityKey(target)))]
290
+ .sort()
291
+ .join("\u001f"),
292
+ };
293
+ }, [targets]);
279
294
  const liveStreaming = options.liveStreaming !== false;
280
295
  const freshnessKey = options.freshnessScopeKey ?? targetKey;
281
296
  const subscriptionKey = `${appActive ? "active" : "inactive"}\u001f${liveStreaming ? "live" : "poll"}\u001f${freshnessKey}`;
@@ -102,6 +102,10 @@ function sanitizePortfolio(portfolio: Portfolio): Portfolio {
102
102
  currency: portfolio.currency,
103
103
  brokerId: portfolio.brokerId,
104
104
  lastSyncedAt: portfolio.lastSyncedAt,
105
+ // Cash and targets are the user's own entries: a pulled portfolio without
106
+ // them would erase them on this device.
107
+ ...(portfolio.cash ? { cash: { ...portfolio.cash } } : {}),
108
+ ...(portfolio.targetWeights ? { targetWeights: { ...portfolio.targetWeights } } : {}),
105
109
  };
106
110
  }
107
111