@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
@@ -0,0 +1,75 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { describe, test } from 'vitest';
6
+
7
+ import * as PluginManifest from './plugin-manifest';
8
+
9
+ describe('PluginManifest', () => {
10
+ describe('parse', () => {
11
+ test('resolves entry and assets relative to manifest URL', ({ expect }) => {
12
+ const resolved = PluginManifest.parse('https://example.com/plugins/foo/manifest.json', {
13
+ id: 'com.example.foo',
14
+ name: 'Foo',
15
+ version: '1.0.0',
16
+ assets: ['index.mjs', 'style.css', 'chunks/lib-abc.js'],
17
+ });
18
+ expect(resolved.id).toBe('com.example.foo');
19
+ expect(resolved.dev).toBe(false);
20
+ expect(resolved.entryUrl).toBe('https://example.com/plugins/foo/index.mjs');
21
+ expect(resolved.assetUrls).toEqual([
22
+ 'https://example.com/plugins/foo/index.mjs',
23
+ 'https://example.com/plugins/foo/style.css',
24
+ 'https://example.com/plugins/foo/chunks/lib-abc.js',
25
+ ]);
26
+ });
27
+
28
+ test('throws if the canonical entry filename is not listed in assets', ({ expect }) => {
29
+ expect(() =>
30
+ PluginManifest.parse('https://example.com/manifest.json', {
31
+ id: 'a',
32
+ name: 'A',
33
+ version: '0.0.1',
34
+ assets: ['style.css'],
35
+ }),
36
+ ).toThrow();
37
+ });
38
+
39
+ test('throws on missing required fields', ({ expect }) => {
40
+ expect(() =>
41
+ PluginManifest.parse('https://example.com/manifest.json', {
42
+ name: 'A',
43
+ version: '0.0.1',
44
+ assets: ['index.mjs'],
45
+ }),
46
+ ).toThrow();
47
+ });
48
+
49
+ test('devEntry flips dev mode and resolves the entry against the manifest URL', ({ expect }) => {
50
+ const resolved = PluginManifest.parse('http://localhost:3967/manifest.json', {
51
+ id: 'com.example.foo',
52
+ name: 'Foo',
53
+ version: '0.0.0-dev',
54
+ assets: [],
55
+ devEntry: 'src/plugin.tsx',
56
+ });
57
+ expect(resolved.dev).toBe(true);
58
+ expect(resolved.entryUrl).toBe('http://localhost:3967/src/plugin.tsx');
59
+ expect(resolved.assetUrls).toEqual([]);
60
+ });
61
+
62
+ test('devEntry waives the canonical-entry-in-assets requirement', ({ expect }) => {
63
+ // No `index.mjs` in assets — would throw without devEntry.
64
+ expect(() =>
65
+ PluginManifest.parse('http://localhost:3967/manifest.json', {
66
+ id: 'a',
67
+ name: 'A',
68
+ version: '0.0.0-dev',
69
+ assets: [],
70
+ devEntry: 'src/plugin.tsx',
71
+ }),
72
+ ).not.toThrow();
73
+ });
74
+ });
75
+ });
@@ -0,0 +1,134 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import * as FetchHttpClient from '@effect/platform/FetchHttpClient';
6
+ import * as HttpClient from '@effect/platform/HttpClient';
7
+ import * as HttpClientRequest from '@effect/platform/HttpClientRequest';
8
+ import * as HttpClientResponse from '@effect/platform/HttpClientResponse';
9
+ import * as Effect from 'effect/Effect';
10
+ import * as Schema from 'effect/Schema';
11
+
12
+ import { BaseError } from '@dxos/errors';
13
+ import { PLUGIN_ENTRY_FILENAME } from '@dxos/protocols';
14
+
15
+ /**
16
+ * Default port the Vite plugin (`composerPlugin`) binds the dev server to.
17
+ *
18
+ * Shared single source of truth — `composerPlugin` reads it as the default
19
+ * port, and the host's "Load Dev Plugin" affordance pre-fills the manifest URL
20
+ * with `http://localhost:${PLUGIN_DEV_SERVER_PORT}/manifest.json`. Lives in
21
+ * app-framework rather than `@dxos/protocols` because the constant is a
22
+ * client-side convention (host loader + Vite plugin) rather than a wire-level
23
+ * protocol.
24
+ */
25
+ export const PLUGIN_DEV_SERVER_PORT = 3967;
26
+
27
+ /**
28
+ * Tagged error for manifest fetch / parse failures. Construction sites set
29
+ * `context.manifestUrl` and `context.reason` (one of `'fetch-failed' |
30
+ * 'http-error' | 'parse-failed' | 'invalid'`) so handlers can route on the
31
+ * specific failure mode.
32
+ */
33
+ export class PluginManifestError extends BaseError.extend('PluginManifestError', 'Plugin manifest is invalid') {}
34
+
35
+ /**
36
+ * Schema for a third-party plugin manifest.
37
+ *
38
+ * Production manifests are published as a sibling of the plugin's entry module
39
+ * ({@link PLUGIN_ENTRY_FILENAME}) and advertise every file the plugin needs at
40
+ * runtime so the host can eagerly cache them for offline use.
41
+ *
42
+ * Dev manifests (served by `vite dev` via {@link composerPlugin}) set `devEntry`
43
+ * to point at the unbundled source entry. The host's loader treats the presence
44
+ * of `devEntry` as the dev-mode signal: it imports the entry directly from the
45
+ * dev server, skips eager asset caching, and skips static stylesheet injection
46
+ * (Vite handles CSS via runtime `<style>` injection during HMR). `assets` is not
47
+ * required to enumerate every file in dev mode — chunks and styles flow through
48
+ * the dev server on demand.
49
+ */
50
+ export const Manifest = Schema.Struct({
51
+ id: Schema.String,
52
+ name: Schema.String,
53
+ version: Schema.String,
54
+ assets: Schema.Array(Schema.String),
55
+ devEntry: Schema.String.pipe(Schema.optional),
56
+ });
57
+
58
+ export type Manifest = Schema.Schema.Type<typeof Manifest>;
59
+
60
+ /**
61
+ * Resolved manifest with all asset paths converted to absolute URLs.
62
+ *
63
+ * `dev` reflects whether the source manifest declared a `devEntry`. Loaders branch
64
+ * on this to skip offline caching and stylesheet injection — both are no-ops (or
65
+ * actively wrong) when the plugin is being served by a Vite dev server.
66
+ */
67
+ export type ResolvedManifest = {
68
+ id: string;
69
+ name: string;
70
+ version: string;
71
+ entryUrl: string;
72
+ assetUrls: readonly string[];
73
+ dev: boolean;
74
+ };
75
+
76
+ /**
77
+ * Resolves a parsed manifest's relative asset paths against the manifest's URL.
78
+ * In dev mode the entry comes from `devEntry`; otherwise it's the canonical
79
+ * {@link PLUGIN_ENTRY_FILENAME} sitting next to the manifest.
80
+ */
81
+ const resolve = (manifestUrl: string, manifest: Manifest): ResolvedManifest => {
82
+ const dev = manifest.devEntry !== undefined;
83
+ return {
84
+ id: manifest.id,
85
+ name: manifest.name,
86
+ version: manifest.version,
87
+ entryUrl: new URL(dev ? manifest.devEntry! : PLUGIN_ENTRY_FILENAME, manifestUrl).toString(),
88
+ assetUrls: manifest.assets.map((asset) => new URL(asset, manifestUrl).toString()),
89
+ dev,
90
+ };
91
+ };
92
+
93
+ /**
94
+ * Synchronous decode + validate + resolve for an in-memory manifest payload. Used
95
+ * by tests and tooling that have the manifest body already loaded; the runtime
96
+ * path goes through {@link fetchManifest}.
97
+ */
98
+ export const parse = (manifestUrl: string, payload: unknown): ResolvedManifest => {
99
+ const manifest = Schema.decodeUnknownSync(Manifest)(payload);
100
+ if (manifest.devEntry === undefined && !manifest.assets.includes(PLUGIN_ENTRY_FILENAME)) {
101
+ throw new Error(`Manifest at ${manifestUrl} does not list ${PLUGIN_ENTRY_FILENAME} in assets.`);
102
+ }
103
+ return resolve(manifestUrl, manifest);
104
+ };
105
+
106
+ /**
107
+ * Fetches and parses a manifest from the given URL via Effect's HTTP client and
108
+ * Schema-driven JSON decoding. All failure modes surface as a single tagged
109
+ * {@link PluginManifestError}; callers route on `error.context.reason`.
110
+ */
111
+ export const fetchManifest = (manifestUrl: string): Effect.Effect<ResolvedManifest, PluginManifestError> =>
112
+ Effect.gen(function* () {
113
+ const response = yield* HttpClientRequest.get(manifestUrl).pipe(
114
+ HttpClient.execute,
115
+ Effect.mapError((cause) => new PluginManifestError({ context: { manifestUrl, reason: 'fetch-failed' }, cause })),
116
+ );
117
+ if (response.status >= 400) {
118
+ return yield* Effect.fail(
119
+ new PluginManifestError({ context: { manifestUrl, reason: 'http-error', status: response.status } }),
120
+ );
121
+ }
122
+ const manifest = yield* HttpClientResponse.schemaBodyJson(Manifest)(response).pipe(
123
+ Effect.mapError((cause) => new PluginManifestError({ context: { manifestUrl, reason: 'parse-failed' }, cause })),
124
+ );
125
+ if (manifest.devEntry === undefined && !manifest.assets.includes(PLUGIN_ENTRY_FILENAME)) {
126
+ return yield* Effect.fail(
127
+ new PluginManifestError({
128
+ context: { manifestUrl, reason: 'invalid' },
129
+ cause: `assets does not include ${PLUGIN_ENTRY_FILENAME}`,
130
+ }),
131
+ );
132
+ }
133
+ return resolve(manifestUrl, manifest);
134
+ }).pipe(Effect.scoped, Effect.provide(FetchHttpClient.layer));
@@ -7,6 +7,7 @@ import * as Effect from 'effect/Effect';
7
7
  import * as Option from 'effect/Option';
8
8
  import * as Pipeable from 'effect/Pipeable';
9
9
 
10
+ import { BaseError } from '@dxos/errors';
10
11
  import { invariant } from '@dxos/invariant';
11
12
 
12
13
  import type * as ActivationEvent from './activation-event';
@@ -45,11 +46,18 @@ export const activate = (event: ActivationEvent.ActivationEvent): Effect.Effect<
45
46
  export const reset = (event: ActivationEvent.ActivationEvent): Effect.Effect<boolean, Error, Service> =>
46
47
  Effect.flatMap(Service, (manager) => manager.reset(event));
47
48
 
49
+ /**
50
+ * Shuts down the plugin manager, deactivating all active modules and clearing lifecycle state.
51
+ * Accesses the PluginManager via the Effect layer system.
52
+ */
53
+ export const shutdown = (): Effect.Effect<boolean, Error, Service> =>
54
+ Effect.flatMap(Service, (manager) => manager.shutdown());
55
+
48
56
  /**
49
57
  * Computes a module ID from plugin ID and export name.
50
58
  */
51
59
  const computeModuleId = (pluginId: string, moduleName: string): string => {
52
- return `${pluginId}/module/${moduleName}`;
60
+ return `${pluginId}.module.${moduleName}`;
53
61
  };
54
62
 
55
63
  /**
@@ -82,16 +90,33 @@ export interface PluginModule {
82
90
  activatesOn: ActivationEvent.Events;
83
91
 
84
92
  /**
85
- * Events which the plugin depends on being activated.
86
- * Plugin is marked as needing reset a plugin activated by a dependent event is removed.
87
- * Events are automatically activated before activation of the plugin.
93
+ * Events that this module fires *before* its own activation runs.
94
+ *
95
+ * When this module is asked to activate (via {@link activatesOn}), the
96
+ * plugin manager first activates every event listed here, ensuring any
97
+ * other modules that contribute to those events have completed before
98
+ * this module's {@link activate} body executes. These events are fired
99
+ * by the framework on this module's behalf — the module does not need
100
+ * to wait for some other code to fire them.
101
+ *
102
+ * The module is marked as needing reset if a module activated by one
103
+ * of these events is later removed.
104
+ *
105
+ * Read as: "this module fires these events before [its] activation".
88
106
  */
89
- activatesBefore?: ActivationEvent.ActivationEvent[];
107
+ firesBeforeActivation?: ActivationEvent.ActivationEvent[];
90
108
 
91
109
  /**
92
- * Events which this plugin triggers upon activation.
110
+ * Events that this module fires *after* its own activation completes.
111
+ *
112
+ * Once this module's {@link activate} body has finished executing, the
113
+ * plugin manager activates every event listed here, causing any modules
114
+ * listening on those events to run. These events are fired by the
115
+ * framework on this module's behalf as part of this module's lifecycle.
116
+ *
117
+ * Read as: "this module fires these events after [its] activation".
93
118
  */
94
- activatesAfter?: ActivationEvent.ActivationEvent[];
119
+ firesAfterActivation?: ActivationEvent.ActivationEvent[];
95
120
 
96
121
  /**
97
122
  * Called when the module is activated.
@@ -109,15 +134,15 @@ class PluginModuleImpl implements PluginModule {
109
134
  readonly [PluginModuleTypeId]: PluginModuleTypeId = PluginModuleTypeId;
110
135
  readonly id: PluginModule['id'];
111
136
  readonly activatesOn: PluginModule['activatesOn'];
112
- readonly activatesBefore?: PluginModule['activatesBefore'];
113
- readonly activatesAfter?: PluginModule['activatesAfter'];
137
+ readonly firesBeforeActivation?: PluginModule['firesBeforeActivation'];
138
+ readonly firesAfterActivation?: PluginModule['firesAfterActivation'];
114
139
  readonly activate: PluginModule['activate'];
115
140
 
116
141
  constructor(options: Omit<PluginModule, typeof PluginModuleTypeId>) {
117
142
  this.id = options.id;
118
143
  this.activatesOn = options.activatesOn;
119
- this.activatesBefore = options.activatesBefore;
120
- this.activatesAfter = options.activatesAfter;
144
+ this.firesBeforeActivation = options.firesBeforeActivation;
145
+ this.firesAfterActivation = options.firesAfterActivation;
121
146
  this.activate = options.activate;
122
147
  }
123
148
  }
@@ -128,7 +153,7 @@ export type Meta = {
128
153
  *
129
154
  * Expected to be in the form of a valid URL.
130
155
  *
131
- * @example dxos.org/plugin/example
156
+ * @example org.dxos.plugin.example
132
157
  */
133
158
  id: string;
134
159
 
@@ -137,11 +162,23 @@ export type Meta = {
137
162
  */
138
163
  name: string;
139
164
 
165
+ /**
166
+ * Semver version string of the plugin, typically the publishing package's
167
+ * `package.json` version.
168
+ */
169
+ version?: string;
170
+
140
171
  /**
141
172
  * Short description of plugin functionality.
142
173
  */
143
174
  description?: string;
144
175
 
176
+ /**
177
+ * Name of the author or organization that created the plugin.
178
+ */
179
+ // TODO(burdon): DID or domain name?
180
+ author?: string;
181
+
145
182
  /**
146
183
  * URL of home page.
147
184
  */
@@ -152,6 +189,14 @@ export type Meta = {
152
189
  */
153
190
  source?: string;
154
191
 
192
+ /**
193
+ * Relative path (inside the published package) to the plugin's bundled MDL
194
+ * specification file — e.g. `'PLUGIN.mdl'` or `'docs/PLUGIN.mdl'`. The file
195
+ * is shipped via the package's `files` entry and resolved by registry
196
+ * surfaces to render an in-app viewer and/or external link.
197
+ */
198
+ spec?: string;
199
+
155
200
  /**
156
201
  * URL of screenshot.
157
202
  */
@@ -171,6 +216,22 @@ export type Meta = {
171
216
  * Icon hue (ChromaticPalette).
172
217
  */
173
218
  iconHue?: string;
219
+
220
+ /**
221
+ * IDs of plugins this plugin functionally depends on.
222
+ *
223
+ * Treated as a convenience by the default `PluginManager` flow:
224
+ * - Enabling this plugin auto-enables the transitive closure of `dependsOn`
225
+ * (installing missing entries from the plugin registry when possible).
226
+ * - Disabling a depended-upon plugin surfaces dependents to the caller; the
227
+ * `PluginManager.disable` API supports an opt-in cascade.
228
+ *
229
+ * Not an invariant: low-level `PluginManager` APIs accept opt-outs
230
+ * (`resolveDependencies: false`, `ignoreDependents: true`) so a caller may
231
+ * substitute an alternative implementation that satisfies the dependent's
232
+ * capability needs in its own way.
233
+ */
234
+ dependsOn?: string[];
174
235
  };
175
236
 
176
237
  /**
@@ -316,6 +377,7 @@ const resolveModule = (
316
377
  export function make<T>(builder: PluginBuilder<T>): PluginFactory<T>;
317
378
  export function make<T>(builder: PluginBuilder<T>): PluginFactory<T> {
318
379
  const meta = builder.meta;
380
+ invariant(!meta.dependsOn?.includes(meta.id), `Plugin ${meta.id} declares itself as a dependency.`);
319
381
 
320
382
  const factory = (options: T) => {
321
383
  const modules = builder.modules.map((module) => resolveModule(meta, module, options));
@@ -324,3 +386,123 @@ export function make<T>(builder: PluginBuilder<T>): PluginFactory<T> {
324
386
 
325
387
  return Object.assign(factory, { meta });
326
388
  }
389
+
390
+ //
391
+ // Lazy plugin loading
392
+ //
393
+
394
+ /**
395
+ * Symbol used to tag lazy plugin stubs with their loader closure.
396
+ * Hidden from enumeration so plugin manager iteration / serialization paths
397
+ * don't trip over it.
398
+ */
399
+ const LazyTag: unique symbol = Symbol.for('@dxos/app-framework/Plugin/Lazy');
400
+
401
+ /**
402
+ * Async loader for a lazy plugin's real implementation.
403
+ * The default export of the loaded module must be a `PluginFactory<T>` —
404
+ * i.e. the same shape `Plugin.make` produces.
405
+ */
406
+ export type LazyLoader<T = void> = () => Promise<{ default: PluginFactory<T> }>;
407
+
408
+ /** Internal: payload carried on a lazy stub. */
409
+ type LazyPayload = { loader: LazyLoader<any>; options: unknown };
410
+
411
+ /**
412
+ * Defines a lazy plugin whose body is loaded on first enable.
413
+ *
414
+ * The returned factory produces a stub `Plugin` that exposes `meta`
415
+ * synchronously (so callers can read `Plugin.meta.id` for free) but defers
416
+ * loading the real plugin's modules until the manager calls
417
+ * `Plugin.resolveLazy`. This lets the plugin's main entry point ship as a
418
+ * tiny meta-only chunk — the heavy capabilities, schema, React surfaces,
419
+ * etc. live behind the dynamic `import()` and become a separate Rollup
420
+ * chunk that is only fetched when the plugin is enabled.
421
+ *
422
+ * @example
423
+ * ```ts
424
+ * // plugin-markdown/src/index.ts
425
+ * import { Plugin } from '@dxos/app-framework';
426
+ * import { meta } from './meta';
427
+ *
428
+ * export const MarkdownPlugin = Plugin.lazy(meta, () => import('./MarkdownPlugin'));
429
+ *
430
+ * // plugin-markdown/src/MarkdownPlugin.tsx
431
+ * export const MarkdownPlugin = Plugin.define(meta).pipe(...heavy modules..., Plugin.make);
432
+ * export default MarkdownPlugin;
433
+ * ```
434
+ */
435
+ export const lazy = <T = void>(meta: Meta, loader: LazyLoader<T>): PluginFactory<T> => {
436
+ const factory = (options: T): Plugin => {
437
+ const stub = new PluginImpl(meta, []);
438
+ Object.defineProperty(stub, LazyTag, {
439
+ value: { loader, options } satisfies LazyPayload,
440
+ enumerable: false,
441
+ });
442
+ return stub;
443
+ };
444
+ return Object.assign(factory, { meta });
445
+ };
446
+
447
+ /**
448
+ * Type guard for lazy plugin stubs produced by {@link lazy}.
449
+ */
450
+ export const isLazy = (plugin: Plugin): boolean => LazyTag in plugin;
451
+
452
+ /**
453
+ * Tagged error for failures during lazy plugin resolution. `context.id` is
454
+ * the lazy plugin's `meta.id`; `context.reason` discriminates the failure
455
+ * mode (`'load-failed' | 'missing-default' | 'invalid-plugin' |
456
+ * 'meta-mismatch'`) so callers can route on it.
457
+ */
458
+ export class LazyPluginError extends BaseError.extend('LazyPluginError', 'Failed to resolve lazy plugin') {}
459
+
460
+ /**
461
+ * Tagged error for plugin-level dependency resolution failures.
462
+ *
463
+ * `context.id` is the plugin id the manager was acting on. `context.reason`
464
+ * discriminates the failure mode:
465
+ * - `'missing'` — declared dep is neither registered nor in the catalog.
466
+ * `context.missing` lists offending ids.
467
+ * - `'install-failed'` — dep was found in the catalog but `add()` failed.
468
+ * `cause` carries the original error.
469
+ * - `'cycle'` — closure walk detected a cycle. `context.path` is the cycle path.
470
+ * - `'core-dependent'` — cascade-disable would have to disable a core plugin.
471
+ * `context.coreDependent` is the blocking id.
472
+ */
473
+ export class PluginDependencyError extends BaseError.extend(
474
+ 'PluginDependencyError',
475
+ 'Plugin dependency resolution failed',
476
+ ) {}
477
+
478
+ /**
479
+ * Resolves a lazy plugin stub to its real plugin.
480
+ * Returns the plugin unchanged if it is not lazy. Failures surface as
481
+ * {@link LazyPluginError} with `context.reason` indicating the failure mode
482
+ * and (for loader failures) `cause` set to the original error.
483
+ */
484
+ export const resolveLazy = (plugin: Plugin): Effect.Effect<Plugin, LazyPluginError> =>
485
+ Effect.gen(function* () {
486
+ if (!isLazy(plugin)) {
487
+ return plugin;
488
+ }
489
+ const id = plugin.meta.id;
490
+ const { loader, options } = (plugin as unknown as { [LazyTag]: LazyPayload })[LazyTag];
491
+ const mod = yield* Effect.tryPromise({
492
+ try: loader,
493
+ catch: (error) => new LazyPluginError({ context: { id, reason: 'load-failed' }, cause: error }),
494
+ });
495
+ if (!mod || typeof mod.default !== 'function') {
496
+ return yield* Effect.fail(new LazyPluginError({ context: { id, reason: 'missing-default' } }));
497
+ }
498
+ const result = mod.default(options);
499
+ if (!isPlugin(result)) {
500
+ return yield* Effect.fail(new LazyPluginError({ context: { id, reason: 'invalid-plugin' } }));
501
+ }
502
+ if (result.meta.id !== id) {
503
+ return yield* Effect.fail(
504
+ new LazyPluginError({ context: { id, reason: 'meta-mismatch', returnedId: result.meta.id } }),
505
+ );
506
+ }
507
+ return result;
508
+ });
@@ -0,0 +1,163 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { Atom, type Registry as AtomRegistry } from '@effect-atom/atom-react';
6
+ import * as Effect from 'effect/Effect';
7
+
8
+ import { runAndForwardErrors } from '@dxos/effect';
9
+ import { log } from '@dxos/log';
10
+
11
+ /**
12
+ * A registry plugin entry as seen by the plugin manager layer.
13
+ * Populated from the registry catalog; represents the latest available version of a plugin.
14
+ *
15
+ * Independently defined from @dxos/protocols PluginEntry — similar shape but not the same type.
16
+ * Implementations of {@link PluginProvider} (e.g. {@link EdgeRegistryPluginProvider})
17
+ * are responsible for mapping their wire-format entries to this shape.
18
+ */
19
+ export type Plugin = {
20
+ id: string;
21
+ name: string;
22
+ description?: string;
23
+ homePage?: string;
24
+ source?: string;
25
+ screenshots?: string[];
26
+ tags?: string[];
27
+ icon?: string;
28
+ iconHue?: string;
29
+ /**
30
+ * IDs of plugins this entry declares as runtime dependencies. Mirrors
31
+ * {@link Plugin.Meta.dependsOn}. Surfaced in the catalog so the manager can
32
+ * resolve and auto-install transitive deps before they are first loaded.
33
+ */
34
+ dependsOn?: string[];
35
+ /** URL to dynamic-import the latest version of this plugin module. */
36
+ moduleUrl: string;
37
+ /** GitHub repository slug, e.g. `owner/name`. Used to fetch version history. */
38
+ repo: string;
39
+ /**
40
+ * Latest known version string, e.g. `v1.2.0`.
41
+ * Corresponds to releaseTag in the wire-format PluginEntry; named `version` here
42
+ * because the plugin manager layer speaks versions, not release tags.
43
+ */
44
+ version: string;
45
+ };
46
+
47
+ /**
48
+ * A single installable version of a registry plugin.
49
+ */
50
+ export type PluginVersion = {
51
+ /** Version string, e.g. `v1.2.0`. */
52
+ tag: string;
53
+ /** URL to dynamic-import this specific version of the plugin module. */
54
+ moduleUrl: string;
55
+ /** Unix ms of when this version was published (optional; omitted when unknown). */
56
+ releasedAt?: number;
57
+ };
58
+
59
+ /**
60
+ * Abstraction over the plugin registry catalog backend.
61
+ * Implementations may call Edge, a local cache, or a stub.
62
+ *
63
+ * The interface itself is transport-agnostic; concrete implementations live
64
+ * alongside it in this package (see {@link EdgeRegistryPluginProvider}) so we
65
+ * avoid `app-framework ↔ edge-client` cycles, but they only need to satisfy
66
+ * this shape — callers can substitute their own.
67
+ */
68
+ export interface PluginProvider {
69
+ /**
70
+ * Returns all healthy registry plugins (latest version of each).
71
+ */
72
+ listPlugins(): Effect.Effect<readonly Plugin[], Error>;
73
+
74
+ /**
75
+ * Returns all known versions of a plugin identified by its GitHub repo slug.
76
+ * Until the backend implements a versions endpoint, implementations MUST
77
+ * return at least one entry representing the current/latest release.
78
+ */
79
+ listVersions(repo: string): Effect.Effect<readonly PluginVersion[], Error>;
80
+
81
+ /**
82
+ * Returns a single plugin entry for the given repo.
83
+ * If `version` is omitted, returns the latest.
84
+ * Fails if the specified version is not found.
85
+ */
86
+ getPlugin(repo: string, version?: string): Effect.Effect<Plugin, Error>;
87
+ }
88
+
89
+ /**
90
+ * Atomic snapshot of the registry catalog as observed by the host.
91
+ * Exposed via {@link Manager.plugins} so consumers can subscribe reactively
92
+ * (loading state, list contents, last error).
93
+ */
94
+ export type PluginsState = {
95
+ entries: readonly Plugin[];
96
+ loading: boolean;
97
+ error: Error | null;
98
+ };
99
+
100
+ /**
101
+ * Default no-op provider used when no `PluginProvider` is supplied at construction
102
+ * time (e.g. tests, stories, environments without an Edge connection).
103
+ *
104
+ * `listPlugins` returns an empty list (so `Manager.plugins` settles to a stable
105
+ * empty state). `listVersions` and `getPlugin` fail with an explicit error so
106
+ * callers know they need to configure a real provider.
107
+ */
108
+ const NULL_PROVIDER: PluginProvider = {
109
+ listPlugins: () => Effect.succeed([] as readonly Plugin[]),
110
+ listVersions: () => Effect.fail(new Error('No plugin registry provider configured')),
111
+ getPlugin: () => Effect.fail(new Error('No plugin registry provider configured')),
112
+ };
113
+
114
+ /**
115
+ * Owns the cached registry catalog state and forwards `listVersions` / `getPlugin`
116
+ * calls to the underlying {@link PluginProvider}. Lives on the `PluginManager`
117
+ * (as `manager.pluginRegistry`) so consumers can reach it through the manager
118
+ * without a separate capability lookup.
119
+ *
120
+ * On construction the manager kicks off a background fetch via `provider.listPlugins()`
121
+ * and writes the result into the {@link plugins} atom. Subscribers see `loading: true`
122
+ * during the fetch and `loading: false` once it settles.
123
+ */
124
+ export class Manager {
125
+ readonly plugins: Atom.Writable<PluginsState>;
126
+ readonly #provider: PluginProvider;
127
+
128
+ constructor(provider: PluginProvider | undefined, atomRegistry: AtomRegistry.Registry) {
129
+ this.#provider = provider ?? NULL_PROVIDER;
130
+ const initialLoading = provider !== undefined;
131
+ this.plugins = Atom.make<PluginsState>({ entries: [], loading: initialLoading, error: null }).pipe(Atom.keepAlive);
132
+
133
+ if (provider !== undefined) {
134
+ // Fire-and-forget initial load. Errors are surfaced via the atom's `error` field.
135
+ void runAndForwardErrors(
136
+ provider.listPlugins().pipe(
137
+ Effect.match({
138
+ onSuccess: (entries) => atomRegistry.set(this.plugins, { entries, loading: false, error: null }),
139
+ onFailure: (error) => {
140
+ log.catch(error);
141
+ atomRegistry.set(this.plugins, { entries: [], loading: false, error });
142
+ },
143
+ }),
144
+ ),
145
+ );
146
+ }
147
+ }
148
+
149
+ /** Forwards to the underlying provider. */
150
+ listPlugins(): Effect.Effect<readonly Plugin[], Error> {
151
+ return this.#provider.listPlugins();
152
+ }
153
+
154
+ /** Forwards to the underlying provider. */
155
+ listVersions(repo: string): Effect.Effect<readonly PluginVersion[], Error> {
156
+ return this.#provider.listVersions(repo);
157
+ }
158
+
159
+ /** Forwards to the underlying provider. */
160
+ getPlugin(repo: string, version?: string): Effect.Effect<Plugin, Error> {
161
+ return this.#provider.getPlugin(repo, version);
162
+ }
163
+ }