@dxos/functions 0.5.3-main.bc67fdb → 0.5.3-main.c8ad1bb

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/lib/browser/index.mjs +642 -424
  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 +627 -421
  5. package/dist/lib/node/index.cjs.map +4 -4
  6. package/dist/lib/node/meta.json +1 -1
  7. package/dist/types/src/index.d.ts +2 -0
  8. package/dist/types/src/index.d.ts.map +1 -1
  9. package/dist/types/src/registry/function-registry.d.ts +24 -0
  10. package/dist/types/src/registry/function-registry.d.ts.map +1 -0
  11. package/dist/types/src/registry/function-registry.test.d.ts +2 -0
  12. package/dist/types/src/registry/function-registry.test.d.ts.map +1 -0
  13. package/dist/types/src/registry/index.d.ts +2 -0
  14. package/dist/types/src/registry/index.d.ts.map +1 -0
  15. package/dist/types/src/runtime/dev-server.d.ts +7 -10
  16. package/dist/types/src/runtime/dev-server.d.ts.map +1 -1
  17. package/dist/types/src/runtime/scheduler.d.ts +10 -59
  18. package/dist/types/src/runtime/scheduler.d.ts.map +1 -1
  19. package/dist/types/src/testing/functions-integration.test.d.ts +2 -0
  20. package/dist/types/src/testing/functions-integration.test.d.ts.map +1 -0
  21. package/dist/types/src/testing/index.d.ts +4 -0
  22. package/dist/types/src/testing/index.d.ts.map +1 -0
  23. package/dist/types/src/testing/setup.d.ts +5 -0
  24. package/dist/types/src/testing/setup.d.ts.map +1 -0
  25. package/dist/types/src/testing/test/handler.d.ts +1 -0
  26. package/dist/types/src/testing/test/handler.d.ts.map +1 -1
  27. package/dist/types/src/testing/types.d.ts +9 -0
  28. package/dist/types/src/testing/types.d.ts.map +1 -0
  29. package/dist/types/src/testing/util.d.ts +3 -0
  30. package/dist/types/src/testing/util.d.ts.map +1 -0
  31. package/dist/types/src/trigger/index.d.ts +2 -0
  32. package/dist/types/src/trigger/index.d.ts.map +1 -0
  33. package/dist/types/src/trigger/trigger-registry.d.ts +40 -0
  34. package/dist/types/src/trigger/trigger-registry.d.ts.map +1 -0
  35. package/dist/types/src/trigger/trigger-registry.test.d.ts +2 -0
  36. package/dist/types/src/trigger/trigger-registry.test.d.ts.map +1 -0
  37. package/dist/types/src/trigger/type/index.d.ts +5 -0
  38. package/dist/types/src/trigger/type/index.d.ts.map +1 -0
  39. package/dist/types/src/trigger/type/subscription-trigger.d.ts +4 -0
  40. package/dist/types/src/trigger/type/subscription-trigger.d.ts.map +1 -0
  41. package/dist/types/src/trigger/type/timer-trigger.d.ts +4 -0
  42. package/dist/types/src/trigger/type/timer-trigger.d.ts.map +1 -0
  43. package/dist/types/src/trigger/type/webhook-trigger.d.ts +4 -0
  44. package/dist/types/src/trigger/type/webhook-trigger.d.ts.map +1 -0
  45. package/dist/types/src/trigger/type/websocket-trigger.d.ts +13 -0
  46. package/dist/types/src/trigger/type/websocket-trigger.d.ts.map +1 -0
  47. package/dist/types/src/types.d.ts +118 -112
  48. package/dist/types/src/types.d.ts.map +1 -1
  49. package/package.json +16 -13
  50. package/schema/functions.json +121 -107
  51. package/src/index.ts +2 -0
  52. package/src/registry/function-registry.test.ts +105 -0
  53. package/src/registry/function-registry.ts +84 -0
  54. package/src/registry/index.ts +5 -0
  55. package/src/runtime/dev-server.test.ts +15 -35
  56. package/src/runtime/dev-server.ts +36 -18
  57. package/src/runtime/scheduler.test.ts +54 -75
  58. package/src/runtime/scheduler.ts +58 -298
  59. package/src/testing/functions-integration.test.ts +99 -0
  60. package/src/testing/index.ts +7 -0
  61. package/src/testing/setup.ts +45 -0
  62. package/src/testing/test/handler.ts +8 -2
  63. package/src/testing/types.ts +9 -0
  64. package/src/testing/util.ts +16 -0
  65. package/src/trigger/index.ts +5 -0
  66. package/src/trigger/trigger-registry.test.ts +229 -0
  67. package/src/trigger/trigger-registry.ts +176 -0
  68. package/src/trigger/type/index.ts +8 -0
  69. package/src/trigger/type/subscription-trigger.ts +73 -0
  70. package/src/trigger/type/timer-trigger.ts +44 -0
  71. package/src/trigger/type/webhook-trigger.ts +47 -0
  72. package/src/trigger/type/websocket-trigger.ts +91 -0
  73. package/src/types.ts +56 -42
@@ -58,22 +58,170 @@ var subscriptionHandler = (handler) => {
58
58
  };
59
59
  };
60
60
 
61
+ // packages/core/functions/src/registry/function-registry.ts
62
+ import { Event } from "@dxos/async";
63
+ import { create, Filter } from "@dxos/client/echo";
64
+ import { Resource } from "@dxos/context";
65
+ import { PublicKey as PublicKey2 } from "@dxos/keys";
66
+ import { ComplexMap } from "@dxos/util";
67
+
68
+ // packages/core/functions/src/types.ts
69
+ import { AST, S, TypedObject } from "@dxos/echo-schema";
70
+ var omitEchoId = (schema) => S.make(AST.omit(schema.ast, [
71
+ "id"
72
+ ]));
73
+ var SubscriptionTriggerSchema = S.struct({
74
+ type: S.literal("subscription"),
75
+ // TODO(burdon): Define query DSL.
76
+ filter: S.array(S.struct({
77
+ type: S.string,
78
+ props: S.optional(S.record(S.string, S.any))
79
+ })),
80
+ options: S.optional(S.struct({
81
+ // Watch changes to object (not just creation).
82
+ deep: S.optional(S.boolean),
83
+ // Debounce changes (delay in ms).
84
+ delay: S.optional(S.number)
85
+ }))
86
+ });
87
+ var TimerTriggerSchema = S.struct({
88
+ type: S.literal("timer"),
89
+ cron: S.string
90
+ });
91
+ var WebhookTriggerSchema = S.mutable(S.struct({
92
+ type: S.literal("webhook"),
93
+ method: S.string,
94
+ // Assigned port.
95
+ port: S.optional(S.number)
96
+ }));
97
+ var WebsocketTriggerSchema = S.struct({
98
+ type: S.literal("websocket"),
99
+ url: S.string,
100
+ init: S.optional(S.record(S.string, S.any))
101
+ });
102
+ var TriggerSpecSchema = S.union(TimerTriggerSchema, WebhookTriggerSchema, WebsocketTriggerSchema, SubscriptionTriggerSchema);
103
+ var FunctionDef = class extends TypedObject({
104
+ typename: "dxos.org/type/FunctionDef",
105
+ version: "0.1.0"
106
+ })({
107
+ uri: S.string,
108
+ description: S.optional(S.string),
109
+ route: S.string,
110
+ // TODO(burdon): NPM/GitHub/Docker/CF URL?
111
+ handler: S.string
112
+ }) {
113
+ };
114
+ var FunctionTrigger = class extends TypedObject({
115
+ typename: "dxos.org/type/FunctionTrigger",
116
+ version: "0.1.0"
117
+ })({
118
+ function: S.string.pipe(S.description("Function ID/URI.")),
119
+ // Context passed to a function.
120
+ meta: S.optional(S.record(S.string, S.any)),
121
+ spec: TriggerSpecSchema
122
+ }) {
123
+ };
124
+ var FunctionManifestSchema = S.struct({
125
+ functions: S.optional(S.mutable(S.array(omitEchoId(FunctionDef)))),
126
+ triggers: S.optional(S.mutable(S.array(omitEchoId(FunctionTrigger))))
127
+ });
128
+
129
+ // packages/core/functions/src/registry/function-registry.ts
130
+ var FunctionRegistry = class extends Resource {
131
+ constructor(_client) {
132
+ super();
133
+ this._client = _client;
134
+ this._functionBySpaceKey = new ComplexMap(PublicKey2.hash);
135
+ this.onFunctionsRegistered = new Event();
136
+ }
137
+ getFunctions(space) {
138
+ return this._functionBySpaceKey.get(space.key) ?? [];
139
+ }
140
+ /**
141
+ * The method loads function definitions from the manifest into the space.
142
+ * We first load all the definitions from the space to deduplicate by functionId.
143
+ */
144
+ // TODO(burdon): This should not be space specific (they are static for the agent).
145
+ async register(space, manifest) {
146
+ if (!manifest.functions?.length) {
147
+ return;
148
+ }
149
+ if (!space.db.graph.runtimeSchemaRegistry.hasSchema(FunctionDef)) {
150
+ space.db.graph.runtimeSchemaRegistry.registerSchema(FunctionDef);
151
+ }
152
+ const { objects: existingDefinitions } = await space.db.query(Filter.schema(FunctionDef)).run();
153
+ const newDefinitions = getNewDefinitions(manifest.functions, existingDefinitions);
154
+ const reactiveObjects = newDefinitions.map((template) => create(FunctionDef, {
155
+ ...template
156
+ }));
157
+ reactiveObjects.forEach((obj) => space.db.add(obj));
158
+ }
159
+ async _open() {
160
+ const spaceListSubscription = this._client.spaces.subscribe(async (spaces) => {
161
+ for (const space of spaces) {
162
+ if (this._functionBySpaceKey.has(space.key)) {
163
+ continue;
164
+ }
165
+ const registered = [];
166
+ this._functionBySpaceKey.set(space.key, registered);
167
+ await space.waitUntilReady();
168
+ if (this._ctx.disposed) {
169
+ break;
170
+ }
171
+ const functionsSubscription = space.db.query(Filter.schema(FunctionDef)).subscribe((definitions) => {
172
+ const newFunctions = getNewDefinitions(definitions.objects, registered);
173
+ if (newFunctions.length > 0) {
174
+ registered.push(...newFunctions);
175
+ this.onFunctionsRegistered.emit({
176
+ space,
177
+ newFunctions
178
+ });
179
+ }
180
+ });
181
+ this._ctx.onDispose(functionsSubscription);
182
+ }
183
+ });
184
+ this._ctx.onDispose(() => spaceListSubscription.unsubscribe());
185
+ }
186
+ async _close(_) {
187
+ this._functionBySpaceKey.clear();
188
+ }
189
+ };
190
+ var getNewDefinitions = (candidateList, existing) => {
191
+ return candidateList.filter((candidate) => existing.find((def) => def.uri === candidate.uri) == null);
192
+ };
193
+
61
194
  // packages/core/functions/src/runtime/dev-server.ts
62
195
  import express from "express";
63
196
  import { getPort } from "get-port-please";
64
197
  import { join } from "@dxos/node-std/path";
65
- import { Event, Trigger } from "@dxos/async";
198
+ import { Event as Event2, Trigger } from "@dxos/async";
199
+ import { Context } from "@dxos/context";
66
200
  import { invariant } from "@dxos/invariant";
67
201
  import { log as log2 } from "@dxos/log";
68
202
  var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/dev-server.ts";
69
203
  var DevServer = class {
70
204
  // prettier-ignore
71
- constructor(_client, _options) {
205
+ constructor(_client, _functionsRegistry, _options) {
72
206
  this._client = _client;
207
+ this._functionsRegistry = _functionsRegistry;
73
208
  this._options = _options;
209
+ this._ctx = createContext();
74
210
  this._handlers = {};
75
211
  this._seq = 0;
76
- this.update = new Event();
212
+ this.update = new Event2();
213
+ this._functionsRegistry.onFunctionsRegistered.on(async ({ newFunctions }) => {
214
+ newFunctions.forEach((def) => this._load(def));
215
+ await this._safeUpdateRegistration();
216
+ log2("new functions loaded", {
217
+ newFunctions
218
+ }, {
219
+ F: __dxlog_file2,
220
+ L: 53,
221
+ S: this,
222
+ C: (f, a) => f(...a)
223
+ });
224
+ });
77
225
  }
78
226
  get stats() {
79
227
  return {
@@ -83,7 +231,7 @@ var DevServer = class {
83
231
  get endpoint() {
84
232
  invariant(this._port, void 0, {
85
233
  F: __dxlog_file2,
86
- L: 54,
234
+ L: 64,
87
235
  S: this,
88
236
  A: [
89
237
  "this._port",
@@ -98,20 +246,6 @@ var DevServer = class {
98
246
  get functions() {
99
247
  return Object.values(this._handlers);
100
248
  }
101
- async initialize() {
102
- for (const def of this._options.manifest.functions) {
103
- try {
104
- await this._load(def);
105
- } catch (err) {
106
- log2.error("parsing function (check manifest)", err, {
107
- F: __dxlog_file2,
108
- L: 71,
109
- S: this,
110
- C: (f, a) => f(...a)
111
- });
112
- }
113
- }
114
- }
115
249
  async start() {
116
250
  invariant(!this._server, void 0, {
117
251
  F: __dxlog_file2,
@@ -128,6 +262,7 @@ var DevServer = class {
128
262
  S: this,
129
263
  C: (f, a) => f(...a)
130
264
  });
265
+ this._ctx = createContext();
131
266
  const app = express();
132
267
  app.use(express.json());
133
268
  app.post("/:path", async (req, res) => {
@@ -137,7 +272,7 @@ var DevServer = class {
137
272
  path: path2
138
273
  }, {
139
274
  F: __dxlog_file2,
140
- L: 87,
275
+ L: 88,
141
276
  S: this,
142
277
  C: (f, a) => f(...a)
143
278
  });
@@ -150,7 +285,7 @@ var DevServer = class {
150
285
  } catch (err) {
151
286
  log2.catch(err, void 0, {
152
287
  F: __dxlog_file2,
153
- L: 97,
288
+ L: 98,
154
289
  S: this,
155
290
  C: (f, a) => f(...a)
156
291
  });
@@ -169,11 +304,7 @@ var DevServer = class {
169
304
  this._server = app.listen(this._port);
170
305
  try {
171
306
  const { registrationId, endpoint } = await this._client.services.services.FunctionRegistryService.register({
172
- endpoint: this.endpoint,
173
- functions: this.functions.map(({ def: { id, path: path2 } }) => ({
174
- id,
175
- path: path2
176
- }))
307
+ endpoint: this.endpoint
177
308
  });
178
309
  log2.info("registered", {
179
310
  endpoint
@@ -185,6 +316,7 @@ var DevServer = class {
185
316
  });
186
317
  this._proxy = endpoint;
187
318
  this._functionServiceRegistration = registrationId;
319
+ await this._functionsRegistry.open(this._ctx);
188
320
  } catch (err) {
189
321
  await this.stop();
190
322
  throw new Error("FunctionRegistryService not available (check plugin is configured).");
@@ -193,7 +325,7 @@ var DevServer = class {
193
325
  port: this._port
194
326
  }, {
195
327
  F: __dxlog_file2,
196
- L: 121,
328
+ L: 124,
197
329
  S: this,
198
330
  C: (f, a) => f(...a)
199
331
  });
@@ -201,7 +333,7 @@ var DevServer = class {
201
333
  async stop() {
202
334
  invariant(this._server, void 0, {
203
335
  F: __dxlog_file2,
204
- L: 125,
336
+ L: 128,
205
337
  S: this,
206
338
  A: [
207
339
  "this._server",
@@ -210,7 +342,7 @@ var DevServer = class {
210
342
  });
211
343
  log2.info("stopping...", void 0, {
212
344
  F: __dxlog_file2,
213
- L: 126,
345
+ L: 129,
214
346
  S: this,
215
347
  C: (f, a) => f(...a)
216
348
  });
@@ -218,7 +350,7 @@ var DevServer = class {
218
350
  this._server.close(async () => {
219
351
  log2.info("server stopped", void 0, {
220
352
  F: __dxlog_file2,
221
- L: 130,
353
+ L: 133,
222
354
  S: this,
223
355
  C: (f, a) => f(...a)
224
356
  });
@@ -226,7 +358,7 @@ var DevServer = class {
226
358
  if (this._functionServiceRegistration) {
227
359
  invariant(this._client.services.services.FunctionRegistryService, void 0, {
228
360
  F: __dxlog_file2,
229
- L: 133,
361
+ L: 136,
230
362
  S: this,
231
363
  A: [
232
364
  "this._client.services.services.FunctionRegistryService",
@@ -240,7 +372,7 @@ var DevServer = class {
240
372
  registrationId: this._functionServiceRegistration
241
373
  }, {
242
374
  F: __dxlog_file2,
243
- L: 138,
375
+ L: 141,
244
376
  S: this,
245
377
  C: (f, a) => f(...a)
246
378
  });
@@ -257,7 +389,7 @@ var DevServer = class {
257
389
  this._server = void 0;
258
390
  log2.info("stopped", void 0, {
259
391
  F: __dxlog_file2,
260
- L: 152,
392
+ L: 155,
261
393
  S: this,
262
394
  C: (f, a) => f(...a)
263
395
  });
@@ -266,14 +398,14 @@ var DevServer = class {
266
398
  * Load function.
267
399
  */
268
400
  async _load(def, force = false) {
269
- const { id, path: path2, handler } = def;
401
+ const { uri, route, handler } = def;
270
402
  const filePath = join(this._options.baseDir, handler);
271
403
  log2.info("loading", {
272
- id,
404
+ uri,
273
405
  force
274
406
  }, {
275
407
  F: __dxlog_file2,
276
- L: 161,
408
+ L: 164,
277
409
  S: this,
278
410
  C: (f, a) => f(...a)
279
411
  });
@@ -284,13 +416,40 @@ var DevServer = class {
284
416
  }
285
417
  const module = __require(filePath);
286
418
  if (typeof module.default !== "function") {
287
- throw new Error(`Handler must export default function: ${id}`);
419
+ throw new Error(`Handler must export default function: ${uri}`);
288
420
  }
289
- this._handlers[path2] = {
421
+ this._handlers[route] = {
290
422
  def,
291
423
  handler: module.default
292
424
  };
293
425
  }
426
+ async _safeUpdateRegistration() {
427
+ invariant(this._functionServiceRegistration, void 0, {
428
+ F: __dxlog_file2,
429
+ L: 186,
430
+ S: this,
431
+ A: [
432
+ "this._functionServiceRegistration",
433
+ ""
434
+ ]
435
+ });
436
+ try {
437
+ await this._client.services.services.FunctionRegistryService.updateRegistration({
438
+ registrationId: this._functionServiceRegistration,
439
+ functions: this.functions.map(({ def: { id, route } }) => ({
440
+ id,
441
+ route
442
+ }))
443
+ });
444
+ } catch (e) {
445
+ log2.catch(e, void 0, {
446
+ F: __dxlog_file2,
447
+ L: 193,
448
+ S: this,
449
+ C: (f, a) => f(...a)
450
+ });
451
+ }
452
+ }
294
453
  /**
295
454
  * Invoke function.
296
455
  */
@@ -302,7 +461,7 @@ var DevServer = class {
302
461
  path: path2
303
462
  }, {
304
463
  F: __dxlog_file2,
305
- L: 188,
464
+ L: 204,
306
465
  S: this,
307
466
  C: (f, a) => f(...a)
308
467
  });
@@ -316,7 +475,7 @@ var DevServer = class {
316
475
  duration: Date.now() - now
317
476
  }, {
318
477
  F: __dxlog_file2,
319
- L: 191,
478
+ L: 207,
320
479
  S: this,
321
480
  C: (f, a) => f(...a)
322
481
  });
@@ -327,7 +486,7 @@ var DevServer = class {
327
486
  const { handler } = this._handlers[path2] ?? {};
328
487
  invariant(handler, `invalid path: ${path2}`, {
329
488
  F: __dxlog_file2,
330
- L: 198,
489
+ L: 214,
331
490
  S: this,
332
491
  A: [
333
492
  "handler",
@@ -353,125 +512,98 @@ var DevServer = class {
353
512
  return statusCode;
354
513
  }
355
514
  };
515
+ var createContext = () => new Context({
516
+ name: "DevServer"
517
+ });
356
518
 
357
519
  // packages/core/functions/src/runtime/scheduler.ts
358
- import { CronJob } from "cron";
359
- import { getPort as getPort2 } from "get-port-please";
360
- import http from "@dxos/node-std/http";
361
520
  import path from "@dxos/node-std/path";
362
- import WebSocket from "ws";
363
- import { TextV0Type } from "@braneframe/types";
364
- import { debounce, DeferredTask, sleep, Trigger as Trigger2 } from "@dxos/async";
365
- import { createSubscription, Filter, getAutomergeObjectCore } from "@dxos/client/echo";
366
- import { Context } from "@dxos/context";
367
- import { invariant as invariant2 } from "@dxos/invariant";
521
+ import { Context as Context2 } from "@dxos/context";
368
522
  import { log as log3 } from "@dxos/log";
369
- import { ComplexMap } from "@dxos/util";
370
523
  var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/scheduler.ts";
371
524
  var Scheduler = class {
372
- constructor(_client, _manifest, _options = {}) {
373
- this._client = _client;
374
- this._manifest = _manifest;
525
+ constructor(functions, triggers, _options = {}) {
526
+ this.functions = functions;
527
+ this.triggers = triggers;
375
528
  this._options = _options;
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
- }, []);
529
+ this._ctx = createContext2();
530
+ this.functions.onFunctionsRegistered.on(async ({ space, newFunctions }) => {
531
+ await this._safeActivateTriggers(space, this.triggers.getInactiveTriggers(space), newFunctions);
532
+ });
533
+ this.triggers.registered.on(async ({ space, triggers: triggers2 }) => {
534
+ await this._safeActivateTriggers(space, triggers2, this.functions.getFunctions(space));
535
+ });
383
536
  }
384
537
  async start() {
385
- this._client.spaces.subscribe(async (spaces) => {
386
- for (const space of spaces) {
387
- await space.waitUntilReady();
388
- for (const trigger of this._manifest.triggers ?? []) {
389
- await this.mount(new Context(), space, trigger);
390
- }
391
- }
392
- });
538
+ await this._ctx.dispose();
539
+ this._ctx = createContext2();
540
+ await this.functions.open(this._ctx);
541
+ await this.triggers.open(this._ctx);
393
542
  }
394
543
  async stop() {
395
- for (const { id, spaceKey } of this._mounts.keys()) {
396
- await this.unmount(id, spaceKey);
397
- }
544
+ await this._ctx.dispose();
545
+ await this.functions.close();
546
+ await this.triggers.close();
398
547
  }
399
- /**
400
- * Mount trigger.
401
- */
402
- async mount(ctx, space, trigger) {
403
- const key = {
404
- spaceKey: space.key,
405
- id: trigger.function
406
- };
407
- const def = this._manifest.functions.find((config) => config.id === trigger.function);
408
- invariant2(def, `Function not found: ${trigger.function}`, {
409
- F: __dxlog_file3,
410
- L: 76,
411
- S: this,
412
- A: [
413
- "def",
414
- "`Function not found: ${trigger.function}`"
415
- ]
548
+ async register(space, manifest) {
549
+ await this.functions.register(space, manifest);
550
+ await this.triggers.register(space, manifest);
551
+ }
552
+ async _safeActivateTriggers(space, triggers, functions) {
553
+ const mountTasks = triggers.map((trigger) => {
554
+ return this.activate(space, functions, trigger);
416
555
  });
417
- const exists = this._mounts.get(key);
418
- if (!exists) {
419
- this._mounts.set(key, {
420
- ctx,
421
- trigger
422
- });
423
- log3("mount", {
424
- space: space.key,
425
- trigger
556
+ await Promise.all(mountTasks).catch(log3.catch);
557
+ }
558
+ async activate(space, functions, fnTrigger) {
559
+ const definition = functions.find((def) => def.uri === fnTrigger.function);
560
+ if (!definition) {
561
+ log3.info("function is not found for trigger", {
562
+ fnTrigger
426
563
  }, {
427
564
  F: __dxlog_file3,
428
- L: 82,
565
+ L: 74,
429
566
  S: this,
430
567
  C: (f, a) => f(...a)
431
568
  });
432
- if (ctx.disposed) {
433
- return;
434
- }
435
- if (trigger.timer) {
436
- await this._createTimer(ctx, space, def, trigger);
437
- }
438
- if (trigger.webhook) {
439
- await this._createWebhook(ctx, space, def, trigger);
440
- }
441
- if (trigger.websocket) {
442
- await this._createWebsocket(ctx, space, def, trigger);
443
- }
444
- if (trigger.subscription) {
445
- await this._createSubscription(ctx, space, def, trigger);
446
- }
447
- }
448
- }
449
- async unmount(id, spaceKey) {
450
- const key = {
451
- id,
452
- spaceKey
453
- };
454
- const { ctx } = this._mounts.get(key) ?? {};
455
- if (ctx) {
456
- this._mounts.delete(key);
457
- await ctx.dispose();
569
+ return;
458
570
  }
571
+ await this.triggers.activate({
572
+ space
573
+ }, fnTrigger, async (args) => {
574
+ return this._execFunction(definition, {
575
+ meta: fnTrigger.meta,
576
+ data: {
577
+ ...args,
578
+ spaceKey: space.key
579
+ }
580
+ });
581
+ });
582
+ log3("activated trigger", {
583
+ space: space.key,
584
+ trigger: fnTrigger
585
+ }, {
586
+ F: __dxlog_file3,
587
+ L: 84,
588
+ S: this,
589
+ C: (f, a) => f(...a)
590
+ });
459
591
  }
460
- async _execFunction(def, trigger, data) {
592
+ async _execFunction(def, { data, meta }) {
461
593
  let status = 0;
462
594
  try {
463
- const payload = Object.assign({}, {
464
- meta: trigger.meta
595
+ const payload = Object.assign({}, meta && {
596
+ meta
465
597
  }, data);
466
598
  const { endpoint, callback } = this._options;
467
599
  if (endpoint) {
468
- const url = path.join(endpoint, def.path);
600
+ const url = path.join(endpoint, def.route);
469
601
  log3.info("exec", {
470
- function: def.id,
602
+ function: def.uri,
471
603
  url
472
604
  }, {
473
605
  F: __dxlog_file3,
474
- L: 128,
606
+ L: 100,
475
607
  S: this,
476
608
  C: (f, a) => f(...a)
477
609
  });
@@ -485,10 +617,10 @@ var Scheduler = class {
485
617
  status = response.status;
486
618
  } else if (callback) {
487
619
  log3.info("exec", {
488
- function: def.id
620
+ function: def.uri
489
621
  }, {
490
622
  F: __dxlog_file3,
491
- L: 139,
623
+ L: 111,
492
624
  S: this,
493
625
  C: (f, a) => f(...a)
494
626
  });
@@ -498,21 +630,21 @@ var Scheduler = class {
498
630
  throw new Error(`Response: ${status}`);
499
631
  }
500
632
  log3.info("done", {
501
- function: def.id,
633
+ function: def.uri,
502
634
  status
503
635
  }, {
504
636
  F: __dxlog_file3,
505
- L: 149,
637
+ L: 121,
506
638
  S: this,
507
639
  C: (f, a) => f(...a)
508
640
  });
509
641
  } catch (err) {
510
642
  log3.error("error", {
511
- function: def.id,
643
+ function: def.uri,
512
644
  error: err.message
513
645
  }, {
514
646
  F: __dxlog_file3,
515
- L: 151,
647
+ L: 123,
516
648
  S: this,
517
649
  C: (f, a) => f(...a)
518
650
  });
@@ -520,336 +652,422 @@ var Scheduler = class {
520
652
  }
521
653
  return status;
522
654
  }
523
- //
524
- // Triggers
525
- //
526
- /**
527
- * Cron timer.
528
- */
529
- async _createTimer(ctx, space, def, trigger) {
530
- log3.info("timer", {
531
- space: space.key,
532
- trigger
655
+ };
656
+ var createContext2 = () => new Context2({
657
+ name: "FunctionScheduler"
658
+ });
659
+
660
+ // packages/core/functions/src/trigger/trigger-registry.ts
661
+ import { Event as Event3 } from "@dxos/async";
662
+ import { create as create2, Filter as Filter3 } from "@dxos/client/echo";
663
+ import { Context as Context3, Resource as Resource2 } from "@dxos/context";
664
+ import { invariant as invariant2 } from "@dxos/invariant";
665
+ import { PublicKey as PublicKey3 } from "@dxos/keys";
666
+ import { log as log8 } from "@dxos/log";
667
+ import { ComplexMap as ComplexMap2 } from "@dxos/util";
668
+
669
+ // packages/core/functions/src/trigger/type/subscription-trigger.ts
670
+ import { TextV0Type } from "@braneframe/types";
671
+ import { debounce, DeferredTask } from "@dxos/async";
672
+ import { createSubscription, Filter as Filter2, getAutomergeObjectCore } from "@dxos/echo-db";
673
+ import { log as log4 } from "@dxos/log";
674
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/functions/src/trigger/type/subscription-trigger.ts";
675
+ var createSubscriptionTrigger = async (ctx, triggerCtx, spec, callback) => {
676
+ const objectIds = /* @__PURE__ */ new Set();
677
+ const task = new DeferredTask(ctx, async () => {
678
+ if (objectIds.size > 0) {
679
+ await callback({
680
+ objects: Array.from(objectIds)
681
+ });
682
+ objectIds.clear();
683
+ }
684
+ });
685
+ const subscriptions = [];
686
+ const subscription = createSubscription(({ added, updated }) => {
687
+ log4.info("updated", {
688
+ added: added.length,
689
+ updated: updated.length
533
690
  }, {
534
- F: __dxlog_file3,
535
- L: 166,
536
- S: this,
691
+ F: __dxlog_file4,
692
+ L: 32,
693
+ S: void 0,
537
694
  C: (f, a) => f(...a)
538
695
  });
539
- const spec = trigger.timer;
540
- const task = new DeferredTask(ctx, async () => {
541
- await this._execFunction(def, trigger, {
542
- spaceKey: space.key
696
+ for (const object of added) {
697
+ objectIds.add(object.id);
698
+ }
699
+ for (const object of updated) {
700
+ objectIds.add(object.id);
701
+ }
702
+ task.schedule();
703
+ });
704
+ subscriptions.push(() => subscription.unsubscribe());
705
+ const { filter, options: { deep, delay } = {} } = spec;
706
+ const update = ({ objects }) => {
707
+ subscription.update(objects);
708
+ if (deep) {
709
+ log4.info("update", {
710
+ objects: objects.length
711
+ }, {
712
+ F: __dxlog_file4,
713
+ L: 52,
714
+ S: void 0,
715
+ C: (f, a) => f(...a)
716
+ });
717
+ for (const object of objects) {
718
+ const content = object.content;
719
+ if (content instanceof TextV0Type) {
720
+ subscriptions.push(getAutomergeObjectCore(content).updates.on(debounce(() => subscription.update([
721
+ object
722
+ ]), 1e3)));
723
+ }
724
+ }
725
+ }
726
+ };
727
+ const query = triggerCtx.space.db.query(Filter2.or(filter.map(({ type, props }) => Filter2.typename(type, props))));
728
+ subscriptions.push(query.subscribe(delay ? debounce(update, delay) : update));
729
+ ctx.onDispose(() => {
730
+ subscriptions.forEach((unsubscribe) => unsubscribe());
731
+ });
732
+ };
733
+
734
+ // packages/core/functions/src/trigger/type/timer-trigger.ts
735
+ import { CronJob } from "cron";
736
+ import { DeferredTask as DeferredTask2 } from "@dxos/async";
737
+ import { log as log5 } from "@dxos/log";
738
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/functions/src/trigger/type/timer-trigger.ts";
739
+ var createTimerTrigger = async (ctx, triggerContext, spec, callback) => {
740
+ const task = new DeferredTask2(ctx, async () => {
741
+ await callback({});
742
+ });
743
+ let last = 0;
744
+ let run = 0;
745
+ const job = CronJob.from({
746
+ cronTime: spec.cron,
747
+ runOnInit: false,
748
+ onTick: () => {
749
+ const now = Date.now();
750
+ const delta = last ? now - last : 0;
751
+ last = now;
752
+ run++;
753
+ log5.info("tick", {
754
+ space: triggerContext.space.key.truncate(),
755
+ count: run,
756
+ delta
757
+ }, {
758
+ F: __dxlog_file5,
759
+ L: 37,
760
+ S: void 0,
761
+ C: (f, a) => f(...a)
543
762
  });
763
+ task.schedule();
764
+ }
765
+ });
766
+ job.start();
767
+ ctx.onDispose(() => job.stop());
768
+ };
769
+
770
+ // packages/core/functions/src/trigger/type/webhook-trigger.ts
771
+ import { getPort as getPort2 } from "get-port-please";
772
+ import http from "@dxos/node-std/http";
773
+ import { log as log6 } from "@dxos/log";
774
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/core/functions/src/trigger/type/webhook-trigger.ts";
775
+ var createWebhookTrigger = async (ctx, _, spec, callback) => {
776
+ const server = http.createServer(async (req, res) => {
777
+ if (req.method !== spec.method) {
778
+ res.statusCode = 405;
779
+ return res.end();
780
+ }
781
+ res.statusCode = await callback({});
782
+ res.end();
783
+ });
784
+ const port = await getPort2({
785
+ random: true
786
+ });
787
+ server.listen(port, () => {
788
+ log6.info("started webhook", {
789
+ port
790
+ }, {
791
+ F: __dxlog_file6,
792
+ L: 40,
793
+ S: void 0,
794
+ C: (f, a) => f(...a)
544
795
  });
545
- let last = 0;
546
- let run = 0;
547
- const job = CronJob.from({
548
- cronTime: spec.cron,
549
- runOnInit: false,
550
- onTick: () => {
551
- const now = Date.now();
552
- const delta = last ? now - last : 0;
553
- last = now;
554
- run++;
555
- log3.info("tick", {
556
- space: space.key.truncate(),
557
- count: run,
558
- delta
796
+ spec.port = port;
797
+ });
798
+ ctx.onDispose(() => {
799
+ server.close();
800
+ });
801
+ };
802
+
803
+ // packages/core/functions/src/trigger/type/websocket-trigger.ts
804
+ import WebSocket from "ws";
805
+ import { sleep, Trigger as Trigger2 } from "@dxos/async";
806
+ import { log as log7 } from "@dxos/log";
807
+ var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/core/functions/src/trigger/type/websocket-trigger.ts";
808
+ var createWebsocketTrigger = async (ctx, triggerCtx, spec, callback, options = {
809
+ retryDelay: 2,
810
+ maxAttempts: 5
811
+ }) => {
812
+ const { url, init } = spec;
813
+ let ws;
814
+ for (let attempt = 1; attempt <= options.maxAttempts; attempt++) {
815
+ const open = new Trigger2();
816
+ ws = new WebSocket(url);
817
+ Object.assign(ws, {
818
+ onopen: () => {
819
+ log7.info("opened", {
820
+ url
559
821
  }, {
560
- F: __dxlog_file3,
561
- L: 186,
562
- S: this,
822
+ F: __dxlog_file7,
823
+ L: 39,
824
+ S: void 0,
563
825
  C: (f, a) => f(...a)
564
826
  });
565
- task.schedule();
827
+ if (spec.init) {
828
+ ws.send(new TextEncoder().encode(JSON.stringify(init)));
829
+ }
830
+ open.wake(true);
831
+ },
832
+ onclose: (event) => {
833
+ log7.info("closed", {
834
+ url,
835
+ code: event.code
836
+ }, {
837
+ F: __dxlog_file7,
838
+ L: 48,
839
+ S: void 0,
840
+ C: (f, a) => f(...a)
841
+ });
842
+ if (event.code === 1006) {
843
+ setTimeout(async () => {
844
+ log7.info(`reconnecting in ${options.retryDelay}s...`, {
845
+ url
846
+ }, {
847
+ F: __dxlog_file7,
848
+ L: 53,
849
+ S: void 0,
850
+ C: (f, a) => f(...a)
851
+ });
852
+ await createWebsocketTrigger(ctx, triggerCtx, spec, callback, options);
853
+ }, options.retryDelay * 1e3);
854
+ }
855
+ open.wake(false);
856
+ },
857
+ onerror: (event) => {
858
+ log7.catch(event.error, {
859
+ url
860
+ }, {
861
+ F: __dxlog_file7,
862
+ L: 62,
863
+ S: void 0,
864
+ C: (f, a) => f(...a)
865
+ });
866
+ },
867
+ onmessage: async (event) => {
868
+ try {
869
+ log7.info("message", void 0, {
870
+ F: __dxlog_file7,
871
+ L: 67,
872
+ S: void 0,
873
+ C: (f, a) => f(...a)
874
+ });
875
+ const data = JSON.parse(new TextDecoder().decode(event.data));
876
+ await callback({
877
+ data
878
+ });
879
+ } catch (err) {
880
+ log7.catch(err, {
881
+ url
882
+ }, {
883
+ F: __dxlog_file7,
884
+ L: 71,
885
+ S: void 0,
886
+ C: (f, a) => f(...a)
887
+ });
888
+ }
566
889
  }
567
890
  });
568
- job.start();
569
- ctx.onDispose(() => job.stop());
891
+ const isOpen = await open.wait();
892
+ if (isOpen) {
893
+ break;
894
+ } else {
895
+ const wait = Math.pow(attempt, 2) * options.retryDelay;
896
+ if (attempt < options.maxAttempts) {
897
+ log7.warn(`failed to connect; trying again in ${wait}s`, {
898
+ attempt
899
+ }, {
900
+ F: __dxlog_file7,
901
+ L: 82,
902
+ S: void 0,
903
+ C: (f, a) => f(...a)
904
+ });
905
+ await sleep(wait * 1e3);
906
+ }
907
+ }
570
908
  }
571
- /**
572
- * Webhook.
573
- */
574
- async _createWebhook(ctx, space, def, trigger) {
575
- log3.info("webhook", {
576
- space: space.key,
909
+ ctx.onDispose(() => {
910
+ ws?.close();
911
+ });
912
+ };
913
+
914
+ // packages/core/functions/src/trigger/trigger-registry.ts
915
+ var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/core/functions/src/trigger/trigger-registry.ts";
916
+ var triggerHandlers = {
917
+ subscription: createSubscriptionTrigger,
918
+ timer: createTimerTrigger,
919
+ webhook: createWebhookTrigger,
920
+ websocket: createWebsocketTrigger
921
+ };
922
+ var TriggerRegistry = class extends Resource2 {
923
+ constructor(_client, _options) {
924
+ super();
925
+ this._client = _client;
926
+ this._options = _options;
927
+ this._triggersBySpaceKey = new ComplexMap2(PublicKey3.hash);
928
+ this.registered = new Event3();
929
+ this.removed = new Event3();
930
+ }
931
+ getActiveTriggers(space) {
932
+ return this._getTriggers(space, (t) => t.activationCtx != null);
933
+ }
934
+ getInactiveTriggers(space) {
935
+ return this._getTriggers(space, (t) => t.activationCtx == null);
936
+ }
937
+ async activate(triggerCtx, trigger, callback) {
938
+ log8("activate", {
939
+ space: triggerCtx.space.key,
577
940
  trigger
578
941
  }, {
579
- F: __dxlog_file3,
580
- L: 199,
942
+ F: __dxlog_file8,
943
+ L: 73,
581
944
  S: this,
582
945
  C: (f, a) => f(...a)
583
946
  });
584
- const spec = trigger.webhook;
585
- const server = http.createServer(async (req, res) => {
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
592
- });
593
- res.end();
594
- });
595
- const port = await getPort2({
596
- random: true
947
+ const activationCtx = new Context3({
948
+ name: `trigger_${trigger.function}`
597
949
  });
598
- server.listen(port, () => {
599
- log3.info("started webhook", {
600
- port
601
- }, {
602
- F: __dxlog_file3,
603
- L: 223,
604
- S: this,
605
- C: (f, a) => f(...a)
606
- });
607
- spec.port = port;
608
- });
609
- ctx.onDispose(() => {
610
- server.close();
950
+ this._ctx.onDispose(() => activationCtx.dispose());
951
+ const registeredTrigger = this._triggersBySpaceKey.get(triggerCtx.space.key)?.find((reg) => reg.trigger.id === trigger.id);
952
+ invariant2(registeredTrigger, `Trigger is not registered: ${trigger.function}`, {
953
+ F: __dxlog_file8,
954
+ L: 79,
955
+ S: this,
956
+ A: [
957
+ "registeredTrigger",
958
+ "`Trigger is not registered: ${trigger.function}`"
959
+ ]
611
960
  });
961
+ registeredTrigger.activationCtx = activationCtx;
962
+ try {
963
+ const options = this._options?.[trigger.spec.type];
964
+ await triggerHandlers[trigger.spec.type](activationCtx, triggerCtx, trigger.spec, callback, options);
965
+ } catch (err) {
966
+ delete registeredTrigger.activationCtx;
967
+ throw err;
968
+ }
612
969
  }
613
970
  /**
614
- * Websocket.
615
- * NOTE: The port must be unique, so the same hook cannot be used for multiple spaces.
971
+ * Loads triggers from the manifest into the space.
616
972
  */
617
- async _createWebsocket(ctx, space, def, trigger, options = {
618
- retryDelay: 2,
619
- maxAttempts: 5
620
- }) {
621
- log3.info("websocket", {
622
- space: space.key,
623
- trigger
973
+ async register(space, manifest) {
974
+ log8("register", {
975
+ space: space.key
624
976
  }, {
625
- F: __dxlog_file3,
626
- L: 249,
977
+ F: __dxlog_file8,
978
+ L: 95,
627
979
  S: this,
628
980
  C: (f, a) => f(...a)
629
981
  });
630
- const spec = trigger.websocket;
631
- const { url, init } = spec;
632
- let ws;
633
- for (let attempt = 1; attempt <= options.maxAttempts; attempt++) {
634
- const open = new Trigger2();
635
- ws = new WebSocket(url);
636
- Object.assign(ws, {
637
- onopen: () => {
638
- log3.info("opened", {
639
- url
640
- }, {
641
- F: __dxlog_file3,
642
- L: 260,
643
- S: this,
644
- C: (f, a) => f(...a)
645
- });
646
- if (spec.init) {
647
- ws.send(new TextEncoder().encode(JSON.stringify(init)));
648
- }
649
- open.wake(true);
650
- },
651
- onclose: (event) => {
652
- log3.info("closed", {
653
- url,
654
- code: event.code
655
- }, {
656
- F: __dxlog_file3,
657
- L: 269,
658
- S: this,
659
- C: (f, a) => f(...a)
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
- }
674
- open.wake(false);
675
- },
676
- onerror: (event) => {
677
- log3.catch(event.error, {
678
- url
679
- }, {
680
- F: __dxlog_file3,
681
- L: 283,
682
- S: this,
683
- C: (f, a) => f(...a)
684
- });
685
- },
686
- onmessage: async (event) => {
687
- try {
688
- const data = JSON.parse(new TextDecoder().decode(event.data));
689
- await this._execFunction(def, trigger, {
690
- spaceKey: space.key,
691
- data
692
- });
693
- } catch (err) {
694
- log3.catch(err, {
695
- url
696
- }, {
697
- F: __dxlog_file3,
698
- L: 291,
699
- S: this,
700
- C: (f, a) => f(...a)
701
- });
702
- }
982
+ if (!manifest.triggers?.length) {
983
+ return;
984
+ }
985
+ if (!space.db.graph.runtimeSchemaRegistry.hasSchema(FunctionTrigger)) {
986
+ space.db.graph.runtimeSchemaRegistry.registerSchema(FunctionTrigger);
987
+ }
988
+ const reactiveObjects = manifest.triggers.map((template) => create2(FunctionTrigger, {
989
+ ...template
990
+ }));
991
+ reactiveObjects.forEach((obj) => space.db.add(obj));
992
+ }
993
+ async _open() {
994
+ const spaceListSubscription = this._client.spaces.subscribe(async (spaces) => {
995
+ for (const space of spaces) {
996
+ if (this._triggersBySpaceKey.has(space.key)) {
997
+ continue;
703
998
  }
704
- });
705
- const isOpen = await open.wait();
706
- if (isOpen) {
707
- break;
708
- } else {
709
- const wait = Math.pow(attempt, 2) * options.retryDelay;
710
- if (attempt < options.maxAttempts) {
711
- log3.warn(`failed to connect; trying again in ${wait}s`, {
712
- attempt
713
- }, {
714
- F: __dxlog_file3,
715
- L: 302,
716
- S: this,
717
- C: (f, a) => f(...a)
718
- });
719
- await sleep(wait * 1e3);
999
+ const registered = [];
1000
+ this._triggersBySpaceKey.set(space.key, registered);
1001
+ await space.waitUntilReady();
1002
+ if (this._ctx.disposed) {
1003
+ break;
720
1004
  }
1005
+ const functionsSubscription = space.db.query(Filter3.schema(FunctionTrigger)).subscribe(async (triggers) => {
1006
+ await this._handleRemovedTriggers(space, triggers.objects, registered);
1007
+ this._handleNewTriggers(space, triggers.objects, registered);
1008
+ });
1009
+ this._ctx.onDispose(functionsSubscription);
721
1010
  }
722
- }
723
- ctx.onDispose(() => {
724
- ws?.close();
725
1011
  });
1012
+ this._ctx.onDispose(() => spaceListSubscription.unsubscribe());
726
1013
  }
727
- /**
728
- * ECHO subscription.
729
- */
730
- async _createSubscription(ctx, space, def, trigger) {
731
- log3.info("subscription", {
732
- space: space.key,
733
- trigger
734
- }, {
735
- F: __dxlog_file3,
736
- L: 317,
737
- S: this,
738
- C: (f, a) => f(...a)
1014
+ async _close(_) {
1015
+ this._triggersBySpaceKey.clear();
1016
+ }
1017
+ _handleNewTriggers(space, allTriggers, registered) {
1018
+ const newTriggers = allTriggers.filter((candidate) => {
1019
+ return registered.find((reg) => reg.trigger.id === candidate.id) == null;
739
1020
  });
740
- const spec = trigger.subscription;
741
- const objectIds = /* @__PURE__ */ new Set();
742
- const task = new DeferredTask(ctx, async () => {
743
- await this._execFunction(def, trigger, {
1021
+ if (newTriggers.length > 0) {
1022
+ const newRegisteredTriggers = newTriggers.map((trigger) => ({
1023
+ trigger
1024
+ }));
1025
+ registered.push(...newRegisteredTriggers);
1026
+ log8("registered new triggers", () => ({
744
1027
  spaceKey: space.key,
745
- objects: Array.from(objectIds)
746
- });
747
- });
748
- const subscriptions = [];
749
- const subscription = createSubscription(({ added, updated }) => {
750
- log3.info("updated", {
751
- added: added.length,
752
- updated: updated.length
753
- }, {
754
- F: __dxlog_file3,
755
- L: 329,
1028
+ functions: newTriggers.map((t) => t.function)
1029
+ }), {
1030
+ F: __dxlog_file8,
1031
+ L: 146,
756
1032
  S: this,
757
1033
  C: (f, a) => f(...a)
758
1034
  });
759
- for (const object of added) {
760
- objectIds.add(object.id);
761
- }
762
- for (const object of updated) {
763
- objectIds.add(object.id);
764
- }
765
- task.schedule();
766
- });
767
- subscriptions.push(() => subscription.unsubscribe());
768
- const { filter, options: { deep, delay } = {} } = spec;
769
- const update = ({ objects }) => {
770
- subscription.update(objects);
771
- if (deep) {
772
- log3.info("update", {
773
- objects: objects.length
774
- }, {
775
- F: __dxlog_file3,
776
- L: 349,
777
- S: this,
778
- C: (f, a) => f(...a)
779
- });
780
- for (const object of objects) {
781
- const content = object.content;
782
- if (content instanceof TextV0Type) {
783
- subscriptions.push(getAutomergeObjectCore(content).updates.on(debounce(() => subscription.update([
784
- object
785
- ]), 1e3)));
786
- }
787
- }
1035
+ this.registered.emit({
1036
+ space,
1037
+ triggers: newTriggers
1038
+ });
1039
+ }
1040
+ }
1041
+ async _handleRemovedTriggers(space, allTriggers, registered) {
1042
+ const removed = [];
1043
+ for (let i = registered.length - 1; i >= 0; i--) {
1044
+ const wasRemoved = allTriggers.find((trigger) => trigger.id === registered[i].trigger.id) == null;
1045
+ if (wasRemoved) {
1046
+ const unregistered = registered.splice(i, 1)[0];
1047
+ await unregistered.activationCtx?.dispose();
1048
+ removed.push(unregistered.trigger);
788
1049
  }
789
- };
790
- const query = space.db.query(Filter.or(filter.map(({ type, props }) => Filter.typename(type, props))));
791
- subscriptions.push(query.subscribe(delay ? debounce(update, delay) : update));
792
- ctx.onDispose(() => {
793
- subscriptions.forEach((unsubscribe) => unsubscribe());
794
- });
1050
+ }
1051
+ if (removed.length > 0) {
1052
+ this.removed.emit({
1053
+ space,
1054
+ triggers: removed
1055
+ });
1056
+ }
1057
+ }
1058
+ _getTriggers(space, predicate) {
1059
+ const allSpaceTriggers = this._triggersBySpaceKey.get(space.key) ?? [];
1060
+ return allSpaceTriggers.filter(predicate).map((trigger) => trigger.trigger);
795
1061
  }
796
1062
  };
797
-
798
- // packages/core/functions/src/types.ts
799
- import * as S from "@effect/schema/Schema";
800
- var TimerTriggerSchema = S.struct({
801
- cron: S.string
802
- });
803
- var WebhookTriggerSchema = S.mutable(S.struct({
804
- method: S.string,
805
- // Assigned port.
806
- port: S.optional(S.number)
807
- }));
808
- var WebsocketTriggerSchema = S.struct({
809
- url: S.string,
810
- init: S.optional(S.record(S.string, S.any))
811
- });
812
- var SubscriptionTriggerSchema = S.struct({
813
- spaceKey: S.optional(S.string),
814
- // TODO(burdon): Define query DSL.
815
- filter: S.array(S.struct({
816
- type: S.string,
817
- props: S.optional(S.record(S.string, S.any))
818
- })),
819
- options: S.optional(S.struct({
820
- // Watch changes to object (not just creation).
821
- deep: S.optional(S.boolean),
822
- // Debounce changes (delay in ms).
823
- delay: S.optional(S.number)
824
- }))
825
- });
826
- var FunctionTriggerSchema = S.struct({
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.
831
- timer: S.optional(TimerTriggerSchema),
832
- webhook: S.optional(WebhookTriggerSchema),
833
- websocket: S.optional(WebsocketTriggerSchema),
834
- subscription: S.optional(SubscriptionTriggerSchema)
835
- });
836
- var FunctionDefSchema = S.struct({
837
- id: S.string,
838
- // name: S.string,
839
- description: S.optional(S.string),
840
- // TODO(burdon): Rename route?
841
- path: S.string,
842
- // TODO(burdon): NPM/GitHub/Docker/CF URL?
843
- handler: S.string
844
- });
845
- var FunctionManifestSchema = S.struct({
846
- functions: S.mutable(S.array(FunctionDefSchema)),
847
- triggers: S.optional(S.mutable(S.array(FunctionTriggerSchema)))
848
- });
849
1063
  export {
850
1064
  DevServer,
1065
+ FunctionDef,
851
1066
  FunctionManifestSchema,
1067
+ FunctionRegistry,
1068
+ FunctionTrigger,
852
1069
  Scheduler,
1070
+ TriggerRegistry,
853
1071
  subscriptionHandler
854
1072
  };
855
1073
  //# sourceMappingURL=index.mjs.map