@dxos/functions 0.5.4-next.70d721e → 0.5.4
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/README.md +1 -1
- package/dist/lib/browser/index.mjs +1075 -17
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +1069 -15
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/function/function-registry.d.ts +0 -1
- package/dist/types/src/function/function-registry.d.ts.map +1 -1
- package/dist/types/src/runtime/dev-server.d.ts +0 -1
- package/dist/types/src/runtime/dev-server.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +0 -1
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/setup.d.ts +1 -11
- package/dist/types/src/testing/setup.d.ts.map +1 -1
- package/dist/types/src/testing/util.d.ts +0 -2
- package/dist/types/src/testing/util.d.ts.map +1 -1
- package/package.json +14 -23
- package/src/function/function-registry.test.ts +1 -14
- package/src/function/function-registry.ts +0 -10
- package/src/runtime/dev-server.test.ts +24 -42
- package/src/runtime/dev-server.ts +13 -17
- package/src/testing/functions-integration.test.ts +45 -8
- package/src/testing/index.ts +0 -1
- package/src/testing/setup.ts +6 -63
- package/src/testing/util.ts +0 -10
- package/dist/lib/browser/chunk-ERL6PHMU.mjs +0 -1084
- package/dist/lib/browser/chunk-ERL6PHMU.mjs.map +0 -7
- package/dist/lib/browser/testing/index.mjs +0 -148
- package/dist/lib/browser/testing/index.mjs.map +0 -7
- package/dist/lib/node/chunk-INM6XAL7.cjs +0 -1097
- package/dist/lib/node/chunk-INM6XAL7.cjs.map +0 -7
- package/dist/lib/node/testing/index.cjs +0 -169
- package/dist/lib/node/testing/index.cjs.map +0 -7
- package/dist/types/src/testing/manifest.d.ts +0 -3
- package/dist/types/src/testing/manifest.d.ts.map +0 -1
- package/dist/types/src/testing/plugin-init.d.ts +0 -6
- package/dist/types/src/testing/plugin-init.d.ts.map +0 -1
- package/src/testing/manifest.ts +0 -15
- package/src/testing/plugin-init.ts +0 -20
|
@@ -7,7 +7,7 @@ import { getPort } from 'get-port-please';
|
|
|
7
7
|
import type http from 'http';
|
|
8
8
|
import { join } from 'node:path';
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { Event, Trigger } from '@dxos/async';
|
|
11
11
|
import { type Client } from '@dxos/client';
|
|
12
12
|
import { Context } from '@dxos/context';
|
|
13
13
|
import { invariant } from '@dxos/invariant';
|
|
@@ -45,7 +45,14 @@ export class DevServer {
|
|
|
45
45
|
private readonly _client: Client,
|
|
46
46
|
private readonly _functionsRegistry: FunctionRegistry,
|
|
47
47
|
private readonly _options: DevServerOptions,
|
|
48
|
-
) {
|
|
48
|
+
) {
|
|
49
|
+
// TODO(burdon): Add/remove listener in start/stop.
|
|
50
|
+
this._functionsRegistry.registered.on(async ({ added }) => {
|
|
51
|
+
added.forEach((def) => this._load(def));
|
|
52
|
+
await this._safeUpdateRegistration();
|
|
53
|
+
log('new functions loaded', { added });
|
|
54
|
+
});
|
|
55
|
+
}
|
|
49
56
|
|
|
50
57
|
get stats() {
|
|
51
58
|
return {
|
|
@@ -85,7 +92,7 @@ export class DevServer {
|
|
|
85
92
|
}
|
|
86
93
|
|
|
87
94
|
// TODO(burdon): Get function context.
|
|
88
|
-
res.statusCode = await
|
|
95
|
+
res.statusCode = await this.invoke('/' + path, req.body);
|
|
89
96
|
res.end();
|
|
90
97
|
} catch (err: any) {
|
|
91
98
|
log.catch(err);
|
|
@@ -94,7 +101,7 @@ export class DevServer {
|
|
|
94
101
|
}
|
|
95
102
|
});
|
|
96
103
|
|
|
97
|
-
this._port =
|
|
104
|
+
this._port = await getPort({ host: 'localhost', port: 7200, portRange: [7200, 7299] });
|
|
98
105
|
this._server = app.listen(this._port);
|
|
99
106
|
|
|
100
107
|
try {
|
|
@@ -108,8 +115,7 @@ export class DevServer {
|
|
|
108
115
|
this._functionServiceRegistration = registrationId;
|
|
109
116
|
|
|
110
117
|
// Open after registration, so that it can be updated with the list of function definitions.
|
|
111
|
-
await this.
|
|
112
|
-
this._ctx.onDispose(this._functionsRegistry.registered.on(({ added }) => this._handleNewFunctions(added)));
|
|
118
|
+
await this._functionsRegistry.open(this._ctx);
|
|
113
119
|
} catch (err: any) {
|
|
114
120
|
await this.stop();
|
|
115
121
|
throw new Error('FunctionRegistryService not available (check plugin is configured).');
|
|
@@ -119,12 +125,8 @@ export class DevServer {
|
|
|
119
125
|
}
|
|
120
126
|
|
|
121
127
|
async stop() {
|
|
122
|
-
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
|
|
128
|
+
invariant(this._server);
|
|
126
129
|
log.info('stopping...');
|
|
127
|
-
await this._ctx.dispose();
|
|
128
130
|
|
|
129
131
|
const trigger = new Trigger();
|
|
130
132
|
this._server.close(async () => {
|
|
@@ -153,12 +155,6 @@ export class DevServer {
|
|
|
153
155
|
log.info('stopped');
|
|
154
156
|
}
|
|
155
157
|
|
|
156
|
-
private async _handleNewFunctions(newFunctions: FunctionDef[]) {
|
|
157
|
-
newFunctions.forEach((def) => this._load(def));
|
|
158
|
-
await this._safeUpdateRegistration();
|
|
159
|
-
log('new functions loaded', { newFunctions });
|
|
160
|
-
}
|
|
161
|
-
|
|
162
158
|
/**
|
|
163
159
|
* Load function.
|
|
164
160
|
*/
|
|
@@ -3,15 +3,20 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { expect } from 'chai';
|
|
6
|
+
import path from 'path';
|
|
6
7
|
|
|
7
|
-
import { Trigger } from '@dxos/async';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { Trigger, waitForCondition } from '@dxos/async';
|
|
9
|
+
import { type Client } from '@dxos/client';
|
|
10
|
+
import { create, type Space } from '@dxos/client/echo';
|
|
11
|
+
import { performInvitation, TestBuilder } from '@dxos/client/testing';
|
|
12
|
+
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
10
13
|
import { describe, test } from '@dxos/test';
|
|
11
14
|
|
|
12
|
-
import { initFunctionsPlugin } from './plugin-init';
|
|
13
15
|
import { setTestCallHandler } from './test/handler';
|
|
14
|
-
import {
|
|
16
|
+
import { FunctionRegistry } from '../function';
|
|
17
|
+
import { DevServer, Scheduler } from '../runtime';
|
|
18
|
+
import { createFunctionRuntime, createInitializedClients, TestType } from '../testing';
|
|
19
|
+
import { TriggerRegistry } from '../trigger';
|
|
15
20
|
import { FunctionDef, FunctionTrigger } from '../types';
|
|
16
21
|
|
|
17
22
|
describe('functions e2e', () => {
|
|
@@ -25,11 +30,13 @@ describe('functions e2e', () => {
|
|
|
25
30
|
|
|
26
31
|
test('a function gets triggered in response to another peer object creations', async () => {
|
|
27
32
|
// TODO(burdon): Create builder pattern.
|
|
28
|
-
const functionRuntime = await
|
|
33
|
+
const functionRuntime = await createFunctionRuntime(testBuilder);
|
|
34
|
+
const devServer = await startDevServer(functionRuntime);
|
|
35
|
+
const scheduler = await startScheduler(functionRuntime, devServer);
|
|
29
36
|
|
|
30
37
|
const app = (await createInitializedClients(testBuilder, 1))[0];
|
|
31
38
|
const space = await app.spaces.create();
|
|
32
|
-
await inviteMember(space, functionRuntime
|
|
39
|
+
await inviteMember(space, functionRuntime);
|
|
33
40
|
|
|
34
41
|
const uri = 'example.com/function/test';
|
|
35
42
|
space.db.add(create(FunctionDef, { uri, route: '/test', handler: 'test' }));
|
|
@@ -52,7 +59,7 @@ describe('functions e2e', () => {
|
|
|
52
59
|
return args.response.status(200);
|
|
53
60
|
});
|
|
54
61
|
|
|
55
|
-
await
|
|
62
|
+
await waitTriggersReplicated(space, scheduler);
|
|
56
63
|
const addedObject = space.db.add(create(TestType, { title: '42' }));
|
|
57
64
|
|
|
58
65
|
const callArgs = await called.wait();
|
|
@@ -60,4 +67,34 @@ describe('functions e2e', () => {
|
|
|
60
67
|
expect(callArgs.objects).to.deep.eq([addedObject.id]);
|
|
61
68
|
expect(callArgs.spaceKey).to.eq(space.key.toHex());
|
|
62
69
|
});
|
|
70
|
+
|
|
71
|
+
const waitTriggersReplicated = async (space: Space, scheduler: Scheduler) => {
|
|
72
|
+
await waitForCondition({ condition: () => scheduler.triggers.getActiveTriggers(space).length > 0 });
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// TODO(burdon): Factor out utils to builder pattern.
|
|
76
|
+
|
|
77
|
+
const startScheduler = async (client: Client, devServer: DevServer) => {
|
|
78
|
+
const functionRegistry = new FunctionRegistry(client);
|
|
79
|
+
const triggerRegistry = new TriggerRegistry(client);
|
|
80
|
+
const scheduler = new Scheduler(functionRegistry, triggerRegistry, { endpoint: devServer.endpoint });
|
|
81
|
+
await scheduler.start();
|
|
82
|
+
testBuilder.ctx.onDispose(() => scheduler.stop());
|
|
83
|
+
return scheduler;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const startDevServer = async (client: Client) => {
|
|
87
|
+
const functionRegistry = new FunctionRegistry(client);
|
|
88
|
+
const server = new DevServer(client, functionRegistry, {
|
|
89
|
+
baseDir: path.join(__dirname, '../testing'),
|
|
90
|
+
});
|
|
91
|
+
await server.start();
|
|
92
|
+
testBuilder.ctx.onDispose(() => server.stop());
|
|
93
|
+
return server;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const inviteMember = async (host: Space, guest: Client) => {
|
|
97
|
+
const [{ invitation: hostInvitation }] = await Promise.all(performInvitation({ host, guest: guest.spaces }));
|
|
98
|
+
expect(hostInvitation?.state).to.eq(Invitation.State.SUCCESS);
|
|
99
|
+
};
|
|
63
100
|
});
|
package/src/testing/index.ts
CHANGED
package/src/testing/setup.ts
CHANGED
|
@@ -2,24 +2,14 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
import { waitForCondition } from '@dxos/async';
|
|
5
|
+
import { FunctionsPlugin } from '@dxos/agent';
|
|
8
6
|
import { Client, Config } from '@dxos/client';
|
|
9
|
-
import { type Space } from '@dxos/client/echo';
|
|
10
7
|
import { type TestBuilder } from '@dxos/client/testing';
|
|
11
8
|
import { range } from '@dxos/util';
|
|
12
9
|
|
|
13
10
|
import { TestType } from './types';
|
|
14
|
-
import { FunctionRegistry } from '../function';
|
|
15
|
-
import { DevServer, type DevServerOptions, Scheduler } from '../runtime';
|
|
16
|
-
import { TriggerRegistry } from '../trigger';
|
|
17
11
|
import { FunctionDef, FunctionTrigger } from '../types';
|
|
18
12
|
|
|
19
|
-
let functionsPort = 8081;
|
|
20
|
-
|
|
21
|
-
export type FunctionsPluginInitializer = (client: Client) => Promise<{ close: () => Promise<void> }>;
|
|
22
|
-
|
|
23
13
|
// TODO(burdon): Extend/wrap TestBuilder.
|
|
24
14
|
|
|
25
15
|
export const createInitializedClients = async (testBuilder: TestBuilder, count: number = 1, config?: Config) => {
|
|
@@ -35,66 +25,19 @@ export const createInitializedClients = async (testBuilder: TestBuilder, count:
|
|
|
35
25
|
);
|
|
36
26
|
};
|
|
37
27
|
|
|
38
|
-
export const createFunctionRuntime = async (
|
|
39
|
-
testBuilder: TestBuilder,
|
|
40
|
-
pluginInitializer: FunctionsPluginInitializer,
|
|
41
|
-
): Promise<Client> => {
|
|
28
|
+
export const createFunctionRuntime = async (testBuilder: TestBuilder): Promise<Client> => {
|
|
42
29
|
const config = new Config({
|
|
43
30
|
runtime: {
|
|
44
31
|
agent: {
|
|
45
|
-
plugins: [{ id: 'dxos.org/agent/plugin/functions', config: { port:
|
|
32
|
+
plugins: [{ id: 'dxos.org/agent/plugin/functions', config: { port: 8080 } }],
|
|
46
33
|
},
|
|
47
34
|
},
|
|
48
35
|
});
|
|
49
36
|
|
|
50
37
|
const [client] = await createInitializedClients(testBuilder, 1, config);
|
|
51
|
-
const plugin =
|
|
38
|
+
const plugin = new FunctionsPlugin();
|
|
39
|
+
await plugin.initialize({ client, clientServices: client.services });
|
|
40
|
+
await plugin.open();
|
|
52
41
|
testBuilder.ctx.onDispose(() => plugin.close());
|
|
53
42
|
return client;
|
|
54
43
|
};
|
|
55
|
-
|
|
56
|
-
export const startFunctionsHost = async (
|
|
57
|
-
testBuilder: TestBuilder,
|
|
58
|
-
pluginInitializer: FunctionsPluginInitializer,
|
|
59
|
-
options?: DevServerOptions,
|
|
60
|
-
) => {
|
|
61
|
-
const functionRuntime = await createFunctionRuntime(testBuilder, pluginInitializer);
|
|
62
|
-
const functionsRegistry = new FunctionRegistry(functionRuntime);
|
|
63
|
-
const devServer = await startDevServer(testBuilder, functionRuntime, functionsRegistry, options);
|
|
64
|
-
const scheduler = await startScheduler(testBuilder, functionRuntime, devServer, functionsRegistry);
|
|
65
|
-
return {
|
|
66
|
-
scheduler,
|
|
67
|
-
client: functionRuntime,
|
|
68
|
-
waitHasActiveTriggers: async (space: Space) => {
|
|
69
|
-
await waitForCondition({ condition: () => scheduler.triggers.getActiveTriggers(space).length > 0 });
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
const startScheduler = async (
|
|
75
|
-
testBuilder: TestBuilder,
|
|
76
|
-
client: Client,
|
|
77
|
-
devServer: DevServer,
|
|
78
|
-
functionRegistry: FunctionRegistry,
|
|
79
|
-
) => {
|
|
80
|
-
const triggerRegistry = new TriggerRegistry(client);
|
|
81
|
-
const scheduler = new Scheduler(functionRegistry, triggerRegistry, { endpoint: devServer.endpoint });
|
|
82
|
-
await scheduler.start();
|
|
83
|
-
testBuilder.ctx.onDispose(() => scheduler.stop());
|
|
84
|
-
return scheduler;
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const startDevServer = async (
|
|
88
|
-
testBuilder: TestBuilder,
|
|
89
|
-
client: Client,
|
|
90
|
-
functionRegistry: FunctionRegistry,
|
|
91
|
-
options?: { baseDir?: string },
|
|
92
|
-
) => {
|
|
93
|
-
const server = new DevServer(client, functionRegistry, {
|
|
94
|
-
baseDir: path.join(__dirname, '../testing'),
|
|
95
|
-
...options,
|
|
96
|
-
});
|
|
97
|
-
await server.start();
|
|
98
|
-
testBuilder.ctx.onDispose(() => server.stop());
|
|
99
|
-
return server;
|
|
100
|
-
};
|
package/src/testing/util.ts
CHANGED
|
@@ -2,11 +2,8 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import type { Client } from '@dxos/client';
|
|
6
5
|
import { Filter, type Space } from '@dxos/client/echo';
|
|
7
|
-
import { performInvitation } from '@dxos/client/testing';
|
|
8
6
|
import { invariant } from '@dxos/invariant';
|
|
9
|
-
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
10
7
|
|
|
11
8
|
import { FunctionTrigger } from '../types';
|
|
12
9
|
|
|
@@ -17,10 +14,3 @@ export const triggerWebhook = async (space: Space, uri: string) => {
|
|
|
17
14
|
invariant(trigger.spec.type === 'webhook');
|
|
18
15
|
void fetch(`http://localhost:${trigger.spec.port}`);
|
|
19
16
|
};
|
|
20
|
-
|
|
21
|
-
export const inviteMember = async (host: Space, guest: Client) => {
|
|
22
|
-
const [{ invitation: hostInvitation }] = await Promise.all(performInvitation({ host, guest: guest.spaces }));
|
|
23
|
-
if (hostInvitation?.state !== Invitation.State.SUCCESS) {
|
|
24
|
-
throw new Error(`Expected ${hostInvitation?.state} to be ${Invitation.State.SUCCESS}.`);
|
|
25
|
-
}
|
|
26
|
-
};
|