@dxos/app-framework 0.8.4-main.b97322e → 0.8.4-main.bcb3aa67d6

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