@dxos/functions 0.5.3-main.f9b873d → 0.5.3-next.57eca40

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