@dxos/functions 0.5.3-main.2c59258 → 0.5.3-main.3456876
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 +422 -663
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +419 -648
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/handler.d.ts +12 -32
- package/dist/types/src/handler.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +0 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/runtime/dev-server.d.ts +10 -7
- package/dist/types/src/runtime/dev-server.d.ts.map +1 -1
- package/dist/types/src/runtime/scheduler.d.ts +59 -10
- package/dist/types/src/runtime/scheduler.d.ts.map +1 -1
- package/dist/types/src/testing/test/handler.d.ts +0 -1
- package/dist/types/src/testing/test/handler.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +112 -118
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +13 -16
- package/schema/functions.json +103 -122
- package/src/handler.ts +27 -50
- package/src/index.ts +0 -2
- package/src/runtime/dev-server.test.ts +35 -15
- package/src/runtime/dev-server.ts +22 -40
- package/src/runtime/scheduler.test.ts +75 -54
- package/src/runtime/scheduler.ts +300 -67
- package/src/testing/test/handler.ts +2 -8
- package/src/types.ts +40 -56
- package/dist/types/src/registry/function-registry.d.ts +0 -24
- package/dist/types/src/registry/function-registry.d.ts.map +0 -1
- package/dist/types/src/registry/function-registry.test.d.ts +0 -2
- package/dist/types/src/registry/function-registry.test.d.ts.map +0 -1
- package/dist/types/src/registry/index.d.ts +0 -2
- package/dist/types/src/registry/index.d.ts.map +0 -1
- package/dist/types/src/testing/functions-integration.test.d.ts +0 -2
- package/dist/types/src/testing/functions-integration.test.d.ts.map +0 -1
- package/dist/types/src/testing/index.d.ts +0 -4
- package/dist/types/src/testing/index.d.ts.map +0 -1
- package/dist/types/src/testing/setup.d.ts +0 -5
- package/dist/types/src/testing/setup.d.ts.map +0 -1
- package/dist/types/src/testing/types.d.ts +0 -9
- package/dist/types/src/testing/types.d.ts.map +0 -1
- package/dist/types/src/testing/util.d.ts +0 -3
- package/dist/types/src/testing/util.d.ts.map +0 -1
- package/dist/types/src/trigger/index.d.ts +0 -2
- package/dist/types/src/trigger/index.d.ts.map +0 -1
- package/dist/types/src/trigger/trigger-registry.d.ts +0 -40
- package/dist/types/src/trigger/trigger-registry.d.ts.map +0 -1
- package/dist/types/src/trigger/trigger-registry.test.d.ts +0 -2
- package/dist/types/src/trigger/trigger-registry.test.d.ts.map +0 -1
- package/dist/types/src/trigger/type/index.d.ts +0 -5
- package/dist/types/src/trigger/type/index.d.ts.map +0 -1
- package/dist/types/src/trigger/type/subscription-trigger.d.ts +0 -4
- package/dist/types/src/trigger/type/subscription-trigger.d.ts.map +0 -1
- package/dist/types/src/trigger/type/timer-trigger.d.ts +0 -4
- package/dist/types/src/trigger/type/timer-trigger.d.ts.map +0 -1
- package/dist/types/src/trigger/type/webhook-trigger.d.ts +0 -4
- package/dist/types/src/trigger/type/webhook-trigger.d.ts.map +0 -1
- package/dist/types/src/trigger/type/websocket-trigger.d.ts +0 -13
- package/dist/types/src/trigger/type/websocket-trigger.d.ts.map +0 -1
- package/src/registry/function-registry.test.ts +0 -105
- package/src/registry/function-registry.ts +0 -84
- package/src/registry/index.ts +0 -5
- package/src/testing/functions-integration.test.ts +0 -99
- package/src/testing/index.ts +0 -7
- package/src/testing/setup.ts +0 -45
- package/src/testing/types.ts +0 -9
- package/src/testing/util.ts +0 -16
- package/src/trigger/index.ts +0 -5
- package/src/trigger/trigger-registry.test.ts +0 -229
- package/src/trigger/trigger-registry.ts +0 -176
- package/src/trigger/type/index.ts +0 -8
- package/src/trigger/type/subscription-trigger.ts +0 -73
- package/src/trigger/type/timer-trigger.ts +0 -44
- package/src/trigger/type/webhook-trigger.ts +0 -47
- package/src/trigger/type/websocket-trigger.ts +0 -91
|
@@ -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
|
|
23
|
+
return ({ event, context, ...rest }) => {
|
|
24
24
|
const { client } = context;
|
|
25
|
-
const space =
|
|
26
|
-
const objects = space
|
|
27
|
-
if (!!
|
|
25
|
+
const space = event.spaceKey ? client.spaces.get(PublicKey.from(event.spaceKey)) : void 0;
|
|
26
|
+
const objects = space && event.objects?.map((id) => space.db.getObjectById(id)).filter(nonNullable);
|
|
27
|
+
if (!!event.spaceKey && !space) {
|
|
28
28
|
log.warn("invalid space", {
|
|
29
|
-
|
|
29
|
+
event
|
|
30
30
|
}, {
|
|
31
31
|
F: __dxlog_file,
|
|
32
|
-
L:
|
|
32
|
+
L: 68,
|
|
33
33
|
S: void 0,
|
|
34
34
|
C: (f, a) => f(...a)
|
|
35
35
|
});
|
|
@@ -39,18 +39,15 @@ var subscriptionHandler = (handler) => {
|
|
|
39
39
|
objects: objects?.length
|
|
40
40
|
}, {
|
|
41
41
|
F: __dxlog_file,
|
|
42
|
-
L:
|
|
42
|
+
L: 70,
|
|
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
|
-
|
|
51
|
-
space,
|
|
52
|
-
objects
|
|
53
|
-
}
|
|
49
|
+
space,
|
|
50
|
+
objects
|
|
54
51
|
},
|
|
55
52
|
context,
|
|
56
53
|
...rest
|
|
@@ -58,170 +55,22 @@ var subscriptionHandler = (handler) => {
|
|
|
58
55
|
};
|
|
59
56
|
};
|
|
60
57
|
|
|
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
|
-
|
|
194
58
|
// packages/core/functions/src/runtime/dev-server.ts
|
|
195
59
|
import express from "express";
|
|
196
60
|
import { getPort } from "get-port-please";
|
|
197
61
|
import { join } from "@dxos/node-std/path";
|
|
198
|
-
import { Event
|
|
199
|
-
import { Context } from "@dxos/context";
|
|
62
|
+
import { Event, Trigger } from "@dxos/async";
|
|
200
63
|
import { invariant } from "@dxos/invariant";
|
|
201
64
|
import { log as log2 } from "@dxos/log";
|
|
202
65
|
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/dev-server.ts";
|
|
203
66
|
var DevServer = class {
|
|
204
67
|
// prettier-ignore
|
|
205
|
-
constructor(_client,
|
|
68
|
+
constructor(_client, _options) {
|
|
206
69
|
this._client = _client;
|
|
207
|
-
this._functionsRegistry = _functionsRegistry;
|
|
208
70
|
this._options = _options;
|
|
209
|
-
this._ctx = createContext();
|
|
210
71
|
this._handlers = {};
|
|
211
72
|
this._seq = 0;
|
|
212
|
-
this.update = new
|
|
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
|
-
});
|
|
73
|
+
this.update = new Event();
|
|
225
74
|
}
|
|
226
75
|
get stats() {
|
|
227
76
|
return {
|
|
@@ -231,7 +80,7 @@ var DevServer = class {
|
|
|
231
80
|
get endpoint() {
|
|
232
81
|
invariant(this._port, void 0, {
|
|
233
82
|
F: __dxlog_file2,
|
|
234
|
-
L:
|
|
83
|
+
L: 54,
|
|
235
84
|
S: this,
|
|
236
85
|
A: [
|
|
237
86
|
"this._port",
|
|
@@ -246,6 +95,20 @@ var DevServer = class {
|
|
|
246
95
|
get functions() {
|
|
247
96
|
return Object.values(this._handlers);
|
|
248
97
|
}
|
|
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
|
+
}
|
|
249
112
|
async start() {
|
|
250
113
|
invariant(!this._server, void 0, {
|
|
251
114
|
F: __dxlog_file2,
|
|
@@ -262,7 +125,6 @@ var DevServer = class {
|
|
|
262
125
|
S: this,
|
|
263
126
|
C: (f, a) => f(...a)
|
|
264
127
|
});
|
|
265
|
-
this._ctx = createContext();
|
|
266
128
|
const app = express();
|
|
267
129
|
app.use(express.json());
|
|
268
130
|
app.post("/:path", async (req, res) => {
|
|
@@ -272,7 +134,7 @@ var DevServer = class {
|
|
|
272
134
|
path: path2
|
|
273
135
|
}, {
|
|
274
136
|
F: __dxlog_file2,
|
|
275
|
-
L:
|
|
137
|
+
L: 87,
|
|
276
138
|
S: this,
|
|
277
139
|
C: (f, a) => f(...a)
|
|
278
140
|
});
|
|
@@ -285,7 +147,7 @@ var DevServer = class {
|
|
|
285
147
|
} catch (err) {
|
|
286
148
|
log2.catch(err, void 0, {
|
|
287
149
|
F: __dxlog_file2,
|
|
288
|
-
L:
|
|
150
|
+
L: 97,
|
|
289
151
|
S: this,
|
|
290
152
|
C: (f, a) => f(...a)
|
|
291
153
|
});
|
|
@@ -304,7 +166,11 @@ var DevServer = class {
|
|
|
304
166
|
this._server = app.listen(this._port);
|
|
305
167
|
try {
|
|
306
168
|
const { registrationId, endpoint } = await this._client.services.services.FunctionRegistryService.register({
|
|
307
|
-
endpoint: this.endpoint
|
|
169
|
+
endpoint: this.endpoint,
|
|
170
|
+
functions: this.functions.map(({ def: { id, path: path2 } }) => ({
|
|
171
|
+
id,
|
|
172
|
+
path: path2
|
|
173
|
+
}))
|
|
308
174
|
});
|
|
309
175
|
log2.info("registered", {
|
|
310
176
|
endpoint
|
|
@@ -316,7 +182,6 @@ var DevServer = class {
|
|
|
316
182
|
});
|
|
317
183
|
this._proxy = endpoint;
|
|
318
184
|
this._functionServiceRegistration = registrationId;
|
|
319
|
-
await this._functionsRegistry.open(this._ctx);
|
|
320
185
|
} catch (err) {
|
|
321
186
|
await this.stop();
|
|
322
187
|
throw new Error("FunctionRegistryService not available (check plugin is configured).");
|
|
@@ -325,7 +190,7 @@ var DevServer = class {
|
|
|
325
190
|
port: this._port
|
|
326
191
|
}, {
|
|
327
192
|
F: __dxlog_file2,
|
|
328
|
-
L:
|
|
193
|
+
L: 121,
|
|
329
194
|
S: this,
|
|
330
195
|
C: (f, a) => f(...a)
|
|
331
196
|
});
|
|
@@ -333,7 +198,7 @@ var DevServer = class {
|
|
|
333
198
|
async stop() {
|
|
334
199
|
invariant(this._server, void 0, {
|
|
335
200
|
F: __dxlog_file2,
|
|
336
|
-
L:
|
|
201
|
+
L: 125,
|
|
337
202
|
S: this,
|
|
338
203
|
A: [
|
|
339
204
|
"this._server",
|
|
@@ -342,7 +207,7 @@ var DevServer = class {
|
|
|
342
207
|
});
|
|
343
208
|
log2.info("stopping...", void 0, {
|
|
344
209
|
F: __dxlog_file2,
|
|
345
|
-
L:
|
|
210
|
+
L: 126,
|
|
346
211
|
S: this,
|
|
347
212
|
C: (f, a) => f(...a)
|
|
348
213
|
});
|
|
@@ -350,7 +215,7 @@ var DevServer = class {
|
|
|
350
215
|
this._server.close(async () => {
|
|
351
216
|
log2.info("server stopped", void 0, {
|
|
352
217
|
F: __dxlog_file2,
|
|
353
|
-
L:
|
|
218
|
+
L: 130,
|
|
354
219
|
S: this,
|
|
355
220
|
C: (f, a) => f(...a)
|
|
356
221
|
});
|
|
@@ -358,7 +223,7 @@ var DevServer = class {
|
|
|
358
223
|
if (this._functionServiceRegistration) {
|
|
359
224
|
invariant(this._client.services.services.FunctionRegistryService, void 0, {
|
|
360
225
|
F: __dxlog_file2,
|
|
361
|
-
L:
|
|
226
|
+
L: 133,
|
|
362
227
|
S: this,
|
|
363
228
|
A: [
|
|
364
229
|
"this._client.services.services.FunctionRegistryService",
|
|
@@ -372,7 +237,7 @@ var DevServer = class {
|
|
|
372
237
|
registrationId: this._functionServiceRegistration
|
|
373
238
|
}, {
|
|
374
239
|
F: __dxlog_file2,
|
|
375
|
-
L:
|
|
240
|
+
L: 138,
|
|
376
241
|
S: this,
|
|
377
242
|
C: (f, a) => f(...a)
|
|
378
243
|
});
|
|
@@ -389,7 +254,7 @@ var DevServer = class {
|
|
|
389
254
|
this._server = void 0;
|
|
390
255
|
log2.info("stopped", void 0, {
|
|
391
256
|
F: __dxlog_file2,
|
|
392
|
-
L:
|
|
257
|
+
L: 152,
|
|
393
258
|
S: this,
|
|
394
259
|
C: (f, a) => f(...a)
|
|
395
260
|
});
|
|
@@ -398,14 +263,14 @@ var DevServer = class {
|
|
|
398
263
|
* Load function.
|
|
399
264
|
*/
|
|
400
265
|
async _load(def, force = false) {
|
|
401
|
-
const {
|
|
266
|
+
const { id, path: path2, handler } = def;
|
|
402
267
|
const filePath = join(this._options.baseDir, handler);
|
|
403
268
|
log2.info("loading", {
|
|
404
|
-
|
|
269
|
+
id,
|
|
405
270
|
force
|
|
406
271
|
}, {
|
|
407
272
|
F: __dxlog_file2,
|
|
408
|
-
L:
|
|
273
|
+
L: 161,
|
|
409
274
|
S: this,
|
|
410
275
|
C: (f, a) => f(...a)
|
|
411
276
|
});
|
|
@@ -416,40 +281,13 @@ var DevServer = class {
|
|
|
416
281
|
}
|
|
417
282
|
const module = __require(filePath);
|
|
418
283
|
if (typeof module.default !== "function") {
|
|
419
|
-
throw new Error(`Handler must export default function: ${
|
|
284
|
+
throw new Error(`Handler must export default function: ${id}`);
|
|
420
285
|
}
|
|
421
|
-
this._handlers[
|
|
286
|
+
this._handlers[path2] = {
|
|
422
287
|
def,
|
|
423
288
|
handler: module.default
|
|
424
289
|
};
|
|
425
290
|
}
|
|
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
|
-
}
|
|
453
291
|
/**
|
|
454
292
|
* Invoke function.
|
|
455
293
|
*/
|
|
@@ -461,13 +299,11 @@ var DevServer = class {
|
|
|
461
299
|
path: path2
|
|
462
300
|
}, {
|
|
463
301
|
F: __dxlog_file2,
|
|
464
|
-
L:
|
|
302
|
+
L: 188,
|
|
465
303
|
S: this,
|
|
466
304
|
C: (f, a) => f(...a)
|
|
467
305
|
});
|
|
468
|
-
const statusCode = await this._invoke(path2,
|
|
469
|
-
data
|
|
470
|
-
});
|
|
306
|
+
const statusCode = await this._invoke(path2, data);
|
|
471
307
|
log2.info("res", {
|
|
472
308
|
seq,
|
|
473
309
|
path: path2,
|
|
@@ -475,7 +311,7 @@ var DevServer = class {
|
|
|
475
311
|
duration: Date.now() - now
|
|
476
312
|
}, {
|
|
477
313
|
F: __dxlog_file2,
|
|
478
|
-
L:
|
|
314
|
+
L: 191,
|
|
479
315
|
S: this,
|
|
480
316
|
C: (f, a) => f(...a)
|
|
481
317
|
});
|
|
@@ -486,7 +322,7 @@ var DevServer = class {
|
|
|
486
322
|
const { handler } = this._handlers[path2] ?? {};
|
|
487
323
|
invariant(handler, `invalid path: ${path2}`, {
|
|
488
324
|
F: __dxlog_file2,
|
|
489
|
-
L:
|
|
325
|
+
L: 198,
|
|
490
326
|
S: this,
|
|
491
327
|
A: [
|
|
492
328
|
"handler",
|
|
@@ -512,98 +348,123 @@ var DevServer = class {
|
|
|
512
348
|
return statusCode;
|
|
513
349
|
}
|
|
514
350
|
};
|
|
515
|
-
var createContext = () => new Context({
|
|
516
|
-
name: "DevServer"
|
|
517
|
-
});
|
|
518
351
|
|
|
519
352
|
// 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";
|
|
520
356
|
import path from "@dxos/node-std/path";
|
|
521
|
-
import
|
|
357
|
+
import WebSocket from "ws";
|
|
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";
|
|
522
363
|
import { log as log3 } from "@dxos/log";
|
|
364
|
+
import { ComplexMap } from "@dxos/util";
|
|
523
365
|
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/scheduler.ts";
|
|
524
366
|
var Scheduler = class {
|
|
525
|
-
constructor(
|
|
526
|
-
this.
|
|
527
|
-
this.
|
|
367
|
+
constructor(_client, _manifest, _options = {}) {
|
|
368
|
+
this._client = _client;
|
|
369
|
+
this._manifest = _manifest;
|
|
528
370
|
this._options = _options;
|
|
529
|
-
this.
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
})
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
});
|
|
371
|
+
this._mounts = new ComplexMap(({ spaceKey, id }) => `${spaceKey.toHex()}:${id}`);
|
|
372
|
+
}
|
|
373
|
+
get mounts() {
|
|
374
|
+
return Array.from(this._mounts.values()).reduce((acc, { trigger }) => {
|
|
375
|
+
acc.push(trigger);
|
|
376
|
+
return acc;
|
|
377
|
+
}, []);
|
|
536
378
|
}
|
|
537
379
|
async start() {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
380
|
+
this._client.spaces.subscribe(async (spaces) => {
|
|
381
|
+
for (const space of spaces) {
|
|
382
|
+
await space.waitUntilReady();
|
|
383
|
+
for (const trigger of this._manifest.triggers ?? []) {
|
|
384
|
+
await this.mount(new Context(), space, trigger);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
});
|
|
542
388
|
}
|
|
543
389
|
async stop() {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
}
|
|
548
|
-
async register(space, manifest) {
|
|
549
|
-
await this.functions.register(space, manifest);
|
|
550
|
-
await this.triggers.register(space, manifest);
|
|
390
|
+
for (const { id, spaceKey } of this._mounts.keys()) {
|
|
391
|
+
await this.unmount(id, spaceKey);
|
|
392
|
+
}
|
|
551
393
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
394
|
+
/**
|
|
395
|
+
* Mount trigger.
|
|
396
|
+
*/
|
|
397
|
+
async mount(ctx, space, trigger) {
|
|
398
|
+
const key = {
|
|
399
|
+
spaceKey: space.key,
|
|
400
|
+
id: trigger.function
|
|
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
|
+
]
|
|
555
411
|
});
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
412
|
+
const exists = this._mounts.get(key);
|
|
413
|
+
if (!exists) {
|
|
414
|
+
this._mounts.set(key, {
|
|
415
|
+
ctx,
|
|
416
|
+
trigger
|
|
417
|
+
});
|
|
418
|
+
log3("mount", {
|
|
419
|
+
space: space.key,
|
|
420
|
+
trigger
|
|
563
421
|
}, {
|
|
564
422
|
F: __dxlog_file3,
|
|
565
|
-
L:
|
|
423
|
+
L: 89,
|
|
566
424
|
S: this,
|
|
567
425
|
C: (f, a) => f(...a)
|
|
568
426
|
});
|
|
569
|
-
|
|
427
|
+
if (ctx.disposed) {
|
|
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();
|
|
570
453
|
}
|
|
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
|
-
});
|
|
591
454
|
}
|
|
592
|
-
|
|
593
|
-
|
|
455
|
+
// TODO(burdon): Pass in Space key (common context).
|
|
456
|
+
async _execFunction(def, data) {
|
|
594
457
|
try {
|
|
595
|
-
|
|
596
|
-
meta
|
|
597
|
-
}, data);
|
|
458
|
+
let status = 0;
|
|
598
459
|
const { endpoint, callback } = this._options;
|
|
599
460
|
if (endpoint) {
|
|
600
|
-
const url = path.join(endpoint, def.
|
|
461
|
+
const url = path.join(endpoint, def.path);
|
|
601
462
|
log3.info("exec", {
|
|
602
|
-
function: def.
|
|
463
|
+
function: def.id,
|
|
603
464
|
url
|
|
604
465
|
}, {
|
|
605
466
|
F: __dxlog_file3,
|
|
606
|
-
L:
|
|
467
|
+
L: 133,
|
|
607
468
|
S: this,
|
|
608
469
|
C: (f, a) => f(...a)
|
|
609
470
|
});
|
|
@@ -612,462 +473,360 @@ var Scheduler = class {
|
|
|
612
473
|
headers: {
|
|
613
474
|
"Content-Type": "application/json"
|
|
614
475
|
},
|
|
615
|
-
body: JSON.stringify(
|
|
476
|
+
body: JSON.stringify(data)
|
|
616
477
|
});
|
|
617
478
|
status = response.status;
|
|
618
479
|
} else if (callback) {
|
|
619
480
|
log3.info("exec", {
|
|
620
|
-
function: def.
|
|
481
|
+
function: def.id
|
|
621
482
|
}, {
|
|
622
483
|
F: __dxlog_file3,
|
|
623
|
-
L:
|
|
484
|
+
L: 144,
|
|
624
485
|
S: this,
|
|
625
486
|
C: (f, a) => f(...a)
|
|
626
487
|
});
|
|
627
|
-
status = await callback(
|
|
488
|
+
status = await callback(data) ?? 200;
|
|
628
489
|
}
|
|
629
490
|
if (status && status >= 400) {
|
|
630
491
|
throw new Error(`Response: ${status}`);
|
|
631
492
|
}
|
|
632
493
|
log3.info("done", {
|
|
633
|
-
function: def.
|
|
494
|
+
function: def.id,
|
|
634
495
|
status
|
|
635
496
|
}, {
|
|
636
497
|
F: __dxlog_file3,
|
|
637
|
-
L:
|
|
498
|
+
L: 154,
|
|
638
499
|
S: this,
|
|
639
500
|
C: (f, a) => f(...a)
|
|
640
501
|
});
|
|
502
|
+
return status;
|
|
641
503
|
} catch (err) {
|
|
642
504
|
log3.error("error", {
|
|
643
|
-
function: def.
|
|
505
|
+
function: def.id,
|
|
644
506
|
error: err.message
|
|
645
507
|
}, {
|
|
646
508
|
F: __dxlog_file3,
|
|
647
|
-
L:
|
|
509
|
+
L: 157,
|
|
648
510
|
S: this,
|
|
649
511
|
C: (f, a) => f(...a)
|
|
650
512
|
});
|
|
651
|
-
|
|
513
|
+
return 500;
|
|
652
514
|
}
|
|
653
|
-
return status;
|
|
654
515
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
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
|
|
516
|
+
//
|
|
517
|
+
// Triggers
|
|
518
|
+
//
|
|
519
|
+
/**
|
|
520
|
+
* Cron timer.
|
|
521
|
+
*/
|
|
522
|
+
async _createTimer(ctx, space, def, trigger) {
|
|
523
|
+
log3.info("timer", {
|
|
524
|
+
space: space.key,
|
|
525
|
+
trigger
|
|
690
526
|
}, {
|
|
691
|
-
F:
|
|
692
|
-
L:
|
|
693
|
-
S:
|
|
527
|
+
F: __dxlog_file3,
|
|
528
|
+
L: 170,
|
|
529
|
+
S: this,
|
|
694
530
|
C: (f, a) => f(...a)
|
|
695
531
|
});
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
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)
|
|
532
|
+
const { cron } = trigger;
|
|
533
|
+
const task = new DeferredTask(ctx, async () => {
|
|
534
|
+
await this._execFunction(def, {
|
|
535
|
+
spaceKey: space.key
|
|
762
536
|
});
|
|
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)
|
|
795
537
|
});
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
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
|
|
538
|
+
let last = 0;
|
|
539
|
+
let run = 0;
|
|
540
|
+
const job = CronJob.from({
|
|
541
|
+
cronTime: cron,
|
|
542
|
+
runOnInit: false,
|
|
543
|
+
onTick: () => {
|
|
544
|
+
const now = Date.now();
|
|
545
|
+
const delta = last ? now - last : 0;
|
|
546
|
+
last = now;
|
|
547
|
+
run++;
|
|
548
|
+
log3.info("tick", {
|
|
549
|
+
space: space.key.truncate(),
|
|
550
|
+
count: run,
|
|
551
|
+
delta
|
|
821
552
|
}, {
|
|
822
|
-
F:
|
|
823
|
-
L:
|
|
824
|
-
S:
|
|
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...`, {
|
|
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,
|
|
553
|
+
F: __dxlog_file3,
|
|
554
|
+
L: 190,
|
|
555
|
+
S: this,
|
|
864
556
|
C: (f, a) => f(...a)
|
|
865
557
|
});
|
|
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
|
-
}
|
|
558
|
+
task.schedule();
|
|
889
559
|
}
|
|
890
560
|
});
|
|
891
|
-
|
|
892
|
-
|
|
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
|
-
}
|
|
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);
|
|
561
|
+
job.start();
|
|
562
|
+
ctx.onDispose(() => job.stop());
|
|
933
563
|
}
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
async
|
|
938
|
-
|
|
939
|
-
space:
|
|
564
|
+
/**
|
|
565
|
+
* Webhook.
|
|
566
|
+
*/
|
|
567
|
+
async _createWebhook(ctx, space, def, trigger) {
|
|
568
|
+
log3.info("webhook", {
|
|
569
|
+
space: space.key,
|
|
940
570
|
trigger
|
|
941
571
|
}, {
|
|
942
|
-
F:
|
|
943
|
-
L:
|
|
572
|
+
F: __dxlog_file3,
|
|
573
|
+
L: 203,
|
|
944
574
|
S: this,
|
|
945
575
|
C: (f, a) => f(...a)
|
|
946
576
|
});
|
|
947
|
-
const
|
|
948
|
-
|
|
577
|
+
const server = http.createServer(async (req, res) => {
|
|
578
|
+
if (req.method !== trigger.method) {
|
|
579
|
+
res.statusCode = 405;
|
|
580
|
+
return res.end();
|
|
581
|
+
}
|
|
582
|
+
res.statusCode = await this._execFunction(def, {
|
|
583
|
+
spaceKey: space.key
|
|
584
|
+
});
|
|
585
|
+
res.end();
|
|
949
586
|
});
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
587
|
+
const port = await getPort2({
|
|
588
|
+
random: true
|
|
589
|
+
});
|
|
590
|
+
server.listen(port, () => {
|
|
591
|
+
log3.info("started webhook", {
|
|
592
|
+
port
|
|
593
|
+
}, {
|
|
594
|
+
F: __dxlog_file3,
|
|
595
|
+
L: 226,
|
|
596
|
+
S: this,
|
|
597
|
+
C: (f, a) => f(...a)
|
|
598
|
+
});
|
|
599
|
+
trigger.port = port;
|
|
600
|
+
});
|
|
601
|
+
ctx.onDispose(() => {
|
|
602
|
+
server.close();
|
|
960
603
|
});
|
|
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
|
-
}
|
|
969
604
|
}
|
|
970
605
|
/**
|
|
971
|
-
*
|
|
606
|
+
* Websocket.
|
|
607
|
+
* NOTE: The port must be unique, so the same hook cannot be used for multiple spaces.
|
|
972
608
|
*/
|
|
973
|
-
async
|
|
974
|
-
|
|
975
|
-
|
|
609
|
+
async _createWebsocket(ctx, space, def, trigger, options = {
|
|
610
|
+
retryDelay: 2,
|
|
611
|
+
maxAttempts: 5
|
|
612
|
+
}) {
|
|
613
|
+
log3.info("websocket", {
|
|
614
|
+
space: space.key,
|
|
615
|
+
trigger
|
|
976
616
|
}, {
|
|
977
|
-
F:
|
|
978
|
-
L:
|
|
617
|
+
F: __dxlog_file3,
|
|
618
|
+
L: 252,
|
|
979
619
|
S: this,
|
|
980
620
|
C: (f, a) => f(...a)
|
|
981
621
|
});
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
622
|
+
const { url } = trigger;
|
|
623
|
+
let ws;
|
|
624
|
+
for (let attempt = 1; attempt <= options.maxAttempts; attempt++) {
|
|
625
|
+
const open = new Trigger2();
|
|
626
|
+
ws = new WebSocket(url);
|
|
627
|
+
Object.assign(ws, {
|
|
628
|
+
onopen: () => {
|
|
629
|
+
log3.info("opened", {
|
|
630
|
+
url
|
|
631
|
+
}, {
|
|
632
|
+
F: __dxlog_file3,
|
|
633
|
+
L: 262,
|
|
634
|
+
S: this,
|
|
635
|
+
C: (f, a) => f(...a)
|
|
636
|
+
});
|
|
637
|
+
if (trigger.init) {
|
|
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
|
+
}
|
|
998
682
|
}
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
683
|
+
});
|
|
684
|
+
const isOpen = await open.wait();
|
|
685
|
+
if (isOpen) {
|
|
686
|
+
break;
|
|
687
|
+
} else {
|
|
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);
|
|
1004
699
|
}
|
|
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
700
|
}
|
|
701
|
+
}
|
|
702
|
+
ctx.onDispose(() => {
|
|
703
|
+
ws?.close();
|
|
1011
704
|
});
|
|
1012
|
-
this._ctx.onDispose(() => spaceListSubscription.unsubscribe());
|
|
1013
|
-
}
|
|
1014
|
-
async _close(_) {
|
|
1015
|
-
this._triggersBySpaceKey.clear();
|
|
1016
705
|
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
706
|
+
/**
|
|
707
|
+
* ECHO subscription.
|
|
708
|
+
*/
|
|
709
|
+
async _createSubscription(ctx, space, def, trigger) {
|
|
710
|
+
log3.info("subscription", {
|
|
711
|
+
space: space.key,
|
|
712
|
+
trigger
|
|
713
|
+
}, {
|
|
714
|
+
F: __dxlog_file3,
|
|
715
|
+
L: 311,
|
|
716
|
+
S: this,
|
|
717
|
+
C: (f, a) => f(...a)
|
|
1020
718
|
});
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
}));
|
|
1025
|
-
registered.push(...newRegisteredTriggers);
|
|
1026
|
-
log8("registered new triggers", () => ({
|
|
719
|
+
const objectIds = /* @__PURE__ */ new Set();
|
|
720
|
+
const task = new DeferredTask(ctx, async () => {
|
|
721
|
+
await this._execFunction(def, {
|
|
1027
722
|
spaceKey: space.key,
|
|
1028
|
-
|
|
1029
|
-
})
|
|
1030
|
-
|
|
1031
|
-
|
|
723
|
+
objects: Array.from(objectIds)
|
|
724
|
+
});
|
|
725
|
+
});
|
|
726
|
+
const subscriptions = [];
|
|
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,
|
|
1032
734
|
S: this,
|
|
1033
735
|
C: (f, a) => f(...a)
|
|
1034
736
|
});
|
|
1035
|
-
|
|
1036
|
-
|
|
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);
|
|
737
|
+
for (const object of added) {
|
|
738
|
+
objectIds.add(object.id);
|
|
1049
739
|
}
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
740
|
+
for (const object of updated) {
|
|
741
|
+
objectIds.add(object.id);
|
|
742
|
+
}
|
|
743
|
+
task.schedule();
|
|
744
|
+
});
|
|
745
|
+
subscriptions.push(() => subscription.unsubscribe());
|
|
746
|
+
const { filter, options: { deep, delay } = {} } = trigger;
|
|
747
|
+
const update = ({ objects }) => {
|
|
748
|
+
subscription.update(objects);
|
|
749
|
+
if (deep) {
|
|
750
|
+
log3.info("update", {
|
|
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
|
+
}
|
|
766
|
+
}
|
|
767
|
+
};
|
|
768
|
+
const query = space.db.query(Filter.or(filter.map(({ type, props }) => Filter.typename(type, props))));
|
|
769
|
+
subscriptions.push(query.subscribe(delay ? debounce(update, delay) : update));
|
|
770
|
+
ctx.onDispose(() => {
|
|
771
|
+
subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
772
|
+
});
|
|
1061
773
|
}
|
|
1062
774
|
};
|
|
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
|
+
});
|
|
1063
826
|
export {
|
|
1064
827
|
DevServer,
|
|
1065
|
-
FunctionDef,
|
|
1066
828
|
FunctionManifestSchema,
|
|
1067
|
-
FunctionRegistry,
|
|
1068
|
-
FunctionTrigger,
|
|
1069
829
|
Scheduler,
|
|
1070
|
-
TriggerRegistry,
|
|
1071
830
|
subscriptionHandler
|
|
1072
831
|
};
|
|
1073
832
|
//# sourceMappingURL=index.mjs.map
|