@dxos/functions 0.5.3-main.b41a319 → 0.5.3-main.bbd33a9
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-P3HPDHNI.mjs +86 -0
- package/dist/lib/browser/chunk-P3HPDHNI.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +722 -487
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/types.mjs +14 -0
- package/dist/lib/browser/types.mjs.map +7 -0
- package/dist/lib/node/chunk-KTLM3JNV.cjs +103 -0
- package/dist/lib/node/chunk-KTLM3JNV.cjs.map +7 -0
- package/dist/lib/node/index.cjs +709 -480
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/types.cjs +35 -0
- package/dist/lib/node/types.cjs.map +7 -0
- package/dist/types/src/browser/index.d.ts +2 -0
- package/dist/types/src/browser/index.d.ts.map +1 -0
- package/dist/types/src/function/function-registry.d.ts +24 -0
- package/dist/types/src/function/function-registry.d.ts.map +1 -0
- package/dist/types/src/function/function-registry.test.d.ts +2 -0
- package/dist/types/src/function/function-registry.test.d.ts.map +1 -0
- package/dist/types/src/function/index.d.ts +2 -0
- package/dist/types/src/function/index.d.ts.map +1 -0
- package/dist/types/src/handler.d.ts.map +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/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 +11 -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 +143 -122
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +30 -14
- package/schema/functions.json +139 -116
- package/src/browser/index.ts +5 -0
- package/src/function/function-registry.test.ts +105 -0
- package/src/function/function-registry.ts +90 -0
- package/src/function/index.ts +5 -0
- package/src/index.ts +2 -0
- package/src/runtime/dev-server.test.ts +15 -35
- package/src/runtime/dev-server.ts +37 -20
- package/src/runtime/scheduler.test.ts +54 -75
- package/src/runtime/scheduler.ts +70 -297
- 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 +255 -0
- package/src/trigger/trigger-registry.ts +201 -0
- package/src/trigger/type/index.ts +8 -0
- package/src/trigger/type/subscription-trigger.ts +80 -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 +78 -53
|
@@ -9,14 +9,15 @@ import { join } from 'node:path';
|
|
|
9
9
|
|
|
10
10
|
import { Event, Trigger } from '@dxos/async';
|
|
11
11
|
import { type Client } from '@dxos/client';
|
|
12
|
+
import { Context } from '@dxos/context';
|
|
12
13
|
import { invariant } from '@dxos/invariant';
|
|
13
14
|
import { log } from '@dxos/log';
|
|
14
15
|
|
|
16
|
+
import { type FunctionRegistry } from '../function';
|
|
15
17
|
import { type FunctionContext, type FunctionEvent, type FunctionHandler, type FunctionResponse } from '../handler';
|
|
16
|
-
import { type FunctionDef
|
|
18
|
+
import { type FunctionDef } from '../types';
|
|
17
19
|
|
|
18
20
|
export type DevServerOptions = {
|
|
19
|
-
manifest: FunctionManifest;
|
|
20
21
|
baseDir: string;
|
|
21
22
|
port?: number;
|
|
22
23
|
reload?: boolean;
|
|
@@ -27,6 +28,8 @@ export type DevServerOptions = {
|
|
|
27
28
|
* Functions dev server provides a local HTTP server for testing functions.
|
|
28
29
|
*/
|
|
29
30
|
export class DevServer {
|
|
31
|
+
private _ctx = createContext();
|
|
32
|
+
|
|
30
33
|
// Function handlers indexed by name (URL path).
|
|
31
34
|
private readonly _handlers: Record<string, { def: FunctionDef; handler: FunctionHandler<any> }> = {};
|
|
32
35
|
|
|
@@ -38,11 +41,17 @@ export class DevServer {
|
|
|
38
41
|
|
|
39
42
|
public readonly update = new Event<number>();
|
|
40
43
|
|
|
41
|
-
// prettier-ignore
|
|
42
44
|
constructor(
|
|
43
45
|
private readonly _client: Client,
|
|
46
|
+
private readonly _functionsRegistry: FunctionRegistry,
|
|
44
47
|
private readonly _options: DevServerOptions,
|
|
45
|
-
) {
|
|
48
|
+
) {
|
|
49
|
+
this._functionsRegistry.registered.on(async ({ added }) => {
|
|
50
|
+
added.forEach((def) => this._load(def));
|
|
51
|
+
await this._safeUpdateRegistration();
|
|
52
|
+
log('new functions loaded', { added });
|
|
53
|
+
});
|
|
54
|
+
}
|
|
46
55
|
|
|
47
56
|
get stats() {
|
|
48
57
|
return {
|
|
@@ -63,19 +72,10 @@ export class DevServer {
|
|
|
63
72
|
return Object.values(this._handlers);
|
|
64
73
|
}
|
|
65
74
|
|
|
66
|
-
async initialize() {
|
|
67
|
-
for (const def of this._options.manifest.functions) {
|
|
68
|
-
try {
|
|
69
|
-
await this._load(def);
|
|
70
|
-
} catch (err) {
|
|
71
|
-
log.error('parsing function (check manifest)', err);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
75
|
async start() {
|
|
77
76
|
invariant(!this._server);
|
|
78
77
|
log.info('starting...');
|
|
78
|
+
this._ctx = createContext();
|
|
79
79
|
|
|
80
80
|
// TODO(burdon): Move to hono.
|
|
81
81
|
const app = express();
|
|
@@ -107,12 +107,14 @@ export class DevServer {
|
|
|
107
107
|
// Register functions.
|
|
108
108
|
const { registrationId, endpoint } = await this._client.services.services.FunctionRegistryService!.register({
|
|
109
109
|
endpoint: this.endpoint,
|
|
110
|
-
functions: this.functions.map(({ def: { id, path } }) => ({ id, path })),
|
|
111
110
|
});
|
|
112
111
|
|
|
113
112
|
log.info('registered', { endpoint });
|
|
114
113
|
this._proxy = endpoint;
|
|
115
114
|
this._functionServiceRegistration = registrationId;
|
|
115
|
+
|
|
116
|
+
// Open after registration, so that it can be updated with the list of function definitions.
|
|
117
|
+
await this._functionsRegistry.open(this._ctx);
|
|
116
118
|
} catch (err: any) {
|
|
117
119
|
await this.stop();
|
|
118
120
|
throw new Error('FunctionRegistryService not available (check plugin is configured).');
|
|
@@ -155,10 +157,10 @@ export class DevServer {
|
|
|
155
157
|
/**
|
|
156
158
|
* Load function.
|
|
157
159
|
*/
|
|
158
|
-
private async _load(def: FunctionDef, force
|
|
159
|
-
const {
|
|
160
|
+
private async _load(def: FunctionDef, force?: boolean | undefined) {
|
|
161
|
+
const { uri, route, handler } = def;
|
|
160
162
|
const filePath = join(this._options.baseDir, handler);
|
|
161
|
-
log.info('loading', {
|
|
163
|
+
log.info('loading', { uri, force });
|
|
162
164
|
|
|
163
165
|
// Remove from cache.
|
|
164
166
|
if (force) {
|
|
@@ -169,13 +171,26 @@ export class DevServer {
|
|
|
169
171
|
});
|
|
170
172
|
}
|
|
171
173
|
|
|
174
|
+
// TODO(burdon): Import types.
|
|
172
175
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
173
176
|
const module = require(filePath);
|
|
174
177
|
if (typeof module.default !== 'function') {
|
|
175
|
-
throw new Error(`Handler must export default function: ${
|
|
178
|
+
throw new Error(`Handler must export default function: ${uri}`);
|
|
176
179
|
}
|
|
177
180
|
|
|
178
|
-
this._handlers[
|
|
181
|
+
this._handlers[route] = { def, handler: module.default };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private async _safeUpdateRegistration(): Promise<void> {
|
|
185
|
+
invariant(this._functionServiceRegistration);
|
|
186
|
+
try {
|
|
187
|
+
await this._client.services.services.FunctionRegistryService!.updateRegistration({
|
|
188
|
+
registrationId: this._functionServiceRegistration,
|
|
189
|
+
functions: this.functions.map(({ def: { id, route } }) => ({ id, route })),
|
|
190
|
+
});
|
|
191
|
+
} catch (e) {
|
|
192
|
+
log.catch(e);
|
|
193
|
+
}
|
|
179
194
|
}
|
|
180
195
|
|
|
181
196
|
/**
|
|
@@ -214,3 +229,5 @@ export class DevServer {
|
|
|
214
229
|
return statusCode;
|
|
215
230
|
}
|
|
216
231
|
}
|
|
232
|
+
|
|
233
|
+
const createContext = () => new Context({ name: 'DevServer' });
|
|
@@ -6,40 +6,43 @@ import { expect } from 'chai';
|
|
|
6
6
|
import WebSocket from 'ws';
|
|
7
7
|
|
|
8
8
|
import { Trigger } from '@dxos/async';
|
|
9
|
-
import { Client } from '@dxos/client';
|
|
9
|
+
import { type Client } from '@dxos/client';
|
|
10
10
|
import { TestBuilder } from '@dxos/client/testing';
|
|
11
|
-
import { create
|
|
11
|
+
import { create } from '@dxos/echo-schema';
|
|
12
12
|
import { describe, test } from '@dxos/test';
|
|
13
13
|
|
|
14
|
-
import { Scheduler } from './scheduler';
|
|
15
|
-
import {
|
|
14
|
+
import { Scheduler, type SchedulerOptions } from './scheduler';
|
|
15
|
+
import { FunctionRegistry } from '../function';
|
|
16
|
+
import { createInitializedClients, TestType, triggerWebhook } from '../testing';
|
|
17
|
+
import { TriggerRegistry } from '../trigger';
|
|
18
|
+
import { type FunctionManifest } from '../types';
|
|
16
19
|
|
|
17
20
|
// TODO(burdon): Test we can add and remove triggers.
|
|
18
21
|
describe('scheduler', () => {
|
|
22
|
+
let testBuilder: TestBuilder;
|
|
19
23
|
let client: Client;
|
|
20
24
|
before(async () => {
|
|
21
|
-
|
|
22
|
-
client =
|
|
23
|
-
await client.initialize();
|
|
24
|
-
await client.halo.createIdentity();
|
|
25
|
+
testBuilder = new TestBuilder();
|
|
26
|
+
client = (await createInitializedClients(testBuilder, 1))[0];
|
|
25
27
|
});
|
|
26
28
|
after(async () => {
|
|
27
|
-
await
|
|
29
|
+
await testBuilder.destroy();
|
|
28
30
|
});
|
|
29
31
|
|
|
30
32
|
test('timer', async () => {
|
|
31
33
|
const manifest: FunctionManifest = {
|
|
32
34
|
functions: [
|
|
33
35
|
{
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
uri: 'example.com/function/test',
|
|
37
|
+
route: '/test',
|
|
36
38
|
handler: 'test',
|
|
37
39
|
},
|
|
38
40
|
],
|
|
39
41
|
triggers: [
|
|
40
42
|
{
|
|
41
43
|
function: 'example.com/function/test',
|
|
42
|
-
|
|
44
|
+
spec: {
|
|
45
|
+
type: 'timer',
|
|
43
46
|
cron: '0/1 * * * * *', // Every 1s.
|
|
44
47
|
},
|
|
45
48
|
},
|
|
@@ -48,18 +51,13 @@ describe('scheduler', () => {
|
|
|
48
51
|
|
|
49
52
|
let count = 0;
|
|
50
53
|
const done = new Trigger();
|
|
51
|
-
const scheduler =
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
},
|
|
54
|
+
const scheduler = createScheduler(async () => {
|
|
55
|
+
if (++count === 3) {
|
|
56
|
+
done.wake();
|
|
57
|
+
}
|
|
57
58
|
});
|
|
58
|
-
|
|
59
|
+
await scheduler.register(client.spaces.default, manifest);
|
|
59
60
|
await scheduler.start();
|
|
60
|
-
after(async () => {
|
|
61
|
-
await scheduler.stop();
|
|
62
|
-
});
|
|
63
61
|
|
|
64
62
|
await done.wait({ timeout: 5_000 });
|
|
65
63
|
expect(count).to.equal(3);
|
|
@@ -69,15 +67,16 @@ describe('scheduler', () => {
|
|
|
69
67
|
const manifest: FunctionManifest = {
|
|
70
68
|
functions: [
|
|
71
69
|
{
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
uri: 'example.com/function/test',
|
|
71
|
+
route: '/test',
|
|
74
72
|
handler: 'test',
|
|
75
73
|
},
|
|
76
74
|
],
|
|
77
75
|
triggers: [
|
|
78
76
|
{
|
|
79
77
|
function: 'example.com/function/test',
|
|
80
|
-
|
|
78
|
+
spec: {
|
|
79
|
+
type: 'webhook',
|
|
81
80
|
method: 'GET',
|
|
82
81
|
},
|
|
83
82
|
},
|
|
@@ -85,23 +84,14 @@ describe('scheduler', () => {
|
|
|
85
84
|
};
|
|
86
85
|
|
|
87
86
|
const done = new Trigger();
|
|
88
|
-
const scheduler =
|
|
89
|
-
|
|
90
|
-
done.wake();
|
|
91
|
-
},
|
|
87
|
+
const scheduler = createScheduler(async () => {
|
|
88
|
+
done.wake();
|
|
92
89
|
});
|
|
93
|
-
|
|
90
|
+
const space = await client.spaces.create();
|
|
91
|
+
await scheduler.register(space, manifest);
|
|
94
92
|
await scheduler.start();
|
|
95
|
-
after(async () => {
|
|
96
|
-
await scheduler.stop();
|
|
97
|
-
});
|
|
98
93
|
|
|
99
|
-
setTimeout(() =>
|
|
100
|
-
const mount: WebhookTrigger = scheduler.mounts.find(
|
|
101
|
-
(mount) => mount.function === 'example.com/function/test',
|
|
102
|
-
)!.webhook!;
|
|
103
|
-
void fetch(`http://localhost:${mount.port}`);
|
|
104
|
-
});
|
|
94
|
+
setTimeout(async () => triggerWebhook(space, manifest.functions![0].uri));
|
|
105
95
|
|
|
106
96
|
await done.wait();
|
|
107
97
|
});
|
|
@@ -110,15 +100,16 @@ describe('scheduler', () => {
|
|
|
110
100
|
const manifest: FunctionManifest = {
|
|
111
101
|
functions: [
|
|
112
102
|
{
|
|
113
|
-
|
|
114
|
-
|
|
103
|
+
uri: 'example.com/function/test',
|
|
104
|
+
route: '/test',
|
|
115
105
|
handler: 'test',
|
|
116
106
|
},
|
|
117
107
|
],
|
|
118
108
|
triggers: [
|
|
119
109
|
{
|
|
120
110
|
function: 'example.com/function/test',
|
|
121
|
-
|
|
111
|
+
spec: {
|
|
112
|
+
type: 'websocket',
|
|
122
113
|
// url: 'https://hub.dxos.network/api/mailbox/test',
|
|
123
114
|
url: 'http://localhost:8081',
|
|
124
115
|
init: {
|
|
@@ -130,16 +121,11 @@ describe('scheduler', () => {
|
|
|
130
121
|
};
|
|
131
122
|
|
|
132
123
|
const done = new Trigger();
|
|
133
|
-
const scheduler =
|
|
134
|
-
|
|
135
|
-
done.wake();
|
|
136
|
-
},
|
|
124
|
+
const scheduler = createScheduler(async () => {
|
|
125
|
+
done.wake();
|
|
137
126
|
});
|
|
138
|
-
|
|
127
|
+
await scheduler.register(client.spaces.default, manifest);
|
|
139
128
|
await scheduler.start();
|
|
140
|
-
after(async () => {
|
|
141
|
-
await scheduler.stop();
|
|
142
|
-
});
|
|
143
129
|
|
|
144
130
|
// Test server.
|
|
145
131
|
setTimeout(() => {
|
|
@@ -157,29 +143,20 @@ describe('scheduler', () => {
|
|
|
157
143
|
});
|
|
158
144
|
|
|
159
145
|
test('subscription', async () => {
|
|
160
|
-
class TestType extends TypedObject({ typename: 'example.com/type/Test', version: '0.1.0' })({
|
|
161
|
-
title: S.string,
|
|
162
|
-
}) {}
|
|
163
|
-
client.addSchema(TestType);
|
|
164
|
-
|
|
165
146
|
const manifest: FunctionManifest = {
|
|
166
147
|
functions: [
|
|
167
148
|
{
|
|
168
|
-
|
|
169
|
-
|
|
149
|
+
uri: 'example.com/function/test',
|
|
150
|
+
route: '/test',
|
|
170
151
|
handler: 'test',
|
|
171
152
|
},
|
|
172
153
|
],
|
|
173
154
|
triggers: [
|
|
174
155
|
{
|
|
175
156
|
function: 'example.com/function/test',
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
filter: [
|
|
179
|
-
{
|
|
180
|
-
type: TestType.typename,
|
|
181
|
-
},
|
|
182
|
-
],
|
|
157
|
+
spec: {
|
|
158
|
+
type: 'subscription',
|
|
159
|
+
filter: [{ type: TestType.typename }],
|
|
183
160
|
},
|
|
184
161
|
},
|
|
185
162
|
],
|
|
@@ -187,20 +164,14 @@ describe('scheduler', () => {
|
|
|
187
164
|
|
|
188
165
|
let count = 0;
|
|
189
166
|
const done = new Trigger();
|
|
190
|
-
const scheduler =
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
},
|
|
167
|
+
const scheduler = createScheduler(async () => {
|
|
168
|
+
if (++count === 2) {
|
|
169
|
+
done.wake();
|
|
170
|
+
}
|
|
196
171
|
});
|
|
197
|
-
|
|
172
|
+
await scheduler.register(client.spaces.default, manifest);
|
|
198
173
|
await scheduler.start();
|
|
199
|
-
after(async () => {
|
|
200
|
-
await scheduler.stop();
|
|
201
|
-
});
|
|
202
174
|
|
|
203
|
-
// TODO(burdon): Query for Expando?
|
|
204
175
|
setTimeout(() => {
|
|
205
176
|
const space = client.spaces.default;
|
|
206
177
|
const object = create(TestType, { title: 'Hello world!' });
|
|
@@ -209,4 +180,12 @@ describe('scheduler', () => {
|
|
|
209
180
|
|
|
210
181
|
await done.wait();
|
|
211
182
|
});
|
|
183
|
+
|
|
184
|
+
const createScheduler = (callback: SchedulerOptions['callback']) => {
|
|
185
|
+
const scheduler = new Scheduler(new FunctionRegistry(client), new TriggerRegistry(client), { callback });
|
|
186
|
+
after(async () => {
|
|
187
|
+
await scheduler.stop();
|
|
188
|
+
});
|
|
189
|
+
return scheduler;
|
|
190
|
+
};
|
|
212
191
|
});
|