@dxos/app-framework 0.8.4-main.9be5663bfe → 0.8.4-main.abd8ff62ef

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 (278) hide show
  1. package/dist/lib/browser/{capability-BBBBAPDI.mjs → capability-Q5XRXRD2.mjs} +10 -10
  2. package/dist/lib/browser/{capability-OP63CD5N.mjs → capability-V7LR4LQN.mjs} +11 -11
  3. package/dist/lib/browser/capability-V7LR4LQN.mjs.map +7 -0
  4. package/dist/lib/browser/{chunk-T3Y4AEKX.mjs → chunk-23D4SJUE.mjs} +3 -3
  5. package/dist/lib/browser/{chunk-T3Y4AEKX.mjs.map → chunk-23D4SJUE.mjs.map} +1 -1
  6. package/dist/lib/browser/{chunk-2CKCJ6PN.mjs → chunk-3JWJXGLK.mjs} +1 -1
  7. package/dist/lib/browser/{chunk-2CKCJ6PN.mjs.map → chunk-3JWJXGLK.mjs.map} +1 -1
  8. package/dist/lib/browser/{chunk-GX4TUNM6.mjs → chunk-3ZS2A3DN.mjs} +170 -226
  9. package/dist/lib/browser/chunk-3ZS2A3DN.mjs.map +7 -0
  10. package/dist/lib/browser/{chunk-I34GF4NG.mjs → chunk-45CHLTBV.mjs} +2 -2
  11. package/dist/lib/browser/chunk-5LAIGWLU.mjs +467 -0
  12. package/dist/lib/browser/chunk-5LAIGWLU.mjs.map +7 -0
  13. package/dist/lib/browser/{chunk-QSXYHXCE.mjs → chunk-66IXTIVK.mjs} +1 -1
  14. package/dist/lib/browser/{chunk-QSXYHXCE.mjs.map → chunk-66IXTIVK.mjs.map} +2 -2
  15. package/dist/lib/browser/{chunk-TGX63LTL.mjs → chunk-FJ4765WW.mjs} +1 -1
  16. package/dist/lib/browser/{chunk-TGX63LTL.mjs.map → chunk-FJ4765WW.mjs.map} +2 -2
  17. package/dist/lib/browser/chunk-G7SDBRKH.mjs +1 -0
  18. package/dist/lib/browser/chunk-JXCBZSBJ.mjs +372 -0
  19. package/dist/lib/browser/chunk-JXCBZSBJ.mjs.map +7 -0
  20. package/dist/lib/browser/chunk-MX5DKEJH.mjs +584 -0
  21. package/dist/lib/browser/chunk-MX5DKEJH.mjs.map +7 -0
  22. package/dist/lib/browser/{chunk-JKWMHZP6.mjs → chunk-WBHCSOBW.mjs} +2 -2
  23. package/dist/lib/browser/chunk-WBHCSOBW.mjs.map +7 -0
  24. package/dist/lib/browser/{chunk-FU4GAFUQ.mjs → chunk-Z55LVAGN.mjs} +80 -15
  25. package/dist/lib/browser/chunk-Z55LVAGN.mjs.map +7 -0
  26. package/dist/lib/browser/{chunk-F7FW2RK2.mjs → chunk-ZGJAZSNE.mjs} +7 -32
  27. package/dist/lib/browser/chunk-ZGJAZSNE.mjs.map +7 -0
  28. package/dist/lib/browser/cli/index.mjs +11 -27
  29. package/dist/lib/browser/cli/index.mjs.map +2 -2
  30. package/dist/lib/browser/common/activation-events.mjs +7 -7
  31. package/dist/lib/browser/common/capabilities.mjs +7 -7
  32. package/dist/lib/browser/core/activation-event.mjs +1 -1
  33. package/dist/lib/browser/core/capability.mjs +1 -1
  34. package/dist/lib/browser/core/plugin-manager.mjs +6 -4
  35. package/dist/lib/browser/core/plugin.mjs +10 -2
  36. package/dist/lib/browser/core/url-loader.mjs +13 -5
  37. package/dist/lib/browser/index.mjs +22 -18
  38. package/dist/lib/browser/index.mjs.map +3 -3
  39. package/dist/lib/browser/{invoker-capability-H5PPENOC.mjs → invoker-capability-LNX4CGIV.mjs} +12 -11
  40. package/dist/lib/browser/invoker-capability-LNX4CGIV.mjs.map +7 -0
  41. package/dist/lib/browser/meta.json +1 -1
  42. package/dist/lib/browser/testing/index.mjs +144 -27
  43. package/dist/lib/browser/testing/index.mjs.map +4 -4
  44. package/dist/lib/browser/testing/react.mjs +78 -0
  45. package/dist/lib/browser/testing/react.mjs.map +7 -0
  46. package/dist/lib/browser/ui/index.mjs +18 -14
  47. package/dist/lib/node-esm/{capability-AWBEMRYR.mjs → capability-EW5GJCI6.mjs} +10 -10
  48. package/dist/lib/node-esm/{capability-WFEG6CIZ.mjs → capability-YKBMMD53.mjs} +11 -11
  49. package/dist/lib/node-esm/capability-YKBMMD53.mjs.map +7 -0
  50. package/dist/lib/node-esm/{chunk-FKE4Z3D6.mjs → chunk-37Z53PXZ.mjs} +1 -1
  51. package/dist/lib/node-esm/{chunk-FKE4Z3D6.mjs.map → chunk-37Z53PXZ.mjs.map} +2 -2
  52. package/dist/lib/node-esm/{chunk-WZCSOX5Q.mjs → chunk-6XW6LET6.mjs} +2 -2
  53. package/dist/lib/node-esm/{chunk-URWHJQT2.mjs → chunk-D347W3KO.mjs} +7 -32
  54. package/dist/lib/node-esm/chunk-D347W3KO.mjs.map +7 -0
  55. package/dist/lib/node-esm/chunk-D5PO2WXX.mjs +373 -0
  56. package/dist/lib/node-esm/chunk-D5PO2WXX.mjs.map +7 -0
  57. package/dist/lib/node-esm/{chunk-ULUEXB7Q.mjs → chunk-HTBJU5FX.mjs} +80 -15
  58. package/dist/lib/node-esm/chunk-HTBJU5FX.mjs.map +7 -0
  59. package/dist/lib/node-esm/chunk-KM2F6GH6.mjs +468 -0
  60. package/dist/lib/node-esm/chunk-KM2F6GH6.mjs.map +7 -0
  61. package/dist/lib/node-esm/{chunk-EL3R25OQ.mjs → chunk-OZ7DZA5Z.mjs} +1 -1
  62. package/dist/lib/node-esm/{chunk-BCEOLX47.mjs → chunk-Q7XBFII4.mjs} +170 -226
  63. package/dist/lib/node-esm/chunk-Q7XBFII4.mjs.map +7 -0
  64. package/dist/lib/node-esm/{chunk-VKHGNEDB.mjs → chunk-SBS2YMPT.mjs} +3 -3
  65. package/dist/lib/node-esm/{chunk-VKHGNEDB.mjs.map → chunk-SBS2YMPT.mjs.map} +1 -1
  66. package/dist/lib/node-esm/{chunk-42KBWDE4.mjs → chunk-SDJ4B2LU.mjs} +1 -1
  67. package/dist/lib/node-esm/{chunk-42KBWDE4.mjs.map → chunk-SDJ4B2LU.mjs.map} +1 -1
  68. package/dist/lib/node-esm/{chunk-G3RTFSNG.mjs → chunk-WFSRZKBP.mjs} +2 -2
  69. package/dist/lib/node-esm/chunk-WFSRZKBP.mjs.map +7 -0
  70. package/dist/lib/node-esm/chunk-WKTLE7MG.mjs +585 -0
  71. package/dist/lib/node-esm/chunk-WKTLE7MG.mjs.map +7 -0
  72. package/dist/lib/node-esm/{chunk-ZZ7CKK6W.mjs → chunk-XOCUANHO.mjs} +1 -1
  73. package/dist/lib/node-esm/{chunk-ZZ7CKK6W.mjs.map → chunk-XOCUANHO.mjs.map} +2 -2
  74. package/dist/lib/node-esm/cli/index.mjs +11 -27
  75. package/dist/lib/node-esm/cli/index.mjs.map +2 -2
  76. package/dist/lib/node-esm/common/activation-events.mjs +7 -7
  77. package/dist/lib/node-esm/common/capabilities.mjs +7 -7
  78. package/dist/lib/node-esm/core/activation-event.mjs +1 -1
  79. package/dist/lib/node-esm/core/capability.mjs +1 -1
  80. package/dist/lib/node-esm/core/plugin-manager.mjs +6 -4
  81. package/dist/lib/node-esm/core/plugin.mjs +10 -2
  82. package/dist/lib/node-esm/core/url-loader.mjs +13 -5
  83. package/dist/lib/node-esm/index.mjs +22 -18
  84. package/dist/lib/node-esm/index.mjs.map +3 -3
  85. package/dist/lib/node-esm/{invoker-capability-S3ZA527J.mjs → invoker-capability-O4T5PHLA.mjs} +12 -11
  86. package/dist/lib/node-esm/invoker-capability-O4T5PHLA.mjs.map +7 -0
  87. package/dist/lib/node-esm/meta.json +1 -1
  88. package/dist/lib/node-esm/testing/index.mjs +144 -27
  89. package/dist/lib/node-esm/testing/index.mjs.map +4 -4
  90. package/dist/lib/node-esm/testing/react.mjs +79 -0
  91. package/dist/lib/node-esm/testing/react.mjs.map +7 -0
  92. package/dist/lib/node-esm/ui/index.mjs +18 -14
  93. package/dist/plugin/node-esm/index.mjs +480 -32
  94. package/dist/plugin/node-esm/index.mjs.map +4 -4
  95. package/dist/plugin/node-esm/meta.json +1 -1
  96. package/dist/types/src/common/capabilities.d.ts +2 -1
  97. package/dist/types/src/common/capabilities.d.ts.map +1 -1
  98. package/dist/types/src/common/operations.d.ts +1 -1
  99. package/dist/types/src/common/operations.d.ts.map +1 -1
  100. package/dist/types/src/core/activation-event.d.ts +4 -4
  101. package/dist/types/src/core/activation-event.d.ts.map +1 -1
  102. package/dist/types/src/core/capability-manager.d.ts.map +1 -1
  103. package/dist/types/src/core/capability.d.ts +2 -2
  104. package/dist/types/src/core/capability.d.ts.map +1 -1
  105. package/dist/types/src/core/index.d.ts +2 -0
  106. package/dist/types/src/core/index.d.ts.map +1 -1
  107. package/dist/types/src/core/plugin-asset-cache.d.ts +71 -0
  108. package/dist/types/src/core/plugin-asset-cache.d.ts.map +1 -0
  109. package/dist/types/src/core/plugin-manager.d.ts +51 -2
  110. package/dist/types/src/core/plugin-manager.d.ts.map +1 -1
  111. package/dist/types/src/core/plugin-manifest.d.ts +76 -0
  112. package/dist/types/src/core/plugin-manifest.d.ts.map +1 -0
  113. package/dist/types/src/core/plugin-manifest.test.d.ts +2 -0
  114. package/dist/types/src/core/plugin-manifest.test.d.ts.map +1 -0
  115. package/dist/types/src/core/plugin.d.ts +107 -6
  116. package/dist/types/src/core/plugin.d.ts.map +1 -1
  117. package/dist/types/src/core/url-loader.d.ts +90 -3
  118. package/dist/types/src/core/url-loader.d.ts.map +1 -1
  119. package/dist/types/src/helpers.d.ts.map +1 -1
  120. package/dist/types/src/plugin-operation/history/capability.d.ts.map +1 -1
  121. package/dist/types/src/plugin-operation/history/errors.d.ts +6 -6
  122. package/dist/types/src/plugin-operation/history/errors.d.ts.map +1 -1
  123. package/dist/types/src/plugin-operation/history/history-tracker.d.ts +1 -1
  124. package/dist/types/src/plugin-operation/history/history-tracker.d.ts.map +1 -1
  125. package/dist/types/src/plugin-operation/history/types.d.ts +1 -1
  126. package/dist/types/src/plugin-operation/history/types.d.ts.map +1 -1
  127. package/dist/types/src/plugin-operation/history/undo-mapping.d.ts +1 -1
  128. package/dist/types/src/plugin-operation/history/undo-mapping.d.ts.map +1 -1
  129. package/dist/types/src/plugin-operation/history/undo-registry.d.ts +1 -1
  130. package/dist/types/src/plugin-operation/history/undo-registry.d.ts.map +1 -1
  131. package/dist/types/src/plugin-operation/invoker-capability.d.ts +1 -1
  132. package/dist/types/src/plugin-operation/invoker-capability.d.ts.map +1 -1
  133. package/dist/types/src/plugin-operation/testing.d.ts +2 -1
  134. package/dist/types/src/plugin-operation/testing.d.ts.map +1 -1
  135. package/dist/types/src/plugin-runtime/capability.d.ts +1 -1
  136. package/dist/types/src/plugin-runtime/capability.d.ts.map +1 -1
  137. package/dist/types/src/testing/harness.d.ts +67 -0
  138. package/dist/types/src/testing/harness.d.ts.map +1 -0
  139. package/dist/types/src/testing/index.d.ts +1 -0
  140. package/dist/types/src/testing/index.d.ts.map +1 -1
  141. package/dist/types/src/testing/react.d.ts +27 -0
  142. package/dist/types/src/testing/react.d.ts.map +1 -0
  143. package/dist/types/src/testing/react.test.d.ts +2 -0
  144. package/dist/types/src/testing/react.test.d.ts.map +1 -0
  145. package/dist/types/src/testing/service.d.ts.map +1 -1
  146. package/dist/types/src/testing/withPluginManager.d.ts.map +1 -1
  147. package/dist/types/src/testing/withPluginManager.stories.d.ts.map +1 -1
  148. package/dist/types/src/ui/components/App/App.d.ts.map +1 -1
  149. package/dist/types/src/ui/components/App/App.stories.d.ts.map +1 -1
  150. package/dist/types/src/ui/components/Placeholder/Placeholder.d.ts +64 -0
  151. package/dist/types/src/ui/components/Placeholder/Placeholder.d.ts.map +1 -0
  152. package/dist/types/src/ui/components/Placeholder/Placeholder.stories.d.ts +19 -0
  153. package/dist/types/src/ui/components/Placeholder/Placeholder.stories.d.ts.map +1 -0
  154. package/dist/types/src/ui/components/Placeholder/index.d.ts +2 -0
  155. package/dist/types/src/ui/components/Placeholder/index.d.ts.map +1 -0
  156. package/dist/types/src/ui/components/PluginManager/PluginManagerContext.stories.d.ts.map +1 -1
  157. package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts +16 -4
  158. package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts.map +1 -1
  159. package/dist/types/src/ui/components/Surface/SurfaceComponent.stories.d.ts.map +1 -1
  160. package/dist/types/src/ui/components/Surface/SurfaceProfilerContext.d.ts.map +1 -1
  161. package/dist/types/src/ui/components/Surface/index.d.ts +16 -6
  162. package/dist/types/src/ui/components/Surface/index.d.ts.map +1 -1
  163. package/dist/types/src/ui/components/Surface/types.d.ts +110 -9
  164. package/dist/types/src/ui/components/Surface/types.d.ts.map +1 -1
  165. package/dist/types/src/ui/components/Surface/types.test.d.ts +2 -0
  166. package/dist/types/src/ui/components/Surface/types.test.d.ts.map +1 -0
  167. package/dist/types/src/ui/components/index.d.ts +1 -0
  168. package/dist/types/src/ui/components/index.d.ts.map +1 -1
  169. package/dist/types/src/ui/hooks/useApp.d.ts +29 -3
  170. package/dist/types/src/ui/hooks/useApp.d.ts.map +1 -1
  171. package/dist/types/src/ui/hooks/useCapabilities.d.ts.map +1 -1
  172. package/dist/types/src/ui/hooks/useLoading.d.ts.map +1 -1
  173. package/dist/types/src/ui/hooks/useSettingsState.d.ts.map +1 -1
  174. package/dist/types/src/vite-plugin/boot-loader/BootLoader.stories.d.ts +34 -0
  175. package/dist/types/src/vite-plugin/boot-loader/BootLoader.stories.d.ts.map +1 -0
  176. package/dist/types/src/vite-plugin/boot-loader/index.d.ts +52 -0
  177. package/dist/types/src/vite-plugin/boot-loader/index.d.ts.map +1 -0
  178. package/dist/types/src/vite-plugin/composer/index.d.ts +34 -0
  179. package/dist/types/src/vite-plugin/composer/index.d.ts.map +1 -0
  180. package/dist/types/src/vite-plugin/import-map/index.d.ts +28 -0
  181. package/dist/types/src/vite-plugin/import-map/index.d.ts.map +1 -0
  182. package/dist/types/src/vite-plugin/index.d.ts +4 -2
  183. package/dist/types/src/vite-plugin/index.d.ts.map +1 -1
  184. package/dist/types/src/vite-plugin/manifest.d.ts +37 -0
  185. package/dist/types/src/vite-plugin/manifest.d.ts.map +1 -0
  186. package/dist/types/src/vite-plugin/manifest.test.d.ts +2 -0
  187. package/dist/types/src/vite-plugin/manifest.test.d.ts.map +1 -0
  188. package/dist/types/src/vite-plugin/packages.d.ts +10 -4
  189. package/dist/types/src/vite-plugin/packages.d.ts.map +1 -1
  190. package/dist/types/tsconfig.tsbuildinfo +1 -1
  191. package/moon.yml +1 -0
  192. package/package.json +33 -59
  193. package/src/common/capabilities.ts +2 -1
  194. package/src/common/operations.ts +1 -1
  195. package/src/core/capability.ts +1 -1
  196. package/src/core/index.ts +2 -0
  197. package/src/core/plugin-asset-cache.ts +60 -0
  198. package/src/core/plugin-manager.test.ts +246 -5
  199. package/src/core/plugin-manager.ts +167 -25
  200. package/src/core/plugin-manifest.test.ts +48 -0
  201. package/src/core/plugin-manifest.ts +102 -0
  202. package/src/core/plugin.ts +135 -10
  203. package/src/core/url-loader.test.ts +104 -5
  204. package/src/core/url-loader.ts +226 -37
  205. package/src/plugin-operation/OperationPlugin.ts +2 -2
  206. package/src/plugin-operation/history/capability.ts +1 -1
  207. package/src/plugin-operation/history/history-tracker.test.ts +2 -1
  208. package/src/plugin-operation/history/history-tracker.ts +1 -1
  209. package/src/plugin-operation/history/types.ts +1 -1
  210. package/src/plugin-operation/history/undo-mapping.ts +1 -1
  211. package/src/plugin-operation/history/undo-registry.ts +1 -1
  212. package/src/plugin-operation/invoker-capability.ts +2 -1
  213. package/src/plugin-operation/testing.ts +2 -1
  214. package/src/plugin-runtime/RuntimePlugin.ts +2 -2
  215. package/src/testing/harness.ts +229 -0
  216. package/src/testing/index.ts +1 -0
  217. package/src/testing/react.test.tsx +48 -0
  218. package/src/testing/react.tsx +113 -0
  219. package/src/testing/withPluginManager.stories.tsx +1 -1
  220. package/src/ui/components/App/App.stories.tsx +1 -1
  221. package/src/ui/components/App/App.tsx +25 -2
  222. package/src/ui/components/Placeholder/Placeholder.stories.tsx +77 -0
  223. package/src/ui/components/Placeholder/Placeholder.tsx +155 -0
  224. package/src/ui/components/Placeholder/index.ts +5 -0
  225. package/src/ui/components/PluginManager/PluginManagerContext.stories.tsx +4 -2
  226. package/src/ui/components/Surface/SurfaceComponent.stories.tsx +1 -1
  227. package/src/ui/components/Surface/SurfaceComponent.tsx +83 -46
  228. package/src/ui/components/Surface/index.ts +20 -1
  229. package/src/ui/components/Surface/types.test.ts +126 -0
  230. package/src/ui/components/Surface/types.ts +164 -12
  231. package/src/ui/components/index.ts +1 -0
  232. package/src/ui/hooks/useApp.tsx +165 -41
  233. package/src/ui/hooks/useLoading.tsx +14 -6
  234. package/src/vite-plugin/boot-loader/BootLoader.stories.tsx +263 -0
  235. package/src/vite-plugin/boot-loader/boot-loader.css +294 -0
  236. package/src/vite-plugin/boot-loader/boot-loader.js +274 -0
  237. package/src/vite-plugin/boot-loader/index.ts +112 -0
  238. package/src/vite-plugin/composer/index.ts +277 -0
  239. package/src/vite-plugin/import-map/index.ts +524 -0
  240. package/src/vite-plugin/index.ts +6 -2
  241. package/src/vite-plugin/manifest.test.ts +24 -0
  242. package/src/vite-plugin/manifest.ts +50 -0
  243. package/src/vite-plugin/packages.ts +169 -10
  244. package/tsconfig.json +9 -0
  245. package/.swc/plugins/linux_x86_64_19.0.0/727453fb3a62f7f1d952a41e051ca8a6f88cadc45cee43c6a4d1aa45f9b75665.wasmer-v7 +0 -0
  246. package/dist/lib/browser/capability-OP63CD5N.mjs.map +0 -7
  247. package/dist/lib/browser/chunk-F7FW2RK2.mjs.map +0 -7
  248. package/dist/lib/browser/chunk-FU4GAFUQ.mjs.map +0 -7
  249. package/dist/lib/browser/chunk-GX4TUNM6.mjs.map +0 -7
  250. package/dist/lib/browser/chunk-JKWMHZP6.mjs.map +0 -7
  251. package/dist/lib/browser/chunk-LVJW5EFU.mjs +0 -157
  252. package/dist/lib/browser/chunk-LVJW5EFU.mjs.map +0 -7
  253. package/dist/lib/browser/chunk-RFSO3JRG.mjs +0 -1
  254. package/dist/lib/browser/chunk-WPE6AL7I.mjs +0 -905
  255. package/dist/lib/browser/chunk-WPE6AL7I.mjs.map +0 -7
  256. package/dist/lib/browser/invoker-capability-H5PPENOC.mjs.map +0 -7
  257. package/dist/lib/node-esm/capability-WFEG6CIZ.mjs.map +0 -7
  258. package/dist/lib/node-esm/chunk-4A3ZCMI3.mjs +0 -158
  259. package/dist/lib/node-esm/chunk-4A3ZCMI3.mjs.map +0 -7
  260. package/dist/lib/node-esm/chunk-BCEOLX47.mjs.map +0 -7
  261. package/dist/lib/node-esm/chunk-G3RTFSNG.mjs.map +0 -7
  262. package/dist/lib/node-esm/chunk-LQKOTNJW.mjs +0 -906
  263. package/dist/lib/node-esm/chunk-LQKOTNJW.mjs.map +0 -7
  264. package/dist/lib/node-esm/chunk-ULUEXB7Q.mjs.map +0 -7
  265. package/dist/lib/node-esm/chunk-URWHJQT2.mjs.map +0 -7
  266. package/dist/lib/node-esm/invoker-capability-S3ZA527J.mjs.map +0 -7
  267. package/dist/types/src/vite-plugin/composer-plugin.d.ts +0 -18
  268. package/dist/types/src/vite-plugin/composer-plugin.d.ts.map +0 -1
  269. package/dist/types/src/vite-plugin/import-map-plugin.d.ts +0 -16
  270. package/dist/types/src/vite-plugin/import-map-plugin.d.ts.map +0 -1
  271. package/src/vite-plugin/composer-plugin.ts +0 -128
  272. package/src/vite-plugin/import-map-plugin.ts +0 -314
  273. /package/dist/lib/browser/{capability-BBBBAPDI.mjs.map → capability-Q5XRXRD2.mjs.map} +0 -0
  274. /package/dist/lib/browser/{chunk-I34GF4NG.mjs.map → chunk-45CHLTBV.mjs.map} +0 -0
  275. /package/dist/lib/browser/{chunk-RFSO3JRG.mjs.map → chunk-G7SDBRKH.mjs.map} +0 -0
  276. /package/dist/lib/node-esm/{capability-AWBEMRYR.mjs.map → capability-EW5GJCI6.mjs.map} +0 -0
  277. /package/dist/lib/node-esm/{chunk-WZCSOX5Q.mjs.map → chunk-6XW6LET6.mjs.map} +0 -0
  278. /package/dist/lib/node-esm/{chunk-EL3R25OQ.mjs.map → chunk-OZ7DZA5Z.mjs.map} +0 -0
@@ -0,0 +1,524 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { init as initCjsLexer, parse as parseCjs } from 'cjs-module-lexer';
6
+ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
7
+ import { createRequire as nodeCreateRequire } from 'node:module';
8
+ import path from 'node:path';
9
+ import { type Plugin } from 'vite';
10
+
11
+ import { DEFAULT_PACKAGES } from '../packages';
12
+
13
+ const require = nodeCreateRequire(import.meta.url);
14
+
15
+ //
16
+ // Import map helpers.
17
+ // These utilities support `importMapPlugin` by resolving package entrypoints,
18
+ // detecting module formats (ESM vs CJS), and extracting named exports from CJS bundles.
19
+ //
20
+
21
+ /** Strips vite query strings (e.g. `?v=123`) from resolved module IDs. */
22
+ const trimQueryString = (id: string) => id.replace(/\?.*$/, '');
23
+
24
+ /**
25
+ * Determines whether a resolved file is an ESM module by checking file extension
26
+ * first (.mjs/.cjs), then walking up to the nearest package.json for `"type": "module"`.
27
+ */
28
+ const resolvedIdIsEsmModule = (resolvedId: string): boolean => {
29
+ const file = trimQueryString(resolvedId);
30
+ if (file.endsWith('.mjs') || file.endsWith('.mts')) {
31
+ return true;
32
+ }
33
+ if (file.endsWith('.cjs') || file.endsWith('.cts')) {
34
+ return false;
35
+ }
36
+
37
+ let directory = path.dirname(file);
38
+ while (directory !== path.dirname(directory)) {
39
+ const packageJsonPath = path.join(directory, 'package.json');
40
+ if (existsSync(packageJsonPath)) {
41
+ try {
42
+ const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as { type?: string };
43
+ return pkg.type === 'module';
44
+ } catch {
45
+ return false;
46
+ }
47
+ }
48
+ directory = path.dirname(directory);
49
+ }
50
+
51
+ return false;
52
+ };
53
+
54
+ /**
55
+ * Walks up from a resolved file path to find the package.json whose `"name"` matches
56
+ * the given package name. Returns the path to that package.json, or undefined.
57
+ */
58
+ const findPackageJsonPath = (resolvedId: string, packageName: string): string | undefined => {
59
+ let directory = path.dirname(trimQueryString(resolvedId));
60
+
61
+ while (directory !== path.dirname(directory)) {
62
+ const packageJsonPath = path.join(directory, 'package.json');
63
+ if (existsSync(packageJsonPath)) {
64
+ try {
65
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as { name?: string };
66
+ if (packageJson.name === packageName) {
67
+ return packageJsonPath;
68
+ }
69
+ } catch {}
70
+ }
71
+
72
+ directory = path.dirname(directory);
73
+ }
74
+
75
+ return undefined;
76
+ };
77
+
78
+ /** Resolves a package name to its package.json path using Node's require.resolve. */
79
+ const resolvePackageJsonPath = (packageName: string): string | undefined => {
80
+ try {
81
+ return findPackageJsonPath(require.resolve(packageName), packageName);
82
+ } catch {
83
+ return undefined;
84
+ }
85
+ };
86
+
87
+ /**
88
+ * Resolves a package name to its package.json via vite's own resolver, which handles
89
+ * workspace-linked packages whose `exports` field omits `./package.json` (so
90
+ * `require.resolve` can't find it). Falls back to `require.resolve`.
91
+ */
92
+ const resolvePackageJsonPathViaContext = async (
93
+ ctx: { resolve: (id: string) => Promise<{ id: string } | null> },
94
+ packageName: string,
95
+ ): Promise<string | undefined> => {
96
+ try {
97
+ const resolved = await ctx.resolve(packageName);
98
+ if (resolved) {
99
+ const viaResolved = findPackageJsonPath(resolved.id, packageName);
100
+ if (viaResolved) {
101
+ return viaResolved;
102
+ }
103
+ }
104
+ } catch {
105
+ // fall through
106
+ }
107
+ return resolvePackageJsonPath(packageName);
108
+ };
109
+
110
+ /**
111
+ * Subpath exports that should be excluded from the import map.
112
+ * These are node-only entrypoints (vite plugins, native addons) that shouldn't be pre-bundled for the browser.
113
+ *
114
+ * TODO(wittjosiah): Replace these hand-maintained lists (plus {@link GLOBALLY_EXCLUDED_SUBPATHS}
115
+ * and {@link BUILD_TOOL_SUBPATH}) with a structural check — e.g. honour an `agent`/`node`-only
116
+ * export condition in package.json, or probe each subpath's resolved file for `node:*` imports
117
+ * at build time — so every new server-only entrypoint doesn't require a code change here.
118
+ */
119
+ const importMapExcludedSubpaths: Readonly<Record<string, ReadonlySet<string>>> = {
120
+ '@dxos/app-framework': new Set(['vite-plugin']),
121
+ // `@dxos/lit-grid/testing` re-exports a playwright page-object manager and pulls
122
+ // `@playwright/test` (and transitive playwright-core) into the browser bundle.
123
+ '@dxos/lit-grid': new Set(['testing']),
124
+ '@dxos/react-ui-mosaic': new Set(['playwright']),
125
+ '@dxos/react-ui-stack': new Set(['playwright']),
126
+ '@dxos/react-ui-table': new Set(['playwright']),
127
+ '@dxos/ui-theme': new Set(['plugin']),
128
+ // `solid-js/web/storage` is a server-only helper that pulls
129
+ // `node:async_hooks` (AsyncLocalStorage) and has no browser shim. Client
130
+ // code imports `solid-js`, `solid-js/store`, and `solid-js/web` only.
131
+ 'solid-js': new Set(['web/storage']),
132
+ };
133
+
134
+ /**
135
+ * Subpaths common to many packages that should always be excluded.
136
+ * `./playwright` subdirectories house e2e test harnesses that pull in
137
+ * `@playwright/test` (a node-only package that breaks the browser bundle).
138
+ * `./testing` subpaths similarly expose node-only test helpers (e.g.
139
+ * `@dxos/edge-client/testing` pulls `@dxos/node-std/http` which has no
140
+ * browser analogue) and aren't part of the plugin-facing surface.
141
+ */
142
+ const GLOBALLY_EXCLUDED_SUBPATHS = new Set(['playwright', 'testing']);
143
+
144
+ /**
145
+ * Regex for subpaths that are always node-only build tooling — vite, esbuild, and
146
+ * rollup plugins. These use `node:*` imports and will blow up the browser bundle if
147
+ * they end up in the import map.
148
+ */
149
+ const BUILD_TOOL_SUBPATH = /^(vite-plugin|esbuild-plugin|rollup-plugin|plugin)$/;
150
+
151
+ /**
152
+ * Asset subpaths (CSS, PCSS, images, etc.) that the host already loads via its own
153
+ * bundle — for example `@dxos/react-ui-form` pulls in `@dxos/lit-ui/dx-tag-picker.pcss`
154
+ * on startup, so the stylesheet is already attached to the DOM by the time a remote
155
+ * plugin runs. Community plugins should still be able to _import_ those specifiers
156
+ * (rolldown leaves the `import "…"` statements in the output when the package is
157
+ * externalized), so the import map maps them to a no-op empty ES module. The
158
+ * resulting browser fetch is a cheap no-op; the styles are never re-downloaded
159
+ * and never re-injected.
160
+ */
161
+ const ASSET_SUBPATH = /\.(css|pcss|scss|sass|less|json|node|wasm|html|svg|png|jpe?g|gif|webp|ico)$/;
162
+ const isAssetSubpath = (specifier: string) => ASSET_SUBPATH.test(specifier);
163
+
164
+ /**
165
+ * Walks `baseDir` and returns every file that matches the pattern `${baseDir}/<rest>${extension}`.
166
+ * Used to expand wildcard exports like `./proto/*` → `./dist/src/proto/gen/*.js` into their
167
+ * full set of bare specifiers.
168
+ */
169
+ const walkDirectoryForExtension = (baseDir: string, extension: string): string[] => {
170
+ if (!existsSync(baseDir)) {
171
+ return [];
172
+ }
173
+ const results: string[] = [];
174
+ const walk = (dir: string) => {
175
+ let entries: string[];
176
+ try {
177
+ entries = readdirSync(dir);
178
+ } catch {
179
+ return;
180
+ }
181
+ for (const entry of entries) {
182
+ const full = path.join(dir, entry);
183
+ let stat;
184
+ try {
185
+ stat = statSync(full);
186
+ } catch {
187
+ continue;
188
+ }
189
+ if (stat.isDirectory()) {
190
+ walk(full);
191
+ } else if (entry.endsWith(extension)) {
192
+ results.push(path.relative(baseDir, full).slice(0, -extension.length));
193
+ }
194
+ }
195
+ };
196
+ walk(baseDir);
197
+ return results;
198
+ };
199
+
200
+ /** Resolves an `exports` value to a single target path for pattern expansion. */
201
+ const pickPatternTarget = (value: unknown): string | undefined => {
202
+ if (typeof value === 'string') {
203
+ return value;
204
+ }
205
+ if (value && typeof value === 'object') {
206
+ const obj = value as Record<string, unknown>;
207
+ const preferred = obj.browser ?? obj.import ?? obj.default ?? obj.module;
208
+ return typeof preferred === 'string' ? preferred : undefined;
209
+ }
210
+ return undefined;
211
+ };
212
+
213
+ /**
214
+ * Expands a wildcard export like `./proto/*` into concrete subpath specifiers by walking
215
+ * the target directory. Returns `<packageName>/<subpath>` strings.
216
+ */
217
+ const expandWildcardExport = (
218
+ packageName: string,
219
+ packageJsonDir: string,
220
+ exportKey: string,
221
+ exportValue: unknown,
222
+ ): string[] => {
223
+ const target = pickPatternTarget(exportValue);
224
+ if (!target || !target.includes('*')) {
225
+ return [];
226
+ }
227
+ // Split on `*` — at most one supported (node's exports pattern semantics).
228
+ const keyStarIndex = exportKey.indexOf('*');
229
+ const targetStarIndex = target.indexOf('*');
230
+ if (keyStarIndex === -1 || targetStarIndex === -1) {
231
+ return [];
232
+ }
233
+ const keyPrefix = exportKey.slice(2, keyStarIndex); // drop leading './'
234
+ const targetPrefix = target.slice(2, targetStarIndex); // drop leading './'
235
+ const targetSuffix = target.slice(targetStarIndex + 1);
236
+ const baseDir = path.resolve(packageJsonDir, targetPrefix);
237
+ const files = walkDirectoryForExtension(baseDir, targetSuffix || '.js');
238
+ return files.map((relativeNoExt) => `${packageName}/${keyPrefix}${relativeNoExt}`);
239
+ };
240
+
241
+ /**
242
+ * Reads a package's `exports` field and returns all subpath entrypoints as bare specifiers
243
+ * (e.g. `@dxos/client`, `@dxos/client/echo`, `@dxos/lit-ui/dx-tag-picker.pcss`,
244
+ * `@dxos/protocols/proto/dxos/echo/metadata`). Wildcard patterns are expanded by walking
245
+ * the corresponding output directory. Falls back to just the package name if exports is
246
+ * absent or simple.
247
+ */
248
+ const getPackageEntrypoints = (packageName: string, packageJsonPath: string): string[] => {
249
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as {
250
+ exports?: Record<string, unknown> | string | string[];
251
+ };
252
+ const exportsField = packageJson.exports;
253
+
254
+ if (!exportsField || typeof exportsField === 'string' || Array.isArray(exportsField)) {
255
+ return [packageName];
256
+ }
257
+
258
+ const exportKeys = Object.keys(exportsField);
259
+ if (!exportKeys.some((key) => key.startsWith('.'))) {
260
+ return [packageName];
261
+ }
262
+
263
+ const excluded = importMapExcludedSubpaths[packageName];
264
+ const packageJsonDir = path.dirname(packageJsonPath);
265
+ const modules = exportKeys.flatMap((key) => {
266
+ if (key === '.') {
267
+ return [packageName];
268
+ }
269
+
270
+ if (!key.startsWith('./') || key === './package.json') {
271
+ return [];
272
+ }
273
+
274
+ // Skip `.d.ts` subpath exports — these are meant to be imported as raw text
275
+ // (e.g. `@dxos/echo-query/api.d.ts?raw` for in-editor type hints), not as
276
+ // ES modules. Enumerating them here would have vite try to bundle the
277
+ // declaration file's imports (protobufjs, effect, etc.), which breaks.
278
+ if (key.endsWith('.d.ts')) {
279
+ return [];
280
+ }
281
+
282
+ if (key.includes('*')) {
283
+ // Expand wildcard patterns like `./proto/*` into concrete specifiers.
284
+ return expandWildcardExport(packageName, packageJsonDir, key, (exportsField as Record<string, unknown>)[key]);
285
+ }
286
+
287
+ const subpath = key.slice(2);
288
+ if (excluded?.has(subpath) || GLOBALLY_EXCLUDED_SUBPATHS.has(subpath) || BUILD_TOOL_SUBPATH.test(subpath)) {
289
+ return [];
290
+ }
291
+
292
+ return [`${packageName}/${subpath}`];
293
+ });
294
+
295
+ return modules.length > 0 ? modules : [packageName];
296
+ };
297
+
298
+ /**
299
+ * Statically analyzes a CJS file to extract named exports using cjs-module-lexer.
300
+ * Follows re-exports (e.g. react's index.js re-exports from cjs/react.development.js)
301
+ * and deduplicates. Filters out `default` and `__esModule` pseudo-exports.
302
+ */
303
+ const getCjsNamedExports = (filePath: string, seen = new Set<string>()): string[] => {
304
+ const normalized = trimQueryString(filePath);
305
+ if (seen.has(normalized)) {
306
+ return [];
307
+ }
308
+ seen.add(normalized);
309
+
310
+ try {
311
+ const source = readFileSync(normalized, 'utf8');
312
+ const { exports: directExports, reexports } = parseCjs(source);
313
+
314
+ const reexportedNames = reexports.flatMap((reexport) => {
315
+ const resolved = path.resolve(path.dirname(normalized), reexport);
316
+ const candidates = [resolved, `${resolved}.js`, `${resolved}.cjs`];
317
+ const found = candidates.find((candidate) => existsSync(candidate));
318
+ return found ? getCjsNamedExports(found, seen) : [];
319
+ });
320
+
321
+ return [...new Set([...directExports, ...reexportedNames])].filter(
322
+ (name) => name !== 'default' && name !== '__esModule',
323
+ );
324
+ } catch {
325
+ return [];
326
+ }
327
+ };
328
+
329
+ /**
330
+ * Vite plugin for the **host app** (Composer). Generates a browser import map so that
331
+ * shared packages are loaded once and reused by both the host and any remote plugins.
332
+ *
333
+ * In production, emits a dedicated chunk per specifier via virtual wrapper modules,
334
+ * then injects a `<script type="importmap">` tag into the HTML with the chunk URLs.
335
+ *
336
+ * Virtual wrappers are needed because directly emitting chunks for CJS packages loses
337
+ * their named exports due to Rollup's interop. The wrapper re-exports them explicitly.
338
+ *
339
+ * TODO: Restore dev-mode support. The previous dev path mapped each bare specifier to
340
+ * its absolute `/@fs/` file path during `buildStart`, which forced `this.resolve()` for
341
+ * hundreds of subpath entrypoints and raced with Vite's optimize-deps static scan —
342
+ * producing non-deterministic chunk content (~34 chunks drifting per warm-start) and
343
+ * triggering Vite 8.0.10's `runOptimizer` / `commitProcessing` crashes (`fileHash` /
344
+ * `browserHash` undefined on partial-discovery batches). Plugin is currently gated to
345
+ * `apply: 'build'` to avoid both. A working dev-mode implementation would need to
346
+ * resolve specifiers without participating in the optimize-deps phase — candidates:
347
+ * - defer resolution to `transformIndexHtml` (after the optimizer settles)
348
+ * - read resolved paths from `environment.depsOptimizer.metadata` rather than calling
349
+ * `this.resolve()` ourselves
350
+ * - resolve via `require.resolve` directly, bypassing the Vite plugin pipeline entirely
351
+ */
352
+ export const importMapPlugin = (options?: { packages?: string[] }): Plugin[] => {
353
+ const packages = options?.packages ?? DEFAULT_PACKAGES;
354
+ const WRAPPER_PREFIX = '\0import-map:';
355
+ const chunkRefIds: Record<string, string> = {};
356
+ const resolvedIds: Record<string, string> = {};
357
+ let imports: Record<string, string> = {};
358
+ let modules: string[] = [];
359
+ let importMapIsDev = false;
360
+ let base = '/';
361
+
362
+ return [
363
+ // Phase 1: Resolve all package entrypoints and emit chunks.
364
+ //
365
+ // Build-only. The dev-mode path (recording absolute `/@fs/` paths in
366
+ // `imports[]`) is non-functional today and was actively harmful: calling
367
+ // `this.resolve()` for every shared package's subpath entrypoints during
368
+ // `buildStart` races with Vite's optimize-deps static scan, producing
369
+ // non-deterministic chunk content (~34 drifting chunks per warm-start) and
370
+ // triggering Vite 8.0.10's `runOptimizer` / `commitProcessing` crashes
371
+ // (`fileHash` / `browserHash` undefined on partial-discovery batches) when
372
+ // the resolved set doesn't match the optimizer's metadata. The importmap
373
+ // is only meaningful for production builds where remote plugins consume
374
+ // the emitted wrapper chunks.
375
+ {
376
+ name: 'import-map:get-chunk-ref-ids',
377
+ apply: 'build',
378
+ configResolved(config) {
379
+ base = config.base ?? '/';
380
+ },
381
+ async buildStart() {
382
+ importMapIsDev = this.environment.mode === 'dev';
383
+ await initCjsLexer();
384
+
385
+ // Expand each package name into its full set of subpath entrypoints
386
+ // (e.g. '@dxos/client' -> ['@dxos/client', '@dxos/client/echo', ...]).
387
+ modules = [
388
+ ...new Set(
389
+ (
390
+ await Promise.all(
391
+ packages.map(async (packageName) => {
392
+ const packageJsonPath = await resolvePackageJsonPathViaContext(this, packageName);
393
+ if (!packageJsonPath) {
394
+ this.warn(`Unable to locate package.json for import map package: ${packageName}`);
395
+ return [packageName];
396
+ }
397
+
398
+ return getPackageEntrypoints(packageName, packageJsonPath);
399
+ }),
400
+ )
401
+ ).flat(),
402
+ ),
403
+ ];
404
+
405
+ for (const specifier of modules) {
406
+ if (isAssetSubpath(specifier)) {
407
+ // Host already loaded the real asset; remote plugins need the specifier to
408
+ // resolve to *something* that behaves as an ES module. A data URL is the
409
+ // cheapest option — no chunk to emit (rolldown tree-shakes an empty wrapper
410
+ // even with `preserveSignature: 'strict'`, so the referenced file wouldn't
411
+ // exist at runtime), no extra network request, and the same path in dev and prod.
412
+ imports[specifier] = 'data:text/javascript;charset=utf-8,export%20%7B%7D%3B';
413
+ continue;
414
+ }
415
+
416
+ const resolved = await this.resolve(specifier);
417
+ if (!resolved) {
418
+ this.warn(`Import map: unable to resolve "${specifier}"; omitting from import map.`);
419
+ continue;
420
+ }
421
+
422
+ if (importMapIsDev) {
423
+ // Dev: map bare specifier to the resolved file path, prefixed with vite's
424
+ // `/@fs/` escape hatch so absolute node_modules paths (including
425
+ // `.vite/deps/*` pre-bundled chunks) round-trip through the dev server
426
+ // instead of being resolved against the document origin as a 404.
427
+ const filePath = trimQueryString(resolved.id);
428
+ imports[specifier] = filePath.startsWith('/') ? `/@fs${filePath}` : filePath;
429
+ } else {
430
+ // Prod: emit a virtual wrapper chunk that re-exports from the real module.
431
+ resolvedIds[specifier] = resolved.id;
432
+ chunkRefIds[specifier] = this.emitFile({
433
+ type: 'chunk',
434
+ id: `${WRAPPER_PREFIX}${specifier}`,
435
+ preserveSignature: 'strict',
436
+ });
437
+ }
438
+ }
439
+ },
440
+
441
+ // Intercept virtual wrapper IDs so Rollup doesn't try to resolve them as files.
442
+ resolveId(id) {
443
+ if (id.startsWith(WRAPPER_PREFIX)) {
444
+ return id;
445
+ }
446
+ },
447
+
448
+ // Generate the source for each virtual wrapper module.
449
+ // For CJS packages, explicitly re-export named exports (discovered via static analysis)
450
+ // because Rollup's `export *` interop doesn't preserve them for CJS.
451
+ //
452
+ // Each wrapper also emits a local `__keepalive` export. Without this, Rolldown
453
+ // (Vite 8) tree-shakes wrappers whose `export *` re-exports happen to coincide
454
+ // with another already-emitted chunk's exports — typical for umbrella packages
455
+ // (e.g. `@dxos/compute` re-exports from `@dxos/operation`, so the operation
456
+ // wrapper looks "redundant" to Rolldown and gets merged away). A local const
457
+ // export has no upstream source for Rolldown to dedupe against, so the chunk
458
+ // is always preserved and the import-map entry resolves at runtime.
459
+ load(id) {
460
+ if (!id.startsWith(WRAPPER_PREFIX)) {
461
+ return;
462
+ }
463
+ const specifier = id.slice(WRAPPER_PREFIX.length);
464
+ const keepalive = `export const __dxosImportMapKeepalive = ${JSON.stringify(specifier)};`;
465
+ const resolvedId = resolvedIds[specifier];
466
+ if (!resolvedId) {
467
+ return `export * from ${JSON.stringify(specifier)};\n${keepalive}\n`;
468
+ }
469
+
470
+ const filePath = trimQueryString(resolvedId);
471
+ const isCjs = filePath.endsWith('.cjs') || (!filePath.endsWith('.mjs') && !resolvedIdIsEsmModule(filePath));
472
+
473
+ if (isCjs) {
474
+ const named = getCjsNamedExports(filePath);
475
+ if (named.length > 0) {
476
+ return `export { default, ${named.join(', ')} } from ${JSON.stringify(specifier)};\n${keepalive}\n`;
477
+ }
478
+ }
479
+ return `export * from ${JSON.stringify(specifier)};\n${keepalive}\n`;
480
+ },
481
+
482
+ // After bundling, map each specifier to the URL of its emitted chunk. Preserves
483
+ // asset-subpath entries already written to `imports` during `buildStart` (those
484
+ // use an inline data URL rather than an emitted chunk).
485
+ generateBundle() {
486
+ if (importMapIsDev) {
487
+ return;
488
+ }
489
+
490
+ imports = {
491
+ ...imports,
492
+ ...Object.fromEntries(
493
+ modules
494
+ .filter((specifier) => chunkRefIds[specifier] !== undefined)
495
+ .map((specifier) => [specifier, `${base}${this.getFileName(chunkRefIds[specifier])}`]),
496
+ ),
497
+ };
498
+ },
499
+ },
500
+
501
+ // Phase 2: Inject the import map into the HTML as a <script type="importmap"> tag.
502
+ {
503
+ name: 'import-map:transform-index-html',
504
+ apply: 'build',
505
+ enforce: 'post',
506
+ transformIndexHtml(html: string) {
507
+ const tags = [
508
+ {
509
+ tag: 'script',
510
+ attrs: {
511
+ type: 'importmap',
512
+ },
513
+ children: JSON.stringify({ imports }, null, 2),
514
+ },
515
+ ];
516
+
517
+ return {
518
+ html,
519
+ tags,
520
+ };
521
+ },
522
+ },
523
+ ];
524
+ };
@@ -2,5 +2,9 @@
2
2
  // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
- export * from './composer-plugin';
6
- export * from './import-map-plugin';
5
+ export * from './boot-loader';
6
+ export * from './composer';
7
+ export * from './import-map';
8
+ // `manifest` and `packages` are shared data used by composer / import-map; they
9
+ // stay at the top level because they're not vite plugins themselves.
10
+ export * from './packages';
@@ -0,0 +1,24 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { describe, test } from 'vitest';
6
+
7
+ import { serializeManifest } from './manifest';
8
+
9
+ describe('serializeManifest', () => {
10
+ test('serializes plugin meta plus assets', ({ expect }) => {
11
+ const json = serializeManifest(
12
+ { id: 'org.example.plugin', name: 'Example', description: 'Demo', tags: ['new'], version: '1.2.3' },
13
+ { assets: ['index.mjs', 'style.css', 'chunks/lib-abc.js'] },
14
+ );
15
+ expect(JSON.parse(json)).toEqual({
16
+ id: 'org.example.plugin',
17
+ name: 'Example',
18
+ description: 'Demo',
19
+ tags: ['new'],
20
+ version: '1.2.3',
21
+ assets: ['index.mjs', 'style.css', 'chunks/lib-abc.js'],
22
+ });
23
+ });
24
+ });
@@ -0,0 +1,50 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { PLUGIN_ENTRY_FILENAME } from '@dxos/protocols';
6
+
7
+ import { type Plugin } from '../core';
8
+
9
+ /**
10
+ * Name of the asset written alongside the built module bundle.
11
+ * The DXOS community registry resolves each published plugin by fetching this file
12
+ * from the repo's latest GitHub Release, so authors should not rename it.
13
+ */
14
+ export const MANIFEST_ASSET_NAME = 'manifest.json';
15
+
16
+ /**
17
+ * Canonical entry filename (re-exported from `@dxos/protocols`) so vite-plugin
18
+ * consumers don't have to reach into the protocols package directly.
19
+ */
20
+ export const ENTRY_FILENAME = PLUGIN_ENTRY_FILENAME;
21
+
22
+ /**
23
+ * Plugin metadata required to emit a manifest at build time.
24
+ *
25
+ * Extends `Plugin.Meta` with a build-time `version` field that is not
26
+ * relevant to the runtime plugin definition itself.
27
+ */
28
+ export type BuildMeta = Plugin.Meta & { version: string };
29
+
30
+ /**
31
+ * Serializes a plugin's public metadata + bundle layout into the format consumed
32
+ * by the host loader (see `UrlLoader.make` and `PluginManifest.parse`).
33
+ *
34
+ * The host fetches this manifest, resolves every entry in `assets` against the
35
+ * manifest URL, and persists them via the platform `PluginAssetCache` so the
36
+ * plugin works offline. The entry module is always {@link ENTRY_FILENAME}; it
37
+ * must appear in `assets`.
38
+ *
39
+ * Exported from a vite-free module so tests and tooling can validate manifests
40
+ * without paying the cost of loading vite + esbuild.
41
+ */
42
+ export const serializeManifest = (meta: BuildMeta, { assets }: { assets: readonly string[] }): string =>
43
+ JSON.stringify(
44
+ {
45
+ ...meta,
46
+ assets,
47
+ },
48
+ null,
49
+ 2,
50
+ );