@dxos/functions 0.6.13 → 0.6.14-main.1366248
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/{chunk-CRAAIWU6.mjs → chunk-2O2MAZE4.mjs} +28 -14
- package/dist/lib/browser/chunk-2O2MAZE4.mjs.map +7 -0
- package/dist/lib/browser/{chunk-2I75VGHZ.mjs → chunk-TNNXVSUN.mjs} +16 -12
- package/dist/lib/browser/chunk-TNNXVSUN.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +7 -5
- package/dist/lib/browser/index.mjs.map +2 -2
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +9 -9
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/types.mjs +1 -1
- package/dist/lib/node/{chunk-JV3VNH5X.cjs → chunk-O44VB3FE.cjs} +19 -8
- package/dist/lib/node/chunk-O44VB3FE.cjs.map +7 -0
- package/dist/lib/node/{chunk-OGLDVNFE.cjs → chunk-SCSHB4MF.cjs} +42 -37
- package/dist/lib/node/chunk-SCSHB4MF.cjs.map +7 -0
- package/dist/lib/node/index.cjs +16 -14
- package/dist/lib/node/index.cjs.map +2 -2
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +15 -15
- package/dist/lib/node/testing/index.cjs.map +3 -3
- package/dist/lib/node/types.cjs +5 -5
- package/dist/lib/node/types.cjs.map +1 -1
- package/dist/lib/node-esm/chunk-AM7SW4YW.mjs +604 -0
- package/dist/lib/node-esm/chunk-AM7SW4YW.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-TJ4S5QZ3.mjs +90 -0
- package/dist/lib/node-esm/chunk-TJ4S5QZ3.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +94 -0
- package/dist/lib/node-esm/index.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -0
- package/dist/lib/node-esm/testing/index.mjs +666 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/lib/node-esm/types.mjs +14 -0
- package/dist/lib/node-esm/types.mjs.map +7 -0
- package/dist/types/src/handler.d.ts +2 -2
- package/dist/types/src/handler.d.ts.map +1 -1
- package/dist/types/src/testing/setup.d.ts.map +1 -1
- package/dist/types/src/trigger/type/websocket-trigger.d.ts +1 -0
- package/dist/types/src/trigger/type/websocket-trigger.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +2 -2
- package/package.json +23 -27
- package/src/function/function-registry.test.ts +3 -2
- package/src/handler.ts +3 -2
- package/src/runtime/dev-server.test.ts +7 -6
- package/src/runtime/scheduler.test.ts +9 -6
- package/src/testing/functions-integration.test.ts +7 -5
- package/src/testing/setup.ts +10 -3
- package/src/trigger/trigger-registry.test.ts +3 -5
- package/src/trigger/type/websocket-trigger.ts +22 -6
- package/src/types.ts +4 -4
- package/dist/lib/browser/chunk-2I75VGHZ.mjs.map +0 -7
- package/dist/lib/browser/chunk-CRAAIWU6.mjs.map +0 -7
- package/dist/lib/node/chunk-JV3VNH5X.cjs.map +0 -7
- package/dist/lib/node/chunk-OGLDVNFE.cjs.map +0 -7
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
FunctionRegistry,
|
|
4
|
+
TriggerRegistry
|
|
5
|
+
} from "../chunk-AM7SW4YW.mjs";
|
|
6
|
+
import {
|
|
7
|
+
FunctionDef,
|
|
8
|
+
FunctionTrigger,
|
|
9
|
+
__require
|
|
10
|
+
} from "../chunk-TJ4S5QZ3.mjs";
|
|
11
|
+
|
|
12
|
+
// packages/core/functions/src/testing/setup.ts
|
|
13
|
+
import { getRandomPort } from "get-port-please";
|
|
14
|
+
import path2 from "node:path";
|
|
15
|
+
import { waitForCondition } from "@dxos/async";
|
|
16
|
+
import { Client, Config } from "@dxos/client";
|
|
17
|
+
import { range } from "@dxos/util";
|
|
18
|
+
|
|
19
|
+
// packages/core/functions/src/testing/types.ts
|
|
20
|
+
import { S, TypedObject } from "@dxos/echo-schema";
|
|
21
|
+
var TestType = class extends TypedObject({
|
|
22
|
+
typename: "example.com/type/Test",
|
|
23
|
+
version: "0.1.0"
|
|
24
|
+
})({
|
|
25
|
+
title: S.String
|
|
26
|
+
}) {
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// packages/core/functions/src/runtime/dev-server.ts
|
|
30
|
+
import express from "express";
|
|
31
|
+
import { getPort } from "get-port-please";
|
|
32
|
+
import { join } from "node:path";
|
|
33
|
+
import { asyncTimeout, Event, Trigger } from "@dxos/async";
|
|
34
|
+
import { Context } from "@dxos/context";
|
|
35
|
+
import { invariant } from "@dxos/invariant";
|
|
36
|
+
import { log } from "@dxos/log";
|
|
37
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/dev-server.ts";
|
|
38
|
+
var FN_TIMEOUT = 2e4;
|
|
39
|
+
var DevServer = class {
|
|
40
|
+
constructor(_client, _functionsRegistry, _options) {
|
|
41
|
+
this._client = _client;
|
|
42
|
+
this._functionsRegistry = _functionsRegistry;
|
|
43
|
+
this._options = _options;
|
|
44
|
+
this._ctx = createContext();
|
|
45
|
+
this._handlers = {};
|
|
46
|
+
this._seq = 0;
|
|
47
|
+
this.update = new Event();
|
|
48
|
+
}
|
|
49
|
+
get stats() {
|
|
50
|
+
return {
|
|
51
|
+
seq: this._seq
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
get endpoint() {
|
|
55
|
+
invariant(this._port, void 0, {
|
|
56
|
+
F: __dxlog_file,
|
|
57
|
+
L: 60,
|
|
58
|
+
S: this,
|
|
59
|
+
A: [
|
|
60
|
+
"this._port",
|
|
61
|
+
""
|
|
62
|
+
]
|
|
63
|
+
});
|
|
64
|
+
return `http://localhost:${this._port}`;
|
|
65
|
+
}
|
|
66
|
+
get proxy() {
|
|
67
|
+
return this._proxy;
|
|
68
|
+
}
|
|
69
|
+
get functions() {
|
|
70
|
+
return Object.values(this._handlers);
|
|
71
|
+
}
|
|
72
|
+
async start() {
|
|
73
|
+
invariant(!this._server, void 0, {
|
|
74
|
+
F: __dxlog_file,
|
|
75
|
+
L: 73,
|
|
76
|
+
S: this,
|
|
77
|
+
A: [
|
|
78
|
+
"!this._server",
|
|
79
|
+
""
|
|
80
|
+
]
|
|
81
|
+
});
|
|
82
|
+
log.info("starting...", void 0, {
|
|
83
|
+
F: __dxlog_file,
|
|
84
|
+
L: 74,
|
|
85
|
+
S: this,
|
|
86
|
+
C: (f, a) => f(...a)
|
|
87
|
+
});
|
|
88
|
+
this._ctx = createContext();
|
|
89
|
+
const app = express();
|
|
90
|
+
app.use(express.json());
|
|
91
|
+
app.post("/:path", async (req, res) => {
|
|
92
|
+
const { path: path3 } = req.params;
|
|
93
|
+
try {
|
|
94
|
+
log.info("calling", {
|
|
95
|
+
path: path3
|
|
96
|
+
}, {
|
|
97
|
+
F: __dxlog_file,
|
|
98
|
+
L: 84,
|
|
99
|
+
S: this,
|
|
100
|
+
C: (f, a) => f(...a)
|
|
101
|
+
});
|
|
102
|
+
if (this._options.reload) {
|
|
103
|
+
const { def } = this._handlers["/" + path3];
|
|
104
|
+
await this._load(def, true);
|
|
105
|
+
}
|
|
106
|
+
res.statusCode = await asyncTimeout(this.invoke("/" + path3, req.body), FN_TIMEOUT);
|
|
107
|
+
res.end();
|
|
108
|
+
} catch (err) {
|
|
109
|
+
log.catch(err, void 0, {
|
|
110
|
+
F: __dxlog_file,
|
|
111
|
+
L: 94,
|
|
112
|
+
S: this,
|
|
113
|
+
C: (f, a) => f(...a)
|
|
114
|
+
});
|
|
115
|
+
res.statusCode = 500;
|
|
116
|
+
res.end();
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
this._port = this._options.port ?? await getPort({
|
|
120
|
+
host: "localhost",
|
|
121
|
+
port: 7200,
|
|
122
|
+
portRange: [
|
|
123
|
+
7200,
|
|
124
|
+
7299
|
|
125
|
+
]
|
|
126
|
+
});
|
|
127
|
+
this._server = app.listen(this._port);
|
|
128
|
+
try {
|
|
129
|
+
const { registrationId, endpoint } = await this._client.services.services.FunctionRegistryService.register({
|
|
130
|
+
endpoint: this.endpoint
|
|
131
|
+
});
|
|
132
|
+
log.info("registered", {
|
|
133
|
+
endpoint
|
|
134
|
+
}, {
|
|
135
|
+
F: __dxlog_file,
|
|
136
|
+
L: 109,
|
|
137
|
+
S: this,
|
|
138
|
+
C: (f, a) => f(...a)
|
|
139
|
+
});
|
|
140
|
+
this._proxy = endpoint;
|
|
141
|
+
this._functionServiceRegistration = registrationId;
|
|
142
|
+
await this._handleNewFunctions(this._functionsRegistry.getUniqueByUri());
|
|
143
|
+
this._ctx.onDispose(this._functionsRegistry.registered.on(({ added }) => this._handleNewFunctions(added)));
|
|
144
|
+
} catch (err) {
|
|
145
|
+
await this.stop();
|
|
146
|
+
throw new Error("FunctionRegistryService not available (check plugin is configured).");
|
|
147
|
+
}
|
|
148
|
+
log.info("started", {
|
|
149
|
+
port: this._port
|
|
150
|
+
}, {
|
|
151
|
+
F: __dxlog_file,
|
|
152
|
+
L: 121,
|
|
153
|
+
S: this,
|
|
154
|
+
C: (f, a) => f(...a)
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
async stop() {
|
|
158
|
+
if (!this._server) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
log.info("stopping...", void 0, {
|
|
162
|
+
F: __dxlog_file,
|
|
163
|
+
L: 129,
|
|
164
|
+
S: this,
|
|
165
|
+
C: (f, a) => f(...a)
|
|
166
|
+
});
|
|
167
|
+
await this._ctx.dispose();
|
|
168
|
+
const trigger = new Trigger();
|
|
169
|
+
this._server.close(async () => {
|
|
170
|
+
log.info("server stopped", void 0, {
|
|
171
|
+
F: __dxlog_file,
|
|
172
|
+
L: 134,
|
|
173
|
+
S: this,
|
|
174
|
+
C: (f, a) => f(...a)
|
|
175
|
+
});
|
|
176
|
+
try {
|
|
177
|
+
if (this._functionServiceRegistration) {
|
|
178
|
+
invariant(this._client.services.services.FunctionRegistryService, void 0, {
|
|
179
|
+
F: __dxlog_file,
|
|
180
|
+
L: 137,
|
|
181
|
+
S: this,
|
|
182
|
+
A: [
|
|
183
|
+
"this._client.services.services.FunctionRegistryService",
|
|
184
|
+
""
|
|
185
|
+
]
|
|
186
|
+
});
|
|
187
|
+
await this._client.services.services.FunctionRegistryService.unregister({
|
|
188
|
+
registrationId: this._functionServiceRegistration
|
|
189
|
+
});
|
|
190
|
+
log.info("unregistered", {
|
|
191
|
+
registrationId: this._functionServiceRegistration
|
|
192
|
+
}, {
|
|
193
|
+
F: __dxlog_file,
|
|
194
|
+
L: 142,
|
|
195
|
+
S: this,
|
|
196
|
+
C: (f, a) => f(...a)
|
|
197
|
+
});
|
|
198
|
+
this._functionServiceRegistration = void 0;
|
|
199
|
+
this._proxy = void 0;
|
|
200
|
+
}
|
|
201
|
+
trigger.wake();
|
|
202
|
+
} catch (err) {
|
|
203
|
+
trigger.throw(err);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
await trigger.wait();
|
|
207
|
+
this._port = void 0;
|
|
208
|
+
this._server = void 0;
|
|
209
|
+
log.info("stopped", void 0, {
|
|
210
|
+
F: __dxlog_file,
|
|
211
|
+
L: 156,
|
|
212
|
+
S: this,
|
|
213
|
+
C: (f, a) => f(...a)
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
async _handleNewFunctions(newFunctions) {
|
|
217
|
+
newFunctions.forEach((def) => this._load(def));
|
|
218
|
+
await this._safeUpdateRegistration();
|
|
219
|
+
log("new functions loaded", {
|
|
220
|
+
newFunctions
|
|
221
|
+
}, {
|
|
222
|
+
F: __dxlog_file,
|
|
223
|
+
L: 162,
|
|
224
|
+
S: this,
|
|
225
|
+
C: (f, a) => f(...a)
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Load function.
|
|
230
|
+
*/
|
|
231
|
+
async _load(def, force) {
|
|
232
|
+
const { uri, route, handler } = def;
|
|
233
|
+
const filePath = join(this._options.baseDir, handler);
|
|
234
|
+
log.info("loading", {
|
|
235
|
+
uri,
|
|
236
|
+
force
|
|
237
|
+
}, {
|
|
238
|
+
F: __dxlog_file,
|
|
239
|
+
L: 171,
|
|
240
|
+
S: this,
|
|
241
|
+
C: (f, a) => f(...a)
|
|
242
|
+
});
|
|
243
|
+
if (force) {
|
|
244
|
+
Object.keys(__require.cache).filter((key) => key.startsWith(filePath)).forEach((key) => {
|
|
245
|
+
delete __require.cache[key];
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
const module = __require(filePath);
|
|
249
|
+
if (typeof module.default !== "function") {
|
|
250
|
+
throw new Error(`Handler must export default function: ${uri}`);
|
|
251
|
+
}
|
|
252
|
+
this._handlers[route] = {
|
|
253
|
+
def,
|
|
254
|
+
handler: module.default
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
async _safeUpdateRegistration() {
|
|
258
|
+
invariant(this._functionServiceRegistration, void 0, {
|
|
259
|
+
F: __dxlog_file,
|
|
260
|
+
L: 193,
|
|
261
|
+
S: this,
|
|
262
|
+
A: [
|
|
263
|
+
"this._functionServiceRegistration",
|
|
264
|
+
""
|
|
265
|
+
]
|
|
266
|
+
});
|
|
267
|
+
try {
|
|
268
|
+
await this._client.services.services.FunctionRegistryService.updateRegistration({
|
|
269
|
+
registrationId: this._functionServiceRegistration,
|
|
270
|
+
functions: this.functions.map(({ def: { id, route } }) => ({
|
|
271
|
+
id,
|
|
272
|
+
route
|
|
273
|
+
}))
|
|
274
|
+
});
|
|
275
|
+
} catch (err) {
|
|
276
|
+
log.catch(err, void 0, {
|
|
277
|
+
F: __dxlog_file,
|
|
278
|
+
L: 200,
|
|
279
|
+
S: this,
|
|
280
|
+
C: (f, a) => f(...a)
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Invoke function.
|
|
286
|
+
*/
|
|
287
|
+
async invoke(path3, data) {
|
|
288
|
+
const seq = ++this._seq;
|
|
289
|
+
const now = Date.now();
|
|
290
|
+
log.info("req", {
|
|
291
|
+
seq,
|
|
292
|
+
path: path3
|
|
293
|
+
}, {
|
|
294
|
+
F: __dxlog_file,
|
|
295
|
+
L: 211,
|
|
296
|
+
S: this,
|
|
297
|
+
C: (f, a) => f(...a)
|
|
298
|
+
});
|
|
299
|
+
const statusCode = await this._invoke(path3, {
|
|
300
|
+
data
|
|
301
|
+
});
|
|
302
|
+
log.info("res", {
|
|
303
|
+
seq,
|
|
304
|
+
path: path3,
|
|
305
|
+
statusCode,
|
|
306
|
+
duration: Date.now() - now
|
|
307
|
+
}, {
|
|
308
|
+
F: __dxlog_file,
|
|
309
|
+
L: 214,
|
|
310
|
+
S: this,
|
|
311
|
+
C: (f, a) => f(...a)
|
|
312
|
+
});
|
|
313
|
+
this.update.emit(statusCode);
|
|
314
|
+
return statusCode;
|
|
315
|
+
}
|
|
316
|
+
async _invoke(path3, event) {
|
|
317
|
+
const { handler } = this._handlers[path3] ?? {};
|
|
318
|
+
invariant(handler, `invalid path: ${path3}`, {
|
|
319
|
+
F: __dxlog_file,
|
|
320
|
+
L: 221,
|
|
321
|
+
S: this,
|
|
322
|
+
A: [
|
|
323
|
+
"handler",
|
|
324
|
+
"`invalid path: ${path}`"
|
|
325
|
+
]
|
|
326
|
+
});
|
|
327
|
+
const context = {
|
|
328
|
+
client: this._client,
|
|
329
|
+
dataDir: this._options.dataDir
|
|
330
|
+
};
|
|
331
|
+
let statusCode = 200;
|
|
332
|
+
const response = {
|
|
333
|
+
status: (code) => {
|
|
334
|
+
statusCode = code;
|
|
335
|
+
return response;
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
await handler({
|
|
339
|
+
context,
|
|
340
|
+
event,
|
|
341
|
+
response
|
|
342
|
+
});
|
|
343
|
+
return statusCode;
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
var createContext = () => new Context({
|
|
347
|
+
name: "DevServer"
|
|
348
|
+
}, {
|
|
349
|
+
F: __dxlog_file,
|
|
350
|
+
L: 240
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
// packages/core/functions/src/runtime/scheduler.ts
|
|
354
|
+
import path from "node:path";
|
|
355
|
+
import { Mutex } from "@dxos/async";
|
|
356
|
+
import { loadObjectReferences } from "@dxos/client/echo";
|
|
357
|
+
import { Context as Context2 } from "@dxos/context";
|
|
358
|
+
import { Reference } from "@dxos/echo-protocol";
|
|
359
|
+
import { log as log2 } from "@dxos/log";
|
|
360
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/scheduler.ts";
|
|
361
|
+
var Scheduler = class {
|
|
362
|
+
constructor(functions, triggers, _options = {}) {
|
|
363
|
+
this.functions = functions;
|
|
364
|
+
this.triggers = triggers;
|
|
365
|
+
this._options = _options;
|
|
366
|
+
this._ctx = createContext2();
|
|
367
|
+
this._functionUriToCallMutex = /* @__PURE__ */ new Map();
|
|
368
|
+
this.functions.registered.on(async ({ space, added }) => {
|
|
369
|
+
await this._safeActivateTriggers(space, this.triggers.getInactiveTriggers(space), added);
|
|
370
|
+
});
|
|
371
|
+
this.triggers.registered.on(async ({ space, triggers: triggers2 }) => {
|
|
372
|
+
await this._safeActivateTriggers(space, triggers2, this.functions.getFunctions(space));
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
async start() {
|
|
376
|
+
await this._ctx.dispose();
|
|
377
|
+
this._ctx = createContext2();
|
|
378
|
+
await this.functions.open(this._ctx);
|
|
379
|
+
await this.triggers.open(this._ctx);
|
|
380
|
+
}
|
|
381
|
+
async stop() {
|
|
382
|
+
await this._ctx.dispose();
|
|
383
|
+
await this.functions.close();
|
|
384
|
+
await this.triggers.close();
|
|
385
|
+
}
|
|
386
|
+
// TODO(burdon): Remove and update registries directly?
|
|
387
|
+
async register(space, manifest) {
|
|
388
|
+
await this.functions.register(space, manifest.functions);
|
|
389
|
+
await this.triggers.register(space, manifest);
|
|
390
|
+
}
|
|
391
|
+
async _safeActivateTriggers(space, triggers, functions) {
|
|
392
|
+
const mountTasks = triggers.map((trigger) => {
|
|
393
|
+
return this.activate(space, functions, trigger);
|
|
394
|
+
});
|
|
395
|
+
await Promise.all(mountTasks).catch(log2.catch);
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Activate trigger.
|
|
399
|
+
*/
|
|
400
|
+
// TODO(burdon): How are triggers deactivated?
|
|
401
|
+
async activate(space, functions, trigger) {
|
|
402
|
+
const definition = functions.find((def) => def.uri === trigger.function);
|
|
403
|
+
if (!definition) {
|
|
404
|
+
log2.info("function is not found for trigger", {
|
|
405
|
+
trigger
|
|
406
|
+
}, {
|
|
407
|
+
F: __dxlog_file2,
|
|
408
|
+
L: 85,
|
|
409
|
+
S: this,
|
|
410
|
+
C: (f, a) => f(...a)
|
|
411
|
+
});
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
const execFunction = async (args) => {
|
|
415
|
+
const mutex = this._functionUriToCallMutex.get(definition.uri) ?? new Mutex();
|
|
416
|
+
this._functionUriToCallMutex.set(definition.uri, mutex);
|
|
417
|
+
log2.info("function triggered, waiting for mutex", {
|
|
418
|
+
uri: definition.uri
|
|
419
|
+
}, {
|
|
420
|
+
F: __dxlog_file2,
|
|
421
|
+
L: 93,
|
|
422
|
+
S: this,
|
|
423
|
+
C: (f, a) => f(...a)
|
|
424
|
+
});
|
|
425
|
+
return mutex.executeSynchronized(async () => {
|
|
426
|
+
log2.info("mutex acquired", {
|
|
427
|
+
uri: definition.uri
|
|
428
|
+
}, {
|
|
429
|
+
F: __dxlog_file2,
|
|
430
|
+
L: 95,
|
|
431
|
+
S: this,
|
|
432
|
+
C: (f, a) => f(...a)
|
|
433
|
+
});
|
|
434
|
+
await loadObjectReferences(trigger, (t) => Object.values(t.meta ?? {}));
|
|
435
|
+
const meta = {};
|
|
436
|
+
for (const [key, value] of Object.entries(trigger.meta ?? {})) {
|
|
437
|
+
if (value instanceof Reference) {
|
|
438
|
+
const object = await space.db.loadObjectById(value.objectId);
|
|
439
|
+
if (object) {
|
|
440
|
+
meta[key] = object;
|
|
441
|
+
}
|
|
442
|
+
} else {
|
|
443
|
+
meta[key] = value;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
return this._execFunction(definition, trigger, {
|
|
447
|
+
meta,
|
|
448
|
+
data: {
|
|
449
|
+
...args,
|
|
450
|
+
spaceKey: space.key
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
};
|
|
455
|
+
await this.triggers.activate(space, trigger, execFunction);
|
|
456
|
+
log2("activated trigger", {
|
|
457
|
+
space: space.key,
|
|
458
|
+
trigger
|
|
459
|
+
}, {
|
|
460
|
+
F: __dxlog_file2,
|
|
461
|
+
L: 119,
|
|
462
|
+
S: this,
|
|
463
|
+
C: (f, a) => f(...a)
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Invoke function RPC.
|
|
468
|
+
*/
|
|
469
|
+
async _execFunction(def, trigger, { meta, data }) {
|
|
470
|
+
let status = 0;
|
|
471
|
+
try {
|
|
472
|
+
const payload = Object.assign({}, meta && {
|
|
473
|
+
meta
|
|
474
|
+
}, data);
|
|
475
|
+
const { endpoint, callback } = this._options;
|
|
476
|
+
if (endpoint) {
|
|
477
|
+
const url = path.join(endpoint, def.route);
|
|
478
|
+
log2.info("exec", {
|
|
479
|
+
function: def.uri,
|
|
480
|
+
url,
|
|
481
|
+
triggerType: trigger.spec.type
|
|
482
|
+
}, {
|
|
483
|
+
F: __dxlog_file2,
|
|
484
|
+
L: 139,
|
|
485
|
+
S: this,
|
|
486
|
+
C: (f, a) => f(...a)
|
|
487
|
+
});
|
|
488
|
+
const response = await fetch(url, {
|
|
489
|
+
method: "POST",
|
|
490
|
+
headers: {
|
|
491
|
+
"Content-Type": "application/json"
|
|
492
|
+
},
|
|
493
|
+
body: JSON.stringify(payload)
|
|
494
|
+
});
|
|
495
|
+
status = response.status;
|
|
496
|
+
} else if (callback) {
|
|
497
|
+
log2.info("exec", {
|
|
498
|
+
function: def.uri
|
|
499
|
+
}, {
|
|
500
|
+
F: __dxlog_file2,
|
|
501
|
+
L: 150,
|
|
502
|
+
S: this,
|
|
503
|
+
C: (f, a) => f(...a)
|
|
504
|
+
});
|
|
505
|
+
status = await callback(payload) ?? 200;
|
|
506
|
+
}
|
|
507
|
+
if (status && status >= 400) {
|
|
508
|
+
throw new Error(`Response: ${status}`);
|
|
509
|
+
}
|
|
510
|
+
log2.info("done", {
|
|
511
|
+
function: def.uri,
|
|
512
|
+
status
|
|
513
|
+
}, {
|
|
514
|
+
F: __dxlog_file2,
|
|
515
|
+
L: 160,
|
|
516
|
+
S: this,
|
|
517
|
+
C: (f, a) => f(...a)
|
|
518
|
+
});
|
|
519
|
+
} catch (err) {
|
|
520
|
+
log2.error("error", {
|
|
521
|
+
function: def.uri,
|
|
522
|
+
error: err.message
|
|
523
|
+
}, {
|
|
524
|
+
F: __dxlog_file2,
|
|
525
|
+
L: 162,
|
|
526
|
+
S: this,
|
|
527
|
+
C: (f, a) => f(...a)
|
|
528
|
+
});
|
|
529
|
+
status = 500;
|
|
530
|
+
}
|
|
531
|
+
return status;
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
var createContext2 = () => new Context2({
|
|
535
|
+
name: "FunctionScheduler"
|
|
536
|
+
}, {
|
|
537
|
+
F: __dxlog_file2,
|
|
538
|
+
L: 170
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
// packages/core/functions/src/testing/setup.ts
|
|
542
|
+
var createInitializedClients = async (testBuilder, count = 1, config) => {
|
|
543
|
+
const clients = range(count).map(() => new Client({
|
|
544
|
+
config,
|
|
545
|
+
services: testBuilder.createLocalClientServices(),
|
|
546
|
+
types: [
|
|
547
|
+
FunctionDef,
|
|
548
|
+
FunctionTrigger,
|
|
549
|
+
TestType
|
|
550
|
+
]
|
|
551
|
+
}));
|
|
552
|
+
testBuilder.ctx.onDispose(() => Promise.all(clients.map((client) => client.destroy())));
|
|
553
|
+
return Promise.all(clients.map(async (client, index) => {
|
|
554
|
+
await client.initialize();
|
|
555
|
+
await client.halo.createIdentity({
|
|
556
|
+
displayName: `Peer ${index}`
|
|
557
|
+
});
|
|
558
|
+
await client.spaces.waitUntilReady();
|
|
559
|
+
return client;
|
|
560
|
+
}));
|
|
561
|
+
};
|
|
562
|
+
var createFunctionRuntime = async (testBuilder, pluginInitializer) => {
|
|
563
|
+
const functionsPort = await getRandomPort("127.0.0.1");
|
|
564
|
+
const config = new Config({
|
|
565
|
+
runtime: {
|
|
566
|
+
agent: {
|
|
567
|
+
plugins: [
|
|
568
|
+
{
|
|
569
|
+
id: "dxos.org/agent/plugin/functions",
|
|
570
|
+
config: {
|
|
571
|
+
port: functionsPort
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
]
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
const [client] = await createInitializedClients(testBuilder, 1, config);
|
|
579
|
+
const plugin = await pluginInitializer(client);
|
|
580
|
+
testBuilder.ctx.onDispose(() => plugin.close());
|
|
581
|
+
return client;
|
|
582
|
+
};
|
|
583
|
+
var startFunctionsHost = async (testBuilder, pluginInitializer, options) => {
|
|
584
|
+
const functionRuntime = await createFunctionRuntime(testBuilder, pluginInitializer);
|
|
585
|
+
const functionsRegistry = new FunctionRegistry(functionRuntime);
|
|
586
|
+
const devServer = await startDevServer(testBuilder, functionRuntime, functionsRegistry, options);
|
|
587
|
+
const scheduler = await startScheduler(testBuilder, functionRuntime, devServer, functionsRegistry);
|
|
588
|
+
return {
|
|
589
|
+
scheduler,
|
|
590
|
+
client: functionRuntime,
|
|
591
|
+
waitForActiveTriggers: async (space) => {
|
|
592
|
+
await waitForCondition({
|
|
593
|
+
condition: () => scheduler.triggers.getActiveTriggers(space).length > 0
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
};
|
|
598
|
+
var startScheduler = async (testBuilder, client, devServer, functionRegistry) => {
|
|
599
|
+
const triggerRegistry = new TriggerRegistry(client);
|
|
600
|
+
const scheduler = new Scheduler(functionRegistry, triggerRegistry, {
|
|
601
|
+
endpoint: devServer.endpoint
|
|
602
|
+
});
|
|
603
|
+
await scheduler.start();
|
|
604
|
+
testBuilder.ctx.onDispose(() => scheduler.stop());
|
|
605
|
+
return scheduler;
|
|
606
|
+
};
|
|
607
|
+
var startDevServer = async (testBuilder, client, functionRegistry, options) => {
|
|
608
|
+
const server = new DevServer(client, functionRegistry, {
|
|
609
|
+
baseDir: path2.join(__dirname, "../testing"),
|
|
610
|
+
port: await getRandomPort("127.0.0.1"),
|
|
611
|
+
...options
|
|
612
|
+
});
|
|
613
|
+
await server.start();
|
|
614
|
+
testBuilder.ctx.onDispose(() => server.stop());
|
|
615
|
+
return server;
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
// packages/core/functions/src/testing/util.ts
|
|
619
|
+
import { Filter } from "@dxos/client/echo";
|
|
620
|
+
import { performInvitation } from "@dxos/client/testing";
|
|
621
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
622
|
+
import { Invitation } from "@dxos/protocols/proto/dxos/client/services";
|
|
623
|
+
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/functions/src/testing/util.ts";
|
|
624
|
+
var triggerWebhook = async (space, uri) => {
|
|
625
|
+
const trigger = (await space.db.query(Filter.schema(FunctionTrigger, (t) => t.function === uri)).run()).objects[0];
|
|
626
|
+
invariant2(trigger.spec.type === "webhook", void 0, {
|
|
627
|
+
F: __dxlog_file3,
|
|
628
|
+
L: 17,
|
|
629
|
+
S: void 0,
|
|
630
|
+
A: [
|
|
631
|
+
"trigger.spec.type === 'webhook'",
|
|
632
|
+
""
|
|
633
|
+
]
|
|
634
|
+
});
|
|
635
|
+
void fetch(`http://localhost:${trigger.spec.port}`);
|
|
636
|
+
};
|
|
637
|
+
var inviteMember = async (host, guest) => {
|
|
638
|
+
const [{ invitation: hostInvitation }] = await Promise.all(performInvitation({
|
|
639
|
+
host,
|
|
640
|
+
guest: guest.spaces
|
|
641
|
+
}));
|
|
642
|
+
if (hostInvitation?.state !== Invitation.State.SUCCESS) {
|
|
643
|
+
throw new Error(`Expected ${hostInvitation?.state} to be ${Invitation.State.SUCCESS}.`);
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
// packages/core/functions/src/testing/manifest.ts
|
|
648
|
+
var testFunctionManifest = {
|
|
649
|
+
functions: [
|
|
650
|
+
{
|
|
651
|
+
uri: "example.com/function/test",
|
|
652
|
+
route: "test",
|
|
653
|
+
handler: "test"
|
|
654
|
+
}
|
|
655
|
+
]
|
|
656
|
+
};
|
|
657
|
+
export {
|
|
658
|
+
TestType,
|
|
659
|
+
createFunctionRuntime,
|
|
660
|
+
createInitializedClients,
|
|
661
|
+
inviteMember,
|
|
662
|
+
startFunctionsHost,
|
|
663
|
+
testFunctionManifest,
|
|
664
|
+
triggerWebhook
|
|
665
|
+
};
|
|
666
|
+
//# sourceMappingURL=index.mjs.map
|