@dxos/functions 0.5.3-main.d7fe7b5 → 0.5.3-main.e76d664
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 +429 -802
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +426 -787
- 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 -33
- 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 +13 -16
- package/dist/types/src/runtime/dev-server.d.ts.map +1 -1
- package/dist/types/src/runtime/scheduler.d.ts +27 -12
- package/dist/types/src/runtime/scheduler.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +101 -129
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +13 -18
- package/schema/functions.json +101 -128
- package/src/handler.ts +31 -54
- package/src/index.ts +0 -2
- package/src/runtime/dev-server.ts +52 -104
- package/src/runtime/scheduler.test.ts +73 -56
- package/src/runtime/scheduler.ts +271 -79
- package/src/types.ts +32 -57
- 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/runtime/dev-server.test.d.ts +0 -2
- package/dist/types/src/runtime/dev-server.test.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/test/handler.d.ts +0 -4
- package/dist/types/src/testing/test/handler.d.ts.map +0 -1
- package/dist/types/src/testing/test/index.d.ts +0 -3
- package/dist/types/src/testing/test/index.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/runtime/dev-server.test.ts +0 -60
- 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/test/handler.ts +0 -15
- package/src/testing/test/index.ts +0 -7
- 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.space ? client.spaces.get(PublicKey.from(event.space)) : void 0;
|
|
26
|
+
const objects = space && event.objects?.map((id) => space.db.getObjectById(id)).filter(nonNullable);
|
|
27
|
+
if (!!event.space && !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,180 +55,26 @@ 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.isSchemaRegistered(FunctionDef)) {
|
|
150
|
-
space.db.graph.runtimeSchemaRegistry.registerSchema(FunctionDef);
|
|
151
|
-
}
|
|
152
|
-
const { objects: existingDefinitions } = await space.db.query(Filter.schema(FunctionDef)).run();
|
|
153
|
-
const newDefinitions = getNewDefinitions(manifest.functions, existingDefinitions);
|
|
154
|
-
const reactiveObjects = newDefinitions.map((template) => create(FunctionDef, {
|
|
155
|
-
...template
|
|
156
|
-
}));
|
|
157
|
-
reactiveObjects.forEach((obj) => space.db.add(obj));
|
|
158
|
-
}
|
|
159
|
-
async _open() {
|
|
160
|
-
const spaceListSubscription = this._client.spaces.subscribe(async (spaces) => {
|
|
161
|
-
for (const space of spaces) {
|
|
162
|
-
if (this._functionBySpaceKey.has(space.key)) {
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
const registered = [];
|
|
166
|
-
this._functionBySpaceKey.set(space.key, registered);
|
|
167
|
-
await space.waitUntilReady();
|
|
168
|
-
if (this._ctx.disposed) {
|
|
169
|
-
break;
|
|
170
|
-
}
|
|
171
|
-
const functionsSubscription = space.db.query(Filter.schema(FunctionDef)).subscribe((definitions) => {
|
|
172
|
-
const newFunctions = getNewDefinitions(definitions.objects, registered);
|
|
173
|
-
if (newFunctions.length > 0) {
|
|
174
|
-
registered.push(...newFunctions);
|
|
175
|
-
this.onFunctionsRegistered.emit({
|
|
176
|
-
space,
|
|
177
|
-
newFunctions
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
this._ctx.onDispose(functionsSubscription);
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
this._ctx.onDispose(() => spaceListSubscription.unsubscribe());
|
|
185
|
-
}
|
|
186
|
-
async _close(_) {
|
|
187
|
-
this._functionBySpaceKey.clear();
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
|
-
var getNewDefinitions = (candidateList, existing) => {
|
|
191
|
-
return candidateList.filter((candidate) => existing.find((def) => def.uri === candidate.uri) == null);
|
|
192
|
-
};
|
|
193
|
-
|
|
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 {
|
|
199
|
-
import { Context } from "@dxos/context";
|
|
62
|
+
import { 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 Event2();
|
|
213
|
-
this._functionsRegistry.onFunctionsRegistered.on(async ({ newFunctions }) => {
|
|
214
|
-
newFunctions.forEach((def) => this._load(def));
|
|
215
|
-
await this._safeUpdateRegistration();
|
|
216
|
-
log2("new functions loaded", {
|
|
217
|
-
newFunctions
|
|
218
|
-
}, {
|
|
219
|
-
F: __dxlog_file2,
|
|
220
|
-
L: 53,
|
|
221
|
-
S: this,
|
|
222
|
-
C: (f, a) => f(...a)
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
get stats() {
|
|
227
|
-
return {
|
|
228
|
-
seq: this._seq
|
|
229
|
-
};
|
|
230
73
|
}
|
|
231
74
|
get endpoint() {
|
|
232
75
|
invariant(this._port, void 0, {
|
|
233
76
|
F: __dxlog_file2,
|
|
234
|
-
L:
|
|
77
|
+
L: 46,
|
|
235
78
|
S: this,
|
|
236
79
|
A: [
|
|
237
80
|
"this._port",
|
|
@@ -246,46 +89,44 @@ var DevServer = class {
|
|
|
246
89
|
get functions() {
|
|
247
90
|
return Object.values(this._handlers);
|
|
248
91
|
}
|
|
92
|
+
async initialize() {
|
|
93
|
+
for (const def of this._options.manifest.functions) {
|
|
94
|
+
try {
|
|
95
|
+
await this._load(def);
|
|
96
|
+
} catch (err) {
|
|
97
|
+
log2.error("parsing function (check manifest)", err, {
|
|
98
|
+
F: __dxlog_file2,
|
|
99
|
+
L: 63,
|
|
100
|
+
S: this,
|
|
101
|
+
C: (f, a) => f(...a)
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
249
106
|
async start() {
|
|
250
|
-
invariant(!this._server, void 0, {
|
|
251
|
-
F: __dxlog_file2,
|
|
252
|
-
L: 77,
|
|
253
|
-
S: this,
|
|
254
|
-
A: [
|
|
255
|
-
"!this._server",
|
|
256
|
-
""
|
|
257
|
-
]
|
|
258
|
-
});
|
|
259
|
-
log2.info("starting...", void 0, {
|
|
260
|
-
F: __dxlog_file2,
|
|
261
|
-
L: 78,
|
|
262
|
-
S: this,
|
|
263
|
-
C: (f, a) => f(...a)
|
|
264
|
-
});
|
|
265
|
-
this._ctx = createContext();
|
|
266
107
|
const app = express();
|
|
267
108
|
app.use(express.json());
|
|
268
|
-
app.post("/:
|
|
269
|
-
const {
|
|
109
|
+
app.post("/:name", async (req, res) => {
|
|
110
|
+
const { name } = req.params;
|
|
270
111
|
try {
|
|
271
112
|
log2.info("calling", {
|
|
272
|
-
|
|
113
|
+
name
|
|
273
114
|
}, {
|
|
274
115
|
F: __dxlog_file2,
|
|
275
|
-
L:
|
|
116
|
+
L: 75,
|
|
276
117
|
S: this,
|
|
277
118
|
C: (f, a) => f(...a)
|
|
278
119
|
});
|
|
279
120
|
if (this._options.reload) {
|
|
280
|
-
const { def } = this._handlers[
|
|
121
|
+
const { def } = this._handlers[name];
|
|
281
122
|
await this._load(def, true);
|
|
282
123
|
}
|
|
283
|
-
res.statusCode = await this.
|
|
124
|
+
res.statusCode = await this._invoke(name, req.body);
|
|
284
125
|
res.end();
|
|
285
126
|
} catch (err) {
|
|
286
127
|
log2.catch(err, void 0, {
|
|
287
128
|
F: __dxlog_file2,
|
|
288
|
-
L:
|
|
129
|
+
L: 84,
|
|
289
130
|
S: this,
|
|
290
131
|
C: (f, a) => f(...a)
|
|
291
132
|
});
|
|
@@ -304,195 +145,93 @@ var DevServer = class {
|
|
|
304
145
|
this._server = app.listen(this._port);
|
|
305
146
|
try {
|
|
306
147
|
const { registrationId, endpoint } = await this._client.services.services.FunctionRegistryService.register({
|
|
307
|
-
endpoint: this.endpoint
|
|
148
|
+
endpoint: this.endpoint,
|
|
149
|
+
functions: this.functions.map(({ def: { name } }) => ({
|
|
150
|
+
name
|
|
151
|
+
}))
|
|
308
152
|
});
|
|
309
153
|
log2.info("registered", {
|
|
154
|
+
registrationId,
|
|
310
155
|
endpoint
|
|
311
156
|
}, {
|
|
312
157
|
F: __dxlog_file2,
|
|
313
|
-
L:
|
|
158
|
+
L: 100,
|
|
314
159
|
S: this,
|
|
315
160
|
C: (f, a) => f(...a)
|
|
316
161
|
});
|
|
162
|
+
this._registrationId = registrationId;
|
|
317
163
|
this._proxy = endpoint;
|
|
318
|
-
this._functionServiceRegistration = registrationId;
|
|
319
|
-
await this._functionsRegistry.open(this._ctx);
|
|
320
164
|
} catch (err) {
|
|
321
165
|
await this.stop();
|
|
322
166
|
throw new Error("FunctionRegistryService not available (check plugin is configured).");
|
|
323
167
|
}
|
|
324
|
-
log2.info("started", {
|
|
325
|
-
port: this._port
|
|
326
|
-
}, {
|
|
327
|
-
F: __dxlog_file2,
|
|
328
|
-
L: 124,
|
|
329
|
-
S: this,
|
|
330
|
-
C: (f, a) => f(...a)
|
|
331
|
-
});
|
|
332
168
|
}
|
|
333
169
|
async stop() {
|
|
334
|
-
invariant(this._server, void 0, {
|
|
335
|
-
F: __dxlog_file2,
|
|
336
|
-
L: 128,
|
|
337
|
-
S: this,
|
|
338
|
-
A: [
|
|
339
|
-
"this._server",
|
|
340
|
-
""
|
|
341
|
-
]
|
|
342
|
-
});
|
|
343
|
-
log2.info("stopping...", void 0, {
|
|
344
|
-
F: __dxlog_file2,
|
|
345
|
-
L: 129,
|
|
346
|
-
S: this,
|
|
347
|
-
C: (f, a) => f(...a)
|
|
348
|
-
});
|
|
349
170
|
const trigger = new Trigger();
|
|
350
|
-
this._server
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
""
|
|
366
|
-
]
|
|
367
|
-
});
|
|
368
|
-
await this._client.services.services.FunctionRegistryService.unregister({
|
|
369
|
-
registrationId: this._functionServiceRegistration
|
|
370
|
-
});
|
|
371
|
-
log2.info("unregistered", {
|
|
372
|
-
registrationId: this._functionServiceRegistration
|
|
373
|
-
}, {
|
|
374
|
-
F: __dxlog_file2,
|
|
375
|
-
L: 141,
|
|
376
|
-
S: this,
|
|
377
|
-
C: (f, a) => f(...a)
|
|
378
|
-
});
|
|
379
|
-
this._functionServiceRegistration = void 0;
|
|
380
|
-
this._proxy = void 0;
|
|
381
|
-
}
|
|
382
|
-
trigger.wake();
|
|
383
|
-
} catch (err) {
|
|
384
|
-
trigger.throw(err);
|
|
171
|
+
this._server?.close(async () => {
|
|
172
|
+
if (this._registrationId) {
|
|
173
|
+
await this._client.services.services.FunctionRegistryService.unregister({
|
|
174
|
+
registrationId: this._registrationId
|
|
175
|
+
});
|
|
176
|
+
log2.info("unregistered", {
|
|
177
|
+
registrationId: this._registrationId
|
|
178
|
+
}, {
|
|
179
|
+
F: __dxlog_file2,
|
|
180
|
+
L: 117,
|
|
181
|
+
S: this,
|
|
182
|
+
C: (f, a) => f(...a)
|
|
183
|
+
});
|
|
184
|
+
this._registrationId = void 0;
|
|
185
|
+
this._proxy = void 0;
|
|
385
186
|
}
|
|
187
|
+
trigger.wake();
|
|
386
188
|
});
|
|
387
189
|
await trigger.wait();
|
|
388
190
|
this._port = void 0;
|
|
389
191
|
this._server = void 0;
|
|
390
|
-
log2.info("stopped", void 0, {
|
|
391
|
-
F: __dxlog_file2,
|
|
392
|
-
L: 155,
|
|
393
|
-
S: this,
|
|
394
|
-
C: (f, a) => f(...a)
|
|
395
|
-
});
|
|
396
192
|
}
|
|
397
193
|
/**
|
|
398
194
|
* Load function.
|
|
399
195
|
*/
|
|
400
|
-
async _load(def,
|
|
401
|
-
const {
|
|
402
|
-
const
|
|
196
|
+
async _load(def, flush = false) {
|
|
197
|
+
const { id, name, handler } = def;
|
|
198
|
+
const path = join(this._options.directory, handler);
|
|
403
199
|
log2.info("loading", {
|
|
404
|
-
|
|
405
|
-
force
|
|
200
|
+
id
|
|
406
201
|
}, {
|
|
407
202
|
F: __dxlog_file2,
|
|
408
|
-
L:
|
|
203
|
+
L: 136,
|
|
409
204
|
S: this,
|
|
410
205
|
C: (f, a) => f(...a)
|
|
411
206
|
});
|
|
412
|
-
if (
|
|
413
|
-
Object.keys(__require.cache).filter((key) => key.startsWith(
|
|
414
|
-
delete __require.cache[key];
|
|
415
|
-
});
|
|
207
|
+
if (flush) {
|
|
208
|
+
Object.keys(__require.cache).filter((key) => key.startsWith(path)).forEach((key) => delete __require.cache[key]);
|
|
416
209
|
}
|
|
417
|
-
const module = __require(
|
|
210
|
+
const module = __require(path);
|
|
418
211
|
if (typeof module.default !== "function") {
|
|
419
|
-
throw new Error(`Handler must export default function: ${
|
|
212
|
+
throw new Error(`Handler must export default function: ${id}`);
|
|
420
213
|
}
|
|
421
|
-
this._handlers[
|
|
214
|
+
this._handlers[name] = {
|
|
422
215
|
def,
|
|
423
216
|
handler: module.default
|
|
424
217
|
};
|
|
425
218
|
}
|
|
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
219
|
/**
|
|
454
|
-
* Invoke function.
|
|
220
|
+
* Invoke function handler.
|
|
455
221
|
*/
|
|
456
|
-
async
|
|
222
|
+
async _invoke(name, event) {
|
|
457
223
|
const seq = ++this._seq;
|
|
458
224
|
const now = Date.now();
|
|
459
225
|
log2.info("req", {
|
|
460
226
|
seq,
|
|
461
|
-
|
|
227
|
+
name
|
|
462
228
|
}, {
|
|
463
229
|
F: __dxlog_file2,
|
|
464
|
-
L:
|
|
230
|
+
L: 161,
|
|
465
231
|
S: this,
|
|
466
232
|
C: (f, a) => f(...a)
|
|
467
233
|
});
|
|
468
|
-
const
|
|
469
|
-
data
|
|
470
|
-
});
|
|
471
|
-
log2.info("res", {
|
|
472
|
-
seq,
|
|
473
|
-
path: path2,
|
|
474
|
-
statusCode,
|
|
475
|
-
duration: Date.now() - now
|
|
476
|
-
}, {
|
|
477
|
-
F: __dxlog_file2,
|
|
478
|
-
L: 207,
|
|
479
|
-
S: this,
|
|
480
|
-
C: (f, a) => f(...a)
|
|
481
|
-
});
|
|
482
|
-
this.update.emit(statusCode);
|
|
483
|
-
return statusCode;
|
|
484
|
-
}
|
|
485
|
-
async _invoke(path2, event) {
|
|
486
|
-
const { handler } = this._handlers[path2] ?? {};
|
|
487
|
-
invariant(handler, `invalid path: ${path2}`, {
|
|
488
|
-
F: __dxlog_file2,
|
|
489
|
-
L: 214,
|
|
490
|
-
S: this,
|
|
491
|
-
A: [
|
|
492
|
-
"handler",
|
|
493
|
-
"`invalid path: ${path}`"
|
|
494
|
-
]
|
|
495
|
-
});
|
|
234
|
+
const { handler } = this._handlers[name];
|
|
496
235
|
const context = {
|
|
497
236
|
client: this._client,
|
|
498
237
|
dataDir: this._options.dataDir
|
|
@@ -509,565 +248,453 @@ var DevServer = class {
|
|
|
509
248
|
event,
|
|
510
249
|
response
|
|
511
250
|
});
|
|
251
|
+
log2.info("res", {
|
|
252
|
+
seq,
|
|
253
|
+
name,
|
|
254
|
+
statusCode,
|
|
255
|
+
duration: Date.now() - now
|
|
256
|
+
}, {
|
|
257
|
+
F: __dxlog_file2,
|
|
258
|
+
L: 178,
|
|
259
|
+
S: this,
|
|
260
|
+
C: (f, a) => f(...a)
|
|
261
|
+
});
|
|
512
262
|
return statusCode;
|
|
513
263
|
}
|
|
514
264
|
};
|
|
515
|
-
var createContext = () => new Context({
|
|
516
|
-
name: "DevServer"
|
|
517
|
-
});
|
|
518
265
|
|
|
519
266
|
// packages/core/functions/src/runtime/scheduler.ts
|
|
520
|
-
import
|
|
521
|
-
import
|
|
267
|
+
import { CronJob } from "cron";
|
|
268
|
+
import http from "@dxos/node-std/http";
|
|
269
|
+
import WebSocket from "ws";
|
|
270
|
+
import { TextV0Type } from "@braneframe/types";
|
|
271
|
+
import { debounce, DeferredTask, sleep, Trigger as Trigger2 } from "@dxos/async";
|
|
272
|
+
import { createSubscription, Filter, getAutomergeObjectCore } from "@dxos/client/echo";
|
|
273
|
+
import { Context } from "@dxos/context";
|
|
274
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
522
275
|
import { log as log3 } from "@dxos/log";
|
|
276
|
+
import { ComplexMap } from "@dxos/util";
|
|
523
277
|
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/scheduler.ts";
|
|
524
278
|
var Scheduler = class {
|
|
525
|
-
constructor(
|
|
526
|
-
this.
|
|
527
|
-
this.
|
|
279
|
+
constructor(_client, _manifest, _options = {}) {
|
|
280
|
+
this._client = _client;
|
|
281
|
+
this._manifest = _manifest;
|
|
528
282
|
this._options = _options;
|
|
529
|
-
this.
|
|
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
|
-
});
|
|
283
|
+
this._mounts = new ComplexMap(({ id, spaceKey }) => `${spaceKey.toHex()}:${id}`);
|
|
536
284
|
}
|
|
537
285
|
async start() {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
286
|
+
this._client.spaces.subscribe(async (spaces) => {
|
|
287
|
+
for (const space of spaces) {
|
|
288
|
+
await space.waitUntilReady();
|
|
289
|
+
for (const trigger of this._manifest.triggers ?? []) {
|
|
290
|
+
await this.mount(new Context(), space, trigger);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
});
|
|
542
294
|
}
|
|
543
295
|
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);
|
|
296
|
+
for (const { id, spaceKey } of this._mounts.keys()) {
|
|
297
|
+
await this.unmount(id, spaceKey);
|
|
298
|
+
}
|
|
551
299
|
}
|
|
552
|
-
async
|
|
553
|
-
const
|
|
554
|
-
|
|
300
|
+
async mount(ctx, space, trigger) {
|
|
301
|
+
const key = {
|
|
302
|
+
id: trigger.function,
|
|
303
|
+
spaceKey: space.key
|
|
304
|
+
};
|
|
305
|
+
const def = this._manifest.functions.find((config) => config.id === trigger.function);
|
|
306
|
+
invariant2(def, `Function not found: ${trigger.function}`, {
|
|
307
|
+
F: __dxlog_file3,
|
|
308
|
+
L: 72,
|
|
309
|
+
S: this,
|
|
310
|
+
A: [
|
|
311
|
+
"def",
|
|
312
|
+
"`Function not found: ${trigger.function}`"
|
|
313
|
+
]
|
|
555
314
|
});
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
315
|
+
const exists = this._mounts.get(key);
|
|
316
|
+
if (!exists) {
|
|
317
|
+
this._mounts.set(key, {
|
|
318
|
+
ctx,
|
|
319
|
+
trigger
|
|
320
|
+
});
|
|
321
|
+
log3("mount", {
|
|
322
|
+
space: space.key,
|
|
323
|
+
trigger
|
|
563
324
|
}, {
|
|
564
325
|
F: __dxlog_file3,
|
|
565
|
-
L:
|
|
326
|
+
L: 78,
|
|
566
327
|
S: this,
|
|
567
328
|
C: (f, a) => f(...a)
|
|
568
329
|
});
|
|
569
|
-
|
|
330
|
+
if (ctx.disposed) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
if (trigger.timer) {
|
|
334
|
+
await this._createTimer(ctx, space, def, trigger.timer);
|
|
335
|
+
}
|
|
336
|
+
if (trigger.webhook) {
|
|
337
|
+
await this._createWebhook(ctx, space, def, trigger.webhook);
|
|
338
|
+
}
|
|
339
|
+
if (trigger.websocket) {
|
|
340
|
+
await this._createWebsocket(ctx, space, def, trigger.websocket);
|
|
341
|
+
}
|
|
342
|
+
if (trigger.subscription) {
|
|
343
|
+
await this._createSubscription(ctx, space, def, trigger.subscription);
|
|
344
|
+
}
|
|
570
345
|
}
|
|
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
346
|
}
|
|
592
|
-
async
|
|
593
|
-
|
|
347
|
+
async unmount(id, spaceKey) {
|
|
348
|
+
const key = {
|
|
349
|
+
id,
|
|
350
|
+
spaceKey
|
|
351
|
+
};
|
|
352
|
+
const { ctx } = this._mounts.get(key) ?? {};
|
|
353
|
+
if (ctx) {
|
|
354
|
+
this._mounts.delete(key);
|
|
355
|
+
await ctx.dispose();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
// TODO(burdon): Pass in Space key (common context).
|
|
359
|
+
async _execFunction(def, data) {
|
|
594
360
|
try {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
},
|
|
361
|
+
log3.info("exec", {
|
|
362
|
+
function: def.id
|
|
363
|
+
}, {
|
|
364
|
+
F: __dxlog_file3,
|
|
365
|
+
L: 117,
|
|
366
|
+
S: this,
|
|
367
|
+
C: (f, a) => f(...a)
|
|
368
|
+
});
|
|
598
369
|
const { endpoint, callback } = this._options;
|
|
599
370
|
if (endpoint) {
|
|
600
|
-
|
|
601
|
-
log3.info("exec", {
|
|
602
|
-
function: def.uri,
|
|
603
|
-
url
|
|
604
|
-
}, {
|
|
605
|
-
F: __dxlog_file3,
|
|
606
|
-
L: 100,
|
|
607
|
-
S: this,
|
|
608
|
-
C: (f, a) => f(...a)
|
|
609
|
-
});
|
|
610
|
-
const response = await fetch(url, {
|
|
371
|
+
await fetch(`${this._options.endpoint}/${def.name}`, {
|
|
611
372
|
method: "POST",
|
|
612
373
|
headers: {
|
|
613
374
|
"Content-Type": "application/json"
|
|
614
375
|
},
|
|
615
|
-
body: JSON.stringify(
|
|
376
|
+
body: JSON.stringify(data)
|
|
616
377
|
});
|
|
617
|
-
status = response.status;
|
|
618
378
|
} else if (callback) {
|
|
619
|
-
|
|
620
|
-
function: def.uri
|
|
621
|
-
}, {
|
|
622
|
-
F: __dxlog_file3,
|
|
623
|
-
L: 111,
|
|
624
|
-
S: this,
|
|
625
|
-
C: (f, a) => f(...a)
|
|
626
|
-
});
|
|
627
|
-
status = await callback(payload) ?? 200;
|
|
628
|
-
}
|
|
629
|
-
if (status && status >= 400) {
|
|
630
|
-
throw new Error(`Response: ${status}`);
|
|
379
|
+
await callback(data);
|
|
631
380
|
}
|
|
632
381
|
log3.info("done", {
|
|
633
|
-
function: def.
|
|
634
|
-
status
|
|
382
|
+
function: def.id
|
|
635
383
|
}, {
|
|
636
384
|
F: __dxlog_file3,
|
|
637
|
-
L:
|
|
385
|
+
L: 133,
|
|
638
386
|
S: this,
|
|
639
387
|
C: (f, a) => f(...a)
|
|
640
388
|
});
|
|
641
389
|
} catch (err) {
|
|
642
390
|
log3.error("error", {
|
|
643
|
-
function: def.
|
|
391
|
+
function: def.id,
|
|
644
392
|
error: err.message
|
|
645
393
|
}, {
|
|
646
394
|
F: __dxlog_file3,
|
|
647
|
-
L:
|
|
395
|
+
L: 135,
|
|
648
396
|
S: this,
|
|
649
397
|
C: (f, a) => f(...a)
|
|
650
398
|
});
|
|
651
|
-
status = 500;
|
|
652
399
|
}
|
|
653
|
-
return status;
|
|
654
400
|
}
|
|
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
|
|
401
|
+
//
|
|
402
|
+
// Triggers
|
|
403
|
+
//
|
|
404
|
+
/**
|
|
405
|
+
* Cron timer.
|
|
406
|
+
*/
|
|
407
|
+
async _createTimer(ctx, space, def, trigger) {
|
|
408
|
+
log3.info("timer", {
|
|
409
|
+
space: space.key,
|
|
410
|
+
trigger
|
|
690
411
|
}, {
|
|
691
|
-
F:
|
|
692
|
-
L:
|
|
693
|
-
S:
|
|
412
|
+
F: __dxlog_file3,
|
|
413
|
+
L: 147,
|
|
414
|
+
S: this,
|
|
694
415
|
C: (f, a) => f(...a)
|
|
695
416
|
});
|
|
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)
|
|
417
|
+
const { cron } = trigger;
|
|
418
|
+
const task = new DeferredTask(ctx, async () => {
|
|
419
|
+
await this._execFunction(def, {
|
|
420
|
+
space: space.key
|
|
716
421
|
});
|
|
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)
|
|
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)
|
|
795
422
|
});
|
|
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
|
|
423
|
+
let last = 0;
|
|
424
|
+
let run = 0;
|
|
425
|
+
const job = CronJob.from({
|
|
426
|
+
cronTime: cron,
|
|
427
|
+
runOnInit: false,
|
|
428
|
+
onTick: () => {
|
|
429
|
+
const now = Date.now();
|
|
430
|
+
const delta = last ? now - last : 0;
|
|
431
|
+
last = now;
|
|
432
|
+
run++;
|
|
433
|
+
log3.info("tick", {
|
|
434
|
+
space: space.key.truncate(),
|
|
435
|
+
count: run,
|
|
436
|
+
delta
|
|
821
437
|
}, {
|
|
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,
|
|
438
|
+
F: __dxlog_file3,
|
|
439
|
+
L: 167,
|
|
440
|
+
S: this,
|
|
864
441
|
C: (f, a) => f(...a)
|
|
865
442
|
});
|
|
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
|
-
}
|
|
443
|
+
task.schedule();
|
|
889
444
|
}
|
|
890
445
|
});
|
|
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();
|
|
446
|
+
job.start();
|
|
447
|
+
ctx.onDispose(() => job.stop());
|
|
930
448
|
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
async activate(triggerCtx, trigger, callback) {
|
|
938
|
-
log8("activate", {
|
|
939
|
-
space: triggerCtx.space.key,
|
|
449
|
+
/**
|
|
450
|
+
* Webhook.
|
|
451
|
+
*/
|
|
452
|
+
async _createWebhook(ctx, space, def, trigger) {
|
|
453
|
+
log3.info("webhook", {
|
|
454
|
+
space: space.key,
|
|
940
455
|
trigger
|
|
941
456
|
}, {
|
|
942
|
-
F:
|
|
943
|
-
L:
|
|
457
|
+
F: __dxlog_file3,
|
|
458
|
+
L: 180,
|
|
944
459
|
S: this,
|
|
945
460
|
C: (f, a) => f(...a)
|
|
946
461
|
});
|
|
947
|
-
const
|
|
948
|
-
|
|
462
|
+
const { port } = trigger;
|
|
463
|
+
const server = http.createServer(async (req, res) => {
|
|
464
|
+
await this._execFunction(def, {
|
|
465
|
+
space: space.key
|
|
466
|
+
});
|
|
949
467
|
});
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
468
|
+
server.listen(port, () => {
|
|
469
|
+
log3.info("started webhook", {
|
|
470
|
+
port
|
|
471
|
+
}, {
|
|
472
|
+
F: __dxlog_file3,
|
|
473
|
+
L: 189,
|
|
474
|
+
S: this,
|
|
475
|
+
C: (f, a) => f(...a)
|
|
476
|
+
});
|
|
477
|
+
});
|
|
478
|
+
ctx.onDispose(() => {
|
|
479
|
+
server.close();
|
|
960
480
|
});
|
|
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
481
|
}
|
|
970
482
|
/**
|
|
971
|
-
*
|
|
483
|
+
* Websocket.
|
|
972
484
|
*/
|
|
973
|
-
async
|
|
974
|
-
|
|
975
|
-
|
|
485
|
+
async _createWebsocket(ctx, space, def, trigger, options = {
|
|
486
|
+
retryDelay: 2,
|
|
487
|
+
maxAttempts: 5
|
|
488
|
+
}) {
|
|
489
|
+
log3.info("websocket", {
|
|
490
|
+
space: space.key,
|
|
491
|
+
trigger
|
|
976
492
|
}, {
|
|
977
|
-
F:
|
|
978
|
-
L:
|
|
493
|
+
F: __dxlog_file3,
|
|
494
|
+
L: 213,
|
|
979
495
|
S: this,
|
|
980
496
|
C: (f, a) => f(...a)
|
|
981
497
|
});
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
498
|
+
const { url } = trigger;
|
|
499
|
+
let ws;
|
|
500
|
+
for (let attempt = 1; attempt <= options.maxAttempts; attempt++) {
|
|
501
|
+
const open = new Trigger2();
|
|
502
|
+
ws = new WebSocket(url);
|
|
503
|
+
Object.assign(ws, {
|
|
504
|
+
onopen: () => {
|
|
505
|
+
log3.info("opened", {
|
|
506
|
+
url
|
|
507
|
+
}, {
|
|
508
|
+
F: __dxlog_file3,
|
|
509
|
+
L: 223,
|
|
510
|
+
S: this,
|
|
511
|
+
C: (f, a) => f(...a)
|
|
512
|
+
});
|
|
513
|
+
if (trigger.init) {
|
|
514
|
+
ws.send(new TextEncoder().encode(JSON.stringify(trigger.init)));
|
|
515
|
+
}
|
|
516
|
+
open.wake(true);
|
|
517
|
+
},
|
|
518
|
+
onclose: () => {
|
|
519
|
+
log3.info("closed", {
|
|
520
|
+
url
|
|
521
|
+
}, {
|
|
522
|
+
F: __dxlog_file3,
|
|
523
|
+
L: 232,
|
|
524
|
+
S: this,
|
|
525
|
+
C: (f, a) => f(...a)
|
|
526
|
+
});
|
|
527
|
+
open.wake(false);
|
|
528
|
+
},
|
|
529
|
+
onerror: (event) => {
|
|
530
|
+
log3.catch(event.error, {
|
|
531
|
+
url
|
|
532
|
+
}, {
|
|
533
|
+
F: __dxlog_file3,
|
|
534
|
+
L: 237,
|
|
535
|
+
S: this,
|
|
536
|
+
C: (f, a) => f(...a)
|
|
537
|
+
});
|
|
538
|
+
},
|
|
539
|
+
onmessage: async (event) => {
|
|
540
|
+
try {
|
|
541
|
+
const data = JSON.parse(new TextDecoder().decode(event.data));
|
|
542
|
+
await this._execFunction(def, {
|
|
543
|
+
space: space.key,
|
|
544
|
+
data
|
|
545
|
+
});
|
|
546
|
+
} catch (err) {
|
|
547
|
+
log3.catch(err, {
|
|
548
|
+
url
|
|
549
|
+
}, {
|
|
550
|
+
F: __dxlog_file3,
|
|
551
|
+
L: 245,
|
|
552
|
+
S: this,
|
|
553
|
+
C: (f, a) => f(...a)
|
|
554
|
+
});
|
|
555
|
+
}
|
|
998
556
|
}
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
557
|
+
});
|
|
558
|
+
const isOpen = await open.wait();
|
|
559
|
+
if (isOpen) {
|
|
560
|
+
break;
|
|
561
|
+
} else {
|
|
562
|
+
const wait = Math.pow(attempt, 2) * options.retryDelay;
|
|
563
|
+
if (attempt < options.maxAttempts) {
|
|
564
|
+
log3.warn(`failed to connect; trying again in ${wait}s`, {
|
|
565
|
+
attempt
|
|
566
|
+
}, {
|
|
567
|
+
F: __dxlog_file3,
|
|
568
|
+
L: 256,
|
|
569
|
+
S: this,
|
|
570
|
+
C: (f, a) => f(...a)
|
|
571
|
+
});
|
|
572
|
+
await sleep(wait * 1e3);
|
|
1004
573
|
}
|
|
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
574
|
}
|
|
575
|
+
}
|
|
576
|
+
ctx.onDispose(() => {
|
|
577
|
+
ws?.close();
|
|
1011
578
|
});
|
|
1012
|
-
this._ctx.onDispose(() => spaceListSubscription.unsubscribe());
|
|
1013
|
-
}
|
|
1014
|
-
async _close(_) {
|
|
1015
|
-
this._triggersBySpaceKey.clear();
|
|
1016
579
|
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
580
|
+
/**
|
|
581
|
+
* ECHO subscription.
|
|
582
|
+
*/
|
|
583
|
+
async _createSubscription(ctx, space, def, trigger) {
|
|
584
|
+
log3.info("subscription", {
|
|
585
|
+
space: space.key,
|
|
586
|
+
trigger
|
|
587
|
+
}, {
|
|
588
|
+
F: __dxlog_file3,
|
|
589
|
+
L: 271,
|
|
590
|
+
S: this,
|
|
591
|
+
C: (f, a) => f(...a)
|
|
1020
592
|
});
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
593
|
+
const objectIds = /* @__PURE__ */ new Set();
|
|
594
|
+
const task = new DeferredTask(ctx, async () => {
|
|
595
|
+
await this._execFunction(def, {
|
|
596
|
+
space: space.key,
|
|
597
|
+
objects: Array.from(objectIds)
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
const subscriptions = [];
|
|
601
|
+
const subscription = createSubscription(({ added, updated }) => {
|
|
602
|
+
log3.info("updated", {
|
|
603
|
+
added: added.length,
|
|
604
|
+
updated: updated.length
|
|
605
|
+
}, {
|
|
606
|
+
F: __dxlog_file3,
|
|
607
|
+
L: 281,
|
|
1032
608
|
S: this,
|
|
1033
609
|
C: (f, a) => f(...a)
|
|
1034
610
|
});
|
|
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);
|
|
611
|
+
for (const object of added) {
|
|
612
|
+
objectIds.add(object.id);
|
|
1049
613
|
}
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
614
|
+
for (const object of updated) {
|
|
615
|
+
objectIds.add(object.id);
|
|
616
|
+
}
|
|
617
|
+
task.schedule();
|
|
618
|
+
});
|
|
619
|
+
subscriptions.push(() => subscription.unsubscribe());
|
|
620
|
+
const { filter, options: { deep, delay } = {} } = trigger;
|
|
621
|
+
const update = ({ objects }) => {
|
|
622
|
+
subscription.update(objects);
|
|
623
|
+
if (deep) {
|
|
624
|
+
log3.info("update", {
|
|
625
|
+
objects: objects.length
|
|
626
|
+
}, {
|
|
627
|
+
F: __dxlog_file3,
|
|
628
|
+
L: 301,
|
|
629
|
+
S: this,
|
|
630
|
+
C: (f, a) => f(...a)
|
|
631
|
+
});
|
|
632
|
+
for (const object of objects) {
|
|
633
|
+
const content = object.content;
|
|
634
|
+
if (content instanceof TextV0Type) {
|
|
635
|
+
subscriptions.push(getAutomergeObjectCore(content).updates.on(debounce(() => subscription.update([
|
|
636
|
+
object
|
|
637
|
+
]), 1e3)));
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
const query = space.db.query(Filter.or(filter.map(({ type, props }) => Filter.typename(type, props))));
|
|
643
|
+
subscriptions.push(query.subscribe(delay ? debounce(update, delay) : update));
|
|
644
|
+
ctx.onDispose(() => {
|
|
645
|
+
subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
646
|
+
});
|
|
1061
647
|
}
|
|
1062
648
|
};
|
|
649
|
+
|
|
650
|
+
// packages/core/functions/src/types.ts
|
|
651
|
+
import * as S from "@effect/schema/Schema";
|
|
652
|
+
var TimerTriggerSchema = S.struct({
|
|
653
|
+
cron: S.string
|
|
654
|
+
});
|
|
655
|
+
var WebhookTriggerSchema = S.struct({
|
|
656
|
+
port: S.number
|
|
657
|
+
});
|
|
658
|
+
var WebsocketTriggerSchema = S.struct({
|
|
659
|
+
url: S.string,
|
|
660
|
+
init: S.optional(S.record(S.string, S.any))
|
|
661
|
+
});
|
|
662
|
+
var SubscriptionTriggerSchema = S.struct({
|
|
663
|
+
spaceKey: S.optional(S.string),
|
|
664
|
+
// TODO(burdon): Define query DSL.
|
|
665
|
+
filter: S.array(S.struct({
|
|
666
|
+
type: S.string,
|
|
667
|
+
props: S.optional(S.record(S.string, S.any))
|
|
668
|
+
})),
|
|
669
|
+
options: S.optional(S.struct({
|
|
670
|
+
// Watch changes to object (not just creation).
|
|
671
|
+
deep: S.optional(S.boolean),
|
|
672
|
+
// Debounce changes (delay in ms).
|
|
673
|
+
delay: S.optional(S.number)
|
|
674
|
+
}))
|
|
675
|
+
});
|
|
676
|
+
var FunctionTriggerSchema = S.struct({
|
|
677
|
+
function: S.string.pipe(S.description("Function ID/URI.")),
|
|
678
|
+
timer: S.optional(TimerTriggerSchema),
|
|
679
|
+
webhook: S.optional(WebhookTriggerSchema),
|
|
680
|
+
websocket: S.optional(WebsocketTriggerSchema),
|
|
681
|
+
subscription: S.optional(SubscriptionTriggerSchema)
|
|
682
|
+
});
|
|
683
|
+
var FunctionDefSchema = S.struct({
|
|
684
|
+
id: S.string,
|
|
685
|
+
description: S.optional(S.string),
|
|
686
|
+
name: S.string,
|
|
687
|
+
// TODO(burdon): NPM/GitHub URL?
|
|
688
|
+
handler: S.string
|
|
689
|
+
});
|
|
690
|
+
var FunctionManifestSchema = S.struct({
|
|
691
|
+
functions: S.mutable(S.array(FunctionDefSchema)),
|
|
692
|
+
triggers: S.mutable(S.array(FunctionTriggerSchema))
|
|
693
|
+
});
|
|
1063
694
|
export {
|
|
1064
695
|
DevServer,
|
|
1065
|
-
FunctionDef,
|
|
1066
696
|
FunctionManifestSchema,
|
|
1067
|
-
FunctionRegistry,
|
|
1068
|
-
FunctionTrigger,
|
|
1069
697
|
Scheduler,
|
|
1070
|
-
TriggerRegistry,
|
|
1071
698
|
subscriptionHandler
|
|
1072
699
|
};
|
|
1073
700
|
//# sourceMappingURL=index.mjs.map
|