@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,347 @@
1
+ import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
2
+ import { isPlugin } from "./core/plugin.mjs";
3
+ import { n as fetchManifest } from "./chunk-plugin-manifest.mjs";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Option from "effect/Option";
6
+ import { log } from "@dxos/log";
7
+ import { BaseError } from "@dxos/errors";
8
+ //#region src/core/plugin-asset-cache.ts
9
+ var plugin_asset_cache_exports = /* @__PURE__ */ __exportAll({
10
+ PluginAssetCacheError: () => PluginAssetCacheError,
11
+ noop: () => noop
12
+ });
13
+ /**
14
+ * Tagged error for cache operations. Construction sites supply `context.operation`
15
+ * (one of `'cache' | 'evict' | 'resolve' | 'list'`) and `context.pluginId` so
16
+ * downstream handlers can route on the failed call without scanning the message.
17
+ */
18
+ var PluginAssetCacheError = class extends BaseError.extend("PluginAssetCacheError", "Plugin asset cache operation failed") {};
19
+ /**
20
+ * No-op implementation. Used when no platform cache is registered (tests, unsupported environments).
21
+ * Plugins still load — they just have no offline guarantee.
22
+ */
23
+ var noop = () => ({
24
+ cache: () => Effect.void,
25
+ evict: () => Effect.void,
26
+ resolve: (_pluginId, url) => Effect.succeed(url),
27
+ list: () => Effect.succeed([])
28
+ });
29
+ //#endregion
30
+ //#region src/core/url-loader.ts
31
+ var url_loader_exports = /* @__PURE__ */ __exportAll({
32
+ RemotePluginLoadError: () => RemotePluginLoadError,
33
+ getInstalledVersion: () => getInstalledVersion,
34
+ getRemoteEntries: () => getRemoteEntries,
35
+ isLocalUrl: () => isLocalUrl,
36
+ make: () => make,
37
+ preload: () => preload,
38
+ setInstalledVersion: () => setInstalledVersion,
39
+ uninstall: () => uninstall
40
+ });
41
+ var __dxlog_file = "/__w/dxos/dxos/packages/sdk/app-framework/src/core/url-loader.ts";
42
+ var DEFAULT_KEY = "org.dxos.composer.remote-plugins";
43
+ /**
44
+ * Tagged error for any failure during remote plugin loading. Construction sites
45
+ * set `context.locator` and `context.reason` (one of `'invalid-locator' |
46
+ * 'manifest-error' | 'cache-error' | 'import-failed' | 'meta-missing' |
47
+ * 'meta-mismatch' | 'duplicate-id'`) so handlers can route on the failure mode.
48
+ */
49
+ var RemotePluginLoadError = class extends BaseError.extend("RemotePluginLoadError", "Failed to load remote plugin") {};
50
+ var defaultStorage = () => ({
51
+ get: (key) => localStorage.getItem(key),
52
+ set: (key, value) => localStorage.setItem(key, value)
53
+ });
54
+ var getPersistedRemotePlugins = (storage, key) => {
55
+ try {
56
+ const parsed = JSON.parse(storage.get(key) ?? "[]");
57
+ if (!Array.isArray(parsed)) return [];
58
+ return parsed.filter((entry) => typeof entry === "object" && entry !== null && typeof entry.id === "string" && typeof entry.url === "string" && (entry.version === void 0 || typeof entry.version === "string"));
59
+ } catch {
60
+ return [];
61
+ }
62
+ };
63
+ var persistRemotePlugin = (storage, key, entry) => {
64
+ try {
65
+ const entries = getPersistedRemotePlugins(storage, key).filter((existing) => existing.id !== entry.id);
66
+ entries.push(entry);
67
+ storage.set(key, JSON.stringify(entries));
68
+ } catch (error) {
69
+ log.warn("failed to persist remote plugin entry", {
70
+ entry,
71
+ error
72
+ }, {
73
+ "~LogMeta": "~LogMeta",
74
+ F: __dxlog_file,
75
+ L: 99,
76
+ S: void 0
77
+ });
78
+ }
79
+ };
80
+ var removePersistedRemotePlugin = (storage, key, pluginId) => {
81
+ try {
82
+ const entries = getPersistedRemotePlugins(storage, key).filter((existing) => existing.id !== pluginId);
83
+ storage.set(key, JSON.stringify(entries));
84
+ } catch (error) {
85
+ log.warn("failed to remove remote plugin entry", {
86
+ pluginId,
87
+ error
88
+ }, {
89
+ "~LogMeta": "~LogMeta",
90
+ F: __dxlog_file,
91
+ L: 108,
92
+ S: void 0
93
+ });
94
+ }
95
+ };
96
+ var isUrl = (locator) => {
97
+ try {
98
+ const url = new URL(locator);
99
+ return url.protocol === "http:" || url.protocol === "https:";
100
+ } catch {
101
+ return false;
102
+ }
103
+ };
104
+ /**
105
+ * Returns true when the URL's hostname is the local host (localhost, 127.0.0.1, or ::1).
106
+ */
107
+ var isLocalUrl = (locator) => {
108
+ try {
109
+ const hostname = new URL(locator).hostname.toLowerCase();
110
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
111
+ } catch {
112
+ return false;
113
+ }
114
+ };
115
+ /**
116
+ * Returns the list of remote plugin entries previously persisted by {@link make}.
117
+ * Useful for UI code that needs to know which loaded plugins were installed from a URL
118
+ * (e.g. to surface a tag on remote or localhost-hosted plugins).
119
+ */
120
+ var getRemoteEntries = (options = {}) => {
121
+ return getPersistedRemotePlugins(options.storage ?? defaultStorage(), options.key ?? DEFAULT_KEY);
122
+ };
123
+ /**
124
+ * Updates the persisted installed version for an already-stored remote plugin entry.
125
+ * Does nothing if the plugin has not been persisted yet.
126
+ */
127
+ var setInstalledVersion = (id, version, options = {}) => {
128
+ const storage = options.storage ?? defaultStorage();
129
+ const key = options.key ?? DEFAULT_KEY;
130
+ const entries = getPersistedRemotePlugins(storage, key);
131
+ const index = entries.findIndex((entry) => entry.id === id);
132
+ if (index === -1) return;
133
+ entries[index] = {
134
+ ...entries[index],
135
+ version
136
+ };
137
+ try {
138
+ storage.set(key, JSON.stringify(entries));
139
+ } catch (error) {
140
+ log.warn("failed to update installed version for remote plugin", {
141
+ id,
142
+ version,
143
+ error
144
+ }, {
145
+ "~LogMeta": "~LogMeta",
146
+ F: __dxlog_file,
147
+ L: 161,
148
+ S: void 0
149
+ });
150
+ }
151
+ };
152
+ /**
153
+ * Returns the installed version string for the given plugin id, or `undefined` if
154
+ * the plugin was installed without version tracking (legacy install) or is not found.
155
+ */
156
+ var getInstalledVersion = (id, options = {}) => {
157
+ return getPersistedRemotePlugins(options.storage ?? defaultStorage(), options.key ?? DEFAULT_KEY).find((entry) => entry.id === id)?.version;
158
+ };
159
+ var normalizePluginExport = (mod) => {
160
+ const exported = mod.default;
161
+ if (isPlugin(exported)) return exported;
162
+ if (typeof exported === "function") {
163
+ const result = exported();
164
+ if (isPlugin(result)) return result;
165
+ }
166
+ throw new Error("Remote module default export is not a Plugin or a zero-arg plugin factory.");
167
+ };
168
+ /**
169
+ * Loads stylesheets declared in the manifest by appending `<link rel="stylesheet">` elements to the host document.
170
+ * Each link is tagged with `data-dxos-plugin-id` so `uninstall` can clean them up.
171
+ */
172
+ var loadStylesheets = (manifest, cache) => Effect.gen(function* () {
173
+ if (typeof document === "undefined") return;
174
+ const cssUrls = manifest.assetUrls.filter((url) => url.endsWith(".css"));
175
+ for (const url of cssUrls) {
176
+ const resolved = yield* cache.resolve(manifest.key, url);
177
+ if (document.querySelector(`link[data-dxos-plugin-id="${manifest.key}"][href="${resolved}"]`)) continue;
178
+ const link = document.createElement("link");
179
+ link.rel = "stylesheet";
180
+ link.href = resolved;
181
+ link.dataset.dxosPluginId = manifest.key;
182
+ document.head.appendChild(link);
183
+ }
184
+ });
185
+ var loadFromManifest = (manifestUrl, cache) => Effect.gen(function* () {
186
+ log.info("loading remote plugin", { manifestUrl }, {
187
+ "~LogMeta": "~LogMeta",
188
+ F: __dxlog_file,
189
+ L: 220,
190
+ S: this
191
+ });
192
+ const manifest = yield* fetchManifest(manifestUrl).pipe(Effect.mapError((cause) => new RemotePluginLoadError({
193
+ context: {
194
+ locator: manifestUrl,
195
+ reason: "manifest-error"
196
+ },
197
+ cause
198
+ })));
199
+ const wrapCacheError = Effect.mapError((cause) => new RemotePluginLoadError({
200
+ context: {
201
+ locator: manifestUrl,
202
+ reason: "cache-error"
203
+ },
204
+ cause
205
+ }));
206
+ if (!manifest.dev) {
207
+ const cachedUrls = manifest.assetUrls.indexOf(manifestUrl) === -1 ? [manifestUrl, ...manifest.assetUrls] : manifest.assetUrls;
208
+ yield* cache.cache(manifest.key, cachedUrls).pipe(wrapCacheError);
209
+ }
210
+ const entryUrl = manifest.dev ? manifest.entryUrl : yield* cache.resolve(manifest.key, manifest.entryUrl).pipe(wrapCacheError);
211
+ const plugin = normalizePluginExport(yield* Effect.tryPromise({
212
+ try: () => import(
213
+ /* @vite-ignore */
214
+ entryUrl
215
+ ),
216
+ catch: (cause) => new RemotePluginLoadError({
217
+ context: {
218
+ locator: manifestUrl,
219
+ reason: "import-failed"
220
+ },
221
+ cause
222
+ })
223
+ }));
224
+ if (!plugin.meta.profile.key || !plugin.meta.profile.name) return yield* Effect.fail(new RemotePluginLoadError({ context: {
225
+ locator: manifestUrl,
226
+ reason: "meta-missing"
227
+ } }));
228
+ if (plugin.meta.profile.key !== manifest.key) return yield* Effect.fail(new RemotePluginLoadError({ context: {
229
+ locator: manifestUrl,
230
+ reason: "meta-mismatch",
231
+ metaId: plugin.meta.profile.key,
232
+ manifestId: manifest.key
233
+ } }));
234
+ if (!manifest.dev) yield* loadStylesheets(manifest, cache).pipe(wrapCacheError);
235
+ return {
236
+ plugin,
237
+ manifest
238
+ };
239
+ });
240
+ /**
241
+ * Preloads previously persisted remote plugins from storage. Per-entry failures
242
+ * are logged and swallowed — the returned effect always succeeds with whichever
243
+ * plugins loaded cleanly, so a single bad entry can't block the host's startup.
244
+ */
245
+ var preload = (options = {}) => Effect.gen(function* () {
246
+ const storage = options.storage ?? defaultStorage();
247
+ const key = options.key ?? DEFAULT_KEY;
248
+ const cache = options.cache ?? noop();
249
+ const onPluginLoaded = options.onPluginLoaded;
250
+ const entries = getPersistedRemotePlugins(storage, key);
251
+ if (entries.length === 0) return [];
252
+ log.info("preloading remote plugins", { count: entries.length }, {
253
+ "~LogMeta": "~LogMeta",
254
+ F: __dxlog_file,
255
+ L: 296,
256
+ S: this
257
+ });
258
+ const total = entries.length;
259
+ let loaded = 0;
260
+ return (yield* Effect.all(entries.map((entry) => loadFromManifest(entry.url, cache).pipe(Effect.tapError((error) => Effect.sync(() => log.warn("failed to preload remote plugin", {
261
+ entry,
262
+ error
263
+ }, {
264
+ "~LogMeta": "~LogMeta",
265
+ F: __dxlog_file,
266
+ L: 302,
267
+ S: this
268
+ }))), Effect.option, Effect.tap(() => Effect.sync(() => {
269
+ loaded += 1;
270
+ }).pipe(Effect.andThen(Effect.try(() => onPluginLoaded?.(loaded, total))), Effect.tapError((error) => Effect.sync(() => log.warn("onPluginLoaded threw", {
271
+ loaded,
272
+ total,
273
+ error
274
+ }, {
275
+ "~LogMeta": "~LogMeta",
276
+ F: __dxlog_file,
277
+ L: 314,
278
+ S: this
279
+ }))), Effect.ignore)))), { concurrency: "unbounded" })).flatMap((result) => Option.match(result, {
280
+ onNone: () => [],
281
+ onSome: ({ plugin }) => [plugin]
282
+ }));
283
+ });
284
+ /**
285
+ * Creates a plugin loader that resolves built-in plugins by ID or loads remote plugins from URLs.
286
+ *
287
+ * Remote URLs must point at a plugin manifest (`plugin.json`). The loader fetches the manifest,
288
+ * eagerly persists every declared asset via the configured `PluginAssetCache`, then dynamic-imports
289
+ * the entry module.
290
+ */
291
+ var make = (builtinPlugins, options = {}) => {
292
+ const storage = options.storage ?? defaultStorage();
293
+ const key = options.key ?? DEFAULT_KEY;
294
+ const cache = options.cache ?? noop();
295
+ return (locator) => Effect.gen(function* () {
296
+ const builtin = builtinPlugins.find((plugin) => plugin.meta.profile.key === locator);
297
+ if (builtin) return { plugin: builtin };
298
+ if (!isUrl(locator)) return yield* Effect.fail(new RemotePluginLoadError({ context: {
299
+ locator,
300
+ reason: "invalid-locator"
301
+ } }));
302
+ const { plugin, manifest } = yield* loadFromManifest(locator, cache);
303
+ if (!manifest.dev) {
304
+ if (builtinPlugins.find((existing) => existing.meta.profile.key === plugin.meta.profile.key)) return yield* Effect.fail(new RemotePluginLoadError({ context: {
305
+ locator,
306
+ reason: "duplicate-id",
307
+ id: plugin.meta.profile.key
308
+ } }));
309
+ persistRemotePlugin(storage, key, {
310
+ id: plugin.meta.profile.key,
311
+ url: locator
312
+ });
313
+ }
314
+ return {
315
+ plugin,
316
+ dev: manifest.dev
317
+ };
318
+ });
319
+ };
320
+ /**
321
+ * Removes a previously installed remote plugin: drops the persisted entry, evicts cached
322
+ * assets, and removes any stylesheet `<link>` tags that {@link loadFromManifest} appended.
323
+ *
324
+ * Cache eviction failures are logged and swallowed — the persisted entry has already been
325
+ * dropped so the user-visible state is consistent regardless of whether the platform
326
+ * cache cooperated.
327
+ */
328
+ var uninstall = (pluginId, options = {}) => Effect.gen(function* () {
329
+ const storage = options.storage ?? defaultStorage();
330
+ const key = options.key ?? DEFAULT_KEY;
331
+ const cache = options.cache ?? noop();
332
+ removePersistedRemotePlugin(storage, key, pluginId);
333
+ if (typeof document !== "undefined") document.querySelectorAll(`link[data-dxos-plugin-id="${pluginId}"]`).forEach((node) => node.remove());
334
+ yield* cache.evict(pluginId).pipe(Effect.tapError((error) => Effect.sync(() => log.warn("failed to evict plugin assets", {
335
+ pluginId,
336
+ error
337
+ }, {
338
+ "~LogMeta": "~LogMeta",
339
+ F: __dxlog_file,
340
+ L: 389,
341
+ S: this
342
+ }))), Effect.ignore);
343
+ });
344
+ //#endregion
345
+ export { make as a, uninstall as c, isLocalUrl as i, url_loader_exports as l, getInstalledVersion as n, preload as o, getRemoteEntries as r, setInstalledVersion as s, RemotePluginLoadError as t, plugin_asset_cache_exports as u };
346
+
347
+ //# sourceMappingURL=chunk-url-loader.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-url-loader.mjs","names":[],"sources":["../../src/core/plugin-asset-cache.ts","../../src/core/url-loader.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport { BaseError } from '@dxos/errors';\n\n/**\n * Tagged error for cache operations. Construction sites supply `context.operation`\n * (one of `'cache' | 'evict' | 'resolve' | 'list'`) and `context.pluginId` so\n * downstream handlers can route on the failed call without scanning the message.\n */\nexport class PluginAssetCacheError extends BaseError.extend(\n 'PluginAssetCacheError',\n 'Plugin asset cache operation failed',\n) {}\n\n/**\n * Per-platform offline cache for third-party plugin assets.\n *\n * Implementations live alongside their platform glue (composer-app's\n * `asset-cache/{tauri,service-worker}.ts`); the no-op default is for tests\n * and unsupported environments.\n */\nexport interface Cache {\n /**\n * Persist all listed URLs under the namespace of `pluginId`. Idempotent.\n * The order of `urls` is significant — the first entry is treated as the entry module.\n */\n cache(pluginId: string, urls: readonly string[]): Effect.Effect<void, PluginAssetCacheError>;\n\n /**\n * Drop all assets for a plugin (uninstall).\n */\n evict(pluginId: string): Effect.Effect<void, PluginAssetCacheError>;\n\n /**\n * Resolves a plugin asset URL to a platform-specific cached URL when one is available.\n * Returns the original URL when no cached copy exists or the platform serves cached\n * responses transparently (e.g. service worker fetch interception on web).\n */\n resolve(pluginId: string, url: string): Effect.Effect<string, PluginAssetCacheError>;\n\n /**\n * List currently cached plugin ids (diagnostic).\n */\n list(): Effect.Effect<readonly string[], PluginAssetCacheError>;\n}\n\n/**\n * No-op implementation. Used when no platform cache is registered (tests, unsupported environments).\n * Plugins still load — they just have no offline guarantee.\n */\nexport const noop = (): Cache => ({\n cache: () => Effect.void,\n evict: () => Effect.void,\n resolve: (_pluginId, url) => Effect.succeed(url),\n list: () => Effect.succeed([] as readonly string[]),\n});\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\n\nimport { BaseError } from '@dxos/errors';\nimport { log } from '@dxos/log';\n\nimport * as Plugin from './plugin';\nimport * as PluginAssetCache from './plugin-asset-cache';\nimport * as PluginManifest from './plugin-manifest';\n\nconst DEFAULT_KEY = 'org.dxos.composer.remote-plugins';\n\n/**\n * Tagged error for any failure during remote plugin loading. Construction sites\n * set `context.locator` and `context.reason` (one of `'invalid-locator' |\n * 'manifest-error' | 'cache-error' | 'import-failed' | 'meta-missing' |\n * 'meta-mismatch' | 'duplicate-id'`) so handlers can route on the failure mode.\n */\nexport class RemotePluginLoadError extends BaseError.extend('RemotePluginLoadError', 'Failed to load remote plugin') {}\n\n/**\n * Abstraction over key-value storage (defaults to localStorage).\n */\nexport type Storage = {\n get(key: string): string | null;\n set(key: string, value: string): void;\n};\n\n/**\n * Options for configuring the URL loader.\n */\nexport type Options = {\n storage?: Storage;\n key?: string;\n /**\n * Per-platform offline asset cache. Defaults to a no-op cache (no offline support).\n */\n cache?: PluginAssetCache.Cache;\n};\n\n/**\n * Options for the preload entry point. Adds an optional progress hook so hosts\n * can drive a boot-loader counter (`Loading plugins (3/12)…`) as remote plugin\n * manifests resolve. Resolution order is *not* deterministic — each entry races\n * its own network fetch — so callers should treat `loaded` as a count, not an\n * index. Failures are still swallowed; `loaded` advances on both success and\n * recoverable failure so the counter always reaches `total`.\n */\nexport type PreloadOptions = Options & {\n onPluginLoaded?: (loaded: number, total: number) => void;\n};\n\n/**\n * Persisted record of a remote plugin that has been loaded previously.\n *\n * `url` is the URL of the plugin manifest (`plugin.json`), not the entry module.\n */\nexport type RemotePluginView = {\n id: string;\n url: string;\n /** Installed version string, e.g. `v1.0.0`. Populated after install from a community catalog entry. */\n version?: string;\n};\n\nconst defaultStorage = (): Storage => ({\n get: (key) => localStorage.getItem(key),\n set: (key, value) => localStorage.setItem(key, value),\n});\n\nconst getPersistedRemotePlugins = (storage: Storage, key: string): RemotePluginView[] => {\n try {\n const parsed: unknown = JSON.parse(storage.get(key) ?? '[]');\n if (!Array.isArray(parsed)) {\n return [];\n }\n return parsed.filter(\n (entry): entry is RemotePluginView =>\n typeof entry === 'object' &&\n entry !== null &&\n typeof entry.id === 'string' &&\n typeof entry.url === 'string' &&\n (entry.version === undefined || typeof entry.version === 'string'),\n );\n } catch {\n return [];\n }\n};\n\nconst persistRemotePlugin = (storage: Storage, key: string, entry: RemotePluginView): void => {\n try {\n const entries = getPersistedRemotePlugins(storage, key).filter((existing) => existing.id !== entry.id);\n entries.push(entry);\n storage.set(key, JSON.stringify(entries));\n } catch (error) {\n log.warn('failed to persist remote plugin entry', { entry, error });\n }\n};\n\nconst removePersistedRemotePlugin = (storage: Storage, key: string, pluginId: string): void => {\n try {\n const entries = getPersistedRemotePlugins(storage, key).filter((existing) => existing.id !== pluginId);\n storage.set(key, JSON.stringify(entries));\n } catch (error) {\n log.warn('failed to remove remote plugin entry', { pluginId, error });\n }\n};\n\nconst isUrl = (locator: string): boolean => {\n try {\n const url = new URL(locator);\n return url.protocol === 'http:' || url.protocol === 'https:';\n } catch {\n return false;\n }\n};\n\n/**\n * Returns true when the URL's hostname is the local host (localhost, 127.0.0.1, or ::1).\n */\nexport const isLocalUrl = (locator: string): boolean => {\n try {\n const hostname = new URL(locator).hostname.toLowerCase();\n // WHATWG URL returns '::1' without brackets in Node; some browsers return '[::1]'.\n return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1' || hostname === '[::1]';\n } catch {\n return false;\n }\n};\n\n/**\n * Returns the list of remote plugin entries previously persisted by {@link make}.\n * Useful for UI code that needs to know which loaded plugins were installed from a URL\n * (e.g. to surface a tag on remote or localhost-hosted plugins).\n */\nexport const getRemoteEntries = (options: Options = {}): readonly RemotePluginView[] => {\n const storage = options.storage ?? defaultStorage();\n const key = options.key ?? DEFAULT_KEY;\n return getPersistedRemotePlugins(storage, key);\n};\n\n/**\n * Updates the persisted installed version for an already-stored remote plugin entry.\n * Does nothing if the plugin has not been persisted yet.\n */\nexport const setInstalledVersion = (id: string, version: string, options: Options = {}): void => {\n const storage = options.storage ?? defaultStorage();\n const key = options.key ?? DEFAULT_KEY;\n const entries = getPersistedRemotePlugins(storage, key);\n const index = entries.findIndex((entry) => entry.id === id);\n if (index === -1) {\n return;\n }\n entries[index] = { ...entries[index], version };\n try {\n storage.set(key, JSON.stringify(entries));\n } catch (error) {\n log.warn('failed to update installed version for remote plugin', { id, version, error });\n }\n};\n\n/**\n * Returns the installed version string for the given plugin id, or `undefined` if\n * the plugin was installed without version tracking (legacy install) or is not found.\n */\nexport const getInstalledVersion = (id: string, options: Options = {}): string | undefined => {\n const storage = options.storage ?? defaultStorage();\n const key = options.key ?? DEFAULT_KEY;\n return getPersistedRemotePlugins(storage, key).find((entry) => entry.id === id)?.version;\n};\n\nconst normalizePluginExport = (mod: Record<string, unknown>): Plugin.Plugin => {\n const exported = mod.default;\n if (Plugin.isPlugin(exported)) {\n return exported;\n }\n if (typeof exported === 'function') {\n const result = (exported as () => unknown)();\n if (Plugin.isPlugin(result)) {\n return result;\n }\n }\n throw new Error('Remote module default export is not a Plugin or a zero-arg plugin factory.');\n};\n\n/**\n * Loads stylesheets declared in the manifest by appending `<link rel=\"stylesheet\">` elements to the host document.\n * Each link is tagged with `data-dxos-plugin-id` so `uninstall` can clean them up.\n */\nconst loadStylesheets = (\n manifest: PluginManifest.ResolvedManifest,\n cache: PluginAssetCache.Cache,\n): Effect.Effect<void, PluginAssetCache.PluginAssetCacheError> =>\n Effect.gen(function* () {\n if (typeof document === 'undefined') {\n return;\n }\n const cssUrls = manifest.assetUrls.filter((url) => url.endsWith('.css'));\n for (const url of cssUrls) {\n const resolved = yield* cache.resolve(manifest.key, url);\n if (document.querySelector(`link[data-dxos-plugin-id=\"${manifest.key}\"][href=\"${resolved}\"]`)) {\n continue;\n }\n const link = document.createElement('link');\n link.rel = 'stylesheet';\n link.href = resolved;\n link.dataset.dxosPluginId = manifest.key;\n document.head.appendChild(link);\n }\n });\n\nconst loadFromManifest = (\n manifestUrl: string,\n cache: PluginAssetCache.Cache,\n): Effect.Effect<{ plugin: Plugin.Plugin; manifest: PluginManifest.ResolvedManifest }, RemotePluginLoadError> =>\n Effect.gen(function* () {\n log.info('loading remote plugin', { manifestUrl });\n const manifest = yield* PluginManifest.fetchManifest(manifestUrl).pipe(\n Effect.mapError(\n (cause) => new RemotePluginLoadError({ context: { locator: manifestUrl, reason: 'manifest-error' }, cause }),\n ),\n );\n const wrapCacheError = Effect.mapError(\n (cause: PluginAssetCache.PluginAssetCacheError) =>\n new RemotePluginLoadError({ context: { locator: manifestUrl, reason: 'cache-error' }, cause }),\n );\n // Dev-mode manifests (served by `vite dev` via composerPlugin) skip the offline\n // cache: chunks resolve on demand through the dev server, the asset list isn't\n // enumerable up-front, and CSS arrives via runtime <style> injection rather than\n // static <link> tags.\n if (!manifest.dev) {\n // Cache the manifest URL alongside its declared assets. Without it, `preload` on a\n // subsequent reload would fetch the manifest from the network — and fail when the\n // plugin's host is offline, dropping the plugin from the runtime.\n const cachedUrls =\n manifest.assetUrls.indexOf(manifestUrl) === -1 ? [manifestUrl, ...manifest.assetUrls] : manifest.assetUrls;\n yield* cache.cache(manifest.key, cachedUrls).pipe(wrapCacheError);\n }\n const entryUrl = manifest.dev\n ? manifest.entryUrl\n : yield* cache.resolve(manifest.key, manifest.entryUrl).pipe(wrapCacheError);\n const mod = yield* Effect.tryPromise({\n try: () => import(/* @vite-ignore */ entryUrl),\n catch: (cause) =>\n new RemotePluginLoadError({ context: { locator: manifestUrl, reason: 'import-failed' }, cause }),\n });\n const plugin = normalizePluginExport(mod);\n if (!plugin.meta.profile.key || !plugin.meta.profile.name) {\n return yield* Effect.fail(\n new RemotePluginLoadError({ context: { locator: manifestUrl, reason: 'meta-missing' } }),\n );\n }\n if (plugin.meta.profile.key !== manifest.key) {\n return yield* Effect.fail(\n new RemotePluginLoadError({\n context: {\n locator: manifestUrl,\n reason: 'meta-mismatch',\n metaId: plugin.meta.profile.key,\n manifestId: manifest.key,\n },\n }),\n );\n }\n // Append stylesheets only after the entry imports cleanly and meta validation\n // passes. If we did this earlier and the import or meta checks failed, the\n // `<link>` tags would leak into the host DOM with no plugin to own their\n // teardown — `uninstall` only runs for plugins the manager actually accepted.\n // In dev mode, Vite injects styles at runtime via the module graph, so static\n // <link> tags would duplicate (or 404, since CSS chunks aren't pre-built).\n if (!manifest.dev) {\n yield* loadStylesheets(manifest, cache).pipe(wrapCacheError);\n }\n return { plugin, manifest };\n });\n\n/**\n * Preloads previously persisted remote plugins from storage. Per-entry failures\n * are logged and swallowed — the returned effect always succeeds with whichever\n * plugins loaded cleanly, so a single bad entry can't block the host's startup.\n */\nexport const preload = (options: PreloadOptions = {}): Effect.Effect<Plugin.Plugin[], never> =>\n Effect.gen(function* () {\n const storage = options.storage ?? defaultStorage();\n const key = options.key ?? DEFAULT_KEY;\n const cache = options.cache ?? PluginAssetCache.noop();\n const onPluginLoaded = options.onPluginLoaded;\n\n const entries = getPersistedRemotePlugins(storage, key);\n if (entries.length === 0) {\n return [];\n }\n log.info('preloading remote plugins', { count: entries.length });\n const total = entries.length;\n let loaded = 0;\n const results = yield* Effect.all(\n entries.map((entry) =>\n loadFromManifest(entry.url, cache).pipe(\n Effect.tapError((error) => Effect.sync(() => log.warn('failed to preload remote plugin', { entry, error }))),\n Effect.option,\n // Tick the progress hook on both success and recoverable failure so\n // the counter monotonically reaches `total`. The host-supplied\n // callback is best-effort: any synchronous throw flows through\n // `Effect.try` and gets logged + ignored so a buggy hook can't\n // derail the preload.\n Effect.tap(() =>\n Effect.sync(() => {\n loaded += 1;\n }).pipe(\n Effect.andThen(Effect.try(() => onPluginLoaded?.(loaded, total))),\n Effect.tapError((error) => Effect.sync(() => log.warn('onPluginLoaded threw', { loaded, total, error }))),\n Effect.ignore,\n ),\n ),\n ),\n ),\n { concurrency: 'unbounded' },\n );\n return results.flatMap((result) =>\n Option.match(result, {\n onNone: () => [],\n onSome: ({ plugin }) => [plugin],\n }),\n );\n });\n\n/**\n * Creates a plugin loader that resolves built-in plugins by ID or loads remote plugins from URLs.\n *\n * Remote URLs must point at a plugin manifest (`plugin.json`). The loader fetches the manifest,\n * eagerly persists every declared asset via the configured `PluginAssetCache`, then dynamic-imports\n * the entry module.\n */\nexport const make = (builtinPlugins: Plugin.Plugin[], options: Options = {}) => {\n const storage = options.storage ?? defaultStorage();\n const key = options.key ?? DEFAULT_KEY;\n const cache = options.cache ?? PluginAssetCache.noop();\n\n return (locator: string): Effect.Effect<{ plugin: Plugin.Plugin; dev?: boolean }, RemotePluginLoadError> =>\n Effect.gen(function* () {\n const builtin = builtinPlugins.find((plugin) => plugin.meta.profile.key === locator);\n if (builtin) {\n return { plugin: builtin };\n }\n if (!isUrl(locator)) {\n return yield* Effect.fail(new RemotePluginLoadError({ context: { locator, reason: 'invalid-locator' } }));\n }\n const { plugin, manifest } = yield* loadFromManifest(locator, cache);\n // Production manifests can't collide with a builtin id — that would make\n // the registered plugin permanently inaccessible. Dev manifests are\n // allowed to collide because the manager shadow primitive handles the\n // overlap (dev plugin takes the id slot for the session, original is\n // restored on uninstall or page reload).\n if (!manifest.dev) {\n const duplicate = builtinPlugins.find((existing) => existing.meta.profile.key === plugin.meta.profile.key);\n if (duplicate) {\n return yield* Effect.fail(\n new RemotePluginLoadError({ context: { locator, reason: 'duplicate-id', id: plugin.meta.profile.key } }),\n );\n }\n persistRemotePlugin(storage, key, { id: plugin.meta.profile.key, url: locator });\n }\n return { plugin, dev: manifest.dev };\n });\n};\n\n/**\n * Removes a previously installed remote plugin: drops the persisted entry, evicts cached\n * assets, and removes any stylesheet `<link>` tags that {@link loadFromManifest} appended.\n *\n * Cache eviction failures are logged and swallowed — the persisted entry has already been\n * dropped so the user-visible state is consistent regardless of whether the platform\n * cache cooperated.\n */\nexport const uninstall = (pluginId: string, options: Options = {}): Effect.Effect<void, never> =>\n Effect.gen(function* () {\n const storage = options.storage ?? defaultStorage();\n const key = options.key ?? DEFAULT_KEY;\n const cache = options.cache ?? PluginAssetCache.noop();\n\n removePersistedRemotePlugin(storage, key, pluginId);\n if (typeof document !== 'undefined') {\n document.querySelectorAll(`link[data-dxos-plugin-id=\"${pluginId}\"]`).forEach((node) => node.remove());\n }\n yield* cache.evict(pluginId).pipe(\n Effect.tapError((error) => Effect.sync(() => log.warn('failed to evict plugin assets', { pluginId, error }))),\n Effect.ignore,\n );\n });\n"],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ import { Service } from "./core/capability.mjs";
2
+ import { a as make } from "./chunk-plugin-manager.mjs";
3
+ import { Service as Service$1 } from "./core/plugin.mjs";
4
+ import { AtomRegistry, Command as Command$1, Layer as Layer$1, PluginManager } from "./common/capabilities.mjs";
5
+ import { Startup } from "./common/activation-events.mjs";
6
+ import * as Effect from "effect/Effect";
7
+ import * as Layer from "effect/Layer";
8
+ import { invariant } from "@dxos/invariant";
9
+ import * as Command from "@effect/cli/Command";
10
+ //#region src/cli/cli.ts
11
+ var __dxlog_file = "/__w/dxos/dxos/packages/sdk/app-framework/src/cli/cli.ts";
12
+ var defaultPluginLoader = (plugins) => (id) => Effect.sync(() => {
13
+ const plugin = plugins.find((plugin) => plugin.meta.profile.key === id);
14
+ invariant(plugin, `Plugin not found: ${id}`, {
15
+ "~LogMeta": "~LogMeta",
16
+ F: __dxlog_file,
17
+ L: 19,
18
+ S: void 0,
19
+ A: ["plugin", "`Plugin not found: ${id}`"]
20
+ });
21
+ return { plugin };
22
+ });
23
+ /**
24
+ * Creates a CLI application with plugin support.
25
+ * Similar to useApp but for CLI environments.
26
+ *
27
+ * @example
28
+ * const plugins = [ClientPluginCLI()];
29
+ * const manager = await createCliApp({ plugins });
30
+ * const commands = manager.capabilities.getAll(Capabilities.Command);
31
+ *
32
+ * @param options.pluginManager Optional existing PluginManager instance.
33
+ * @param options.pluginLoader Function to load plugins by ID.
34
+ * @param options.plugins All plugins available to the application. Plugins whose `meta.profile.tags` includes `'system'` are treated as core.
35
+ * @param options.enabled Enabled plugins.
36
+ * @param options.safeMode Whether to enable safe mode, which disables optional plugins.
37
+ */
38
+ var createCliApp = Effect.fn(function* ({ rootCommand, subCommands: subCommandsProp, pluginManager: pluginManagerProp, pluginLoader: pluginLoaderProp, plugins: pluginsProp = [], enabled: enabledProp = [], safeMode = false }) {
39
+ const plugins = pluginsProp;
40
+ const pluginLoader = pluginLoaderProp ?? defaultPluginLoader(plugins);
41
+ const manager = pluginManagerProp ?? make({
42
+ pluginLoader,
43
+ plugins,
44
+ enabled: safeMode ? [] : enabledProp
45
+ });
46
+ manager.capabilities.contribute({
47
+ interface: PluginManager,
48
+ implementation: manager,
49
+ module: "org.dxos.app-framework.plugin-manager"
50
+ });
51
+ manager.capabilities.contribute({
52
+ interface: AtomRegistry,
53
+ implementation: manager.registry,
54
+ module: "org.dxos.app-framework.atom-registry"
55
+ });
56
+ yield* manager.activate(Startup);
57
+ const layers = manager.capabilities.getAll(Layer$1);
58
+ const capabilityServiceLayer = Layer.succeed(Service, manager.capabilities);
59
+ const pluginServiceLayer = Layer.succeed(Service$1, manager);
60
+ const layer = Layer.mergeAll(capabilityServiceLayer, pluginServiceLayer, ...layers);
61
+ const pluginCommands = manager.capabilities.getAll(Command$1);
62
+ const subCommands = subCommandsProp ? [...subCommandsProp, ...pluginCommands] : pluginCommands;
63
+ invariant(subCommands.length > 0, "No subcommands provided", {
64
+ "~LogMeta": "~LogMeta",
65
+ F: __dxlog_file,
66
+ L: 98,
67
+ S: this,
68
+ A: ["subCommands.length > 0", "'No subcommands provided'"]
69
+ });
70
+ return {
71
+ command: rootCommand.pipe(Command.withSubcommands(subCommands)),
72
+ layer
73
+ };
74
+ });
75
+ //#endregion
76
+ export { createCliApp };
77
+
78
+ //# sourceMappingURL=cli.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../../src/cli/cli.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Command from '@effect/cli/Command';\nimport * as Effect from 'effect/Effect';\nimport * as Layer from 'effect/Layer';\n\nimport { invariant } from '@dxos/invariant';\n\nimport { ActivationEvents, Capabilities } from '../common';\nimport { Capability, Plugin, PluginManager } from '../core';\n\nconst defaultPluginLoader =\n (plugins: Plugin.Plugin[]): PluginManager.ManagerOptions['pluginLoader'] =>\n (id: string) =>\n Effect.sync(() => {\n const plugin = plugins.find((plugin) => plugin.meta.profile.key === id);\n invariant(plugin, `Plugin not found: ${id}`);\n return { plugin };\n });\n\ntype SubCommands = [Command.Command<any, any, any, any>, ...Array<Command.Command<any, any, any, any>>];\n\nexport type CreateCliAppOptions = {\n rootCommand: Command.Command<any, any, any, any>;\n subCommands?: SubCommands;\n pluginManager?: PluginManager.PluginManager;\n pluginLoader?: PluginManager.ManagerOptions['pluginLoader'];\n plugins?: Plugin.Plugin[];\n enabled?: string[];\n safeMode?: boolean;\n};\n\n/**\n * Creates a CLI application with plugin support.\n * Similar to useApp but for CLI environments.\n *\n * @example\n * const plugins = [ClientPluginCLI()];\n * const manager = await createCliApp({ plugins });\n * const commands = manager.capabilities.getAll(Capabilities.Command);\n *\n * @param options.pluginManager Optional existing PluginManager instance.\n * @param options.pluginLoader Function to load plugins by ID.\n * @param options.plugins All plugins available to the application. Plugins whose `meta.profile.tags` includes `'system'` are treated as core.\n * @param options.enabled Enabled plugins.\n * @param options.safeMode Whether to enable safe mode, which disables optional plugins.\n */\nexport const createCliApp = Effect.fn(function* ({\n rootCommand,\n subCommands: subCommandsProp,\n pluginManager: pluginManagerProp,\n pluginLoader: pluginLoaderProp,\n plugins: pluginsProp = [],\n enabled: enabledProp = [],\n safeMode = false,\n}: CreateCliAppOptions) {\n const plugins = pluginsProp;\n const pluginLoader = pluginLoaderProp ?? defaultPluginLoader(plugins);\n const enabled = safeMode ? [] : enabledProp;\n const manager =\n pluginManagerProp ??\n PluginManager.make({\n pluginLoader,\n plugins,\n enabled,\n });\n\n manager.capabilities.contribute({\n interface: Capabilities.PluginManager,\n implementation: manager,\n module: 'org.dxos.app-framework.plugin-manager',\n });\n\n manager.capabilities.contribute({\n interface: Capabilities.AtomRegistry,\n implementation: manager.registry,\n module: 'org.dxos.app-framework.atom-registry',\n });\n\n // Activate startup event to load CLI commands and Effect layers.\n yield* manager.activate(ActivationEvents.Startup);\n\n // Gather all layers and merge them into a single layer.\n const layers = manager.capabilities.getAll(Capabilities.Layer);\n const capabilityServiceLayer = Layer.succeed(Capability.Service, manager.capabilities);\n const pluginServiceLayer = Layer.succeed(Plugin.Service, manager);\n const layer = (Layer.mergeAll as (...args: Layer.Layer<any, any, any>[]) => Layer.Layer<any, any, never>)(\n capabilityServiceLayer,\n pluginServiceLayer,\n ...layers,\n );\n\n // Gather all commands and provide them to the root command.\n const pluginCommands = manager.capabilities.getAll(Capabilities.Command);\n const subCommands = subCommandsProp ? [...subCommandsProp, ...pluginCommands] : pluginCommands;\n invariant(subCommands.length > 0, 'No subcommands provided');\n const command = rootCommand.pipe(Command.withSubcommands(subCommands as SubCommands));\n\n return { command, layer };\n});\n"],"mappings":";;;;;;;;;;;AAaA,IAAM,uBACH,aACA,OACC,OAAO,WAAW;CAChB,MAAM,SAAS,QAAQ,MAAM,WAAW,OAAO,KAAK,QAAQ,QAAQ,EAAE;CACtE,UAAU,QAAQ,qBAAqB,MAAG;EAAA,YAAA;EAAA,GAAA;EAAA,GAAA;EAAA,GAAA,KAAA;EAAA,GAAA,CAAA,UAAA,2BAAA;CAAA,CAAC;CAC3C,OAAO,EAAE,OAAO;AAClB,CAAC;;;;;;;;;;;;;;;;AA6BL,IAAa,eAAe,OAAO,GAAG,WAAW,EAC/C,aACA,aAAa,iBACb,eAAe,mBACf,cAAc,kBACd,SAAS,cAAc,CAAC,GACxB,SAAS,cAAc,CAAC,GACxB,WAAW,SACW;CACtB,MAAM,UAAU;CAChB,MAAM,eAAe,oBAAoB,oBAAoB,OAAO;CAEpE,MAAM,UACJ,qBACA,KAAmB;EACjB;EACA;EACA,SANY,WAAW,CAAC,IAAI;CAO9B,CAAC;CAEH,QAAQ,aAAa,WAAW;EAC9B,WAAW;EACX,gBAAgB;EAChB,QAAQ;CACV,CAAC;CAED,QAAQ,aAAa,WAAW;EAC9B,WAAW;EACX,gBAAgB,QAAQ;EACxB,QAAQ;CACV,CAAC;CAGD,OAAO,QAAQ,SAAS,OAAwB;CAGhD,MAAM,SAAS,QAAQ,aAAa,OAAO,OAAkB;CAC7D,MAAM,yBAAyB,MAAM,QAAQ,SAAoB,QAAQ,YAAY;CACrF,MAAM,qBAAqB,MAAM,QAAQ,WAAgB,OAAO;CAChE,MAAM,QAAS,MAAM,SACnB,wBACA,oBACA,GAAG,MACL;CAGA,MAAM,iBAAiB,QAAQ,aAAa,OAAO,SAAoB;CACvE,MAAM,cAAc,kBAAkB,CAAC,GAAG,iBAAiB,GAAG,cAAc,IAAI;CAChF,UAAU,YAAY,SAAS,GAAG,2BAAwB;EAAA,YAAA;EAAA,GAAA;EAAA,GAAA;EAAA,GAAA;EAAA,GAAA,CAAA,0BAAA,2BAAA;CAAA,CAAC;CAG3D,OAAO;EAAE,SAFO,YAAY,KAAK,QAAQ,gBAAgB,WAA0B,CAE1E;EAAS;CAAM;AAC1B,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { t as __exportAll } from "../chunk-rolldown-runtime.mjs";
2
+ import { make } from "../core/activation-event.mjs";
3
+ //#region src/common/activation-events.ts
4
+ var activation_events_exports = /* @__PURE__ */ __exportAll({
5
+ ProcessManagerReady: () => ProcessManagerReady,
6
+ SetupProcessManager: () => SetupProcessManager,
7
+ SetupReactSurface: () => SetupReactSurface,
8
+ Startup: () => Startup
9
+ });
10
+ /**
11
+ * Fired when the app is started.
12
+ */
13
+ var Startup = make("org.dxos.app-framework.event.startup");
14
+ /**
15
+ * Fired to load any newly available surfaces.
16
+ */
17
+ var SetupReactSurface = make("org.dxos.app-framework.event.setupReactSurface");
18
+ /**
19
+ * Fired before the process manager is created.
20
+ * Plugins should contribute their {@link Capabilities.LayerSpec} entries and
21
+ * {@link Capabilities.OperationHandler} sets before this event fires so the
22
+ * process manager's {@link ServiceResolver} and {@link OperationInvoker} pick
23
+ * them up at construction time.
24
+ */
25
+ var SetupProcessManager = make("org.dxos.app-framework.event.setupProcessManager");
26
+ /**
27
+ * Fired after the process manager runtime is ready and its derived capabilities
28
+ * (`ProcessManagerRuntime`, `ServiceResolver`, `ProcessMonitor`, `OperationInvoker`)
29
+ * have been contributed.
30
+ */
31
+ var ProcessManagerReady = make("org.dxos.app-framework.event.processManagerReady");
32
+ //#endregion
33
+ export { ProcessManagerReady, SetupProcessManager, SetupReactSurface, Startup, activation_events_exports as t };
34
+
35
+ //# sourceMappingURL=activation-events.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activation-events.mjs","names":[],"sources":["../../../src/common/activation-events.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport { ActivationEvent as ActivationEvent$ } from '../core';\n\n/**\n * Fired when the app is started.\n */\nexport const Startup = ActivationEvent$.make('org.dxos.app-framework.event.startup');\n\n//\n// Dependent Events\n//\n\n/**\n * Fired to load any newly available surfaces.\n */\nexport const SetupReactSurface = ActivationEvent$.make('org.dxos.app-framework.event.setupReactSurface');\n\n/**\n * Fired before the process manager is created.\n * Plugins should contribute their {@link Capabilities.LayerSpec} entries and\n * {@link Capabilities.OperationHandler} sets before this event fires so the\n * process manager's {@link ServiceResolver} and {@link OperationInvoker} pick\n * them up at construction time.\n */\nexport const SetupProcessManager = ActivationEvent$.make('org.dxos.app-framework.event.setupProcessManager');\n\n//\n// Triggered Events\n//\n\n/**\n * Fired after the process manager runtime is ready and its derived capabilities\n * (`ProcessManagerRuntime`, `ServiceResolver`, `ProcessMonitor`, `OperationInvoker`)\n * have been contributed.\n */\nexport const ProcessManagerReady = ActivationEvent$.make('org.dxos.app-framework.event.processManagerReady');\n"],"mappings":";;;;;;;;;;;;AASA,IAAa,UAAU,KAAsB,sCAAsC;;;;AASnF,IAAa,oBAAoB,KAAsB,gDAAgD;;;;;;;;AASvG,IAAa,sBAAsB,KAAsB,kDAAkD;;;;;;AAW3G,IAAa,sBAAsB,KAAsB,kDAAkD"}