@dxos/functions 0.5.3-main.c3feabc → 0.5.3-main.cb47aab
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 +663 -422
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +648 -419
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/handler.d.ts +32 -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 +7 -10
- package/dist/types/src/runtime/dev-server.d.ts.map +1 -1
- package/dist/types/src/runtime/scheduler.d.ts +10 -59
- 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 +1 -0
- package/dist/types/src/testing/test/handler.d.ts.map +1 -1
- 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 +118 -112
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +16 -13
- package/schema/functions.json +122 -103
- package/src/handler.ts +50 -27
- 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 +15 -35
- package/src/runtime/dev-server.ts +40 -22
- package/src/runtime/scheduler.test.ts +54 -75
- package/src/runtime/scheduler.ts +67 -300
- 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 +8 -2
- 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 +56 -40
|
@@ -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,22 +58,170 @@ 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.hasSchema(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 { Event, 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;
|
|
73
|
-
this.update = new
|
|
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
|
+
});
|
|
74
225
|
}
|
|
75
226
|
get stats() {
|
|
76
227
|
return {
|
|
@@ -80,7 +231,7 @@ var DevServer = class {
|
|
|
80
231
|
get endpoint() {
|
|
81
232
|
invariant(this._port, void 0, {
|
|
82
233
|
F: __dxlog_file2,
|
|
83
|
-
L:
|
|
234
|
+
L: 64,
|
|
84
235
|
S: this,
|
|
85
236
|
A: [
|
|
86
237
|
"this._port",
|
|
@@ -95,20 +246,6 @@ var DevServer = class {
|
|
|
95
246
|
get functions() {
|
|
96
247
|
return Object.values(this._handlers);
|
|
97
248
|
}
|
|
98
|
-
async initialize() {
|
|
99
|
-
for (const def of this._options.manifest.functions) {
|
|
100
|
-
try {
|
|
101
|
-
await this._load(def);
|
|
102
|
-
} catch (err) {
|
|
103
|
-
log2.error("parsing function (check manifest)", err, {
|
|
104
|
-
F: __dxlog_file2,
|
|
105
|
-
L: 71,
|
|
106
|
-
S: this,
|
|
107
|
-
C: (f, a) => f(...a)
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
249
|
async start() {
|
|
113
250
|
invariant(!this._server, void 0, {
|
|
114
251
|
F: __dxlog_file2,
|
|
@@ -125,6 +262,7 @@ var DevServer = class {
|
|
|
125
262
|
S: this,
|
|
126
263
|
C: (f, a) => f(...a)
|
|
127
264
|
});
|
|
265
|
+
this._ctx = createContext();
|
|
128
266
|
const app = express();
|
|
129
267
|
app.use(express.json());
|
|
130
268
|
app.post("/:path", async (req, res) => {
|
|
@@ -134,7 +272,7 @@ var DevServer = class {
|
|
|
134
272
|
path: path2
|
|
135
273
|
}, {
|
|
136
274
|
F: __dxlog_file2,
|
|
137
|
-
L:
|
|
275
|
+
L: 88,
|
|
138
276
|
S: this,
|
|
139
277
|
C: (f, a) => f(...a)
|
|
140
278
|
});
|
|
@@ -147,7 +285,7 @@ var DevServer = class {
|
|
|
147
285
|
} catch (err) {
|
|
148
286
|
log2.catch(err, void 0, {
|
|
149
287
|
F: __dxlog_file2,
|
|
150
|
-
L:
|
|
288
|
+
L: 98,
|
|
151
289
|
S: this,
|
|
152
290
|
C: (f, a) => f(...a)
|
|
153
291
|
});
|
|
@@ -166,11 +304,7 @@ var DevServer = class {
|
|
|
166
304
|
this._server = app.listen(this._port);
|
|
167
305
|
try {
|
|
168
306
|
const { registrationId, endpoint } = await this._client.services.services.FunctionRegistryService.register({
|
|
169
|
-
endpoint: this.endpoint
|
|
170
|
-
functions: this.functions.map(({ def: { id, path: path2 } }) => ({
|
|
171
|
-
id,
|
|
172
|
-
path: path2
|
|
173
|
-
}))
|
|
307
|
+
endpoint: this.endpoint
|
|
174
308
|
});
|
|
175
309
|
log2.info("registered", {
|
|
176
310
|
endpoint
|
|
@@ -182,6 +316,7 @@ var DevServer = class {
|
|
|
182
316
|
});
|
|
183
317
|
this._proxy = endpoint;
|
|
184
318
|
this._functionServiceRegistration = registrationId;
|
|
319
|
+
await this._functionsRegistry.open(this._ctx);
|
|
185
320
|
} catch (err) {
|
|
186
321
|
await this.stop();
|
|
187
322
|
throw new Error("FunctionRegistryService not available (check plugin is configured).");
|
|
@@ -190,7 +325,7 @@ var DevServer = class {
|
|
|
190
325
|
port: this._port
|
|
191
326
|
}, {
|
|
192
327
|
F: __dxlog_file2,
|
|
193
|
-
L:
|
|
328
|
+
L: 124,
|
|
194
329
|
S: this,
|
|
195
330
|
C: (f, a) => f(...a)
|
|
196
331
|
});
|
|
@@ -198,7 +333,7 @@ var DevServer = class {
|
|
|
198
333
|
async stop() {
|
|
199
334
|
invariant(this._server, void 0, {
|
|
200
335
|
F: __dxlog_file2,
|
|
201
|
-
L:
|
|
336
|
+
L: 128,
|
|
202
337
|
S: this,
|
|
203
338
|
A: [
|
|
204
339
|
"this._server",
|
|
@@ -207,7 +342,7 @@ var DevServer = class {
|
|
|
207
342
|
});
|
|
208
343
|
log2.info("stopping...", void 0, {
|
|
209
344
|
F: __dxlog_file2,
|
|
210
|
-
L:
|
|
345
|
+
L: 129,
|
|
211
346
|
S: this,
|
|
212
347
|
C: (f, a) => f(...a)
|
|
213
348
|
});
|
|
@@ -215,7 +350,7 @@ var DevServer = class {
|
|
|
215
350
|
this._server.close(async () => {
|
|
216
351
|
log2.info("server stopped", void 0, {
|
|
217
352
|
F: __dxlog_file2,
|
|
218
|
-
L:
|
|
353
|
+
L: 133,
|
|
219
354
|
S: this,
|
|
220
355
|
C: (f, a) => f(...a)
|
|
221
356
|
});
|
|
@@ -223,7 +358,7 @@ var DevServer = class {
|
|
|
223
358
|
if (this._functionServiceRegistration) {
|
|
224
359
|
invariant(this._client.services.services.FunctionRegistryService, void 0, {
|
|
225
360
|
F: __dxlog_file2,
|
|
226
|
-
L:
|
|
361
|
+
L: 136,
|
|
227
362
|
S: this,
|
|
228
363
|
A: [
|
|
229
364
|
"this._client.services.services.FunctionRegistryService",
|
|
@@ -237,7 +372,7 @@ var DevServer = class {
|
|
|
237
372
|
registrationId: this._functionServiceRegistration
|
|
238
373
|
}, {
|
|
239
374
|
F: __dxlog_file2,
|
|
240
|
-
L:
|
|
375
|
+
L: 141,
|
|
241
376
|
S: this,
|
|
242
377
|
C: (f, a) => f(...a)
|
|
243
378
|
});
|
|
@@ -254,7 +389,7 @@ var DevServer = class {
|
|
|
254
389
|
this._server = void 0;
|
|
255
390
|
log2.info("stopped", void 0, {
|
|
256
391
|
F: __dxlog_file2,
|
|
257
|
-
L:
|
|
392
|
+
L: 155,
|
|
258
393
|
S: this,
|
|
259
394
|
C: (f, a) => f(...a)
|
|
260
395
|
});
|
|
@@ -263,14 +398,14 @@ var DevServer = class {
|
|
|
263
398
|
* Load function.
|
|
264
399
|
*/
|
|
265
400
|
async _load(def, force = false) {
|
|
266
|
-
const {
|
|
401
|
+
const { uri, route, handler } = def;
|
|
267
402
|
const filePath = join(this._options.baseDir, handler);
|
|
268
403
|
log2.info("loading", {
|
|
269
|
-
|
|
404
|
+
uri,
|
|
270
405
|
force
|
|
271
406
|
}, {
|
|
272
407
|
F: __dxlog_file2,
|
|
273
|
-
L:
|
|
408
|
+
L: 164,
|
|
274
409
|
S: this,
|
|
275
410
|
C: (f, a) => f(...a)
|
|
276
411
|
});
|
|
@@ -281,13 +416,40 @@ var DevServer = class {
|
|
|
281
416
|
}
|
|
282
417
|
const module = __require(filePath);
|
|
283
418
|
if (typeof module.default !== "function") {
|
|
284
|
-
throw new Error(`Handler must export default function: ${
|
|
419
|
+
throw new Error(`Handler must export default function: ${uri}`);
|
|
285
420
|
}
|
|
286
|
-
this._handlers[
|
|
421
|
+
this._handlers[route] = {
|
|
287
422
|
def,
|
|
288
423
|
handler: module.default
|
|
289
424
|
};
|
|
290
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
|
+
}
|
|
291
453
|
/**
|
|
292
454
|
* Invoke function.
|
|
293
455
|
*/
|
|
@@ -299,11 +461,13 @@ var DevServer = class {
|
|
|
299
461
|
path: path2
|
|
300
462
|
}, {
|
|
301
463
|
F: __dxlog_file2,
|
|
302
|
-
L:
|
|
464
|
+
L: 204,
|
|
303
465
|
S: this,
|
|
304
466
|
C: (f, a) => f(...a)
|
|
305
467
|
});
|
|
306
|
-
const statusCode = await this._invoke(path2,
|
|
468
|
+
const statusCode = await this._invoke(path2, {
|
|
469
|
+
data
|
|
470
|
+
});
|
|
307
471
|
log2.info("res", {
|
|
308
472
|
seq,
|
|
309
473
|
path: path2,
|
|
@@ -311,7 +475,7 @@ var DevServer = class {
|
|
|
311
475
|
duration: Date.now() - now
|
|
312
476
|
}, {
|
|
313
477
|
F: __dxlog_file2,
|
|
314
|
-
L:
|
|
478
|
+
L: 207,
|
|
315
479
|
S: this,
|
|
316
480
|
C: (f, a) => f(...a)
|
|
317
481
|
});
|
|
@@ -322,7 +486,7 @@ var DevServer = class {
|
|
|
322
486
|
const { handler } = this._handlers[path2] ?? {};
|
|
323
487
|
invariant(handler, `invalid path: ${path2}`, {
|
|
324
488
|
F: __dxlog_file2,
|
|
325
|
-
L:
|
|
489
|
+
L: 214,
|
|
326
490
|
S: this,
|
|
327
491
|
A: [
|
|
328
492
|
"handler",
|
|
@@ -348,123 +512,98 @@ var DevServer = class {
|
|
|
348
512
|
return statusCode;
|
|
349
513
|
}
|
|
350
514
|
};
|
|
515
|
+
var createContext = () => new Context({
|
|
516
|
+
name: "DevServer"
|
|
517
|
+
});
|
|
351
518
|
|
|
352
519
|
// packages/core/functions/src/runtime/scheduler.ts
|
|
353
|
-
import { CronJob } from "cron";
|
|
354
|
-
import { getPort as getPort2 } from "get-port-please";
|
|
355
|
-
import http from "@dxos/node-std/http";
|
|
356
520
|
import path from "@dxos/node-std/path";
|
|
357
|
-
import
|
|
358
|
-
import { TextV0Type } from "@braneframe/types";
|
|
359
|
-
import { debounce, DeferredTask, sleep, Trigger as Trigger2 } from "@dxos/async";
|
|
360
|
-
import { createSubscription, Filter, getAutomergeObjectCore } from "@dxos/client/echo";
|
|
361
|
-
import { Context } from "@dxos/context";
|
|
362
|
-
import { invariant as invariant2 } from "@dxos/invariant";
|
|
521
|
+
import { Context as Context2 } from "@dxos/context";
|
|
363
522
|
import { log as log3 } from "@dxos/log";
|
|
364
|
-
import { ComplexMap } from "@dxos/util";
|
|
365
523
|
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/scheduler.ts";
|
|
366
524
|
var Scheduler = class {
|
|
367
|
-
constructor(
|
|
368
|
-
this.
|
|
369
|
-
this.
|
|
525
|
+
constructor(functions, triggers, _options = {}) {
|
|
526
|
+
this.functions = functions;
|
|
527
|
+
this.triggers = triggers;
|
|
370
528
|
this._options = _options;
|
|
371
|
-
this.
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
}
|
|
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
|
+
});
|
|
378
536
|
}
|
|
379
537
|
async start() {
|
|
380
|
-
this.
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
await this.mount(new Context(), space, trigger);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
});
|
|
538
|
+
await this._ctx.dispose();
|
|
539
|
+
this._ctx = createContext2();
|
|
540
|
+
await this.functions.open(this._ctx);
|
|
541
|
+
await this.triggers.open(this._ctx);
|
|
388
542
|
}
|
|
389
543
|
async stop() {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
544
|
+
await this._ctx.dispose();
|
|
545
|
+
await this.functions.close();
|
|
546
|
+
await this.triggers.close();
|
|
393
547
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
};
|
|
402
|
-
const def = this._manifest.functions.find((config) => config.id === trigger.function);
|
|
403
|
-
invariant2(def, `Function not found: ${trigger.function}`, {
|
|
404
|
-
F: __dxlog_file3,
|
|
405
|
-
L: 83,
|
|
406
|
-
S: this,
|
|
407
|
-
A: [
|
|
408
|
-
"def",
|
|
409
|
-
"`Function not found: ${trigger.function}`"
|
|
410
|
-
]
|
|
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);
|
|
411
555
|
});
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
space: space.key,
|
|
420
|
-
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
|
|
421
563
|
}, {
|
|
422
564
|
F: __dxlog_file3,
|
|
423
|
-
L:
|
|
565
|
+
L: 74,
|
|
424
566
|
S: this,
|
|
425
567
|
C: (f, a) => f(...a)
|
|
426
568
|
});
|
|
427
|
-
|
|
428
|
-
return;
|
|
429
|
-
}
|
|
430
|
-
if (trigger.timer) {
|
|
431
|
-
await this._createTimer(ctx, space, def, trigger.timer);
|
|
432
|
-
}
|
|
433
|
-
if (trigger.webhook) {
|
|
434
|
-
await this._createWebhook(ctx, space, def, trigger.webhook);
|
|
435
|
-
}
|
|
436
|
-
if (trigger.websocket) {
|
|
437
|
-
await this._createWebsocket(ctx, space, def, trigger.websocket);
|
|
438
|
-
}
|
|
439
|
-
if (trigger.subscription) {
|
|
440
|
-
await this._createSubscription(ctx, space, def, trigger.subscription);
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
async unmount(id, spaceKey) {
|
|
445
|
-
const key = {
|
|
446
|
-
id,
|
|
447
|
-
spaceKey
|
|
448
|
-
};
|
|
449
|
-
const { ctx } = this._mounts.get(key) ?? {};
|
|
450
|
-
if (ctx) {
|
|
451
|
-
this._mounts.delete(key);
|
|
452
|
-
await ctx.dispose();
|
|
569
|
+
return;
|
|
453
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
|
+
});
|
|
454
591
|
}
|
|
455
|
-
|
|
456
|
-
|
|
592
|
+
async _execFunction(def, { data, meta }) {
|
|
593
|
+
let status = 0;
|
|
457
594
|
try {
|
|
458
|
-
|
|
595
|
+
const payload = Object.assign({}, meta && {
|
|
596
|
+
meta
|
|
597
|
+
}, data);
|
|
459
598
|
const { endpoint, callback } = this._options;
|
|
460
599
|
if (endpoint) {
|
|
461
|
-
const url = path.join(endpoint, def.
|
|
600
|
+
const url = path.join(endpoint, def.route);
|
|
462
601
|
log3.info("exec", {
|
|
463
|
-
function: def.
|
|
602
|
+
function: def.uri,
|
|
464
603
|
url
|
|
465
604
|
}, {
|
|
466
605
|
F: __dxlog_file3,
|
|
467
|
-
L:
|
|
606
|
+
L: 100,
|
|
468
607
|
S: this,
|
|
469
608
|
C: (f, a) => f(...a)
|
|
470
609
|
});
|
|
@@ -473,360 +612,462 @@ var Scheduler = class {
|
|
|
473
612
|
headers: {
|
|
474
613
|
"Content-Type": "application/json"
|
|
475
614
|
},
|
|
476
|
-
body: JSON.stringify(
|
|
615
|
+
body: JSON.stringify(payload)
|
|
477
616
|
});
|
|
478
617
|
status = response.status;
|
|
479
618
|
} else if (callback) {
|
|
480
619
|
log3.info("exec", {
|
|
481
|
-
function: def.
|
|
620
|
+
function: def.uri
|
|
482
621
|
}, {
|
|
483
622
|
F: __dxlog_file3,
|
|
484
|
-
L:
|
|
623
|
+
L: 111,
|
|
485
624
|
S: this,
|
|
486
625
|
C: (f, a) => f(...a)
|
|
487
626
|
});
|
|
488
|
-
status = await callback(
|
|
627
|
+
status = await callback(payload) ?? 200;
|
|
489
628
|
}
|
|
490
629
|
if (status && status >= 400) {
|
|
491
630
|
throw new Error(`Response: ${status}`);
|
|
492
631
|
}
|
|
493
632
|
log3.info("done", {
|
|
494
|
-
function: def.
|
|
633
|
+
function: def.uri,
|
|
495
634
|
status
|
|
496
635
|
}, {
|
|
497
636
|
F: __dxlog_file3,
|
|
498
|
-
L:
|
|
637
|
+
L: 121,
|
|
499
638
|
S: this,
|
|
500
639
|
C: (f, a) => f(...a)
|
|
501
640
|
});
|
|
502
|
-
return status;
|
|
503
641
|
} catch (err) {
|
|
504
642
|
log3.error("error", {
|
|
505
|
-
function: def.
|
|
643
|
+
function: def.uri,
|
|
506
644
|
error: err.message
|
|
507
645
|
}, {
|
|
508
646
|
F: __dxlog_file3,
|
|
509
|
-
L:
|
|
647
|
+
L: 123,
|
|
510
648
|
S: this,
|
|
511
649
|
C: (f, a) => f(...a)
|
|
512
650
|
});
|
|
513
|
-
|
|
651
|
+
status = 500;
|
|
514
652
|
}
|
|
653
|
+
return status;
|
|
515
654
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
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)
|
|
681
|
+
});
|
|
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
|
|
526
690
|
}, {
|
|
527
|
-
F:
|
|
528
|
-
L:
|
|
529
|
-
S:
|
|
691
|
+
F: __dxlog_file4,
|
|
692
|
+
L: 32,
|
|
693
|
+
S: void 0,
|
|
530
694
|
C: (f, a) => f(...a)
|
|
531
695
|
});
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
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)
|
|
716
|
+
});
|
|
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
|
|
757
|
+
}, {
|
|
758
|
+
F: __dxlog_file5,
|
|
759
|
+
L: 37,
|
|
760
|
+
S: void 0,
|
|
761
|
+
C: (f, a) => f(...a)
|
|
536
762
|
});
|
|
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
|
|
790
|
+
}, {
|
|
791
|
+
F: __dxlog_file6,
|
|
792
|
+
L: 40,
|
|
793
|
+
S: void 0,
|
|
794
|
+
C: (f, a) => f(...a)
|
|
537
795
|
});
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
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
|
|
552
821
|
}, {
|
|
553
|
-
F:
|
|
554
|
-
L:
|
|
555
|
-
S:
|
|
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,
|
|
556
840
|
C: (f, a) => f(...a)
|
|
557
841
|
});
|
|
558
|
-
|
|
842
|
+
if (event.code === 1006) {
|
|
843
|
+
setTimeout(async () => {
|
|
844
|
+
log7.info(`reconnecting in ${options.retryDelay}s...`, {
|
|
845
|
+
url
|
|
846
|
+
}, {
|
|
847
|
+
F: __dxlog_file7,
|
|
848
|
+
L: 53,
|
|
849
|
+
S: void 0,
|
|
850
|
+
C: (f, a) => f(...a)
|
|
851
|
+
});
|
|
852
|
+
await createWebsocketTrigger(ctx, triggerCtx, spec, callback, options);
|
|
853
|
+
}, options.retryDelay * 1e3);
|
|
854
|
+
}
|
|
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
|
|
882
|
+
}, {
|
|
883
|
+
F: __dxlog_file7,
|
|
884
|
+
L: 71,
|
|
885
|
+
S: void 0,
|
|
886
|
+
C: (f, a) => f(...a)
|
|
887
|
+
});
|
|
888
|
+
}
|
|
559
889
|
}
|
|
560
890
|
});
|
|
561
|
-
|
|
562
|
-
|
|
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
|
+
}
|
|
907
|
+
}
|
|
563
908
|
}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
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,
|
|
570
940
|
trigger
|
|
571
941
|
}, {
|
|
572
|
-
F:
|
|
573
|
-
L:
|
|
942
|
+
F: __dxlog_file8,
|
|
943
|
+
L: 73,
|
|
574
944
|
S: this,
|
|
575
945
|
C: (f, a) => f(...a)
|
|
576
946
|
});
|
|
577
|
-
const
|
|
578
|
-
|
|
579
|
-
res.statusCode = 405;
|
|
580
|
-
return res.end();
|
|
581
|
-
}
|
|
582
|
-
res.statusCode = await this._execFunction(def, {
|
|
583
|
-
spaceKey: space.key
|
|
584
|
-
});
|
|
585
|
-
res.end();
|
|
586
|
-
});
|
|
587
|
-
const port = await getPort2({
|
|
588
|
-
random: true
|
|
947
|
+
const activationCtx = new Context3({
|
|
948
|
+
name: `trigger_${trigger.function}`
|
|
589
949
|
});
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
});
|
|
601
|
-
ctx.onDispose(() => {
|
|
602
|
-
server.close();
|
|
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
|
+
]
|
|
603
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
|
+
}
|
|
604
969
|
}
|
|
605
970
|
/**
|
|
606
|
-
*
|
|
607
|
-
* NOTE: The port must be unique, so the same hook cannot be used for multiple spaces.
|
|
971
|
+
* Loads triggers from the manifest into the space.
|
|
608
972
|
*/
|
|
609
|
-
async
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
}) {
|
|
613
|
-
log3.info("websocket", {
|
|
614
|
-
space: space.key,
|
|
615
|
-
trigger
|
|
973
|
+
async register(space, manifest) {
|
|
974
|
+
log8("register", {
|
|
975
|
+
space: space.key
|
|
616
976
|
}, {
|
|
617
|
-
F:
|
|
618
|
-
L:
|
|
977
|
+
F: __dxlog_file8,
|
|
978
|
+
L: 95,
|
|
619
979
|
S: this,
|
|
620
980
|
C: (f, a) => f(...a)
|
|
621
981
|
});
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
ws.send(new TextEncoder().encode(JSON.stringify(trigger.init)));
|
|
639
|
-
}
|
|
640
|
-
open.wake(true);
|
|
641
|
-
},
|
|
642
|
-
// TODO(burdon): Config retry if server closes?
|
|
643
|
-
onclose: (event) => {
|
|
644
|
-
log3.info("closed", {
|
|
645
|
-
url,
|
|
646
|
-
code: event.code
|
|
647
|
-
}, {
|
|
648
|
-
F: __dxlog_file3,
|
|
649
|
-
L: 272,
|
|
650
|
-
S: this,
|
|
651
|
-
C: (f, a) => f(...a)
|
|
652
|
-
});
|
|
653
|
-
open.wake(false);
|
|
654
|
-
},
|
|
655
|
-
onerror: (event) => {
|
|
656
|
-
log3.catch(event.error, {
|
|
657
|
-
url
|
|
658
|
-
}, {
|
|
659
|
-
F: __dxlog_file3,
|
|
660
|
-
L: 277,
|
|
661
|
-
S: this,
|
|
662
|
-
C: (f, a) => f(...a)
|
|
663
|
-
});
|
|
664
|
-
},
|
|
665
|
-
onmessage: async (event) => {
|
|
666
|
-
try {
|
|
667
|
-
const data = JSON.parse(new TextDecoder().decode(event.data));
|
|
668
|
-
await this._execFunction(def, {
|
|
669
|
-
spaceKey: space.key,
|
|
670
|
-
data
|
|
671
|
-
});
|
|
672
|
-
} catch (err) {
|
|
673
|
-
log3.catch(err, {
|
|
674
|
-
url
|
|
675
|
-
}, {
|
|
676
|
-
F: __dxlog_file3,
|
|
677
|
-
L: 285,
|
|
678
|
-
S: this,
|
|
679
|
-
C: (f, a) => f(...a)
|
|
680
|
-
});
|
|
681
|
-
}
|
|
982
|
+
if (!manifest.triggers?.length) {
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
if (!space.db.graph.runtimeSchemaRegistry.hasSchema(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;
|
|
682
998
|
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
const wait = Math.pow(attempt, 2) * options.retryDelay;
|
|
689
|
-
if (attempt < options.maxAttempts) {
|
|
690
|
-
log3.warn(`failed to connect; trying again in ${wait}s`, {
|
|
691
|
-
attempt
|
|
692
|
-
}, {
|
|
693
|
-
F: __dxlog_file3,
|
|
694
|
-
L: 296,
|
|
695
|
-
S: this,
|
|
696
|
-
C: (f, a) => f(...a)
|
|
697
|
-
});
|
|
698
|
-
await sleep(wait * 1e3);
|
|
999
|
+
const registered = [];
|
|
1000
|
+
this._triggersBySpaceKey.set(space.key, registered);
|
|
1001
|
+
await space.waitUntilReady();
|
|
1002
|
+
if (this._ctx.disposed) {
|
|
1003
|
+
break;
|
|
699
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);
|
|
700
1010
|
}
|
|
701
|
-
}
|
|
702
|
-
ctx.onDispose(() => {
|
|
703
|
-
ws?.close();
|
|
704
1011
|
});
|
|
1012
|
+
this._ctx.onDispose(() => spaceListSubscription.unsubscribe());
|
|
705
1013
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
trigger
|
|
713
|
-
}, {
|
|
714
|
-
F: __dxlog_file3,
|
|
715
|
-
L: 311,
|
|
716
|
-
S: this,
|
|
717
|
-
C: (f, a) => f(...a)
|
|
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;
|
|
718
1020
|
});
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
1021
|
+
if (newTriggers.length > 0) {
|
|
1022
|
+
const newRegisteredTriggers = newTriggers.map((trigger) => ({
|
|
1023
|
+
trigger
|
|
1024
|
+
}));
|
|
1025
|
+
registered.push(...newRegisteredTriggers);
|
|
1026
|
+
log8("registered new triggers", () => ({
|
|
722
1027
|
spaceKey: space.key,
|
|
723
|
-
|
|
724
|
-
})
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
const subscription = createSubscription(({ added, updated }) => {
|
|
728
|
-
log3.info("updated", {
|
|
729
|
-
added: added.length,
|
|
730
|
-
updated: updated.length
|
|
731
|
-
}, {
|
|
732
|
-
F: __dxlog_file3,
|
|
733
|
-
L: 321,
|
|
1028
|
+
functions: newTriggers.map((t) => t.function)
|
|
1029
|
+
}), {
|
|
1030
|
+
F: __dxlog_file8,
|
|
1031
|
+
L: 146,
|
|
734
1032
|
S: this,
|
|
735
1033
|
C: (f, a) => f(...a)
|
|
736
1034
|
});
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
objects: objects.length
|
|
752
|
-
}, {
|
|
753
|
-
F: __dxlog_file3,
|
|
754
|
-
L: 342,
|
|
755
|
-
S: this,
|
|
756
|
-
C: (f, a) => f(...a)
|
|
757
|
-
});
|
|
758
|
-
for (const object of objects) {
|
|
759
|
-
const content = object.content;
|
|
760
|
-
if (content instanceof TextV0Type) {
|
|
761
|
-
subscriptions.push(getAutomergeObjectCore(content).updates.on(debounce(() => subscription.update([
|
|
762
|
-
object
|
|
763
|
-
]), 1e3)));
|
|
764
|
-
}
|
|
765
|
-
}
|
|
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);
|
|
766
1049
|
}
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
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);
|
|
773
1061
|
}
|
|
774
1062
|
};
|
|
775
|
-
|
|
776
|
-
// packages/core/functions/src/types.ts
|
|
777
|
-
import * as S from "@effect/schema/Schema";
|
|
778
|
-
var TimerTriggerSchema = S.struct({
|
|
779
|
-
cron: S.string
|
|
780
|
-
});
|
|
781
|
-
var WebhookTriggerSchema = S.mutable(S.struct({
|
|
782
|
-
method: S.string,
|
|
783
|
-
// Assigned port.
|
|
784
|
-
port: S.optional(S.number)
|
|
785
|
-
}));
|
|
786
|
-
var WebsocketTriggerSchema = S.struct({
|
|
787
|
-
url: S.string,
|
|
788
|
-
init: S.optional(S.record(S.string, S.any))
|
|
789
|
-
});
|
|
790
|
-
var SubscriptionTriggerSchema = S.struct({
|
|
791
|
-
spaceKey: S.optional(S.string),
|
|
792
|
-
// TODO(burdon): Define query DSL.
|
|
793
|
-
filter: S.array(S.struct({
|
|
794
|
-
type: S.string,
|
|
795
|
-
props: S.optional(S.record(S.string, S.any))
|
|
796
|
-
})),
|
|
797
|
-
options: S.optional(S.struct({
|
|
798
|
-
// Watch changes to object (not just creation).
|
|
799
|
-
deep: S.optional(S.boolean),
|
|
800
|
-
// Debounce changes (delay in ms).
|
|
801
|
-
delay: S.optional(S.number)
|
|
802
|
-
}))
|
|
803
|
-
});
|
|
804
|
-
var FunctionTriggerSchema = S.struct({
|
|
805
|
-
function: S.string.pipe(S.description("Function ID/URI.")),
|
|
806
|
-
// Context passed to function.
|
|
807
|
-
context: S.optional(S.record(S.string, S.any)),
|
|
808
|
-
// Triggers.
|
|
809
|
-
timer: S.optional(TimerTriggerSchema),
|
|
810
|
-
webhook: S.optional(WebhookTriggerSchema),
|
|
811
|
-
websocket: S.optional(WebsocketTriggerSchema),
|
|
812
|
-
subscription: S.optional(SubscriptionTriggerSchema)
|
|
813
|
-
});
|
|
814
|
-
var FunctionDefSchema = S.struct({
|
|
815
|
-
id: S.string,
|
|
816
|
-
// name: S.string,
|
|
817
|
-
description: S.optional(S.string),
|
|
818
|
-
path: S.string,
|
|
819
|
-
// TODO(burdon): NPM/GitHub/Docker/CF URL?
|
|
820
|
-
handler: S.string
|
|
821
|
-
});
|
|
822
|
-
var FunctionManifestSchema = S.struct({
|
|
823
|
-
functions: S.mutable(S.array(FunctionDefSchema)),
|
|
824
|
-
triggers: S.optional(S.mutable(S.array(FunctionTriggerSchema)))
|
|
825
|
-
});
|
|
826
1063
|
export {
|
|
827
1064
|
DevServer,
|
|
1065
|
+
FunctionDef,
|
|
828
1066
|
FunctionManifestSchema,
|
|
1067
|
+
FunctionRegistry,
|
|
1068
|
+
FunctionTrigger,
|
|
829
1069
|
Scheduler,
|
|
1070
|
+
TriggerRegistry,
|
|
830
1071
|
subscriptionHandler
|
|
831
1072
|
};
|
|
832
1073
|
//# sourceMappingURL=index.mjs.map
|