@dxos/app-framework 0.7.5-main.9d2a38b → 0.7.5-main.b19bfc8
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/app-graph-builder-S4MYSHQA.mjs +137 -0
- package/dist/lib/browser/app-graph-builder-S4MYSHQA.mjs.map +7 -0
- package/dist/lib/browser/{chunk-GNLU3GAU.mjs → chunk-BCMEJONP.mjs} +660 -823
- package/dist/lib/browser/chunk-BCMEJONP.mjs.map +7 -0
- package/dist/lib/browser/chunk-QS4J6O47.mjs +285 -0
- package/dist/lib/browser/chunk-QS4J6O47.mjs.map +7 -0
- package/dist/lib/browser/chunk-SRZH2PQ2.mjs +32 -0
- package/dist/lib/browser/chunk-SRZH2PQ2.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +57 -74
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/intent-dispatcher-GFBH7T2J.mjs +11 -0
- package/dist/lib/browser/intent-dispatcher-GFBH7T2J.mjs.map +7 -0
- package/dist/lib/browser/intent-resolver-KAFM7CQH.mjs +39 -0
- package/dist/lib/browser/intent-resolver-KAFM7CQH.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/store-L3VRR7II.mjs +29 -0
- package/dist/lib/browser/store-L3VRR7II.mjs.map +7 -0
- package/dist/lib/browser/testing/index.mjs +10 -3
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/worker.mjs +77 -0
- package/dist/lib/browser/worker.mjs.map +7 -0
- package/dist/lib/node/app-graph-builder-VMHWFCTP.cjs +146 -0
- package/dist/lib/node/app-graph-builder-VMHWFCTP.cjs.map +7 -0
- package/dist/lib/node/chunk-7Y6KJ3OK.cjs +1466 -0
- package/dist/lib/node/chunk-7Y6KJ3OK.cjs.map +7 -0
- package/dist/lib/node/chunk-B65NJEIJ.cjs +308 -0
- package/dist/lib/node/chunk-B65NJEIJ.cjs.map +7 -0
- package/dist/lib/node/chunk-VCIHQZSN.cjs +58 -0
- package/dist/lib/node/chunk-VCIHQZSN.cjs.map +7 -0
- package/dist/lib/node/index.cjs +106 -118
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/intent-dispatcher-PRCC4KZT.cjs +32 -0
- package/dist/lib/node/intent-dispatcher-PRCC4KZT.cjs.map +7 -0
- package/dist/lib/node/intent-resolver-OZDKCHPW.cjs +46 -0
- package/dist/lib/node/intent-resolver-OZDKCHPW.cjs.map +7 -0
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/store-BVUKNVKL.cjs +44 -0
- package/dist/lib/node/store-BVUKNVKL.cjs.map +7 -0
- package/dist/lib/node/testing/index.cjs +15 -8
- package/dist/lib/node/testing/index.cjs.map +3 -3
- package/dist/lib/node/worker.cjs +99 -0
- package/dist/lib/node/worker.cjs.map +7 -0
- package/dist/lib/node-esm/app-graph-builder-XHI5IIXQ.mjs +138 -0
- package/dist/lib/node-esm/app-graph-builder-XHI5IIXQ.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-CBT75GCX.mjs +34 -0
- package/dist/lib/node-esm/chunk-CBT75GCX.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-JDAHZRYQ.mjs +286 -0
- package/dist/lib/node-esm/chunk-JDAHZRYQ.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-KPMTPXQI.mjs → chunk-TVIR2PHY.mjs} +660 -823
- package/dist/lib/node-esm/chunk-TVIR2PHY.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +57 -74
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/intent-dispatcher-LGACN32C.mjs +12 -0
- package/dist/lib/node-esm/intent-dispatcher-LGACN32C.mjs.map +7 -0
- package/dist/lib/node-esm/intent-resolver-RBNG76ZX.mjs +40 -0
- package/dist/lib/node-esm/intent-resolver-RBNG76ZX.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/store-PHTOEREN.mjs +30 -0
- package/dist/lib/node-esm/store-PHTOEREN.mjs.map +7 -0
- package/dist/lib/node-esm/testing/index.mjs +10 -3
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/worker.mjs +78 -0
- package/dist/lib/node-esm/worker.mjs.map +7 -0
- package/dist/types/src/App.d.ts +2 -2
- package/dist/types/src/App.d.ts.map +1 -1
- package/dist/types/src/common/capabilities.d.ts +88 -124
- package/dist/types/src/common/capabilities.d.ts.map +1 -1
- package/dist/types/src/common/events.d.ts +22 -11
- package/dist/types/src/common/events.d.ts.map +1 -1
- package/dist/types/src/common/file.d.ts +1 -1
- package/dist/types/src/common/file.d.ts.map +1 -1
- package/dist/types/src/common/graph.d.ts +2 -2
- package/dist/types/src/common/graph.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/layout.d.ts +218 -121
- package/dist/types/src/common/layout.d.ts.map +1 -1
- package/dist/types/src/common/surface.d.ts +3 -3
- package/dist/types/src/common/surface.d.ts.map +1 -1
- package/dist/types/src/common/translations.d.ts +7 -7
- package/dist/types/src/common/translations.d.ts.map +1 -1
- package/dist/types/src/core/capabilities.d.ts +6 -2
- package/dist/types/src/core/capabilities.d.ts.map +1 -1
- package/dist/types/src/core/manager.d.ts +2 -9
- package/dist/types/src/core/manager.d.ts.map +1 -1
- package/dist/types/src/core/plugin.d.ts +5 -2
- package/dist/types/src/core/plugin.d.ts.map +1 -1
- package/dist/types/src/playground/debug/Debug.d.ts +2 -3
- package/dist/types/src/playground/debug/Debug.d.ts.map +1 -1
- package/dist/types/src/playground/debug/plugin.d.ts +1 -1
- package/dist/types/src/playground/debug/plugin.d.ts.map +1 -1
- package/dist/types/src/playground/generator/Main.d.ts +2 -3
- package/dist/types/src/playground/generator/Main.d.ts.map +1 -1
- package/dist/types/src/playground/generator/Toolbar.d.ts +2 -3
- package/dist/types/src/playground/generator/Toolbar.d.ts.map +1 -1
- package/dist/types/src/playground/generator/generator.d.ts +5 -3
- package/dist/types/src/playground/generator/generator.d.ts.map +1 -1
- package/dist/types/src/playground/generator/plugin.d.ts +1 -1
- package/dist/types/src/playground/generator/plugin.d.ts.map +1 -1
- package/dist/types/src/playground/layout/Layout.d.ts +2 -2
- package/dist/types/src/playground/layout/Layout.d.ts.map +1 -1
- package/dist/types/src/playground/layout/plugin.d.ts +1 -1
- package/dist/types/src/playground/layout/plugin.d.ts.map +1 -1
- package/dist/types/src/playground/logger/Toolbar.d.ts +2 -3
- package/dist/types/src/playground/logger/Toolbar.d.ts.map +1 -1
- package/dist/types/src/playground/logger/plugin.d.ts +1 -1
- package/dist/types/src/playground/logger/plugin.d.ts.map +1 -1
- package/dist/types/src/playground/logger/schema.d.ts +1 -1
- package/dist/types/src/playground/logger/schema.d.ts.map +1 -1
- package/dist/types/src/playground/playground.stories.d.ts +2 -3
- package/dist/types/src/playground/playground.stories.d.ts.map +1 -1
- package/dist/types/src/plugin-intent/IntentPlugin.d.ts +1 -1
- package/dist/types/src/plugin-intent/IntentPlugin.d.ts.map +1 -1
- package/dist/types/src/plugin-intent/actions.d.ts +1 -1
- package/dist/types/src/plugin-intent/actions.d.ts.map +1 -1
- package/dist/types/src/plugin-intent/index.d.ts +0 -1
- package/dist/types/src/plugin-intent/index.d.ts.map +1 -1
- package/dist/types/src/plugin-intent/intent-dispatcher.d.ts +27 -20
- package/dist/types/src/plugin-intent/intent-dispatcher.d.ts.map +1 -1
- package/dist/types/src/plugin-intent/intent.d.ts +3 -3
- package/dist/types/src/plugin-intent/intent.d.ts.map +1 -1
- package/dist/types/src/plugin-settings/SettingsPlugin.d.ts +1 -1
- package/dist/types/src/plugin-settings/SettingsPlugin.d.ts.map +1 -1
- package/dist/types/src/plugin-settings/actions.d.ts +11 -1
- package/dist/types/src/plugin-settings/actions.d.ts.map +1 -1
- package/dist/types/src/plugin-settings/app-graph-builder.d.ts +197 -0
- package/dist/types/src/plugin-settings/app-graph-builder.d.ts.map +1 -0
- package/dist/types/src/plugin-settings/intent-resolver.d.ts +4 -0
- package/dist/types/src/plugin-settings/intent-resolver.d.ts.map +1 -0
- package/dist/types/src/plugin-settings/store.d.ts +5 -0
- package/dist/types/src/plugin-settings/store.d.ts.map +1 -0
- package/dist/types/src/plugin-settings/translations.d.ts +11 -0
- package/dist/types/src/plugin-settings/translations.d.ts.map +1 -0
- package/dist/types/src/react/ErrorBoundary.d.ts +1 -1
- package/dist/types/src/{plugin-intent → react}/IntentContext.d.ts +1 -1
- package/dist/types/src/react/IntentContext.d.ts.map +1 -0
- package/dist/types/src/react/Surface.d.ts.map +1 -1
- package/dist/types/src/react/Surface.stories.d.ts +15 -0
- package/dist/types/src/react/Surface.stories.d.ts.map +1 -0
- package/dist/types/src/react/common.d.ts +13 -0
- package/dist/types/src/react/common.d.ts.map +1 -0
- package/dist/types/src/react/index.d.ts +2 -0
- package/dist/types/src/react/index.d.ts.map +1 -1
- package/dist/types/src/react/useCapabilities.d.ts.map +1 -1
- package/dist/types/src/react/useIntentResolver.d.ts +3 -0
- package/dist/types/src/react/useIntentResolver.d.ts.map +1 -0
- package/dist/types/src/testing/withPluginManager.d.ts +1 -1
- package/dist/types/src/testing/withPluginManager.d.ts.map +1 -1
- package/dist/types/src/worker.d.ts +4 -0
- package/dist/types/src/worker.d.ts.map +1 -0
- package/package.json +29 -21
- package/project.json +4 -3
- package/src/App.tsx +17 -15
- package/src/common/capabilities.ts +30 -11
- package/src/common/events.ts +16 -2
- package/src/common/file.ts +1 -1
- package/src/common/graph.ts +2 -2
- package/src/common/index.ts +0 -1
- package/src/common/layout.ts +207 -126
- package/src/common/surface.ts +2 -2
- package/src/common/translations.ts +7 -8
- package/src/core/capabilities.ts +16 -7
- package/src/core/manager.test.ts +22 -73
- package/src/core/manager.ts +105 -91
- package/src/core/plugin.ts +6 -3
- package/src/playground/debug/plugin.ts +1 -1
- package/src/playground/generator/Toolbar.tsx +11 -11
- package/src/playground/generator/generator.ts +25 -0
- package/src/playground/generator/plugin.ts +6 -1
- package/src/playground/layout/plugin.ts +1 -1
- package/src/playground/logger/Toolbar.tsx +2 -1
- package/src/playground/logger/plugin.ts +7 -4
- package/src/playground/logger/schema.ts +1 -1
- package/src/plugin-intent/IntentPlugin.tsx +3 -43
- package/src/plugin-intent/actions.ts +1 -1
- package/src/plugin-intent/errors.ts +1 -1
- package/src/plugin-intent/index.ts +0 -1
- package/src/plugin-intent/intent-dispatcher.test.ts +48 -29
- package/src/plugin-intent/intent-dispatcher.ts +81 -42
- package/src/plugin-intent/intent.ts +5 -5
- package/src/plugin-settings/SettingsPlugin.ts +19 -13
- package/src/plugin-settings/actions.ts +11 -1
- package/src/plugin-settings/app-graph-builder.ts +122 -0
- package/src/plugin-settings/intent-resolver.ts +34 -0
- package/src/plugin-settings/store.ts +30 -0
- package/src/plugin-settings/translations.ts +17 -0
- package/src/{plugin-intent → react}/IntentContext.tsx +2 -2
- package/src/react/Surface.stories.tsx +96 -0
- package/src/react/Surface.tsx +11 -8
- package/src/react/common.ts +12 -0
- package/src/react/index.ts +2 -0
- package/src/react/useCapabilities.ts +1 -0
- package/src/react/useIntentResolver.ts +22 -0
- package/src/testing/withPluginManager.tsx +11 -3
- package/src/worker.ts +11 -0
- package/tsconfig.json +7 -13
- package/dist/lib/browser/chunk-GNLU3GAU.mjs.map +0 -7
- package/dist/lib/node/chunk-FBA4BB3J.cjs +0 -1639
- package/dist/lib/node/chunk-FBA4BB3J.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-KPMTPXQI.mjs.map +0 -7
- package/dist/types/src/common/navigation.d.ts +0 -241
- package/dist/types/src/common/navigation.d.ts.map +0 -1
- package/dist/types/src/plugin-intent/IntentContext.d.ts.map +0 -1
- package/src/common/navigation.ts +0 -199
- /package/{.eslintrc.js → .eslintrc.cjs} +0 -0
package/src/core/manager.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { untracked } from '@preact/signals-core';
|
|
6
|
-
import { Effect, Either, Match } from 'effect';
|
|
6
|
+
import { Array as A, Effect, Either, Match, pipe } from 'effect';
|
|
7
7
|
|
|
8
8
|
import { Event } from '@dxos/async';
|
|
9
9
|
import { create, type ReactiveObject } from '@dxos/live-object';
|
|
@@ -35,7 +35,6 @@ type PluginManagerState = {
|
|
|
35
35
|
// Modules
|
|
36
36
|
modules: PluginModule[];
|
|
37
37
|
active: string[];
|
|
38
|
-
pendingRemoval: string[];
|
|
39
38
|
|
|
40
39
|
// Events
|
|
41
40
|
eventsFired: string[];
|
|
@@ -67,7 +66,6 @@ export class PluginManager {
|
|
|
67
66
|
enabled,
|
|
68
67
|
modules: [],
|
|
69
68
|
active: [],
|
|
70
|
-
pendingRemoval: [],
|
|
71
69
|
pendingReset: [],
|
|
72
70
|
eventsFired: [],
|
|
73
71
|
});
|
|
@@ -121,15 +119,6 @@ export class PluginManager {
|
|
|
121
119
|
return this._state.active;
|
|
122
120
|
}
|
|
123
121
|
|
|
124
|
-
/**
|
|
125
|
-
* Ids of modules which are pending removal.
|
|
126
|
-
*
|
|
127
|
-
* @reactive
|
|
128
|
-
*/
|
|
129
|
-
get pendingRemoval(): ReactiveObject<readonly string[]> {
|
|
130
|
-
return this._state.pendingRemoval;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
122
|
/**
|
|
134
123
|
* Ids of events which have been fired.
|
|
135
124
|
*
|
|
@@ -165,8 +154,8 @@ export class PluginManager {
|
|
|
165
154
|
* Enables a plugin.
|
|
166
155
|
* @param id The id of the plugin.
|
|
167
156
|
*/
|
|
168
|
-
enable(id: string): boolean {
|
|
169
|
-
return untracked(() => {
|
|
157
|
+
enable(id: string): Promise<boolean> {
|
|
158
|
+
return untracked(async () => {
|
|
170
159
|
log('enable plugin', { id });
|
|
171
160
|
const plugin = this._getPlugin(id);
|
|
172
161
|
if (!plugin) {
|
|
@@ -181,6 +170,15 @@ export class PluginManager {
|
|
|
181
170
|
this._addModule(module);
|
|
182
171
|
this._setPendingResetByModule(module);
|
|
183
172
|
});
|
|
173
|
+
|
|
174
|
+
log('pending reset', { events: [...this.pendingReset] });
|
|
175
|
+
await Effect.runPromise(
|
|
176
|
+
Effect.all(
|
|
177
|
+
this.pendingReset.map((event) => this._activate(event)),
|
|
178
|
+
{ concurrency: 'unbounded' },
|
|
179
|
+
),
|
|
180
|
+
);
|
|
181
|
+
|
|
184
182
|
return true;
|
|
185
183
|
});
|
|
186
184
|
}
|
|
@@ -206,8 +204,8 @@ export class PluginManager {
|
|
|
206
204
|
* Disables a plugin.
|
|
207
205
|
* @param id The id of the plugin.
|
|
208
206
|
*/
|
|
209
|
-
disable(id: string): boolean {
|
|
210
|
-
return untracked(() => {
|
|
207
|
+
disable(id: string): Promise<boolean> {
|
|
208
|
+
return untracked(async () => {
|
|
211
209
|
log('disable plugin', { id });
|
|
212
210
|
if (this._state.core.includes(id)) {
|
|
213
211
|
return false;
|
|
@@ -221,16 +219,10 @@ export class PluginManager {
|
|
|
221
219
|
const enabledIndex = this._state.enabled.findIndex((enabled) => enabled === id);
|
|
222
220
|
if (enabledIndex !== -1) {
|
|
223
221
|
this._state.enabled.splice(enabledIndex, 1);
|
|
222
|
+
await Effect.runPromise(this._deactivate(id));
|
|
224
223
|
|
|
225
224
|
plugin.modules.forEach((module) => {
|
|
226
|
-
|
|
227
|
-
this._setPendingResetByModule(module);
|
|
228
|
-
if (!this._state.pendingRemoval.includes(module.id)) {
|
|
229
|
-
this._state.pendingRemoval.push(module.id);
|
|
230
|
-
}
|
|
231
|
-
} else {
|
|
232
|
-
this._removeModule(module.id);
|
|
233
|
-
}
|
|
225
|
+
this._removeModule(module.id);
|
|
234
226
|
});
|
|
235
227
|
}
|
|
236
228
|
|
|
@@ -328,14 +320,8 @@ export class PluginManager {
|
|
|
328
320
|
const activationEvents = getEvents(module.activatesOn)
|
|
329
321
|
.map(eventKey)
|
|
330
322
|
.filter((key) => this._state.eventsFired.includes(key));
|
|
331
|
-
const parentEvents = activationEvents.flatMap((event) => {
|
|
332
|
-
const modules = this._getActiveModules().filter((module) =>
|
|
333
|
-
module.activatesBefore?.map(eventKey).includes(event),
|
|
334
|
-
);
|
|
335
|
-
return modules.flatMap((module) => getEvents(module.activatesOn)).map(eventKey);
|
|
336
|
-
});
|
|
337
323
|
|
|
338
|
-
const pendingReset = Array.from(new Set(
|
|
324
|
+
const pendingReset = Array.from(new Set(activationEvents)).filter(
|
|
339
325
|
(event) => !this._state.pendingReset.includes(event),
|
|
340
326
|
);
|
|
341
327
|
if (pendingReset.length > 0) {
|
|
@@ -345,64 +331,91 @@ export class PluginManager {
|
|
|
345
331
|
});
|
|
346
332
|
}
|
|
347
333
|
|
|
334
|
+
/**
|
|
335
|
+
* @internal
|
|
336
|
+
*/
|
|
348
337
|
// TODO(wittjosiah): Improve error typing.
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
return Effect.gen(function* () {
|
|
338
|
+
_activate(event: ActivationEvent | string): Effect.Effect<boolean, Error> {
|
|
339
|
+
return Effect.gen(this, function* () {
|
|
352
340
|
const key = typeof event === 'string' ? event : eventKey(event);
|
|
353
341
|
log('activating', { key });
|
|
354
|
-
const pendingIndex =
|
|
342
|
+
const pendingIndex = this._state.pendingReset.findIndex((event) => event === key);
|
|
355
343
|
if (pendingIndex !== -1) {
|
|
356
|
-
|
|
344
|
+
this._state.pendingReset.splice(pendingIndex, 1);
|
|
357
345
|
}
|
|
358
346
|
|
|
359
|
-
const modules =
|
|
347
|
+
const modules = this._getInactiveModulesByEvent(key).filter((module) => {
|
|
348
|
+
const allOf = isAllOf(module.activatesOn);
|
|
349
|
+
if (!allOf) {
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const events = module.activatesOn.events.filter((event) => eventKey(event) !== key);
|
|
354
|
+
return events.every((event) => this._state.eventsFired.includes(eventKey(event)));
|
|
355
|
+
});
|
|
360
356
|
if (modules.length === 0) {
|
|
361
357
|
log('no modules to activate', { key });
|
|
358
|
+
if (!this._state.eventsFired.includes(key)) {
|
|
359
|
+
this._state.eventsFired.push(key);
|
|
360
|
+
}
|
|
362
361
|
return false;
|
|
363
362
|
}
|
|
364
363
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
for (const module of modules) {
|
|
368
|
-
if (
|
|
369
|
-
isAllOf(module.activatesOn) &&
|
|
370
|
-
!module.activatesOn.events
|
|
371
|
-
.filter((event) => eventKey(event) !== key)
|
|
372
|
-
.every((event) => self._state.eventsFired.includes(eventKey(event)))
|
|
373
|
-
) {
|
|
374
|
-
continue;
|
|
375
|
-
}
|
|
364
|
+
log('activating modules', { key, modules: modules.map((module) => module.id) });
|
|
365
|
+
this.activation.emit({ event: key, state: 'activating' });
|
|
376
366
|
|
|
377
|
-
|
|
367
|
+
// Concurrently triggers loading of lazy capabilities.
|
|
368
|
+
const getCapabilities = yield* Effect.all(
|
|
369
|
+
modules.map(({ activate }) =>
|
|
370
|
+
Effect.tryPromise({
|
|
371
|
+
try: async () => activate(this.context),
|
|
372
|
+
catch: (error) => error as Error,
|
|
373
|
+
}),
|
|
374
|
+
),
|
|
375
|
+
{ concurrency: 'unbounded' },
|
|
376
|
+
);
|
|
378
377
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
378
|
+
const result = yield* pipe(
|
|
379
|
+
modules,
|
|
380
|
+
A.zip(getCapabilities),
|
|
381
|
+
A.map(([module, getCapabilities]) => this._activateModule(module, getCapabilities)),
|
|
382
|
+
// TODO(wittjosiah): This currently can't be run in parallel.
|
|
383
|
+
// Running this with concurrency causes races with `allOf` activation events.
|
|
384
|
+
Effect.all,
|
|
385
|
+
Effect.either,
|
|
386
|
+
);
|
|
384
387
|
|
|
385
|
-
|
|
388
|
+
if (Either.isLeft(result)) {
|
|
389
|
+
this.activation.emit({ event: key, state: 'error', error: result.left });
|
|
390
|
+
yield* Effect.fail(result.left);
|
|
386
391
|
}
|
|
387
392
|
|
|
388
|
-
if (!
|
|
389
|
-
|
|
393
|
+
if (!this._state.eventsFired.includes(key)) {
|
|
394
|
+
this._state.eventsFired.push(key);
|
|
390
395
|
}
|
|
391
396
|
|
|
392
|
-
|
|
397
|
+
this.activation.emit({ event: key, state: 'activated' });
|
|
393
398
|
log('activated', { key });
|
|
394
399
|
|
|
395
400
|
return true;
|
|
396
401
|
});
|
|
397
402
|
}
|
|
398
403
|
|
|
399
|
-
private _activateModule(
|
|
400
|
-
|
|
401
|
-
|
|
404
|
+
private _activateModule(
|
|
405
|
+
module: PluginModule,
|
|
406
|
+
getCapabilities: AnyCapability | AnyCapability[] | (() => Promise<AnyCapability | AnyCapability[]>),
|
|
407
|
+
): Effect.Effect<void, Error> {
|
|
408
|
+
return Effect.gen(this, function* () {
|
|
409
|
+
yield* Effect.all(module.activatesBefore?.map((event) => this._activate(event)) ?? [], {
|
|
410
|
+
concurrency: 'unbounded',
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
log('activating module...', { module: module.id });
|
|
402
414
|
// TODO(wittjosiah): This is not handling errors thrown if this is synchronous.
|
|
403
|
-
const
|
|
404
|
-
const
|
|
405
|
-
|
|
415
|
+
const maybeCapabilities = typeof getCapabilities === 'function' ? getCapabilities() : getCapabilities;
|
|
416
|
+
const resolvedCapabilities = yield* Match.value(maybeCapabilities).pipe(
|
|
417
|
+
// TODO(wittjosiah): Activate with an effect?
|
|
418
|
+
// Match.when(Effect.isEffect, (effect) => effect),
|
|
406
419
|
Match.when(isPromise, (promise) =>
|
|
407
420
|
Effect.tryPromise({
|
|
408
421
|
try: () => promise,
|
|
@@ -411,42 +424,48 @@ export class PluginManager {
|
|
|
411
424
|
),
|
|
412
425
|
Match.orElse((program) => Effect.succeed(program)),
|
|
413
426
|
);
|
|
414
|
-
const capabilities = Match.value(
|
|
427
|
+
const capabilities = Match.value(resolvedCapabilities).pipe(
|
|
415
428
|
Match.when(Array.isArray, (array) => array),
|
|
416
429
|
Match.orElse((value) => [value]),
|
|
417
430
|
);
|
|
418
431
|
capabilities.forEach((capability) => {
|
|
419
|
-
|
|
432
|
+
this.context.contributeCapability({ module: module.id, ...capability });
|
|
433
|
+
});
|
|
434
|
+
this._state.active.push(module.id);
|
|
435
|
+
this._capabilities.set(module.id, capabilities);
|
|
436
|
+
log('activated module', { module: module.id });
|
|
437
|
+
|
|
438
|
+
yield* Effect.all(module.activatesAfter?.map((event) => this._activate(event)) ?? [], {
|
|
439
|
+
concurrency: 'unbounded',
|
|
420
440
|
});
|
|
421
|
-
self._state.active.push(module.id);
|
|
422
|
-
self._capabilities.set(module.id, capabilities);
|
|
423
441
|
});
|
|
424
442
|
}
|
|
425
443
|
|
|
426
444
|
private _deactivate(id: string): Effect.Effect<boolean, Error> {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
const plugin = self._getPlugin(id);
|
|
445
|
+
return Effect.gen(this, function* () {
|
|
446
|
+
const plugin = this._getPlugin(id);
|
|
430
447
|
if (!plugin) {
|
|
431
448
|
return false;
|
|
432
449
|
}
|
|
433
450
|
|
|
434
451
|
const modules = plugin.modules;
|
|
435
|
-
const results = yield* Effect.all(
|
|
452
|
+
const results = yield* Effect.all(
|
|
453
|
+
modules.map((module) => this._deactivateModule(module)),
|
|
454
|
+
{ concurrency: 'unbounded' },
|
|
455
|
+
);
|
|
436
456
|
return results.every((result) => result);
|
|
437
457
|
});
|
|
438
458
|
}
|
|
439
459
|
|
|
440
460
|
private _deactivateModule(module: PluginModule): Effect.Effect<boolean, Error> {
|
|
441
|
-
|
|
442
|
-
return Effect.gen(function* () {
|
|
461
|
+
return Effect.gen(this, function* () {
|
|
443
462
|
const id = module.id;
|
|
444
463
|
log('deactivating', { id });
|
|
445
464
|
|
|
446
|
-
const capabilities =
|
|
465
|
+
const capabilities = this._capabilities.get(id);
|
|
447
466
|
if (capabilities) {
|
|
448
467
|
for (const capability of capabilities) {
|
|
449
|
-
|
|
468
|
+
this.context.removeCapability(capability.interface, capability.implementation);
|
|
450
469
|
const program = capability.deactivate?.();
|
|
451
470
|
yield* Match.value(program).pipe(
|
|
452
471
|
Match.when(Effect.isEffect, (effect) => effect),
|
|
@@ -459,12 +478,12 @@ export class PluginManager {
|
|
|
459
478
|
Match.orElse((program) => Effect.succeed(program)),
|
|
460
479
|
);
|
|
461
480
|
}
|
|
462
|
-
|
|
481
|
+
this._capabilities.delete(id);
|
|
463
482
|
}
|
|
464
483
|
|
|
465
|
-
const activeIndex =
|
|
484
|
+
const activeIndex = this._state.active.findIndex((event) => event === id);
|
|
466
485
|
if (activeIndex !== -1) {
|
|
467
|
-
|
|
486
|
+
this._state.active.splice(activeIndex, 1);
|
|
468
487
|
}
|
|
469
488
|
|
|
470
489
|
log('deactivated', { id });
|
|
@@ -473,22 +492,17 @@ export class PluginManager {
|
|
|
473
492
|
}
|
|
474
493
|
|
|
475
494
|
private _reset(event: ActivationEvent | string): Effect.Effect<boolean, Error> {
|
|
476
|
-
|
|
477
|
-
return Effect.gen(function* () {
|
|
495
|
+
return Effect.gen(this, function* () {
|
|
478
496
|
const key = typeof event === 'string' ? event : eventKey(event);
|
|
479
497
|
log('reset', { key });
|
|
480
|
-
const modules =
|
|
481
|
-
const results = yield* Effect.all(
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
self._removeModule(id);
|
|
486
|
-
});
|
|
487
|
-
self._state.pendingRemoval.splice(0, self._state.pendingRemoval.length);
|
|
488
|
-
}
|
|
498
|
+
const modules = this._getActiveModulesByEvent(key);
|
|
499
|
+
const results = yield* Effect.all(
|
|
500
|
+
modules.map((module) => this._deactivateModule(module)),
|
|
501
|
+
{ concurrency: 'unbounded' },
|
|
502
|
+
);
|
|
489
503
|
|
|
490
504
|
if (results.every((result) => result)) {
|
|
491
|
-
return yield*
|
|
505
|
+
return yield* this._activate(key);
|
|
492
506
|
} else {
|
|
493
507
|
return false;
|
|
494
508
|
}
|
package/src/core/plugin.ts
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { type Effect } from 'effect';
|
|
6
|
-
|
|
7
5
|
import { type MaybePromise } from '@dxos/util';
|
|
8
6
|
|
|
9
7
|
import { type AnyCapability, type PluginsContext } from './capabilities';
|
|
@@ -39,7 +37,7 @@ interface PluginModuleInterface {
|
|
|
39
37
|
*/
|
|
40
38
|
activate: (
|
|
41
39
|
context: PluginsContext,
|
|
42
|
-
) => MaybePromise<AnyCapability | AnyCapability[]> |
|
|
40
|
+
) => MaybePromise<AnyCapability | AnyCapability[]> | Promise<() => Promise<AnyCapability | AnyCapability[]>>;
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
/**
|
|
@@ -98,6 +96,11 @@ export type PluginMeta = {
|
|
|
98
96
|
*/
|
|
99
97
|
source?: string;
|
|
100
98
|
|
|
99
|
+
/**
|
|
100
|
+
* URL of screenshot.
|
|
101
|
+
*/
|
|
102
|
+
screenshots?: string[];
|
|
103
|
+
|
|
101
104
|
/**
|
|
102
105
|
* Tags to help categorize the plugin.
|
|
103
106
|
*/
|
|
@@ -6,34 +6,34 @@ import React, { useCallback } from 'react';
|
|
|
6
6
|
|
|
7
7
|
import { Button } from '@dxos/react-ui';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { createGeneratorIntent, createPluginId, Number } from './generator';
|
|
10
10
|
import { Capabilities } from '../../common';
|
|
11
11
|
import { contributes } from '../../core';
|
|
12
|
-
import {
|
|
12
|
+
import { createIntent } from '../../plugin-intent';
|
|
13
|
+
import { useCapabilities, useIntentDispatcher, usePluginManager } from '../../react';
|
|
13
14
|
|
|
14
15
|
export const Toolbar = () => {
|
|
15
16
|
const manager = usePluginManager();
|
|
17
|
+
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
16
18
|
|
|
17
19
|
const handleAdd = useCallback(async () => {
|
|
18
20
|
const id = createPluginId(Math.random().toString(16).substring(2, 8));
|
|
19
21
|
await manager.add(id);
|
|
20
22
|
}, [manager]);
|
|
21
23
|
|
|
22
|
-
const
|
|
23
|
-
if (manager.pendingReset.includes(CountEvent.id)) {
|
|
24
|
-
await manager.reset(CountEvent);
|
|
25
|
-
} else {
|
|
26
|
-
await manager.activate(CountEvent);
|
|
27
|
-
}
|
|
28
|
-
}, [manager]);
|
|
24
|
+
const count = useCapabilities(Number).reduce((acc, curr) => acc + curr, 0);
|
|
29
25
|
|
|
30
|
-
const
|
|
26
|
+
const generatorPlugins = manager.plugins.filter((plugin) => plugin.meta.id.startsWith('dxos.org/test/generator/'));
|
|
31
27
|
|
|
32
28
|
return (
|
|
33
29
|
<>
|
|
34
30
|
<Button onClick={handleAdd}>Add</Button>
|
|
35
|
-
<Button onClick={handleCount}>Count</Button>
|
|
36
31
|
<div className='flex items-center'>Count: {count}</div>
|
|
32
|
+
{generatorPlugins.map((plugin) => (
|
|
33
|
+
<Button key={plugin.meta.id} onClick={() => dispatch(createIntent(createGeneratorIntent(plugin.meta.id)))}>
|
|
34
|
+
{plugin.meta.id.replace('dxos.org/test/generator/', '')}
|
|
35
|
+
</Button>
|
|
36
|
+
))}
|
|
37
37
|
</>
|
|
38
38
|
);
|
|
39
39
|
};
|
|
@@ -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.SetupIntentResolver,
|
|
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:
|
|
16
|
+
activate: Main,
|
|
17
|
+
}),
|
|
18
|
+
defineModule({
|
|
19
|
+
id: 'dxos.org/test/generator/toolbar',
|
|
20
|
+
activatesOn: Events.Startup,
|
|
21
|
+
activate: Toolbar,
|
|
17
22
|
}),
|
|
18
23
|
]);
|
|
@@ -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
|
|
12
|
+
import { createIntent } from '../../plugin-intent';
|
|
13
|
+
import { useIntentDispatcher } from '../../react';
|
|
13
14
|
|
|
14
15
|
export const Logger = () => {
|
|
15
16
|
const { dispatchPromise } = useIntentDispatcher();
|
|
@@ -15,12 +15,15 @@ export const LoggerPlugin = () =>
|
|
|
15
15
|
definePlugin({ id: 'dxos.org/test/logger' }, [
|
|
16
16
|
defineModule({
|
|
17
17
|
id: 'dxos.org/test/logger/intents',
|
|
18
|
-
activatesOn: Events.
|
|
18
|
+
activatesOn: Events.SetupIntentResolver,
|
|
19
19
|
activate: () => [
|
|
20
20
|
contributes(
|
|
21
21
|
Capabilities.IntentResolver,
|
|
22
|
-
createResolver(
|
|
23
|
-
|
|
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:
|
|
34
|
+
activate: Toolbar,
|
|
32
35
|
}),
|
|
33
36
|
]);
|
|
@@ -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 {
|
|
13
|
-
import {
|
|
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: (
|
|
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
|
]);
|
|
@@ -15,7 +15,7 @@ export class BaseError extends Error {
|
|
|
15
15
|
readonly context?: Record<string, any>,
|
|
16
16
|
) {
|
|
17
17
|
// TODO(dmaretskyi): Error.cause.
|
|
18
|
-
super(message ?? code);
|
|
18
|
+
super(message ?? code, { cause: context });
|
|
19
19
|
this.name = code;
|
|
20
20
|
// NOTE: Restores prototype chain (https://stackoverflow.com/a/48342359).
|
|
21
21
|
Object.setPrototypeOf(this, new.target.prototype);
|