@dxos/plugin-automation 0.8.4-main.dedc0f3 → 0.8.4-main.ead640a
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/AutomationPanel-3PHA5FTJ.mjs +11 -0
- package/dist/lib/browser/AutomationSettings-OQERZHTG.mjs +69 -0
- package/dist/lib/browser/AutomationSettings-OQERZHTG.mjs.map +7 -0
- package/dist/lib/browser/{FunctionsContainer-U4HASI4P.mjs → FunctionsContainer-V3OM6BFC.mjs} +7 -7
- package/dist/lib/browser/FunctionsContainer-V3OM6BFC.mjs.map +7 -0
- package/dist/lib/browser/{FunctionsPanel-I443Y6KB.mjs → FunctionsPanel-PTFCCUTB.mjs} +3 -3
- package/dist/lib/browser/{app-graph-builder-4OFKIRAI.mjs → app-graph-builder-DV5HMFX4.mjs} +10 -9
- package/dist/lib/browser/app-graph-builder-DV5HMFX4.mjs.map +7 -0
- package/dist/lib/browser/chunk-2AOFK5FW.mjs +14 -0
- package/dist/lib/browser/chunk-2AOFK5FW.mjs.map +7 -0
- package/dist/lib/browser/chunk-5ARH77PV.mjs +15 -0
- package/dist/lib/browser/chunk-5ARH77PV.mjs.map +7 -0
- package/dist/lib/browser/{chunk-Z5DT4MHW.mjs → chunk-DK7R2HNC.mjs} +20 -7
- package/dist/lib/browser/chunk-DK7R2HNC.mjs.map +7 -0
- package/dist/lib/browser/chunk-MVPRI3DB.mjs +53 -0
- package/dist/lib/browser/chunk-MVPRI3DB.mjs.map +7 -0
- package/dist/lib/browser/chunk-QACR4BJ7.mjs +14 -0
- package/dist/lib/browser/chunk-QACR4BJ7.mjs.map +7 -0
- package/dist/lib/browser/{chunk-S4SM663I.mjs → chunk-SUKAEYF3.mjs} +55 -18
- package/dist/lib/browser/chunk-SUKAEYF3.mjs.map +7 -0
- package/dist/lib/browser/{chunk-HN7OHFCB.mjs → chunk-VGBZKM3O.mjs} +3 -3
- package/dist/lib/browser/chunk-VGBZKM3O.mjs.map +7 -0
- package/dist/lib/browser/{chunk-4TWQV33E.mjs → chunk-VL3KSDVL.mjs} +57 -17
- package/dist/lib/browser/chunk-VL3KSDVL.mjs.map +7 -0
- package/dist/lib/browser/{chunk-GW5U2DGT.mjs → chunk-WWURMV25.mjs} +2 -4
- package/dist/lib/browser/chunk-WWURMV25.mjs.map +7 -0
- package/dist/lib/browser/compute-runtime-YJREH6WP.mjs +160 -0
- package/dist/lib/browser/compute-runtime-YJREH6WP.mjs.map +7 -0
- package/dist/lib/browser/hooks/index.mjs +12 -0
- package/dist/lib/browser/index.mjs +35 -14
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/{intent-resolver-4PSYSQQG.mjs → intent-resolver-D2OHKQRR.mjs} +5 -5
- package/dist/lib/browser/{intent-resolver-4PSYSQQG.mjs.map → intent-resolver-D2OHKQRR.mjs.map} +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{react-surface-M52XGLXY.mjs → react-surface-POLPITTD.mjs} +18 -19
- package/dist/lib/browser/react-surface-POLPITTD.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +2 -2
- package/dist/lib/node-esm/{AutomationPanel-MKOLA2FE.mjs → AutomationPanel-EETYIR35.mjs} +4 -4
- package/dist/lib/node-esm/AutomationSettings-MCB7HK3O.mjs +70 -0
- package/dist/lib/node-esm/AutomationSettings-MCB7HK3O.mjs.map +7 -0
- package/dist/lib/node-esm/{FunctionsContainer-VZIVURH6.mjs → FunctionsContainer-UGODP6YP.mjs} +7 -7
- package/dist/lib/node-esm/FunctionsContainer-UGODP6YP.mjs.map +7 -0
- package/dist/lib/node-esm/{FunctionsPanel-ELINCXPW.mjs → FunctionsPanel-JQFBRVOC.mjs} +3 -3
- package/dist/lib/node-esm/FunctionsPanel-JQFBRVOC.mjs.map +7 -0
- package/dist/lib/node-esm/{app-graph-builder-555IHYOB.mjs → app-graph-builder-TR2WXPX2.mjs} +10 -9
- package/dist/lib/node-esm/app-graph-builder-TR2WXPX2.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-NK5N3QKD.mjs → chunk-5MQJPJR2.mjs} +2 -4
- package/dist/lib/node-esm/chunk-5MQJPJR2.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-AONUBWBE.mjs → chunk-AKHETVIQ.mjs} +57 -17
- package/dist/lib/node-esm/chunk-AKHETVIQ.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-OEZNHUL2.mjs → chunk-CJUI6AKX.mjs} +3 -3
- package/dist/lib/node-esm/chunk-CJUI6AKX.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-CX2AILIS.mjs → chunk-G72KHW5A.mjs} +20 -7
- package/dist/lib/node-esm/chunk-G72KHW5A.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-ZOJVKPCA.mjs → chunk-J3XEBEIE.mjs} +55 -18
- package/dist/lib/node-esm/chunk-J3XEBEIE.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-LB7UQ4SK.mjs +16 -0
- package/dist/lib/node-esm/chunk-LB7UQ4SK.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-QCA543ZR.mjs +54 -0
- package/dist/lib/node-esm/chunk-QCA543ZR.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-TXFXIA34.mjs +16 -0
- package/dist/lib/node-esm/chunk-TXFXIA34.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-WHCSOUNN.mjs +16 -0
- package/dist/lib/node-esm/chunk-WHCSOUNN.mjs.map +7 -0
- package/dist/lib/node-esm/compute-runtime-CMEPAYND.mjs +161 -0
- package/dist/lib/node-esm/compute-runtime-CMEPAYND.mjs.map +7 -0
- package/dist/lib/node-esm/hooks/index.mjs +13 -0
- package/dist/lib/node-esm/hooks/index.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +35 -14
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/{intent-resolver-6ZGBUILG.mjs → intent-resolver-2LGBVXT5.mjs} +5 -5
- package/dist/lib/node-esm/{intent-resolver-6ZGBUILG.mjs.map → intent-resolver-2LGBVXT5.mjs.map} +1 -1
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/{react-surface-Y6AOXM75.mjs → react-surface-5ULTTXAZ.mjs} +18 -19
- package/dist/lib/node-esm/react-surface-5ULTTXAZ.mjs.map +7 -0
- package/dist/lib/node-esm/types/index.mjs +2 -2
- package/dist/types/src/AutomationPlugin.d.ts +1 -1
- package/dist/types/src/AutomationPlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/capabilities.d.ts +19 -0
- package/dist/types/src/capabilities/capabilities.d.ts.map +1 -0
- package/dist/types/src/capabilities/compute-runtime.d.ts +5 -0
- package/dist/types/src/capabilities/compute-runtime.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +2 -0
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/components/AutomationPanel/AutomationPanel.d.ts.map +1 -1
- package/dist/types/src/components/AutomationPanel/AutomationPanel.stories.d.ts +3 -1
- package/dist/types/src/components/AutomationPanel/AutomationPanel.stories.d.ts.map +1 -1
- package/dist/types/src/components/AutomationSettings.d.ts +5 -0
- package/dist/types/src/components/AutomationSettings.d.ts.map +1 -0
- package/dist/types/src/components/FunctionsPanel/FunctionsPanel.d.ts.map +1 -1
- package/dist/types/src/components/TriggerEditor/FunctionInputEditor.d.ts.map +1 -1
- package/dist/types/src/components/TriggerEditor/SpecSelector.d.ts.map +1 -1
- package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts +4 -2
- package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts.map +1 -1
- package/dist/types/src/components/TriggerEditor/TriggerEditor.stories.d.ts +7 -4
- package/dist/types/src/components/TriggerEditor/TriggerEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TriggerSettings.d.ts +6 -0
- package/dist/types/src/components/TriggerSettings.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +1 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/events.d.ts +4 -0
- package/dist/types/src/events.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +3 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -0
- package/dist/types/src/hooks/useComputeRuntimeCallback.d.ts +8 -0
- package/dist/types/src/hooks/useComputeRuntimeCallback.d.ts.map +1 -0
- package/dist/types/src/hooks/useTriggerRuntimeControls.d.ts +11 -0
- package/dist/types/src/hooks/useTriggerRuntimeControls.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +2 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +0 -1
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +3 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/schema.d.ts +1 -1
- package/dist/types/src/types/schema.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +58 -37
- package/src/AutomationPlugin.tsx +35 -29
- package/src/capabilities/app-graph-builder.ts +8 -7
- package/src/capabilities/capabilities.ts +48 -0
- package/src/capabilities/compute-runtime.ts +138 -0
- package/src/capabilities/index.ts +3 -0
- package/src/capabilities/react-surface.tsx +13 -14
- package/src/components/AutomationPanel/AutomationPanel.stories.tsx +3 -6
- package/src/components/AutomationPanel/AutomationPanel.tsx +78 -15
- package/src/components/{AutomationContainer.tsx → AutomationSettings.tsx} +8 -6
- package/src/components/FunctionsContainer.tsx +4 -4
- package/src/components/FunctionsPanel/FunctionsPanel.tsx +16 -4
- package/src/components/TriggerEditor/FunctionInputEditor.tsx +8 -2
- package/src/components/TriggerEditor/SpecSelector.tsx +10 -4
- package/src/components/TriggerEditor/TriggerEditor.stories.tsx +57 -18
- package/src/components/TriggerEditor/TriggerEditor.tsx +35 -9
- package/src/components/TriggerSettings.tsx +25 -0
- package/src/components/index.ts +1 -1
- package/src/events.ts +11 -0
- package/src/hooks/index.ts +6 -0
- package/src/hooks/useComputeRuntimeCallback.ts +30 -0
- package/src/hooks/useTriggerRuntimeControls.ts +52 -0
- package/src/index.ts +2 -0
- package/src/meta.ts +1 -3
- package/src/testing/test-functions.ts +1 -1
- package/src/translations.ts +5 -0
- package/src/types/schema.ts +1 -1
- package/dist/lib/browser/AutomationContainer-BRV5AJZ3.mjs +0 -35
- package/dist/lib/browser/AutomationContainer-BRV5AJZ3.mjs.map +0 -7
- package/dist/lib/browser/AutomationPanel-N5HFJJXW.mjs +0 -11
- package/dist/lib/browser/FunctionsContainer-U4HASI4P.mjs.map +0 -7
- package/dist/lib/browser/app-graph-builder-4OFKIRAI.mjs.map +0 -7
- package/dist/lib/browser/chunk-4TWQV33E.mjs.map +0 -7
- package/dist/lib/browser/chunk-EIY2EUWC.mjs +0 -14
- package/dist/lib/browser/chunk-EIY2EUWC.mjs.map +0 -7
- package/dist/lib/browser/chunk-GW5U2DGT.mjs.map +0 -7
- package/dist/lib/browser/chunk-HN7OHFCB.mjs.map +0 -7
- package/dist/lib/browser/chunk-S4SM663I.mjs.map +0 -7
- package/dist/lib/browser/chunk-Z5DT4MHW.mjs.map +0 -7
- package/dist/lib/browser/react-surface-M52XGLXY.mjs.map +0 -7
- package/dist/lib/node-esm/AutomationContainer-FYRDTERM.mjs +0 -36
- package/dist/lib/node-esm/AutomationContainer-FYRDTERM.mjs.map +0 -7
- package/dist/lib/node-esm/FunctionsContainer-VZIVURH6.mjs.map +0 -7
- package/dist/lib/node-esm/app-graph-builder-555IHYOB.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-AONUBWBE.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-CICQ73ZT.mjs +0 -16
- package/dist/lib/node-esm/chunk-CICQ73ZT.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-CX2AILIS.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-NK5N3QKD.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-OEZNHUL2.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-ZOJVKPCA.mjs.map +0 -7
- package/dist/lib/node-esm/react-surface-Y6AOXM75.mjs.map +0 -7
- package/dist/types/src/components/AutomationContainer.d.ts +0 -5
- package/dist/types/src/components/AutomationContainer.d.ts.map +0 -1
- /package/dist/lib/browser/{AutomationPanel-N5HFJJXW.mjs.map → AutomationPanel-3PHA5FTJ.mjs.map} +0 -0
- /package/dist/lib/browser/{FunctionsPanel-I443Y6KB.mjs.map → FunctionsPanel-PTFCCUTB.mjs.map} +0 -0
- /package/dist/lib/{node-esm/AutomationPanel-MKOLA2FE.mjs.map → browser/hooks/index.mjs.map} +0 -0
- /package/dist/lib/node-esm/{FunctionsPanel-ELINCXPW.mjs.map → AutomationPanel-EETYIR35.mjs.map} +0 -0
|
@@ -2,19 +2,24 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import * as Array from 'effect/Array';
|
|
6
|
+
import * as Function from 'effect/Function';
|
|
7
|
+
import * as Match from 'effect/Match';
|
|
8
|
+
import * as Schema from 'effect/Schema';
|
|
9
|
+
import React, { useMemo, useState } from 'react';
|
|
7
10
|
|
|
8
|
-
import { Filter, Obj } from '@dxos/echo';
|
|
11
|
+
import { Filter, Obj, Tag } from '@dxos/echo';
|
|
9
12
|
import { FunctionTrigger, FunctionType, ScriptType } from '@dxos/functions';
|
|
13
|
+
import { useTypeOptions } from '@dxos/plugin-space';
|
|
10
14
|
import { type Client, useClient } from '@dxos/react-client';
|
|
11
15
|
import { type Space, getSpace, useQuery } from '@dxos/react-client/echo';
|
|
12
16
|
import { Clipboard, IconButton, Input, Separator, type ThemedClassName, useTranslation } from '@dxos/react-ui';
|
|
13
17
|
import { ControlItem, controlItemClasses } from '@dxos/react-ui-form';
|
|
14
18
|
import { List } from '@dxos/react-ui-list';
|
|
15
19
|
import { ghostHover, mx } from '@dxos/react-ui-theme';
|
|
20
|
+
import { DataType } from '@dxos/schema';
|
|
16
21
|
|
|
17
|
-
import {
|
|
22
|
+
import { meta } from '../../meta';
|
|
18
23
|
import { TriggerEditor, type TriggerEditorProps } from '../TriggerEditor';
|
|
19
24
|
|
|
20
25
|
const grid = 'grid grid-cols-[40px_1fr_32px] min-bs-[2.5rem]';
|
|
@@ -28,11 +33,15 @@ export type AutomationPanelProps = ThemedClassName<{
|
|
|
28
33
|
|
|
29
34
|
// TODO(burdon): Factor out common layout with ViewEditor.
|
|
30
35
|
export const AutomationPanel = ({ classNames, space, object, initialTrigger, onDone }: AutomationPanelProps) => {
|
|
31
|
-
const { t } = useTranslation(
|
|
36
|
+
const { t } = useTranslation(meta.id);
|
|
32
37
|
const client = useClient();
|
|
33
|
-
const triggers = useQuery(space, Filter.type(FunctionTrigger));
|
|
34
38
|
const functions = useQuery(space, Filter.type(FunctionType));
|
|
35
|
-
const
|
|
39
|
+
const triggers = useQuery(space, Filter.type(FunctionTrigger));
|
|
40
|
+
const filteredTriggers = useMemo(() => {
|
|
41
|
+
return object ? triggers.filter(triggerMatch(object)) : triggers;
|
|
42
|
+
}, [object, triggers]);
|
|
43
|
+
const tags = useQuery(space, Filter.type(Tag.Tag));
|
|
44
|
+
const types = useTypeOptions({ space, annotation: ['dynamic', 'limited-static', 'object-form'] });
|
|
36
45
|
|
|
37
46
|
const [trigger, setTrigger] = useState<FunctionTrigger | undefined>(initialTrigger);
|
|
38
47
|
const [selected, setSelected] = useState<FunctionTrigger>();
|
|
@@ -73,18 +82,30 @@ export const AutomationPanel = ({ classNames, space, object, initialTrigger, onD
|
|
|
73
82
|
if (trigger) {
|
|
74
83
|
return (
|
|
75
84
|
<ControlItem title={t('trigger editor title')}>
|
|
76
|
-
<TriggerEditor
|
|
85
|
+
<TriggerEditor
|
|
86
|
+
space={space}
|
|
87
|
+
trigger={trigger}
|
|
88
|
+
readonlySpec={Boolean(object)}
|
|
89
|
+
tags={tags}
|
|
90
|
+
types={types}
|
|
91
|
+
onSave={handleSave}
|
|
92
|
+
onCancel={handleCancel}
|
|
93
|
+
/>
|
|
77
94
|
</ControlItem>
|
|
78
95
|
);
|
|
79
96
|
}
|
|
80
97
|
|
|
81
98
|
return (
|
|
82
99
|
<div className={mx(controlItemClasses, classNames)}>
|
|
83
|
-
{
|
|
84
|
-
<List.Root<FunctionTrigger>
|
|
85
|
-
{
|
|
100
|
+
{filteredTriggers.length > 0 && (
|
|
101
|
+
<List.Root<FunctionTrigger>
|
|
102
|
+
items={filteredTriggers}
|
|
103
|
+
isItem={Schema.is(FunctionTrigger)}
|
|
104
|
+
getId={(field) => field.id}
|
|
105
|
+
>
|
|
106
|
+
{({ items: filteredTriggers }) => (
|
|
86
107
|
<div role='list' className='flex flex-col w-full'>
|
|
87
|
-
{
|
|
108
|
+
{filteredTriggers?.map((trigger) => {
|
|
88
109
|
const copyAction = getCopyAction(client, trigger);
|
|
89
110
|
return (
|
|
90
111
|
<List.Item<FunctionTrigger>
|
|
@@ -104,7 +125,7 @@ export const AutomationPanel = ({ classNames, space, object, initialTrigger, onD
|
|
|
104
125
|
classNames='px-1 cursor-pointer w-0 shrink truncate'
|
|
105
126
|
onClick={() => handleSelect(trigger)}
|
|
106
127
|
>
|
|
107
|
-
{getFunctionName(
|
|
128
|
+
{getFunctionName(functions, trigger) ?? '∅'}
|
|
108
129
|
</List.ItemTitle>
|
|
109
130
|
|
|
110
131
|
{/* TODO: a better way to expose copy action */}
|
|
@@ -124,7 +145,7 @@ export const AutomationPanel = ({ classNames, space, object, initialTrigger, onD
|
|
|
124
145
|
)}
|
|
125
146
|
</List.Root>
|
|
126
147
|
)}
|
|
127
|
-
{
|
|
148
|
+
{filteredTriggers.length > 0 && <Separator classNames='mlb-4' />}
|
|
128
149
|
<IconButton icon='ph--plus--regular' label={t('new trigger label')} onClick={handleAdd} />
|
|
129
150
|
</div>
|
|
130
151
|
);
|
|
@@ -150,10 +171,52 @@ const getWebhookUrl = (client: Client, trigger: FunctionTrigger) => {
|
|
|
150
171
|
return new URL(`/webhook/${spaceId}:${trigger.id}`, edgeUrl).toString();
|
|
151
172
|
};
|
|
152
173
|
|
|
153
|
-
const getFunctionName = (
|
|
174
|
+
const getFunctionName = (functions: FunctionType[], trigger: FunctionTrigger) => {
|
|
154
175
|
// TODO(wittjosiah): Truncation should be done in the UI.
|
|
155
176
|
// Warning that the List component is currently a can of worms.
|
|
156
177
|
const shortId = trigger.function && `${trigger.function.dxn.toString().slice(0, 16)}…`;
|
|
157
178
|
const functionObject = functions.find((fn) => fn === trigger.function?.target);
|
|
158
179
|
return functionObject?.name ?? shortId;
|
|
159
180
|
};
|
|
181
|
+
|
|
182
|
+
const scriptMatch = (script: ScriptType) => (trigger: FunctionTrigger) => {
|
|
183
|
+
const fn = trigger.function?.target;
|
|
184
|
+
if (!Obj.instanceOf(FunctionType, fn)) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return fn.source?.target === script;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const projectMatch = (project: DataType.Project) => {
|
|
192
|
+
const viewQueries = Function.pipe(
|
|
193
|
+
project.collections,
|
|
194
|
+
Array.map((collection) => collection.target),
|
|
195
|
+
Array.filter(Schema.is(DataType.View)),
|
|
196
|
+
Array.map((view) => Obj.getSnapshot(view).query.ast),
|
|
197
|
+
Array.map((ast) => JSON.stringify(ast)),
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
return (trigger: FunctionTrigger) => {
|
|
201
|
+
const spec = Obj.getSnapshot(trigger).spec;
|
|
202
|
+
if (spec?.kind !== 'subscription') {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// TODO(wittjosiah): Implement better way of comparing query ASTs.
|
|
207
|
+
return viewQueries.some((query) => JSON.stringify(spec.query) === query);
|
|
208
|
+
};
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const triggerMatch = Match.type<Obj.Any>().pipe(
|
|
212
|
+
Match.withReturnType<(trigger: FunctionTrigger) => boolean>(),
|
|
213
|
+
Match.when(
|
|
214
|
+
(obj) => Obj.instanceOf(ScriptType, obj),
|
|
215
|
+
(obj) => scriptMatch(obj),
|
|
216
|
+
),
|
|
217
|
+
Match.when(
|
|
218
|
+
(obj) => Obj.instanceOf(DataType.Project, obj),
|
|
219
|
+
(obj) => projectMatch(obj),
|
|
220
|
+
),
|
|
221
|
+
Match.orElse((_obj) => () => true),
|
|
222
|
+
);
|
|
@@ -7,22 +7,24 @@ import React from 'react';
|
|
|
7
7
|
import { useTranslation } from '@dxos/react-ui';
|
|
8
8
|
import { ControlPage, ControlSection } from '@dxos/react-ui-form';
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { meta } from '../meta';
|
|
11
11
|
|
|
12
12
|
import { AutomationPanel, type AutomationPanelProps } from './AutomationPanel';
|
|
13
|
+
import { TriggersSettings } from './TriggerSettings';
|
|
13
14
|
|
|
14
|
-
export const
|
|
15
|
-
const { t } = useTranslation(
|
|
15
|
+
export const AutomationSettings = (props: AutomationPanelProps) => {
|
|
16
|
+
const { t } = useTranslation(meta.id);
|
|
16
17
|
return (
|
|
17
18
|
<ControlPage>
|
|
18
19
|
<ControlSection
|
|
19
|
-
title={t('automation verbose label', { ns:
|
|
20
|
-
description={t('automation description', { ns:
|
|
20
|
+
title={t('automation verbose label', { ns: meta.id })}
|
|
21
|
+
description={t('automation description', { ns: meta.id })}
|
|
21
22
|
>
|
|
22
23
|
<AutomationPanel {...props} />
|
|
24
|
+
<TriggersSettings space={props.space} />
|
|
23
25
|
</ControlSection>
|
|
24
26
|
</ControlPage>
|
|
25
27
|
);
|
|
26
28
|
};
|
|
27
29
|
|
|
28
|
-
export default
|
|
30
|
+
export default AutomationSettings;
|
|
@@ -8,17 +8,17 @@ import { type Space } from '@dxos/react-client/echo';
|
|
|
8
8
|
import { useTranslation } from '@dxos/react-ui';
|
|
9
9
|
import { ControlPage, ControlSection } from '@dxos/react-ui-form';
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { meta } from '../meta';
|
|
12
12
|
|
|
13
13
|
import { FunctionsPanel } from './FunctionsPanel';
|
|
14
14
|
|
|
15
15
|
export const FunctionsContainer = ({ space }: { space: Space }) => {
|
|
16
|
-
const { t } = useTranslation(
|
|
16
|
+
const { t } = useTranslation(meta.id);
|
|
17
17
|
return (
|
|
18
18
|
<ControlPage>
|
|
19
19
|
<ControlSection
|
|
20
|
-
title={t('functions verbose label', { ns:
|
|
21
|
-
description={t('functions description', { ns:
|
|
20
|
+
title={t('functions verbose label', { ns: meta.id })}
|
|
21
|
+
description={t('functions description', { ns: meta.id })}
|
|
22
22
|
>
|
|
23
23
|
<FunctionsPanel space={space} />
|
|
24
24
|
</ControlSection>
|
|
@@ -2,18 +2,19 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
6
|
import React, { useCallback, useMemo } from 'react';
|
|
7
7
|
|
|
8
8
|
import { LayoutAction, createIntent, useIntentDispatcher } from '@dxos/app-framework';
|
|
9
9
|
import { FunctionType, ScriptType } from '@dxos/functions';
|
|
10
|
+
import { SpaceAction } from '@dxos/plugin-space/types';
|
|
10
11
|
import { Filter, type Space, fullyQualifiedId, useQuery } from '@dxos/react-client/echo';
|
|
11
|
-
import { Button, useTranslation } from '@dxos/react-ui';
|
|
12
|
+
import { Button, IconButton, useTranslation } from '@dxos/react-ui';
|
|
12
13
|
import { controlItemClasses } from '@dxos/react-ui-form';
|
|
13
14
|
import { List } from '@dxos/react-ui-list';
|
|
14
15
|
import { ghostHover, mx } from '@dxos/react-ui-theme';
|
|
15
16
|
|
|
16
|
-
import {
|
|
17
|
+
import { meta } from '../../meta';
|
|
17
18
|
|
|
18
19
|
const grid = 'grid grid-cols-[1fr_auto] min-bs-[2.5rem]';
|
|
19
20
|
|
|
@@ -22,7 +23,7 @@ export type FunctionsPanelProps = {
|
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
export const FunctionsPanel = ({ space }: FunctionsPanelProps) => {
|
|
25
|
-
const { t } = useTranslation(
|
|
26
|
+
const { t } = useTranslation(meta.id);
|
|
26
27
|
const functions = useQuery(space, Filter.type(FunctionType));
|
|
27
28
|
const scripts = useQuery(space, Filter.type(ScriptType));
|
|
28
29
|
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
@@ -63,6 +64,11 @@ export const FunctionsPanel = ({ space }: FunctionsPanelProps) => {
|
|
|
63
64
|
[functionToScriptMap, dispatch],
|
|
64
65
|
);
|
|
65
66
|
|
|
67
|
+
const handleDelete = useCallback(
|
|
68
|
+
(func: FunctionType) => dispatch(createIntent(SpaceAction.RemoveObjects, { objects: [func] })),
|
|
69
|
+
[dispatch],
|
|
70
|
+
);
|
|
71
|
+
|
|
66
72
|
return (
|
|
67
73
|
<div role='none' className={mx(controlItemClasses)}>
|
|
68
74
|
{functions.length > 0 && (
|
|
@@ -84,6 +90,12 @@ export const FunctionsPanel = ({ space }: FunctionsPanelProps) => {
|
|
|
84
90
|
{functionToScriptMap[func.id] && (
|
|
85
91
|
<Button onClick={() => handleGoToScript(func)}>{t('go to function source button label')}</Button>
|
|
86
92
|
)}
|
|
93
|
+
<IconButton
|
|
94
|
+
iconOnly
|
|
95
|
+
icon='ph--trash--regular'
|
|
96
|
+
label={t('delete function button label')}
|
|
97
|
+
onClick={() => handleDelete(func)}
|
|
98
|
+
/>
|
|
87
99
|
</List.Item>
|
|
88
100
|
))}
|
|
89
101
|
</div>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import React, { useCallback, useMemo } from 'react';
|
|
6
6
|
|
|
7
7
|
import { Ref, Type } from '@dxos/echo';
|
|
8
|
-
import { type JsonPath } from '@dxos/echo
|
|
8
|
+
import { type JsonPath } from '@dxos/echo/internal';
|
|
9
9
|
import { type FunctionType } from '@dxos/functions';
|
|
10
10
|
import { useOnTransition } from '@dxos/react-ui';
|
|
11
11
|
import { Form, type FormInputStateProps, type QueryRefOptions, useFormValues } from '@dxos/react-ui-form';
|
|
@@ -39,7 +39,13 @@ export const FunctionInputEditor = ({
|
|
|
39
39
|
useOnTransition(
|
|
40
40
|
// Clear function parameter input when the function changes.
|
|
41
41
|
selectedFunctionValue,
|
|
42
|
-
(prevValue) =>
|
|
42
|
+
(prevValue) => {
|
|
43
|
+
if (!Ref.isRef(prevValue) || !Ref.isRef(selectedFunctionValue)) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return prevValue.dxn.toString() !== selectedFunctionValue.dxn.toString();
|
|
48
|
+
},
|
|
43
49
|
(currValue) => currValue !== undefined,
|
|
44
50
|
() => onValueChange('object', {}),
|
|
45
51
|
);
|
|
@@ -4,16 +4,17 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { useCallback, useMemo } from 'react';
|
|
6
6
|
|
|
7
|
+
import { Filter, Query } from '@dxos/echo';
|
|
7
8
|
import { type FunctionTrigger, TriggerKinds, type TriggerType } from '@dxos/functions';
|
|
8
9
|
import { useTranslation } from '@dxos/react-ui';
|
|
9
10
|
import { type InputProps, SelectInput, useInputProps } from '@dxos/react-ui-form';
|
|
10
11
|
|
|
11
|
-
import {
|
|
12
|
+
import { meta } from '../../meta';
|
|
12
13
|
|
|
13
14
|
export type SpecSelectorProps = InputProps;
|
|
14
15
|
|
|
15
16
|
export const SpecSelector = (props: SpecSelectorProps) => {
|
|
16
|
-
const { t } = useTranslation(
|
|
17
|
+
const { t } = useTranslation(meta.id);
|
|
17
18
|
const specProps = useInputProps(['spec' satisfies keyof FunctionTrigger]);
|
|
18
19
|
|
|
19
20
|
const handleTypeChange = useCallback(
|
|
@@ -23,9 +24,14 @@ export const SpecSelector = (props: SpecSelectorProps) => {
|
|
|
23
24
|
case 'timer':
|
|
24
25
|
return { kind: 'timer', cron: '' };
|
|
25
26
|
case 'subscription':
|
|
26
|
-
return {
|
|
27
|
+
return {
|
|
28
|
+
kind: 'subscription',
|
|
29
|
+
query: {
|
|
30
|
+
ast: Query.select(Filter.nothing()).ast,
|
|
31
|
+
},
|
|
32
|
+
};
|
|
27
33
|
case 'queue':
|
|
28
|
-
return { kind: 'queue', queue: '' };
|
|
34
|
+
return { kind: 'queue', queue: 'dxn:' };
|
|
29
35
|
case 'email':
|
|
30
36
|
return { kind: 'email' };
|
|
31
37
|
case 'webhook':
|
|
@@ -2,33 +2,55 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
|
-
import React, {
|
|
6
|
+
import React, { useState } from 'react';
|
|
9
7
|
|
|
10
|
-
import { Obj } from '@dxos/echo';
|
|
8
|
+
import { Filter, Obj, Ref, Tag, Type } from '@dxos/echo';
|
|
11
9
|
import { FunctionTrigger, FunctionType } from '@dxos/functions';
|
|
10
|
+
import { invariant } from '@dxos/invariant';
|
|
12
11
|
import { faker } from '@dxos/random';
|
|
13
|
-
import {
|
|
14
|
-
import { ContactType, withClientProvider } from '@dxos/react-client/testing';
|
|
15
|
-
import {
|
|
12
|
+
import { useQuery } from '@dxos/react-client/echo';
|
|
13
|
+
import { ContactType, useClientProvider, withClientProvider } from '@dxos/react-client/testing';
|
|
14
|
+
import { useAsyncEffect } from '@dxos/react-ui';
|
|
15
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
16
|
+
import { DataType } from '@dxos/schema';
|
|
16
17
|
|
|
17
18
|
import { functions } from '../../testing';
|
|
18
19
|
import { translations } from '../../translations';
|
|
19
20
|
|
|
20
|
-
import { TriggerEditor } from './TriggerEditor';
|
|
21
|
+
import { TriggerEditor, type TriggerEditorProps } from './TriggerEditor';
|
|
22
|
+
|
|
23
|
+
const types = [
|
|
24
|
+
// TODO(burdon): Get label from annotation.
|
|
25
|
+
{ value: Type.getTypename(DataType.Organization), label: 'Organization' },
|
|
26
|
+
{ value: Type.getTypename(DataType.Person), label: 'Person' },
|
|
27
|
+
{ value: Type.getTypename(DataType.Project), label: 'Project' },
|
|
28
|
+
{ value: Type.getTypename(DataType.Employer), label: 'Employer' },
|
|
29
|
+
];
|
|
21
30
|
|
|
22
|
-
const DefaultStory = () => {
|
|
23
|
-
const
|
|
24
|
-
const space = spaces[1];
|
|
31
|
+
const DefaultStory = (props: Partial<TriggerEditorProps>) => {
|
|
32
|
+
const { space } = useClientProvider();
|
|
25
33
|
const [trigger, setTrigger] = useState<FunctionTrigger>();
|
|
26
|
-
|
|
34
|
+
const tags = useQuery(space, Filter.type(Tag.Tag));
|
|
35
|
+
|
|
36
|
+
useAsyncEffect(async () => {
|
|
27
37
|
if (!space) {
|
|
28
38
|
return;
|
|
29
39
|
}
|
|
30
40
|
|
|
31
|
-
const
|
|
41
|
+
const result = await space.db.query(Filter.type(FunctionType)).run();
|
|
42
|
+
const fn = result.objects.find((fn) => fn.name === 'example.com/function/forex');
|
|
43
|
+
invariant(fn);
|
|
44
|
+
const trigger = space.db.add(
|
|
45
|
+
Obj.make(FunctionTrigger, {
|
|
46
|
+
function: Ref.make(fn),
|
|
47
|
+
spec: { kind: 'webhook' },
|
|
48
|
+
input: {
|
|
49
|
+
from: 'USD',
|
|
50
|
+
to: 'JPY',
|
|
51
|
+
},
|
|
52
|
+
}),
|
|
53
|
+
);
|
|
32
54
|
setTrigger(trigger);
|
|
33
55
|
}, [space]);
|
|
34
56
|
|
|
@@ -38,7 +60,14 @@ const DefaultStory = () => {
|
|
|
38
60
|
|
|
39
61
|
return (
|
|
40
62
|
<div role='none' className='w-[32rem] bs-fit border border-separator rounded-sm'>
|
|
41
|
-
<TriggerEditor
|
|
63
|
+
<TriggerEditor
|
|
64
|
+
space={space}
|
|
65
|
+
trigger={trigger}
|
|
66
|
+
types={types}
|
|
67
|
+
tags={tags}
|
|
68
|
+
onSave={(values) => console.log('on save', values)}
|
|
69
|
+
{...props}
|
|
70
|
+
/>
|
|
42
71
|
</div>
|
|
43
72
|
);
|
|
44
73
|
};
|
|
@@ -48,11 +77,17 @@ const meta = {
|
|
|
48
77
|
component: TriggerEditor as any,
|
|
49
78
|
render: DefaultStory,
|
|
50
79
|
decorators: [
|
|
80
|
+
withTheme,
|
|
81
|
+
withLayout({ container: 'column' }),
|
|
51
82
|
withClientProvider({
|
|
52
83
|
createIdentity: true,
|
|
53
84
|
createSpace: true,
|
|
54
|
-
types: [FunctionType, FunctionTrigger, ContactType],
|
|
55
|
-
|
|
85
|
+
types: [Tag.Tag, FunctionType, FunctionTrigger, ContactType],
|
|
86
|
+
onCreateSpace: ({ space }) => {
|
|
87
|
+
space.db.add(Tag.make({ label: 'Important' }));
|
|
88
|
+
space.db.add(Tag.make({ label: 'Investor' }));
|
|
89
|
+
space.db.add(Tag.make({ label: 'New' }));
|
|
90
|
+
|
|
56
91
|
for (const fn of functions) {
|
|
57
92
|
space.db.add(Obj.make(FunctionType, fn));
|
|
58
93
|
}
|
|
@@ -66,8 +101,6 @@ const meta = {
|
|
|
66
101
|
});
|
|
67
102
|
},
|
|
68
103
|
}),
|
|
69
|
-
withLayout({ fullscreen: true, classNames: 'flex justify-center m-2' }),
|
|
70
|
-
withTheme,
|
|
71
104
|
],
|
|
72
105
|
parameters: {
|
|
73
106
|
translations,
|
|
@@ -79,3 +112,9 @@ export default meta;
|
|
|
79
112
|
type Story = StoryObj<typeof meta>;
|
|
80
113
|
|
|
81
114
|
export const Default: Story = {};
|
|
115
|
+
|
|
116
|
+
export const ReadonlySpec: Story = {
|
|
117
|
+
args: {
|
|
118
|
+
readonlySpec: true,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
import React, { useCallback, useMemo } from 'react';
|
|
6
6
|
|
|
7
7
|
import { ComputeGraph } from '@dxos/conductor';
|
|
8
|
-
import { Type } from '@dxos/echo';
|
|
8
|
+
import { type Query, Type } from '@dxos/echo';
|
|
9
9
|
import { FunctionTrigger, FunctionType, ScriptType } from '@dxos/functions';
|
|
10
10
|
import { Filter, Ref, type Space, useQuery } from '@dxos/react-client/echo';
|
|
11
|
-
import {
|
|
11
|
+
import { Input } from '@dxos/react-ui';
|
|
12
|
+
import { QueryForm, type QueryFormProps } from '@dxos/react-ui-components';
|
|
13
|
+
import { type CustomInputMap, Form, InputHeader, SelectInput, useRefQueryLookupHandler } from '@dxos/react-ui-form';
|
|
12
14
|
|
|
13
15
|
import { FunctionInputEditor, type FunctionInputEditorProps } from './FunctionInputEditor';
|
|
14
16
|
import { SpecSelector } from './SpecSelector';
|
|
@@ -16,17 +18,19 @@ import { SpecSelector } from './SpecSelector';
|
|
|
16
18
|
export type TriggerEditorProps = {
|
|
17
19
|
space: Space;
|
|
18
20
|
trigger: FunctionTrigger;
|
|
21
|
+
// TODO(wittjosiah): This needs to apply to whole spec but currently only applies to spec.kind & spec.query.
|
|
22
|
+
readonlySpec?: boolean;
|
|
19
23
|
onSave?: (trigger: Omit<FunctionTrigger, 'id'>) => void;
|
|
20
24
|
onCancel?: () => void;
|
|
21
|
-
}
|
|
25
|
+
} & Pick<QueryFormProps, 'types' | 'tags'>;
|
|
22
26
|
|
|
23
|
-
export const TriggerEditor = ({ space, trigger, onSave, onCancel }: TriggerEditorProps) => {
|
|
24
|
-
const handleSave = (values: FunctionTrigger) => {
|
|
27
|
+
export const TriggerEditor = ({ space, trigger, readonlySpec, types, tags, onSave, onCancel }: TriggerEditorProps) => {
|
|
28
|
+
const handleSave = ({ id: _, ...values }: FunctionTrigger) => {
|
|
25
29
|
onSave?.(values);
|
|
26
30
|
};
|
|
27
31
|
|
|
28
32
|
const handleRefQueryLookup = useRefQueryLookupHandler({ space });
|
|
29
|
-
const Custom = useCustomInputs(space, handleRefQueryLookup);
|
|
33
|
+
const Custom = useCustomInputs({ space, readonlySpec, types, tags, onQueryRefOptions: handleRefQueryLookup });
|
|
30
34
|
|
|
31
35
|
return (
|
|
32
36
|
<Form
|
|
@@ -41,7 +45,13 @@ export const TriggerEditor = ({ space, trigger, onSave, onCancel }: TriggerEdito
|
|
|
41
45
|
);
|
|
42
46
|
};
|
|
43
47
|
|
|
44
|
-
|
|
48
|
+
type UseCustomInputsProps = {
|
|
49
|
+
space: Space;
|
|
50
|
+
readonlySpec?: boolean;
|
|
51
|
+
onQueryRefOptions: FunctionInputEditorProps['onQueryRefOptions'];
|
|
52
|
+
} & Pick<QueryFormProps, 'types' | 'tags'>;
|
|
53
|
+
|
|
54
|
+
const useCustomInputs = ({ space, readonlySpec, types, tags, onQueryRefOptions }: UseCustomInputsProps) => {
|
|
45
55
|
const functions = useQuery(space, Filter.type(FunctionType));
|
|
46
56
|
const workflows = useQuery(space, Filter.type(ComputeGraph));
|
|
47
57
|
const scripts = useQuery(space, Filter.type(ScriptType));
|
|
@@ -80,14 +90,30 @@ const useCustomInputs = (space: Space, onQueryRefOptions: FunctionInputEditorPro
|
|
|
80
90
|
},
|
|
81
91
|
|
|
82
92
|
// Spec selector.
|
|
83
|
-
['spec.kind' as const]: SpecSelector
|
|
93
|
+
['spec.kind' as const]: (props) => <SpecSelector {...props} readonly={readonlySpec ? 'disabled-input' : false} />,
|
|
94
|
+
|
|
95
|
+
// TODO(wittjosiah): Copied from ViewEditor.
|
|
96
|
+
// Query input editor.
|
|
97
|
+
['spec.query' as const]: (props) => {
|
|
98
|
+
const handleChange = useCallback(
|
|
99
|
+
(query: Query.Any) => props.onValueChange('object', { ast: query.ast }),
|
|
100
|
+
[props.onValueChange],
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<Input.Root>
|
|
105
|
+
<InputHeader label={props.label} />
|
|
106
|
+
<QueryForm initialQuery={(props.getValue() as any).ast} types={types} tags={tags} onChange={handleChange} />
|
|
107
|
+
</Input.Root>
|
|
108
|
+
);
|
|
109
|
+
},
|
|
84
110
|
|
|
85
111
|
// Function input editor.
|
|
86
112
|
['input' as const]: (props) => (
|
|
87
113
|
<FunctionInputEditor {...props} functions={functions} onQueryRefOptions={onQueryRefOptions} />
|
|
88
114
|
),
|
|
89
115
|
}),
|
|
90
|
-
[workflows, scripts, functions],
|
|
116
|
+
[workflows, scripts, functions, readonlySpec],
|
|
91
117
|
);
|
|
92
118
|
};
|
|
93
119
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { type Space } from '@dxos/react-client/echo';
|
|
8
|
+
import { Input, useTranslation } from '@dxos/react-ui';
|
|
9
|
+
import { ControlItemInput } from '@dxos/react-ui-form';
|
|
10
|
+
|
|
11
|
+
import { useTriggerRuntimeControls } from '../hooks';
|
|
12
|
+
import { meta } from '../meta';
|
|
13
|
+
|
|
14
|
+
export const TriggersSettings = ({ space }: { space: Space }) => {
|
|
15
|
+
const { triggers, isRunning, start, stop } = useTriggerRuntimeControls(space);
|
|
16
|
+
const { t } = useTranslation(meta.id);
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div className='container-max-width grid grid-cols-1 md:grid-cols-[1fr_min-content]'>
|
|
20
|
+
<ControlItemInput title={t('runtime label')} description={t('runtime description')}>
|
|
21
|
+
<Input.Switch classNames='justify-self-end' checked={isRunning} onCheckedChange={isRunning ? stop : start} />
|
|
22
|
+
</ControlItemInput>
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
};
|
package/src/components/index.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { lazy } from 'react';
|
|
|
6
6
|
|
|
7
7
|
export * from './TriggerEditor';
|
|
8
8
|
|
|
9
|
-
export const AutomationContainer = lazy(() => import('./AutomationContainer'));
|
|
10
9
|
export const AutomationPanel = lazy(() => import('./AutomationPanel'));
|
|
10
|
+
export const AutomationSettings = lazy(() => import('./AutomationSettings'));
|
|
11
11
|
export const FunctionsContainer = lazy(() => import('./FunctionsContainer'));
|
|
12
12
|
export const FunctionsPanel = lazy(() => import('./FunctionsPanel'));
|
package/src/events.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { defineEvent } from '@dxos/app-framework';
|
|
6
|
+
|
|
7
|
+
import { meta } from './meta';
|
|
8
|
+
|
|
9
|
+
export namespace AutomationEvents {
|
|
10
|
+
export const ComputeRuntimeReady = defineEvent(`${meta.id}/event/compute-runtime-ready`);
|
|
11
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import type * as Effect from 'effect/Effect';
|
|
6
|
+
import { useCallback } from 'react';
|
|
7
|
+
|
|
8
|
+
import { useCapability } from '@dxos/app-framework';
|
|
9
|
+
import type { Space } from '@dxos/react-client/echo';
|
|
10
|
+
|
|
11
|
+
import { AutomationCapabilities } from '../capabilities';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Create an effectful function that has access to compute services
|
|
15
|
+
*/
|
|
16
|
+
export const useComputeRuntimeCallback = <T>(
|
|
17
|
+
space: Space | undefined,
|
|
18
|
+
fn: () => Effect.Effect<T, any, AutomationCapabilities.ComputeServices>,
|
|
19
|
+
deps?: React.DependencyList,
|
|
20
|
+
): (() => Promise<T>) => {
|
|
21
|
+
const computeRuntime = useCapability(AutomationCapabilities.ComputeRuntime);
|
|
22
|
+
const runtime = space !== undefined ? computeRuntime.getRuntime(space.id) : undefined;
|
|
23
|
+
|
|
24
|
+
return useCallback(() => {
|
|
25
|
+
if (!runtime) {
|
|
26
|
+
throw new TypeError('Space not provided to useComputeRuntimeCallback');
|
|
27
|
+
}
|
|
28
|
+
return runtime.runPromise(fn());
|
|
29
|
+
}, [runtime, ...(deps ?? [])]);
|
|
30
|
+
};
|