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