@dxos/functions 0.5.3-main.2ad0afc → 0.5.3-main.3456876

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