@dxos/compute 0.8.4-main.e098934 → 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/{chunk-XQYPCRZP.mjs → chunk-C7KLV2IU.mjs} +60 -56
- package/dist/lib/browser/chunk-C7KLV2IU.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +24 -4
- package/dist/lib/browser/testing/index.mjs.map +4 -4
- package/dist/lib/node-esm/{chunk-2XWWSGAU.mjs → chunk-K37OSZ5O.mjs} +60 -56
- package/dist/lib/node-esm/chunk-K37OSZ5O.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +1 -1
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +24 -4
- package/dist/lib/node-esm/testing/index.mjs.map +4 -4
- package/dist/types/src/compute-graph-registry.d.ts +8 -3
- package/dist/types/src/compute-graph-registry.d.ts.map +1 -1
- package/dist/types/src/compute-graph-registry.test.d.ts +2 -0
- package/dist/types/src/compute-graph-registry.test.d.ts.map +1 -0
- package/dist/types/src/compute-graph.d.ts +4 -1
- package/dist/types/src/compute-graph.d.ts.map +1 -1
- package/dist/types/src/functions/async-function.d.ts +11 -9
- package/dist/types/src/functions/async-function.d.ts.map +1 -1
- package/dist/types/src/functions/edge-function.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +1 -0
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/test-builder.d.ts +1 -1
- package/dist/types/src/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/testing/test-runtime.d.ts +10 -0
- package/dist/types/src/testing/test-runtime.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +22 -22
- package/src/compute-graph-registry.test.ts +138 -0
- package/src/compute-graph-registry.ts +17 -8
- package/src/compute-graph.ts +7 -4
- package/src/functions/async-function.ts +13 -12
- package/src/functions/edge-function.ts +21 -18
- package/src/testing/index.ts +1 -0
- package/src/testing/test-builder.ts +8 -2
- package/src/testing/test-runtime.ts +38 -0
- package/dist/lib/browser/chunk-XQYPCRZP.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-2XWWSGAU.mjs.map +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/compute",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.ead640a",
|
|
4
4
|
"description": "Low-level compute API",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -35,32 +35,32 @@
|
|
|
35
35
|
"src"
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@preact/signals-core": "^1.
|
|
39
|
-
"effect": "3.
|
|
38
|
+
"@preact/signals-core": "^1.12.1",
|
|
39
|
+
"effect": "3.18.3",
|
|
40
40
|
"lodash.defaultsdeep": "^4.6.1",
|
|
41
|
-
"@dxos/
|
|
42
|
-
"@dxos/
|
|
43
|
-
"@dxos/client": "0.8.4-main.
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/echo
|
|
46
|
-
"@dxos/
|
|
47
|
-
"@dxos/functions": "0.8.4-main.
|
|
48
|
-
"@dxos/
|
|
49
|
-
"@dxos/
|
|
50
|
-
"@dxos/
|
|
51
|
-
"@dxos/
|
|
52
|
-
"@dxos/
|
|
53
|
-
"@dxos/protocols": "0.8.4-main.
|
|
54
|
-
"@dxos/schema": "0.8.4-main.
|
|
55
|
-
"@dxos/
|
|
56
|
-
"@dxos/
|
|
41
|
+
"@dxos/ai": "0.8.4-main.ead640a",
|
|
42
|
+
"@dxos/async": "0.8.4-main.ead640a",
|
|
43
|
+
"@dxos/client": "0.8.4-main.ead640a",
|
|
44
|
+
"@dxos/context": "0.8.4-main.ead640a",
|
|
45
|
+
"@dxos/echo": "0.8.4-main.ead640a",
|
|
46
|
+
"@dxos/debug": "0.8.4-main.ead640a",
|
|
47
|
+
"@dxos/functions": "0.8.4-main.ead640a",
|
|
48
|
+
"@dxos/keys": "0.8.4-main.ead640a",
|
|
49
|
+
"@dxos/live-object": "0.8.4-main.ead640a",
|
|
50
|
+
"@dxos/invariant": "0.8.4-main.ead640a",
|
|
51
|
+
"@dxos/log": "0.8.4-main.ead640a",
|
|
52
|
+
"@dxos/plugin-automation": "0.8.4-main.ead640a",
|
|
53
|
+
"@dxos/protocols": "0.8.4-main.ead640a",
|
|
54
|
+
"@dxos/schema": "0.8.4-main.ead640a",
|
|
55
|
+
"@dxos/util": "0.8.4-main.ead640a",
|
|
56
|
+
"@dxos/vendor-hyperformula": "0.8.4-main.ead640a"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@tldraw/indices": "^2.0.0-alpha.14",
|
|
60
60
|
"@types/lodash.defaultsdeep": "^4.6.6",
|
|
61
|
-
"@dxos/echo
|
|
62
|
-
"@dxos/
|
|
63
|
-
"@dxos/
|
|
61
|
+
"@dxos/echo": "0.8.4-main.ead640a",
|
|
62
|
+
"@dxos/echo-db": "0.8.4-main.ead640a",
|
|
63
|
+
"@dxos/random": "0.8.4-main.ead640a"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
66
|
"effect": "^3.13.3"
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
|
+
import { describe, expect, onTestFinished, test } from 'vitest';
|
|
7
|
+
|
|
8
|
+
import { Trigger } from '@dxos/async';
|
|
9
|
+
import { fullyQualifiedId } from '@dxos/client/echo';
|
|
10
|
+
import { FunctionDefinition, FunctionType } from '@dxos/functions';
|
|
11
|
+
|
|
12
|
+
import { ComputeGraphRegistry, defaultPlugins } from './compute-graph-registry';
|
|
13
|
+
import { TestBuilder, createMockedComputeRuntimeProvider } from './testing';
|
|
14
|
+
|
|
15
|
+
describe('ComputeGraphRegistry', () => {
|
|
16
|
+
const add = FunctionDefinition.make({
|
|
17
|
+
key: 'add',
|
|
18
|
+
name: 'add',
|
|
19
|
+
description: 'Adds two numbers',
|
|
20
|
+
inputSchema: Schema.Struct({ a: Schema.Number, b: Schema.Number }),
|
|
21
|
+
outputSchema: Schema.Number,
|
|
22
|
+
handler: ({ data }) => data.a + data.b,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('invokes user function through compute graph', async () => {
|
|
26
|
+
const computeRuntime = createMockedComputeRuntimeProvider({ functions: [add] });
|
|
27
|
+
const testBuilder = new TestBuilder({ types: [FunctionType], computeRuntime });
|
|
28
|
+
await testBuilder.open();
|
|
29
|
+
onTestFinished(async () => {
|
|
30
|
+
await testBuilder.close();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const space = await testBuilder.client.spaces.create();
|
|
34
|
+
|
|
35
|
+
// Register Edge function plugin and runtime in the registry.
|
|
36
|
+
const registry = new ComputeGraphRegistry({ plugins: defaultPlugins, computeRuntime });
|
|
37
|
+
await registry.open();
|
|
38
|
+
onTestFinished(async () => {
|
|
39
|
+
await registry.close();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const graph = registry.createGraph(space);
|
|
43
|
+
await graph.open();
|
|
44
|
+
onTestFinished(async () => {
|
|
45
|
+
await graph.close();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Create a function object in ECHO with binding that maps to DX calls.
|
|
49
|
+
const functionObj = FunctionDefinition.serialize(add);
|
|
50
|
+
functionObj.binding = 'ADD';
|
|
51
|
+
space.db.add(functionObj);
|
|
52
|
+
|
|
53
|
+
const node = await graph.getOrCreateNode('sheet').open();
|
|
54
|
+
|
|
55
|
+
// Wait for value update after async invocation completes.
|
|
56
|
+
const trigger = new Trigger<number>();
|
|
57
|
+
node.update.on(() => {
|
|
58
|
+
const value = node.getValue({ row: 0, col: 0 });
|
|
59
|
+
if (typeof value === 'number') {
|
|
60
|
+
trigger.wake(value);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Set formula using binding; graph will map to DX("ADD", ...)
|
|
65
|
+
node.setValue({ row: 0, col: 0 }, '=ADD(2, 3)');
|
|
66
|
+
|
|
67
|
+
const result = await trigger.wait();
|
|
68
|
+
expect(result).to.equal(5);
|
|
69
|
+
|
|
70
|
+
// Sanity check: mapping to fully qualified id works as well.
|
|
71
|
+
const bindingId = graph.mapFunctionBindingToId('ADD(2, 3)');
|
|
72
|
+
expect(bindingId.startsWith(`${fullyQualifiedId(functionObj)}`)).to.be.true;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('adding a function binding updates autocomplete and enables execution', async () => {
|
|
76
|
+
const computeRuntime = createMockedComputeRuntimeProvider({ functions: [add] });
|
|
77
|
+
const testBuilder = new TestBuilder({ types: [FunctionType], computeRuntime });
|
|
78
|
+
await testBuilder.open();
|
|
79
|
+
onTestFinished(async () => {
|
|
80
|
+
await testBuilder.close();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const space = await testBuilder.client.spaces.create();
|
|
84
|
+
|
|
85
|
+
const registry = new ComputeGraphRegistry({ plugins: defaultPlugins, computeRuntime });
|
|
86
|
+
await registry.open();
|
|
87
|
+
onTestFinished(async () => {
|
|
88
|
+
await registry.close();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const graph = registry.createGraph(space);
|
|
92
|
+
await graph.open();
|
|
93
|
+
onTestFinished(async () => {
|
|
94
|
+
await graph.close();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Initially there should be no echo functions.
|
|
98
|
+
expect(graph.getFunctions({ standard: false, echo: true }).length).to.equal(0);
|
|
99
|
+
|
|
100
|
+
// Subscribe for function updates.
|
|
101
|
+
const functionsUpdated = new Trigger<void>();
|
|
102
|
+
const unsubscribe = graph.update.on((e) => {
|
|
103
|
+
if (e.type === 'functionsUpdated') {
|
|
104
|
+
functionsUpdated.wake();
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
onTestFinished(unsubscribe);
|
|
108
|
+
|
|
109
|
+
// Add a bound function to the space; graph should pick it up.
|
|
110
|
+
const functionObj = FunctionDefinition.serialize(add);
|
|
111
|
+
functionObj.binding = 'ADD';
|
|
112
|
+
space.db.add(functionObj);
|
|
113
|
+
|
|
114
|
+
await functionsUpdated.wait();
|
|
115
|
+
|
|
116
|
+
// Autocomplete should now include the binding.
|
|
117
|
+
const echoFunctions = graph.getFunctions({ standard: false, echo: true }).map((f) => f.name);
|
|
118
|
+
expect(echoFunctions).toContain('ADD');
|
|
119
|
+
|
|
120
|
+
// And the binding should execute when used in a formula.
|
|
121
|
+
const node = await graph.getOrCreateNode('sheet').open();
|
|
122
|
+
onTestFinished(async () => {
|
|
123
|
+
await node.close();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const valueSettled = new Trigger<number>();
|
|
127
|
+
node.update.on(() => {
|
|
128
|
+
const value = node.getValue({ row: 0, col: 0 });
|
|
129
|
+
if (typeof value === 'number') {
|
|
130
|
+
valueSettled.wake(value);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
node.setValue({ row: 0, col: 0 }, '=ADD(2, 3)');
|
|
135
|
+
const result = await valueSettled.wait();
|
|
136
|
+
expect(result).to.equal(5);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import type * as ManagedRuntime from 'effect/ManagedRuntime';
|
|
5
6
|
import defaultsDeep from 'lodash.defaultsdeep';
|
|
6
7
|
|
|
7
8
|
import { type Space, type SpaceId } from '@dxos/client/echo';
|
|
8
9
|
import { Resource } from '@dxos/context';
|
|
10
|
+
import { type FunctionInvocationService } from '@dxos/functions';
|
|
9
11
|
import { invariant } from '@dxos/invariant';
|
|
10
12
|
import { log } from '@dxos/log';
|
|
11
13
|
import type { ConfigParams, FunctionPluginDefinition, FunctionTranslationsPackage } from '@dxos/vendor-hyperformula';
|
|
@@ -19,15 +21,16 @@ export type ComputeGraphPlugin = {
|
|
|
19
21
|
translations: FunctionTranslationsPackage;
|
|
20
22
|
};
|
|
21
23
|
|
|
24
|
+
export type FunctionsRuntimeProvider = {
|
|
25
|
+
getRuntime(spaceId: SpaceId): ManagedRuntime.ManagedRuntime<FunctionInvocationService, never>;
|
|
26
|
+
};
|
|
27
|
+
|
|
22
28
|
export type ComputeGraphOptions = {
|
|
23
29
|
plugins?: ComputeGraphPlugin[];
|
|
30
|
+
computeRuntime: FunctionsRuntimeProvider;
|
|
24
31
|
} & Partial<FunctionContextOptions> &
|
|
25
32
|
Partial<ConfigParams>;
|
|
26
33
|
|
|
27
|
-
export const defaultOptions: ComputeGraphOptions = {
|
|
28
|
-
licenseKey: 'gpl-v3',
|
|
29
|
-
};
|
|
30
|
-
|
|
31
34
|
export const defaultPlugins: ComputeGraphPlugin[] = [
|
|
32
35
|
{
|
|
33
36
|
plugin: EdgeFunctionPlugin,
|
|
@@ -35,23 +38,26 @@ export const defaultPlugins: ComputeGraphPlugin[] = [
|
|
|
35
38
|
},
|
|
36
39
|
];
|
|
37
40
|
|
|
41
|
+
export const defaultOptions: Partial<ComputeGraphOptions> = {
|
|
42
|
+
licenseKey: 'gpl-v3',
|
|
43
|
+
};
|
|
44
|
+
|
|
38
45
|
/**
|
|
39
46
|
* Manages a collection of ComputeGraph instances for each space.
|
|
40
47
|
*
|
|
41
48
|
* [ComputePlugin] => [ComputeGraphRegistry] => [ComputeGraph(Space)] => [ComputeNode(Object)]
|
|
42
49
|
*
|
|
43
50
|
* NOTE: The ComputeGraphRegistry manages the hierarchy of resources via its root Context.
|
|
44
|
-
* NOTE: The package.json file defines the packaged #hyperformula module.
|
|
45
51
|
*/
|
|
46
52
|
// TODO(burdon): Move graph into separate plugin; isolate HF deps.
|
|
47
53
|
export class ComputeGraphRegistry extends Resource {
|
|
48
54
|
private readonly _graphs = new Map<SpaceId, ComputeGraph>();
|
|
49
|
-
|
|
50
55
|
private readonly _options: ComputeGraphOptions;
|
|
51
56
|
|
|
52
|
-
constructor(options: ComputeGraphOptions
|
|
57
|
+
constructor(options: ComputeGraphOptions) {
|
|
53
58
|
super();
|
|
54
59
|
this._options = defaultsDeep({}, options, defaultOptions);
|
|
60
|
+
this._options.plugins = this._options.plugins ?? defaultPlugins;
|
|
55
61
|
this._options.plugins?.forEach(({ plugin, translations }) => {
|
|
56
62
|
HyperFormula.registerFunctionPlugin(plugin, translations);
|
|
57
63
|
});
|
|
@@ -74,7 +80,10 @@ export class ComputeGraphRegistry extends Resource {
|
|
|
74
80
|
createGraph(space: Space): ComputeGraph {
|
|
75
81
|
invariant(!this._graphs.has(space.id), `ComputeGraph already exists for space: ${space.id}`);
|
|
76
82
|
const hf = HyperFormula.buildEmpty(this._options);
|
|
77
|
-
|
|
83
|
+
invariant(this._options.computeRuntime, 'ComputeRuntime is required');
|
|
84
|
+
const runtime = this._options.computeRuntime.getRuntime(space.id);
|
|
85
|
+
|
|
86
|
+
const graph = new ComputeGraph(hf, runtime, space, this._options);
|
|
78
87
|
this._graphs.set(space.id, graph);
|
|
79
88
|
return graph;
|
|
80
89
|
}
|
package/src/compute-graph.ts
CHANGED
|
@@ -2,17 +2,19 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import type * as ManagedRuntime from 'effect/ManagedRuntime';
|
|
6
|
+
|
|
5
7
|
import { Event } from '@dxos/async';
|
|
6
8
|
import { Filter, type Space, fullyQualifiedId } from '@dxos/client/echo';
|
|
7
9
|
import { FQ_ID_LENGTH } from '@dxos/client/echo';
|
|
8
10
|
import { Resource } from '@dxos/context';
|
|
9
|
-
import { getTypename } from '@dxos/echo
|
|
10
|
-
import { FunctionType } from '@dxos/functions';
|
|
11
|
+
import { getTypename } from '@dxos/echo/internal';
|
|
12
|
+
import { type FunctionInvocationService, FunctionType } from '@dxos/functions';
|
|
11
13
|
import { invariant } from '@dxos/invariant';
|
|
12
14
|
import { PublicKey } from '@dxos/keys';
|
|
13
15
|
import { log } from '@dxos/log';
|
|
14
16
|
import { isNonNullable } from '@dxos/util';
|
|
15
|
-
import {
|
|
17
|
+
import type { Listeners } from '@dxos/vendor-hyperformula';
|
|
16
18
|
import { ExportedCellChange, type HyperFormula } from '@dxos/vendor-hyperformula';
|
|
17
19
|
|
|
18
20
|
import { ComputeNode } from './compute-node';
|
|
@@ -67,12 +69,13 @@ export class ComputeGraph extends Resource {
|
|
|
67
69
|
|
|
68
70
|
constructor(
|
|
69
71
|
private readonly _hf: HyperFormula,
|
|
72
|
+
private readonly _runtime: ManagedRuntime.ManagedRuntime<FunctionInvocationService, never>,
|
|
70
73
|
private readonly _space?: Space,
|
|
71
74
|
private readonly _options?: Partial<FunctionContextOptions>,
|
|
72
75
|
) {
|
|
73
76
|
super();
|
|
74
77
|
|
|
75
|
-
this.context = new FunctionContext(this._hf, this._space, {
|
|
78
|
+
this.context = new FunctionContext(this._hf, this._runtime, this._space, {
|
|
76
79
|
...this._options,
|
|
77
80
|
onUpdate: (update) => {
|
|
78
81
|
this._options?.onUpdate?.(update);
|
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import type * as ManagedRuntime from 'effect/ManagedRuntime';
|
|
5
6
|
import defaultsDeep from 'lodash.defaultsdeep';
|
|
6
7
|
|
|
7
8
|
import { type CleanupFn, debounce } from '@dxos/async';
|
|
8
|
-
import {
|
|
9
|
+
import type { Space } from '@dxos/client/echo';
|
|
10
|
+
import { type FunctionInvocationService } from '@dxos/functions';
|
|
9
11
|
import { log } from '@dxos/log';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
12
|
+
import type { RawInterpreterValue, SimpleCellAddress } from '@dxos/vendor-hyperformula';
|
|
13
|
+
import type { InterpreterState } from '@dxos/vendor-hyperformula';
|
|
14
|
+
import type { InterpreterValue } from '@dxos/vendor-hyperformula';
|
|
15
|
+
import type { ProcedureAst } from '@dxos/vendor-hyperformula';
|
|
14
16
|
import { CellError, EmptyValue, ErrorType, FunctionPlugin, type HyperFormula } from '@dxos/vendor-hyperformula';
|
|
15
17
|
|
|
16
18
|
// TODO(burdon): Create API gateways:
|
|
@@ -33,14 +35,12 @@ export type FunctionOptions = {
|
|
|
33
35
|
export type FunctionContextOptions = {
|
|
34
36
|
defaultTtl: number;
|
|
35
37
|
debounceDelay: number;
|
|
36
|
-
remoteFunctionUrl: string;
|
|
37
38
|
onUpdate?: (update: FunctionUpdateEvent) => void;
|
|
38
39
|
};
|
|
39
40
|
|
|
40
|
-
export const defaultFunctionContextOptions: FunctionContextOptions = {
|
|
41
|
+
export const defaultFunctionContextOptions: Pick<FunctionContextOptions, 'defaultTtl' | 'debounceDelay'> = {
|
|
41
42
|
defaultTtl: 5_000,
|
|
42
43
|
debounceDelay: 200,
|
|
43
|
-
remoteFunctionUrl: 'https://edge.dxos.workers.dev/functions', // TODO(burdon): Config.
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
/**
|
|
@@ -77,6 +77,7 @@ export class FunctionContext {
|
|
|
77
77
|
|
|
78
78
|
constructor(
|
|
79
79
|
private readonly _hf: HyperFormula,
|
|
80
|
+
private readonly _runtime: ManagedRuntime.ManagedRuntime<FunctionInvocationService, never>,
|
|
80
81
|
private readonly _space: Space | undefined,
|
|
81
82
|
_options?: Partial<FunctionContextOptions>,
|
|
82
83
|
) {
|
|
@@ -93,14 +94,14 @@ export class FunctionContext {
|
|
|
93
94
|
return this._space;
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
get remoteFunctionUrl() {
|
|
97
|
-
return this._options.remoteFunctionUrl;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
97
|
get info() {
|
|
101
98
|
return { cache: this._cache.size, invocations: this._invocations };
|
|
102
99
|
}
|
|
103
100
|
|
|
101
|
+
get runtime() {
|
|
102
|
+
return this._runtime;
|
|
103
|
+
}
|
|
104
|
+
|
|
104
105
|
flush(): void {
|
|
105
106
|
this._cache.clear();
|
|
106
107
|
this._invocations = {};
|
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { effect } from '@preact/signals-core';
|
|
6
|
-
import
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
import * as SchemaAST from 'effect/SchemaAST';
|
|
7
8
|
|
|
8
|
-
import { Filter
|
|
9
|
-
import { toEffectSchema } from '@dxos/echo
|
|
10
|
-
import { FunctionType,
|
|
9
|
+
import { Filter } from '@dxos/client/echo';
|
|
10
|
+
import { toEffectSchema } from '@dxos/echo/internal';
|
|
11
|
+
import { ComputeEventLogger, FunctionInvocationService, FunctionType, TracingService } from '@dxos/functions';
|
|
12
|
+
import { FunctionDefinition } from '@dxos/functions';
|
|
11
13
|
import { log } from '@dxos/log';
|
|
12
14
|
import { isNonNullable } from '@dxos/util';
|
|
13
15
|
import { type ProcedureAst } from '@dxos/vendor-hyperformula';
|
|
@@ -55,25 +57,26 @@ export class EdgeFunctionPlugin extends AsyncFunctionPlugin {
|
|
|
55
57
|
this.context.createSubscription(ast.procedureName, unsubscribe);
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
const
|
|
60
|
+
const runtime = this.context.runtime;
|
|
61
|
+
const functionDef = FunctionDefinition.deserialize(fn);
|
|
62
|
+
// If input schema exists, construct an object from args using the schema props order, otherwise pass { args }.
|
|
63
|
+
let input: any;
|
|
59
64
|
if (fn.inputSchema) {
|
|
60
65
|
const schema = toEffectSchema(fn.inputSchema);
|
|
61
|
-
SchemaAST.getPropertySignatures(schema.ast)
|
|
62
|
-
|
|
66
|
+
const props = SchemaAST.getPropertySignatures(schema.ast);
|
|
67
|
+
input = {} as any;
|
|
68
|
+
props.forEach(({ name }, index) => {
|
|
69
|
+
input[name.toString()] = args[index];
|
|
63
70
|
});
|
|
64
71
|
} else {
|
|
65
|
-
|
|
72
|
+
input = { args: args.filter(isNonNullable) };
|
|
66
73
|
}
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const result = await response.text();
|
|
74
|
-
log('function executed', { result });
|
|
75
|
-
|
|
76
|
-
return result;
|
|
74
|
+
const result = runtime.runPromise(
|
|
75
|
+
Effect.gen(function* () {
|
|
76
|
+
return yield* FunctionInvocationService.invokeFunction(functionDef, input);
|
|
77
|
+
}).pipe(Effect.provide(ComputeEventLogger.layerFromTracing), Effect.provide(TracingService.layerNoop)),
|
|
78
|
+
);
|
|
79
|
+
return result as any;
|
|
77
80
|
};
|
|
78
81
|
|
|
79
82
|
return this.runAsyncFunction(ast, state, handler(true), { ttl: FUNCTION_TTL });
|
package/src/testing/index.ts
CHANGED
|
@@ -8,7 +8,9 @@ import { invariant } from '@dxos/invariant';
|
|
|
8
8
|
|
|
9
9
|
import { type ComputeGraphOptions, ComputeGraphRegistry } from '../compute-graph-registry';
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
import { createMockedComputeRuntimeProvider } from './test-runtime';
|
|
12
|
+
|
|
13
|
+
export type TestBuilderOptions = ClientOptions & Partial<ComputeGraphOptions>;
|
|
12
14
|
|
|
13
15
|
// TODO(burdon): Reconcile with @dxos/client/testing.
|
|
14
16
|
export class TestBuilder extends Resource {
|
|
@@ -43,7 +45,11 @@ export class TestBuilder extends Resource {
|
|
|
43
45
|
this._client = undefined;
|
|
44
46
|
});
|
|
45
47
|
|
|
46
|
-
const registry = new ComputeGraphRegistry(
|
|
48
|
+
const registry = new ComputeGraphRegistry({
|
|
49
|
+
...this._options,
|
|
50
|
+
// Provide a default runtime provider for tests if not supplied.
|
|
51
|
+
computeRuntime: this._options.computeRuntime ?? createMockedComputeRuntimeProvider(),
|
|
52
|
+
});
|
|
47
53
|
await registry.open();
|
|
48
54
|
this._registry = registry;
|
|
49
55
|
this._ctx.onDispose(async () => {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Layer from 'effect/Layer';
|
|
6
|
+
import * as ManagedRuntime from 'effect/ManagedRuntime';
|
|
7
|
+
|
|
8
|
+
import { AiService } from '@dxos/ai';
|
|
9
|
+
import { type SpaceId } from '@dxos/client/echo';
|
|
10
|
+
import {
|
|
11
|
+
CredentialsService,
|
|
12
|
+
DatabaseService,
|
|
13
|
+
type FunctionDefinition,
|
|
14
|
+
FunctionInvocationService,
|
|
15
|
+
QueueService,
|
|
16
|
+
} from '@dxos/functions';
|
|
17
|
+
|
|
18
|
+
import { type FunctionsRuntimeProvider } from '../compute-graph-registry';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Minimal compute runtime provider for tests and Storybook.
|
|
22
|
+
* Will cause errors if used for actual runtime.
|
|
23
|
+
*/
|
|
24
|
+
export const createMockedComputeRuntimeProvider = ({
|
|
25
|
+
functions,
|
|
26
|
+
}: { functions?: FunctionDefinition<any, any>[] } = {}): FunctionsRuntimeProvider => {
|
|
27
|
+
return {
|
|
28
|
+
getRuntime: (_spaceId: SpaceId) =>
|
|
29
|
+
ManagedRuntime.make(
|
|
30
|
+
FunctionInvocationService.layerTest({ functions }).pipe(
|
|
31
|
+
Layer.provide(AiService.notAvailable),
|
|
32
|
+
Layer.provide(CredentialsService.configuredLayer([])),
|
|
33
|
+
Layer.provide(DatabaseService.notAvailable),
|
|
34
|
+
Layer.provide(QueueService.notAvailable),
|
|
35
|
+
),
|
|
36
|
+
),
|
|
37
|
+
};
|
|
38
|
+
};
|