@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.
Files changed (40) hide show
  1. package/README.md +1 -1
  2. package/dist/lib/browser/index.mjs +1075 -17
  3. package/dist/lib/browser/index.mjs.map +4 -4
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/node/index.cjs +1069 -15
  6. package/dist/lib/node/index.cjs.map +4 -4
  7. package/dist/lib/node/meta.json +1 -1
  8. package/dist/types/src/function/function-registry.d.ts +0 -1
  9. package/dist/types/src/function/function-registry.d.ts.map +1 -1
  10. package/dist/types/src/runtime/dev-server.d.ts +0 -1
  11. package/dist/types/src/runtime/dev-server.d.ts.map +1 -1
  12. package/dist/types/src/testing/index.d.ts +0 -1
  13. package/dist/types/src/testing/index.d.ts.map +1 -1
  14. package/dist/types/src/testing/setup.d.ts +1 -11
  15. package/dist/types/src/testing/setup.d.ts.map +1 -1
  16. package/dist/types/src/testing/util.d.ts +0 -2
  17. package/dist/types/src/testing/util.d.ts.map +1 -1
  18. package/package.json +14 -23
  19. package/src/function/function-registry.test.ts +1 -14
  20. package/src/function/function-registry.ts +0 -10
  21. package/src/runtime/dev-server.test.ts +24 -42
  22. package/src/runtime/dev-server.ts +13 -17
  23. package/src/testing/functions-integration.test.ts +45 -8
  24. package/src/testing/index.ts +0 -1
  25. package/src/testing/setup.ts +6 -63
  26. package/src/testing/util.ts +0 -10
  27. package/dist/lib/browser/chunk-ERL6PHMU.mjs +0 -1084
  28. package/dist/lib/browser/chunk-ERL6PHMU.mjs.map +0 -7
  29. package/dist/lib/browser/testing/index.mjs +0 -148
  30. package/dist/lib/browser/testing/index.mjs.map +0 -7
  31. package/dist/lib/node/chunk-INM6XAL7.cjs +0 -1097
  32. package/dist/lib/node/chunk-INM6XAL7.cjs.map +0 -7
  33. package/dist/lib/node/testing/index.cjs +0 -169
  34. package/dist/lib/node/testing/index.cjs.map +0 -7
  35. package/dist/types/src/testing/manifest.d.ts +0 -3
  36. package/dist/types/src/testing/manifest.d.ts.map +0 -1
  37. package/dist/types/src/testing/plugin-init.d.ts +0 -6
  38. package/dist/types/src/testing/plugin-init.d.ts.map +0 -1
  39. package/src/testing/manifest.ts +0 -15
  40. 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 { asyncTimeout, Event, Trigger } from '@dxos/async';
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 asyncTimeout(this.invoke('/' + path, req.body), 20_000);
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 = this._options.port ?? (await getPort({ host: 'localhost', port: 7200, portRange: [7200, 7299] }));
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._handleNewFunctions(this._functionsRegistry.getUniqueByUri());
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
- if (!this._server) {
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 { create } from '@dxos/client/echo';
9
- import { TestBuilder } from '@dxos/client/testing';
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 { createInitializedClients, inviteMember, startFunctionsHost, TestType } from '../testing';
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 startFunctionsHost(testBuilder, initFunctionsPlugin);
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.client);
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 functionRuntime.waitHasActiveTriggers(space);
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
  });
@@ -5,4 +5,3 @@
5
5
  export * from './setup';
6
6
  export * from './types';
7
7
  export * from './util';
8
- export * from './manifest';
@@ -2,24 +2,14 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import path from 'node:path';
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: functionsPort++ } }],
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 = await pluginInitializer(client);
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
- };
@@ -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
- };