@dxos/app-framework 0.4.2 → 0.4.3-main.c3bfede

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 (33) hide show
  1. package/dist/lib/browser/{chunk-7MQ2DVDI.mjs → chunk-7IKXIZSH.mjs} +9 -1
  2. package/dist/lib/browser/chunk-7IKXIZSH.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +4 -2
  4. package/dist/lib/browser/index.mjs.map +1 -1
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/{plugin-PGPBA2JO.mjs → plugin-ZVKSZO5F.mjs} +59 -12
  7. package/dist/lib/browser/plugin-ZVKSZO5F.mjs.map +7 -0
  8. package/dist/lib/node/{chunk-FETX4EGX.cjs → chunk-DKDFEZCJ.cjs} +13 -4
  9. package/dist/lib/node/chunk-DKDFEZCJ.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +12 -10
  11. package/dist/lib/node/index.cjs.map +1 -1
  12. package/dist/lib/node/meta.json +1 -1
  13. package/dist/lib/node/{plugin-HLGHD6N5.cjs → plugin-WBEJNM7Z.cjs} +60 -18
  14. package/dist/lib/node/plugin-WBEJNM7Z.cjs.map +7 -0
  15. package/dist/types/src/plugins/IntentPlugin/IntentContext.d.ts +10 -4
  16. package/dist/types/src/plugins/IntentPlugin/IntentContext.d.ts.map +1 -1
  17. package/dist/types/src/plugins/IntentPlugin/helpers.d.ts +6 -0
  18. package/dist/types/src/plugins/IntentPlugin/helpers.d.ts.map +1 -0
  19. package/dist/types/src/plugins/IntentPlugin/intent.d.ts +17 -0
  20. package/dist/types/src/plugins/IntentPlugin/intent.d.ts.map +1 -1
  21. package/dist/types/src/plugins/IntentPlugin/plugin.d.ts.map +1 -1
  22. package/dist/types/src/plugins/IntentPlugin/provides.d.ts +6 -0
  23. package/dist/types/src/plugins/IntentPlugin/provides.d.ts.map +1 -1
  24. package/package.json +8 -8
  25. package/src/plugins/IntentPlugin/IntentContext.tsx +14 -5
  26. package/src/plugins/IntentPlugin/helpers.ts +11 -0
  27. package/src/plugins/IntentPlugin/intent.ts +20 -0
  28. package/src/plugins/IntentPlugin/plugin.tsx +49 -10
  29. package/src/plugins/IntentPlugin/provides.ts +12 -0
  30. package/dist/lib/browser/chunk-7MQ2DVDI.mjs.map +0 -7
  31. package/dist/lib/browser/plugin-PGPBA2JO.mjs.map +0 -7
  32. package/dist/lib/node/chunk-FETX4EGX.cjs.map +0 -7
  33. package/dist/lib/node/plugin-HLGHD6N5.cjs.map +0 -7
@@ -11,4 +11,10 @@ export type IntentPluginProvides = {
11
11
  };
12
12
  export declare const parseIntentPlugin: (plugin: Plugin) => Plugin<IntentPluginProvides> | undefined;
13
13
  export declare const parseIntentResolverPlugin: (plugin: Plugin) => Plugin<IntentResolverProvides> | undefined;
14
+ export declare enum IntentAction {
15
+ /**
16
+ * Fired after an intent is dispatched if the intent is undoable.
17
+ */
18
+ SHOW_UNDO = "dxos.org/plugin/intent/show-undo"
19
+ }
14
20
  //# sourceMappingURL=provides.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"provides.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/IntentPlugin/provides.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE;QACN,QAAQ,EAAE,cAAc,CAAC;KAC1B,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,aAAa,CAAC;CACvB,CAAC;AAEF,eAAO,MAAM,iBAAiB,WAAY,MAAM,6CACkD,CAAC;AAEnG,eAAO,MAAM,yBAAyB,WAAY,MAAM,+CAC4C,CAAC"}
1
+ {"version":3,"file":"provides.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/IntentPlugin/provides.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE;QACN,QAAQ,EAAE,cAAc,CAAC;KAC1B,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,aAAa,CAAC;CACvB,CAAC;AAEF,eAAO,MAAM,iBAAiB,WAAY,MAAM,6CACkD,CAAC;AAEnG,eAAO,MAAM,yBAAyB,WAAY,MAAM,+CAC4C,CAAC;AAOrG,oBAAY,YAAY;IACtB;;OAEG;IACH,SAAS,qCAA+B;CACzC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/app-framework",
3
- "version": "0.4.2",
3
+ "version": "0.4.3-main.c3bfede",
4
4
  "description": "A framework for building applications from composible plugins.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -16,13 +16,13 @@
16
16
  "@preact/signals-react": "^1.3.6",
17
17
  "deepsignal": "1.4.0-shallow.0",
18
18
  "zod": "^3.22.4",
19
- "@dxos/app-graph": "0.4.2",
20
- "@dxos/async": "0.4.2",
21
- "@dxos/debug": "0.4.2",
22
- "@dxos/local-storage": "0.4.2",
23
- "@dxos/invariant": "0.4.2",
24
- "@dxos/util": "0.4.2",
25
- "@dxos/log": "0.4.2"
19
+ "@dxos/async": "0.4.3-main.c3bfede",
20
+ "@dxos/app-graph": "0.4.3-main.c3bfede",
21
+ "@dxos/local-storage": "0.4.3-main.c3bfede",
22
+ "@dxos/debug": "0.4.3-main.c3bfede",
23
+ "@dxos/util": "0.4.3-main.c3bfede",
24
+ "@dxos/invariant": "0.4.3-main.c3bfede",
25
+ "@dxos/log": "0.4.3-main.c3bfede"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/react": "^18.0.21",
@@ -5,27 +5,36 @@
5
5
  import { type Context, createContext, useContext, type Provider } from 'react';
6
6
  import { useEffect } from 'react';
7
7
 
8
- import { type UnsubscribeCallback } from '@dxos/async';
8
+ import type { UnsubscribeCallback } from '@dxos/async';
9
9
 
10
- import { type IntentDispatcher, type IntentResolver } from './intent';
10
+ import type { Intent, IntentDispatcher, IntentResolver, IntentResult } from './intent';
11
+
12
+ export type IntentExecution = {
13
+ intent: Intent;
14
+ result: IntentResult;
15
+ };
11
16
 
12
17
  export type IntentContext = {
13
18
  dispatch: IntentDispatcher;
19
+ undo: () => Promise<IntentResult | void>;
20
+ history: IntentExecution[][];
14
21
  registerResolver: (pluginId: string, resolver: IntentResolver) => UnsubscribeCallback;
15
22
  };
16
23
 
17
24
  const IntentContext: Context<IntentContext> = createContext<IntentContext>({
18
25
  dispatch: async () => ({}),
26
+ undo: async () => ({}),
27
+ history: [],
19
28
  registerResolver: () => () => {},
20
29
  });
21
30
 
22
31
  /**
23
- * @deprecated
24
- * TODO(burdon): Use useIntentDispatcher.
32
+ * @deprecated Prefer granular hooks.
33
+ * TODO(burdon): Remove. Use useIntentDispatcher.
25
34
  */
26
35
  export const useIntent = () => useContext(IntentContext);
27
36
 
28
- // TODO(burdon): Dispatch or Dispatcher?
37
+ // TODO(burdon): Rename useIntentDispatch.
29
38
  export const useIntentDispatcher = (): IntentDispatcher => {
30
39
  const { dispatch } = useIntent();
31
40
  return dispatch;
@@ -0,0 +1,11 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { type IntentExecution } from './IntentContext';
6
+
7
+ /**
8
+ * Check if the chain of intents is undoable.
9
+ */
10
+ export const isUndoable = (chain: IntentExecution[]): boolean =>
11
+ chain.length > 0 && chain.every(({ result }) => result.undoable);
@@ -30,6 +30,11 @@ export type Intent = {
30
30
  */
31
31
  action: string;
32
32
 
33
+ /**
34
+ * Whether or not the intent is being undone.
35
+ */
36
+ undo?: boolean;
37
+
33
38
  /**
34
39
  * Any data needed to perform the desired action.
35
40
  */
@@ -44,6 +49,21 @@ export type IntentResult = {
44
49
  */
45
50
  data?: any;
46
51
 
52
+ /**
53
+ * If provided, the action will be undoable.
54
+ */
55
+ undoable?: {
56
+ /**
57
+ * Message to display to the user when indicating that the action can be undone.
58
+ */
59
+ message: string;
60
+
61
+ /**
62
+ * Will be merged with the original intent data when firing the undo intent.
63
+ */
64
+ data?: IntentData;
65
+ };
66
+
47
67
  /**
48
68
  * An error that occurred while performing the action.
49
69
  *
@@ -7,21 +7,33 @@ import React from 'react';
7
7
 
8
8
  import { log } from '@dxos/log';
9
9
 
10
- import { type IntentContext, IntentProvider } from './IntentContext';
11
- import type { Intent, IntentResolver, IntentResult } from './intent';
10
+ import { type IntentContext, IntentProvider, type IntentExecution } from './IntentContext';
11
+ import { isUndoable } from './helpers';
12
+ import type { Intent, IntentResolver } from './intent';
12
13
  import IntentMeta from './meta';
13
- import { type IntentPluginProvides, type IntentResolverProvides, parseIntentResolverPlugin } from './provides';
14
+ import {
15
+ type IntentPluginProvides,
16
+ type IntentResolverProvides,
17
+ parseIntentResolverPlugin,
18
+ IntentAction,
19
+ } from './provides';
14
20
  import type { PluginDefinition } from '../PluginHost';
15
21
  import { filterPlugins, findPlugin } from '../helpers';
16
22
 
17
23
  const EXECUTION_LIMIT = 1000;
24
+ const HISTORY_LIMIT = 100;
18
25
 
19
26
  /**
20
27
  * Allows plugins to register intent handlers and routes sent intents to the appropriate plugin.
21
28
  * Inspired by https://developer.android.com/reference/android/content/Intent.
22
29
  */
23
30
  const IntentPlugin = (): PluginDefinition<IntentPluginProvides> => {
24
- const state = deepSignal<IntentContext>({ dispatch: async () => ({}), registerResolver: () => () => {} });
31
+ const state = deepSignal<IntentContext>({
32
+ dispatch: async () => ({}),
33
+ undo: async () => ({}),
34
+ history: [],
35
+ registerResolver: () => () => {},
36
+ });
25
37
 
26
38
  const dynamicResolvers = new Set<{ plugin: string; resolver: IntentResolver }>();
27
39
 
@@ -77,24 +89,51 @@ const IntentPlugin = (): PluginDefinition<IntentPluginProvides> => {
77
89
  };
78
90
  }
79
91
 
80
- let result: IntentResult | undefined;
81
- for (const intent of Array.isArray(intentOrArray) ? intentOrArray : [intentOrArray]) {
82
- const data = intent.data ? { result: result?.data, ...intent.data } : result?.data;
83
- result = (await dispatch({ ...intent, data })) ?? undefined;
92
+ const executionResults: IntentExecution[] = [];
93
+ const chain = Array.isArray(intentOrArray) ? intentOrArray : [intentOrArray];
94
+ for (const intent of chain) {
95
+ const { result: prevResult } = executionResults.at(-1) ?? {};
96
+ const data = intent.data ? { result: prevResult?.data, ...intent.data } : prevResult?.data;
97
+ const result = await dispatch({ ...intent, data });
84
98
 
85
- if (result?.error) {
99
+ if (!result || result?.error) {
86
100
  break;
87
101
  }
88
102
 
103
+ executionResults.push({ intent, result });
104
+
105
+ // TODO(wittjosiah): How does undo work with returned intents?
89
106
  result?.intents?.forEach((intents) => {
90
107
  void dispatchChain(intents, depth + 1);
91
108
  });
92
109
  }
93
110
 
94
- return result;
111
+ state.history = [...state.history.slice(0, HISTORY_LIMIT - 2), executionResults];
112
+
113
+ if (isUndoable(executionResults)) {
114
+ void dispatch({ action: IntentAction.SHOW_UNDO, data: { results: executionResults } });
115
+ }
116
+
117
+ return executionResults.at(-1)?.result;
118
+ };
119
+
120
+ const undo = async () => {
121
+ const last = state.history.findLastIndex(isUndoable);
122
+ const chain =
123
+ last !== -1 &&
124
+ state.history[last]?.map(({ intent, result }): Intent => {
125
+ const data = result.undoable?.data ? { ...intent.data, ...result.undoable.data } : intent.data;
126
+ return { ...intent, data, undo: true };
127
+ });
128
+ if (chain) {
129
+ const result = await dispatchChain(chain);
130
+ state.history = state.history.filter((_, index) => index !== last);
131
+ return result;
132
+ }
95
133
  };
96
134
 
97
135
  state.dispatch = dispatchChain;
136
+ state.undo = undo;
98
137
  state.registerResolver = (plugin, resolver) => {
99
138
  const entry = { plugin, resolver };
100
139
  dynamicResolvers.add(entry);
@@ -21,3 +21,15 @@ export const parseIntentPlugin = (plugin: Plugin) =>
21
21
 
22
22
  export const parseIntentResolverPlugin = (plugin: Plugin) =>
23
23
  (plugin.provides as any).intent?.resolver ? (plugin as Plugin<IntentResolverProvides>) : undefined;
24
+
25
+ //
26
+ // Intents
27
+ //
28
+
29
+ const INTENT_ACTION = 'dxos.org/plugin/intent';
30
+ export enum IntentAction {
31
+ /**
32
+ * Fired after an intent is dispatched if the intent is undoable.
33
+ */
34
+ SHOW_UNDO = `${INTENT_ACTION}/show-undo`,
35
+ }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/plugins/IntentPlugin/provides.ts", "../../../src/plugins/IntentPlugin/IntentContext.tsx", "../../../src/plugins/IntentPlugin/meta.ts"],
4
- "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { type IntentContext } from './IntentContext';\nimport { type IntentResolver } from './intent';\nimport { type Plugin } from '../PluginHost';\n\nexport type IntentResolverProvides = {\n intent: {\n resolver: IntentResolver;\n };\n};\n\nexport type IntentPluginProvides = {\n intent: IntentContext;\n};\n\nexport const parseIntentPlugin = (plugin: Plugin) =>\n (plugin.provides as any).intent?.dispatch ? (plugin as Plugin<IntentPluginProvides>) : undefined;\n\nexport const parseIntentResolverPlugin = (plugin: Plugin) =>\n (plugin.provides as any).intent?.resolver ? (plugin as Plugin<IntentResolverProvides>) : undefined;\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Context, createContext, useContext, type Provider } from 'react';\nimport { useEffect } from 'react';\n\nimport { type UnsubscribeCallback } from '@dxos/async';\n\nimport { type IntentDispatcher, type IntentResolver } from './intent';\n\nexport type IntentContext = {\n dispatch: IntentDispatcher;\n registerResolver: (pluginId: string, resolver: IntentResolver) => UnsubscribeCallback;\n};\n\nconst IntentContext: Context<IntentContext> = createContext<IntentContext>({\n dispatch: async () => ({}),\n registerResolver: () => () => {},\n});\n\n/**\n * @deprecated\n * TODO(burdon): Use useIntentDispatcher.\n */\nexport const useIntent = () => useContext(IntentContext);\n\n// TODO(burdon): Dispatch or Dispatcher?\nexport const useIntentDispatcher = (): IntentDispatcher => {\n const { dispatch } = useIntent();\n return dispatch;\n};\n\nexport const useIntentResolver = (plugin: string, resolver: IntentResolver) => {\n const { registerResolver } = useIntent();\n useEffect(() => {\n return registerResolver(plugin, resolver);\n }, [plugin, resolver]);\n};\n\nexport const IntentProvider: Provider<IntentContext> = IntentContext.Provider;\n", "//\n// Copyright 2023 DXOS.org\n//\n\nconst IntentMeta = {\n id: 'dxos.org/plugin/intent',\n};\n\nexport default IntentMeta;\n"],
5
- "mappings": ";AAkBO,IAAMA,oBAAoB,CAACC,WAC/BA,OAAOC,SAAiBC,QAAQC,WAAYH,SAA0CI;AAElF,IAAMC,4BAA4B,CAACL,WACvCA,OAAOC,SAAiBC,QAAQI,WAAYN,SAA4CI;;;AClB3F,SAAuBG,eAAeC,kBAAiC;AACvE,SAASC,iBAAiB;AAW1B,IAAMC,gBAAwCC,8BAA6B;EACzEC,UAAU,aAAa,CAAC;EACxBC,kBAAkB,MAAM,MAAA;EAAO;AACjC,CAAA;AAMO,IAAMC,YAAY,MAAMC,WAAWL,aAAAA;AAGnC,IAAMM,sBAAsB,MAAA;AACjC,QAAM,EAAEJ,SAAQ,IAAKE,UAAAA;AACrB,SAAOF;AACT;AAEO,IAAMK,oBAAoB,CAACC,QAAgBC,aAAAA;AAChD,QAAM,EAAEN,iBAAgB,IAAKC,UAAAA;AAC7BM,YAAU,MAAA;AACR,WAAOP,iBAAiBK,QAAQC,QAAAA;EAClC,GAAG;IAACD;IAAQC;GAAS;AACvB;AAEO,IAAME,iBAA0CX,cAAcY;;;ACpCrE,IAAMC,aAAa;EACjBC,IAAI;AACN;AAEA,IAAA,eAAeD;",
6
- "names": ["parseIntentPlugin", "plugin", "provides", "intent", "dispatch", "undefined", "parseIntentResolverPlugin", "resolver", "createContext", "useContext", "useEffect", "IntentContext", "createContext", "dispatch", "registerResolver", "useIntent", "useContext", "useIntentDispatcher", "useIntentResolver", "plugin", "resolver", "useEffect", "IntentProvider", "Provider", "IntentMeta", "id"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/plugins/IntentPlugin/plugin.tsx"],
4
- "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { deepSignal } from 'deepsignal/react';\nimport React from 'react';\n\nimport { log } from '@dxos/log';\n\nimport { type IntentContext, IntentProvider } from './IntentContext';\nimport type { Intent, IntentResolver, IntentResult } from './intent';\nimport IntentMeta from './meta';\nimport { type IntentPluginProvides, type IntentResolverProvides, parseIntentResolverPlugin } from './provides';\nimport type { PluginDefinition } from '../PluginHost';\nimport { filterPlugins, findPlugin } from '../helpers';\n\nconst EXECUTION_LIMIT = 1000;\n\n/**\n * Allows plugins to register intent handlers and routes sent intents to the appropriate plugin.\n * Inspired by https://developer.android.com/reference/android/content/Intent.\n */\nconst IntentPlugin = (): PluginDefinition<IntentPluginProvides> => {\n const state = deepSignal<IntentContext>({ dispatch: async () => ({}), registerResolver: () => () => {} });\n\n const dynamicResolvers = new Set<{ plugin: string; resolver: IntentResolver }>();\n\n return {\n meta: IntentMeta,\n ready: async (plugins) => {\n // Dispatch intent to associated plugin.\n const dispatch = async (intent: Intent) => {\n if (intent.plugin) {\n for (const entry of dynamicResolvers) {\n if (entry.plugin === intent.plugin) {\n const result = await entry.resolver(intent, plugins);\n if (result) {\n return result;\n }\n }\n }\n\n const plugin = findPlugin<IntentResolverProvides>(plugins, intent.plugin);\n const result = plugin?.provides.intent.resolver(intent, plugins);\n return result;\n }\n\n for (const entry of dynamicResolvers) {\n const result = await entry.resolver(intent, plugins);\n if (result) {\n return result;\n }\n }\n\n // Return resolved value from first plugin that handles the intent.\n for (const plugin of filterPlugins(plugins, parseIntentResolverPlugin)) {\n const result = await plugin.provides.intent.resolver(intent, plugins);\n if (result) {\n return result;\n }\n }\n\n // https://vitejs.dev/guide/env-and-mode#env-variables\n // TODO(wittjosiah): How to handle this more generically?\n if (import.meta?.env?.DEV) {\n log.warn('No plugin found to handle intent', intent);\n }\n };\n\n // Sequentially dispatch array of invents.\n const dispatchChain = async (intentOrArray: Intent | Intent[], depth = 0) => {\n if (depth > EXECUTION_LIMIT) {\n return {\n error: new Error(\n `Intent execution limit exceeded (${EXECUTION_LIMIT} iterations). This is likely due to an infinite loop within intent resolvers.`,\n ),\n };\n }\n\n let result: IntentResult | undefined;\n for (const intent of Array.isArray(intentOrArray) ? intentOrArray : [intentOrArray]) {\n const data = intent.data ? { result: result?.data, ...intent.data } : result?.data;\n result = (await dispatch({ ...intent, data })) ?? undefined;\n\n if (result?.error) {\n break;\n }\n\n result?.intents?.forEach((intents) => {\n void dispatchChain(intents, depth + 1);\n });\n }\n\n return result;\n };\n\n state.dispatch = dispatchChain;\n state.registerResolver = (plugin, resolver) => {\n const entry = { plugin, resolver };\n dynamicResolvers.add(entry);\n return () => dynamicResolvers.delete(entry);\n };\n },\n provides: {\n intent: state,\n context: ({ children }) => <IntentProvider value={state}>{children}</IntentProvider>,\n },\n };\n};\n\nexport default IntentPlugin;\n"],
5
- "mappings": ";;;;;;;;;;;AAIA,SAASA,kBAAkB;AAC3B,OAAOC,WAAW;AAElB,SAASC,WAAW;;AASpB,IAAMC,kBAAkB;AAMxB,IAAMC,eAAe,MAAA;AACnB,QAAMC,QAAQC,WAA0B;IAAEC,UAAU,aAAa,CAAC;IAAIC,kBAAkB,MAAM,MAAA;IAAO;EAAE,CAAA;AAEvG,QAAMC,mBAAmB,oBAAIC,IAAAA;AAE7B,SAAO;IACLC,MAAMC;IACNC,OAAO,OAAOC,YAAAA;AAEZ,YAAMP,WAAW,OAAOQ,WAAAA;AACtB,YAAIA,OAAOC,QAAQ;AACjB,qBAAWC,SAASR,kBAAkB;AACpC,gBAAIQ,MAAMD,WAAWD,OAAOC,QAAQ;AAClC,oBAAME,UAAS,MAAMD,MAAME,SAASJ,QAAQD,OAAAA;AAC5C,kBAAII,SAAQ;AACV,uBAAOA;cACT;YACF;UACF;AAEA,gBAAMF,SAASI,WAAmCN,SAASC,OAAOC,MAAM;AACxE,gBAAME,SAASF,QAAQK,SAASN,OAAOI,SAASJ,QAAQD,OAAAA;AACxD,iBAAOI;QACT;AAEA,mBAAWD,SAASR,kBAAkB;AACpC,gBAAMS,SAAS,MAAMD,MAAME,SAASJ,QAAQD,OAAAA;AAC5C,cAAII,QAAQ;AACV,mBAAOA;UACT;QACF;AAGA,mBAAWF,UAAUM,cAAcR,SAASS,yBAAAA,GAA4B;AACtE,gBAAML,SAAS,MAAMF,OAAOK,SAASN,OAAOI,SAASJ,QAAQD,OAAAA;AAC7D,cAAII,QAAQ;AACV,mBAAOA;UACT;QACF;AAIA,YAAI,aAAaM,KAAKC,KAAK;AACzBC,cAAIC,KAAK,oCAAoCZ,QAAAA;;;;;;QAC/C;MACF;AAGA,YAAMa,gBAAgB,OAAOC,eAAkCC,QAAQ,MAAC;AACtE,YAAIA,QAAQ3B,iBAAiB;AAC3B,iBAAO;YACL4B,OAAO,IAAIC,MACT,oCAAoC7B,eAAAA,+EAA8F;UAEtI;QACF;AAEA,YAAIe;AACJ,mBAAWH,UAAUkB,MAAMC,QAAQL,aAAAA,IAAiBA,gBAAgB;UAACA;WAAgB;AACnF,gBAAMM,OAAOpB,OAAOoB,OAAO;YAAEjB,QAAQA,QAAQiB;YAAM,GAAGpB,OAAOoB;UAAK,IAAIjB,QAAQiB;AAC9EjB,mBAAU,MAAMX,SAAS;YAAE,GAAGQ;YAAQoB;UAAK,CAAA,KAAOC;AAElD,cAAIlB,QAAQa,OAAO;AACjB;UACF;AAEAb,kBAAQmB,SAASC,QAAQ,CAACD,YAAAA;AACxB,iBAAKT,cAAcS,SAASP,QAAQ,CAAA;UACtC,CAAA;QACF;AAEA,eAAOZ;MACT;AAEAb,YAAME,WAAWqB;AACjBvB,YAAMG,mBAAmB,CAACQ,QAAQG,aAAAA;AAChC,cAAMF,QAAQ;UAAED;UAAQG;QAAS;AACjCV,yBAAiB8B,IAAItB,KAAAA;AACrB,eAAO,MAAMR,iBAAiB+B,OAAOvB,KAAAA;MACvC;IACF;IACAI,UAAU;MACRN,QAAQV;MACRoC,SAAS,CAAC,EAAEC,SAAQ,MAAO,sBAAA,cAACC,gBAAAA;QAAeC,OAAOvC;SAAQqC,QAAAA;IAC5D;EACF;AACF;AAEA,IAAA,iBAAetC;",
6
- "names": ["deepSignal", "React", "log", "EXECUTION_LIMIT", "IntentPlugin", "state", "deepSignal", "dispatch", "registerResolver", "dynamicResolvers", "Set", "meta", "IntentMeta", "ready", "plugins", "intent", "plugin", "entry", "result", "resolver", "findPlugin", "provides", "filterPlugins", "parseIntentResolverPlugin", "env", "DEV", "log", "warn", "dispatchChain", "intentOrArray", "depth", "error", "Error", "Array", "isArray", "data", "undefined", "intents", "forEach", "add", "delete", "context", "children", "IntentProvider", "value"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/plugins/IntentPlugin/provides.ts", "../../../src/plugins/IntentPlugin/IntentContext.tsx", "../../../src/plugins/IntentPlugin/meta.ts"],
4
- "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { type IntentContext } from './IntentContext';\nimport { type IntentResolver } from './intent';\nimport { type Plugin } from '../PluginHost';\n\nexport type IntentResolverProvides = {\n intent: {\n resolver: IntentResolver;\n };\n};\n\nexport type IntentPluginProvides = {\n intent: IntentContext;\n};\n\nexport const parseIntentPlugin = (plugin: Plugin) =>\n (plugin.provides as any).intent?.dispatch ? (plugin as Plugin<IntentPluginProvides>) : undefined;\n\nexport const parseIntentResolverPlugin = (plugin: Plugin) =>\n (plugin.provides as any).intent?.resolver ? (plugin as Plugin<IntentResolverProvides>) : undefined;\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Context, createContext, useContext, type Provider } from 'react';\nimport { useEffect } from 'react';\n\nimport { type UnsubscribeCallback } from '@dxos/async';\n\nimport { type IntentDispatcher, type IntentResolver } from './intent';\n\nexport type IntentContext = {\n dispatch: IntentDispatcher;\n registerResolver: (pluginId: string, resolver: IntentResolver) => UnsubscribeCallback;\n};\n\nconst IntentContext: Context<IntentContext> = createContext<IntentContext>({\n dispatch: async () => ({}),\n registerResolver: () => () => {},\n});\n\n/**\n * @deprecated\n * TODO(burdon): Use useIntentDispatcher.\n */\nexport const useIntent = () => useContext(IntentContext);\n\n// TODO(burdon): Dispatch or Dispatcher?\nexport const useIntentDispatcher = (): IntentDispatcher => {\n const { dispatch } = useIntent();\n return dispatch;\n};\n\nexport const useIntentResolver = (plugin: string, resolver: IntentResolver) => {\n const { registerResolver } = useIntent();\n useEffect(() => {\n return registerResolver(plugin, resolver);\n }, [plugin, resolver]);\n};\n\nexport const IntentProvider: Provider<IntentContext> = IntentContext.Provider;\n", "//\n// Copyright 2023 DXOS.org\n//\n\nconst IntentMeta = {\n id: 'dxos.org/plugin/intent',\n};\n\nexport default IntentMeta;\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACIA,mBAAuE;AACvE,IAAAA,gBAA0B;ADanB,IAAMC,oBAAoB,CAACC,WAC/BA,OAAOC,SAAiBC,QAAQC,WAAYH,SAA0CI;AAElF,IAAMC,4BAA4B,CAACL,WACvCA,OAAOC,SAAiBC,QAAQI,WAAYN,SAA4CI;ACN3F,IAAMG,gBAAwCC,gDAA6B;EACzEL,UAAU,aAAa,CAAC;EACxBM,kBAAkB,MAAM,MAAA;EAAO;AACjC,CAAA;AAMO,IAAMC,YAAY,UAAMC,yBAAWJ,aAAAA;AAGnC,IAAMK,sBAAsB,MAAA;AACjC,QAAM,EAAET,SAAQ,IAAKO,UAAAA;AACrB,SAAOP;AACT;AAEO,IAAMU,oBAAoB,CAACb,QAAgBM,aAAAA;AAChD,QAAM,EAAEG,iBAAgB,IAAKC,UAAAA;AAC7BI,+BAAU,MAAA;AACR,WAAOL,iBAAiBT,QAAQM,QAAAA;EAClC,GAAG;IAACN;IAAQM;GAAS;AACvB;AAEO,IAAMS,iBAA0CR,cAAcS;ACpCrE,IAAMC,aAAa;EACjBC,IAAI;AACN;AAEA,IAAA,eAAeD;",
6
- "names": ["import_react", "parseIntentPlugin", "plugin", "provides", "intent", "dispatch", "undefined", "parseIntentResolverPlugin", "resolver", "IntentContext", "createContext", "registerResolver", "useIntent", "useContext", "useIntentDispatcher", "useIntentResolver", "useEffect", "IntentProvider", "Provider", "IntentMeta", "id"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/plugins/IntentPlugin/plugin.tsx"],
4
- "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { deepSignal } from 'deepsignal/react';\nimport React from 'react';\n\nimport { log } from '@dxos/log';\n\nimport { type IntentContext, IntentProvider } from './IntentContext';\nimport type { Intent, IntentResolver, IntentResult } from './intent';\nimport IntentMeta from './meta';\nimport { type IntentPluginProvides, type IntentResolverProvides, parseIntentResolverPlugin } from './provides';\nimport type { PluginDefinition } from '../PluginHost';\nimport { filterPlugins, findPlugin } from '../helpers';\n\nconst EXECUTION_LIMIT = 1000;\n\n/**\n * Allows plugins to register intent handlers and routes sent intents to the appropriate plugin.\n * Inspired by https://developer.android.com/reference/android/content/Intent.\n */\nconst IntentPlugin = (): PluginDefinition<IntentPluginProvides> => {\n const state = deepSignal<IntentContext>({ dispatch: async () => ({}), registerResolver: () => () => {} });\n\n const dynamicResolvers = new Set<{ plugin: string; resolver: IntentResolver }>();\n\n return {\n meta: IntentMeta,\n ready: async (plugins) => {\n // Dispatch intent to associated plugin.\n const dispatch = async (intent: Intent) => {\n if (intent.plugin) {\n for (const entry of dynamicResolvers) {\n if (entry.plugin === intent.plugin) {\n const result = await entry.resolver(intent, plugins);\n if (result) {\n return result;\n }\n }\n }\n\n const plugin = findPlugin<IntentResolverProvides>(plugins, intent.plugin);\n const result = plugin?.provides.intent.resolver(intent, plugins);\n return result;\n }\n\n for (const entry of dynamicResolvers) {\n const result = await entry.resolver(intent, plugins);\n if (result) {\n return result;\n }\n }\n\n // Return resolved value from first plugin that handles the intent.\n for (const plugin of filterPlugins(plugins, parseIntentResolverPlugin)) {\n const result = await plugin.provides.intent.resolver(intent, plugins);\n if (result) {\n return result;\n }\n }\n\n // https://vitejs.dev/guide/env-and-mode#env-variables\n // TODO(wittjosiah): How to handle this more generically?\n if (import.meta?.env?.DEV) {\n log.warn('No plugin found to handle intent', intent);\n }\n };\n\n // Sequentially dispatch array of invents.\n const dispatchChain = async (intentOrArray: Intent | Intent[], depth = 0) => {\n if (depth > EXECUTION_LIMIT) {\n return {\n error: new Error(\n `Intent execution limit exceeded (${EXECUTION_LIMIT} iterations). This is likely due to an infinite loop within intent resolvers.`,\n ),\n };\n }\n\n let result: IntentResult | undefined;\n for (const intent of Array.isArray(intentOrArray) ? intentOrArray : [intentOrArray]) {\n const data = intent.data ? { result: result?.data, ...intent.data } : result?.data;\n result = (await dispatch({ ...intent, data })) ?? undefined;\n\n if (result?.error) {\n break;\n }\n\n result?.intents?.forEach((intents) => {\n void dispatchChain(intents, depth + 1);\n });\n }\n\n return result;\n };\n\n state.dispatch = dispatchChain;\n state.registerResolver = (plugin, resolver) => {\n const entry = { plugin, resolver };\n dynamicResolvers.add(entry);\n return () => dynamicResolvers.delete(entry);\n };\n },\n provides: {\n intent: state,\n context: ({ children }) => <IntentProvider value={state}>{children}</IntentProvider>,\n },\n };\n};\n\nexport default IntentPlugin;\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,mBAA2B;AAC3B,IAAAA,gBAAkB;AAElB,iBAAoB;;;AASpB,IAAMC,kBAAkB;AAMxB,IAAMC,eAAe,MAAA;AACnB,QAAMC,YAAQC,yBAA0B;IAAEC,UAAU,aAAa,CAAC;IAAIC,kBAAkB,MAAM,MAAA;IAAO;EAAE,CAAA;AAEvG,QAAMC,mBAAmB,oBAAIC,IAAAA;AAE7B,SAAO;IACLC,MAAMC;IACNC,OAAO,OAAOC,YAAAA;AAEZ,YAAMP,WAAW,OAAOQ,WAAAA;AACtB,YAAIA,OAAOC,QAAQ;AACjB,qBAAWC,SAASR,kBAAkB;AACpC,gBAAIQ,MAAMD,WAAWD,OAAOC,QAAQ;AAClC,oBAAME,UAAS,MAAMD,MAAME,SAASJ,QAAQD,OAAAA;AAC5C,kBAAII,SAAQ;AACV,uBAAOA;cACT;YACF;UACF;AAEA,gBAAMF,aAASI,kCAAmCN,SAASC,OAAOC,MAAM;AACxE,gBAAME,SAASF,QAAQK,SAASN,OAAOI,SAASJ,QAAQD,OAAAA;AACxD,iBAAOI;QACT;AAEA,mBAAWD,SAASR,kBAAkB;AACpC,gBAAMS,SAAS,MAAMD,MAAME,SAASJ,QAAQD,OAAAA;AAC5C,cAAII,QAAQ;AACV,mBAAOA;UACT;QACF;AAGA,mBAAWF,cAAUM,qCAAcR,SAASS,+CAAAA,GAA4B;AACtE,gBAAML,SAAS,MAAMF,OAAOK,SAASN,OAAOI,SAASJ,QAAQD,OAAAA;AAC7D,cAAII,QAAQ;AACV,mBAAOA;UACT;QACF;AAIA,YAAI,aAAaM,KAAKC,KAAK;AACzBC,yBAAIC,KAAK,oCAAoCZ,QAAAA;;;;;;QAC/C;MACF;AAGA,YAAMa,gBAAgB,OAAOC,eAAkCC,QAAQ,MAAC;AACtE,YAAIA,QAAQ3B,iBAAiB;AAC3B,iBAAO;YACL4B,OAAO,IAAIC,MACT,oCAAoC7B,eAAAA,+EAA8F;UAEtI;QACF;AAEA,YAAIe;AACJ,mBAAWH,UAAUkB,MAAMC,QAAQL,aAAAA,IAAiBA,gBAAgB;UAACA;WAAgB;AACnF,gBAAMM,OAAOpB,OAAOoB,OAAO;YAAEjB,QAAQA,QAAQiB;YAAM,GAAGpB,OAAOoB;UAAK,IAAIjB,QAAQiB;AAC9EjB,mBAAU,MAAMX,SAAS;YAAE,GAAGQ;YAAQoB;UAAK,CAAA,KAAOC;AAElD,cAAIlB,QAAQa,OAAO;AACjB;UACF;AAEAb,kBAAQmB,SAASC,QAAQ,CAACD,YAAAA;AACxB,iBAAKT,cAAcS,SAASP,QAAQ,CAAA;UACtC,CAAA;QACF;AAEA,eAAOZ;MACT;AAEAb,YAAME,WAAWqB;AACjBvB,YAAMG,mBAAmB,CAACQ,QAAQG,aAAAA;AAChC,cAAMF,QAAQ;UAAED;UAAQG;QAAS;AACjCV,yBAAiB8B,IAAItB,KAAAA;AACrB,eAAO,MAAMR,iBAAiB+B,OAAOvB,KAAAA;MACvC;IACF;IACAI,UAAU;MACRN,QAAQV;MACRoC,SAAS,CAAC,EAAEC,SAAQ,MAAO,8BAAAC,QAAA,cAACC,sCAAAA;QAAeC,OAAOxC;SAAQqC,QAAAA;IAC5D;EACF;AACF;AAEA,IAAA,iBAAetC;",
6
- "names": ["import_react", "EXECUTION_LIMIT", "IntentPlugin", "state", "deepSignal", "dispatch", "registerResolver", "dynamicResolvers", "Set", "meta", "IntentMeta", "ready", "plugins", "intent", "plugin", "entry", "result", "resolver", "findPlugin", "provides", "filterPlugins", "parseIntentResolverPlugin", "env", "DEV", "log", "warn", "dispatchChain", "intentOrArray", "depth", "error", "Error", "Array", "isArray", "data", "undefined", "intents", "forEach", "add", "delete", "context", "children", "React", "IntentProvider", "value"]
7
- }