@dxos/functions 0.5.2 → 0.5.3-main.0a95b55
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 +829 -265
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +817 -266
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/handler.d.ts +33 -12
- package/dist/types/src/handler.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +3 -1
- 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 +15 -7
- package/dist/types/src/runtime/dev-server.d.ts.map +1 -1
- package/dist/types/src/runtime/dev-server.test.d.ts +2 -0
- package/dist/types/src/runtime/dev-server.test.d.ts.map +1 -0
- package/dist/types/src/runtime/scheduler.d.ts +14 -15
- 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 +4 -0
- package/dist/types/src/testing/test/handler.d.ts.map +1 -0
- package/dist/types/src/testing/test/index.d.ts +3 -0
- package/dist/types/src/testing/test/index.d.ts.map +1 -0
- 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 +188 -0
- package/dist/types/src/types.d.ts.map +1 -0
- package/dist/types/tools/schema.d.ts +2 -0
- package/dist/types/tools/schema.d.ts.map +1 -0
- package/package.json +23 -11
- package/schema/functions.json +197 -0
- package/src/handler.ts +56 -26
- package/src/index.ts +3 -1
- 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 +60 -0
- package/src/runtime/dev-server.ts +104 -52
- package/src/runtime/scheduler.test.ts +154 -21
- package/src/runtime/scheduler.ts +76 -152
- 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 +15 -0
- package/src/testing/test/index.ts +7 -0
- 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 +101 -0
- package/dist/types/src/manifest.d.ts +0 -26
- package/dist/types/src/manifest.d.ts.map +0 -1
- package/src/manifest.ts +0 -42
|
@@ -20,16 +20,16 @@ import { log } from "@dxos/log";
|
|
|
20
20
|
import { nonNullable } from "@dxos/util";
|
|
21
21
|
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/functions/src/handler.ts";
|
|
22
22
|
var subscriptionHandler = (handler) => {
|
|
23
|
-
return ({ event, context, ...rest }) => {
|
|
23
|
+
return ({ event: { data }, context, ...rest }) => {
|
|
24
24
|
const { client } = context;
|
|
25
|
-
const space =
|
|
26
|
-
const objects = space
|
|
27
|
-
if (!!
|
|
25
|
+
const space = data.spaceKey ? client.spaces.get(PublicKey.from(data.spaceKey)) : void 0;
|
|
26
|
+
const objects = space ? data.objects?.map((id) => space.db.getObjectById(id)).filter(nonNullable) : [];
|
|
27
|
+
if (!!data.spaceKey && !space) {
|
|
28
28
|
log.warn("invalid space", {
|
|
29
|
-
|
|
29
|
+
data
|
|
30
30
|
}, {
|
|
31
31
|
F: __dxlog_file,
|
|
32
|
-
L:
|
|
32
|
+
L: 91,
|
|
33
33
|
S: void 0,
|
|
34
34
|
C: (f, a) => f(...a)
|
|
35
35
|
});
|
|
@@ -39,15 +39,18 @@ var subscriptionHandler = (handler) => {
|
|
|
39
39
|
objects: objects?.length
|
|
40
40
|
}, {
|
|
41
41
|
F: __dxlog_file,
|
|
42
|
-
L:
|
|
42
|
+
L: 93,
|
|
43
43
|
S: void 0,
|
|
44
44
|
C: (f, a) => f(...a)
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
return handler({
|
|
48
48
|
event: {
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
data: {
|
|
50
|
+
...data,
|
|
51
|
+
space,
|
|
52
|
+
objects
|
|
53
|
+
}
|
|
51
54
|
},
|
|
52
55
|
context,
|
|
53
56
|
...rest
|
|
@@ -55,26 +58,180 @@ var subscriptionHandler = (handler) => {
|
|
|
55
58
|
};
|
|
56
59
|
};
|
|
57
60
|
|
|
61
|
+
// packages/core/functions/src/registry/function-registry.ts
|
|
62
|
+
import { Event } from "@dxos/async";
|
|
63
|
+
import { create, Filter } from "@dxos/client/echo";
|
|
64
|
+
import { Resource } from "@dxos/context";
|
|
65
|
+
import { PublicKey as PublicKey2 } from "@dxos/keys";
|
|
66
|
+
import { ComplexMap } from "@dxos/util";
|
|
67
|
+
|
|
68
|
+
// packages/core/functions/src/types.ts
|
|
69
|
+
import { AST, S, TypedObject } from "@dxos/echo-schema";
|
|
70
|
+
var omitEchoId = (schema) => S.make(AST.omit(schema.ast, [
|
|
71
|
+
"id"
|
|
72
|
+
]));
|
|
73
|
+
var SubscriptionTriggerSchema = S.struct({
|
|
74
|
+
type: S.literal("subscription"),
|
|
75
|
+
// TODO(burdon): Define query DSL.
|
|
76
|
+
filter: S.array(S.struct({
|
|
77
|
+
type: S.string,
|
|
78
|
+
props: S.optional(S.record(S.string, S.any))
|
|
79
|
+
})),
|
|
80
|
+
options: S.optional(S.struct({
|
|
81
|
+
// Watch changes to object (not just creation).
|
|
82
|
+
deep: S.optional(S.boolean),
|
|
83
|
+
// Debounce changes (delay in ms).
|
|
84
|
+
delay: S.optional(S.number)
|
|
85
|
+
}))
|
|
86
|
+
});
|
|
87
|
+
var TimerTriggerSchema = S.struct({
|
|
88
|
+
type: S.literal("timer"),
|
|
89
|
+
cron: S.string
|
|
90
|
+
});
|
|
91
|
+
var WebhookTriggerSchema = S.mutable(S.struct({
|
|
92
|
+
type: S.literal("webhook"),
|
|
93
|
+
method: S.string,
|
|
94
|
+
// Assigned port.
|
|
95
|
+
port: S.optional(S.number)
|
|
96
|
+
}));
|
|
97
|
+
var WebsocketTriggerSchema = S.struct({
|
|
98
|
+
type: S.literal("websocket"),
|
|
99
|
+
url: S.string,
|
|
100
|
+
init: S.optional(S.record(S.string, S.any))
|
|
101
|
+
});
|
|
102
|
+
var TriggerSpecSchema = S.union(TimerTriggerSchema, WebhookTriggerSchema, WebsocketTriggerSchema, SubscriptionTriggerSchema);
|
|
103
|
+
var FunctionDef = class extends TypedObject({
|
|
104
|
+
typename: "dxos.org/type/FunctionDef",
|
|
105
|
+
version: "0.1.0"
|
|
106
|
+
})({
|
|
107
|
+
uri: S.string,
|
|
108
|
+
description: S.optional(S.string),
|
|
109
|
+
route: S.string,
|
|
110
|
+
// TODO(burdon): NPM/GitHub/Docker/CF URL?
|
|
111
|
+
handler: S.string
|
|
112
|
+
}) {
|
|
113
|
+
};
|
|
114
|
+
var FunctionTrigger = class extends TypedObject({
|
|
115
|
+
typename: "dxos.org/type/FunctionTrigger",
|
|
116
|
+
version: "0.1.0"
|
|
117
|
+
})({
|
|
118
|
+
function: S.string.pipe(S.description("Function ID/URI.")),
|
|
119
|
+
// Context passed to a function.
|
|
120
|
+
meta: S.optional(S.record(S.string, S.any)),
|
|
121
|
+
spec: TriggerSpecSchema
|
|
122
|
+
}) {
|
|
123
|
+
};
|
|
124
|
+
var FunctionManifestSchema = S.struct({
|
|
125
|
+
functions: S.optional(S.mutable(S.array(omitEchoId(FunctionDef)))),
|
|
126
|
+
triggers: S.optional(S.mutable(S.array(omitEchoId(FunctionTrigger))))
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// packages/core/functions/src/registry/function-registry.ts
|
|
130
|
+
var FunctionRegistry = class extends Resource {
|
|
131
|
+
constructor(_client) {
|
|
132
|
+
super();
|
|
133
|
+
this._client = _client;
|
|
134
|
+
this._functionBySpaceKey = new ComplexMap(PublicKey2.hash);
|
|
135
|
+
this.onFunctionsRegistered = new Event();
|
|
136
|
+
}
|
|
137
|
+
getFunctions(space) {
|
|
138
|
+
return this._functionBySpaceKey.get(space.key) ?? [];
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The method loads function definitions from the manifest into the space.
|
|
142
|
+
* We first load all the definitions from the space to deduplicate by functionId.
|
|
143
|
+
*/
|
|
144
|
+
// TODO(burdon): This should not be space specific (they are static for the agent).
|
|
145
|
+
async register(space, manifest) {
|
|
146
|
+
if (!manifest.functions?.length) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (!space.db.graph.runtimeSchemaRegistry.hasSchema(FunctionDef)) {
|
|
150
|
+
space.db.graph.runtimeSchemaRegistry.registerSchema(FunctionDef);
|
|
151
|
+
}
|
|
152
|
+
const { objects: existingDefinitions } = await space.db.query(Filter.schema(FunctionDef)).run();
|
|
153
|
+
const newDefinitions = getNewDefinitions(manifest.functions, existingDefinitions);
|
|
154
|
+
const reactiveObjects = newDefinitions.map((template) => create(FunctionDef, {
|
|
155
|
+
...template
|
|
156
|
+
}));
|
|
157
|
+
reactiveObjects.forEach((obj) => space.db.add(obj));
|
|
158
|
+
}
|
|
159
|
+
async _open() {
|
|
160
|
+
const spaceListSubscription = this._client.spaces.subscribe(async (spaces) => {
|
|
161
|
+
for (const space of spaces) {
|
|
162
|
+
if (this._functionBySpaceKey.has(space.key)) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const registered = [];
|
|
166
|
+
this._functionBySpaceKey.set(space.key, registered);
|
|
167
|
+
await space.waitUntilReady();
|
|
168
|
+
if (this._ctx.disposed) {
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
const functionsSubscription = space.db.query(Filter.schema(FunctionDef)).subscribe((definitions) => {
|
|
172
|
+
const newFunctions = getNewDefinitions(definitions.objects, registered);
|
|
173
|
+
if (newFunctions.length > 0) {
|
|
174
|
+
registered.push(...newFunctions);
|
|
175
|
+
this.onFunctionsRegistered.emit({
|
|
176
|
+
space,
|
|
177
|
+
newFunctions
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
this._ctx.onDispose(functionsSubscription);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
this._ctx.onDispose(() => spaceListSubscription.unsubscribe());
|
|
185
|
+
}
|
|
186
|
+
async _close(_) {
|
|
187
|
+
this._functionBySpaceKey.clear();
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
var getNewDefinitions = (candidateList, existing) => {
|
|
191
|
+
return candidateList.filter((candidate) => existing.find((def) => def.uri === candidate.uri) == null);
|
|
192
|
+
};
|
|
193
|
+
|
|
58
194
|
// packages/core/functions/src/runtime/dev-server.ts
|
|
59
195
|
import express from "express";
|
|
60
196
|
import { getPort } from "get-port-please";
|
|
61
197
|
import { join } from "@dxos/node-std/path";
|
|
62
|
-
import { Trigger } from "@dxos/async";
|
|
198
|
+
import { Event as Event2, Trigger } from "@dxos/async";
|
|
199
|
+
import { Context } from "@dxos/context";
|
|
63
200
|
import { invariant } from "@dxos/invariant";
|
|
64
201
|
import { log as log2 } from "@dxos/log";
|
|
65
202
|
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/dev-server.ts";
|
|
66
203
|
var DevServer = class {
|
|
67
204
|
// prettier-ignore
|
|
68
|
-
constructor(_client, _options) {
|
|
205
|
+
constructor(_client, _functionsRegistry, _options) {
|
|
69
206
|
this._client = _client;
|
|
207
|
+
this._functionsRegistry = _functionsRegistry;
|
|
70
208
|
this._options = _options;
|
|
209
|
+
this._ctx = createContext();
|
|
71
210
|
this._handlers = {};
|
|
72
211
|
this._seq = 0;
|
|
212
|
+
this.update = new Event2();
|
|
213
|
+
this._functionsRegistry.onFunctionsRegistered.on(async ({ newFunctions }) => {
|
|
214
|
+
newFunctions.forEach((def) => this._load(def));
|
|
215
|
+
await this._safeUpdateRegistration();
|
|
216
|
+
log2("new functions loaded", {
|
|
217
|
+
newFunctions
|
|
218
|
+
}, {
|
|
219
|
+
F: __dxlog_file2,
|
|
220
|
+
L: 53,
|
|
221
|
+
S: this,
|
|
222
|
+
C: (f, a) => f(...a)
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
get stats() {
|
|
227
|
+
return {
|
|
228
|
+
seq: this._seq
|
|
229
|
+
};
|
|
73
230
|
}
|
|
74
231
|
get endpoint() {
|
|
75
232
|
invariant(this._port, void 0, {
|
|
76
233
|
F: __dxlog_file2,
|
|
77
|
-
L:
|
|
234
|
+
L: 64,
|
|
78
235
|
S: this,
|
|
79
236
|
A: [
|
|
80
237
|
"this._port",
|
|
@@ -89,44 +246,46 @@ var DevServer = class {
|
|
|
89
246
|
get functions() {
|
|
90
247
|
return Object.values(this._handlers);
|
|
91
248
|
}
|
|
92
|
-
async initialize() {
|
|
93
|
-
for (const def of this._options.manifest.functions) {
|
|
94
|
-
try {
|
|
95
|
-
await this._load(def);
|
|
96
|
-
} catch (err) {
|
|
97
|
-
log2.error("parsing function (check manifest)", err, {
|
|
98
|
-
F: __dxlog_file2,
|
|
99
|
-
L: 63,
|
|
100
|
-
S: this,
|
|
101
|
-
C: (f, a) => f(...a)
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
249
|
async start() {
|
|
250
|
+
invariant(!this._server, void 0, {
|
|
251
|
+
F: __dxlog_file2,
|
|
252
|
+
L: 77,
|
|
253
|
+
S: this,
|
|
254
|
+
A: [
|
|
255
|
+
"!this._server",
|
|
256
|
+
""
|
|
257
|
+
]
|
|
258
|
+
});
|
|
259
|
+
log2.info("starting...", void 0, {
|
|
260
|
+
F: __dxlog_file2,
|
|
261
|
+
L: 78,
|
|
262
|
+
S: this,
|
|
263
|
+
C: (f, a) => f(...a)
|
|
264
|
+
});
|
|
265
|
+
this._ctx = createContext();
|
|
107
266
|
const app = express();
|
|
108
267
|
app.use(express.json());
|
|
109
|
-
app.post("/:
|
|
110
|
-
const {
|
|
268
|
+
app.post("/:path", async (req, res) => {
|
|
269
|
+
const { path: path2 } = req.params;
|
|
111
270
|
try {
|
|
112
271
|
log2.info("calling", {
|
|
113
|
-
|
|
272
|
+
path: path2
|
|
114
273
|
}, {
|
|
115
274
|
F: __dxlog_file2,
|
|
116
|
-
L:
|
|
275
|
+
L: 88,
|
|
117
276
|
S: this,
|
|
118
277
|
C: (f, a) => f(...a)
|
|
119
278
|
});
|
|
120
279
|
if (this._options.reload) {
|
|
121
|
-
const { def } = this._handlers[
|
|
280
|
+
const { def } = this._handlers["/" + path2];
|
|
122
281
|
await this._load(def, true);
|
|
123
282
|
}
|
|
124
|
-
res.statusCode = await this.
|
|
283
|
+
res.statusCode = await this.invoke("/" + path2, req.body);
|
|
125
284
|
res.end();
|
|
126
285
|
} catch (err) {
|
|
127
286
|
log2.catch(err, void 0, {
|
|
128
287
|
F: __dxlog_file2,
|
|
129
|
-
L:
|
|
288
|
+
L: 98,
|
|
130
289
|
S: this,
|
|
131
290
|
C: (f, a) => f(...a)
|
|
132
291
|
});
|
|
@@ -145,93 +304,195 @@ var DevServer = class {
|
|
|
145
304
|
this._server = app.listen(this._port);
|
|
146
305
|
try {
|
|
147
306
|
const { registrationId, endpoint } = await this._client.services.services.FunctionRegistryService.register({
|
|
148
|
-
endpoint: this.endpoint
|
|
149
|
-
functions: this.functions.map(({ def: { name } }) => ({
|
|
150
|
-
name
|
|
151
|
-
}))
|
|
307
|
+
endpoint: this.endpoint
|
|
152
308
|
});
|
|
153
309
|
log2.info("registered", {
|
|
154
|
-
registrationId,
|
|
155
310
|
endpoint
|
|
156
311
|
}, {
|
|
157
312
|
F: __dxlog_file2,
|
|
158
|
-
L:
|
|
313
|
+
L: 113,
|
|
159
314
|
S: this,
|
|
160
315
|
C: (f, a) => f(...a)
|
|
161
316
|
});
|
|
162
|
-
this._registrationId = registrationId;
|
|
163
317
|
this._proxy = endpoint;
|
|
318
|
+
this._functionServiceRegistration = registrationId;
|
|
319
|
+
await this._functionsRegistry.open(this._ctx);
|
|
164
320
|
} catch (err) {
|
|
165
321
|
await this.stop();
|
|
166
322
|
throw new Error("FunctionRegistryService not available (check plugin is configured).");
|
|
167
323
|
}
|
|
324
|
+
log2.info("started", {
|
|
325
|
+
port: this._port
|
|
326
|
+
}, {
|
|
327
|
+
F: __dxlog_file2,
|
|
328
|
+
L: 124,
|
|
329
|
+
S: this,
|
|
330
|
+
C: (f, a) => f(...a)
|
|
331
|
+
});
|
|
168
332
|
}
|
|
169
333
|
async stop() {
|
|
334
|
+
invariant(this._server, void 0, {
|
|
335
|
+
F: __dxlog_file2,
|
|
336
|
+
L: 128,
|
|
337
|
+
S: this,
|
|
338
|
+
A: [
|
|
339
|
+
"this._server",
|
|
340
|
+
""
|
|
341
|
+
]
|
|
342
|
+
});
|
|
343
|
+
log2.info("stopping...", void 0, {
|
|
344
|
+
F: __dxlog_file2,
|
|
345
|
+
L: 129,
|
|
346
|
+
S: this,
|
|
347
|
+
C: (f, a) => f(...a)
|
|
348
|
+
});
|
|
170
349
|
const trigger = new Trigger();
|
|
171
|
-
this._server
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
350
|
+
this._server.close(async () => {
|
|
351
|
+
log2.info("server stopped", void 0, {
|
|
352
|
+
F: __dxlog_file2,
|
|
353
|
+
L: 133,
|
|
354
|
+
S: this,
|
|
355
|
+
C: (f, a) => f(...a)
|
|
356
|
+
});
|
|
357
|
+
try {
|
|
358
|
+
if (this._functionServiceRegistration) {
|
|
359
|
+
invariant(this._client.services.services.FunctionRegistryService, void 0, {
|
|
360
|
+
F: __dxlog_file2,
|
|
361
|
+
L: 136,
|
|
362
|
+
S: this,
|
|
363
|
+
A: [
|
|
364
|
+
"this._client.services.services.FunctionRegistryService",
|
|
365
|
+
""
|
|
366
|
+
]
|
|
367
|
+
});
|
|
368
|
+
await this._client.services.services.FunctionRegistryService.unregister({
|
|
369
|
+
registrationId: this._functionServiceRegistration
|
|
370
|
+
});
|
|
371
|
+
log2.info("unregistered", {
|
|
372
|
+
registrationId: this._functionServiceRegistration
|
|
373
|
+
}, {
|
|
374
|
+
F: __dxlog_file2,
|
|
375
|
+
L: 141,
|
|
376
|
+
S: this,
|
|
377
|
+
C: (f, a) => f(...a)
|
|
378
|
+
});
|
|
379
|
+
this._functionServiceRegistration = void 0;
|
|
380
|
+
this._proxy = void 0;
|
|
381
|
+
}
|
|
382
|
+
trigger.wake();
|
|
383
|
+
} catch (err) {
|
|
384
|
+
trigger.throw(err);
|
|
186
385
|
}
|
|
187
|
-
trigger.wake();
|
|
188
386
|
});
|
|
189
387
|
await trigger.wait();
|
|
190
388
|
this._port = void 0;
|
|
191
389
|
this._server = void 0;
|
|
390
|
+
log2.info("stopped", void 0, {
|
|
391
|
+
F: __dxlog_file2,
|
|
392
|
+
L: 155,
|
|
393
|
+
S: this,
|
|
394
|
+
C: (f, a) => f(...a)
|
|
395
|
+
});
|
|
192
396
|
}
|
|
193
397
|
/**
|
|
194
398
|
* Load function.
|
|
195
399
|
*/
|
|
196
|
-
async _load(def,
|
|
197
|
-
const {
|
|
198
|
-
const
|
|
400
|
+
async _load(def, force = false) {
|
|
401
|
+
const { uri, route, handler } = def;
|
|
402
|
+
const filePath = join(this._options.baseDir, handler);
|
|
199
403
|
log2.info("loading", {
|
|
200
|
-
|
|
404
|
+
uri,
|
|
405
|
+
force
|
|
201
406
|
}, {
|
|
202
407
|
F: __dxlog_file2,
|
|
203
|
-
L:
|
|
408
|
+
L: 164,
|
|
204
409
|
S: this,
|
|
205
410
|
C: (f, a) => f(...a)
|
|
206
411
|
});
|
|
207
|
-
if (
|
|
208
|
-
Object.keys(__require.cache).filter((key) => key.startsWith(
|
|
412
|
+
if (force) {
|
|
413
|
+
Object.keys(__require.cache).filter((key) => key.startsWith(filePath)).forEach((key) => {
|
|
414
|
+
delete __require.cache[key];
|
|
415
|
+
});
|
|
209
416
|
}
|
|
210
|
-
const module = __require(
|
|
417
|
+
const module = __require(filePath);
|
|
211
418
|
if (typeof module.default !== "function") {
|
|
212
|
-
throw new Error(`Handler must export default function: ${
|
|
419
|
+
throw new Error(`Handler must export default function: ${uri}`);
|
|
213
420
|
}
|
|
214
|
-
this._handlers[
|
|
421
|
+
this._handlers[route] = {
|
|
215
422
|
def,
|
|
216
423
|
handler: module.default
|
|
217
424
|
};
|
|
218
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
|
+
}
|
|
219
453
|
/**
|
|
220
|
-
* Invoke function
|
|
454
|
+
* Invoke function.
|
|
221
455
|
*/
|
|
222
|
-
async
|
|
456
|
+
async invoke(path2, data) {
|
|
223
457
|
const seq = ++this._seq;
|
|
224
458
|
const now = Date.now();
|
|
225
459
|
log2.info("req", {
|
|
226
460
|
seq,
|
|
227
|
-
|
|
461
|
+
path: path2
|
|
228
462
|
}, {
|
|
229
463
|
F: __dxlog_file2,
|
|
230
|
-
L:
|
|
464
|
+
L: 204,
|
|
231
465
|
S: this,
|
|
232
466
|
C: (f, a) => f(...a)
|
|
233
467
|
});
|
|
234
|
-
const
|
|
468
|
+
const statusCode = await this._invoke(path2, {
|
|
469
|
+
data
|
|
470
|
+
});
|
|
471
|
+
log2.info("res", {
|
|
472
|
+
seq,
|
|
473
|
+
path: path2,
|
|
474
|
+
statusCode,
|
|
475
|
+
duration: Date.now() - now
|
|
476
|
+
}, {
|
|
477
|
+
F: __dxlog_file2,
|
|
478
|
+
L: 207,
|
|
479
|
+
S: this,
|
|
480
|
+
C: (f, a) => f(...a)
|
|
481
|
+
});
|
|
482
|
+
this.update.emit(statusCode);
|
|
483
|
+
return statusCode;
|
|
484
|
+
}
|
|
485
|
+
async _invoke(path2, event) {
|
|
486
|
+
const { handler } = this._handlers[path2] ?? {};
|
|
487
|
+
invariant(handler, `invalid path: ${path2}`, {
|
|
488
|
+
F: __dxlog_file2,
|
|
489
|
+
L: 214,
|
|
490
|
+
S: this,
|
|
491
|
+
A: [
|
|
492
|
+
"handler",
|
|
493
|
+
"`invalid path: ${path}`"
|
|
494
|
+
]
|
|
495
|
+
});
|
|
235
496
|
const context = {
|
|
236
497
|
client: this._client,
|
|
237
498
|
dataDir: this._options.dataDir
|
|
@@ -248,262 +509,565 @@ var DevServer = class {
|
|
|
248
509
|
event,
|
|
249
510
|
response
|
|
250
511
|
});
|
|
251
|
-
log2.info("res", {
|
|
252
|
-
seq,
|
|
253
|
-
name,
|
|
254
|
-
statusCode,
|
|
255
|
-
duration: Date.now() - now
|
|
256
|
-
}, {
|
|
257
|
-
F: __dxlog_file2,
|
|
258
|
-
L: 178,
|
|
259
|
-
S: this,
|
|
260
|
-
C: (f, a) => f(...a)
|
|
261
|
-
});
|
|
262
512
|
return statusCode;
|
|
263
513
|
}
|
|
264
514
|
};
|
|
515
|
+
var createContext = () => new Context({
|
|
516
|
+
name: "DevServer"
|
|
517
|
+
});
|
|
265
518
|
|
|
266
519
|
// packages/core/functions/src/runtime/scheduler.ts
|
|
267
|
-
import
|
|
268
|
-
import {
|
|
269
|
-
import { debounce, DeferredTask } from "@dxos/async";
|
|
270
|
-
import { Filter, createSubscription, getAutomergeObjectCore } from "@dxos/client/echo";
|
|
271
|
-
import { Context } from "@dxos/context";
|
|
272
|
-
import { invariant as invariant2 } from "@dxos/invariant";
|
|
520
|
+
import path from "@dxos/node-std/path";
|
|
521
|
+
import { Context as Context2 } from "@dxos/context";
|
|
273
522
|
import { log as log3 } from "@dxos/log";
|
|
274
|
-
import { ComplexMap } from "@dxos/util";
|
|
275
523
|
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/scheduler.ts";
|
|
276
524
|
var Scheduler = class {
|
|
277
|
-
constructor(
|
|
278
|
-
this.
|
|
279
|
-
this.
|
|
525
|
+
constructor(functions, triggers, _options = {}) {
|
|
526
|
+
this.functions = functions;
|
|
527
|
+
this.triggers = triggers;
|
|
280
528
|
this._options = _options;
|
|
281
|
-
this.
|
|
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
|
+
});
|
|
282
536
|
}
|
|
283
537
|
async start() {
|
|
284
|
-
this.
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
await this.mount(new Context(), space, trigger);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
});
|
|
538
|
+
await this._ctx.dispose();
|
|
539
|
+
this._ctx = createContext2();
|
|
540
|
+
await this.functions.open(this._ctx);
|
|
541
|
+
await this.triggers.open(this._ctx);
|
|
292
542
|
}
|
|
293
543
|
async stop() {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
544
|
+
await this._ctx.dispose();
|
|
545
|
+
await this.functions.close();
|
|
546
|
+
await this.triggers.close();
|
|
297
547
|
}
|
|
298
|
-
async
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
F: __dxlog_file3,
|
|
306
|
-
L: 63,
|
|
307
|
-
S: this,
|
|
308
|
-
A: [
|
|
309
|
-
"def",
|
|
310
|
-
"`Function not found: ${trigger.function}`"
|
|
311
|
-
]
|
|
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);
|
|
312
555
|
});
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
space: space.key,
|
|
321
|
-
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
|
|
322
563
|
}, {
|
|
323
564
|
F: __dxlog_file3,
|
|
324
|
-
L:
|
|
565
|
+
L: 74,
|
|
325
566
|
S: this,
|
|
326
567
|
C: (f, a) => f(...a)
|
|
327
568
|
});
|
|
328
|
-
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
if (trigger.schedule) {
|
|
332
|
-
this._createTimer(ctx, space, def, trigger);
|
|
333
|
-
}
|
|
334
|
-
for (const triggerSubscription of trigger.subscriptions ?? []) {
|
|
335
|
-
this._createSubscription(ctx, space, def, triggerSubscription);
|
|
336
|
-
}
|
|
569
|
+
return;
|
|
337
570
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
await ctx.dispose();
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
_createTimer(ctx, space, def, trigger) {
|
|
351
|
-
const task = new DeferredTask(ctx, async () => {
|
|
352
|
-
await this._execFunction(def, {
|
|
353
|
-
space: space.key
|
|
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
|
+
}
|
|
354
580
|
});
|
|
355
581
|
});
|
|
356
|
-
|
|
582
|
+
log3("activated trigger", {
|
|
583
|
+
space: space.key,
|
|
584
|
+
trigger: fnTrigger
|
|
585
|
+
}, {
|
|
357
586
|
F: __dxlog_file3,
|
|
358
|
-
L:
|
|
587
|
+
L: 84,
|
|
359
588
|
S: this,
|
|
360
|
-
|
|
361
|
-
"trigger.schedule",
|
|
362
|
-
""
|
|
363
|
-
]
|
|
589
|
+
C: (f, a) => f(...a)
|
|
364
590
|
});
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
log3.info("
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
delta
|
|
591
|
+
}
|
|
592
|
+
async _execFunction(def, { data, meta }) {
|
|
593
|
+
let status = 0;
|
|
594
|
+
try {
|
|
595
|
+
const payload = Object.assign({}, meta && {
|
|
596
|
+
meta
|
|
597
|
+
}, data);
|
|
598
|
+
const { endpoint, callback } = this._options;
|
|
599
|
+
if (endpoint) {
|
|
600
|
+
const url = path.join(endpoint, def.route);
|
|
601
|
+
log3.info("exec", {
|
|
602
|
+
function: def.uri,
|
|
603
|
+
url
|
|
379
604
|
}, {
|
|
380
605
|
F: __dxlog_file3,
|
|
381
|
-
L:
|
|
606
|
+
L: 100,
|
|
382
607
|
S: this,
|
|
383
608
|
C: (f, a) => f(...a)
|
|
384
609
|
});
|
|
385
|
-
|
|
610
|
+
const response = await fetch(url, {
|
|
611
|
+
method: "POST",
|
|
612
|
+
headers: {
|
|
613
|
+
"Content-Type": "application/json"
|
|
614
|
+
},
|
|
615
|
+
body: JSON.stringify(payload)
|
|
616
|
+
});
|
|
617
|
+
status = response.status;
|
|
618
|
+
} else if (callback) {
|
|
619
|
+
log3.info("exec", {
|
|
620
|
+
function: def.uri
|
|
621
|
+
}, {
|
|
622
|
+
F: __dxlog_file3,
|
|
623
|
+
L: 111,
|
|
624
|
+
S: this,
|
|
625
|
+
C: (f, a) => f(...a)
|
|
626
|
+
});
|
|
627
|
+
status = await callback(payload) ?? 200;
|
|
386
628
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
629
|
+
if (status && status >= 400) {
|
|
630
|
+
throw new Error(`Response: ${status}`);
|
|
631
|
+
}
|
|
632
|
+
log3.info("done", {
|
|
633
|
+
function: def.uri,
|
|
634
|
+
status
|
|
635
|
+
}, {
|
|
636
|
+
F: __dxlog_file3,
|
|
637
|
+
L: 121,
|
|
638
|
+
S: this,
|
|
639
|
+
C: (f, a) => f(...a)
|
|
640
|
+
});
|
|
641
|
+
} catch (err) {
|
|
642
|
+
log3.error("error", {
|
|
643
|
+
function: def.uri,
|
|
644
|
+
error: err.message
|
|
645
|
+
}, {
|
|
646
|
+
F: __dxlog_file3,
|
|
647
|
+
L: 123,
|
|
648
|
+
S: this,
|
|
649
|
+
C: (f, a) => f(...a)
|
|
650
|
+
});
|
|
651
|
+
status = 500;
|
|
652
|
+
}
|
|
653
|
+
return status;
|
|
390
654
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
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({
|
|
405
680
|
objects: Array.from(objectIds)
|
|
406
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
|
|
690
|
+
}, {
|
|
691
|
+
F: __dxlog_file4,
|
|
692
|
+
L: 32,
|
|
693
|
+
S: void 0,
|
|
694
|
+
C: (f, a) => f(...a)
|
|
407
695
|
});
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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
|
|
413
711
|
}, {
|
|
414
|
-
F:
|
|
415
|
-
L:
|
|
416
|
-
S:
|
|
712
|
+
F: __dxlog_file4,
|
|
713
|
+
L: 52,
|
|
714
|
+
S: void 0,
|
|
417
715
|
C: (f, a) => f(...a)
|
|
418
716
|
});
|
|
419
|
-
for (const object of
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
+
}
|
|
424
724
|
}
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
const query = triggerCtx.space.db.query(Filter2.or(filter.map(({ type, props }) => Filter2.typename(type, props))));
|
|
728
|
+
subscriptions.push(query.subscribe(delay ? debounce(update, delay) : update));
|
|
729
|
+
ctx.onDispose(() => {
|
|
730
|
+
subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
731
|
+
});
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
// packages/core/functions/src/trigger/type/timer-trigger.ts
|
|
735
|
+
import { CronJob } from "cron";
|
|
736
|
+
import { DeferredTask as DeferredTask2 } from "@dxos/async";
|
|
737
|
+
import { log as log5 } from "@dxos/log";
|
|
738
|
+
var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/functions/src/trigger/type/timer-trigger.ts";
|
|
739
|
+
var createTimerTrigger = async (ctx, triggerContext, spec, callback) => {
|
|
740
|
+
const task = new DeferredTask2(ctx, async () => {
|
|
741
|
+
await callback({});
|
|
742
|
+
});
|
|
743
|
+
let last = 0;
|
|
744
|
+
let run = 0;
|
|
745
|
+
const job = CronJob.from({
|
|
746
|
+
cronTime: spec.cron,
|
|
747
|
+
runOnInit: false,
|
|
748
|
+
onTick: () => {
|
|
749
|
+
const now = Date.now();
|
|
750
|
+
const delta = last ? now - last : 0;
|
|
751
|
+
last = now;
|
|
752
|
+
run++;
|
|
753
|
+
log5.info("tick", {
|
|
754
|
+
space: triggerContext.space.key.truncate(),
|
|
755
|
+
count: run,
|
|
756
|
+
delta
|
|
757
|
+
}, {
|
|
758
|
+
F: __dxlog_file5,
|
|
759
|
+
L: 37,
|
|
760
|
+
S: void 0,
|
|
761
|
+
C: (f, a) => f(...a)
|
|
762
|
+
});
|
|
425
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)
|
|
426
795
|
});
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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
|
|
436
821
|
}, {
|
|
437
|
-
F:
|
|
438
|
-
L:
|
|
439
|
-
S:
|
|
822
|
+
F: __dxlog_file7,
|
|
823
|
+
L: 39,
|
|
824
|
+
S: void 0,
|
|
440
825
|
C: (f, a) => f(...a)
|
|
441
826
|
});
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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
|
+
});
|
|
449
888
|
}
|
|
450
889
|
}
|
|
451
|
-
};
|
|
452
|
-
const query = space.db.query(Filter.typename(type, props));
|
|
453
|
-
subscriptions.push(query.subscribe(delay ? debounce(update, delay * 1e3) : update));
|
|
454
|
-
ctx.onDispose(() => {
|
|
455
|
-
subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
456
890
|
});
|
|
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
|
+
}
|
|
908
|
+
}
|
|
909
|
+
ctx.onDispose(() => {
|
|
910
|
+
ws?.close();
|
|
911
|
+
});
|
|
912
|
+
};
|
|
913
|
+
|
|
914
|
+
// packages/core/functions/src/trigger/trigger-registry.ts
|
|
915
|
+
var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/core/functions/src/trigger/trigger-registry.ts";
|
|
916
|
+
var triggerHandlers = {
|
|
917
|
+
subscription: createSubscriptionTrigger,
|
|
918
|
+
timer: createTimerTrigger,
|
|
919
|
+
webhook: createWebhookTrigger,
|
|
920
|
+
websocket: createWebsocketTrigger
|
|
921
|
+
};
|
|
922
|
+
var TriggerRegistry = class extends Resource2 {
|
|
923
|
+
constructor(_client, _options) {
|
|
924
|
+
super();
|
|
925
|
+
this._client = _client;
|
|
926
|
+
this._options = _options;
|
|
927
|
+
this._triggersBySpaceKey = new ComplexMap2(PublicKey3.hash);
|
|
928
|
+
this.registered = new Event3();
|
|
929
|
+
this.removed = new Event3();
|
|
930
|
+
}
|
|
931
|
+
getActiveTriggers(space) {
|
|
932
|
+
return this._getTriggers(space, (t) => t.activationCtx != null);
|
|
933
|
+
}
|
|
934
|
+
getInactiveTriggers(space) {
|
|
935
|
+
return this._getTriggers(space, (t) => t.activationCtx == null);
|
|
457
936
|
}
|
|
458
|
-
async
|
|
937
|
+
async activate(triggerCtx, trigger, callback) {
|
|
938
|
+
log8("activate", {
|
|
939
|
+
space: triggerCtx.space.key,
|
|
940
|
+
trigger
|
|
941
|
+
}, {
|
|
942
|
+
F: __dxlog_file8,
|
|
943
|
+
L: 73,
|
|
944
|
+
S: this,
|
|
945
|
+
C: (f, a) => f(...a)
|
|
946
|
+
});
|
|
947
|
+
const activationCtx = new Context3({
|
|
948
|
+
name: `trigger_${trigger.function}`
|
|
949
|
+
});
|
|
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
|
+
]
|
|
960
|
+
});
|
|
961
|
+
registeredTrigger.activationCtx = activationCtx;
|
|
459
962
|
try {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
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
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* Loads triggers from the manifest into the space.
|
|
972
|
+
*/
|
|
973
|
+
async register(space, manifest) {
|
|
974
|
+
log8("register", {
|
|
975
|
+
space: space.key
|
|
976
|
+
}, {
|
|
977
|
+
F: __dxlog_file8,
|
|
978
|
+
L: 95,
|
|
979
|
+
S: this,
|
|
980
|
+
C: (f, a) => f(...a)
|
|
981
|
+
});
|
|
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;
|
|
998
|
+
}
|
|
999
|
+
const registered = [];
|
|
1000
|
+
this._triggersBySpaceKey.set(space.key, registered);
|
|
1001
|
+
await space.waitUntilReady();
|
|
1002
|
+
if (this._ctx.disposed) {
|
|
1003
|
+
break;
|
|
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);
|
|
477
1008
|
});
|
|
478
|
-
|
|
479
|
-
} else if (callback) {
|
|
480
|
-
status = await callback(data);
|
|
1009
|
+
this._ctx.onDispose(functionsSubscription);
|
|
481
1010
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
1011
|
+
});
|
|
1012
|
+
this._ctx.onDispose(() => spaceListSubscription.unsubscribe());
|
|
1013
|
+
}
|
|
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;
|
|
1020
|
+
});
|
|
1021
|
+
if (newTriggers.length > 0) {
|
|
1022
|
+
const newRegisteredTriggers = newTriggers.map((trigger) => ({
|
|
1023
|
+
trigger
|
|
1024
|
+
}));
|
|
1025
|
+
registered.push(...newRegisteredTriggers);
|
|
1026
|
+
log8("registered new triggers", () => ({
|
|
1027
|
+
spaceKey: space.key,
|
|
1028
|
+
functions: newTriggers.map((t) => t.function)
|
|
1029
|
+
}), {
|
|
1030
|
+
F: __dxlog_file8,
|
|
1031
|
+
L: 146,
|
|
488
1032
|
S: this,
|
|
489
1033
|
C: (f, a) => f(...a)
|
|
490
1034
|
});
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
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);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
if (removed.length > 0) {
|
|
1052
|
+
this.removed.emit({
|
|
1053
|
+
space,
|
|
1054
|
+
triggers: removed
|
|
500
1055
|
});
|
|
501
1056
|
}
|
|
502
1057
|
}
|
|
1058
|
+
_getTriggers(space, predicate) {
|
|
1059
|
+
const allSpaceTriggers = this._triggersBySpaceKey.get(space.key) ?? [];
|
|
1060
|
+
return allSpaceTriggers.filter(predicate).map((trigger) => trigger.trigger);
|
|
1061
|
+
}
|
|
503
1062
|
};
|
|
504
1063
|
export {
|
|
505
1064
|
DevServer,
|
|
1065
|
+
FunctionDef,
|
|
1066
|
+
FunctionManifestSchema,
|
|
1067
|
+
FunctionRegistry,
|
|
1068
|
+
FunctionTrigger,
|
|
506
1069
|
Scheduler,
|
|
1070
|
+
TriggerRegistry,
|
|
507
1071
|
subscriptionHandler
|
|
508
1072
|
};
|
|
509
1073
|
//# sourceMappingURL=index.mjs.map
|