@dxos/app-framework 0.8.4-main.40e3dcdf1b → 0.8.4-main.43cb759274
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.
- package/LICENSE +102 -5
- package/README.md +1 -1
- package/dist/lib/browser/{capability-LF3OXSCV.mjs → capability-JCUNWZ66.mjs} +12 -11
- package/dist/lib/browser/capability-JCUNWZ66.mjs.map +7 -0
- package/dist/lib/browser/{chunk-WEBSGU5L.mjs → chunk-2ZIYRDAF.mjs} +20 -5
- package/dist/lib/browser/chunk-2ZIYRDAF.mjs.map +7 -0
- package/dist/lib/browser/chunk-5GY3YOEL.mjs +28 -0
- package/dist/lib/browser/chunk-5GY3YOEL.mjs.map +7 -0
- package/dist/lib/browser/{chunk-272IPLHQ.mjs → chunk-66IXTIVK.mjs} +3 -2
- package/dist/lib/browser/chunk-66IXTIVK.mjs.map +7 -0
- package/dist/lib/browser/chunk-7MBI66M7.mjs +587 -0
- package/dist/lib/browser/chunk-7MBI66M7.mjs.map +7 -0
- package/dist/lib/browser/chunk-BMLCIFZW.mjs +1467 -0
- package/dist/lib/browser/chunk-BMLCIFZW.mjs.map +7 -0
- package/dist/lib/browser/{chunk-7M4467QR.mjs → chunk-DC3WRPBV.mjs} +100 -18
- package/dist/lib/browser/chunk-DC3WRPBV.mjs.map +7 -0
- package/dist/lib/browser/{chunk-JGWCBVKJ.mjs → chunk-E5DTDNDY.mjs} +28 -55
- package/dist/lib/browser/chunk-E5DTDNDY.mjs.map +7 -0
- package/dist/lib/browser/{chunk-TGX63LTL.mjs → chunk-FJ4765WW.mjs} +1 -1
- package/dist/lib/browser/{chunk-TGX63LTL.mjs.map → chunk-FJ4765WW.mjs.map} +2 -2
- package/dist/lib/browser/chunk-FO3IYSLV.mjs +68 -0
- package/dist/lib/browser/chunk-FO3IYSLV.mjs.map +7 -0
- package/dist/lib/browser/{chunk-YNFPIQGB.mjs → chunk-IW44C7UL.mjs} +9 -2
- package/dist/lib/browser/chunk-IW44C7UL.mjs.map +7 -0
- package/dist/lib/browser/chunk-KZUDO43J.mjs +422 -0
- package/dist/lib/browser/chunk-KZUDO43J.mjs.map +7 -0
- package/dist/lib/browser/chunk-Q2GLJTVV.mjs +12 -0
- package/dist/lib/browser/chunk-Q2GLJTVV.mjs.map +7 -0
- package/dist/lib/browser/chunk-VBG5F5UF.mjs +476 -0
- package/dist/lib/browser/chunk-VBG5F5UF.mjs.map +7 -0
- package/dist/lib/browser/cli/index.mjs +15 -30
- package/dist/lib/browser/cli/index.mjs.map +3 -3
- package/dist/lib/browser/common/activation-events.mjs +11 -14
- package/dist/lib/browser/common/capabilities.mjs +19 -8
- package/dist/lib/browser/core/activation-event.mjs +1 -1
- package/dist/lib/browser/core/capability.mjs +5 -1
- package/dist/lib/browser/core/plugin-manager.mjs +8 -4
- package/dist/lib/browser/core/plugin.mjs +18 -4
- package/dist/lib/browser/core/url-loader.mjs +24 -0
- package/dist/lib/browser/index.mjs +51 -54
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/process-manager-capability-4FUA4TRP.mjs +89 -0
- package/dist/lib/browser/process-manager-capability-4FUA4TRP.mjs.map +7 -0
- package/dist/lib/browser/testing/index.mjs +198 -54
- package/dist/lib/browser/testing/index.mjs.map +4 -4
- package/dist/lib/browser/testing/react.mjs +78 -0
- package/dist/lib/browser/testing/react.mjs.map +7 -0
- package/dist/lib/browser/ui/index.mjs +22 -15
- package/dist/lib/node-esm/{capability-SA6LPYGL.mjs → capability-7TCQK7ZP.mjs} +12 -11
- package/dist/lib/node-esm/capability-7TCQK7ZP.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-FKE4Z3D6.mjs → chunk-37Z53PXZ.mjs} +1 -1
- package/dist/lib/node-esm/{chunk-FKE4Z3D6.mjs.map → chunk-37Z53PXZ.mjs.map} +2 -2
- package/dist/lib/node-esm/chunk-4BX2AE47.mjs +1468 -0
- package/dist/lib/node-esm/chunk-4BX2AE47.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-SB5ODNPX.mjs → chunk-CTKEZHKF.mjs} +9 -2
- package/dist/lib/node-esm/chunk-CTKEZHKF.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-FPW45EZH.mjs +14 -0
- package/dist/lib/node-esm/chunk-FPW45EZH.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-GEWHMHM7.mjs +588 -0
- package/dist/lib/node-esm/chunk-GEWHMHM7.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-KFZEB6BV.mjs +29 -0
- package/dist/lib/node-esm/chunk-KFZEB6BV.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-3D66SZHP.mjs → chunk-KSH4OVMY.mjs} +28 -55
- package/dist/lib/node-esm/chunk-KSH4OVMY.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-SQICGJBW.mjs → chunk-QBUGLR4H.mjs} +20 -5
- package/dist/lib/node-esm/chunk-QBUGLR4H.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-WK7OIQKI.mjs +70 -0
- package/dist/lib/node-esm/chunk-WK7OIQKI.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-XGBUKRCK.mjs +423 -0
- package/dist/lib/node-esm/chunk-XGBUKRCK.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-JHFY3HTS.mjs → chunk-XIGPWXCQ.mjs} +100 -18
- package/dist/lib/node-esm/chunk-XIGPWXCQ.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-6REV5DE7.mjs → chunk-XOCUANHO.mjs} +3 -2
- package/dist/lib/node-esm/chunk-XOCUANHO.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-Y7JKXNDU.mjs +477 -0
- package/dist/lib/node-esm/chunk-Y7JKXNDU.mjs.map +7 -0
- package/dist/lib/node-esm/cli/index.mjs +15 -30
- package/dist/lib/node-esm/cli/index.mjs.map +3 -3
- package/dist/lib/node-esm/common/activation-events.mjs +11 -14
- package/dist/lib/node-esm/common/capabilities.mjs +19 -8
- package/dist/lib/node-esm/core/activation-event.mjs +1 -1
- package/dist/lib/node-esm/core/capability.mjs +5 -1
- package/dist/lib/node-esm/core/plugin-manager.mjs +8 -4
- package/dist/lib/node-esm/core/plugin.mjs +18 -4
- package/dist/lib/node-esm/core/url-loader.mjs +25 -0
- package/dist/lib/node-esm/index.mjs +51 -54
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/process-manager-capability-F23D62RN.mjs +90 -0
- package/dist/lib/node-esm/process-manager-capability-F23D62RN.mjs.map +7 -0
- package/dist/lib/node-esm/testing/index.mjs +198 -54
- package/dist/lib/node-esm/testing/index.mjs.map +4 -4
- package/dist/lib/node-esm/testing/react.mjs +79 -0
- package/dist/lib/node-esm/testing/react.mjs.map +7 -0
- package/dist/lib/node-esm/ui/index.mjs +22 -15
- package/dist/plugin/node-esm/index.mjs +971 -0
- package/dist/plugin/node-esm/index.mjs.map +7 -0
- package/dist/plugin/node-esm/meta.json +1 -0
- package/dist/types/src/cli/cli.d.ts +1 -3
- package/dist/types/src/cli/cli.d.ts.map +1 -1
- package/dist/types/src/common/activation-events.d.ts +10 -13
- package/dist/types/src/common/activation-events.d.ts.map +1 -1
- package/dist/types/src/common/annotations.d.ts +1 -0
- package/dist/types/src/common/annotations.d.ts.map +1 -0
- package/dist/types/src/common/capabilities.d.ts +113 -12
- package/dist/types/src/common/capabilities.d.ts.map +1 -1
- package/dist/types/src/common/index.d.ts +0 -1
- package/dist/types/src/common/index.d.ts.map +1 -1
- package/dist/types/src/common/translations.d.ts +1 -1
- package/dist/types/src/core/activation-event.d.ts +4 -4
- package/dist/types/src/core/activation-event.d.ts.map +1 -1
- package/dist/types/src/core/capability-manager.d.ts +9 -0
- package/dist/types/src/core/capability-manager.d.ts.map +1 -1
- package/dist/types/src/core/capability.d.ts +13 -2
- package/dist/types/src/core/capability.d.ts.map +1 -1
- package/dist/types/src/core/edge-registry-plugin-provider.d.ts +30 -0
- package/dist/types/src/core/edge-registry-plugin-provider.d.ts.map +1 -0
- package/dist/types/src/core/index.d.ts +6 -0
- package/dist/types/src/core/index.d.ts.map +1 -1
- package/dist/types/src/core/plugin-asset-cache.d.ts +71 -0
- package/dist/types/src/core/plugin-asset-cache.d.ts.map +1 -0
- package/dist/types/src/core/plugin-manager.d.ts +238 -7
- package/dist/types/src/core/plugin-manager.d.ts.map +1 -1
- package/dist/types/src/core/plugin-manifest.d.ts +101 -0
- package/dist/types/src/core/plugin-manifest.d.ts.map +1 -0
- package/dist/types/src/core/plugin-manifest.test.d.ts +2 -0
- package/dist/types/src/core/plugin-manifest.test.d.ts.map +1 -0
- package/dist/types/src/core/plugin.d.ts +213 -11
- package/dist/types/src/core/plugin.d.ts.map +1 -1
- package/dist/types/src/core/registry.d.ts +107 -0
- package/dist/types/src/core/registry.d.ts.map +1 -0
- package/dist/types/src/core/url-loader.d.ts +127 -0
- package/dist/types/src/core/url-loader.d.ts.map +1 -0
- package/dist/types/src/core/url-loader.test.d.ts +2 -0
- package/dist/types/src/core/url-loader.test.d.ts.map +1 -0
- package/dist/types/src/helpers.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/plugin-process-manager/ProcessManagerPlugin.d.ts +3 -0
- package/dist/types/src/plugin-process-manager/ProcessManagerPlugin.d.ts.map +1 -0
- package/dist/types/src/plugin-process-manager/history/capability.d.ts.map +1 -0
- package/dist/types/src/plugin-process-manager/history/errors.d.ts +32 -0
- package/dist/types/src/plugin-process-manager/history/errors.d.ts.map +1 -0
- package/dist/types/src/plugin-process-manager/history/history-tracker.d.ts +32 -0
- package/dist/types/src/plugin-process-manager/history/history-tracker.d.ts.map +1 -0
- package/dist/types/src/plugin-process-manager/history/history-tracker.test.d.ts.map +1 -0
- package/dist/types/src/plugin-process-manager/history/index.d.ts.map +1 -0
- package/dist/types/src/{plugin-operation → plugin-process-manager}/history/types.d.ts +1 -1
- package/dist/types/src/plugin-process-manager/history/types.d.ts.map +1 -0
- package/dist/types/src/{plugin-operation → plugin-process-manager}/history/undo-mapping.d.ts +2 -2
- package/dist/types/src/plugin-process-manager/history/undo-mapping.d.ts.map +1 -0
- package/dist/types/src/{plugin-operation → plugin-process-manager}/history/undo-registry.d.ts +1 -1
- package/dist/types/src/plugin-process-manager/history/undo-registry.d.ts.map +1 -0
- package/dist/types/src/plugin-process-manager/history/undo-registry.test.d.ts.map +1 -0
- package/dist/types/src/plugin-process-manager/index.d.ts +3 -0
- package/dist/types/src/plugin-process-manager/index.d.ts.map +1 -0
- package/dist/types/src/plugin-process-manager/meta.d.ts +3 -0
- package/dist/types/src/plugin-process-manager/meta.d.ts.map +1 -0
- package/dist/types/src/plugin-process-manager/process-manager-capability.d.ts +8 -0
- package/dist/types/src/plugin-process-manager/process-manager-capability.d.ts.map +1 -0
- package/dist/types/src/plugin-process-manager/testing.d.ts +59 -0
- package/dist/types/src/plugin-process-manager/testing.d.ts.map +1 -0
- package/dist/types/src/testing/harness.d.ts +79 -0
- package/dist/types/src/testing/harness.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +1 -0
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/react.d.ts +27 -0
- package/dist/types/src/testing/react.d.ts.map +1 -0
- package/dist/types/src/testing/react.test.d.ts +2 -0
- package/dist/types/src/testing/react.test.d.ts.map +1 -0
- package/dist/types/src/testing/service.d.ts.map +1 -1
- package/dist/types/src/testing/withPluginManager.d.ts.map +1 -1
- package/dist/types/src/testing/withPluginManager.stories.d.ts.map +1 -1
- package/dist/types/src/ui/components/App/App.d.ts +36 -3
- package/dist/types/src/ui/components/App/App.d.ts.map +1 -1
- package/dist/types/src/ui/components/App/App.stories.d.ts +2 -2
- package/dist/types/src/ui/components/App/App.stories.d.ts.map +1 -1
- package/dist/types/src/ui/components/PluginManager/PluginManagerContext.stories.d.ts.map +1 -1
- package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts +16 -4
- package/dist/types/src/ui/components/Surface/SurfaceComponent.d.ts.map +1 -1
- package/dist/types/src/ui/components/Surface/SurfaceComponent.stories.d.ts.map +1 -1
- package/dist/types/src/ui/components/Surface/SurfaceInfo.d.ts.map +1 -1
- package/dist/types/src/ui/components/Surface/SurfaceProfilerContext.d.ts +48 -0
- package/dist/types/src/ui/components/Surface/SurfaceProfilerContext.d.ts.map +1 -0
- package/dist/types/src/ui/components/Surface/index.d.ts +22 -6
- package/dist/types/src/ui/components/Surface/index.d.ts.map +1 -1
- package/dist/types/src/ui/components/Surface/types.d.ts +110 -9
- package/dist/types/src/ui/components/Surface/types.d.ts.map +1 -1
- package/dist/types/src/ui/components/Surface/types.test.d.ts +2 -0
- package/dist/types/src/ui/components/Surface/types.test.d.ts.map +1 -0
- package/dist/types/src/ui/hooks/index.d.ts +1 -1
- package/dist/types/src/ui/hooks/index.d.ts.map +1 -1
- package/dist/types/src/ui/hooks/useApp.d.ts +42 -12
- package/dist/types/src/ui/hooks/useApp.d.ts.map +1 -1
- package/dist/types/src/ui/hooks/useApp.test.d.ts +2 -0
- package/dist/types/src/ui/hooks/useApp.test.d.ts.map +1 -0
- package/dist/types/src/ui/hooks/useCapabilities.d.ts.map +1 -1
- package/dist/types/src/ui/hooks/useLoading.d.ts.map +1 -1
- package/dist/types/src/ui/hooks/useProcessManagerRuntime.d.ts +30 -0
- package/dist/types/src/ui/hooks/useProcessManagerRuntime.d.ts.map +1 -0
- package/dist/types/src/ui/hooks/useSettingsState.d.ts.map +1 -1
- package/dist/types/src/vite-plugin/boot-loader/index.d.ts +2 -0
- package/dist/types/src/vite-plugin/boot-loader/index.d.ts.map +1 -0
- package/dist/types/src/vite-plugin/boot-loader/loader.d.ts +54 -0
- package/dist/types/src/vite-plugin/boot-loader/loader.d.ts.map +1 -0
- package/dist/types/src/vite-plugin/composer/index.d.ts +34 -0
- package/dist/types/src/vite-plugin/composer/index.d.ts.map +1 -0
- package/dist/types/src/vite-plugin/import-map/index.d.ts +28 -0
- package/dist/types/src/vite-plugin/import-map/index.d.ts.map +1 -0
- package/dist/types/src/vite-plugin/index.d.ts +5 -0
- package/dist/types/src/vite-plugin/index.d.ts.map +1 -0
- package/dist/types/src/vite-plugin/manifest.d.ts +41 -0
- package/dist/types/src/vite-plugin/manifest.d.ts.map +1 -0
- package/dist/types/src/vite-plugin/manifest.test.d.ts +2 -0
- package/dist/types/src/vite-plugin/manifest.test.d.ts.map +1 -0
- package/dist/types/src/vite-plugin/packages.d.ts +13 -0
- package/dist/types/src/vite-plugin/packages.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/moon.yml +15 -0
- package/package.json +61 -57
- package/src/cli/cli.ts +2 -7
- package/src/common/activation-events.ts +10 -15
- package/src/common/annotations.ts +3 -0
- package/src/common/capabilities.ts +147 -16
- package/src/common/index.ts +0 -1
- package/src/common/translations.ts +1 -1
- package/src/core/activation-event.ts +4 -1
- package/src/core/capability-manager.test.ts +4 -1
- package/src/core/capability-manager.ts +44 -2
- package/src/core/capability.ts +20 -2
- package/src/core/edge-registry-plugin-provider.ts +92 -0
- package/src/core/index.ts +6 -0
- package/src/core/plugin-asset-cache.ts +60 -0
- package/src/core/plugin-manager.test.ts +1103 -19
- package/src/core/plugin-manager.ts +1176 -199
- package/src/core/plugin-manifest.test.ts +75 -0
- package/src/core/plugin-manifest.ts +134 -0
- package/src/core/plugin.ts +235 -17
- package/src/core/registry.ts +163 -0
- package/src/core/url-loader.test.ts +222 -0
- package/src/core/url-loader.ts +388 -0
- package/src/index.ts +1 -2
- package/src/plugin-process-manager/ProcessManagerPlugin.ts +24 -0
- package/src/{plugin-operation → plugin-process-manager}/history/capability.ts +1 -2
- package/src/plugin-process-manager/history/errors.ts +7 -0
- package/src/{plugin-operation → plugin-process-manager}/history/history-tracker.test.ts +61 -88
- package/src/{plugin-operation → plugin-process-manager}/history/history-tracker.ts +33 -18
- package/src/{plugin-operation → plugin-process-manager}/history/types.ts +1 -1
- package/src/{plugin-operation → plugin-process-manager}/history/undo-mapping.ts +2 -2
- package/src/{plugin-operation → plugin-process-manager}/history/undo-registry.test.ts +6 -5
- package/src/{plugin-operation → plugin-process-manager}/history/undo-registry.ts +1 -1
- package/src/{plugin-operation → plugin-process-manager}/index.ts +1 -1
- package/src/plugin-process-manager/meta.ts +16 -0
- package/src/plugin-process-manager/process-manager-capability.ts +178 -0
- package/src/{plugin-operation → plugin-process-manager}/testing.ts +32 -50
- package/src/testing/harness.ts +247 -0
- package/src/testing/index.ts +1 -0
- package/src/testing/react.test.tsx +50 -0
- package/src/testing/react.tsx +113 -0
- package/src/testing/service.ts +2 -2
- package/src/testing/withPluginManager.stories.tsx +1 -2
- package/src/testing/withPluginManager.tsx +47 -21
- package/src/ui/components/App/App.stories.tsx +12 -20
- package/src/ui/components/App/App.tsx +97 -10
- package/src/ui/components/PluginManager/PluginManagerContext.stories.tsx +13 -17
- package/src/ui/components/Surface/SurfaceComponent.stories.tsx +17 -16
- package/src/ui/components/Surface/SurfaceComponent.tsx +111 -55
- package/src/ui/components/Surface/SurfaceInfo.tsx +0 -1
- package/src/ui/components/Surface/SurfaceProfilerContext.tsx +207 -0
- package/src/ui/components/Surface/index.ts +35 -1
- package/src/ui/components/Surface/types.test.ts +126 -0
- package/src/ui/components/Surface/types.ts +185 -12
- package/src/ui/hooks/index.ts +1 -1
- package/src/ui/hooks/useApp.test.tsx +161 -0
- package/src/ui/hooks/useApp.tsx +215 -24
- package/src/ui/hooks/useCapabilities.ts +1 -0
- package/src/ui/hooks/useLoading.tsx +14 -6
- package/src/ui/hooks/useProcessManagerRuntime.ts +78 -0
- package/src/vite-plugin/boot-loader/BootLoader.solid-stories.tsx +198 -0
- package/src/vite-plugin/boot-loader/index.ts +5 -0
- package/src/vite-plugin/boot-loader/loader-app/Loader.tsx +166 -0
- package/src/vite-plugin/boot-loader/loader-app/boot-loader.css +311 -0
- package/src/vite-plugin/boot-loader/loader-app/bridge.ts +25 -0
- package/src/vite-plugin/boot-loader/loader-app/entry.tsx +21 -0
- package/src/vite-plugin/boot-loader/loader-app/mount.tsx +70 -0
- package/src/vite-plugin/boot-loader/loader-app/store.test.ts +137 -0
- package/src/vite-plugin/boot-loader/loader-app/store.ts +149 -0
- package/src/vite-plugin/boot-loader/loader-app/tsconfig.json +11 -0
- package/src/vite-plugin/boot-loader/loader-app/types.ts +78 -0
- package/src/vite-plugin/boot-loader/loader.ts +193 -0
- package/src/vite-plugin/composer/index.ts +306 -0
- package/src/vite-plugin/import-map/index.ts +527 -0
- package/src/vite-plugin/index.ts +10 -0
- package/src/vite-plugin/manifest.test.ts +55 -0
- package/src/vite-plugin/manifest.ts +63 -0
- package/src/vite-plugin/packages.ts +187 -0
- package/tsconfig.json +26 -3
- package/tsconfig.node.json +1 -1
- package/vitest.config.ts +1 -1
- package/.swc/plugins/linux_x86_64_19.0.0/727453fb3a62f7f1d952a41e051ca8a6f88cadc45cee43c6a4d1aa45f9b75665.wasmer-v7 +0 -0
- package/dist/lib/browser/capability-6YMUBS54.mjs +0 -37
- package/dist/lib/browser/capability-6YMUBS54.mjs.map +0 -7
- package/dist/lib/browser/capability-LF3OXSCV.mjs.map +0 -7
- package/dist/lib/browser/chunk-272IPLHQ.mjs.map +0 -7
- package/dist/lib/browser/chunk-2UI6ACB5.mjs +0 -824
- package/dist/lib/browser/chunk-2UI6ACB5.mjs.map +0 -7
- package/dist/lib/browser/chunk-7M4467QR.mjs.map +0 -7
- package/dist/lib/browser/chunk-JGWCBVKJ.mjs.map +0 -7
- package/dist/lib/browser/chunk-M5IC326L.mjs +0 -34
- package/dist/lib/browser/chunk-M5IC326L.mjs.map +0 -7
- package/dist/lib/browser/chunk-NQPYE365.mjs +0 -703
- package/dist/lib/browser/chunk-NQPYE365.mjs.map +0 -7
- package/dist/lib/browser/chunk-TIEBZMTF.mjs +0 -44
- package/dist/lib/browser/chunk-TIEBZMTF.mjs.map +0 -7
- package/dist/lib/browser/chunk-WEBSGU5L.mjs.map +0 -7
- package/dist/lib/browser/chunk-YAFEA4GV.mjs +0 -1
- package/dist/lib/browser/chunk-YNFPIQGB.mjs.map +0 -7
- package/dist/lib/browser/invoker-capability-4AUGUMM5.mjs +0 -36
- package/dist/lib/browser/invoker-capability-4AUGUMM5.mjs.map +0 -7
- package/dist/lib/node-esm/capability-R5O2OYX4.mjs +0 -38
- package/dist/lib/node-esm/capability-R5O2OYX4.mjs.map +0 -7
- package/dist/lib/node-esm/capability-SA6LPYGL.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-3D66SZHP.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-6REV5DE7.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-FHWPWQRJ.mjs +0 -704
- package/dist/lib/node-esm/chunk-FHWPWQRJ.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-JHFY3HTS.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-JIVXIJ2T.mjs +0 -825
- package/dist/lib/node-esm/chunk-JIVXIJ2T.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-NHXBSAQR.mjs +0 -35
- package/dist/lib/node-esm/chunk-NHXBSAQR.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-SB5ODNPX.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-SCDGIGGU.mjs +0 -45
- package/dist/lib/node-esm/chunk-SCDGIGGU.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-SQICGJBW.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-Z4TJPSMP.mjs +0 -2
- package/dist/lib/node-esm/invoker-capability-3YDIVDWV.mjs +0 -37
- package/dist/lib/node-esm/invoker-capability-3YDIVDWV.mjs.map +0 -7
- package/dist/types/src/common/operations.d.ts +0 -33
- package/dist/types/src/common/operations.d.ts.map +0 -1
- package/dist/types/src/plugin-operation/OperationPlugin.d.ts +0 -3
- package/dist/types/src/plugin-operation/OperationPlugin.d.ts.map +0 -1
- package/dist/types/src/plugin-operation/history/capability.d.ts.map +0 -1
- package/dist/types/src/plugin-operation/history/errors.d.ts +0 -5
- package/dist/types/src/plugin-operation/history/errors.d.ts.map +0 -1
- package/dist/types/src/plugin-operation/history/history-tracker.d.ts +0 -18
- package/dist/types/src/plugin-operation/history/history-tracker.d.ts.map +0 -1
- package/dist/types/src/plugin-operation/history/history-tracker.test.d.ts.map +0 -1
- package/dist/types/src/plugin-operation/history/index.d.ts.map +0 -1
- package/dist/types/src/plugin-operation/history/types.d.ts.map +0 -1
- package/dist/types/src/plugin-operation/history/undo-mapping.d.ts.map +0 -1
- package/dist/types/src/plugin-operation/history/undo-registry.d.ts.map +0 -1
- package/dist/types/src/plugin-operation/history/undo-registry.test.d.ts.map +0 -1
- package/dist/types/src/plugin-operation/index.d.ts +0 -3
- package/dist/types/src/plugin-operation/index.d.ts.map +0 -1
- package/dist/types/src/plugin-operation/invoker-capability.d.ts +0 -6
- package/dist/types/src/plugin-operation/invoker-capability.d.ts.map +0 -1
- package/dist/types/src/plugin-operation/meta.d.ts +0 -3
- package/dist/types/src/plugin-operation/meta.d.ts.map +0 -1
- package/dist/types/src/plugin-operation/testing.d.ts +0 -109
- package/dist/types/src/plugin-operation/testing.d.ts.map +0 -1
- package/dist/types/src/plugin-runtime/RuntimePlugin.d.ts +0 -3
- package/dist/types/src/plugin-runtime/RuntimePlugin.d.ts.map +0 -1
- package/dist/types/src/plugin-runtime/capability.d.ts +0 -6
- package/dist/types/src/plugin-runtime/capability.d.ts.map +0 -1
- package/dist/types/src/plugin-runtime/index.d.ts +0 -2
- package/dist/types/src/plugin-runtime/index.d.ts.map +0 -1
- package/dist/types/src/plugin-runtime/meta.d.ts +0 -3
- package/dist/types/src/plugin-runtime/meta.d.ts.map +0 -1
- package/dist/types/src/ui/hooks/useOperationResolver.d.ts +0 -19
- package/dist/types/src/ui/hooks/useOperationResolver.d.ts.map +0 -1
- package/src/common/operations.ts +0 -38
- package/src/plugin-operation/OperationPlugin.ts +0 -25
- package/src/plugin-operation/history/errors.ts +0 -11
- package/src/plugin-operation/invoker-capability.ts +0 -40
- package/src/plugin-operation/meta.ts +0 -11
- package/src/plugin-runtime/RuntimePlugin.ts +0 -20
- package/src/plugin-runtime/capability.ts +0 -53
- package/src/plugin-runtime/index.ts +0 -5
- package/src/plugin-runtime/meta.ts +0 -11
- package/src/ui/hooks/useOperationResolver.ts +0 -40
- /package/dist/lib/browser/{chunk-YAFEA4GV.mjs.map → core/url-loader.mjs.map} +0 -0
- /package/dist/lib/node-esm/{chunk-Z4TJPSMP.mjs.map → core/url-loader.mjs.map} +0 -0
- /package/dist/types/src/{plugin-operation → plugin-process-manager}/history/capability.d.ts +0 -0
- /package/dist/types/src/{plugin-operation → plugin-process-manager}/history/history-tracker.test.d.ts +0 -0
- /package/dist/types/src/{plugin-operation → plugin-process-manager}/history/index.d.ts +0 -0
- /package/dist/types/src/{plugin-operation → plugin-process-manager}/history/undo-registry.test.d.ts +0 -0
- /package/src/{plugin-operation → plugin-process-manager}/history/index.ts +0 -0
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { afterEach, assert, describe, it } from '@effect/vitest';
|
|
6
5
|
import { type Atom, Registry } from '@effect-atom/atom-react';
|
|
6
|
+
import { afterEach, assert, describe, it } from '@effect/vitest';
|
|
7
|
+
import * as Cause from 'effect/Cause';
|
|
7
8
|
import * as Duration from 'effect/Duration';
|
|
8
9
|
import * as Effect from 'effect/Effect';
|
|
10
|
+
import * as Exit from 'effect/Exit';
|
|
9
11
|
import * as Fiber from 'effect/Fiber';
|
|
10
12
|
import * as Match from 'effect/Match';
|
|
11
13
|
import * as PubSub from 'effect/PubSub';
|
|
@@ -13,10 +15,10 @@ import * as Queue from 'effect/Queue';
|
|
|
13
15
|
import * as TestClock from 'effect/TestClock';
|
|
14
16
|
|
|
15
17
|
import { invariant } from '@dxos/invariant';
|
|
18
|
+
import { DXN } from '@dxos/keys';
|
|
16
19
|
import { type LogConfig, type LogEntry, LogLevel, log } from '@dxos/log';
|
|
17
20
|
|
|
18
21
|
import { ActivationEvents } from '../common';
|
|
19
|
-
|
|
20
22
|
import * as ActivationEvent from './activation-event';
|
|
21
23
|
import * as Capability from './capability';
|
|
22
24
|
import type * as CapabilityManager from './capability-manager';
|
|
@@ -30,7 +32,7 @@ const Total = Capability.make<{ total: number }>('org.dxos.test.total');
|
|
|
30
32
|
const CountEvent = ActivationEvent.make('org.dxos.test.count');
|
|
31
33
|
const FailEvent = ActivationEvent.make('org.dxos.test.fail');
|
|
32
34
|
|
|
33
|
-
const testMeta = {
|
|
35
|
+
const testMeta = Plugin.makeMeta({ key: DXN.make('org.dxos.plugin.test'), name: 'Test' });
|
|
34
36
|
|
|
35
37
|
// TODO(wittjosiah): Factor out?
|
|
36
38
|
const atomCounter = (registry: Registry.Registry, atom: Atom.Atom<any>) => {
|
|
@@ -60,7 +62,7 @@ describe('PluginManager', () => {
|
|
|
60
62
|
const pluginLoader = Effect.fn(function* (id: string) {
|
|
61
63
|
const plugin = plugins.find((plugin) => plugin.meta.id === id);
|
|
62
64
|
invariant(plugin, `Plugin not found: ${id}`);
|
|
63
|
-
return plugin;
|
|
65
|
+
return { plugin };
|
|
64
66
|
});
|
|
65
67
|
|
|
66
68
|
afterEach(() => {
|
|
@@ -75,14 +77,138 @@ describe('PluginManager', () => {
|
|
|
75
77
|
|
|
76
78
|
const manager = PluginManager.make({ pluginLoader });
|
|
77
79
|
const added = yield* manager.add(testMeta.id);
|
|
78
|
-
assert.
|
|
80
|
+
assert.strictEqual(added, testPlugin);
|
|
79
81
|
assert.deepStrictEqual(manager.getPlugins(), [testPlugin]);
|
|
80
|
-
|
|
82
|
+
assert.deepStrictEqual(manager.getEnabled(), []);
|
|
83
|
+
const removed = yield* manager.remove(testMeta.id);
|
|
81
84
|
assert.isTrue(removed);
|
|
82
85
|
assert.deepStrictEqual(manager.getPlugins(), []);
|
|
83
86
|
}),
|
|
84
87
|
);
|
|
85
88
|
|
|
89
|
+
it.effect('should add plugin when locator differs from meta.id', () =>
|
|
90
|
+
Effect.gen(function* () {
|
|
91
|
+
const Test = Plugin.make(Plugin.define(testMeta));
|
|
92
|
+
const testPlugin = Test();
|
|
93
|
+
|
|
94
|
+
const urlLocator = 'https://example.com/plugin.mjs';
|
|
95
|
+
const urlLoader = Effect.fn(function* (locator: string) {
|
|
96
|
+
if (locator === urlLocator) {
|
|
97
|
+
return { plugin: testPlugin };
|
|
98
|
+
}
|
|
99
|
+
return yield* Effect.fail(new Error(`Unknown locator: ${locator}`));
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const manager = PluginManager.make({ pluginLoader: urlLoader });
|
|
103
|
+
const added = yield* manager.add(urlLocator);
|
|
104
|
+
assert.strictEqual(added, testPlugin);
|
|
105
|
+
assert.deepStrictEqual(manager.getPlugins(), [testPlugin]);
|
|
106
|
+
assert.deepStrictEqual(manager.getEnabled(), []);
|
|
107
|
+
yield* manager.enable(added.meta.id);
|
|
108
|
+
assert.deepStrictEqual(manager.getEnabled(), [testMeta.id]);
|
|
109
|
+
}),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
it.effect('dev plugin shadows an existing plugin with the same id', () =>
|
|
113
|
+
Effect.gen(function* () {
|
|
114
|
+
const productionPlugin = Plugin.make(
|
|
115
|
+
Plugin.define(testMeta).pipe(
|
|
116
|
+
Plugin.addModule({
|
|
117
|
+
id: 'Prod',
|
|
118
|
+
activatesOn: ActivationEvents.Startup,
|
|
119
|
+
activate: () => Effect.succeed(Capability.contributes(String, { string: 'prod' })),
|
|
120
|
+
}),
|
|
121
|
+
),
|
|
122
|
+
)();
|
|
123
|
+
const devPlugin = Plugin.make(
|
|
124
|
+
Plugin.define(testMeta).pipe(
|
|
125
|
+
Plugin.addModule({
|
|
126
|
+
id: 'Dev',
|
|
127
|
+
activatesOn: ActivationEvents.Startup,
|
|
128
|
+
activate: () => Effect.succeed(Capability.contributes(String, { string: 'dev' })),
|
|
129
|
+
}),
|
|
130
|
+
),
|
|
131
|
+
)();
|
|
132
|
+
|
|
133
|
+
const loader = Effect.fn(function* (locator: string) {
|
|
134
|
+
if (locator === 'prod') {
|
|
135
|
+
return { plugin: productionPlugin };
|
|
136
|
+
}
|
|
137
|
+
if (locator === 'dev') {
|
|
138
|
+
return { plugin: devPlugin, dev: true };
|
|
139
|
+
}
|
|
140
|
+
return yield* Effect.fail(new Error(`Unknown locator: ${locator}`));
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const manager = PluginManager.make({ pluginLoader: loader });
|
|
144
|
+
yield* manager.add('prod');
|
|
145
|
+
yield* manager.enable(testMeta.id);
|
|
146
|
+
yield* manager.activate(ActivationEvents.Startup);
|
|
147
|
+
assert.deepStrictEqual(
|
|
148
|
+
manager.capabilities.getAll(String).map((value) => value.string),
|
|
149
|
+
['prod'],
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
// Loading the dev plugin with the same id swaps it into the id slot.
|
|
153
|
+
yield* manager.add('dev');
|
|
154
|
+
yield* manager.enable(testMeta.id);
|
|
155
|
+
assert.strictEqual(
|
|
156
|
+
manager.getPlugins().find((plugin) => plugin.meta.id === testMeta.id),
|
|
157
|
+
devPlugin,
|
|
158
|
+
);
|
|
159
|
+
yield* manager.reset(ActivationEvents.Startup);
|
|
160
|
+
assert.deepStrictEqual(
|
|
161
|
+
manager.capabilities.getAll(String).map((value) => value.string),
|
|
162
|
+
['dev'],
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// Removing the dev plugin restores the original and re-enables it
|
|
166
|
+
// because it was enabled at shadow time.
|
|
167
|
+
yield* manager.remove(testMeta.id);
|
|
168
|
+
assert.strictEqual(
|
|
169
|
+
manager.getPlugins().find((plugin) => plugin.meta.id === testMeta.id),
|
|
170
|
+
productionPlugin,
|
|
171
|
+
);
|
|
172
|
+
assert.isTrue(manager.getEnabled().includes(testMeta.id));
|
|
173
|
+
yield* manager.reset(ActivationEvents.Startup);
|
|
174
|
+
assert.deepStrictEqual(
|
|
175
|
+
manager.capabilities.getAll(String).map((value) => value.string),
|
|
176
|
+
['prod'],
|
|
177
|
+
);
|
|
178
|
+
}),
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
it.effect('dev plugin add does not auto-enable a previously-disabled shadow target', () =>
|
|
182
|
+
Effect.gen(function* () {
|
|
183
|
+
const productionPlugin = Plugin.make(Plugin.define(testMeta))();
|
|
184
|
+
const devPlugin = Plugin.make(Plugin.define(testMeta))();
|
|
185
|
+
const loader = Effect.fn(function* (locator: string) {
|
|
186
|
+
if (locator === 'prod') {
|
|
187
|
+
return { plugin: productionPlugin };
|
|
188
|
+
}
|
|
189
|
+
if (locator === 'dev') {
|
|
190
|
+
return { plugin: devPlugin, dev: true };
|
|
191
|
+
}
|
|
192
|
+
return yield* Effect.fail(new Error(`Unknown locator: ${locator}`));
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const manager = PluginManager.make({ pluginLoader: loader });
|
|
196
|
+
yield* manager.add('prod');
|
|
197
|
+
// Production plugin is registered but explicitly NOT enabled.
|
|
198
|
+
assert.deepStrictEqual(manager.getEnabled(), []);
|
|
199
|
+
|
|
200
|
+
yield* manager.add('dev');
|
|
201
|
+
yield* manager.remove(testMeta.id);
|
|
202
|
+
|
|
203
|
+
// Original is restored but stays disabled, matching its pre-shadow state.
|
|
204
|
+
assert.strictEqual(
|
|
205
|
+
manager.getPlugins().find((plugin) => plugin.meta.id === testMeta.id),
|
|
206
|
+
productionPlugin,
|
|
207
|
+
);
|
|
208
|
+
assert.deepStrictEqual(manager.getEnabled(), []);
|
|
209
|
+
}),
|
|
210
|
+
);
|
|
211
|
+
|
|
86
212
|
it.effect('should support factory pattern with options', () =>
|
|
87
213
|
Effect.gen(function* () {
|
|
88
214
|
type TestPluginOptions = { count: number };
|
|
@@ -103,7 +229,7 @@ describe('PluginManager', () => {
|
|
|
103
229
|
const plugin = TestPluginFactory({ count: 5 });
|
|
104
230
|
plugins = [plugin];
|
|
105
231
|
|
|
106
|
-
const manager = PluginManager.make({ plugins: [plugin],
|
|
232
|
+
const manager = PluginManager.make({ plugins: [plugin], pluginLoader });
|
|
107
233
|
yield* manager.enable(testMeta.id);
|
|
108
234
|
yield* manager.activate(ActivationEvents.Startup);
|
|
109
235
|
const strings = manager.capabilities.getAll(String);
|
|
@@ -125,7 +251,7 @@ describe('PluginManager', () => {
|
|
|
125
251
|
);
|
|
126
252
|
|
|
127
253
|
const testPlugin = Test();
|
|
128
|
-
const manager = PluginManager.make({ plugins: [testPlugin],
|
|
254
|
+
const manager = PluginManager.make({ plugins: [testPlugin], pluginLoader });
|
|
129
255
|
yield* manager.enable(testMeta.id);
|
|
130
256
|
assert.deepStrictEqual(manager.getEnabled(), [Test.meta.id]);
|
|
131
257
|
assert.deepStrictEqual(manager.getModules(), [testPlugin.modules[0]]);
|
|
@@ -197,6 +323,7 @@ describe('PluginManager', () => {
|
|
|
197
323
|
|
|
198
324
|
const manager = PluginManager.make({ pluginLoader });
|
|
199
325
|
yield* manager.add(testMeta.id);
|
|
326
|
+
yield* manager.enable(testMeta.id);
|
|
200
327
|
const error = yield* Effect.flip(manager.activate(FailEvent));
|
|
201
328
|
assert.strictEqual(error.message, 'test');
|
|
202
329
|
}),
|
|
@@ -229,6 +356,7 @@ describe('PluginManager', () => {
|
|
|
229
356
|
|
|
230
357
|
const manager = PluginManager.make({ pluginLoader });
|
|
231
358
|
yield* manager.add(testMeta.id);
|
|
359
|
+
yield* manager.enable(testMeta.id);
|
|
232
360
|
const error = yield* Effect.flip(manager.activate(DefectEvent));
|
|
233
361
|
|
|
234
362
|
// Verify the error was caught and propagated.
|
|
@@ -275,6 +403,7 @@ describe('PluginManager', () => {
|
|
|
275
403
|
|
|
276
404
|
const manager = PluginManager.make({ pluginLoader });
|
|
277
405
|
yield* manager.add(testMeta.id);
|
|
406
|
+
yield* manager.enable(testMeta.id);
|
|
278
407
|
const error = yield* Effect.flip(manager.activate(DefectEvent));
|
|
279
408
|
|
|
280
409
|
// Verify the error was caught and propagated.
|
|
@@ -339,6 +468,7 @@ describe('PluginManager', () => {
|
|
|
339
468
|
);
|
|
340
469
|
|
|
341
470
|
yield* manager.add(testMeta.id);
|
|
471
|
+
yield* manager.enable(testMeta.id);
|
|
342
472
|
yield* manager.activate(ActivationEvents.Startup);
|
|
343
473
|
yield* activating.await;
|
|
344
474
|
yield* activated.await;
|
|
@@ -389,6 +519,7 @@ describe('PluginManager', () => {
|
|
|
389
519
|
|
|
390
520
|
{
|
|
391
521
|
yield* manager.add(testMeta.id);
|
|
522
|
+
yield* manager.enable(testMeta.id);
|
|
392
523
|
const result = yield* manager.activate(ActivationEvents.Startup);
|
|
393
524
|
assert.isTrue(result);
|
|
394
525
|
assert.deepStrictEqual(manager.getActive(), [testPlugin.modules[0].id]);
|
|
@@ -419,7 +550,7 @@ describe('PluginManager', () => {
|
|
|
419
550
|
|
|
420
551
|
it.effect('should be able to fire custom activation events', () =>
|
|
421
552
|
Effect.gen(function* () {
|
|
422
|
-
const Plugin1 = Plugin.define({
|
|
553
|
+
const Plugin1 = Plugin.define(Plugin.makeMeta({ key: DXN.make('org.dxos.test.plugin1'), name: 'Plugin 1' })).pipe(
|
|
423
554
|
Plugin.addModule({
|
|
424
555
|
activatesOn: CountEvent,
|
|
425
556
|
id: 'Plugin1',
|
|
@@ -427,7 +558,7 @@ describe('PluginManager', () => {
|
|
|
427
558
|
}),
|
|
428
559
|
Plugin.make,
|
|
429
560
|
);
|
|
430
|
-
const Plugin2 = Plugin.define({
|
|
561
|
+
const Plugin2 = Plugin.define(Plugin.makeMeta({ key: DXN.make('org.dxos.test.plugin2'), name: 'Plugin 2' })).pipe(
|
|
431
562
|
Plugin.addModule({
|
|
432
563
|
activatesOn: CountEvent,
|
|
433
564
|
id: 'Plugin2',
|
|
@@ -435,7 +566,7 @@ describe('PluginManager', () => {
|
|
|
435
566
|
}),
|
|
436
567
|
Plugin.make,
|
|
437
568
|
);
|
|
438
|
-
const Plugin3 = Plugin.define({
|
|
569
|
+
const Plugin3 = Plugin.define(Plugin.makeMeta({ key: DXN.make('org.dxos.test.plugin3'), name: 'Plugin 3' })).pipe(
|
|
439
570
|
Plugin.addModule({
|
|
440
571
|
activatesOn: CountEvent,
|
|
441
572
|
id: 'Plugin3',
|
|
@@ -453,16 +584,19 @@ describe('PluginManager', () => {
|
|
|
453
584
|
assert.strictEqual(manager.capabilities.getAll(Number).length, 0);
|
|
454
585
|
|
|
455
586
|
yield* manager.add(Plugin1.meta.id);
|
|
587
|
+
yield* manager.enable(Plugin1.meta.id);
|
|
456
588
|
yield* manager.activate(CountEvent);
|
|
457
589
|
assert.deepStrictEqual(manager.getActive(), [plugin1.modules[0].id]);
|
|
458
590
|
assert.strictEqual(manager.capabilities.getAll(Number).length, 1);
|
|
459
591
|
|
|
460
592
|
yield* manager.add(Plugin2.meta.id);
|
|
593
|
+
yield* manager.enable(Plugin2.meta.id);
|
|
461
594
|
yield* manager.activate(CountEvent);
|
|
462
595
|
assert.deepStrictEqual(manager.getActive(), [plugin1.modules[0].id, plugin2.modules[0].id]);
|
|
463
596
|
assert.strictEqual(manager.capabilities.getAll(Number).length, 2);
|
|
464
597
|
|
|
465
598
|
yield* manager.add(Plugin3.meta.id);
|
|
599
|
+
yield* manager.enable(Plugin3.meta.id);
|
|
466
600
|
yield* manager.activate(CountEvent);
|
|
467
601
|
assert.deepStrictEqual(manager.getActive(), [
|
|
468
602
|
plugin1.modules[0].id,
|
|
@@ -493,6 +627,7 @@ describe('PluginManager', () => {
|
|
|
493
627
|
assert.strictEqual(manager.capabilities.getAll(String).length, 0);
|
|
494
628
|
|
|
495
629
|
yield* manager.add(testMeta.id);
|
|
630
|
+
yield* manager.enable(testMeta.id);
|
|
496
631
|
yield* manager.activate(ActivationEvents.Startup);
|
|
497
632
|
assert.deepStrictEqual(manager.getActive(), []);
|
|
498
633
|
assert.strictEqual(manager.capabilities.getAll(String).length, 0);
|
|
@@ -526,6 +661,7 @@ describe('PluginManager', () => {
|
|
|
526
661
|
assert.strictEqual(count, 0);
|
|
527
662
|
|
|
528
663
|
yield* manager.add(testMeta.id);
|
|
664
|
+
yield* manager.enable(testMeta.id);
|
|
529
665
|
yield* manager.activate(CountEvent);
|
|
530
666
|
assert.deepStrictEqual(manager.getActive(), [testPlugin.modules[0].id]);
|
|
531
667
|
assert.strictEqual(manager.capabilities.getAll(String).length, 1);
|
|
@@ -546,11 +682,11 @@ describe('PluginManager', () => {
|
|
|
546
682
|
state.total = numbers.reduce((acc: number, n: { number: number }) => acc + n.number, 0);
|
|
547
683
|
};
|
|
548
684
|
|
|
549
|
-
const Count = Plugin.define({
|
|
685
|
+
const Count = Plugin.define(Plugin.makeMeta({ key: DXN.make('org.dxos.test.count'), name: 'Count' })).pipe(
|
|
550
686
|
Plugin.addModule({
|
|
551
687
|
id: 'Count',
|
|
552
688
|
activatesOn: ActivationEvents.Startup,
|
|
553
|
-
|
|
689
|
+
firesBeforeActivation: [CountEvent],
|
|
554
690
|
activate: Effect.fnUntraced(function* () {
|
|
555
691
|
const capabilityManager = yield* Capability.Service;
|
|
556
692
|
computeTotal(capabilityManager);
|
|
@@ -585,7 +721,9 @@ describe('PluginManager', () => {
|
|
|
585
721
|
const manager = PluginManager.make({ pluginLoader });
|
|
586
722
|
{
|
|
587
723
|
yield* manager.add(Test.meta.id);
|
|
724
|
+
yield* manager.enable(Test.meta.id);
|
|
588
725
|
yield* manager.add(Count.meta.id);
|
|
726
|
+
yield* manager.enable(Count.meta.id);
|
|
589
727
|
yield* manager.activate(ActivationEvents.Startup);
|
|
590
728
|
assert.deepStrictEqual(manager.getActive(), [
|
|
591
729
|
...testPlugin.modules.map((m) => m.id),
|
|
@@ -626,7 +764,7 @@ describe('PluginManager', () => {
|
|
|
626
764
|
|
|
627
765
|
it.effect('should be reactive', () =>
|
|
628
766
|
Effect.gen(function* () {
|
|
629
|
-
const Plugin1 = Plugin.define({
|
|
767
|
+
const Plugin1 = Plugin.define(Plugin.makeMeta({ key: DXN.make('org.dxos.test.plugin1'), name: 'Plugin 1' })).pipe(
|
|
630
768
|
Plugin.addModule({
|
|
631
769
|
activatesOn: CountEvent,
|
|
632
770
|
id: 'Plugin1',
|
|
@@ -634,7 +772,7 @@ describe('PluginManager', () => {
|
|
|
634
772
|
}),
|
|
635
773
|
Plugin.make,
|
|
636
774
|
);
|
|
637
|
-
const Plugin2 = Plugin.define({
|
|
775
|
+
const Plugin2 = Plugin.define(Plugin.makeMeta({ key: DXN.make('org.dxos.test.plugin2'), name: 'Plugin 2' })).pipe(
|
|
638
776
|
Plugin.addModule({
|
|
639
777
|
activatesOn: CountEvent,
|
|
640
778
|
id: 'Plugin2',
|
|
@@ -642,7 +780,7 @@ describe('PluginManager', () => {
|
|
|
642
780
|
}),
|
|
643
781
|
Plugin.make,
|
|
644
782
|
);
|
|
645
|
-
const Plugin3 = Plugin.define({
|
|
783
|
+
const Plugin3 = Plugin.define(Plugin.makeMeta({ key: DXN.make('org.dxos.test.plugin3'), name: 'Plugin 3' })).pipe(
|
|
646
784
|
Plugin.addModule({
|
|
647
785
|
activatesOn: CountEvent,
|
|
648
786
|
id: 'Plugin3',
|
|
@@ -668,6 +806,7 @@ describe('PluginManager', () => {
|
|
|
668
806
|
assert.strictEqual(pendingResetUpdates.count, 0);
|
|
669
807
|
|
|
670
808
|
yield* manager.add(Plugin1.meta.id);
|
|
809
|
+
yield* manager.enable(Plugin1.meta.id);
|
|
671
810
|
assert.strictEqual(pluginUpdates.count, 1);
|
|
672
811
|
assert.strictEqual(enabledUpdates.count, 1);
|
|
673
812
|
assert.strictEqual(modulesUpdates.count, 1);
|
|
@@ -684,6 +823,7 @@ describe('PluginManager', () => {
|
|
|
684
823
|
assert.strictEqual(pendingResetUpdates.count, 0);
|
|
685
824
|
|
|
686
825
|
yield* manager.add(Plugin2.meta.id);
|
|
826
|
+
yield* manager.enable(Plugin2.meta.id);
|
|
687
827
|
assert.strictEqual(pluginUpdates.count, 2);
|
|
688
828
|
assert.strictEqual(enabledUpdates.count, 2);
|
|
689
829
|
assert.strictEqual(modulesUpdates.count, 2);
|
|
@@ -700,6 +840,7 @@ describe('PluginManager', () => {
|
|
|
700
840
|
assert.strictEqual(pendingResetUpdates.count, 2);
|
|
701
841
|
|
|
702
842
|
yield* manager.add(Plugin3.meta.id);
|
|
843
|
+
yield* manager.enable(Plugin3.meta.id);
|
|
703
844
|
assert.strictEqual(pluginUpdates.count, 3);
|
|
704
845
|
assert.strictEqual(enabledUpdates.count, 3);
|
|
705
846
|
assert.strictEqual(modulesUpdates.count, 3);
|
|
@@ -724,7 +865,7 @@ describe('PluginManager', () => {
|
|
|
724
865
|
assert.strictEqual(eventsFiredUpdates.count, 1);
|
|
725
866
|
assert.strictEqual(pendingResetUpdates.count, 4);
|
|
726
867
|
|
|
727
|
-
manager.remove(Plugin1.meta.id);
|
|
868
|
+
yield* manager.remove(Plugin1.meta.id);
|
|
728
869
|
assert.strictEqual(pluginUpdates.count, 4);
|
|
729
870
|
assert.strictEqual(enabledUpdates.count, 4);
|
|
730
871
|
assert.strictEqual(modulesUpdates.count, 4);
|
|
@@ -753,7 +894,9 @@ describe('PluginManager', () => {
|
|
|
753
894
|
});
|
|
754
895
|
|
|
755
896
|
const SlowEvent = ActivationEvent.make('org.dxos.test.slow');
|
|
756
|
-
const SlowPlugin = Plugin.define(
|
|
897
|
+
const SlowPlugin = Plugin.define(
|
|
898
|
+
Plugin.makeMeta({ key: DXN.make('org.dxos.test.slowPlugin'), name: 'Slow Plugin' }),
|
|
899
|
+
).pipe(
|
|
757
900
|
Plugin.addModule({
|
|
758
901
|
id: 'SlowModule',
|
|
759
902
|
activatesOn: SlowEvent,
|
|
@@ -771,6 +914,7 @@ describe('PluginManager', () => {
|
|
|
771
914
|
|
|
772
915
|
const manager = PluginManager.make({ pluginLoader });
|
|
773
916
|
yield* manager.add(SlowPlugin.meta.id);
|
|
917
|
+
yield* manager.enable(SlowPlugin.meta.id);
|
|
774
918
|
|
|
775
919
|
// Fork the activation so we can control time with TestClock.
|
|
776
920
|
const activationFiber = yield* Effect.fork(manager.activate(SlowEvent));
|
|
@@ -799,7 +943,12 @@ describe('PluginManager', () => {
|
|
|
799
943
|
const EventB = ActivationEvent.make('org.dxos.test.event-b');
|
|
800
944
|
|
|
801
945
|
let activateCallCount = 0;
|
|
802
|
-
const ConcurrentPlugin = Plugin.define(
|
|
946
|
+
const ConcurrentPlugin = Plugin.define(
|
|
947
|
+
Plugin.makeMeta({
|
|
948
|
+
key: DXN.make('org.dxos.test.concurrentPlugin'),
|
|
949
|
+
name: 'Concurrent Plugin',
|
|
950
|
+
}),
|
|
951
|
+
).pipe(
|
|
803
952
|
Plugin.addModule({
|
|
804
953
|
id: 'ConcurrentModule',
|
|
805
954
|
// Module activates on either event - this allows two different events to race.
|
|
@@ -819,6 +968,7 @@ describe('PluginManager', () => {
|
|
|
819
968
|
|
|
820
969
|
const manager = PluginManager.make({ pluginLoader });
|
|
821
970
|
yield* manager.add(ConcurrentPlugin.meta.id);
|
|
971
|
+
yield* manager.enable(ConcurrentPlugin.meta.id);
|
|
822
972
|
|
|
823
973
|
// Fork two concurrent activations with DIFFERENT events.
|
|
824
974
|
// Both events trigger the same module, so both will try to call _loadModule.
|
|
@@ -842,4 +992,938 @@ describe('PluginManager', () => {
|
|
|
842
992
|
assert.strictEqual(strings[0].string, 'concurrent');
|
|
843
993
|
}),
|
|
844
994
|
);
|
|
995
|
+
|
|
996
|
+
it.effect('should deactivate all active modules on shutdown', () =>
|
|
997
|
+
Effect.gen(function* () {
|
|
998
|
+
const Plugin1 = Plugin.define(Plugin.makeMeta({ key: DXN.make('org.dxos.test.plugin1'), name: 'Plugin 1' })).pipe(
|
|
999
|
+
Plugin.addModule({
|
|
1000
|
+
activatesOn: ActivationEvents.Startup,
|
|
1001
|
+
id: 'Plugin1',
|
|
1002
|
+
activate: () => Effect.succeed(Capability.contributes(String, { string: 'hello' })),
|
|
1003
|
+
}),
|
|
1004
|
+
Plugin.make,
|
|
1005
|
+
);
|
|
1006
|
+
const Plugin2 = Plugin.define(Plugin.makeMeta({ key: DXN.make('org.dxos.test.plugin2'), name: 'Plugin 2' })).pipe(
|
|
1007
|
+
Plugin.addModule({
|
|
1008
|
+
activatesOn: ActivationEvents.Startup,
|
|
1009
|
+
id: 'Plugin2',
|
|
1010
|
+
activate: () => Effect.succeed(Capability.contributes(Number, { number: 42 })),
|
|
1011
|
+
}),
|
|
1012
|
+
Plugin.make,
|
|
1013
|
+
);
|
|
1014
|
+
const plugin1 = Plugin1();
|
|
1015
|
+
const plugin2 = Plugin2();
|
|
1016
|
+
plugins = [plugin1, plugin2];
|
|
1017
|
+
|
|
1018
|
+
const manager = PluginManager.make({ pluginLoader });
|
|
1019
|
+
yield* manager.add(Plugin1.meta.id);
|
|
1020
|
+
yield* manager.enable(Plugin1.meta.id);
|
|
1021
|
+
yield* manager.add(Plugin2.meta.id);
|
|
1022
|
+
yield* manager.enable(Plugin2.meta.id);
|
|
1023
|
+
yield* manager.activate(ActivationEvents.Startup);
|
|
1024
|
+
assert.strictEqual(manager.getActive().length, 2);
|
|
1025
|
+
assert.strictEqual(manager.capabilities.getAll(String).length, 1);
|
|
1026
|
+
assert.strictEqual(manager.capabilities.getAll(Number).length, 1);
|
|
1027
|
+
|
|
1028
|
+
const result = yield* manager.shutdown();
|
|
1029
|
+
assert.isTrue(result);
|
|
1030
|
+
assert.deepStrictEqual(manager.getActive(), []);
|
|
1031
|
+
assert.strictEqual(manager.capabilities.getAll(String).length, 0);
|
|
1032
|
+
assert.strictEqual(manager.capabilities.getAll(Number).length, 0);
|
|
1033
|
+
}),
|
|
1034
|
+
);
|
|
1035
|
+
|
|
1036
|
+
it.effect('should run capability deactivate hooks during shutdown', () =>
|
|
1037
|
+
Effect.gen(function* () {
|
|
1038
|
+
let deactivated = false;
|
|
1039
|
+
const Test = Plugin.define(testMeta).pipe(
|
|
1040
|
+
Plugin.addModule({
|
|
1041
|
+
id: 'WithDeactivate',
|
|
1042
|
+
activatesOn: ActivationEvents.Startup,
|
|
1043
|
+
activate: () =>
|
|
1044
|
+
Effect.succeed(
|
|
1045
|
+
Capability.contributes(String, { string: 'hello' }, () =>
|
|
1046
|
+
Effect.sync(() => {
|
|
1047
|
+
deactivated = true;
|
|
1048
|
+
}),
|
|
1049
|
+
),
|
|
1050
|
+
),
|
|
1051
|
+
}),
|
|
1052
|
+
Plugin.make,
|
|
1053
|
+
);
|
|
1054
|
+
const testPlugin = Test();
|
|
1055
|
+
plugins = [testPlugin];
|
|
1056
|
+
|
|
1057
|
+
const manager = PluginManager.make({ pluginLoader });
|
|
1058
|
+
yield* manager.add(testMeta.id);
|
|
1059
|
+
yield* manager.enable(testMeta.id);
|
|
1060
|
+
yield* manager.activate(ActivationEvents.Startup);
|
|
1061
|
+
assert.isFalse(deactivated);
|
|
1062
|
+
|
|
1063
|
+
yield* manager.shutdown();
|
|
1064
|
+
assert.isTrue(deactivated);
|
|
1065
|
+
}),
|
|
1066
|
+
);
|
|
1067
|
+
|
|
1068
|
+
it.effect('should deactivate modules in reverse activation order during shutdown', () =>
|
|
1069
|
+
Effect.gen(function* () {
|
|
1070
|
+
const deactivationOrder: string[] = [];
|
|
1071
|
+
const Plugin1 = Plugin.define(Plugin.makeMeta({ key: DXN.make('org.dxos.test.plugin1'), name: 'Plugin 1' })).pipe(
|
|
1072
|
+
Plugin.addModule({
|
|
1073
|
+
activatesOn: ActivationEvents.Startup,
|
|
1074
|
+
id: 'First',
|
|
1075
|
+
activate: () =>
|
|
1076
|
+
Effect.succeed(
|
|
1077
|
+
Capability.contributes(String, { string: 'first' }, () =>
|
|
1078
|
+
Effect.sync(() => {
|
|
1079
|
+
deactivationOrder.push('First');
|
|
1080
|
+
}),
|
|
1081
|
+
),
|
|
1082
|
+
),
|
|
1083
|
+
}),
|
|
1084
|
+
Plugin.make,
|
|
1085
|
+
);
|
|
1086
|
+
const Plugin2 = Plugin.define(Plugin.makeMeta({ key: DXN.make('org.dxos.test.plugin2'), name: 'Plugin 2' })).pipe(
|
|
1087
|
+
Plugin.addModule({
|
|
1088
|
+
activatesOn: ActivationEvents.Startup,
|
|
1089
|
+
id: 'Second',
|
|
1090
|
+
activate: () =>
|
|
1091
|
+
Effect.succeed(
|
|
1092
|
+
Capability.contributes(Number, { number: 2 }, () =>
|
|
1093
|
+
Effect.sync(() => {
|
|
1094
|
+
deactivationOrder.push('Second');
|
|
1095
|
+
}),
|
|
1096
|
+
),
|
|
1097
|
+
),
|
|
1098
|
+
}),
|
|
1099
|
+
Plugin.make,
|
|
1100
|
+
);
|
|
1101
|
+
plugins = [Plugin1(), Plugin2()];
|
|
1102
|
+
|
|
1103
|
+
const manager = PluginManager.make({ pluginLoader });
|
|
1104
|
+
yield* manager.add(Plugin1.meta.id);
|
|
1105
|
+
yield* manager.enable(Plugin1.meta.id);
|
|
1106
|
+
yield* manager.add(Plugin2.meta.id);
|
|
1107
|
+
yield* manager.enable(Plugin2.meta.id);
|
|
1108
|
+
yield* manager.activate(ActivationEvents.Startup);
|
|
1109
|
+
|
|
1110
|
+
yield* manager.shutdown();
|
|
1111
|
+
assert.deepStrictEqual(deactivationOrder, ['Second', 'First']);
|
|
1112
|
+
}),
|
|
1113
|
+
);
|
|
1114
|
+
|
|
1115
|
+
it.effect('should clear lifecycle bookkeeping during shutdown', () =>
|
|
1116
|
+
Effect.gen(function* () {
|
|
1117
|
+
const Test = Plugin.define(testMeta).pipe(
|
|
1118
|
+
Plugin.addModule({
|
|
1119
|
+
id: 'Hello',
|
|
1120
|
+
activatesOn: ActivationEvents.Startup,
|
|
1121
|
+
activate: () => Effect.succeed(Capability.contributes(String, { string: 'hello' })),
|
|
1122
|
+
}),
|
|
1123
|
+
Plugin.make,
|
|
1124
|
+
);
|
|
1125
|
+
const testPlugin = Test();
|
|
1126
|
+
plugins = [testPlugin];
|
|
1127
|
+
|
|
1128
|
+
const manager = PluginManager.make({ pluginLoader });
|
|
1129
|
+
yield* manager.add(testMeta.id);
|
|
1130
|
+
yield* manager.enable(testMeta.id);
|
|
1131
|
+
yield* manager.activate(ActivationEvents.Startup);
|
|
1132
|
+
assert.isTrue(manager.getEventsFired().length > 0);
|
|
1133
|
+
|
|
1134
|
+
yield* manager.shutdown();
|
|
1135
|
+
assert.deepStrictEqual(manager.getEventsFired(), []);
|
|
1136
|
+
assert.deepStrictEqual(manager.getPendingReset(), []);
|
|
1137
|
+
assert.deepStrictEqual(manager.getActive(), []);
|
|
1138
|
+
}),
|
|
1139
|
+
);
|
|
1140
|
+
|
|
1141
|
+
it.effect('should interrupt in-flight activation during shutdown', () =>
|
|
1142
|
+
Effect.gen(function* () {
|
|
1143
|
+
const activationStarted = yield* Effect.makeLatch(false);
|
|
1144
|
+
const allowActivationToComplete = yield* Effect.makeLatch(false);
|
|
1145
|
+
const Test = Plugin.define(testMeta).pipe(
|
|
1146
|
+
Plugin.addModule({
|
|
1147
|
+
id: 'Hello',
|
|
1148
|
+
activatesOn: ActivationEvents.Startup,
|
|
1149
|
+
activate: () =>
|
|
1150
|
+
Effect.gen(function* () {
|
|
1151
|
+
yield* activationStarted.open;
|
|
1152
|
+
yield* allowActivationToComplete.await;
|
|
1153
|
+
return Capability.contributes(String, { string: 'hello' });
|
|
1154
|
+
}),
|
|
1155
|
+
}),
|
|
1156
|
+
Plugin.make,
|
|
1157
|
+
);
|
|
1158
|
+
const testPlugin = Test();
|
|
1159
|
+
plugins = [testPlugin];
|
|
1160
|
+
|
|
1161
|
+
const manager = PluginManager.make({ pluginLoader });
|
|
1162
|
+
yield* manager.add(testMeta.id);
|
|
1163
|
+
yield* manager.enable(testMeta.id);
|
|
1164
|
+
|
|
1165
|
+
const activationFiber = yield* Effect.fork(manager.activate(ActivationEvents.Startup));
|
|
1166
|
+
yield* activationStarted.await;
|
|
1167
|
+
|
|
1168
|
+
const shutdownFiber = yield* Effect.fork(manager.shutdown());
|
|
1169
|
+
yield* allowActivationToComplete.open;
|
|
1170
|
+
|
|
1171
|
+
const shutdownResult = yield* Fiber.join(shutdownFiber);
|
|
1172
|
+
const activationExit = yield* Fiber.await(activationFiber);
|
|
1173
|
+
|
|
1174
|
+
assert.isTrue(shutdownResult);
|
|
1175
|
+
assert.isTrue(Exit.isFailure(activationExit));
|
|
1176
|
+
if (Exit.isFailure(activationExit)) {
|
|
1177
|
+
assert.isTrue(Cause.isInterruptedOnly(activationExit.cause));
|
|
1178
|
+
}
|
|
1179
|
+
assert.strictEqual(manager.capabilities.getAll(String).length, 0);
|
|
1180
|
+
assert.deepStrictEqual(manager.getActive(), []);
|
|
1181
|
+
assert.deepStrictEqual(manager.getEventsFired(), []);
|
|
1182
|
+
}),
|
|
1183
|
+
);
|
|
1184
|
+
|
|
1185
|
+
it.effect('should preserve plugins, core, enabled, and modules after shutdown', () =>
|
|
1186
|
+
Effect.gen(function* () {
|
|
1187
|
+
const Test = Plugin.define(testMeta).pipe(
|
|
1188
|
+
Plugin.addModule({
|
|
1189
|
+
id: 'Hello',
|
|
1190
|
+
activatesOn: ActivationEvents.Startup,
|
|
1191
|
+
activate: () => Effect.succeed(Capability.contributes(String, { string: 'hello' })),
|
|
1192
|
+
}),
|
|
1193
|
+
Plugin.make,
|
|
1194
|
+
);
|
|
1195
|
+
const testPlugin = Test();
|
|
1196
|
+
plugins = [testPlugin];
|
|
1197
|
+
|
|
1198
|
+
const manager = PluginManager.make({ pluginLoader });
|
|
1199
|
+
yield* manager.add(testMeta.id);
|
|
1200
|
+
yield* manager.enable(testMeta.id);
|
|
1201
|
+
yield* manager.activate(ActivationEvents.Startup);
|
|
1202
|
+
|
|
1203
|
+
const pluginsBefore = manager.getPlugins();
|
|
1204
|
+
const coreBefore = manager.getCore();
|
|
1205
|
+
const enabledBefore = manager.getEnabled();
|
|
1206
|
+
const modulesBefore = manager.getModules();
|
|
1207
|
+
|
|
1208
|
+
yield* manager.shutdown();
|
|
1209
|
+
|
|
1210
|
+
assert.deepStrictEqual(manager.getPlugins(), pluginsBefore);
|
|
1211
|
+
assert.deepStrictEqual(manager.getCore(), coreBefore);
|
|
1212
|
+
assert.deepStrictEqual(manager.getEnabled(), enabledBefore);
|
|
1213
|
+
assert.deepStrictEqual(manager.getModules(), modulesBefore);
|
|
1214
|
+
}),
|
|
1215
|
+
);
|
|
1216
|
+
|
|
1217
|
+
it.effect('should allow re-activation after shutdown', () =>
|
|
1218
|
+
Effect.gen(function* () {
|
|
1219
|
+
let activateCount = 0;
|
|
1220
|
+
const Test = Plugin.define(testMeta).pipe(
|
|
1221
|
+
Plugin.addModule({
|
|
1222
|
+
id: 'Hello',
|
|
1223
|
+
activatesOn: ActivationEvents.Startup,
|
|
1224
|
+
activate: () => {
|
|
1225
|
+
activateCount++;
|
|
1226
|
+
return Effect.succeed(Capability.contributes(String, { string: 'hello' }));
|
|
1227
|
+
},
|
|
1228
|
+
}),
|
|
1229
|
+
Plugin.make,
|
|
1230
|
+
);
|
|
1231
|
+
const testPlugin = Test();
|
|
1232
|
+
plugins = [testPlugin];
|
|
1233
|
+
|
|
1234
|
+
const manager = PluginManager.make({ pluginLoader });
|
|
1235
|
+
yield* manager.add(testMeta.id);
|
|
1236
|
+
yield* manager.enable(testMeta.id);
|
|
1237
|
+
yield* manager.activate(ActivationEvents.Startup);
|
|
1238
|
+
assert.strictEqual(activateCount, 1);
|
|
1239
|
+
assert.deepStrictEqual(manager.getActive(), [testPlugin.modules[0].id]);
|
|
1240
|
+
|
|
1241
|
+
yield* manager.shutdown();
|
|
1242
|
+
assert.deepStrictEqual(manager.getActive(), []);
|
|
1243
|
+
|
|
1244
|
+
yield* manager.activate(ActivationEvents.Startup);
|
|
1245
|
+
assert.strictEqual(activateCount, 2);
|
|
1246
|
+
assert.deepStrictEqual(manager.getActive(), [testPlugin.modules[0].id]);
|
|
1247
|
+
assert.strictEqual(manager.capabilities.getAll(String).length, 1);
|
|
1248
|
+
}),
|
|
1249
|
+
);
|
|
1250
|
+
|
|
1251
|
+
describe('Plugin.lazy', () => {
|
|
1252
|
+
const lazyMeta = Plugin.makeMeta({ key: DXN.make('org.dxos.plugin.lazy'), name: 'Lazy' });
|
|
1253
|
+
|
|
1254
|
+
it('exposes meta synchronously without invoking the loader', () => {
|
|
1255
|
+
let loaderCalls = 0;
|
|
1256
|
+
const Real = Plugin.make(Plugin.define<void>(lazyMeta));
|
|
1257
|
+
const LazyTest = Plugin.lazy(lazyMeta, () => {
|
|
1258
|
+
loaderCalls++;
|
|
1259
|
+
return Promise.resolve({ default: Real });
|
|
1260
|
+
});
|
|
1261
|
+
|
|
1262
|
+
assert.strictEqual(LazyTest.meta.id, lazyMeta.id);
|
|
1263
|
+
assert.strictEqual(LazyTest.meta.name, 'Lazy');
|
|
1264
|
+
assert.strictEqual(loaderCalls, 0);
|
|
1265
|
+
|
|
1266
|
+
const stub = LazyTest();
|
|
1267
|
+
assert.strictEqual(stub.meta.id, lazyMeta.id);
|
|
1268
|
+
assert.deepStrictEqual([...stub.modules], []);
|
|
1269
|
+
assert.isTrue(Plugin.isLazy(stub));
|
|
1270
|
+
assert.strictEqual(loaderCalls, 0);
|
|
1271
|
+
});
|
|
1272
|
+
|
|
1273
|
+
it.effect('resolves the loader on enable and registers the real plugin modules', () =>
|
|
1274
|
+
Effect.gen(function* () {
|
|
1275
|
+
let loaderCalls = 0;
|
|
1276
|
+
const Real = Plugin.define(lazyMeta).pipe(
|
|
1277
|
+
Plugin.addModule({
|
|
1278
|
+
id: 'Hello',
|
|
1279
|
+
activatesOn: ActivationEvents.Startup,
|
|
1280
|
+
activate: () => Effect.succeed(Capability.contributes(String, { string: 'hello' })),
|
|
1281
|
+
}),
|
|
1282
|
+
Plugin.make,
|
|
1283
|
+
);
|
|
1284
|
+
const LazyTest = Plugin.lazy(lazyMeta, () => {
|
|
1285
|
+
loaderCalls++;
|
|
1286
|
+
return Promise.resolve({ default: Real });
|
|
1287
|
+
});
|
|
1288
|
+
|
|
1289
|
+
const lazyStub = LazyTest();
|
|
1290
|
+
plugins = [lazyStub];
|
|
1291
|
+
|
|
1292
|
+
const manager = PluginManager.make({ pluginLoader });
|
|
1293
|
+
yield* manager.add(lazyMeta.id);
|
|
1294
|
+
// Loader has not been invoked yet — only meta is exposed.
|
|
1295
|
+
assert.strictEqual(loaderCalls, 0);
|
|
1296
|
+
assert.deepStrictEqual(manager.getModules(), []);
|
|
1297
|
+
|
|
1298
|
+
yield* manager.enable(lazyMeta.id);
|
|
1299
|
+
assert.strictEqual(loaderCalls, 1);
|
|
1300
|
+
// After enable the registered plugin should be the real one (not the stub),
|
|
1301
|
+
// and its modules should be registered with the manager.
|
|
1302
|
+
const registered = manager.getPlugins().find((p) => p.meta.id === lazyMeta.id);
|
|
1303
|
+
assert.isDefined(registered);
|
|
1304
|
+
assert.isFalse(Plugin.isLazy(registered!));
|
|
1305
|
+
assert.strictEqual(registered!.modules.length, 1);
|
|
1306
|
+
|
|
1307
|
+
yield* manager.activate(ActivationEvents.Startup);
|
|
1308
|
+
assert.strictEqual(manager.capabilities.getAll(String).length, 1);
|
|
1309
|
+
}),
|
|
1310
|
+
);
|
|
1311
|
+
|
|
1312
|
+
it.effect('does not invoke the loader if the plugin is never enabled', () =>
|
|
1313
|
+
Effect.gen(function* () {
|
|
1314
|
+
let loaderCalls = 0;
|
|
1315
|
+
const Real = Plugin.make(Plugin.define<void>(lazyMeta));
|
|
1316
|
+
const LazyTest = Plugin.lazy(lazyMeta, () => {
|
|
1317
|
+
loaderCalls++;
|
|
1318
|
+
return Promise.resolve({ default: Real });
|
|
1319
|
+
});
|
|
1320
|
+
const lazyStub = LazyTest();
|
|
1321
|
+
plugins = [lazyStub];
|
|
1322
|
+
|
|
1323
|
+
const manager = PluginManager.make({ pluginLoader });
|
|
1324
|
+
yield* manager.add(lazyMeta.id);
|
|
1325
|
+
|
|
1326
|
+
// Activate an event that has no listeners — the lazy plugin must not load.
|
|
1327
|
+
yield* manager.activate(ActivationEvents.Startup);
|
|
1328
|
+
assert.strictEqual(loaderCalls, 0);
|
|
1329
|
+
}),
|
|
1330
|
+
);
|
|
1331
|
+
|
|
1332
|
+
it.effect('forwards factory options to the real plugin factory', () =>
|
|
1333
|
+
Effect.gen(function* () {
|
|
1334
|
+
type Opts = { greeting: string };
|
|
1335
|
+
const RealFactory = (opts: Opts) =>
|
|
1336
|
+
Plugin.define(lazyMeta).pipe(
|
|
1337
|
+
Plugin.addModule({
|
|
1338
|
+
id: 'Hello',
|
|
1339
|
+
activatesOn: ActivationEvents.Startup,
|
|
1340
|
+
activate: () => Effect.succeed(Capability.contributes(String, { string: opts.greeting })),
|
|
1341
|
+
}),
|
|
1342
|
+
Plugin.make,
|
|
1343
|
+
)(undefined as void);
|
|
1344
|
+
|
|
1345
|
+
const RealFactoryWithMeta = Object.assign(RealFactory, { meta: lazyMeta });
|
|
1346
|
+
|
|
1347
|
+
const LazyTest = Plugin.lazy<Opts>(lazyMeta, () => Promise.resolve({ default: RealFactoryWithMeta }));
|
|
1348
|
+
const lazyStub = LazyTest({ greeting: 'hola' });
|
|
1349
|
+
plugins = [lazyStub];
|
|
1350
|
+
|
|
1351
|
+
const manager = PluginManager.make({ pluginLoader });
|
|
1352
|
+
yield* manager.add(lazyMeta.id);
|
|
1353
|
+
yield* manager.enable(lazyMeta.id);
|
|
1354
|
+
yield* manager.activate(ActivationEvents.Startup);
|
|
1355
|
+
|
|
1356
|
+
const all = manager.capabilities.getAll(String);
|
|
1357
|
+
assert.strictEqual(all.length, 1);
|
|
1358
|
+
assert.strictEqual(all[0].string, 'hola');
|
|
1359
|
+
}),
|
|
1360
|
+
);
|
|
1361
|
+
|
|
1362
|
+
it.effect('wraps loader rejections in a descriptive error', () =>
|
|
1363
|
+
Effect.gen(function* () {
|
|
1364
|
+
const LazyTest = Plugin.lazy(lazyMeta, () =>
|
|
1365
|
+
Promise.reject<{ default: Plugin.PluginFactory }>(new Error('boom')),
|
|
1366
|
+
);
|
|
1367
|
+
const lazyStub = LazyTest();
|
|
1368
|
+
plugins = [lazyStub];
|
|
1369
|
+
|
|
1370
|
+
const manager = PluginManager.make({ pluginLoader });
|
|
1371
|
+
yield* manager.add(lazyMeta.id);
|
|
1372
|
+
|
|
1373
|
+
const exit = yield* Effect.exit(manager.enable(lazyMeta.id));
|
|
1374
|
+
assert.isTrue(Exit.isFailure(exit));
|
|
1375
|
+
if (Exit.isFailure(exit)) {
|
|
1376
|
+
const failure = Cause.failureOption(exit.cause);
|
|
1377
|
+
assert.isTrue(failure._tag === 'Some');
|
|
1378
|
+
if (failure._tag === 'Some') {
|
|
1379
|
+
assert.isTrue(Plugin.LazyPluginError.is(failure.value));
|
|
1380
|
+
assert.strictEqual((failure.value as Plugin.LazyPluginError).context.id, lazyMeta.id);
|
|
1381
|
+
assert.strictEqual((failure.value as Plugin.LazyPluginError).context.reason, 'load-failed');
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
}),
|
|
1385
|
+
);
|
|
1386
|
+
|
|
1387
|
+
it.effect('publishes a lazy:<id> error message when resolution fails', () =>
|
|
1388
|
+
Effect.gen(function* () {
|
|
1389
|
+
const LazyTest = Plugin.lazy(lazyMeta, () =>
|
|
1390
|
+
Promise.reject<{ default: Plugin.PluginFactory }>(new Error('boom')),
|
|
1391
|
+
);
|
|
1392
|
+
const lazyStub = LazyTest();
|
|
1393
|
+
plugins = [lazyStub];
|
|
1394
|
+
|
|
1395
|
+
const manager = PluginManager.make({ pluginLoader });
|
|
1396
|
+
// Subscribe first so we don't miss the activating/error pair.
|
|
1397
|
+
const queue = yield* PubSub.subscribe(manager.activation);
|
|
1398
|
+
yield* manager.add(lazyMeta.id);
|
|
1399
|
+
yield* Effect.exit(manager.enable(lazyMeta.id));
|
|
1400
|
+
const messages = yield* Queue.takeAll(queue);
|
|
1401
|
+
|
|
1402
|
+
const errorMessage = [...messages].find((m) => m.module === `lazy:${lazyMeta.id}` && m.state === 'error');
|
|
1403
|
+
assert.isDefined(errorMessage);
|
|
1404
|
+
assert.isDefined(errorMessage!.error);
|
|
1405
|
+
}).pipe(Effect.scoped),
|
|
1406
|
+
);
|
|
1407
|
+
|
|
1408
|
+
it.effect('coalesces concurrent lazy resolutions of the same plugin id', () =>
|
|
1409
|
+
Effect.gen(function* () {
|
|
1410
|
+
let factoryCalls = 0;
|
|
1411
|
+
// System-tagged so the constructor's core derivation picks it up,
|
|
1412
|
+
// forcing an implicit enable that races the explicit one below.
|
|
1413
|
+
const coreLazyMeta = { ...lazyMeta, tags: ['system'] };
|
|
1414
|
+
const Real = (() => {
|
|
1415
|
+
const inner = Plugin.make(
|
|
1416
|
+
Plugin.define<void>(coreLazyMeta).pipe(
|
|
1417
|
+
Plugin.addModule({
|
|
1418
|
+
id: 'Hello',
|
|
1419
|
+
activatesOn: ActivationEvents.Startup,
|
|
1420
|
+
activate: () => Effect.succeed(Capability.contributes(String, { string: 'hello' })),
|
|
1421
|
+
}),
|
|
1422
|
+
),
|
|
1423
|
+
);
|
|
1424
|
+
const factory = (() => {
|
|
1425
|
+
factoryCalls++;
|
|
1426
|
+
return inner();
|
|
1427
|
+
}) as Plugin.PluginFactory;
|
|
1428
|
+
return Object.assign(factory, { meta: coreLazyMeta });
|
|
1429
|
+
})();
|
|
1430
|
+
const LazyTest = Plugin.lazy(coreLazyMeta, () => Promise.resolve({ default: Real }));
|
|
1431
|
+
const lazyStub = LazyTest();
|
|
1432
|
+
// `manager.enable(id)` is implicitly called twice — once from the
|
|
1433
|
+
// constructor's core/enabled chain, once from our explicit call. With
|
|
1434
|
+
// coalescing, the underlying factory should still run exactly once.
|
|
1435
|
+
plugins = [lazyStub];
|
|
1436
|
+
const manager = PluginManager.make({ pluginLoader, plugins });
|
|
1437
|
+
yield* manager.enable(coreLazyMeta.id);
|
|
1438
|
+
assert.strictEqual(factoryCalls, 1);
|
|
1439
|
+
}),
|
|
1440
|
+
);
|
|
1441
|
+
|
|
1442
|
+
it.effect('fails with a tagged error when the factory output is not a Plugin', () =>
|
|
1443
|
+
Effect.gen(function* () {
|
|
1444
|
+
const BadFactory = Object.assign(() => ({ not: 'a plugin' }) as any, { meta: lazyMeta });
|
|
1445
|
+
const LazyTest = Plugin.lazy(lazyMeta, () => Promise.resolve({ default: BadFactory }));
|
|
1446
|
+
const lazyStub = LazyTest();
|
|
1447
|
+
plugins = [lazyStub];
|
|
1448
|
+
|
|
1449
|
+
const manager = PluginManager.make({ pluginLoader });
|
|
1450
|
+
yield* manager.add(lazyMeta.id);
|
|
1451
|
+
|
|
1452
|
+
const exit = yield* Effect.exit(manager.enable(lazyMeta.id));
|
|
1453
|
+
assert.isTrue(Exit.isFailure(exit));
|
|
1454
|
+
if (Exit.isFailure(exit)) {
|
|
1455
|
+
const failure = Cause.failureOption(exit.cause);
|
|
1456
|
+
assert.isTrue(failure._tag === 'Some');
|
|
1457
|
+
if (failure._tag === 'Some') {
|
|
1458
|
+
assert.isTrue(Plugin.LazyPluginError.is(failure.value));
|
|
1459
|
+
assert.strictEqual((failure.value as Plugin.LazyPluginError).context.reason, 'invalid-plugin');
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
}),
|
|
1463
|
+
);
|
|
1464
|
+
});
|
|
1465
|
+
|
|
1466
|
+
describe('timeouts and failure tracking', () => {
|
|
1467
|
+
// Atom subscriptions fire synchronously when the registry's `_set` runs,
|
|
1468
|
+
// even from a forked fiber on the default runtime. Wrapping in
|
|
1469
|
+
// `Effect.async` lets a TestClock-driven test wait for state produced by
|
|
1470
|
+
// a background `_runForkedFiber` (e.g. the auto-disable triggered when a
|
|
1471
|
+
// module activation times out) without relying on real-time `sleep`.
|
|
1472
|
+
const waitFor = <T>(registry: Registry.Registry, atom: Atom.Atom<T>, predicate: (value: T) => boolean) =>
|
|
1473
|
+
Effect.async<void>((resume) => {
|
|
1474
|
+
if (predicate(registry.get(atom))) {
|
|
1475
|
+
resume(Effect.void);
|
|
1476
|
+
return;
|
|
1477
|
+
}
|
|
1478
|
+
let resolved = false;
|
|
1479
|
+
const dispose = registry.subscribe(atom, () => {
|
|
1480
|
+
if (!resolved && predicate(registry.get(atom))) {
|
|
1481
|
+
resolved = true;
|
|
1482
|
+
dispose();
|
|
1483
|
+
resume(Effect.void);
|
|
1484
|
+
}
|
|
1485
|
+
});
|
|
1486
|
+
return Effect.sync(() => {
|
|
1487
|
+
if (!resolved) {
|
|
1488
|
+
dispose();
|
|
1489
|
+
}
|
|
1490
|
+
});
|
|
1491
|
+
});
|
|
1492
|
+
|
|
1493
|
+
it.effect('records and auto-disables a plugin whose module exceeds the activation timeout', () =>
|
|
1494
|
+
Effect.gen(function* () {
|
|
1495
|
+
const SlowEvent = ActivationEvent.make('org.dxos.test.activation-timeout');
|
|
1496
|
+
const SlowPlugin = Plugin.define(
|
|
1497
|
+
Plugin.makeMeta({
|
|
1498
|
+
key: DXN.make('org.dxos.test.slowActivation'),
|
|
1499
|
+
name: 'Slow Activation',
|
|
1500
|
+
}),
|
|
1501
|
+
).pipe(
|
|
1502
|
+
Plugin.addModule({
|
|
1503
|
+
id: 'Slow',
|
|
1504
|
+
activatesOn: SlowEvent,
|
|
1505
|
+
activate: Effect.fnUntraced(function* () {
|
|
1506
|
+
yield* Effect.sleep(Duration.seconds(60));
|
|
1507
|
+
return Capability.contributes(String, { string: 'never' });
|
|
1508
|
+
}),
|
|
1509
|
+
}),
|
|
1510
|
+
Plugin.make,
|
|
1511
|
+
);
|
|
1512
|
+
plugins = [SlowPlugin()];
|
|
1513
|
+
|
|
1514
|
+
const registry = Registry.make();
|
|
1515
|
+
const manager = PluginManager.make({
|
|
1516
|
+
pluginLoader,
|
|
1517
|
+
registry,
|
|
1518
|
+
activationTimeout: Duration.seconds(2),
|
|
1519
|
+
});
|
|
1520
|
+
yield* manager.add(SlowPlugin.meta.id);
|
|
1521
|
+
yield* manager.enable(SlowPlugin.meta.id);
|
|
1522
|
+
|
|
1523
|
+
const fiber = yield* Effect.fork(manager.activate(SlowEvent));
|
|
1524
|
+
// Push past the 2s activation timeout. The forked module fiber is on
|
|
1525
|
+
// TestClock too, so the timeout fires deterministically.
|
|
1526
|
+
yield* TestClock.adjust(Duration.seconds(3));
|
|
1527
|
+
const exit = yield* Fiber.await(fiber);
|
|
1528
|
+
assert.isTrue(Exit.isFailure(exit));
|
|
1529
|
+
|
|
1530
|
+
const failed = manager.getFailed();
|
|
1531
|
+
assert.strictEqual(failed.length, 1);
|
|
1532
|
+
assert.strictEqual(failed[0].id, SlowPlugin.meta.id);
|
|
1533
|
+
assert.strictEqual(failed[0].phase, 'activation');
|
|
1534
|
+
assert.strictEqual(failed[0].reason, 'timeout');
|
|
1535
|
+
|
|
1536
|
+
// Auto-disable runs in a forked fiber on the default runtime; wait for
|
|
1537
|
+
// the `enabled` atom to settle to the disabled state.
|
|
1538
|
+
yield* waitFor(registry, manager.enabled, (ids) => !ids.includes(SlowPlugin.meta.id));
|
|
1539
|
+
assert.deepStrictEqual(manager.getEnabled(), []);
|
|
1540
|
+
}),
|
|
1541
|
+
);
|
|
1542
|
+
|
|
1543
|
+
it.effect('records and auto-disables a lazy plugin whose loader exceeds the load timeout', () =>
|
|
1544
|
+
Effect.gen(function* () {
|
|
1545
|
+
const lazyMeta = Plugin.makeMeta({ key: DXN.make('org.dxos.test.slowLoad'), name: 'Slow Load' });
|
|
1546
|
+
// The dynamic import never resolves; the manager's load timeout should
|
|
1547
|
+
// surface this as a `LazyPluginError` whose `cause` is `PluginTimeoutError`.
|
|
1548
|
+
const LazyTest = Plugin.lazy(lazyMeta, () => new Promise<{ default: Plugin.PluginFactory }>(() => {}));
|
|
1549
|
+
plugins = [LazyTest()];
|
|
1550
|
+
|
|
1551
|
+
const registry = Registry.make();
|
|
1552
|
+
const manager = PluginManager.make({
|
|
1553
|
+
pluginLoader,
|
|
1554
|
+
registry,
|
|
1555
|
+
loadTimeout: Duration.seconds(1),
|
|
1556
|
+
});
|
|
1557
|
+
yield* manager.add(lazyMeta.id);
|
|
1558
|
+
|
|
1559
|
+
const enableFiber = yield* Effect.fork(manager.enable(lazyMeta.id));
|
|
1560
|
+
yield* TestClock.adjust(Duration.seconds(2));
|
|
1561
|
+
const exit = yield* Fiber.await(enableFiber);
|
|
1562
|
+
assert.isTrue(Exit.isFailure(exit));
|
|
1563
|
+
|
|
1564
|
+
// The wrapped `LazyPluginError` carries the timeout error as its cause.
|
|
1565
|
+
if (Exit.isFailure(exit)) {
|
|
1566
|
+
const failure = Cause.failureOption(exit.cause);
|
|
1567
|
+
if (failure._tag === 'Some') {
|
|
1568
|
+
assert.isTrue(Plugin.LazyPluginError.is(failure.value));
|
|
1569
|
+
const lazyError = failure.value as Plugin.LazyPluginError;
|
|
1570
|
+
assert.isTrue(PluginManager.PluginTimeoutError.is(lazyError.cause as Error));
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
const failed = manager.getFailed();
|
|
1575
|
+
assert.strictEqual(failed.length, 1);
|
|
1576
|
+
assert.strictEqual(failed[0].id, lazyMeta.id);
|
|
1577
|
+
assert.strictEqual(failed[0].phase, 'load');
|
|
1578
|
+
assert.strictEqual(failed[0].reason, 'timeout');
|
|
1579
|
+
|
|
1580
|
+
// The plugin was added to `enabled` before the lazy resolution failed,
|
|
1581
|
+
// so the auto-disable fork should clear it.
|
|
1582
|
+
yield* waitFor(registry, manager.enabled, (ids) => !ids.includes(lazyMeta.id));
|
|
1583
|
+
}),
|
|
1584
|
+
);
|
|
1585
|
+
|
|
1586
|
+
it.effect('records non-timeout activation errors as reason: error', () =>
|
|
1587
|
+
Effect.gen(function* () {
|
|
1588
|
+
const FailingEvent = ActivationEvent.make('org.dxos.test.activation-error');
|
|
1589
|
+
const FailingPlugin = Plugin.define(
|
|
1590
|
+
Plugin.makeMeta({ key: DXN.make('org.dxos.test.failing'), name: 'Failing' }),
|
|
1591
|
+
).pipe(
|
|
1592
|
+
Plugin.addModule({
|
|
1593
|
+
id: 'Boom',
|
|
1594
|
+
activatesOn: FailingEvent,
|
|
1595
|
+
activate: () => Effect.fail(new Error('boom')),
|
|
1596
|
+
}),
|
|
1597
|
+
Plugin.make,
|
|
1598
|
+
);
|
|
1599
|
+
plugins = [FailingPlugin()];
|
|
1600
|
+
|
|
1601
|
+
const registry = Registry.make();
|
|
1602
|
+
const manager = PluginManager.make({ pluginLoader, registry });
|
|
1603
|
+
yield* manager.add(FailingPlugin.meta.id);
|
|
1604
|
+
yield* manager.enable(FailingPlugin.meta.id);
|
|
1605
|
+
|
|
1606
|
+
const exit = yield* Effect.exit(manager.activate(FailingEvent));
|
|
1607
|
+
assert.isTrue(Exit.isFailure(exit));
|
|
1608
|
+
|
|
1609
|
+
const failed = manager.getFailed();
|
|
1610
|
+
assert.strictEqual(failed.length, 1);
|
|
1611
|
+
assert.strictEqual(failed[0].reason, 'error');
|
|
1612
|
+
assert.strictEqual(failed[0].error.message, 'boom');
|
|
1613
|
+
|
|
1614
|
+
yield* waitFor(registry, manager.enabled, (ids) => !ids.includes(FailingPlugin.meta.id));
|
|
1615
|
+
}),
|
|
1616
|
+
);
|
|
1617
|
+
|
|
1618
|
+
it.effect('does not auto-disable a core plugin even though the failure is recorded', () =>
|
|
1619
|
+
Effect.gen(function* () {
|
|
1620
|
+
const FailingEvent = ActivationEvent.make('org.dxos.test.core-fail');
|
|
1621
|
+
const CorePlugin = Plugin.define(
|
|
1622
|
+
Plugin.makeMeta({ key: DXN.make('org.dxos.test.core'), name: 'Core', tags: ['system'] }),
|
|
1623
|
+
).pipe(
|
|
1624
|
+
Plugin.addModule({
|
|
1625
|
+
id: 'Boom',
|
|
1626
|
+
activatesOn: FailingEvent,
|
|
1627
|
+
activate: () => Effect.fail(new Error('boom')),
|
|
1628
|
+
}),
|
|
1629
|
+
Plugin.make,
|
|
1630
|
+
);
|
|
1631
|
+
const corePlugin = CorePlugin();
|
|
1632
|
+
plugins = [corePlugin];
|
|
1633
|
+
|
|
1634
|
+
const manager = PluginManager.make({
|
|
1635
|
+
pluginLoader,
|
|
1636
|
+
plugins: [corePlugin],
|
|
1637
|
+
});
|
|
1638
|
+
// Core is auto-enabled via the constructor's enable chain.
|
|
1639
|
+
const exit = yield* Effect.exit(manager.activate(FailingEvent));
|
|
1640
|
+
assert.isTrue(Exit.isFailure(exit));
|
|
1641
|
+
|
|
1642
|
+
assert.strictEqual(manager.getFailed().length, 1);
|
|
1643
|
+
// Core stays enabled; host opted into it being non-removable.
|
|
1644
|
+
assert.deepStrictEqual(manager.getEnabled(), [corePlugin.meta.id]);
|
|
1645
|
+
}),
|
|
1646
|
+
);
|
|
1647
|
+
|
|
1648
|
+
it.effect('clearFailure removes the failure record and re-enable starts fresh', () =>
|
|
1649
|
+
Effect.gen(function* () {
|
|
1650
|
+
let shouldFail = true;
|
|
1651
|
+
const Event = ActivationEvent.make('org.dxos.test.flaky');
|
|
1652
|
+
const FlakyPlugin = Plugin.define(
|
|
1653
|
+
Plugin.makeMeta({ key: DXN.make('org.dxos.test.flaky'), name: 'Flaky' }),
|
|
1654
|
+
).pipe(
|
|
1655
|
+
Plugin.addModule({
|
|
1656
|
+
id: 'Maybe',
|
|
1657
|
+
activatesOn: Event,
|
|
1658
|
+
activate: () =>
|
|
1659
|
+
shouldFail
|
|
1660
|
+
? Effect.fail(new Error('first try'))
|
|
1661
|
+
: Effect.succeed(Capability.contributes(String, { string: 'ok' })),
|
|
1662
|
+
}),
|
|
1663
|
+
Plugin.make,
|
|
1664
|
+
);
|
|
1665
|
+
const flakyPlugin = FlakyPlugin();
|
|
1666
|
+
plugins = [flakyPlugin];
|
|
1667
|
+
|
|
1668
|
+
const registry = Registry.make();
|
|
1669
|
+
const manager = PluginManager.make({ pluginLoader, registry });
|
|
1670
|
+
yield* manager.add(flakyPlugin.meta.id);
|
|
1671
|
+
yield* manager.enable(flakyPlugin.meta.id);
|
|
1672
|
+
|
|
1673
|
+
yield* Effect.exit(manager.activate(Event));
|
|
1674
|
+
assert.strictEqual(manager.getFailed().length, 1);
|
|
1675
|
+
yield* waitFor(registry, manager.enabled, (ids) => !ids.includes(flakyPlugin.meta.id));
|
|
1676
|
+
|
|
1677
|
+
// Calling `enable` again clears the prior failure record before
|
|
1678
|
+
// attempting resolution; verify the explicit API does too.
|
|
1679
|
+
assert.isTrue(manager.clearFailure(flakyPlugin.meta.id));
|
|
1680
|
+
assert.strictEqual(manager.getFailed().length, 0);
|
|
1681
|
+
assert.isFalse(manager.clearFailure(flakyPlugin.meta.id));
|
|
1682
|
+
|
|
1683
|
+
// Retry: enable + reset the activation event so the module re-runs.
|
|
1684
|
+
shouldFail = false;
|
|
1685
|
+
yield* manager.enable(flakyPlugin.meta.id);
|
|
1686
|
+
yield* manager.reset(Event);
|
|
1687
|
+
assert.strictEqual(manager.getFailed().length, 0);
|
|
1688
|
+
assert.strictEqual(manager.capabilities.getAll(String).length, 1);
|
|
1689
|
+
}),
|
|
1690
|
+
);
|
|
1691
|
+
});
|
|
1692
|
+
|
|
1693
|
+
describe('plugin dependencies (dependsOn)', () => {
|
|
1694
|
+
// Build a small plugin with a `dependsOn` chain. The helper keeps each test
|
|
1695
|
+
// focused on the dependency semantics rather than module wiring.
|
|
1696
|
+
// These dependency-graph tests use short opaque ids (`'a'`, `'coreClient'`,
|
|
1697
|
+
// `'org.dxos.missing'`) as graph keys rather than real DXNs. `id`/`dependsOn` are
|
|
1698
|
+
// bare strings; only `key` (an unused DXN here) is cast at this test-only boundary.
|
|
1699
|
+
const makePlugin = (id: string, dependsOn?: string[], tags?: string[]) =>
|
|
1700
|
+
Plugin.make(
|
|
1701
|
+
Plugin.define({
|
|
1702
|
+
id,
|
|
1703
|
+
key: id as unknown as DXN.DXN,
|
|
1704
|
+
name: id,
|
|
1705
|
+
dependsOn,
|
|
1706
|
+
tags,
|
|
1707
|
+
}),
|
|
1708
|
+
)();
|
|
1709
|
+
|
|
1710
|
+
it.effect('enable resolves the transitive closure in dependency-first order', () =>
|
|
1711
|
+
Effect.gen(function* () {
|
|
1712
|
+
const a = makePlugin('a');
|
|
1713
|
+
const b = makePlugin('b', ['a']);
|
|
1714
|
+
const c = makePlugin('c', ['b']);
|
|
1715
|
+
const manager = PluginManager.make({ plugins: [a, b, c], pluginLoader });
|
|
1716
|
+
|
|
1717
|
+
const ok = yield* manager.enable('c');
|
|
1718
|
+
assert.isTrue(ok);
|
|
1719
|
+
// `a` enables first, then `b`, then `c`.
|
|
1720
|
+
assert.deepStrictEqual(manager.getEnabled(), ['a', 'b', 'c']);
|
|
1721
|
+
}),
|
|
1722
|
+
);
|
|
1723
|
+
|
|
1724
|
+
it.effect('enable is idempotent when dependencies are already enabled', () =>
|
|
1725
|
+
Effect.gen(function* () {
|
|
1726
|
+
const a = makePlugin('a');
|
|
1727
|
+
const b = makePlugin('b', ['a']);
|
|
1728
|
+
const manager = PluginManager.make({ plugins: [a, b], pluginLoader });
|
|
1729
|
+
|
|
1730
|
+
yield* manager.enable('a');
|
|
1731
|
+
yield* manager.enable('b');
|
|
1732
|
+
assert.deepStrictEqual(manager.getEnabled(), ['a', 'b']);
|
|
1733
|
+
// Re-enabling shouldn't duplicate entries.
|
|
1734
|
+
yield* manager.enable('b');
|
|
1735
|
+
assert.deepStrictEqual(manager.getEnabled(), ['a', 'b']);
|
|
1736
|
+
}),
|
|
1737
|
+
);
|
|
1738
|
+
|
|
1739
|
+
it.effect('enable with a missing declared dependency records a PluginDependencyError', () =>
|
|
1740
|
+
Effect.gen(function* () {
|
|
1741
|
+
const dependent = makePlugin('dependent', ['org.dxos.missing']);
|
|
1742
|
+
const manager = PluginManager.make({ plugins: [dependent], pluginLoader });
|
|
1743
|
+
|
|
1744
|
+
const ok = yield* manager.enable('dependent');
|
|
1745
|
+
assert.isFalse(ok);
|
|
1746
|
+
assert.deepStrictEqual(manager.getEnabled(), []);
|
|
1747
|
+
const failures = manager.getFailed();
|
|
1748
|
+
assert.strictEqual(failures.length, 1);
|
|
1749
|
+
assert.strictEqual(failures[0].id, 'dependent');
|
|
1750
|
+
assert.instanceOf(failures[0].error, Plugin.PluginDependencyError);
|
|
1751
|
+
}),
|
|
1752
|
+
);
|
|
1753
|
+
|
|
1754
|
+
it.effect('enable detects A↔B cycle and records a cycle failure', () =>
|
|
1755
|
+
Effect.gen(function* () {
|
|
1756
|
+
const a = makePlugin('a', ['b']);
|
|
1757
|
+
const b = makePlugin('b', ['a']);
|
|
1758
|
+
const manager = PluginManager.make({ plugins: [a, b], pluginLoader });
|
|
1759
|
+
|
|
1760
|
+
const ok = yield* manager.enable('a');
|
|
1761
|
+
assert.isFalse(ok);
|
|
1762
|
+
assert.deepStrictEqual(manager.getEnabled(), []);
|
|
1763
|
+
const failures = manager.getFailed();
|
|
1764
|
+
assert.strictEqual(failures.length, 1);
|
|
1765
|
+
assert.instanceOf(failures[0].error, Plugin.PluginDependencyError);
|
|
1766
|
+
}),
|
|
1767
|
+
);
|
|
1768
|
+
|
|
1769
|
+
it.effect('enable with resolveDependencies: false skips closure walk and missing-dep check', () =>
|
|
1770
|
+
Effect.gen(function* () {
|
|
1771
|
+
// Dependent declares a dep that is intentionally unregistered — the
|
|
1772
|
+
// caller has accepted responsibility for satisfying it some other way.
|
|
1773
|
+
const dependent = makePlugin('dependent', ['org.dxos.alt-impl']);
|
|
1774
|
+
const manager = PluginManager.make({ plugins: [dependent], pluginLoader });
|
|
1775
|
+
|
|
1776
|
+
const ok = yield* manager.enable('dependent', { resolveDependencies: false });
|
|
1777
|
+
assert.isTrue(ok);
|
|
1778
|
+
assert.deepStrictEqual(manager.getEnabled(), ['dependent']);
|
|
1779
|
+
assert.strictEqual(manager.getFailed().length, 0);
|
|
1780
|
+
}),
|
|
1781
|
+
);
|
|
1782
|
+
|
|
1783
|
+
it.effect('disable cascades to transitive dependents by default', () =>
|
|
1784
|
+
Effect.gen(function* () {
|
|
1785
|
+
const a = makePlugin('a');
|
|
1786
|
+
const b = makePlugin('b', ['a']);
|
|
1787
|
+
const c = makePlugin('c', ['b']);
|
|
1788
|
+
const manager = PluginManager.make({ plugins: [a, b, c], pluginLoader });
|
|
1789
|
+
|
|
1790
|
+
yield* manager.enable('c');
|
|
1791
|
+
assert.deepStrictEqual(manager.getEnabled(), ['a', 'b', 'c']);
|
|
1792
|
+
|
|
1793
|
+
const ok = yield* manager.disable('a');
|
|
1794
|
+
assert.isTrue(ok);
|
|
1795
|
+
// Cascade tears down `c` (leaf) and `b` before `a`.
|
|
1796
|
+
assert.deepStrictEqual(manager.getEnabled(), []);
|
|
1797
|
+
}),
|
|
1798
|
+
);
|
|
1799
|
+
|
|
1800
|
+
it.effect('default disable refuses when a transitive dependent is core', () =>
|
|
1801
|
+
Effect.gen(function* () {
|
|
1802
|
+
const lib = makePlugin('lib');
|
|
1803
|
+
const coreClient = makePlugin('coreClient', ['lib'], ['system']);
|
|
1804
|
+
const manager = PluginManager.make({
|
|
1805
|
+
plugins: [lib, coreClient],
|
|
1806
|
+
enabled: ['lib'],
|
|
1807
|
+
pluginLoader,
|
|
1808
|
+
});
|
|
1809
|
+
|
|
1810
|
+
const exit = yield* Effect.exit(manager.disable('lib'));
|
|
1811
|
+
assert.isTrue(Exit.isFailure(exit));
|
|
1812
|
+
// No state mutation when cascade is refused for a core dependent.
|
|
1813
|
+
assert.isTrue(manager.getEnabled().includes('lib'));
|
|
1814
|
+
assert.isTrue(manager.getEnabled().includes('coreClient'));
|
|
1815
|
+
}),
|
|
1816
|
+
);
|
|
1817
|
+
|
|
1818
|
+
it.effect('disable with cascade: false disables only the target', () =>
|
|
1819
|
+
Effect.gen(function* () {
|
|
1820
|
+
const a = makePlugin('a');
|
|
1821
|
+
const b = makePlugin('b', ['a']);
|
|
1822
|
+
const manager = PluginManager.make({ plugins: [a, b], pluginLoader });
|
|
1823
|
+
|
|
1824
|
+
yield* manager.enable('b');
|
|
1825
|
+
const ok = yield* manager.disable('a', { cascade: false });
|
|
1826
|
+
assert.isTrue(ok);
|
|
1827
|
+
// `b` is left enabled-but-broken (no `a` to satisfy its declared dep).
|
|
1828
|
+
assert.deepStrictEqual(manager.getEnabled(), ['b']);
|
|
1829
|
+
}),
|
|
1830
|
+
);
|
|
1831
|
+
|
|
1832
|
+
it.effect('getDependencies and getDependents reflect the declared graph', () =>
|
|
1833
|
+
Effect.gen(function* () {
|
|
1834
|
+
const a = makePlugin('a');
|
|
1835
|
+
const b = makePlugin('b', ['a']);
|
|
1836
|
+
const c = makePlugin('c', ['b']);
|
|
1837
|
+
const manager = PluginManager.make({ plugins: [a, b, c], pluginLoader });
|
|
1838
|
+
|
|
1839
|
+
assert.deepStrictEqual([...manager.getDependencies('c', { transitive: false })], ['b']);
|
|
1840
|
+
assert.deepStrictEqual([...manager.getDependencies('c', { transitive: true })], ['a', 'b']);
|
|
1841
|
+
|
|
1842
|
+
assert.deepStrictEqual([...manager.getDependents('a', { transitive: false })], ['b']);
|
|
1843
|
+
assert.deepStrictEqual([...manager.getDependents('a', { transitive: true })], ['c', 'b']);
|
|
1844
|
+
|
|
1845
|
+
yield* manager.enable('b');
|
|
1846
|
+
assert.deepStrictEqual([...manager.getDependents('a', { transitive: true, enabledOnly: true })], ['b']);
|
|
1847
|
+
}),
|
|
1848
|
+
);
|
|
1849
|
+
|
|
1850
|
+
it.effect('enable installs a catalog-only dependency via add() before enabling it', () =>
|
|
1851
|
+
Effect.gen(function* () {
|
|
1852
|
+
// The "remote" plugin is not pre-registered; the loader knows about
|
|
1853
|
+
// it, simulating a fetch from the registry.
|
|
1854
|
+
const remote = makePlugin('remote');
|
|
1855
|
+
const dependent = makePlugin('dependent', ['remote']);
|
|
1856
|
+
const remoteLoader = Effect.fn(function* (id: string) {
|
|
1857
|
+
if (id === 'remote') {
|
|
1858
|
+
return { plugin: remote };
|
|
1859
|
+
}
|
|
1860
|
+
throw new Error(`Unknown id: ${id}`);
|
|
1861
|
+
});
|
|
1862
|
+
|
|
1863
|
+
const registry = Registry.make();
|
|
1864
|
+
const manager = PluginManager.make({
|
|
1865
|
+
plugins: [dependent],
|
|
1866
|
+
pluginLoader: remoteLoader,
|
|
1867
|
+
registry,
|
|
1868
|
+
});
|
|
1869
|
+
// Seed the catalog with the remote entry so the dependency walk can
|
|
1870
|
+
// discover it.
|
|
1871
|
+
registry.set(manager.pluginRegistry.plugins, {
|
|
1872
|
+
entries: [
|
|
1873
|
+
{
|
|
1874
|
+
id: 'remote',
|
|
1875
|
+
name: 'Remote',
|
|
1876
|
+
moduleUrl: 'about:blank',
|
|
1877
|
+
repo: 'example/remote',
|
|
1878
|
+
version: 'v0.0.0',
|
|
1879
|
+
},
|
|
1880
|
+
],
|
|
1881
|
+
loading: false,
|
|
1882
|
+
error: null,
|
|
1883
|
+
});
|
|
1884
|
+
|
|
1885
|
+
const ok = yield* manager.enable('dependent');
|
|
1886
|
+
assert.isTrue(ok);
|
|
1887
|
+
assert.deepStrictEqual(manager.getEnabled(), ['remote', 'dependent']);
|
|
1888
|
+
assert.isTrue(manager.getPlugins().some((plugin) => plugin.meta.id === 'remote'));
|
|
1889
|
+
}),
|
|
1890
|
+
);
|
|
1891
|
+
|
|
1892
|
+
it.effect('enable records install-failed when a catalog-only dep fails to load', () =>
|
|
1893
|
+
Effect.gen(function* () {
|
|
1894
|
+
const dependent = makePlugin('dependent', ['remote-broken']);
|
|
1895
|
+
const failingLoader = Effect.fn(function* (_id: string) {
|
|
1896
|
+
return yield* Effect.fail(new Error('fetch failed'));
|
|
1897
|
+
});
|
|
1898
|
+
|
|
1899
|
+
const registry = Registry.make();
|
|
1900
|
+
const manager = PluginManager.make({
|
|
1901
|
+
plugins: [dependent],
|
|
1902
|
+
pluginLoader: failingLoader,
|
|
1903
|
+
registry,
|
|
1904
|
+
});
|
|
1905
|
+
registry.set(manager.pluginRegistry.plugins, {
|
|
1906
|
+
entries: [
|
|
1907
|
+
{
|
|
1908
|
+
id: 'remote-broken',
|
|
1909
|
+
name: 'Broken',
|
|
1910
|
+
moduleUrl: 'about:blank',
|
|
1911
|
+
repo: 'example/broken',
|
|
1912
|
+
version: 'v0.0.0',
|
|
1913
|
+
},
|
|
1914
|
+
],
|
|
1915
|
+
loading: false,
|
|
1916
|
+
error: null,
|
|
1917
|
+
});
|
|
1918
|
+
|
|
1919
|
+
const ok = yield* manager.enable('dependent');
|
|
1920
|
+
assert.isFalse(ok);
|
|
1921
|
+
assert.deepStrictEqual(manager.getEnabled(), []);
|
|
1922
|
+
const failures = manager.getFailed();
|
|
1923
|
+
assert.strictEqual(failures.length, 1);
|
|
1924
|
+
assert.strictEqual(failures[0].id, 'dependent');
|
|
1925
|
+
assert.instanceOf(failures[0].error, Plugin.PluginDependencyError);
|
|
1926
|
+
}),
|
|
1927
|
+
);
|
|
1928
|
+
});
|
|
845
1929
|
});
|