gloomberb 0.10.4 → 0.11.0

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 (555) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +30 -3
  3. package/README.zh-CN.md +1 -1
  4. package/package.json +5 -5
  5. package/src/api-client/auth.ts +34 -0
  6. package/src/api-client/data.ts +25 -0
  7. package/src/api-client/index.test.ts +81 -0
  8. package/src/api-client/index.ts +37 -0
  9. package/src/api-client/request.test.ts +30 -0
  10. package/src/api-client/request.ts +44 -20
  11. package/src/api-client/socket-health.test.ts +79 -0
  12. package/src/api-client/socket.ts +108 -5
  13. package/src/api-client/types.ts +139 -0
  14. package/src/app/global-shortcuts.test.tsx +144 -9
  15. package/src/app/global-shortcuts.ts +33 -11
  16. package/src/app/runtime/broker-import.ts +39 -5
  17. package/src/app/runtime/startup.ts +7 -1
  18. package/src/app/runtime/update.ts +26 -1
  19. package/src/app.tsx +36 -19
  20. package/src/brokers/broker-ticker-sync.ts +3 -8
  21. package/src/brokers/sync-broker-instance.test.ts +59 -0
  22. package/src/brokers/sync-broker-instance.ts +37 -13
  23. package/src/capabilities/chart-series.ts +318 -0
  24. package/src/capabilities/factories.ts +46 -0
  25. package/src/capabilities/index.ts +1 -0
  26. package/src/capabilities/registry.test.ts +135 -0
  27. package/src/capabilities/registry.ts +31 -4
  28. package/src/capabilities/types.ts +52 -0
  29. package/src/cli/commands/overview.ts +2 -2
  30. package/src/cli/desktop-pane-shot.ts +2 -0
  31. package/src/cli/entry.ts +11 -1
  32. package/src/cli/native-smoke.ts +5 -0
  33. package/src/cli/pane-functions/catalog.ts +4 -0
  34. package/src/cli/pane-functions/data.ts +6 -1
  35. package/src/cli/pane-functions/discovery.ts +76 -10
  36. package/src/cli/pane-functions/report.test.ts +76 -0
  37. package/src/cli/pane-functions/report.ts +7 -1
  38. package/src/cli/pane-functions/resolver.ts +4 -1
  39. package/src/cli/pane-functions/screenshot.test.ts +56 -3
  40. package/src/cli/pane-functions/screenshot.ts +68 -5
  41. package/src/components/chart/composite/composite-chart.test.tsx +54 -0
  42. package/src/components/chart/composite/composite-chart.tsx +92 -15
  43. package/src/components/chart/composite/downsample.test.ts +136 -0
  44. package/src/components/chart/composite/downsample.ts +208 -0
  45. package/src/components/chart/composite/panel-series.test.ts +66 -0
  46. package/src/components/chart/composite/panel-series.ts +101 -0
  47. package/src/components/chart/multi-line/renderer.ts +1 -0
  48. package/src/components/chart/native/kitty/index.test.ts +25 -3
  49. package/src/components/chart/native/kitty/support.ts +37 -3
  50. package/src/components/chart/native/use-chart-text-fallback.ts +19 -0
  51. package/src/components/chart/static/chart/surface.tsx +43 -12
  52. package/src/components/chart/static/multi-line-chart-surface.tsx +41 -5
  53. package/src/components/command-bar/commands/direct/index.ts +18 -0
  54. package/src/components/command-bar/commands/registry.ts +14 -0
  55. package/src/components/command-bar/routes/root/article-results.test.ts +83 -0
  56. package/src/components/command-bar/routes/root/article-results.ts +55 -0
  57. package/src/components/command-bar/routes/root/command-items.ts +3 -0
  58. package/src/components/command-bar/routes/root/provider-search.ts +7 -3
  59. package/src/components/command-bar/routes/root/results.ts +38 -11
  60. package/src/components/command-bar/routes/root/runtime.ts +6 -2
  61. package/src/components/command-bar/routes/ticker-search/actions.ts +1 -1
  62. package/src/components/command-bar/routes/ticker-search/results.test.ts +34 -0
  63. package/src/components/command-bar/routes/ticker-search/results.ts +4 -6
  64. package/src/components/command-bar/routes/ticker-search/route.ts +2 -2
  65. package/src/components/command-bar/routing/list-state.ts +2 -1
  66. package/src/components/command-bar/surface/assist.test.tsx +13 -6
  67. package/src/components/command-bar/surface/index.test.tsx +65 -0
  68. package/src/components/command-bar/surface/index.tsx +35 -0
  69. package/src/components/command-bar/view-model.test.ts +43 -0
  70. package/src/components/command-bar/view-model.ts +41 -4
  71. package/src/components/command-bar/workflow/collection-actions.test.ts +94 -0
  72. package/src/components/command-bar/workflow/collection-actions.ts +14 -3
  73. package/src/components/command-bar/workflow/ops.test.ts +136 -0
  74. package/src/components/command-bar/workflow/ops.ts +71 -1
  75. package/src/components/index.ts +1 -1
  76. package/src/components/layout/header.test.tsx +35 -0
  77. package/src/components/layout/header.tsx +101 -9
  78. package/src/components/layout/pane/cycle-order.test.ts +0 -1
  79. package/src/components/layout/pane/footer/index.test.tsx +19 -19
  80. package/src/components/layout/pane/header.tsx +2 -13
  81. package/src/components/layout/shell/index.tsx +3 -0
  82. package/src/components/layout/status-bar.test.tsx +36 -1
  83. package/src/components/layout/status-bar.tsx +48 -2
  84. package/src/components/onboarding/account-step/chooser-panel.tsx +27 -50
  85. package/src/components/onboarding/account-step/form-panel.tsx +85 -29
  86. package/src/components/onboarding/account-step/index.tsx +10 -1
  87. package/src/components/onboarding/account-step/model.ts +3 -3
  88. package/src/components/onboarding/account-step/qr-panel.tsx +68 -0
  89. package/src/components/onboarding/account-step/signed-in-panel.tsx +4 -7
  90. package/src/components/onboarding/onboarding-frame.tsx +517 -0
  91. package/src/components/onboarding/onboarding-steps.tsx +0 -264
  92. package/src/components/onboarding/onboarding-wizard.test.tsx +626 -200
  93. package/src/components/onboarding/onboarding-wizard.tsx +848 -266
  94. package/src/components/onboarding/portfolio-step/broker-fields-panel.tsx +81 -51
  95. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +14 -20
  96. package/src/components/onboarding/portfolio-step/broker-sync-panel.tsx +8 -11
  97. package/src/components/onboarding/portfolio-step/choose-panel.tsx +18 -22
  98. package/src/components/onboarding/portfolio-step/index.tsx +3 -0
  99. package/src/components/onboarding/portfolio-step/types.ts +3 -0
  100. package/src/components/onboarding/wizard-account.ts +38 -3
  101. package/src/components/onboarding/wizard-broker-sync.ts +45 -67
  102. package/src/components/onboarding/wizard-model.ts +18 -2
  103. package/src/components/ui/data-table/opentui/index.tsx +29 -9
  104. package/src/components/ui/data-table/types.ts +2 -0
  105. package/src/components/ui/external-link.tsx +4 -2
  106. package/src/components/ui/loading.tsx +1 -1
  107. package/src/components/ui/status.tsx +76 -4
  108. package/src/components/ui/table-layout.test.ts +24 -0
  109. package/src/components/ui/table-layout.ts +90 -23
  110. package/src/components/ui/tabs.tsx +86 -4
  111. package/src/core/app-services.ts +14 -3
  112. package/src/core/connection-health.test.ts +143 -0
  113. package/src/core/connection-health.ts +295 -0
  114. package/src/core/state/app/layout-reducer.ts +34 -0
  115. package/src/core/state/app/layout.ts +21 -0
  116. package/src/core/state/app/state.ts +10 -0
  117. package/src/core/state/app/types.ts +7 -1
  118. package/src/data/config/chart-settings.ts +11 -2
  119. package/src/data/config/store/index.test.ts +83 -9
  120. package/src/data/config/store/migrations.ts +16 -0
  121. package/src/data/config/store/normalize.ts +59 -1
  122. package/src/data/fred-series.ts +8 -2
  123. package/src/i18n/es.ts +126 -0
  124. package/src/i18n/ja.ts +126 -0
  125. package/src/i18n/ko.ts +126 -0
  126. package/src/i18n/zh-cn.ts +126 -0
  127. package/src/i18n/zh-tw.ts +126 -0
  128. package/src/market-data/coordinator/chart.ts +3 -2
  129. package/src/market-data/hooks.tsx +3 -2
  130. package/src/market-data/market/status.test.ts +12 -0
  131. package/src/market-data/market/status.ts +34 -17
  132. package/src/news/aggregator.test.ts +39 -0
  133. package/src/news/aggregator.ts +78 -22
  134. package/src/news/hooks.ts +4 -0
  135. package/src/notifications/app-notifier.test.ts +28 -6
  136. package/src/notifications/app-notifier.ts +24 -5
  137. package/src/plugins/broker-sync/broker-sync.test.ts +130 -0
  138. package/src/plugins/broker-sync/native-loader.ts +10 -0
  139. package/src/plugins/broker-sync/normalize.ts +271 -0
  140. package/src/plugins/broker-sync/public.ts +122 -0
  141. package/src/plugins/broker-sync/robinhood-native.ts +367 -0
  142. package/src/plugins/broker-sync/robinhood.ts +114 -0
  143. package/src/plugins/broker-sync/simplefin-native.ts +201 -0
  144. package/src/plugins/broker-sync/simplefin.ts +70 -0
  145. package/src/plugins/builtin/account-management/footer.ts +8 -19
  146. package/src/plugins/builtin/account-management/pane.tsx +13 -8
  147. package/src/plugins/builtin/ai/pi/host.ts +6 -5
  148. package/src/plugins/builtin/ai/providers.ts +1 -1
  149. package/src/plugins/builtin/ai/screener/footer.ts +0 -7
  150. package/src/plugins/builtin/ai/screener/pane.test.tsx +2 -1
  151. package/src/plugins/builtin/ai/screener/pane.tsx +3 -1
  152. package/src/plugins/builtin/ai/screener/results-view.tsx +8 -0
  153. package/src/plugins/builtin/ai/screener/runner.ts +7 -3
  154. package/src/plugins/builtin/ai/workspace/pane.tsx +13 -2
  155. package/src/plugins/builtin/alerts/alert-engine.ts +13 -0
  156. package/src/plugins/builtin/alerts/constants.ts +2 -1
  157. package/src/plugins/builtin/alerts/index.tsx +67 -36
  158. package/src/plugins/builtin/alerts/pane.tsx +17 -65
  159. package/src/plugins/builtin/analytics/index.tsx +24 -11
  160. package/src/plugins/builtin/analytics/pane-model.ts +48 -14
  161. package/src/plugins/builtin/analytics/portfolio-selection.ts +19 -0
  162. package/src/plugins/builtin/analytics/view.tsx +5 -3
  163. package/src/plugins/builtin/broker-manager/detail.tsx +7 -2
  164. package/src/plugins/builtin/broker-manager/footer.ts +4 -9
  165. package/src/plugins/builtin/broker-manager/index.tsx +1 -6
  166. package/src/plugins/builtin/buildout/detail/ui.tsx +15 -4
  167. package/src/plugins/builtin/buildout/pane/header.tsx +1 -0
  168. package/src/plugins/builtin/changelog/index.tsx +60 -45
  169. package/src/plugins/builtin/chart-composer/catalog-inventory.test.ts +97 -0
  170. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +366 -0
  171. package/src/plugins/builtin/chart-composer/chart-spec.ts +21 -1
  172. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +307 -0
  173. package/src/plugins/builtin/chart-composer/editor/controller.ts +1 -1
  174. package/src/plugins/builtin/chart-composer/index.tsx +45 -5
  175. package/src/plugins/builtin/chart-composer/pane.tsx +7 -88
  176. package/src/plugins/builtin/chart-composer/presets.test.ts +48 -0
  177. package/src/plugins/builtin/chart-composer/presets.ts +69 -3
  178. package/src/plugins/builtin/chart-composer/quick-add.tsx +8 -5
  179. package/src/plugins/builtin/chart-composer/semantic.ts +18 -14
  180. package/src/plugins/builtin/chart-composer/series-catalog.test.ts +39 -0
  181. package/src/plugins/builtin/chart-composer/series-catalog.ts +104 -3
  182. package/src/plugins/builtin/chart-composer/shortcuts.test.ts +10 -10
  183. package/src/plugins/builtin/chart-composer/shortcuts.ts +4 -12
  184. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +230 -23
  185. package/src/plugins/builtin/chat/chat.test.tsx +22 -0
  186. package/src/plugins/builtin/chat/content/composer.tsx +6 -4
  187. package/src/plugins/builtin/chat/content/index.tsx +7 -0
  188. package/src/plugins/builtin/chat/content/snapshot.ts +9 -2
  189. package/src/plugins/builtin/chat/content/transcript.tsx +10 -0
  190. package/src/plugins/builtin/chat/controller/channel-actions.ts +11 -1
  191. package/src/plugins/builtin/chat/controller/channels.ts +13 -3
  192. package/src/plugins/builtin/chat/controller/index.ts +61 -7
  193. package/src/plugins/builtin/chat/controller/lifecycle.ts +3 -0
  194. package/src/plugins/builtin/chat/controller/message-loading.ts +5 -1
  195. package/src/plugins/builtin/chat/controller/messages.ts +3 -1
  196. package/src/plugins/builtin/chat/controller/notifications.ts +32 -11
  197. package/src/plugins/builtin/chat/controller/state.ts +7 -0
  198. package/src/plugins/builtin/chat/controller/view.ts +1 -0
  199. package/src/plugins/builtin/chat/controller.test.ts +117 -28
  200. package/src/plugins/builtin/cloud/auth-commands.ts +18 -0
  201. package/src/plugins/builtin/cloud/device-signin-dialog.tsx +243 -0
  202. package/src/plugins/builtin/cloud/device-signin.test.ts +217 -0
  203. package/src/plugins/builtin/cloud/device-signin.ts +240 -0
  204. package/src/plugins/builtin/cloud/plugin.tsx +7 -1
  205. package/src/plugins/builtin/cloud-tweets/table.tsx +57 -10
  206. package/src/plugins/builtin/composite-plugins.ts +39 -5
  207. package/src/plugins/builtin/congress-trades/detail.tsx +2 -4
  208. package/src/plugins/builtin/congress-trades/footer.ts +7 -16
  209. package/src/plugins/builtin/congress-trades/pane.tsx +12 -20
  210. package/src/plugins/builtin/connections/index.ts +53 -0
  211. package/src/plugins/builtin/connections/pane.test.tsx +79 -0
  212. package/src/plugins/builtin/connections/pane.tsx +258 -0
  213. package/src/plugins/builtin/correlation/index.test.tsx +2 -1
  214. package/src/plugins/builtin/correlation/index.tsx +58 -19
  215. package/src/plugins/builtin/correlation/matrix/model.ts +18 -7
  216. package/src/plugins/builtin/correlation/relationship/pane.tsx +43 -19
  217. package/src/plugins/builtin/correlation/settings.ts +1 -1
  218. package/src/plugins/builtin/credit-conditions/client.test.ts +80 -0
  219. package/src/plugins/builtin/credit-conditions/client.ts +105 -0
  220. package/src/plugins/builtin/credit-conditions/index.test.tsx +111 -0
  221. package/src/plugins/builtin/credit-conditions/index.tsx +227 -0
  222. package/src/plugins/builtin/credit-conditions/model.test.ts +43 -0
  223. package/src/plugins/builtin/credit-conditions/model.ts +64 -0
  224. package/src/plugins/builtin/debug/index.tsx +11 -6
  225. package/src/plugins/builtin/dividend-yield/client.test.ts +58 -0
  226. package/src/plugins/builtin/dividend-yield/client.ts +252 -0
  227. package/src/plugins/builtin/dividend-yield/index.tsx +61 -0
  228. package/src/plugins/builtin/dividend-yield/model.ts +83 -0
  229. package/src/plugins/builtin/dividend-yield/pane.tsx +305 -0
  230. package/src/plugins/builtin/dividend-yield/types.ts +22 -0
  231. package/src/plugins/builtin/earnings/data/cache.test.ts +10 -7
  232. package/src/plugins/builtin/earnings/data/cache.ts +23 -9
  233. package/src/plugins/builtin/earnings/index.tsx +57 -6
  234. package/src/plugins/builtin/econ/calendar-model.test.ts +37 -3
  235. package/src/plugins/builtin/econ/calendar-model.ts +32 -26
  236. package/src/plugins/builtin/econ/fred-cache.test.ts +8 -1
  237. package/src/plugins/builtin/econ/fred-series-map.ts +18 -0
  238. package/src/plugins/builtin/econ/index.test.tsx +105 -0
  239. package/src/plugins/builtin/econ/index.tsx +95 -52
  240. package/src/plugins/builtin/fear-greed/cache.test.ts +18 -3
  241. package/src/plugins/builtin/fear-greed/cache.ts +18 -6
  242. package/src/plugins/builtin/fear-greed/charts.tsx +9 -2
  243. package/src/plugins/builtin/fear-greed/format.ts +0 -9
  244. package/src/plugins/builtin/fear-greed/pane.tsx +21 -21
  245. package/src/plugins/builtin/futures/contracts.ts +100 -0
  246. package/src/plugins/builtin/futures/index.tsx +298 -0
  247. package/src/plugins/builtin/futures/model.test.ts +129 -0
  248. package/src/plugins/builtin/futures/model.ts +136 -0
  249. package/src/plugins/builtin/futures/pane.test.tsx +239 -0
  250. package/src/plugins/builtin/futures/table.test.ts +155 -0
  251. package/src/plugins/builtin/futures/table.tsx +241 -0
  252. package/src/plugins/builtin/fx-matrix/index.tsx +160 -134
  253. package/src/plugins/builtin/fx-matrix/pairs.ts +12 -1
  254. package/src/plugins/builtin/fx-matrix/pane.test.tsx +109 -0
  255. package/src/plugins/builtin/help/components.tsx +1 -30
  256. package/src/plugins/builtin/help/index.tsx +5 -25
  257. package/src/plugins/builtin/holders/pane.tsx +17 -12
  258. package/src/plugins/builtin/holders/treemap.tsx +4 -2
  259. package/src/plugins/builtin/insider/index.tsx +20 -10
  260. package/src/plugins/builtin/ipo-calendar/cache.ts +101 -0
  261. package/src/plugins/builtin/ipo-calendar/client.test.ts +31 -0
  262. package/src/plugins/builtin/ipo-calendar/client.ts +296 -0
  263. package/src/plugins/builtin/ipo-calendar/index.tsx +65 -0
  264. package/src/plugins/builtin/ipo-calendar/model.ts +145 -0
  265. package/src/plugins/builtin/ipo-calendar/pane.tsx +328 -0
  266. package/src/plugins/builtin/ipo-calendar/types.ts +19 -0
  267. package/src/plugins/builtin/kelly-sizer/index.test.tsx +3 -3
  268. package/src/plugins/builtin/kelly-sizer/model.ts +8 -1
  269. package/src/plugins/builtin/kelly-sizer/pane.tsx +33 -15
  270. package/src/plugins/builtin/kelly-sizer/view.tsx +12 -5
  271. package/src/plugins/builtin/market-halts/client.ts +114 -0
  272. package/src/plugins/builtin/market-halts/index.tsx +29 -0
  273. package/src/plugins/builtin/market-halts/model.test.ts +187 -0
  274. package/src/plugins/builtin/market-halts/model.ts +291 -0
  275. package/src/plugins/builtin/market-halts/pane.tsx +256 -0
  276. package/src/plugins/builtin/market-heatmap/data.test.ts +1 -0
  277. package/src/plugins/builtin/market-heatmap/data.ts +12 -3
  278. package/src/plugins/builtin/market-heatmap/index.tsx +131 -46
  279. package/src/plugins/builtin/market-movers/index.tsx +145 -63
  280. package/src/plugins/builtin/market-movers/model.ts +19 -3
  281. package/src/plugins/builtin/market-movers/screener.test.ts +108 -0
  282. package/src/plugins/builtin/market-movers/screener.ts +168 -10
  283. package/src/plugins/builtin/news/index.tsx +78 -99
  284. package/src/plugins/builtin/news/wire/article-search.test.ts +72 -0
  285. package/src/plugins/builtin/news/wire/article-search.ts +140 -0
  286. package/src/plugins/builtin/news/wire/breaking/pane.tsx +18 -8
  287. package/src/plugins/builtin/news/wire/categories.ts +35 -0
  288. package/src/plugins/builtin/news/wire/default-feeds.ts +47 -1
  289. package/src/plugins/builtin/news/wire/feed-config.test.ts +10 -5
  290. package/src/plugins/builtin/news/wire/index.tsx +41 -4
  291. package/src/plugins/builtin/news/wire/industry-pane.tsx +38 -19
  292. package/src/plugins/builtin/news/wire/news/detail-view.tsx +10 -5
  293. package/src/plugins/builtin/news/wire/news/footer.ts +1 -0
  294. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +15 -6
  295. package/src/plugins/builtin/news/wire/news/query-presets.ts +16 -2
  296. package/src/plugins/builtin/news/wire/news/table.test.tsx +54 -0
  297. package/src/plugins/builtin/news/wire/news/table.tsx +111 -17
  298. package/src/plugins/builtin/notes/files.test.ts +18 -0
  299. package/src/plugins/builtin/notes/files.ts +10 -4
  300. package/src/plugins/builtin/notes/index.tsx +4 -1
  301. package/src/plugins/builtin/notes/quick-notes-pane.test.tsx +182 -0
  302. package/src/plugins/builtin/notes/quick-notes-pane.tsx +74 -31
  303. package/src/plugins/builtin/notes/ticker-notes-tab.test.tsx +380 -0
  304. package/src/plugins/builtin/notes/ticker-notes-tab.tsx +97 -40
  305. package/src/plugins/builtin/options/calc-seed.test.ts +118 -0
  306. package/src/plugins/builtin/options/calc-seed.ts +52 -0
  307. package/src/plugins/builtin/options/footer.ts +4 -1
  308. package/src/plugins/builtin/options/index.tsx +22 -1
  309. package/src/plugins/builtin/options/live-quotes.ts +8 -0
  310. package/src/plugins/builtin/options/table.ts +3 -1
  311. package/src/plugins/builtin/options/view.test.tsx +5 -1
  312. package/src/plugins/builtin/options/view.tsx +112 -17
  313. package/src/plugins/builtin/options-calculator/index.tsx +35 -0
  314. package/src/plugins/builtin/options-calculator/model.test.ts +172 -0
  315. package/src/plugins/builtin/options-calculator/model.ts +273 -0
  316. package/src/plugins/builtin/options-calculator/pane.test.tsx +138 -0
  317. package/src/plugins/builtin/options-calculator/pane.tsx +218 -0
  318. package/src/plugins/builtin/plugin-module.test.ts +16 -2
  319. package/src/plugins/builtin/plugin-module.ts +5 -1
  320. package/src/plugins/builtin/polls/client.ts +57 -0
  321. package/src/plugins/builtin/polls/hooks.ts +20 -0
  322. package/src/plugins/builtin/polls/index.tsx +35 -0
  323. package/src/plugins/builtin/polls/normalize.test.ts +230 -0
  324. package/src/plugins/builtin/polls/normalize.ts +319 -0
  325. package/src/plugins/builtin/polls/pane.tsx +722 -0
  326. package/src/plugins/builtin/polls/types.ts +77 -0
  327. package/src/plugins/builtin/portfolio-list/header.tsx +15 -4
  328. package/src/plugins/builtin/portfolio-list/pane/index.tsx +7 -3
  329. package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +1 -1
  330. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +2 -0
  331. package/src/plugins/builtin/portfolio-list/summary/index.tsx +2 -1
  332. package/src/plugins/builtin/research/analyst-pane.tsx +40 -67
  333. package/src/plugins/builtin/research/corporate-actions-pane.tsx +25 -19
  334. package/src/plugins/builtin/research/index.test.ts +6 -25
  335. package/src/plugins/builtin/research/index.tsx +11 -5
  336. package/src/plugins/builtin/research/relative-valuation-pane.tsx +86 -26
  337. package/src/plugins/builtin/scanner/denied.tsx +31 -0
  338. package/src/plugins/builtin/scanner/feed.ts +94 -0
  339. package/src/plugins/builtin/scanner/flow-model.test.ts +85 -0
  340. package/src/plugins/builtin/scanner/flow-model.ts +133 -0
  341. package/src/plugins/builtin/scanner/flow-pane.tsx +197 -0
  342. package/src/plugins/builtin/scanner/hilo-bars.tsx +121 -0
  343. package/src/plugins/builtin/scanner/hilo-model.test.ts +44 -0
  344. package/src/plugins/builtin/scanner/hilo-model.ts +75 -0
  345. package/src/plugins/builtin/scanner/hilo-pane.tsx +165 -0
  346. package/src/plugins/builtin/scanner/index.ts +109 -0
  347. package/src/plugins/builtin/scanner/waiting.tsx +11 -0
  348. package/src/plugins/builtin/sec/index.tsx +11 -7
  349. package/src/plugins/builtin/sectors/index.tsx +105 -81
  350. package/src/plugins/builtin/sectors/move-bar.tsx +43 -0
  351. package/src/plugins/builtin/sectors/sector-data.ts +18 -0
  352. package/src/plugins/builtin/sectors/sector-model.ts +13 -15
  353. package/src/plugins/builtin/shared/auto-refresh.ts +51 -0
  354. package/src/plugins/builtin/shared/cloud-upgrade.ts +29 -5
  355. package/src/plugins/builtin/shared/pane-footer.ts +16 -3
  356. package/src/plugins/builtin/shared/screener-live-quotes.test.ts +91 -0
  357. package/src/plugins/builtin/shared/screener-live-quotes.ts +134 -0
  358. package/src/plugins/builtin/shared/table-pane.ts +1 -5
  359. package/src/plugins/builtin/shared/use-quote-board.test.tsx +211 -0
  360. package/src/plugins/builtin/shared/use-quote-board.ts +195 -0
  361. package/src/plugins/builtin/short-interest/client.ts +124 -0
  362. package/src/plugins/builtin/short-interest/index.tsx +62 -0
  363. package/src/plugins/builtin/short-interest/model.ts +113 -0
  364. package/src/plugins/builtin/short-interest/pane.tsx +252 -0
  365. package/src/plugins/builtin/short-interest/types.ts +9 -0
  366. package/src/plugins/builtin/substack/article-detail.tsx +15 -1
  367. package/src/plugins/builtin/substack/pane-footer.ts +3 -22
  368. package/src/plugins/builtin/substack/pane.tsx +1 -2
  369. package/src/plugins/builtin/thirteenf/api.ts +4 -1
  370. package/src/plugins/builtin/thirteenf/model.ts +2 -0
  371. package/src/plugins/builtin/thirteenf/pane.tsx +6 -2
  372. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +10 -4
  373. package/src/plugins/builtin/ticker-detail/data-panes/provider-search.tsx +67 -11
  374. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +15 -8
  375. package/src/plugins/builtin/ticker-detail/index.tsx +5 -21
  376. package/src/plugins/builtin/ticker-detail/overview/components.tsx +72 -32
  377. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +0 -10
  378. package/src/plugins/builtin/ticker-detail/pane.tsx +4 -1
  379. package/src/plugins/builtin/ticker-detail/quote-monitor/card.test.tsx +70 -0
  380. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +34 -26
  381. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +96 -49
  382. package/src/plugins/builtin/ticker-detail/research-tabs.ts +32 -0
  383. package/src/plugins/builtin/ticker-research-backend-plugin.ts +2 -9
  384. package/src/plugins/builtin/ticker-research-plugin.ts +6 -0
  385. package/src/plugins/builtin/treasury-auctions/cache.test.ts +173 -0
  386. package/src/plugins/builtin/treasury-auctions/cache.ts +104 -0
  387. package/src/plugins/builtin/treasury-auctions/client.test.ts +181 -0
  388. package/src/plugins/builtin/treasury-auctions/client.ts +151 -0
  389. package/src/plugins/builtin/treasury-auctions/index.tsx +90 -0
  390. package/src/plugins/builtin/treasury-auctions/model.test.ts +147 -0
  391. package/src/plugins/builtin/treasury-auctions/model.ts +188 -0
  392. package/src/plugins/builtin/treasury-auctions/pane.test.tsx +170 -0
  393. package/src/plugins/builtin/treasury-auctions/pane.tsx +417 -0
  394. package/src/plugins/builtin/treasury-auctions/types.ts +59 -0
  395. package/src/plugins/builtin/tv/pane.tsx +67 -7
  396. package/src/plugins/builtin/tv/youtube-embed.test.ts +84 -0
  397. package/src/plugins/builtin/tv/youtube-embed.ts +173 -0
  398. package/src/plugins/builtin/tv/youtube-stream.ts +27 -2
  399. package/src/plugins/builtin/volatility/client.ts +87 -0
  400. package/src/plugins/builtin/volatility/index.test.tsx +107 -0
  401. package/src/plugins/builtin/volatility/index.tsx +193 -0
  402. package/src/plugins/builtin/volatility/model.test.ts +66 -0
  403. package/src/plugins/builtin/volatility/model.ts +113 -0
  404. package/src/plugins/builtin/world-indices/footer.ts +43 -23
  405. package/src/plugins/builtin/world-indices/index.tsx +44 -93
  406. package/src/plugins/builtin/world-indices/indices.ts +11 -2
  407. package/src/plugins/builtin/world-indices/model.ts +17 -24
  408. package/src/plugins/builtin/world-indices/pane.test.tsx +116 -0
  409. package/src/plugins/builtin/world-indices/table.tsx +82 -37
  410. package/src/plugins/builtin/yield-curve/index.tsx +16 -7
  411. package/src/plugins/builtin/yield-curve/treasury-data.test.ts +25 -0
  412. package/src/plugins/builtin/yield-curve/treasury-data.ts +16 -0
  413. package/src/plugins/catalog-backend.ts +8 -0
  414. package/src/plugins/catalog-ui.ts +8 -0
  415. package/src/plugins/event-bus.ts +1 -0
  416. package/src/plugins/ibkr/gateway/account-loaders.ts +1 -1
  417. package/src/plugins/ibkr/trade/tab/footer.ts +9 -23
  418. package/src/plugins/ibkr/trade/tab/index.tsx +1 -5
  419. package/src/plugins/ibkr/trading/pane-view.tsx +149 -87
  420. package/src/plugins/ibkr/trading/pane.tsx +2 -2
  421. package/src/plugins/ownership.ts +3 -0
  422. package/src/plugins/pane-manager/floating.ts +8 -2
  423. package/src/plugins/pane-manager.test.ts +13 -0
  424. package/src/plugins/prediction-markets/backend-plugin.ts +2 -0
  425. package/src/plugins/prediction-markets/capability.test.ts +281 -0
  426. package/src/plugins/prediction-markets/capability.ts +171 -0
  427. package/src/plugins/prediction-markets/chart.tsx +18 -26
  428. package/src/plugins/prediction-markets/columns.ts +3 -2
  429. package/src/plugins/prediction-markets/controller/catalog.ts +15 -12
  430. package/src/plugins/prediction-markets/controller/detail.ts +4 -0
  431. package/src/plugins/prediction-markets/controller/index.ts +0 -15
  432. package/src/plugins/prediction-markets/detail/book.tsx +3 -23
  433. package/src/plugins/prediction-markets/detail/overview.tsx +0 -18
  434. package/src/plugins/prediction-markets/detail/pane.tsx +36 -12
  435. package/src/plugins/prediction-markets/detail.test.tsx +1 -1
  436. package/src/plugins/prediction-markets/index.tsx +2 -0
  437. package/src/plugins/prediction-markets/metrics.ts +28 -18
  438. package/src/plugins/prediction-markets/navigation.ts +1 -0
  439. package/src/plugins/prediction-markets/pane.test.tsx +1 -1
  440. package/src/plugins/prediction-markets/pane.tsx +0 -1
  441. package/src/plugins/prediction-markets/services/fetch.ts +3 -2
  442. package/src/plugins/prediction-markets/services/kalshi/adapter.ts +82 -23
  443. package/src/plugins/prediction-markets/services/kalshi/normalize.ts +3 -1
  444. package/src/plugins/prediction-markets/services/kalshi/types.ts +14 -0
  445. package/src/plugins/prediction-markets/services/polymarket/adapter.ts +26 -14
  446. package/src/plugins/prediction-markets/services/polymarket/detail.ts +67 -18
  447. package/src/plugins/prediction-markets/test-helpers.tsx +0 -1
  448. package/src/plugins/prediction-markets/types.ts +3 -9
  449. package/src/plugins/registry/context.ts +6 -1
  450. package/src/plugins/registry/index.test.ts +2 -0
  451. package/src/plugins/registry/index.ts +53 -3
  452. package/src/plugins/runtime/context.tsx +4 -2
  453. package/src/plugins/runtime/index.tsx +9 -0
  454. package/src/remote/controller.test.ts +17 -4
  455. package/src/renderers/electrobun/bun/application-menu/index.ts +1 -1
  456. package/src/renderers/electrobun/bun/core-capabilities.ts +2 -2
  457. package/src/renderers/electrobun/bun/desktop/capability-bridge.test.ts +64 -0
  458. package/src/renderers/electrobun/bun/desktop/capability-bridge.ts +41 -7
  459. package/src/renderers/electrobun/bun/desktop/host-requests.ts +5 -2
  460. package/src/renderers/electrobun/bun/desktop/http-fetch.test.ts +33 -0
  461. package/src/renderers/electrobun/bun/desktop/http-fetch.ts +53 -6
  462. package/src/renderers/electrobun/bun/desktop/initialization.test.ts +27 -0
  463. package/src/renderers/electrobun/bun/desktop/initialization.ts +6 -1
  464. package/src/renderers/electrobun/bun/index.ts +1 -0
  465. package/src/renderers/electrobun/shared/protocol.ts +2 -0
  466. package/src/renderers/electrobun/view/app-services.ts +25 -2
  467. package/src/renderers/electrobun/view/build-assets.ts +5 -0
  468. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +186 -65
  469. package/src/renderers/electrobun/view/cli-pane-shot-health.test.ts +25 -0
  470. package/src/renderers/electrobun/view/cli-pane-shot-health.ts +31 -0
  471. package/src/renderers/electrobun/view/data-table/index.tsx +10 -11
  472. package/src/renderers/electrobun/view/data-table/row.tsx +2 -0
  473. package/src/renderers/electrobun/view/desktop/controls.test.tsx +68 -0
  474. package/src/renderers/electrobun/view/desktop/controls.tsx +25 -1
  475. package/src/renderers/electrobun/view/host/box.test.tsx +80 -0
  476. package/src/renderers/electrobun/view/host/media-surface.tsx +70 -3
  477. package/src/renderers/electrobun/view/host/tabs.tsx +97 -5
  478. package/src/renderers/electrobun/view/host/text.tsx +5 -2
  479. package/src/renderers/electrobun/view/input-host.test.tsx +140 -0
  480. package/src/renderers/electrobun/view/input-host.tsx +4 -2
  481. package/src/renderers/electrobun/view/native-stubs/backend-rpc.ts +19 -1
  482. package/src/renderers/electrobun/view/native-stubs/broker-sync-native-loader.ts +7 -0
  483. package/src/renderers/electrobun/view/native-stubs/chart/kitty-support.ts +10 -0
  484. package/src/renderers/electrobun/view/remote/capability-invoker.test.ts +46 -0
  485. package/src/renderers/electrobun/view/remote/capability-invoker.ts +34 -13
  486. package/src/renderers/electrobun/view/remote/connection-health-backend.ts +19 -0
  487. package/src/renderers/electrobun/view/remote/connection-health.test.ts +44 -0
  488. package/src/renderers/electrobun/view/remote/connection-health.ts +49 -0
  489. package/src/renderers/electrobun/view/styles.css +5 -0
  490. package/src/renderers/opentui/dialog-host.tsx +279 -80
  491. package/src/renderers/opentui/host.tsx +15 -1
  492. package/src/renderers/opentui/terminal-media.test.ts +102 -0
  493. package/src/renderers/opentui/terminal-media.ts +142 -0
  494. package/src/renderers/opentui/test-utils.tsx +9 -6
  495. package/src/renderers/opentui/toast-host.tsx +136 -7
  496. package/src/renderers/opentui/ui-host.tsx +19 -3
  497. package/src/sources/provider-router/connection-health.test.ts +28 -0
  498. package/src/sources/provider-router/connection-health.ts +47 -0
  499. package/src/sources/provider-router/history.test.ts +53 -4
  500. package/src/sources/provider-router/history.ts +119 -14
  501. package/src/sources/provider-router/index.ts +12 -1
  502. package/src/sources/yahoo-finance/history.ts +2 -9
  503. package/src/sources/yahoo-finance/http.ts +1 -1
  504. package/src/sources/yahoo-finance/symbols.ts +1 -1
  505. package/src/sources/yahoo-finance/types.ts +19 -0
  506. package/src/state/app/context/index.test.tsx +101 -0
  507. package/src/state/app/context/index.tsx +10 -0
  508. package/src/state/config-save-scheduler.ts +1 -6
  509. package/src/state/persist-scheduler.test.ts +31 -0
  510. package/src/state/persist-scheduler.ts +21 -8
  511. package/src/sync/auth-transition.test.ts +36 -0
  512. package/src/sync/auth-transition.ts +22 -0
  513. package/src/sync/core-contributors.test.ts +58 -1
  514. package/src/sync/core-contributors.ts +39 -2
  515. package/src/sync/react.ts +9 -0
  516. package/src/test-support/plugin-runtime.ts +3 -0
  517. package/src/theme/font-scale.test.ts +14 -0
  518. package/src/theme/font-scale.ts +69 -0
  519. package/src/tickers/search/index.test.ts +284 -2
  520. package/src/tickers/search/index.ts +55 -18
  521. package/src/tickers/search/ranking.ts +143 -18
  522. package/src/tickers/search/types.ts +1 -0
  523. package/src/time-series/alignment.ts +15 -11
  524. package/src/time-series/field-catalog.ts +10 -0
  525. package/src/time-series/fundamentals.ts +19 -11
  526. package/src/time-series/hooks.ts +8 -2
  527. package/src/time-series/parsed-history-cache.ts +35 -0
  528. package/src/time-series/resolution.ts +10 -0
  529. package/src/time-series/resolve.test.ts +332 -3
  530. package/src/time-series/resolve.ts +225 -25
  531. package/src/time-series/spec-studies.test.ts +23 -0
  532. package/src/time-series/spec.ts +21 -2
  533. package/src/time-series/transforms.test.ts +76 -1
  534. package/src/time-series/transforms.ts +39 -8
  535. package/src/time-series/types.ts +9 -2
  536. package/src/time-series/use-chart-resolution.test.tsx +105 -1
  537. package/src/time-series/use-chart-resolution.ts +67 -13
  538. package/src/types/config.ts +30 -27
  539. package/src/types/media.ts +2 -0
  540. package/src/types/plugin.ts +8 -1
  541. package/src/ui/host.tsx +4 -0
  542. package/src/ui/pointer-drag.ts +12 -0
  543. package/src/ui/qr.test.ts +79 -0
  544. package/src/ui/qr.ts +82 -0
  545. package/src/updater/github-releases.test.ts +3 -3
  546. package/src/updater/github-releases.ts +1 -1
  547. package/src/updater.test.ts +117 -16
  548. package/src/updater.ts +32 -5
  549. package/src/utils/exchanges.ts +1 -0
  550. package/src/utils/external-url.test.ts +32 -0
  551. package/src/utils/external-url.ts +23 -0
  552. package/src/utils/sort-values.ts +31 -0
  553. package/src/utils/zoned-date-time.ts +67 -0
  554. package/src/components/onboarding/wizard-keyboard.ts +0 -306
  555. package/src/plugins/builtin/chart-composer/date-window-dialog.tsx +0 -120
@@ -0,0 +1,318 @@
1
+ import { debugLog } from "../utils/debug-log";
2
+ import type {
3
+ CapabilitySeriesSource,
4
+ ChartSeriesSpec,
5
+ ChartViewportSpec,
6
+ ResolvedSeries,
7
+ SeriesStyle,
8
+ SeriesTimestampMode,
9
+ SeriesTransform,
10
+ TimeSeriesPoint,
11
+ } from "../time-series/types";
12
+ import type {
13
+ CapabilityInvoker,
14
+ CapabilitySchema,
15
+ ChartSeriesCatalogItem,
16
+ ChartSeriesCatalogRequest,
17
+ ChartSeriesResolveRequest,
18
+ } from "./types";
19
+
20
+ export const CHART_SERIES_CAPABILITY_KIND = "chart-series";
21
+ export const MAX_CHART_SERIES_CATALOG_ITEMS = 32;
22
+ export const MAX_CHART_SERIES_POINTS = 20_000;
23
+
24
+ const CAPABILITY_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$/;
25
+ const SERIES_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._~%:/-]{0,239}$/;
26
+ const chartSeriesLog = debugLog.createLogger("chart-series");
27
+ const ranges = new Set(["1D", "1W", "1M", "3M", "6M", "1Y", "5Y", "ALL"]);
28
+ const resolutions = new Set(["auto", "1m", "5m", "15m", "30m", "45m", "1h", "1d", "1wk", "1mo"]);
29
+ const periods = new Set(["auto", "daily", "weekly", "monthly", "quarterly", "annual", "ttm"]);
30
+ const styles = new Set(["line", "area", "step", "columns", "points", "candles", "ohlc", "hlc"]);
31
+ const transforms = new Set(["raw", "percent", "index100", "yoy", "qoq", "log"]);
32
+ const shapes = new Set(["scalar", "ohlcv", "event", "band"]);
33
+ const axes = new Set(["left", "right"]);
34
+ const interpolations = new Set(["none", "step-after"]);
35
+
36
+ export function isValidChartCapabilityId(value: string): boolean {
37
+ return CAPABILITY_ID_PATTERN.test(value);
38
+ }
39
+
40
+ export function isValidChartSeriesId(value: string): boolean {
41
+ return SERIES_ID_PATTERN.test(value);
42
+ }
43
+
44
+ function fail(path: string, message: string): never {
45
+ throw new Error(`Invalid chart series ${path}: ${message}`);
46
+ }
47
+
48
+ function object(value: unknown, path: string): Record<string, unknown> {
49
+ if (!value || typeof value !== "object" || Array.isArray(value)) fail(path, "expected an object.");
50
+ return value as Record<string, unknown>;
51
+ }
52
+
53
+ function onlyKeys(input: Record<string, unknown>, path: string, allowed: readonly string[]): void {
54
+ const extra = Object.keys(input).find((key) => !allowed.includes(key));
55
+ if (extra) fail(path, `unsupported field "${extra}".`);
56
+ }
57
+
58
+ function boundedString(value: unknown, path: string, max: number): string {
59
+ if (typeof value !== "string" || value.length === 0 || value.length > max) {
60
+ fail(path, `expected a non-empty string of at most ${max} characters.`);
61
+ }
62
+ return value;
63
+ }
64
+
65
+ function optionalString(value: unknown, path: string, max: number): string | undefined {
66
+ return value === undefined ? undefined : boundedString(value, path, max);
67
+ }
68
+
69
+ function enumValue<T extends string>(value: unknown, path: string, allowed: Set<string>): T {
70
+ if (typeof value !== "string" || !allowed.has(value)) fail(path, `unsupported value ${String(value)}.`);
71
+ return value as T;
72
+ }
73
+
74
+ function finiteNumber(value: unknown, path: string, nullable = false): number | null {
75
+ if (nullable && value === null) return null;
76
+ if (typeof value !== "number" || !Number.isFinite(value)) fail(path, "expected a finite number.");
77
+ return value;
78
+ }
79
+
80
+ function optionalNumber(value: unknown, path: string, nullable = false): number | null | undefined {
81
+ return value === undefined ? undefined : finiteNumber(value, path, nullable);
82
+ }
83
+
84
+ function dateValue(value: unknown, path: string): Date {
85
+ const date = value instanceof Date ? new Date(value) : new Date(value as string | number);
86
+ if (!Number.isFinite(date.getTime())) fail(path, "expected a valid date.");
87
+ return date;
88
+ }
89
+
90
+ function catalogRequest(value: unknown): ChartSeriesCatalogRequest {
91
+ const input = object(value, "catalog request");
92
+ onlyKeys(input, "catalog request", ["query", "limit"]);
93
+ const query = input.query === undefined ? undefined : optionalString(input.query, "catalog query", 200);
94
+ const limit = input.limit === undefined ? 8 : input.limit;
95
+ if (!Number.isInteger(limit) || (limit as number) < 1 || (limit as number) > MAX_CHART_SERIES_CATALOG_ITEMS) {
96
+ fail("catalog limit", `expected an integer from 1 to ${MAX_CHART_SERIES_CATALOG_ITEMS}.`);
97
+ }
98
+ return { ...(query !== undefined ? { query } : {}), limit: limit as number };
99
+ }
100
+
101
+ function viewport(value: unknown): ChartViewportSpec {
102
+ const input = object(value, "resolve viewport");
103
+ onlyKeys(input, "resolve viewport", ["range", "resolution", "dateWindow", "maxPoints"]);
104
+ const range = enumValue<ChartViewportSpec["range"]>(input.range, "viewport range", ranges);
105
+ const resolution = enumValue<ChartViewportSpec["resolution"]>(input.resolution, "viewport resolution", resolutions);
106
+ let dateWindow: ChartViewportSpec["dateWindow"];
107
+ if (input.dateWindow !== undefined) {
108
+ const window = object(input.dateWindow, "viewport date window");
109
+ onlyKeys(window, "viewport date window", ["start", "end"]);
110
+ const start = boundedString(window.start, "viewport start", 40);
111
+ const end = boundedString(window.end, "viewport end", 40);
112
+ const startTime = Date.parse(start);
113
+ const endTime = Date.parse(end);
114
+ if (!Number.isFinite(startTime) || !Number.isFinite(endTime) || startTime > endTime) {
115
+ fail("viewport date window", "expected valid ordered bounds.");
116
+ }
117
+ dateWindow = { start, end };
118
+ }
119
+ let maxPoints: number | undefined;
120
+ if (input.maxPoints !== undefined) {
121
+ if (!Number.isInteger(input.maxPoints) || (input.maxPoints as number) < 1 || (input.maxPoints as number) > 10_000) {
122
+ fail("viewport maxPoints", "expected an integer from 1 to 10000.");
123
+ }
124
+ maxPoints = input.maxPoints as number;
125
+ }
126
+ return { range, resolution, ...(dateWindow ? { dateWindow } : {}), ...(maxPoints ? { maxPoints } : {}) };
127
+ }
128
+
129
+ function resolveRequest(value: unknown): ChartSeriesResolveRequest {
130
+ const input = object(value, "resolve request");
131
+ onlyKeys(input, "resolve request", ["seriesId", "viewport"]);
132
+ const seriesId = boundedString(input.seriesId, "series ID", 240);
133
+ if (!isValidChartSeriesId(seriesId)) fail("series ID", "contains unsupported characters.");
134
+ return { seriesId, viewport: viewport(input.viewport) };
135
+ }
136
+
137
+ function catalogItem(value: unknown, index: number): ChartSeriesCatalogItem {
138
+ const item = object(value, `catalog item ${index}`);
139
+ onlyKeys(item, `catalog item ${index}`, ["seriesId", "label", "description", "detail", "style", "transform"]);
140
+ const seriesId = boundedString(item.seriesId, `catalog item ${index} series ID`, 240);
141
+ if (!isValidChartSeriesId(seriesId)) fail(`catalog item ${index} series ID`, "contains unsupported characters.");
142
+ return {
143
+ seriesId,
144
+ label: boundedString(item.label, `catalog item ${index} label`, 160),
145
+ ...(optionalString(item.description, `catalog item ${index} description`, 500) !== undefined
146
+ ? { description: item.description as string }
147
+ : {}),
148
+ ...(optionalString(item.detail, `catalog item ${index} detail`, 100) !== undefined
149
+ ? { detail: item.detail as string }
150
+ : {}),
151
+ ...(item.style !== undefined ? { style: enumValue<SeriesStyle>(item.style, `catalog item ${index} style`, styles) } : {}),
152
+ ...(item.transform !== undefined ? { transform: enumValue<SeriesTransform>(item.transform, `catalog item ${index} transform`, transforms) } : {}),
153
+ };
154
+ }
155
+
156
+ function catalogOutput(value: unknown): ChartSeriesCatalogItem[] {
157
+ if (!Array.isArray(value)) fail("catalog output", "expected an array.");
158
+ if (value.length > MAX_CHART_SERIES_CATALOG_ITEMS) {
159
+ fail("catalog output", `returned ${value.length} items; maximum is ${MAX_CHART_SERIES_CATALOG_ITEMS}.`);
160
+ }
161
+ return value.map(catalogItem);
162
+ }
163
+
164
+ function point(value: unknown, index: number): TimeSeriesPoint {
165
+ const input = object(value, `point ${index}`);
166
+ onlyKeys(input, `point ${index}`, [
167
+ "date", "observedAt", "availableAt", "value", "open", "high", "low", "close", "volume", "periodLabel", "provenance",
168
+ ]);
169
+ const provenance = input.provenance === undefined ? undefined : object(input.provenance, `point ${index} provenance`);
170
+ if (provenance) onlyKeys(provenance, `point ${index} provenance`, ["providerId", "quality"]);
171
+ const quality = provenance?.quality === undefined
172
+ ? undefined
173
+ : enumValue<"reported" | "derived" | "estimated">(
174
+ provenance.quality,
175
+ `point ${index} provenance quality`,
176
+ new Set(["reported", "derived", "estimated"]),
177
+ );
178
+ return {
179
+ date: dateValue(input.date, `point ${index} date`),
180
+ observedAt: dateValue(input.observedAt, `point ${index} observedAt`),
181
+ ...(input.availableAt !== undefined ? { availableAt: dateValue(input.availableAt, `point ${index} availableAt`) } : {}),
182
+ value: finiteNumber(input.value, `point ${index} value`, true),
183
+ ...(input.open !== undefined ? { open: optionalNumber(input.open, `point ${index} open`, true) } : {}),
184
+ ...(input.high !== undefined ? { high: optionalNumber(input.high, `point ${index} high`, true) } : {}),
185
+ ...(input.low !== undefined ? { low: optionalNumber(input.low, `point ${index} low`, true) } : {}),
186
+ ...(input.close !== undefined ? { close: optionalNumber(input.close, `point ${index} close`, true) } : {}),
187
+ ...(input.volume !== undefined ? { volume: optionalNumber(input.volume, `point ${index} volume`, true) } : {}),
188
+ ...(input.periodLabel !== undefined ? { periodLabel: boundedString(input.periodLabel, `point ${index} period label`, 120) } : {}),
189
+ ...(provenance ? {
190
+ provenance: {
191
+ ...(provenance.providerId !== undefined
192
+ ? { providerId: boundedString(provenance.providerId, `point ${index} provider ID`, 80) }
193
+ : {}),
194
+ ...(quality ? { quality } : {}),
195
+ },
196
+ } : {}),
197
+ };
198
+ }
199
+
200
+ function resolvedOutput(value: unknown): ResolvedSeries {
201
+ const input = object(value, "resolve output");
202
+ onlyKeys(input, "resolve output", [
203
+ "id", "label", "color", "unit", "unitGroup", "nativeFrequency", "timestampMode", "dataShape", "style", "transform",
204
+ "axis", "panelId", "interpolation", "timeBasis", "latestChangePercent", "points", "warning", "hidden",
205
+ ]);
206
+ if (!Array.isArray(input.points)) fail("resolve output points", "expected an array.");
207
+ if (input.points.length > MAX_CHART_SERIES_POINTS) {
208
+ fail("resolve output points", `returned ${input.points.length} points; maximum is ${MAX_CHART_SERIES_POINTS}.`);
209
+ }
210
+ const timeBasis = input.timeBasis === undefined ? undefined : object(input.timeBasis, "resolve output time basis");
211
+ if (timeBasis) onlyKeys(timeBasis, "resolve output time basis", ["kind", "timeZone", "cadenceMs"]);
212
+ const kind = timeBasis?.kind === undefined ? undefined : enumValue<"market">(timeBasis.kind, "time basis kind", new Set(["market"]));
213
+ const warning = optionalString(input.warning, "resolve output warning", 500);
214
+ return {
215
+ id: boundedString(input.id, "resolve output ID", 160),
216
+ label: boundedString(input.label, "resolve output label", 160),
217
+ color: boundedString(input.color, "resolve output color", 64),
218
+ unit: typeof input.unit === "string" && input.unit.length <= 64 ? input.unit : fail("resolve output unit", "expected a string of at most 64 characters."),
219
+ unitGroup: boundedString(input.unitGroup, "resolve output unit group", 64),
220
+ nativeFrequency: enumValue(input.nativeFrequency, "resolve output frequency", periods),
221
+ ...(input.timestampMode !== undefined
222
+ ? { timestampMode: enumValue<SeriesTimestampMode>(input.timestampMode, "resolve output timestamp mode", new Set(["available-at", "period-end"])) }
223
+ : {}),
224
+ dataShape: enumValue(input.dataShape, "resolve output data shape", shapes),
225
+ style: enumValue(input.style, "resolve output style", styles),
226
+ transform: enumValue(input.transform, "resolve output transform", transforms),
227
+ axis: enumValue(input.axis, "resolve output axis", axes),
228
+ panelId: boundedString(input.panelId, "resolve output panel ID", 80),
229
+ interpolation: enumValue(input.interpolation, "resolve output interpolation", interpolations),
230
+ ...(timeBasis ? {
231
+ timeBasis: {
232
+ kind: kind!,
233
+ timeZone: boundedString(timeBasis.timeZone, "time basis timezone", 80),
234
+ ...(timeBasis.cadenceMs !== undefined
235
+ ? { cadenceMs: finiteNumber(timeBasis.cadenceMs, "time basis cadence") as number }
236
+ : {}),
237
+ },
238
+ } : {}),
239
+ ...(input.latestChangePercent !== undefined
240
+ ? { latestChangePercent: finiteNumber(input.latestChangePercent, "resolve output latest change") as number }
241
+ : {}),
242
+ points: input.points.map(point),
243
+ ...(warning !== undefined ? { warning } : {}),
244
+ ...(input.hidden !== undefined
245
+ ? typeof input.hidden === "boolean" ? { hidden: input.hidden } : fail("resolve output hidden", "expected a boolean.")
246
+ : {}),
247
+ };
248
+ }
249
+
250
+ export const chartSeriesCatalogRequestSchema: CapabilitySchema<ChartSeriesCatalogRequest> = { parse: catalogRequest };
251
+ export const chartSeriesCatalogOutputSchema: CapabilitySchema<ChartSeriesCatalogItem[]> = { parse: catalogOutput };
252
+ export const chartSeriesResolveRequestSchema: CapabilitySchema<ChartSeriesResolveRequest> = { parse: resolveRequest };
253
+ export const chartSeriesResolveOutputSchema: CapabilitySchema<ResolvedSeries> = { parse: resolvedOutput };
254
+
255
+ export function chartSeriesCapabilityManifests(invoker: CapabilityInvoker) {
256
+ return invoker.capabilityManifests(CHART_SERIES_CAPABILITY_KIND);
257
+ }
258
+
259
+ export async function searchChartSeriesCapabilities(
260
+ invoker: CapabilityInvoker,
261
+ query: string,
262
+ limit = 8,
263
+ signal?: AbortSignal,
264
+ ): Promise<Array<ChartSeriesCatalogItem & { capabilityId: string; capabilityName: string }>> {
265
+ const manifests = chartSeriesCapabilityManifests(invoker);
266
+ const results = await Promise.allSettled(manifests.map(async (manifest) => {
267
+ const items = await invoker.invokeCapability<ChartSeriesCatalogItem[]>(
268
+ manifest.id,
269
+ query.trim() ? "search" : "catalog",
270
+ { query, limit },
271
+ { signal },
272
+ );
273
+ return items.map((item) => ({
274
+ ...item,
275
+ capabilityId: manifest.id,
276
+ capabilityName: manifest.name,
277
+ }));
278
+ }));
279
+ if (signal?.aborted) throw signal.reason ?? new DOMException("Aborted", "AbortError");
280
+ for (const [index, result] of results.entries()) {
281
+ if (result.status === "rejected") {
282
+ chartSeriesLog.warn("Chart series provider catalog failed", {
283
+ capabilityId: manifests[index]?.id,
284
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
285
+ });
286
+ }
287
+ }
288
+ return results.flatMap((result) => result.status === "fulfilled" ? result.value : []).slice(0, limit);
289
+ }
290
+
291
+ export function chartSeriesSourceKey(
292
+ source: CapabilitySeriesSource,
293
+ viewport?: ChartViewportSpec,
294
+ ): string {
295
+ return JSON.stringify([
296
+ "chart-series",
297
+ source.capabilityId,
298
+ source.seriesId,
299
+ viewport ? {
300
+ range: viewport.range,
301
+ resolution: viewport.resolution,
302
+ dateWindow: viewport.dateWindow ?? null,
303
+ maxPoints: viewport.maxPoints ?? null,
304
+ } : null,
305
+ ]);
306
+ }
307
+
308
+ export function createChartSeriesResolver(invoker: CapabilityInvoker) {
309
+ return async (
310
+ source: CapabilitySeriesSource,
311
+ viewport: ChartViewportSpec,
312
+ _spec: ChartSeriesSpec,
313
+ ): Promise<ResolvedSeries> => invoker.invokeCapability<ResolvedSeries>(
314
+ source.capabilityId,
315
+ "resolve",
316
+ { seriesId: source.seriesId, viewport } satisfies ChartSeriesResolveRequest,
317
+ );
318
+ }
@@ -1,8 +1,17 @@
1
1
  import type { AssetDataProvider } from "../types/data-provider";
2
2
  import type { NewsDataProvider } from "../types/capability-route-source";
3
+ import {
4
+ chartSeriesCatalogOutputSchema,
5
+ chartSeriesCatalogRequestSchema,
6
+ chartSeriesResolveOutputSchema,
7
+ chartSeriesResolveRequestSchema,
8
+ isValidChartCapabilityId,
9
+ } from "./chart-series";
3
10
  import type {
4
11
  AssetDataCapability,
5
12
  CapabilityOperation,
13
+ ChartSeriesCapability,
14
+ ChartSeriesProvider,
6
15
  NewsCapability,
7
16
  } from "./types";
8
17
 
@@ -84,6 +93,43 @@ export function assetDataProvider(provider: AssetDataProvider): AssetDataCapabil
84
93
  };
85
94
  }
86
95
 
96
+ export function chartSeriesProvider(options: {
97
+ id: string;
98
+ name: string;
99
+ priority?: number;
100
+ provider: ChartSeriesProvider;
101
+ }): ChartSeriesCapability {
102
+ if (!isValidChartCapabilityId(options.id)) {
103
+ throw new Error(`Invalid chart series capability ID "${options.id}".`);
104
+ }
105
+ return {
106
+ id: options.id,
107
+ kind: "chart-series",
108
+ name: options.name,
109
+ priority: options.priority,
110
+ provider: options.provider,
111
+ operations: {
112
+ catalog: {
113
+ ...op((input: any, ctx) => options.provider.catalog?.({ ...input, signal: ctx.signal }) ?? [], "query"),
114
+ input: chartSeriesCatalogRequestSchema,
115
+ output: chartSeriesCatalogOutputSchema,
116
+ },
117
+ search: {
118
+ ...op((input: any, ctx) => options.provider.search?.({ ...input, signal: ctx.signal })
119
+ ?? options.provider.catalog?.({ ...input, signal: ctx.signal })
120
+ ?? [], "query"),
121
+ input: chartSeriesCatalogRequestSchema,
122
+ output: chartSeriesCatalogOutputSchema,
123
+ },
124
+ resolve: {
125
+ ...op((input: any, ctx) => options.provider.resolve({ ...input, signal: ctx.signal }), "query"),
126
+ input: chartSeriesResolveRequestSchema,
127
+ output: chartSeriesResolveOutputSchema,
128
+ },
129
+ },
130
+ };
131
+ }
132
+
87
133
  export function newsProvider(options: {
88
134
  id: string;
89
135
  name: string;
@@ -2,3 +2,4 @@ export * from "./types";
2
2
  export * from "./registry";
3
3
  export * from "./factories";
4
4
  export * from "./service-capabilities";
5
+ export * from "./chart-series";
@@ -1,5 +1,7 @@
1
1
  import { describe, expect, test } from "bun:test";
2
+ import { chartSeriesProvider } from "./factories";
2
3
  import { CapabilityRegistry } from "./registry";
4
+ import { ConnectionHealthRegistry } from "../core/connection-health";
3
5
  import type { CapabilitySchema, PluginCapability } from "./types";
4
6
 
5
7
  interface IncrementInput {
@@ -82,6 +84,54 @@ describe("CapabilityRegistry", () => {
82
84
  .resolves.toBe(2);
83
85
  });
84
86
 
87
+ test("reports invokable provider operations through the connection boundary", async () => {
88
+ let clock = 10;
89
+ const health = new ConnectionHealthRegistry({ clock: () => clock });
90
+ health.registerSource({ id: "asset-data.test", name: "Test", kind: "asset-data" });
91
+ const registry = new CapabilityRegistry({ connectionHealth: health });
92
+ registry.register("plugin-a", testCapability({
93
+ id: "asset-data.test",
94
+ kind: "asset-data",
95
+ operations: {
96
+ read: {
97
+ kind: "read",
98
+ handler: async () => {
99
+ clock = 16;
100
+ return "ok";
101
+ },
102
+ },
103
+ },
104
+ }));
105
+
106
+ await expect(registry.invoke("asset-data.test", "read", {})).resolves.toBe("ok");
107
+ expect(health.getSnapshot().sources[0]).toMatchObject({
108
+ status: "connected",
109
+ lastOperation: "read",
110
+ lastLatencyMs: 6,
111
+ });
112
+ });
113
+
114
+ test("does not treat cached and static capability operations as network health", async () => {
115
+ const health = new ConnectionHealthRegistry();
116
+ health.registerSource({ id: "asset-data.test", name: "Test", kind: "asset-data" });
117
+ const registry = new CapabilityRegistry({ connectionHealth: health });
118
+ registry.register("plugin-a", testCapability({
119
+ id: "asset-data.test",
120
+ kind: "asset-data",
121
+ operations: Object.fromEntries([
122
+ "canProvide",
123
+ "getCachedFinancialsForTargets",
124
+ "getChartResolutionSupport",
125
+ ].map((id) => [id, { kind: "read", handler: () => true }])),
126
+ }));
127
+
128
+ await registry.invoke("asset-data.test", "canProvide", {});
129
+ await registry.invoke("asset-data.test", "getCachedFinancialsForTargets", {});
130
+ await registry.invoke("asset-data.test", "getChartResolutionSupport", {});
131
+
132
+ expect(health.getSnapshot().sources[0]).toMatchObject({ status: "idle", lastRequestAt: null });
133
+ });
134
+
85
135
  test("emits renderer-safe manifests only when requested", () => {
86
136
  const registry = new CapabilityRegistry();
87
137
  registry.register("plugin-a", testCapability());
@@ -139,6 +189,91 @@ describe("CapabilityRegistry", () => {
139
189
  expect(registry.list()).toEqual([]);
140
190
  });
141
191
 
192
+ test("registers, searches, resolves, disables, and disposes chart-series providers", async () => {
193
+ let enabled = true;
194
+ const registry = new CapabilityRegistry({ isPluginEnabled: () => enabled });
195
+ const dispose = registry.register("charts", chartSeriesProvider({
196
+ id: "charts.test",
197
+ name: "Test Charts",
198
+ provider: {
199
+ search: ({ query }) => [{ seriesId: "one", label: `Result ${query}` }],
200
+ resolve: ({ seriesId }) => ({
201
+ id: seriesId,
202
+ label: "Resolved",
203
+ color: "#fff",
204
+ unit: "value",
205
+ unitGroup: "value",
206
+ nativeFrequency: "daily",
207
+ dataShape: "scalar",
208
+ style: "line",
209
+ transform: "raw",
210
+ axis: "left",
211
+ panelId: "main",
212
+ interpolation: "none",
213
+ points: [{ date: new Date("2026-01-01"), observedAt: new Date("2026-01-01"), value: 1 }],
214
+ }),
215
+ },
216
+ }));
217
+
218
+ await expect(registry.invoke<any[]>("charts.test", "search", { query: "x" }))
219
+ .resolves.toEqual([{ seriesId: "one", label: "Result x" }]);
220
+ await expect(registry.invoke<any>("charts.test", "resolve", {
221
+ seriesId: "one",
222
+ viewport: { range: "1M", resolution: "auto" },
223
+ })).resolves.toMatchObject({ id: "one", points: [{ value: 1 }] });
224
+
225
+ enabled = false;
226
+ expect(registry.list("chart-series")).toEqual([]);
227
+ await expect(registry.invoke("charts.test", "search", {})).rejects.toThrow("not available");
228
+ enabled = true;
229
+ dispose();
230
+ await expect(registry.invoke("charts.test", "search", {})).rejects.toThrow("not available");
231
+ });
232
+
233
+ test("rejects unsafe chart-series requests and provider output before rendering", async () => {
234
+ const registry = new CapabilityRegistry();
235
+ registry.register("charts", chartSeriesProvider({
236
+ id: "charts.unsafe",
237
+ name: "Unsafe Charts",
238
+ provider: {
239
+ catalog: () => [{ seriesId: "ok", label: "x".repeat(161) }],
240
+ resolve: ({ seriesId }) => ({
241
+ id: seriesId,
242
+ label: "Unsafe",
243
+ color: "#ffffff",
244
+ unit: "value",
245
+ unitGroup: "value",
246
+ nativeFrequency: "daily",
247
+ dataShape: "scalar",
248
+ style: "line",
249
+ transform: "raw",
250
+ axis: "left",
251
+ panelId: "main",
252
+ interpolation: "none",
253
+ points: [{ date: new Date("invalid"), observedAt: new Date(), value: 1 }],
254
+ }),
255
+ },
256
+ }));
257
+
258
+ await expect(registry.invoke("charts.unsafe", "catalog", { query: "x".repeat(201), limit: 8 }, { renderer: true }))
259
+ .rejects.toThrow("catalog query");
260
+ await expect(registry.invoke("charts.unsafe", "catalog", { query: "x", limit: 8 }, { renderer: true }))
261
+ .rejects.toThrow("catalog item 0 label");
262
+ await expect(registry.invoke("charts.unsafe", "resolve", {
263
+ seriesId: "bad?query",
264
+ viewport: { range: "1M", resolution: "auto" },
265
+ }, { renderer: true })).rejects.toThrow("series ID");
266
+ await expect(registry.invoke("charts.unsafe", "resolve", {
267
+ seriesId: "safe-id",
268
+ parameters: {},
269
+ viewport: { range: "1M", resolution: "auto" },
270
+ }, { renderer: true })).rejects.toThrow('unsupported field "parameters"');
271
+ await expect(registry.invoke("charts.unsafe", "resolve", {
272
+ seriesId: "safe-id",
273
+ viewport: { range: "1M", resolution: "auto" },
274
+ }, { renderer: true })).rejects.toThrow("point 0 date");
275
+ });
276
+
142
277
  test("disposes subscriptions that finish after their capability is removed", async () => {
143
278
  const registry = new CapabilityRegistry();
144
279
  let finishSubscribe!: (dispose: () => void) => void;
@@ -12,6 +12,15 @@ interface SubscriptionEntry {
12
12
  dispose: () => void;
13
13
  }
14
14
 
15
+ const LOCAL_READ_OPERATIONS = new Set([
16
+ "canProvide",
17
+ "supports",
18
+ "getCachedFinancialsForTargets",
19
+ "getCachedNews",
20
+ "getChartResolutionSupport",
21
+ "getChartResolutionCapabilities",
22
+ ]);
23
+
15
24
  export class CapabilityRegistry {
16
25
  private readonly capabilities = new Map<string, RegisteredCapability>();
17
26
  private readonly subscriptions = new Map<string, SubscriptionEntry>();
@@ -57,8 +66,17 @@ export class CapabilityRegistry {
57
66
  ));
58
67
  }
59
68
 
60
- manifests({ rendererOnly = false }: { rendererOnly?: boolean } = {}): CapabilityManifest[] {
61
- return this.list().map(({ capability }) => {
69
+ manifests({
70
+ rendererOnly = false,
71
+ includeDisabled = false,
72
+ }: { rendererOnly?: boolean; includeDisabled?: boolean } = {}): CapabilityManifest[] {
73
+ const entries = includeDisabled
74
+ ? [...this.capabilities.values()].sort((left, right) => (
75
+ (left.capability.priority ?? 1000) - (right.capability.priority ?? 1000)
76
+ || left.capability.id.localeCompare(right.capability.id)
77
+ ))
78
+ : this.list();
79
+ return entries.map(({ capability }) => {
62
80
  const operations: CapabilityOperationManifest[] = Object.entries(capability.operations)
63
81
  .filter(([, operation]) => !rendererOnly || operation.rendererSafe === true)
64
82
  .map(([id, operation]) => ({
@@ -82,12 +100,21 @@ export class CapabilityRegistry {
82
100
  capabilityId: string,
83
101
  operationId: string,
84
102
  payload: unknown,
85
- options: { renderer?: boolean } = {},
103
+ options: { renderer?: boolean; signal?: AbortSignal } = {},
86
104
  ): Promise<T> {
87
105
  const { capability, operation } = this.resolveOperation(capabilityId, operationId, options);
88
106
  if (!operation.handler) throw new Error(`Capability operation "${capabilityId}.${operationId}" is not invokable.`);
107
+ options.signal?.throwIfAborted();
89
108
  const input = (operation.input ?? recordSchema).parse(payload);
90
- const result = await operation.handler(input, { capability, operationId });
109
+ const invoke = () => Promise.resolve(operation.handler!(input, {
110
+ capability,
111
+ operationId,
112
+ signal: options.signal,
113
+ }));
114
+ const health = this.options.connectionHealth;
115
+ const result = health?.hasSource(capabilityId) && !LOCAL_READ_OPERATIONS.has(operationId)
116
+ ? await health.track(capabilityId, operationId, invoke)
117
+ : await invoke();
91
118
  return (operation.output ? operation.output.parse(result) : result) as T;
92
119
  }
93
120
 
@@ -1,6 +1,13 @@
1
1
  import type { CachePolicyMap } from "../types/persistence";
2
+ import type { ConnectionHealthRegistry } from "../core/connection-health";
2
3
  import type { AssetDataProvider } from "../types/data-provider";
3
4
  import type { NewsDataProvider } from "../types/capability-route-source";
5
+ import type {
6
+ ChartViewportSpec,
7
+ ResolvedSeries,
8
+ SeriesStyle,
9
+ SeriesTransform,
10
+ } from "../time-series/types";
4
11
 
5
12
  type CapabilityOperationKind = "read" | "query" | "action" | "stream";
6
13
  type CapabilitySideEffectLevel = "none" | "local-write" | "network-write" | "external-trade" | "external-side-effect";
@@ -8,6 +15,7 @@ type CapabilitySideEffectLevel = "none" | "local-write" | "network-write" | "ext
8
15
  type CapabilityKind =
9
16
  | "asset-data"
10
17
  | "news"
18
+ | "chart-series"
11
19
  | "plugin-service";
12
20
 
13
21
  export interface CapabilitySchema<T = unknown> {
@@ -17,6 +25,7 @@ export interface CapabilitySchema<T = unknown> {
17
25
  interface CapabilityHandlerContext {
18
26
  capability: PluginCapability;
19
27
  operationId: string;
28
+ signal?: AbortSignal;
20
29
  }
21
30
 
22
31
  type CapabilityStreamEmit<T = unknown> = (event: T) => void;
@@ -65,6 +74,38 @@ export interface NewsCapability extends PluginCapability {
65
74
  readonly provider: NewsDataProvider;
66
75
  }
67
76
 
77
+ export interface ChartSeriesCatalogItem {
78
+ seriesId: string;
79
+ label: string;
80
+ description?: string;
81
+ detail?: string;
82
+ style?: SeriesStyle;
83
+ transform?: SeriesTransform;
84
+ }
85
+
86
+ export interface ChartSeriesCatalogRequest {
87
+ query?: string;
88
+ limit?: number;
89
+ signal?: AbortSignal;
90
+ }
91
+
92
+ export interface ChartSeriesResolveRequest {
93
+ seriesId: string;
94
+ viewport: ChartViewportSpec;
95
+ signal?: AbortSignal;
96
+ }
97
+
98
+ export interface ChartSeriesProvider {
99
+ catalog?(request: ChartSeriesCatalogRequest): Promise<ChartSeriesCatalogItem[]> | ChartSeriesCatalogItem[];
100
+ search?(request: ChartSeriesCatalogRequest): Promise<ChartSeriesCatalogItem[]> | ChartSeriesCatalogItem[];
101
+ resolve(request: ChartSeriesResolveRequest): Promise<ResolvedSeries> | ResolvedSeries;
102
+ }
103
+
104
+ export interface ChartSeriesCapability extends PluginCapability {
105
+ readonly kind: "chart-series";
106
+ readonly provider: ChartSeriesProvider;
107
+ }
108
+
68
109
  export interface CapabilityOperationManifest {
69
110
  id: string;
70
111
  kind: CapabilityOperationKind;
@@ -92,6 +133,17 @@ export interface CapabilityManifest {
92
133
  export interface CapabilityRegistryOptions {
93
134
  isPluginEnabled?(pluginId: string): boolean;
94
135
  isCapabilityEnabled?(capability: PluginCapability, pluginId: string): boolean;
136
+ connectionHealth?: ConnectionHealthRegistry;
137
+ }
138
+
139
+ export interface CapabilityInvoker {
140
+ capabilityManifests(kind?: string): CapabilityManifest[];
141
+ invokeCapability<T = unknown>(
142
+ capabilityId: string,
143
+ operationId: string,
144
+ payload: unknown,
145
+ options?: { signal?: AbortSignal },
146
+ ): Promise<T>;
95
147
  }
96
148
 
97
149
  export interface RegisteredCapability {