@dxos/app-framework 0.7.5-main.9d2a38b → 0.7.5-main.ff8607b

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/dist/lib/browser/app-graph-builder-F7VZ6LRN.mjs +137 -0
  2. package/dist/lib/browser/app-graph-builder-F7VZ6LRN.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-GNLU3GAU.mjs → chunk-ATRNTMSS.mjs} +623 -819
  4. package/dist/lib/browser/chunk-ATRNTMSS.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-LDJ3T4V3.mjs +32 -0
  6. package/dist/lib/browser/chunk-LDJ3T4V3.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-WS6SU6HI.mjs +285 -0
  8. package/dist/lib/browser/chunk-WS6SU6HI.mjs.map +7 -0
  9. package/dist/lib/browser/index.mjs +57 -74
  10. package/dist/lib/browser/index.mjs.map +4 -4
  11. package/dist/lib/browser/intent-dispatcher-E6J7E5Y5.mjs +11 -0
  12. package/dist/lib/browser/intent-dispatcher-E6J7E5Y5.mjs.map +7 -0
  13. package/dist/lib/browser/intent-resolver-XLE4L3LS.mjs +38 -0
  14. package/dist/lib/browser/intent-resolver-XLE4L3LS.mjs.map +7 -0
  15. package/dist/lib/browser/meta.json +1 -1
  16. package/dist/lib/browser/store-QU2IKFAI.mjs +19 -0
  17. package/dist/lib/browser/store-QU2IKFAI.mjs.map +7 -0
  18. package/dist/lib/browser/testing/index.mjs +10 -3
  19. package/dist/lib/browser/testing/index.mjs.map +3 -3
  20. package/dist/lib/browser/worker.mjs +77 -0
  21. package/dist/lib/browser/worker.mjs.map +7 -0
  22. package/dist/lib/node/app-graph-builder-JGBADFF7.cjs +146 -0
  23. package/dist/lib/node/app-graph-builder-JGBADFF7.cjs.map +7 -0
  24. package/dist/lib/node/chunk-QLVQ6PND.cjs +58 -0
  25. package/dist/lib/node/chunk-QLVQ6PND.cjs.map +7 -0
  26. package/dist/lib/node/chunk-WKC6YMEQ.cjs +1433 -0
  27. package/dist/lib/node/chunk-WKC6YMEQ.cjs.map +7 -0
  28. package/dist/lib/node/chunk-WRWRZKZU.cjs +308 -0
  29. package/dist/lib/node/chunk-WRWRZKZU.cjs.map +7 -0
  30. package/dist/lib/node/index.cjs +106 -118
  31. package/dist/lib/node/index.cjs.map +4 -4
  32. package/dist/lib/node/intent-dispatcher-CFBKDZQR.cjs +32 -0
  33. package/dist/lib/node/intent-dispatcher-CFBKDZQR.cjs.map +7 -0
  34. package/dist/lib/node/intent-resolver-3TKCXP4S.cjs +45 -0
  35. package/dist/lib/node/intent-resolver-3TKCXP4S.cjs.map +7 -0
  36. package/dist/lib/node/meta.json +1 -1
  37. package/dist/lib/node/store-4QMUUU2A.cjs +34 -0
  38. package/dist/lib/node/store-4QMUUU2A.cjs.map +7 -0
  39. package/dist/lib/node/testing/index.cjs +15 -8
  40. package/dist/lib/node/testing/index.cjs.map +3 -3
  41. package/dist/lib/node/worker.cjs +99 -0
  42. package/dist/lib/node/worker.cjs.map +7 -0
  43. package/dist/lib/node-esm/app-graph-builder-2QEX57NX.mjs +138 -0
  44. package/dist/lib/node-esm/app-graph-builder-2QEX57NX.mjs.map +7 -0
  45. package/dist/lib/node-esm/{chunk-KPMTPXQI.mjs → chunk-44J2VZBB.mjs} +623 -819
  46. package/dist/lib/node-esm/chunk-44J2VZBB.mjs.map +7 -0
  47. package/dist/lib/node-esm/chunk-CNJYZNSL.mjs +34 -0
  48. package/dist/lib/node-esm/chunk-CNJYZNSL.mjs.map +7 -0
  49. package/dist/lib/node-esm/chunk-HTLXL32I.mjs +286 -0
  50. package/dist/lib/node-esm/chunk-HTLXL32I.mjs.map +7 -0
  51. package/dist/lib/node-esm/index.mjs +57 -74
  52. package/dist/lib/node-esm/index.mjs.map +4 -4
  53. package/dist/lib/node-esm/intent-dispatcher-LDQGDZ62.mjs +12 -0
  54. package/dist/lib/node-esm/intent-dispatcher-LDQGDZ62.mjs.map +7 -0
  55. package/dist/lib/node-esm/intent-resolver-7VJWN67U.mjs +39 -0
  56. package/dist/lib/node-esm/intent-resolver-7VJWN67U.mjs.map +7 -0
  57. package/dist/lib/node-esm/meta.json +1 -1
  58. package/dist/lib/node-esm/store-VWDAYUQY.mjs +20 -0
  59. package/dist/lib/node-esm/store-VWDAYUQY.mjs.map +7 -0
  60. package/dist/lib/node-esm/testing/index.mjs +10 -3
  61. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  62. package/dist/lib/node-esm/worker.mjs +78 -0
  63. package/dist/lib/node-esm/worker.mjs.map +7 -0
  64. package/dist/types/src/App.d.ts.map +1 -1
  65. package/dist/types/src/common/capabilities.d.ts +63 -110
  66. package/dist/types/src/common/capabilities.d.ts.map +1 -1
  67. package/dist/types/src/common/events.d.ts +8 -1
  68. package/dist/types/src/common/events.d.ts.map +1 -1
  69. package/dist/types/src/common/file.d.ts +1 -1
  70. package/dist/types/src/common/file.d.ts.map +1 -1
  71. package/dist/types/src/common/graph.d.ts +2 -2
  72. package/dist/types/src/common/graph.d.ts.map +1 -1
  73. package/dist/types/src/common/index.d.ts +0 -1
  74. package/dist/types/src/common/index.d.ts.map +1 -1
  75. package/dist/types/src/common/layout.d.ts +204 -121
  76. package/dist/types/src/common/layout.d.ts.map +1 -1
  77. package/dist/types/src/common/surface.d.ts +3 -3
  78. package/dist/types/src/common/surface.d.ts.map +1 -1
  79. package/dist/types/src/common/translations.d.ts +7 -7
  80. package/dist/types/src/common/translations.d.ts.map +1 -1
  81. package/dist/types/src/core/capabilities.d.ts +6 -2
  82. package/dist/types/src/core/capabilities.d.ts.map +1 -1
  83. package/dist/types/src/core/manager.d.ts +2 -9
  84. package/dist/types/src/core/manager.d.ts.map +1 -1
  85. package/dist/types/src/core/plugin.d.ts +5 -2
  86. package/dist/types/src/core/plugin.d.ts.map +1 -1
  87. package/dist/types/src/playground/generator/Toolbar.d.ts.map +1 -1
  88. package/dist/types/src/playground/generator/generator.d.ts +2 -0
  89. package/dist/types/src/playground/generator/generator.d.ts.map +1 -1
  90. package/dist/types/src/playground/generator/plugin.d.ts.map +1 -1
  91. package/dist/types/src/playground/logger/Toolbar.d.ts.map +1 -1
  92. package/dist/types/src/playground/logger/plugin.d.ts.map +1 -1
  93. package/dist/types/src/playground/logger/schema.d.ts +1 -1
  94. package/dist/types/src/playground/logger/schema.d.ts.map +1 -1
  95. package/dist/types/src/plugin-intent/IntentPlugin.d.ts.map +1 -1
  96. package/dist/types/src/plugin-intent/actions.d.ts +1 -1
  97. package/dist/types/src/plugin-intent/actions.d.ts.map +1 -1
  98. package/dist/types/src/plugin-intent/index.d.ts +0 -1
  99. package/dist/types/src/plugin-intent/index.d.ts.map +1 -1
  100. package/dist/types/src/plugin-intent/intent-dispatcher.d.ts +27 -20
  101. package/dist/types/src/plugin-intent/intent-dispatcher.d.ts.map +1 -1
  102. package/dist/types/src/plugin-intent/intent.d.ts +3 -3
  103. package/dist/types/src/plugin-intent/intent.d.ts.map +1 -1
  104. package/dist/types/src/plugin-settings/SettingsPlugin.d.ts.map +1 -1
  105. package/dist/types/src/plugin-settings/actions.d.ts +11 -1
  106. package/dist/types/src/plugin-settings/actions.d.ts.map +1 -1
  107. package/dist/types/src/plugin-settings/app-graph-builder.d.ts +197 -0
  108. package/dist/types/src/plugin-settings/app-graph-builder.d.ts.map +1 -0
  109. package/dist/types/src/plugin-settings/intent-resolver.d.ts +4 -0
  110. package/dist/types/src/plugin-settings/intent-resolver.d.ts.map +1 -0
  111. package/dist/types/src/plugin-settings/store.d.ts +5 -0
  112. package/dist/types/src/plugin-settings/store.d.ts.map +1 -0
  113. package/dist/types/src/plugin-settings/translations.d.ts +11 -0
  114. package/dist/types/src/plugin-settings/translations.d.ts.map +1 -0
  115. package/dist/types/src/{plugin-intent → react}/IntentContext.d.ts +1 -1
  116. package/dist/types/src/react/IntentContext.d.ts.map +1 -0
  117. package/dist/types/src/react/Surface.d.ts.map +1 -1
  118. package/dist/types/src/react/Surface.stories.d.ts +16 -0
  119. package/dist/types/src/react/Surface.stories.d.ts.map +1 -0
  120. package/dist/types/src/react/common.d.ts +12 -0
  121. package/dist/types/src/react/common.d.ts.map +1 -0
  122. package/dist/types/src/react/index.d.ts +2 -0
  123. package/dist/types/src/react/index.d.ts.map +1 -1
  124. package/dist/types/src/react/useIntentResolver.d.ts +3 -0
  125. package/dist/types/src/react/useIntentResolver.d.ts.map +1 -0
  126. package/dist/types/src/testing/withPluginManager.d.ts +1 -1
  127. package/dist/types/src/testing/withPluginManager.d.ts.map +1 -1
  128. package/dist/types/src/worker.d.ts +4 -0
  129. package/dist/types/src/worker.d.ts.map +1 -0
  130. package/package.json +26 -20
  131. package/project.json +3 -3
  132. package/src/App.tsx +15 -14
  133. package/src/common/capabilities.ts +20 -11
  134. package/src/common/events.ts +10 -1
  135. package/src/common/file.ts +1 -1
  136. package/src/common/graph.ts +2 -2
  137. package/src/common/index.ts +0 -1
  138. package/src/common/layout.ts +194 -126
  139. package/src/common/surface.ts +2 -2
  140. package/src/common/translations.ts +7 -8
  141. package/src/core/capabilities.ts +16 -7
  142. package/src/core/manager.test.ts +22 -73
  143. package/src/core/manager.ts +105 -91
  144. package/src/core/plugin.ts +6 -3
  145. package/src/playground/debug/plugin.ts +1 -1
  146. package/src/playground/generator/Toolbar.tsx +11 -11
  147. package/src/playground/generator/generator.ts +25 -0
  148. package/src/playground/generator/plugin.ts +6 -1
  149. package/src/playground/layout/plugin.ts +1 -1
  150. package/src/playground/logger/Toolbar.tsx +2 -1
  151. package/src/playground/logger/plugin.ts +6 -3
  152. package/src/playground/logger/schema.ts +1 -1
  153. package/src/plugin-intent/IntentPlugin.tsx +3 -43
  154. package/src/plugin-intent/actions.ts +1 -1
  155. package/src/plugin-intent/index.ts +0 -1
  156. package/src/plugin-intent/intent-dispatcher.test.ts +48 -29
  157. package/src/plugin-intent/intent-dispatcher.ts +76 -41
  158. package/src/plugin-intent/intent.ts +5 -5
  159. package/src/plugin-settings/SettingsPlugin.ts +19 -13
  160. package/src/plugin-settings/actions.ts +11 -1
  161. package/src/plugin-settings/app-graph-builder.ts +122 -0
  162. package/src/plugin-settings/intent-resolver.ts +28 -0
  163. package/src/plugin-settings/store.ts +20 -0
  164. package/src/plugin-settings/translations.ts +17 -0
  165. package/src/{plugin-intent → react}/IntentContext.tsx +2 -2
  166. package/src/react/Surface.stories.tsx +96 -0
  167. package/src/react/Surface.tsx +11 -8
  168. package/src/react/common.ts +12 -0
  169. package/src/react/index.ts +2 -0
  170. package/src/react/useIntentResolver.ts +22 -0
  171. package/src/testing/withPluginManager.tsx +11 -3
  172. package/src/worker.ts +11 -0
  173. package/tsconfig.json +3 -3
  174. package/dist/lib/browser/chunk-GNLU3GAU.mjs.map +0 -7
  175. package/dist/lib/node/chunk-FBA4BB3J.cjs +0 -1639
  176. package/dist/lib/node/chunk-FBA4BB3J.cjs.map +0 -7
  177. package/dist/lib/node-esm/chunk-KPMTPXQI.mjs.map +0 -7
  178. package/dist/types/src/common/navigation.d.ts +0 -241
  179. package/dist/types/src/common/navigation.d.ts.map +0 -1
  180. package/dist/types/src/plugin-intent/IntentContext.d.ts.map +0 -1
  181. package/src/common/navigation.ts +0 -199
@@ -2,7 +2,11 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ import { Schema as S } from '@effect/schema';
6
+
7
+ import { Capabilities, Events } from '../../common';
5
8
  import { contributes, defineEvent, defineCapability, defineModule, definePlugin } from '../../core';
9
+ import { createResolver, type IntentSchema } from '../../plugin-intent';
6
10
 
7
11
  export const Number = defineCapability<number>('dxos.org/test/generator/number');
8
12
 
@@ -10,6 +14,15 @@ export const CountEvent = defineEvent('dxos.org/test/generator/count');
10
14
 
11
15
  export const createPluginId = (id: string) => `dxos.org/test/generator/${id}`;
12
16
 
17
+ export const createGeneratorIntent = (id: string) => {
18
+ class Alert extends S.TaggedClass<Alert>()(`${createPluginId(id)}/action/alert`, {
19
+ input: S.Void,
20
+ output: S.Void,
21
+ }) {}
22
+
23
+ return Alert as unknown as IntentSchema<any, any>;
24
+ };
25
+
13
26
  export const createNumberPlugin = (id: string) => {
14
27
  const number = Math.floor(Math.random() * 100);
15
28
 
@@ -19,5 +32,17 @@ export const createNumberPlugin = (id: string) => {
19
32
  activatesOn: CountEvent,
20
33
  activate: () => contributes(Number, number),
21
34
  }),
35
+ defineModule({
36
+ id: `${id}/intent-resolver`,
37
+ activatesOn: Events.SetupIntents,
38
+ activate: () =>
39
+ contributes(
40
+ Capabilities.IntentResolver,
41
+ createResolver({
42
+ intent: createGeneratorIntent(id),
43
+ resolve: () => window.alert(JSON.stringify({ number })),
44
+ }),
45
+ ),
46
+ }),
22
47
  ]);
23
48
  };
@@ -13,6 +13,11 @@ export const GeneratorPlugin = () =>
13
13
  defineModule({
14
14
  id: 'dxos.org/test/generator/main',
15
15
  activatesOn: Events.Startup,
16
- activate: async () => [await Main(), await Toolbar()],
16
+ activate: Main,
17
+ }),
18
+ defineModule({
19
+ id: 'dxos.org/test/generator/toolbar',
20
+ activatesOn: Events.Startup,
21
+ activate: Toolbar,
17
22
  }),
18
23
  ]);
@@ -12,6 +12,6 @@ export const LayoutPlugin = () =>
12
12
  defineModule({
13
13
  id: 'dxos.org/test/layout/root',
14
14
  activatesOn: Events.Startup,
15
- activate: () => Layout(),
15
+ activate: Layout,
16
16
  }),
17
17
  ]);
@@ -9,7 +9,8 @@ import { Button } from '@dxos/react-ui';
9
9
  import { Log } from './schema';
10
10
  import { Capabilities, createSurface } from '../../common';
11
11
  import { contributes } from '../../core';
12
- import { createIntent, useIntentDispatcher } from '../../plugin-intent';
12
+ import { createIntent } from '../../plugin-intent';
13
+ import { useIntentDispatcher } from '../../react';
13
14
 
14
15
  export const Logger = () => {
15
16
  const { dispatchPromise } = useIntentDispatcher();
@@ -19,8 +19,11 @@ export const LoggerPlugin = () =>
19
19
  activate: () => [
20
20
  contributes(
21
21
  Capabilities.IntentResolver,
22
- createResolver(Log, ({ message }) => {
23
- log.info(message);
22
+ createResolver({
23
+ intent: Log,
24
+ resolve: ({ message }) => {
25
+ log.info(message);
26
+ },
24
27
  }),
25
28
  ),
26
29
  ],
@@ -28,6 +31,6 @@ export const LoggerPlugin = () =>
28
31
  defineModule({
29
32
  id: 'dxos.org/test/logger/surfaces',
30
33
  activatesOn: Events.Startup,
31
- activate: () => Toolbar(),
34
+ activate: Toolbar,
32
35
  }),
33
36
  ]);
@@ -2,7 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { S } from '@dxos/echo-schema';
5
+ import { Schema as S } from '@effect/schema';
6
6
 
7
7
  export class Log extends S.TaggedClass<Log>()('dxos.org/test/logger/log', {
8
8
  input: S.Struct({
@@ -2,19 +2,9 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Effect } from 'effect';
6
- import React from 'react';
7
-
8
- import { create } from '@dxos/live-object';
9
-
10
- import { IntentProvider } from './IntentContext';
11
5
  import { INTENT_PLUGIN } from './actions';
12
- import { type AnyIntentResolver, createDispatcher, type IntentContext } from './intent-dispatcher';
13
- import { Capabilities, Events } from '../common';
14
- import { contributes, defineModule, definePlugin } from '../core';
15
-
16
- const defaultEffect = () => Effect.fail(new Error('Intent runtime not ready'));
17
- const defaultPromise = () => Effect.runPromise(defaultEffect());
6
+ import { Events } from '../common';
7
+ import { defineModule, definePlugin, lazy } from '../core';
18
8
 
19
9
  export const IntentPlugin = () =>
20
10
  definePlugin({ id: INTENT_PLUGIN }, [
@@ -24,37 +14,7 @@ export const IntentPlugin = () =>
24
14
  // This is fine for now because it's how it worked prior to capabilities api anyways.
25
15
  // In the future, the intent dispatcher should be able to be reset without resetting the entire app.
26
16
  activatesOn: Events.Startup,
27
- activatesBefore: [Events.SetupIntents],
28
17
  activatesAfter: [Events.DispatcherReady],
29
- activate: (context) => {
30
- const state = create<IntentContext>({
31
- dispatch: defaultEffect,
32
- dispatchPromise: defaultPromise,
33
- undo: defaultEffect,
34
- undoPromise: defaultPromise,
35
- });
36
-
37
- // TODO(wittjosiah): Make getResolver callback async and allow resolvers to be requested on demand.
38
- const { dispatch, dispatchPromise, undo, undoPromise } = createDispatcher((module) =>
39
- context
40
- .requestCapabilities(Capabilities.IntentResolver, (c, moduleId): c is AnyIntentResolver => {
41
- return module ? moduleId === module : true;
42
- })
43
- .flat(),
44
- );
45
-
46
- state.dispatch = dispatch;
47
- state.dispatchPromise = dispatchPromise;
48
- state.undo = undo;
49
- state.undoPromise = undoPromise;
50
-
51
- return [
52
- contributes(Capabilities.IntentDispatcher, state),
53
- contributes(Capabilities.ReactContext, {
54
- id: INTENT_PLUGIN,
55
- context: ({ children }) => <IntentProvider value={state}>{children}</IntentProvider>,
56
- }),
57
- ];
58
- },
18
+ activate: lazy(() => import('./intent-dispatcher')),
59
19
  }),
60
20
  ]);
@@ -2,7 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { S } from '@dxos/echo-schema';
5
+ import { Schema as S } from '@effect/schema';
6
6
 
7
7
  import { Label } from './intent';
8
8
 
@@ -5,5 +5,4 @@
5
5
  export * from './actions';
6
6
  export * from './intent';
7
7
  export * from './intent-dispatcher';
8
- export * from './IntentContext';
9
8
  export * from './IntentPlugin';
@@ -2,11 +2,10 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ import { Schema as S } from '@effect/schema';
5
6
  import { Effect, Fiber, pipe } from 'effect';
6
7
  import { describe, expect, test } from 'vitest';
7
8
 
8
- import { S } from '@dxos/echo-schema';
9
-
10
9
  import { chain, createIntent } from './intent';
11
10
  import { type AnyIntentResolver, createDispatcher, createResolver } from './intent-dispatcher';
12
11
 
@@ -108,8 +107,8 @@ describe('Intent dispatcher', () => {
108
107
  const { dispatch } = createDispatcher(() => [computeResolver, toStringResolver, concatResolver]);
109
108
  const intent = pipe(createIntent(Compute, { value: 1 }), chain(ToString, {}), chain(Concat, { plus: '!' }));
110
109
 
111
- expect(intent.first.action).toBe(Compute._tag);
112
- expect(intent.last.action).toBe(Concat._tag);
110
+ expect(intent.first.id).toBe(Compute._tag);
111
+ expect(intent.last.id).toBe(Concat._tag);
113
112
  expect(intent.all.length).toBe(3);
114
113
 
115
114
  const program = Effect.gen(function* () {
@@ -137,7 +136,10 @@ describe('Intent dispatcher', () => {
137
136
  });
138
137
 
139
138
  test('filter resolvers by plugin', async () => {
140
- const otherComputeResolver = createResolver(Compute, async (data) => ({ data: { value: data?.value * 3 } }));
139
+ const otherComputeResolver = createResolver({
140
+ intent: Compute,
141
+ resolve: async (data) => ({ data: { value: data?.value * 3 } }),
142
+ });
141
143
  const { dispatch } = createDispatcher((module) => (module === 'test' ? [computeResolver] : [otherComputeResolver]));
142
144
  const program = Effect.gen(function* () {
143
145
  const a = yield* dispatch(createIntent(Compute, { value: 1 }));
@@ -153,8 +155,10 @@ describe('Intent dispatcher', () => {
153
155
  });
154
156
 
155
157
  test('filter resolvers by predicate', async () => {
156
- const conditionalComputeResolver = createResolver(Compute, async (data) => ({ data: { value: data?.value * 3 } }), {
158
+ const conditionalComputeResolver = createResolver({
159
+ intent: Compute,
157
160
  filter: (data): data is { value: number } => data?.value > 1,
161
+ resolve: async (data) => ({ data: { value: data?.value * 3 } }),
158
162
  });
159
163
  const { dispatch } = createDispatcher(() => [conditionalComputeResolver, computeResolver]);
160
164
  const program = Effect.gen(function* () {
@@ -171,8 +175,10 @@ describe('Intent dispatcher', () => {
171
175
  });
172
176
 
173
177
  test('hoist resolvers', async () => {
174
- const hoistedComputeResolver = createResolver(Compute, async (data) => ({ data: { value: data?.value * 3 } }), {
175
- disposition: 'hoist',
178
+ const hoistedComputeResolver = createResolver({
179
+ intent: Compute,
180
+ position: 'hoist',
181
+ resolve: async (data) => ({ data: { value: data?.value * 3 } }),
176
182
  });
177
183
  const { dispatchPromise } = createDispatcher(() => [computeResolver, hoistedComputeResolver]);
178
184
  const { data } = await dispatchPromise(createIntent(Compute, { value: 1 }));
@@ -180,13 +186,17 @@ describe('Intent dispatcher', () => {
180
186
  });
181
187
 
182
188
  test('fallback resolvers', async () => {
183
- const conditionalComputeResolver = createResolver(Compute, async (data) => ({ data: { value: data?.value * 2 } }), {
189
+ const conditionalComputeResolver = createResolver({
190
+ intent: Compute,
184
191
  filter: (data): data is { value: number } => data?.value === 1,
192
+ resolve: async (data) => ({ data: { value: data?.value * 2 } }),
185
193
  });
186
- const fallbackComputeResolver = createResolver(Compute, async (data) => ({ data: { value: data?.value * 3 } }), {
187
- disposition: 'fallback',
194
+ const fallbackComputeResolver = createResolver({
195
+ intent: Compute,
196
+ position: 'fallback',
197
+ resolve: async (data) => ({ data: { value: data?.value * 3 } }),
188
198
  });
189
- const { dispatch } = createDispatcher(() => [fallbackComputeResolver, conditionalComputeResolver]);
199
+ const { dispatch } = createDispatcher(() => [conditionalComputeResolver, fallbackComputeResolver]);
190
200
  const program = Effect.gen(function* () {
191
201
  const a = yield* dispatch(createIntent(Compute, { value: 1 }));
192
202
 
@@ -224,8 +234,9 @@ class ToString extends S.TaggedClass<ToString>()('ToString', {
224
234
  }),
225
235
  }) {}
226
236
 
227
- const toStringResolver = createResolver(ToString, async (data) => {
228
- return { data: { string: data.value.toString() } };
237
+ const toStringResolver = createResolver({
238
+ intent: ToString,
239
+ resolve: async (data) => ({ data: { string: data.value.toString() } }),
229
240
  });
230
241
 
231
242
  class Compute extends S.TaggedClass<Compute>()('Compute', {
@@ -237,16 +248,19 @@ class Compute extends S.TaggedClass<Compute>()('Compute', {
237
248
  }),
238
249
  }) {}
239
250
 
240
- const computeResolver = createResolver(Compute, (data, undo) => {
241
- return Effect.gen(function* () {
242
- if (undo) {
243
- return { data: { value: data.value / 2 } };
244
- }
245
-
246
- yield* Effect.sleep(data.value * 10);
247
- const value = data.value * 2;
248
- return { data: { value }, undoable: { message: 'test', data: { value } } };
249
- });
251
+ const computeResolver = createResolver({
252
+ intent: Compute,
253
+ resolve: (data, undo) => {
254
+ return Effect.gen(function* () {
255
+ if (undo) {
256
+ return { data: { value: data.value / 2 } };
257
+ }
258
+
259
+ yield* Effect.sleep(data.value * 10);
260
+ const value = data.value * 2;
261
+ return { data: { value }, undoable: { message: 'test', data: { value } } };
262
+ });
263
+ },
250
264
  });
251
265
 
252
266
  class Concat extends S.TaggedClass<Concat>()('Concat', {
@@ -259,8 +273,9 @@ class Concat extends S.TaggedClass<Concat>()('Concat', {
259
273
  }),
260
274
  }) {}
261
275
 
262
- const concatResolver = createResolver(Concat, async (data) => {
263
- return { data: { string: data.string + data.plus } };
276
+ const concatResolver = createResolver({
277
+ intent: Concat,
278
+ resolve: async (data) => ({ data: { string: data.string + data.plus } }),
264
279
  });
265
280
 
266
281
  class Add extends S.TaggedClass<Add>()('Add', {
@@ -268,8 +283,9 @@ class Add extends S.TaggedClass<Add>()('Add', {
268
283
  output: S.Number,
269
284
  }) {}
270
285
 
271
- const addResolver = createResolver(Add, async (data) => {
272
- return { data: data[0] + data[1] };
286
+ const addResolver = createResolver({
287
+ intent: Add,
288
+ resolve: async (data) => ({ data: data[0] + data[1] }),
273
289
  });
274
290
 
275
291
  class SideEffect extends S.TaggedClass<SideEffect>()('SideEffect', {
@@ -277,4 +293,7 @@ class SideEffect extends S.TaggedClass<SideEffect>()('SideEffect', {
277
293
  output: S.Void,
278
294
  }) {}
279
295
 
280
- const sideEffectResolver = createResolver(SideEffect, async () => {});
296
+ const sideEffectResolver = createResolver({
297
+ intent: SideEffect,
298
+ resolve: async () => {},
299
+ });
@@ -5,7 +5,8 @@
5
5
  import { Effect, Option, pipe, Ref } from 'effect';
6
6
  import { type Simplify } from 'effect/Types';
7
7
 
8
- import { type MaybePromise, byDisposition, type Disposition } from '@dxos/util';
8
+ import { create } from '@dxos/live-object';
9
+ import { byPosition, type MaybePromise, type Position, type GuardedType } from '@dxos/util';
9
10
 
10
11
  import { IntentAction } from './actions';
11
12
  import { CycleDetectedError, NoResolversError } from './errors';
@@ -21,6 +22,8 @@ import {
21
22
  type IntentSchema,
22
23
  type Label,
23
24
  } from './intent';
25
+ import { Events, Capabilities } from '../common';
26
+ import { contributes, type PluginsContext } from '../core';
24
27
 
25
28
  const EXECUTION_LIMIT = 100;
26
29
  const HISTORY_LIMIT = 100;
@@ -28,13 +31,13 @@ const HISTORY_LIMIT = 100;
28
31
  /**
29
32
  * The return value of an intent effect.
30
33
  */
31
- export type IntentEffectResult<Fields extends IntentParams> = {
34
+ export type IntentEffectResult<Input, Output> = {
32
35
  /**
33
36
  * The output of the action that was performed.
34
37
  *
35
38
  * If the intent is apart of a chain of intents, the data will be passed to the next intent.
36
39
  */
37
- data?: IntentResultData<Fields>;
40
+ data?: Output;
38
41
 
39
42
  /**
40
43
  * If provided, the action will be undoable.
@@ -48,7 +51,7 @@ export type IntentEffectResult<Fields extends IntentParams> = {
48
51
  /**
49
52
  * Will be merged with the original intent data when firing the undo intent.
50
53
  */
51
- data?: Partial<IntentData<Fields>>;
54
+ data?: Partial<Input>;
52
55
  };
53
56
 
54
57
  /**
@@ -66,33 +69,32 @@ export type IntentEffectResult<Fields extends IntentParams> = {
66
69
  intents?: AnyIntentChain[];
67
70
  };
68
71
 
69
- export type AnyIntentEffectResult = IntentEffectResult<any>;
72
+ export type AnyIntentEffectResult = IntentEffectResult<any, any>;
70
73
 
71
74
  /**
72
75
  * The result of an intent dispatcher.
73
76
  */
74
- export type IntentDispatcherResult<Fields extends IntentParams> = Pick<IntentEffectResult<Fields>, 'data' | 'error'>;
77
+ export type IntentDispatcherResult<Input, Output> = Pick<IntentEffectResult<Input, Output>, 'data' | 'error'>;
75
78
 
76
79
  /**
77
80
  * The implementation of an intent effect.
78
81
  */
79
- export type IntentEffectDefinition<Fields extends IntentParams> = (
80
- data: IntentData<Fields>,
82
+ export type IntentEffectDefinition<Input, Output> = (
83
+ data: Input,
81
84
  undo: boolean,
82
- ) => MaybePromise<IntentEffectResult<Fields> | void> | Effect.Effect<IntentEffectResult<Fields> | void>;
85
+ ) => MaybePromise<IntentEffectResult<Input, Output> | void> | Effect.Effect<IntentEffectResult<Input, Output> | void>;
83
86
 
84
87
  /**
85
88
  * Intent resolver to match intents to their effects.
86
89
  */
87
- export type IntentResolver<Tag extends string, Fields extends IntentParams> = Readonly<{
88
- action: Tag;
89
- disposition?: Disposition;
90
- // TODO(wittjosiah): Would be nice to make this a guard for intents with optional data.
91
- filter?: (data: IntentData<Fields>) => boolean;
92
- effect: IntentEffectDefinition<Fields>;
90
+ export type IntentResolver<Tag extends string, Fields extends IntentParams, Data = IntentData<Fields>> = Readonly<{
91
+ intent: IntentSchema<Tag, Fields>;
92
+ position?: Position;
93
+ filter?: (data: IntentData<Fields>) => data is Data;
94
+ resolve: IntentEffectDefinition<GuardedType<IntentResolver<Tag, Fields, Data>['filter']>, IntentResultData<Fields>>;
93
95
  }>;
94
96
 
95
- export type AnyIntentResolver = IntentResolver<any, any>;
97
+ export type AnyIntentResolver = IntentResolver<any, any, any>;
96
98
 
97
99
  /**
98
100
  * Creates an intent resolver to match intents to their effects.
@@ -101,22 +103,16 @@ export type AnyIntentResolver = IntentResolver<any, any>;
101
103
  * @param params.disposition Determines the priority of the resolver when multiple are resolved.
102
104
  * @param params.filter Optional filter to determine if the resolver should be used.
103
105
  */
104
- export const createResolver = <Tag extends string, Fields extends IntentParams>(
105
- schema: IntentSchema<Tag, Fields>,
106
- effect: IntentEffectDefinition<Fields>,
107
- params: Pick<IntentResolver<Tag, Fields>, 'disposition' | 'filter'> = {},
108
- ): IntentResolver<Tag, Fields> => ({
109
- action: schema._tag,
110
- effect,
111
- ...params,
112
- });
106
+ export const createResolver = <Tag extends string, Fields extends IntentParams, Data = IntentData<Fields>>(
107
+ resolver: IntentResolver<Tag, Fields, Data>,
108
+ ) => resolver;
113
109
 
114
110
  /**
115
111
  * Invokes intents and returns the result.
116
112
  */
117
113
  export type PromiseIntentDispatcher = <Fields extends IntentParams>(
118
114
  intent: IntentChain<any, any, any, Fields>,
119
- ) => Promise<Simplify<IntentDispatcherResult<Fields>>>;
115
+ ) => Promise<Simplify<IntentDispatcherResult<IntentData<Fields>, IntentResultData<Fields>>>>;
120
116
 
121
117
  /**
122
118
  * Creates an effect for intents.
@@ -124,9 +120,15 @@ export type PromiseIntentDispatcher = <Fields extends IntentParams>(
124
120
  export type IntentDispatcher = <Fields extends IntentParams>(
125
121
  intent: IntentChain<any, any, any, Fields>,
126
122
  depth?: number,
127
- ) => Effect.Effect<Simplify<Required<IntentDispatcherResult<Fields>>['data']>, Error>;
128
-
129
- type IntentResult<Tag extends string, Fields extends IntentParams> = IntentEffectResult<Fields> & {
123
+ ) => Effect.Effect<
124
+ Simplify<Required<IntentDispatcherResult<IntentData<Fields>, IntentResultData<Fields>>>['data']>,
125
+ Error
126
+ >;
127
+
128
+ type IntentResult<Tag extends string, Fields extends IntentParams> = IntentEffectResult<
129
+ IntentData<Fields>,
130
+ IntentResultData<Fields>
131
+ > & {
130
132
  _intent: Intent<Tag, Fields>;
131
133
  };
132
134
 
@@ -135,7 +137,7 @@ export type AnyIntentResult = IntentResult<any, any>;
135
137
  /**
136
138
  * Invokes the most recent undoable intent with undo flags.
137
139
  */
138
- export type PromiseIntentUndo = () => Promise<IntentDispatcherResult<any>>;
140
+ export type PromiseIntentUndo = () => Promise<IntentDispatcherResult<any, any>>;
139
141
 
140
142
  /**
141
143
  * Creates an effect which undoes the last intent.
@@ -158,7 +160,7 @@ export type IntentContext = {
158
160
  /**
159
161
  * Sets of an intent dispatcher.
160
162
  *
161
- * @param resolvers An array of available intent resolvers.
163
+ * @param getResolvers A function that returns an array of available intent resolvers.
162
164
  * @param params.historyLimit The maximum number of intent results to keep in history.
163
165
  * @param params.executionLimit The maximum recursion depth of intent chains.
164
166
  */
@@ -168,24 +170,20 @@ export const createDispatcher = (
168
170
  ): IntentContext => {
169
171
  const historyRef = Effect.runSync(Ref.make<AnyIntentResult[][]>([]));
170
172
 
171
- const handleIntent = (intent: AnyIntent) => {
172
- return Effect.gen(function* () {
173
+ const handleIntent = (intent: AnyIntent) =>
174
+ Effect.gen(function* () {
173
175
  const candidates = getResolvers(intent.module)
174
- .filter((r) => r.action === intent.action)
176
+ .filter((r) => r.intent._tag === intent.id)
175
177
  .filter((r) => !r.filter || r.filter(intent.data))
176
- .toSorted(byDisposition);
178
+ .toSorted(byPosition);
177
179
  if (candidates.length === 0) {
178
- return {
179
- _intent: intent,
180
- error: new NoResolversError(intent.action),
181
- } as AnyIntentResult;
180
+ yield* Effect.fail(new NoResolversError(intent.id));
182
181
  }
183
182
 
184
- const effect = candidates[0].effect(intent.data, intent.undo ?? false);
183
+ const effect = candidates[0].resolve(intent.data, intent.undo ?? false);
185
184
  const result = Effect.isEffect(effect) ? yield* effect : yield* Effect.promise(async () => effect);
186
185
  return { _intent: intent, ...result } as AnyIntentResult;
187
186
  });
188
- };
189
187
 
190
188
  const dispatch: IntentDispatcher = (intentChain, depth = 0) => {
191
189
  return Effect.gen(function* () {
@@ -267,3 +265,40 @@ export const createDispatcher = (
267
265
 
268
266
  return { dispatch, dispatchPromise, undo, undoPromise };
269
267
  };
268
+
269
+ const defaultEffect = () => Effect.fail(new Error('Intent runtime not ready'));
270
+ const defaultPromise = () => Effect.runPromise(defaultEffect());
271
+
272
+ export default (context: PluginsContext) => {
273
+ const state = create<IntentContext>({
274
+ dispatch: defaultEffect,
275
+ dispatchPromise: defaultPromise,
276
+ undo: defaultEffect,
277
+ undoPromise: defaultPromise,
278
+ });
279
+
280
+ // TODO(wittjosiah): Make getResolver callback async and allow resolvers to be requested on demand.
281
+ const { dispatch, dispatchPromise, undo, undoPromise } = createDispatcher((module) =>
282
+ context
283
+ .requestCapabilities(Capabilities.IntentResolver, (c, moduleId): c is AnyIntentResolver => {
284
+ return module ? moduleId === module : true;
285
+ })
286
+ .flat(),
287
+ );
288
+
289
+ const manager = context.requestCapability(Capabilities.PluginManager);
290
+ state.dispatch = (intentChain, depth) => {
291
+ return Effect.gen(function* () {
292
+ yield* manager._activate(Events.SetupIntents);
293
+ return yield* dispatch(intentChain, depth);
294
+ });
295
+ };
296
+ state.dispatchPromise = async (intentChain) => {
297
+ await manager.activate(Events.SetupIntents);
298
+ return await dispatchPromise(intentChain);
299
+ };
300
+ state.undo = undo;
301
+ state.undoPromise = undoPromise;
302
+
303
+ return contributes(Capabilities.IntentDispatcher, state);
304
+ };
@@ -2,7 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { S } from '@dxos/echo-schema';
5
+ import { Schema as S } from '@effect/schema';
6
6
 
7
7
  export type IntentParams = {
8
8
  readonly input: S.Schema.All;
@@ -25,9 +25,9 @@ export type Intent<Tag extends string, Fields extends IntentParams> = {
25
25
  _schema: IntentSchema<Tag, Fields>;
26
26
 
27
27
  /**
28
- * The action to perform.
28
+ * The id of the intent.
29
29
  */
30
- action: Tag;
30
+ id: Tag;
31
31
 
32
32
  /**
33
33
  * Any data needed to perform the desired action.
@@ -84,7 +84,7 @@ export const createIntent = <Tag extends string, Fields extends IntentParams>(
84
84
  const intent = {
85
85
  ...params,
86
86
  _schema: schema,
87
- action: schema._tag,
87
+ id: schema._tag,
88
88
  data,
89
89
  } satisfies Intent<Tag, Fields>;
90
90
 
@@ -122,7 +122,7 @@ export const chain =
122
122
  const last = {
123
123
  ...params,
124
124
  _schema: schema,
125
- action: schema._tag,
125
+ id: schema._tag,
126
126
  data,
127
127
  } satisfies Intent<NextTag, NextFields>;
128
128
 
@@ -2,12 +2,12 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { RootSettingsStore } from '@dxos/local-storage';
6
-
7
5
  import { SETTINGS_PLUGIN } from './actions';
6
+ import translations from './translations';
8
7
  import { Capabilities, Events } from '../common';
9
- import { contributes, defineModule, definePlugin } from '../core';
8
+ import { contributes, defineModule, definePlugin, lazy } from '../core';
10
9
 
10
+ // TODO(wittjosiah): Add options to exclude some modules.
11
11
  export const SettingsPlugin = () =>
12
12
  definePlugin({ id: SETTINGS_PLUGIN }, [
13
13
  defineModule({
@@ -15,15 +15,21 @@ export const SettingsPlugin = () =>
15
15
  activatesOn: Events.Startup,
16
16
  activatesBefore: [Events.SetupSettings],
17
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
- },
18
+ activate: lazy(() => import('./store')),
19
+ }),
20
+ defineModule({
21
+ id: `${SETTINGS_PLUGIN}/module/translations`,
22
+ activatesOn: Events.SetupTranslations,
23
+ activate: () => contributes(Capabilities.Translations, translations),
24
+ }),
25
+ defineModule({
26
+ id: `${SETTINGS_PLUGIN}/module/intent-resolver`,
27
+ activatesOn: Events.SetupIntents,
28
+ activate: lazy(() => import('./intent-resolver')),
29
+ }),
30
+ defineModule({
31
+ id: `${SETTINGS_PLUGIN}/module/app-graph-builder`,
32
+ activatesOn: Events.SetupAppGraph,
33
+ activate: lazy(() => import('./app-graph-builder')),
28
34
  }),
29
35
  ]);