@dxos/functions 0.5.3-main.d7fe7b5 → 0.5.3-main.e76d664

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/lib/browser/index.mjs +429 -802
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node/index.cjs +426 -787
  5. package/dist/lib/node/index.cjs.map +4 -4
  6. package/dist/lib/node/meta.json +1 -1
  7. package/dist/types/src/handler.d.ts +12 -33
  8. package/dist/types/src/handler.d.ts.map +1 -1
  9. package/dist/types/src/index.d.ts +0 -2
  10. package/dist/types/src/index.d.ts.map +1 -1
  11. package/dist/types/src/runtime/dev-server.d.ts +13 -16
  12. package/dist/types/src/runtime/dev-server.d.ts.map +1 -1
  13. package/dist/types/src/runtime/scheduler.d.ts +27 -12
  14. package/dist/types/src/runtime/scheduler.d.ts.map +1 -1
  15. package/dist/types/src/types.d.ts +101 -129
  16. package/dist/types/src/types.d.ts.map +1 -1
  17. package/package.json +13 -18
  18. package/schema/functions.json +101 -128
  19. package/src/handler.ts +31 -54
  20. package/src/index.ts +0 -2
  21. package/src/runtime/dev-server.ts +52 -104
  22. package/src/runtime/scheduler.test.ts +73 -56
  23. package/src/runtime/scheduler.ts +271 -79
  24. package/src/types.ts +32 -57
  25. package/dist/types/src/registry/function-registry.d.ts +0 -24
  26. package/dist/types/src/registry/function-registry.d.ts.map +0 -1
  27. package/dist/types/src/registry/function-registry.test.d.ts +0 -2
  28. package/dist/types/src/registry/function-registry.test.d.ts.map +0 -1
  29. package/dist/types/src/registry/index.d.ts +0 -2
  30. package/dist/types/src/registry/index.d.ts.map +0 -1
  31. package/dist/types/src/runtime/dev-server.test.d.ts +0 -2
  32. package/dist/types/src/runtime/dev-server.test.d.ts.map +0 -1
  33. package/dist/types/src/testing/functions-integration.test.d.ts +0 -2
  34. package/dist/types/src/testing/functions-integration.test.d.ts.map +0 -1
  35. package/dist/types/src/testing/index.d.ts +0 -4
  36. package/dist/types/src/testing/index.d.ts.map +0 -1
  37. package/dist/types/src/testing/setup.d.ts +0 -5
  38. package/dist/types/src/testing/setup.d.ts.map +0 -1
  39. package/dist/types/src/testing/test/handler.d.ts +0 -4
  40. package/dist/types/src/testing/test/handler.d.ts.map +0 -1
  41. package/dist/types/src/testing/test/index.d.ts +0 -3
  42. package/dist/types/src/testing/test/index.d.ts.map +0 -1
  43. package/dist/types/src/testing/types.d.ts +0 -9
  44. package/dist/types/src/testing/types.d.ts.map +0 -1
  45. package/dist/types/src/testing/util.d.ts +0 -3
  46. package/dist/types/src/testing/util.d.ts.map +0 -1
  47. package/dist/types/src/trigger/index.d.ts +0 -2
  48. package/dist/types/src/trigger/index.d.ts.map +0 -1
  49. package/dist/types/src/trigger/trigger-registry.d.ts +0 -40
  50. package/dist/types/src/trigger/trigger-registry.d.ts.map +0 -1
  51. package/dist/types/src/trigger/trigger-registry.test.d.ts +0 -2
  52. package/dist/types/src/trigger/trigger-registry.test.d.ts.map +0 -1
  53. package/dist/types/src/trigger/type/index.d.ts +0 -5
  54. package/dist/types/src/trigger/type/index.d.ts.map +0 -1
  55. package/dist/types/src/trigger/type/subscription-trigger.d.ts +0 -4
  56. package/dist/types/src/trigger/type/subscription-trigger.d.ts.map +0 -1
  57. package/dist/types/src/trigger/type/timer-trigger.d.ts +0 -4
  58. package/dist/types/src/trigger/type/timer-trigger.d.ts.map +0 -1
  59. package/dist/types/src/trigger/type/webhook-trigger.d.ts +0 -4
  60. package/dist/types/src/trigger/type/webhook-trigger.d.ts.map +0 -1
  61. package/dist/types/src/trigger/type/websocket-trigger.d.ts +0 -13
  62. package/dist/types/src/trigger/type/websocket-trigger.d.ts.map +0 -1
  63. package/src/registry/function-registry.test.ts +0 -105
  64. package/src/registry/function-registry.ts +0 -84
  65. package/src/registry/index.ts +0 -5
  66. package/src/runtime/dev-server.test.ts +0 -60
  67. package/src/testing/functions-integration.test.ts +0 -99
  68. package/src/testing/index.ts +0 -7
  69. package/src/testing/setup.ts +0 -45
  70. package/src/testing/test/handler.ts +0 -15
  71. package/src/testing/test/index.ts +0 -7
  72. package/src/testing/types.ts +0 -9
  73. package/src/testing/util.ts +0 -16
  74. package/src/trigger/index.ts +0 -5
  75. package/src/trigger/trigger-registry.test.ts +0 -229
  76. package/src/trigger/trigger-registry.ts +0 -176
  77. package/src/trigger/type/index.ts +0 -8
  78. package/src/trigger/type/subscription-trigger.ts +0 -73
  79. package/src/trigger/type/timer-trigger.ts +0 -44
  80. package/src/trigger/type/webhook-trigger.ts +0 -47
  81. package/src/trigger/type/websocket-trigger.ts +0 -91
@@ -29,54 +29,31 @@ 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
- var import_get_port_please2 = require("get-port-please");
75
47
  var import_node_http = __toESM(require("node:http"));
76
- var import_log7 = require("@dxos/log");
77
48
  var import_ws = __toESM(require("ws"));
78
- var import_async6 = require("@dxos/async");
79
- var import_log8 = require("@dxos/log");
49
+ var import_types = require("@braneframe/types");
50
+ var import_async2 = require("@dxos/async");
51
+ var import_echo = require("@dxos/client/echo");
52
+ var import_context = require("@dxos/context");
53
+ var import_invariant2 = require("@dxos/invariant");
54
+ var import_log3 = require("@dxos/log");
55
+ var import_util2 = require("@dxos/util");
56
+ var S = __toESM(require("@effect/schema/Schema"));
80
57
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
81
58
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
82
59
  }) : x)(function(x) {
@@ -86,16 +63,16 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
86
63
  });
87
64
  var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/functions/src/handler.ts";
88
65
  var subscriptionHandler = (handler) => {
89
- return ({ event: { data }, context, ...rest }) => {
66
+ return ({ event, context, ...rest }) => {
90
67
  const { client } = context;
91
- const space = data.spaceKey ? client.spaces.get(import_client.PublicKey.from(data.spaceKey)) : void 0;
92
- const objects = space ? data.objects?.map((id) => space.db.getObjectById(id)).filter(import_util.nonNullable) : [];
93
- if (!!data.spaceKey && !space) {
68
+ const space = event.space ? client.spaces.get(import_client.PublicKey.from(event.space)) : void 0;
69
+ const objects = space && event.objects?.map((id) => space.db.getObjectById(id)).filter(import_util.nonNullable);
70
+ if (!!event.space && !space) {
94
71
  import_log.log.warn("invalid space", {
95
- data
72
+ event
96
73
  }, {
97
74
  F: __dxlog_file,
98
- L: 91,
75
+ L: 68,
99
76
  S: void 0,
100
77
  C: (f, a) => f(...a)
101
78
  });
@@ -105,178 +82,34 @@ var subscriptionHandler = (handler) => {
105
82
  objects: objects?.length
106
83
  }, {
107
84
  F: __dxlog_file,
108
- L: 93,
85
+ L: 70,
109
86
  S: void 0,
110
87
  C: (f, a) => f(...a)
111
88
  });
112
89
  }
113
90
  return handler({
114
91
  event: {
115
- data: {
116
- ...data,
117
- space,
118
- objects
119
- }
92
+ space,
93
+ objects
120
94
  },
121
95
  context,
122
96
  ...rest
123
97
  });
124
98
  };
125
99
  };
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
100
  var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/dev-server.ts";
248
101
  var DevServer = class {
249
102
  // prettier-ignore
250
- constructor(_client, _functionsRegistry, _options) {
103
+ constructor(_client, _options) {
251
104
  this._client = _client;
252
- this._functionsRegistry = _functionsRegistry;
253
105
  this._options = _options;
254
- this._ctx = createContext();
255
106
  this._handlers = {};
256
107
  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
- });
270
- }
271
- get stats() {
272
- return {
273
- seq: this._seq
274
- };
275
108
  }
276
109
  get endpoint() {
277
110
  (0, import_invariant.invariant)(this._port, void 0, {
278
111
  F: __dxlog_file2,
279
- L: 64,
112
+ L: 46,
280
113
  S: this,
281
114
  A: [
282
115
  "this._port",
@@ -291,46 +124,44 @@ var DevServer = class {
291
124
  get functions() {
292
125
  return Object.values(this._handlers);
293
126
  }
127
+ async initialize() {
128
+ for (const def of this._options.manifest.functions) {
129
+ try {
130
+ await this._load(def);
131
+ } catch (err) {
132
+ import_log2.log.error("parsing function (check manifest)", err, {
133
+ F: __dxlog_file2,
134
+ L: 63,
135
+ S: this,
136
+ C: (f, a) => f(...a)
137
+ });
138
+ }
139
+ }
140
+ }
294
141
  async start() {
295
- (0, import_invariant.invariant)(!this._server, void 0, {
296
- F: __dxlog_file2,
297
- L: 77,
298
- S: this,
299
- A: [
300
- "!this._server",
301
- ""
302
- ]
303
- });
304
- import_log2.log.info("starting...", void 0, {
305
- F: __dxlog_file2,
306
- L: 78,
307
- S: this,
308
- C: (f, a) => f(...a)
309
- });
310
- this._ctx = createContext();
311
142
  const app = (0, import_express.default)();
312
143
  app.use(import_express.default.json());
313
- app.post("/:path", async (req, res) => {
314
- const { path: path2 } = req.params;
144
+ app.post("/:name", async (req, res) => {
145
+ const { name } = req.params;
315
146
  try {
316
147
  import_log2.log.info("calling", {
317
- path: path2
148
+ name
318
149
  }, {
319
150
  F: __dxlog_file2,
320
- L: 88,
151
+ L: 75,
321
152
  S: this,
322
153
  C: (f, a) => f(...a)
323
154
  });
324
155
  if (this._options.reload) {
325
- const { def } = this._handlers["/" + path2];
156
+ const { def } = this._handlers[name];
326
157
  await this._load(def, true);
327
158
  }
328
- res.statusCode = await this.invoke("/" + path2, req.body);
159
+ res.statusCode = await this._invoke(name, req.body);
329
160
  res.end();
330
161
  } catch (err) {
331
162
  import_log2.log.catch(err, void 0, {
332
163
  F: __dxlog_file2,
333
- L: 98,
164
+ L: 84,
334
165
  S: this,
335
166
  C: (f, a) => f(...a)
336
167
  });
@@ -349,195 +180,93 @@ var DevServer = class {
349
180
  this._server = app.listen(this._port);
350
181
  try {
351
182
  const { registrationId, endpoint } = await this._client.services.services.FunctionRegistryService.register({
352
- endpoint: this.endpoint
183
+ endpoint: this.endpoint,
184
+ functions: this.functions.map(({ def: { name } }) => ({
185
+ name
186
+ }))
353
187
  });
354
188
  import_log2.log.info("registered", {
189
+ registrationId,
355
190
  endpoint
356
191
  }, {
357
192
  F: __dxlog_file2,
358
- L: 113,
193
+ L: 100,
359
194
  S: this,
360
195
  C: (f, a) => f(...a)
361
196
  });
197
+ this._registrationId = registrationId;
362
198
  this._proxy = endpoint;
363
- this._functionServiceRegistration = registrationId;
364
- await this._functionsRegistry.open(this._ctx);
365
199
  } catch (err) {
366
200
  await this.stop();
367
201
  throw new Error("FunctionRegistryService not available (check plugin is configured).");
368
202
  }
369
- import_log2.log.info("started", {
370
- port: this._port
371
- }, {
372
- F: __dxlog_file2,
373
- L: 124,
374
- S: this,
375
- C: (f, a) => f(...a)
376
- });
377
203
  }
378
204
  async stop() {
379
- (0, import_invariant.invariant)(this._server, void 0, {
380
- F: __dxlog_file2,
381
- L: 128,
382
- S: this,
383
- A: [
384
- "this._server",
385
- ""
386
- ]
387
- });
388
- import_log2.log.info("stopping...", void 0, {
389
- F: __dxlog_file2,
390
- L: 129,
391
- S: this,
392
- C: (f, a) => f(...a)
393
- });
394
- const trigger = new import_async2.Trigger();
395
- this._server.close(async () => {
396
- import_log2.log.info("server stopped", void 0, {
397
- F: __dxlog_file2,
398
- L: 133,
399
- S: this,
400
- C: (f, a) => f(...a)
401
- });
402
- try {
403
- if (this._functionServiceRegistration) {
404
- (0, import_invariant.invariant)(this._client.services.services.FunctionRegistryService, void 0, {
405
- F: __dxlog_file2,
406
- L: 136,
407
- S: this,
408
- A: [
409
- "this._client.services.services.FunctionRegistryService",
410
- ""
411
- ]
412
- });
413
- await this._client.services.services.FunctionRegistryService.unregister({
414
- registrationId: this._functionServiceRegistration
415
- });
416
- import_log2.log.info("unregistered", {
417
- registrationId: this._functionServiceRegistration
418
- }, {
419
- F: __dxlog_file2,
420
- L: 141,
421
- S: this,
422
- C: (f, a) => f(...a)
423
- });
424
- this._functionServiceRegistration = void 0;
425
- this._proxy = void 0;
426
- }
427
- trigger.wake();
428
- } catch (err) {
429
- trigger.throw(err);
205
+ const trigger = new import_async.Trigger();
206
+ this._server?.close(async () => {
207
+ if (this._registrationId) {
208
+ await this._client.services.services.FunctionRegistryService.unregister({
209
+ registrationId: this._registrationId
210
+ });
211
+ import_log2.log.info("unregistered", {
212
+ registrationId: this._registrationId
213
+ }, {
214
+ F: __dxlog_file2,
215
+ L: 117,
216
+ S: this,
217
+ C: (f, a) => f(...a)
218
+ });
219
+ this._registrationId = void 0;
220
+ this._proxy = void 0;
430
221
  }
222
+ trigger.wake();
431
223
  });
432
224
  await trigger.wait();
433
225
  this._port = void 0;
434
226
  this._server = void 0;
435
- import_log2.log.info("stopped", void 0, {
436
- F: __dxlog_file2,
437
- L: 155,
438
- S: this,
439
- C: (f, a) => f(...a)
440
- });
441
227
  }
442
228
  /**
443
229
  * Load function.
444
230
  */
445
- async _load(def, force = false) {
446
- const { uri, route, handler } = def;
447
- const filePath = (0, import_node_path.join)(this._options.baseDir, handler);
231
+ async _load(def, flush = false) {
232
+ const { id, name, handler } = def;
233
+ const path = (0, import_node_path.join)(this._options.directory, handler);
448
234
  import_log2.log.info("loading", {
449
- uri,
450
- force
235
+ id
451
236
  }, {
452
237
  F: __dxlog_file2,
453
- L: 164,
238
+ L: 136,
454
239
  S: this,
455
240
  C: (f, a) => f(...a)
456
241
  });
457
- if (force) {
458
- Object.keys(__require.cache).filter((key) => key.startsWith(filePath)).forEach((key) => {
459
- delete __require.cache[key];
460
- });
242
+ if (flush) {
243
+ Object.keys(__require.cache).filter((key) => key.startsWith(path)).forEach((key) => delete __require.cache[key]);
461
244
  }
462
- const module2 = __require(filePath);
245
+ const module2 = __require(path);
463
246
  if (typeof module2.default !== "function") {
464
- throw new Error(`Handler must export default function: ${uri}`);
247
+ throw new Error(`Handler must export default function: ${id}`);
465
248
  }
466
- this._handlers[route] = {
249
+ this._handlers[name] = {
467
250
  def,
468
251
  handler: module2.default
469
252
  };
470
253
  }
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
254
  /**
499
- * Invoke function.
255
+ * Invoke function handler.
500
256
  */
501
- async invoke(path2, data) {
257
+ async _invoke(name, event) {
502
258
  const seq = ++this._seq;
503
259
  const now = Date.now();
504
260
  import_log2.log.info("req", {
505
261
  seq,
506
- path: path2
262
+ name
507
263
  }, {
508
264
  F: __dxlog_file2,
509
- L: 204,
265
+ L: 161,
510
266
  S: this,
511
267
  C: (f, a) => f(...a)
512
268
  });
513
- const statusCode = await this._invoke(path2, {
514
- data
515
- });
516
- import_log2.log.info("res", {
517
- seq,
518
- path: path2,
519
- statusCode,
520
- duration: Date.now() - now
521
- }, {
522
- F: __dxlog_file2,
523
- L: 207,
524
- S: this,
525
- C: (f, a) => f(...a)
526
- });
527
- this.update.emit(statusCode);
528
- return statusCode;
529
- }
530
- async _invoke(path2, event) {
531
- const { handler } = this._handlers[path2] ?? {};
532
- (0, import_invariant.invariant)(handler, `invalid path: ${path2}`, {
533
- F: __dxlog_file2,
534
- L: 214,
535
- S: this,
536
- A: [
537
- "handler",
538
- "`invalid path: ${path}`"
539
- ]
540
- });
269
+ const { handler } = this._handlers[name];
541
270
  const context = {
542
271
  client: this._client,
543
272
  dataDir: this._options.dataDir
@@ -554,529 +283,439 @@ var DevServer = class {
554
283
  event,
555
284
  response
556
285
  });
286
+ import_log2.log.info("res", {
287
+ seq,
288
+ name,
289
+ statusCode,
290
+ duration: Date.now() - now
291
+ }, {
292
+ F: __dxlog_file2,
293
+ L: 178,
294
+ S: this,
295
+ C: (f, a) => f(...a)
296
+ });
557
297
  return statusCode;
558
298
  }
559
299
  };
560
- var createContext = () => new import_context2.Context({
561
- name: "DevServer"
562
- });
563
300
  var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/scheduler.ts";
564
301
  var Scheduler = class {
565
- constructor(functions, triggers, _options = {}) {
566
- this.functions = functions;
567
- this.triggers = triggers;
302
+ constructor(_client, _manifest, _options = {}) {
303
+ this._client = _client;
304
+ this._manifest = _manifest;
568
305
  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
- });
306
+ this._mounts = new import_util2.ComplexMap(({ id, spaceKey }) => `${spaceKey.toHex()}:${id}`);
576
307
  }
577
308
  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);
309
+ this._client.spaces.subscribe(async (spaces) => {
310
+ for (const space of spaces) {
311
+ await space.waitUntilReady();
312
+ for (const trigger of this._manifest.triggers ?? []) {
313
+ await this.mount(new import_context.Context(), space, trigger);
314
+ }
315
+ }
316
+ });
582
317
  }
583
318
  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);
319
+ for (const { id, spaceKey } of this._mounts.keys()) {
320
+ await this.unmount(id, spaceKey);
321
+ }
591
322
  }
592
- async _safeActivateTriggers(space, triggers, functions) {
593
- const mountTasks = triggers.map((trigger) => {
594
- return this.activate(space, functions, trigger);
323
+ async mount(ctx, space, trigger) {
324
+ const key = {
325
+ id: trigger.function,
326
+ spaceKey: space.key
327
+ };
328
+ const def = this._manifest.functions.find((config) => config.id === trigger.function);
329
+ (0, import_invariant2.invariant)(def, `Function not found: ${trigger.function}`, {
330
+ F: __dxlog_file3,
331
+ L: 72,
332
+ S: this,
333
+ A: [
334
+ "def",
335
+ "`Function not found: ${trigger.function}`"
336
+ ]
595
337
  });
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
338
+ const exists = this._mounts.get(key);
339
+ if (!exists) {
340
+ this._mounts.set(key, {
341
+ ctx,
342
+ trigger
343
+ });
344
+ (0, import_log3.log)("mount", {
345
+ space: space.key,
346
+ trigger
603
347
  }, {
604
348
  F: __dxlog_file3,
605
- L: 74,
349
+ L: 78,
606
350
  S: this,
607
351
  C: (f, a) => f(...a)
608
352
  });
609
- return;
353
+ if (ctx.disposed) {
354
+ return;
355
+ }
356
+ if (trigger.timer) {
357
+ await this._createTimer(ctx, space, def, trigger.timer);
358
+ }
359
+ if (trigger.webhook) {
360
+ await this._createWebhook(ctx, space, def, trigger.webhook);
361
+ }
362
+ if (trigger.websocket) {
363
+ await this._createWebsocket(ctx, space, def, trigger.websocket);
364
+ }
365
+ if (trigger.subscription) {
366
+ await this._createSubscription(ctx, space, def, trigger.subscription);
367
+ }
610
368
  }
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
369
  }
632
- async _execFunction(def, { data, meta }) {
633
- let status = 0;
370
+ async unmount(id, spaceKey) {
371
+ const key = {
372
+ id,
373
+ spaceKey
374
+ };
375
+ const { ctx } = this._mounts.get(key) ?? {};
376
+ if (ctx) {
377
+ this._mounts.delete(key);
378
+ await ctx.dispose();
379
+ }
380
+ }
381
+ // TODO(burdon): Pass in Space key (common context).
382
+ async _execFunction(def, data) {
634
383
  try {
635
- const payload = Object.assign({}, meta && {
636
- meta
637
- }, data);
384
+ import_log3.log.info("exec", {
385
+ function: def.id
386
+ }, {
387
+ F: __dxlog_file3,
388
+ L: 117,
389
+ S: this,
390
+ C: (f, a) => f(...a)
391
+ });
638
392
  const { endpoint, callback } = this._options;
639
393
  if (endpoint) {
640
- const url = import_node_path2.default.join(endpoint, def.route);
641
- import_log3.log.info("exec", {
642
- function: def.uri,
643
- url
644
- }, {
645
- F: __dxlog_file3,
646
- L: 100,
647
- S: this,
648
- C: (f, a) => f(...a)
649
- });
650
- const response = await fetch(url, {
394
+ await fetch(`${this._options.endpoint}/${def.name}`, {
651
395
  method: "POST",
652
396
  headers: {
653
397
  "Content-Type": "application/json"
654
398
  },
655
- body: JSON.stringify(payload)
399
+ body: JSON.stringify(data)
656
400
  });
657
- status = response.status;
658
401
  } else if (callback) {
659
- import_log3.log.info("exec", {
660
- function: def.uri
661
- }, {
662
- F: __dxlog_file3,
663
- L: 111,
664
- S: this,
665
- C: (f, a) => f(...a)
666
- });
667
- status = await callback(payload) ?? 200;
668
- }
669
- if (status && status >= 400) {
670
- throw new Error(`Response: ${status}`);
402
+ await callback(data);
671
403
  }
672
404
  import_log3.log.info("done", {
673
- function: def.uri,
674
- status
405
+ function: def.id
675
406
  }, {
676
407
  F: __dxlog_file3,
677
- L: 121,
408
+ L: 133,
678
409
  S: this,
679
410
  C: (f, a) => f(...a)
680
411
  });
681
412
  } catch (err) {
682
413
  import_log3.log.error("error", {
683
- function: def.uri,
414
+ function: def.id,
684
415
  error: err.message
685
416
  }, {
686
417
  F: __dxlog_file3,
687
- L: 123,
418
+ L: 135,
688
419
  S: this,
689
420
  C: (f, a) => f(...a)
690
421
  });
691
- status = 500;
692
422
  }
693
- return status;
694
423
  }
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
424
+ //
425
+ // Triggers
426
+ //
427
+ /**
428
+ * Cron timer.
429
+ */
430
+ async _createTimer(ctx, space, def, trigger) {
431
+ import_log3.log.info("timer", {
432
+ space: space.key,
433
+ trigger
715
434
  }, {
716
- F: __dxlog_file4,
717
- L: 32,
718
- S: void 0,
435
+ F: __dxlog_file3,
436
+ L: 147,
437
+ S: this,
719
438
  C: (f, a) => f(...a)
720
439
  });
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)
440
+ const { cron } = trigger;
441
+ const task = new import_async2.DeferredTask(ctx, async () => {
442
+ await this._execFunction(def, {
443
+ space: space.key
782
444
  });
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
445
  });
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
846
- }, {
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
446
+ let last = 0;
447
+ let run = 0;
448
+ const job = import_cron.CronJob.from({
449
+ cronTime: cron,
450
+ runOnInit: false,
451
+ onTick: () => {
452
+ const now = Date.now();
453
+ const delta = last ? now - last : 0;
454
+ last = now;
455
+ run++;
456
+ import_log3.log.info("tick", {
457
+ space: space.key.truncate(),
458
+ count: run,
459
+ delta
870
460
  }, {
871
- F: __dxlog_file7,
872
- L: 62,
873
- S: void 0,
461
+ F: __dxlog_file3,
462
+ L: 167,
463
+ S: this,
874
464
  C: (f, a) => f(...a)
875
465
  });
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
- }
466
+ task.schedule();
899
467
  }
900
468
  });
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);
941
- }
942
- getInactiveTriggers(space) {
943
- return this._getTriggers(space, (t) => t.activationCtx == null);
469
+ job.start();
470
+ ctx.onDispose(() => job.stop());
944
471
  }
945
- async activate(triggerCtx, trigger, callback) {
946
- (0, import_log4.log)("activate", {
947
- space: triggerCtx.space.key,
472
+ /**
473
+ * Webhook.
474
+ */
475
+ async _createWebhook(ctx, space, def, trigger) {
476
+ import_log3.log.info("webhook", {
477
+ space: space.key,
948
478
  trigger
949
479
  }, {
950
- F: __dxlog_file8,
951
- L: 73,
480
+ F: __dxlog_file3,
481
+ L: 180,
952
482
  S: this,
953
483
  C: (f, a) => f(...a)
954
484
  });
955
- const activationCtx = new import_context4.Context({
956
- name: `trigger_${trigger.function}`
485
+ const { port } = trigger;
486
+ const server = import_node_http.default.createServer(async (req, res) => {
487
+ await this._execFunction(def, {
488
+ space: space.key
489
+ });
957
490
  });
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
- ]
491
+ server.listen(port, () => {
492
+ import_log3.log.info("started webhook", {
493
+ port
494
+ }, {
495
+ F: __dxlog_file3,
496
+ L: 189,
497
+ S: this,
498
+ C: (f, a) => f(...a)
499
+ });
500
+ });
501
+ ctx.onDispose(() => {
502
+ server.close();
968
503
  });
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
504
  }
978
505
  /**
979
- * Loads triggers from the manifest into the space.
506
+ * Websocket.
980
507
  */
981
- async register(space, manifest) {
982
- (0, import_log4.log)("register", {
983
- space: space.key
508
+ async _createWebsocket(ctx, space, def, trigger, options = {
509
+ retryDelay: 2,
510
+ maxAttempts: 5
511
+ }) {
512
+ import_log3.log.info("websocket", {
513
+ space: space.key,
514
+ trigger
984
515
  }, {
985
- F: __dxlog_file8,
986
- L: 95,
516
+ F: __dxlog_file3,
517
+ L: 213,
987
518
  S: this,
988
519
  C: (f, a) => f(...a)
989
520
  });
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;
521
+ const { url } = trigger;
522
+ let ws;
523
+ for (let attempt = 1; attempt <= options.maxAttempts; attempt++) {
524
+ const open = new import_async2.Trigger();
525
+ ws = new import_ws.default(url);
526
+ Object.assign(ws, {
527
+ onopen: () => {
528
+ import_log3.log.info("opened", {
529
+ url
530
+ }, {
531
+ F: __dxlog_file3,
532
+ L: 223,
533
+ S: this,
534
+ C: (f, a) => f(...a)
535
+ });
536
+ if (trigger.init) {
537
+ ws.send(new TextEncoder().encode(JSON.stringify(trigger.init)));
538
+ }
539
+ open.wake(true);
540
+ },
541
+ onclose: () => {
542
+ import_log3.log.info("closed", {
543
+ url
544
+ }, {
545
+ F: __dxlog_file3,
546
+ L: 232,
547
+ S: this,
548
+ C: (f, a) => f(...a)
549
+ });
550
+ open.wake(false);
551
+ },
552
+ onerror: (event) => {
553
+ import_log3.log.catch(event.error, {
554
+ url
555
+ }, {
556
+ F: __dxlog_file3,
557
+ L: 237,
558
+ S: this,
559
+ C: (f, a) => f(...a)
560
+ });
561
+ },
562
+ onmessage: async (event) => {
563
+ try {
564
+ const data = JSON.parse(new TextDecoder().decode(event.data));
565
+ await this._execFunction(def, {
566
+ space: space.key,
567
+ data
568
+ });
569
+ } catch (err) {
570
+ import_log3.log.catch(err, {
571
+ url
572
+ }, {
573
+ F: __dxlog_file3,
574
+ L: 245,
575
+ S: this,
576
+ C: (f, a) => f(...a)
577
+ });
578
+ }
1006
579
  }
1007
- const registered = [];
1008
- this._triggersBySpaceKey.set(space.key, registered);
1009
- await space.waitUntilReady();
1010
- if (this._ctx.disposed) {
1011
- break;
580
+ });
581
+ const isOpen = await open.wait();
582
+ if (isOpen) {
583
+ break;
584
+ } else {
585
+ const wait = Math.pow(attempt, 2) * options.retryDelay;
586
+ if (attempt < options.maxAttempts) {
587
+ import_log3.log.warn(`failed to connect; trying again in ${wait}s`, {
588
+ attempt
589
+ }, {
590
+ F: __dxlog_file3,
591
+ L: 256,
592
+ S: this,
593
+ C: (f, a) => f(...a)
594
+ });
595
+ await (0, import_async2.sleep)(wait * 1e3);
1012
596
  }
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
597
  }
598
+ }
599
+ ctx.onDispose(() => {
600
+ ws?.close();
1019
601
  });
1020
- this._ctx.onDispose(() => spaceListSubscription.unsubscribe());
1021
- }
1022
- async _close(_) {
1023
- this._triggersBySpaceKey.clear();
1024
602
  }
1025
- _handleNewTriggers(space, allTriggers, registered) {
1026
- const newTriggers = allTriggers.filter((candidate) => {
1027
- return registered.find((reg) => reg.trigger.id === candidate.id) == null;
603
+ /**
604
+ * ECHO subscription.
605
+ */
606
+ async _createSubscription(ctx, space, def, trigger) {
607
+ import_log3.log.info("subscription", {
608
+ space: space.key,
609
+ trigger
610
+ }, {
611
+ F: __dxlog_file3,
612
+ L: 271,
613
+ S: this,
614
+ C: (f, a) => f(...a)
1028
615
  });
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", () => ({
1035
- spaceKey: space.key,
1036
- functions: newTriggers.map((t) => t.function)
1037
- }), {
1038
- F: __dxlog_file8,
1039
- L: 146,
616
+ const objectIds = /* @__PURE__ */ new Set();
617
+ const task = new import_async2.DeferredTask(ctx, async () => {
618
+ await this._execFunction(def, {
619
+ space: space.key,
620
+ objects: Array.from(objectIds)
621
+ });
622
+ });
623
+ const subscriptions = [];
624
+ const subscription = (0, import_echo.createSubscription)(({ added, updated }) => {
625
+ import_log3.log.info("updated", {
626
+ added: added.length,
627
+ updated: updated.length
628
+ }, {
629
+ F: __dxlog_file3,
630
+ L: 281,
1040
631
  S: this,
1041
632
  C: (f, a) => f(...a)
1042
633
  });
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);
634
+ for (const object of added) {
635
+ objectIds.add(object.id);
1057
636
  }
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);
637
+ for (const object of updated) {
638
+ objectIds.add(object.id);
639
+ }
640
+ task.schedule();
641
+ });
642
+ subscriptions.push(() => subscription.unsubscribe());
643
+ const { filter, options: { deep, delay } = {} } = trigger;
644
+ const update = ({ objects }) => {
645
+ subscription.update(objects);
646
+ if (deep) {
647
+ import_log3.log.info("update", {
648
+ objects: objects.length
649
+ }, {
650
+ F: __dxlog_file3,
651
+ L: 301,
652
+ S: this,
653
+ C: (f, a) => f(...a)
654
+ });
655
+ for (const object of objects) {
656
+ const content = object.content;
657
+ if (content instanceof import_types.TextV0Type) {
658
+ subscriptions.push((0, import_echo.getAutomergeObjectCore)(content).updates.on((0, import_async2.debounce)(() => subscription.update([
659
+ object
660
+ ]), 1e3)));
661
+ }
662
+ }
663
+ }
664
+ };
665
+ const query = space.db.query(import_echo.Filter.or(filter.map(({ type, props }) => import_echo.Filter.typename(type, props))));
666
+ subscriptions.push(query.subscribe(delay ? (0, import_async2.debounce)(update, delay) : update));
667
+ ctx.onDispose(() => {
668
+ subscriptions.forEach((unsubscribe) => unsubscribe());
669
+ });
1069
670
  }
1070
671
  };
672
+ var TimerTriggerSchema = S.struct({
673
+ cron: S.string
674
+ });
675
+ var WebhookTriggerSchema = S.struct({
676
+ port: S.number
677
+ });
678
+ var WebsocketTriggerSchema = S.struct({
679
+ url: S.string,
680
+ init: S.optional(S.record(S.string, S.any))
681
+ });
682
+ var SubscriptionTriggerSchema = S.struct({
683
+ spaceKey: S.optional(S.string),
684
+ // TODO(burdon): Define query DSL.
685
+ filter: S.array(S.struct({
686
+ type: S.string,
687
+ props: S.optional(S.record(S.string, S.any))
688
+ })),
689
+ options: S.optional(S.struct({
690
+ // Watch changes to object (not just creation).
691
+ deep: S.optional(S.boolean),
692
+ // Debounce changes (delay in ms).
693
+ delay: S.optional(S.number)
694
+ }))
695
+ });
696
+ var FunctionTriggerSchema = S.struct({
697
+ function: S.string.pipe(S.description("Function ID/URI.")),
698
+ timer: S.optional(TimerTriggerSchema),
699
+ webhook: S.optional(WebhookTriggerSchema),
700
+ websocket: S.optional(WebsocketTriggerSchema),
701
+ subscription: S.optional(SubscriptionTriggerSchema)
702
+ });
703
+ var FunctionDefSchema = S.struct({
704
+ id: S.string,
705
+ description: S.optional(S.string),
706
+ name: S.string,
707
+ // TODO(burdon): NPM/GitHub URL?
708
+ handler: S.string
709
+ });
710
+ var FunctionManifestSchema = S.struct({
711
+ functions: S.mutable(S.array(FunctionDefSchema)),
712
+ triggers: S.mutable(S.array(FunctionTriggerSchema))
713
+ });
1071
714
  // Annotate the CommonJS export names for ESM import in node:
1072
715
  0 && (module.exports = {
1073
716
  DevServer,
1074
- FunctionDef,
1075
717
  FunctionManifestSchema,
1076
- FunctionRegistry,
1077
- FunctionTrigger,
1078
718
  Scheduler,
1079
- TriggerRegistry,
1080
719
  subscriptionHandler
1081
720
  });
1082
721
  //# sourceMappingURL=index.cjs.map