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