@dxos/functions 0.5.3-main.d7fe7b5 → 0.5.3-main.e76d664
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 +429 -802
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +426 -787
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/handler.d.ts +12 -33
- package/dist/types/src/handler.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +0 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/runtime/dev-server.d.ts +13 -16
- package/dist/types/src/runtime/dev-server.d.ts.map +1 -1
- package/dist/types/src/runtime/scheduler.d.ts +27 -12
- package/dist/types/src/runtime/scheduler.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +101 -129
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +13 -18
- package/schema/functions.json +101 -128
- package/src/handler.ts +31 -54
- package/src/index.ts +0 -2
- package/src/runtime/dev-server.ts +52 -104
- package/src/runtime/scheduler.test.ts +73 -56
- package/src/runtime/scheduler.ts +271 -79
- package/src/types.ts +32 -57
- package/dist/types/src/registry/function-registry.d.ts +0 -24
- package/dist/types/src/registry/function-registry.d.ts.map +0 -1
- package/dist/types/src/registry/function-registry.test.d.ts +0 -2
- package/dist/types/src/registry/function-registry.test.d.ts.map +0 -1
- package/dist/types/src/registry/index.d.ts +0 -2
- package/dist/types/src/registry/index.d.ts.map +0 -1
- package/dist/types/src/runtime/dev-server.test.d.ts +0 -2
- package/dist/types/src/runtime/dev-server.test.d.ts.map +0 -1
- package/dist/types/src/testing/functions-integration.test.d.ts +0 -2
- package/dist/types/src/testing/functions-integration.test.d.ts.map +0 -1
- package/dist/types/src/testing/index.d.ts +0 -4
- package/dist/types/src/testing/index.d.ts.map +0 -1
- package/dist/types/src/testing/setup.d.ts +0 -5
- package/dist/types/src/testing/setup.d.ts.map +0 -1
- package/dist/types/src/testing/test/handler.d.ts +0 -4
- package/dist/types/src/testing/test/handler.d.ts.map +0 -1
- package/dist/types/src/testing/test/index.d.ts +0 -3
- package/dist/types/src/testing/test/index.d.ts.map +0 -1
- package/dist/types/src/testing/types.d.ts +0 -9
- package/dist/types/src/testing/types.d.ts.map +0 -1
- package/dist/types/src/testing/util.d.ts +0 -3
- package/dist/types/src/testing/util.d.ts.map +0 -1
- package/dist/types/src/trigger/index.d.ts +0 -2
- package/dist/types/src/trigger/index.d.ts.map +0 -1
- package/dist/types/src/trigger/trigger-registry.d.ts +0 -40
- package/dist/types/src/trigger/trigger-registry.d.ts.map +0 -1
- package/dist/types/src/trigger/trigger-registry.test.d.ts +0 -2
- package/dist/types/src/trigger/trigger-registry.test.d.ts.map +0 -1
- package/dist/types/src/trigger/type/index.d.ts +0 -5
- package/dist/types/src/trigger/type/index.d.ts.map +0 -1
- package/dist/types/src/trigger/type/subscription-trigger.d.ts +0 -4
- package/dist/types/src/trigger/type/subscription-trigger.d.ts.map +0 -1
- package/dist/types/src/trigger/type/timer-trigger.d.ts +0 -4
- package/dist/types/src/trigger/type/timer-trigger.d.ts.map +0 -1
- package/dist/types/src/trigger/type/webhook-trigger.d.ts +0 -4
- package/dist/types/src/trigger/type/webhook-trigger.d.ts.map +0 -1
- package/dist/types/src/trigger/type/websocket-trigger.d.ts +0 -13
- package/dist/types/src/trigger/type/websocket-trigger.d.ts.map +0 -1
- package/src/registry/function-registry.test.ts +0 -105
- package/src/registry/function-registry.ts +0 -84
- package/src/registry/index.ts +0 -5
- package/src/runtime/dev-server.test.ts +0 -60
- package/src/testing/functions-integration.test.ts +0 -99
- package/src/testing/index.ts +0 -7
- package/src/testing/setup.ts +0 -45
- package/src/testing/test/handler.ts +0 -15
- package/src/testing/test/index.ts +0 -7
- package/src/testing/types.ts +0 -9
- package/src/testing/util.ts +0 -16
- package/src/trigger/index.ts +0 -5
- package/src/trigger/trigger-registry.test.ts +0 -229
- package/src/trigger/trigger-registry.ts +0 -176
- package/src/trigger/type/index.ts +0 -8
- package/src/trigger/type/subscription-trigger.ts +0 -73
- package/src/trigger/type/timer-trigger.ts +0 -44
- package/src/trigger/type/webhook-trigger.ts +0 -47
- package/src/trigger/type/websocket-trigger.ts +0 -91
package/src/runtime/scheduler.ts
CHANGED
|
@@ -2,18 +2,31 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import { CronJob } from 'cron';
|
|
6
|
+
import http from 'node:http';
|
|
7
|
+
import WebSocket from 'ws';
|
|
6
8
|
|
|
7
|
-
import {
|
|
9
|
+
import { TextV0Type } from '@braneframe/types';
|
|
10
|
+
import { debounce, DeferredTask, sleep, Trigger } from '@dxos/async';
|
|
11
|
+
import { type Client, type PublicKey } from '@dxos/client';
|
|
12
|
+
import { createSubscription, Filter, getAutomergeObjectCore, type Query, type Space } from '@dxos/client/echo';
|
|
8
13
|
import { Context } from '@dxos/context';
|
|
14
|
+
import { invariant } from '@dxos/invariant';
|
|
9
15
|
import { log } from '@dxos/log';
|
|
16
|
+
import { ComplexMap } from '@dxos/util';
|
|
10
17
|
|
|
11
|
-
import { type
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
import { type FunctionSubscriptionEvent } from '../handler';
|
|
19
|
+
import {
|
|
20
|
+
type FunctionDef,
|
|
21
|
+
type FunctionManifest,
|
|
22
|
+
type FunctionTrigger,
|
|
23
|
+
type SubscriptionTrigger,
|
|
24
|
+
type TimerTrigger,
|
|
25
|
+
type WebhookTrigger,
|
|
26
|
+
type WebsocketTrigger,
|
|
27
|
+
} from '../types';
|
|
15
28
|
|
|
16
|
-
|
|
29
|
+
type Callback = (data: FunctionSubscriptionEvent) => Promise<void>;
|
|
17
30
|
|
|
18
31
|
export type SchedulerOptions = {
|
|
19
32
|
endpoint?: string;
|
|
@@ -21,111 +34,290 @@ export type SchedulerOptions = {
|
|
|
21
34
|
};
|
|
22
35
|
|
|
23
36
|
/**
|
|
24
|
-
* The scheduler triggers function
|
|
37
|
+
* The scheduler triggers function exectuion based on various triggers.
|
|
25
38
|
*/
|
|
26
39
|
export class Scheduler {
|
|
27
|
-
|
|
40
|
+
// Map of mounted functions.
|
|
41
|
+
private readonly _mounts = new ComplexMap<
|
|
42
|
+
{ id: string; spaceKey: PublicKey },
|
|
43
|
+
{ ctx: Context; trigger: FunctionTrigger }
|
|
44
|
+
>(({ id, spaceKey }) => `${spaceKey.toHex()}:${id}`);
|
|
28
45
|
|
|
29
46
|
constructor(
|
|
30
|
-
|
|
31
|
-
|
|
47
|
+
private readonly _client: Client,
|
|
48
|
+
private readonly _manifest: FunctionManifest,
|
|
32
49
|
private readonly _options: SchedulerOptions = {},
|
|
33
|
-
) {
|
|
34
|
-
this.functions.onFunctionsRegistered.on(async ({ space, newFunctions }) => {
|
|
35
|
-
await this._safeActivateTriggers(space, this.triggers.getInactiveTriggers(space), newFunctions);
|
|
36
|
-
});
|
|
37
|
-
this.triggers.registered.on(async ({ space, triggers }) => {
|
|
38
|
-
await this._safeActivateTriggers(space, triggers, this.functions.getFunctions(space));
|
|
39
|
-
});
|
|
40
|
-
}
|
|
50
|
+
) {}
|
|
41
51
|
|
|
42
52
|
async start() {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
this._client.spaces.subscribe(async (spaces) => {
|
|
54
|
+
for (const space of spaces) {
|
|
55
|
+
await space.waitUntilReady();
|
|
56
|
+
for (const trigger of this._manifest.triggers ?? []) {
|
|
57
|
+
await this.mount(new Context(), space, trigger);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
47
61
|
}
|
|
48
62
|
|
|
49
63
|
async stop() {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
64
|
+
for (const { id, spaceKey } of this._mounts.keys()) {
|
|
65
|
+
await this.unmount(id, spaceKey);
|
|
66
|
+
}
|
|
53
67
|
}
|
|
54
68
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
69
|
+
private async mount(ctx: Context, space: Space, trigger: FunctionTrigger) {
|
|
70
|
+
const key = { id: trigger.function, spaceKey: space.key };
|
|
71
|
+
const def = this._manifest.functions.find((config) => config.id === trigger.function);
|
|
72
|
+
invariant(def, `Function not found: ${trigger.function}`);
|
|
59
73
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
74
|
+
// TODO(burdon): Currently supports only one trigger declaration per function.
|
|
75
|
+
const exists = this._mounts.get(key);
|
|
76
|
+
if (!exists) {
|
|
77
|
+
this._mounts.set(key, { ctx, trigger });
|
|
78
|
+
log('mount', { space: space.key, trigger });
|
|
79
|
+
if (ctx.disposed) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
//
|
|
84
|
+
// Triggers types.
|
|
85
|
+
//
|
|
86
|
+
|
|
87
|
+
if (trigger.timer) {
|
|
88
|
+
await this._createTimer(ctx, space, def, trigger.timer);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (trigger.webhook) {
|
|
92
|
+
await this._createWebhook(ctx, space, def, trigger.webhook);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (trigger.websocket) {
|
|
96
|
+
await this._createWebsocket(ctx, space, def, trigger.websocket);
|
|
97
|
+
}
|
|
70
98
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
log.info('function is not found for trigger', { fnTrigger });
|
|
75
|
-
return;
|
|
99
|
+
if (trigger.subscription) {
|
|
100
|
+
await this._createSubscription(ctx, space, def, trigger.subscription);
|
|
101
|
+
}
|
|
76
102
|
}
|
|
103
|
+
}
|
|
77
104
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
105
|
+
private async unmount(id: string, spaceKey: PublicKey) {
|
|
106
|
+
const key = { id, spaceKey };
|
|
107
|
+
const { ctx } = this._mounts.get(key) ?? {};
|
|
108
|
+
if (ctx) {
|
|
109
|
+
this._mounts.delete(key);
|
|
110
|
+
await ctx.dispose();
|
|
111
|
+
}
|
|
85
112
|
}
|
|
86
113
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
{ data, meta }: { data: TData; meta?: TMeta },
|
|
90
|
-
): Promise<number> {
|
|
91
|
-
let status = 0;
|
|
114
|
+
// TODO(burdon): Pass in Space key (common context).
|
|
115
|
+
private async _execFunction(def: FunctionDef, data: any) {
|
|
92
116
|
try {
|
|
93
|
-
|
|
94
|
-
const payload = Object.assign({}, meta && ({ meta } satisfies FunctionEventMeta<TMeta>), data);
|
|
95
|
-
|
|
117
|
+
log.info('exec', { function: def.id });
|
|
96
118
|
const { endpoint, callback } = this._options;
|
|
97
119
|
if (endpoint) {
|
|
98
120
|
// TODO(burdon): Move out of scheduler (generalize as callback).
|
|
99
|
-
|
|
100
|
-
log.info('exec', { function: def.uri, url });
|
|
101
|
-
const response = await fetch(url, {
|
|
121
|
+
await fetch(`${this._options.endpoint}/${def.name}`, {
|
|
102
122
|
method: 'POST',
|
|
103
123
|
headers: {
|
|
104
124
|
'Content-Type': 'application/json',
|
|
105
125
|
},
|
|
106
|
-
body: JSON.stringify(
|
|
126
|
+
body: JSON.stringify(data),
|
|
107
127
|
});
|
|
108
|
-
|
|
109
|
-
status = response.status;
|
|
110
128
|
} else if (callback) {
|
|
111
|
-
|
|
112
|
-
status = (await callback(payload)) ?? 200;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Check errors.
|
|
116
|
-
if (status && status >= 400) {
|
|
117
|
-
throw new Error(`Response: ${status}`);
|
|
129
|
+
await callback(data);
|
|
118
130
|
}
|
|
119
131
|
|
|
120
132
|
// const result = await response.json();
|
|
121
|
-
log.info('done', { function: def.
|
|
133
|
+
log.info('done', { function: def.id });
|
|
122
134
|
} catch (err: any) {
|
|
123
|
-
log.error('error', { function: def.
|
|
124
|
-
status = 500;
|
|
135
|
+
log.error('error', { function: def.id, error: err.message });
|
|
125
136
|
}
|
|
137
|
+
}
|
|
126
138
|
|
|
127
|
-
|
|
139
|
+
//
|
|
140
|
+
// Triggers
|
|
141
|
+
//
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Cron timer.
|
|
145
|
+
*/
|
|
146
|
+
private async _createTimer(ctx: Context, space: Space, def: FunctionDef, trigger: TimerTrigger) {
|
|
147
|
+
log.info('timer', { space: space.key, trigger });
|
|
148
|
+
const { cron } = trigger;
|
|
149
|
+
|
|
150
|
+
const task = new DeferredTask(ctx, async () => {
|
|
151
|
+
await this._execFunction(def, { space: space.key });
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
let last = 0;
|
|
155
|
+
let run = 0;
|
|
156
|
+
// https://www.npmjs.com/package/cron#constructor
|
|
157
|
+
const job = CronJob.from({
|
|
158
|
+
cronTime: cron,
|
|
159
|
+
runOnInit: false,
|
|
160
|
+
onTick: () => {
|
|
161
|
+
// TODO(burdon): Check greater than 30s (use cron-parser).
|
|
162
|
+
const now = Date.now();
|
|
163
|
+
const delta = last ? now - last : 0;
|
|
164
|
+
last = now;
|
|
165
|
+
|
|
166
|
+
run++;
|
|
167
|
+
log.info('tick', { space: space.key.truncate(), count: run, delta });
|
|
168
|
+
task.schedule();
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
job.start();
|
|
173
|
+
ctx.onDispose(() => job.stop());
|
|
128
174
|
}
|
|
129
|
-
}
|
|
130
175
|
|
|
131
|
-
|
|
176
|
+
/**
|
|
177
|
+
* Webhook.
|
|
178
|
+
*/
|
|
179
|
+
private async _createWebhook(ctx: Context, space: Space, def: FunctionDef, trigger: WebhookTrigger) {
|
|
180
|
+
log.info('webhook', { space: space.key, trigger });
|
|
181
|
+
const { port } = trigger;
|
|
182
|
+
|
|
183
|
+
// TODO(burdon): POST JSON.
|
|
184
|
+
const server = http.createServer(async (req, res) => {
|
|
185
|
+
await this._execFunction(def, { space: space.key });
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
server.listen(port, () => {
|
|
189
|
+
log.info('started webhook', { port });
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
ctx.onDispose(() => {
|
|
193
|
+
server.close();
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Websocket.
|
|
199
|
+
*/
|
|
200
|
+
private async _createWebsocket(
|
|
201
|
+
ctx: Context,
|
|
202
|
+
space: Space,
|
|
203
|
+
def: FunctionDef,
|
|
204
|
+
trigger: WebsocketTrigger,
|
|
205
|
+
options: {
|
|
206
|
+
retryDelay: number;
|
|
207
|
+
maxAttempts: number;
|
|
208
|
+
} = {
|
|
209
|
+
retryDelay: 2,
|
|
210
|
+
maxAttempts: 5,
|
|
211
|
+
},
|
|
212
|
+
) {
|
|
213
|
+
log.info('websocket', { space: space.key, trigger });
|
|
214
|
+
const { url } = trigger;
|
|
215
|
+
|
|
216
|
+
let ws: WebSocket;
|
|
217
|
+
for (let attempt = 1; attempt <= options.maxAttempts; attempt++) {
|
|
218
|
+
const open = new Trigger<boolean>();
|
|
219
|
+
|
|
220
|
+
ws = new WebSocket(url);
|
|
221
|
+
Object.assign(ws, {
|
|
222
|
+
onopen: () => {
|
|
223
|
+
log.info('opened', { url });
|
|
224
|
+
if (trigger.init) {
|
|
225
|
+
ws.send(new TextEncoder().encode(JSON.stringify(trigger.init)));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
open.wake(true);
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
onclose: () => {
|
|
232
|
+
log.info('closed', { url });
|
|
233
|
+
open.wake(false);
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
onerror: (event) => {
|
|
237
|
+
log.catch(event.error, { url });
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
onmessage: async (event) => {
|
|
241
|
+
try {
|
|
242
|
+
const data = JSON.parse(new TextDecoder().decode(event.data as Uint8Array));
|
|
243
|
+
await this._execFunction(def, { space: space.key, data });
|
|
244
|
+
} catch (err) {
|
|
245
|
+
log.catch(err, { url });
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
} satisfies Partial<WebSocket>);
|
|
249
|
+
|
|
250
|
+
const isOpen = await open.wait();
|
|
251
|
+
if (isOpen) {
|
|
252
|
+
break;
|
|
253
|
+
} else {
|
|
254
|
+
const wait = Math.pow(attempt, 2) * options.retryDelay;
|
|
255
|
+
if (attempt < options.maxAttempts) {
|
|
256
|
+
log.warn(`failed to connect; trying again in ${wait}s`, { attempt });
|
|
257
|
+
await sleep(wait * 1_000);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
ctx.onDispose(() => {
|
|
263
|
+
ws?.close();
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* ECHO subscription.
|
|
269
|
+
*/
|
|
270
|
+
private async _createSubscription(ctx: Context, space: Space, def: FunctionDef, trigger: SubscriptionTrigger) {
|
|
271
|
+
log.info('subscription', { space: space.key, trigger });
|
|
272
|
+
const objectIds = new Set<string>();
|
|
273
|
+
const task = new DeferredTask(ctx, async () => {
|
|
274
|
+
await this._execFunction(def, { space: space.key, objects: Array.from(objectIds) });
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// TODO(burdon): Don't fire initially.
|
|
278
|
+
// TODO(burdon): Subscription is called THREE times.
|
|
279
|
+
const subscriptions: (() => void)[] = [];
|
|
280
|
+
const subscription = createSubscription(({ added, updated }) => {
|
|
281
|
+
log.info('updated', { added: added.length, updated: updated.length });
|
|
282
|
+
for (const object of added) {
|
|
283
|
+
objectIds.add(object.id);
|
|
284
|
+
}
|
|
285
|
+
for (const object of updated) {
|
|
286
|
+
objectIds.add(object.id);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
task.schedule();
|
|
290
|
+
});
|
|
291
|
+
subscriptions.push(() => subscription.unsubscribe());
|
|
292
|
+
|
|
293
|
+
// TODO(burdon): Create queue. Only allow one invocation per trigger at a time?
|
|
294
|
+
// TODO(burdon): Disable trigger if keeps failing.
|
|
295
|
+
const { filter, options: { deep, delay } = {} } = trigger;
|
|
296
|
+
const update = ({ objects }: Query) => {
|
|
297
|
+
subscription.update(objects);
|
|
298
|
+
|
|
299
|
+
// TODO(burdon): Hack to monitor changes to Document's text object.
|
|
300
|
+
if (deep) {
|
|
301
|
+
log.info('update', { objects: objects.length });
|
|
302
|
+
for (const object of objects) {
|
|
303
|
+
const content = object.content;
|
|
304
|
+
if (content instanceof TextV0Type) {
|
|
305
|
+
subscriptions.push(
|
|
306
|
+
getAutomergeObjectCore(content).updates.on(debounce(() => subscription.update([object]), 1_000)),
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
// TODO(burdon): Is Filter.or implemented?
|
|
314
|
+
// TODO(burdon): [Bug]: all callbacks are fired on the first mutation.
|
|
315
|
+
// TODO(burdon): [Bug]: not updated when document is deleted (either top or hierarchically).
|
|
316
|
+
const query = space.db.query(Filter.or(filter.map(({ type, props }) => Filter.typename(type, props))));
|
|
317
|
+
subscriptions.push(query.subscribe(delay ? debounce(update, delay) : update));
|
|
318
|
+
|
|
319
|
+
ctx.onDispose(() => {
|
|
320
|
+
subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -2,21 +2,23 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as S from '@effect/schema/Schema';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const TimerTriggerSchema = S.struct({
|
|
8
|
+
cron: S.string,
|
|
9
|
+
});
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
const WebhookTriggerSchema = S.struct({
|
|
12
|
+
port: S.number,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const WebsocketTriggerSchema = S.struct({
|
|
16
|
+
url: S.string,
|
|
17
|
+
init: S.optional(S.record(S.string, S.any)),
|
|
18
|
+
});
|
|
17
19
|
|
|
18
20
|
const SubscriptionTriggerSchema = S.struct({
|
|
19
|
-
|
|
21
|
+
spaceKey: S.optional(S.string),
|
|
20
22
|
// TODO(burdon): Define query DSL.
|
|
21
23
|
filter: S.array(
|
|
22
24
|
S.struct({
|
|
@@ -33,69 +35,42 @@ const SubscriptionTriggerSchema = S.struct({
|
|
|
33
35
|
}),
|
|
34
36
|
),
|
|
35
37
|
});
|
|
36
|
-
export type SubscriptionTrigger = S.Schema.Type<typeof SubscriptionTriggerSchema>;
|
|
37
38
|
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
const FunctionTriggerSchema = S.struct({
|
|
40
|
+
function: S.string.pipe(S.description('Function ID/URI.')),
|
|
41
|
+
|
|
42
|
+
timer: S.optional(TimerTriggerSchema),
|
|
43
|
+
webhook: S.optional(WebhookTriggerSchema),
|
|
44
|
+
websocket: S.optional(WebsocketTriggerSchema),
|
|
45
|
+
subscription: S.optional(SubscriptionTriggerSchema),
|
|
41
46
|
});
|
|
42
|
-
export type TimerTrigger = S.Schema.Type<typeof TimerTriggerSchema>;
|
|
43
47
|
|
|
44
|
-
|
|
45
|
-
S.struct({
|
|
46
|
-
type: S.literal('webhook'),
|
|
47
|
-
method: S.string,
|
|
48
|
-
// Assigned port.
|
|
49
|
-
port: S.optional(S.number),
|
|
50
|
-
}),
|
|
51
|
-
);
|
|
48
|
+
export type TimerTrigger = S.Schema.Type<typeof TimerTriggerSchema>;
|
|
52
49
|
export type WebhookTrigger = S.Schema.Type<typeof WebhookTriggerSchema>;
|
|
53
|
-
|
|
54
|
-
const WebsocketTriggerSchema = S.struct({
|
|
55
|
-
type: S.literal('websocket'),
|
|
56
|
-
url: S.string,
|
|
57
|
-
init: S.optional(S.record(S.string, S.any)),
|
|
58
|
-
});
|
|
59
50
|
export type WebsocketTrigger = S.Schema.Type<typeof WebsocketTriggerSchema>;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
TimerTriggerSchema,
|
|
63
|
-
WebhookTriggerSchema,
|
|
64
|
-
WebsocketTriggerSchema,
|
|
65
|
-
SubscriptionTriggerSchema,
|
|
66
|
-
);
|
|
67
|
-
export type TriggerSpec = TimerTrigger | WebhookTrigger | WebsocketTrigger | SubscriptionTrigger;
|
|
51
|
+
export type SubscriptionTrigger = S.Schema.Type<typeof SubscriptionTriggerSchema>;
|
|
52
|
+
export type FunctionTrigger = S.Schema.Type<typeof FunctionTriggerSchema>;
|
|
68
53
|
|
|
69
54
|
/**
|
|
70
55
|
* Function definition.
|
|
71
56
|
*/
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
})({
|
|
76
|
-
uri: S.string,
|
|
57
|
+
// TODO(burdon): Name vs. path?
|
|
58
|
+
const FunctionDefSchema = S.struct({
|
|
59
|
+
id: S.string,
|
|
77
60
|
description: S.optional(S.string),
|
|
78
|
-
|
|
79
|
-
// TODO(burdon): NPM/GitHub
|
|
61
|
+
name: S.string,
|
|
62
|
+
// TODO(burdon): NPM/GitHub URL?
|
|
80
63
|
handler: S.string,
|
|
81
|
-
})
|
|
64
|
+
});
|
|
82
65
|
|
|
83
|
-
export
|
|
84
|
-
typename: 'dxos.org/type/FunctionTrigger',
|
|
85
|
-
version: '0.1.0',
|
|
86
|
-
})({
|
|
87
|
-
function: S.string.pipe(S.description('Function ID/URI.')),
|
|
88
|
-
// Context passed to a function.
|
|
89
|
-
meta: S.optional(S.record(S.string, S.any)),
|
|
90
|
-
spec: TriggerSpecSchema,
|
|
91
|
-
}) {}
|
|
66
|
+
export type FunctionDef = S.Schema.Type<typeof FunctionDefSchema>;
|
|
92
67
|
|
|
93
68
|
/**
|
|
94
69
|
* Function manifest file.
|
|
95
70
|
*/
|
|
96
71
|
export const FunctionManifestSchema = S.struct({
|
|
97
|
-
functions: S.
|
|
98
|
-
triggers: S.
|
|
72
|
+
functions: S.mutable(S.array(FunctionDefSchema)),
|
|
73
|
+
triggers: S.mutable(S.array(FunctionTriggerSchema)),
|
|
99
74
|
});
|
|
100
75
|
|
|
101
76
|
export type FunctionManifest = S.Schema.Type<typeof FunctionManifestSchema>;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { Event } from '@dxos/async';
|
|
2
|
-
import { type Client } from '@dxos/client';
|
|
3
|
-
import { type Space } from '@dxos/client/echo';
|
|
4
|
-
import { type Context, Resource } from '@dxos/context';
|
|
5
|
-
import { FunctionDef, type FunctionManifest } from '../types';
|
|
6
|
-
export type FunctionsRegisteredEvent = {
|
|
7
|
-
space: Space;
|
|
8
|
-
newFunctions: FunctionDef[];
|
|
9
|
-
};
|
|
10
|
-
export declare class FunctionRegistry extends Resource {
|
|
11
|
-
private readonly _client;
|
|
12
|
-
private readonly _functionBySpaceKey;
|
|
13
|
-
readonly onFunctionsRegistered: Event<FunctionsRegisteredEvent>;
|
|
14
|
-
constructor(_client: Client);
|
|
15
|
-
getFunctions(space: Space): FunctionDef[];
|
|
16
|
-
/**
|
|
17
|
-
* The method loads function definitions from the manifest into the space.
|
|
18
|
-
* We first load all the definitions from the space to deduplicate by functionId.
|
|
19
|
-
*/
|
|
20
|
-
register(space: Space, manifest: FunctionManifest): Promise<void>;
|
|
21
|
-
protected _open(): Promise<void>;
|
|
22
|
-
protected _close(_: Context): Promise<void>;
|
|
23
|
-
}
|
|
24
|
-
//# sourceMappingURL=function-registry.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"function-registry.d.ts","sourceRoot":"","sources":["../../../../src/registry/function-registry.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAkB,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,KAAK,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAIvD,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE9D,MAAM,MAAM,wBAAwB,GAAG;IACrC,KAAK,EAAE,KAAK,CAAC;IACb,YAAY,EAAE,WAAW,EAAE,CAAC;CAC7B,CAAC;AAEF,qBAAa,gBAAiB,SAAQ,QAAQ;IAKhC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA4D;IAEhG,SAAgB,qBAAqB,kCAAyC;gBAEjD,OAAO,EAAE,MAAM;IAIrC,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,WAAW,EAAE;IAIhD;;;OAGG;IAEU,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;cAcrD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;cA0BtB,MAAM,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CAG3D"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"function-registry.test.d.ts","sourceRoot":"","sources":["../../../../src/registry/function-registry.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/registry/index.ts"],"names":[],"mappings":"AAIA,cAAc,qBAAqB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dev-server.test.d.ts","sourceRoot":"","sources":["../../../../src/runtime/dev-server.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"functions-integration.test.d.ts","sourceRoot":"","sources":["../../../../src/testing/functions-integration.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/testing/index.ts"],"names":[],"mappings":"AAIA,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC"}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { Client, Config } from '@dxos/client';
|
|
2
|
-
import { type TestBuilder } from '@dxos/client/testing';
|
|
3
|
-
export declare const createInitializedClients: (testBuilder: TestBuilder, count?: number, config?: Config) => Promise<Client[]>;
|
|
4
|
-
export declare const createFunctionRuntime: (testBuilder: TestBuilder) => Promise<Client>;
|
|
5
|
-
//# sourceMappingURL=setup.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../../src/testing/setup.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAQxD,eAAO,MAAM,wBAAwB,gBAAuB,WAAW,UAAS,MAAM,WAAe,MAAM,sBAW1G,CAAC;AAEF,eAAO,MAAM,qBAAqB,gBAAuB,WAAW,KAAG,QAAQ,MAAM,CAiBpF,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../../src/testing/test/handler.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAIrD,eAAO,MAAM,kBAAkB,YAAa,gBAAgB,GAAG,CAAC,SAE/D,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,eAAe,CAAC,GAAG,CAExC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/testing/test/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,eAAe,OAAO,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/testing/types.ts"],"names":[],"mappings":";;;;;AAMA,qBAAa,QAAS,SAAQ,aAE5B;CAAG"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../../../src/testing/util.ts"],"names":[],"mappings":"AAIA,OAAO,EAAU,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAKvD,eAAO,MAAM,cAAc,UAAiB,KAAK,OAAO,MAAM,kBAM7D,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/trigger/index.ts"],"names":[],"mappings":"AAIA,cAAc,oBAAoB,CAAC"}
|