@dxos/functions 0.6.10 → 0.6.11-staging.32b42e4

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 (44) hide show
  1. package/dist/lib/browser/{chunk-YSDC6YCF.mjs → chunk-2I75VGHZ.mjs} +3 -3
  2. package/dist/lib/browser/{chunk-YSDC6YCF.mjs.map → chunk-2I75VGHZ.mjs.map} +3 -3
  3. package/dist/lib/browser/chunk-CRAAIWU6.mjs +590 -0
  4. package/dist/lib/browser/chunk-CRAAIWU6.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +9 -7
  6. package/dist/lib/browser/index.mjs.map +1 -1
  7. package/dist/lib/browser/meta.json +1 -1
  8. package/dist/lib/browser/testing/index.mjs +522 -11
  9. package/dist/lib/browser/testing/index.mjs.map +4 -4
  10. package/dist/lib/browser/types.mjs +1 -1
  11. package/dist/lib/node/{chunk-3E6PY6JH.cjs → chunk-JV3VNH5X.cjs} +6 -6
  12. package/dist/lib/node/{chunk-3E6PY6JH.cjs.map → chunk-JV3VNH5X.cjs.map} +3 -3
  13. package/dist/lib/node/chunk-OGLDVNFE.cjs +613 -0
  14. package/dist/lib/node/chunk-OGLDVNFE.cjs.map +7 -0
  15. package/dist/lib/node/index.cjs +14 -12
  16. package/dist/lib/node/index.cjs.map +1 -1
  17. package/dist/lib/node/meta.json +1 -1
  18. package/dist/lib/node/testing/index.cjs +521 -13
  19. package/dist/lib/node/testing/index.cjs.map +4 -4
  20. package/dist/lib/node/types.cjs +5 -5
  21. package/dist/lib/node/types.cjs.map +1 -1
  22. package/dist/types/src/index.d.ts +0 -1
  23. package/dist/types/src/index.d.ts.map +1 -1
  24. package/dist/types/src/runtime/scheduler.d.ts.map +1 -1
  25. package/dist/types/src/trigger/index.d.ts +1 -0
  26. package/dist/types/src/trigger/index.d.ts.map +1 -1
  27. package/dist/types/src/trigger/trigger-registry.d.ts +5 -4
  28. package/dist/types/src/trigger/trigger-registry.d.ts.map +1 -1
  29. package/dist/types/src/trigger/type/index.d.ts +0 -1
  30. package/dist/types/src/trigger/type/index.d.ts.map +1 -1
  31. package/dist/types/src/types.d.ts +4 -0
  32. package/dist/types/src/types.d.ts.map +1 -1
  33. package/package.json +14 -14
  34. package/src/index.ts +1 -1
  35. package/src/runtime/scheduler.ts +6 -3
  36. package/src/trigger/index.ts +1 -0
  37. package/src/trigger/trigger-registry.test.ts +2 -1
  38. package/src/trigger/trigger-registry.ts +15 -7
  39. package/src/trigger/type/index.ts +1 -1
  40. package/src/types.ts +1 -0
  41. package/dist/lib/browser/chunk-OERXFETS.mjs +0 -1120
  42. package/dist/lib/browser/chunk-OERXFETS.mjs.map +0 -7
  43. package/dist/lib/node/chunk-ITQU6E54.cjs +0 -1133
  44. package/dist/lib/node/chunk-ITQU6E54.cjs.map +0 -7
@@ -2,8 +2,7 @@ import "@dxos/node-std/globals";
2
2
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
3
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
4
  }) : x)(function(x) {
5
- if (typeof require !== "undefined")
6
- return require.apply(this, arguments);
5
+ if (typeof require !== "undefined") return require.apply(this, arguments);
7
6
  throw Error('Dynamic require of "' + x + '" is not supported');
8
7
  });
9
8
 
@@ -60,6 +59,7 @@ var FunctionTrigger = class extends TypedObject({
60
59
  typename: "dxos.org/type/FunctionTrigger",
61
60
  version: "0.1.0"
62
61
  })({
62
+ name: S.optional(S.String),
63
63
  enabled: S.optional(S.Boolean),
64
64
  function: S.String.pipe(S.description("Function URI.")),
65
65
  // The `meta` property is merged into the event data passed to the function.
@@ -83,4 +83,4 @@ export {
83
83
  FunctionManifestSchema,
84
84
  FUNCTION_SCHEMA
85
85
  };
86
- //# sourceMappingURL=chunk-YSDC6YCF.mjs.map
86
+ //# sourceMappingURL=chunk-2I75VGHZ.mjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/types.ts"],
4
- "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { RawObject, S, TypedObject } from '@dxos/echo-schema';\n\n/**\n * Type discriminator for TriggerSpec.\n * Every spec has a type field of type FunctionTriggerType that we can use to understand which\n * type we're working with.\n * https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions\n */\nexport type FunctionTriggerType = 'subscription' | 'timer' | 'webhook' | 'websocket';\n\nconst SubscriptionTriggerSchema = S.mutable(\n S.Struct({\n type: S.Literal('subscription'),\n // TODO(burdon): Define query DSL (from ECHO).\n filter: S.Array(\n S.Struct({\n type: S.String,\n props: S.optional(S.Record(S.String, S.Any)),\n }),\n ),\n options: S.optional(\n S.Struct({\n // Watch changes to object (not just creation).\n deep: S.optional(S.Boolean),\n // Debounce changes (delay in ms).\n delay: S.optional(S.Number),\n }),\n ),\n }),\n);\n\nexport type SubscriptionTrigger = S.Schema.Type<typeof SubscriptionTriggerSchema>;\n\nconst TimerTriggerSchema = S.mutable(\n S.Struct({\n type: S.Literal('timer'),\n cron: S.String,\n }),\n);\n\nexport type TimerTrigger = S.Schema.Type<typeof TimerTriggerSchema>;\n\nconst WebhookTriggerSchema = S.mutable(\n S.Struct({\n type: S.Literal('webhook'),\n method: S.String,\n // Assigned port.\n port: S.optional(S.Number),\n }),\n);\n\nexport type WebhookTrigger = S.Schema.Type<typeof WebhookTriggerSchema>;\n\nconst WebsocketTriggerSchema = S.mutable(\n S.Struct({\n type: S.Literal('websocket'),\n url: S.String,\n init: S.optional(S.Record(S.String, S.Any)),\n }),\n);\n\nexport type WebsocketTrigger = S.Schema.Type<typeof WebsocketTriggerSchema>;\n\nconst TriggerSpecSchema = S.Union(\n TimerTriggerSchema,\n WebhookTriggerSchema,\n WebsocketTriggerSchema,\n SubscriptionTriggerSchema,\n);\n\nexport type TriggerSpec = TimerTrigger | WebhookTrigger | WebsocketTrigger | SubscriptionTrigger;\n\n/**\n * Function definition.\n */\nexport class FunctionDef extends TypedObject({\n typename: 'dxos.org/type/FunctionDef',\n version: '0.1.0',\n})({\n uri: S.String,\n description: S.optional(S.String),\n route: S.String,\n handler: S.String,\n}) {}\n\n/**\n * Function trigger.\n */\nexport class FunctionTrigger extends TypedObject({\n typename: 'dxos.org/type/FunctionTrigger',\n version: '0.1.0',\n})({\n enabled: S.optional(S.Boolean),\n function: S.String.pipe(S.description('Function URI.')),\n // The `meta` property is merged into the event data passed to the function.\n meta: S.optional(S.mutable(S.Record(S.String, S.Any))),\n spec: TriggerSpecSchema,\n}) {}\n\n/**\n * Function manifest file.\n */\nexport const FunctionManifestSchema = S.Struct({\n functions: S.optional(S.mutable(S.Array(RawObject(FunctionDef)))),\n triggers: S.optional(S.mutable(S.Array(RawObject(FunctionTrigger)))),\n});\n\nexport type FunctionManifest = S.Schema.Type<typeof FunctionManifestSchema>;\n\n// TODO(burdon): Standards?\nexport const FUNCTION_SCHEMA = [FunctionDef, FunctionTrigger];\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;AAIA,SAASA,WAAWC,GAAGC,mBAAmB;AAU1C,IAAMC,4BAA4BC,EAAEC,QAClCD,EAAEE,OAAO;EACPC,MAAMH,EAAEI,QAAQ,cAAA;;EAEhBC,QAAQL,EAAEM,MACRN,EAAEE,OAAO;IACPC,MAAMH,EAAEO;IACRC,OAAOR,EAAES,SAAST,EAAEU,OAAOV,EAAEO,QAAQP,EAAEW,GAAG,CAAA;EAC5C,CAAA,CAAA;EAEFC,SAASZ,EAAES,SACTT,EAAEE,OAAO;;IAEPW,MAAMb,EAAES,SAAST,EAAEc,OAAO;;IAE1BC,OAAOf,EAAES,SAAST,EAAEgB,MAAM;EAC5B,CAAA,CAAA;AAEJ,CAAA,CAAA;AAKF,IAAMC,qBAAqBjB,EAAEC,QAC3BD,EAAEE,OAAO;EACPC,MAAMH,EAAEI,QAAQ,OAAA;EAChBc,MAAMlB,EAAEO;AACV,CAAA,CAAA;AAKF,IAAMY,uBAAuBnB,EAAEC,QAC7BD,EAAEE,OAAO;EACPC,MAAMH,EAAEI,QAAQ,SAAA;EAChBgB,QAAQpB,EAAEO;;EAEVc,MAAMrB,EAAES,SAAST,EAAEgB,MAAM;AAC3B,CAAA,CAAA;AAKF,IAAMM,yBAAyBtB,EAAEC,QAC/BD,EAAEE,OAAO;EACPC,MAAMH,EAAEI,QAAQ,WAAA;EAChBmB,KAAKvB,EAAEO;EACPiB,MAAMxB,EAAES,SAAST,EAAEU,OAAOV,EAAEO,QAAQP,EAAEW,GAAG,CAAA;AAC3C,CAAA,CAAA;AAKF,IAAMc,oBAAoBzB,EAAE0B,MAC1BT,oBACAE,sBACAG,wBACAvB,yBAAAA;AAQK,IAAM4B,cAAN,cAA0BC,YAAY;EAC3CC,UAAU;EACVC,SAAS;AACX,CAAA,EAAG;EACDC,KAAK/B,EAAEO;EACPyB,aAAahC,EAAES,SAAST,EAAEO,MAAM;EAChC0B,OAAOjC,EAAEO;EACT2B,SAASlC,EAAEO;AACb,CAAA,EAAA;AAAI;AAKG,IAAM4B,kBAAN,cAA8BP,YAAY;EAC/CC,UAAU;EACVC,SAAS;AACX,CAAA,EAAG;EACDM,SAASpC,EAAES,SAAST,EAAEc,OAAO;EAC7BuB,UAAUrC,EAAEO,OAAO+B,KAAKtC,EAAEgC,YAAY,eAAA,CAAA;;EAEtCO,MAAMvC,EAAES,SAAST,EAAEC,QAAQD,EAAEU,OAAOV,EAAEO,QAAQP,EAAEW,GAAG,CAAA,CAAA;EACnD6B,MAAMf;AACR,CAAA,EAAA;AAAI;AAKG,IAAMgB,yBAAyBzC,EAAEE,OAAO;EAC7CwC,WAAW1C,EAAES,SAAST,EAAEC,QAAQD,EAAEM,MAAMqC,UAAUhB,WAAAA,CAAAA,CAAAA,CAAAA;EAClDiB,UAAU5C,EAAES,SAAST,EAAEC,QAAQD,EAAEM,MAAMqC,UAAUR,eAAAA,CAAAA,CAAAA,CAAAA;AACnD,CAAA;AAKO,IAAMU,kBAAkB;EAAClB;EAAaQ;;",
6
- "names": ["RawObject", "S", "TypedObject", "SubscriptionTriggerSchema", "S", "mutable", "Struct", "type", "Literal", "filter", "Array", "String", "props", "optional", "Record", "Any", "options", "deep", "Boolean", "delay", "Number", "TimerTriggerSchema", "cron", "WebhookTriggerSchema", "method", "port", "WebsocketTriggerSchema", "url", "init", "TriggerSpecSchema", "Union", "FunctionDef", "TypedObject", "typename", "version", "uri", "description", "route", "handler", "FunctionTrigger", "enabled", "function", "pipe", "meta", "spec", "FunctionManifestSchema", "functions", "RawObject", "triggers", "FUNCTION_SCHEMA"]
4
+ "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { RawObject, S, TypedObject } from '@dxos/echo-schema';\n\n/**\n * Type discriminator for TriggerSpec.\n * Every spec has a type field of type FunctionTriggerType that we can use to understand which\n * type we're working with.\n * https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions\n */\nexport type FunctionTriggerType = 'subscription' | 'timer' | 'webhook' | 'websocket';\n\nconst SubscriptionTriggerSchema = S.mutable(\n S.Struct({\n type: S.Literal('subscription'),\n // TODO(burdon): Define query DSL (from ECHO).\n filter: S.Array(\n S.Struct({\n type: S.String,\n props: S.optional(S.Record(S.String, S.Any)),\n }),\n ),\n options: S.optional(\n S.Struct({\n // Watch changes to object (not just creation).\n deep: S.optional(S.Boolean),\n // Debounce changes (delay in ms).\n delay: S.optional(S.Number),\n }),\n ),\n }),\n);\n\nexport type SubscriptionTrigger = S.Schema.Type<typeof SubscriptionTriggerSchema>;\n\nconst TimerTriggerSchema = S.mutable(\n S.Struct({\n type: S.Literal('timer'),\n cron: S.String,\n }),\n);\n\nexport type TimerTrigger = S.Schema.Type<typeof TimerTriggerSchema>;\n\nconst WebhookTriggerSchema = S.mutable(\n S.Struct({\n type: S.Literal('webhook'),\n method: S.String,\n // Assigned port.\n port: S.optional(S.Number),\n }),\n);\n\nexport type WebhookTrigger = S.Schema.Type<typeof WebhookTriggerSchema>;\n\nconst WebsocketTriggerSchema = S.mutable(\n S.Struct({\n type: S.Literal('websocket'),\n url: S.String,\n init: S.optional(S.Record(S.String, S.Any)),\n }),\n);\n\nexport type WebsocketTrigger = S.Schema.Type<typeof WebsocketTriggerSchema>;\n\nconst TriggerSpecSchema = S.Union(\n TimerTriggerSchema,\n WebhookTriggerSchema,\n WebsocketTriggerSchema,\n SubscriptionTriggerSchema,\n);\n\nexport type TriggerSpec = TimerTrigger | WebhookTrigger | WebsocketTrigger | SubscriptionTrigger;\n\n/**\n * Function definition.\n */\nexport class FunctionDef extends TypedObject({\n typename: 'dxos.org/type/FunctionDef',\n version: '0.1.0',\n})({\n uri: S.String,\n description: S.optional(S.String),\n route: S.String,\n handler: S.String,\n}) {}\n\n/**\n * Function trigger.\n */\nexport class FunctionTrigger extends TypedObject({\n typename: 'dxos.org/type/FunctionTrigger',\n version: '0.1.0',\n})({\n name: S.optional(S.String),\n enabled: S.optional(S.Boolean),\n function: S.String.pipe(S.description('Function URI.')),\n // The `meta` property is merged into the event data passed to the function.\n meta: S.optional(S.mutable(S.Record(S.String, S.Any))),\n spec: TriggerSpecSchema,\n}) {}\n\n/**\n * Function manifest file.\n */\nexport const FunctionManifestSchema = S.Struct({\n functions: S.optional(S.mutable(S.Array(RawObject(FunctionDef)))),\n triggers: S.optional(S.mutable(S.Array(RawObject(FunctionTrigger)))),\n});\n\nexport type FunctionManifest = S.Schema.Type<typeof FunctionManifestSchema>;\n\n// TODO(burdon): Standards?\nexport const FUNCTION_SCHEMA = [FunctionDef, FunctionTrigger];\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;AAIA,SAASA,WAAWC,GAAGC,mBAAmB;AAU1C,IAAMC,4BAA4BC,EAAEC,QAClCD,EAAEE,OAAO;EACPC,MAAMH,EAAEI,QAAQ,cAAA;;EAEhBC,QAAQL,EAAEM,MACRN,EAAEE,OAAO;IACPC,MAAMH,EAAEO;IACRC,OAAOR,EAAES,SAAST,EAAEU,OAAOV,EAAEO,QAAQP,EAAEW,GAAG,CAAA;EAC5C,CAAA,CAAA;EAEFC,SAASZ,EAAES,SACTT,EAAEE,OAAO;;IAEPW,MAAMb,EAAES,SAAST,EAAEc,OAAO;;IAE1BC,OAAOf,EAAES,SAAST,EAAEgB,MAAM;EAC5B,CAAA,CAAA;AAEJ,CAAA,CAAA;AAKF,IAAMC,qBAAqBjB,EAAEC,QAC3BD,EAAEE,OAAO;EACPC,MAAMH,EAAEI,QAAQ,OAAA;EAChBc,MAAMlB,EAAEO;AACV,CAAA,CAAA;AAKF,IAAMY,uBAAuBnB,EAAEC,QAC7BD,EAAEE,OAAO;EACPC,MAAMH,EAAEI,QAAQ,SAAA;EAChBgB,QAAQpB,EAAEO;;EAEVc,MAAMrB,EAAES,SAAST,EAAEgB,MAAM;AAC3B,CAAA,CAAA;AAKF,IAAMM,yBAAyBtB,EAAEC,QAC/BD,EAAEE,OAAO;EACPC,MAAMH,EAAEI,QAAQ,WAAA;EAChBmB,KAAKvB,EAAEO;EACPiB,MAAMxB,EAAES,SAAST,EAAEU,OAAOV,EAAEO,QAAQP,EAAEW,GAAG,CAAA;AAC3C,CAAA,CAAA;AAKF,IAAMc,oBAAoBzB,EAAE0B,MAC1BT,oBACAE,sBACAG,wBACAvB,yBAAAA;AAQK,IAAM4B,cAAN,cAA0BC,YAAY;EAC3CC,UAAU;EACVC,SAAS;AACX,CAAA,EAAG;EACDC,KAAK/B,EAAEO;EACPyB,aAAahC,EAAES,SAAST,EAAEO,MAAM;EAChC0B,OAAOjC,EAAEO;EACT2B,SAASlC,EAAEO;AACb,CAAA,EAAA;AAAI;AAKG,IAAM4B,kBAAN,cAA8BP,YAAY;EAC/CC,UAAU;EACVC,SAAS;AACX,CAAA,EAAG;EACDM,MAAMpC,EAAES,SAAST,EAAEO,MAAM;EACzB8B,SAASrC,EAAES,SAAST,EAAEc,OAAO;EAC7BwB,UAAUtC,EAAEO,OAAOgC,KAAKvC,EAAEgC,YAAY,eAAA,CAAA;;EAEtCQ,MAAMxC,EAAES,SAAST,EAAEC,QAAQD,EAAEU,OAAOV,EAAEO,QAAQP,EAAEW,GAAG,CAAA,CAAA;EACnD8B,MAAMhB;AACR,CAAA,EAAA;AAAI;AAKG,IAAMiB,yBAAyB1C,EAAEE,OAAO;EAC7CyC,WAAW3C,EAAES,SAAST,EAAEC,QAAQD,EAAEM,MAAMsC,UAAUjB,WAAAA,CAAAA,CAAAA,CAAAA;EAClDkB,UAAU7C,EAAES,SAAST,EAAEC,QAAQD,EAAEM,MAAMsC,UAAUT,eAAAA,CAAAA,CAAAA,CAAAA;AACnD,CAAA;AAKO,IAAMW,kBAAkB;EAACnB;EAAaQ;;",
6
+ "names": ["RawObject", "S", "TypedObject", "SubscriptionTriggerSchema", "S", "mutable", "Struct", "type", "Literal", "filter", "Array", "String", "props", "optional", "Record", "Any", "options", "deep", "Boolean", "delay", "Number", "TimerTriggerSchema", "cron", "WebhookTriggerSchema", "method", "port", "WebsocketTriggerSchema", "url", "init", "TriggerSpecSchema", "Union", "FunctionDef", "TypedObject", "typename", "version", "uri", "description", "route", "handler", "FunctionTrigger", "name", "enabled", "function", "pipe", "meta", "spec", "FunctionManifestSchema", "functions", "RawObject", "triggers", "FUNCTION_SCHEMA"]
7
7
  }
@@ -0,0 +1,590 @@
1
+ import "@dxos/node-std/globals";
2
+ import {
3
+ FunctionDef,
4
+ FunctionTrigger
5
+ } from "./chunk-2I75VGHZ.mjs";
6
+
7
+ // packages/core/functions/src/function/function-registry.ts
8
+ import { Event } from "@dxos/async";
9
+ import { create, Filter } from "@dxos/client/echo";
10
+ import { Resource } from "@dxos/context";
11
+ import { PublicKey } from "@dxos/keys";
12
+ import { log } from "@dxos/log";
13
+ import { ComplexMap, diff } from "@dxos/util";
14
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/functions/src/function/function-registry.ts";
15
+ var FunctionRegistry = class extends Resource {
16
+ constructor(_client) {
17
+ super();
18
+ this._client = _client;
19
+ this._functionBySpaceKey = new ComplexMap(PublicKey.hash);
20
+ this.registered = new Event();
21
+ }
22
+ getFunctions(space) {
23
+ return this._functionBySpaceKey.get(space.key) ?? [];
24
+ }
25
+ getUniqueByUri() {
26
+ const uniqueByUri = [
27
+ ...this._functionBySpaceKey.values()
28
+ ].flatMap((defs) => defs).reduce((acc, v) => {
29
+ acc.set(v.uri, v);
30
+ return acc;
31
+ }, /* @__PURE__ */ new Map());
32
+ return [
33
+ ...uniqueByUri.values()
34
+ ];
35
+ }
36
+ /**
37
+ * Loads function definitions from the manifest into the space.
38
+ * We first load all the definitions from the space to deduplicate by functionId.
39
+ */
40
+ async register(space, functions) {
41
+ log("register", {
42
+ space: space.key,
43
+ functions: functions?.length ?? 0
44
+ }, {
45
+ F: __dxlog_file,
46
+ L: 48,
47
+ S: this,
48
+ C: (f, a) => f(...a)
49
+ });
50
+ if (!functions?.length) {
51
+ return;
52
+ }
53
+ if (!space.db.graph.schemaRegistry.hasSchema(FunctionDef)) {
54
+ space.db.graph.schemaRegistry.addSchema([
55
+ FunctionDef
56
+ ]);
57
+ }
58
+ const { objects: existing } = await space.db.query(Filter.schema(FunctionDef)).run();
59
+ const { added } = diff(existing, functions, (a, b) => a.uri === b.uri);
60
+ added.forEach((def) => space.db.add(create(FunctionDef, def)));
61
+ if (added.length > 0) {
62
+ await space.db.flush({
63
+ indexes: true,
64
+ updates: true
65
+ });
66
+ }
67
+ }
68
+ async _open() {
69
+ log.info("opening...", void 0, {
70
+ F: __dxlog_file,
71
+ L: 68,
72
+ S: this,
73
+ C: (f, a) => f(...a)
74
+ });
75
+ const spacesSubscription = this._client.spaces.subscribe(async (spaces) => {
76
+ for (const space of spaces) {
77
+ if (this._functionBySpaceKey.has(space.key)) {
78
+ continue;
79
+ }
80
+ const registered = [];
81
+ this._functionBySpaceKey.set(space.key, registered);
82
+ await space.waitUntilReady();
83
+ if (this._ctx.disposed) {
84
+ break;
85
+ }
86
+ this._ctx.onDispose(space.db.query(Filter.schema(FunctionDef)).subscribe(({ objects }) => {
87
+ const { added } = diff(registered, objects, (a, b) => a.uri === b.uri);
88
+ if (added.length > 0) {
89
+ registered.push(...added);
90
+ this.registered.emit({
91
+ space,
92
+ added
93
+ });
94
+ }
95
+ }));
96
+ }
97
+ });
98
+ this._ctx.onDispose(() => spacesSubscription.unsubscribe());
99
+ }
100
+ async _close(_) {
101
+ log.info("closing...", void 0, {
102
+ F: __dxlog_file,
103
+ L: 101,
104
+ S: this,
105
+ C: (f, a) => f(...a)
106
+ });
107
+ this._functionBySpaceKey.clear();
108
+ }
109
+ };
110
+
111
+ // packages/core/functions/src/trigger/type/subscription-trigger.ts
112
+ import { debounce, UpdateScheduler } from "@dxos/async";
113
+ import { Filter as Filter2 } from "@dxos/client/echo";
114
+ import { createSubscription } from "@dxos/echo-db";
115
+ import { log as log2 } from "@dxos/log";
116
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/functions/src/trigger/type/subscription-trigger.ts";
117
+ var createSubscriptionTrigger = async (ctx, space, spec, callback) => {
118
+ const objectIds = /* @__PURE__ */ new Set();
119
+ const task = new UpdateScheduler(ctx, async () => {
120
+ if (objectIds.size > 0) {
121
+ const objects = Array.from(objectIds);
122
+ objectIds.clear();
123
+ await callback({
124
+ objects
125
+ });
126
+ }
127
+ }, {
128
+ maxFrequency: 4
129
+ });
130
+ const subscriptions = [];
131
+ const subscription = createSubscription(({ added, updated }) => {
132
+ const sizeBefore = objectIds.size;
133
+ for (const object of added) {
134
+ objectIds.add(object.id);
135
+ }
136
+ for (const object of updated) {
137
+ objectIds.add(object.id);
138
+ }
139
+ if (objectIds.size > sizeBefore) {
140
+ log2.info("updated", {
141
+ added: added.length,
142
+ updated: updated.length
143
+ }, {
144
+ F: __dxlog_file2,
145
+ L: 46,
146
+ S: void 0,
147
+ C: (f, a) => f(...a)
148
+ });
149
+ task.trigger();
150
+ }
151
+ });
152
+ subscriptions.push(() => subscription.unsubscribe());
153
+ const { filter, options: { deep, delay } = {} } = spec;
154
+ const update = ({ objects }) => {
155
+ log2.info("update", {
156
+ objects: objects.length
157
+ }, {
158
+ F: __dxlog_file2,
159
+ L: 56,
160
+ S: void 0,
161
+ C: (f, a) => f(...a)
162
+ });
163
+ subscription.update(objects);
164
+ if (deep) {
165
+ }
166
+ };
167
+ log2.info("subscription", {
168
+ filter
169
+ }, {
170
+ F: __dxlog_file2,
171
+ L: 74,
172
+ S: void 0,
173
+ C: (f, a) => f(...a)
174
+ });
175
+ if (filter) {
176
+ const query = space.db.query(Filter2.typename(filter[0].type, filter[0].props));
177
+ subscriptions.push(query.subscribe(delay ? debounce(update, delay) : update));
178
+ }
179
+ ctx.onDispose(() => {
180
+ subscriptions.forEach((unsubscribe) => unsubscribe());
181
+ });
182
+ };
183
+
184
+ // packages/core/functions/src/trigger/type/timer-trigger.ts
185
+ import { CronJob } from "cron";
186
+ import { DeferredTask } from "@dxos/async";
187
+ import { log as log3 } from "@dxos/log";
188
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/functions/src/trigger/type/timer-trigger.ts";
189
+ var createTimerTrigger = async (ctx, space, spec, callback) => {
190
+ const task = new DeferredTask(ctx, async () => {
191
+ await callback({});
192
+ });
193
+ let last = 0;
194
+ let run = 0;
195
+ const job = CronJob.from({
196
+ cronTime: spec.cron,
197
+ runOnInit: false,
198
+ onTick: () => {
199
+ const now = Date.now();
200
+ const delta = last ? now - last : 0;
201
+ last = now;
202
+ run++;
203
+ log3.info("tick", {
204
+ space: space.key.truncate(),
205
+ count: run,
206
+ delta
207
+ }, {
208
+ F: __dxlog_file3,
209
+ L: 38,
210
+ S: void 0,
211
+ C: (f, a) => f(...a)
212
+ });
213
+ task.schedule();
214
+ }
215
+ });
216
+ job.start();
217
+ ctx.onDispose(() => job.stop());
218
+ };
219
+
220
+ // packages/core/functions/src/trigger/type/websocket-trigger.ts
221
+ import WebSocket from "ws";
222
+ import { sleep, Trigger } from "@dxos/async";
223
+ import { log as log4 } from "@dxos/log";
224
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/functions/src/trigger/type/websocket-trigger.ts";
225
+ var createWebsocketTrigger = async (ctx, space, spec, callback, options = {
226
+ retryDelay: 2,
227
+ maxAttempts: 5
228
+ }) => {
229
+ const { url, init } = spec;
230
+ let wasOpen = false;
231
+ let ws;
232
+ for (let attempt = 1; attempt <= options.maxAttempts; attempt++) {
233
+ const open = new Trigger();
234
+ ws = new WebSocket(url);
235
+ Object.assign(ws, {
236
+ onopen: () => {
237
+ log4.info("opened", {
238
+ url
239
+ }, {
240
+ F: __dxlog_file4,
241
+ L: 41,
242
+ S: void 0,
243
+ C: (f, a) => f(...a)
244
+ });
245
+ if (spec.init) {
246
+ ws.send(new TextEncoder().encode(JSON.stringify(init)));
247
+ }
248
+ open.wake(true);
249
+ },
250
+ onclose: (event) => {
251
+ log4.info("closed", {
252
+ url,
253
+ code: event.code
254
+ }, {
255
+ F: __dxlog_file4,
256
+ L: 50,
257
+ S: void 0,
258
+ C: (f, a) => f(...a)
259
+ });
260
+ if (event.code === 1006 && wasOpen && !ctx.disposed) {
261
+ setTimeout(async () => {
262
+ log4.info(`reconnecting in ${options.retryDelay}s...`, {
263
+ url
264
+ }, {
265
+ F: __dxlog_file4,
266
+ L: 55,
267
+ S: void 0,
268
+ C: (f, a) => f(...a)
269
+ });
270
+ await createWebsocketTrigger(ctx, space, spec, callback, options);
271
+ }, options.retryDelay * 1e3);
272
+ }
273
+ open.wake(false);
274
+ },
275
+ onerror: (event) => {
276
+ log4.catch(event.error, {
277
+ url
278
+ }, {
279
+ F: __dxlog_file4,
280
+ L: 63,
281
+ S: void 0,
282
+ C: (f, a) => f(...a)
283
+ });
284
+ open.wake(false);
285
+ },
286
+ onmessage: async (event) => {
287
+ try {
288
+ log4.info("message", void 0, {
289
+ F: __dxlog_file4,
290
+ L: 69,
291
+ S: void 0,
292
+ C: (f, a) => f(...a)
293
+ });
294
+ const data = JSON.parse(new TextDecoder().decode(event.data));
295
+ await callback({
296
+ data
297
+ });
298
+ } catch (err) {
299
+ log4.catch(err, {
300
+ url
301
+ }, {
302
+ F: __dxlog_file4,
303
+ L: 73,
304
+ S: void 0,
305
+ C: (f, a) => f(...a)
306
+ });
307
+ }
308
+ }
309
+ });
310
+ const isOpen = await open.wait();
311
+ if (ctx.disposed) {
312
+ break;
313
+ }
314
+ if (isOpen) {
315
+ wasOpen = true;
316
+ break;
317
+ }
318
+ const wait = Math.pow(attempt, 2) * options.retryDelay;
319
+ if (attempt < options.maxAttempts) {
320
+ log4.warn(`failed to connect; trying again in ${wait}s`, {
321
+ attempt
322
+ }, {
323
+ F: __dxlog_file4,
324
+ L: 88,
325
+ S: void 0,
326
+ C: (f, a) => f(...a)
327
+ });
328
+ await sleep(wait * 1e3);
329
+ }
330
+ }
331
+ ctx.onDispose(() => {
332
+ ws?.close();
333
+ });
334
+ };
335
+
336
+ // packages/core/functions/src/trigger/trigger-registry.ts
337
+ import { Event as Event2 } from "@dxos/async";
338
+ import { create as create2, Filter as Filter3, getMeta } from "@dxos/client/echo";
339
+ import { Context, Resource as Resource2 } from "@dxos/context";
340
+ import { compareForeignKeys, ECHO_ATTR_META, foreignKey } from "@dxos/echo-schema";
341
+ import { invariant } from "@dxos/invariant";
342
+ import { PublicKey as PublicKey2 } from "@dxos/keys";
343
+ import { log as log5 } from "@dxos/log";
344
+ import { ComplexMap as ComplexMap2, diff as diff2 } from "@dxos/util";
345
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/functions/src/trigger/trigger-registry.ts";
346
+ var triggerFactory = {
347
+ subscription: createSubscriptionTrigger,
348
+ timer: createTimerTrigger,
349
+ // TODO(burdon): Cannot use in browser.
350
+ // webhook: createWebhookTrigger,
351
+ webhook: null,
352
+ websocket: createWebsocketTrigger
353
+ };
354
+ var TriggerRegistry = class extends Resource2 {
355
+ constructor(_client, _options) {
356
+ super();
357
+ this._client = _client;
358
+ this._options = _options;
359
+ this._triggersBySpaceKey = new ComplexMap2(PublicKey2.hash);
360
+ this.registered = new Event2();
361
+ this.removed = new Event2();
362
+ }
363
+ getActiveTriggers(space) {
364
+ return this._getTriggers(space, (t) => t.activationCtx != null);
365
+ }
366
+ getInactiveTriggers(space) {
367
+ return this._getTriggers(space, (t) => t.activationCtx == null);
368
+ }
369
+ /**
370
+ * Set callback for trigger.
371
+ */
372
+ async activate(space, trigger, callback) {
373
+ log5("activate", {
374
+ space: space.key,
375
+ trigger
376
+ }, {
377
+ F: __dxlog_file5,
378
+ L: 77,
379
+ S: this,
380
+ C: (f, a) => f(...a)
381
+ });
382
+ const activationCtx = new Context({
383
+ name: `FunctionTrigger-${trigger.function}`
384
+ }, {
385
+ F: __dxlog_file5,
386
+ L: 79
387
+ });
388
+ this._ctx.onDispose(() => activationCtx.dispose());
389
+ const registeredTrigger = this._triggersBySpaceKey.get(space.key)?.find((reg) => reg.trigger.id === trigger.id);
390
+ invariant(registeredTrigger, `Trigger is not registered: ${trigger.function}`, {
391
+ F: __dxlog_file5,
392
+ L: 82,
393
+ S: this,
394
+ A: [
395
+ "registeredTrigger",
396
+ "`Trigger is not registered: ${trigger.function}`"
397
+ ]
398
+ });
399
+ registeredTrigger.activationCtx = activationCtx;
400
+ try {
401
+ const options = this._options?.[trigger.spec.type];
402
+ const createTrigger = triggerFactory[trigger.spec.type];
403
+ invariant(createTrigger, `Trigger factory not found: ${trigger.spec.type}`, {
404
+ F: __dxlog_file5,
405
+ L: 89,
406
+ S: this,
407
+ A: [
408
+ "createTrigger",
409
+ "`Trigger factory not found: ${trigger.spec.type}`"
410
+ ]
411
+ });
412
+ await createTrigger(activationCtx, space, trigger.spec, callback, options);
413
+ } catch (err) {
414
+ delete registeredTrigger.activationCtx;
415
+ throw err;
416
+ }
417
+ }
418
+ /**
419
+ * Loads triggers from the manifest into the space.
420
+ */
421
+ async register(space, manifest) {
422
+ log5("register", {
423
+ space: space.key
424
+ }, {
425
+ F: __dxlog_file5,
426
+ L: 101,
427
+ S: this,
428
+ C: (f, a) => f(...a)
429
+ });
430
+ if (!manifest.triggers?.length) {
431
+ return;
432
+ }
433
+ if (!space.db.graph.schemaRegistry.hasSchema(FunctionTrigger)) {
434
+ space.db.graph.schemaRegistry.addSchema([
435
+ FunctionTrigger
436
+ ]);
437
+ }
438
+ const manifestTriggers = manifest.triggers.map((trigger) => {
439
+ let keys = trigger[ECHO_ATTR_META]?.keys;
440
+ delete trigger[ECHO_ATTR_META];
441
+ if (!keys?.length) {
442
+ keys = [
443
+ foreignKey("manifest", [
444
+ trigger.function,
445
+ trigger.spec.type
446
+ ].join(":"))
447
+ ];
448
+ }
449
+ return create2(FunctionTrigger, trigger, {
450
+ keys
451
+ });
452
+ });
453
+ const { objects: existing } = await space.db.query(Filter3.schema(FunctionTrigger)).run();
454
+ const { added } = diff2(existing, manifestTriggers, compareForeignKeys);
455
+ added.forEach((trigger) => {
456
+ space.db.add(trigger);
457
+ log5.info("added", {
458
+ meta: getMeta(trigger)
459
+ }, {
460
+ F: __dxlog_file5,
461
+ L: 128,
462
+ S: this,
463
+ C: (f, a) => f(...a)
464
+ });
465
+ });
466
+ if (added.length > 0) {
467
+ await space.db.flush();
468
+ }
469
+ }
470
+ async _open() {
471
+ log5.info("open...", void 0, {
472
+ F: __dxlog_file5,
473
+ L: 137,
474
+ S: this,
475
+ C: (f, a) => f(...a)
476
+ });
477
+ const spaceListSubscription = this._client.spaces.subscribe(async (spaces) => {
478
+ for (const space of spaces) {
479
+ if (this._triggersBySpaceKey.has(space.key)) {
480
+ continue;
481
+ }
482
+ const registered = [];
483
+ this._triggersBySpaceKey.set(space.key, registered);
484
+ await space.waitUntilReady();
485
+ if (this._ctx.disposed) {
486
+ break;
487
+ }
488
+ this._ctx.onDispose(space.db.query(Filter3.schema(FunctionTrigger)).subscribe(async ({ objects: current }) => {
489
+ log5.info("update", {
490
+ space: space.key,
491
+ registered: registered.length,
492
+ current: current.length
493
+ }, {
494
+ F: __dxlog_file5,
495
+ L: 154,
496
+ S: this,
497
+ C: (f, a) => f(...a)
498
+ });
499
+ await this._handleRemovedTriggers(space, current, registered);
500
+ this._handleNewTriggers(space, current, registered);
501
+ }));
502
+ }
503
+ });
504
+ this._ctx.onDispose(() => spaceListSubscription.unsubscribe());
505
+ log5.info("opened", void 0, {
506
+ F: __dxlog_file5,
507
+ L: 163,
508
+ S: this,
509
+ C: (f, a) => f(...a)
510
+ });
511
+ }
512
+ async _close(_) {
513
+ log5.info("close...", void 0, {
514
+ F: __dxlog_file5,
515
+ L: 167,
516
+ S: this,
517
+ C: (f, a) => f(...a)
518
+ });
519
+ this._triggersBySpaceKey.clear();
520
+ log5.info("closed", void 0, {
521
+ F: __dxlog_file5,
522
+ L: 169,
523
+ S: this,
524
+ C: (f, a) => f(...a)
525
+ });
526
+ }
527
+ _handleNewTriggers(space, current, registered) {
528
+ const added = current.filter((candidate) => {
529
+ return candidate.enabled && registered.find((reg) => reg.trigger.id === candidate.id) == null;
530
+ });
531
+ if (added.length > 0) {
532
+ const newRegisteredTriggers = added.map((trigger) => ({
533
+ trigger
534
+ }));
535
+ registered.push(...newRegisteredTriggers);
536
+ log5.info("added", () => ({
537
+ spaceKey: space.key,
538
+ triggers: added.map((trigger) => trigger.function)
539
+ }), {
540
+ F: __dxlog_file5,
541
+ L: 180,
542
+ S: this,
543
+ C: (f, a) => f(...a)
544
+ });
545
+ this.registered.emit({
546
+ space,
547
+ triggers: added
548
+ });
549
+ }
550
+ }
551
+ async _handleRemovedTriggers(space, current, registered) {
552
+ const removed = [];
553
+ for (let i = registered.length - 1; i >= 0; i--) {
554
+ const wasRemoved = current.filter((trigger) => trigger.enabled).find((trigger) => trigger.id === registered[i].trigger.id) == null;
555
+ if (wasRemoved) {
556
+ const unregistered = registered.splice(i, 1)[0];
557
+ await unregistered.activationCtx?.dispose();
558
+ removed.push(unregistered.trigger);
559
+ }
560
+ }
561
+ if (removed.length > 0) {
562
+ log5.info("removed", () => ({
563
+ spaceKey: space.key,
564
+ triggers: removed.map((trigger) => trigger.function)
565
+ }), {
566
+ F: __dxlog_file5,
567
+ L: 206,
568
+ S: this,
569
+ C: (f, a) => f(...a)
570
+ });
571
+ this.removed.emit({
572
+ space,
573
+ triggers: removed
574
+ });
575
+ }
576
+ }
577
+ _getTriggers(space, predicate) {
578
+ const allSpaceTriggers = this._triggersBySpaceKey.get(space.key) ?? [];
579
+ return allSpaceTriggers.filter(predicate).map((trigger) => trigger.trigger);
580
+ }
581
+ };
582
+
583
+ export {
584
+ FunctionRegistry,
585
+ createSubscriptionTrigger,
586
+ createTimerTrigger,
587
+ createWebsocketTrigger,
588
+ TriggerRegistry
589
+ };
590
+ //# sourceMappingURL=chunk-CRAAIWU6.mjs.map