@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.
Files changed (81) hide show
  1. package/dist/lib/browser/index.mjs +429 -802
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node/index.cjs +426 -787
  5. package/dist/lib/node/index.cjs.map +4 -4
  6. package/dist/lib/node/meta.json +1 -1
  7. package/dist/types/src/handler.d.ts +12 -33
  8. package/dist/types/src/handler.d.ts.map +1 -1
  9. package/dist/types/src/index.d.ts +0 -2
  10. package/dist/types/src/index.d.ts.map +1 -1
  11. package/dist/types/src/runtime/dev-server.d.ts +13 -16
  12. package/dist/types/src/runtime/dev-server.d.ts.map +1 -1
  13. package/dist/types/src/runtime/scheduler.d.ts +27 -12
  14. package/dist/types/src/runtime/scheduler.d.ts.map +1 -1
  15. package/dist/types/src/types.d.ts +101 -129
  16. package/dist/types/src/types.d.ts.map +1 -1
  17. package/package.json +13 -18
  18. package/schema/functions.json +101 -128
  19. package/src/handler.ts +31 -54
  20. package/src/index.ts +0 -2
  21. package/src/runtime/dev-server.ts +52 -104
  22. package/src/runtime/scheduler.test.ts +73 -56
  23. package/src/runtime/scheduler.ts +271 -79
  24. package/src/types.ts +32 -57
  25. package/dist/types/src/registry/function-registry.d.ts +0 -24
  26. package/dist/types/src/registry/function-registry.d.ts.map +0 -1
  27. package/dist/types/src/registry/function-registry.test.d.ts +0 -2
  28. package/dist/types/src/registry/function-registry.test.d.ts.map +0 -1
  29. package/dist/types/src/registry/index.d.ts +0 -2
  30. package/dist/types/src/registry/index.d.ts.map +0 -1
  31. package/dist/types/src/runtime/dev-server.test.d.ts +0 -2
  32. package/dist/types/src/runtime/dev-server.test.d.ts.map +0 -1
  33. package/dist/types/src/testing/functions-integration.test.d.ts +0 -2
  34. package/dist/types/src/testing/functions-integration.test.d.ts.map +0 -1
  35. package/dist/types/src/testing/index.d.ts +0 -4
  36. package/dist/types/src/testing/index.d.ts.map +0 -1
  37. package/dist/types/src/testing/setup.d.ts +0 -5
  38. package/dist/types/src/testing/setup.d.ts.map +0 -1
  39. package/dist/types/src/testing/test/handler.d.ts +0 -4
  40. package/dist/types/src/testing/test/handler.d.ts.map +0 -1
  41. package/dist/types/src/testing/test/index.d.ts +0 -3
  42. package/dist/types/src/testing/test/index.d.ts.map +0 -1
  43. package/dist/types/src/testing/types.d.ts +0 -9
  44. package/dist/types/src/testing/types.d.ts.map +0 -1
  45. package/dist/types/src/testing/util.d.ts +0 -3
  46. package/dist/types/src/testing/util.d.ts.map +0 -1
  47. package/dist/types/src/trigger/index.d.ts +0 -2
  48. package/dist/types/src/trigger/index.d.ts.map +0 -1
  49. package/dist/types/src/trigger/trigger-registry.d.ts +0 -40
  50. package/dist/types/src/trigger/trigger-registry.d.ts.map +0 -1
  51. package/dist/types/src/trigger/trigger-registry.test.d.ts +0 -2
  52. package/dist/types/src/trigger/trigger-registry.test.d.ts.map +0 -1
  53. package/dist/types/src/trigger/type/index.d.ts +0 -5
  54. package/dist/types/src/trigger/type/index.d.ts.map +0 -1
  55. package/dist/types/src/trigger/type/subscription-trigger.d.ts +0 -4
  56. package/dist/types/src/trigger/type/subscription-trigger.d.ts.map +0 -1
  57. package/dist/types/src/trigger/type/timer-trigger.d.ts +0 -4
  58. package/dist/types/src/trigger/type/timer-trigger.d.ts.map +0 -1
  59. package/dist/types/src/trigger/type/webhook-trigger.d.ts +0 -4
  60. package/dist/types/src/trigger/type/webhook-trigger.d.ts.map +0 -1
  61. package/dist/types/src/trigger/type/websocket-trigger.d.ts +0 -13
  62. package/dist/types/src/trigger/type/websocket-trigger.d.ts.map +0 -1
  63. package/src/registry/function-registry.test.ts +0 -105
  64. package/src/registry/function-registry.ts +0 -84
  65. package/src/registry/index.ts +0 -5
  66. package/src/runtime/dev-server.test.ts +0 -60
  67. package/src/testing/functions-integration.test.ts +0 -99
  68. package/src/testing/index.ts +0 -7
  69. package/src/testing/setup.ts +0 -45
  70. package/src/testing/test/handler.ts +0 -15
  71. package/src/testing/test/index.ts +0 -7
  72. package/src/testing/types.ts +0 -9
  73. package/src/testing/util.ts +0 -16
  74. package/src/trigger/index.ts +0 -5
  75. package/src/trigger/trigger-registry.test.ts +0 -229
  76. package/src/trigger/trigger-registry.ts +0 -176
  77. package/src/trigger/type/index.ts +0 -8
  78. package/src/trigger/type/subscription-trigger.ts +0 -73
  79. package/src/trigger/type/timer-trigger.ts +0 -44
  80. package/src/trigger/type/webhook-trigger.ts +0 -47
  81. package/src/trigger/type/websocket-trigger.ts +0 -91
@@ -2,18 +2,31 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import path from 'node:path';
5
+ import { CronJob } from 'cron';
6
+ import http from 'node:http';
7
+ import WebSocket from 'ws';
6
8
 
7
- import { type Space } from '@dxos/client/echo';
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 FunctionEventMeta } from '../handler';
12
- import { type FunctionRegistry } from '../registry';
13
- import { type TriggerRegistry } from '../trigger';
14
- import { type FunctionDef, type FunctionManifest, type FunctionTrigger } from '../types';
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
- export type Callback = (data: any) => Promise<void | number>;
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 execution based on various triggers.
37
+ * The scheduler triggers function exectuion based on various triggers.
25
38
  */
26
39
  export class Scheduler {
27
- private _ctx = createContext();
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
- public readonly functions: FunctionRegistry,
31
- public readonly triggers: TriggerRegistry,
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
- await this._ctx.dispose();
44
- this._ctx = createContext();
45
- await this.functions.open(this._ctx);
46
- await this.triggers.open(this._ctx);
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
- await this._ctx.dispose();
51
- await this.functions.close();
52
- await this.triggers.close();
64
+ for (const { id, spaceKey } of this._mounts.keys()) {
65
+ await this.unmount(id, spaceKey);
66
+ }
53
67
  }
54
68
 
55
- public async register(space: Space, manifest: FunctionManifest) {
56
- await this.functions.register(space, manifest);
57
- await this.triggers.register(space, manifest);
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
- private async _safeActivateTriggers(
61
- space: Space,
62
- triggers: FunctionTrigger[],
63
- functions: FunctionDef[],
64
- ): Promise<void> {
65
- const mountTasks = triggers.map((trigger) => {
66
- return this.activate(space, functions, trigger);
67
- });
68
- await Promise.all(mountTasks).catch(log.catch);
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
- private async activate(space: Space, functions: FunctionDef[], fnTrigger: FunctionTrigger) {
72
- const definition = functions.find((def) => def.uri === fnTrigger.function);
73
- if (!definition) {
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
- await this.triggers.activate({ space }, fnTrigger, async (args) => {
79
- return this._execFunction(definition, {
80
- meta: fnTrigger.meta,
81
- data: { ...args, spaceKey: space.key },
82
- });
83
- });
84
- log('activated trigger', { space: space.key, trigger: fnTrigger });
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
- private async _execFunction<TData, TMeta>(
88
- def: FunctionDef,
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
- // TODO(burdon): Pass in Space key (common context)?
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
- const url = path.join(endpoint, def.route);
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(payload),
126
+ body: JSON.stringify(data),
107
127
  });
108
-
109
- status = response.status;
110
128
  } else if (callback) {
111
- log.info('exec', { function: def.uri });
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.uri, status });
133
+ log.info('done', { function: def.id });
122
134
  } catch (err: any) {
123
- log.error('error', { function: def.uri, error: err.message });
124
- status = 500;
135
+ log.error('error', { function: def.id, error: err.message });
125
136
  }
137
+ }
126
138
 
127
- return status;
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
- const createContext = () => new Context({ name: 'FunctionScheduler' });
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 { AST, S, TypedObject } from '@dxos/echo-schema';
5
+ import * as S from '@effect/schema/Schema';
6
6
 
7
- // TODO(burdon): Factor out.
8
- const omitEchoId = <T>(schema: S.Schema<T>): S.Schema<Omit<T, 'id'>> => S.make(AST.omit(schema.ast, ['id']));
7
+ const TimerTriggerSchema = S.struct({
8
+ cron: S.string,
9
+ });
9
10
 
10
- /**
11
- * Type discriminator for TriggerSpec.
12
- * Every spec has a type field of type FunctionTriggerType that we can use to understand which
13
- * type we're working with.
14
- * https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions
15
- */
16
- export type FunctionTriggerType = 'subscription' | 'timer' | 'webhook' | 'websocket';
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
- type: S.literal('subscription'),
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 TimerTriggerSchema = S.struct({
39
- type: S.literal('timer'),
40
- cron: S.string,
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
- const WebhookTriggerSchema = S.mutable(
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
- const TriggerSpecSchema = S.union(
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
- export class FunctionDef extends TypedObject({
73
- typename: 'dxos.org/type/FunctionDef',
74
- version: '0.1.0',
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
- route: S.string,
79
- // TODO(burdon): NPM/GitHub/Docker/CF URL?
61
+ name: S.string,
62
+ // TODO(burdon): NPM/GitHub URL?
80
63
  handler: S.string,
81
- }) {}
64
+ });
82
65
 
83
- export class FunctionTrigger extends TypedObject({
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.optional(S.mutable(S.array(omitEchoId(FunctionDef)))),
98
- triggers: S.optional(S.mutable(S.array(omitEchoId(FunctionTrigger)))),
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,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=function-registry.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"function-registry.test.d.ts","sourceRoot":"","sources":["../../../../src/registry/function-registry.test.ts"],"names":[],"mappings":""}
@@ -1,2 +0,0 @@
1
- export * from './function-registry';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/registry/index.ts"],"names":[],"mappings":"AAIA,cAAc,qBAAqB,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=dev-server.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dev-server.test.d.ts","sourceRoot":"","sources":["../../../../src/runtime/dev-server.test.ts"],"names":[],"mappings":""}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=functions-integration.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"functions-integration.test.d.ts","sourceRoot":"","sources":["../../../../src/testing/functions-integration.test.ts"],"names":[],"mappings":""}
@@ -1,4 +0,0 @@
1
- export * from './setup';
2
- export * from './types';
3
- export * from './util';
4
- //# sourceMappingURL=index.d.ts.map
@@ -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,4 +0,0 @@
1
- import { type FunctionHandler } from '../../handler';
2
- export declare const setTestCallHandler: (handler: FunctionHandler<any>) => void;
3
- export declare const handler: FunctionHandler<any>;
4
- //# sourceMappingURL=handler.d.ts.map
@@ -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,3 +0,0 @@
1
- import { handler } from './handler';
2
- export default handler;
3
- //# sourceMappingURL=index.d.ts.map
@@ -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,9 +0,0 @@
1
- declare const TestType_base: import("@dxos/echo-schema").EchoSchemaClass<{
2
- title: string;
3
- } & {
4
- id: string;
5
- }>;
6
- export declare class TestType extends TestType_base {
7
- }
8
- export {};
9
- //# sourceMappingURL=types.d.ts.map
@@ -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,3 +0,0 @@
1
- import { type Space } from '@dxos/client/echo';
2
- export declare const triggerWebhook: (space: Space, uri: string) => Promise<void>;
3
- //# sourceMappingURL=util.d.ts.map
@@ -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,2 +0,0 @@
1
- export * from './trigger-registry';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/trigger/index.ts"],"names":[],"mappings":"AAIA,cAAc,oBAAoB,CAAC"}