@dxos/app-framework 0.8.4-main.7ace549 → 0.8.4-main.8360d9e660

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 (482) hide show
  1. package/.storybook/main.mts +2 -4
  2. package/.storybook/preview.mts +2 -2
  3. package/dist/lib/browser/capability-5OFLR7J4.mjs +34 -0
  4. package/dist/lib/browser/capability-5OFLR7J4.mjs.map +7 -0
  5. package/dist/lib/browser/capability-ZHQDZRP5.mjs +37 -0
  6. package/dist/lib/browser/capability-ZHQDZRP5.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-272IPLHQ.mjs +47 -0
  8. package/dist/lib/browser/chunk-272IPLHQ.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-3VXJONTI.mjs +933 -0
  10. package/dist/lib/browser/chunk-3VXJONTI.mjs.map +7 -0
  11. package/dist/lib/browser/chunk-7AL6SKIV.mjs +728 -0
  12. package/dist/lib/browser/chunk-7AL6SKIV.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-GH3M2LIW.mjs +148 -0
  14. package/dist/lib/browser/chunk-GH3M2LIW.mjs.map +7 -0
  15. package/dist/lib/browser/chunk-J5LGTIGS.mjs +10 -0
  16. package/dist/lib/browser/chunk-JGWCBVKJ.mjs +170 -0
  17. package/dist/lib/browser/chunk-JGWCBVKJ.mjs.map +7 -0
  18. package/dist/lib/browser/chunk-M5IC326L.mjs +34 -0
  19. package/dist/lib/browser/chunk-M5IC326L.mjs.map +7 -0
  20. package/dist/lib/browser/chunk-TGX63LTL.mjs +8 -0
  21. package/dist/lib/browser/chunk-TGX63LTL.mjs.map +7 -0
  22. package/dist/lib/browser/chunk-TIEBZMTF.mjs +44 -0
  23. package/dist/lib/browser/chunk-TIEBZMTF.mjs.map +7 -0
  24. package/dist/lib/browser/chunk-WEBSGU5L.mjs +80 -0
  25. package/dist/lib/browser/chunk-WEBSGU5L.mjs.map +7 -0
  26. package/dist/lib/browser/chunk-YAFEA4GV.mjs +1 -0
  27. package/dist/lib/browser/chunk-YNFPIQGB.mjs +76 -0
  28. package/dist/lib/browser/chunk-YNFPIQGB.mjs.map +7 -0
  29. package/dist/lib/browser/cli/index.mjs +89 -0
  30. package/dist/lib/browser/cli/index.mjs.map +7 -0
  31. package/dist/lib/browser/common/activation-events.mjs +23 -0
  32. package/dist/lib/browser/common/capabilities.mjs +45 -0
  33. package/dist/lib/browser/core/activation-event.mjs +20 -0
  34. package/dist/lib/browser/core/activation-event.mjs.map +7 -0
  35. package/dist/lib/browser/core/capability.mjs +28 -0
  36. package/dist/lib/browser/core/capability.mjs.map +7 -0
  37. package/dist/lib/browser/core/plugin-manager.mjs +15 -0
  38. package/dist/lib/browser/core/plugin-manager.mjs.map +7 -0
  39. package/dist/lib/browser/core/plugin.mjs +29 -0
  40. package/dist/lib/browser/core/plugin.mjs.map +7 -0
  41. package/dist/lib/browser/index.mjs +88 -121
  42. package/dist/lib/browser/index.mjs.map +4 -4
  43. package/dist/lib/browser/invoker-capability-YTTQ2OBB.mjs +36 -0
  44. package/dist/lib/browser/invoker-capability-YTTQ2OBB.mjs.map +7 -0
  45. package/dist/lib/browser/meta.json +1 -1
  46. package/dist/lib/browser/testing/index.mjs +107 -40
  47. package/dist/lib/browser/testing/index.mjs.map +4 -4
  48. package/dist/lib/browser/ui/index.mjs +45 -0
  49. package/dist/lib/browser/ui/index.mjs.map +7 -0
  50. package/dist/lib/node-esm/capability-CRHZKL6T.mjs +35 -0
  51. package/dist/lib/node-esm/capability-CRHZKL6T.mjs.map +7 -0
  52. package/dist/lib/node-esm/capability-W5C5464H.mjs +38 -0
  53. package/dist/lib/node-esm/capability-W5C5464H.mjs.map +7 -0
  54. package/dist/lib/node-esm/chunk-3D66SZHP.mjs +171 -0
  55. package/dist/lib/node-esm/chunk-3D66SZHP.mjs.map +7 -0
  56. package/dist/lib/node-esm/chunk-6REV5DE7.mjs +48 -0
  57. package/dist/lib/node-esm/chunk-6REV5DE7.mjs.map +7 -0
  58. package/dist/lib/node-esm/chunk-CMDON4NG.mjs +934 -0
  59. package/dist/lib/node-esm/chunk-CMDON4NG.mjs.map +7 -0
  60. package/dist/lib/node-esm/chunk-FKE4Z3D6.mjs +10 -0
  61. package/dist/lib/node-esm/chunk-FKE4Z3D6.mjs.map +7 -0
  62. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +11 -0
  63. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +7 -0
  64. package/dist/lib/node-esm/chunk-I7FZT4A7.mjs +729 -0
  65. package/dist/lib/node-esm/chunk-I7FZT4A7.mjs.map +7 -0
  66. package/dist/lib/node-esm/chunk-LHCJGNXK.mjs +149 -0
  67. package/dist/lib/node-esm/chunk-LHCJGNXK.mjs.map +7 -0
  68. package/dist/lib/node-esm/chunk-NHXBSAQR.mjs +35 -0
  69. package/dist/lib/node-esm/chunk-NHXBSAQR.mjs.map +7 -0
  70. package/dist/lib/node-esm/chunk-SB5ODNPX.mjs +77 -0
  71. package/dist/lib/node-esm/chunk-SB5ODNPX.mjs.map +7 -0
  72. package/dist/lib/node-esm/chunk-SCDGIGGU.mjs +45 -0
  73. package/dist/lib/node-esm/chunk-SCDGIGGU.mjs.map +7 -0
  74. package/dist/lib/node-esm/chunk-SQICGJBW.mjs +81 -0
  75. package/dist/lib/node-esm/chunk-SQICGJBW.mjs.map +7 -0
  76. package/dist/lib/node-esm/chunk-Z4TJPSMP.mjs +2 -0
  77. package/dist/lib/node-esm/chunk-Z4TJPSMP.mjs.map +7 -0
  78. package/dist/lib/node-esm/cli/index.mjs +90 -0
  79. package/dist/lib/node-esm/cli/index.mjs.map +7 -0
  80. package/dist/lib/node-esm/common/activation-events.mjs +24 -0
  81. package/dist/lib/node-esm/common/activation-events.mjs.map +7 -0
  82. package/dist/lib/node-esm/common/capabilities.mjs +46 -0
  83. package/dist/lib/node-esm/common/capabilities.mjs.map +7 -0
  84. package/dist/lib/node-esm/core/activation-event.mjs +21 -0
  85. package/dist/lib/node-esm/core/activation-event.mjs.map +7 -0
  86. package/dist/lib/node-esm/core/capability.mjs +29 -0
  87. package/dist/lib/node-esm/core/capability.mjs.map +7 -0
  88. package/dist/lib/node-esm/core/plugin-manager.mjs +16 -0
  89. package/dist/lib/node-esm/core/plugin-manager.mjs.map +7 -0
  90. package/dist/lib/node-esm/core/plugin.mjs +30 -0
  91. package/dist/lib/node-esm/core/plugin.mjs.map +7 -0
  92. package/dist/lib/node-esm/index.mjs +88 -121
  93. package/dist/lib/node-esm/index.mjs.map +4 -4
  94. package/dist/lib/node-esm/invoker-capability-BU26474T.mjs +37 -0
  95. package/dist/lib/node-esm/invoker-capability-BU26474T.mjs.map +7 -0
  96. package/dist/lib/node-esm/meta.json +1 -1
  97. package/dist/lib/node-esm/testing/index.mjs +107 -40
  98. package/dist/lib/node-esm/testing/index.mjs.map +4 -4
  99. package/dist/lib/node-esm/ui/index.mjs +46 -0
  100. package/dist/lib/node-esm/ui/index.mjs.map +7 -0
  101. package/dist/types/src/cli/cli.d.ts +39 -0
  102. package/dist/types/src/cli/cli.d.ts.map +1 -0
  103. package/dist/types/src/cli/index.d.ts +2 -0
  104. package/dist/types/src/cli/index.d.ts.map +1 -0
  105. package/dist/types/src/common/activation-events.d.ts +27 -0
  106. package/dist/types/src/common/activation-events.d.ts.map +1 -0
  107. package/dist/types/src/common/capabilities.d.ts +114 -194
  108. package/dist/types/src/common/capabilities.d.ts.map +1 -1
  109. package/dist/types/src/common/index.d.ts +4 -8
  110. package/dist/types/src/common/index.d.ts.map +1 -1
  111. package/dist/types/src/common/operations.d.ts +33 -0
  112. package/dist/types/src/common/operations.d.ts.map +1 -0
  113. package/dist/types/src/common/translations.d.ts +7 -7
  114. package/dist/types/src/common/translations.d.ts.map +1 -1
  115. package/dist/types/src/context.d.ts +5 -0
  116. package/dist/types/src/context.d.ts.map +1 -0
  117. package/dist/types/src/core/{events.d.ts → activation-event.d.ts} +7 -7
  118. package/dist/types/src/core/activation-event.d.ts.map +1 -0
  119. package/dist/types/src/core/capability-manager.d.ts +43 -0
  120. package/dist/types/src/core/capability-manager.d.ts.map +1 -0
  121. package/dist/types/src/core/capability-manager.test.d.ts +2 -0
  122. package/dist/types/src/core/capability-manager.test.d.ts.map +1 -0
  123. package/dist/types/src/core/capability.d.ts +150 -0
  124. package/dist/types/src/core/capability.d.ts.map +1 -0
  125. package/dist/types/src/core/index.d.ts +5 -4
  126. package/dist/types/src/core/index.d.ts.map +1 -1
  127. package/dist/types/src/core/plugin-manager.d.ts +72 -0
  128. package/dist/types/src/core/plugin-manager.d.ts.map +1 -0
  129. package/dist/types/src/core/plugin-manager.test.d.ts +2 -0
  130. package/dist/types/src/core/plugin-manager.test.d.ts.map +1 -0
  131. package/dist/types/src/core/plugin.d.ts +98 -35
  132. package/dist/types/src/core/plugin.d.ts.map +1 -1
  133. package/dist/types/src/index.d.ts +3 -2
  134. package/dist/types/src/index.d.ts.map +1 -1
  135. package/dist/types/src/plugin-operation/OperationPlugin.d.ts +3 -0
  136. package/dist/types/src/plugin-operation/OperationPlugin.d.ts.map +1 -0
  137. package/dist/types/src/plugin-operation/history/capability.d.ts +7 -0
  138. package/dist/types/src/plugin-operation/history/capability.d.ts.map +1 -0
  139. package/dist/types/src/plugin-operation/history/errors.d.ts +5 -0
  140. package/dist/types/src/plugin-operation/history/errors.d.ts.map +1 -0
  141. package/dist/types/src/plugin-operation/history/history-tracker.d.ts +18 -0
  142. package/dist/types/src/plugin-operation/history/history-tracker.d.ts.map +1 -0
  143. package/dist/types/src/plugin-operation/history/history-tracker.test.d.ts +2 -0
  144. package/dist/types/src/plugin-operation/history/history-tracker.test.d.ts.map +1 -0
  145. package/dist/types/src/plugin-operation/history/index.d.ts +6 -0
  146. package/dist/types/src/plugin-operation/history/index.d.ts.map +1 -0
  147. package/dist/types/src/plugin-operation/history/types.d.ts +13 -0
  148. package/dist/types/src/plugin-operation/history/types.d.ts.map +1 -0
  149. package/dist/types/src/plugin-operation/history/undo-mapping.d.ts +101 -0
  150. package/dist/types/src/plugin-operation/history/undo-mapping.d.ts.map +1 -0
  151. package/dist/types/src/plugin-operation/history/undo-registry.d.ts +23 -0
  152. package/dist/types/src/plugin-operation/history/undo-registry.d.ts.map +1 -0
  153. package/dist/types/src/plugin-operation/history/undo-registry.test.d.ts +2 -0
  154. package/dist/types/src/plugin-operation/history/undo-registry.test.d.ts.map +1 -0
  155. package/dist/types/src/plugin-operation/index.d.ts +3 -0
  156. package/dist/types/src/plugin-operation/index.d.ts.map +1 -0
  157. package/dist/types/src/plugin-operation/invoker-capability.d.ts +6 -0
  158. package/dist/types/src/plugin-operation/invoker-capability.d.ts.map +1 -0
  159. package/dist/types/src/plugin-operation/meta.d.ts +3 -0
  160. package/dist/types/src/plugin-operation/meta.d.ts.map +1 -0
  161. package/dist/types/src/plugin-operation/testing.d.ts +109 -0
  162. package/dist/types/src/plugin-operation/testing.d.ts.map +1 -0
  163. package/dist/types/src/plugin-runtime/RuntimePlugin.d.ts +3 -0
  164. package/dist/types/src/plugin-runtime/RuntimePlugin.d.ts.map +1 -0
  165. package/dist/types/src/plugin-runtime/capability.d.ts +6 -0
  166. package/dist/types/src/plugin-runtime/capability.d.ts.map +1 -0
  167. package/dist/types/src/plugin-runtime/index.d.ts +2 -0
  168. package/dist/types/src/plugin-runtime/index.d.ts.map +1 -0
  169. package/dist/types/src/plugin-runtime/meta.d.ts +3 -0
  170. package/dist/types/src/plugin-runtime/meta.d.ts.map +1 -0
  171. package/dist/types/src/testing/index.d.ts +1 -0
  172. package/dist/types/src/testing/index.d.ts.map +1 -1
  173. package/dist/types/src/testing/service.d.ts +8 -0
  174. package/dist/types/src/testing/service.d.ts.map +1 -0
  175. package/dist/types/src/testing/withPluginManager.d.ts +4 -4
  176. package/dist/types/src/testing/withPluginManager.d.ts.map +1 -1
  177. package/dist/types/src/ui/components/App/App.d.ts +8 -0
  178. package/dist/types/src/ui/components/App/App.d.ts.map +1 -0
  179. package/dist/types/src/ui/components/App/App.stories.d.ts +19 -0
  180. package/dist/types/src/ui/components/App/App.stories.d.ts.map +1 -0
  181. package/dist/types/src/ui/components/App/index.d.ts +2 -0
  182. package/dist/types/src/ui/components/App/index.d.ts.map +1 -0
  183. package/dist/types/src/{react/Surface.stories.d.ts → ui/components/PluginManager/PluginManagerContext.stories.d.ts} +2 -3
  184. package/dist/types/src/ui/components/PluginManager/PluginManagerContext.stories.d.ts.map +1 -0
  185. package/dist/types/src/ui/components/PluginManager/PluginManagerProvider.d.ts +10 -0
  186. package/dist/types/src/ui/components/PluginManager/PluginManagerProvider.d.ts.map +1 -0
  187. package/dist/types/src/ui/components/PluginManager/index.d.ts +2 -0
  188. package/dist/types/src/ui/components/PluginManager/index.d.ts.map +1 -0
  189. package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts +12 -0
  190. package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts.map +1 -0
  191. package/dist/types/src/{react/App.stories.d.ts → ui/components/Surface/SurfaceComponent.stories.d.ts} +1 -1
  192. package/dist/types/src/ui/components/Surface/SurfaceComponent.stories.d.ts.map +1 -0
  193. package/dist/types/src/ui/components/Surface/SurfaceInfo.d.ts +11 -0
  194. package/dist/types/src/ui/components/Surface/SurfaceInfo.d.ts.map +1 -0
  195. package/dist/types/src/ui/components/Surface/context.d.ts +5 -0
  196. package/dist/types/src/ui/components/Surface/context.d.ts.map +1 -0
  197. package/dist/types/src/ui/components/Surface/index.d.ts +20 -0
  198. package/dist/types/src/ui/components/Surface/index.d.ts.map +1 -0
  199. package/dist/types/src/ui/components/Surface/types.d.ts +96 -0
  200. package/dist/types/src/ui/components/Surface/types.d.ts.map +1 -0
  201. package/dist/types/src/ui/components/index.d.ts +4 -0
  202. package/dist/types/src/ui/components/index.d.ts.map +1 -0
  203. package/dist/types/src/ui/hooks/index.d.ts +7 -0
  204. package/dist/types/src/ui/hooks/index.d.ts.map +1 -0
  205. package/dist/types/src/{react → ui/hooks}/useApp.d.ts +18 -12
  206. package/dist/types/src/ui/hooks/useApp.d.ts.map +1 -0
  207. package/dist/types/src/ui/hooks/useApp.test.d.ts +2 -0
  208. package/dist/types/src/ui/hooks/useApp.test.d.ts.map +1 -0
  209. package/dist/types/src/ui/hooks/useCapabilities.d.ts +31 -0
  210. package/dist/types/src/ui/hooks/useCapabilities.d.ts.map +1 -0
  211. package/dist/types/src/{react → ui/hooks}/useLoading.d.ts +1 -2
  212. package/dist/types/src/ui/hooks/useLoading.d.ts.map +1 -0
  213. package/dist/types/src/ui/hooks/useOperationResolver.d.ts +19 -0
  214. package/dist/types/src/ui/hooks/useOperationResolver.d.ts.map +1 -0
  215. package/dist/types/src/ui/hooks/useSettingsState.d.ts +10 -0
  216. package/dist/types/src/ui/hooks/useSettingsState.d.ts.map +1 -0
  217. package/dist/types/src/ui/hooks/useSurface.d.ts +3 -0
  218. package/dist/types/src/ui/hooks/useSurface.d.ts.map +1 -0
  219. package/dist/types/src/ui/index.d.ts +3 -0
  220. package/dist/types/src/ui/index.d.ts.map +1 -0
  221. package/dist/types/tsconfig.tsbuildinfo +1 -1
  222. package/moon.yml +10 -6
  223. package/package.json +111 -50
  224. package/src/cli/cli.ts +107 -0
  225. package/src/{playground/debug → cli}/index.ts +1 -1
  226. package/src/common/activation-events.ts +44 -0
  227. package/src/common/capabilities.ts +174 -210
  228. package/src/common/index.ts +4 -8
  229. package/src/common/operations.ts +38 -0
  230. package/src/common/translations.ts +17 -9
  231. package/src/context.ts +9 -0
  232. package/src/core/{events.ts → activation-event.ts} +6 -6
  233. package/src/core/capability-manager.test.ts +151 -0
  234. package/src/core/capability-manager.ts +171 -0
  235. package/src/core/capability.ts +236 -0
  236. package/src/core/index.ts +5 -4
  237. package/src/core/plugin-manager.test.ts +1093 -0
  238. package/src/core/plugin-manager.ts +868 -0
  239. package/src/core/plugin.ts +230 -40
  240. package/src/index.ts +3 -2
  241. package/src/plugin-operation/OperationPlugin.ts +25 -0
  242. package/src/plugin-operation/history/capability.ts +37 -0
  243. package/src/plugin-operation/history/errors.ts +11 -0
  244. package/src/plugin-operation/history/history-tracker.test.ts +380 -0
  245. package/src/plugin-operation/history/history-tracker.ts +129 -0
  246. package/src/plugin-operation/history/index.ts +9 -0
  247. package/src/plugin-operation/history/types.ts +17 -0
  248. package/src/plugin-operation/history/undo-mapping.ts +135 -0
  249. package/src/plugin-operation/history/undo-registry.test.ts +73 -0
  250. package/src/plugin-operation/history/undo-registry.ts +54 -0
  251. package/src/plugin-operation/index.ts +6 -0
  252. package/src/plugin-operation/invoker-capability.ts +40 -0
  253. package/src/plugin-operation/meta.ts +11 -0
  254. package/src/plugin-operation/testing.ts +174 -0
  255. package/src/plugin-runtime/RuntimePlugin.ts +20 -0
  256. package/src/plugin-runtime/capability.ts +53 -0
  257. package/src/{playground/logger → plugin-runtime}/index.ts +1 -1
  258. package/src/plugin-runtime/meta.ts +11 -0
  259. package/src/testing/index.ts +1 -0
  260. package/src/testing/service.ts +52 -0
  261. package/src/testing/withPluginManager.stories.tsx +7 -7
  262. package/src/testing/withPluginManager.tsx +63 -38
  263. package/src/ui/components/App/App.stories.tsx +92 -0
  264. package/src/{react → ui/components/App}/App.tsx +9 -11
  265. package/src/{playground/layout → ui/components/App}/index.ts +1 -1
  266. package/src/ui/components/PluginManager/PluginManagerContext.stories.tsx +183 -0
  267. package/src/{react → ui/components/PluginManager}/PluginManagerProvider.ts +3 -3
  268. package/src/ui/components/PluginManager/index.ts +5 -0
  269. package/src/{react/Surface.stories.tsx → ui/components/Surface/SurfaceComponent.stories.tsx} +41 -23
  270. package/src/ui/components/Surface/SurfaceComponent.tsx +247 -0
  271. package/src/ui/components/Surface/SurfaceInfo.tsx +107 -0
  272. package/src/ui/components/Surface/context.ts +12 -0
  273. package/src/ui/components/Surface/index.ts +20 -0
  274. package/src/ui/components/Surface/types.ts +117 -0
  275. package/src/ui/components/index.ts +7 -0
  276. package/src/ui/hooks/index.ts +10 -0
  277. package/src/ui/hooks/useApp.test.tsx +159 -0
  278. package/src/ui/hooks/useApp.tsx +219 -0
  279. package/src/ui/hooks/useCapabilities.ts +67 -0
  280. package/src/{react → ui/hooks}/useLoading.tsx +4 -6
  281. package/src/ui/hooks/useOperationResolver.ts +40 -0
  282. package/src/ui/hooks/useSettingsState.ts +26 -0
  283. package/src/ui/hooks/useSurface.ts +13 -0
  284. package/src/ui/index.ts +6 -0
  285. package/tsconfig.json +11 -29
  286. package/tsconfig.node.json +1 -3
  287. package/typedoc.json +2 -4
  288. package/vitest.config.ts +1 -1
  289. package/.swc/plugins/linux_x86_64_19.0.0/fce1bdb8e20a094e4af08bad09cc81497ed0e2e7c51223b07d371063cca18429.wasmer-v7 +0 -0
  290. package/dist/lib/browser/app-graph-builder-OIEZZC45.mjs +0 -138
  291. package/dist/lib/browser/app-graph-builder-OIEZZC45.mjs.map +0 -7
  292. package/dist/lib/browser/chunk-6XKO24JP.mjs +0 -483
  293. package/dist/lib/browser/chunk-6XKO24JP.mjs.map +0 -7
  294. package/dist/lib/browser/chunk-SCPE4ZO2.mjs +0 -35
  295. package/dist/lib/browser/chunk-SCPE4ZO2.mjs.map +0 -7
  296. package/dist/lib/browser/chunk-WPW5VVAX.mjs +0 -1651
  297. package/dist/lib/browser/chunk-WPW5VVAX.mjs.map +0 -7
  298. package/dist/lib/browser/intent-dispatcher-LZ4AE66E.mjs +0 -11
  299. package/dist/lib/browser/intent-resolver-QVCKRX6G.mjs +0 -39
  300. package/dist/lib/browser/intent-resolver-QVCKRX6G.mjs.map +0 -7
  301. package/dist/lib/browser/react/index.mjs +0 -34
  302. package/dist/lib/browser/store-CNPHOYTJ.mjs +0 -30
  303. package/dist/lib/browser/store-CNPHOYTJ.mjs.map +0 -7
  304. package/dist/lib/node-esm/app-graph-builder-EBU4NVWD.mjs +0 -139
  305. package/dist/lib/node-esm/app-graph-builder-EBU4NVWD.mjs.map +0 -7
  306. package/dist/lib/node-esm/chunk-3UPX5OIS.mjs +0 -484
  307. package/dist/lib/node-esm/chunk-3UPX5OIS.mjs.map +0 -7
  308. package/dist/lib/node-esm/chunk-XJZGUJ3H.mjs +0 -1653
  309. package/dist/lib/node-esm/chunk-XJZGUJ3H.mjs.map +0 -7
  310. package/dist/lib/node-esm/chunk-ZX63QUGE.mjs +0 -37
  311. package/dist/lib/node-esm/chunk-ZX63QUGE.mjs.map +0 -7
  312. package/dist/lib/node-esm/intent-dispatcher-MGOJ3CHD.mjs +0 -12
  313. package/dist/lib/node-esm/intent-resolver-URF3HN3G.mjs +0 -40
  314. package/dist/lib/node-esm/intent-resolver-URF3HN3G.mjs.map +0 -7
  315. package/dist/lib/node-esm/react/index.mjs +0 -35
  316. package/dist/lib/node-esm/store-RK5B4XEL.mjs +0 -31
  317. package/dist/lib/node-esm/store-RK5B4XEL.mjs.map +0 -7
  318. package/dist/types/src/common/collaboration.d.ts +0 -20
  319. package/dist/types/src/common/collaboration.d.ts.map +0 -1
  320. package/dist/types/src/common/events.d.ts +0 -52
  321. package/dist/types/src/common/events.d.ts.map +0 -1
  322. package/dist/types/src/common/file.d.ts +0 -14
  323. package/dist/types/src/common/file.d.ts.map +0 -1
  324. package/dist/types/src/common/graph.d.ts +0 -21
  325. package/dist/types/src/common/graph.d.ts.map +0 -1
  326. package/dist/types/src/common/layout.d.ts +0 -279
  327. package/dist/types/src/common/layout.d.ts.map +0 -1
  328. package/dist/types/src/common/surface.d.ts +0 -68
  329. package/dist/types/src/common/surface.d.ts.map +0 -1
  330. package/dist/types/src/core/capabilities.d.ts +0 -117
  331. package/dist/types/src/core/capabilities.d.ts.map +0 -1
  332. package/dist/types/src/core/capabilities.test.d.ts +0 -2
  333. package/dist/types/src/core/capabilities.test.d.ts.map +0 -1
  334. package/dist/types/src/core/events.d.ts.map +0 -1
  335. package/dist/types/src/core/manager.d.ts +0 -126
  336. package/dist/types/src/core/manager.d.ts.map +0 -1
  337. package/dist/types/src/core/manager.test.d.ts +0 -2
  338. package/dist/types/src/core/manager.test.d.ts.map +0 -1
  339. package/dist/types/src/playground/debug/Debug.d.ts +0 -6
  340. package/dist/types/src/playground/debug/Debug.d.ts.map +0 -1
  341. package/dist/types/src/playground/debug/index.d.ts +0 -2
  342. package/dist/types/src/playground/debug/index.d.ts.map +0 -1
  343. package/dist/types/src/playground/debug/plugin.d.ts +0 -2
  344. package/dist/types/src/playground/debug/plugin.d.ts.map +0 -1
  345. package/dist/types/src/playground/generator/Main.d.ts +0 -6
  346. package/dist/types/src/playground/generator/Main.d.ts.map +0 -1
  347. package/dist/types/src/playground/generator/Toolbar.d.ts +0 -6
  348. package/dist/types/src/playground/generator/Toolbar.d.ts.map +0 -1
  349. package/dist/types/src/playground/generator/generator.d.ts +0 -7
  350. package/dist/types/src/playground/generator/generator.d.ts.map +0 -1
  351. package/dist/types/src/playground/generator/index.d.ts +0 -3
  352. package/dist/types/src/playground/generator/index.d.ts.map +0 -1
  353. package/dist/types/src/playground/generator/plugin.d.ts +0 -2
  354. package/dist/types/src/playground/generator/plugin.d.ts.map +0 -1
  355. package/dist/types/src/playground/layout/Layout.d.ts +0 -8
  356. package/dist/types/src/playground/layout/Layout.d.ts.map +0 -1
  357. package/dist/types/src/playground/layout/index.d.ts +0 -2
  358. package/dist/types/src/playground/layout/index.d.ts.map +0 -1
  359. package/dist/types/src/playground/layout/plugin.d.ts +0 -2
  360. package/dist/types/src/playground/layout/plugin.d.ts.map +0 -1
  361. package/dist/types/src/playground/logger/Toolbar.d.ts +0 -6
  362. package/dist/types/src/playground/logger/Toolbar.d.ts.map +0 -1
  363. package/dist/types/src/playground/logger/index.d.ts +0 -2
  364. package/dist/types/src/playground/logger/index.d.ts.map +0 -1
  365. package/dist/types/src/playground/logger/plugin.d.ts +0 -2
  366. package/dist/types/src/playground/logger/plugin.d.ts.map +0 -1
  367. package/dist/types/src/playground/logger/schema.d.ts +0 -13
  368. package/dist/types/src/playground/logger/schema.d.ts.map +0 -1
  369. package/dist/types/src/playground/playground.stories.d.ts +0 -11
  370. package/dist/types/src/playground/playground.stories.d.ts.map +0 -1
  371. package/dist/types/src/plugin-intent/IntentPlugin.d.ts +0 -2
  372. package/dist/types/src/plugin-intent/IntentPlugin.d.ts.map +0 -1
  373. package/dist/types/src/plugin-intent/actions.d.ts +0 -36
  374. package/dist/types/src/plugin-intent/actions.d.ts.map +0 -1
  375. package/dist/types/src/plugin-intent/errors.d.ts +0 -16
  376. package/dist/types/src/plugin-intent/errors.d.ts.map +0 -1
  377. package/dist/types/src/plugin-intent/index.d.ts +0 -6
  378. package/dist/types/src/plugin-intent/index.d.ts.map +0 -1
  379. package/dist/types/src/plugin-intent/intent-dispatcher.d.ts +0 -139
  380. package/dist/types/src/plugin-intent/intent-dispatcher.d.ts.map +0 -1
  381. package/dist/types/src/plugin-intent/intent-dispatcher.test.d.ts +0 -2
  382. package/dist/types/src/plugin-intent/intent-dispatcher.test.d.ts.map +0 -1
  383. package/dist/types/src/plugin-intent/intent.d.ts +0 -63
  384. package/dist/types/src/plugin-intent/intent.d.ts.map +0 -1
  385. package/dist/types/src/plugin-intent/meta.d.ts +0 -3
  386. package/dist/types/src/plugin-intent/meta.d.ts.map +0 -1
  387. package/dist/types/src/plugin-settings/SettingsPlugin.d.ts +0 -2
  388. package/dist/types/src/plugin-settings/SettingsPlugin.d.ts.map +0 -1
  389. package/dist/types/src/plugin-settings/actions.d.ts +0 -25
  390. package/dist/types/src/plugin-settings/actions.d.ts.map +0 -1
  391. package/dist/types/src/plugin-settings/app-graph-builder.d.ts +0 -4
  392. package/dist/types/src/plugin-settings/app-graph-builder.d.ts.map +0 -1
  393. package/dist/types/src/plugin-settings/index.d.ts +0 -3
  394. package/dist/types/src/plugin-settings/index.d.ts.map +0 -1
  395. package/dist/types/src/plugin-settings/intent-resolver.d.ts +0 -4
  396. package/dist/types/src/plugin-settings/intent-resolver.d.ts.map +0 -1
  397. package/dist/types/src/plugin-settings/meta.d.ts +0 -3
  398. package/dist/types/src/plugin-settings/meta.d.ts.map +0 -1
  399. package/dist/types/src/plugin-settings/store.d.ts +0 -5
  400. package/dist/types/src/plugin-settings/store.d.ts.map +0 -1
  401. package/dist/types/src/plugin-settings/translations.d.ts +0 -11
  402. package/dist/types/src/plugin-settings/translations.d.ts.map +0 -1
  403. package/dist/types/src/react/App.d.ts +0 -10
  404. package/dist/types/src/react/App.d.ts.map +0 -1
  405. package/dist/types/src/react/App.stories.d.ts.map +0 -1
  406. package/dist/types/src/react/DefaultFallback.d.ts +0 -8
  407. package/dist/types/src/react/DefaultFallback.d.ts.map +0 -1
  408. package/dist/types/src/react/ErrorBoundary.d.ts +0 -30
  409. package/dist/types/src/react/ErrorBoundary.d.ts.map +0 -1
  410. package/dist/types/src/react/IntentContext.d.ts +0 -8
  411. package/dist/types/src/react/IntentContext.d.ts.map +0 -1
  412. package/dist/types/src/react/PluginManagerProvider.d.ts +0 -10
  413. package/dist/types/src/react/PluginManagerProvider.d.ts.map +0 -1
  414. package/dist/types/src/react/Surface.d.ts +0 -12
  415. package/dist/types/src/react/Surface.d.ts.map +0 -1
  416. package/dist/types/src/react/Surface.stories.d.ts.map +0 -1
  417. package/dist/types/src/react/common.d.ts +0 -13
  418. package/dist/types/src/react/common.d.ts.map +0 -1
  419. package/dist/types/src/react/index.d.ts +0 -9
  420. package/dist/types/src/react/index.d.ts.map +0 -1
  421. package/dist/types/src/react/types.d.ts +0 -14
  422. package/dist/types/src/react/types.d.ts.map +0 -1
  423. package/dist/types/src/react/useApp.d.ts.map +0 -1
  424. package/dist/types/src/react/useCapabilities.d.ts +0 -13
  425. package/dist/types/src/react/useCapabilities.d.ts.map +0 -1
  426. package/dist/types/src/react/useIntentResolver.d.ts +0 -3
  427. package/dist/types/src/react/useIntentResolver.d.ts.map +0 -1
  428. package/dist/types/src/react/useLoading.d.ts.map +0 -1
  429. package/src/common/collaboration.ts +0 -18
  430. package/src/common/events.ts +0 -79
  431. package/src/common/file.ts +0 -22
  432. package/src/common/graph.ts +0 -30
  433. package/src/common/layout.ts +0 -277
  434. package/src/common/surface.ts +0 -88
  435. package/src/core/capabilities.test.ts +0 -136
  436. package/src/core/capabilities.ts +0 -263
  437. package/src/core/manager.test.ts +0 -516
  438. package/src/core/manager.ts +0 -604
  439. package/src/playground/debug/Debug.tsx +0 -39
  440. package/src/playground/debug/plugin.ts +0 -16
  441. package/src/playground/generator/Main.tsx +0 -70
  442. package/src/playground/generator/Toolbar.tsx +0 -47
  443. package/src/playground/generator/generator.ts +0 -48
  444. package/src/playground/generator/index.ts +0 -6
  445. package/src/playground/generator/plugin.ts +0 -22
  446. package/src/playground/layout/Layout.tsx +0 -33
  447. package/src/playground/layout/plugin.ts +0 -18
  448. package/src/playground/logger/Toolbar.tsx +0 -30
  449. package/src/playground/logger/plugin.ts +0 -41
  450. package/src/playground/logger/schema.ts +0 -12
  451. package/src/playground/playground.stories.tsx +0 -46
  452. package/src/plugin-intent/IntentPlugin.ts +0 -20
  453. package/src/plugin-intent/actions.ts +0 -31
  454. package/src/plugin-intent/errors.ts +0 -40
  455. package/src/plugin-intent/index.ts +0 -9
  456. package/src/plugin-intent/intent-dispatcher.test.ts +0 -286
  457. package/src/plugin-intent/intent-dispatcher.ts +0 -342
  458. package/src/plugin-intent/intent.ts +0 -154
  459. package/src/plugin-intent/meta.ts +0 -10
  460. package/src/plugin-settings/SettingsPlugin.ts +0 -34
  461. package/src/plugin-settings/actions.ts +0 -25
  462. package/src/plugin-settings/app-graph-builder.ts +0 -160
  463. package/src/plugin-settings/index.ts +0 -6
  464. package/src/plugin-settings/intent-resolver.ts +0 -35
  465. package/src/plugin-settings/meta.ts +0 -10
  466. package/src/plugin-settings/store.ts +0 -33
  467. package/src/plugin-settings/translations.ts +0 -19
  468. package/src/react/App.stories.tsx +0 -33
  469. package/src/react/DefaultFallback.tsx +0 -26
  470. package/src/react/ErrorBoundary.tsx +0 -56
  471. package/src/react/IntentContext.tsx +0 -35
  472. package/src/react/Surface.tsx +0 -109
  473. package/src/react/common.ts +0 -13
  474. package/src/react/index.ts +0 -14
  475. package/src/react/types.ts +0 -38
  476. package/src/react/useApp.tsx +0 -165
  477. package/src/react/useCapabilities.ts +0 -31
  478. package/src/react/useIntentResolver.ts +0 -22
  479. /package/dist/lib/browser/{intent-dispatcher-LZ4AE66E.mjs.map → chunk-J5LGTIGS.mjs.map} +0 -0
  480. /package/dist/lib/browser/{react/index.mjs.map → chunk-YAFEA4GV.mjs.map} +0 -0
  481. /package/dist/lib/{node-esm/intent-dispatcher-MGOJ3CHD.mjs.map → browser/common/activation-events.mjs.map} +0 -0
  482. /package/dist/lib/{node-esm/react/index.mjs.map → browser/common/capabilities.mjs.map} +0 -0
@@ -0,0 +1,1093 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { afterEach, assert, describe, it } from '@effect/vitest';
6
+ import { type Atom, Registry } from '@effect-atom/atom-react';
7
+ import * as Cause from 'effect/Cause';
8
+ import * as Exit from 'effect/Exit';
9
+ import * as Duration from 'effect/Duration';
10
+ import * as Effect from 'effect/Effect';
11
+ import * as Fiber from 'effect/Fiber';
12
+ import * as Match from 'effect/Match';
13
+ import * as PubSub from 'effect/PubSub';
14
+ import * as Queue from 'effect/Queue';
15
+ import * as TestClock from 'effect/TestClock';
16
+
17
+ import { invariant } from '@dxos/invariant';
18
+ import { type LogConfig, type LogEntry, LogLevel, log } from '@dxos/log';
19
+
20
+ import { ActivationEvents } from '../common';
21
+
22
+ import * as ActivationEvent from './activation-event';
23
+ import * as Capability from './capability';
24
+ import type * as CapabilityManager from './capability-manager';
25
+ import * as Plugin from './plugin';
26
+ import * as PluginManager from './plugin-manager';
27
+
28
+ const String = Capability.make<{ string: string }>('org.dxos.test.string');
29
+ const Number = Capability.make<{ number: number }>('org.dxos.test.number');
30
+ const Total = Capability.make<{ total: number }>('org.dxos.test.total');
31
+
32
+ const CountEvent = ActivationEvent.make('org.dxos.test.count');
33
+ const FailEvent = ActivationEvent.make('org.dxos.test.fail');
34
+
35
+ const testMeta = { id: 'org.dxos.plugin.test', name: 'Test' };
36
+
37
+ // TODO(wittjosiah): Factor out?
38
+ const atomCounter = (registry: Registry.Registry, atom: Atom.Atom<any>) => {
39
+ let count = 0;
40
+ let initial = true;
41
+ const dispose = registry.subscribe(
42
+ atom,
43
+ () => {
44
+ if (initial) {
45
+ initial = false;
46
+ return;
47
+ }
48
+ count++;
49
+ },
50
+ { immediate: true },
51
+ );
52
+ return {
53
+ get count() {
54
+ return count;
55
+ },
56
+ [Symbol.dispose]: dispose,
57
+ };
58
+ };
59
+
60
+ describe('PluginManager', () => {
61
+ let plugins: Plugin.Plugin[] = [];
62
+ const pluginLoader = Effect.fn(function* (id: string) {
63
+ const plugin = plugins.find((plugin) => plugin.meta.id === id);
64
+ invariant(plugin, `Plugin not found: ${id}`);
65
+ return plugin;
66
+ });
67
+
68
+ afterEach(() => {
69
+ plugins = [];
70
+ });
71
+
72
+ it.effect('should be able to add and remove plugins', () =>
73
+ Effect.gen(function* () {
74
+ const Test = Plugin.make(Plugin.define(testMeta));
75
+ const testPlugin = Test();
76
+ plugins = [testPlugin];
77
+
78
+ const manager = PluginManager.make({ pluginLoader });
79
+ const added = yield* manager.add(testMeta.id);
80
+ assert.isTrue(added);
81
+ assert.deepStrictEqual(manager.getPlugins(), [testPlugin]);
82
+ const removed = manager.remove(testMeta.id);
83
+ assert.isTrue(removed);
84
+ assert.deepStrictEqual(manager.getPlugins(), []);
85
+ }),
86
+ );
87
+
88
+ it.effect('should support factory pattern with options', () =>
89
+ Effect.gen(function* () {
90
+ type TestPluginOptions = { count: number };
91
+ const TestPluginFactory = Plugin.define<TestPluginOptions>(testMeta).pipe(
92
+ Plugin.addModule((options: TestPluginOptions) => ({
93
+ id: 'Hello',
94
+ activatesOn: ActivationEvents.Startup,
95
+ activate: () => Effect.succeed(Capability.contributes(String, { string: `hello-${options.count}` })),
96
+ })),
97
+ Plugin.addModule({
98
+ id: 'World',
99
+ activatesOn: ActivationEvents.Startup,
100
+ activate: () => Effect.succeed(Capability.contributes(String, { string: 'world' })),
101
+ }),
102
+ Plugin.make,
103
+ );
104
+
105
+ const plugin = TestPluginFactory({ count: 5 });
106
+ plugins = [plugin];
107
+
108
+ const manager = PluginManager.make({ plugins: [plugin], core: [], pluginLoader });
109
+ yield* manager.enable(testMeta.id);
110
+ yield* manager.activate(ActivationEvents.Startup);
111
+ const strings = manager.capabilities.getAll(String);
112
+ assert.strictEqual(strings.length, 2);
113
+ assert.strictEqual(strings[0].string, 'hello-5');
114
+ assert.strictEqual(strings[1].string, 'world');
115
+ }),
116
+ );
117
+
118
+ it.effect('should be able to enable and disable plugins', () =>
119
+ Effect.gen(function* () {
120
+ const Test = Plugin.define(testMeta).pipe(
121
+ Plugin.addModule({
122
+ id: 'Hello',
123
+ activatesOn: ActivationEvents.Startup,
124
+ activate: () => Effect.succeed(Capability.contributes(String, { string: 'hello' })),
125
+ }),
126
+ Plugin.make,
127
+ );
128
+
129
+ const testPlugin = Test();
130
+ const manager = PluginManager.make({ plugins: [testPlugin], core: [], pluginLoader });
131
+ yield* manager.enable(testMeta.id);
132
+ assert.deepStrictEqual(manager.getEnabled(), [Test.meta.id]);
133
+ assert.deepStrictEqual(manager.getModules(), [testPlugin.modules[0]]);
134
+ yield* manager.disable(testMeta.id);
135
+ assert.deepStrictEqual(manager.getEnabled(), []);
136
+ assert.deepStrictEqual(manager.getModules(), []);
137
+ }),
138
+ );
139
+
140
+ it.effect('should be able to activate plugins', () =>
141
+ Effect.gen(function* () {
142
+ const Test = Plugin.define(testMeta).pipe(
143
+ Plugin.addModule({
144
+ id: 'Hello',
145
+ activatesOn: ActivationEvents.Startup,
146
+ activate: () => Effect.succeed(Capability.contributes(String, { string: 'hello' })),
147
+ }),
148
+ Plugin.make,
149
+ );
150
+
151
+ const testPlugin = Test();
152
+ const manager = PluginManager.make({ plugins: [testPlugin], pluginLoader });
153
+ yield* manager.enable(Test.meta.id);
154
+ assert.deepStrictEqual(manager.getPlugins(), [testPlugin]);
155
+ assert.deepStrictEqual(manager.getEnabled(), [Test.meta.id]);
156
+ assert.deepStrictEqual(manager.getModules(), [testPlugin.modules[0]]);
157
+ assert.deepStrictEqual(manager.getActive(), []);
158
+ assert.deepStrictEqual(manager.getEventsFired(), []);
159
+ yield* manager.activate(ActivationEvents.Startup);
160
+ assert.deepStrictEqual(manager.getActive(), [testPlugin.modules[0].id]);
161
+ assert.deepStrictEqual(manager.getEventsFired(), [ActivationEvents.Startup.id]);
162
+ }),
163
+ );
164
+
165
+ it.effect('should handle activate returning void', () =>
166
+ Effect.gen(function* () {
167
+ const Test = Plugin.define(testMeta).pipe(
168
+ Plugin.addModule({
169
+ id: 'NoCapabilities',
170
+ activatesOn: ActivationEvents.Startup,
171
+ activate: Effect.fnUntraced(function* () {}),
172
+ }),
173
+ Plugin.make,
174
+ );
175
+
176
+ const testPlugin = Test();
177
+ const manager = PluginManager.make({ plugins: [testPlugin], pluginLoader });
178
+ yield* manager.enable(Test.meta.id);
179
+
180
+ const result = yield* manager.activate(ActivationEvents.Startup);
181
+ assert.isTrue(result);
182
+ assert.deepStrictEqual(manager.getActive(), [testPlugin.modules[0].id]);
183
+ assert.strictEqual(manager.capabilities.getAll(String).length, 0);
184
+ }),
185
+ );
186
+
187
+ it.effect('should propagate errors thrown by module activate callbacks', () =>
188
+ Effect.gen(function* () {
189
+ plugins = [
190
+ Plugin.define(testMeta).pipe(
191
+ Plugin.addModule({
192
+ activatesOn: FailEvent,
193
+ id: 'Fail',
194
+ activate: () => Effect.fail(new Error('test')),
195
+ }),
196
+ Plugin.make,
197
+ )(),
198
+ ];
199
+
200
+ const manager = PluginManager.make({ pluginLoader });
201
+ yield* manager.add(testMeta.id);
202
+ const error = yield* Effect.flip(manager.activate(FailEvent));
203
+ assert.strictEqual(error.message, 'test');
204
+ }),
205
+ );
206
+
207
+ it.effect('should catch and log defects (synchronous throws) in module activation', () =>
208
+ Effect.gen(function* () {
209
+ const DefectEvent = ActivationEvent.make('org.dxos.test.defect');
210
+ const capturedErrors: LogEntry[] = [];
211
+ const removeProcessor = log.addProcessor((_config: LogConfig, entry: LogEntry) => {
212
+ if (entry.level === LogLevel.ERROR) {
213
+ capturedErrors.push(entry);
214
+ }
215
+ });
216
+
217
+ plugins = [
218
+ Plugin.define(testMeta).pipe(
219
+ Plugin.addModule({
220
+ activatesOn: DefectEvent,
221
+ id: 'DefectInEffectSync',
222
+ activate: () =>
223
+ Effect.sync(() => {
224
+ // This is a defect - a synchronous throw inside Effect.sync.
225
+ throw new Error('defect in Effect.sync');
226
+ }),
227
+ }),
228
+ Plugin.make,
229
+ )(),
230
+ ];
231
+
232
+ const manager = PluginManager.make({ pluginLoader });
233
+ yield* manager.add(testMeta.id);
234
+ const error = yield* Effect.flip(manager.activate(DefectEvent));
235
+
236
+ // Verify the error was caught and propagated.
237
+ assert.strictEqual(error.message, 'defect in Effect.sync');
238
+
239
+ // Verify the error was logged with isDefect: true.
240
+ const defectLog = capturedErrors.find(
241
+ (entry) =>
242
+ entry.message?.includes('module failed to activate') &&
243
+ entry.context?.module === 'org.dxos.plugin.test.module.DefectInEffectSync',
244
+ );
245
+ assert.isNotNull(defectLog, 'Expected error log for defect');
246
+ assert.strictEqual(defectLog?.context?.isDefect, true, 'Expected isDefect to be true for synchronous throw');
247
+
248
+ removeProcessor();
249
+ }),
250
+ );
251
+
252
+ it.effect('should catch and log defects when activate throws before returning Effect', () =>
253
+ Effect.gen(function* () {
254
+ const DefectEvent = ActivationEvent.make('org.dxos.test.defect-immediate');
255
+ const capturedErrors: LogEntry[] = [];
256
+ const removeProcessor = log.addProcessor((_config: LogConfig, entry: LogEntry) => {
257
+ if (entry.level === LogLevel.ERROR) {
258
+ capturedErrors.push(entry);
259
+ }
260
+ });
261
+
262
+ plugins = [
263
+ Plugin.define(testMeta).pipe(
264
+ Plugin.addModule({
265
+ activatesOn: DefectEvent,
266
+ id: 'DefectImmediate',
267
+ activate: () => {
268
+ // This throws immediately before even returning an Effect.
269
+ // This is the most severe type of defect.
270
+ throw new Error('immediate throw before Effect');
271
+ return Effect.succeed(undefined);
272
+ },
273
+ }),
274
+ Plugin.make,
275
+ )(),
276
+ ];
277
+
278
+ const manager = PluginManager.make({ pluginLoader });
279
+ yield* manager.add(testMeta.id);
280
+ const error = yield* Effect.flip(manager.activate(DefectEvent));
281
+
282
+ // Verify the error was caught and propagated.
283
+ assert.strictEqual(error.message, 'immediate throw before Effect');
284
+
285
+ // Verify the error was logged with isDefect: true.
286
+ const defectLog = capturedErrors.find(
287
+ (entry) =>
288
+ entry.message?.includes('module failed to activate') &&
289
+ entry.context?.module === 'org.dxos.plugin.test.module.DefectImmediate',
290
+ );
291
+ assert.isNotNull(defectLog, 'Expected error log for immediate defect');
292
+ assert.strictEqual(
293
+ defectLog?.context?.isDefect,
294
+ true,
295
+ 'Expected isDefect to be true for immediate throw before Effect',
296
+ );
297
+
298
+ removeProcessor();
299
+ }),
300
+ );
301
+
302
+ it.effect('should fire activation events', () =>
303
+ Effect.gen(function* () {
304
+ plugins = [
305
+ Plugin.define(testMeta).pipe(
306
+ Plugin.addModule({
307
+ id: 'Hello',
308
+ activatesOn: ActivationEvents.Startup,
309
+ activate: () => Effect.succeed(Capability.contributes(String, { string: 'hello' })),
310
+ }),
311
+ Plugin.addModule({
312
+ activatesOn: FailEvent,
313
+ id: 'Fail',
314
+ activate: () => Effect.fail(new Error('test')),
315
+ }),
316
+ Plugin.make,
317
+ )(),
318
+ ];
319
+
320
+ const manager = PluginManager.make({ pluginLoader });
321
+ const activating = yield* Effect.makeLatch(false);
322
+ const activated = yield* Effect.makeLatch(false);
323
+ const error = yield* Effect.makeLatch(false);
324
+
325
+ const activationFiber = PubSub.subscribe(manager.activation).pipe(
326
+ Effect.flatMap((queue) =>
327
+ Queue.take(queue).pipe(
328
+ Effect.flatMap(({ state }) =>
329
+ Match.value(state).pipe(
330
+ Match.when('activating', () => activating.open),
331
+ Match.when('activated', () => activated.open),
332
+ Match.when('error', () => error.open),
333
+ Match.orElse(() => Effect.succeed(undefined)),
334
+ ),
335
+ ),
336
+ Effect.forever,
337
+ ),
338
+ ),
339
+ Effect.scoped,
340
+ Effect.runFork,
341
+ );
342
+
343
+ yield* manager.add(testMeta.id);
344
+ yield* manager.activate(ActivationEvents.Startup);
345
+ yield* activating.await;
346
+ yield* activated.await;
347
+
348
+ const activating2 = yield* Effect.makeLatch(false);
349
+ const activationFiber2 = PubSub.subscribe(manager.activation).pipe(
350
+ Effect.flatMap((queue) =>
351
+ Queue.take(queue).pipe(
352
+ Effect.flatMap(({ state }) =>
353
+ Match.value(state).pipe(
354
+ Match.when('activating', () => activating2.open),
355
+ Match.orElse(() => Effect.succeed(undefined)),
356
+ ),
357
+ ),
358
+ Effect.forever,
359
+ ),
360
+ ),
361
+ Effect.scoped,
362
+ Effect.runFork,
363
+ );
364
+
365
+ yield* manager.activate(FailEvent).pipe(Effect.catchAll(() => Effect.succeed(false)));
366
+ yield* activating2.await;
367
+ yield* error.await;
368
+ yield* Fiber.interrupt(activationFiber);
369
+ yield* Fiber.interrupt(activationFiber2);
370
+ }),
371
+ );
372
+
373
+ it.effect('should be able to reset an activation event', () =>
374
+ Effect.gen(function* () {
375
+ let count = 0;
376
+ const Test = Plugin.define(testMeta).pipe(
377
+ Plugin.addModule({
378
+ id: 'Hello',
379
+ activatesOn: ActivationEvents.Startup,
380
+ activate: () => {
381
+ count++;
382
+ return Effect.succeed(Capability.contributes(String, { string: 'hello' }));
383
+ },
384
+ }),
385
+ Plugin.make,
386
+ );
387
+ const testPlugin = Test();
388
+ plugins = [testPlugin];
389
+
390
+ const manager = PluginManager.make({ pluginLoader });
391
+
392
+ {
393
+ yield* manager.add(testMeta.id);
394
+ const result = yield* manager.activate(ActivationEvents.Startup);
395
+ assert.isTrue(result);
396
+ assert.deepStrictEqual(manager.getActive(), [testPlugin.modules[0].id]);
397
+ assert.strictEqual(count, 1);
398
+ }
399
+
400
+ {
401
+ const result = yield* manager.activate(ActivationEvents.Startup);
402
+ assert.isFalse(result);
403
+ }
404
+
405
+ {
406
+ const result = yield* manager.reset(ActivationEvents.Startup);
407
+ assert.isTrue(result);
408
+ assert.strictEqual(count, 2);
409
+ }
410
+ }),
411
+ );
412
+
413
+ it.effect('should not fire an unknown event', () =>
414
+ Effect.gen(function* () {
415
+ const manager = PluginManager.make({ pluginLoader });
416
+ const UnknownEvent = ActivationEvent.make('unknown');
417
+ const result = yield* manager.activate(UnknownEvent);
418
+ assert.isFalse(result);
419
+ }),
420
+ );
421
+
422
+ it.effect('should be able to fire custom activation events', () =>
423
+ Effect.gen(function* () {
424
+ const Plugin1 = Plugin.define({ id: 'org.dxos.test.plugin-1', name: 'Plugin 1' }).pipe(
425
+ Plugin.addModule({
426
+ activatesOn: CountEvent,
427
+ id: 'Plugin1',
428
+ activate: () => Effect.succeed([Capability.contributes(Number, { number: 1 })]),
429
+ }),
430
+ Plugin.make,
431
+ );
432
+ const Plugin2 = Plugin.define({ id: 'org.dxos.test.plugin-2', name: 'Plugin 2' }).pipe(
433
+ Plugin.addModule({
434
+ activatesOn: CountEvent,
435
+ id: 'Plugin2',
436
+ activate: () => Effect.succeed([Capability.contributes(Number, { number: 2 })]),
437
+ }),
438
+ Plugin.make,
439
+ );
440
+ const Plugin3 = Plugin.define({ id: 'org.dxos.test.plugin-3', name: 'Plugin 3' }).pipe(
441
+ Plugin.addModule({
442
+ activatesOn: CountEvent,
443
+ id: 'Plugin3',
444
+ activate: () => Effect.succeed([Capability.contributes(Number, { number: 3 })]),
445
+ }),
446
+ Plugin.make,
447
+ );
448
+ const plugin1 = Plugin1();
449
+ const plugin2 = Plugin2();
450
+ const plugin3 = Plugin3();
451
+ plugins = [plugin1, plugin2, plugin3];
452
+
453
+ const manager = PluginManager.make({ pluginLoader });
454
+ assert.deepStrictEqual(manager.getActive(), []);
455
+ assert.strictEqual(manager.capabilities.getAll(Number).length, 0);
456
+
457
+ yield* manager.add(Plugin1.meta.id);
458
+ yield* manager.activate(CountEvent);
459
+ assert.deepStrictEqual(manager.getActive(), [plugin1.modules[0].id]);
460
+ assert.strictEqual(manager.capabilities.getAll(Number).length, 1);
461
+
462
+ yield* manager.add(Plugin2.meta.id);
463
+ yield* manager.activate(CountEvent);
464
+ assert.deepStrictEqual(manager.getActive(), [plugin1.modules[0].id, plugin2.modules[0].id]);
465
+ assert.strictEqual(manager.capabilities.getAll(Number).length, 2);
466
+
467
+ yield* manager.add(Plugin3.meta.id);
468
+ yield* manager.activate(CountEvent);
469
+ assert.deepStrictEqual(manager.getActive(), [
470
+ plugin1.modules[0].id,
471
+ plugin2.modules[0].id,
472
+ plugin3.modules[0].id,
473
+ ]);
474
+ assert.strictEqual(manager.capabilities.getAll(Number).length, 3);
475
+ }),
476
+ );
477
+
478
+ it.effect('should only activate modules after all activatation events have been fired', () =>
479
+ Effect.gen(function* () {
480
+ const Test = Plugin.define(testMeta).pipe(
481
+ Plugin.addModule({
482
+ activatesOn: ActivationEvent.allOf(ActivationEvents.Startup, CountEvent),
483
+ id: 'Hello',
484
+ activate: () => {
485
+ return Effect.succeed(Capability.contributes(String, { string: 'hello' }));
486
+ },
487
+ }),
488
+ Plugin.make,
489
+ );
490
+ const testPlugin = Test();
491
+ plugins = [testPlugin];
492
+
493
+ const manager = PluginManager.make({ pluginLoader });
494
+ assert.deepStrictEqual(manager.getActive(), []);
495
+ assert.strictEqual(manager.capabilities.getAll(String).length, 0);
496
+
497
+ yield* manager.add(testMeta.id);
498
+ yield* manager.activate(ActivationEvents.Startup);
499
+ assert.deepStrictEqual(manager.getActive(), []);
500
+ assert.strictEqual(manager.capabilities.getAll(String).length, 0);
501
+
502
+ yield* manager.activate(CountEvent);
503
+ assert.deepStrictEqual(manager.getActive(), [testPlugin.modules[0].id]);
504
+ assert.strictEqual(manager.capabilities.getAll(String).length, 1);
505
+ }),
506
+ );
507
+
508
+ it.effect('should only activate modules once when multiple activatation events have been fired', () =>
509
+ Effect.gen(function* () {
510
+ let count = 0;
511
+ const Test = Plugin.define(testMeta).pipe(
512
+ Plugin.addModule({
513
+ id: 'Hello',
514
+ activatesOn: ActivationEvent.oneOf(ActivationEvents.Startup, CountEvent),
515
+ activate: () => {
516
+ count++;
517
+ return Effect.succeed(Capability.contributes(String, { string: 'hello' }));
518
+ },
519
+ }),
520
+ Plugin.make,
521
+ );
522
+ const testPlugin = Test();
523
+ plugins = [testPlugin];
524
+
525
+ const manager = PluginManager.make({ pluginLoader });
526
+ assert.deepStrictEqual(manager.getActive(), []);
527
+ assert.strictEqual(manager.capabilities.getAll(String).length, 0);
528
+ assert.strictEqual(count, 0);
529
+
530
+ yield* manager.add(testMeta.id);
531
+ yield* manager.activate(CountEvent);
532
+ assert.deepStrictEqual(manager.getActive(), [testPlugin.modules[0].id]);
533
+ assert.strictEqual(manager.capabilities.getAll(String).length, 1);
534
+ assert.strictEqual(count, 1);
535
+
536
+ yield* manager.activate(ActivationEvents.Startup);
537
+ assert.deepStrictEqual(manager.getActive(), [testPlugin.modules[0].id]);
538
+ assert.strictEqual(manager.capabilities.getAll(String).length, 1);
539
+ assert.strictEqual(count, 1);
540
+ }),
541
+ );
542
+
543
+ it.effect('should be able to disable and re-enable an active plugin', () =>
544
+ Effect.gen(function* () {
545
+ const state = { total: 0 };
546
+ const computeTotal = (capabilityManager: CapabilityManager.CapabilityManager) => {
547
+ const numbers = capabilityManager.getAll(Number);
548
+ state.total = numbers.reduce((acc: number, n: { number: number }) => acc + n.number, 0);
549
+ };
550
+
551
+ const Count = Plugin.define({ id: 'org.dxos.test.count', name: 'Count' }).pipe(
552
+ Plugin.addModule({
553
+ id: 'Count',
554
+ activatesOn: ActivationEvents.Startup,
555
+ activatesBefore: [CountEvent],
556
+ activate: Effect.fnUntraced(function* () {
557
+ const capabilityManager = yield* Capability.Service;
558
+ computeTotal(capabilityManager);
559
+ return Capability.contributes(Total, state);
560
+ }),
561
+ }),
562
+ Plugin.make,
563
+ );
564
+
565
+ const Test = Plugin.define(testMeta).pipe(
566
+ Plugin.addModule({
567
+ activatesOn: CountEvent,
568
+ id: 'Test1',
569
+ activate: () => Effect.succeed(Capability.contributes(Number, { number: 1 })),
570
+ }),
571
+ Plugin.addModule({
572
+ id: 'Test2',
573
+ activatesOn: CountEvent,
574
+ activate: () => Effect.succeed(Capability.contributes(Number, { number: 2 })),
575
+ }),
576
+ Plugin.addModule({
577
+ id: 'Test3',
578
+ activatesOn: CountEvent,
579
+ activate: () => Effect.succeed(Capability.contributes(Number, { number: 3 })),
580
+ }),
581
+ Plugin.make,
582
+ );
583
+ const countPlugin = Count();
584
+ const testPlugin = Test();
585
+ plugins = [countPlugin, testPlugin];
586
+
587
+ const manager = PluginManager.make({ pluginLoader });
588
+ {
589
+ yield* manager.add(Test.meta.id);
590
+ yield* manager.add(Count.meta.id);
591
+ yield* manager.activate(ActivationEvents.Startup);
592
+ assert.deepStrictEqual(manager.getActive(), [
593
+ ...testPlugin.modules.map((m) => m.id),
594
+ countPlugin.modules[0].id,
595
+ ]);
596
+ assert.deepStrictEqual(manager.getPendingReset(), []);
597
+
598
+ const totals = manager.capabilities.getAll(Total);
599
+ assert.strictEqual(totals.length, 1);
600
+ assert.strictEqual(totals[0].total, 6);
601
+ }
602
+
603
+ {
604
+ yield* manager.disable(Test.meta.id);
605
+ assert.deepStrictEqual(manager.getActive(), [countPlugin.modules[0].id]);
606
+ assert.deepStrictEqual(manager.getPendingReset(), []);
607
+
608
+ const totals = manager.capabilities.getAll(Total);
609
+ assert.strictEqual(totals.length, 1);
610
+ // Total doesn't change because it is not reactive.
611
+ assert.strictEqual(totals[0].total, 6);
612
+ }
613
+
614
+ {
615
+ yield* manager.enable(Test.meta.id);
616
+ assert.deepStrictEqual(manager.getActive(), [
617
+ countPlugin.modules[0].id,
618
+ ...testPlugin.modules.map((m) => m.id),
619
+ ]);
620
+ assert.deepStrictEqual(manager.getPendingReset(), []);
621
+
622
+ const totals = manager.capabilities.getAll(Total);
623
+ assert.strictEqual(totals.length, 1);
624
+ assert.strictEqual(totals[0].total, 6);
625
+ }
626
+ }),
627
+ );
628
+
629
+ it.effect('should be reactive', () =>
630
+ Effect.gen(function* () {
631
+ const Plugin1 = Plugin.define({ id: 'org.dxos.test.plugin-1', name: 'Plugin 1' }).pipe(
632
+ Plugin.addModule({
633
+ activatesOn: CountEvent,
634
+ id: 'Plugin1',
635
+ activate: () => Effect.succeed([Capability.contributes(Number, { number: 1 })]),
636
+ }),
637
+ Plugin.make,
638
+ );
639
+ const Plugin2 = Plugin.define({ id: 'org.dxos.test.plugin-2', name: 'Plugin 2' }).pipe(
640
+ Plugin.addModule({
641
+ activatesOn: CountEvent,
642
+ id: 'Plugin2',
643
+ activate: () => Effect.succeed([Capability.contributes(Number, { number: 2 })]),
644
+ }),
645
+ Plugin.make,
646
+ );
647
+ const Plugin3 = Plugin.define({ id: 'org.dxos.test.plugin-3', name: 'Plugin 3' }).pipe(
648
+ Plugin.addModule({
649
+ activatesOn: CountEvent,
650
+ id: 'Plugin3',
651
+ activate: () => Effect.succeed([Capability.contributes(Number, { number: 3 })]),
652
+ }),
653
+ Plugin.make,
654
+ );
655
+ plugins = [Plugin1(), Plugin2(), Plugin3()];
656
+
657
+ const registry = Registry.make();
658
+ const manager = PluginManager.make({ pluginLoader, registry });
659
+ using pluginUpdates = atomCounter(registry, manager.plugins);
660
+ using enabledUpdates = atomCounter(registry, manager.enabled);
661
+ using modulesUpdates = atomCounter(registry, manager.modules);
662
+ using activeUpdates = atomCounter(registry, manager.active);
663
+ using eventsFiredUpdates = atomCounter(registry, manager.eventsFired);
664
+ using pendingResetUpdates = atomCounter(registry, manager.pendingReset);
665
+ assert.strictEqual(pluginUpdates.count, 0);
666
+ assert.strictEqual(enabledUpdates.count, 0);
667
+ assert.strictEqual(modulesUpdates.count, 0);
668
+ assert.strictEqual(activeUpdates.count, 0);
669
+ assert.strictEqual(eventsFiredUpdates.count, 0);
670
+ assert.strictEqual(pendingResetUpdates.count, 0);
671
+
672
+ yield* manager.add(Plugin1.meta.id);
673
+ assert.strictEqual(pluginUpdates.count, 1);
674
+ assert.strictEqual(enabledUpdates.count, 1);
675
+ assert.strictEqual(modulesUpdates.count, 1);
676
+ assert.strictEqual(activeUpdates.count, 0);
677
+ assert.strictEqual(eventsFiredUpdates.count, 0);
678
+ assert.strictEqual(pendingResetUpdates.count, 0);
679
+
680
+ yield* manager.activate(CountEvent);
681
+ assert.strictEqual(pluginUpdates.count, 1);
682
+ assert.strictEqual(enabledUpdates.count, 1);
683
+ assert.strictEqual(modulesUpdates.count, 1);
684
+ assert.strictEqual(activeUpdates.count, 1);
685
+ assert.strictEqual(eventsFiredUpdates.count, 1);
686
+ assert.strictEqual(pendingResetUpdates.count, 0);
687
+
688
+ yield* manager.add(Plugin2.meta.id);
689
+ assert.strictEqual(pluginUpdates.count, 2);
690
+ assert.strictEqual(enabledUpdates.count, 2);
691
+ assert.strictEqual(modulesUpdates.count, 2);
692
+ assert.strictEqual(activeUpdates.count, 2);
693
+ assert.strictEqual(eventsFiredUpdates.count, 1);
694
+ assert.strictEqual(pendingResetUpdates.count, 2);
695
+
696
+ yield* manager.activate(CountEvent);
697
+ assert.strictEqual(pluginUpdates.count, 2);
698
+ assert.strictEqual(enabledUpdates.count, 2);
699
+ assert.strictEqual(modulesUpdates.count, 2);
700
+ assert.strictEqual(activeUpdates.count, 2);
701
+ assert.strictEqual(eventsFiredUpdates.count, 1);
702
+ assert.strictEqual(pendingResetUpdates.count, 2);
703
+
704
+ yield* manager.add(Plugin3.meta.id);
705
+ assert.strictEqual(pluginUpdates.count, 3);
706
+ assert.strictEqual(enabledUpdates.count, 3);
707
+ assert.strictEqual(modulesUpdates.count, 3);
708
+ assert.strictEqual(activeUpdates.count, 3);
709
+ assert.strictEqual(eventsFiredUpdates.count, 1);
710
+ assert.strictEqual(pendingResetUpdates.count, 4);
711
+
712
+ yield* manager.reset(CountEvent);
713
+ assert.strictEqual(pluginUpdates.count, 3);
714
+ assert.strictEqual(enabledUpdates.count, 3);
715
+ assert.strictEqual(modulesUpdates.count, 3);
716
+ // Starts at 3, plus deactivates 3, plus activates 3.
717
+ assert.strictEqual(activeUpdates.count, 9);
718
+ assert.strictEqual(eventsFiredUpdates.count, 1);
719
+ assert.strictEqual(pendingResetUpdates.count, 4);
720
+
721
+ yield* manager.disable(Plugin1.meta.id);
722
+ assert.strictEqual(pluginUpdates.count, 3);
723
+ assert.strictEqual(enabledUpdates.count, 4);
724
+ assert.strictEqual(modulesUpdates.count, 4);
725
+ assert.strictEqual(activeUpdates.count, 10);
726
+ assert.strictEqual(eventsFiredUpdates.count, 1);
727
+ assert.strictEqual(pendingResetUpdates.count, 4);
728
+
729
+ manager.remove(Plugin1.meta.id);
730
+ assert.strictEqual(pluginUpdates.count, 4);
731
+ assert.strictEqual(enabledUpdates.count, 4);
732
+ assert.strictEqual(modulesUpdates.count, 4);
733
+ assert.strictEqual(activeUpdates.count, 10);
734
+ assert.strictEqual(eventsFiredUpdates.count, 1);
735
+ assert.strictEqual(pendingResetUpdates.count, 4);
736
+
737
+ yield* manager.reset(CountEvent);
738
+ assert.strictEqual(pluginUpdates.count, 4);
739
+ assert.strictEqual(enabledUpdates.count, 4);
740
+ assert.strictEqual(modulesUpdates.count, 4);
741
+ // Starts at 10, plus deactivates 2, plus activates 2.
742
+ assert.strictEqual(activeUpdates.count, 14);
743
+ assert.strictEqual(eventsFiredUpdates.count, 1);
744
+ assert.strictEqual(pendingResetUpdates.count, 4);
745
+ }),
746
+ );
747
+
748
+ it.effect('should log a warning when a module takes too long to activate', () =>
749
+ Effect.gen(function* () {
750
+ const capturedWarnings: LogEntry[] = [];
751
+ const removeProcessor = log.addProcessor((_config: LogConfig, entry: LogEntry) => {
752
+ if (entry.level === LogLevel.WARN) {
753
+ capturedWarnings.push(entry);
754
+ }
755
+ });
756
+
757
+ const SlowEvent = ActivationEvent.make('org.dxos.test.slow');
758
+ const SlowPlugin = Plugin.define({ id: 'org.dxos.test.slow-plugin', name: 'Slow Plugin' }).pipe(
759
+ Plugin.addModule({
760
+ id: 'SlowModule',
761
+ activatesOn: SlowEvent,
762
+ activate: Effect.fnUntraced(function* () {
763
+ // Simulate a slow activation that takes 15 seconds.
764
+ yield* Effect.sleep(Duration.seconds(15));
765
+ return Capability.contributes(String, { string: 'slow' });
766
+ }),
767
+ }),
768
+ Plugin.make,
769
+ );
770
+
771
+ const slowPlugin = SlowPlugin();
772
+ plugins = [slowPlugin];
773
+
774
+ const manager = PluginManager.make({ pluginLoader });
775
+ yield* manager.add(SlowPlugin.meta.id);
776
+
777
+ // Fork the activation so we can control time with TestClock.
778
+ const activationFiber = yield* Effect.fork(manager.activate(SlowEvent));
779
+
780
+ // Advance time past the 10 second warning threshold.
781
+ yield* TestClock.adjust(Duration.seconds(11));
782
+
783
+ // Check that the warning was logged.
784
+ assert.isTrue(
785
+ capturedWarnings.some((entry) => entry.message?.includes('module is taking a long time to activate')),
786
+ 'Expected a warning about slow module activation',
787
+ );
788
+
789
+ // Advance time to let the module finish activating.
790
+ yield* TestClock.adjust(Duration.seconds(5));
791
+ yield* Fiber.join(activationFiber);
792
+
793
+ removeProcessor();
794
+ }),
795
+ );
796
+
797
+ it.effect('should prevent concurrent loads of the same module via semaphore', () =>
798
+ Effect.gen(function* () {
799
+ // Two different events that both can trigger the same module.
800
+ const EventA = ActivationEvent.make('org.dxos.test.event-a');
801
+ const EventB = ActivationEvent.make('org.dxos.test.event-b');
802
+
803
+ let activateCallCount = 0;
804
+ const ConcurrentPlugin = Plugin.define({ id: 'org.dxos.test.concurrent-plugin', name: 'Concurrent Plugin' }).pipe(
805
+ Plugin.addModule({
806
+ id: 'ConcurrentModule',
807
+ // Module activates on either event - this allows two different events to race.
808
+ activatesOn: ActivationEvent.oneOf(EventA, EventB),
809
+ activate: Effect.fnUntraced(function* () {
810
+ activateCallCount++;
811
+ // Simulate slow activation to create window for race condition.
812
+ yield* Effect.sleep(Duration.seconds(5));
813
+ return Capability.contributes(String, { string: 'concurrent' });
814
+ }),
815
+ }),
816
+ Plugin.make,
817
+ );
818
+
819
+ const concurrentPlugin = ConcurrentPlugin();
820
+ plugins = [concurrentPlugin];
821
+
822
+ const manager = PluginManager.make({ pluginLoader });
823
+ yield* manager.add(ConcurrentPlugin.meta.id);
824
+
825
+ // Fork two concurrent activations with DIFFERENT events.
826
+ // Both events trigger the same module, so both will try to call _loadModule.
827
+ // Without the semaphore, both would start loading the same module.
828
+ const fiber1 = yield* Effect.fork(manager.activate(EventA));
829
+ const fiber2 = yield* Effect.fork(manager.activate(EventB));
830
+
831
+ // Advance time to let both activations complete.
832
+ yield* TestClock.adjust(Duration.seconds(6));
833
+
834
+ yield* Fiber.join(fiber1);
835
+ yield* Fiber.join(fiber2);
836
+
837
+ // The semaphore should ensure the module's activate function is only called once,
838
+ // even when two different events race to load the same module.
839
+ assert.strictEqual(activateCallCount, 1, 'module activate should only be called once due to semaphore');
840
+
841
+ // Verify the capability was contributed.
842
+ const strings = manager.capabilities.getAll(String);
843
+ assert.isTrue(strings.length >= 1, 'capability should be contributed');
844
+ assert.strictEqual(strings[0].string, 'concurrent');
845
+ }),
846
+ );
847
+
848
+ it.effect('should deactivate all active modules on shutdown', () =>
849
+ Effect.gen(function* () {
850
+ const Plugin1 = Plugin.define({ id: 'org.dxos.test.plugin-1', name: 'Plugin 1' }).pipe(
851
+ Plugin.addModule({
852
+ activatesOn: ActivationEvents.Startup,
853
+ id: 'Plugin1',
854
+ activate: () => Effect.succeed(Capability.contributes(String, { string: 'hello' })),
855
+ }),
856
+ Plugin.make,
857
+ );
858
+ const Plugin2 = Plugin.define({ id: 'org.dxos.test.plugin-2', name: 'Plugin 2' }).pipe(
859
+ Plugin.addModule({
860
+ activatesOn: ActivationEvents.Startup,
861
+ id: 'Plugin2',
862
+ activate: () => Effect.succeed(Capability.contributes(Number, { number: 42 })),
863
+ }),
864
+ Plugin.make,
865
+ );
866
+ const plugin1 = Plugin1();
867
+ const plugin2 = Plugin2();
868
+ plugins = [plugin1, plugin2];
869
+
870
+ const manager = PluginManager.make({ pluginLoader });
871
+ yield* manager.add(Plugin1.meta.id);
872
+ yield* manager.add(Plugin2.meta.id);
873
+ yield* manager.activate(ActivationEvents.Startup);
874
+ assert.strictEqual(manager.getActive().length, 2);
875
+ assert.strictEqual(manager.capabilities.getAll(String).length, 1);
876
+ assert.strictEqual(manager.capabilities.getAll(Number).length, 1);
877
+
878
+ const result = yield* manager.shutdown();
879
+ assert.isTrue(result);
880
+ assert.deepStrictEqual(manager.getActive(), []);
881
+ assert.strictEqual(manager.capabilities.getAll(String).length, 0);
882
+ assert.strictEqual(manager.capabilities.getAll(Number).length, 0);
883
+ }),
884
+ );
885
+
886
+ it.effect('should run capability deactivate hooks during shutdown', () =>
887
+ Effect.gen(function* () {
888
+ let deactivated = false;
889
+ const Test = Plugin.define(testMeta).pipe(
890
+ Plugin.addModule({
891
+ id: 'WithDeactivate',
892
+ activatesOn: ActivationEvents.Startup,
893
+ activate: () =>
894
+ Effect.succeed(
895
+ Capability.contributes(String, { string: 'hello' }, () =>
896
+ Effect.sync(() => {
897
+ deactivated = true;
898
+ }),
899
+ ),
900
+ ),
901
+ }),
902
+ Plugin.make,
903
+ );
904
+ const testPlugin = Test();
905
+ plugins = [testPlugin];
906
+
907
+ const manager = PluginManager.make({ pluginLoader });
908
+ yield* manager.add(testMeta.id);
909
+ yield* manager.activate(ActivationEvents.Startup);
910
+ assert.isFalse(deactivated);
911
+
912
+ yield* manager.shutdown();
913
+ assert.isTrue(deactivated);
914
+ }),
915
+ );
916
+
917
+ it.effect('should deactivate modules in reverse activation order during shutdown', () =>
918
+ Effect.gen(function* () {
919
+ const deactivationOrder: string[] = [];
920
+ const Plugin1 = Plugin.define({ id: 'org.dxos.test.plugin-1', name: 'Plugin 1' }).pipe(
921
+ Plugin.addModule({
922
+ activatesOn: ActivationEvents.Startup,
923
+ id: 'First',
924
+ activate: () =>
925
+ Effect.succeed(
926
+ Capability.contributes(String, { string: 'first' }, () =>
927
+ Effect.sync(() => {
928
+ deactivationOrder.push('First');
929
+ }),
930
+ ),
931
+ ),
932
+ }),
933
+ Plugin.make,
934
+ );
935
+ const Plugin2 = Plugin.define({ id: 'org.dxos.test.plugin-2', name: 'Plugin 2' }).pipe(
936
+ Plugin.addModule({
937
+ activatesOn: ActivationEvents.Startup,
938
+ id: 'Second',
939
+ activate: () =>
940
+ Effect.succeed(
941
+ Capability.contributes(Number, { number: 2 }, () =>
942
+ Effect.sync(() => {
943
+ deactivationOrder.push('Second');
944
+ }),
945
+ ),
946
+ ),
947
+ }),
948
+ Plugin.make,
949
+ );
950
+ plugins = [Plugin1(), Plugin2()];
951
+
952
+ const manager = PluginManager.make({ pluginLoader });
953
+ yield* manager.add(Plugin1.meta.id);
954
+ yield* manager.add(Plugin2.meta.id);
955
+ yield* manager.activate(ActivationEvents.Startup);
956
+
957
+ yield* manager.shutdown();
958
+ assert.deepStrictEqual(deactivationOrder, ['Second', 'First']);
959
+ }),
960
+ );
961
+
962
+ it.effect('should clear lifecycle bookkeeping during shutdown', () =>
963
+ Effect.gen(function* () {
964
+ const Test = Plugin.define(testMeta).pipe(
965
+ Plugin.addModule({
966
+ id: 'Hello',
967
+ activatesOn: ActivationEvents.Startup,
968
+ activate: () => Effect.succeed(Capability.contributes(String, { string: 'hello' })),
969
+ }),
970
+ Plugin.make,
971
+ );
972
+ const testPlugin = Test();
973
+ plugins = [testPlugin];
974
+
975
+ const manager = PluginManager.make({ pluginLoader });
976
+ yield* manager.add(testMeta.id);
977
+ yield* manager.activate(ActivationEvents.Startup);
978
+ assert.isTrue(manager.getEventsFired().length > 0);
979
+
980
+ yield* manager.shutdown();
981
+ assert.deepStrictEqual(manager.getEventsFired(), []);
982
+ assert.deepStrictEqual(manager.getPendingReset(), []);
983
+ assert.deepStrictEqual(manager.getActive(), []);
984
+ }),
985
+ );
986
+
987
+ it.effect('should interrupt in-flight activation during shutdown', () =>
988
+ Effect.gen(function* () {
989
+ const activationStarted = yield* Effect.makeLatch(false);
990
+ const allowActivationToComplete = yield* Effect.makeLatch(false);
991
+ const Test = Plugin.define(testMeta).pipe(
992
+ Plugin.addModule({
993
+ id: 'Hello',
994
+ activatesOn: ActivationEvents.Startup,
995
+ activate: () =>
996
+ Effect.gen(function* () {
997
+ yield* activationStarted.open;
998
+ yield* allowActivationToComplete.await;
999
+ return Capability.contributes(String, { string: 'hello' });
1000
+ }),
1001
+ }),
1002
+ Plugin.make,
1003
+ );
1004
+ const testPlugin = Test();
1005
+ plugins = [testPlugin];
1006
+
1007
+ const manager = PluginManager.make({ pluginLoader });
1008
+ yield* manager.add(testMeta.id);
1009
+
1010
+ const activationFiber = yield* Effect.fork(manager.activate(ActivationEvents.Startup));
1011
+ yield* activationStarted.await;
1012
+
1013
+ const shutdownFiber = yield* Effect.fork(manager.shutdown());
1014
+ yield* allowActivationToComplete.open;
1015
+
1016
+ const shutdownResult = yield* Fiber.join(shutdownFiber);
1017
+ const activationExit = yield* Fiber.await(activationFiber);
1018
+
1019
+ assert.isTrue(shutdownResult);
1020
+ assert.isTrue(Exit.isFailure(activationExit));
1021
+ if (Exit.isFailure(activationExit)) {
1022
+ assert.isTrue(Cause.isInterruptedOnly(activationExit.cause));
1023
+ }
1024
+ assert.strictEqual(manager.capabilities.getAll(String).length, 0);
1025
+ assert.deepStrictEqual(manager.getActive(), []);
1026
+ assert.deepStrictEqual(manager.getEventsFired(), []);
1027
+ }),
1028
+ );
1029
+
1030
+ it.effect('should preserve plugins, core, enabled, and modules after shutdown', () =>
1031
+ Effect.gen(function* () {
1032
+ const Test = Plugin.define(testMeta).pipe(
1033
+ Plugin.addModule({
1034
+ id: 'Hello',
1035
+ activatesOn: ActivationEvents.Startup,
1036
+ activate: () => Effect.succeed(Capability.contributes(String, { string: 'hello' })),
1037
+ }),
1038
+ Plugin.make,
1039
+ );
1040
+ const testPlugin = Test();
1041
+ plugins = [testPlugin];
1042
+
1043
+ const manager = PluginManager.make({ pluginLoader });
1044
+ yield* manager.add(testMeta.id);
1045
+ yield* manager.activate(ActivationEvents.Startup);
1046
+
1047
+ const pluginsBefore = manager.getPlugins();
1048
+ const coreBefore = manager.getCore();
1049
+ const enabledBefore = manager.getEnabled();
1050
+ const modulesBefore = manager.getModules();
1051
+
1052
+ yield* manager.shutdown();
1053
+
1054
+ assert.deepStrictEqual(manager.getPlugins(), pluginsBefore);
1055
+ assert.deepStrictEqual(manager.getCore(), coreBefore);
1056
+ assert.deepStrictEqual(manager.getEnabled(), enabledBefore);
1057
+ assert.deepStrictEqual(manager.getModules(), modulesBefore);
1058
+ }),
1059
+ );
1060
+
1061
+ it.effect('should allow re-activation after shutdown', () =>
1062
+ Effect.gen(function* () {
1063
+ let activateCount = 0;
1064
+ const Test = Plugin.define(testMeta).pipe(
1065
+ Plugin.addModule({
1066
+ id: 'Hello',
1067
+ activatesOn: ActivationEvents.Startup,
1068
+ activate: () => {
1069
+ activateCount++;
1070
+ return Effect.succeed(Capability.contributes(String, { string: 'hello' }));
1071
+ },
1072
+ }),
1073
+ Plugin.make,
1074
+ );
1075
+ const testPlugin = Test();
1076
+ plugins = [testPlugin];
1077
+
1078
+ const manager = PluginManager.make({ pluginLoader });
1079
+ yield* manager.add(testMeta.id);
1080
+ yield* manager.activate(ActivationEvents.Startup);
1081
+ assert.strictEqual(activateCount, 1);
1082
+ assert.deepStrictEqual(manager.getActive(), [testPlugin.modules[0].id]);
1083
+
1084
+ yield* manager.shutdown();
1085
+ assert.deepStrictEqual(manager.getActive(), []);
1086
+
1087
+ yield* manager.activate(ActivationEvents.Startup);
1088
+ assert.strictEqual(activateCount, 2);
1089
+ assert.deepStrictEqual(manager.getActive(), [testPlugin.modules[0].id]);
1090
+ assert.strictEqual(manager.capabilities.getAll(String).length, 1);
1091
+ }),
1092
+ );
1093
+ });