@dxos/app-framework 0.8.4-main.c85a9c8dae → 0.8.4-main.d05539e30a

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 (387) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/browser/{capability-7RLVE42K.mjs → capability-K5XIVCQU.mjs} +12 -11
  4. package/dist/lib/browser/capability-K5XIVCQU.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-5AHASNDW.mjs +95 -0
  6. package/dist/lib/browser/chunk-5AHASNDW.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-5GY3YOEL.mjs +28 -0
  8. package/dist/lib/browser/chunk-5GY3YOEL.mjs.map +7 -0
  9. package/dist/lib/browser/{chunk-PKQT6C53.mjs → chunk-66IXTIVK.mjs} +3 -2
  10. package/dist/lib/browser/chunk-66IXTIVK.mjs.map +7 -0
  11. package/dist/lib/browser/{chunk-ZRWBPIZG.mjs → chunk-BRK6GYNB.mjs} +14 -42
  12. package/dist/lib/browser/chunk-BRK6GYNB.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-FJ4765WW.mjs +8 -0
  14. package/dist/lib/browser/{chunk-FHQTHCX7.mjs.map → chunk-FJ4765WW.mjs.map} +3 -3
  15. package/dist/lib/browser/chunk-FO3IYSLV.mjs +68 -0
  16. package/dist/lib/browser/chunk-FO3IYSLV.mjs.map +7 -0
  17. package/dist/lib/browser/{chunk-YNFPIQGB.mjs → chunk-IW44C7UL.mjs} +9 -2
  18. package/dist/lib/browser/chunk-IW44C7UL.mjs.map +7 -0
  19. package/dist/lib/browser/{chunk-5RJNZV7K.mjs → chunk-KFDF7KR3.mjs} +11 -13
  20. package/dist/lib/browser/{chunk-5RJNZV7K.mjs.map → chunk-KFDF7KR3.mjs.map} +3 -3
  21. package/dist/lib/browser/chunk-KLHQNYJ2.mjs +422 -0
  22. package/dist/lib/browser/chunk-KLHQNYJ2.mjs.map +7 -0
  23. package/dist/lib/browser/chunk-QLML5QFJ.mjs +581 -0
  24. package/dist/lib/browser/chunk-QLML5QFJ.mjs.map +7 -0
  25. package/dist/lib/browser/{chunk-FNKT2QQ2.mjs → chunk-SLX73WRZ.mjs} +90 -17
  26. package/dist/lib/browser/chunk-SLX73WRZ.mjs.map +7 -0
  27. package/dist/lib/browser/chunk-UVTGHZQF.mjs +513 -0
  28. package/dist/lib/browser/chunk-UVTGHZQF.mjs.map +7 -0
  29. package/dist/lib/browser/chunk-VJ5PFAWC.mjs +1446 -0
  30. package/dist/lib/browser/chunk-VJ5PFAWC.mjs.map +7 -0
  31. package/dist/lib/browser/cli/index.mjs +17 -32
  32. package/dist/lib/browser/cli/index.mjs.map +3 -3
  33. package/dist/lib/browser/common/activation-events.mjs +11 -14
  34. package/dist/lib/browser/common/capabilities.mjs +19 -8
  35. package/dist/lib/browser/core/activation-event.mjs +1 -1
  36. package/dist/lib/browser/core/capability.mjs +5 -1
  37. package/dist/lib/browser/core/plugin-manager.mjs +8 -4
  38. package/dist/lib/browser/core/plugin.mjs +16 -4
  39. package/dist/lib/browser/core/url-loader.mjs +24 -0
  40. package/dist/lib/browser/index.mjs +47 -49
  41. package/dist/lib/browser/index.mjs.map +4 -4
  42. package/dist/lib/browser/meta.json +1 -1
  43. package/dist/lib/browser/process-manager-capability-JIWLN7SU.mjs +89 -0
  44. package/dist/lib/browser/process-manager-capability-JIWLN7SU.mjs.map +7 -0
  45. package/dist/lib/browser/testing/index.mjs +199 -56
  46. package/dist/lib/browser/testing/index.mjs.map +4 -4
  47. package/dist/lib/browser/testing/react.mjs +78 -0
  48. package/dist/lib/browser/testing/react.mjs.map +7 -0
  49. package/dist/lib/browser/ui/index.mjs +24 -15
  50. package/dist/lib/node-esm/{capability-EVZK4REM.mjs → capability-RLKFFLTB.mjs} +12 -11
  51. package/dist/lib/node-esm/capability-RLKFFLTB.mjs.map +7 -0
  52. package/dist/lib/node-esm/{chunk-UEWJDI2L.mjs → chunk-37Z53PXZ.mjs} +2 -2
  53. package/dist/lib/node-esm/{chunk-UEWJDI2L.mjs.map → chunk-37Z53PXZ.mjs.map} +3 -3
  54. package/dist/lib/node-esm/chunk-42UNAKYO.mjs +423 -0
  55. package/dist/lib/node-esm/chunk-42UNAKYO.mjs.map +7 -0
  56. package/dist/lib/node-esm/{chunk-CJCQS2YL.mjs → chunk-6S45OMUP.mjs} +90 -17
  57. package/dist/lib/node-esm/chunk-6S45OMUP.mjs.map +7 -0
  58. package/dist/lib/node-esm/{chunk-2A4PRBIX.mjs → chunk-BYHYYJZH.mjs} +14 -42
  59. package/dist/lib/node-esm/chunk-BYHYYJZH.mjs.map +7 -0
  60. package/dist/lib/node-esm/{chunk-SB5ODNPX.mjs → chunk-CTKEZHKF.mjs} +9 -2
  61. package/dist/lib/node-esm/chunk-CTKEZHKF.mjs.map +7 -0
  62. package/dist/lib/node-esm/chunk-JNT72ZCN.mjs +514 -0
  63. package/dist/lib/node-esm/chunk-JNT72ZCN.mjs.map +7 -0
  64. package/dist/lib/node-esm/chunk-KFZEB6BV.mjs +29 -0
  65. package/dist/lib/node-esm/chunk-KFZEB6BV.mjs.map +7 -0
  66. package/dist/lib/node-esm/chunk-LJNUFNDO.mjs +582 -0
  67. package/dist/lib/node-esm/chunk-LJNUFNDO.mjs.map +7 -0
  68. package/dist/lib/node-esm/{chunk-VUIUFIGT.mjs → chunk-OUEMWPIW.mjs} +11 -13
  69. package/dist/lib/node-esm/{chunk-VUIUFIGT.mjs.map → chunk-OUEMWPIW.mjs.map} +3 -3
  70. package/dist/lib/node-esm/chunk-PW2VYGOS.mjs +96 -0
  71. package/dist/lib/node-esm/chunk-PW2VYGOS.mjs.map +7 -0
  72. package/dist/lib/node-esm/chunk-SFYCO3PT.mjs +1447 -0
  73. package/dist/lib/node-esm/chunk-SFYCO3PT.mjs.map +7 -0
  74. package/dist/lib/node-esm/chunk-WK7OIQKI.mjs +70 -0
  75. package/dist/lib/node-esm/chunk-WK7OIQKI.mjs.map +7 -0
  76. package/dist/lib/node-esm/{chunk-7OWSHPYK.mjs → chunk-XOCUANHO.mjs} +3 -2
  77. package/dist/lib/node-esm/chunk-XOCUANHO.mjs.map +7 -0
  78. package/dist/lib/node-esm/cli/index.mjs +17 -32
  79. package/dist/lib/node-esm/cli/index.mjs.map +3 -3
  80. package/dist/lib/node-esm/common/activation-events.mjs +11 -14
  81. package/dist/lib/node-esm/common/capabilities.mjs +19 -8
  82. package/dist/lib/node-esm/core/activation-event.mjs +1 -1
  83. package/dist/lib/node-esm/core/capability.mjs +5 -1
  84. package/dist/lib/node-esm/core/plugin-manager.mjs +8 -4
  85. package/dist/lib/node-esm/core/plugin.mjs +16 -4
  86. package/dist/lib/node-esm/core/url-loader.mjs +25 -0
  87. package/dist/lib/node-esm/index.mjs +47 -49
  88. package/dist/lib/node-esm/index.mjs.map +4 -4
  89. package/dist/lib/node-esm/meta.json +1 -1
  90. package/dist/lib/node-esm/process-manager-capability-PHKLO2BL.mjs +90 -0
  91. package/dist/lib/node-esm/process-manager-capability-PHKLO2BL.mjs.map +7 -0
  92. package/dist/lib/node-esm/testing/index.mjs +199 -56
  93. package/dist/lib/node-esm/testing/index.mjs.map +4 -4
  94. package/dist/lib/node-esm/testing/react.mjs +79 -0
  95. package/dist/lib/node-esm/testing/react.mjs.map +7 -0
  96. package/dist/lib/node-esm/ui/index.mjs +24 -15
  97. package/dist/plugin/node-esm/index.mjs +893 -0
  98. package/dist/plugin/node-esm/index.mjs.map +7 -0
  99. package/dist/plugin/node-esm/meta.json +1 -0
  100. package/dist/types/src/cli/cli.d.ts +1 -3
  101. package/dist/types/src/cli/cli.d.ts.map +1 -1
  102. package/dist/types/src/common/activation-events.d.ts +10 -13
  103. package/dist/types/src/common/activation-events.d.ts.map +1 -1
  104. package/dist/types/src/common/annotations.d.ts +1 -0
  105. package/dist/types/src/common/annotations.d.ts.map +1 -0
  106. package/dist/types/src/common/capabilities.d.ts +113 -12
  107. package/dist/types/src/common/capabilities.d.ts.map +1 -1
  108. package/dist/types/src/common/operations.d.ts +8 -22
  109. package/dist/types/src/common/operations.d.ts.map +1 -1
  110. package/dist/types/src/core/activation-event.d.ts +5 -5
  111. package/dist/types/src/core/activation-event.d.ts.map +1 -1
  112. package/dist/types/src/core/capability-manager.d.ts +5 -0
  113. package/dist/types/src/core/capability-manager.d.ts.map +1 -1
  114. package/dist/types/src/core/capability.d.ts +13 -2
  115. package/dist/types/src/core/capability.d.ts.map +1 -1
  116. package/dist/types/src/core/edge-registry-plugin-provider.d.ts +30 -0
  117. package/dist/types/src/core/edge-registry-plugin-provider.d.ts.map +1 -0
  118. package/dist/types/src/core/index.d.ts +6 -0
  119. package/dist/types/src/core/index.d.ts.map +1 -1
  120. package/dist/types/src/core/plugin-asset-cache.d.ts +71 -0
  121. package/dist/types/src/core/plugin-asset-cache.d.ts.map +1 -0
  122. package/dist/types/src/core/plugin-manager.d.ts +238 -7
  123. package/dist/types/src/core/plugin-manager.d.ts.map +1 -1
  124. package/dist/types/src/core/plugin-manifest.d.ts +101 -0
  125. package/dist/types/src/core/plugin-manifest.d.ts.map +1 -0
  126. package/dist/types/src/core/plugin-manifest.test.d.ts +2 -0
  127. package/dist/types/src/core/plugin-manifest.test.d.ts.map +1 -0
  128. package/dist/types/src/core/plugin.d.ts +182 -7
  129. package/dist/types/src/core/plugin.d.ts.map +1 -1
  130. package/dist/types/src/core/registry.d.ts +107 -0
  131. package/dist/types/src/core/registry.d.ts.map +1 -0
  132. package/dist/types/src/core/url-loader.d.ts +127 -0
  133. package/dist/types/src/core/url-loader.d.ts.map +1 -0
  134. package/dist/types/src/core/url-loader.test.d.ts +2 -0
  135. package/dist/types/src/core/url-loader.test.d.ts.map +1 -0
  136. package/dist/types/src/helpers.d.ts.map +1 -1
  137. package/dist/types/src/index.d.ts +1 -2
  138. package/dist/types/src/index.d.ts.map +1 -1
  139. package/dist/types/src/plugin-process-manager/ProcessManagerPlugin.d.ts +3 -0
  140. package/dist/types/src/plugin-process-manager/ProcessManagerPlugin.d.ts.map +1 -0
  141. package/dist/types/src/plugin-process-manager/history/capability.d.ts.map +1 -0
  142. package/dist/types/src/plugin-process-manager/history/errors.d.ts +32 -0
  143. package/dist/types/src/plugin-process-manager/history/errors.d.ts.map +1 -0
  144. package/dist/types/src/{plugin-operation → plugin-process-manager}/history/history-tracker.d.ts +1 -1
  145. package/dist/types/src/plugin-process-manager/history/history-tracker.d.ts.map +1 -0
  146. package/dist/types/src/plugin-process-manager/history/history-tracker.test.d.ts.map +1 -0
  147. package/dist/types/src/plugin-process-manager/history/index.d.ts.map +1 -0
  148. package/dist/types/src/{plugin-operation → plugin-process-manager}/history/types.d.ts +1 -1
  149. package/dist/types/src/plugin-process-manager/history/types.d.ts.map +1 -0
  150. package/dist/types/src/{plugin-operation → plugin-process-manager}/history/undo-mapping.d.ts +1 -1
  151. package/dist/types/src/plugin-process-manager/history/undo-mapping.d.ts.map +1 -0
  152. package/dist/types/src/{plugin-operation → plugin-process-manager}/history/undo-registry.d.ts +1 -1
  153. package/dist/types/src/plugin-process-manager/history/undo-registry.d.ts.map +1 -0
  154. package/dist/types/src/plugin-process-manager/history/undo-registry.test.d.ts.map +1 -0
  155. package/dist/types/src/plugin-process-manager/index.d.ts +3 -0
  156. package/dist/types/src/plugin-process-manager/index.d.ts.map +1 -0
  157. package/dist/types/src/plugin-process-manager/meta.d.ts.map +1 -0
  158. package/dist/types/src/plugin-process-manager/process-manager-capability.d.ts +8 -0
  159. package/dist/types/src/plugin-process-manager/process-manager-capability.d.ts.map +1 -0
  160. package/dist/types/src/plugin-process-manager/testing.d.ts +59 -0
  161. package/dist/types/src/plugin-process-manager/testing.d.ts.map +1 -0
  162. package/dist/types/src/testing/harness.d.ts +79 -0
  163. package/dist/types/src/testing/harness.d.ts.map +1 -0
  164. package/dist/types/src/testing/index.d.ts +1 -0
  165. package/dist/types/src/testing/index.d.ts.map +1 -1
  166. package/dist/types/src/testing/react.d.ts +27 -0
  167. package/dist/types/src/testing/react.d.ts.map +1 -0
  168. package/dist/types/src/testing/react.test.d.ts +2 -0
  169. package/dist/types/src/testing/react.test.d.ts.map +1 -0
  170. package/dist/types/src/testing/service.d.ts.map +1 -1
  171. package/dist/types/src/testing/withPluginManager.d.ts.map +1 -1
  172. package/dist/types/src/testing/withPluginManager.stories.d.ts.map +1 -1
  173. package/dist/types/src/ui/components/App/App.d.ts +3 -2
  174. package/dist/types/src/ui/components/App/App.d.ts.map +1 -1
  175. package/dist/types/src/ui/components/App/App.stories.d.ts +2 -2
  176. package/dist/types/src/ui/components/App/App.stories.d.ts.map +1 -1
  177. package/dist/types/src/ui/components/Placeholder/Placeholder.d.ts +64 -0
  178. package/dist/types/src/ui/components/Placeholder/Placeholder.d.ts.map +1 -0
  179. package/dist/types/src/ui/components/Placeholder/Placeholder.stories.d.ts +19 -0
  180. package/dist/types/src/ui/components/Placeholder/Placeholder.stories.d.ts.map +1 -0
  181. package/dist/types/src/ui/components/Placeholder/index.d.ts +2 -0
  182. package/dist/types/src/ui/components/Placeholder/index.d.ts.map +1 -0
  183. package/dist/types/src/ui/components/PluginManager/PluginManagerContext.stories.d.ts.map +1 -1
  184. package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts +16 -4
  185. package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts.map +1 -1
  186. package/dist/types/src/ui/components/Surface/SurfaceComponent.stories.d.ts.map +1 -1
  187. package/dist/types/src/ui/components/Surface/SurfaceInfo.d.ts.map +1 -1
  188. package/dist/types/src/ui/components/Surface/SurfaceProfilerContext.d.ts +48 -0
  189. package/dist/types/src/ui/components/Surface/SurfaceProfilerContext.d.ts.map +1 -0
  190. package/dist/types/src/ui/components/Surface/index.d.ts +22 -6
  191. package/dist/types/src/ui/components/Surface/index.d.ts.map +1 -1
  192. package/dist/types/src/ui/components/Surface/types.d.ts +110 -9
  193. package/dist/types/src/ui/components/Surface/types.d.ts.map +1 -1
  194. package/dist/types/src/ui/components/Surface/types.test.d.ts +2 -0
  195. package/dist/types/src/ui/components/Surface/types.test.d.ts.map +1 -0
  196. package/dist/types/src/ui/components/index.d.ts +1 -0
  197. package/dist/types/src/ui/components/index.d.ts.map +1 -1
  198. package/dist/types/src/ui/hooks/index.d.ts +1 -1
  199. package/dist/types/src/ui/hooks/index.d.ts.map +1 -1
  200. package/dist/types/src/ui/hooks/useApp.d.ts +47 -11
  201. package/dist/types/src/ui/hooks/useApp.d.ts.map +1 -1
  202. package/dist/types/src/ui/hooks/useApp.test.d.ts +2 -0
  203. package/dist/types/src/ui/hooks/useApp.test.d.ts.map +1 -0
  204. package/dist/types/src/ui/hooks/useCapabilities.d.ts.map +1 -1
  205. package/dist/types/src/ui/hooks/useLoading.d.ts.map +1 -1
  206. package/dist/types/src/ui/hooks/useProcessManagerRuntime.d.ts +24 -0
  207. package/dist/types/src/ui/hooks/useProcessManagerRuntime.d.ts.map +1 -0
  208. package/dist/types/src/ui/hooks/useSettingsState.d.ts.map +1 -1
  209. package/dist/types/src/vite-plugin/boot-loader/BootLoader.stories.d.ts +34 -0
  210. package/dist/types/src/vite-plugin/boot-loader/BootLoader.stories.d.ts.map +1 -0
  211. package/dist/types/src/vite-plugin/boot-loader/index.d.ts +2 -0
  212. package/dist/types/src/vite-plugin/boot-loader/index.d.ts.map +1 -0
  213. package/dist/types/src/vite-plugin/boot-loader/loader.d.ts +51 -0
  214. package/dist/types/src/vite-plugin/boot-loader/loader.d.ts.map +1 -0
  215. package/dist/types/src/vite-plugin/composer/index.d.ts +34 -0
  216. package/dist/types/src/vite-plugin/composer/index.d.ts.map +1 -0
  217. package/dist/types/src/vite-plugin/import-map/index.d.ts +28 -0
  218. package/dist/types/src/vite-plugin/import-map/index.d.ts.map +1 -0
  219. package/dist/types/src/vite-plugin/index.d.ts +5 -0
  220. package/dist/types/src/vite-plugin/index.d.ts.map +1 -0
  221. package/dist/types/src/vite-plugin/manifest.d.ts +41 -0
  222. package/dist/types/src/vite-plugin/manifest.d.ts.map +1 -0
  223. package/dist/types/src/vite-plugin/manifest.test.d.ts +2 -0
  224. package/dist/types/src/vite-plugin/manifest.test.d.ts.map +1 -0
  225. package/dist/types/src/vite-plugin/packages.d.ts +13 -0
  226. package/dist/types/src/vite-plugin/packages.d.ts.map +1 -0
  227. package/dist/types/tsconfig.tsbuildinfo +1 -1
  228. package/moon.yml +15 -0
  229. package/package.json +53 -54
  230. package/src/cli/cli.ts +4 -9
  231. package/src/common/activation-events.ts +12 -17
  232. package/src/common/annotations.ts +3 -0
  233. package/src/common/capabilities.ts +160 -29
  234. package/src/common/operations.ts +7 -10
  235. package/src/context.ts +1 -1
  236. package/src/core/activation-event.ts +5 -2
  237. package/src/core/capability-manager.test.ts +1 -1
  238. package/src/core/capability-manager.ts +22 -1
  239. package/src/core/capability.ts +20 -2
  240. package/src/core/edge-registry-plugin-provider.ts +92 -0
  241. package/src/core/index.ts +6 -0
  242. package/src/core/plugin-asset-cache.ts +60 -0
  243. package/src/core/plugin-manager.test.ts +1085 -31
  244. package/src/core/plugin-manager.ts +1170 -198
  245. package/src/core/plugin-manifest.test.ts +75 -0
  246. package/src/core/plugin-manifest.ts +134 -0
  247. package/src/core/plugin.ts +194 -12
  248. package/src/core/registry.ts +163 -0
  249. package/src/core/url-loader.test.ts +221 -0
  250. package/src/core/url-loader.ts +388 -0
  251. package/src/index.ts +1 -2
  252. package/src/plugin-process-manager/ProcessManagerPlugin.ts +24 -0
  253. package/src/{plugin-operation → plugin-process-manager}/history/capability.ts +1 -2
  254. package/src/plugin-process-manager/history/errors.ts +7 -0
  255. package/src/{plugin-operation → plugin-process-manager}/history/history-tracker.test.ts +37 -43
  256. package/src/{plugin-operation → plugin-process-manager}/history/history-tracker.ts +1 -2
  257. package/src/{plugin-operation → plugin-process-manager}/history/types.ts +1 -1
  258. package/src/{plugin-operation → plugin-process-manager}/history/undo-mapping.ts +1 -1
  259. package/src/{plugin-operation → plugin-process-manager}/history/undo-registry.test.ts +3 -4
  260. package/src/{plugin-operation → plugin-process-manager}/history/undo-registry.ts +1 -1
  261. package/src/{plugin-operation → plugin-process-manager}/index.ts +1 -1
  262. package/src/plugin-process-manager/meta.ts +14 -0
  263. package/src/plugin-process-manager/process-manager-capability.ts +178 -0
  264. package/src/{plugin-operation → plugin-process-manager}/testing.ts +26 -45
  265. package/src/testing/harness.ts +247 -0
  266. package/src/testing/index.ts +1 -0
  267. package/src/testing/react.test.tsx +48 -0
  268. package/src/testing/react.tsx +113 -0
  269. package/src/testing/service.ts +4 -4
  270. package/src/testing/withPluginManager.stories.tsx +1 -2
  271. package/src/testing/withPluginManager.tsx +45 -20
  272. package/src/ui/components/App/App.stories.tsx +7 -13
  273. package/src/ui/components/App/App.tsx +29 -5
  274. package/src/ui/components/Placeholder/Placeholder.stories.tsx +77 -0
  275. package/src/ui/components/Placeholder/Placeholder.tsx +155 -0
  276. package/src/{plugin-runtime → ui/components/Placeholder}/index.ts +1 -1
  277. package/src/ui/components/PluginManager/PluginManagerContext.stories.tsx +8 -7
  278. package/src/ui/components/Surface/SurfaceComponent.stories.tsx +16 -15
  279. package/src/ui/components/Surface/SurfaceComponent.tsx +111 -55
  280. package/src/ui/components/Surface/SurfaceInfo.tsx +0 -1
  281. package/src/ui/components/Surface/SurfaceProfilerContext.tsx +207 -0
  282. package/src/ui/components/Surface/index.ts +35 -1
  283. package/src/ui/components/Surface/types.test.ts +126 -0
  284. package/src/ui/components/Surface/types.ts +164 -12
  285. package/src/ui/components/index.ts +1 -0
  286. package/src/ui/hooks/index.ts +1 -1
  287. package/src/ui/hooks/useApp.test.tsx +159 -0
  288. package/src/ui/hooks/useApp.tsx +229 -24
  289. package/src/ui/hooks/useLoading.tsx +14 -6
  290. package/src/ui/hooks/useProcessManagerRuntime.ts +68 -0
  291. package/src/vite-plugin/boot-loader/BootLoader.stories.tsx +270 -0
  292. package/src/vite-plugin/boot-loader/boot-loader.css +320 -0
  293. package/src/vite-plugin/boot-loader/boot-loader.js +325 -0
  294. package/src/vite-plugin/boot-loader/index.ts +5 -0
  295. package/src/vite-plugin/boot-loader/loader.ts +123 -0
  296. package/src/vite-plugin/composer/index.ts +306 -0
  297. package/src/vite-plugin/import-map/index.ts +527 -0
  298. package/src/vite-plugin/index.ts +10 -0
  299. package/src/vite-plugin/manifest.test.ts +46 -0
  300. package/src/vite-plugin/manifest.ts +57 -0
  301. package/src/vite-plugin/packages.ts +187 -0
  302. package/tsconfig.json +25 -1
  303. package/tsconfig.node.json +1 -1
  304. package/vitest.config.ts +1 -1
  305. package/.swc/plugins/linux_x86_64_19.0.0/727453fb3a62f7f1d952a41e051ca8a6f88cadc45cee43c6a4d1aa45f9b75665.wasmer-v7 +0 -0
  306. package/dist/lib/browser/capability-2GL5JAGJ.mjs +0 -37
  307. package/dist/lib/browser/capability-2GL5JAGJ.mjs.map +0 -7
  308. package/dist/lib/browser/capability-7RLVE42K.mjs.map +0 -7
  309. package/dist/lib/browser/chunk-4CTRO67U.mjs +0 -703
  310. package/dist/lib/browser/chunk-4CTRO67U.mjs.map +0 -7
  311. package/dist/lib/browser/chunk-FHQTHCX7.mjs +0 -8
  312. package/dist/lib/browser/chunk-FNKT2QQ2.mjs.map +0 -7
  313. package/dist/lib/browser/chunk-HE27PNNQ.mjs +0 -824
  314. package/dist/lib/browser/chunk-HE27PNNQ.mjs.map +0 -7
  315. package/dist/lib/browser/chunk-NPUEVX42.mjs +0 -34
  316. package/dist/lib/browser/chunk-NPUEVX42.mjs.map +0 -7
  317. package/dist/lib/browser/chunk-PKQT6C53.mjs.map +0 -7
  318. package/dist/lib/browser/chunk-REORGDJT.mjs +0 -80
  319. package/dist/lib/browser/chunk-REORGDJT.mjs.map +0 -7
  320. package/dist/lib/browser/chunk-YAFEA4GV.mjs +0 -1
  321. package/dist/lib/browser/chunk-YNFPIQGB.mjs.map +0 -7
  322. package/dist/lib/browser/chunk-ZRWBPIZG.mjs.map +0 -7
  323. package/dist/lib/browser/invoker-capability-BNLVNYHU.mjs +0 -36
  324. package/dist/lib/browser/invoker-capability-BNLVNYHU.mjs.map +0 -7
  325. package/dist/lib/node-esm/capability-CHIMU6LX.mjs +0 -38
  326. package/dist/lib/node-esm/capability-CHIMU6LX.mjs.map +0 -7
  327. package/dist/lib/node-esm/capability-EVZK4REM.mjs.map +0 -7
  328. package/dist/lib/node-esm/chunk-2A4PRBIX.mjs.map +0 -7
  329. package/dist/lib/node-esm/chunk-7CPNAEGV.mjs +0 -704
  330. package/dist/lib/node-esm/chunk-7CPNAEGV.mjs.map +0 -7
  331. package/dist/lib/node-esm/chunk-7OWSHPYK.mjs.map +0 -7
  332. package/dist/lib/node-esm/chunk-CJCQS2YL.mjs.map +0 -7
  333. package/dist/lib/node-esm/chunk-DTCHT2X2.mjs +0 -825
  334. package/dist/lib/node-esm/chunk-DTCHT2X2.mjs.map +0 -7
  335. package/dist/lib/node-esm/chunk-JAZVHID3.mjs +0 -35
  336. package/dist/lib/node-esm/chunk-JAZVHID3.mjs.map +0 -7
  337. package/dist/lib/node-esm/chunk-SB5ODNPX.mjs.map +0 -7
  338. package/dist/lib/node-esm/chunk-UFW652GS.mjs +0 -81
  339. package/dist/lib/node-esm/chunk-UFW652GS.mjs.map +0 -7
  340. package/dist/lib/node-esm/chunk-Z4TJPSMP.mjs +0 -2
  341. package/dist/lib/node-esm/invoker-capability-VF6SP44V.mjs +0 -37
  342. package/dist/lib/node-esm/invoker-capability-VF6SP44V.mjs.map +0 -7
  343. package/dist/types/src/plugin-operation/OperationPlugin.d.ts +0 -3
  344. package/dist/types/src/plugin-operation/OperationPlugin.d.ts.map +0 -1
  345. package/dist/types/src/plugin-operation/history/capability.d.ts.map +0 -1
  346. package/dist/types/src/plugin-operation/history/errors.d.ts +0 -5
  347. package/dist/types/src/plugin-operation/history/errors.d.ts.map +0 -1
  348. package/dist/types/src/plugin-operation/history/history-tracker.d.ts.map +0 -1
  349. package/dist/types/src/plugin-operation/history/history-tracker.test.d.ts.map +0 -1
  350. package/dist/types/src/plugin-operation/history/index.d.ts.map +0 -1
  351. package/dist/types/src/plugin-operation/history/types.d.ts.map +0 -1
  352. package/dist/types/src/plugin-operation/history/undo-mapping.d.ts.map +0 -1
  353. package/dist/types/src/plugin-operation/history/undo-registry.d.ts.map +0 -1
  354. package/dist/types/src/plugin-operation/history/undo-registry.test.d.ts.map +0 -1
  355. package/dist/types/src/plugin-operation/index.d.ts +0 -3
  356. package/dist/types/src/plugin-operation/index.d.ts.map +0 -1
  357. package/dist/types/src/plugin-operation/invoker-capability.d.ts +0 -6
  358. package/dist/types/src/plugin-operation/invoker-capability.d.ts.map +0 -1
  359. package/dist/types/src/plugin-operation/meta.d.ts.map +0 -1
  360. package/dist/types/src/plugin-operation/testing.d.ts +0 -109
  361. package/dist/types/src/plugin-operation/testing.d.ts.map +0 -1
  362. package/dist/types/src/plugin-runtime/RuntimePlugin.d.ts +0 -3
  363. package/dist/types/src/plugin-runtime/RuntimePlugin.d.ts.map +0 -1
  364. package/dist/types/src/plugin-runtime/capability.d.ts +0 -6
  365. package/dist/types/src/plugin-runtime/capability.d.ts.map +0 -1
  366. package/dist/types/src/plugin-runtime/index.d.ts +0 -2
  367. package/dist/types/src/plugin-runtime/index.d.ts.map +0 -1
  368. package/dist/types/src/plugin-runtime/meta.d.ts +0 -3
  369. package/dist/types/src/plugin-runtime/meta.d.ts.map +0 -1
  370. package/dist/types/src/ui/hooks/useOperationResolver.d.ts +0 -19
  371. package/dist/types/src/ui/hooks/useOperationResolver.d.ts.map +0 -1
  372. package/src/plugin-operation/OperationPlugin.ts +0 -25
  373. package/src/plugin-operation/history/errors.ts +0 -11
  374. package/src/plugin-operation/invoker-capability.ts +0 -40
  375. package/src/plugin-operation/meta.ts +0 -11
  376. package/src/plugin-runtime/RuntimePlugin.ts +0 -20
  377. package/src/plugin-runtime/capability.ts +0 -53
  378. package/src/plugin-runtime/meta.ts +0 -11
  379. package/src/ui/hooks/useOperationResolver.ts +0 -40
  380. /package/dist/lib/browser/{chunk-YAFEA4GV.mjs.map → core/url-loader.mjs.map} +0 -0
  381. /package/dist/lib/node-esm/{chunk-Z4TJPSMP.mjs.map → core/url-loader.mjs.map} +0 -0
  382. /package/dist/types/src/{plugin-operation → plugin-process-manager}/history/capability.d.ts +0 -0
  383. /package/dist/types/src/{plugin-operation → plugin-process-manager}/history/history-tracker.test.d.ts +0 -0
  384. /package/dist/types/src/{plugin-operation → plugin-process-manager}/history/index.d.ts +0 -0
  385. /package/dist/types/src/{plugin-operation → plugin-process-manager}/history/undo-registry.test.d.ts +0 -0
  386. /package/dist/types/src/{plugin-operation → plugin-process-manager}/meta.d.ts +0 -0
  387. /package/src/{plugin-operation → plugin-process-manager}/history/index.ts +0 -0
@@ -2,9 +2,11 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ import { useAtomValue } from '@effect-atom/atom-react';
5
6
  import React, {
6
7
  Fragment,
7
8
  type NamedExoticComponent,
9
+ Profiler,
8
10
  type RefAttributes,
9
11
  Suspense,
10
12
  forwardRef,
@@ -21,22 +23,29 @@ import { byPosition } from '@dxos/util';
21
23
 
22
24
  import { Capabilities } from '../../../common';
23
25
  import { type CapabilityManager } from '../../../core';
24
- import { useCapabilities } from '../../hooks';
25
-
26
+ import { usePluginManager } from '../PluginManager/PluginManagerProvider';
26
27
  import { SurfaceContext } from './context';
27
28
  import { SurfaceInfo } from './SurfaceInfo';
28
- import { type Definition, type Props, type WebComponentDefinition } from './types';
29
-
30
- const DEFAULT_PLACEHOLDER = <Fragment />;
29
+ import { useSurfaceProfilerCallback } from './SurfaceProfilerContext';
30
+ import {
31
+ type Definition,
32
+ type Props,
33
+ type RoleToken,
34
+ type TokenData,
35
+ type TypedProps,
36
+ type WebComponentDefinition,
37
+ } from './types';
31
38
 
32
39
  const DEBUG = import.meta.env.VITE_DEBUG;
33
40
 
41
+ const DEFAULT_PLACEHOLDER = <Fragment />;
42
+
34
43
  /**
35
44
  * Wrapper component for rendering Web Component surfaces.
36
45
  * Handles creation, prop setting, and cleanup of Web Components.
37
46
  */
38
47
  const WebComponentWrapper = memo(
39
- forwardRef<HTMLElement, Props & { definition: WebComponentDefinition }>(
48
+ forwardRef<HTMLElement, Omit<Props, 'role'> & { role: string; definition: WebComponentDefinition }>(
40
49
  ({ id, role, data, limit, definition, ...rest }, forwardedRef) => {
41
50
  const containerRef = useRef<HTMLDivElement>(null);
42
51
  const elementRef = useRef<HTMLElement | null>(null);
@@ -106,9 +115,11 @@ WebComponentWrapper.displayName = 'WebComponentWrapper';
106
115
  */
107
116
  // TODO(burdon): Allow DebugPlugin to provide different fallback using react-ui ErrorFallback.
108
117
  const SurfaceContextProvider = memo(
109
- forwardRef<HTMLElement, Props & { definition: Definition }>(
118
+ forwardRef<HTMLElement, Omit<Props, 'role'> & { role: string; definition: Definition }>(
110
119
  ({ id, role, data, limit, fallback = ErrorFallback, definition, ...rest }, forwardedRef) => {
111
120
  const contextValue = useMemo(() => ({ id, role, data }), [id, role, data]);
121
+ const onProfilerRender = useSurfaceProfilerCallback();
122
+ const profilerId = `surface/${id}/${role}`;
112
123
 
113
124
  // Handle Web Component surfaces
114
125
  if (definition.kind === 'web-component') {
@@ -137,10 +148,16 @@ const SurfaceContextProvider = memo(
137
148
  if (debug) {
138
149
  return (
139
150
  <ErrorBoundary name='surface' resetKeys={[data]} FallbackComponent={fallback}>
140
- <div role='none' className='contents' data-id={id} data-role={role}>
151
+ <div className='contents' data-id={id} data-role={role}>
141
152
  <SurfaceContext.Provider value={contextValue}>
142
153
  <SurfaceInfo ref={forwardedRef}>
143
- <Component id={id} role={role} data={data} limit={limit} {...rest} />
154
+ {onProfilerRender && !profilerId.includes('org.dxos.plugin.debug') ? (
155
+ <Profiler id={profilerId} onRender={onProfilerRender}>
156
+ <Component id={id} role={role} data={data} limit={limit} {...rest} />
157
+ </Profiler>
158
+ ) : (
159
+ <Component id={id} role={role} data={data} limit={limit} {...rest} />
160
+ )}
144
161
  </SurfaceInfo>
145
162
  </SurfaceContext.Provider>
146
163
  </div>
@@ -150,7 +167,7 @@ const SurfaceContextProvider = memo(
150
167
 
151
168
  return (
152
169
  <ErrorBoundary name='surface' resetKeys={[data]} FallbackComponent={fallback}>
153
- <div role='none' className='contents' data-id={id} data-role={role}>
170
+ <div className='contents' data-id={id} data-role={role}>
154
171
  <SurfaceContext.Provider value={contextValue}>
155
172
  <Component id={id} role={role} data={data} limit={limit} {...rest} ref={forwardedRef} />
156
173
  </SurfaceContext.Provider>
@@ -167,43 +184,53 @@ SurfaceContextProvider.displayName = 'SurfaceContextProvider';
167
184
  * A surface is a named region of the screen that can be populated by plugins.
168
185
  */
169
186
  // TODO(burdon): Remove `ref` since relying on this would be error prone.
170
- export const SurfaceComponent: NamedExoticComponent<Props & RefAttributes<HTMLElement>> = memo(
171
- forwardRef(({ id: _id, role, data: dataProp, limit, placeholder = DEFAULT_PLACEHOLDER, ...rest }, forwardedRef) => {
172
- const data = useDefaultValue(dataProp, () => ({}));
173
-
174
- // TODO(wittjosiah): This will make all surfaces depend on a single signal.
175
- // This isn't ideal because it means that any change to the data will cause all surfaces to re-render.
176
- // This effectively means that plugin modules which contribute surfaces need to all be activated at startup.
177
- // This should be fine for now because it's how it worked prior to capabilities api anyway.
178
- // In the future, it would be nice to be able to bucket the surface contributions by role.
179
- const surfaces = useSurfaces();
180
-
181
- // NOTE: Memoizing the candidates makes the surface not re-render based on reactivity within data.
182
- const definitions = findCandidates(surfaces, { role, data });
183
- const candidates = limit ? definitions.slice(0, limit) : definitions;
184
- if (DEBUG && candidates.length === 0) {
185
- log.warn('no candidates for surface', { role, data });
186
- return null;
187
- }
187
+ export const SurfaceComponent = memo(
188
+ forwardRef<HTMLElement, Props & { type?: RoleToken<any> }>(
189
+ ({ id: _id, role, type, data: dataProp, limit, placeholder = DEFAULT_PLACEHOLDER, ...rest }, forwardedRef) => {
190
+ const data = useDefaultValue(dataProp, () => ({}));
191
+ // TODO(wittjosiah): This will make all surfaces depend on a single signal.
192
+ // This isn't ideal because it means that any change to the data will cause all surfaces to re-render.
193
+ // This effectively means that plugin modules which contribute surfaces need to all be activated at startup.
194
+ // This should be fine for now because it's how it worked prior to capabilities api anyway.
195
+ // In the future, it would be nice to be able to bucket the surface contributions by role.
196
+ const surfaces = useSurfaces();
197
+
198
+ const effectiveRole = role ?? type?.role;
199
+ if (effectiveRole == null) {
200
+ if (DEBUG) {
201
+ log.warn('Surface has neither `role` nor `type` prop', { id: _id });
202
+ }
203
+ return null;
204
+ }
188
205
 
189
- return (
190
- <Suspense fallback={placeholder}>
191
- {candidates.map((definition) => (
192
- <SurfaceContextProvider
193
- key={definition.id}
194
- id={definition.id}
195
- role={role}
196
- data={data}
197
- limit={limit}
198
- definition={definition}
199
- ref={forwardedRef}
200
- {...rest}
201
- />
202
- ))}
203
- </Suspense>
204
- );
205
- }),
206
- );
206
+ // NOTE: Memoizing the candidates makes the surface not re-render based on reactivity within data.
207
+ const definitions = findCandidates(surfaces, { role: effectiveRole, data });
208
+ const candidates = limit ? definitions.slice(0, limit) : definitions;
209
+ if (DEBUG && candidates.length === 0) {
210
+ log.warn('no candidates for surface', { role: effectiveRole, data });
211
+ return null;
212
+ }
213
+
214
+ return (
215
+ <Suspense fallback={placeholder}>
216
+ {candidates.map((definition) => (
217
+ <SurfaceContextProvider
218
+ key={definition.id}
219
+ id={definition.id}
220
+ role={effectiveRole}
221
+ data={data}
222
+ limit={limit}
223
+ definition={definition}
224
+ ref={forwardedRef}
225
+ {...rest}
226
+ />
227
+ ))}
228
+ </Suspense>
229
+ );
230
+ },
231
+ ),
232
+ ) as (<TToken extends RoleToken<any>>(props: TypedProps<TToken> & RefAttributes<HTMLElement>) => React.ReactNode) &
233
+ NamedExoticComponent<Props & RefAttributes<HTMLElement>>;
207
234
 
208
235
  SurfaceComponent.displayName = 'Surface';
209
236
 
@@ -217,12 +244,12 @@ const ErrorFallback = ({ error }: Props) => {
217
244
  );
218
245
  };
219
246
 
220
- const findCandidates = (surfaces: Definition[], { role, data }: Pick<Props, 'role' | 'data'>) => {
247
+ const findCandidates = (surfaces: Definition[], { role, data }: { role: string; data: Props['data'] }) => {
221
248
  return Object.values(surfaces)
222
249
  .filter((definition) =>
223
250
  Array.isArray(definition.role) ? definition.role.includes(role) : definition.role === role,
224
251
  )
225
- .filter(({ filter }) => (filter ? filter(data ?? {}) : true))
252
+ .filter(({ filter }) => (filter ? filter(data ?? {}, role) : true))
226
253
  .toSorted(byPosition);
227
254
  };
228
255
 
@@ -230,18 +257,47 @@ const findCandidates = (surfaces: Definition[], { role, data }: Pick<Props, 'rol
230
257
  * @internal
231
258
  */
232
259
  export const useSurfaces = () => {
233
- const surfaces = useCapabilities(Capabilities.ReactSurface);
234
- return useMemo(() => surfaces.flat(), [surfaces]);
260
+ const manager = usePluginManager();
261
+ const surfacesByModule = useAtomValue(manager.capabilities.atomByModule(Capabilities.ReactSurface));
262
+ return useMemo(() => {
263
+ const result: Definition[] = [];
264
+ for (const [moduleId, surfaces] of Object.entries(surfacesByModule)) {
265
+ for (const def of surfaces.flat()) {
266
+ result.push({ ...def, id: `${moduleId}.${def.id}` });
267
+ }
268
+ }
269
+ return result;
270
+ }, [surfacesByModule]);
235
271
  };
236
272
 
237
273
  /**
238
274
  * @returns `true` if there is a contributed surface which matches the specified role & data, `false` otherwise.
275
+ *
276
+ * Two overloads:
277
+ * - Typed: pass a `type` role token and `data` is constrained to the token's
278
+ * declared contract (e.g. `AppSurface.Section` requires `attendableId`).
279
+ * - Legacy: pass a string `role` and `data` is untyped.
239
280
  */
240
- export const isSurfaceAvailable = (
281
+ export function isSurfaceAvailable<TToken extends RoleToken<any>>(
282
+ capabilityManager: CapabilityManager.CapabilityManager,
283
+ args: { type: TToken; data?: TokenData<TToken>; role?: never },
284
+ ): boolean;
285
+ export function isSurfaceAvailable(
241
286
  capabilityManager: CapabilityManager.CapabilityManager,
242
- { role, data }: Pick<Props, 'role' | 'data'>,
243
- ) => {
287
+ args: Pick<Props, 'role' | 'data'> & { type?: undefined },
288
+ ): boolean;
289
+ export function isSurfaceAvailable(
290
+ capabilityManager: CapabilityManager.CapabilityManager,
291
+ { role, type, data }: { role?: string; type?: RoleToken<any>; data?: unknown },
292
+ ): boolean {
293
+ const effectiveRole = role ?? type?.role;
294
+ if (effectiveRole == null) {
295
+ return false;
296
+ }
244
297
  const surfaces = capabilityManager.getAll(Capabilities.ReactSurface);
245
- const candidates = findCandidates(surfaces.flat(), { role, data });
298
+ const candidates = findCandidates(surfaces.flat(), {
299
+ role: effectiveRole,
300
+ data: data as Props['data'],
301
+ });
246
302
  return candidates.length > 0;
247
- };
303
+ }
@@ -65,7 +65,6 @@ export const SurfaceInfo = forwardRef<HTMLElement, SurfaceInfoProps>(({ children
65
65
  {rect &&
66
66
  createPortal(
67
67
  <div
68
- role='none'
69
68
  className='z-[100] fixed flex flex-col-reverse scrollbar-none overflow-auto pointer-events-none'
70
69
  style={{
71
70
  top: rect.top + padding,
@@ -0,0 +1,207 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import React, {
6
+ type Context,
7
+ type PropsWithChildren,
8
+ type ProfilerOnRenderCallback,
9
+ createContext,
10
+ useContext,
11
+ useMemo,
12
+ useRef,
13
+ useSyncExternalStore,
14
+ } from 'react';
15
+
16
+ const MAX_ENTRIES = 500;
17
+
18
+ /**
19
+ * Single profiler record captured from React Profiler onRender callback.
20
+ */
21
+ export type SurfaceProfilerEntry = {
22
+ id: string;
23
+ phase: 'mount' | 'update' | 'nested-update';
24
+ actualDuration: number;
25
+ baseDuration: number;
26
+ startTime: number;
27
+ commitTime: number;
28
+ timestamp: number;
29
+ };
30
+
31
+ /**
32
+ * Aggregated stats for a single profiled surface.
33
+ */
34
+ export type SurfaceProfilerStats = {
35
+ id: string;
36
+ mountCount: number;
37
+ updateCount: number;
38
+ totalRenders: number;
39
+ avgActualDuration: number;
40
+ maxActualDuration: number;
41
+ avgBaseDuration: number;
42
+ lastActualDuration: number;
43
+ lastCommitTime: number;
44
+ };
45
+
46
+ /**
47
+ * Store that collects profiler entries and notifies subscribers.
48
+ */
49
+ class SurfaceProfilerStore {
50
+ private _entries: SurfaceProfilerEntry[] = [];
51
+ private _listeners = new Set<() => void>();
52
+ private _snapshot: readonly SurfaceProfilerEntry[] = [];
53
+ private _pendingNotify = false;
54
+
55
+ /** Records an entry and schedules a deferred notification to avoid re-render loops. */
56
+ record(entry: SurfaceProfilerEntry) {
57
+ this._entries.push(entry);
58
+ if (this._entries.length > MAX_ENTRIES) {
59
+ this._entries = this._entries.slice(-MAX_ENTRIES);
60
+ }
61
+ this._snapshot = [...this._entries];
62
+ this._scheduleNotify();
63
+ }
64
+
65
+ clear() {
66
+ this._entries = [];
67
+ this._snapshot = [];
68
+ this._notifySync();
69
+ }
70
+
71
+ subscribe = (listener: () => void) => {
72
+ this._listeners.add(listener);
73
+ return () => {
74
+ this._listeners.delete(listener);
75
+ };
76
+ };
77
+
78
+ getSnapshot = (): readonly SurfaceProfilerEntry[] => {
79
+ return this._snapshot;
80
+ };
81
+
82
+ /**
83
+ * Defers notification to the next animation frame to break the
84
+ * Profiler onRender → record → notify → re-render → onRender loop.
85
+ */
86
+ private _scheduleNotify() {
87
+ if (!this._pendingNotify) {
88
+ this._pendingNotify = true;
89
+ requestAnimationFrame(() => {
90
+ this._pendingNotify = false;
91
+ this._notifySync();
92
+ });
93
+ }
94
+ }
95
+
96
+ private _notifySync() {
97
+ for (const listener of this._listeners) {
98
+ listener();
99
+ }
100
+ }
101
+ }
102
+
103
+ type SurfaceProfilerContextValue = {
104
+ store: SurfaceProfilerStore;
105
+ };
106
+
107
+ const SurfaceProfilerContext: Context<SurfaceProfilerContextValue | undefined> = createContext<
108
+ SurfaceProfilerContextValue | undefined
109
+ >(undefined);
110
+
111
+ /**
112
+ * Provider that collects React Profiler data from Surface components.
113
+ */
114
+ export const SurfaceProfilerProvider = ({ children }: PropsWithChildren) => {
115
+ const storeRef = useRef<SurfaceProfilerStore>(null);
116
+ if (!storeRef.current) {
117
+ storeRef.current = new SurfaceProfilerStore();
118
+ }
119
+ return (
120
+ <SurfaceProfilerContext.Provider value={{ store: storeRef.current }}>{children}</SurfaceProfilerContext.Provider>
121
+ );
122
+ };
123
+
124
+ /**
125
+ * Returns a stable onRender callback for use with React Profiler.
126
+ */
127
+ export const useSurfaceProfilerCallback = (): ProfilerOnRenderCallback | undefined => {
128
+ const store = useContext(SurfaceProfilerContext)?.store;
129
+ return useMemo<ProfilerOnRenderCallback | undefined>(() => {
130
+ if (!store) {
131
+ return undefined;
132
+ }
133
+ return (id, phase, actualDuration, baseDuration, startTime, commitTime) => {
134
+ store.record({
135
+ id,
136
+ phase,
137
+ actualDuration,
138
+ baseDuration,
139
+ startTime,
140
+ commitTime,
141
+ timestamp: Date.now(),
142
+ });
143
+ };
144
+ }, [store]);
145
+ };
146
+
147
+ /**
148
+ * Returns all profiler entries reactively.
149
+ */
150
+ export const useSurfaceProfilerEntries = (): readonly SurfaceProfilerEntry[] => {
151
+ const context = useContext(SurfaceProfilerContext);
152
+ return useSyncExternalStore(context?.store.subscribe ?? noop, context?.store.getSnapshot ?? emptySnapshot);
153
+ };
154
+
155
+ /**
156
+ * Returns aggregated stats grouped by surface id.
157
+ */
158
+ export const useSurfaceProfilerStats = (): SurfaceProfilerStats[] => {
159
+ const entries = useSurfaceProfilerEntries();
160
+ const statsMap = new Map<string, SurfaceProfilerStats>();
161
+
162
+ for (const entry of entries) {
163
+ let stats = statsMap.get(entry.id);
164
+ if (!stats) {
165
+ stats = {
166
+ id: entry.id,
167
+ mountCount: 0,
168
+ updateCount: 0,
169
+ totalRenders: 0,
170
+ avgActualDuration: 0,
171
+ maxActualDuration: 0,
172
+ avgBaseDuration: 0,
173
+ lastActualDuration: 0,
174
+ lastCommitTime: 0,
175
+ };
176
+ statsMap.set(entry.id, stats);
177
+ }
178
+
179
+ if (entry.phase === 'mount') {
180
+ stats.mountCount++;
181
+ } else {
182
+ stats.updateCount++;
183
+ }
184
+ stats.totalRenders++;
185
+ stats.avgActualDuration =
186
+ (stats.avgActualDuration * (stats.totalRenders - 1) + entry.actualDuration) / stats.totalRenders;
187
+ stats.avgBaseDuration =
188
+ (stats.avgBaseDuration * (stats.totalRenders - 1) + entry.baseDuration) / stats.totalRenders;
189
+ stats.maxActualDuration = Math.max(stats.maxActualDuration, entry.actualDuration);
190
+ stats.lastActualDuration = entry.actualDuration;
191
+ stats.lastCommitTime = entry.commitTime;
192
+ }
193
+
194
+ return [...statsMap.values()].sort((a, b) => b.maxActualDuration - a.maxActualDuration);
195
+ };
196
+
197
+ /**
198
+ * Clears all collected profiler entries.
199
+ */
200
+ export const useSurfaceProfilerClear = (): (() => void) | undefined => {
201
+ const store = useContext(SurfaceProfilerContext)?.store;
202
+ return useMemo(() => (store ? () => store.clear() : undefined), [store]);
203
+ };
204
+
205
+ const noop = () => () => {};
206
+ const EMPTY_SNAPSHOT: readonly SurfaceProfilerEntry[] = [];
207
+ const emptySnapshot = () => EMPTY_SNAPSHOT;
@@ -5,7 +5,25 @@
5
5
  // TODO(wittjosiah): Cleanup to avoid re-naming.
6
6
  import { SurfaceContext } from './context';
7
7
  import { SurfaceComponent, isSurfaceAvailable } from './SurfaceComponent';
8
- import { type Definition as SurfaceDefinition, create as createSurface, createWeb as createWebSurface } from './types';
8
+ import {
9
+ SurfaceProfilerProvider,
10
+ useSurfaceProfilerCallback,
11
+ useSurfaceProfilerClear,
12
+ useSurfaceProfilerEntries,
13
+ useSurfaceProfilerStats,
14
+ } from './SurfaceProfilerContext';
15
+ import {
16
+ type Definition as SurfaceDefinition,
17
+ type RoleToken as SurfaceRoleToken,
18
+ type SurfaceBinding as SurfaceBindingType,
19
+ type SurfaceFilter as SurfaceFilterType,
20
+ type TokenData as SurfaceTokenData,
21
+ type TypedProps as SurfaceTypedProps,
22
+ create as createSurface,
23
+ createWeb as createWebSurface,
24
+ isSurfaceFilter as isSurfaceFilterFn,
25
+ makeType as makeTypeFn,
26
+ } from './types';
9
27
 
10
28
  export namespace Surface {
11
29
  export type Definition = SurfaceDefinition;
@@ -17,4 +35,20 @@ export namespace Surface {
17
35
 
18
36
  export const Surface = SurfaceComponent;
19
37
  export const isAvailable = isSurfaceAvailable;
38
+
39
+ export type RoleToken<TData> = SurfaceRoleToken<TData>;
40
+ export type Binding = SurfaceBindingType;
41
+ export type Filter<TData> = SurfaceFilterType<TData>;
42
+ export type TokenData<T> = SurfaceTokenData<T>;
43
+ export type TypedProps<TToken extends SurfaceRoleToken<any>> = SurfaceTypedProps<TToken>;
44
+ export const makeType = makeTypeFn;
45
+ export const isFilter = isSurfaceFilterFn;
46
+
47
+ export const ProfilerProvider = SurfaceProfilerProvider;
48
+ export const useProfilerCallback = useSurfaceProfilerCallback;
49
+ export const useProfilerEntries = useSurfaceProfilerEntries;
50
+ export const useProfilerStats = useSurfaceProfilerStats;
51
+ export const useProfilerClear = useSurfaceProfilerClear;
20
52
  }
53
+
54
+ export type { SurfaceProfilerEntry, SurfaceProfilerStats } from './SurfaceProfilerContext';
@@ -0,0 +1,126 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { describe, test } from 'vitest';
6
+
7
+ import { type CapabilityManager } from '../../../core';
8
+ import { isSurfaceAvailable } from './SurfaceComponent';
9
+ import { type RoleToken, type SurfaceFilter, create, isSurfaceFilter, makeType } from './types';
10
+
11
+ describe('Surface.makeType', () => {
12
+ test('creates a role token carrying the role string', ({ expect }) => {
13
+ const token = makeType<{ subject: string }>('test-role');
14
+ expect(token.role).toBe('test-role');
15
+ });
16
+
17
+ test('tokens with the same role are independent objects (identity-by-role)', ({ expect }) => {
18
+ const a = makeType<{ x: number }>('shared');
19
+ const b = makeType<{ x: number }>('shared');
20
+ expect(a).not.toBe(b);
21
+ expect(a.role).toBe(b.role);
22
+ });
23
+ });
24
+
25
+ describe('isSurfaceFilter', () => {
26
+ test('distinguishes filter objects from predicate functions', ({ expect }) => {
27
+ const filter: SurfaceFilter<Record<string, any>> = { bindings: [{ role: 'r', guard: () => true }] };
28
+ expect(isSurfaceFilter(filter)).toBe(true);
29
+ expect(isSurfaceFilter(() => true)).toBe(false);
30
+ expect(isSurfaceFilter({})).toBe(false);
31
+ expect(isSurfaceFilter(null)).toBe(false);
32
+ });
33
+ });
34
+
35
+ describe('create', () => {
36
+ test('accepts the legacy { role, filter } shape', ({ expect }) => {
37
+ const def = create({
38
+ id: 'legacy',
39
+ role: 'article',
40
+ filter: (data): data is { x: number } => typeof (data as any).x === 'number',
41
+ component: () => null,
42
+ });
43
+ expect(def.kind).toBe('react');
44
+ expect(def.role).toBe('article');
45
+ expect(def.filter!({ x: 1 })).toBe(true);
46
+ expect(def.filter!({})).toBe(false);
47
+ });
48
+
49
+ test('expands a single-binding SurfaceFilter into a role string', ({ expect }) => {
50
+ const filter: SurfaceFilter<Record<string, any>> = {
51
+ bindings: [{ role: 'article', guard: (d) => (d as any).subject === 'ok' }],
52
+ };
53
+ const def = create({ id: 'typed-single', filter, component: () => null });
54
+ expect(def.role).toBe('article');
55
+ expect(def.filter!({ subject: 'ok' }, 'article')).toBe(true);
56
+ expect(def.filter!({ subject: 'no' }, 'article')).toBe(false);
57
+ });
58
+
59
+ test('expands a multi-binding SurfaceFilter into a role array with role-scoped guards', ({ expect }) => {
60
+ const filter: SurfaceFilter<Record<string, any>> = {
61
+ bindings: [
62
+ { role: 'article', guard: (d) => (d as any).subject === 'a' },
63
+ { role: 'section', guard: (d) => (d as any).subject === 's' },
64
+ ],
65
+ };
66
+ const def = create({ id: 'typed-multi', filter, component: () => null });
67
+ expect(def.role).toEqual(['article', 'section']);
68
+ // Role-specific guard.
69
+ expect(def.filter!({ subject: 'a' }, 'article')).toBe(true);
70
+ expect(def.filter!({ subject: 'a' }, 'section')).toBe(false);
71
+ expect(def.filter!({ subject: 's' }, 'section')).toBe(true);
72
+ expect(def.filter!({ subject: 's' }, 'article')).toBe(false);
73
+ // Without role, any binding may match.
74
+ expect(def.filter!({ subject: 'a' })).toBe(true);
75
+ expect(def.filter!({ subject: 'unknown' })).toBe(false);
76
+ });
77
+
78
+ test('passes position through untouched', ({ expect }) => {
79
+ const filter: SurfaceFilter<Record<string, any>> = { bindings: [{ role: 'r', guard: () => true }] };
80
+ const def = create({ id: 'pos', filter, component: () => null, position: 'last' });
81
+ expect(def.position).toBe('last');
82
+ });
83
+ });
84
+
85
+ describe('role token typing', () => {
86
+ test('makeType preserves TData through token use-sites', ({ expect }) => {
87
+ // Type-level smoke test: if TS compiles, we're fine. The `RoleToken<T>`
88
+ // phantom should not impose a runtime constraint.
89
+ const token: RoleToken<{ subject: number }> = makeType('numeric');
90
+ expect(token.role).toBe('numeric');
91
+ });
92
+ });
93
+
94
+ describe('isSurfaceAvailable typing', () => {
95
+ // These tests double as static assertions: the `@ts-expect-error` comments
96
+ // fail to compile if the surrounding expression typechecks, so they verify
97
+ // the typed overload narrows `data` to the token's declared contract.
98
+ const sectionToken = makeType<{ attendableId: string; subject: string }>('section');
99
+ const capabilityManager = { getAll: () => [] } as unknown as CapabilityManager.CapabilityManager;
100
+
101
+ test('typed overload accepts data matching the token contract', () => {
102
+ // No error — data has all required fields.
103
+ isSurfaceAvailable(capabilityManager, {
104
+ type: sectionToken,
105
+ data: { attendableId: 'id', subject: 'x' },
106
+ });
107
+ });
108
+
109
+ test('typed overload rejects data missing required fields', () => {
110
+ // @ts-expect-error — `data` is missing `attendableId` required by the token.
111
+ isSurfaceAvailable(capabilityManager, { type: sectionToken, data: { subject: 'x' } });
112
+ });
113
+
114
+ test('typed overload rejects data with wrong field type', () => {
115
+ // @ts-expect-error — `attendableId` must be a string, not a number.
116
+ isSurfaceAvailable(capabilityManager, { type: sectionToken, data: { attendableId: 123, subject: 'x' } });
117
+ });
118
+
119
+ test('legacy overload accepts loose data when `role` is a string', () => {
120
+ // No error — legacy overload's `data` is untyped (`Record<string, unknown>`).
121
+ isSurfaceAvailable(capabilityManager, {
122
+ role: 'article',
123
+ data: { anything: 'goes' },
124
+ });
125
+ });
126
+ });