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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/browser/{capability-OP63CD5N.mjs → capability-HRFPP52W.mjs} +11 -11
  4. package/dist/lib/browser/capability-HRFPP52W.mjs.map +7 -0
  5. package/dist/lib/browser/{capability-BBBBAPDI.mjs → capability-VQ236TY5.mjs} +10 -10
  6. package/dist/lib/browser/{chunk-T3Y4AEKX.mjs → chunk-23D4SJUE.mjs} +3 -3
  7. package/dist/lib/browser/{chunk-T3Y4AEKX.mjs.map → chunk-23D4SJUE.mjs.map} +1 -1
  8. package/dist/lib/browser/{chunk-2CKCJ6PN.mjs → chunk-3JWJXGLK.mjs} +1 -1
  9. package/dist/lib/browser/{chunk-2CKCJ6PN.mjs.map → chunk-3JWJXGLK.mjs.map} +1 -1
  10. package/dist/lib/browser/{chunk-I34GF4NG.mjs → chunk-45CHLTBV.mjs} +2 -2
  11. package/dist/lib/browser/{chunk-QSXYHXCE.mjs → chunk-66IXTIVK.mjs} +1 -1
  12. package/dist/lib/browser/{chunk-QSXYHXCE.mjs.map → chunk-66IXTIVK.mjs.map} +2 -2
  13. package/dist/lib/browser/chunk-6GZYCP73.mjs +422 -0
  14. package/dist/lib/browser/chunk-6GZYCP73.mjs.map +7 -0
  15. package/dist/lib/browser/chunk-7VZJR2OA.mjs +581 -0
  16. package/dist/lib/browser/chunk-7VZJR2OA.mjs.map +7 -0
  17. package/dist/lib/browser/{chunk-TGX63LTL.mjs → chunk-FJ4765WW.mjs} +1 -1
  18. package/dist/lib/browser/{chunk-TGX63LTL.mjs.map → chunk-FJ4765WW.mjs.map} +2 -2
  19. package/dist/lib/browser/chunk-FO3IYSLV.mjs +68 -0
  20. package/dist/lib/browser/chunk-FO3IYSLV.mjs.map +7 -0
  21. package/dist/lib/browser/{chunk-FU4GAFUQ.mjs → chunk-G6Q2M2M5.mjs} +85 -15
  22. package/dist/lib/browser/chunk-G6Q2M2M5.mjs.map +7 -0
  23. package/dist/lib/browser/chunk-J43JDWAV.mjs +1445 -0
  24. package/dist/lib/browser/chunk-J43JDWAV.mjs.map +7 -0
  25. package/dist/lib/browser/{chunk-JKWMHZP6.mjs → chunk-WBHCSOBW.mjs} +2 -2
  26. package/dist/lib/browser/chunk-WBHCSOBW.mjs.map +7 -0
  27. package/dist/lib/browser/chunk-XLYULXAO.mjs +469 -0
  28. package/dist/lib/browser/chunk-XLYULXAO.mjs.map +7 -0
  29. package/dist/lib/browser/{chunk-F7FW2RK2.mjs → chunk-ZGJAZSNE.mjs} +7 -32
  30. package/dist/lib/browser/chunk-ZGJAZSNE.mjs.map +7 -0
  31. package/dist/lib/browser/cli/index.mjs +14 -28
  32. package/dist/lib/browser/cli/index.mjs.map +3 -3
  33. package/dist/lib/browser/common/activation-events.mjs +7 -7
  34. package/dist/lib/browser/common/capabilities.mjs +7 -7
  35. package/dist/lib/browser/core/activation-event.mjs +1 -1
  36. package/dist/lib/browser/core/capability.mjs +1 -1
  37. package/dist/lib/browser/core/plugin-manager.mjs +8 -4
  38. package/dist/lib/browser/core/plugin.mjs +12 -2
  39. package/dist/lib/browser/core/url-loader.mjs +17 -5
  40. package/dist/lib/browser/index.mjs +38 -21
  41. package/dist/lib/browser/index.mjs.map +3 -3
  42. package/dist/lib/browser/{invoker-capability-H5PPENOC.mjs → invoker-capability-CEL2L6GX.mjs} +12 -11
  43. package/dist/lib/browser/invoker-capability-CEL2L6GX.mjs.map +7 -0
  44. package/dist/lib/browser/meta.json +1 -1
  45. package/dist/lib/browser/testing/index.mjs +149 -28
  46. package/dist/lib/browser/testing/index.mjs.map +4 -4
  47. package/dist/lib/browser/testing/react.mjs +78 -0
  48. package/dist/lib/browser/testing/react.mjs.map +7 -0
  49. package/dist/lib/browser/ui/index.mjs +18 -14
  50. package/dist/lib/node-esm/{capability-AWBEMRYR.mjs → capability-GS3XJH74.mjs} +10 -10
  51. package/dist/lib/node-esm/{capability-WFEG6CIZ.mjs → capability-I5P7ACFL.mjs} +11 -11
  52. package/dist/lib/node-esm/capability-I5P7ACFL.mjs.map +7 -0
  53. package/dist/lib/node-esm/{chunk-FKE4Z3D6.mjs → chunk-37Z53PXZ.mjs} +1 -1
  54. package/dist/lib/node-esm/{chunk-FKE4Z3D6.mjs.map → chunk-37Z53PXZ.mjs.map} +2 -2
  55. package/dist/lib/node-esm/chunk-4RJL37GN.mjs +423 -0
  56. package/dist/lib/node-esm/chunk-4RJL37GN.mjs.map +7 -0
  57. package/dist/lib/node-esm/{chunk-WZCSOX5Q.mjs → chunk-6XW6LET6.mjs} +2 -2
  58. package/dist/lib/node-esm/{chunk-URWHJQT2.mjs → chunk-D347W3KO.mjs} +7 -32
  59. package/dist/lib/node-esm/chunk-D347W3KO.mjs.map +7 -0
  60. package/dist/lib/node-esm/chunk-DW45NFLW.mjs +1446 -0
  61. package/dist/lib/node-esm/chunk-DW45NFLW.mjs.map +7 -0
  62. package/dist/lib/node-esm/chunk-EI4LYXFY.mjs +470 -0
  63. package/dist/lib/node-esm/chunk-EI4LYXFY.mjs.map +7 -0
  64. package/dist/lib/node-esm/{chunk-ULUEXB7Q.mjs → chunk-KIFOQXD4.mjs} +85 -15
  65. package/dist/lib/node-esm/chunk-KIFOQXD4.mjs.map +7 -0
  66. package/dist/lib/node-esm/{chunk-VKHGNEDB.mjs → chunk-SBS2YMPT.mjs} +3 -3
  67. package/dist/lib/node-esm/{chunk-VKHGNEDB.mjs.map → chunk-SBS2YMPT.mjs.map} +1 -1
  68. package/dist/lib/node-esm/{chunk-42KBWDE4.mjs → chunk-SDJ4B2LU.mjs} +1 -1
  69. package/dist/lib/node-esm/{chunk-42KBWDE4.mjs.map → chunk-SDJ4B2LU.mjs.map} +1 -1
  70. package/dist/lib/node-esm/chunk-V24UWT36.mjs +582 -0
  71. package/dist/lib/node-esm/chunk-V24UWT36.mjs.map +7 -0
  72. package/dist/lib/node-esm/{chunk-G3RTFSNG.mjs → chunk-WFSRZKBP.mjs} +2 -2
  73. package/dist/lib/node-esm/chunk-WFSRZKBP.mjs.map +7 -0
  74. package/dist/lib/node-esm/chunk-WK7OIQKI.mjs +70 -0
  75. package/dist/lib/node-esm/chunk-WK7OIQKI.mjs.map +7 -0
  76. package/dist/lib/node-esm/{chunk-ZZ7CKK6W.mjs → chunk-XOCUANHO.mjs} +1 -1
  77. package/dist/lib/node-esm/{chunk-ZZ7CKK6W.mjs.map → chunk-XOCUANHO.mjs.map} +2 -2
  78. package/dist/lib/node-esm/cli/index.mjs +14 -28
  79. package/dist/lib/node-esm/cli/index.mjs.map +3 -3
  80. package/dist/lib/node-esm/common/activation-events.mjs +7 -7
  81. package/dist/lib/node-esm/common/capabilities.mjs +7 -7
  82. package/dist/lib/node-esm/core/activation-event.mjs +1 -1
  83. package/dist/lib/node-esm/core/capability.mjs +1 -1
  84. package/dist/lib/node-esm/core/plugin-manager.mjs +8 -4
  85. package/dist/lib/node-esm/core/plugin.mjs +12 -2
  86. package/dist/lib/node-esm/core/url-loader.mjs +17 -5
  87. package/dist/lib/node-esm/index.mjs +38 -21
  88. package/dist/lib/node-esm/index.mjs.map +3 -3
  89. package/dist/lib/node-esm/{invoker-capability-S3ZA527J.mjs → invoker-capability-QKUKN2MF.mjs} +12 -11
  90. package/dist/lib/node-esm/invoker-capability-QKUKN2MF.mjs.map +7 -0
  91. package/dist/lib/node-esm/meta.json +1 -1
  92. package/dist/lib/node-esm/testing/index.mjs +149 -28
  93. package/dist/lib/node-esm/testing/index.mjs.map +4 -4
  94. package/dist/lib/node-esm/testing/react.mjs +79 -0
  95. package/dist/lib/node-esm/testing/react.mjs.map +7 -0
  96. package/dist/lib/node-esm/ui/index.mjs +18 -14
  97. package/dist/plugin/node-esm/index.mjs +542 -33
  98. package/dist/plugin/node-esm/index.mjs.map +4 -4
  99. package/dist/plugin/node-esm/meta.json +1 -1
  100. package/dist/types/src/common/annotations.d.ts +1 -0
  101. package/dist/types/src/common/annotations.d.ts.map +1 -0
  102. package/dist/types/src/common/capabilities.d.ts +2 -1
  103. package/dist/types/src/common/capabilities.d.ts.map +1 -1
  104. package/dist/types/src/common/operations.d.ts +1 -1
  105. package/dist/types/src/common/operations.d.ts.map +1 -1
  106. package/dist/types/src/core/activation-event.d.ts +4 -4
  107. package/dist/types/src/core/activation-event.d.ts.map +1 -1
  108. package/dist/types/src/core/capability-manager.d.ts.map +1 -1
  109. package/dist/types/src/core/capability.d.ts +2 -2
  110. package/dist/types/src/core/capability.d.ts.map +1 -1
  111. package/dist/types/src/core/edge-registry-plugin-provider.d.ts +30 -0
  112. package/dist/types/src/core/edge-registry-plugin-provider.d.ts.map +1 -0
  113. package/dist/types/src/core/index.d.ts +5 -0
  114. package/dist/types/src/core/index.d.ts.map +1 -1
  115. package/dist/types/src/core/plugin-asset-cache.d.ts +71 -0
  116. package/dist/types/src/core/plugin-asset-cache.d.ts.map +1 -0
  117. package/dist/types/src/core/plugin-manager.d.ts +230 -5
  118. package/dist/types/src/core/plugin-manager.d.ts.map +1 -1
  119. package/dist/types/src/core/plugin-manifest.d.ts +101 -0
  120. package/dist/types/src/core/plugin-manifest.d.ts.map +1 -0
  121. package/dist/types/src/core/plugin-manifest.test.d.ts +2 -0
  122. package/dist/types/src/core/plugin-manifest.test.d.ts.map +1 -0
  123. package/dist/types/src/core/plugin.d.ts +164 -6
  124. package/dist/types/src/core/plugin.d.ts.map +1 -1
  125. package/dist/types/src/core/registry.d.ts +107 -0
  126. package/dist/types/src/core/registry.d.ts.map +1 -0
  127. package/dist/types/src/core/url-loader.d.ts +105 -3
  128. package/dist/types/src/core/url-loader.d.ts.map +1 -1
  129. package/dist/types/src/helpers.d.ts.map +1 -1
  130. package/dist/types/src/plugin-operation/history/capability.d.ts.map +1 -1
  131. package/dist/types/src/plugin-operation/history/errors.d.ts +6 -6
  132. package/dist/types/src/plugin-operation/history/errors.d.ts.map +1 -1
  133. package/dist/types/src/plugin-operation/history/history-tracker.d.ts +1 -1
  134. package/dist/types/src/plugin-operation/history/history-tracker.d.ts.map +1 -1
  135. package/dist/types/src/plugin-operation/history/types.d.ts +1 -1
  136. package/dist/types/src/plugin-operation/history/types.d.ts.map +1 -1
  137. package/dist/types/src/plugin-operation/history/undo-mapping.d.ts +1 -1
  138. package/dist/types/src/plugin-operation/history/undo-mapping.d.ts.map +1 -1
  139. package/dist/types/src/plugin-operation/history/undo-registry.d.ts +1 -1
  140. package/dist/types/src/plugin-operation/history/undo-registry.d.ts.map +1 -1
  141. package/dist/types/src/plugin-operation/invoker-capability.d.ts +1 -1
  142. package/dist/types/src/plugin-operation/invoker-capability.d.ts.map +1 -1
  143. package/dist/types/src/plugin-operation/meta.d.ts.map +1 -1
  144. package/dist/types/src/plugin-operation/testing.d.ts +2 -1
  145. package/dist/types/src/plugin-operation/testing.d.ts.map +1 -1
  146. package/dist/types/src/plugin-runtime/capability.d.ts +1 -1
  147. package/dist/types/src/plugin-runtime/capability.d.ts.map +1 -1
  148. package/dist/types/src/plugin-runtime/meta.d.ts.map +1 -1
  149. package/dist/types/src/testing/harness.d.ts +67 -0
  150. package/dist/types/src/testing/harness.d.ts.map +1 -0
  151. package/dist/types/src/testing/index.d.ts +1 -0
  152. package/dist/types/src/testing/index.d.ts.map +1 -1
  153. package/dist/types/src/testing/react.d.ts +27 -0
  154. package/dist/types/src/testing/react.d.ts.map +1 -0
  155. package/dist/types/src/testing/react.test.d.ts +2 -0
  156. package/dist/types/src/testing/react.test.d.ts.map +1 -0
  157. package/dist/types/src/testing/service.d.ts.map +1 -1
  158. package/dist/types/src/testing/withPluginManager.d.ts.map +1 -1
  159. package/dist/types/src/testing/withPluginManager.stories.d.ts.map +1 -1
  160. package/dist/types/src/ui/components/App/App.d.ts.map +1 -1
  161. package/dist/types/src/ui/components/App/App.stories.d.ts.map +1 -1
  162. package/dist/types/src/ui/components/Placeholder/Placeholder.d.ts +64 -0
  163. package/dist/types/src/ui/components/Placeholder/Placeholder.d.ts.map +1 -0
  164. package/dist/types/src/ui/components/Placeholder/Placeholder.stories.d.ts +19 -0
  165. package/dist/types/src/ui/components/Placeholder/Placeholder.stories.d.ts.map +1 -0
  166. package/dist/types/src/ui/components/Placeholder/index.d.ts +2 -0
  167. package/dist/types/src/ui/components/Placeholder/index.d.ts.map +1 -0
  168. package/dist/types/src/ui/components/PluginManager/PluginManagerContext.stories.d.ts.map +1 -1
  169. package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts +16 -4
  170. package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts.map +1 -1
  171. package/dist/types/src/ui/components/Surface/SurfaceComponent.stories.d.ts.map +1 -1
  172. package/dist/types/src/ui/components/Surface/SurfaceInfo.d.ts.map +1 -1
  173. package/dist/types/src/ui/components/Surface/SurfaceProfilerContext.d.ts.map +1 -1
  174. package/dist/types/src/ui/components/Surface/index.d.ts +16 -6
  175. package/dist/types/src/ui/components/Surface/index.d.ts.map +1 -1
  176. package/dist/types/src/ui/components/Surface/types.d.ts +110 -9
  177. package/dist/types/src/ui/components/Surface/types.d.ts.map +1 -1
  178. package/dist/types/src/ui/components/Surface/types.test.d.ts +2 -0
  179. package/dist/types/src/ui/components/Surface/types.test.d.ts.map +1 -0
  180. package/dist/types/src/ui/components/index.d.ts +1 -0
  181. package/dist/types/src/ui/components/index.d.ts.map +1 -1
  182. package/dist/types/src/ui/hooks/useApp.d.ts +30 -3
  183. package/dist/types/src/ui/hooks/useApp.d.ts.map +1 -1
  184. package/dist/types/src/ui/hooks/useCapabilities.d.ts.map +1 -1
  185. package/dist/types/src/ui/hooks/useLoading.d.ts.map +1 -1
  186. package/dist/types/src/ui/hooks/useSettingsState.d.ts.map +1 -1
  187. package/dist/types/src/vite-plugin/boot-loader/BootLoader.stories.d.ts +34 -0
  188. package/dist/types/src/vite-plugin/boot-loader/BootLoader.stories.d.ts.map +1 -0
  189. package/dist/types/src/vite-plugin/boot-loader/index.d.ts +2 -0
  190. package/dist/types/src/vite-plugin/boot-loader/index.d.ts.map +1 -0
  191. package/dist/types/src/vite-plugin/boot-loader/loader.d.ts +51 -0
  192. package/dist/types/src/vite-plugin/boot-loader/loader.d.ts.map +1 -0
  193. package/dist/types/src/vite-plugin/composer/index.d.ts +34 -0
  194. package/dist/types/src/vite-plugin/composer/index.d.ts.map +1 -0
  195. package/dist/types/src/vite-plugin/import-map/index.d.ts +28 -0
  196. package/dist/types/src/vite-plugin/import-map/index.d.ts.map +1 -0
  197. package/dist/types/src/vite-plugin/index.d.ts +4 -2
  198. package/dist/types/src/vite-plugin/index.d.ts.map +1 -1
  199. package/dist/types/src/vite-plugin/manifest.d.ts +41 -0
  200. package/dist/types/src/vite-plugin/manifest.d.ts.map +1 -0
  201. package/dist/types/src/vite-plugin/manifest.test.d.ts +2 -0
  202. package/dist/types/src/vite-plugin/manifest.test.d.ts.map +1 -0
  203. package/dist/types/src/vite-plugin/packages.d.ts +10 -4
  204. package/dist/types/src/vite-plugin/packages.d.ts.map +1 -1
  205. package/dist/types/tsconfig.tsbuildinfo +1 -1
  206. package/moon.yml +1 -0
  207. package/package.json +36 -60
  208. package/src/cli/cli.ts +1 -1
  209. package/src/common/annotations.ts +3 -0
  210. package/src/common/capabilities.ts +2 -1
  211. package/src/common/operations.ts +1 -1
  212. package/src/core/capability.ts +1 -1
  213. package/src/core/edge-registry-plugin-provider.ts +92 -0
  214. package/src/core/index.ts +5 -0
  215. package/src/core/plugin-asset-cache.ts +60 -0
  216. package/src/core/plugin-manager.test.ts +793 -7
  217. package/src/core/plugin-manager.ts +846 -44
  218. package/src/core/plugin-manifest.test.ts +75 -0
  219. package/src/core/plugin-manifest.ts +134 -0
  220. package/src/core/plugin.ts +170 -10
  221. package/src/core/registry.ts +163 -0
  222. package/src/core/url-loader.test.ts +148 -6
  223. package/src/core/url-loader.ts +284 -44
  224. package/src/plugin-operation/OperationPlugin.ts +2 -2
  225. package/src/plugin-operation/history/capability.ts +1 -1
  226. package/src/plugin-operation/history/history-tracker.test.ts +2 -1
  227. package/src/plugin-operation/history/history-tracker.ts +1 -1
  228. package/src/plugin-operation/history/types.ts +1 -1
  229. package/src/plugin-operation/history/undo-mapping.ts +1 -1
  230. package/src/plugin-operation/history/undo-registry.ts +1 -1
  231. package/src/plugin-operation/invoker-capability.ts +2 -1
  232. package/src/plugin-operation/meta.ts +1 -0
  233. package/src/plugin-operation/testing.ts +2 -1
  234. package/src/plugin-runtime/RuntimePlugin.ts +2 -2
  235. package/src/plugin-runtime/meta.ts +1 -0
  236. package/src/testing/harness.ts +229 -0
  237. package/src/testing/index.ts +1 -0
  238. package/src/testing/react.test.tsx +48 -0
  239. package/src/testing/react.tsx +113 -0
  240. package/src/testing/service.ts +1 -1
  241. package/src/testing/withPluginManager.stories.tsx +1 -1
  242. package/src/testing/withPluginManager.tsx +0 -2
  243. package/src/ui/components/App/App.stories.tsx +2 -6
  244. package/src/ui/components/App/App.tsx +25 -2
  245. package/src/ui/components/Placeholder/Placeholder.stories.tsx +77 -0
  246. package/src/ui/components/Placeholder/Placeholder.tsx +155 -0
  247. package/src/ui/components/Placeholder/index.ts +5 -0
  248. package/src/ui/components/PluginManager/PluginManagerContext.stories.tsx +4 -2
  249. package/src/ui/components/Surface/SurfaceComponent.stories.tsx +1 -1
  250. package/src/ui/components/Surface/SurfaceComponent.tsx +85 -48
  251. package/src/ui/components/Surface/SurfaceInfo.tsx +0 -1
  252. package/src/ui/components/Surface/index.ts +20 -1
  253. package/src/ui/components/Surface/types.test.ts +126 -0
  254. package/src/ui/components/Surface/types.ts +164 -12
  255. package/src/ui/components/index.ts +1 -0
  256. package/src/ui/hooks/useApp.test.tsx +1 -1
  257. package/src/ui/hooks/useApp.tsx +177 -42
  258. package/src/ui/hooks/useLoading.tsx +14 -6
  259. package/src/vite-plugin/boot-loader/BootLoader.stories.tsx +270 -0
  260. package/src/vite-plugin/boot-loader/boot-loader.css +321 -0
  261. package/src/vite-plugin/boot-loader/boot-loader.js +325 -0
  262. package/src/vite-plugin/boot-loader/index.ts +5 -0
  263. package/src/vite-plugin/boot-loader/loader.ts +123 -0
  264. package/src/vite-plugin/composer/index.ts +306 -0
  265. package/src/vite-plugin/import-map/index.ts +527 -0
  266. package/src/vite-plugin/index.ts +6 -2
  267. package/src/vite-plugin/manifest.test.ts +46 -0
  268. package/src/vite-plugin/manifest.ts +57 -0
  269. package/src/vite-plugin/packages.ts +168 -10
  270. package/tsconfig.json +16 -1
  271. package/.swc/plugins/linux_x86_64_19.0.0/727453fb3a62f7f1d952a41e051ca8a6f88cadc45cee43c6a4d1aa45f9b75665.wasmer-v7 +0 -0
  272. package/dist/lib/browser/capability-OP63CD5N.mjs.map +0 -7
  273. package/dist/lib/browser/chunk-F7FW2RK2.mjs.map +0 -7
  274. package/dist/lib/browser/chunk-FU4GAFUQ.mjs.map +0 -7
  275. package/dist/lib/browser/chunk-GX4TUNM6.mjs +0 -963
  276. package/dist/lib/browser/chunk-GX4TUNM6.mjs.map +0 -7
  277. package/dist/lib/browser/chunk-JKWMHZP6.mjs.map +0 -7
  278. package/dist/lib/browser/chunk-LVJW5EFU.mjs +0 -157
  279. package/dist/lib/browser/chunk-LVJW5EFU.mjs.map +0 -7
  280. package/dist/lib/browser/chunk-RFSO3JRG.mjs +0 -1
  281. package/dist/lib/browser/chunk-RFSO3JRG.mjs.map +0 -7
  282. package/dist/lib/browser/chunk-WPE6AL7I.mjs +0 -905
  283. package/dist/lib/browser/chunk-WPE6AL7I.mjs.map +0 -7
  284. package/dist/lib/browser/invoker-capability-H5PPENOC.mjs.map +0 -7
  285. package/dist/lib/node-esm/capability-WFEG6CIZ.mjs.map +0 -7
  286. package/dist/lib/node-esm/chunk-4A3ZCMI3.mjs +0 -158
  287. package/dist/lib/node-esm/chunk-4A3ZCMI3.mjs.map +0 -7
  288. package/dist/lib/node-esm/chunk-BCEOLX47.mjs +0 -964
  289. package/dist/lib/node-esm/chunk-BCEOLX47.mjs.map +0 -7
  290. package/dist/lib/node-esm/chunk-EL3R25OQ.mjs +0 -2
  291. package/dist/lib/node-esm/chunk-EL3R25OQ.mjs.map +0 -7
  292. package/dist/lib/node-esm/chunk-G3RTFSNG.mjs.map +0 -7
  293. package/dist/lib/node-esm/chunk-LQKOTNJW.mjs +0 -906
  294. package/dist/lib/node-esm/chunk-LQKOTNJW.mjs.map +0 -7
  295. package/dist/lib/node-esm/chunk-ULUEXB7Q.mjs.map +0 -7
  296. package/dist/lib/node-esm/chunk-URWHJQT2.mjs.map +0 -7
  297. package/dist/lib/node-esm/invoker-capability-S3ZA527J.mjs.map +0 -7
  298. package/dist/types/src/vite-plugin/composer-plugin.d.ts +0 -18
  299. package/dist/types/src/vite-plugin/composer-plugin.d.ts.map +0 -1
  300. package/dist/types/src/vite-plugin/import-map-plugin.d.ts +0 -16
  301. package/dist/types/src/vite-plugin/import-map-plugin.d.ts.map +0 -1
  302. package/src/vite-plugin/composer-plugin.ts +0 -128
  303. package/src/vite-plugin/import-map-plugin.ts +0 -314
  304. /package/dist/lib/browser/{capability-BBBBAPDI.mjs.map → capability-VQ236TY5.mjs.map} +0 -0
  305. /package/dist/lib/browser/{chunk-I34GF4NG.mjs.map → chunk-45CHLTBV.mjs.map} +0 -0
  306. /package/dist/lib/node-esm/{capability-AWBEMRYR.mjs.map → capability-GS3XJH74.mjs.map} +0 -0
  307. /package/dist/lib/node-esm/{chunk-WZCSOX5Q.mjs.map → chunk-6XW6LET6.mjs.map} +0 -0
@@ -61,7 +61,7 @@ describe('PluginManager', () => {
61
61
  const pluginLoader = Effect.fn(function* (id: string) {
62
62
  const plugin = plugins.find((plugin) => plugin.meta.id === id);
63
63
  invariant(plugin, `Plugin not found: ${id}`);
64
- return plugin;
64
+ return { plugin };
65
65
  });
66
66
 
67
67
  afterEach(() => {
@@ -76,9 +76,10 @@ describe('PluginManager', () => {
76
76
 
77
77
  const manager = PluginManager.make({ pluginLoader });
78
78
  const added = yield* manager.add(testMeta.id);
79
- assert.isTrue(added);
79
+ assert.strictEqual(added, testPlugin);
80
80
  assert.deepStrictEqual(manager.getPlugins(), [testPlugin]);
81
- const removed = manager.remove(testMeta.id);
81
+ assert.deepStrictEqual(manager.getEnabled(), []);
82
+ const removed = yield* manager.remove(testMeta.id);
82
83
  assert.isTrue(removed);
83
84
  assert.deepStrictEqual(manager.getPlugins(), []);
84
85
  }),
@@ -92,19 +93,121 @@ describe('PluginManager', () => {
92
93
  const urlLocator = 'https://example.com/plugin.mjs';
93
94
  const urlLoader = Effect.fn(function* (locator: string) {
94
95
  if (locator === urlLocator) {
95
- return testPlugin;
96
+ return { plugin: testPlugin };
96
97
  }
97
98
  return yield* Effect.fail(new Error(`Unknown locator: ${locator}`));
98
99
  });
99
100
 
100
101
  const manager = PluginManager.make({ pluginLoader: urlLoader });
101
102
  const added = yield* manager.add(urlLocator);
102
- assert.isTrue(added);
103
+ assert.strictEqual(added, testPlugin);
103
104
  assert.deepStrictEqual(manager.getPlugins(), [testPlugin]);
105
+ assert.deepStrictEqual(manager.getEnabled(), []);
106
+ yield* manager.enable(added.meta.id);
104
107
  assert.deepStrictEqual(manager.getEnabled(), [testMeta.id]);
105
108
  }),
106
109
  );
107
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
+
108
211
  it.effect('should support factory pattern with options', () =>
109
212
  Effect.gen(function* () {
110
213
  type TestPluginOptions = { count: number };
@@ -219,6 +322,7 @@ describe('PluginManager', () => {
219
322
 
220
323
  const manager = PluginManager.make({ pluginLoader });
221
324
  yield* manager.add(testMeta.id);
325
+ yield* manager.enable(testMeta.id);
222
326
  const error = yield* Effect.flip(manager.activate(FailEvent));
223
327
  assert.strictEqual(error.message, 'test');
224
328
  }),
@@ -251,6 +355,7 @@ describe('PluginManager', () => {
251
355
 
252
356
  const manager = PluginManager.make({ pluginLoader });
253
357
  yield* manager.add(testMeta.id);
358
+ yield* manager.enable(testMeta.id);
254
359
  const error = yield* Effect.flip(manager.activate(DefectEvent));
255
360
 
256
361
  // Verify the error was caught and propagated.
@@ -297,6 +402,7 @@ describe('PluginManager', () => {
297
402
 
298
403
  const manager = PluginManager.make({ pluginLoader });
299
404
  yield* manager.add(testMeta.id);
405
+ yield* manager.enable(testMeta.id);
300
406
  const error = yield* Effect.flip(manager.activate(DefectEvent));
301
407
 
302
408
  // Verify the error was caught and propagated.
@@ -361,6 +467,7 @@ describe('PluginManager', () => {
361
467
  );
362
468
 
363
469
  yield* manager.add(testMeta.id);
470
+ yield* manager.enable(testMeta.id);
364
471
  yield* manager.activate(ActivationEvents.Startup);
365
472
  yield* activating.await;
366
473
  yield* activated.await;
@@ -411,6 +518,7 @@ describe('PluginManager', () => {
411
518
 
412
519
  {
413
520
  yield* manager.add(testMeta.id);
521
+ yield* manager.enable(testMeta.id);
414
522
  const result = yield* manager.activate(ActivationEvents.Startup);
415
523
  assert.isTrue(result);
416
524
  assert.deepStrictEqual(manager.getActive(), [testPlugin.modules[0].id]);
@@ -475,16 +583,19 @@ describe('PluginManager', () => {
475
583
  assert.strictEqual(manager.capabilities.getAll(Number).length, 0);
476
584
 
477
585
  yield* manager.add(Plugin1.meta.id);
586
+ yield* manager.enable(Plugin1.meta.id);
478
587
  yield* manager.activate(CountEvent);
479
588
  assert.deepStrictEqual(manager.getActive(), [plugin1.modules[0].id]);
480
589
  assert.strictEqual(manager.capabilities.getAll(Number).length, 1);
481
590
 
482
591
  yield* manager.add(Plugin2.meta.id);
592
+ yield* manager.enable(Plugin2.meta.id);
483
593
  yield* manager.activate(CountEvent);
484
594
  assert.deepStrictEqual(manager.getActive(), [plugin1.modules[0].id, plugin2.modules[0].id]);
485
595
  assert.strictEqual(manager.capabilities.getAll(Number).length, 2);
486
596
 
487
597
  yield* manager.add(Plugin3.meta.id);
598
+ yield* manager.enable(Plugin3.meta.id);
488
599
  yield* manager.activate(CountEvent);
489
600
  assert.deepStrictEqual(manager.getActive(), [
490
601
  plugin1.modules[0].id,
@@ -515,6 +626,7 @@ describe('PluginManager', () => {
515
626
  assert.strictEqual(manager.capabilities.getAll(String).length, 0);
516
627
 
517
628
  yield* manager.add(testMeta.id);
629
+ yield* manager.enable(testMeta.id);
518
630
  yield* manager.activate(ActivationEvents.Startup);
519
631
  assert.deepStrictEqual(manager.getActive(), []);
520
632
  assert.strictEqual(manager.capabilities.getAll(String).length, 0);
@@ -548,6 +660,7 @@ describe('PluginManager', () => {
548
660
  assert.strictEqual(count, 0);
549
661
 
550
662
  yield* manager.add(testMeta.id);
663
+ yield* manager.enable(testMeta.id);
551
664
  yield* manager.activate(CountEvent);
552
665
  assert.deepStrictEqual(manager.getActive(), [testPlugin.modules[0].id]);
553
666
  assert.strictEqual(manager.capabilities.getAll(String).length, 1);
@@ -572,7 +685,7 @@ describe('PluginManager', () => {
572
685
  Plugin.addModule({
573
686
  id: 'Count',
574
687
  activatesOn: ActivationEvents.Startup,
575
- activatesBefore: [CountEvent],
688
+ firesBeforeActivation: [CountEvent],
576
689
  activate: Effect.fnUntraced(function* () {
577
690
  const capabilityManager = yield* Capability.Service;
578
691
  computeTotal(capabilityManager);
@@ -607,7 +720,9 @@ describe('PluginManager', () => {
607
720
  const manager = PluginManager.make({ pluginLoader });
608
721
  {
609
722
  yield* manager.add(Test.meta.id);
723
+ yield* manager.enable(Test.meta.id);
610
724
  yield* manager.add(Count.meta.id);
725
+ yield* manager.enable(Count.meta.id);
611
726
  yield* manager.activate(ActivationEvents.Startup);
612
727
  assert.deepStrictEqual(manager.getActive(), [
613
728
  ...testPlugin.modules.map((m) => m.id),
@@ -690,6 +805,7 @@ describe('PluginManager', () => {
690
805
  assert.strictEqual(pendingResetUpdates.count, 0);
691
806
 
692
807
  yield* manager.add(Plugin1.meta.id);
808
+ yield* manager.enable(Plugin1.meta.id);
693
809
  assert.strictEqual(pluginUpdates.count, 1);
694
810
  assert.strictEqual(enabledUpdates.count, 1);
695
811
  assert.strictEqual(modulesUpdates.count, 1);
@@ -706,6 +822,7 @@ describe('PluginManager', () => {
706
822
  assert.strictEqual(pendingResetUpdates.count, 0);
707
823
 
708
824
  yield* manager.add(Plugin2.meta.id);
825
+ yield* manager.enable(Plugin2.meta.id);
709
826
  assert.strictEqual(pluginUpdates.count, 2);
710
827
  assert.strictEqual(enabledUpdates.count, 2);
711
828
  assert.strictEqual(modulesUpdates.count, 2);
@@ -722,6 +839,7 @@ describe('PluginManager', () => {
722
839
  assert.strictEqual(pendingResetUpdates.count, 2);
723
840
 
724
841
  yield* manager.add(Plugin3.meta.id);
842
+ yield* manager.enable(Plugin3.meta.id);
725
843
  assert.strictEqual(pluginUpdates.count, 3);
726
844
  assert.strictEqual(enabledUpdates.count, 3);
727
845
  assert.strictEqual(modulesUpdates.count, 3);
@@ -746,7 +864,7 @@ describe('PluginManager', () => {
746
864
  assert.strictEqual(eventsFiredUpdates.count, 1);
747
865
  assert.strictEqual(pendingResetUpdates.count, 4);
748
866
 
749
- manager.remove(Plugin1.meta.id);
867
+ yield* manager.remove(Plugin1.meta.id);
750
868
  assert.strictEqual(pluginUpdates.count, 4);
751
869
  assert.strictEqual(enabledUpdates.count, 4);
752
870
  assert.strictEqual(modulesUpdates.count, 4);
@@ -793,6 +911,7 @@ describe('PluginManager', () => {
793
911
 
794
912
  const manager = PluginManager.make({ pluginLoader });
795
913
  yield* manager.add(SlowPlugin.meta.id);
914
+ yield* manager.enable(SlowPlugin.meta.id);
796
915
 
797
916
  // Fork the activation so we can control time with TestClock.
798
917
  const activationFiber = yield* Effect.fork(manager.activate(SlowEvent));
@@ -841,6 +960,7 @@ describe('PluginManager', () => {
841
960
 
842
961
  const manager = PluginManager.make({ pluginLoader });
843
962
  yield* manager.add(ConcurrentPlugin.meta.id);
963
+ yield* manager.enable(ConcurrentPlugin.meta.id);
844
964
 
845
965
  // Fork two concurrent activations with DIFFERENT events.
846
966
  // Both events trigger the same module, so both will try to call _loadModule.
@@ -889,7 +1009,9 @@ describe('PluginManager', () => {
889
1009
 
890
1010
  const manager = PluginManager.make({ pluginLoader });
891
1011
  yield* manager.add(Plugin1.meta.id);
1012
+ yield* manager.enable(Plugin1.meta.id);
892
1013
  yield* manager.add(Plugin2.meta.id);
1014
+ yield* manager.enable(Plugin2.meta.id);
893
1015
  yield* manager.activate(ActivationEvents.Startup);
894
1016
  assert.strictEqual(manager.getActive().length, 2);
895
1017
  assert.strictEqual(manager.capabilities.getAll(String).length, 1);
@@ -926,6 +1048,7 @@ describe('PluginManager', () => {
926
1048
 
927
1049
  const manager = PluginManager.make({ pluginLoader });
928
1050
  yield* manager.add(testMeta.id);
1051
+ yield* manager.enable(testMeta.id);
929
1052
  yield* manager.activate(ActivationEvents.Startup);
930
1053
  assert.isFalse(deactivated);
931
1054
 
@@ -971,7 +1094,9 @@ describe('PluginManager', () => {
971
1094
 
972
1095
  const manager = PluginManager.make({ pluginLoader });
973
1096
  yield* manager.add(Plugin1.meta.id);
1097
+ yield* manager.enable(Plugin1.meta.id);
974
1098
  yield* manager.add(Plugin2.meta.id);
1099
+ yield* manager.enable(Plugin2.meta.id);
975
1100
  yield* manager.activate(ActivationEvents.Startup);
976
1101
 
977
1102
  yield* manager.shutdown();
@@ -994,6 +1119,7 @@ describe('PluginManager', () => {
994
1119
 
995
1120
  const manager = PluginManager.make({ pluginLoader });
996
1121
  yield* manager.add(testMeta.id);
1122
+ yield* manager.enable(testMeta.id);
997
1123
  yield* manager.activate(ActivationEvents.Startup);
998
1124
  assert.isTrue(manager.getEventsFired().length > 0);
999
1125
 
@@ -1026,6 +1152,7 @@ describe('PluginManager', () => {
1026
1152
 
1027
1153
  const manager = PluginManager.make({ pluginLoader });
1028
1154
  yield* manager.add(testMeta.id);
1155
+ yield* manager.enable(testMeta.id);
1029
1156
 
1030
1157
  const activationFiber = yield* Effect.fork(manager.activate(ActivationEvents.Startup));
1031
1158
  yield* activationStarted.await;
@@ -1062,6 +1189,7 @@ describe('PluginManager', () => {
1062
1189
 
1063
1190
  const manager = PluginManager.make({ pluginLoader });
1064
1191
  yield* manager.add(testMeta.id);
1192
+ yield* manager.enable(testMeta.id);
1065
1193
  yield* manager.activate(ActivationEvents.Startup);
1066
1194
 
1067
1195
  const pluginsBefore = manager.getPlugins();
@@ -1097,6 +1225,7 @@ describe('PluginManager', () => {
1097
1225
 
1098
1226
  const manager = PluginManager.make({ pluginLoader });
1099
1227
  yield* manager.add(testMeta.id);
1228
+ yield* manager.enable(testMeta.id);
1100
1229
  yield* manager.activate(ActivationEvents.Startup);
1101
1230
  assert.strictEqual(activateCount, 1);
1102
1231
  assert.deepStrictEqual(manager.getActive(), [testPlugin.modules[0].id]);
@@ -1110,4 +1239,661 @@ describe('PluginManager', () => {
1110
1239
  assert.strictEqual(manager.capabilities.getAll(String).length, 1);
1111
1240
  }),
1112
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
+ const Real = (() => {
1404
+ const inner = Plugin.make(
1405
+ Plugin.define<void>(lazyMeta).pipe(
1406
+ Plugin.addModule({
1407
+ id: 'Hello',
1408
+ activatesOn: ActivationEvents.Startup,
1409
+ activate: () => Effect.succeed(Capability.contributes(String, { string: 'hello' })),
1410
+ }),
1411
+ ),
1412
+ );
1413
+ const factory = (() => {
1414
+ factoryCalls++;
1415
+ return inner();
1416
+ }) as Plugin.PluginFactory;
1417
+ return Object.assign(factory, { meta: lazyMeta });
1418
+ })();
1419
+ const LazyTest = Plugin.lazy(lazyMeta, () => Promise.resolve({ default: Real }));
1420
+ const lazyStub = LazyTest();
1421
+ // `manager.enable(id)` is implicitly called twice — once from the
1422
+ // constructor's core/enabled chain, once from our explicit call. With
1423
+ // coalescing, the underlying factory should still run exactly once.
1424
+ plugins = [lazyStub];
1425
+ const manager = PluginManager.make({ pluginLoader, plugins, core: [lazyMeta.id] });
1426
+ yield* manager.enable(lazyMeta.id);
1427
+ assert.strictEqual(factoryCalls, 1);
1428
+ }),
1429
+ );
1430
+
1431
+ it.effect('fails with a tagged error when the factory output is not a Plugin', () =>
1432
+ Effect.gen(function* () {
1433
+ const BadFactory = Object.assign(() => ({ not: 'a plugin' }) as any, { meta: lazyMeta });
1434
+ const LazyTest = Plugin.lazy(lazyMeta, () => Promise.resolve({ default: BadFactory }));
1435
+ const lazyStub = LazyTest();
1436
+ plugins = [lazyStub];
1437
+
1438
+ const manager = PluginManager.make({ pluginLoader });
1439
+ yield* manager.add(lazyMeta.id);
1440
+
1441
+ const exit = yield* Effect.exit(manager.enable(lazyMeta.id));
1442
+ assert.isTrue(Exit.isFailure(exit));
1443
+ if (Exit.isFailure(exit)) {
1444
+ const failure = Cause.failureOption(exit.cause);
1445
+ assert.isTrue(failure._tag === 'Some');
1446
+ if (failure._tag === 'Some') {
1447
+ assert.isTrue(Plugin.LazyPluginError.is(failure.value));
1448
+ assert.strictEqual((failure.value as Plugin.LazyPluginError).context.reason, 'invalid-plugin');
1449
+ }
1450
+ }
1451
+ }),
1452
+ );
1453
+ });
1454
+
1455
+ describe('timeouts and failure tracking', () => {
1456
+ // Atom subscriptions fire synchronously when the registry's `_set` runs,
1457
+ // even from a forked fiber on the default runtime. Wrapping in
1458
+ // `Effect.async` lets a TestClock-driven test wait for state produced by
1459
+ // a background `_runForkedFiber` (e.g. the auto-disable triggered when a
1460
+ // module activation times out) without relying on real-time `sleep`.
1461
+ const waitFor = <T>(registry: Registry.Registry, atom: Atom.Atom<T>, predicate: (value: T) => boolean) =>
1462
+ Effect.async<void>((resume) => {
1463
+ if (predicate(registry.get(atom))) {
1464
+ resume(Effect.void);
1465
+ return;
1466
+ }
1467
+ let resolved = false;
1468
+ const dispose = registry.subscribe(atom, () => {
1469
+ if (!resolved && predicate(registry.get(atom))) {
1470
+ resolved = true;
1471
+ dispose();
1472
+ resume(Effect.void);
1473
+ }
1474
+ });
1475
+ return Effect.sync(() => {
1476
+ if (!resolved) {
1477
+ dispose();
1478
+ }
1479
+ });
1480
+ });
1481
+
1482
+ it.effect('records and auto-disables a plugin whose module exceeds the activation timeout', () =>
1483
+ Effect.gen(function* () {
1484
+ const SlowEvent = ActivationEvent.make('org.dxos.test.activation-timeout');
1485
+ const SlowPlugin = Plugin.define({ id: 'org.dxos.test.slow-activation', name: 'Slow Activation' }).pipe(
1486
+ Plugin.addModule({
1487
+ id: 'Slow',
1488
+ activatesOn: SlowEvent,
1489
+ activate: Effect.fnUntraced(function* () {
1490
+ yield* Effect.sleep(Duration.seconds(60));
1491
+ return Capability.contributes(String, { string: 'never' });
1492
+ }),
1493
+ }),
1494
+ Plugin.make,
1495
+ );
1496
+ plugins = [SlowPlugin()];
1497
+
1498
+ const registry = Registry.make();
1499
+ const manager = PluginManager.make({
1500
+ pluginLoader,
1501
+ registry,
1502
+ activationTimeout: Duration.seconds(2),
1503
+ });
1504
+ yield* manager.add(SlowPlugin.meta.id);
1505
+ yield* manager.enable(SlowPlugin.meta.id);
1506
+
1507
+ const fiber = yield* Effect.fork(manager.activate(SlowEvent));
1508
+ // Push past the 2s activation timeout. The forked module fiber is on
1509
+ // TestClock too, so the timeout fires deterministically.
1510
+ yield* TestClock.adjust(Duration.seconds(3));
1511
+ const exit = yield* Fiber.await(fiber);
1512
+ assert.isTrue(Exit.isFailure(exit));
1513
+
1514
+ const failed = manager.getFailed();
1515
+ assert.strictEqual(failed.length, 1);
1516
+ assert.strictEqual(failed[0].id, SlowPlugin.meta.id);
1517
+ assert.strictEqual(failed[0].phase, 'activation');
1518
+ assert.strictEqual(failed[0].reason, 'timeout');
1519
+
1520
+ // Auto-disable runs in a forked fiber on the default runtime; wait for
1521
+ // the `enabled` atom to settle to the disabled state.
1522
+ yield* waitFor(registry, manager.enabled, (ids) => !ids.includes(SlowPlugin.meta.id));
1523
+ assert.deepStrictEqual(manager.getEnabled(), []);
1524
+ }),
1525
+ );
1526
+
1527
+ it.effect('records and auto-disables a lazy plugin whose loader exceeds the load timeout', () =>
1528
+ Effect.gen(function* () {
1529
+ const lazyMeta = { id: 'org.dxos.test.slow-load', name: 'Slow Load' };
1530
+ // The dynamic import never resolves; the manager's load timeout should
1531
+ // surface this as a `LazyPluginError` whose `cause` is `PluginTimeoutError`.
1532
+ const LazyTest = Plugin.lazy(lazyMeta, () => new Promise<{ default: Plugin.PluginFactory }>(() => {}));
1533
+ plugins = [LazyTest()];
1534
+
1535
+ const registry = Registry.make();
1536
+ const manager = PluginManager.make({
1537
+ pluginLoader,
1538
+ registry,
1539
+ loadTimeout: Duration.seconds(1),
1540
+ });
1541
+ yield* manager.add(lazyMeta.id);
1542
+
1543
+ const enableFiber = yield* Effect.fork(manager.enable(lazyMeta.id));
1544
+ yield* TestClock.adjust(Duration.seconds(2));
1545
+ const exit = yield* Fiber.await(enableFiber);
1546
+ assert.isTrue(Exit.isFailure(exit));
1547
+
1548
+ // The wrapped `LazyPluginError` carries the timeout error as its cause.
1549
+ if (Exit.isFailure(exit)) {
1550
+ const failure = Cause.failureOption(exit.cause);
1551
+ if (failure._tag === 'Some') {
1552
+ assert.isTrue(Plugin.LazyPluginError.is(failure.value));
1553
+ const lazyError = failure.value as Plugin.LazyPluginError;
1554
+ assert.isTrue(PluginManager.PluginTimeoutError.is(lazyError.cause as Error));
1555
+ }
1556
+ }
1557
+
1558
+ const failed = manager.getFailed();
1559
+ assert.strictEqual(failed.length, 1);
1560
+ assert.strictEqual(failed[0].id, lazyMeta.id);
1561
+ assert.strictEqual(failed[0].phase, 'load');
1562
+ assert.strictEqual(failed[0].reason, 'timeout');
1563
+
1564
+ // The plugin was added to `enabled` before the lazy resolution failed,
1565
+ // so the auto-disable fork should clear it.
1566
+ yield* waitFor(registry, manager.enabled, (ids) => !ids.includes(lazyMeta.id));
1567
+ }),
1568
+ );
1569
+
1570
+ it.effect('records non-timeout activation errors as reason: error', () =>
1571
+ Effect.gen(function* () {
1572
+ const FailingEvent = ActivationEvent.make('org.dxos.test.activation-error');
1573
+ const FailingPlugin = Plugin.define({ id: 'org.dxos.test.failing', name: 'Failing' }).pipe(
1574
+ Plugin.addModule({
1575
+ id: 'Boom',
1576
+ activatesOn: FailingEvent,
1577
+ activate: () => Effect.fail(new Error('boom')),
1578
+ }),
1579
+ Plugin.make,
1580
+ );
1581
+ plugins = [FailingPlugin()];
1582
+
1583
+ const registry = Registry.make();
1584
+ const manager = PluginManager.make({ pluginLoader, registry });
1585
+ yield* manager.add(FailingPlugin.meta.id);
1586
+ yield* manager.enable(FailingPlugin.meta.id);
1587
+
1588
+ const exit = yield* Effect.exit(manager.activate(FailingEvent));
1589
+ assert.isTrue(Exit.isFailure(exit));
1590
+
1591
+ const failed = manager.getFailed();
1592
+ assert.strictEqual(failed.length, 1);
1593
+ assert.strictEqual(failed[0].reason, 'error');
1594
+ assert.strictEqual(failed[0].error.message, 'boom');
1595
+
1596
+ yield* waitFor(registry, manager.enabled, (ids) => !ids.includes(FailingPlugin.meta.id));
1597
+ }),
1598
+ );
1599
+
1600
+ it.effect('does not auto-disable a core plugin even though the failure is recorded', () =>
1601
+ Effect.gen(function* () {
1602
+ const FailingEvent = ActivationEvent.make('org.dxos.test.core-fail');
1603
+ const CorePlugin = Plugin.define({ id: 'org.dxos.test.core', name: 'Core' }).pipe(
1604
+ Plugin.addModule({
1605
+ id: 'Boom',
1606
+ activatesOn: FailingEvent,
1607
+ activate: () => Effect.fail(new Error('boom')),
1608
+ }),
1609
+ Plugin.make,
1610
+ );
1611
+ const corePlugin = CorePlugin();
1612
+ plugins = [corePlugin];
1613
+
1614
+ const manager = PluginManager.make({
1615
+ pluginLoader,
1616
+ plugins: [corePlugin],
1617
+ core: [corePlugin.meta.id],
1618
+ });
1619
+ // Core is auto-enabled via the constructor's enable chain.
1620
+ const exit = yield* Effect.exit(manager.activate(FailingEvent));
1621
+ assert.isTrue(Exit.isFailure(exit));
1622
+
1623
+ assert.strictEqual(manager.getFailed().length, 1);
1624
+ // Core stays enabled; host opted into it being non-removable.
1625
+ assert.deepStrictEqual(manager.getEnabled(), [corePlugin.meta.id]);
1626
+ }),
1627
+ );
1628
+
1629
+ it.effect('clearFailure removes the failure record and re-enable starts fresh', () =>
1630
+ Effect.gen(function* () {
1631
+ let shouldFail = true;
1632
+ const Event = ActivationEvent.make('org.dxos.test.flaky');
1633
+ const FlakyPlugin = Plugin.define({ id: 'org.dxos.test.flaky', name: 'Flaky' }).pipe(
1634
+ Plugin.addModule({
1635
+ id: 'Maybe',
1636
+ activatesOn: Event,
1637
+ activate: () =>
1638
+ shouldFail
1639
+ ? Effect.fail(new Error('first try'))
1640
+ : Effect.succeed(Capability.contributes(String, { string: 'ok' })),
1641
+ }),
1642
+ Plugin.make,
1643
+ );
1644
+ const flakyPlugin = FlakyPlugin();
1645
+ plugins = [flakyPlugin];
1646
+
1647
+ const registry = Registry.make();
1648
+ const manager = PluginManager.make({ pluginLoader, registry });
1649
+ yield* manager.add(flakyPlugin.meta.id);
1650
+ yield* manager.enable(flakyPlugin.meta.id);
1651
+
1652
+ yield* Effect.exit(manager.activate(Event));
1653
+ assert.strictEqual(manager.getFailed().length, 1);
1654
+ yield* waitFor(registry, manager.enabled, (ids) => !ids.includes(flakyPlugin.meta.id));
1655
+
1656
+ // Calling `enable` again clears the prior failure record before
1657
+ // attempting resolution; verify the explicit API does too.
1658
+ assert.isTrue(manager.clearFailure(flakyPlugin.meta.id));
1659
+ assert.strictEqual(manager.getFailed().length, 0);
1660
+ assert.isFalse(manager.clearFailure(flakyPlugin.meta.id));
1661
+
1662
+ // Retry: enable + reset the activation event so the module re-runs.
1663
+ shouldFail = false;
1664
+ yield* manager.enable(flakyPlugin.meta.id);
1665
+ yield* manager.reset(Event);
1666
+ assert.strictEqual(manager.getFailed().length, 0);
1667
+ assert.strictEqual(manager.capabilities.getAll(String).length, 1);
1668
+ }),
1669
+ );
1670
+ });
1671
+
1672
+ describe('plugin dependencies (dependsOn)', () => {
1673
+ // Build a small plugin with a `dependsOn` chain. The helper keeps each test
1674
+ // focused on the dependency semantics rather than module wiring.
1675
+ const makePlugin = (id: string, dependsOn?: string[]) => Plugin.make(Plugin.define({ id, name: id, dependsOn }))();
1676
+
1677
+ it.effect('enable resolves the transitive closure in dependency-first order', () =>
1678
+ Effect.gen(function* () {
1679
+ const a = makePlugin('a');
1680
+ const b = makePlugin('b', ['a']);
1681
+ const c = makePlugin('c', ['b']);
1682
+ const manager = PluginManager.make({ plugins: [a, b, c], core: [], pluginLoader });
1683
+
1684
+ const ok = yield* manager.enable('c');
1685
+ assert.isTrue(ok);
1686
+ // `a` enables first, then `b`, then `c`.
1687
+ assert.deepStrictEqual(manager.getEnabled(), ['a', 'b', 'c']);
1688
+ }),
1689
+ );
1690
+
1691
+ it.effect('enable is idempotent when dependencies are already enabled', () =>
1692
+ Effect.gen(function* () {
1693
+ const a = makePlugin('a');
1694
+ const b = makePlugin('b', ['a']);
1695
+ const manager = PluginManager.make({ plugins: [a, b], core: [], pluginLoader });
1696
+
1697
+ yield* manager.enable('a');
1698
+ yield* manager.enable('b');
1699
+ assert.deepStrictEqual(manager.getEnabled(), ['a', 'b']);
1700
+ // Re-enabling shouldn't duplicate entries.
1701
+ yield* manager.enable('b');
1702
+ assert.deepStrictEqual(manager.getEnabled(), ['a', 'b']);
1703
+ }),
1704
+ );
1705
+
1706
+ it.effect('enable with a missing declared dependency records a PluginDependencyError', () =>
1707
+ Effect.gen(function* () {
1708
+ const dependent = makePlugin('dependent', ['org.dxos.missing']);
1709
+ const manager = PluginManager.make({ plugins: [dependent], core: [], pluginLoader });
1710
+
1711
+ const ok = yield* manager.enable('dependent');
1712
+ assert.isFalse(ok);
1713
+ assert.deepStrictEqual(manager.getEnabled(), []);
1714
+ const failures = manager.getFailed();
1715
+ assert.strictEqual(failures.length, 1);
1716
+ assert.strictEqual(failures[0].id, 'dependent');
1717
+ assert.instanceOf(failures[0].error, Plugin.PluginDependencyError);
1718
+ }),
1719
+ );
1720
+
1721
+ it.effect('enable detects A↔B cycle and records a cycle failure', () =>
1722
+ Effect.gen(function* () {
1723
+ const a = makePlugin('a', ['b']);
1724
+ const b = makePlugin('b', ['a']);
1725
+ const manager = PluginManager.make({ plugins: [a, b], core: [], pluginLoader });
1726
+
1727
+ const ok = yield* manager.enable('a');
1728
+ assert.isFalse(ok);
1729
+ assert.deepStrictEqual(manager.getEnabled(), []);
1730
+ const failures = manager.getFailed();
1731
+ assert.strictEqual(failures.length, 1);
1732
+ assert.instanceOf(failures[0].error, Plugin.PluginDependencyError);
1733
+ }),
1734
+ );
1735
+
1736
+ it.effect('enable with resolveDependencies: false skips closure walk and missing-dep check', () =>
1737
+ Effect.gen(function* () {
1738
+ // Dependent declares a dep that is intentionally unregistered — the
1739
+ // caller has accepted responsibility for satisfying it some other way.
1740
+ const dependent = makePlugin('dependent', ['org.dxos.alt-impl']);
1741
+ const manager = PluginManager.make({ plugins: [dependent], core: [], pluginLoader });
1742
+
1743
+ const ok = yield* manager.enable('dependent', { resolveDependencies: false });
1744
+ assert.isTrue(ok);
1745
+ assert.deepStrictEqual(manager.getEnabled(), ['dependent']);
1746
+ assert.strictEqual(manager.getFailed().length, 0);
1747
+ }),
1748
+ );
1749
+
1750
+ it.effect('disable cascades to transitive dependents by default', () =>
1751
+ Effect.gen(function* () {
1752
+ const a = makePlugin('a');
1753
+ const b = makePlugin('b', ['a']);
1754
+ const c = makePlugin('c', ['b']);
1755
+ const manager = PluginManager.make({ plugins: [a, b, c], core: [], pluginLoader });
1756
+
1757
+ yield* manager.enable('c');
1758
+ assert.deepStrictEqual(manager.getEnabled(), ['a', 'b', 'c']);
1759
+
1760
+ const ok = yield* manager.disable('a');
1761
+ assert.isTrue(ok);
1762
+ // Cascade tears down `c` (leaf) and `b` before `a`.
1763
+ assert.deepStrictEqual(manager.getEnabled(), []);
1764
+ }),
1765
+ );
1766
+
1767
+ it.effect('default disable refuses when a transitive dependent is core', () =>
1768
+ Effect.gen(function* () {
1769
+ const lib = makePlugin('lib');
1770
+ const coreClient = makePlugin('coreClient', ['lib']);
1771
+ const manager = PluginManager.make({
1772
+ plugins: [lib, coreClient],
1773
+ core: ['coreClient'],
1774
+ enabled: ['lib'],
1775
+ pluginLoader,
1776
+ });
1777
+
1778
+ const exit = yield* Effect.exit(manager.disable('lib'));
1779
+ assert.isTrue(Exit.isFailure(exit));
1780
+ // No state mutation when cascade is refused for a core dependent.
1781
+ assert.isTrue(manager.getEnabled().includes('lib'));
1782
+ assert.isTrue(manager.getEnabled().includes('coreClient'));
1783
+ }),
1784
+ );
1785
+
1786
+ it.effect('disable with cascade: false disables only the target', () =>
1787
+ Effect.gen(function* () {
1788
+ const a = makePlugin('a');
1789
+ const b = makePlugin('b', ['a']);
1790
+ const manager = PluginManager.make({ plugins: [a, b], core: [], pluginLoader });
1791
+
1792
+ yield* manager.enable('b');
1793
+ const ok = yield* manager.disable('a', { cascade: false });
1794
+ assert.isTrue(ok);
1795
+ // `b` is left enabled-but-broken (no `a` to satisfy its declared dep).
1796
+ assert.deepStrictEqual(manager.getEnabled(), ['b']);
1797
+ }),
1798
+ );
1799
+
1800
+ it.effect('getDependencies and getDependents reflect the declared graph', () =>
1801
+ Effect.gen(function* () {
1802
+ const a = makePlugin('a');
1803
+ const b = makePlugin('b', ['a']);
1804
+ const c = makePlugin('c', ['b']);
1805
+ const manager = PluginManager.make({ plugins: [a, b, c], core: [], pluginLoader });
1806
+
1807
+ assert.deepStrictEqual([...manager.getDependencies('c', { transitive: false })], ['b']);
1808
+ assert.deepStrictEqual([...manager.getDependencies('c', { transitive: true })], ['a', 'b']);
1809
+
1810
+ assert.deepStrictEqual([...manager.getDependents('a', { transitive: false })], ['b']);
1811
+ assert.deepStrictEqual([...manager.getDependents('a', { transitive: true })], ['c', 'b']);
1812
+
1813
+ yield* manager.enable('b');
1814
+ assert.deepStrictEqual([...manager.getDependents('a', { transitive: true, enabledOnly: true })], ['b']);
1815
+ }),
1816
+ );
1817
+
1818
+ it.effect('enable installs a catalog-only dependency via add() before enabling it', () =>
1819
+ Effect.gen(function* () {
1820
+ // The "remote" plugin is not pre-registered; the loader knows about
1821
+ // it, simulating a fetch from the registry.
1822
+ const remote = makePlugin('remote');
1823
+ const dependent = makePlugin('dependent', ['remote']);
1824
+ const remoteLoader = Effect.fn(function* (id: string) {
1825
+ if (id === 'remote') {
1826
+ return { plugin: remote };
1827
+ }
1828
+ throw new Error(`Unknown id: ${id}`);
1829
+ });
1830
+
1831
+ const registry = Registry.make();
1832
+ const manager = PluginManager.make({
1833
+ plugins: [dependent],
1834
+ core: [],
1835
+ pluginLoader: remoteLoader,
1836
+ registry,
1837
+ });
1838
+ // Seed the catalog with the remote entry so the dependency walk can
1839
+ // discover it.
1840
+ registry.set(manager.pluginRegistry.plugins, {
1841
+ entries: [
1842
+ {
1843
+ id: 'remote',
1844
+ name: 'Remote',
1845
+ moduleUrl: 'about:blank',
1846
+ repo: 'example/remote',
1847
+ version: 'v0.0.0',
1848
+ },
1849
+ ],
1850
+ loading: false,
1851
+ error: null,
1852
+ });
1853
+
1854
+ const ok = yield* manager.enable('dependent');
1855
+ assert.isTrue(ok);
1856
+ assert.deepStrictEqual(manager.getEnabled(), ['remote', 'dependent']);
1857
+ assert.isTrue(manager.getPlugins().some((plugin) => plugin.meta.id === 'remote'));
1858
+ }),
1859
+ );
1860
+
1861
+ it.effect('enable records install-failed when a catalog-only dep fails to load', () =>
1862
+ Effect.gen(function* () {
1863
+ const dependent = makePlugin('dependent', ['remote-broken']);
1864
+ const failingLoader = Effect.fn(function* (_id: string) {
1865
+ return yield* Effect.fail(new Error('fetch failed'));
1866
+ });
1867
+
1868
+ const registry = Registry.make();
1869
+ const manager = PluginManager.make({
1870
+ plugins: [dependent],
1871
+ core: [],
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
+ });
1113
1899
  });