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

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