@dxos/app-framework 0.10.0 → 0.11.1

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 (294) hide show
  1. package/.storybook/main.mts +1 -1
  2. package/.storybook/preview.mts +2 -2
  3. package/CHANGELOG.md +120 -0
  4. package/TASKS.md +11 -0
  5. package/dist/lib/chunk-SurfaceManager.mjs +960 -0
  6. package/dist/lib/chunk-SurfaceManager.mjs.map +1 -0
  7. package/dist/lib/chunk-capability.mjs +15 -0
  8. package/dist/lib/chunk-capability.mjs.map +1 -0
  9. package/dist/lib/{browser/chunk-FJ4765WW.mjs → chunk-context.mjs} +4 -5
  10. package/dist/lib/chunk-context.mjs.map +1 -0
  11. package/dist/lib/chunk-history-tracker.mjs +160 -0
  12. package/dist/lib/chunk-history-tracker.mjs.map +1 -0
  13. package/dist/lib/chunk-plugin-manager.mjs +1390 -0
  14. package/dist/lib/chunk-plugin-manager.mjs.map +1 -0
  15. package/dist/lib/chunk-plugin-manifest.mjs +125 -0
  16. package/dist/lib/chunk-plugin-manifest.mjs.map +1 -0
  17. package/dist/lib/chunk-process-manager-capability.mjs +126 -0
  18. package/dist/lib/chunk-process-manager-capability.mjs.map +1 -0
  19. package/dist/lib/chunk-rolldown-runtime.mjs +13 -0
  20. package/dist/lib/chunk-url-loader.mjs +347 -0
  21. package/dist/lib/chunk-url-loader.mjs.map +1 -0
  22. package/dist/lib/cli.mjs +78 -0
  23. package/dist/lib/cli.mjs.map +1 -0
  24. package/dist/lib/common/activation-events.mjs +35 -0
  25. package/dist/lib/common/activation-events.mjs.map +1 -0
  26. package/dist/lib/common/capabilities.mjs +184 -0
  27. package/dist/lib/common/capabilities.mjs.map +1 -0
  28. package/dist/lib/config.mjs +2 -0
  29. package/dist/lib/core/activation-event.mjs +55 -0
  30. package/dist/lib/core/activation-event.mjs.map +1 -0
  31. package/dist/lib/core/capability.mjs +189 -0
  32. package/dist/lib/core/capability.mjs.map +1 -0
  33. package/dist/lib/core/plugin-manager.mjs +2 -0
  34. package/dist/lib/core/plugin.mjs +340 -0
  35. package/dist/lib/core/plugin.mjs.map +1 -0
  36. package/dist/lib/core/url-loader.mjs +2 -0
  37. package/dist/lib/index.mjs +134 -0
  38. package/dist/lib/index.mjs.map +1 -0
  39. package/dist/lib/testing/react.mjs +70 -0
  40. package/dist/lib/testing/react.mjs.map +1 -0
  41. package/dist/lib/testing.mjs +399 -0
  42. package/dist/lib/testing.mjs.map +1 -0
  43. package/dist/lib/ui.mjs +636 -0
  44. package/dist/lib/ui.mjs.map +1 -0
  45. package/dist/lib/vite-plugin.mjs +986 -0
  46. package/dist/lib/vite-plugin.mjs.map +1 -0
  47. package/dist/plugin/node-esm/index.mjs +4 -3
  48. package/dist/plugin/node-esm/index.mjs.map +3 -3
  49. package/dist/plugin/node-esm/meta.json +1 -1
  50. package/dist/types/src/common/Role.d.ts +31 -0
  51. package/dist/types/src/common/Role.d.ts.map +1 -0
  52. package/dist/types/src/common/Role.test.d.ts +2 -0
  53. package/dist/types/src/common/Role.test.d.ts.map +1 -0
  54. package/dist/types/src/common/capabilities.d.ts +11 -2
  55. package/dist/types/src/common/capabilities.d.ts.map +1 -1
  56. package/dist/types/src/common/index.d.ts +1 -0
  57. package/dist/types/src/common/index.d.ts.map +1 -1
  58. package/dist/types/src/core/capability-manager.d.ts +1 -1
  59. package/dist/types/src/core/capability-manager.d.ts.map +1 -1
  60. package/dist/types/src/core/capability.d.ts +38 -5
  61. package/dist/types/src/core/capability.d.ts.map +1 -1
  62. package/dist/types/src/core/plugin-manager.d.ts +1 -1
  63. package/dist/types/src/core/plugin-manager.d.ts.map +1 -1
  64. package/dist/types/src/core/plugin.d.ts +14 -1
  65. package/dist/types/src/core/plugin.d.ts.map +1 -1
  66. package/dist/types/src/core/registry.d.ts +1 -1
  67. package/dist/types/src/core/registry.d.ts.map +1 -1
  68. package/dist/types/src/plugin-process-manager/history/capability.d.ts +1 -1
  69. package/dist/types/src/plugin-process-manager/process-manager-capability.d.ts +1 -1
  70. package/dist/types/src/plugin-process-manager/process-manager-capability.d.ts.map +1 -1
  71. package/dist/types/src/testing/StorybookErrorFallback.d.ts +15 -0
  72. package/dist/types/src/testing/StorybookErrorFallback.d.ts.map +1 -0
  73. package/dist/types/src/testing/harness.d.ts +1 -1
  74. package/dist/types/src/testing/harness.d.ts.map +1 -1
  75. package/dist/types/src/testing/index.d.ts +2 -0
  76. package/dist/types/src/testing/index.d.ts.map +1 -1
  77. package/dist/types/src/testing/react.d.ts +4 -4
  78. package/dist/types/src/testing/react.d.ts.map +1 -1
  79. package/dist/types/src/testing/withPluginManager.d.ts.map +1 -1
  80. package/dist/types/src/testing/withPluginManager.stories.d.ts +6 -0
  81. package/dist/types/src/testing/withPluginManager.stories.d.ts.map +1 -1
  82. package/dist/types/src/testing/withSurfaceDebug.d.ts +10 -0
  83. package/dist/types/src/testing/withSurfaceDebug.d.ts.map +1 -0
  84. package/dist/types/src/ui/components/App/App.d.ts +0 -32
  85. package/dist/types/src/ui/components/App/App.d.ts.map +1 -1
  86. package/dist/types/src/ui/components/App/index.d.ts +1 -0
  87. package/dist/types/src/ui/components/App/index.d.ts.map +1 -1
  88. package/dist/types/src/ui/components/App/loader.d.ts +35 -0
  89. package/dist/types/src/ui/components/App/loader.d.ts.map +1 -0
  90. package/dist/types/src/ui/components/HomeSection/HomeSection.d.ts +26 -0
  91. package/dist/types/src/ui/components/HomeSection/HomeSection.d.ts.map +1 -0
  92. package/dist/types/src/ui/components/HomeSection/HomeSection.stories.d.ts +15 -0
  93. package/dist/types/src/ui/components/HomeSection/HomeSection.stories.d.ts.map +1 -0
  94. package/dist/types/src/ui/components/HomeSection/index.d.ts +2 -0
  95. package/dist/types/src/ui/components/HomeSection/index.d.ts.map +1 -0
  96. package/dist/types/src/ui/components/NamePopover/NamePopover.d.ts +14 -0
  97. package/dist/types/src/ui/components/NamePopover/NamePopover.d.ts.map +1 -0
  98. package/dist/types/src/ui/components/NamePopover/index.d.ts +2 -0
  99. package/dist/types/src/ui/components/NamePopover/index.d.ts.map +1 -0
  100. package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts +21 -9
  101. package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts.map +1 -1
  102. package/dist/types/src/ui/components/Surface/SurfaceComponent.stories.d.ts +4 -1
  103. package/dist/types/src/ui/components/Surface/SurfaceComponent.stories.d.ts.map +1 -1
  104. package/dist/types/src/ui/components/Surface/SurfaceComponent.test.d.ts +2 -0
  105. package/dist/types/src/ui/components/Surface/SurfaceComponent.test.d.ts.map +1 -0
  106. package/dist/types/src/ui/components/Surface/SurfaceDebug.d.ts +40 -0
  107. package/dist/types/src/ui/components/Surface/SurfaceDebug.d.ts.map +1 -0
  108. package/dist/types/src/ui/components/Surface/SurfaceManager.d.ts +23 -0
  109. package/dist/types/src/ui/components/Surface/SurfaceManager.d.ts.map +1 -0
  110. package/dist/types/src/ui/components/Surface/SurfaceManagerContext.d.ts +11 -0
  111. package/dist/types/src/ui/components/Surface/SurfaceManagerContext.d.ts.map +1 -0
  112. package/dist/types/src/ui/components/Surface/SurfaceMetrics.d.ts +72 -0
  113. package/dist/types/src/ui/components/Surface/SurfaceMetrics.d.ts.map +1 -0
  114. package/dist/types/src/ui/components/Surface/SurfaceProfilerContext.d.ts.map +1 -1
  115. package/dist/types/src/ui/components/Surface/index.d.ts +20 -25
  116. package/dist/types/src/ui/components/Surface/index.d.ts.map +1 -1
  117. package/dist/types/src/ui/components/Surface/types.d.ts +56 -52
  118. package/dist/types/src/ui/components/Surface/types.d.ts.map +1 -1
  119. package/dist/types/src/ui/components/index.d.ts +2 -0
  120. package/dist/types/src/ui/components/index.d.ts.map +1 -1
  121. package/dist/types/src/ui/hooks/useApp.d.ts.map +1 -1
  122. package/dist/types/tsconfig.tsbuildinfo +1 -1
  123. package/moon.yml +25 -18
  124. package/package.json +58 -73
  125. package/src/common/Role.test.ts +41 -0
  126. package/src/common/Role.ts +54 -0
  127. package/src/common/capabilities.ts +17 -2
  128. package/src/common/index.ts +1 -0
  129. package/src/core/capability-manager.test.ts +1 -1
  130. package/src/core/capability-manager.ts +1 -1
  131. package/src/core/capability.ts +45 -6
  132. package/src/core/plugin-manager.test.ts +38 -3
  133. package/src/core/plugin-manager.ts +18 -1
  134. package/src/core/plugin.ts +16 -1
  135. package/src/core/registry.ts +1 -1
  136. package/src/plugin-process-manager/process-manager-capability.ts +25 -2
  137. package/src/testing/StorybookErrorFallback.tsx +40 -0
  138. package/src/testing/harness.ts +1 -1
  139. package/src/testing/index.ts +2 -0
  140. package/src/testing/react.test.tsx +2 -1
  141. package/src/testing/react.tsx +14 -8
  142. package/src/testing/withPluginManager.stories.tsx +16 -1
  143. package/src/testing/withPluginManager.tsx +13 -2
  144. package/src/testing/withSurfaceDebug.tsx +22 -0
  145. package/src/ui/components/App/App.tsx +7 -35
  146. package/src/ui/components/App/index.ts +1 -0
  147. package/src/ui/components/App/loader.ts +39 -0
  148. package/src/ui/components/HomeSection/HomeSection.stories.tsx +43 -0
  149. package/src/ui/components/HomeSection/HomeSection.tsx +69 -0
  150. package/src/ui/components/HomeSection/index.ts +5 -0
  151. package/src/ui/components/NamePopover/NamePopover.tsx +70 -0
  152. package/src/ui/components/NamePopover/index.ts +5 -0
  153. package/src/ui/components/Surface/DESIGN.md +136 -0
  154. package/src/ui/components/Surface/SurfaceComponent.stories.tsx +70 -41
  155. package/src/ui/components/Surface/SurfaceComponent.test.tsx +403 -0
  156. package/src/ui/components/Surface/SurfaceComponent.tsx +209 -189
  157. package/src/ui/components/Surface/SurfaceDebug.tsx +435 -0
  158. package/src/ui/components/Surface/SurfaceManager.ts +95 -0
  159. package/src/ui/components/Surface/SurfaceManagerContext.ts +23 -0
  160. package/src/ui/components/Surface/SurfaceMetrics.ts +202 -0
  161. package/src/ui/components/Surface/SurfaceProfilerContext.tsx +12 -2
  162. package/src/ui/components/Surface/index.ts +24 -22
  163. package/src/ui/components/Surface/types.test.ts +47 -54
  164. package/src/ui/components/Surface/types.ts +66 -91
  165. package/src/ui/components/index.ts +2 -0
  166. package/src/ui/hooks/useApp.tsx +7 -3
  167. package/src/vite-plugin/boot-loader/loader-app/boot-loader.css +1 -1
  168. package/tsconfig.json +3 -0
  169. package/vite.config.ts +26 -0
  170. package/dist/lib/browser/capability-S44TE2H7.mjs +0 -35
  171. package/dist/lib/browser/capability-S44TE2H7.mjs.map +0 -7
  172. package/dist/lib/browser/chunk-3NQLTQCP.mjs +0 -47
  173. package/dist/lib/browser/chunk-3NQLTQCP.mjs.map +0 -7
  174. package/dist/lib/browser/chunk-53HJHMS5.mjs +0 -7
  175. package/dist/lib/browser/chunk-53HJHMS5.mjs.map +0 -7
  176. package/dist/lib/browser/chunk-FJ4765WW.mjs.map +0 -7
  177. package/dist/lib/browser/chunk-J5LGTIGS.mjs +0 -10
  178. package/dist/lib/browser/chunk-J5LGTIGS.mjs.map +0 -7
  179. package/dist/lib/browser/chunk-KRJ4KK2W.mjs +0 -505
  180. package/dist/lib/browser/chunk-KRJ4KK2W.mjs.map +0 -7
  181. package/dist/lib/browser/chunk-LUH4Y5F2.mjs +0 -430
  182. package/dist/lib/browser/chunk-LUH4Y5F2.mjs.map +0 -7
  183. package/dist/lib/browser/chunk-NKVRSMFN.mjs +0 -95
  184. package/dist/lib/browser/chunk-NKVRSMFN.mjs.map +0 -7
  185. package/dist/lib/browser/chunk-OO53M5UK.mjs +0 -260
  186. package/dist/lib/browser/chunk-OO53M5UK.mjs.map +0 -7
  187. package/dist/lib/browser/chunk-OPTS3ZSN.mjs +0 -83
  188. package/dist/lib/browser/chunk-OPTS3ZSN.mjs.map +0 -7
  189. package/dist/lib/browser/chunk-Q2GLJTVV.mjs +0 -12
  190. package/dist/lib/browser/chunk-Q2GLJTVV.mjs.map +0 -7
  191. package/dist/lib/browser/chunk-SYXIYT6T.mjs +0 -143
  192. package/dist/lib/browser/chunk-SYXIYT6T.mjs.map +0 -7
  193. package/dist/lib/browser/chunk-U5MESPM5.mjs +0 -603
  194. package/dist/lib/browser/chunk-U5MESPM5.mjs.map +0 -7
  195. package/dist/lib/browser/chunk-UGYCLOXE.mjs +0 -1471
  196. package/dist/lib/browser/chunk-UGYCLOXE.mjs.map +0 -7
  197. package/dist/lib/browser/chunk-VF37YTXJ.mjs +0 -28
  198. package/dist/lib/browser/chunk-VF37YTXJ.mjs.map +0 -7
  199. package/dist/lib/browser/chunk-YUVQIOTB.mjs +0 -60
  200. package/dist/lib/browser/chunk-YUVQIOTB.mjs.map +0 -7
  201. package/dist/lib/browser/cli/index.mjs +0 -74
  202. package/dist/lib/browser/cli/index.mjs.map +0 -7
  203. package/dist/lib/browser/common/activation-events.mjs +0 -20
  204. package/dist/lib/browser/common/activation-events.mjs.map +0 -7
  205. package/dist/lib/browser/common/capabilities.mjs +0 -56
  206. package/dist/lib/browser/common/capabilities.mjs.map +0 -7
  207. package/dist/lib/browser/config/index.mjs +0 -8
  208. package/dist/lib/browser/config/index.mjs.map +0 -7
  209. package/dist/lib/browser/core/activation-event.mjs +0 -20
  210. package/dist/lib/browser/core/activation-event.mjs.map +0 -7
  211. package/dist/lib/browser/core/capability.mjs +0 -32
  212. package/dist/lib/browser/core/capability.mjs.map +0 -7
  213. package/dist/lib/browser/core/plugin-manager.mjs +0 -19
  214. package/dist/lib/browser/core/plugin-manager.mjs.map +0 -7
  215. package/dist/lib/browser/core/plugin.mjs +0 -51
  216. package/dist/lib/browser/core/plugin.mjs.map +0 -7
  217. package/dist/lib/browser/core/url-loader.mjs +0 -24
  218. package/dist/lib/browser/core/url-loader.mjs.map +0 -7
  219. package/dist/lib/browser/index.mjs +0 -98
  220. package/dist/lib/browser/index.mjs.map +0 -7
  221. package/dist/lib/browser/meta.json +0 -1
  222. package/dist/lib/browser/process-manager-capability-QK4BMKMN.mjs +0 -132
  223. package/dist/lib/browser/process-manager-capability-QK4BMKMN.mjs.map +0 -7
  224. package/dist/lib/browser/testing/index.mjs +0 -359
  225. package/dist/lib/browser/testing/index.mjs.map +0 -7
  226. package/dist/lib/browser/testing/react.mjs +0 -78
  227. package/dist/lib/browser/testing/react.mjs.map +0 -7
  228. package/dist/lib/browser/ui/index.mjs +0 -60
  229. package/dist/lib/browser/ui/index.mjs.map +0 -7
  230. package/dist/lib/node-esm/capability-3YRF77LV.mjs +0 -36
  231. package/dist/lib/node-esm/capability-3YRF77LV.mjs.map +0 -7
  232. package/dist/lib/node-esm/chunk-2UPUZXGU.mjs +0 -9
  233. package/dist/lib/node-esm/chunk-2UPUZXGU.mjs.map +0 -7
  234. package/dist/lib/node-esm/chunk-37Z53PXZ.mjs +0 -10
  235. package/dist/lib/node-esm/chunk-37Z53PXZ.mjs.map +0 -7
  236. package/dist/lib/node-esm/chunk-4SE7SX7N.mjs +0 -261
  237. package/dist/lib/node-esm/chunk-4SE7SX7N.mjs.map +0 -7
  238. package/dist/lib/node-esm/chunk-BMW6P6QA.mjs +0 -48
  239. package/dist/lib/node-esm/chunk-BMW6P6QA.mjs.map +0 -7
  240. package/dist/lib/node-esm/chunk-COHOWGGJ.mjs +0 -144
  241. package/dist/lib/node-esm/chunk-COHOWGGJ.mjs.map +0 -7
  242. package/dist/lib/node-esm/chunk-DRT75WGD.mjs +0 -604
  243. package/dist/lib/node-esm/chunk-DRT75WGD.mjs.map +0 -7
  244. package/dist/lib/node-esm/chunk-FKMVORZN.mjs +0 -431
  245. package/dist/lib/node-esm/chunk-FKMVORZN.mjs.map +0 -7
  246. package/dist/lib/node-esm/chunk-FPW45EZH.mjs +0 -14
  247. package/dist/lib/node-esm/chunk-FPW45EZH.mjs.map +0 -7
  248. package/dist/lib/node-esm/chunk-GLH4LGHZ.mjs +0 -84
  249. package/dist/lib/node-esm/chunk-GLH4LGHZ.mjs.map +0 -7
  250. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
  251. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +0 -7
  252. package/dist/lib/node-esm/chunk-IL7O7IRX.mjs +0 -1472
  253. package/dist/lib/node-esm/chunk-IL7O7IRX.mjs.map +0 -7
  254. package/dist/lib/node-esm/chunk-ND2HETHD.mjs +0 -506
  255. package/dist/lib/node-esm/chunk-ND2HETHD.mjs.map +0 -7
  256. package/dist/lib/node-esm/chunk-TLKYZKPW.mjs +0 -29
  257. package/dist/lib/node-esm/chunk-TLKYZKPW.mjs.map +0 -7
  258. package/dist/lib/node-esm/chunk-VCPGX7WN.mjs +0 -62
  259. package/dist/lib/node-esm/chunk-VCPGX7WN.mjs.map +0 -7
  260. package/dist/lib/node-esm/chunk-XSZMX53L.mjs +0 -96
  261. package/dist/lib/node-esm/chunk-XSZMX53L.mjs.map +0 -7
  262. package/dist/lib/node-esm/cli/index.mjs +0 -75
  263. package/dist/lib/node-esm/cli/index.mjs.map +0 -7
  264. package/dist/lib/node-esm/common/activation-events.mjs +0 -21
  265. package/dist/lib/node-esm/common/activation-events.mjs.map +0 -7
  266. package/dist/lib/node-esm/common/capabilities.mjs +0 -57
  267. package/dist/lib/node-esm/common/capabilities.mjs.map +0 -7
  268. package/dist/lib/node-esm/config/index.mjs +0 -9
  269. package/dist/lib/node-esm/config/index.mjs.map +0 -7
  270. package/dist/lib/node-esm/core/activation-event.mjs +0 -21
  271. package/dist/lib/node-esm/core/activation-event.mjs.map +0 -7
  272. package/dist/lib/node-esm/core/capability.mjs +0 -33
  273. package/dist/lib/node-esm/core/capability.mjs.map +0 -7
  274. package/dist/lib/node-esm/core/plugin-manager.mjs +0 -20
  275. package/dist/lib/node-esm/core/plugin-manager.mjs.map +0 -7
  276. package/dist/lib/node-esm/core/plugin.mjs +0 -52
  277. package/dist/lib/node-esm/core/plugin.mjs.map +0 -7
  278. package/dist/lib/node-esm/core/url-loader.mjs +0 -25
  279. package/dist/lib/node-esm/core/url-loader.mjs.map +0 -7
  280. package/dist/lib/node-esm/index.mjs +0 -99
  281. package/dist/lib/node-esm/index.mjs.map +0 -7
  282. package/dist/lib/node-esm/meta.json +0 -1
  283. package/dist/lib/node-esm/process-manager-capability-5S7WZ2JR.mjs +0 -133
  284. package/dist/lib/node-esm/process-manager-capability-5S7WZ2JR.mjs.map +0 -7
  285. package/dist/lib/node-esm/testing/index.mjs +0 -360
  286. package/dist/lib/node-esm/testing/index.mjs.map +0 -7
  287. package/dist/lib/node-esm/testing/react.mjs +0 -79
  288. package/dist/lib/node-esm/testing/react.mjs.map +0 -7
  289. package/dist/lib/node-esm/ui/index.mjs +0 -61
  290. package/dist/lib/node-esm/ui/index.mjs.map +0 -7
  291. package/dist/types/src/ui/components/Surface/SurfaceInfo.d.ts +0 -11
  292. package/dist/types/src/ui/components/Surface/SurfaceInfo.d.ts.map +0 -1
  293. package/src/ui/components/Surface/SurfaceInfo.tsx +0 -106
  294. package/vitest.config.ts +0 -14
@@ -0,0 +1,1390 @@
1
+ import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
2
+ import { eventKey, getEvents, isAllOf } from "./core/activation-event.mjs";
3
+ import { Service } from "./core/capability.mjs";
4
+ import { LazyPluginError, PluginDependencyError, Service as Service$1, isLazy, resolveLazy } from "./core/plugin.mjs";
5
+ import * as Effect from "effect/Effect";
6
+ import { Atom, Registry } from "@effect-atom/atom";
7
+ import * as Deferred from "effect/Deferred";
8
+ import { invariant } from "@dxos/invariant";
9
+ import { log } from "@dxos/log";
10
+ import { BaseError } from "@dxos/errors";
11
+ import * as Array from "effect/Array";
12
+ import * as Cause from "effect/Cause";
13
+ import * as Duration from "effect/Duration";
14
+ import * as Exit from "effect/Exit";
15
+ import * as Fiber from "effect/Fiber";
16
+ import * as Function from "effect/Function";
17
+ import * as HashSet from "effect/HashSet";
18
+ import * as PubSub from "effect/PubSub";
19
+ import * as Ref from "effect/Ref";
20
+ import * as Scope from "effect/Scope";
21
+ import { EffectEx, Performance } from "@dxos/effect";
22
+ //#region src/core/capability-manager.ts
23
+ var capability_manager_exports = /* @__PURE__ */ __exportAll({ make: () => make$1 });
24
+ var __dxlog_file$2 = "/__w/dxos/dxos/packages/sdk/app-framework/src/core/capability-manager.ts";
25
+ /**
26
+ * Internal implementation of CapabilityManager.
27
+ */
28
+ var CapabilityManagerImpl = class {
29
+ _registry;
30
+ _registeredIdentifiers = /* @__PURE__ */ new Set();
31
+ _capabilityEntries = Atom.family(() => {
32
+ return Atom.make([]).pipe(Atom.keepAlive);
33
+ });
34
+ _capabilities = Atom.family((id) => {
35
+ return Atom.make((get) => {
36
+ return get(this._capabilityEntries(id)).map((c) => c.implementation);
37
+ }).pipe(Atom.keepAlive);
38
+ });
39
+ _capabilitiesByModule = Atom.family((id) => {
40
+ return Atom.make((get) => {
41
+ const entries = get(this._capabilityEntries(id));
42
+ const result = {};
43
+ for (const entry of entries) (result[entry.moduleId] ??= []).push(entry.implementation);
44
+ return result;
45
+ }).pipe(Atom.keepAlive);
46
+ });
47
+ _capability = Atom.family((id) => {
48
+ return Atom.make((get) => {
49
+ const current = get(this._capabilities(id));
50
+ invariant(current.length > 0, `No capability found for ${id}`, {
51
+ "~LogMeta": "~LogMeta",
52
+ F: __dxlog_file$2,
53
+ L: 108,
54
+ S: this,
55
+ A: ["current.length > 0", "`No capability found for ${id}`"]
56
+ });
57
+ return current[0];
58
+ });
59
+ });
60
+ constructor({ registry }) {
61
+ this._registry = registry;
62
+ }
63
+ contribute({ module: moduleId, interface: interfaceDef, implementation }) {
64
+ const current = this._registry.get(this._capabilityEntries(interfaceDef.identifier));
65
+ if (current.some((c) => c.moduleId === moduleId && c.implementation === implementation)) {
66
+ log("capability already contributed, skipping", {
67
+ id: interfaceDef.identifier,
68
+ moduleId
69
+ }, {
70
+ "~LogMeta": "~LogMeta",
71
+ F: __dxlog_file$2,
72
+ L: 129,
73
+ S: this
74
+ });
75
+ return;
76
+ }
77
+ const entry = {
78
+ moduleId,
79
+ implementation
80
+ };
81
+ this._registry.set(this._capabilityEntries(interfaceDef.identifier), [...current, entry]);
82
+ this._registeredIdentifiers.add(interfaceDef.identifier);
83
+ log("capability contributed", {
84
+ id: interfaceDef.identifier,
85
+ moduleId,
86
+ count: current.length
87
+ }, {
88
+ "~LogMeta": "~LogMeta",
89
+ F: __dxlog_file$2,
90
+ L: 136,
91
+ S: this
92
+ });
93
+ }
94
+ remove(interfaceDef, implementation) {
95
+ const current = this._registry.get(this._capabilityEntries(interfaceDef.identifier));
96
+ if (current.length === 0) return;
97
+ const next = current.filter((c) => c.implementation !== implementation);
98
+ if (next.length !== current.length) {
99
+ this._registry.set(this._capabilityEntries(interfaceDef.identifier), next);
100
+ if (next.length === 0) this._registeredIdentifiers.delete(interfaceDef.identifier);
101
+ log("capability removed", {
102
+ id: interfaceDef.identifier,
103
+ count: current.length
104
+ }, {
105
+ "~LogMeta": "~LogMeta",
106
+ F: __dxlog_file$2,
107
+ L: 155,
108
+ S: this
109
+ });
110
+ } else log.warn("capability not removed", { id: interfaceDef.identifier }, {
111
+ "~LogMeta": "~LogMeta",
112
+ F: __dxlog_file$2,
113
+ L: 157,
114
+ S: this
115
+ });
116
+ }
117
+ atom(interfaceDef) {
118
+ return this._capabilities(interfaceDef.identifier);
119
+ }
120
+ getAll(interfaceDef) {
121
+ return this._registry.get(this.atom(interfaceDef));
122
+ }
123
+ get(interfaceDef) {
124
+ const capabilities = this.getAll(interfaceDef);
125
+ if (capabilities.length === 0) {
126
+ log("capability not available", {
127
+ requested: interfaceDef.identifier,
128
+ registered: this.listRegisteredIdentifiers()
129
+ }, {
130
+ "~LogMeta": "~LogMeta",
131
+ F: __dxlog_file$2,
132
+ L: 173,
133
+ S: this
134
+ });
135
+ invariant(capabilities.length > 0, `No capability found for ${interfaceDef.identifier}`, {
136
+ "~LogMeta": "~LogMeta",
137
+ F: __dxlog_file$2,
138
+ L: 177,
139
+ S: this,
140
+ A: ["capabilities.length > 0", "`No capability found for ${interfaceDef.identifier}`"]
141
+ });
142
+ }
143
+ return capabilities[0];
144
+ }
145
+ listRegisteredIdentifiers() {
146
+ return [...this._registeredIdentifiers].sort();
147
+ }
148
+ waitFor(interfaceDef) {
149
+ return Effect.gen(this, function* () {
150
+ const [capability] = this.getAll(interfaceDef);
151
+ if (capability) return capability;
152
+ const deferred = yield* Deferred.make();
153
+ const cancel = this._registry.subscribe(this.atom(interfaceDef), (capabilities) => {
154
+ if (capabilities.length > 0) Effect.runSync(Deferred.succeed(deferred, capabilities[0]));
155
+ });
156
+ const result = yield* Deferred.await(deferred);
157
+ cancel();
158
+ return result;
159
+ });
160
+ }
161
+ atomByModule(interfaceDef) {
162
+ return this._capabilitiesByModule(interfaceDef.identifier);
163
+ }
164
+ };
165
+ /**
166
+ * Creates a new Capability Manager instance.
167
+ */
168
+ var make$1 = (options) => new CapabilityManagerImpl(options);
169
+ //#endregion
170
+ //#region src/core/registry.ts
171
+ var registry_exports = /* @__PURE__ */ __exportAll({ Manager: () => Manager });
172
+ var __dxlog_file$1 = "/__w/dxos/dxos/packages/sdk/app-framework/src/core/registry.ts";
173
+ /**
174
+ * Default no-op provider used when no `PluginProvider` is supplied at construction
175
+ * time (e.g. tests, stories, environments without an Edge connection).
176
+ *
177
+ * `listPlugins` returns an empty list (so `Manager.plugins` settles to a stable
178
+ * empty state). `listVersions` and `getPlugin` fail with an explicit error so
179
+ * callers know they need to configure a real provider.
180
+ */
181
+ var NULL_PROVIDER = {
182
+ listPlugins: () => Effect.succeed([]),
183
+ listVersions: () => Effect.fail(/* @__PURE__ */ new Error("No plugin registry provider configured")),
184
+ getPlugin: () => Effect.fail(/* @__PURE__ */ new Error("No plugin registry provider configured"))
185
+ };
186
+ /**
187
+ * Owns the cached registry catalog state and forwards `listVersions` / `getPlugin`
188
+ * calls to the underlying {@link PluginProvider}. Lives on the `PluginManager`
189
+ * (as `manager.pluginRegistry`) so consumers can reach it through the manager
190
+ * without a separate capability lookup.
191
+ *
192
+ * On construction the manager kicks off a background fetch via `provider.listPlugins()`
193
+ * and writes the result into the {@link plugins} atom. Subscribers see `loading: true`
194
+ * during the fetch and `loading: false` once it settles.
195
+ */
196
+ var Manager = class {
197
+ plugins;
198
+ #provider;
199
+ constructor(provider, atomRegistry) {
200
+ this.#provider = provider ?? NULL_PROVIDER;
201
+ const initialLoading = provider !== void 0;
202
+ this.plugins = Atom.make({
203
+ entries: [],
204
+ loading: initialLoading,
205
+ error: null
206
+ }).pipe(Atom.keepAlive);
207
+ if (provider !== void 0) EffectEx.runAndForwardErrors(provider.listPlugins().pipe(Effect.match({
208
+ onSuccess: (entries) => atomRegistry.set(this.plugins, {
209
+ entries,
210
+ loading: false,
211
+ error: null
212
+ }),
213
+ onFailure: (error) => {
214
+ log.catch(error, void 0, {
215
+ "~LogMeta": "~LogMeta",
216
+ F: __dxlog_file$1,
217
+ L: 100,
218
+ S: this
219
+ });
220
+ atomRegistry.set(this.plugins, {
221
+ entries: [],
222
+ loading: false,
223
+ error
224
+ });
225
+ }
226
+ })));
227
+ }
228
+ /** Forwards to the underlying provider. */
229
+ listPlugins() {
230
+ return this.#provider.listPlugins();
231
+ }
232
+ /** Forwards to the underlying provider. */
233
+ listVersions(id) {
234
+ return this.#provider.listVersions(id);
235
+ }
236
+ /** Forwards to the underlying provider. */
237
+ getPlugin(id, version) {
238
+ return this.#provider.getPlugin(id, version);
239
+ }
240
+ };
241
+ //#endregion
242
+ //#region src/core/plugin-manager.ts
243
+ var plugin_manager_exports = /* @__PURE__ */ __exportAll({
244
+ ManagerTypeId: () => ManagerTypeId,
245
+ PluginInitializationError: () => PluginInitializationError,
246
+ PluginTimeoutError: () => PluginTimeoutError,
247
+ isManager: () => isManager,
248
+ make: () => make
249
+ });
250
+ var __dxlog_file = "/__w/dxos/dxos/packages/sdk/app-framework/src/core/plugin-manager.ts";
251
+ /**
252
+ * Tagged error for failures during the constructor-launched core/enabled
253
+ * `enable()` chain. Surfaces via {@link PluginManager.activate}'s wait on
254
+ * `_initialization` so a caller blocked on initialization gets a typed
255
+ * failure (with the original error preserved as `cause`) instead of an
256
+ * untyped `Error`.
257
+ */
258
+ var PluginInitializationError = class extends BaseError.extend("PluginInitializationError", "Plugin manager initialization failed") {};
259
+ /**
260
+ * Tagged error raised when a plugin exceeds its configured load or activation
261
+ * timeout. The plugin manager records the failure on the `failed` atom and
262
+ * auto-disables the plugin so that one stuck remote does not stall app boot.
263
+ * `context.id` is the plugin id, `context.phase` is `'load'` or `'activation'`.
264
+ */
265
+ var PluginTimeoutError = class extends BaseError.extend("PluginTimeoutError", "Plugin operation timed out") {};
266
+ /** Default deadline for resolving a lazy plugin's dynamic import. */
267
+ var DEFAULT_LOAD_TIMEOUT = Duration.seconds(30);
268
+ /** Default deadline for a single module's `activate()` body. */
269
+ var DEFAULT_ACTIVATION_TIMEOUT = Duration.seconds(30);
270
+ /**
271
+ * Identifier denoting a Manager.
272
+ */
273
+ var ManagerTypeId = Symbol.for("@dxos/app-framework/Manager");
274
+ /**
275
+ * Type guard to check if a value is a PluginManager.
276
+ */
277
+ var isManager = (value) => {
278
+ return typeof value === "object" && value !== null && ManagerTypeId in value;
279
+ };
280
+ /**
281
+ * Internal implementation of PluginManager.
282
+ */
283
+ var ManagerImpl = class {
284
+ [ManagerTypeId] = ManagerTypeId;
285
+ activation = Effect.runSync(PubSub.unbounded());
286
+ capabilities;
287
+ registry;
288
+ pluginRegistry;
289
+ _pluginsAtom;
290
+ _coreAtom;
291
+ _enabledAtom;
292
+ _modulesAtom;
293
+ _activeAtom;
294
+ _eventsFiredAtom;
295
+ _pendingResetAtom;
296
+ _failedAtom;
297
+ _pluginLoader;
298
+ _onRemove;
299
+ _loadTimeout;
300
+ _activationTimeout;
301
+ _capabilities = /* @__PURE__ */ new Map();
302
+ _moduleScopes = /* @__PURE__ */ new Map();
303
+ _moduleMemoMap = /* @__PURE__ */ new Map();
304
+ _moduleSemaphores = /* @__PURE__ */ new Map();
305
+ _resolvingPlugins = /* @__PURE__ */ new Map();
306
+ _devPlugins = /* @__PURE__ */ new Map();
307
+ _devPluginIdsAtom;
308
+ _activatingEvents = Effect.runSync(Ref.make([]));
309
+ _activatingModules = Effect.runSync(Ref.make([]));
310
+ _inFlightFibers = Effect.runSync(Ref.make([]));
311
+ _shutdownSemaphore = Effect.runSync(Effect.makeSemaphore(1));
312
+ _shuttingDown = Effect.runSync(Ref.make(false));
313
+ _initialization = Effect.runSync(Deferred.make());
314
+ constructor({ pluginLoader, plugins = [], enabled = [], registry, pluginRegistryProvider, onRemove, loadTimeout = DEFAULT_LOAD_TIMEOUT, activationTimeout = DEFAULT_ACTIVATION_TIMEOUT }) {
315
+ const core = plugins.filter(({ meta }) => meta.profile.tags?.includes("system")).map(({ meta }) => meta.profile.key);
316
+ this.registry = registry ?? Registry.make();
317
+ this.capabilities = make$1({ registry: this.registry });
318
+ this.pluginRegistry = new Manager(pluginRegistryProvider, this.registry);
319
+ this._pluginLoader = pluginLoader;
320
+ this._onRemove = onRemove;
321
+ this._loadTimeout = loadTimeout;
322
+ this._activationTimeout = activationTimeout;
323
+ this._pluginsAtom = Atom.make(plugins).pipe(Atom.keepAlive);
324
+ this._coreAtom = Atom.make(core).pipe(Atom.keepAlive);
325
+ this._enabledAtom = Atom.make(enabled).pipe(Atom.keepAlive);
326
+ this._modulesAtom = Atom.make([]).pipe(Atom.keepAlive);
327
+ this._activeAtom = Atom.make([]).pipe(Atom.keepAlive);
328
+ this._eventsFiredAtom = Atom.make([]).pipe(Atom.keepAlive);
329
+ this._pendingResetAtom = Atom.make([]).pipe(Atom.keepAlive);
330
+ this._failedAtom = Atom.make([]).pipe(Atom.keepAlive);
331
+ this._devPluginIdsAtom = Atom.make([]).pipe(Atom.keepAlive);
332
+ plugins.forEach((plugin) => this._addPlugin(plugin));
333
+ const initialIds = [.../* @__PURE__ */ new Set([...core, ...enabled])];
334
+ Effect.all(initialIds.map((id) => this.enable(id))).pipe(Effect.mapError((cause) => new PluginInitializationError({ cause })), Effect.tap(() => Deferred.succeed(this._initialization, void 0)), Effect.tapErrorCause((cause) => Deferred.failCause(this._initialization, cause))).pipe(EffectEx.runAndForwardErrors);
335
+ }
336
+ get plugins() {
337
+ return this._pluginsAtom;
338
+ }
339
+ get core() {
340
+ return this._coreAtom;
341
+ }
342
+ /**
343
+ * Ids of plugins that are currently enabled.
344
+ */
345
+ get enabled() {
346
+ return this._enabledAtom;
347
+ }
348
+ /**
349
+ * Modules of plugins which are currently enabled.
350
+ */
351
+ get modules() {
352
+ return this._modulesAtom;
353
+ }
354
+ /**
355
+ * Ids of modules which are currently active.
356
+ */
357
+ get active() {
358
+ return this._activeAtom;
359
+ }
360
+ /**
361
+ * Ids of events which have been fired.
362
+ */
363
+ get eventsFired() {
364
+ return this._eventsFiredAtom;
365
+ }
366
+ /**
367
+ * Ids of modules which are pending reset.
368
+ */
369
+ get pendingReset() {
370
+ return this._pendingResetAtom;
371
+ }
372
+ /**
373
+ * Plugins that failed to load or activate.
374
+ */
375
+ get failed() {
376
+ return this._failedAtom;
377
+ }
378
+ /**
379
+ * Ids of currently-registered plugins that came from a dev source.
380
+ */
381
+ get devPluginIds() {
382
+ return this._devPluginIdsAtom;
383
+ }
384
+ getPlugins() {
385
+ return this._get(this._pluginsAtom);
386
+ }
387
+ getCore() {
388
+ return this._get(this._coreAtom);
389
+ }
390
+ getEnabled() {
391
+ return this._get(this._enabledAtom);
392
+ }
393
+ getModules() {
394
+ return this._get(this._modulesAtom);
395
+ }
396
+ getActive() {
397
+ return this._get(this._activeAtom);
398
+ }
399
+ getEventsFired() {
400
+ return this._get(this._eventsFiredAtom);
401
+ }
402
+ getPendingReset() {
403
+ return this._get(this._pendingResetAtom);
404
+ }
405
+ getFailed() {
406
+ return this._get(this._failedAtom);
407
+ }
408
+ getDevPluginIds() {
409
+ return this._get(this._devPluginIdsAtom);
410
+ }
411
+ /**
412
+ * Marks `id` as dev-sourced. If the plugin displaced an existing one, pass
413
+ * the shadow snapshot so `remove` can restore it. Repeat calls (e.g. a dev
414
+ * plugin reload) preserve the original shadow target — restoration always
415
+ * unwinds back to the real underlying plugin, never an intermediate dev build.
416
+ */
417
+ _markDev(id, shadow) {
418
+ if (this._devPlugins.has(id)) return;
419
+ this._devPlugins.set(id, { shadow });
420
+ this._update(this._devPluginIdsAtom, (ids) => ids.includes(id) ? ids : [...ids, id]);
421
+ }
422
+ /** Drops the dev-plugin entry and returns its shadow data (if any) for restoration. */
423
+ _unmarkDev(id) {
424
+ const entry = this._devPlugins.get(id);
425
+ this._devPlugins.delete(id);
426
+ this._update(this._devPluginIdsAtom, (ids) => ids.filter((existing) => existing !== id));
427
+ return entry?.shadow;
428
+ }
429
+ getDependencies(id, opts) {
430
+ if (!(opts?.transitive !== false)) return this._directDependencies(id);
431
+ return this._computeDependencyClosure(id).order.filter((depId) => depId !== id);
432
+ }
433
+ getDependents(id, opts) {
434
+ return this._collectDependents(id, {
435
+ transitive: opts?.transitive !== false,
436
+ enabledOnly: opts?.enabledOnly === true
437
+ });
438
+ }
439
+ clearFailure(id) {
440
+ const current = this._get(this._failedAtom);
441
+ if (!current.some((failure) => failure.id === id)) return false;
442
+ this._set(this._failedAtom, current.filter((failure) => failure.id !== id));
443
+ return true;
444
+ }
445
+ /**
446
+ * Adds a plugin to the manager via the plugin loader.
447
+ * The plugin is registered but not enabled; call `enable` separately to activate it.
448
+ * @param id The id of the plugin.
449
+ */
450
+ add(id) {
451
+ return Effect.gen(this, function* () {
452
+ log("add plugin", { id }, {
453
+ "~LogMeta": "~LogMeta",
454
+ F: __dxlog_file,
455
+ L: 537,
456
+ S: this
457
+ });
458
+ const { plugin, dev = false } = yield* this._pluginLoader(id);
459
+ const pluginId = plugin.meta.profile.key;
460
+ const existing = this._getPlugin(pluginId);
461
+ if (dev && existing && existing !== plugin) {
462
+ const wasEnabled = this._get(this._enabledAtom).includes(pluginId);
463
+ if (wasEnabled) yield* this.disable(pluginId);
464
+ this._markDev(pluginId, {
465
+ plugin: existing,
466
+ wasEnabled
467
+ });
468
+ this._update(this._pluginsAtom, (plugins) => plugins.map((p) => p.meta.profile.key === pluginId ? plugin : p));
469
+ } else {
470
+ this._addPlugin(plugin);
471
+ if (dev) this._markDev(pluginId);
472
+ }
473
+ return plugin;
474
+ });
475
+ }
476
+ /**
477
+ * Enables a plugin.
478
+ * @param id The id of the plugin.
479
+ * @param opts See {@link PluginManager.enable}.
480
+ */
481
+ enable(id, opts) {
482
+ const resolveDependencies = opts?.resolveDependencies !== false;
483
+ return Effect.gen(this, function* () {
484
+ log("enable plugin", {
485
+ id,
486
+ resolveDependencies
487
+ }, {
488
+ "~LogMeta": "~LogMeta",
489
+ F: __dxlog_file,
490
+ L: 577,
491
+ S: this
492
+ });
493
+ if (!resolveDependencies) return yield* this._enableOne(id);
494
+ if (!this._getPlugin(id) && !this._getCatalogEntry(id)) return yield* this._enableOne(id);
495
+ const walk = this._computeDependencyClosure(id);
496
+ if (walk.cycle) {
497
+ this._recordFailure(id, "load", new PluginDependencyError({ context: {
498
+ id,
499
+ reason: "cycle",
500
+ path: walk.cycle
501
+ } }));
502
+ return false;
503
+ }
504
+ if (walk.missing.length > 0) {
505
+ this._recordFailure(id, "load", new PluginDependencyError({ context: {
506
+ id,
507
+ reason: "missing",
508
+ missing: walk.missing
509
+ } }));
510
+ return false;
511
+ }
512
+ let queue = walk.toInstall.slice();
513
+ const installed = /* @__PURE__ */ new Set();
514
+ while (queue.length > 0) {
515
+ const next = queue.shift();
516
+ if (installed.has(next) || this._getPlugin(next)) continue;
517
+ const installResult = yield* this.add(next).pipe(Effect.either);
518
+ if (installResult._tag === "Left") {
519
+ this._recordFailure(id, "load", new PluginDependencyError({
520
+ context: {
521
+ id,
522
+ reason: "install-failed",
523
+ dependency: next
524
+ },
525
+ cause: installResult.left
526
+ }));
527
+ return false;
528
+ }
529
+ installed.add(next);
530
+ const rewalk = this._computeDependencyClosure(id);
531
+ if (rewalk.cycle) {
532
+ this._recordFailure(id, "load", new PluginDependencyError({ context: {
533
+ id,
534
+ reason: "cycle",
535
+ path: rewalk.cycle
536
+ } }));
537
+ return false;
538
+ }
539
+ if (rewalk.missing.length > 0) {
540
+ this._recordFailure(id, "load", new PluginDependencyError({ context: {
541
+ id,
542
+ reason: "missing",
543
+ missing: rewalk.missing
544
+ } }));
545
+ return false;
546
+ }
547
+ queue = rewalk.toInstall.filter((depId) => !installed.has(depId));
548
+ }
549
+ const order = this._computeDependencyClosure(id).order;
550
+ let succeeded = false;
551
+ for (const depId of order) {
552
+ const ok = yield* this._enableOne(depId);
553
+ if (depId === id) succeeded = ok;
554
+ }
555
+ return succeeded;
556
+ });
557
+ }
558
+ /**
559
+ * Enables a single plugin without consulting its declared dependencies.
560
+ * Used by {@link enable} as the leaf step after closure resolution, and
561
+ * directly when callers pass `{ resolveDependencies: false }`.
562
+ *
563
+ * The underlying operations (`_addModule`, `_setPendingResetByModule`,
564
+ * `activate`) are all idempotent, so this method is safe to call multiple
565
+ * times for the same id. The constructor's bootstrap path relies on this:
566
+ * the persisted `enabled` ids are written into `_enabledAtom` up front, so
567
+ * the very first `enable(id)` for those plugins sees `alreadyEnabled`-style
568
+ * state but still needs to perform the module registration and activation.
569
+ */
570
+ _enableOne(id) {
571
+ return Effect.gen(this, function* () {
572
+ const stub = this._getPlugin(id);
573
+ if (!stub) return false;
574
+ this.clearFailure(id);
575
+ const plugin = yield* this._resolveLazyPlugin(stub);
576
+ this._update(this._enabledAtom, (enabled) => enabled.includes(id) ? enabled : [...enabled, id]);
577
+ plugin.modules.forEach((module) => {
578
+ this._addModule(module);
579
+ this._setPendingResetByModule(module);
580
+ });
581
+ log("pending reset", { events: [...this.getPendingReset()] }, {
582
+ "~LogMeta": "~LogMeta",
583
+ F: __dxlog_file,
584
+ L: 702,
585
+ S: this
586
+ });
587
+ yield* Effect.all(this.getPendingReset().map((event) => this.activate(event)), { concurrency: "unbounded" });
588
+ return true;
589
+ });
590
+ }
591
+ /**
592
+ * Resolves a lazy plugin stub (returned by {@link Plugin.lazy}) to its
593
+ * loaded form and swaps it into `_pluginsAtom`. Returns the input unchanged
594
+ * when the plugin is already resolved, so callers can `yield*` this
595
+ * unconditionally. The lazy stub carries `meta` synchronously but its
596
+ * `modules` list is empty until the loader resolves; the swap ensures
597
+ * subsequent enable/disable operations see the resolved plugin.
598
+ *
599
+ * Concurrent calls for the same id are coalesced via `_resolvingPlugins`:
600
+ * the first caller starts the resolution, every subsequent caller awaits
601
+ * the same `Deferred`. On failure we publish a `lazy:<id>` error message
602
+ * and skip the atom swap so the failure is observable to the activation
603
+ * subscriber and a retry can be attempted.
604
+ */
605
+ _resolveLazyPlugin(plugin) {
606
+ return Effect.gen(this, function* () {
607
+ if (!isLazy(plugin)) return plugin;
608
+ const id = plugin.meta.profile.key;
609
+ const existing = this._resolvingPlugins.get(id);
610
+ if (existing) return yield* Deferred.await(existing);
611
+ const deferred = yield* Deferred.make();
612
+ this._resolvingPlugins.set(id, deferred);
613
+ return yield* Effect.gen(this, function* () {
614
+ log("resolving lazy plugin", { id }, {
615
+ "~LogMeta": "~LogMeta",
616
+ F: __dxlog_file,
617
+ L: 741,
618
+ S: this
619
+ });
620
+ yield* PubSub.publish(this.activation, {
621
+ event: "",
622
+ state: "activating",
623
+ module: `lazy:${id}`
624
+ });
625
+ const resolvedPlugin = yield* resolveLazy(plugin).pipe(Effect.timeoutFail({
626
+ duration: this._loadTimeout,
627
+ onTimeout: () => new LazyPluginError({
628
+ context: {
629
+ id,
630
+ reason: "load-failed"
631
+ },
632
+ cause: new PluginTimeoutError({ context: {
633
+ id,
634
+ phase: "load"
635
+ } })
636
+ })
637
+ }));
638
+ this._update(this._pluginsAtom, (plugins) => plugins.map((p) => p.meta.profile.key === id ? resolvedPlugin : p));
639
+ yield* PubSub.publish(this.activation, {
640
+ event: "",
641
+ state: "activated",
642
+ module: `lazy:${id}`
643
+ });
644
+ return resolvedPlugin;
645
+ }).pipe(Effect.tapError((error) => Effect.gen(this, function* () {
646
+ yield* PubSub.publish(this.activation, {
647
+ event: "",
648
+ state: "error",
649
+ module: `lazy:${id}`,
650
+ error
651
+ });
652
+ this._recordFailure(id, "load", error);
653
+ this._scheduleAutoDisable(id);
654
+ })), Effect.tap((value) => Deferred.succeed(deferred, value)), Effect.tapErrorCause((cause) => Deferred.failCause(deferred, cause)), Effect.ensuring(Effect.sync(() => this._resolvingPlugins.delete(id))));
655
+ });
656
+ }
657
+ /**
658
+ * Removes a plugin from the manager.
659
+ * @param id The id of the plugin.
660
+ * @param opts See {@link PluginManager.remove}.
661
+ */
662
+ remove(id, opts) {
663
+ return Effect.gen(this, function* () {
664
+ log("remove plugin", { id }, {
665
+ "~LogMeta": "~LogMeta",
666
+ F: __dxlog_file,
667
+ L: 784,
668
+ S: this
669
+ });
670
+ const wasDev = this._devPlugins.has(id);
671
+ if (!(yield* this.disable(id, opts))) return false;
672
+ this._removePlugin(id);
673
+ if (this._onRemove) this._runForkedFiber(this._onRemove(id).pipe(Effect.tapError((error) => Effect.sync(() => log.warn("plugin remove hook failed", {
674
+ id,
675
+ error
676
+ }, {
677
+ "~LogMeta": "~LogMeta",
678
+ F: __dxlog_file,
679
+ L: 795,
680
+ S: this
681
+ }))), Effect.ignore));
682
+ if (wasDev) {
683
+ const shadow = this._unmarkDev(id);
684
+ if (shadow) {
685
+ this._addPlugin(shadow.plugin);
686
+ if (shadow.wasEnabled) yield* this.enable(id);
687
+ }
688
+ }
689
+ return true;
690
+ });
691
+ }
692
+ /**
693
+ * Disables a plugin.
694
+ * @param id The id of the plugin.
695
+ * @param opts See {@link PluginManager.disable}.
696
+ */
697
+ disable(id, { cascade = true } = {}) {
698
+ return Effect.gen(this, function* () {
699
+ log("disable plugin", {
700
+ id,
701
+ cascade
702
+ }, {
703
+ "~LogMeta": "~LogMeta",
704
+ F: __dxlog_file,
705
+ L: 826,
706
+ S: this
707
+ });
708
+ if (this._get(this._coreAtom).includes(id)) return false;
709
+ if (!this._getPlugin(id)) return false;
710
+ if (cascade) {
711
+ const enabledDependents = this._collectDependents(id, {
712
+ transitive: true,
713
+ enabledOnly: true
714
+ });
715
+ if (enabledDependents.length > 0) {
716
+ const coreDependent = enabledDependents.find((dependentId) => this._get(this._coreAtom).includes(dependentId));
717
+ if (coreDependent) return yield* Effect.fail(new PluginDependencyError({ context: {
718
+ id,
719
+ reason: "core-dependent",
720
+ coreDependent
721
+ } }));
722
+ for (const dependentId of enabledDependents) yield* this._disableOne(dependentId);
723
+ }
724
+ }
725
+ yield* this._disableOne(id);
726
+ return true;
727
+ });
728
+ }
729
+ /**
730
+ * Disables a single plugin without consulting its dependents. Used by
731
+ * {@link disable} after the dependents pass has run (or been skipped via
732
+ * `cascade: false`).
733
+ */
734
+ _disableOne(id) {
735
+ return Effect.gen(this, function* () {
736
+ if (this._get(this._coreAtom).includes(id)) return false;
737
+ const plugin = this._getPlugin(id);
738
+ if (!plugin) return false;
739
+ if (this._get(this._enabledAtom).findIndex((enabled) => enabled === id) !== -1) {
740
+ this._update(this._enabledAtom, (enabled) => enabled.filter((item) => item !== id));
741
+ yield* this.deactivate(id);
742
+ plugin.modules.forEach((module) => {
743
+ this._removeModule(module.id);
744
+ });
745
+ }
746
+ return true;
747
+ });
748
+ }
749
+ /**
750
+ * Activates plugins based on the activation event.
751
+ * @param event The activation event.
752
+ * @returns Whether the activation was successful.
753
+ */
754
+ activate(event, params) {
755
+ const key = typeof event === "string" ? event : eventKey(event);
756
+ return Effect.gen(this, function* () {
757
+ if (yield* this._isShuttingDown()) {
758
+ log("skipping activation during shutdown", {
759
+ key,
760
+ ...params
761
+ }, {
762
+ "~LogMeta": "~LogMeta",
763
+ F: __dxlog_file,
764
+ L: 901,
765
+ S: this
766
+ });
767
+ return false;
768
+ }
769
+ yield* Deferred.await(this._initialization);
770
+ return yield* Effect.withFiberRuntime((fiber) => this._activateEvent(key, params, fiber).pipe(together(Effect.sleep(Duration.seconds(15)).pipe(Effect.andThen(Effect.sync(() => log.warn("event activation is taking a long time", { event: key }, {
771
+ "~LogMeta": "~LogMeta",
772
+ F: __dxlog_file,
773
+ L: 915,
774
+ S: this
775
+ }))))), Performance.addTrackEntry({
776
+ name: typeof event === "string" ? event : eventKey(event),
777
+ devtools: {
778
+ dataType: "track-entry",
779
+ track: "Event Activation",
780
+ trackGroup: "Composer",
781
+ color: "primary"
782
+ }
783
+ })));
784
+ });
785
+ }
786
+ /**
787
+ * Deactivates all of the modules for a plugin.
788
+ * @param id The id of the plugin.
789
+ * @returns Whether the deactivation was successful.
790
+ */
791
+ deactivate(id) {
792
+ return Effect.gen(this, function* () {
793
+ const plugin = this._getPlugin(id);
794
+ if (!plugin) return false;
795
+ const modules = plugin.modules;
796
+ return (yield* Effect.all(modules.map((module) => this._deactivateModule(module)), { concurrency: "unbounded" })).every((result) => result);
797
+ });
798
+ }
799
+ /**
800
+ * Re-activates the modules that were activated by the event.
801
+ * @param event The activation event.
802
+ * @returns Whether the reset was successful.
803
+ */
804
+ reset(event) {
805
+ return Effect.gen(this, function* () {
806
+ const key = typeof event === "string" ? event : eventKey(event);
807
+ log("reset", { key }, {
808
+ "~LogMeta": "~LogMeta",
809
+ F: __dxlog_file,
810
+ L: 961,
811
+ S: this
812
+ });
813
+ const modules = this._getActiveModulesByEvent(key);
814
+ if ((yield* Effect.all(modules.map((module) => this._deactivateModule(module)), { concurrency: "unbounded" })).every((result) => result)) return yield* this.activate(key);
815
+ else return false;
816
+ });
817
+ }
818
+ shutdown() {
819
+ return this._shutdownSemaphore.withPermits(1)(Effect.gen(this, function* () {
820
+ yield* Ref.set(this._shuttingDown, true);
821
+ log("shutdown", void 0, {
822
+ "~LogMeta": "~LogMeta",
823
+ F: __dxlog_file,
824
+ L: 980,
825
+ S: this
826
+ });
827
+ yield* this._interruptInFlightActivations();
828
+ const activeIds = [...this._get(this._activeAtom)].reverse();
829
+ const allModules = this._get(this._modulesAtom);
830
+ const modulesToDeactivate = activeIds.map((id) => allModules.find((module) => module.id === id)).filter((module) => module != null);
831
+ for (const module of modulesToDeactivate) yield* this._deactivateModule(module);
832
+ this._set(this._eventsFiredAtom, []);
833
+ this._set(this._pendingResetAtom, []);
834
+ this._moduleMemoMap.clear();
835
+ for (const scope of this._moduleScopes.values()) yield* Scope.close(scope, Exit.void);
836
+ this._moduleScopes.clear();
837
+ yield* Ref.set(this._activatingEvents, []);
838
+ yield* Ref.set(this._activatingModules, []);
839
+ log("shutdown complete", void 0, {
840
+ "~LogMeta": "~LogMeta",
841
+ F: __dxlog_file,
842
+ L: 1004,
843
+ S: this
844
+ });
845
+ return true;
846
+ }).pipe(Effect.ensuring(Ref.set(this._shuttingDown, false))));
847
+ }
848
+ _get(atom) {
849
+ return this.registry.get(atom);
850
+ }
851
+ _set(atom, value) {
852
+ this.registry.set(atom, value);
853
+ }
854
+ _update(atom, updater) {
855
+ this._set(atom, updater(this._get(atom)));
856
+ }
857
+ _isShuttingDown() {
858
+ return Ref.get(this._shuttingDown);
859
+ }
860
+ _getPlugin(id) {
861
+ return this._get(this._pluginsAtom).find((plugin) => plugin.meta.profile.key === id);
862
+ }
863
+ _getPluginIdForModule(moduleId) {
864
+ return this._get(this._pluginsAtom).find((plugin) => plugin.modules.some((module) => module.id === moduleId))?.meta.profile.key;
865
+ }
866
+ /** Looks up an id in the cached registry catalog, returning the entry or `undefined`. */
867
+ _getCatalogEntry(id) {
868
+ return this._get(this.pluginRegistry.plugins).entries.find((entry) => entry.profile.key === id);
869
+ }
870
+ /**
871
+ * Returns the direct `dependsOn` declarations for an id, drawing from the
872
+ * registered plugin's meta when available and falling back to the registry
873
+ * catalog entry. Unknown ids return an empty list (callers detect "missing"
874
+ * separately).
875
+ */
876
+ _directDependencies(id) {
877
+ const plugin = this._getPlugin(id);
878
+ if (plugin) return [...plugin.meta.profile.dependsOn ?? []];
879
+ const catalog = this._getCatalogEntry(id);
880
+ return catalog?.profile.dependsOn ? [...catalog.profile.dependsOn] : [];
881
+ }
882
+ /**
883
+ * Computes the transitive dependency closure for an id.
884
+ *
885
+ * Walks {@link _directDependencies} (registered plugins ∪ catalog entries).
886
+ * Returns:
887
+ * - `order`: closure including the root in dependency-first topological order.
888
+ * - `missing`: ids in the closure that are neither registered nor in the catalog.
889
+ * - `toInstall`: ids in the closure that are in the catalog but not yet registered.
890
+ * - `cycle`: when a cycle is detected, the cycle path; otherwise `undefined`.
891
+ */
892
+ _computeDependencyClosure(id) {
893
+ const order = [];
894
+ const visited = /* @__PURE__ */ new Set();
895
+ const onStack = /* @__PURE__ */ new Set();
896
+ const stackPath = [];
897
+ const missing = [];
898
+ const toInstall = [];
899
+ let cycle;
900
+ const knownIds = /* @__PURE__ */ new Set([...this._get(this._pluginsAtom).map((plugin) => plugin.meta.profile.key), ...this._get(this.pluginRegistry.plugins).entries.map((entry) => entry.profile.key)]);
901
+ const visit = (currentId) => {
902
+ if (cycle) return;
903
+ if (visited.has(currentId)) return;
904
+ if (onStack.has(currentId)) {
905
+ const cycleStart = stackPath.indexOf(currentId);
906
+ cycle = [...stackPath.slice(cycleStart), currentId];
907
+ return;
908
+ }
909
+ onStack.add(currentId);
910
+ stackPath.push(currentId);
911
+ if (!knownIds.has(currentId)) missing.push(currentId);
912
+ else if (!this._getPlugin(currentId)) toInstall.push(currentId);
913
+ for (const depId of this._directDependencies(currentId)) {
914
+ visit(depId);
915
+ if (cycle) break;
916
+ }
917
+ onStack.delete(currentId);
918
+ stackPath.pop();
919
+ if (!cycle) {
920
+ visited.add(currentId);
921
+ order.push(currentId);
922
+ }
923
+ };
924
+ visit(id);
925
+ return {
926
+ order,
927
+ missing,
928
+ toInstall,
929
+ cycle
930
+ };
931
+ }
932
+ /**
933
+ * Walks the reverse `dependsOn` edges across registered plugins. With
934
+ * `enabledOnly`, filters the result to currently-enabled ids. Returns
935
+ * dependents in dependents-before-deps order so callers (cascade-disable)
936
+ * can iterate and tear down leaves first.
937
+ */
938
+ _collectDependents(id, opts) {
939
+ const direct = this._get(this._pluginsAtom).filter((plugin) => plugin.meta.profile.dependsOn?.some((dep) => dep === id)).map((plugin) => plugin.meta.profile.key);
940
+ if (!opts.transitive) return opts.enabledOnly ? direct.filter((dependentId) => this._get(this._enabledAtom).includes(dependentId)) : direct;
941
+ const result = [];
942
+ const visited = /* @__PURE__ */ new Set();
943
+ const visit = (currentId) => {
944
+ if (visited.has(currentId)) return;
945
+ visited.add(currentId);
946
+ const parents = this._get(this._pluginsAtom).filter((plugin) => plugin.meta.profile.dependsOn?.some((dep) => dep === currentId)).map((plugin) => plugin.meta.profile.key);
947
+ for (const parentId of parents) {
948
+ visit(parentId);
949
+ if (parentId !== id && !result.includes(parentId)) result.push(parentId);
950
+ }
951
+ };
952
+ visit(id);
953
+ return opts.enabledOnly ? result.filter((dependentId) => this._get(this._enabledAtom).includes(dependentId)) : result;
954
+ }
955
+ /**
956
+ * Records a failure for a plugin. Latest failure wins so the registry UI
957
+ * always sees the most recent reason. Walks the `cause` chain when checking
958
+ * for timeouts: lazy-load timeouts arrive wrapped in `LazyPluginError` (the
959
+ * timeout is the cause), but the operator-visible reason should still be
960
+ * `'timeout'`.
961
+ */
962
+ _recordFailure(id, phase, error) {
963
+ const reason = isTimeoutCause(error) ? "timeout" : "error";
964
+ const failure = {
965
+ id,
966
+ phase,
967
+ reason,
968
+ error,
969
+ timestamp: Date.now()
970
+ };
971
+ log.warn("plugin failed to activate", {
972
+ id,
973
+ phase,
974
+ reason,
975
+ error: error.message
976
+ }, {
977
+ "~LogMeta": "~LogMeta",
978
+ F: __dxlog_file,
979
+ L: 1179,
980
+ S: this
981
+ });
982
+ this._update(this._failedAtom, (current) => [...current.filter((entry) => entry.id !== id), failure]);
983
+ }
984
+ /**
985
+ * Fire-and-forget disable of a failed plugin. Forked because a failure can
986
+ * happen mid-activation chain — yielding a `disable` inline would deadlock
987
+ * on the shared semaphores. Core plugins are skipped (the host opted into
988
+ * them being non-removable; the failure record is enough signal).
989
+ */
990
+ _scheduleAutoDisable(id) {
991
+ if (this._get(this._coreAtom).includes(id)) return;
992
+ if (!this._get(this._enabledAtom).includes(id)) return;
993
+ this._runForkedFiber(this.disable(id).pipe(Effect.tap(() => Effect.sync(() => log.error("plugin auto-disabled", { id }, {
994
+ "~LogMeta": "~LogMeta",
995
+ F: __dxlog_file,
996
+ L: 1202,
997
+ S: this
998
+ }))), Effect.tapError((error) => Effect.sync(() => log.warn("auto-disable failed", {
999
+ id,
1000
+ error
1001
+ }, {
1002
+ "~LogMeta": "~LogMeta",
1003
+ F: __dxlog_file,
1004
+ L: 1203,
1005
+ S: this
1006
+ }))), Effect.ignore));
1007
+ }
1008
+ _getActiveModules() {
1009
+ const active = this._get(this._activeAtom);
1010
+ return this._get(this._modulesAtom).filter((module) => active.includes(module.id));
1011
+ }
1012
+ _getInactiveModules() {
1013
+ const active = this._get(this._activeAtom);
1014
+ return this._get(this._modulesAtom).filter((module) => !active.includes(module.id));
1015
+ }
1016
+ _getActiveModulesByEvent(key) {
1017
+ return this._getActiveModules().filter((module) => getEvents(module.activatesOn).map(eventKey).includes(key));
1018
+ }
1019
+ _getInactiveModulesByEvent(key) {
1020
+ return this._getInactiveModules().filter((module) => getEvents(module.activatesOn).map(eventKey).includes(key));
1021
+ }
1022
+ _setPendingResetByModule(module) {
1023
+ const activationEvents = getEvents(module.activatesOn).map(eventKey).filter((key) => this._get(this._eventsFiredAtom).includes(key));
1024
+ const pendingReset = Array.fromIterable(new Set(activationEvents)).filter((event) => {
1025
+ return !this._get(this._pendingResetAtom).includes(event);
1026
+ });
1027
+ if (pendingReset.length > 0) {
1028
+ log("pending reset", { events: pendingReset }, {
1029
+ "~LogMeta": "~LogMeta",
1030
+ F: __dxlog_file,
1031
+ L: 1241,
1032
+ S: this
1033
+ });
1034
+ this._update(this._pendingResetAtom, (current) => [...current, ...pendingReset]);
1035
+ }
1036
+ }
1037
+ _clearPendingReset(key) {
1038
+ if (this._get(this._pendingResetAtom).findIndex((event) => event === key) !== -1) this._update(this._pendingResetAtom, (pending) => pending.filter((event) => event !== key));
1039
+ }
1040
+ _interruptInFlightActivations() {
1041
+ return Effect.gen(this, function* () {
1042
+ const inFlightFibers = yield* Ref.get(this._inFlightFibers);
1043
+ yield* Effect.forEach(inFlightFibers, (fiber) => Fiber.interrupt(fiber), { concurrency: "unbounded" });
1044
+ });
1045
+ }
1046
+ _trackFiber(ref, fiber) {
1047
+ return Ref.update(ref, (fibers) => [...fibers, fiber]);
1048
+ }
1049
+ _untrackFiber(ref, fiber) {
1050
+ return Ref.update(ref, (fibers) => fibers.filter((trackedFiber) => trackedFiber !== fiber));
1051
+ }
1052
+ /**
1053
+ * Spawns an effect on the default runtime and registers the resulting fiber in
1054
+ * `_inFlightFibers` so {@link shutdown} can interrupt it. Used from sync entry
1055
+ * points like {@link remove} where there is no enclosing Effect to fork from;
1056
+ * inside an Effect chain prefer the existing track/await/untrack pattern.
1057
+ */
1058
+ _runForkedFiber(effect) {
1059
+ const fiber = Effect.runFork(effect);
1060
+ Effect.runSync(this._trackFiber(this._inFlightFibers, fiber));
1061
+ Effect.runFork(Fiber.await(fiber).pipe(Effect.andThen(() => this._untrackFiber(this._inFlightFibers, fiber))));
1062
+ }
1063
+ _addPlugin(plugin) {
1064
+ log("add plugin", { id: plugin.meta.profile.key }, {
1065
+ "~LogMeta": "~LogMeta",
1066
+ F: __dxlog_file,
1067
+ L: 1297,
1068
+ S: this
1069
+ });
1070
+ this._update(this._pluginsAtom, (plugins) => plugins.includes(plugin) ? plugins : [...plugins, plugin]);
1071
+ }
1072
+ _removePlugin(id) {
1073
+ log("remove plugin", { id }, {
1074
+ "~LogMeta": "~LogMeta",
1075
+ F: __dxlog_file,
1076
+ L: 1303,
1077
+ S: this
1078
+ });
1079
+ this._update(this._pluginsAtom, (plugins) => plugins.filter((plugin) => plugin.meta.profile.key !== id));
1080
+ }
1081
+ _addModule(module) {
1082
+ log("add module", { id: module.id }, {
1083
+ "~LogMeta": "~LogMeta",
1084
+ F: __dxlog_file,
1085
+ L: 1308,
1086
+ S: this
1087
+ });
1088
+ this._update(this._modulesAtom, (modules) => modules.includes(module) ? modules : [...modules, module]);
1089
+ }
1090
+ _removeModule(id) {
1091
+ log("remove module", { id }, {
1092
+ "~LogMeta": "~LogMeta",
1093
+ F: __dxlog_file,
1094
+ L: 1314,
1095
+ S: this
1096
+ });
1097
+ this._update(this._modulesAtom, (modules) => modules.filter((module) => module.id !== id));
1098
+ }
1099
+ _activateEvent(key, params, fiber) {
1100
+ return Effect.gen(this, function* () {
1101
+ yield* this._trackFiber(this._inFlightFibers, fiber);
1102
+ log("activating", {
1103
+ key,
1104
+ ...params
1105
+ }, {
1106
+ "~LogMeta": "~LogMeta",
1107
+ F: __dxlog_file,
1108
+ L: 1329,
1109
+ S: this
1110
+ });
1111
+ yield* Ref.update(this._activatingEvents, (activating) => Array.append(activating, key));
1112
+ this._clearPendingReset(key);
1113
+ const activatingEvents = yield* this._activatingEvents;
1114
+ const activatingModules = yield* this._activatingModules;
1115
+ const modules = this._getModulesForActivation(key, activatingEvents, activatingModules);
1116
+ if (modules.length === 0) {
1117
+ log("no modules to activate", { key }, {
1118
+ "~LogMeta": "~LogMeta",
1119
+ F: __dxlog_file,
1120
+ L: 1337,
1121
+ S: this
1122
+ });
1123
+ if (!this._get(this._eventsFiredAtom).includes(key)) this._update(this._eventsFiredAtom, (events) => [...events, key]);
1124
+ return false;
1125
+ }
1126
+ return yield* this._activateModulesForEvent(key, modules, activatingEvents);
1127
+ }).pipe(Effect.ensuring(Effect.all([this._untrackFiber(this._inFlightFibers, fiber), Ref.update(this._activatingEvents, (activating) => Array.filter(activating, (event) => event !== key))])));
1128
+ }
1129
+ _activateModulesForEvent(key, modules, activatingEvents) {
1130
+ const activatingModuleIds = modules.map((module) => module.id);
1131
+ return Effect.gen(this, function* () {
1132
+ yield* Ref.update(this._activatingModules, (activating) => Array.appendAll(activating, activatingModuleIds));
1133
+ log("activation wave", {
1134
+ event: key,
1135
+ modules: activatingModuleIds
1136
+ }, {
1137
+ "~LogMeta": "~LogMeta",
1138
+ F: __dxlog_file,
1139
+ L: 1364,
1140
+ S: this
1141
+ });
1142
+ performance.mark(`event:${key}:start`);
1143
+ yield* PubSub.publish(this.activation, {
1144
+ event: key,
1145
+ state: "activating"
1146
+ });
1147
+ yield* this._activateRelatedEvents(key, this._getBeforeEvents(modules, activatingEvents), "before");
1148
+ const capabilities = yield* this._loadCapabilitiesForModules(key, modules);
1149
+ yield* this._contributeCapabilitiesForModules(modules, capabilities);
1150
+ yield* this._activateRelatedEvents(key, this._getAfterEvents(modules, activatingEvents), "after");
1151
+ if (!this._get(this._eventsFiredAtom).includes(key)) this._update(this._eventsFiredAtom, (events) => [...events, key]);
1152
+ performance.mark(`event:${key}:end`);
1153
+ performance.measure(`event:${key}`, `event:${key}:start`, `event:${key}:end`);
1154
+ yield* PubSub.publish(this.activation, {
1155
+ event: key,
1156
+ state: "activated"
1157
+ });
1158
+ log("activated", { key }, {
1159
+ "~LogMeta": "~LogMeta",
1160
+ F: __dxlog_file,
1161
+ L: 1382,
1162
+ S: this
1163
+ });
1164
+ return true;
1165
+ }).pipe(Effect.ensuring(Ref.update(this._activatingModules, (activating) => Array.filter(activating, (module) => !activatingModuleIds.includes(module)))));
1166
+ }
1167
+ _getModulesForActivation(key, activatingEvents, activatingModules) {
1168
+ return this._getInactiveModulesByEvent(key).filter((module) => {
1169
+ if (!isAllOf(module.activatesOn)) return true;
1170
+ return getEvents(module.activatesOn).filter((event) => eventKey(event) !== key).every((event) => this._get(this._eventsFiredAtom).includes(eventKey(event)) || activatingEvents.includes(eventKey(event))) && !activatingModules.includes(module.id);
1171
+ });
1172
+ }
1173
+ _getBeforeEvents(modules, activatingEvents) {
1174
+ return Function.pipe(modules, Array.flatMap((module) => module.firesBeforeActivation ?? []), HashSet.fromIterable, HashSet.toValues, Array.filter((event) => !activatingEvents.includes(eventKey(event))));
1175
+ }
1176
+ _getAfterEvents(modules, activatingEvents) {
1177
+ return Function.pipe(modules, Array.flatMap((module) => module.firesAfterActivation ?? []), HashSet.fromIterable, HashSet.toValues, Array.filter((event) => !activatingEvents.includes(eventKey(event))));
1178
+ }
1179
+ _activateRelatedEvents(key, events, phase) {
1180
+ const logLabel = phase === "before" ? "firesBeforeActivation" : "firesAfterActivation";
1181
+ const eventKey$1 = phase === "before" ? "beforeEvents" : "afterEvents";
1182
+ return Function.pipe(events, Array.map((event) => this.activate(event, phase === "before" ? { before: key } : { after: key })), Effect.allWith({ concurrency: "unbounded" }), together(Effect.sleep(Duration.seconds(10)).pipe(Effect.andThen(Effect.sync(() => log.warn(`${logLabel} is taking a long time`, {
1183
+ event: key,
1184
+ [eventKey$1]: events.map(eventKey)
1185
+ }, {
1186
+ "~LogMeta": "~LogMeta",
1187
+ F: __dxlog_file,
1188
+ L: 1461,
1189
+ S: this
1190
+ }))))), Effect.asVoid);
1191
+ }
1192
+ _loadCapabilitiesForModules(key, modules) {
1193
+ return Function.pipe(modules, Array.map((mod) => this._loadModule(mod, key)), Effect.allWith({ concurrency: "unbounded" }), Effect.catchAll((error) => {
1194
+ return Effect.gen(this, function* () {
1195
+ yield* PubSub.publish(this.activation, {
1196
+ event: key,
1197
+ state: "error",
1198
+ error
1199
+ });
1200
+ return yield* Effect.fail(error);
1201
+ });
1202
+ }));
1203
+ }
1204
+ _contributeCapabilitiesForModules(modules, capabilities) {
1205
+ return Function.pipe(modules, Array.zip(capabilities), Array.map(([module, capabilitySet]) => this._contributeCapabilities(module, capabilitySet)), Effect.all, Effect.asVoid);
1206
+ }
1207
+ _getModuleSemaphore(moduleId) {
1208
+ let semaphore = this._moduleSemaphores.get(moduleId);
1209
+ if (!semaphore) {
1210
+ semaphore = Effect.runSync(Effect.makeSemaphore(1));
1211
+ this._moduleSemaphores.set(moduleId, semaphore);
1212
+ }
1213
+ return semaphore;
1214
+ }
1215
+ _loadModule = (module, parentEvent) => Effect.gen(this, function* () {
1216
+ const semaphore = this._getModuleSemaphore(module.id);
1217
+ const deferredToAwait = yield* Effect.gen(this, function* () {
1218
+ const existing = this._moduleMemoMap.get(module.id);
1219
+ if (existing) return existing;
1220
+ const deferred = yield* Deferred.make();
1221
+ this._moduleMemoMap.set(module.id, deferred);
1222
+ const scope = yield* Scope.make();
1223
+ const loadEffect = Effect.gen(this, function* () {
1224
+ log("loading module", {
1225
+ module: module.id,
1226
+ parentEvent
1227
+ }, {
1228
+ "~LogMeta": "~LogMeta",
1229
+ F: __dxlog_file,
1230
+ L: 1546,
1231
+ S: this
1232
+ });
1233
+ performance.mark(`module:${module.id}:start`);
1234
+ yield* PubSub.publish(this.activation, {
1235
+ event: parentEvent,
1236
+ state: "activating",
1237
+ module: module.id
1238
+ });
1239
+ const pluginId = this._getPluginIdForModule(module.id);
1240
+ const [duration, capabilities] = yield* module.activate().pipe(Effect.provideService(Service, this.capabilities), Effect.provideService(Service$1, this), Scope.extend(scope), Effect.timeoutFail({
1241
+ duration: this._activationTimeout,
1242
+ onTimeout: () => new PluginTimeoutError({ context: {
1243
+ id: pluginId ?? module.id,
1244
+ module: module.id,
1245
+ phase: "activation"
1246
+ } })
1247
+ }), Effect.timed);
1248
+ const normalized = capabilities == null ? [] : Array.isArray(capabilities) ? capabilities : [capabilities];
1249
+ this._moduleScopes.set(module.id, scope);
1250
+ const elapsed = Duration.toMillis(duration);
1251
+ performance.mark(`module:${module.id}:end`);
1252
+ performance.measure(`module:${module.id}`, `module:${module.id}:start`, `module:${module.id}:end`);
1253
+ yield* PubSub.publish(this.activation, {
1254
+ event: parentEvent,
1255
+ state: "activated",
1256
+ module: module.id
1257
+ });
1258
+ log("loaded module", {
1259
+ module: module.id,
1260
+ parentEvent,
1261
+ elapsed,
1262
+ failed: false
1263
+ }, {
1264
+ "~LogMeta": "~LogMeta",
1265
+ F: __dxlog_file,
1266
+ L: 1572,
1267
+ S: this
1268
+ });
1269
+ return normalized;
1270
+ }).pipe(Effect.tapErrorCause(() => Scope.close(scope, Exit.void)), Effect.withSpan("PluginManager._loadModule"), together(Effect.sleep(Duration.seconds(10)).pipe(Effect.andThen(Effect.sync(() => log.warn(`module is taking a long time to activate`, { module: module.id }, {
1271
+ "~LogMeta": "~LogMeta",
1272
+ F: __dxlog_file,
1273
+ L: 1585,
1274
+ S: this
1275
+ }))))), Performance.addTrackEntry({
1276
+ name: module.id,
1277
+ devtools: {
1278
+ dataType: "track-entry",
1279
+ track: "Module Activation",
1280
+ trackGroup: "Composer",
1281
+ color: "primary"
1282
+ }
1283
+ }));
1284
+ const fiber = yield* Effect.forkDaemon(loadEffect.pipe(Effect.tap((result) => Deferred.succeed(deferred, result)), Effect.catchAllCause((cause) => {
1285
+ const error = Cause.squash(cause);
1286
+ const errorMessage = error instanceof Error ? error.message : String(error);
1287
+ const missingCapability = errorMessage.match(/No capability found for ([^\s\[]+)/)?.[1];
1288
+ log.error("module failed to activate", {
1289
+ module: module.id,
1290
+ parentEvent,
1291
+ missingCapability,
1292
+ registeredCapabilities: this.capabilities.listRegisteredIdentifiers(),
1293
+ error: errorMessage,
1294
+ stack: error instanceof Error ? error.stack : void 0,
1295
+ isDefect: !Cause.isFailure(cause)
1296
+ }, {
1297
+ "~LogMeta": "~LogMeta",
1298
+ F: __dxlog_file,
1299
+ L: 1608,
1300
+ S: this
1301
+ });
1302
+ const normalizedError = error instanceof Error ? error : new Error(String(error));
1303
+ const pluginId = this._getPluginIdForModule(module.id);
1304
+ if (pluginId !== void 0) {
1305
+ this._recordFailure(pluginId, "activation", normalizedError);
1306
+ this._scheduleAutoDisable(pluginId);
1307
+ }
1308
+ return Deferred.fail(deferred, normalizedError);
1309
+ })));
1310
+ yield* this._trackFiber(this._inFlightFibers, fiber);
1311
+ yield* Effect.forkDaemon(Fiber.await(fiber).pipe(Effect.andThen(() => this._untrackFiber(this._inFlightFibers, fiber))));
1312
+ return deferred;
1313
+ }).pipe(semaphore.withPermits(1));
1314
+ return yield* Deferred.await(deferredToAwait);
1315
+ });
1316
+ _contributeCapabilities(module, capabilities) {
1317
+ return Effect.gen(this, function* () {
1318
+ capabilities.forEach((capability) => {
1319
+ this.capabilities.contribute({
1320
+ module: module.id,
1321
+ ...capability
1322
+ });
1323
+ });
1324
+ this._update(this._activeAtom, (active) => [...active, module.id]);
1325
+ this._capabilities.set(module.id, capabilities);
1326
+ });
1327
+ }
1328
+ _deactivateModule(module) {
1329
+ return Effect.gen(this, function* () {
1330
+ const id = module.id;
1331
+ log("deactivating", { id }, {
1332
+ "~LogMeta": "~LogMeta",
1333
+ F: __dxlog_file,
1334
+ L: 1655,
1335
+ S: this
1336
+ });
1337
+ this._moduleMemoMap.delete(id);
1338
+ const capabilities = this._capabilities.get(id);
1339
+ if (capabilities) {
1340
+ for (const capability of capabilities) {
1341
+ this.capabilities.remove(capability.interface, capability.implementation);
1342
+ yield* capability.deactivate?.() ?? Effect.succeed(void 0);
1343
+ }
1344
+ this._capabilities.delete(id);
1345
+ }
1346
+ const scope = this._moduleScopes.get(id);
1347
+ if (scope) {
1348
+ yield* Scope.close(scope, Exit.void);
1349
+ this._moduleScopes.delete(id);
1350
+ }
1351
+ if (this._get(this._activeAtom).findIndex((event) => event === id) !== -1) this._update(this._activeAtom, (active) => active.filter((event) => event !== id));
1352
+ log("deactivated", { id }, {
1353
+ "~LogMeta": "~LogMeta",
1354
+ F: __dxlog_file,
1355
+ L: 1679,
1356
+ S: this
1357
+ });
1358
+ return true;
1359
+ });
1360
+ }
1361
+ };
1362
+ /**
1363
+ * Creates a new Plugin Manager instance.
1364
+ */
1365
+ var make = (options) => new ManagerImpl(options);
1366
+ /**
1367
+ * True when `error` (or anything along its `cause` chain) is a
1368
+ * {@link PluginTimeoutError}. Lazy-load timeouts wrap the timeout inside
1369
+ * `LazyPluginError`, so a shallow check on the outer error misses them.
1370
+ * Bounded depth so a circular chain can't loop forever.
1371
+ */
1372
+ var isTimeoutCause = (error, depth = 0) => {
1373
+ if (depth > 5 || !(error instanceof Error)) return false;
1374
+ if (PluginTimeoutError.is(error)) return true;
1375
+ return isTimeoutCause(error.cause, depth + 1);
1376
+ };
1377
+ /**
1378
+ * Runs an effect concurrently with another effect.
1379
+ * If the first effect completes, the second effect is interrupted.
1380
+ */
1381
+ var together = (togetherEffect) => (effect) => Effect.gen(function* () {
1382
+ const togetherFiber = yield* Effect.fork(togetherEffect);
1383
+ const result = yield* effect;
1384
+ yield* Fiber.interrupt(togetherFiber);
1385
+ return result;
1386
+ });
1387
+ //#endregion
1388
+ export { make as a, capability_manager_exports as c, isManager as i, PluginInitializationError as n, plugin_manager_exports as o, PluginTimeoutError as r, registry_exports as s, ManagerTypeId as t };
1389
+
1390
+ //# sourceMappingURL=chunk-plugin-manager.mjs.map