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