@dxos/functions 0.5.3-main.bc67fdb → 0.5.3-main.c8ad1bb
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/index.mjs +642 -424
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +627 -421
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/index.d.ts +2 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/registry/function-registry.d.ts +24 -0
- package/dist/types/src/registry/function-registry.d.ts.map +1 -0
- package/dist/types/src/registry/function-registry.test.d.ts +2 -0
- package/dist/types/src/registry/function-registry.test.d.ts.map +1 -0
- package/dist/types/src/registry/index.d.ts +2 -0
- package/dist/types/src/registry/index.d.ts.map +1 -0
- package/dist/types/src/runtime/dev-server.d.ts +7 -10
- package/dist/types/src/runtime/dev-server.d.ts.map +1 -1
- package/dist/types/src/runtime/scheduler.d.ts +10 -59
- package/dist/types/src/runtime/scheduler.d.ts.map +1 -1
- package/dist/types/src/testing/functions-integration.test.d.ts +2 -0
- package/dist/types/src/testing/functions-integration.test.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +4 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/setup.d.ts +5 -0
- package/dist/types/src/testing/setup.d.ts.map +1 -0
- package/dist/types/src/testing/test/handler.d.ts +1 -0
- package/dist/types/src/testing/test/handler.d.ts.map +1 -1
- package/dist/types/src/testing/types.d.ts +9 -0
- package/dist/types/src/testing/types.d.ts.map +1 -0
- package/dist/types/src/testing/util.d.ts +3 -0
- package/dist/types/src/testing/util.d.ts.map +1 -0
- package/dist/types/src/trigger/index.d.ts +2 -0
- package/dist/types/src/trigger/index.d.ts.map +1 -0
- package/dist/types/src/trigger/trigger-registry.d.ts +40 -0
- package/dist/types/src/trigger/trigger-registry.d.ts.map +1 -0
- package/dist/types/src/trigger/trigger-registry.test.d.ts +2 -0
- package/dist/types/src/trigger/trigger-registry.test.d.ts.map +1 -0
- package/dist/types/src/trigger/type/index.d.ts +5 -0
- package/dist/types/src/trigger/type/index.d.ts.map +1 -0
- package/dist/types/src/trigger/type/subscription-trigger.d.ts +4 -0
- package/dist/types/src/trigger/type/subscription-trigger.d.ts.map +1 -0
- package/dist/types/src/trigger/type/timer-trigger.d.ts +4 -0
- package/dist/types/src/trigger/type/timer-trigger.d.ts.map +1 -0
- package/dist/types/src/trigger/type/webhook-trigger.d.ts +4 -0
- package/dist/types/src/trigger/type/webhook-trigger.d.ts.map +1 -0
- package/dist/types/src/trigger/type/websocket-trigger.d.ts +13 -0
- package/dist/types/src/trigger/type/websocket-trigger.d.ts.map +1 -0
- package/dist/types/src/types.d.ts +118 -112
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +16 -13
- package/schema/functions.json +121 -107
- package/src/index.ts +2 -0
- package/src/registry/function-registry.test.ts +105 -0
- package/src/registry/function-registry.ts +84 -0
- package/src/registry/index.ts +5 -0
- package/src/runtime/dev-server.test.ts +15 -35
- package/src/runtime/dev-server.ts +36 -18
- package/src/runtime/scheduler.test.ts +54 -75
- package/src/runtime/scheduler.ts +58 -298
- package/src/testing/functions-integration.test.ts +99 -0
- package/src/testing/index.ts +7 -0
- package/src/testing/setup.ts +45 -0
- package/src/testing/test/handler.ts +8 -2
- package/src/testing/types.ts +9 -0
- package/src/testing/util.ts +16 -0
- package/src/trigger/index.ts +5 -0
- package/src/trigger/trigger-registry.test.ts +229 -0
- package/src/trigger/trigger-registry.ts +176 -0
- package/src/trigger/type/index.ts +8 -0
- package/src/trigger/type/subscription-trigger.ts +73 -0
- package/src/trigger/type/timer-trigger.ts +44 -0
- package/src/trigger/type/webhook-trigger.ts +47 -0
- package/src/trigger/type/websocket-trigger.ts +91 -0
- package/src/types.ts +56 -42
package/schema/functions.json
CHANGED
|
@@ -1,26 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"type": "object",
|
|
4
|
-
"required": [
|
|
5
|
-
"functions"
|
|
6
|
-
],
|
|
4
|
+
"required": [],
|
|
7
5
|
"properties": {
|
|
8
6
|
"functions": {
|
|
9
7
|
"type": "array",
|
|
10
8
|
"items": {
|
|
11
9
|
"type": "object",
|
|
12
10
|
"required": [
|
|
13
|
-
"
|
|
14
|
-
"
|
|
11
|
+
"uri",
|
|
12
|
+
"route",
|
|
15
13
|
"handler"
|
|
16
14
|
],
|
|
17
15
|
"properties": {
|
|
18
|
-
"
|
|
16
|
+
"uri": {
|
|
19
17
|
"type": "string",
|
|
20
18
|
"description": "a string",
|
|
21
19
|
"title": "string"
|
|
22
20
|
},
|
|
23
|
-
"
|
|
21
|
+
"route": {
|
|
24
22
|
"type": "string",
|
|
25
23
|
"description": "a string",
|
|
26
24
|
"title": "string"
|
|
@@ -44,7 +42,8 @@
|
|
|
44
42
|
"items": {
|
|
45
43
|
"type": "object",
|
|
46
44
|
"required": [
|
|
47
|
-
"function"
|
|
45
|
+
"function",
|
|
46
|
+
"spec"
|
|
48
47
|
],
|
|
49
48
|
"properties": {
|
|
50
49
|
"function": {
|
|
@@ -61,118 +60,133 @@
|
|
|
61
60
|
"title": "any"
|
|
62
61
|
}
|
|
63
62
|
},
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
|
|
67
|
-
"cron"
|
|
68
|
-
],
|
|
69
|
-
"properties": {
|
|
70
|
-
"cron": {
|
|
71
|
-
"type": "string",
|
|
72
|
-
"description": "a string",
|
|
73
|
-
"title": "string"
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
"additionalProperties": false
|
|
77
|
-
},
|
|
78
|
-
"webhook": {
|
|
79
|
-
"type": "object",
|
|
80
|
-
"required": [
|
|
81
|
-
"method"
|
|
82
|
-
],
|
|
83
|
-
"properties": {
|
|
84
|
-
"method": {
|
|
85
|
-
"type": "string",
|
|
86
|
-
"description": "a string",
|
|
87
|
-
"title": "string"
|
|
88
|
-
},
|
|
89
|
-
"port": {
|
|
90
|
-
"type": "number",
|
|
91
|
-
"description": "a number",
|
|
92
|
-
"title": "number"
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
"additionalProperties": false
|
|
96
|
-
},
|
|
97
|
-
"websocket": {
|
|
98
|
-
"type": "object",
|
|
99
|
-
"required": [
|
|
100
|
-
"url"
|
|
101
|
-
],
|
|
102
|
-
"properties": {
|
|
103
|
-
"url": {
|
|
104
|
-
"type": "string",
|
|
105
|
-
"description": "a string",
|
|
106
|
-
"title": "string"
|
|
107
|
-
},
|
|
108
|
-
"init": {
|
|
63
|
+
"spec": {
|
|
64
|
+
"anyOf": [
|
|
65
|
+
{
|
|
109
66
|
"type": "object",
|
|
110
|
-
"required": [
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
},
|
|
118
|
-
"additionalProperties": false
|
|
119
|
-
},
|
|
120
|
-
"subscription": {
|
|
121
|
-
"type": "object",
|
|
122
|
-
"required": [
|
|
123
|
-
"filter"
|
|
124
|
-
],
|
|
125
|
-
"properties": {
|
|
126
|
-
"spaceKey": {
|
|
127
|
-
"type": "string",
|
|
128
|
-
"description": "a string",
|
|
129
|
-
"title": "string"
|
|
130
|
-
},
|
|
131
|
-
"filter": {
|
|
132
|
-
"type": "array",
|
|
133
|
-
"items": {
|
|
134
|
-
"type": "object",
|
|
135
|
-
"required": [
|
|
136
|
-
"type"
|
|
137
|
-
],
|
|
138
|
-
"properties": {
|
|
139
|
-
"type": {
|
|
140
|
-
"type": "string",
|
|
141
|
-
"description": "a string",
|
|
142
|
-
"title": "string"
|
|
143
|
-
},
|
|
144
|
-
"props": {
|
|
145
|
-
"type": "object",
|
|
146
|
-
"required": [],
|
|
147
|
-
"properties": {},
|
|
148
|
-
"additionalProperties": {
|
|
149
|
-
"$id": "/schemas/any",
|
|
150
|
-
"title": "any"
|
|
151
|
-
}
|
|
152
|
-
}
|
|
67
|
+
"required": [
|
|
68
|
+
"type",
|
|
69
|
+
"cron"
|
|
70
|
+
],
|
|
71
|
+
"properties": {
|
|
72
|
+
"type": {
|
|
73
|
+
"const": "timer"
|
|
153
74
|
},
|
|
154
|
-
"
|
|
155
|
-
|
|
75
|
+
"cron": {
|
|
76
|
+
"type": "string",
|
|
77
|
+
"description": "a string",
|
|
78
|
+
"title": "string"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"additionalProperties": false
|
|
156
82
|
},
|
|
157
|
-
|
|
83
|
+
{
|
|
158
84
|
"type": "object",
|
|
159
|
-
"required": [
|
|
85
|
+
"required": [
|
|
86
|
+
"type",
|
|
87
|
+
"method"
|
|
88
|
+
],
|
|
160
89
|
"properties": {
|
|
161
|
-
"
|
|
162
|
-
"
|
|
163
|
-
|
|
164
|
-
|
|
90
|
+
"type": {
|
|
91
|
+
"const": "webhook"
|
|
92
|
+
},
|
|
93
|
+
"method": {
|
|
94
|
+
"type": "string",
|
|
95
|
+
"description": "a string",
|
|
96
|
+
"title": "string"
|
|
165
97
|
},
|
|
166
|
-
"
|
|
98
|
+
"port": {
|
|
167
99
|
"type": "number",
|
|
168
100
|
"description": "a number",
|
|
169
101
|
"title": "number"
|
|
170
102
|
}
|
|
171
103
|
},
|
|
172
104
|
"additionalProperties": false
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"type": "object",
|
|
108
|
+
"required": [
|
|
109
|
+
"type",
|
|
110
|
+
"url"
|
|
111
|
+
],
|
|
112
|
+
"properties": {
|
|
113
|
+
"type": {
|
|
114
|
+
"const": "websocket"
|
|
115
|
+
},
|
|
116
|
+
"url": {
|
|
117
|
+
"type": "string",
|
|
118
|
+
"description": "a string",
|
|
119
|
+
"title": "string"
|
|
120
|
+
},
|
|
121
|
+
"init": {
|
|
122
|
+
"type": "object",
|
|
123
|
+
"required": [],
|
|
124
|
+
"properties": {},
|
|
125
|
+
"additionalProperties": {
|
|
126
|
+
"$id": "/schemas/any",
|
|
127
|
+
"title": "any"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"additionalProperties": false
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"type": "object",
|
|
135
|
+
"required": [
|
|
136
|
+
"type",
|
|
137
|
+
"filter"
|
|
138
|
+
],
|
|
139
|
+
"properties": {
|
|
140
|
+
"type": {
|
|
141
|
+
"const": "subscription"
|
|
142
|
+
},
|
|
143
|
+
"filter": {
|
|
144
|
+
"type": "array",
|
|
145
|
+
"items": {
|
|
146
|
+
"type": "object",
|
|
147
|
+
"required": [
|
|
148
|
+
"type"
|
|
149
|
+
],
|
|
150
|
+
"properties": {
|
|
151
|
+
"type": {
|
|
152
|
+
"type": "string",
|
|
153
|
+
"description": "a string",
|
|
154
|
+
"title": "string"
|
|
155
|
+
},
|
|
156
|
+
"props": {
|
|
157
|
+
"type": "object",
|
|
158
|
+
"required": [],
|
|
159
|
+
"properties": {},
|
|
160
|
+
"additionalProperties": {
|
|
161
|
+
"$id": "/schemas/any",
|
|
162
|
+
"title": "any"
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"additionalProperties": false
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
"options": {
|
|
170
|
+
"type": "object",
|
|
171
|
+
"required": [],
|
|
172
|
+
"properties": {
|
|
173
|
+
"deep": {
|
|
174
|
+
"type": "boolean",
|
|
175
|
+
"description": "a boolean",
|
|
176
|
+
"title": "boolean"
|
|
177
|
+
},
|
|
178
|
+
"delay": {
|
|
179
|
+
"type": "number",
|
|
180
|
+
"description": "a number",
|
|
181
|
+
"title": "number"
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
"additionalProperties": false
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
"additionalProperties": false
|
|
173
188
|
}
|
|
174
|
-
|
|
175
|
-
"additionalProperties": false
|
|
189
|
+
]
|
|
176
190
|
}
|
|
177
191
|
},
|
|
178
192
|
"additionalProperties": false
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { expect } from 'chai';
|
|
6
|
+
|
|
7
|
+
import { Trigger } from '@dxos/async';
|
|
8
|
+
import { type Client } from '@dxos/client';
|
|
9
|
+
import { TestBuilder } from '@dxos/client/testing';
|
|
10
|
+
import { Context } from '@dxos/context';
|
|
11
|
+
import { Filter } from '@dxos/echo-db';
|
|
12
|
+
import { create } from '@dxos/echo-schema';
|
|
13
|
+
import { describe, test } from '@dxos/test';
|
|
14
|
+
import { range } from '@dxos/util';
|
|
15
|
+
|
|
16
|
+
import { FunctionRegistry } from './function-registry';
|
|
17
|
+
import { createInitializedClients } from '../testing';
|
|
18
|
+
import { FunctionDef, type FunctionManifest } from '../types';
|
|
19
|
+
|
|
20
|
+
const testManifest: FunctionManifest = {
|
|
21
|
+
functions: [
|
|
22
|
+
{
|
|
23
|
+
uri: 'dxos.functions.test/hello',
|
|
24
|
+
route: '/hello',
|
|
25
|
+
handler: 'test',
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
describe('function registry', () => {
|
|
31
|
+
let ctx: Context;
|
|
32
|
+
let testBuilder: TestBuilder;
|
|
33
|
+
beforeEach(async () => {
|
|
34
|
+
ctx = new Context();
|
|
35
|
+
testBuilder = new TestBuilder();
|
|
36
|
+
});
|
|
37
|
+
afterEach(async () => {
|
|
38
|
+
await ctx.dispose();
|
|
39
|
+
await testBuilder.destroy();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('register', () => {
|
|
43
|
+
test('creates new functions', async () => {
|
|
44
|
+
const client = (await createInitializedClients(testBuilder))[0];
|
|
45
|
+
const registry = createRegistry(client);
|
|
46
|
+
const space = await client.spaces.create();
|
|
47
|
+
await registry.register(space, testManifest);
|
|
48
|
+
const { objects: definitions } = await space.db.query(Filter.schema(FunctionDef)).run();
|
|
49
|
+
expect(definitions.length).to.eq(1);
|
|
50
|
+
expect(definitions[0].uri).to.eq(testManifest.functions?.[0]?.uri);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('de-duplicates by function URI', async () => {
|
|
54
|
+
const client = (await createInitializedClients(testBuilder))[0];
|
|
55
|
+
const registry = createRegistry(client);
|
|
56
|
+
const space = await client.spaces.create();
|
|
57
|
+
const existing = space.db.add(create(FunctionDef, { ...testManifest.functions![0] }));
|
|
58
|
+
await registry.register(space, testManifest);
|
|
59
|
+
const { objects: definitions } = await space.db.query(Filter.schema(FunctionDef)).run();
|
|
60
|
+
expect(definitions.length).to.eq(1);
|
|
61
|
+
expect(definitions[0].uri).to.eq(existing.uri);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('onFunctionsRegistered', () => {
|
|
66
|
+
test('called with all registered when opened', async () => {
|
|
67
|
+
const client = (await createInitializedClients(testBuilder))[0];
|
|
68
|
+
const registry = createRegistry(client);
|
|
69
|
+
const space = await client.spaces.create();
|
|
70
|
+
const definitions = range(3, () => create(FunctionDef, { ...testManifest.functions![0] }));
|
|
71
|
+
definitions.forEach((def) => space.db.add(def));
|
|
72
|
+
|
|
73
|
+
const functionRegistered = new Trigger<FunctionDef[]>();
|
|
74
|
+
registry.onFunctionsRegistered.on((fn) => {
|
|
75
|
+
functionRegistered.wake(fn.newFunctions);
|
|
76
|
+
});
|
|
77
|
+
void registry.open(ctx);
|
|
78
|
+
const functions = await functionRegistered.wait();
|
|
79
|
+
const expected = definitions.map((def) => def.uri).sort();
|
|
80
|
+
expect(functions.map((fn) => fn.uri).sort()).to.deep.eq(expected);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('called when a new functions is added', async () => {
|
|
84
|
+
const client = (await createInitializedClients(testBuilder))[0];
|
|
85
|
+
const registry = createRegistry(client);
|
|
86
|
+
const space = await client.spaces.create();
|
|
87
|
+
|
|
88
|
+
const functionRegistered = new Trigger<FunctionDef>();
|
|
89
|
+
registry.onFunctionsRegistered.on((fn) => {
|
|
90
|
+
expect(fn.newFunctions.length).to.eq(1);
|
|
91
|
+
functionRegistered.wake(fn.newFunctions[0]);
|
|
92
|
+
});
|
|
93
|
+
await registry.open(ctx);
|
|
94
|
+
await registry.register(space, testManifest);
|
|
95
|
+
const registered = await functionRegistered.wait();
|
|
96
|
+
expect(registered.uri).to.eq(testManifest.functions![0].uri);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const createRegistry = (client: Client) => {
|
|
101
|
+
const registry = new FunctionRegistry(client);
|
|
102
|
+
ctx.onDispose(() => registry.close());
|
|
103
|
+
return registry;
|
|
104
|
+
};
|
|
105
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Event } from '@dxos/async';
|
|
6
|
+
import { type Client } from '@dxos/client';
|
|
7
|
+
import { create, Filter, type Space } from '@dxos/client/echo';
|
|
8
|
+
import { type Context, Resource } from '@dxos/context';
|
|
9
|
+
import { PublicKey } from '@dxos/keys';
|
|
10
|
+
import { ComplexMap } from '@dxos/util';
|
|
11
|
+
|
|
12
|
+
import { FunctionDef, type FunctionManifest } from '../types';
|
|
13
|
+
|
|
14
|
+
export type FunctionsRegisteredEvent = {
|
|
15
|
+
space: Space;
|
|
16
|
+
newFunctions: FunctionDef[];
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export class FunctionRegistry extends Resource {
|
|
20
|
+
private readonly _functionBySpaceKey = new ComplexMap<PublicKey, FunctionDef[]>(PublicKey.hash);
|
|
21
|
+
|
|
22
|
+
public readonly onFunctionsRegistered = new Event<FunctionsRegisteredEvent>();
|
|
23
|
+
|
|
24
|
+
constructor(private readonly _client: Client) {
|
|
25
|
+
super();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public getFunctions(space: Space): FunctionDef[] {
|
|
29
|
+
return this._functionBySpaceKey.get(space.key) ?? [];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The method loads function definitions from the manifest into the space.
|
|
34
|
+
* We first load all the definitions from the space to deduplicate by functionId.
|
|
35
|
+
*/
|
|
36
|
+
// TODO(burdon): This should not be space specific (they are static for the agent).
|
|
37
|
+
public async register(space: Space, manifest: FunctionManifest): Promise<void> {
|
|
38
|
+
if (!manifest.functions?.length) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!space.db.graph.runtimeSchemaRegistry.hasSchema(FunctionDef)) {
|
|
42
|
+
space.db.graph.runtimeSchemaRegistry.registerSchema(FunctionDef);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const { objects: existingDefinitions } = await space.db.query(Filter.schema(FunctionDef)).run();
|
|
46
|
+
const newDefinitions = getNewDefinitions(manifest.functions, existingDefinitions);
|
|
47
|
+
const reactiveObjects = newDefinitions.map((template) => create(FunctionDef, { ...template }));
|
|
48
|
+
reactiveObjects.forEach((obj) => space.db.add(obj));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
protected override async _open(): Promise<void> {
|
|
52
|
+
const spaceListSubscription = this._client.spaces.subscribe(async (spaces) => {
|
|
53
|
+
for (const space of spaces) {
|
|
54
|
+
if (this._functionBySpaceKey.has(space.key)) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const registered: FunctionDef[] = [];
|
|
58
|
+
this._functionBySpaceKey.set(space.key, registered);
|
|
59
|
+
await space.waitUntilReady();
|
|
60
|
+
if (this._ctx.disposed) {
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const functionsSubscription = space.db.query(Filter.schema(FunctionDef)).subscribe((definitions) => {
|
|
65
|
+
const newFunctions = getNewDefinitions(definitions.objects, registered);
|
|
66
|
+
if (newFunctions.length > 0) {
|
|
67
|
+
registered.push(...newFunctions);
|
|
68
|
+
this.onFunctionsRegistered.emit({ space, newFunctions });
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
this._ctx.onDispose(functionsSubscription);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
this._ctx.onDispose(() => spaceListSubscription.unsubscribe());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
protected override async _close(_: Context): Promise<void> {
|
|
78
|
+
this._functionBySpaceKey.clear();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const getNewDefinitions = <T extends { uri: string }>(candidateList: T[], existing: FunctionDef[]): T[] => {
|
|
83
|
+
return candidateList.filter((candidate) => existing.find((def) => def.uri === candidate.uri) == null);
|
|
84
|
+
};
|
|
@@ -5,12 +5,14 @@
|
|
|
5
5
|
import { expect } from 'chai';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import { Client
|
|
8
|
+
import { waitForCondition } from '@dxos/async';
|
|
9
|
+
import { type Client } from '@dxos/client';
|
|
10
10
|
import { TestBuilder } from '@dxos/client/testing';
|
|
11
|
-
import { describe,
|
|
11
|
+
import { describe, test } from '@dxos/test';
|
|
12
12
|
|
|
13
13
|
import { DevServer } from './dev-server';
|
|
14
|
+
import { FunctionRegistry } from '../registry';
|
|
15
|
+
import { createFunctionRuntime } from '../testing';
|
|
14
16
|
import { type FunctionManifest } from '../types';
|
|
15
17
|
|
|
16
18
|
describe('dev server', () => {
|
|
@@ -18,35 +20,10 @@ describe('dev server', () => {
|
|
|
18
20
|
let testBuilder: TestBuilder;
|
|
19
21
|
before(async () => {
|
|
20
22
|
testBuilder = new TestBuilder();
|
|
21
|
-
|
|
22
|
-
runtime: {
|
|
23
|
-
agent: {
|
|
24
|
-
plugins: [
|
|
25
|
-
{
|
|
26
|
-
id: 'dxos.org/agent/plugin/functions',
|
|
27
|
-
config: {
|
|
28
|
-
port: 8080,
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
],
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
const services = testBuilder.createLocalClientServices();
|
|
37
|
-
client = new Client({ config, services });
|
|
38
|
-
|
|
39
|
-
await client.initialize();
|
|
40
|
-
await client.halo.createIdentity();
|
|
41
|
-
testBuilder.ctx.onDispose(() => client.destroy());
|
|
42
|
-
|
|
43
|
-
// TODO(burdon): Better way to configure plugin? (Rationalize chess.test).
|
|
44
|
-
const functionsPlugin = new FunctionsPlugin();
|
|
45
|
-
await functionsPlugin.initialize({ client, clientServices: services });
|
|
46
|
-
await openAndClose(functionsPlugin);
|
|
47
|
-
|
|
23
|
+
client = await createFunctionRuntime(testBuilder);
|
|
48
24
|
expect(client.services.services.FunctionRegistryService).to.exist;
|
|
49
25
|
});
|
|
26
|
+
|
|
50
27
|
after(async () => {
|
|
51
28
|
await testBuilder.destroy();
|
|
52
29
|
});
|
|
@@ -55,18 +32,19 @@ describe('dev server', () => {
|
|
|
55
32
|
const manifest: FunctionManifest = {
|
|
56
33
|
functions: [
|
|
57
34
|
{
|
|
58
|
-
|
|
59
|
-
|
|
35
|
+
uri: 'example.com/function/test',
|
|
36
|
+
route: 'test',
|
|
60
37
|
handler: 'test',
|
|
61
38
|
},
|
|
62
39
|
],
|
|
63
40
|
};
|
|
64
41
|
|
|
65
|
-
const
|
|
66
|
-
|
|
42
|
+
const registry = new FunctionRegistry(client);
|
|
43
|
+
const server = new DevServer(client, registry, {
|
|
67
44
|
baseDir: path.join(__dirname, '../testing'),
|
|
68
45
|
});
|
|
69
|
-
await
|
|
46
|
+
const space = await client.spaces.create();
|
|
47
|
+
await registry.register(space, manifest);
|
|
70
48
|
await server.start();
|
|
71
49
|
|
|
72
50
|
// TODO(burdon): Doesn't shut down cleanly.
|
|
@@ -74,6 +52,8 @@ describe('dev server', () => {
|
|
|
74
52
|
testBuilder.ctx.onDispose(() => server.stop());
|
|
75
53
|
expect(server).to.exist;
|
|
76
54
|
|
|
55
|
+
await waitForCondition({ condition: () => server.functions.length > 0 });
|
|
56
|
+
|
|
77
57
|
await server.invoke('test', {});
|
|
78
58
|
expect(server.stats.seq).to.eq(1);
|
|
79
59
|
});
|