@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.
- package/dist/lib/browser/{chunk-YSDC6YCF.mjs → chunk-2I75VGHZ.mjs} +3 -3
- package/dist/lib/browser/{chunk-YSDC6YCF.mjs.map → chunk-2I75VGHZ.mjs.map} +3 -3
- package/dist/lib/browser/chunk-CRAAIWU6.mjs +590 -0
- package/dist/lib/browser/chunk-CRAAIWU6.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +9 -7
- package/dist/lib/browser/index.mjs.map +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +522 -11
- package/dist/lib/browser/testing/index.mjs.map +4 -4
- package/dist/lib/browser/types.mjs +1 -1
- package/dist/lib/node/{chunk-3E6PY6JH.cjs → chunk-JV3VNH5X.cjs} +6 -6
- package/dist/lib/node/{chunk-3E6PY6JH.cjs.map → chunk-JV3VNH5X.cjs.map} +3 -3
- package/dist/lib/node/chunk-OGLDVNFE.cjs +613 -0
- package/dist/lib/node/chunk-OGLDVNFE.cjs.map +7 -0
- package/dist/lib/node/index.cjs +14 -12
- package/dist/lib/node/index.cjs.map +1 -1
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +521 -13
- package/dist/lib/node/testing/index.cjs.map +4 -4
- package/dist/lib/node/types.cjs +5 -5
- package/dist/lib/node/types.cjs.map +1 -1
- package/dist/types/src/index.d.ts +0 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/runtime/scheduler.d.ts.map +1 -1
- package/dist/types/src/trigger/index.d.ts +1 -0
- package/dist/types/src/trigger/index.d.ts.map +1 -1
- package/dist/types/src/trigger/trigger-registry.d.ts +5 -4
- package/dist/types/src/trigger/trigger-registry.d.ts.map +1 -1
- package/dist/types/src/trigger/type/index.d.ts +0 -1
- package/dist/types/src/trigger/type/index.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +4 -0
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +14 -14
- package/src/index.ts +1 -1
- package/src/runtime/scheduler.ts +6 -3
- package/src/trigger/index.ts +1 -0
- package/src/trigger/trigger-registry.test.ts +2 -1
- package/src/trigger/trigger-registry.ts +15 -7
- package/src/trigger/type/index.ts +1 -1
- package/src/types.ts +1 -0
- package/dist/lib/browser/chunk-OERXFETS.mjs +0 -1120
- package/dist/lib/browser/chunk-OERXFETS.mjs.map +0 -7
- package/dist/lib/node/chunk-ITQU6E54.cjs +0 -1133
- 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-
|
|
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": "
|
|
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
|