@dxos/app-framework 0.7.5-main.9cb18ac → 0.7.5-main.9d2a38b
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/dist/lib/browser/chunk-GNLU3GAU.mjs +1595 -0
- package/dist/lib/browser/chunk-GNLU3GAU.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +112 -618
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +67 -0
- package/dist/lib/browser/testing/index.mjs.map +7 -0
- package/dist/lib/node/chunk-FBA4BB3J.cjs +1639 -0
- package/dist/lib/node/chunk-FBA4BB3J.cjs.map +7 -0
- package/dist/lib/node/index.cjs +123 -651
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +91 -0
- package/dist/lib/node/testing/index.cjs.map +7 -0
- package/dist/lib/node-esm/chunk-KPMTPXQI.mjs +1597 -0
- package/dist/lib/node-esm/chunk-KPMTPXQI.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +112 -618
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +68 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/types/src/App.d.ts +22 -13
- package/dist/types/src/App.d.ts.map +1 -1
- package/dist/types/src/common/capabilities.d.ts +340 -0
- package/dist/types/src/common/capabilities.d.ts.map +1 -0
- package/dist/types/src/common/events.d.ts +41 -0
- package/dist/types/src/common/events.d.ts.map +1 -0
- package/dist/types/src/common/file.d.ts +14 -0
- package/dist/types/src/common/file.d.ts.map +1 -0
- package/dist/types/src/common/graph.d.ts +21 -0
- package/dist/types/src/common/graph.d.ts.map +1 -0
- package/dist/types/src/{plugins/common → common}/index.d.ts +3 -1
- package/dist/types/src/common/index.d.ts.map +1 -0
- package/dist/types/src/{plugins/common → common}/layout.d.ts +0 -11
- package/dist/types/src/common/layout.d.ts.map +1 -0
- package/dist/types/src/{plugins/common → common}/navigation.d.ts +0 -2
- package/dist/types/src/common/navigation.d.ts.map +1 -0
- package/dist/types/src/{plugins/plugin-surface/SurfaceContext.d.ts → common/surface.d.ts} +12 -29
- package/dist/types/src/common/surface.d.ts.map +1 -0
- package/dist/types/src/{plugins/common → common}/translations.d.ts +0 -11
- package/dist/types/src/common/translations.d.ts.map +1 -0
- package/dist/types/src/core/capabilities.d.ts +90 -0
- package/dist/types/src/core/capabilities.d.ts.map +1 -0
- package/dist/types/src/core/capabilities.test.d.ts +2 -0
- package/dist/types/src/core/capabilities.test.d.ts.map +1 -0
- package/dist/types/src/core/events.d.ts +58 -0
- package/dist/types/src/core/events.d.ts.map +1 -0
- package/dist/types/src/core/index.d.ts +5 -0
- package/dist/types/src/core/index.d.ts.map +1 -0
- package/dist/types/src/core/manager.d.ts +126 -0
- package/dist/types/src/core/manager.d.ts.map +1 -0
- package/dist/types/src/core/manager.test.d.ts +2 -0
- package/dist/types/src/core/manager.test.d.ts.map +1 -0
- package/dist/types/src/core/plugin.d.ts +94 -0
- package/dist/types/src/core/plugin.d.ts.map +1 -0
- package/dist/types/src/helpers.d.ts +10 -0
- package/dist/types/src/helpers.d.ts.map +1 -0
- package/dist/types/src/helpers.test.d.ts +2 -0
- package/dist/types/src/helpers.test.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +5 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/playground/debug/Debug.d.ts +6 -0
- package/dist/types/src/playground/debug/Debug.d.ts.map +1 -0
- package/dist/types/src/playground/debug/index.d.ts +2 -0
- package/dist/types/src/playground/debug/index.d.ts.map +1 -0
- package/dist/types/src/playground/debug/plugin.d.ts +2 -0
- package/dist/types/src/playground/debug/plugin.d.ts.map +1 -0
- package/dist/types/src/playground/generator/Main.d.ts +6 -0
- package/dist/types/src/playground/generator/Main.d.ts.map +1 -0
- package/dist/types/src/playground/generator/Toolbar.d.ts +6 -0
- package/dist/types/src/playground/generator/Toolbar.d.ts.map +1 -0
- package/dist/types/src/playground/generator/generator.d.ts +5 -0
- package/dist/types/src/playground/generator/generator.d.ts.map +1 -0
- package/dist/types/src/playground/generator/index.d.ts +3 -0
- package/dist/types/src/playground/generator/index.d.ts.map +1 -0
- package/dist/types/src/playground/generator/plugin.d.ts +2 -0
- package/dist/types/src/playground/generator/plugin.d.ts.map +1 -0
- package/dist/types/src/playground/layout/Layout.d.ts +8 -0
- package/dist/types/src/playground/layout/Layout.d.ts.map +1 -0
- package/dist/types/src/playground/layout/index.d.ts +2 -0
- package/dist/types/src/playground/layout/index.d.ts.map +1 -0
- package/dist/types/src/playground/layout/plugin.d.ts +2 -0
- package/dist/types/src/playground/layout/plugin.d.ts.map +1 -0
- package/dist/types/src/playground/logger/Toolbar.d.ts +6 -0
- package/dist/types/src/playground/logger/Toolbar.d.ts.map +1 -0
- package/dist/types/src/playground/logger/index.d.ts +2 -0
- package/dist/types/src/playground/logger/index.d.ts.map +1 -0
- package/dist/types/src/playground/logger/plugin.d.ts +2 -0
- package/dist/types/src/playground/logger/plugin.d.ts.map +1 -0
- package/dist/types/src/playground/logger/schema.d.ts +13 -0
- package/dist/types/src/playground/logger/schema.d.ts.map +1 -0
- package/dist/types/src/playground/playground.stories.d.ts +10 -0
- package/dist/types/src/playground/playground.stories.d.ts.map +1 -0
- package/dist/types/src/{plugins/plugin-intent → plugin-intent}/IntentContext.d.ts +1 -1
- package/dist/types/src/plugin-intent/IntentContext.d.ts.map +1 -0
- package/dist/types/src/plugin-intent/IntentPlugin.d.ts +2 -0
- package/dist/types/src/plugin-intent/IntentPlugin.d.ts.map +1 -0
- package/dist/types/src/plugin-intent/actions.d.ts +23 -0
- package/dist/types/src/plugin-intent/actions.d.ts.map +1 -0
- package/dist/types/src/plugin-intent/errors.d.ts +16 -0
- package/dist/types/src/plugin-intent/errors.d.ts.map +1 -0
- package/dist/types/src/plugin-intent/index.d.ts +6 -0
- package/dist/types/src/plugin-intent/index.d.ts.map +1 -0
- package/dist/types/src/{plugins/plugin-intent → plugin-intent}/intent-dispatcher.d.ts +18 -19
- package/dist/types/src/plugin-intent/intent-dispatcher.d.ts.map +1 -0
- package/dist/types/src/plugin-intent/intent-dispatcher.test.d.ts.map +1 -0
- package/dist/types/src/{plugins/plugin-intent → plugin-intent}/intent.d.ts +5 -25
- package/dist/types/src/plugin-intent/intent.d.ts.map +1 -0
- package/dist/types/src/plugin-settings/SettingsPlugin.d.ts +2 -0
- package/dist/types/src/plugin-settings/SettingsPlugin.d.ts.map +1 -0
- package/dist/types/src/{plugins/plugin-settings/provides.d.ts → plugin-settings/actions.d.ts} +1 -10
- package/dist/types/src/plugin-settings/actions.d.ts.map +1 -0
- package/dist/types/src/plugin-settings/index.d.ts +3 -0
- package/dist/types/src/plugin-settings/index.d.ts.map +1 -0
- package/dist/types/src/react/ErrorBoundary.d.ts.map +1 -0
- package/dist/types/src/react/PluginManagerProvider.d.ts +10 -0
- package/dist/types/src/react/PluginManagerProvider.d.ts.map +1 -0
- package/dist/types/src/react/Surface.d.ts +12 -0
- package/dist/types/src/react/Surface.d.ts.map +1 -0
- package/dist/types/src/react/index.d.ts +5 -0
- package/dist/types/src/react/index.d.ts.map +1 -0
- package/dist/types/src/react/useCapabilities.d.ts +13 -0
- package/dist/types/src/react/useCapabilities.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/withPluginManager.d.ts +10 -0
- package/dist/types/src/testing/withPluginManager.d.ts.map +1 -0
- package/dist/types/src/testing/withPluginManager.stories.d.ts +5 -0
- package/dist/types/src/testing/withPluginManager.stories.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +28 -15
- package/project.json +2 -1
- package/src/App.tsx +138 -34
- package/src/common/capabilities.ts +82 -0
- package/src/common/events.ts +63 -0
- package/src/common/file.ts +22 -0
- package/src/common/graph.ts +30 -0
- package/src/{plugins/common → common}/index.ts +3 -1
- package/src/{plugins/common → common}/layout.ts +0 -16
- package/src/{plugins/common → common}/navigation.ts +0 -15
- package/src/{plugins/plugin-surface/SurfaceContext.ts → common/surface.ts} +6 -37
- package/src/common/translations.ts +18 -0
- package/src/core/capabilities.test.ts +116 -0
- package/src/core/capabilities.ts +213 -0
- package/src/core/events.ts +58 -0
- package/src/core/index.ts +8 -0
- package/src/core/manager.test.ts +567 -0
- package/src/core/manager.ts +497 -0
- package/src/core/plugin.ts +128 -0
- package/src/helpers.test.ts +97 -0
- package/src/helpers.ts +45 -0
- package/src/index.ts +6 -3
- package/src/playground/debug/Debug.tsx +39 -0
- package/src/playground/debug/index.ts +5 -0
- package/src/playground/debug/plugin.ts +17 -0
- package/src/playground/generator/Main.tsx +71 -0
- package/src/playground/generator/Toolbar.tsx +46 -0
- package/src/playground/generator/generator.ts +23 -0
- package/src/playground/generator/index.ts +6 -0
- package/src/playground/generator/plugin.ts +18 -0
- package/src/playground/layout/Layout.tsx +33 -0
- package/src/playground/layout/index.ts +5 -0
- package/src/playground/layout/plugin.ts +17 -0
- package/src/playground/logger/Toolbar.tsx +28 -0
- package/src/playground/logger/index.ts +5 -0
- package/src/playground/logger/plugin.ts +33 -0
- package/src/playground/logger/schema.ts +12 -0
- package/src/playground/playground.stories.tsx +34 -0
- package/src/{plugins/plugin-intent → plugin-intent}/IntentContext.tsx +12 -4
- package/src/plugin-intent/IntentPlugin.tsx +60 -0
- package/src/plugin-intent/actions.ts +22 -0
- package/src/plugin-intent/errors.ts +39 -0
- package/src/{plugins/plugin-intent → plugin-intent}/index.ts +2 -6
- package/src/{plugins/plugin-intent → plugin-intent}/intent-dispatcher.test.ts +36 -35
- package/src/{plugins/plugin-intent → plugin-intent}/intent-dispatcher.ts +49 -65
- package/src/{plugins/plugin-intent → plugin-intent}/intent.ts +5 -21
- package/src/plugin-settings/SettingsPlugin.ts +29 -0
- package/src/plugin-settings/actions.ts +17 -0
- package/src/plugin-settings/index.ts +6 -0
- package/src/react/PluginManagerProvider.ts +22 -0
- package/src/react/Surface.tsx +72 -0
- package/src/react/index.ts +8 -0
- package/src/react/useCapabilities.ts +39 -0
- package/src/testing/index.ts +5 -0
- package/src/testing/withPluginManager.stories.tsx +47 -0
- package/src/testing/withPluginManager.tsx +67 -0
- package/tsconfig.json +13 -1
- package/dist/lib/browser/chunk-3E7RY3CE.mjs +0 -72
- package/dist/lib/browser/chunk-3E7RY3CE.mjs.map +0 -7
- package/dist/lib/browser/chunk-QG25ZU2N.mjs +0 -320
- package/dist/lib/browser/chunk-QG25ZU2N.mjs.map +0 -7
- package/dist/lib/browser/chunk-SPDTXTOV.mjs +0 -163
- package/dist/lib/browser/chunk-SPDTXTOV.mjs.map +0 -7
- package/dist/lib/browser/chunk-WBOXEHBE.mjs +0 -51
- package/dist/lib/browser/chunk-WBOXEHBE.mjs.map +0 -7
- package/dist/lib/browser/plugin-intent-T7Y3MJ5C.mjs +0 -32
- package/dist/lib/browser/plugin-intent-T7Y3MJ5C.mjs.map +0 -7
- package/dist/lib/browser/plugin-settings-5U2L2NRU.mjs +0 -15
- package/dist/lib/browser/plugin-settings-5U2L2NRU.mjs.map +0 -7
- package/dist/lib/browser/plugin-surface-OKPF3EQI.mjs +0 -24
- package/dist/lib/browser/plugin-surface-OKPF3EQI.mjs.map +0 -7
- package/dist/lib/node/chunk-BW3RNEVI.cjs +0 -185
- package/dist/lib/node/chunk-BW3RNEVI.cjs.map +0 -7
- package/dist/lib/node/chunk-FCMHRU3M.cjs +0 -70
- package/dist/lib/node/chunk-FCMHRU3M.cjs.map +0 -7
- package/dist/lib/node/chunk-QBM42OQ6.cjs +0 -97
- package/dist/lib/node/chunk-QBM42OQ6.cjs.map +0 -7
- package/dist/lib/node/chunk-VWHAALIN.cjs +0 -344
- package/dist/lib/node/chunk-VWHAALIN.cjs.map +0 -7
- package/dist/lib/node/plugin-intent-F3TQZIUR.cjs +0 -53
- package/dist/lib/node/plugin-intent-F3TQZIUR.cjs.map +0 -7
- package/dist/lib/node/plugin-settings-W6UHMH5M.cjs +0 -36
- package/dist/lib/node/plugin-settings-W6UHMH5M.cjs.map +0 -7
- package/dist/lib/node/plugin-surface-CCSIONYW.cjs +0 -45
- package/dist/lib/node/plugin-surface-CCSIONYW.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-3T5UIJY3.mjs +0 -53
- package/dist/lib/node-esm/chunk-3T5UIJY3.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-4GX7V5ZE.mjs +0 -164
- package/dist/lib/node-esm/chunk-4GX7V5ZE.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-CFOUYXQ6.mjs +0 -321
- package/dist/lib/node-esm/chunk-CFOUYXQ6.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-EYCTSFEJ.mjs +0 -74
- package/dist/lib/node-esm/chunk-EYCTSFEJ.mjs.map +0 -7
- package/dist/lib/node-esm/plugin-intent-W2HQC6LC.mjs +0 -33
- package/dist/lib/node-esm/plugin-intent-W2HQC6LC.mjs.map +0 -7
- package/dist/lib/node-esm/plugin-settings-H5RHNFVC.mjs +0 -16
- package/dist/lib/node-esm/plugin-settings-H5RHNFVC.mjs.map +0 -7
- package/dist/lib/node-esm/plugin-surface-V3YET3UL.mjs +0 -25
- package/dist/lib/node-esm/plugin-surface-V3YET3UL.mjs.map +0 -7
- package/dist/types/src/plugins/common/file.d.ts +0 -22
- package/dist/types/src/plugins/common/file.d.ts.map +0 -1
- package/dist/types/src/plugins/common/graph.d.ts +0 -51
- package/dist/types/src/plugins/common/graph.d.ts.map +0 -1
- package/dist/types/src/plugins/common/index.d.ts.map +0 -1
- package/dist/types/src/plugins/common/layout.d.ts.map +0 -1
- package/dist/types/src/plugins/common/metadata.d.ts +0 -16
- package/dist/types/src/plugins/common/metadata.d.ts.map +0 -1
- package/dist/types/src/plugins/common/navigation.d.ts.map +0 -1
- package/dist/types/src/plugins/common/translations.d.ts.map +0 -1
- package/dist/types/src/plugins/helpers.d.ts +0 -41
- package/dist/types/src/plugins/helpers.d.ts.map +0 -1
- package/dist/types/src/plugins/index.d.ts +0 -7
- package/dist/types/src/plugins/index.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-host/HostPlugin.d.ts +0 -16
- package/dist/types/src/plugins/plugin-host/HostPlugin.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-host/PluginContainer.d.ts +0 -14
- package/dist/types/src/plugins/plugin-host/PluginContainer.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-host/PluginContext.d.ts +0 -47
- package/dist/types/src/plugins/plugin-host/PluginContext.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-host/index.d.ts +0 -6
- package/dist/types/src/plugins/plugin-host/index.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-host/plugin.d.ts +0 -104
- package/dist/types/src/plugins/plugin-host/plugin.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-host/plugin.test.d.ts +0 -35
- package/dist/types/src/plugins/plugin-host/plugin.test.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-intent/IntentContext.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-intent/IntentPlugin.d.ts +0 -8
- package/dist/types/src/plugins/plugin-intent/IntentPlugin.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-intent/index.d.ts +0 -7
- package/dist/types/src/plugins/plugin-intent/index.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-intent/intent-dispatcher.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-intent/intent-dispatcher.test.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-intent/intent.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-intent/meta.d.ts +0 -6
- package/dist/types/src/plugins/plugin-intent/meta.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-intent/provides.d.ts +0 -16
- package/dist/types/src/plugins/plugin-intent/provides.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-settings/SettingsPlugin.d.ts +0 -4
- package/dist/types/src/plugins/plugin-settings/SettingsPlugin.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-settings/index.d.ts +0 -4
- package/dist/types/src/plugins/plugin-settings/index.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-settings/meta.d.ts +0 -5
- package/dist/types/src/plugins/plugin-settings/meta.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-settings/provides.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-surface/ErrorBoundary.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-surface/Surface.d.ts +0 -7
- package/dist/types/src/plugins/plugin-surface/Surface.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-surface/SurfaceContext.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-surface/SurfacePlugin.d.ts +0 -7
- package/dist/types/src/plugins/plugin-surface/SurfacePlugin.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-surface/helpers.d.ts +0 -21
- package/dist/types/src/plugins/plugin-surface/helpers.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-surface/index.d.ts +0 -8
- package/dist/types/src/plugins/plugin-surface/index.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-surface/meta.d.ts +0 -5
- package/dist/types/src/plugins/plugin-surface/meta.d.ts.map +0 -1
- package/dist/types/src/plugins/plugin-surface/provides.d.ts +0 -17
- package/dist/types/src/plugins/plugin-surface/provides.d.ts.map +0 -1
- package/src/plugins/common/file.ts +0 -36
- package/src/plugins/common/graph.ts +0 -70
- package/src/plugins/common/metadata.ts +0 -29
- package/src/plugins/common/translations.ts +0 -36
- package/src/plugins/helpers.ts +0 -92
- package/src/plugins/index.ts +0 -11
- package/src/plugins/plugin-host/HostPlugin.tsx +0 -88
- package/src/plugins/plugin-host/PluginContainer.tsx +0 -120
- package/src/plugins/plugin-host/PluginContext.tsx +0 -79
- package/src/plugins/plugin-host/index.ts +0 -12
- package/src/plugins/plugin-host/plugin.test.ts +0 -158
- package/src/plugins/plugin-host/plugin.ts +0 -133
- package/src/plugins/plugin-intent/IntentPlugin.tsx +0 -75
- package/src/plugins/plugin-intent/meta.ts +0 -11
- package/src/plugins/plugin-intent/provides.ts +0 -26
- package/src/plugins/plugin-settings/SettingsPlugin.tsx +0 -22
- package/src/plugins/plugin-settings/index.ts +0 -9
- package/src/plugins/plugin-settings/meta.ts +0 -9
- package/src/plugins/plugin-settings/provides.ts +0 -34
- package/src/plugins/plugin-surface/Surface.tsx +0 -62
- package/src/plugins/plugin-surface/SurfacePlugin.tsx +0 -45
- package/src/plugins/plugin-surface/helpers.ts +0 -22
- package/src/plugins/plugin-surface/index.ts +0 -14
- package/src/plugins/plugin-surface/meta.ts +0 -9
- package/src/plugins/plugin-surface/provides.ts +0 -27
- /package/dist/types/src/{plugins/plugin-intent → plugin-intent}/intent-dispatcher.test.d.ts +0 -0
- /package/dist/types/src/{plugins/plugin-surface → react}/ErrorBoundary.d.ts +0 -0
- /package/src/{plugins/plugin-surface → react}/ErrorBoundary.tsx +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { S } from '@dxos/echo-schema';
|
|
6
|
+
|
|
7
|
+
import { Label } from './intent';
|
|
8
|
+
|
|
9
|
+
export const INTENT_PLUGIN = 'dxos.org/plugin/intent';
|
|
10
|
+
export const INTENT_ACTION = `${INTENT_PLUGIN}/action`;
|
|
11
|
+
|
|
12
|
+
export namespace IntentAction {
|
|
13
|
+
/**
|
|
14
|
+
* Fired after an intent is dispatched if the intent is undoable.
|
|
15
|
+
*/
|
|
16
|
+
export class ShowUndo extends S.TaggedClass<ShowUndo>()(`${INTENT_ACTION}/show-undo`, {
|
|
17
|
+
input: S.Struct({
|
|
18
|
+
message: Label,
|
|
19
|
+
}),
|
|
20
|
+
output: S.Void,
|
|
21
|
+
}) {}
|
|
22
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// TODO(wittjosiah): Reconcile with @dxos/protocols. Factor out errors.
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* NOTE: Messages should be sentences (Start with a capital letter and end with a period).
|
|
9
|
+
* Errors can optionally include a JSON context object.
|
|
10
|
+
*/
|
|
11
|
+
export class BaseError extends Error {
|
|
12
|
+
constructor(
|
|
13
|
+
readonly code: string,
|
|
14
|
+
message?: string,
|
|
15
|
+
readonly context?: Record<string, any>,
|
|
16
|
+
) {
|
|
17
|
+
// TODO(dmaretskyi): Error.cause.
|
|
18
|
+
super(message ?? code);
|
|
19
|
+
this.name = code;
|
|
20
|
+
// NOTE: Restores prototype chain (https://stackoverflow.com/a/48342359).
|
|
21
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class NoResolversError extends BaseError {
|
|
26
|
+
constructor(action: string) {
|
|
27
|
+
super('NO_RESOLVERS', 'No resolvers were found for the action', { action });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class CycleDetectedError extends BaseError {
|
|
32
|
+
constructor(context?: Record<string, any>) {
|
|
33
|
+
super(
|
|
34
|
+
'CYCLE_DETECTED',
|
|
35
|
+
'Intent execution limit exceeded. This is likely due to an infinite loop within intent resolvers.',
|
|
36
|
+
context,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -2,12 +2,8 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
export * from './actions';
|
|
7
6
|
export * from './intent';
|
|
8
7
|
export * from './intent-dispatcher';
|
|
9
|
-
export * from './provides';
|
|
10
|
-
|
|
11
8
|
export * from './IntentContext';
|
|
12
|
-
|
|
13
|
-
export default IntentPlugin;
|
|
9
|
+
export * from './IntentPlugin';
|
|
@@ -8,11 +8,11 @@ import { describe, expect, test } from 'vitest';
|
|
|
8
8
|
import { S } from '@dxos/echo-schema';
|
|
9
9
|
|
|
10
10
|
import { chain, createIntent } from './intent';
|
|
11
|
-
import { createDispatcher, createResolver } from './intent-dispatcher';
|
|
11
|
+
import { type AnyIntentResolver, createDispatcher, createResolver } from './intent-dispatcher';
|
|
12
12
|
|
|
13
13
|
describe('Intent dispatcher', () => {
|
|
14
14
|
test('throws error if no resolver found', async () => {
|
|
15
|
-
const { dispatchPromise } = createDispatcher(
|
|
15
|
+
const { dispatchPromise } = createDispatcher(() => []);
|
|
16
16
|
const { data, error } = await dispatchPromise(createIntent(ToString, { value: 1 }));
|
|
17
17
|
|
|
18
18
|
expect(data).toBe(undefined);
|
|
@@ -20,7 +20,7 @@ describe('Intent dispatcher', () => {
|
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
test('matches intent to resolver and executes', async () => {
|
|
23
|
-
const { dispatchPromise } = createDispatcher(
|
|
23
|
+
const { dispatchPromise } = createDispatcher(() => [toStringResolver]);
|
|
24
24
|
const { data, error } = await dispatchPromise(createIntent(ToString, { value: 1 }));
|
|
25
25
|
|
|
26
26
|
expect(error).toBe(undefined);
|
|
@@ -28,18 +28,19 @@ describe('Intent dispatcher', () => {
|
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
test('update resolvers', async () => {
|
|
31
|
-
const
|
|
31
|
+
const resolvers: AnyIntentResolver[] = [];
|
|
32
|
+
const { dispatchPromise } = createDispatcher(() => resolvers);
|
|
32
33
|
const { error } = await dispatchPromise(createIntent(ToString, { value: 1 }));
|
|
33
34
|
|
|
34
35
|
expect(error).toBeInstanceOf(Error);
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
resolvers.push(toStringResolver);
|
|
37
38
|
|
|
38
39
|
const { data } = await dispatchPromise(createIntent(ToString, { value: 1 }));
|
|
39
40
|
|
|
40
41
|
expect(data?.string).toBe('1');
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
resolvers.splice(resolvers.indexOf(toStringResolver), 1);
|
|
43
44
|
|
|
44
45
|
{
|
|
45
46
|
const { data, error } = await dispatchPromise(createIntent(ToString, { value: 1 }));
|
|
@@ -50,34 +51,34 @@ describe('Intent dispatcher', () => {
|
|
|
50
51
|
});
|
|
51
52
|
|
|
52
53
|
test('compose intent effects', async () => {
|
|
53
|
-
const { dispatch } = createDispatcher(
|
|
54
|
+
const { dispatch } = createDispatcher(() => [computeResolver]);
|
|
54
55
|
const program = Effect.gen(function* () {
|
|
55
56
|
const a = yield* dispatch(createIntent(Compute, { value: 1 }));
|
|
56
57
|
const b = yield* dispatch(createIntent(Compute, { value: 2 }));
|
|
57
|
-
return b.
|
|
58
|
+
return b.value - a.value;
|
|
58
59
|
});
|
|
59
60
|
|
|
60
61
|
expect(await Effect.runPromise(program)).toBe(2);
|
|
61
62
|
});
|
|
62
63
|
|
|
63
64
|
test('concurrent intent effects', async () => {
|
|
64
|
-
const { dispatch } = createDispatcher(
|
|
65
|
+
const { dispatch } = createDispatcher(() => [computeResolver]);
|
|
65
66
|
const program = Effect.gen(function* () {
|
|
66
67
|
const fiberA = yield* Effect.fork(dispatch(createIntent(Compute, { value: 5 })));
|
|
67
68
|
const fiberB = yield* Effect.fork(dispatch(createIntent(Compute, { value: 2 })));
|
|
68
69
|
const [a, b] = yield* Fiber.join(Fiber.zip(fiberA, fiberB));
|
|
69
|
-
return b.
|
|
70
|
+
return b.value - a.value;
|
|
70
71
|
});
|
|
71
72
|
|
|
72
73
|
expect(await Effect.runPromise(program)).toBe(-6);
|
|
73
74
|
});
|
|
74
75
|
|
|
75
76
|
test('mix & match intent effects with promises', async () => {
|
|
76
|
-
const { dispatch, dispatchPromise } = createDispatcher(
|
|
77
|
+
const { dispatch, dispatchPromise } = createDispatcher(() => [toStringResolver, computeResolver]);
|
|
77
78
|
const program = Effect.gen(function* () {
|
|
78
79
|
const a = yield* dispatch(createIntent(Compute, { value: 2 }));
|
|
79
|
-
const b = yield* dispatch(createIntent(ToString, { value: a.
|
|
80
|
-
return b.
|
|
80
|
+
const b = yield* dispatch(createIntent(ToString, { value: a.value }));
|
|
81
|
+
return b.string;
|
|
81
82
|
});
|
|
82
83
|
|
|
83
84
|
expect(await Effect.runPromise(program)).toBe('4');
|
|
@@ -89,22 +90,22 @@ describe('Intent dispatcher', () => {
|
|
|
89
90
|
});
|
|
90
91
|
|
|
91
92
|
test('undo intent', async () => {
|
|
92
|
-
const { dispatch, undo } = createDispatcher(
|
|
93
|
+
const { dispatch, undo } = createDispatcher(() => [computeResolver]);
|
|
93
94
|
const program = Effect.gen(function* () {
|
|
94
95
|
const a = yield* dispatch(createIntent(Compute, { value: 2 }));
|
|
95
96
|
|
|
96
|
-
expect(a.
|
|
97
|
+
expect(a.value).toBe(4);
|
|
97
98
|
|
|
98
99
|
const b = yield* undo();
|
|
99
100
|
|
|
100
|
-
expect(b
|
|
101
|
+
expect(b.value).toBe(2);
|
|
101
102
|
});
|
|
102
103
|
|
|
103
104
|
await Effect.runPromise(program);
|
|
104
105
|
});
|
|
105
106
|
|
|
106
107
|
test('chain intents', async () => {
|
|
107
|
-
const { dispatch } = createDispatcher(
|
|
108
|
+
const { dispatch } = createDispatcher(() => [computeResolver, toStringResolver, concatResolver]);
|
|
108
109
|
const intent = pipe(createIntent(Compute, { value: 1 }), chain(ToString, {}), chain(Concat, { plus: '!' }));
|
|
109
110
|
|
|
110
111
|
expect(intent.first.action).toBe(Compute._tag);
|
|
@@ -112,24 +113,24 @@ describe('Intent dispatcher', () => {
|
|
|
112
113
|
expect(intent.all.length).toBe(3);
|
|
113
114
|
|
|
114
115
|
const program = Effect.gen(function* () {
|
|
115
|
-
const
|
|
116
|
-
return data
|
|
116
|
+
const data = yield* dispatch(intent);
|
|
117
|
+
return data.string;
|
|
117
118
|
});
|
|
118
119
|
|
|
119
120
|
expect(await Effect.runPromise(program)).toBe('2!');
|
|
120
121
|
});
|
|
121
122
|
|
|
122
123
|
test('undo chained intent', async () => {
|
|
123
|
-
const { dispatch, undo } = createDispatcher(
|
|
124
|
+
const { dispatch, undo } = createDispatcher(() => [computeResolver, toStringResolver, concatResolver]);
|
|
124
125
|
const intent = pipe(createIntent(Compute, { value: 1 }), chain(Compute, {}), chain(Compute, {}));
|
|
125
126
|
const program = Effect.gen(function* () {
|
|
126
127
|
const a = yield* dispatch(intent);
|
|
127
128
|
|
|
128
|
-
expect(a.
|
|
129
|
+
expect(a.value).toBe(8);
|
|
129
130
|
|
|
130
131
|
const b = yield* undo();
|
|
131
132
|
|
|
132
|
-
expect(b
|
|
133
|
+
expect(b.value).toBe(1);
|
|
133
134
|
});
|
|
134
135
|
|
|
135
136
|
await Effect.runPromise(program);
|
|
@@ -137,15 +138,15 @@ describe('Intent dispatcher', () => {
|
|
|
137
138
|
|
|
138
139
|
test('filter resolvers by plugin', async () => {
|
|
139
140
|
const otherComputeResolver = createResolver(Compute, async (data) => ({ data: { value: data?.value * 3 } }));
|
|
140
|
-
const { dispatch } = createDispatcher(
|
|
141
|
+
const { dispatch } = createDispatcher((module) => (module === 'test' ? [computeResolver] : [otherComputeResolver]));
|
|
141
142
|
const program = Effect.gen(function* () {
|
|
142
143
|
const a = yield* dispatch(createIntent(Compute, { value: 1 }));
|
|
143
144
|
|
|
144
|
-
expect(a.
|
|
145
|
+
expect(a.value).toBe(3);
|
|
145
146
|
|
|
146
|
-
const b = yield* dispatch(createIntent(Compute, { value: 1 }, {
|
|
147
|
+
const b = yield* dispatch(createIntent(Compute, { value: 1 }, { module: 'test' }));
|
|
147
148
|
|
|
148
|
-
expect(b.
|
|
149
|
+
expect(b.value).toBe(2);
|
|
149
150
|
});
|
|
150
151
|
|
|
151
152
|
await Effect.runPromise(program);
|
|
@@ -155,15 +156,15 @@ describe('Intent dispatcher', () => {
|
|
|
155
156
|
const conditionalComputeResolver = createResolver(Compute, async (data) => ({ data: { value: data?.value * 3 } }), {
|
|
156
157
|
filter: (data): data is { value: number } => data?.value > 1,
|
|
157
158
|
});
|
|
158
|
-
const { dispatch } = createDispatcher(
|
|
159
|
+
const { dispatch } = createDispatcher(() => [conditionalComputeResolver, computeResolver]);
|
|
159
160
|
const program = Effect.gen(function* () {
|
|
160
161
|
const a = yield* dispatch(createIntent(Compute, { value: 1 }));
|
|
161
162
|
|
|
162
|
-
expect(a.
|
|
163
|
+
expect(a.value).toBe(2);
|
|
163
164
|
|
|
164
165
|
const b = yield* dispatch(createIntent(Compute, { value: 2 }));
|
|
165
166
|
|
|
166
|
-
expect(b.
|
|
167
|
+
expect(b.value).toBe(6);
|
|
167
168
|
});
|
|
168
169
|
|
|
169
170
|
await Effect.runPromise(program);
|
|
@@ -173,7 +174,7 @@ describe('Intent dispatcher', () => {
|
|
|
173
174
|
const hoistedComputeResolver = createResolver(Compute, async (data) => ({ data: { value: data?.value * 3 } }), {
|
|
174
175
|
disposition: 'hoist',
|
|
175
176
|
});
|
|
176
|
-
const { dispatchPromise } = createDispatcher(
|
|
177
|
+
const { dispatchPromise } = createDispatcher(() => [computeResolver, hoistedComputeResolver]);
|
|
177
178
|
const { data } = await dispatchPromise(createIntent(Compute, { value: 1 }));
|
|
178
179
|
expect(data?.value).toBe(3);
|
|
179
180
|
});
|
|
@@ -185,28 +186,28 @@ describe('Intent dispatcher', () => {
|
|
|
185
186
|
const fallbackComputeResolver = createResolver(Compute, async (data) => ({ data: { value: data?.value * 3 } }), {
|
|
186
187
|
disposition: 'fallback',
|
|
187
188
|
});
|
|
188
|
-
const { dispatch } = createDispatcher(
|
|
189
|
+
const { dispatch } = createDispatcher(() => [fallbackComputeResolver, conditionalComputeResolver]);
|
|
189
190
|
const program = Effect.gen(function* () {
|
|
190
191
|
const a = yield* dispatch(createIntent(Compute, { value: 1 }));
|
|
191
192
|
|
|
192
|
-
expect(a.
|
|
193
|
+
expect(a.value).toBe(2);
|
|
193
194
|
|
|
194
195
|
const b = yield* dispatch(createIntent(Compute, { value: 2 }));
|
|
195
196
|
|
|
196
|
-
expect(b.
|
|
197
|
+
expect(b.value).toBe(6);
|
|
197
198
|
});
|
|
198
199
|
|
|
199
200
|
await Effect.runPromise(program);
|
|
200
201
|
});
|
|
201
202
|
|
|
202
203
|
test('non-struct inputs & outputs', async () => {
|
|
203
|
-
const { dispatchPromise } = createDispatcher(
|
|
204
|
+
const { dispatchPromise } = createDispatcher(() => [addResolver]);
|
|
204
205
|
const { data } = await dispatchPromise(createIntent(Add, [1, 1]));
|
|
205
206
|
expect(data).toBe(2);
|
|
206
207
|
});
|
|
207
208
|
|
|
208
209
|
test('empty inputs & outputs', async () => {
|
|
209
|
-
const { dispatchPromise } = createDispatcher(
|
|
210
|
+
const { dispatchPromise } = createDispatcher(() => [sideEffectResolver]);
|
|
210
211
|
const { data } = await dispatchPromise(createIntent(SideEffect));
|
|
211
212
|
expect(data).toBe(undefined);
|
|
212
213
|
});
|
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Effect, Ref } from 'effect';
|
|
5
|
+
import { Effect, Option, pipe, Ref } from 'effect';
|
|
6
|
+
import { type Simplify } from 'effect/Types';
|
|
6
7
|
|
|
7
|
-
import { type MaybePromise,
|
|
8
|
+
import { type MaybePromise, byDisposition, type Disposition } from '@dxos/util';
|
|
8
9
|
|
|
10
|
+
import { IntentAction } from './actions';
|
|
11
|
+
import { CycleDetectedError, NoResolversError } from './errors';
|
|
9
12
|
import {
|
|
10
13
|
createIntent,
|
|
11
|
-
IntentAction,
|
|
12
14
|
type AnyIntent,
|
|
13
15
|
type AnyIntentChain,
|
|
14
16
|
type Intent,
|
|
@@ -53,6 +55,8 @@ export type IntentEffectResult<Fields extends IntentParams> = {
|
|
|
53
55
|
* An error that occurred while performing the action.
|
|
54
56
|
*
|
|
55
57
|
* If the intent is apart of a chain of intents and an error occurs, the chain will be aborted.
|
|
58
|
+
*
|
|
59
|
+
* Return caught error instead of throwing to trigger other intent to be triggered prior to returning the error.
|
|
56
60
|
*/
|
|
57
61
|
error?: Error;
|
|
58
62
|
|
|
@@ -69,15 +73,6 @@ export type AnyIntentEffectResult = IntentEffectResult<any>;
|
|
|
69
73
|
*/
|
|
70
74
|
export type IntentDispatcherResult<Fields extends IntentParams> = Pick<IntentEffectResult<Fields>, 'data' | 'error'>;
|
|
71
75
|
|
|
72
|
-
/**
|
|
73
|
-
* Determines the priority of the effect when multiple intent resolvers are matched.
|
|
74
|
-
*
|
|
75
|
-
* - `static` - The effect is selected in the order it was resolved.
|
|
76
|
-
* - `hoist` - The effect is selected before `static` effects.
|
|
77
|
-
* - `fallback` - The effect is selected after `static` effects.
|
|
78
|
-
*/
|
|
79
|
-
export type IntentDisposition = 'static' | 'hoist' | 'fallback';
|
|
80
|
-
|
|
81
76
|
/**
|
|
82
77
|
* The implementation of an intent effect.
|
|
83
78
|
*/
|
|
@@ -89,13 +84,13 @@ export type IntentEffectDefinition<Fields extends IntentParams> = (
|
|
|
89
84
|
/**
|
|
90
85
|
* Intent resolver to match intents to their effects.
|
|
91
86
|
*/
|
|
92
|
-
export type IntentResolver<Tag extends string, Fields extends IntentParams> = {
|
|
87
|
+
export type IntentResolver<Tag extends string, Fields extends IntentParams> = Readonly<{
|
|
93
88
|
action: Tag;
|
|
94
|
-
disposition?:
|
|
89
|
+
disposition?: Disposition;
|
|
95
90
|
// TODO(wittjosiah): Would be nice to make this a guard for intents with optional data.
|
|
96
91
|
filter?: (data: IntentData<Fields>) => boolean;
|
|
97
92
|
effect: IntentEffectDefinition<Fields>;
|
|
98
|
-
}
|
|
93
|
+
}>;
|
|
99
94
|
|
|
100
95
|
export type AnyIntentResolver = IntentResolver<any, any>;
|
|
101
96
|
|
|
@@ -121,7 +116,7 @@ export const createResolver = <Tag extends string, Fields extends IntentParams>(
|
|
|
121
116
|
*/
|
|
122
117
|
export type PromiseIntentDispatcher = <Fields extends IntentParams>(
|
|
123
118
|
intent: IntentChain<any, any, any, Fields>,
|
|
124
|
-
) => Promise<IntentDispatcherResult<Fields
|
|
119
|
+
) => Promise<Simplify<IntentDispatcherResult<Fields>>>;
|
|
125
120
|
|
|
126
121
|
/**
|
|
127
122
|
* Creates an effect for intents.
|
|
@@ -129,23 +124,23 @@ export type PromiseIntentDispatcher = <Fields extends IntentParams>(
|
|
|
129
124
|
export type IntentDispatcher = <Fields extends IntentParams>(
|
|
130
125
|
intent: IntentChain<any, any, any, Fields>,
|
|
131
126
|
depth?: number,
|
|
132
|
-
) => Effect.Effect<IntentDispatcherResult<Fields>, Error>;
|
|
127
|
+
) => Effect.Effect<Simplify<Required<IntentDispatcherResult<Fields>>['data']>, Error>;
|
|
133
128
|
|
|
134
129
|
type IntentResult<Tag extends string, Fields extends IntentParams> = IntentEffectResult<Fields> & {
|
|
135
130
|
_intent: Intent<Tag, Fields>;
|
|
136
131
|
};
|
|
137
132
|
|
|
138
|
-
type AnyIntentResult = IntentResult<any, any>;
|
|
133
|
+
export type AnyIntentResult = IntentResult<any, any>;
|
|
139
134
|
|
|
140
135
|
/**
|
|
141
136
|
* Invokes the most recent undoable intent with undo flags.
|
|
142
137
|
*/
|
|
143
|
-
export type PromiseIntentUndo = () => Promise<IntentDispatcherResult<any
|
|
138
|
+
export type PromiseIntentUndo = () => Promise<IntentDispatcherResult<any>>;
|
|
144
139
|
|
|
145
140
|
/**
|
|
146
141
|
* Creates an effect which undoes the last intent.
|
|
147
142
|
*/
|
|
148
|
-
export type IntentUndo = () => Effect.Effect<
|
|
143
|
+
export type IntentUndo = () => Effect.Effect<any, Error>;
|
|
149
144
|
|
|
150
145
|
/**
|
|
151
146
|
* Check if a chain of results is undoable.
|
|
@@ -158,7 +153,6 @@ export type IntentContext = {
|
|
|
158
153
|
dispatchPromise: PromiseIntentDispatcher;
|
|
159
154
|
undo: IntentUndo;
|
|
160
155
|
undoPromise: PromiseIntentUndo;
|
|
161
|
-
registerResolver: (id: string, resolver: AnyIntentResolver) => () => void;
|
|
162
156
|
};
|
|
163
157
|
|
|
164
158
|
/**
|
|
@@ -169,40 +163,34 @@ export type IntentContext = {
|
|
|
169
163
|
* @param params.executionLimit The maximum recursion depth of intent chains.
|
|
170
164
|
*/
|
|
171
165
|
export const createDispatcher = (
|
|
172
|
-
|
|
166
|
+
getResolvers: (module?: string) => AnyIntentResolver[],
|
|
173
167
|
{ executionLimit = EXECUTION_LIMIT, historyLimit = HISTORY_LIMIT } = {},
|
|
174
168
|
): IntentContext => {
|
|
175
169
|
const historyRef = Effect.runSync(Ref.make<AnyIntentResult[][]>([]));
|
|
176
170
|
|
|
177
171
|
const handleIntent = (intent: AnyIntent) => {
|
|
178
172
|
return Effect.gen(function* () {
|
|
179
|
-
const candidates =
|
|
180
|
-
.filter(([id, _]) => (intent.plugin ? id === intent.plugin : true))
|
|
181
|
-
.flatMap(([_, resolvers]) => resolvers)
|
|
173
|
+
const candidates = getResolvers(intent.module)
|
|
182
174
|
.filter((r) => r.action === intent.action)
|
|
183
175
|
.filter((r) => !r.filter || r.filter(intent.data))
|
|
184
|
-
.toSorted(
|
|
185
|
-
return a === b ? 0 : a === 'hoist' || b === 'fallback' ? -1 : b === 'hoist' || a === 'fallback' ? 1 : 0;
|
|
186
|
-
});
|
|
176
|
+
.toSorted(byDisposition);
|
|
187
177
|
if (candidates.length === 0) {
|
|
188
178
|
return {
|
|
189
179
|
_intent: intent,
|
|
190
|
-
error: new
|
|
191
|
-
}
|
|
180
|
+
error: new NoResolversError(intent.action),
|
|
181
|
+
} as AnyIntentResult;
|
|
192
182
|
}
|
|
193
183
|
|
|
194
184
|
const effect = candidates[0].effect(intent.data, intent.undo ?? false);
|
|
195
185
|
const result = Effect.isEffect(effect) ? yield* effect : yield* Effect.promise(async () => effect);
|
|
196
|
-
return { _intent: intent, ...result }
|
|
186
|
+
return { _intent: intent, ...result } as AnyIntentResult;
|
|
197
187
|
});
|
|
198
188
|
};
|
|
199
189
|
|
|
200
190
|
const dispatch: IntentDispatcher = (intentChain, depth = 0) => {
|
|
201
191
|
return Effect.gen(function* () {
|
|
202
192
|
if (depth > executionLimit) {
|
|
203
|
-
yield* Effect.fail(
|
|
204
|
-
new Error('Intent execution limit exceeded. This is likely due to an infinite loop within intent resolvers.'),
|
|
205
|
-
);
|
|
193
|
+
yield* Effect.fail(new CycleDetectedError());
|
|
206
194
|
}
|
|
207
195
|
|
|
208
196
|
const resultsRef = yield* Ref.make<AnyIntentResult[]>([]);
|
|
@@ -210,9 +198,6 @@ export const createDispatcher = (
|
|
|
210
198
|
const { data: prev } = (yield* resultsRef.get)[0] ?? {};
|
|
211
199
|
const result = yield* handleIntent({ ...intent, data: { ...intent.data, ...prev } });
|
|
212
200
|
yield* Ref.update(resultsRef, (results) => [result, ...results]);
|
|
213
|
-
if (result.error) {
|
|
214
|
-
break;
|
|
215
|
-
}
|
|
216
201
|
if (result.intents) {
|
|
217
202
|
for (const intent of result.intents) {
|
|
218
203
|
// Returned intents are dispatched but not yielded into results,
|
|
@@ -221,34 +206,39 @@ export const createDispatcher = (
|
|
|
221
206
|
yield* dispatch(intent, depth + 1);
|
|
222
207
|
}
|
|
223
208
|
}
|
|
209
|
+
if (result.error) {
|
|
210
|
+
yield* Effect.fail(result.error);
|
|
211
|
+
}
|
|
224
212
|
}
|
|
225
213
|
|
|
226
214
|
const results = yield* resultsRef.get;
|
|
227
215
|
const result = results[0];
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
next.splice(0, next.length - historyLimit);
|
|
233
|
-
}
|
|
234
|
-
return next;
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
if (result.undoable && isUndoable(results)) {
|
|
238
|
-
// TODO(wittjosiah): Is there a better way to handle showing undo for chains?
|
|
239
|
-
yield* dispatch(createIntent(IntentAction.ShowUndo, { message: result.undoable.message }));
|
|
216
|
+
yield* Ref.update(historyRef, (history) => {
|
|
217
|
+
const next = [...history, results];
|
|
218
|
+
if (next.length > historyLimit) {
|
|
219
|
+
next.splice(0, next.length - historyLimit);
|
|
240
220
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
221
|
+
return next;
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
if (result.undoable && isUndoable(results)) {
|
|
225
|
+
// TODO(wittjosiah): Is there a better way to handle showing undo for chains?
|
|
226
|
+
yield* pipe(
|
|
227
|
+
dispatch(createIntent(IntentAction.ShowUndo, { message: result.undoable.message })),
|
|
228
|
+
Effect.catchSome((err) =>
|
|
229
|
+
err instanceof NoResolversError ? Option.some(Effect.succeed(undefined)) : Option.none(),
|
|
230
|
+
),
|
|
231
|
+
);
|
|
245
232
|
}
|
|
233
|
+
|
|
234
|
+
return result.data;
|
|
246
235
|
});
|
|
247
236
|
};
|
|
248
237
|
|
|
249
238
|
const dispatchPromise: PromiseIntentDispatcher = (intentChain) => {
|
|
250
|
-
|
|
251
|
-
|
|
239
|
+
return Effect.runPromise(dispatch(intentChain))
|
|
240
|
+
.then((data) => ({ data }))
|
|
241
|
+
.catch((error) => ({ error }));
|
|
252
242
|
};
|
|
253
243
|
|
|
254
244
|
const undo: IntentUndo = () => {
|
|
@@ -270,16 +260,10 @@ export const createDispatcher = (
|
|
|
270
260
|
};
|
|
271
261
|
|
|
272
262
|
const undoPromise: PromiseIntentUndo = () => {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const registerResolver = (id: string, resolver: AnyIntentResolver) => {
|
|
278
|
-
resolvers[id] = [...(resolvers[id] ?? []), resolver];
|
|
279
|
-
return () => {
|
|
280
|
-
resolvers[id] = resolvers[id].filter((r) => r !== resolver);
|
|
281
|
-
};
|
|
263
|
+
return Effect.runPromise(undo())
|
|
264
|
+
.then((data) => ({ data }))
|
|
265
|
+
.catch((error) => ({ error }));
|
|
282
266
|
};
|
|
283
267
|
|
|
284
|
-
return { dispatch, dispatchPromise, undo, undoPromise
|
|
268
|
+
return { dispatch, dispatchPromise, undo, undoPromise };
|
|
285
269
|
};
|
|
@@ -4,8 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
import { S } from '@dxos/echo-schema';
|
|
6
6
|
|
|
7
|
-
import { INTENT_PLUGIN } from './meta';
|
|
8
|
-
|
|
9
7
|
export type IntentParams = {
|
|
10
8
|
readonly input: S.Schema.All;
|
|
11
9
|
readonly output: S.Schema.All;
|
|
@@ -37,11 +35,11 @@ export type Intent<Tag extends string, Fields extends IntentParams> = {
|
|
|
37
35
|
data: IntentData<Fields>;
|
|
38
36
|
|
|
39
37
|
/**
|
|
40
|
-
*
|
|
41
|
-
* If specified, the intent will be sent explicitly to the plugin.
|
|
38
|
+
* Module ID.
|
|
39
|
+
* If specified, the intent will be sent explicitly to the plugin module.
|
|
42
40
|
* Otherwise, the intent will be sent to all plugins, in order and the first to resolve a non-null value will be used.
|
|
43
41
|
*/
|
|
44
|
-
|
|
42
|
+
module?: string;
|
|
45
43
|
|
|
46
44
|
/**
|
|
47
45
|
* Whether or not the intent is being undone.
|
|
@@ -78,7 +76,7 @@ export type AnyIntentChain = IntentChain<any, any, any, any>;
|
|
|
78
76
|
export const createIntent = <Tag extends string, Fields extends IntentParams>(
|
|
79
77
|
schema: IntentSchema<Tag, Fields>,
|
|
80
78
|
data: IntentData<Fields> = {},
|
|
81
|
-
params: Pick<AnyIntent, '
|
|
79
|
+
params: Pick<AnyIntent, 'module' | 'undo'> = {},
|
|
82
80
|
): IntentChain<Tag, Tag, Fields, Fields> => {
|
|
83
81
|
// The output of validateSync breaks proxy objects so this is just used for validation.
|
|
84
82
|
// TODO(wittjosiah): Is there a better way to make theses types align?
|
|
@@ -114,7 +112,7 @@ export const chain =
|
|
|
114
112
|
>(
|
|
115
113
|
schema: IntentSchema<NextTag, NextFields>,
|
|
116
114
|
data: Omit<IntentData<NextFields>, keyof IntentResultData<LastFields>> = {},
|
|
117
|
-
params: Pick<AnyIntent, '
|
|
115
|
+
params: Pick<AnyIntent, 'module' | 'undo'> = {},
|
|
118
116
|
) =>
|
|
119
117
|
(
|
|
120
118
|
intent: IntentChain<FirstTag, any, FirstFields, LastFields>,
|
|
@@ -146,17 +144,3 @@ export const Label = S.Union(
|
|
|
146
144
|
S.mutable(S.Tuple(S.String, S.mutable(S.Struct({ ns: S.String, count: S.optional(S.Number) })))),
|
|
147
145
|
);
|
|
148
146
|
export type Label = S.Schema.Type<typeof Label>;
|
|
149
|
-
|
|
150
|
-
export const INTENT_ACTION = `${INTENT_PLUGIN}/action`;
|
|
151
|
-
|
|
152
|
-
export namespace IntentAction {
|
|
153
|
-
/**
|
|
154
|
-
* Fired after an intent is dispatched if the intent is undoable.
|
|
155
|
-
*/
|
|
156
|
-
export class ShowUndo extends S.TaggedClass<ShowUndo>()(`${INTENT_ACTION}/show-undo`, {
|
|
157
|
-
input: S.Struct({
|
|
158
|
-
message: Label,
|
|
159
|
-
}),
|
|
160
|
-
output: S.Void,
|
|
161
|
-
}) {}
|
|
162
|
-
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { RootSettingsStore } from '@dxos/local-storage';
|
|
6
|
+
|
|
7
|
+
import { SETTINGS_PLUGIN } from './actions';
|
|
8
|
+
import { Capabilities, Events } from '../common';
|
|
9
|
+
import { contributes, defineModule, definePlugin } from '../core';
|
|
10
|
+
|
|
11
|
+
export const SettingsPlugin = () =>
|
|
12
|
+
definePlugin({ id: SETTINGS_PLUGIN }, [
|
|
13
|
+
defineModule({
|
|
14
|
+
id: `${SETTINGS_PLUGIN}/module/store`,
|
|
15
|
+
activatesOn: Events.Startup,
|
|
16
|
+
activatesBefore: [Events.SetupSettings],
|
|
17
|
+
activatesAfter: [Events.SettingsReady],
|
|
18
|
+
activate: (context) => {
|
|
19
|
+
const allSettings = context.requestCapabilities(Capabilities.Settings);
|
|
20
|
+
const settingsStore = new RootSettingsStore();
|
|
21
|
+
|
|
22
|
+
allSettings.forEach((setting) => {
|
|
23
|
+
settingsStore.createStore(setting as any);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return contributes(Capabilities.SettingsStore, settingsStore);
|
|
27
|
+
},
|
|
28
|
+
}),
|
|
29
|
+
]);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { S } from '@dxos/echo-schema';
|
|
6
|
+
|
|
7
|
+
export const SETTINGS_PLUGIN = 'dxos.org/plugin/settings';
|
|
8
|
+
export const SETTINGS_ACTION = `${SETTINGS_PLUGIN}/action`;
|
|
9
|
+
|
|
10
|
+
export namespace SettingsAction {
|
|
11
|
+
export class Open extends S.TaggedClass<Open>()(`${SETTINGS_ACTION}/open`, {
|
|
12
|
+
input: S.Struct({
|
|
13
|
+
plugin: S.optional(S.String),
|
|
14
|
+
}),
|
|
15
|
+
output: S.Void,
|
|
16
|
+
}) {}
|
|
17
|
+
}
|