@dxos/functions 0.5.3-main.3b535c7 → 0.5.3-main.3c6700b

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.
@@ -20,16 +20,16 @@ import { log } from "@dxos/log";
20
20
  import { nonNullable } from "@dxos/util";
21
21
  var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/functions/src/handler.ts";
22
22
  var subscriptionHandler = (handler) => {
23
- return ({ event, context, ...rest }) => {
23
+ return ({ event: { data }, context, ...rest }) => {
24
24
  const { client } = context;
25
- const space = event.space ? client.spaces.get(PublicKey.from(event.space)) : void 0;
26
- const objects = space && event.objects?.map((id) => space.db.getObjectById(id)).filter(nonNullable);
27
- if (!!event.space && !space) {
25
+ const space = data.spaceKey ? client.spaces.get(PublicKey.from(data.spaceKey)) : void 0;
26
+ const objects = space ? data.objects?.map((id) => space.db.getObjectById(id)).filter(nonNullable) : [];
27
+ if (!!data.spaceKey && !space) {
28
28
  log.warn("invalid space", {
29
- event
29
+ data
30
30
  }, {
31
31
  F: __dxlog_file,
32
- L: 68,
32
+ L: 91,
33
33
  S: void 0,
34
34
  C: (f, a) => f(...a)
35
35
  });
@@ -39,15 +39,18 @@ var subscriptionHandler = (handler) => {
39
39
  objects: objects?.length
40
40
  }, {
41
41
  F: __dxlog_file,
42
- L: 70,
42
+ L: 93,
43
43
  S: void 0,
44
44
  C: (f, a) => f(...a)
45
45
  });
46
46
  }
47
47
  return handler({
48
48
  event: {
49
- space,
50
- objects
49
+ data: {
50
+ ...data,
51
+ space,
52
+ objects
53
+ }
51
54
  },
52
55
  context,
53
56
  ...rest
@@ -59,7 +62,7 @@ var subscriptionHandler = (handler) => {
59
62
  import express from "express";
60
63
  import { getPort } from "get-port-please";
61
64
  import { join } from "@dxos/node-std/path";
62
- import { Trigger } from "@dxos/async";
65
+ import { Event, Trigger } from "@dxos/async";
63
66
  import { invariant } from "@dxos/invariant";
64
67
  import { log as log2 } from "@dxos/log";
65
68
  var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/dev-server.ts";
@@ -70,11 +73,17 @@ var DevServer = class {
70
73
  this._options = _options;
71
74
  this._handlers = {};
72
75
  this._seq = 0;
76
+ this.update = new Event();
77
+ }
78
+ get stats() {
79
+ return {
80
+ seq: this._seq
81
+ };
73
82
  }
74
83
  get endpoint() {
75
84
  invariant(this._port, void 0, {
76
85
  F: __dxlog_file2,
77
- L: 46,
86
+ L: 54,
78
87
  S: this,
79
88
  A: [
80
89
  "this._port",
@@ -96,7 +105,7 @@ var DevServer = class {
96
105
  } catch (err) {
97
106
  log2.error("parsing function (check manifest)", err, {
98
107
  F: __dxlog_file2,
99
- L: 63,
108
+ L: 71,
100
109
  S: this,
101
110
  C: (f, a) => f(...a)
102
111
  });
@@ -104,29 +113,44 @@ var DevServer = class {
104
113
  }
105
114
  }
106
115
  async start() {
116
+ invariant(!this._server, void 0, {
117
+ F: __dxlog_file2,
118
+ L: 77,
119
+ S: this,
120
+ A: [
121
+ "!this._server",
122
+ ""
123
+ ]
124
+ });
125
+ log2.info("starting...", void 0, {
126
+ F: __dxlog_file2,
127
+ L: 78,
128
+ S: this,
129
+ C: (f, a) => f(...a)
130
+ });
107
131
  const app = express();
108
132
  app.use(express.json());
109
- app.post("/:name", async (req, res) => {
110
- const { name } = req.params;
133
+ app.post("/:path", async (req, res) => {
134
+ const { path: path2 } = req.params;
111
135
  try {
112
136
  log2.info("calling", {
113
- name
137
+ path: path2
114
138
  }, {
115
139
  F: __dxlog_file2,
116
- L: 75,
140
+ L: 87,
117
141
  S: this,
118
142
  C: (f, a) => f(...a)
119
143
  });
120
144
  if (this._options.reload) {
121
- const { def } = this._handlers[name];
145
+ const { def } = this._handlers["/" + path2];
122
146
  await this._load(def, true);
123
147
  }
124
- res.statusCode = await this._invoke(name, req.body);
148
+ res.statusCode = await this.invoke("/" + path2, req.body);
125
149
  res.end();
126
150
  } catch (err) {
127
151
  log2.catch(err, void 0, {
128
152
  F: __dxlog_file2,
129
- L: 84,
153
+ L: 97,
130
154
  S: this,
131
155
  C: (f, a) => f(...a)
132
156
  });
@@ -146,92 +170,170 @@ var DevServer = class {
146
170
  try {
147
171
  const { registrationId, endpoint } = await this._client.services.services.FunctionRegistryService.register({
148
172
  endpoint: this.endpoint,
149
- functions: this.functions.map(({ def: { name } }) => ({
150
- name
173
+ functions: this.functions.map(({ def: { id, path: path2 } }) => ({
174
+ id,
175
+ path: path2
151
176
  }))
152
177
  });
153
178
  log2.info("registered", {
154
- registrationId,
155
179
  endpoint
156
180
  }, {
157
181
  F: __dxlog_file2,
158
- L: 100,
182
+ L: 113,
159
183
  S: this,
160
184
  C: (f, a) => f(...a)
161
185
  });
162
- this._registrationId = registrationId;
163
186
  this._proxy = endpoint;
187
+ this._functionServiceRegistration = registrationId;
164
188
  } catch (err) {
165
189
  await this.stop();
166
190
  throw new Error("FunctionRegistryService not available (check plugin is configured).");
167
191
  }
192
+ log2.info("started", {
193
+ port: this._port
194
+ }, {
195
+ F: __dxlog_file2,
196
+ L: 121,
197
+ S: this,
198
+ C: (f, a) => f(...a)
199
+ });
168
200
  }
169
201
  async stop() {
202
+ invariant(this._server, void 0, {
203
+ F: __dxlog_file2,
204
+ L: 125,
205
+ S: this,
206
+ A: [
207
+ "this._server",
208
+ ""
209
+ ]
210
+ });
211
+ log2.info("stopping...", void 0, {
212
+ F: __dxlog_file2,
213
+ L: 126,
214
+ S: this,
215
+ C: (f, a) => f(...a)
216
+ });
170
217
  const trigger = new Trigger();
171
- this._server?.close(async () => {
172
- if (this._registrationId) {
173
- await this._client.services.services.FunctionRegistryService.unregister({
174
- registrationId: this._registrationId
175
- });
176
- log2.info("unregistered", {
177
- registrationId: this._registrationId
178
- }, {
179
- F: __dxlog_file2,
180
- L: 117,
181
- S: this,
182
- C: (f, a) => f(...a)
183
- });
184
- this._registrationId = void 0;
185
- this._proxy = void 0;
218
+ this._server.close(async () => {
219
+ log2.info("server stopped", void 0, {
220
+ F: __dxlog_file2,
221
+ L: 130,
222
+ S: this,
223
+ C: (f, a) => f(...a)
224
+ });
225
+ try {
226
+ if (this._functionServiceRegistration) {
227
+ invariant(this._client.services.services.FunctionRegistryService, void 0, {
228
+ F: __dxlog_file2,
229
+ L: 133,
230
+ S: this,
231
+ A: [
232
+ "this._client.services.services.FunctionRegistryService",
233
+ ""
234
+ ]
235
+ });
236
+ await this._client.services.services.FunctionRegistryService.unregister({
237
+ registrationId: this._functionServiceRegistration
238
+ });
239
+ log2.info("unregistered", {
240
+ registrationId: this._functionServiceRegistration
241
+ }, {
242
+ F: __dxlog_file2,
243
+ L: 138,
244
+ S: this,
245
+ C: (f, a) => f(...a)
246
+ });
247
+ this._functionServiceRegistration = void 0;
248
+ this._proxy = void 0;
249
+ }
250
+ trigger.wake();
251
+ } catch (err) {
252
+ trigger.throw(err);
186
253
  }
187
- trigger.wake();
188
254
  });
189
255
  await trigger.wait();
190
256
  this._port = void 0;
191
257
  this._server = void 0;
258
+ log2.info("stopped", void 0, {
259
+ F: __dxlog_file2,
260
+ L: 152,
261
+ S: this,
262
+ C: (f, a) => f(...a)
263
+ });
192
264
  }
193
265
  /**
194
266
  * Load function.
195
267
  */
196
- async _load(def, flush = false) {
197
- const { id, name, handler } = def;
198
- const path = join(this._options.directory, handler);
268
+ async _load(def, force = false) {
269
+ const { id, path: path2, handler } = def;
270
+ const filePath = join(this._options.baseDir, handler);
199
271
  log2.info("loading", {
200
- id
272
+ id,
273
+ force
201
274
  }, {
202
275
  F: __dxlog_file2,
203
- L: 136,
276
+ L: 161,
204
277
  S: this,
205
278
  C: (f, a) => f(...a)
206
279
  });
207
- if (flush) {
208
- Object.keys(__require.cache).filter((key) => key.startsWith(path)).forEach((key) => delete __require.cache[key]);
280
+ if (force) {
281
+ Object.keys(__require.cache).filter((key) => key.startsWith(filePath)).forEach((key) => {
282
+ delete __require.cache[key];
283
+ });
209
284
  }
210
- const module = __require(path);
285
+ const module = __require(filePath);
211
286
  if (typeof module.default !== "function") {
212
287
  throw new Error(`Handler must export default function: ${id}`);
213
288
  }
214
- this._handlers[name] = {
289
+ this._handlers[path2] = {
215
290
  def,
216
291
  handler: module.default
217
292
  };
218
293
  }
219
294
  /**
220
- * Invoke function handler.
295
+ * Invoke function.
221
296
  */
222
- async _invoke(name, event) {
297
+ async invoke(path2, data) {
223
298
  const seq = ++this._seq;
224
299
  const now = Date.now();
225
300
  log2.info("req", {
226
301
  seq,
227
- name
302
+ path: path2
228
303
  }, {
229
304
  F: __dxlog_file2,
230
- L: 161,
305
+ L: 188,
306
+ S: this,
307
+ C: (f, a) => f(...a)
308
+ });
309
+ const statusCode = await this._invoke(path2, {
310
+ data
311
+ });
312
+ log2.info("res", {
313
+ seq,
314
+ path: path2,
315
+ statusCode,
316
+ duration: Date.now() - now
317
+ }, {
318
+ F: __dxlog_file2,
319
+ L: 191,
231
320
  S: this,
232
321
  C: (f, a) => f(...a)
233
322
  });
234
- const { handler } = this._handlers[name];
323
+ this.update.emit(statusCode);
324
+ return statusCode;
325
+ }
326
+ async _invoke(path2, event) {
327
+ const { handler } = this._handlers[path2] ?? {};
328
+ invariant(handler, `invalid path: ${path2}`, {
329
+ F: __dxlog_file2,
330
+ L: 198,
331
+ S: this,
332
+ A: [
333
+ "handler",
334
+ "`invalid path: ${path}`"
335
+ ]
336
+ });
235
337
  const context = {
236
338
  client: this._client,
237
339
  dataDir: this._options.dataDir
@@ -248,24 +350,15 @@ var DevServer = class {
248
350
  event,
249
351
  response
250
352
  });
251
- log2.info("res", {
252
- seq,
253
- name,
254
- statusCode,
255
- duration: Date.now() - now
256
- }, {
257
- F: __dxlog_file2,
258
- L: 178,
259
- S: this,
260
- C: (f, a) => f(...a)
261
- });
262
353
  return statusCode;
263
354
  }
264
355
  };
265
356
 
266
357
  // packages/core/functions/src/runtime/scheduler.ts
267
358
  import { CronJob } from "cron";
359
+ import { getPort as getPort2 } from "get-port-please";
268
360
  import http from "@dxos/node-std/http";
361
+ import path from "@dxos/node-std/path";
269
362
  import WebSocket from "ws";
270
363
  import { TextV0Type } from "@braneframe/types";
271
364
  import { debounce, DeferredTask, sleep, Trigger as Trigger2 } from "@dxos/async";
@@ -280,7 +373,13 @@ var Scheduler = class {
280
373
  this._client = _client;
281
374
  this._manifest = _manifest;
282
375
  this._options = _options;
283
- this._mounts = new ComplexMap(({ id, spaceKey }) => `${spaceKey.toHex()}:${id}`);
376
+ this._mounts = new ComplexMap(({ spaceKey, id }) => `${spaceKey.toHex()}:${id}`);
377
+ }
378
+ get mounts() {
379
+ return Array.from(this._mounts.values()).reduce((acc, { trigger }) => {
380
+ acc.push(trigger);
381
+ return acc;
382
+ }, []);
284
383
  }
285
384
  async start() {
286
385
  this._client.spaces.subscribe(async (spaces) => {
@@ -297,15 +396,18 @@ var Scheduler = class {
297
396
  await this.unmount(id, spaceKey);
298
397
  }
299
398
  }
399
+ /**
400
+ * Mount trigger.
401
+ */
300
402
  async mount(ctx, space, trigger) {
301
403
  const key = {
302
- id: trigger.function,
303
- spaceKey: space.key
404
+ spaceKey: space.key,
405
+ id: trigger.function
304
406
  };
305
407
  const def = this._manifest.functions.find((config) => config.id === trigger.function);
306
408
  invariant2(def, `Function not found: ${trigger.function}`, {
307
409
  F: __dxlog_file3,
308
- L: 72,
410
+ L: 76,
309
411
  S: this,
310
412
  A: [
311
413
  "def",
@@ -323,7 +425,7 @@ var Scheduler = class {
323
425
  trigger
324
426
  }, {
325
427
  F: __dxlog_file3,
326
- L: 78,
428
+ L: 82,
327
429
  S: this,
328
430
  C: (f, a) => f(...a)
329
431
  });
@@ -331,16 +433,16 @@ var Scheduler = class {
331
433
  return;
332
434
  }
333
435
  if (trigger.timer) {
334
- await this._createTimer(ctx, space, def, trigger.timer);
436
+ await this._createTimer(ctx, space, def, trigger);
335
437
  }
336
438
  if (trigger.webhook) {
337
- await this._createWebhook(ctx, space, def, trigger.webhook);
439
+ await this._createWebhook(ctx, space, def, trigger);
338
440
  }
339
441
  if (trigger.websocket) {
340
- await this._createWebsocket(ctx, space, def, trigger.websocket);
442
+ await this._createWebsocket(ctx, space, def, trigger);
341
443
  }
342
444
  if (trigger.subscription) {
343
- await this._createSubscription(ctx, space, def, trigger.subscription);
445
+ await this._createSubscription(ctx, space, def, trigger);
344
446
  }
345
447
  }
346
448
  }
@@ -355,34 +457,52 @@ var Scheduler = class {
355
457
  await ctx.dispose();
356
458
  }
357
459
  }
358
- // TODO(burdon): Pass in Space key (common context).
359
- async _execFunction(def, data) {
460
+ async _execFunction(def, trigger, data) {
461
+ let status = 0;
360
462
  try {
361
- log3.info("exec", {
362
- function: def.id
363
- }, {
364
- F: __dxlog_file3,
365
- L: 117,
366
- S: this,
367
- C: (f, a) => f(...a)
368
- });
463
+ const payload = Object.assign({}, {
464
+ meta: trigger.meta
465
+ }, data);
369
466
  const { endpoint, callback } = this._options;
370
467
  if (endpoint) {
371
- await fetch(`${this._options.endpoint}/${def.name}`, {
468
+ const url = path.join(endpoint, def.path);
469
+ log3.info("exec", {
470
+ function: def.id,
471
+ url
472
+ }, {
473
+ F: __dxlog_file3,
474
+ L: 128,
475
+ S: this,
476
+ C: (f, a) => f(...a)
477
+ });
478
+ const response = await fetch(url, {
372
479
  method: "POST",
373
480
  headers: {
374
481
  "Content-Type": "application/json"
375
482
  },
376
- body: JSON.stringify(data)
483
+ body: JSON.stringify(payload)
377
484
  });
485
+ status = response.status;
378
486
  } else if (callback) {
379
- await callback(data);
487
+ log3.info("exec", {
488
+ function: def.id
489
+ }, {
490
+ F: __dxlog_file3,
491
+ L: 139,
492
+ S: this,
493
+ C: (f, a) => f(...a)
494
+ });
495
+ status = await callback(payload) ?? 200;
496
+ }
497
+ if (status && status >= 400) {
498
+ throw new Error(`Response: ${status}`);
380
499
  }
381
500
  log3.info("done", {
382
- function: def.id
501
+ function: def.id,
502
+ status
383
503
  }, {
384
504
  F: __dxlog_file3,
385
- L: 133,
505
+ L: 149,
386
506
  S: this,
387
507
  C: (f, a) => f(...a)
388
508
  });
@@ -392,11 +512,13 @@ var Scheduler = class {
392
512
  error: err.message
393
513
  }, {
394
514
  F: __dxlog_file3,
395
- L: 135,
515
+ L: 151,
396
516
  S: this,
397
517
  C: (f, a) => f(...a)
398
518
  });
519
+ status = 500;
399
520
  }
521
+ return status;
400
522
  }
401
523
  //
402
524
  // Triggers
@@ -410,20 +532,20 @@ var Scheduler = class {
410
532
  trigger
411
533
  }, {
412
534
  F: __dxlog_file3,
413
- L: 147,
535
+ L: 166,
414
536
  S: this,
415
537
  C: (f, a) => f(...a)
416
538
  });
417
- const { cron } = trigger;
539
+ const spec = trigger.timer;
418
540
  const task = new DeferredTask(ctx, async () => {
419
- await this._execFunction(def, {
420
- space: space.key
541
+ await this._execFunction(def, trigger, {
542
+ spaceKey: space.key
421
543
  });
422
544
  });
423
545
  let last = 0;
424
546
  let run = 0;
425
547
  const job = CronJob.from({
426
- cronTime: cron,
548
+ cronTime: spec.cron,
427
549
  runOnInit: false,
428
550
  onTick: () => {
429
551
  const now = Date.now();
@@ -436,7 +558,7 @@ var Scheduler = class {
436
558
  delta
437
559
  }, {
438
560
  F: __dxlog_file3,
439
- L: 167,
561
+ L: 186,
440
562
  S: this,
441
563
  C: (f, a) => f(...a)
442
564
  });
@@ -455,25 +577,34 @@ var Scheduler = class {
455
577
  trigger
456
578
  }, {
457
579
  F: __dxlog_file3,
458
- L: 180,
580
+ L: 199,
459
581
  S: this,
460
582
  C: (f, a) => f(...a)
461
583
  });
462
- const { port } = trigger;
584
+ const spec = trigger.webhook;
463
585
  const server = http.createServer(async (req, res) => {
464
- await this._execFunction(def, {
465
- space: space.key
586
+ if (req.method !== spec.method) {
587
+ res.statusCode = 405;
588
+ return res.end();
589
+ }
590
+ res.statusCode = await this._execFunction(def, trigger, {
591
+ spaceKey: space.key
466
592
  });
593
+ res.end();
594
+ });
595
+ const port = await getPort2({
596
+ random: true
467
597
  });
468
598
  server.listen(port, () => {
469
599
  log3.info("started webhook", {
470
600
  port
471
601
  }, {
472
602
  F: __dxlog_file3,
473
- L: 189,
603
+ L: 223,
474
604
  S: this,
475
605
  C: (f, a) => f(...a)
476
606
  });
607
+ spec.port = port;
477
608
  });
478
609
  ctx.onDispose(() => {
479
610
  server.close();
@@ -481,6 +612,7 @@ var Scheduler = class {
481
612
  }
482
613
  /**
483
614
  * Websocket.
615
+ * NOTE: The port must be unique, so the same hook cannot be used for multiple spaces.
484
616
  */
485
617
  async _createWebsocket(ctx, space, def, trigger, options = {
486
618
  retryDelay: 2,
@@ -491,11 +623,12 @@ var Scheduler = class {
491
623
  trigger
492
624
  }, {
493
625
  F: __dxlog_file3,
494
- L: 213,
626
+ L: 249,
495
627
  S: this,
496
628
  C: (f, a) => f(...a)
497
629
  });
498
- const { url } = trigger;
630
+ const spec = trigger.websocket;
631
+ const { url, init } = spec;
499
632
  let ws;
500
633
  for (let attempt = 1; attempt <= options.maxAttempts; attempt++) {
501
634
  const open = new Trigger2();
@@ -506,24 +639,38 @@ var Scheduler = class {
506
639
  url
507
640
  }, {
508
641
  F: __dxlog_file3,
509
- L: 223,
642
+ L: 260,
510
643
  S: this,
511
644
  C: (f, a) => f(...a)
512
645
  });
513
- if (trigger.init) {
514
- ws.send(new TextEncoder().encode(JSON.stringify(trigger.init)));
646
+ if (spec.init) {
647
+ ws.send(new TextEncoder().encode(JSON.stringify(init)));
515
648
  }
516
649
  open.wake(true);
517
650
  },
518
- onclose: () => {
651
+ onclose: (event) => {
519
652
  log3.info("closed", {
520
- url
653
+ url,
654
+ code: event.code
521
655
  }, {
522
656
  F: __dxlog_file3,
523
- L: 232,
657
+ L: 269,
524
658
  S: this,
525
659
  C: (f, a) => f(...a)
526
660
  });
661
+ if (event.code === 1006) {
662
+ setTimeout(async () => {
663
+ log3.info(`reconnecting in ${options.retryDelay}s...`, {
664
+ url
665
+ }, {
666
+ F: __dxlog_file3,
667
+ L: 274,
668
+ S: this,
669
+ C: (f, a) => f(...a)
670
+ });
671
+ await this._createWebsocket(ctx, space, def, trigger, options);
672
+ }, options.retryDelay * 1e3);
673
+ }
527
674
  open.wake(false);
528
675
  },
529
676
  onerror: (event) => {
@@ -531,7 +678,7 @@ var Scheduler = class {
531
678
  url
532
679
  }, {
533
680
  F: __dxlog_file3,
534
- L: 237,
681
+ L: 283,
535
682
  S: this,
536
683
  C: (f, a) => f(...a)
537
684
  });
@@ -539,8 +686,8 @@ var Scheduler = class {
539
686
  onmessage: async (event) => {
540
687
  try {
541
688
  const data = JSON.parse(new TextDecoder().decode(event.data));
542
- await this._execFunction(def, {
543
- space: space.key,
689
+ await this._execFunction(def, trigger, {
690
+ spaceKey: space.key,
544
691
  data
545
692
  });
546
693
  } catch (err) {
@@ -548,7 +695,7 @@ var Scheduler = class {
548
695
  url
549
696
  }, {
550
697
  F: __dxlog_file3,
551
- L: 245,
698
+ L: 291,
552
699
  S: this,
553
700
  C: (f, a) => f(...a)
554
701
  });
@@ -565,7 +712,7 @@ var Scheduler = class {
565
712
  attempt
566
713
  }, {
567
714
  F: __dxlog_file3,
568
- L: 256,
715
+ L: 302,
569
716
  S: this,
570
717
  C: (f, a) => f(...a)
571
718
  });
@@ -586,14 +733,15 @@ var Scheduler = class {
586
733
  trigger
587
734
  }, {
588
735
  F: __dxlog_file3,
589
- L: 271,
736
+ L: 317,
590
737
  S: this,
591
738
  C: (f, a) => f(...a)
592
739
  });
740
+ const spec = trigger.subscription;
593
741
  const objectIds = /* @__PURE__ */ new Set();
594
742
  const task = new DeferredTask(ctx, async () => {
595
- await this._execFunction(def, {
596
- space: space.key,
743
+ await this._execFunction(def, trigger, {
744
+ spaceKey: space.key,
597
745
  objects: Array.from(objectIds)
598
746
  });
599
747
  });
@@ -604,7 +752,7 @@ var Scheduler = class {
604
752
  updated: updated.length
605
753
  }, {
606
754
  F: __dxlog_file3,
607
- L: 281,
755
+ L: 329,
608
756
  S: this,
609
757
  C: (f, a) => f(...a)
610
758
  });
@@ -617,7 +765,7 @@ var Scheduler = class {
617
765
  task.schedule();
618
766
  });
619
767
  subscriptions.push(() => subscription.unsubscribe());
620
- const { filter, options: { deep, delay } = {} } = trigger;
768
+ const { filter, options: { deep, delay } = {} } = spec;
621
769
  const update = ({ objects }) => {
622
770
  subscription.update(objects);
623
771
  if (deep) {
@@ -625,7 +773,7 @@ var Scheduler = class {
625
773
  objects: objects.length
626
774
  }, {
627
775
  F: __dxlog_file3,
628
- L: 301,
776
+ L: 349,
629
777
  S: this,
630
778
  C: (f, a) => f(...a)
631
779
  });
@@ -652,9 +800,11 @@ import * as S from "@effect/schema/Schema";
652
800
  var TimerTriggerSchema = S.struct({
653
801
  cron: S.string
654
802
  });
655
- var WebhookTriggerSchema = S.struct({
656
- port: S.number
657
- });
803
+ var WebhookTriggerSchema = S.mutable(S.struct({
804
+ method: S.string,
805
+ // Assigned port.
806
+ port: S.optional(S.number)
807
+ }));
658
808
  var WebsocketTriggerSchema = S.struct({
659
809
  url: S.string,
660
810
  init: S.optional(S.record(S.string, S.any))
@@ -675,6 +825,9 @@ var SubscriptionTriggerSchema = S.struct({
675
825
  });
676
826
  var FunctionTriggerSchema = S.struct({
677
827
  function: S.string.pipe(S.description("Function ID/URI.")),
828
+ // Context passed to function.
829
+ meta: S.optional(S.record(S.string, S.any)),
830
+ // Triggers.
678
831
  timer: S.optional(TimerTriggerSchema),
679
832
  webhook: S.optional(WebhookTriggerSchema),
680
833
  websocket: S.optional(WebsocketTriggerSchema),
@@ -682,14 +835,16 @@ var FunctionTriggerSchema = S.struct({
682
835
  });
683
836
  var FunctionDefSchema = S.struct({
684
837
  id: S.string,
838
+ // name: S.string,
685
839
  description: S.optional(S.string),
686
- name: S.string,
687
- // TODO(burdon): NPM/GitHub URL?
840
+ // TODO(burdon): Rename route?
841
+ path: S.string,
842
+ // TODO(burdon): NPM/GitHub/Docker/CF URL?
688
843
  handler: S.string
689
844
  });
690
845
  var FunctionManifestSchema = S.struct({
691
846
  functions: S.mutable(S.array(FunctionDefSchema)),
692
- triggers: S.mutable(S.array(FunctionTriggerSchema))
847
+ triggers: S.optional(S.mutable(S.array(FunctionTriggerSchema)))
693
848
  });
694
849
  export {
695
850
  DevServer,