@dxos/app-framework 0.8.4-main.74a063c4e0 → 0.8.4-main.765dc60934

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 (307) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/browser/{capability-OP63CD5N.mjs → capability-HRFPP52W.mjs} +11 -11
  4. package/dist/lib/browser/capability-HRFPP52W.mjs.map +7 -0
  5. package/dist/lib/browser/{capability-BBBBAPDI.mjs → capability-VQ236TY5.mjs} +10 -10
  6. package/dist/lib/browser/{chunk-T3Y4AEKX.mjs → chunk-23D4SJUE.mjs} +3 -3
  7. package/dist/lib/browser/{chunk-T3Y4AEKX.mjs.map → chunk-23D4SJUE.mjs.map} +1 -1
  8. package/dist/lib/browser/{chunk-2CKCJ6PN.mjs → chunk-3JWJXGLK.mjs} +1 -1
  9. package/dist/lib/browser/{chunk-2CKCJ6PN.mjs.map → chunk-3JWJXGLK.mjs.map} +1 -1
  10. package/dist/lib/browser/{chunk-I34GF4NG.mjs → chunk-45CHLTBV.mjs} +2 -2
  11. package/dist/lib/browser/{chunk-QSXYHXCE.mjs → chunk-66IXTIVK.mjs} +1 -1
  12. package/dist/lib/browser/{chunk-QSXYHXCE.mjs.map → chunk-66IXTIVK.mjs.map} +2 -2
  13. package/dist/lib/browser/chunk-6GZYCP73.mjs +422 -0
  14. package/dist/lib/browser/chunk-6GZYCP73.mjs.map +7 -0
  15. package/dist/lib/browser/chunk-7VZJR2OA.mjs +581 -0
  16. package/dist/lib/browser/chunk-7VZJR2OA.mjs.map +7 -0
  17. package/dist/lib/browser/{chunk-TGX63LTL.mjs → chunk-FJ4765WW.mjs} +1 -1
  18. package/dist/lib/browser/{chunk-TGX63LTL.mjs.map → chunk-FJ4765WW.mjs.map} +2 -2
  19. package/dist/lib/browser/chunk-FO3IYSLV.mjs +68 -0
  20. package/dist/lib/browser/chunk-FO3IYSLV.mjs.map +7 -0
  21. package/dist/lib/browser/{chunk-FU4GAFUQ.mjs → chunk-G6Q2M2M5.mjs} +85 -15
  22. package/dist/lib/browser/chunk-G6Q2M2M5.mjs.map +7 -0
  23. package/dist/lib/browser/chunk-J43JDWAV.mjs +1445 -0
  24. package/dist/lib/browser/chunk-J43JDWAV.mjs.map +7 -0
  25. package/dist/lib/browser/{chunk-JKWMHZP6.mjs → chunk-WBHCSOBW.mjs} +2 -2
  26. package/dist/lib/browser/chunk-WBHCSOBW.mjs.map +7 -0
  27. package/dist/lib/browser/chunk-XLYULXAO.mjs +469 -0
  28. package/dist/lib/browser/chunk-XLYULXAO.mjs.map +7 -0
  29. package/dist/lib/browser/{chunk-F7FW2RK2.mjs → chunk-ZGJAZSNE.mjs} +7 -32
  30. package/dist/lib/browser/chunk-ZGJAZSNE.mjs.map +7 -0
  31. package/dist/lib/browser/cli/index.mjs +14 -28
  32. package/dist/lib/browser/cli/index.mjs.map +3 -3
  33. package/dist/lib/browser/common/activation-events.mjs +7 -7
  34. package/dist/lib/browser/common/capabilities.mjs +7 -7
  35. package/dist/lib/browser/core/activation-event.mjs +1 -1
  36. package/dist/lib/browser/core/capability.mjs +1 -1
  37. package/dist/lib/browser/core/plugin-manager.mjs +8 -4
  38. package/dist/lib/browser/core/plugin.mjs +12 -2
  39. package/dist/lib/browser/core/url-loader.mjs +17 -5
  40. package/dist/lib/browser/index.mjs +38 -21
  41. package/dist/lib/browser/index.mjs.map +3 -3
  42. package/dist/lib/browser/{invoker-capability-H5PPENOC.mjs → invoker-capability-CEL2L6GX.mjs} +12 -11
  43. package/dist/lib/browser/invoker-capability-CEL2L6GX.mjs.map +7 -0
  44. package/dist/lib/browser/meta.json +1 -1
  45. package/dist/lib/browser/testing/index.mjs +149 -28
  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 +18 -14
  50. package/dist/lib/node-esm/{capability-AWBEMRYR.mjs → capability-GS3XJH74.mjs} +10 -10
  51. package/dist/lib/node-esm/{capability-WFEG6CIZ.mjs → capability-I5P7ACFL.mjs} +11 -11
  52. package/dist/lib/node-esm/capability-I5P7ACFL.mjs.map +7 -0
  53. package/dist/lib/node-esm/{chunk-FKE4Z3D6.mjs → chunk-37Z53PXZ.mjs} +1 -1
  54. package/dist/lib/node-esm/{chunk-FKE4Z3D6.mjs.map → chunk-37Z53PXZ.mjs.map} +2 -2
  55. package/dist/lib/node-esm/chunk-4RJL37GN.mjs +423 -0
  56. package/dist/lib/node-esm/chunk-4RJL37GN.mjs.map +7 -0
  57. package/dist/lib/node-esm/{chunk-WZCSOX5Q.mjs → chunk-6XW6LET6.mjs} +2 -2
  58. package/dist/lib/node-esm/{chunk-URWHJQT2.mjs → chunk-D347W3KO.mjs} +7 -32
  59. package/dist/lib/node-esm/chunk-D347W3KO.mjs.map +7 -0
  60. package/dist/lib/node-esm/chunk-DW45NFLW.mjs +1446 -0
  61. package/dist/lib/node-esm/chunk-DW45NFLW.mjs.map +7 -0
  62. package/dist/lib/node-esm/chunk-EI4LYXFY.mjs +470 -0
  63. package/dist/lib/node-esm/chunk-EI4LYXFY.mjs.map +7 -0
  64. package/dist/lib/node-esm/{chunk-ULUEXB7Q.mjs → chunk-KIFOQXD4.mjs} +85 -15
  65. package/dist/lib/node-esm/chunk-KIFOQXD4.mjs.map +7 -0
  66. package/dist/lib/node-esm/{chunk-VKHGNEDB.mjs → chunk-SBS2YMPT.mjs} +3 -3
  67. package/dist/lib/node-esm/{chunk-VKHGNEDB.mjs.map → chunk-SBS2YMPT.mjs.map} +1 -1
  68. package/dist/lib/node-esm/{chunk-42KBWDE4.mjs → chunk-SDJ4B2LU.mjs} +1 -1
  69. package/dist/lib/node-esm/{chunk-42KBWDE4.mjs.map → chunk-SDJ4B2LU.mjs.map} +1 -1
  70. package/dist/lib/node-esm/chunk-V24UWT36.mjs +582 -0
  71. package/dist/lib/node-esm/chunk-V24UWT36.mjs.map +7 -0
  72. package/dist/lib/node-esm/{chunk-G3RTFSNG.mjs → chunk-WFSRZKBP.mjs} +2 -2
  73. package/dist/lib/node-esm/chunk-WFSRZKBP.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-ZZ7CKK6W.mjs → chunk-XOCUANHO.mjs} +1 -1
  77. package/dist/lib/node-esm/{chunk-ZZ7CKK6W.mjs.map → chunk-XOCUANHO.mjs.map} +2 -2
  78. package/dist/lib/node-esm/cli/index.mjs +14 -28
  79. package/dist/lib/node-esm/cli/index.mjs.map +3 -3
  80. package/dist/lib/node-esm/common/activation-events.mjs +7 -7
  81. package/dist/lib/node-esm/common/capabilities.mjs +7 -7
  82. package/dist/lib/node-esm/core/activation-event.mjs +1 -1
  83. package/dist/lib/node-esm/core/capability.mjs +1 -1
  84. package/dist/lib/node-esm/core/plugin-manager.mjs +8 -4
  85. package/dist/lib/node-esm/core/plugin.mjs +12 -2
  86. package/dist/lib/node-esm/core/url-loader.mjs +17 -5
  87. package/dist/lib/node-esm/index.mjs +38 -21
  88. package/dist/lib/node-esm/index.mjs.map +3 -3
  89. package/dist/lib/node-esm/{invoker-capability-S3ZA527J.mjs → invoker-capability-QKUKN2MF.mjs} +12 -11
  90. package/dist/lib/node-esm/invoker-capability-QKUKN2MF.mjs.map +7 -0
  91. package/dist/lib/node-esm/meta.json +1 -1
  92. package/dist/lib/node-esm/testing/index.mjs +149 -28
  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 +18 -14
  97. package/dist/plugin/node-esm/index.mjs +542 -33
  98. package/dist/plugin/node-esm/index.mjs.map +4 -4
  99. package/dist/plugin/node-esm/meta.json +1 -1
  100. package/dist/types/src/common/annotations.d.ts +1 -0
  101. package/dist/types/src/common/annotations.d.ts.map +1 -0
  102. package/dist/types/src/common/capabilities.d.ts +2 -1
  103. package/dist/types/src/common/capabilities.d.ts.map +1 -1
  104. package/dist/types/src/common/operations.d.ts +1 -1
  105. package/dist/types/src/common/operations.d.ts.map +1 -1
  106. package/dist/types/src/core/activation-event.d.ts +4 -4
  107. package/dist/types/src/core/activation-event.d.ts.map +1 -1
  108. package/dist/types/src/core/capability-manager.d.ts.map +1 -1
  109. package/dist/types/src/core/capability.d.ts +2 -2
  110. package/dist/types/src/core/capability.d.ts.map +1 -1
  111. package/dist/types/src/core/edge-registry-plugin-provider.d.ts +30 -0
  112. package/dist/types/src/core/edge-registry-plugin-provider.d.ts.map +1 -0
  113. package/dist/types/src/core/index.d.ts +5 -0
  114. package/dist/types/src/core/index.d.ts.map +1 -1
  115. package/dist/types/src/core/plugin-asset-cache.d.ts +71 -0
  116. package/dist/types/src/core/plugin-asset-cache.d.ts.map +1 -0
  117. package/dist/types/src/core/plugin-manager.d.ts +230 -5
  118. package/dist/types/src/core/plugin-manager.d.ts.map +1 -1
  119. package/dist/types/src/core/plugin-manifest.d.ts +101 -0
  120. package/dist/types/src/core/plugin-manifest.d.ts.map +1 -0
  121. package/dist/types/src/core/plugin-manifest.test.d.ts +2 -0
  122. package/dist/types/src/core/plugin-manifest.test.d.ts.map +1 -0
  123. package/dist/types/src/core/plugin.d.ts +164 -6
  124. package/dist/types/src/core/plugin.d.ts.map +1 -1
  125. package/dist/types/src/core/registry.d.ts +107 -0
  126. package/dist/types/src/core/registry.d.ts.map +1 -0
  127. package/dist/types/src/core/url-loader.d.ts +105 -3
  128. package/dist/types/src/core/url-loader.d.ts.map +1 -1
  129. package/dist/types/src/helpers.d.ts.map +1 -1
  130. package/dist/types/src/plugin-operation/history/capability.d.ts.map +1 -1
  131. package/dist/types/src/plugin-operation/history/errors.d.ts +6 -6
  132. package/dist/types/src/plugin-operation/history/errors.d.ts.map +1 -1
  133. package/dist/types/src/plugin-operation/history/history-tracker.d.ts +1 -1
  134. package/dist/types/src/plugin-operation/history/history-tracker.d.ts.map +1 -1
  135. package/dist/types/src/plugin-operation/history/types.d.ts +1 -1
  136. package/dist/types/src/plugin-operation/history/types.d.ts.map +1 -1
  137. package/dist/types/src/plugin-operation/history/undo-mapping.d.ts +1 -1
  138. package/dist/types/src/plugin-operation/history/undo-mapping.d.ts.map +1 -1
  139. package/dist/types/src/plugin-operation/history/undo-registry.d.ts +1 -1
  140. package/dist/types/src/plugin-operation/history/undo-registry.d.ts.map +1 -1
  141. package/dist/types/src/plugin-operation/invoker-capability.d.ts +1 -1
  142. package/dist/types/src/plugin-operation/invoker-capability.d.ts.map +1 -1
  143. package/dist/types/src/plugin-operation/meta.d.ts.map +1 -1
  144. package/dist/types/src/plugin-operation/testing.d.ts +2 -1
  145. package/dist/types/src/plugin-operation/testing.d.ts.map +1 -1
  146. package/dist/types/src/plugin-runtime/capability.d.ts +1 -1
  147. package/dist/types/src/plugin-runtime/capability.d.ts.map +1 -1
  148. package/dist/types/src/plugin-runtime/meta.d.ts.map +1 -1
  149. package/dist/types/src/testing/harness.d.ts +67 -0
  150. package/dist/types/src/testing/harness.d.ts.map +1 -0
  151. package/dist/types/src/testing/index.d.ts +1 -0
  152. package/dist/types/src/testing/index.d.ts.map +1 -1
  153. package/dist/types/src/testing/react.d.ts +27 -0
  154. package/dist/types/src/testing/react.d.ts.map +1 -0
  155. package/dist/types/src/testing/react.test.d.ts +2 -0
  156. package/dist/types/src/testing/react.test.d.ts.map +1 -0
  157. package/dist/types/src/testing/service.d.ts.map +1 -1
  158. package/dist/types/src/testing/withPluginManager.d.ts.map +1 -1
  159. package/dist/types/src/testing/withPluginManager.stories.d.ts.map +1 -1
  160. package/dist/types/src/ui/components/App/App.d.ts.map +1 -1
  161. package/dist/types/src/ui/components/App/App.stories.d.ts.map +1 -1
  162. package/dist/types/src/ui/components/Placeholder/Placeholder.d.ts +64 -0
  163. package/dist/types/src/ui/components/Placeholder/Placeholder.d.ts.map +1 -0
  164. package/dist/types/src/ui/components/Placeholder/Placeholder.stories.d.ts +19 -0
  165. package/dist/types/src/ui/components/Placeholder/Placeholder.stories.d.ts.map +1 -0
  166. package/dist/types/src/ui/components/Placeholder/index.d.ts +2 -0
  167. package/dist/types/src/ui/components/Placeholder/index.d.ts.map +1 -0
  168. package/dist/types/src/ui/components/PluginManager/PluginManagerContext.stories.d.ts.map +1 -1
  169. package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts +16 -4
  170. package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts.map +1 -1
  171. package/dist/types/src/ui/components/Surface/SurfaceComponent.stories.d.ts.map +1 -1
  172. package/dist/types/src/ui/components/Surface/SurfaceInfo.d.ts.map +1 -1
  173. package/dist/types/src/ui/components/Surface/SurfaceProfilerContext.d.ts.map +1 -1
  174. package/dist/types/src/ui/components/Surface/index.d.ts +16 -6
  175. package/dist/types/src/ui/components/Surface/index.d.ts.map +1 -1
  176. package/dist/types/src/ui/components/Surface/types.d.ts +110 -9
  177. package/dist/types/src/ui/components/Surface/types.d.ts.map +1 -1
  178. package/dist/types/src/ui/components/Surface/types.test.d.ts +2 -0
  179. package/dist/types/src/ui/components/Surface/types.test.d.ts.map +1 -0
  180. package/dist/types/src/ui/components/index.d.ts +1 -0
  181. package/dist/types/src/ui/components/index.d.ts.map +1 -1
  182. package/dist/types/src/ui/hooks/useApp.d.ts +30 -3
  183. package/dist/types/src/ui/hooks/useApp.d.ts.map +1 -1
  184. package/dist/types/src/ui/hooks/useCapabilities.d.ts.map +1 -1
  185. package/dist/types/src/ui/hooks/useLoading.d.ts.map +1 -1
  186. package/dist/types/src/ui/hooks/useSettingsState.d.ts.map +1 -1
  187. package/dist/types/src/vite-plugin/boot-loader/BootLoader.stories.d.ts +34 -0
  188. package/dist/types/src/vite-plugin/boot-loader/BootLoader.stories.d.ts.map +1 -0
  189. package/dist/types/src/vite-plugin/boot-loader/index.d.ts +2 -0
  190. package/dist/types/src/vite-plugin/boot-loader/index.d.ts.map +1 -0
  191. package/dist/types/src/vite-plugin/boot-loader/loader.d.ts +51 -0
  192. package/dist/types/src/vite-plugin/boot-loader/loader.d.ts.map +1 -0
  193. package/dist/types/src/vite-plugin/composer/index.d.ts +34 -0
  194. package/dist/types/src/vite-plugin/composer/index.d.ts.map +1 -0
  195. package/dist/types/src/vite-plugin/import-map/index.d.ts +28 -0
  196. package/dist/types/src/vite-plugin/import-map/index.d.ts.map +1 -0
  197. package/dist/types/src/vite-plugin/index.d.ts +4 -2
  198. package/dist/types/src/vite-plugin/index.d.ts.map +1 -1
  199. package/dist/types/src/vite-plugin/manifest.d.ts +41 -0
  200. package/dist/types/src/vite-plugin/manifest.d.ts.map +1 -0
  201. package/dist/types/src/vite-plugin/manifest.test.d.ts +2 -0
  202. package/dist/types/src/vite-plugin/manifest.test.d.ts.map +1 -0
  203. package/dist/types/src/vite-plugin/packages.d.ts +10 -4
  204. package/dist/types/src/vite-plugin/packages.d.ts.map +1 -1
  205. package/dist/types/tsconfig.tsbuildinfo +1 -1
  206. package/moon.yml +1 -0
  207. package/package.json +36 -60
  208. package/src/cli/cli.ts +1 -1
  209. package/src/common/annotations.ts +3 -0
  210. package/src/common/capabilities.ts +2 -1
  211. package/src/common/operations.ts +1 -1
  212. package/src/core/capability.ts +1 -1
  213. package/src/core/edge-registry-plugin-provider.ts +92 -0
  214. package/src/core/index.ts +5 -0
  215. package/src/core/plugin-asset-cache.ts +60 -0
  216. package/src/core/plugin-manager.test.ts +793 -7
  217. package/src/core/plugin-manager.ts +846 -44
  218. package/src/core/plugin-manifest.test.ts +75 -0
  219. package/src/core/plugin-manifest.ts +134 -0
  220. package/src/core/plugin.ts +170 -10
  221. package/src/core/registry.ts +163 -0
  222. package/src/core/url-loader.test.ts +148 -6
  223. package/src/core/url-loader.ts +284 -44
  224. package/src/plugin-operation/OperationPlugin.ts +2 -2
  225. package/src/plugin-operation/history/capability.ts +1 -1
  226. package/src/plugin-operation/history/history-tracker.test.ts +2 -1
  227. package/src/plugin-operation/history/history-tracker.ts +1 -1
  228. package/src/plugin-operation/history/types.ts +1 -1
  229. package/src/plugin-operation/history/undo-mapping.ts +1 -1
  230. package/src/plugin-operation/history/undo-registry.ts +1 -1
  231. package/src/plugin-operation/invoker-capability.ts +2 -1
  232. package/src/plugin-operation/meta.ts +1 -0
  233. package/src/plugin-operation/testing.ts +2 -1
  234. package/src/plugin-runtime/RuntimePlugin.ts +2 -2
  235. package/src/plugin-runtime/meta.ts +1 -0
  236. package/src/testing/harness.ts +229 -0
  237. package/src/testing/index.ts +1 -0
  238. package/src/testing/react.test.tsx +48 -0
  239. package/src/testing/react.tsx +113 -0
  240. package/src/testing/service.ts +1 -1
  241. package/src/testing/withPluginManager.stories.tsx +1 -1
  242. package/src/testing/withPluginManager.tsx +0 -2
  243. package/src/ui/components/App/App.stories.tsx +2 -6
  244. package/src/ui/components/App/App.tsx +25 -2
  245. package/src/ui/components/Placeholder/Placeholder.stories.tsx +77 -0
  246. package/src/ui/components/Placeholder/Placeholder.tsx +155 -0
  247. package/src/ui/components/Placeholder/index.ts +5 -0
  248. package/src/ui/components/PluginManager/PluginManagerContext.stories.tsx +4 -2
  249. package/src/ui/components/Surface/SurfaceComponent.stories.tsx +1 -1
  250. package/src/ui/components/Surface/SurfaceComponent.tsx +85 -48
  251. package/src/ui/components/Surface/SurfaceInfo.tsx +0 -1
  252. package/src/ui/components/Surface/index.ts +20 -1
  253. package/src/ui/components/Surface/types.test.ts +126 -0
  254. package/src/ui/components/Surface/types.ts +164 -12
  255. package/src/ui/components/index.ts +1 -0
  256. package/src/ui/hooks/useApp.test.tsx +1 -1
  257. package/src/ui/hooks/useApp.tsx +177 -42
  258. package/src/ui/hooks/useLoading.tsx +14 -6
  259. package/src/vite-plugin/boot-loader/BootLoader.stories.tsx +270 -0
  260. package/src/vite-plugin/boot-loader/boot-loader.css +321 -0
  261. package/src/vite-plugin/boot-loader/boot-loader.js +325 -0
  262. package/src/vite-plugin/boot-loader/index.ts +5 -0
  263. package/src/vite-plugin/boot-loader/loader.ts +123 -0
  264. package/src/vite-plugin/composer/index.ts +306 -0
  265. package/src/vite-plugin/import-map/index.ts +527 -0
  266. package/src/vite-plugin/index.ts +6 -2
  267. package/src/vite-plugin/manifest.test.ts +46 -0
  268. package/src/vite-plugin/manifest.ts +57 -0
  269. package/src/vite-plugin/packages.ts +168 -10
  270. package/tsconfig.json +16 -1
  271. package/.swc/plugins/linux_x86_64_19.0.0/727453fb3a62f7f1d952a41e051ca8a6f88cadc45cee43c6a4d1aa45f9b75665.wasmer-v7 +0 -0
  272. package/dist/lib/browser/capability-OP63CD5N.mjs.map +0 -7
  273. package/dist/lib/browser/chunk-F7FW2RK2.mjs.map +0 -7
  274. package/dist/lib/browser/chunk-FU4GAFUQ.mjs.map +0 -7
  275. package/dist/lib/browser/chunk-GX4TUNM6.mjs +0 -963
  276. package/dist/lib/browser/chunk-GX4TUNM6.mjs.map +0 -7
  277. package/dist/lib/browser/chunk-JKWMHZP6.mjs.map +0 -7
  278. package/dist/lib/browser/chunk-LVJW5EFU.mjs +0 -157
  279. package/dist/lib/browser/chunk-LVJW5EFU.mjs.map +0 -7
  280. package/dist/lib/browser/chunk-RFSO3JRG.mjs +0 -1
  281. package/dist/lib/browser/chunk-RFSO3JRG.mjs.map +0 -7
  282. package/dist/lib/browser/chunk-WPE6AL7I.mjs +0 -905
  283. package/dist/lib/browser/chunk-WPE6AL7I.mjs.map +0 -7
  284. package/dist/lib/browser/invoker-capability-H5PPENOC.mjs.map +0 -7
  285. package/dist/lib/node-esm/capability-WFEG6CIZ.mjs.map +0 -7
  286. package/dist/lib/node-esm/chunk-4A3ZCMI3.mjs +0 -158
  287. package/dist/lib/node-esm/chunk-4A3ZCMI3.mjs.map +0 -7
  288. package/dist/lib/node-esm/chunk-BCEOLX47.mjs +0 -964
  289. package/dist/lib/node-esm/chunk-BCEOLX47.mjs.map +0 -7
  290. package/dist/lib/node-esm/chunk-EL3R25OQ.mjs +0 -2
  291. package/dist/lib/node-esm/chunk-EL3R25OQ.mjs.map +0 -7
  292. package/dist/lib/node-esm/chunk-G3RTFSNG.mjs.map +0 -7
  293. package/dist/lib/node-esm/chunk-LQKOTNJW.mjs +0 -906
  294. package/dist/lib/node-esm/chunk-LQKOTNJW.mjs.map +0 -7
  295. package/dist/lib/node-esm/chunk-ULUEXB7Q.mjs.map +0 -7
  296. package/dist/lib/node-esm/chunk-URWHJQT2.mjs.map +0 -7
  297. package/dist/lib/node-esm/invoker-capability-S3ZA527J.mjs.map +0 -7
  298. package/dist/types/src/vite-plugin/composer-plugin.d.ts +0 -18
  299. package/dist/types/src/vite-plugin/composer-plugin.d.ts.map +0 -1
  300. package/dist/types/src/vite-plugin/import-map-plugin.d.ts +0 -16
  301. package/dist/types/src/vite-plugin/import-map-plugin.d.ts.map +0 -1
  302. package/src/vite-plugin/composer-plugin.ts +0 -128
  303. package/src/vite-plugin/import-map-plugin.ts +0 -314
  304. /package/dist/lib/browser/{capability-BBBBAPDI.mjs.map → capability-VQ236TY5.mjs.map} +0 -0
  305. /package/dist/lib/browser/{chunk-I34GF4NG.mjs.map → chunk-45CHLTBV.mjs.map} +0 -0
  306. /package/dist/lib/node-esm/{capability-AWBEMRYR.mjs.map → capability-GS3XJH74.mjs.map} +0 -0
  307. /package/dist/lib/node-esm/{chunk-WZCSOX5Q.mjs.map → chunk-6XW6LET6.mjs.map} +0 -0
@@ -6,8 +6,63 @@ import type { FC, PropsWithChildren, ReactNode, RefCallback } from 'react';
6
6
 
7
7
  import type { MakeOptional, Position } from '@dxos/util';
8
8
 
9
+ /**
10
+ * A typed role token. Carries a role string for runtime dispatch plus a phantom
11
+ * data type so consumers using `type={SomeToken}` and providers using
12
+ * `AppSurface.object(SomeToken, ...)` share a single type-level contract.
13
+ *
14
+ * Mint via {@link makeType}.
15
+ */
16
+ export type RoleToken<TData> = {
17
+ readonly role: string;
18
+ /** Covariant phantom; never accessed at runtime. */
19
+ readonly _phantom?: TData;
20
+ };
21
+
22
+ /**
23
+ * One entry in a {@link SurfaceFilter} — a role plus the guard that validates
24
+ * the data shape when the Surface dispatcher is matching that role.
25
+ */
26
+ export type SurfaceBinding = {
27
+ readonly role: string;
28
+ readonly guard: (data: unknown) => boolean;
29
+ };
30
+
31
+ /**
32
+ * Typed filter binding for {@link create}. Combines one or more `(role, guard)`
33
+ * pairs so a provider can register against multiple roles while keeping the
34
+ * role and its data shape in a single expression.
35
+ */
36
+ export type SurfaceFilter<TData> = {
37
+ readonly bindings: ReadonlyArray<SurfaceBinding>;
38
+ /** Covariant phantom; never accessed at runtime. */
39
+ readonly _phantom?: TData;
40
+ };
41
+
42
+ /**
43
+ * Narrow data type carried by a role token.
44
+ */
45
+ export type TokenData<T> = T extends RoleToken<infer D> ? D : never;
46
+
47
+ /**
48
+ * Runtime guard for {@link SurfaceFilter}. Distinguishes new-style filter
49
+ * bindings from legacy predicate filters.
50
+ */
51
+ export const isSurfaceFilter = (value: unknown): value is SurfaceFilter<any> =>
52
+ typeof value === 'object' && value !== null && Array.isArray((value as { bindings?: unknown }).bindings);
53
+
54
+ /**
55
+ * Mints a typed role token. Identity is structural; all tokens with the same
56
+ * `role` string are interchangeable at runtime.
57
+ */
58
+ export const makeType = <TData>(role: string): RoleToken<TData> => ({ role });
59
+
9
60
  /**
10
61
  * Props that are passed to the Surface component.
62
+ *
63
+ * The role can be provided either as a string via `role` or as a typed
64
+ * {@link RoleToken} via `type`. The typed form narrows `data` to the token's
65
+ * declared shape.
11
66
  */
12
67
  export type Props<T extends Record<string, any> = Record<string, unknown>> = {
13
68
  /**
@@ -21,16 +76,46 @@ export type Props<T extends Record<string, any> = Record<string, unknown>> = {
21
76
  * The placeholder component will be rendered while the surface component is loading.
22
77
  */
23
78
  placeholder?: ReactNode;
24
- } & MakeOptional<CoreProps<T>, 'id' | 'data'> &
25
- /**
79
+ } & MakeOptional<CoreProps<T>, 'id' | 'data' | 'role'> & {
80
+ /**
81
+ * Explicitly disallow `type` on the untyped Props overload — if the caller
82
+ * provides `type={AppSurface.X}`, TypeScript must route to the typed
83
+ * overload (where `data` is narrowed by the token). Without this, the
84
+ * catch-all index signature below would accept any `type` value and mask
85
+ * the typed overload entirely.
86
+ */
87
+ type?: undefined;
88
+ } /**
26
89
  * Additional props to pass to the component.
27
90
  * These props are not used by Surface itself but may be used by components which resolve the surface.
28
91
  * Exclude known prop names to prevent overriding well-defined props.
29
- */
30
- {
31
- [K in keyof Record<string, any>]: K extends keyof CoreProps<T> | 'fallback' | 'placeholder' ? never : any;
92
+ */ & {
93
+ [K in keyof Record<string, any>]: K extends keyof CoreProps<T> | 'fallback' | 'placeholder' | 'type' ? never : any;
32
94
  };
33
95
 
96
+ /**
97
+ * Typed Surface consumer props — carries the role/data-shape contract via a
98
+ * {@link RoleToken}. Available as a typed overload of the Surface component so
99
+ * ad-hoc `type` fields on arbitrary spread props don't conflict with the
100
+ * untyped consumer form.
101
+ */
102
+ export type TypedProps<TToken extends RoleToken<any>> = {
103
+ fallback?: FC<{ error: Error; data?: any }>;
104
+ placeholder?: ReactNode;
105
+ id?: string;
106
+ type: TToken;
107
+ data?: TokenData<TToken>;
108
+ limit?: number | undefined;
109
+ } & {
110
+ /**
111
+ * Additional pass-through props. Known prop names are excluded so the
112
+ * catch-all doesn't widen `data` / `type` / etc. to `any` at the intersection.
113
+ */
114
+ [K in keyof Record<string, any>]: K extends 'fallback' | 'placeholder' | 'id' | 'type' | 'data' | 'limit'
115
+ ? never
116
+ : any;
117
+ };
118
+
34
119
  /**
35
120
  * NOTE: If `[key: string]: unknown` is included in shared types, when re-used other fields become unknown as well.
36
121
  */
@@ -41,7 +126,10 @@ export type CoreProps<T extends Record<string, any> = Record<string, unknown>> =
41
126
  id: string;
42
127
 
43
128
  /**
44
- * Role defines how the data should be rendered.
129
+ * Role defines how the data should be rendered. For new code, prefer the
130
+ * typed overload of the Surface component which takes a {@link RoleToken}
131
+ * via the `type` prop instead — it enforces a role/data contract at compile
132
+ * time.
45
133
  */
46
134
  role: string;
47
135
 
@@ -71,6 +159,10 @@ export type ComponentFunction<T extends Record<string, any> = Record<string, any
71
159
 
72
160
  /**
73
161
  * Definition of when a React component surface should be rendered.
162
+ *
163
+ * The optional `filter` receives the consumer-supplied `role` as its second
164
+ * argument so a single definition can encode role-specific guards (used by the
165
+ * {@link SurfaceFilter}-based `create` overload).
74
166
  */
75
167
  export type ReactDefinition<T extends Record<string, any> = any> = Readonly<{
76
168
  kind: 'react';
@@ -78,7 +170,7 @@ export type ReactDefinition<T extends Record<string, any> = any> = Readonly<{
78
170
  role: string | string[];
79
171
  position?: Position;
80
172
  component: ComponentFunction<T>;
81
- filter?: (data: Record<string, unknown>) => data is T;
173
+ filter?: (data: Record<string, unknown>, role?: string) => data is T;
82
174
  }>;
83
175
 
84
176
  /**
@@ -94,7 +186,7 @@ export type WebComponentDefinition<T extends Record<string, any> = any> = Readon
94
186
  * The Web Component will receive the same props as React surfaces via properties/attributes.
95
187
  */
96
188
  tagName: string;
97
- filter?: (data: Record<string, unknown>) => data is T;
189
+ filter?: (data: Record<string, unknown>, role?: string) => data is T;
98
190
  }>;
99
191
 
100
192
  /**
@@ -102,16 +194,76 @@ export type WebComponentDefinition<T extends Record<string, any> = any> = Readon
102
194
  */
103
195
  export type Definition<T extends Record<string, any> = any> = ReactDefinition<T> | WebComponentDefinition<T>;
104
196
 
197
+ /**
198
+ * Typed React surface definition — role is derived from the filter's bindings.
199
+ */
200
+ export type TypedReactDefinition<T extends Record<string, any> = any> = Readonly<{
201
+ id: string;
202
+ filter: SurfaceFilter<T>;
203
+ component: ComponentFunction<T>;
204
+ position?: Position;
205
+ }>;
206
+
207
+ /**
208
+ * Typed Web Component surface definition.
209
+ */
210
+ export type TypedWebComponentDefinition<T extends Record<string, any> = any> = Readonly<{
211
+ id: string;
212
+ filter: SurfaceFilter<T>;
213
+ tagName: string;
214
+ position?: Position;
215
+ }>;
216
+
217
+ const expandBindings = <T extends Record<string, any>>(
218
+ filter: SurfaceFilter<T>,
219
+ ): { role: string | string[]; guard: (data: Record<string, unknown>, role?: string) => data is T } => {
220
+ const bindings = filter.bindings;
221
+ const roles = Array.from(new Set(bindings.map((binding) => binding.role)));
222
+ const guard = (data: Record<string, unknown>, role?: string): data is T => {
223
+ if (role != null) {
224
+ // Multiple bindings may share a role (e.g. via `oneOf` of same-role filters);
225
+ // the role matches if ANY of them passes.
226
+ return bindings.some((entry) => entry.role === role && entry.guard(data));
227
+ }
228
+ return bindings.some((entry) => entry.guard(data));
229
+ };
230
+ return { role: roles.length === 1 ? roles[0] : roles, guard };
231
+ };
232
+
105
233
  /**
106
234
  * Creates a React surface definition.
107
235
  */
108
- export const create = <T extends Record<string, any> = any>(
236
+ export function create<T extends Record<string, any> = any>(definition: TypedReactDefinition<T>): ReactDefinition<T>;
237
+ export function create<T extends Record<string, any> = any>(
109
238
  definition: Omit<ReactDefinition<T>, 'kind'>,
110
- ): ReactDefinition<T> => ({ ...definition, kind: 'react' });
239
+ ): ReactDefinition<T>;
240
+ export function create<T extends Record<string, any> = any>(
241
+ definition: TypedReactDefinition<T> | Omit<ReactDefinition<T>, 'kind'>,
242
+ ): ReactDefinition<T> {
243
+ if (isSurfaceFilter(definition.filter)) {
244
+ const { id, filter, component, position } = definition as TypedReactDefinition<T>;
245
+ const { role, guard } = expandBindings(filter);
246
+ return { kind: 'react', id, role, position, component, filter: guard };
247
+ }
248
+ return { ...(definition as Omit<ReactDefinition<T>, 'kind'>), kind: 'react' };
249
+ }
111
250
 
112
251
  /**
113
252
  * Creates a Web Component surface definition.
114
253
  */
115
- export const createWeb = <T extends Record<string, any> = any>(
254
+ export function createWeb<T extends Record<string, any> = any>(
255
+ definition: TypedWebComponentDefinition<T>,
256
+ ): WebComponentDefinition<T>;
257
+ export function createWeb<T extends Record<string, any> = any>(
116
258
  definition: Omit<WebComponentDefinition<T>, 'kind'>,
117
- ): WebComponentDefinition<T> => ({ ...definition, kind: 'web-component' });
259
+ ): WebComponentDefinition<T>;
260
+ export function createWeb<T extends Record<string, any> = any>(
261
+ definition: TypedWebComponentDefinition<T> | Omit<WebComponentDefinition<T>, 'kind'>,
262
+ ): WebComponentDefinition<T> {
263
+ if (isSurfaceFilter(definition.filter)) {
264
+ const { id, filter, tagName, position } = definition as TypedWebComponentDefinition<T>;
265
+ const { role, guard } = expandBindings(filter);
266
+ return { kind: 'web-component', id, role, position, tagName, filter: guard };
267
+ }
268
+ return { ...(definition as Omit<WebComponentDefinition<T>, 'kind'>), kind: 'web-component' };
269
+ }
@@ -3,5 +3,6 @@
3
3
  //
4
4
 
5
5
  export * from './App';
6
+ export * from './Placeholder';
6
7
  export * from './PluginManager';
7
8
  export * from './Surface';
@@ -20,7 +20,7 @@ const pluginLoader = (plugins: Plugin.Plugin[]) =>
20
20
  if (!plugin) {
21
21
  return yield* Effect.fail(new Error(`Plugin not found: ${id}`));
22
22
  }
23
- return plugin;
23
+ return { plugin };
24
24
  });
25
25
 
26
26
  const TestHost = ({ manager }: { manager: PluginManager.PluginManager }) => {
@@ -30,8 +30,33 @@ export type StartupProgress = {
30
30
  total: number;
31
31
  /** Fractional progress (0-1). */
32
32
  progress: number;
33
- /** Human-readable label for the currently activating module. */
34
- status?: string;
33
+ /**
34
+ * Raw activation event key (e.g. `org.dxos.app-framework.event.startup`).
35
+ * Set on event-level transitions, *and* on module-level transitions where
36
+ * it carries the parent activation event that first triggered the
37
+ * module's load (plumbed through `_loadCapabilitiesForModules` →
38
+ * `_loadModule`). Consumers can use this either as the primary id (when
39
+ * {@link module} is absent) or as an extra "context" field alongside
40
+ * {@link module}.
41
+ */
42
+ event?: string;
43
+ /**
44
+ * Raw module id (e.g. `org.dxos.plugin.observability.module.ReactSurface`)
45
+ * when the in-flight activation is module-level. When present,
46
+ * {@link event} may also be set, identifying the parent activation that
47
+ * triggered this module's load.
48
+ */
49
+ module?: string;
50
+ /**
51
+ * Pre-humanized label for the currently surfaced transition (module
52
+ * label if {@link module} is set, otherwise the event label), supplied
53
+ * for consumers that want a sensible default. Hosts that prefer to
54
+ * render their own label can read the raw {@link event}/{@link module}
55
+ * fields and ignore this — the framework leaves the policy choice
56
+ * (which transitions to surface, how to format them, whether to drop
57
+ * sub-modules entirely) to the host's `Placeholder`.
58
+ */
59
+ humanizedName?: string;
35
60
  };
36
61
 
37
62
  export type PlaceholderProps = {
@@ -42,6 +67,8 @@ export type PlaceholderProps = {
42
67
  export type UseAppOptions = {
43
68
  pluginManager?: PluginManager.PluginManager;
44
69
  pluginLoader?: PluginManager.ManagerOptions['pluginLoader'];
70
+ onPluginRemove?: PluginManager.ManagerOptions['onRemove'];
71
+ pluginRegistryProvider?: PluginManager.ManagerOptions['pluginRegistryProvider'];
45
72
  plugins?: Plugin.Plugin[];
46
73
  core?: string[];
47
74
  defaults?: string[];
@@ -86,6 +113,8 @@ export type UseAppOptions = {
86
113
  export const useApp = ({
87
114
  pluginManager,
88
115
  pluginLoader: pluginLoaderProp,
116
+ onPluginRemove,
117
+ pluginRegistryProvider,
89
118
  plugins: pluginsProp,
90
119
  core: coreProp,
91
120
  defaults: defaultsProp,
@@ -109,7 +138,7 @@ export const useApp = ({
109
138
  Effect.sync(() => {
110
139
  const plugin = plugins.find((plugin) => plugin.meta.id === id);
111
140
  invariant(plugin, `Plugin not found: ${id}`);
112
- return plugin;
141
+ return { plugin };
113
142
  })),
114
143
  [pluginLoaderProp, plugins],
115
144
  );
@@ -131,10 +160,19 @@ export const useApp = ({
131
160
  );
132
161
  const isExternalManager = !!pluginManager;
133
162
  const manager = useMemo(() => {
134
- const mgr = pluginManager ?? PluginManager.make({ pluginLoader, plugins, core, enabled });
163
+ const mgr =
164
+ pluginManager ??
165
+ PluginManager.make({
166
+ pluginLoader,
167
+ plugins,
168
+ core,
169
+ enabled,
170
+ onRemove: onPluginRemove,
171
+ pluginRegistryProvider,
172
+ });
135
173
  log('useApp: useMemo created/reused manager', { provided: !!pluginManager });
136
174
  return mgr;
137
- }, [pluginManager, pluginLoader, plugins, core, enabled]);
175
+ }, [pluginManager, pluginLoader, plugins, core, enabled, onPluginRemove, pluginRegistryProvider]);
138
176
 
139
177
  useEffect(() => {
140
178
  if (!cacheEnabled) {
@@ -164,38 +202,89 @@ export const useApp = ({
164
202
  module: 'org.dxos.app-framework.atom-registry',
165
203
  });
166
204
 
167
- // Poll manager atoms for progress (avoids PubSub subscription race).
168
- const progressInterval = setInterval(() => {
169
- if (readyRef.current) {
170
- clearInterval(progressInterval);
171
- return;
172
- }
173
- const active = manager.getActive();
174
- const modules = manager.getModules();
175
- const total = modules.length;
176
- const activated = active.length;
177
- const lastModule = active.length > 0 ? active[active.length - 1] : undefined;
178
- setStartupProgress({
179
- activated,
180
- total,
181
- progress: total > 0 ? activated / total : 0,
182
- status: lastModule ? humanizeModuleId(lastModule) : undefined,
183
- });
184
- }, 100);
185
-
186
205
  const fiber = Effect.gen(function* () {
187
206
  const queue = yield* PubSub.subscribe(manager.activation);
188
207
  const listener = yield* Effect.forkDaemon(
189
208
  Queue.take(queue).pipe(
190
- Effect.tap(({ event, state, error: error$ }) =>
209
+ Effect.tap(({ event, state, module, error: error$ }) =>
191
210
  Effect.sync(() => {
192
- if (event === ActivationEvents.Startup.id && state === 'activated') {
211
+ // Event-level Startup activated (no `module` field) fires once,
212
+ // after every module triggered by Startup has finished. Module
213
+ // activations now also carry their parent event id (so the trace
214
+ // can attribute a module to its triggering event), which means
215
+ // each module activated under Startup publishes
216
+ // `{ event: 'startup', state: 'activated', module: <id> }`. Without
217
+ // the `!module` guard the listener would mark the app ready on
218
+ // the *first* such module rather than waiting for the event-level
219
+ // completion — leaving downstream capabilities (operation-invoker,
220
+ // app-graph, …) un-registered when the placeholder dismisses.
221
+ if (event === ActivationEvents.Startup.id && state === 'activated' && !module) {
193
222
  clearTimeout(timeoutId);
194
- clearInterval(progressInterval);
195
223
  setReady(true);
196
224
  readyRef.current = true;
197
225
  // Trigger startup profiler dump if available.
198
226
  (globalThis as any).composer?.profiler?.dump();
227
+ // Notify any host observability layer that startup completed.
228
+ // A `CustomEvent` keeps this generic — app-framework doesn't
229
+ // import a provider, and consumers can capture the startup
230
+ // summary without us picking one.
231
+ if (typeof window !== 'undefined') {
232
+ window.dispatchEvent(new CustomEvent('app-framework:startup-activated'));
233
+ }
234
+ return;
235
+ }
236
+ // `activating` is the start-of-load signal. Surface the raw
237
+ // `module` (or `event`) plus a pre-humanized label so the
238
+ // host placeholder can decide what to render — show
239
+ // everything, suppress noisy sub-modules, group by plugin,
240
+ // or apply its own formatting. We intentionally do NOT touch
241
+ // these fields on `activated`: pairing the two would cause
242
+ // back-to-back identical updates to the host's effect, which
243
+ // the boot loader treats as a re-trigger because
244
+ // `progress.progress` moved. Leaving the label alone on
245
+ // completion keeps it accurate ("now activating X") until
246
+ // the next module starts.
247
+ if (module && state === 'activating' && !readyRef.current) {
248
+ setStartupProgress((current) => ({
249
+ ...current,
250
+ // `event` here is the activation event that first
251
+ // triggered this module load (plumbed through
252
+ // `_loadCapabilitiesForModules` → `_loadModule`).
253
+ // Falsy/empty falls back to undefined so consumers can
254
+ // tell "no parent context" from "parent context: <X>".
255
+ event: event || undefined,
256
+ module,
257
+ humanizedName: humanizeModuleId(module),
258
+ }));
259
+ }
260
+ // Update the activation count when a module commits. The
261
+ // ring's fraction comes from this; `event`/`module`/
262
+ // `humanizedName` were set by the matching `activating`
263
+ // message above and are left alone so the count can advance
264
+ // without re-firing the host's status callback.
265
+ if (module && state === 'activated' && !readyRef.current) {
266
+ const active = manager.getActive();
267
+ const total = manager.getModules().length;
268
+ setStartupProgress((current) => ({
269
+ ...current,
270
+ activated: active.length,
271
+ total,
272
+ progress: total > 0 ? active.length / total : 0,
273
+ }));
274
+ }
275
+ // Event-level `activating` (no `module`) — fired at the start
276
+ // of `_activateModulesForEvent` and recursively for each
277
+ // before/after event. Surfaces a label during the gap before
278
+ // the first module-level message lands; subsequent module
279
+ // updates immediately overwrite this with a more specific
280
+ // label.
281
+ if (event && !module && state === 'activating' && !readyRef.current) {
282
+ setStartupProgress((current) => ({
283
+ ...current,
284
+ event,
285
+ module: undefined,
286
+ humanizedName: humanizeEventKey(event),
287
+ }));
199
288
  }
200
289
  if (error$ && !readyRef.current) {
201
290
  setError(error$);
@@ -232,7 +321,6 @@ export const useApp = ({
232
321
  return () => {
233
322
  log('useApp: effect cleanup');
234
323
  clearTimeout(timeoutId);
235
- clearInterval(progressInterval);
236
324
  void runAndForwardErrors(Fiber.interrupt(fiber));
237
325
  if (!isExternalManager) {
238
326
  void runAndForwardErrors(manager.shutdown());
@@ -240,6 +328,9 @@ export const useApp = ({
240
328
  };
241
329
  }, [manager]);
242
330
 
331
+ const progressRef = useRef(startupProgress);
332
+ progressRef.current = startupProgress;
333
+
243
334
  return useCallback(
244
335
  () => (
245
336
  <ErrorBoundary name='app' FallbackComponent={fallback}>
@@ -251,7 +342,7 @@ export const useApp = ({
251
342
  ready={ready}
252
343
  error={error}
253
344
  debounce={debounce}
254
- progress={startupProgress}
345
+ progress={progressRef.current}
255
346
  />
256
347
  </RegistryContext.Provider>
257
348
  </ContextProtocolProvider>
@@ -269,21 +360,65 @@ const setupDevtools = (manager: PluginManager.PluginManager) => {
269
360
 
270
361
  /**
271
362
  * Extracts a human-readable label from a module ID.
272
- * E.g., "org.dxos.plugin.markdown.module.ReactSurface" → "Markdown".
363
+ *
364
+ * Module IDs follow `org.dxos.plugin.<plugin-slug>.module.<module-name>`,
365
+ * where `<plugin-slug>` is kebab-case and `<module-name>` is either an
366
+ * explicit string (often kebab-case, e.g. `'observability'`, `'namespace'`)
367
+ * or a capability tag in PascalCase from `Capability.getModuleTag(...)`
368
+ * (e.g. `'ReactSurface'`, `'AppGraphBuilder'`). The output is
369
+ * `"Title Case Plugin: kebab-module"` so the visible status names both the
370
+ * plugin and the aspect being activated, helping disambiguate the multiple
371
+ * modules a plugin contributes.
372
+ *
373
+ * Examples:
374
+ * - "org.dxos.plugin.markdown.module.ReactSurface" → "Markdown: react-surface"
375
+ * - "org.dxos.plugin.observability.module.AppGraphBuilder" → "Observability: app-graph-builder"
376
+ * - "org.dxos.plugin.observability.module.observability" → "Observability"
377
+ * (the module name matches the plugin slug — collapsed to avoid
378
+ * "Observability: observability" noise.)
273
379
  */
274
380
  const humanizeModuleId = (moduleId: string): string => {
275
- // Extract plugin name from pattern: org.dxos.plugin.<name>.module.<capability>
276
- const pluginMatch = moduleId.match(/\.plugin\.([^.]+)\./);
277
- if (pluginMatch) {
278
- const name = pluginMatch[1];
279
- // Convert kebab-case to title case.
280
- return name
281
- .split('-')
282
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
283
- .join(' ');
381
+ const match = moduleId.match(/\.plugin\.([^.]+)\.module\.(.+)$/);
382
+ if (!match) {
383
+ // Fallback: use the last segment.
384
+ const parts = moduleId.split('.');
385
+ return parts[parts.length - 1];
386
+ }
387
+ const [, pluginSlug, moduleName] = match;
388
+ const pluginLabel = pluginSlug
389
+ .split('-')
390
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
391
+ .join(' ');
392
+ // Normalise the module name to kebab-case so PascalCase capability tags
393
+ // ("ReactSurface") read consistently with explicit kebab IDs
394
+ // ("operation-handler"). The two-step substitution handles consecutive
395
+ // uppercase runs (`URLLoader` → `url-loader`) without splitting them
396
+ // mid-acronym.
397
+ const moduleLabel = moduleName
398
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
399
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
400
+ .toLowerCase();
401
+ // Capability modules whose name matches the plugin slug (e.g. the
402
+ // observability plugin's `observability` capability module) would render
403
+ // as "Observability: observability" — drop the redundant suffix.
404
+ if (moduleLabel === pluginSlug) {
405
+ return pluginLabel;
284
406
  }
407
+ return `${pluginLabel}: ${moduleLabel}`;
408
+ };
285
409
 
286
- // Fallback: use the last segment.
287
- const parts = moduleId.split('.');
288
- return parts[parts.length - 1];
410
+ /**
411
+ * Extracts a human-readable label from an activation event key.
412
+ * E.g., "org.dxos.app-framework.event.setup-react-surface" → "Setup React Surface".
413
+ */
414
+ const humanizeEventKey = (eventKey: string): string => {
415
+ // Strip a leading specifier (composite key form: "<id>:<specifier>").
416
+ const id = eventKey.split(':')[0];
417
+ // Match the trailing segment after `.event.`.
418
+ const match = id.match(/\.event\.(.+)$/);
419
+ const slug = match ? match[1] : (id.split('.').pop() ?? id);
420
+ return slug
421
+ .split('-')
422
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
423
+ .join(' ');
289
424
  };
@@ -2,7 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { useEffect, useState } from 'react';
5
+ import { useEffect, useRef, useState } from 'react';
6
6
 
7
7
  export enum LoadingState {
8
8
  Loading = 0,
@@ -23,6 +23,14 @@ export enum LoadingState {
23
23
  */
24
24
  export const useLoading = (ready: boolean, debounce = 0) => {
25
25
  const [stage, setStage] = useState<LoadingState>(LoadingState.Loading);
26
+ // Mirror `ready` into a ref so the interval's `setStage` callback can read
27
+ // the latest value without depending on the effect re-running. The pure
28
+ // closure-capture pattern (with `ready` in deps) sticks the FSM at
29
+ // `FadeIn` whenever HMR doesn't propagate the dep change end-to-end —
30
+ // a ref sidesteps that entirely and fires the read on every tick.
31
+ const readyRef = useRef(ready);
32
+ readyRef.current = ready;
33
+
26
34
  useEffect(() => {
27
35
  if (!debounce) {
28
36
  return;
@@ -30,18 +38,18 @@ export const useLoading = (ready: boolean, debounce = 0) => {
30
38
 
31
39
  const i = setInterval(() => {
32
40
  setStage((stage) => {
41
+ const isReady = readyRef.current;
33
42
  switch (stage) {
34
43
  case LoadingState.Loading: {
35
- if (!ready) {
44
+ if (!isReady) {
36
45
  return LoadingState.FadeIn;
37
- } else {
38
- clearInterval(i);
39
- return LoadingState.Done;
40
46
  }
47
+ clearInterval(i);
48
+ return LoadingState.Done;
41
49
  }
42
50
 
43
51
  case LoadingState.FadeIn: {
44
- if (ready) {
52
+ if (isReady) {
45
53
  return LoadingState.FadeOut;
46
54
  }
47
55
  break;