@dxos/app-framework 0.8.4-main.8360d9e660 → 0.8.4-main.8baae0fced

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 (380) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/browser/{capability-5OFLR7J4.mjs → capability-K5XIVCQU.mjs} +12 -11
  4. package/dist/lib/browser/capability-K5XIVCQU.mjs.map +7 -0
  5. package/dist/lib/browser/{chunk-WEBSGU5L.mjs → chunk-5AHASNDW.mjs} +20 -5
  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-272IPLHQ.mjs → chunk-66IXTIVK.mjs} +3 -2
  10. package/dist/lib/browser/chunk-66IXTIVK.mjs.map +7 -0
  11. package/dist/lib/browser/{chunk-JGWCBVKJ.mjs → chunk-BRK6GYNB.mjs} +14 -42
  12. package/dist/lib/browser/chunk-BRK6GYNB.mjs.map +7 -0
  13. package/dist/lib/browser/{chunk-TGX63LTL.mjs → chunk-FJ4765WW.mjs} +1 -1
  14. package/dist/lib/browser/{chunk-TGX63LTL.mjs.map → chunk-FJ4765WW.mjs.map} +2 -2
  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-TIEBZMTF.mjs → chunk-KFDF7KR3.mjs} +9 -11
  20. package/dist/lib/browser/{chunk-TIEBZMTF.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-GH3M2LIW.mjs → chunk-SLX73WRZ.mjs} +85 -15
  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 +15 -30
  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 +12 -2
  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 +164 -34
  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-CRHZKL6T.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-FKE4Z3D6.mjs → chunk-37Z53PXZ.mjs} +1 -1
  53. package/dist/lib/node-esm/{chunk-FKE4Z3D6.mjs.map → chunk-37Z53PXZ.mjs.map} +2 -2
  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-LHCJGNXK.mjs → chunk-6S45OMUP.mjs} +85 -15
  57. package/dist/lib/node-esm/chunk-6S45OMUP.mjs.map +7 -0
  58. package/dist/lib/node-esm/{chunk-3D66SZHP.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-SCDGIGGU.mjs → chunk-OUEMWPIW.mjs} +9 -11
  69. package/dist/lib/node-esm/{chunk-SCDGIGGU.mjs.map → chunk-OUEMWPIW.mjs.map} +3 -3
  70. package/dist/lib/node-esm/{chunk-SQICGJBW.mjs → chunk-PW2VYGOS.mjs} +20 -5
  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-6REV5DE7.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 +15 -30
  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 +12 -2
  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 +164 -34
  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 +4 -4
  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 +232 -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 +176 -6
  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/useCapabilities.d.ts.map +1 -1
  203. package/dist/types/src/ui/hooks/useLoading.d.ts.map +1 -1
  204. package/dist/types/src/ui/hooks/useProcessManagerRuntime.d.ts +24 -0
  205. package/dist/types/src/ui/hooks/useProcessManagerRuntime.d.ts.map +1 -0
  206. package/dist/types/src/ui/hooks/useSettingsState.d.ts.map +1 -1
  207. package/dist/types/src/vite-plugin/boot-loader/BootLoader.stories.d.ts +34 -0
  208. package/dist/types/src/vite-plugin/boot-loader/BootLoader.stories.d.ts.map +1 -0
  209. package/dist/types/src/vite-plugin/boot-loader/index.d.ts +2 -0
  210. package/dist/types/src/vite-plugin/boot-loader/index.d.ts.map +1 -0
  211. package/dist/types/src/vite-plugin/boot-loader/loader.d.ts +51 -0
  212. package/dist/types/src/vite-plugin/boot-loader/loader.d.ts.map +1 -0
  213. package/dist/types/src/vite-plugin/composer/index.d.ts +34 -0
  214. package/dist/types/src/vite-plugin/composer/index.d.ts.map +1 -0
  215. package/dist/types/src/vite-plugin/import-map/index.d.ts +28 -0
  216. package/dist/types/src/vite-plugin/import-map/index.d.ts.map +1 -0
  217. package/dist/types/src/vite-plugin/index.d.ts +5 -0
  218. package/dist/types/src/vite-plugin/index.d.ts.map +1 -0
  219. package/dist/types/src/vite-plugin/manifest.d.ts +41 -0
  220. package/dist/types/src/vite-plugin/manifest.d.ts.map +1 -0
  221. package/dist/types/src/vite-plugin/manifest.test.d.ts +2 -0
  222. package/dist/types/src/vite-plugin/manifest.test.d.ts.map +1 -0
  223. package/dist/types/src/vite-plugin/packages.d.ts +13 -0
  224. package/dist/types/src/vite-plugin/packages.d.ts.map +1 -0
  225. package/dist/types/tsconfig.tsbuildinfo +1 -1
  226. package/moon.yml +15 -0
  227. package/package.json +53 -54
  228. package/src/cli/cli.ts +2 -7
  229. package/src/common/activation-events.ts +10 -15
  230. package/src/common/annotations.ts +3 -0
  231. package/src/common/capabilities.ts +147 -16
  232. package/src/common/operations.ts +5 -8
  233. package/src/core/activation-event.ts +4 -1
  234. package/src/core/capability-manager.test.ts +1 -1
  235. package/src/core/capability-manager.ts +22 -1
  236. package/src/core/capability.ts +20 -2
  237. package/src/core/edge-registry-plugin-provider.ts +92 -0
  238. package/src/core/index.ts +6 -0
  239. package/src/core/plugin-asset-cache.ts +60 -0
  240. package/src/core/plugin-manager.test.ts +816 -10
  241. package/src/core/plugin-manager.ts +865 -46
  242. package/src/core/plugin-manifest.test.ts +75 -0
  243. package/src/core/plugin-manifest.ts +134 -0
  244. package/src/core/plugin.ts +185 -10
  245. package/src/core/registry.ts +163 -0
  246. package/src/core/url-loader.test.ts +221 -0
  247. package/src/core/url-loader.ts +388 -0
  248. package/src/index.ts +1 -2
  249. package/src/plugin-process-manager/ProcessManagerPlugin.ts +24 -0
  250. package/src/{plugin-operation → plugin-process-manager}/history/capability.ts +1 -2
  251. package/src/plugin-process-manager/history/errors.ts +7 -0
  252. package/src/{plugin-operation → plugin-process-manager}/history/history-tracker.test.ts +37 -43
  253. package/src/{plugin-operation → plugin-process-manager}/history/history-tracker.ts +1 -2
  254. package/src/{plugin-operation → plugin-process-manager}/history/types.ts +1 -1
  255. package/src/{plugin-operation → plugin-process-manager}/history/undo-mapping.ts +1 -1
  256. package/src/{plugin-operation → plugin-process-manager}/history/undo-registry.test.ts +3 -4
  257. package/src/{plugin-operation → plugin-process-manager}/history/undo-registry.ts +1 -1
  258. package/src/{plugin-operation → plugin-process-manager}/index.ts +1 -1
  259. package/src/plugin-process-manager/meta.ts +14 -0
  260. package/src/plugin-process-manager/process-manager-capability.ts +178 -0
  261. package/src/{plugin-operation → plugin-process-manager}/testing.ts +26 -45
  262. package/src/testing/harness.ts +247 -0
  263. package/src/testing/index.ts +1 -0
  264. package/src/testing/react.test.tsx +48 -0
  265. package/src/testing/react.tsx +113 -0
  266. package/src/testing/service.ts +2 -2
  267. package/src/testing/withPluginManager.stories.tsx +1 -2
  268. package/src/testing/withPluginManager.tsx +5 -4
  269. package/src/ui/components/App/App.stories.tsx +5 -11
  270. package/src/ui/components/App/App.tsx +29 -5
  271. package/src/ui/components/Placeholder/Placeholder.stories.tsx +77 -0
  272. package/src/ui/components/Placeholder/Placeholder.tsx +155 -0
  273. package/src/{plugin-runtime → ui/components/Placeholder}/index.ts +1 -1
  274. package/src/ui/components/PluginManager/PluginManagerContext.stories.tsx +5 -4
  275. package/src/ui/components/Surface/SurfaceComponent.stories.tsx +16 -15
  276. package/src/ui/components/Surface/SurfaceComponent.tsx +111 -55
  277. package/src/ui/components/Surface/SurfaceInfo.tsx +0 -1
  278. package/src/ui/components/Surface/SurfaceProfilerContext.tsx +207 -0
  279. package/src/ui/components/Surface/index.ts +35 -1
  280. package/src/ui/components/Surface/types.test.ts +126 -0
  281. package/src/ui/components/Surface/types.ts +164 -12
  282. package/src/ui/components/index.ts +1 -0
  283. package/src/ui/hooks/index.ts +1 -1
  284. package/src/ui/hooks/useApp.test.tsx +2 -2
  285. package/src/ui/hooks/useApp.tsx +216 -17
  286. package/src/ui/hooks/useLoading.tsx +14 -6
  287. package/src/ui/hooks/useProcessManagerRuntime.ts +68 -0
  288. package/src/vite-plugin/boot-loader/BootLoader.stories.tsx +270 -0
  289. package/src/vite-plugin/boot-loader/boot-loader.css +320 -0
  290. package/src/vite-plugin/boot-loader/boot-loader.js +325 -0
  291. package/src/vite-plugin/boot-loader/index.ts +5 -0
  292. package/src/vite-plugin/boot-loader/loader.ts +123 -0
  293. package/src/vite-plugin/composer/index.ts +306 -0
  294. package/src/vite-plugin/import-map/index.ts +527 -0
  295. package/src/vite-plugin/index.ts +10 -0
  296. package/src/vite-plugin/manifest.test.ts +46 -0
  297. package/src/vite-plugin/manifest.ts +57 -0
  298. package/src/vite-plugin/packages.ts +187 -0
  299. package/tsconfig.json +25 -1
  300. package/tsconfig.node.json +1 -1
  301. package/.swc/plugins/linux_x86_64_19.0.0/727453fb3a62f7f1d952a41e051ca8a6f88cadc45cee43c6a4d1aa45f9b75665.wasmer-v7 +0 -0
  302. package/dist/lib/browser/capability-5OFLR7J4.mjs.map +0 -7
  303. package/dist/lib/browser/capability-ZHQDZRP5.mjs +0 -37
  304. package/dist/lib/browser/capability-ZHQDZRP5.mjs.map +0 -7
  305. package/dist/lib/browser/chunk-272IPLHQ.mjs.map +0 -7
  306. package/dist/lib/browser/chunk-3VXJONTI.mjs +0 -933
  307. package/dist/lib/browser/chunk-3VXJONTI.mjs.map +0 -7
  308. package/dist/lib/browser/chunk-7AL6SKIV.mjs +0 -728
  309. package/dist/lib/browser/chunk-7AL6SKIV.mjs.map +0 -7
  310. package/dist/lib/browser/chunk-GH3M2LIW.mjs.map +0 -7
  311. package/dist/lib/browser/chunk-JGWCBVKJ.mjs.map +0 -7
  312. package/dist/lib/browser/chunk-M5IC326L.mjs +0 -34
  313. package/dist/lib/browser/chunk-M5IC326L.mjs.map +0 -7
  314. package/dist/lib/browser/chunk-WEBSGU5L.mjs.map +0 -7
  315. package/dist/lib/browser/chunk-YAFEA4GV.mjs +0 -1
  316. package/dist/lib/browser/chunk-YNFPIQGB.mjs.map +0 -7
  317. package/dist/lib/browser/invoker-capability-YTTQ2OBB.mjs +0 -36
  318. package/dist/lib/browser/invoker-capability-YTTQ2OBB.mjs.map +0 -7
  319. package/dist/lib/node-esm/capability-CRHZKL6T.mjs.map +0 -7
  320. package/dist/lib/node-esm/capability-W5C5464H.mjs +0 -38
  321. package/dist/lib/node-esm/capability-W5C5464H.mjs.map +0 -7
  322. package/dist/lib/node-esm/chunk-3D66SZHP.mjs.map +0 -7
  323. package/dist/lib/node-esm/chunk-6REV5DE7.mjs.map +0 -7
  324. package/dist/lib/node-esm/chunk-CMDON4NG.mjs +0 -934
  325. package/dist/lib/node-esm/chunk-CMDON4NG.mjs.map +0 -7
  326. package/dist/lib/node-esm/chunk-I7FZT4A7.mjs +0 -729
  327. package/dist/lib/node-esm/chunk-I7FZT4A7.mjs.map +0 -7
  328. package/dist/lib/node-esm/chunk-LHCJGNXK.mjs.map +0 -7
  329. package/dist/lib/node-esm/chunk-NHXBSAQR.mjs +0 -35
  330. package/dist/lib/node-esm/chunk-NHXBSAQR.mjs.map +0 -7
  331. package/dist/lib/node-esm/chunk-SB5ODNPX.mjs.map +0 -7
  332. package/dist/lib/node-esm/chunk-SQICGJBW.mjs.map +0 -7
  333. package/dist/lib/node-esm/chunk-Z4TJPSMP.mjs +0 -2
  334. package/dist/lib/node-esm/invoker-capability-BU26474T.mjs +0 -37
  335. package/dist/lib/node-esm/invoker-capability-BU26474T.mjs.map +0 -7
  336. package/dist/types/src/plugin-operation/OperationPlugin.d.ts +0 -3
  337. package/dist/types/src/plugin-operation/OperationPlugin.d.ts.map +0 -1
  338. package/dist/types/src/plugin-operation/history/capability.d.ts.map +0 -1
  339. package/dist/types/src/plugin-operation/history/errors.d.ts +0 -5
  340. package/dist/types/src/plugin-operation/history/errors.d.ts.map +0 -1
  341. package/dist/types/src/plugin-operation/history/history-tracker.d.ts.map +0 -1
  342. package/dist/types/src/plugin-operation/history/history-tracker.test.d.ts.map +0 -1
  343. package/dist/types/src/plugin-operation/history/index.d.ts.map +0 -1
  344. package/dist/types/src/plugin-operation/history/types.d.ts.map +0 -1
  345. package/dist/types/src/plugin-operation/history/undo-mapping.d.ts.map +0 -1
  346. package/dist/types/src/plugin-operation/history/undo-registry.d.ts.map +0 -1
  347. package/dist/types/src/plugin-operation/history/undo-registry.test.d.ts.map +0 -1
  348. package/dist/types/src/plugin-operation/index.d.ts +0 -3
  349. package/dist/types/src/plugin-operation/index.d.ts.map +0 -1
  350. package/dist/types/src/plugin-operation/invoker-capability.d.ts +0 -6
  351. package/dist/types/src/plugin-operation/invoker-capability.d.ts.map +0 -1
  352. package/dist/types/src/plugin-operation/meta.d.ts.map +0 -1
  353. package/dist/types/src/plugin-operation/testing.d.ts +0 -109
  354. package/dist/types/src/plugin-operation/testing.d.ts.map +0 -1
  355. package/dist/types/src/plugin-runtime/RuntimePlugin.d.ts +0 -3
  356. package/dist/types/src/plugin-runtime/RuntimePlugin.d.ts.map +0 -1
  357. package/dist/types/src/plugin-runtime/capability.d.ts +0 -6
  358. package/dist/types/src/plugin-runtime/capability.d.ts.map +0 -1
  359. package/dist/types/src/plugin-runtime/index.d.ts +0 -2
  360. package/dist/types/src/plugin-runtime/index.d.ts.map +0 -1
  361. package/dist/types/src/plugin-runtime/meta.d.ts +0 -3
  362. package/dist/types/src/plugin-runtime/meta.d.ts.map +0 -1
  363. package/dist/types/src/ui/hooks/useOperationResolver.d.ts +0 -19
  364. package/dist/types/src/ui/hooks/useOperationResolver.d.ts.map +0 -1
  365. package/src/plugin-operation/OperationPlugin.ts +0 -25
  366. package/src/plugin-operation/history/errors.ts +0 -11
  367. package/src/plugin-operation/invoker-capability.ts +0 -40
  368. package/src/plugin-operation/meta.ts +0 -11
  369. package/src/plugin-runtime/RuntimePlugin.ts +0 -20
  370. package/src/plugin-runtime/capability.ts +0 -53
  371. package/src/plugin-runtime/meta.ts +0 -11
  372. package/src/ui/hooks/useOperationResolver.ts +0 -40
  373. /package/dist/lib/browser/{chunk-YAFEA4GV.mjs.map → core/url-loader.mjs.map} +0 -0
  374. /package/dist/lib/node-esm/{chunk-Z4TJPSMP.mjs.map → core/url-loader.mjs.map} +0 -0
  375. /package/dist/types/src/{plugin-operation → plugin-process-manager}/history/capability.d.ts +0 -0
  376. /package/dist/types/src/{plugin-operation → plugin-process-manager}/history/history-tracker.test.d.ts +0 -0
  377. /package/dist/types/src/{plugin-operation → plugin-process-manager}/history/index.d.ts +0 -0
  378. /package/dist/types/src/{plugin-operation → plugin-process-manager}/history/undo-registry.test.d.ts +0 -0
  379. /package/dist/types/src/{plugin-operation → plugin-process-manager}/meta.d.ts +0 -0
  380. /package/src/{plugin-operation → plugin-process-manager}/history/index.ts +0 -0
@@ -2,12 +2,12 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { afterEach, assert, describe, it } from '@effect/vitest';
6
5
  import { type Atom, Registry } from '@effect-atom/atom-react';
6
+ import { afterEach, assert, describe, it } from '@effect/vitest';
7
7
  import * as Cause from 'effect/Cause';
8
- import * as Exit from 'effect/Exit';
9
8
  import * as Duration from 'effect/Duration';
10
9
  import * as Effect from 'effect/Effect';
10
+ import * as Exit from 'effect/Exit';
11
11
  import * as Fiber from 'effect/Fiber';
12
12
  import * as Match from 'effect/Match';
13
13
  import * as PubSub from 'effect/PubSub';
@@ -18,7 +18,6 @@ import { invariant } from '@dxos/invariant';
18
18
  import { type LogConfig, type LogEntry, LogLevel, log } from '@dxos/log';
19
19
 
20
20
  import { ActivationEvents } from '../common';
21
-
22
21
  import * as ActivationEvent from './activation-event';
23
22
  import * as Capability from './capability';
24
23
  import type * as CapabilityManager from './capability-manager';
@@ -62,7 +61,7 @@ describe('PluginManager', () => {
62
61
  const pluginLoader = Effect.fn(function* (id: string) {
63
62
  const plugin = plugins.find((plugin) => plugin.meta.id === id);
64
63
  invariant(plugin, `Plugin not found: ${id}`);
65
- return plugin;
64
+ return { plugin };
66
65
  });
67
66
 
68
67
  afterEach(() => {
@@ -77,14 +76,138 @@ describe('PluginManager', () => {
77
76
 
78
77
  const manager = PluginManager.make({ pluginLoader });
79
78
  const added = yield* manager.add(testMeta.id);
80
- assert.isTrue(added);
79
+ assert.strictEqual(added, testPlugin);
81
80
  assert.deepStrictEqual(manager.getPlugins(), [testPlugin]);
82
- const removed = manager.remove(testMeta.id);
81
+ assert.deepStrictEqual(manager.getEnabled(), []);
82
+ const removed = yield* manager.remove(testMeta.id);
83
83
  assert.isTrue(removed);
84
84
  assert.deepStrictEqual(manager.getPlugins(), []);
85
85
  }),
86
86
  );
87
87
 
88
+ it.effect('should add plugin when locator differs from meta.id', () =>
89
+ Effect.gen(function* () {
90
+ const Test = Plugin.make(Plugin.define(testMeta));
91
+ const testPlugin = Test();
92
+
93
+ const urlLocator = 'https://example.com/plugin.mjs';
94
+ const urlLoader = Effect.fn(function* (locator: string) {
95
+ if (locator === urlLocator) {
96
+ return { plugin: testPlugin };
97
+ }
98
+ return yield* Effect.fail(new Error(`Unknown locator: ${locator}`));
99
+ });
100
+
101
+ const manager = PluginManager.make({ pluginLoader: urlLoader });
102
+ const added = yield* manager.add(urlLocator);
103
+ assert.strictEqual(added, testPlugin);
104
+ assert.deepStrictEqual(manager.getPlugins(), [testPlugin]);
105
+ assert.deepStrictEqual(manager.getEnabled(), []);
106
+ yield* manager.enable(added.meta.id);
107
+ assert.deepStrictEqual(manager.getEnabled(), [testMeta.id]);
108
+ }),
109
+ );
110
+
111
+ it.effect('dev plugin shadows an existing plugin with the same id', () =>
112
+ Effect.gen(function* () {
113
+ const productionPlugin = Plugin.make(
114
+ Plugin.define(testMeta).pipe(
115
+ Plugin.addModule({
116
+ id: 'Prod',
117
+ activatesOn: ActivationEvents.Startup,
118
+ activate: () => Effect.succeed(Capability.contributes(String, { string: 'prod' })),
119
+ }),
120
+ ),
121
+ )();
122
+ const devPlugin = Plugin.make(
123
+ Plugin.define(testMeta).pipe(
124
+ Plugin.addModule({
125
+ id: 'Dev',
126
+ activatesOn: ActivationEvents.Startup,
127
+ activate: () => Effect.succeed(Capability.contributes(String, { string: 'dev' })),
128
+ }),
129
+ ),
130
+ )();
131
+
132
+ const loader = Effect.fn(function* (locator: string) {
133
+ if (locator === 'prod') {
134
+ return { plugin: productionPlugin };
135
+ }
136
+ if (locator === 'dev') {
137
+ return { plugin: devPlugin, dev: true };
138
+ }
139
+ return yield* Effect.fail(new Error(`Unknown locator: ${locator}`));
140
+ });
141
+
142
+ const manager = PluginManager.make({ pluginLoader: loader });
143
+ yield* manager.add('prod');
144
+ yield* manager.enable(testMeta.id);
145
+ yield* manager.activate(ActivationEvents.Startup);
146
+ assert.deepStrictEqual(
147
+ manager.capabilities.getAll(String).map((value) => value.string),
148
+ ['prod'],
149
+ );
150
+
151
+ // Loading the dev plugin with the same id swaps it into the id slot.
152
+ yield* manager.add('dev');
153
+ yield* manager.enable(testMeta.id);
154
+ assert.strictEqual(
155
+ manager.getPlugins().find((plugin) => plugin.meta.id === testMeta.id),
156
+ devPlugin,
157
+ );
158
+ yield* manager.reset(ActivationEvents.Startup);
159
+ assert.deepStrictEqual(
160
+ manager.capabilities.getAll(String).map((value) => value.string),
161
+ ['dev'],
162
+ );
163
+
164
+ // Removing the dev plugin restores the original and re-enables it
165
+ // because it was enabled at shadow time.
166
+ yield* manager.remove(testMeta.id);
167
+ assert.strictEqual(
168
+ manager.getPlugins().find((plugin) => plugin.meta.id === testMeta.id),
169
+ productionPlugin,
170
+ );
171
+ assert.isTrue(manager.getEnabled().includes(testMeta.id));
172
+ yield* manager.reset(ActivationEvents.Startup);
173
+ assert.deepStrictEqual(
174
+ manager.capabilities.getAll(String).map((value) => value.string),
175
+ ['prod'],
176
+ );
177
+ }),
178
+ );
179
+
180
+ it.effect('dev plugin add does not auto-enable a previously-disabled shadow target', () =>
181
+ Effect.gen(function* () {
182
+ const productionPlugin = Plugin.make(Plugin.define(testMeta))();
183
+ const devPlugin = Plugin.make(Plugin.define(testMeta))();
184
+ const loader = Effect.fn(function* (locator: string) {
185
+ if (locator === 'prod') {
186
+ return { plugin: productionPlugin };
187
+ }
188
+ if (locator === 'dev') {
189
+ return { plugin: devPlugin, dev: true };
190
+ }
191
+ return yield* Effect.fail(new Error(`Unknown locator: ${locator}`));
192
+ });
193
+
194
+ const manager = PluginManager.make({ pluginLoader: loader });
195
+ yield* manager.add('prod');
196
+ // Production plugin is registered but explicitly NOT enabled.
197
+ assert.deepStrictEqual(manager.getEnabled(), []);
198
+
199
+ yield* manager.add('dev');
200
+ yield* manager.remove(testMeta.id);
201
+
202
+ // Original is restored but stays disabled, matching its pre-shadow state.
203
+ assert.strictEqual(
204
+ manager.getPlugins().find((plugin) => plugin.meta.id === testMeta.id),
205
+ productionPlugin,
206
+ );
207
+ assert.deepStrictEqual(manager.getEnabled(), []);
208
+ }),
209
+ );
210
+
88
211
  it.effect('should support factory pattern with options', () =>
89
212
  Effect.gen(function* () {
90
213
  type TestPluginOptions = { count: number };
@@ -105,7 +228,7 @@ describe('PluginManager', () => {
105
228
  const plugin = TestPluginFactory({ count: 5 });
106
229
  plugins = [plugin];
107
230
 
108
- const manager = PluginManager.make({ plugins: [plugin], core: [], pluginLoader });
231
+ const manager = PluginManager.make({ plugins: [plugin], pluginLoader });
109
232
  yield* manager.enable(testMeta.id);
110
233
  yield* manager.activate(ActivationEvents.Startup);
111
234
  const strings = manager.capabilities.getAll(String);
@@ -127,7 +250,7 @@ describe('PluginManager', () => {
127
250
  );
128
251
 
129
252
  const testPlugin = Test();
130
- const manager = PluginManager.make({ plugins: [testPlugin], core: [], pluginLoader });
253
+ const manager = PluginManager.make({ plugins: [testPlugin], pluginLoader });
131
254
  yield* manager.enable(testMeta.id);
132
255
  assert.deepStrictEqual(manager.getEnabled(), [Test.meta.id]);
133
256
  assert.deepStrictEqual(manager.getModules(), [testPlugin.modules[0]]);
@@ -199,6 +322,7 @@ describe('PluginManager', () => {
199
322
 
200
323
  const manager = PluginManager.make({ pluginLoader });
201
324
  yield* manager.add(testMeta.id);
325
+ yield* manager.enable(testMeta.id);
202
326
  const error = yield* Effect.flip(manager.activate(FailEvent));
203
327
  assert.strictEqual(error.message, 'test');
204
328
  }),
@@ -231,6 +355,7 @@ describe('PluginManager', () => {
231
355
 
232
356
  const manager = PluginManager.make({ pluginLoader });
233
357
  yield* manager.add(testMeta.id);
358
+ yield* manager.enable(testMeta.id);
234
359
  const error = yield* Effect.flip(manager.activate(DefectEvent));
235
360
 
236
361
  // Verify the error was caught and propagated.
@@ -277,6 +402,7 @@ describe('PluginManager', () => {
277
402
 
278
403
  const manager = PluginManager.make({ pluginLoader });
279
404
  yield* manager.add(testMeta.id);
405
+ yield* manager.enable(testMeta.id);
280
406
  const error = yield* Effect.flip(manager.activate(DefectEvent));
281
407
 
282
408
  // Verify the error was caught and propagated.
@@ -341,6 +467,7 @@ describe('PluginManager', () => {
341
467
  );
342
468
 
343
469
  yield* manager.add(testMeta.id);
470
+ yield* manager.enable(testMeta.id);
344
471
  yield* manager.activate(ActivationEvents.Startup);
345
472
  yield* activating.await;
346
473
  yield* activated.await;
@@ -391,6 +518,7 @@ describe('PluginManager', () => {
391
518
 
392
519
  {
393
520
  yield* manager.add(testMeta.id);
521
+ yield* manager.enable(testMeta.id);
394
522
  const result = yield* manager.activate(ActivationEvents.Startup);
395
523
  assert.isTrue(result);
396
524
  assert.deepStrictEqual(manager.getActive(), [testPlugin.modules[0].id]);
@@ -455,16 +583,19 @@ describe('PluginManager', () => {
455
583
  assert.strictEqual(manager.capabilities.getAll(Number).length, 0);
456
584
 
457
585
  yield* manager.add(Plugin1.meta.id);
586
+ yield* manager.enable(Plugin1.meta.id);
458
587
  yield* manager.activate(CountEvent);
459
588
  assert.deepStrictEqual(manager.getActive(), [plugin1.modules[0].id]);
460
589
  assert.strictEqual(manager.capabilities.getAll(Number).length, 1);
461
590
 
462
591
  yield* manager.add(Plugin2.meta.id);
592
+ yield* manager.enable(Plugin2.meta.id);
463
593
  yield* manager.activate(CountEvent);
464
594
  assert.deepStrictEqual(manager.getActive(), [plugin1.modules[0].id, plugin2.modules[0].id]);
465
595
  assert.strictEqual(manager.capabilities.getAll(Number).length, 2);
466
596
 
467
597
  yield* manager.add(Plugin3.meta.id);
598
+ yield* manager.enable(Plugin3.meta.id);
468
599
  yield* manager.activate(CountEvent);
469
600
  assert.deepStrictEqual(manager.getActive(), [
470
601
  plugin1.modules[0].id,
@@ -495,6 +626,7 @@ describe('PluginManager', () => {
495
626
  assert.strictEqual(manager.capabilities.getAll(String).length, 0);
496
627
 
497
628
  yield* manager.add(testMeta.id);
629
+ yield* manager.enable(testMeta.id);
498
630
  yield* manager.activate(ActivationEvents.Startup);
499
631
  assert.deepStrictEqual(manager.getActive(), []);
500
632
  assert.strictEqual(manager.capabilities.getAll(String).length, 0);
@@ -528,6 +660,7 @@ describe('PluginManager', () => {
528
660
  assert.strictEqual(count, 0);
529
661
 
530
662
  yield* manager.add(testMeta.id);
663
+ yield* manager.enable(testMeta.id);
531
664
  yield* manager.activate(CountEvent);
532
665
  assert.deepStrictEqual(manager.getActive(), [testPlugin.modules[0].id]);
533
666
  assert.strictEqual(manager.capabilities.getAll(String).length, 1);
@@ -552,7 +685,7 @@ describe('PluginManager', () => {
552
685
  Plugin.addModule({
553
686
  id: 'Count',
554
687
  activatesOn: ActivationEvents.Startup,
555
- activatesBefore: [CountEvent],
688
+ firesBeforeActivation: [CountEvent],
556
689
  activate: Effect.fnUntraced(function* () {
557
690
  const capabilityManager = yield* Capability.Service;
558
691
  computeTotal(capabilityManager);
@@ -587,7 +720,9 @@ describe('PluginManager', () => {
587
720
  const manager = PluginManager.make({ pluginLoader });
588
721
  {
589
722
  yield* manager.add(Test.meta.id);
723
+ yield* manager.enable(Test.meta.id);
590
724
  yield* manager.add(Count.meta.id);
725
+ yield* manager.enable(Count.meta.id);
591
726
  yield* manager.activate(ActivationEvents.Startup);
592
727
  assert.deepStrictEqual(manager.getActive(), [
593
728
  ...testPlugin.modules.map((m) => m.id),
@@ -670,6 +805,7 @@ describe('PluginManager', () => {
670
805
  assert.strictEqual(pendingResetUpdates.count, 0);
671
806
 
672
807
  yield* manager.add(Plugin1.meta.id);
808
+ yield* manager.enable(Plugin1.meta.id);
673
809
  assert.strictEqual(pluginUpdates.count, 1);
674
810
  assert.strictEqual(enabledUpdates.count, 1);
675
811
  assert.strictEqual(modulesUpdates.count, 1);
@@ -686,6 +822,7 @@ describe('PluginManager', () => {
686
822
  assert.strictEqual(pendingResetUpdates.count, 0);
687
823
 
688
824
  yield* manager.add(Plugin2.meta.id);
825
+ yield* manager.enable(Plugin2.meta.id);
689
826
  assert.strictEqual(pluginUpdates.count, 2);
690
827
  assert.strictEqual(enabledUpdates.count, 2);
691
828
  assert.strictEqual(modulesUpdates.count, 2);
@@ -702,6 +839,7 @@ describe('PluginManager', () => {
702
839
  assert.strictEqual(pendingResetUpdates.count, 2);
703
840
 
704
841
  yield* manager.add(Plugin3.meta.id);
842
+ yield* manager.enable(Plugin3.meta.id);
705
843
  assert.strictEqual(pluginUpdates.count, 3);
706
844
  assert.strictEqual(enabledUpdates.count, 3);
707
845
  assert.strictEqual(modulesUpdates.count, 3);
@@ -726,7 +864,7 @@ describe('PluginManager', () => {
726
864
  assert.strictEqual(eventsFiredUpdates.count, 1);
727
865
  assert.strictEqual(pendingResetUpdates.count, 4);
728
866
 
729
- manager.remove(Plugin1.meta.id);
867
+ yield* manager.remove(Plugin1.meta.id);
730
868
  assert.strictEqual(pluginUpdates.count, 4);
731
869
  assert.strictEqual(enabledUpdates.count, 4);
732
870
  assert.strictEqual(modulesUpdates.count, 4);
@@ -773,6 +911,7 @@ describe('PluginManager', () => {
773
911
 
774
912
  const manager = PluginManager.make({ pluginLoader });
775
913
  yield* manager.add(SlowPlugin.meta.id);
914
+ yield* manager.enable(SlowPlugin.meta.id);
776
915
 
777
916
  // Fork the activation so we can control time with TestClock.
778
917
  const activationFiber = yield* Effect.fork(manager.activate(SlowEvent));
@@ -821,6 +960,7 @@ describe('PluginManager', () => {
821
960
 
822
961
  const manager = PluginManager.make({ pluginLoader });
823
962
  yield* manager.add(ConcurrentPlugin.meta.id);
963
+ yield* manager.enable(ConcurrentPlugin.meta.id);
824
964
 
825
965
  // Fork two concurrent activations with DIFFERENT events.
826
966
  // Both events trigger the same module, so both will try to call _loadModule.
@@ -869,7 +1009,9 @@ describe('PluginManager', () => {
869
1009
 
870
1010
  const manager = PluginManager.make({ pluginLoader });
871
1011
  yield* manager.add(Plugin1.meta.id);
1012
+ yield* manager.enable(Plugin1.meta.id);
872
1013
  yield* manager.add(Plugin2.meta.id);
1014
+ yield* manager.enable(Plugin2.meta.id);
873
1015
  yield* manager.activate(ActivationEvents.Startup);
874
1016
  assert.strictEqual(manager.getActive().length, 2);
875
1017
  assert.strictEqual(manager.capabilities.getAll(String).length, 1);
@@ -906,6 +1048,7 @@ describe('PluginManager', () => {
906
1048
 
907
1049
  const manager = PluginManager.make({ pluginLoader });
908
1050
  yield* manager.add(testMeta.id);
1051
+ yield* manager.enable(testMeta.id);
909
1052
  yield* manager.activate(ActivationEvents.Startup);
910
1053
  assert.isFalse(deactivated);
911
1054
 
@@ -951,7 +1094,9 @@ describe('PluginManager', () => {
951
1094
 
952
1095
  const manager = PluginManager.make({ pluginLoader });
953
1096
  yield* manager.add(Plugin1.meta.id);
1097
+ yield* manager.enable(Plugin1.meta.id);
954
1098
  yield* manager.add(Plugin2.meta.id);
1099
+ yield* manager.enable(Plugin2.meta.id);
955
1100
  yield* manager.activate(ActivationEvents.Startup);
956
1101
 
957
1102
  yield* manager.shutdown();
@@ -974,6 +1119,7 @@ describe('PluginManager', () => {
974
1119
 
975
1120
  const manager = PluginManager.make({ pluginLoader });
976
1121
  yield* manager.add(testMeta.id);
1122
+ yield* manager.enable(testMeta.id);
977
1123
  yield* manager.activate(ActivationEvents.Startup);
978
1124
  assert.isTrue(manager.getEventsFired().length > 0);
979
1125
 
@@ -1006,6 +1152,7 @@ describe('PluginManager', () => {
1006
1152
 
1007
1153
  const manager = PluginManager.make({ pluginLoader });
1008
1154
  yield* manager.add(testMeta.id);
1155
+ yield* manager.enable(testMeta.id);
1009
1156
 
1010
1157
  const activationFiber = yield* Effect.fork(manager.activate(ActivationEvents.Startup));
1011
1158
  yield* activationStarted.await;
@@ -1042,6 +1189,7 @@ describe('PluginManager', () => {
1042
1189
 
1043
1190
  const manager = PluginManager.make({ pluginLoader });
1044
1191
  yield* manager.add(testMeta.id);
1192
+ yield* manager.enable(testMeta.id);
1045
1193
  yield* manager.activate(ActivationEvents.Startup);
1046
1194
 
1047
1195
  const pluginsBefore = manager.getPlugins();
@@ -1077,6 +1225,7 @@ describe('PluginManager', () => {
1077
1225
 
1078
1226
  const manager = PluginManager.make({ pluginLoader });
1079
1227
  yield* manager.add(testMeta.id);
1228
+ yield* manager.enable(testMeta.id);
1080
1229
  yield* manager.activate(ActivationEvents.Startup);
1081
1230
  assert.strictEqual(activateCount, 1);
1082
1231
  assert.deepStrictEqual(manager.getActive(), [testPlugin.modules[0].id]);
@@ -1090,4 +1239,661 @@ describe('PluginManager', () => {
1090
1239
  assert.strictEqual(manager.capabilities.getAll(String).length, 1);
1091
1240
  }),
1092
1241
  );
1242
+
1243
+ describe('Plugin.lazy', () => {
1244
+ const lazyMeta = { id: 'org.dxos.plugin.lazy', name: 'Lazy' };
1245
+
1246
+ it('exposes meta synchronously without invoking the loader', () => {
1247
+ let loaderCalls = 0;
1248
+ const Real = Plugin.make(Plugin.define<void>(lazyMeta));
1249
+ const LazyTest = Plugin.lazy(lazyMeta, () => {
1250
+ loaderCalls++;
1251
+ return Promise.resolve({ default: Real });
1252
+ });
1253
+
1254
+ assert.strictEqual(LazyTest.meta.id, lazyMeta.id);
1255
+ assert.strictEqual(LazyTest.meta.name, 'Lazy');
1256
+ assert.strictEqual(loaderCalls, 0);
1257
+
1258
+ const stub = LazyTest();
1259
+ assert.strictEqual(stub.meta.id, lazyMeta.id);
1260
+ assert.deepStrictEqual([...stub.modules], []);
1261
+ assert.isTrue(Plugin.isLazy(stub));
1262
+ assert.strictEqual(loaderCalls, 0);
1263
+ });
1264
+
1265
+ it.effect('resolves the loader on enable and registers the real plugin modules', () =>
1266
+ Effect.gen(function* () {
1267
+ let loaderCalls = 0;
1268
+ const Real = Plugin.define(lazyMeta).pipe(
1269
+ Plugin.addModule({
1270
+ id: 'Hello',
1271
+ activatesOn: ActivationEvents.Startup,
1272
+ activate: () => Effect.succeed(Capability.contributes(String, { string: 'hello' })),
1273
+ }),
1274
+ Plugin.make,
1275
+ );
1276
+ const LazyTest = Plugin.lazy(lazyMeta, () => {
1277
+ loaderCalls++;
1278
+ return Promise.resolve({ default: Real });
1279
+ });
1280
+
1281
+ const lazyStub = LazyTest();
1282
+ plugins = [lazyStub];
1283
+
1284
+ const manager = PluginManager.make({ pluginLoader });
1285
+ yield* manager.add(lazyMeta.id);
1286
+ // Loader has not been invoked yet — only meta is exposed.
1287
+ assert.strictEqual(loaderCalls, 0);
1288
+ assert.deepStrictEqual(manager.getModules(), []);
1289
+
1290
+ yield* manager.enable(lazyMeta.id);
1291
+ assert.strictEqual(loaderCalls, 1);
1292
+ // After enable the registered plugin should be the real one (not the stub),
1293
+ // and its modules should be registered with the manager.
1294
+ const registered = manager.getPlugins().find((p) => p.meta.id === lazyMeta.id);
1295
+ assert.isDefined(registered);
1296
+ assert.isFalse(Plugin.isLazy(registered!));
1297
+ assert.strictEqual(registered!.modules.length, 1);
1298
+
1299
+ yield* manager.activate(ActivationEvents.Startup);
1300
+ assert.strictEqual(manager.capabilities.getAll(String).length, 1);
1301
+ }),
1302
+ );
1303
+
1304
+ it.effect('does not invoke the loader if the plugin is never enabled', () =>
1305
+ Effect.gen(function* () {
1306
+ let loaderCalls = 0;
1307
+ const Real = Plugin.make(Plugin.define<void>(lazyMeta));
1308
+ const LazyTest = Plugin.lazy(lazyMeta, () => {
1309
+ loaderCalls++;
1310
+ return Promise.resolve({ default: Real });
1311
+ });
1312
+ const lazyStub = LazyTest();
1313
+ plugins = [lazyStub];
1314
+
1315
+ const manager = PluginManager.make({ pluginLoader });
1316
+ yield* manager.add(lazyMeta.id);
1317
+
1318
+ // Activate an event that has no listeners — the lazy plugin must not load.
1319
+ yield* manager.activate(ActivationEvents.Startup);
1320
+ assert.strictEqual(loaderCalls, 0);
1321
+ }),
1322
+ );
1323
+
1324
+ it.effect('forwards factory options to the real plugin factory', () =>
1325
+ Effect.gen(function* () {
1326
+ type Opts = { greeting: string };
1327
+ const RealFactory = (opts: Opts) =>
1328
+ Plugin.define(lazyMeta).pipe(
1329
+ Plugin.addModule({
1330
+ id: 'Hello',
1331
+ activatesOn: ActivationEvents.Startup,
1332
+ activate: () => Effect.succeed(Capability.contributes(String, { string: opts.greeting })),
1333
+ }),
1334
+ Plugin.make,
1335
+ )(undefined as void);
1336
+
1337
+ const RealFactoryWithMeta = Object.assign(RealFactory, { meta: lazyMeta });
1338
+
1339
+ const LazyTest = Plugin.lazy<Opts>(lazyMeta, () => Promise.resolve({ default: RealFactoryWithMeta }));
1340
+ const lazyStub = LazyTest({ greeting: 'hola' });
1341
+ plugins = [lazyStub];
1342
+
1343
+ const manager = PluginManager.make({ pluginLoader });
1344
+ yield* manager.add(lazyMeta.id);
1345
+ yield* manager.enable(lazyMeta.id);
1346
+ yield* manager.activate(ActivationEvents.Startup);
1347
+
1348
+ const all = manager.capabilities.getAll(String);
1349
+ assert.strictEqual(all.length, 1);
1350
+ assert.strictEqual(all[0].string, 'hola');
1351
+ }),
1352
+ );
1353
+
1354
+ it.effect('wraps loader rejections in a descriptive error', () =>
1355
+ Effect.gen(function* () {
1356
+ const LazyTest = Plugin.lazy(lazyMeta, () =>
1357
+ Promise.reject<{ default: Plugin.PluginFactory }>(new Error('boom')),
1358
+ );
1359
+ const lazyStub = LazyTest();
1360
+ plugins = [lazyStub];
1361
+
1362
+ const manager = PluginManager.make({ pluginLoader });
1363
+ yield* manager.add(lazyMeta.id);
1364
+
1365
+ const exit = yield* Effect.exit(manager.enable(lazyMeta.id));
1366
+ assert.isTrue(Exit.isFailure(exit));
1367
+ if (Exit.isFailure(exit)) {
1368
+ const failure = Cause.failureOption(exit.cause);
1369
+ assert.isTrue(failure._tag === 'Some');
1370
+ if (failure._tag === 'Some') {
1371
+ assert.isTrue(Plugin.LazyPluginError.is(failure.value));
1372
+ assert.strictEqual((failure.value as Plugin.LazyPluginError).context.id, lazyMeta.id);
1373
+ assert.strictEqual((failure.value as Plugin.LazyPluginError).context.reason, 'load-failed');
1374
+ }
1375
+ }
1376
+ }),
1377
+ );
1378
+
1379
+ it.effect('publishes a lazy:<id> error message when resolution fails', () =>
1380
+ Effect.gen(function* () {
1381
+ const LazyTest = Plugin.lazy(lazyMeta, () =>
1382
+ Promise.reject<{ default: Plugin.PluginFactory }>(new Error('boom')),
1383
+ );
1384
+ const lazyStub = LazyTest();
1385
+ plugins = [lazyStub];
1386
+
1387
+ const manager = PluginManager.make({ pluginLoader });
1388
+ // Subscribe first so we don't miss the activating/error pair.
1389
+ const queue = yield* PubSub.subscribe(manager.activation);
1390
+ yield* manager.add(lazyMeta.id);
1391
+ yield* Effect.exit(manager.enable(lazyMeta.id));
1392
+ const messages = yield* Queue.takeAll(queue);
1393
+
1394
+ const errorMessage = [...messages].find((m) => m.module === `lazy:${lazyMeta.id}` && m.state === 'error');
1395
+ assert.isDefined(errorMessage);
1396
+ assert.isDefined(errorMessage!.error);
1397
+ }).pipe(Effect.scoped),
1398
+ );
1399
+
1400
+ it.effect('coalesces concurrent lazy resolutions of the same plugin id', () =>
1401
+ Effect.gen(function* () {
1402
+ let factoryCalls = 0;
1403
+ // System-tagged so the constructor's core derivation picks it up,
1404
+ // forcing an implicit enable that races the explicit one below.
1405
+ const coreLazyMeta = { ...lazyMeta, tags: ['system'] };
1406
+ const Real = (() => {
1407
+ const inner = Plugin.make(
1408
+ Plugin.define<void>(coreLazyMeta).pipe(
1409
+ Plugin.addModule({
1410
+ id: 'Hello',
1411
+ activatesOn: ActivationEvents.Startup,
1412
+ activate: () => Effect.succeed(Capability.contributes(String, { string: 'hello' })),
1413
+ }),
1414
+ ),
1415
+ );
1416
+ const factory = (() => {
1417
+ factoryCalls++;
1418
+ return inner();
1419
+ }) as Plugin.PluginFactory;
1420
+ return Object.assign(factory, { meta: coreLazyMeta });
1421
+ })();
1422
+ const LazyTest = Plugin.lazy(coreLazyMeta, () => Promise.resolve({ default: Real }));
1423
+ const lazyStub = LazyTest();
1424
+ // `manager.enable(id)` is implicitly called twice — once from the
1425
+ // constructor's core/enabled chain, once from our explicit call. With
1426
+ // coalescing, the underlying factory should still run exactly once.
1427
+ plugins = [lazyStub];
1428
+ const manager = PluginManager.make({ pluginLoader, plugins });
1429
+ yield* manager.enable(coreLazyMeta.id);
1430
+ assert.strictEqual(factoryCalls, 1);
1431
+ }),
1432
+ );
1433
+
1434
+ it.effect('fails with a tagged error when the factory output is not a Plugin', () =>
1435
+ Effect.gen(function* () {
1436
+ const BadFactory = Object.assign(() => ({ not: 'a plugin' }) as any, { meta: lazyMeta });
1437
+ const LazyTest = Plugin.lazy(lazyMeta, () => Promise.resolve({ default: BadFactory }));
1438
+ const lazyStub = LazyTest();
1439
+ plugins = [lazyStub];
1440
+
1441
+ const manager = PluginManager.make({ pluginLoader });
1442
+ yield* manager.add(lazyMeta.id);
1443
+
1444
+ const exit = yield* Effect.exit(manager.enable(lazyMeta.id));
1445
+ assert.isTrue(Exit.isFailure(exit));
1446
+ if (Exit.isFailure(exit)) {
1447
+ const failure = Cause.failureOption(exit.cause);
1448
+ assert.isTrue(failure._tag === 'Some');
1449
+ if (failure._tag === 'Some') {
1450
+ assert.isTrue(Plugin.LazyPluginError.is(failure.value));
1451
+ assert.strictEqual((failure.value as Plugin.LazyPluginError).context.reason, 'invalid-plugin');
1452
+ }
1453
+ }
1454
+ }),
1455
+ );
1456
+ });
1457
+
1458
+ describe('timeouts and failure tracking', () => {
1459
+ // Atom subscriptions fire synchronously when the registry's `_set` runs,
1460
+ // even from a forked fiber on the default runtime. Wrapping in
1461
+ // `Effect.async` lets a TestClock-driven test wait for state produced by
1462
+ // a background `_runForkedFiber` (e.g. the auto-disable triggered when a
1463
+ // module activation times out) without relying on real-time `sleep`.
1464
+ const waitFor = <T>(registry: Registry.Registry, atom: Atom.Atom<T>, predicate: (value: T) => boolean) =>
1465
+ Effect.async<void>((resume) => {
1466
+ if (predicate(registry.get(atom))) {
1467
+ resume(Effect.void);
1468
+ return;
1469
+ }
1470
+ let resolved = false;
1471
+ const dispose = registry.subscribe(atom, () => {
1472
+ if (!resolved && predicate(registry.get(atom))) {
1473
+ resolved = true;
1474
+ dispose();
1475
+ resume(Effect.void);
1476
+ }
1477
+ });
1478
+ return Effect.sync(() => {
1479
+ if (!resolved) {
1480
+ dispose();
1481
+ }
1482
+ });
1483
+ });
1484
+
1485
+ it.effect('records and auto-disables a plugin whose module exceeds the activation timeout', () =>
1486
+ Effect.gen(function* () {
1487
+ const SlowEvent = ActivationEvent.make('org.dxos.test.activation-timeout');
1488
+ const SlowPlugin = Plugin.define({ id: 'org.dxos.test.slow-activation', name: 'Slow Activation' }).pipe(
1489
+ Plugin.addModule({
1490
+ id: 'Slow',
1491
+ activatesOn: SlowEvent,
1492
+ activate: Effect.fnUntraced(function* () {
1493
+ yield* Effect.sleep(Duration.seconds(60));
1494
+ return Capability.contributes(String, { string: 'never' });
1495
+ }),
1496
+ }),
1497
+ Plugin.make,
1498
+ );
1499
+ plugins = [SlowPlugin()];
1500
+
1501
+ const registry = Registry.make();
1502
+ const manager = PluginManager.make({
1503
+ pluginLoader,
1504
+ registry,
1505
+ activationTimeout: Duration.seconds(2),
1506
+ });
1507
+ yield* manager.add(SlowPlugin.meta.id);
1508
+ yield* manager.enable(SlowPlugin.meta.id);
1509
+
1510
+ const fiber = yield* Effect.fork(manager.activate(SlowEvent));
1511
+ // Push past the 2s activation timeout. The forked module fiber is on
1512
+ // TestClock too, so the timeout fires deterministically.
1513
+ yield* TestClock.adjust(Duration.seconds(3));
1514
+ const exit = yield* Fiber.await(fiber);
1515
+ assert.isTrue(Exit.isFailure(exit));
1516
+
1517
+ const failed = manager.getFailed();
1518
+ assert.strictEqual(failed.length, 1);
1519
+ assert.strictEqual(failed[0].id, SlowPlugin.meta.id);
1520
+ assert.strictEqual(failed[0].phase, 'activation');
1521
+ assert.strictEqual(failed[0].reason, 'timeout');
1522
+
1523
+ // Auto-disable runs in a forked fiber on the default runtime; wait for
1524
+ // the `enabled` atom to settle to the disabled state.
1525
+ yield* waitFor(registry, manager.enabled, (ids) => !ids.includes(SlowPlugin.meta.id));
1526
+ assert.deepStrictEqual(manager.getEnabled(), []);
1527
+ }),
1528
+ );
1529
+
1530
+ it.effect('records and auto-disables a lazy plugin whose loader exceeds the load timeout', () =>
1531
+ Effect.gen(function* () {
1532
+ const lazyMeta = { id: 'org.dxos.test.slow-load', name: 'Slow Load' };
1533
+ // The dynamic import never resolves; the manager's load timeout should
1534
+ // surface this as a `LazyPluginError` whose `cause` is `PluginTimeoutError`.
1535
+ const LazyTest = Plugin.lazy(lazyMeta, () => new Promise<{ default: Plugin.PluginFactory }>(() => {}));
1536
+ plugins = [LazyTest()];
1537
+
1538
+ const registry = Registry.make();
1539
+ const manager = PluginManager.make({
1540
+ pluginLoader,
1541
+ registry,
1542
+ loadTimeout: Duration.seconds(1),
1543
+ });
1544
+ yield* manager.add(lazyMeta.id);
1545
+
1546
+ const enableFiber = yield* Effect.fork(manager.enable(lazyMeta.id));
1547
+ yield* TestClock.adjust(Duration.seconds(2));
1548
+ const exit = yield* Fiber.await(enableFiber);
1549
+ assert.isTrue(Exit.isFailure(exit));
1550
+
1551
+ // The wrapped `LazyPluginError` carries the timeout error as its cause.
1552
+ if (Exit.isFailure(exit)) {
1553
+ const failure = Cause.failureOption(exit.cause);
1554
+ if (failure._tag === 'Some') {
1555
+ assert.isTrue(Plugin.LazyPluginError.is(failure.value));
1556
+ const lazyError = failure.value as Plugin.LazyPluginError;
1557
+ assert.isTrue(PluginManager.PluginTimeoutError.is(lazyError.cause as Error));
1558
+ }
1559
+ }
1560
+
1561
+ const failed = manager.getFailed();
1562
+ assert.strictEqual(failed.length, 1);
1563
+ assert.strictEqual(failed[0].id, lazyMeta.id);
1564
+ assert.strictEqual(failed[0].phase, 'load');
1565
+ assert.strictEqual(failed[0].reason, 'timeout');
1566
+
1567
+ // The plugin was added to `enabled` before the lazy resolution failed,
1568
+ // so the auto-disable fork should clear it.
1569
+ yield* waitFor(registry, manager.enabled, (ids) => !ids.includes(lazyMeta.id));
1570
+ }),
1571
+ );
1572
+
1573
+ it.effect('records non-timeout activation errors as reason: error', () =>
1574
+ Effect.gen(function* () {
1575
+ const FailingEvent = ActivationEvent.make('org.dxos.test.activation-error');
1576
+ const FailingPlugin = Plugin.define({ id: 'org.dxos.test.failing', name: 'Failing' }).pipe(
1577
+ Plugin.addModule({
1578
+ id: 'Boom',
1579
+ activatesOn: FailingEvent,
1580
+ activate: () => Effect.fail(new Error('boom')),
1581
+ }),
1582
+ Plugin.make,
1583
+ );
1584
+ plugins = [FailingPlugin()];
1585
+
1586
+ const registry = Registry.make();
1587
+ const manager = PluginManager.make({ pluginLoader, registry });
1588
+ yield* manager.add(FailingPlugin.meta.id);
1589
+ yield* manager.enable(FailingPlugin.meta.id);
1590
+
1591
+ const exit = yield* Effect.exit(manager.activate(FailingEvent));
1592
+ assert.isTrue(Exit.isFailure(exit));
1593
+
1594
+ const failed = manager.getFailed();
1595
+ assert.strictEqual(failed.length, 1);
1596
+ assert.strictEqual(failed[0].reason, 'error');
1597
+ assert.strictEqual(failed[0].error.message, 'boom');
1598
+
1599
+ yield* waitFor(registry, manager.enabled, (ids) => !ids.includes(FailingPlugin.meta.id));
1600
+ }),
1601
+ );
1602
+
1603
+ it.effect('does not auto-disable a core plugin even though the failure is recorded', () =>
1604
+ Effect.gen(function* () {
1605
+ const FailingEvent = ActivationEvent.make('org.dxos.test.core-fail');
1606
+ const CorePlugin = Plugin.define({ id: 'org.dxos.test.core', name: 'Core', tags: ['system'] }).pipe(
1607
+ Plugin.addModule({
1608
+ id: 'Boom',
1609
+ activatesOn: FailingEvent,
1610
+ activate: () => Effect.fail(new Error('boom')),
1611
+ }),
1612
+ Plugin.make,
1613
+ );
1614
+ const corePlugin = CorePlugin();
1615
+ plugins = [corePlugin];
1616
+
1617
+ const manager = PluginManager.make({
1618
+ pluginLoader,
1619
+ plugins: [corePlugin],
1620
+ });
1621
+ // Core is auto-enabled via the constructor's enable chain.
1622
+ const exit = yield* Effect.exit(manager.activate(FailingEvent));
1623
+ assert.isTrue(Exit.isFailure(exit));
1624
+
1625
+ assert.strictEqual(manager.getFailed().length, 1);
1626
+ // Core stays enabled; host opted into it being non-removable.
1627
+ assert.deepStrictEqual(manager.getEnabled(), [corePlugin.meta.id]);
1628
+ }),
1629
+ );
1630
+
1631
+ it.effect('clearFailure removes the failure record and re-enable starts fresh', () =>
1632
+ Effect.gen(function* () {
1633
+ let shouldFail = true;
1634
+ const Event = ActivationEvent.make('org.dxos.test.flaky');
1635
+ const FlakyPlugin = Plugin.define({ id: 'org.dxos.test.flaky', name: 'Flaky' }).pipe(
1636
+ Plugin.addModule({
1637
+ id: 'Maybe',
1638
+ activatesOn: Event,
1639
+ activate: () =>
1640
+ shouldFail
1641
+ ? Effect.fail(new Error('first try'))
1642
+ : Effect.succeed(Capability.contributes(String, { string: 'ok' })),
1643
+ }),
1644
+ Plugin.make,
1645
+ );
1646
+ const flakyPlugin = FlakyPlugin();
1647
+ plugins = [flakyPlugin];
1648
+
1649
+ const registry = Registry.make();
1650
+ const manager = PluginManager.make({ pluginLoader, registry });
1651
+ yield* manager.add(flakyPlugin.meta.id);
1652
+ yield* manager.enable(flakyPlugin.meta.id);
1653
+
1654
+ yield* Effect.exit(manager.activate(Event));
1655
+ assert.strictEqual(manager.getFailed().length, 1);
1656
+ yield* waitFor(registry, manager.enabled, (ids) => !ids.includes(flakyPlugin.meta.id));
1657
+
1658
+ // Calling `enable` again clears the prior failure record before
1659
+ // attempting resolution; verify the explicit API does too.
1660
+ assert.isTrue(manager.clearFailure(flakyPlugin.meta.id));
1661
+ assert.strictEqual(manager.getFailed().length, 0);
1662
+ assert.isFalse(manager.clearFailure(flakyPlugin.meta.id));
1663
+
1664
+ // Retry: enable + reset the activation event so the module re-runs.
1665
+ shouldFail = false;
1666
+ yield* manager.enable(flakyPlugin.meta.id);
1667
+ yield* manager.reset(Event);
1668
+ assert.strictEqual(manager.getFailed().length, 0);
1669
+ assert.strictEqual(manager.capabilities.getAll(String).length, 1);
1670
+ }),
1671
+ );
1672
+ });
1673
+
1674
+ describe('plugin dependencies (dependsOn)', () => {
1675
+ // Build a small plugin with a `dependsOn` chain. The helper keeps each test
1676
+ // focused on the dependency semantics rather than module wiring.
1677
+ const makePlugin = (id: string, dependsOn?: string[], tags?: string[]) =>
1678
+ Plugin.make(Plugin.define({ id, name: id, dependsOn, tags }))();
1679
+
1680
+ it.effect('enable resolves the transitive closure in dependency-first order', () =>
1681
+ Effect.gen(function* () {
1682
+ const a = makePlugin('a');
1683
+ const b = makePlugin('b', ['a']);
1684
+ const c = makePlugin('c', ['b']);
1685
+ const manager = PluginManager.make({ plugins: [a, b, c], pluginLoader });
1686
+
1687
+ const ok = yield* manager.enable('c');
1688
+ assert.isTrue(ok);
1689
+ // `a` enables first, then `b`, then `c`.
1690
+ assert.deepStrictEqual(manager.getEnabled(), ['a', 'b', 'c']);
1691
+ }),
1692
+ );
1693
+
1694
+ it.effect('enable is idempotent when dependencies are already enabled', () =>
1695
+ Effect.gen(function* () {
1696
+ const a = makePlugin('a');
1697
+ const b = makePlugin('b', ['a']);
1698
+ const manager = PluginManager.make({ plugins: [a, b], pluginLoader });
1699
+
1700
+ yield* manager.enable('a');
1701
+ yield* manager.enable('b');
1702
+ assert.deepStrictEqual(manager.getEnabled(), ['a', 'b']);
1703
+ // Re-enabling shouldn't duplicate entries.
1704
+ yield* manager.enable('b');
1705
+ assert.deepStrictEqual(manager.getEnabled(), ['a', 'b']);
1706
+ }),
1707
+ );
1708
+
1709
+ it.effect('enable with a missing declared dependency records a PluginDependencyError', () =>
1710
+ Effect.gen(function* () {
1711
+ const dependent = makePlugin('dependent', ['org.dxos.missing']);
1712
+ const manager = PluginManager.make({ plugins: [dependent], pluginLoader });
1713
+
1714
+ const ok = yield* manager.enable('dependent');
1715
+ assert.isFalse(ok);
1716
+ assert.deepStrictEqual(manager.getEnabled(), []);
1717
+ const failures = manager.getFailed();
1718
+ assert.strictEqual(failures.length, 1);
1719
+ assert.strictEqual(failures[0].id, 'dependent');
1720
+ assert.instanceOf(failures[0].error, Plugin.PluginDependencyError);
1721
+ }),
1722
+ );
1723
+
1724
+ it.effect('enable detects A↔B cycle and records a cycle failure', () =>
1725
+ Effect.gen(function* () {
1726
+ const a = makePlugin('a', ['b']);
1727
+ const b = makePlugin('b', ['a']);
1728
+ const manager = PluginManager.make({ plugins: [a, b], pluginLoader });
1729
+
1730
+ const ok = yield* manager.enable('a');
1731
+ assert.isFalse(ok);
1732
+ assert.deepStrictEqual(manager.getEnabled(), []);
1733
+ const failures = manager.getFailed();
1734
+ assert.strictEqual(failures.length, 1);
1735
+ assert.instanceOf(failures[0].error, Plugin.PluginDependencyError);
1736
+ }),
1737
+ );
1738
+
1739
+ it.effect('enable with resolveDependencies: false skips closure walk and missing-dep check', () =>
1740
+ Effect.gen(function* () {
1741
+ // Dependent declares a dep that is intentionally unregistered — the
1742
+ // caller has accepted responsibility for satisfying it some other way.
1743
+ const dependent = makePlugin('dependent', ['org.dxos.alt-impl']);
1744
+ const manager = PluginManager.make({ plugins: [dependent], pluginLoader });
1745
+
1746
+ const ok = yield* manager.enable('dependent', { resolveDependencies: false });
1747
+ assert.isTrue(ok);
1748
+ assert.deepStrictEqual(manager.getEnabled(), ['dependent']);
1749
+ assert.strictEqual(manager.getFailed().length, 0);
1750
+ }),
1751
+ );
1752
+
1753
+ it.effect('disable cascades to transitive dependents by default', () =>
1754
+ Effect.gen(function* () {
1755
+ const a = makePlugin('a');
1756
+ const b = makePlugin('b', ['a']);
1757
+ const c = makePlugin('c', ['b']);
1758
+ const manager = PluginManager.make({ plugins: [a, b, c], pluginLoader });
1759
+
1760
+ yield* manager.enable('c');
1761
+ assert.deepStrictEqual(manager.getEnabled(), ['a', 'b', 'c']);
1762
+
1763
+ const ok = yield* manager.disable('a');
1764
+ assert.isTrue(ok);
1765
+ // Cascade tears down `c` (leaf) and `b` before `a`.
1766
+ assert.deepStrictEqual(manager.getEnabled(), []);
1767
+ }),
1768
+ );
1769
+
1770
+ it.effect('default disable refuses when a transitive dependent is core', () =>
1771
+ Effect.gen(function* () {
1772
+ const lib = makePlugin('lib');
1773
+ const coreClient = makePlugin('coreClient', ['lib'], ['system']);
1774
+ const manager = PluginManager.make({
1775
+ plugins: [lib, coreClient],
1776
+ enabled: ['lib'],
1777
+ pluginLoader,
1778
+ });
1779
+
1780
+ const exit = yield* Effect.exit(manager.disable('lib'));
1781
+ assert.isTrue(Exit.isFailure(exit));
1782
+ // No state mutation when cascade is refused for a core dependent.
1783
+ assert.isTrue(manager.getEnabled().includes('lib'));
1784
+ assert.isTrue(manager.getEnabled().includes('coreClient'));
1785
+ }),
1786
+ );
1787
+
1788
+ it.effect('disable with cascade: false disables only the target', () =>
1789
+ Effect.gen(function* () {
1790
+ const a = makePlugin('a');
1791
+ const b = makePlugin('b', ['a']);
1792
+ const manager = PluginManager.make({ plugins: [a, b], pluginLoader });
1793
+
1794
+ yield* manager.enable('b');
1795
+ const ok = yield* manager.disable('a', { cascade: false });
1796
+ assert.isTrue(ok);
1797
+ // `b` is left enabled-but-broken (no `a` to satisfy its declared dep).
1798
+ assert.deepStrictEqual(manager.getEnabled(), ['b']);
1799
+ }),
1800
+ );
1801
+
1802
+ it.effect('getDependencies and getDependents reflect the declared graph', () =>
1803
+ Effect.gen(function* () {
1804
+ const a = makePlugin('a');
1805
+ const b = makePlugin('b', ['a']);
1806
+ const c = makePlugin('c', ['b']);
1807
+ const manager = PluginManager.make({ plugins: [a, b, c], pluginLoader });
1808
+
1809
+ assert.deepStrictEqual([...manager.getDependencies('c', { transitive: false })], ['b']);
1810
+ assert.deepStrictEqual([...manager.getDependencies('c', { transitive: true })], ['a', 'b']);
1811
+
1812
+ assert.deepStrictEqual([...manager.getDependents('a', { transitive: false })], ['b']);
1813
+ assert.deepStrictEqual([...manager.getDependents('a', { transitive: true })], ['c', 'b']);
1814
+
1815
+ yield* manager.enable('b');
1816
+ assert.deepStrictEqual([...manager.getDependents('a', { transitive: true, enabledOnly: true })], ['b']);
1817
+ }),
1818
+ );
1819
+
1820
+ it.effect('enable installs a catalog-only dependency via add() before enabling it', () =>
1821
+ Effect.gen(function* () {
1822
+ // The "remote" plugin is not pre-registered; the loader knows about
1823
+ // it, simulating a fetch from the registry.
1824
+ const remote = makePlugin('remote');
1825
+ const dependent = makePlugin('dependent', ['remote']);
1826
+ const remoteLoader = Effect.fn(function* (id: string) {
1827
+ if (id === 'remote') {
1828
+ return { plugin: remote };
1829
+ }
1830
+ throw new Error(`Unknown id: ${id}`);
1831
+ });
1832
+
1833
+ const registry = Registry.make();
1834
+ const manager = PluginManager.make({
1835
+ plugins: [dependent],
1836
+ pluginLoader: remoteLoader,
1837
+ registry,
1838
+ });
1839
+ // Seed the catalog with the remote entry so the dependency walk can
1840
+ // discover it.
1841
+ registry.set(manager.pluginRegistry.plugins, {
1842
+ entries: [
1843
+ {
1844
+ id: 'remote',
1845
+ name: 'Remote',
1846
+ moduleUrl: 'about:blank',
1847
+ repo: 'example/remote',
1848
+ version: 'v0.0.0',
1849
+ },
1850
+ ],
1851
+ loading: false,
1852
+ error: null,
1853
+ });
1854
+
1855
+ const ok = yield* manager.enable('dependent');
1856
+ assert.isTrue(ok);
1857
+ assert.deepStrictEqual(manager.getEnabled(), ['remote', 'dependent']);
1858
+ assert.isTrue(manager.getPlugins().some((plugin) => plugin.meta.id === 'remote'));
1859
+ }),
1860
+ );
1861
+
1862
+ it.effect('enable records install-failed when a catalog-only dep fails to load', () =>
1863
+ Effect.gen(function* () {
1864
+ const dependent = makePlugin('dependent', ['remote-broken']);
1865
+ const failingLoader = Effect.fn(function* (_id: string) {
1866
+ return yield* Effect.fail(new Error('fetch failed'));
1867
+ });
1868
+
1869
+ const registry = Registry.make();
1870
+ const manager = PluginManager.make({
1871
+ plugins: [dependent],
1872
+ pluginLoader: failingLoader,
1873
+ registry,
1874
+ });
1875
+ registry.set(manager.pluginRegistry.plugins, {
1876
+ entries: [
1877
+ {
1878
+ id: 'remote-broken',
1879
+ name: 'Broken',
1880
+ moduleUrl: 'about:blank',
1881
+ repo: 'example/broken',
1882
+ version: 'v0.0.0',
1883
+ },
1884
+ ],
1885
+ loading: false,
1886
+ error: null,
1887
+ });
1888
+
1889
+ const ok = yield* manager.enable('dependent');
1890
+ assert.isFalse(ok);
1891
+ assert.deepStrictEqual(manager.getEnabled(), []);
1892
+ const failures = manager.getFailed();
1893
+ assert.strictEqual(failures.length, 1);
1894
+ assert.strictEqual(failures[0].id, 'dependent');
1895
+ assert.instanceOf(failures[0].error, Plugin.PluginDependencyError);
1896
+ }),
1897
+ );
1898
+ });
1093
1899
  });