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

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
@@ -58,170 +58,22 @@ 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.isSchemaRegistered(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
-
194
61
  // packages/core/functions/src/runtime/dev-server.ts
195
62
  import express from "express";
196
63
  import { getPort } from "get-port-please";
197
64
  import { join } from "@dxos/node-std/path";
198
- import { Event as Event2, Trigger } from "@dxos/async";
199
- import { Context } from "@dxos/context";
65
+ import { Event, Trigger } from "@dxos/async";
200
66
  import { invariant } from "@dxos/invariant";
201
67
  import { log as log2 } from "@dxos/log";
202
68
  var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/dev-server.ts";
203
69
  var DevServer = class {
204
70
  // prettier-ignore
205
- constructor(_client, _functionsRegistry, _options) {
71
+ constructor(_client, _options) {
206
72
  this._client = _client;
207
- this._functionsRegistry = _functionsRegistry;
208
73
  this._options = _options;
209
- this._ctx = createContext();
210
74
  this._handlers = {};
211
75
  this._seq = 0;
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
- });
76
+ this.update = new Event();
225
77
  }
226
78
  get stats() {
227
79
  return {
@@ -231,7 +83,7 @@ var DevServer = class {
231
83
  get endpoint() {
232
84
  invariant(this._port, void 0, {
233
85
  F: __dxlog_file2,
234
- L: 64,
86
+ L: 54,
235
87
  S: this,
236
88
  A: [
237
89
  "this._port",
@@ -246,6 +98,20 @@ var DevServer = class {
246
98
  get functions() {
247
99
  return Object.values(this._handlers);
248
100
  }
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
+ }
249
115
  async start() {
250
116
  invariant(!this._server, void 0, {
251
117
  F: __dxlog_file2,
@@ -262,7 +128,6 @@ var DevServer = class {
262
128
  S: this,
263
129
  C: (f, a) => f(...a)
264
130
  });
265
- this._ctx = createContext();
266
131
  const app = express();
267
132
  app.use(express.json());
268
133
  app.post("/:path", async (req, res) => {
@@ -272,7 +137,7 @@ var DevServer = class {
272
137
  path: path2
273
138
  }, {
274
139
  F: __dxlog_file2,
275
- L: 88,
140
+ L: 87,
276
141
  S: this,
277
142
  C: (f, a) => f(...a)
278
143
  });
@@ -285,7 +150,7 @@ var DevServer = class {
285
150
  } catch (err) {
286
151
  log2.catch(err, void 0, {
287
152
  F: __dxlog_file2,
288
- L: 98,
153
+ L: 97,
289
154
  S: this,
290
155
  C: (f, a) => f(...a)
291
156
  });
@@ -304,7 +169,11 @@ var DevServer = class {
304
169
  this._server = app.listen(this._port);
305
170
  try {
306
171
  const { registrationId, endpoint } = await this._client.services.services.FunctionRegistryService.register({
307
- endpoint: this.endpoint
172
+ endpoint: this.endpoint,
173
+ functions: this.functions.map(({ def: { id, path: path2 } }) => ({
174
+ id,
175
+ path: path2
176
+ }))
308
177
  });
309
178
  log2.info("registered", {
310
179
  endpoint
@@ -316,7 +185,6 @@ var DevServer = class {
316
185
  });
317
186
  this._proxy = endpoint;
318
187
  this._functionServiceRegistration = registrationId;
319
- await this._functionsRegistry.open(this._ctx);
320
188
  } catch (err) {
321
189
  await this.stop();
322
190
  throw new Error("FunctionRegistryService not available (check plugin is configured).");
@@ -325,7 +193,7 @@ var DevServer = class {
325
193
  port: this._port
326
194
  }, {
327
195
  F: __dxlog_file2,
328
- L: 124,
196
+ L: 121,
329
197
  S: this,
330
198
  C: (f, a) => f(...a)
331
199
  });
@@ -333,7 +201,7 @@ var DevServer = class {
333
201
  async stop() {
334
202
  invariant(this._server, void 0, {
335
203
  F: __dxlog_file2,
336
- L: 128,
204
+ L: 125,
337
205
  S: this,
338
206
  A: [
339
207
  "this._server",
@@ -342,7 +210,7 @@ var DevServer = class {
342
210
  });
343
211
  log2.info("stopping...", void 0, {
344
212
  F: __dxlog_file2,
345
- L: 129,
213
+ L: 126,
346
214
  S: this,
347
215
  C: (f, a) => f(...a)
348
216
  });
@@ -350,7 +218,7 @@ var DevServer = class {
350
218
  this._server.close(async () => {
351
219
  log2.info("server stopped", void 0, {
352
220
  F: __dxlog_file2,
353
- L: 133,
221
+ L: 130,
354
222
  S: this,
355
223
  C: (f, a) => f(...a)
356
224
  });
@@ -358,7 +226,7 @@ var DevServer = class {
358
226
  if (this._functionServiceRegistration) {
359
227
  invariant(this._client.services.services.FunctionRegistryService, void 0, {
360
228
  F: __dxlog_file2,
361
- L: 136,
229
+ L: 133,
362
230
  S: this,
363
231
  A: [
364
232
  "this._client.services.services.FunctionRegistryService",
@@ -372,7 +240,7 @@ var DevServer = class {
372
240
  registrationId: this._functionServiceRegistration
373
241
  }, {
374
242
  F: __dxlog_file2,
375
- L: 141,
243
+ L: 138,
376
244
  S: this,
377
245
  C: (f, a) => f(...a)
378
246
  });
@@ -389,7 +257,7 @@ var DevServer = class {
389
257
  this._server = void 0;
390
258
  log2.info("stopped", void 0, {
391
259
  F: __dxlog_file2,
392
- L: 155,
260
+ L: 152,
393
261
  S: this,
394
262
  C: (f, a) => f(...a)
395
263
  });
@@ -398,14 +266,14 @@ var DevServer = class {
398
266
  * Load function.
399
267
  */
400
268
  async _load(def, force = false) {
401
- const { uri, route, handler } = def;
269
+ const { id, path: path2, handler } = def;
402
270
  const filePath = join(this._options.baseDir, handler);
403
271
  log2.info("loading", {
404
- uri,
272
+ id,
405
273
  force
406
274
  }, {
407
275
  F: __dxlog_file2,
408
- L: 164,
276
+ L: 161,
409
277
  S: this,
410
278
  C: (f, a) => f(...a)
411
279
  });
@@ -416,40 +284,13 @@ var DevServer = class {
416
284
  }
417
285
  const module = __require(filePath);
418
286
  if (typeof module.default !== "function") {
419
- throw new Error(`Handler must export default function: ${uri}`);
287
+ throw new Error(`Handler must export default function: ${id}`);
420
288
  }
421
- this._handlers[route] = {
289
+ this._handlers[path2] = {
422
290
  def,
423
291
  handler: module.default
424
292
  };
425
293
  }
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
- }
453
294
  /**
454
295
  * Invoke function.
455
296
  */
@@ -461,7 +302,7 @@ var DevServer = class {
461
302
  path: path2
462
303
  }, {
463
304
  F: __dxlog_file2,
464
- L: 204,
305
+ L: 188,
465
306
  S: this,
466
307
  C: (f, a) => f(...a)
467
308
  });
@@ -475,7 +316,7 @@ var DevServer = class {
475
316
  duration: Date.now() - now
476
317
  }, {
477
318
  F: __dxlog_file2,
478
- L: 207,
319
+ L: 191,
479
320
  S: this,
480
321
  C: (f, a) => f(...a)
481
322
  });
@@ -486,7 +327,7 @@ var DevServer = class {
486
327
  const { handler } = this._handlers[path2] ?? {};
487
328
  invariant(handler, `invalid path: ${path2}`, {
488
329
  F: __dxlog_file2,
489
- L: 214,
330
+ L: 198,
490
331
  S: this,
491
332
  A: [
492
333
  "handler",
@@ -512,98 +353,125 @@ var DevServer = class {
512
353
  return statusCode;
513
354
  }
514
355
  };
515
- var createContext = () => new Context({
516
- name: "DevServer"
517
- });
518
356
 
519
357
  // 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";
520
361
  import path from "@dxos/node-std/path";
521
- import { Context as Context2 } from "@dxos/context";
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";
522
368
  import { log as log3 } from "@dxos/log";
369
+ import { ComplexMap } from "@dxos/util";
523
370
  var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/scheduler.ts";
524
371
  var Scheduler = class {
525
- constructor(functions, triggers, _options = {}) {
526
- this.functions = functions;
527
- this.triggers = triggers;
372
+ constructor(_client, _manifest, _options = {}) {
373
+ this._client = _client;
374
+ this._manifest = _manifest;
528
375
  this._options = _options;
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
- });
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
+ }, []);
536
383
  }
537
384
  async start() {
538
- await this._ctx.dispose();
539
- this._ctx = createContext2();
540
- await this.functions.open(this._ctx);
541
- await this.triggers.open(this._ctx);
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
+ });
542
393
  }
543
394
  async stop() {
544
- await this._ctx.dispose();
545
- await this.functions.close();
546
- await this.triggers.close();
547
- }
548
- async register(space, manifest) {
549
- await this.functions.register(space, manifest);
550
- await this.triggers.register(space, manifest);
395
+ for (const { id, spaceKey } of this._mounts.keys()) {
396
+ await this.unmount(id, spaceKey);
397
+ }
551
398
  }
552
- async _safeActivateTriggers(space, triggers, functions) {
553
- const mountTasks = triggers.map((trigger) => {
554
- return this.activate(space, functions, trigger);
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
+ ]
555
416
  });
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
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
563
426
  }, {
564
427
  F: __dxlog_file3,
565
- L: 74,
428
+ L: 82,
566
429
  S: this,
567
430
  C: (f, a) => f(...a)
568
431
  });
569
- return;
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
+ }
570
447
  }
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
- });
591
448
  }
592
- async _execFunction(def, { data, meta }) {
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();
458
+ }
459
+ }
460
+ async _execFunction(def, trigger, data) {
593
461
  let status = 0;
594
462
  try {
595
- const payload = Object.assign({}, meta && {
596
- meta
463
+ const payload = Object.assign({}, {
464
+ meta: trigger.meta
597
465
  }, data);
598
466
  const { endpoint, callback } = this._options;
599
467
  if (endpoint) {
600
- const url = path.join(endpoint, def.route);
468
+ const url = path.join(endpoint, def.path);
601
469
  log3.info("exec", {
602
- function: def.uri,
470
+ function: def.id,
603
471
  url
604
472
  }, {
605
473
  F: __dxlog_file3,
606
- L: 100,
474
+ L: 128,
607
475
  S: this,
608
476
  C: (f, a) => f(...a)
609
477
  });
@@ -617,10 +485,10 @@ var Scheduler = class {
617
485
  status = response.status;
618
486
  } else if (callback) {
619
487
  log3.info("exec", {
620
- function: def.uri
488
+ function: def.id
621
489
  }, {
622
490
  F: __dxlog_file3,
623
- L: 111,
491
+ L: 139,
624
492
  S: this,
625
493
  C: (f, a) => f(...a)
626
494
  });
@@ -630,21 +498,21 @@ var Scheduler = class {
630
498
  throw new Error(`Response: ${status}`);
631
499
  }
632
500
  log3.info("done", {
633
- function: def.uri,
501
+ function: def.id,
634
502
  status
635
503
  }, {
636
504
  F: __dxlog_file3,
637
- L: 121,
505
+ L: 149,
638
506
  S: this,
639
507
  C: (f, a) => f(...a)
640
508
  });
641
509
  } catch (err) {
642
510
  log3.error("error", {
643
- function: def.uri,
511
+ function: def.id,
644
512
  error: err.message
645
513
  }, {
646
514
  F: __dxlog_file3,
647
- L: 123,
515
+ L: 151,
648
516
  S: this,
649
517
  C: (f, a) => f(...a)
650
518
  });
@@ -652,422 +520,336 @@ var Scheduler = class {
652
520
  }
653
521
  return status;
654
522
  }
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
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
690
533
  }, {
691
- F: __dxlog_file4,
692
- L: 32,
693
- S: void 0,
534
+ F: __dxlog_file3,
535
+ L: 166,
536
+ S: this,
694
537
  C: (f, a) => f(...a)
695
538
  });
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)
539
+ const spec = trigger.timer;
540
+ const task = new DeferredTask(ctx, async () => {
541
+ await this._execFunction(def, trigger, {
542
+ spaceKey: space.key
762
543
  });
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)
795
544
  });
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
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
821
559
  }, {
822
- F: __dxlog_file7,
823
- L: 39,
824
- S: void 0,
825
- C: (f, a) => f(...a)
826
- });
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,
560
+ F: __dxlog_file3,
561
+ L: 186,
562
+ S: this,
864
563
  C: (f, a) => f(...a)
865
564
  });
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
- }
565
+ task.schedule();
889
566
  }
890
567
  });
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
- }
568
+ job.start();
569
+ ctx.onDispose(() => job.stop());
908
570
  }
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,
571
+ /**
572
+ * Webhook.
573
+ */
574
+ async _createWebhook(ctx, space, def, trigger) {
575
+ log3.info("webhook", {
576
+ space: space.key,
940
577
  trigger
941
578
  }, {
942
- F: __dxlog_file8,
943
- L: 73,
579
+ F: __dxlog_file3,
580
+ L: 199,
944
581
  S: this,
945
582
  C: (f, a) => f(...a)
946
583
  });
947
- const activationCtx = new Context3({
948
- name: `trigger_${trigger.function}`
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();
949
594
  });
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
- ]
595
+ const port = await getPort2({
596
+ random: true
597
+ });
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();
960
611
  });
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
- }
969
612
  }
970
613
  /**
971
- * Loads triggers from the manifest into the space.
614
+ * Websocket.
615
+ * NOTE: The port must be unique, so the same hook cannot be used for multiple spaces.
972
616
  */
973
- async register(space, manifest) {
974
- log8("register", {
975
- space: space.key
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
976
624
  }, {
977
- F: __dxlog_file8,
978
- L: 95,
625
+ F: __dxlog_file3,
626
+ L: 249,
979
627
  S: this,
980
628
  C: (f, a) => f(...a)
981
629
  });
982
- if (!manifest.triggers?.length) {
983
- return;
984
- }
985
- if (!space.db.graph.runtimeSchemaRegistry.isSchemaRegistered(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;
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
+ }
998
703
  }
999
- const registered = [];
1000
- this._triggersBySpaceKey.set(space.key, registered);
1001
- await space.waitUntilReady();
1002
- if (this._ctx.disposed) {
1003
- break;
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);
1004
720
  }
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);
1010
721
  }
722
+ }
723
+ ctx.onDispose(() => {
724
+ ws?.close();
1011
725
  });
1012
- this._ctx.onDispose(() => spaceListSubscription.unsubscribe());
1013
726
  }
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;
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)
1020
739
  });
1021
- if (newTriggers.length > 0) {
1022
- const newRegisteredTriggers = newTriggers.map((trigger) => ({
1023
- trigger
1024
- }));
1025
- registered.push(...newRegisteredTriggers);
1026
- log8("registered new triggers", () => ({
740
+ const spec = trigger.subscription;
741
+ const objectIds = /* @__PURE__ */ new Set();
742
+ const task = new DeferredTask(ctx, async () => {
743
+ await this._execFunction(def, trigger, {
1027
744
  spaceKey: space.key,
1028
- functions: newTriggers.map((t) => t.function)
1029
- }), {
1030
- F: __dxlog_file8,
1031
- L: 146,
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,
1032
756
  S: this,
1033
757
  C: (f, a) => f(...a)
1034
758
  });
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);
759
+ for (const object of added) {
760
+ objectIds.add(object.id);
1049
761
  }
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);
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
+ }
788
+ }
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
+ });
1061
795
  }
1062
796
  };
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
+ });
1063
849
  export {
1064
850
  DevServer,
1065
- FunctionDef,
1066
851
  FunctionManifestSchema,
1067
- FunctionRegistry,
1068
- FunctionTrigger,
1069
852
  Scheduler,
1070
- TriggerRegistry,
1071
853
  subscriptionHandler
1072
854
  };
1073
855
  //# sourceMappingURL=index.mjs.map