@dxos/functions 0.5.2 → 0.5.3-main.088a2c8
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 +492 -146
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +488 -143
- 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 +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/runtime/dev-server.d.ts +17 -6
- 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 +55 -7
- package/dist/types/src/runtime/scheduler.d.ts.map +1 -1
- package/dist/types/src/testing/test/handler.d.ts +3 -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/types.d.ts +182 -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 +20 -11
- package/schema/functions.json +183 -0
- package/src/handler.ts +56 -26
- package/src/index.ts +1 -1
- package/src/runtime/dev-server.test.ts +80 -0
- package/src/runtime/dev-server.ts +74 -40
- package/src/runtime/scheduler.test.ts +163 -9
- package/src/runtime/scheduler.ts +228 -64
- package/src/testing/test/handler.ts +9 -0
- package/src/testing/test/index.ts +7 -0
- package/src/types.ts +87 -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
package/dist/lib/node/index.cjs
CHANGED
|
@@ -29,6 +29,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
29
29
|
var node_exports = {};
|
|
30
30
|
__export(node_exports, {
|
|
31
31
|
DevServer: () => DevServer,
|
|
32
|
+
FunctionManifestSchema: () => FunctionManifestSchema,
|
|
32
33
|
Scheduler: () => Scheduler,
|
|
33
34
|
subscriptionHandler: () => subscriptionHandler
|
|
34
35
|
});
|
|
@@ -43,6 +44,10 @@ var import_async = require("@dxos/async");
|
|
|
43
44
|
var import_invariant = require("@dxos/invariant");
|
|
44
45
|
var import_log2 = require("@dxos/log");
|
|
45
46
|
var import_cron = require("cron");
|
|
47
|
+
var import_get_port_please2 = require("get-port-please");
|
|
48
|
+
var import_node_http = __toESM(require("node:http"));
|
|
49
|
+
var import_node_path2 = __toESM(require("node:path"));
|
|
50
|
+
var import_ws = __toESM(require("ws"));
|
|
46
51
|
var import_types = require("@braneframe/types");
|
|
47
52
|
var import_async2 = require("@dxos/async");
|
|
48
53
|
var import_echo = require("@dxos/client/echo");
|
|
@@ -50,6 +55,7 @@ var import_context = require("@dxos/context");
|
|
|
50
55
|
var import_invariant2 = require("@dxos/invariant");
|
|
51
56
|
var import_log3 = require("@dxos/log");
|
|
52
57
|
var import_util2 = require("@dxos/util");
|
|
58
|
+
var S = __toESM(require("@effect/schema/Schema"));
|
|
53
59
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
54
60
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
55
61
|
}) : x)(function(x) {
|
|
@@ -59,16 +65,16 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
59
65
|
});
|
|
60
66
|
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/functions/src/handler.ts";
|
|
61
67
|
var subscriptionHandler = (handler) => {
|
|
62
|
-
return ({ event, context, ...rest }) => {
|
|
68
|
+
return ({ event: { data }, context, ...rest }) => {
|
|
63
69
|
const { client } = context;
|
|
64
|
-
const space =
|
|
65
|
-
const objects = space
|
|
66
|
-
if (!!
|
|
70
|
+
const space = data.spaceKey ? client.spaces.get(import_client.PublicKey.from(data.spaceKey)) : void 0;
|
|
71
|
+
const objects = space ? data.objects?.map((id) => space.db.getObjectById(id)).filter(import_util.nonNullable) : [];
|
|
72
|
+
if (!!data.spaceKey && !space) {
|
|
67
73
|
import_log.log.warn("invalid space", {
|
|
68
|
-
|
|
74
|
+
data
|
|
69
75
|
}, {
|
|
70
76
|
F: __dxlog_file,
|
|
71
|
-
L:
|
|
77
|
+
L: 91,
|
|
72
78
|
S: void 0,
|
|
73
79
|
C: (f, a) => f(...a)
|
|
74
80
|
});
|
|
@@ -78,15 +84,18 @@ var subscriptionHandler = (handler) => {
|
|
|
78
84
|
objects: objects?.length
|
|
79
85
|
}, {
|
|
80
86
|
F: __dxlog_file,
|
|
81
|
-
L:
|
|
87
|
+
L: 93,
|
|
82
88
|
S: void 0,
|
|
83
89
|
C: (f, a) => f(...a)
|
|
84
90
|
});
|
|
85
91
|
}
|
|
86
92
|
return handler({
|
|
87
93
|
event: {
|
|
88
|
-
|
|
89
|
-
|
|
94
|
+
data: {
|
|
95
|
+
...data,
|
|
96
|
+
space,
|
|
97
|
+
objects
|
|
98
|
+
}
|
|
90
99
|
},
|
|
91
100
|
context,
|
|
92
101
|
...rest
|
|
@@ -101,11 +110,17 @@ var DevServer = class {
|
|
|
101
110
|
this._options = _options;
|
|
102
111
|
this._handlers = {};
|
|
103
112
|
this._seq = 0;
|
|
113
|
+
this.update = new import_async.Event();
|
|
114
|
+
}
|
|
115
|
+
get stats() {
|
|
116
|
+
return {
|
|
117
|
+
seq: this._seq
|
|
118
|
+
};
|
|
104
119
|
}
|
|
105
120
|
get endpoint() {
|
|
106
121
|
(0, import_invariant.invariant)(this._port, void 0, {
|
|
107
122
|
F: __dxlog_file2,
|
|
108
|
-
L:
|
|
123
|
+
L: 54,
|
|
109
124
|
S: this,
|
|
110
125
|
A: [
|
|
111
126
|
"this._port",
|
|
@@ -127,7 +142,7 @@ var DevServer = class {
|
|
|
127
142
|
} catch (err) {
|
|
128
143
|
import_log2.log.error("parsing function (check manifest)", err, {
|
|
129
144
|
F: __dxlog_file2,
|
|
130
|
-
L:
|
|
145
|
+
L: 71,
|
|
131
146
|
S: this,
|
|
132
147
|
C: (f, a) => f(...a)
|
|
133
148
|
});
|
|
@@ -135,29 +150,44 @@ var DevServer = class {
|
|
|
135
150
|
}
|
|
136
151
|
}
|
|
137
152
|
async start() {
|
|
153
|
+
(0, import_invariant.invariant)(!this._server, void 0, {
|
|
154
|
+
F: __dxlog_file2,
|
|
155
|
+
L: 77,
|
|
156
|
+
S: this,
|
|
157
|
+
A: [
|
|
158
|
+
"!this._server",
|
|
159
|
+
""
|
|
160
|
+
]
|
|
161
|
+
});
|
|
162
|
+
import_log2.log.info("starting...", void 0, {
|
|
163
|
+
F: __dxlog_file2,
|
|
164
|
+
L: 78,
|
|
165
|
+
S: this,
|
|
166
|
+
C: (f, a) => f(...a)
|
|
167
|
+
});
|
|
138
168
|
const app = (0, import_express.default)();
|
|
139
169
|
app.use(import_express.default.json());
|
|
140
|
-
app.post("/:
|
|
141
|
-
const {
|
|
170
|
+
app.post("/:path", async (req, res) => {
|
|
171
|
+
const { path: path2 } = req.params;
|
|
142
172
|
try {
|
|
143
173
|
import_log2.log.info("calling", {
|
|
144
|
-
|
|
174
|
+
path: path2
|
|
145
175
|
}, {
|
|
146
176
|
F: __dxlog_file2,
|
|
147
|
-
L:
|
|
177
|
+
L: 87,
|
|
148
178
|
S: this,
|
|
149
179
|
C: (f, a) => f(...a)
|
|
150
180
|
});
|
|
151
181
|
if (this._options.reload) {
|
|
152
|
-
const { def } = this._handlers[
|
|
182
|
+
const { def } = this._handlers["/" + path2];
|
|
153
183
|
await this._load(def, true);
|
|
154
184
|
}
|
|
155
|
-
res.statusCode = await this.
|
|
185
|
+
res.statusCode = await this.invoke("/" + path2, req.body);
|
|
156
186
|
res.end();
|
|
157
187
|
} catch (err) {
|
|
158
188
|
import_log2.log.catch(err, void 0, {
|
|
159
189
|
F: __dxlog_file2,
|
|
160
|
-
L:
|
|
190
|
+
L: 97,
|
|
161
191
|
S: this,
|
|
162
192
|
C: (f, a) => f(...a)
|
|
163
193
|
});
|
|
@@ -177,92 +207,170 @@ var DevServer = class {
|
|
|
177
207
|
try {
|
|
178
208
|
const { registrationId, endpoint } = await this._client.services.services.FunctionRegistryService.register({
|
|
179
209
|
endpoint: this.endpoint,
|
|
180
|
-
functions: this.functions.map(({ def: {
|
|
181
|
-
|
|
210
|
+
functions: this.functions.map(({ def: { id, path: path2 } }) => ({
|
|
211
|
+
id,
|
|
212
|
+
path: path2
|
|
182
213
|
}))
|
|
183
214
|
});
|
|
184
215
|
import_log2.log.info("registered", {
|
|
185
|
-
registrationId,
|
|
186
216
|
endpoint
|
|
187
217
|
}, {
|
|
188
218
|
F: __dxlog_file2,
|
|
189
|
-
L:
|
|
219
|
+
L: 113,
|
|
190
220
|
S: this,
|
|
191
221
|
C: (f, a) => f(...a)
|
|
192
222
|
});
|
|
193
|
-
this._registrationId = registrationId;
|
|
194
223
|
this._proxy = endpoint;
|
|
224
|
+
this._functionServiceRegistration = registrationId;
|
|
195
225
|
} catch (err) {
|
|
196
226
|
await this.stop();
|
|
197
227
|
throw new Error("FunctionRegistryService not available (check plugin is configured).");
|
|
198
228
|
}
|
|
229
|
+
import_log2.log.info("started", {
|
|
230
|
+
port: this._port
|
|
231
|
+
}, {
|
|
232
|
+
F: __dxlog_file2,
|
|
233
|
+
L: 121,
|
|
234
|
+
S: this,
|
|
235
|
+
C: (f, a) => f(...a)
|
|
236
|
+
});
|
|
199
237
|
}
|
|
200
238
|
async stop() {
|
|
239
|
+
(0, import_invariant.invariant)(this._server, void 0, {
|
|
240
|
+
F: __dxlog_file2,
|
|
241
|
+
L: 125,
|
|
242
|
+
S: this,
|
|
243
|
+
A: [
|
|
244
|
+
"this._server",
|
|
245
|
+
""
|
|
246
|
+
]
|
|
247
|
+
});
|
|
248
|
+
import_log2.log.info("stopping...", void 0, {
|
|
249
|
+
F: __dxlog_file2,
|
|
250
|
+
L: 126,
|
|
251
|
+
S: this,
|
|
252
|
+
C: (f, a) => f(...a)
|
|
253
|
+
});
|
|
201
254
|
const trigger = new import_async.Trigger();
|
|
202
|
-
this._server
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
255
|
+
this._server.close(async () => {
|
|
256
|
+
import_log2.log.info("server stopped", void 0, {
|
|
257
|
+
F: __dxlog_file2,
|
|
258
|
+
L: 130,
|
|
259
|
+
S: this,
|
|
260
|
+
C: (f, a) => f(...a)
|
|
261
|
+
});
|
|
262
|
+
try {
|
|
263
|
+
if (this._functionServiceRegistration) {
|
|
264
|
+
(0, import_invariant.invariant)(this._client.services.services.FunctionRegistryService, void 0, {
|
|
265
|
+
F: __dxlog_file2,
|
|
266
|
+
L: 133,
|
|
267
|
+
S: this,
|
|
268
|
+
A: [
|
|
269
|
+
"this._client.services.services.FunctionRegistryService",
|
|
270
|
+
""
|
|
271
|
+
]
|
|
272
|
+
});
|
|
273
|
+
await this._client.services.services.FunctionRegistryService.unregister({
|
|
274
|
+
registrationId: this._functionServiceRegistration
|
|
275
|
+
});
|
|
276
|
+
import_log2.log.info("unregistered", {
|
|
277
|
+
registrationId: this._functionServiceRegistration
|
|
278
|
+
}, {
|
|
279
|
+
F: __dxlog_file2,
|
|
280
|
+
L: 138,
|
|
281
|
+
S: this,
|
|
282
|
+
C: (f, a) => f(...a)
|
|
283
|
+
});
|
|
284
|
+
this._functionServiceRegistration = void 0;
|
|
285
|
+
this._proxy = void 0;
|
|
286
|
+
}
|
|
287
|
+
trigger.wake();
|
|
288
|
+
} catch (err) {
|
|
289
|
+
trigger.throw(err);
|
|
217
290
|
}
|
|
218
|
-
trigger.wake();
|
|
219
291
|
});
|
|
220
292
|
await trigger.wait();
|
|
221
293
|
this._port = void 0;
|
|
222
294
|
this._server = void 0;
|
|
295
|
+
import_log2.log.info("stopped", void 0, {
|
|
296
|
+
F: __dxlog_file2,
|
|
297
|
+
L: 152,
|
|
298
|
+
S: this,
|
|
299
|
+
C: (f, a) => f(...a)
|
|
300
|
+
});
|
|
223
301
|
}
|
|
224
302
|
/**
|
|
225
303
|
* Load function.
|
|
226
304
|
*/
|
|
227
|
-
async _load(def,
|
|
228
|
-
const { id,
|
|
229
|
-
const
|
|
305
|
+
async _load(def, force = false) {
|
|
306
|
+
const { id, path: path2, handler } = def;
|
|
307
|
+
const filePath = (0, import_node_path.join)(this._options.baseDir, handler);
|
|
230
308
|
import_log2.log.info("loading", {
|
|
231
|
-
id
|
|
309
|
+
id,
|
|
310
|
+
force
|
|
232
311
|
}, {
|
|
233
312
|
F: __dxlog_file2,
|
|
234
|
-
L:
|
|
313
|
+
L: 161,
|
|
235
314
|
S: this,
|
|
236
315
|
C: (f, a) => f(...a)
|
|
237
316
|
});
|
|
238
|
-
if (
|
|
239
|
-
Object.keys(__require.cache).filter((key) => key.startsWith(
|
|
317
|
+
if (force) {
|
|
318
|
+
Object.keys(__require.cache).filter((key) => key.startsWith(filePath)).forEach((key) => {
|
|
319
|
+
delete __require.cache[key];
|
|
320
|
+
});
|
|
240
321
|
}
|
|
241
|
-
const module2 = __require(
|
|
322
|
+
const module2 = __require(filePath);
|
|
242
323
|
if (typeof module2.default !== "function") {
|
|
243
324
|
throw new Error(`Handler must export default function: ${id}`);
|
|
244
325
|
}
|
|
245
|
-
this._handlers[
|
|
326
|
+
this._handlers[path2] = {
|
|
246
327
|
def,
|
|
247
328
|
handler: module2.default
|
|
248
329
|
};
|
|
249
330
|
}
|
|
250
331
|
/**
|
|
251
|
-
* Invoke function
|
|
332
|
+
* Invoke function.
|
|
252
333
|
*/
|
|
253
|
-
async
|
|
334
|
+
async invoke(path2, data) {
|
|
254
335
|
const seq = ++this._seq;
|
|
255
336
|
const now = Date.now();
|
|
256
337
|
import_log2.log.info("req", {
|
|
257
338
|
seq,
|
|
258
|
-
|
|
339
|
+
path: path2
|
|
259
340
|
}, {
|
|
260
341
|
F: __dxlog_file2,
|
|
261
|
-
L:
|
|
342
|
+
L: 188,
|
|
343
|
+
S: this,
|
|
344
|
+
C: (f, a) => f(...a)
|
|
345
|
+
});
|
|
346
|
+
const statusCode = await this._invoke(path2, {
|
|
347
|
+
data
|
|
348
|
+
});
|
|
349
|
+
import_log2.log.info("res", {
|
|
350
|
+
seq,
|
|
351
|
+
path: path2,
|
|
352
|
+
statusCode,
|
|
353
|
+
duration: Date.now() - now
|
|
354
|
+
}, {
|
|
355
|
+
F: __dxlog_file2,
|
|
356
|
+
L: 191,
|
|
262
357
|
S: this,
|
|
263
358
|
C: (f, a) => f(...a)
|
|
264
359
|
});
|
|
265
|
-
|
|
360
|
+
this.update.emit(statusCode);
|
|
361
|
+
return statusCode;
|
|
362
|
+
}
|
|
363
|
+
async _invoke(path2, event) {
|
|
364
|
+
const { handler } = this._handlers[path2] ?? {};
|
|
365
|
+
(0, import_invariant.invariant)(handler, `invalid path: ${path2}`, {
|
|
366
|
+
F: __dxlog_file2,
|
|
367
|
+
L: 198,
|
|
368
|
+
S: this,
|
|
369
|
+
A: [
|
|
370
|
+
"handler",
|
|
371
|
+
"`invalid path: ${path}`"
|
|
372
|
+
]
|
|
373
|
+
});
|
|
266
374
|
const context = {
|
|
267
375
|
client: this._client,
|
|
268
376
|
dataDir: this._options.dataDir
|
|
@@ -279,17 +387,6 @@ var DevServer = class {
|
|
|
279
387
|
event,
|
|
280
388
|
response
|
|
281
389
|
});
|
|
282
|
-
import_log2.log.info("res", {
|
|
283
|
-
seq,
|
|
284
|
-
name,
|
|
285
|
-
statusCode,
|
|
286
|
-
duration: Date.now() - now
|
|
287
|
-
}, {
|
|
288
|
-
F: __dxlog_file2,
|
|
289
|
-
L: 178,
|
|
290
|
-
S: this,
|
|
291
|
-
C: (f, a) => f(...a)
|
|
292
|
-
});
|
|
293
390
|
return statusCode;
|
|
294
391
|
}
|
|
295
392
|
};
|
|
@@ -299,7 +396,13 @@ var Scheduler = class {
|
|
|
299
396
|
this._client = _client;
|
|
300
397
|
this._manifest = _manifest;
|
|
301
398
|
this._options = _options;
|
|
302
|
-
this._mounts = new import_util2.ComplexMap(({
|
|
399
|
+
this._mounts = new import_util2.ComplexMap(({ spaceKey, id }) => `${spaceKey.toHex()}:${id}`);
|
|
400
|
+
}
|
|
401
|
+
get mounts() {
|
|
402
|
+
return Array.from(this._mounts.values()).reduce((acc, { trigger }) => {
|
|
403
|
+
acc.push(trigger);
|
|
404
|
+
return acc;
|
|
405
|
+
}, []);
|
|
303
406
|
}
|
|
304
407
|
async start() {
|
|
305
408
|
this._client.spaces.subscribe(async (spaces) => {
|
|
@@ -316,15 +419,18 @@ var Scheduler = class {
|
|
|
316
419
|
await this.unmount(id, spaceKey);
|
|
317
420
|
}
|
|
318
421
|
}
|
|
422
|
+
/**
|
|
423
|
+
* Mount trigger.
|
|
424
|
+
*/
|
|
319
425
|
async mount(ctx, space, trigger) {
|
|
320
426
|
const key = {
|
|
321
|
-
|
|
322
|
-
|
|
427
|
+
spaceKey: space.key,
|
|
428
|
+
id: trigger.function
|
|
323
429
|
};
|
|
324
430
|
const def = this._manifest.functions.find((config) => config.id === trigger.function);
|
|
325
431
|
(0, import_invariant2.invariant)(def, `Function not found: ${trigger.function}`, {
|
|
326
432
|
F: __dxlog_file3,
|
|
327
|
-
L:
|
|
433
|
+
L: 76,
|
|
328
434
|
S: this,
|
|
329
435
|
A: [
|
|
330
436
|
"def",
|
|
@@ -342,18 +448,24 @@ var Scheduler = class {
|
|
|
342
448
|
trigger
|
|
343
449
|
}, {
|
|
344
450
|
F: __dxlog_file3,
|
|
345
|
-
L:
|
|
451
|
+
L: 82,
|
|
346
452
|
S: this,
|
|
347
453
|
C: (f, a) => f(...a)
|
|
348
454
|
});
|
|
349
455
|
if (ctx.disposed) {
|
|
350
456
|
return;
|
|
351
457
|
}
|
|
352
|
-
if (trigger.
|
|
353
|
-
this._createTimer(ctx, space, def, trigger);
|
|
458
|
+
if (trigger.timer) {
|
|
459
|
+
await this._createTimer(ctx, space, def, trigger);
|
|
460
|
+
}
|
|
461
|
+
if (trigger.webhook) {
|
|
462
|
+
await this._createWebhook(ctx, space, def, trigger);
|
|
463
|
+
}
|
|
464
|
+
if (trigger.websocket) {
|
|
465
|
+
await this._createWebsocket(ctx, space, def, trigger);
|
|
354
466
|
}
|
|
355
|
-
|
|
356
|
-
this._createSubscription(ctx, space, def,
|
|
467
|
+
if (trigger.subscription) {
|
|
468
|
+
await this._createSubscription(ctx, space, def, trigger);
|
|
357
469
|
}
|
|
358
470
|
}
|
|
359
471
|
}
|
|
@@ -368,25 +480,95 @@ var Scheduler = class {
|
|
|
368
480
|
await ctx.dispose();
|
|
369
481
|
}
|
|
370
482
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
483
|
+
async _execFunction(def, trigger, data) {
|
|
484
|
+
let status = 0;
|
|
485
|
+
try {
|
|
486
|
+
const payload = Object.assign({}, {
|
|
487
|
+
meta: trigger.meta
|
|
488
|
+
}, data);
|
|
489
|
+
const { endpoint, callback } = this._options;
|
|
490
|
+
if (endpoint) {
|
|
491
|
+
const url = import_node_path2.default.join(endpoint, def.path);
|
|
492
|
+
import_log3.log.info("exec", {
|
|
493
|
+
function: def.id,
|
|
494
|
+
url
|
|
495
|
+
}, {
|
|
496
|
+
F: __dxlog_file3,
|
|
497
|
+
L: 128,
|
|
498
|
+
S: this,
|
|
499
|
+
C: (f, a) => f(...a)
|
|
500
|
+
});
|
|
501
|
+
const response = await fetch(url, {
|
|
502
|
+
method: "POST",
|
|
503
|
+
headers: {
|
|
504
|
+
"Content-Type": "application/json"
|
|
505
|
+
},
|
|
506
|
+
body: JSON.stringify(payload)
|
|
507
|
+
});
|
|
508
|
+
status = response.status;
|
|
509
|
+
} else if (callback) {
|
|
510
|
+
import_log3.log.info("exec", {
|
|
511
|
+
function: def.id
|
|
512
|
+
}, {
|
|
513
|
+
F: __dxlog_file3,
|
|
514
|
+
L: 139,
|
|
515
|
+
S: this,
|
|
516
|
+
C: (f, a) => f(...a)
|
|
517
|
+
});
|
|
518
|
+
status = await callback(payload) ?? 200;
|
|
519
|
+
}
|
|
520
|
+
if (status && status >= 400) {
|
|
521
|
+
throw new Error(`Response: ${status}`);
|
|
522
|
+
}
|
|
523
|
+
import_log3.log.info("done", {
|
|
524
|
+
function: def.id,
|
|
525
|
+
status
|
|
526
|
+
}, {
|
|
527
|
+
F: __dxlog_file3,
|
|
528
|
+
L: 149,
|
|
529
|
+
S: this,
|
|
530
|
+
C: (f, a) => f(...a)
|
|
375
531
|
});
|
|
376
|
-
})
|
|
377
|
-
|
|
532
|
+
} catch (err) {
|
|
533
|
+
import_log3.log.error("error", {
|
|
534
|
+
function: def.id,
|
|
535
|
+
error: err.message
|
|
536
|
+
}, {
|
|
537
|
+
F: __dxlog_file3,
|
|
538
|
+
L: 151,
|
|
539
|
+
S: this,
|
|
540
|
+
C: (f, a) => f(...a)
|
|
541
|
+
});
|
|
542
|
+
status = 500;
|
|
543
|
+
}
|
|
544
|
+
return status;
|
|
545
|
+
}
|
|
546
|
+
//
|
|
547
|
+
// Triggers
|
|
548
|
+
//
|
|
549
|
+
/**
|
|
550
|
+
* Cron timer.
|
|
551
|
+
*/
|
|
552
|
+
async _createTimer(ctx, space, def, trigger) {
|
|
553
|
+
import_log3.log.info("timer", {
|
|
554
|
+
space: space.key,
|
|
555
|
+
trigger
|
|
556
|
+
}, {
|
|
378
557
|
F: __dxlog_file3,
|
|
379
|
-
L:
|
|
558
|
+
L: 166,
|
|
380
559
|
S: this,
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
560
|
+
C: (f, a) => f(...a)
|
|
561
|
+
});
|
|
562
|
+
const spec = trigger.timer;
|
|
563
|
+
const task = new import_async2.DeferredTask(ctx, async () => {
|
|
564
|
+
await this._execFunction(def, trigger, {
|
|
565
|
+
spaceKey: space.key
|
|
566
|
+
});
|
|
385
567
|
});
|
|
386
568
|
let last = 0;
|
|
387
569
|
let run = 0;
|
|
388
570
|
const job = import_cron.CronJob.from({
|
|
389
|
-
cronTime:
|
|
571
|
+
cronTime: spec.cron,
|
|
390
572
|
runOnInit: false,
|
|
391
573
|
onTick: () => {
|
|
392
574
|
const now = Date.now();
|
|
@@ -399,7 +581,7 @@ var Scheduler = class {
|
|
|
399
581
|
delta
|
|
400
582
|
}, {
|
|
401
583
|
F: __dxlog_file3,
|
|
402
|
-
L:
|
|
584
|
+
L: 186,
|
|
403
585
|
S: this,
|
|
404
586
|
C: (f, a) => f(...a)
|
|
405
587
|
});
|
|
@@ -409,20 +591,180 @@ var Scheduler = class {
|
|
|
409
591
|
job.start();
|
|
410
592
|
ctx.onDispose(() => job.stop());
|
|
411
593
|
}
|
|
412
|
-
|
|
594
|
+
/**
|
|
595
|
+
* Webhook.
|
|
596
|
+
*/
|
|
597
|
+
async _createWebhook(ctx, space, def, trigger) {
|
|
598
|
+
import_log3.log.info("webhook", {
|
|
599
|
+
space: space.key,
|
|
600
|
+
trigger
|
|
601
|
+
}, {
|
|
602
|
+
F: __dxlog_file3,
|
|
603
|
+
L: 199,
|
|
604
|
+
S: this,
|
|
605
|
+
C: (f, a) => f(...a)
|
|
606
|
+
});
|
|
607
|
+
const spec = trigger.webhook;
|
|
608
|
+
const server = import_node_http.default.createServer(async (req, res) => {
|
|
609
|
+
if (req.method !== spec.method) {
|
|
610
|
+
res.statusCode = 405;
|
|
611
|
+
return res.end();
|
|
612
|
+
}
|
|
613
|
+
res.statusCode = await this._execFunction(def, trigger, {
|
|
614
|
+
spaceKey: space.key
|
|
615
|
+
});
|
|
616
|
+
res.end();
|
|
617
|
+
});
|
|
618
|
+
const port = await (0, import_get_port_please2.getPort)({
|
|
619
|
+
random: true
|
|
620
|
+
});
|
|
621
|
+
server.listen(port, () => {
|
|
622
|
+
import_log3.log.info("started webhook", {
|
|
623
|
+
port
|
|
624
|
+
}, {
|
|
625
|
+
F: __dxlog_file3,
|
|
626
|
+
L: 223,
|
|
627
|
+
S: this,
|
|
628
|
+
C: (f, a) => f(...a)
|
|
629
|
+
});
|
|
630
|
+
spec.port = port;
|
|
631
|
+
});
|
|
632
|
+
ctx.onDispose(() => {
|
|
633
|
+
server.close();
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Websocket.
|
|
638
|
+
* NOTE: The port must be unique, so the same hook cannot be used for multiple spaces.
|
|
639
|
+
*/
|
|
640
|
+
async _createWebsocket(ctx, space, def, trigger, options = {
|
|
641
|
+
retryDelay: 2,
|
|
642
|
+
maxAttempts: 5
|
|
643
|
+
}) {
|
|
644
|
+
import_log3.log.info("websocket", {
|
|
645
|
+
space: space.key,
|
|
646
|
+
trigger
|
|
647
|
+
}, {
|
|
648
|
+
F: __dxlog_file3,
|
|
649
|
+
L: 249,
|
|
650
|
+
S: this,
|
|
651
|
+
C: (f, a) => f(...a)
|
|
652
|
+
});
|
|
653
|
+
const spec = trigger.websocket;
|
|
654
|
+
const { url, init } = spec;
|
|
655
|
+
let ws;
|
|
656
|
+
for (let attempt = 1; attempt <= options.maxAttempts; attempt++) {
|
|
657
|
+
const open = new import_async2.Trigger();
|
|
658
|
+
ws = new import_ws.default(url);
|
|
659
|
+
Object.assign(ws, {
|
|
660
|
+
onopen: () => {
|
|
661
|
+
import_log3.log.info("opened", {
|
|
662
|
+
url
|
|
663
|
+
}, {
|
|
664
|
+
F: __dxlog_file3,
|
|
665
|
+
L: 260,
|
|
666
|
+
S: this,
|
|
667
|
+
C: (f, a) => f(...a)
|
|
668
|
+
});
|
|
669
|
+
if (spec.init) {
|
|
670
|
+
ws.send(new TextEncoder().encode(JSON.stringify(init)));
|
|
671
|
+
}
|
|
672
|
+
open.wake(true);
|
|
673
|
+
},
|
|
674
|
+
onclose: (event) => {
|
|
675
|
+
import_log3.log.info("closed", {
|
|
676
|
+
url,
|
|
677
|
+
code: event.code
|
|
678
|
+
}, {
|
|
679
|
+
F: __dxlog_file3,
|
|
680
|
+
L: 269,
|
|
681
|
+
S: this,
|
|
682
|
+
C: (f, a) => f(...a)
|
|
683
|
+
});
|
|
684
|
+
if (event.code === 1006) {
|
|
685
|
+
setTimeout(async () => {
|
|
686
|
+
import_log3.log.info(`reconnecting in ${options.retryDelay}s...`, {
|
|
687
|
+
url
|
|
688
|
+
}, {
|
|
689
|
+
F: __dxlog_file3,
|
|
690
|
+
L: 274,
|
|
691
|
+
S: this,
|
|
692
|
+
C: (f, a) => f(...a)
|
|
693
|
+
});
|
|
694
|
+
await this._createWebsocket(ctx, space, def, trigger, options);
|
|
695
|
+
}, options.retryDelay * 1e3);
|
|
696
|
+
}
|
|
697
|
+
open.wake(false);
|
|
698
|
+
},
|
|
699
|
+
onerror: (event) => {
|
|
700
|
+
import_log3.log.catch(event.error, {
|
|
701
|
+
url
|
|
702
|
+
}, {
|
|
703
|
+
F: __dxlog_file3,
|
|
704
|
+
L: 283,
|
|
705
|
+
S: this,
|
|
706
|
+
C: (f, a) => f(...a)
|
|
707
|
+
});
|
|
708
|
+
},
|
|
709
|
+
onmessage: async (event) => {
|
|
710
|
+
try {
|
|
711
|
+
const data = JSON.parse(new TextDecoder().decode(event.data));
|
|
712
|
+
await this._execFunction(def, trigger, {
|
|
713
|
+
spaceKey: space.key,
|
|
714
|
+
data
|
|
715
|
+
});
|
|
716
|
+
} catch (err) {
|
|
717
|
+
import_log3.log.catch(err, {
|
|
718
|
+
url
|
|
719
|
+
}, {
|
|
720
|
+
F: __dxlog_file3,
|
|
721
|
+
L: 291,
|
|
722
|
+
S: this,
|
|
723
|
+
C: (f, a) => f(...a)
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
const isOpen = await open.wait();
|
|
729
|
+
if (isOpen) {
|
|
730
|
+
break;
|
|
731
|
+
} else {
|
|
732
|
+
const wait = Math.pow(attempt, 2) * options.retryDelay;
|
|
733
|
+
if (attempt < options.maxAttempts) {
|
|
734
|
+
import_log3.log.warn(`failed to connect; trying again in ${wait}s`, {
|
|
735
|
+
attempt
|
|
736
|
+
}, {
|
|
737
|
+
F: __dxlog_file3,
|
|
738
|
+
L: 302,
|
|
739
|
+
S: this,
|
|
740
|
+
C: (f, a) => f(...a)
|
|
741
|
+
});
|
|
742
|
+
await (0, import_async2.sleep)(wait * 1e3);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
ctx.onDispose(() => {
|
|
747
|
+
ws?.close();
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* ECHO subscription.
|
|
752
|
+
*/
|
|
753
|
+
async _createSubscription(ctx, space, def, trigger) {
|
|
413
754
|
import_log3.log.info("subscription", {
|
|
414
755
|
space: space.key,
|
|
415
|
-
|
|
756
|
+
trigger
|
|
416
757
|
}, {
|
|
417
758
|
F: __dxlog_file3,
|
|
418
|
-
L:
|
|
759
|
+
L: 317,
|
|
419
760
|
S: this,
|
|
420
761
|
C: (f, a) => f(...a)
|
|
421
762
|
});
|
|
763
|
+
const spec = trigger.subscription;
|
|
422
764
|
const objectIds = /* @__PURE__ */ new Set();
|
|
423
765
|
const task = new import_async2.DeferredTask(ctx, async () => {
|
|
424
|
-
await this._execFunction(def, {
|
|
425
|
-
|
|
766
|
+
await this._execFunction(def, trigger, {
|
|
767
|
+
spaceKey: space.key,
|
|
426
768
|
objects: Array.from(objectIds)
|
|
427
769
|
});
|
|
428
770
|
});
|
|
@@ -433,7 +775,7 @@ var Scheduler = class {
|
|
|
433
775
|
updated: updated.length
|
|
434
776
|
}, {
|
|
435
777
|
F: __dxlog_file3,
|
|
436
|
-
L:
|
|
778
|
+
L: 329,
|
|
437
779
|
S: this,
|
|
438
780
|
C: (f, a) => f(...a)
|
|
439
781
|
});
|
|
@@ -446,17 +788,15 @@ var Scheduler = class {
|
|
|
446
788
|
task.schedule();
|
|
447
789
|
});
|
|
448
790
|
subscriptions.push(() => subscription.unsubscribe());
|
|
449
|
-
const {
|
|
791
|
+
const { filter, options: { deep, delay } = {} } = spec;
|
|
450
792
|
const update = ({ objects }) => {
|
|
451
793
|
subscription.update(objects);
|
|
452
794
|
if (deep) {
|
|
453
795
|
import_log3.log.info("update", {
|
|
454
|
-
type,
|
|
455
|
-
deep,
|
|
456
796
|
objects: objects.length
|
|
457
797
|
}, {
|
|
458
798
|
F: __dxlog_file3,
|
|
459
|
-
L:
|
|
799
|
+
L: 349,
|
|
460
800
|
S: this,
|
|
461
801
|
C: (f, a) => f(...a)
|
|
462
802
|
});
|
|
@@ -470,61 +810,66 @@ var Scheduler = class {
|
|
|
470
810
|
}
|
|
471
811
|
}
|
|
472
812
|
};
|
|
473
|
-
const query = space.db.query(import_echo.Filter.typename(type, props));
|
|
474
|
-
subscriptions.push(query.subscribe(delay ? (0, import_async2.debounce)(update, delay
|
|
813
|
+
const query = space.db.query(import_echo.Filter.or(filter.map(({ type, props }) => import_echo.Filter.typename(type, props))));
|
|
814
|
+
subscriptions.push(query.subscribe(delay ? (0, import_async2.debounce)(update, delay) : update));
|
|
475
815
|
ctx.onDispose(() => {
|
|
476
816
|
subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
477
817
|
});
|
|
478
818
|
}
|
|
479
|
-
async _execFunction(def, data) {
|
|
480
|
-
try {
|
|
481
|
-
(0, import_log3.log)("request", {
|
|
482
|
-
function: def.id
|
|
483
|
-
}, {
|
|
484
|
-
F: __dxlog_file3,
|
|
485
|
-
L: 183,
|
|
486
|
-
S: this,
|
|
487
|
-
C: (f, a) => f(...a)
|
|
488
|
-
});
|
|
489
|
-
const { endpoint, callback } = this._options;
|
|
490
|
-
let status = 0;
|
|
491
|
-
if (endpoint) {
|
|
492
|
-
const response = await fetch(`${this._options.endpoint}/${def.name}`, {
|
|
493
|
-
method: "POST",
|
|
494
|
-
headers: {
|
|
495
|
-
"Content-Type": "application/json"
|
|
496
|
-
},
|
|
497
|
-
body: JSON.stringify(data)
|
|
498
|
-
});
|
|
499
|
-
status = response.status;
|
|
500
|
-
} else if (callback) {
|
|
501
|
-
status = await callback(data);
|
|
502
|
-
}
|
|
503
|
-
(0, import_log3.log)("result", {
|
|
504
|
-
function: def.id,
|
|
505
|
-
result: status
|
|
506
|
-
}, {
|
|
507
|
-
F: __dxlog_file3,
|
|
508
|
-
L: 202,
|
|
509
|
-
S: this,
|
|
510
|
-
C: (f, a) => f(...a)
|
|
511
|
-
});
|
|
512
|
-
} catch (err) {
|
|
513
|
-
import_log3.log.error("error", {
|
|
514
|
-
function: def.id,
|
|
515
|
-
error: err.message
|
|
516
|
-
}, {
|
|
517
|
-
F: __dxlog_file3,
|
|
518
|
-
L: 204,
|
|
519
|
-
S: this,
|
|
520
|
-
C: (f, a) => f(...a)
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
819
|
};
|
|
820
|
+
var TimerTriggerSchema = S.struct({
|
|
821
|
+
cron: S.string
|
|
822
|
+
});
|
|
823
|
+
var WebhookTriggerSchema = S.mutable(S.struct({
|
|
824
|
+
method: S.string,
|
|
825
|
+
// Assigned port.
|
|
826
|
+
port: S.optional(S.number)
|
|
827
|
+
}));
|
|
828
|
+
var WebsocketTriggerSchema = S.struct({
|
|
829
|
+
url: S.string,
|
|
830
|
+
init: S.optional(S.record(S.string, S.any))
|
|
831
|
+
});
|
|
832
|
+
var SubscriptionTriggerSchema = S.struct({
|
|
833
|
+
spaceKey: S.optional(S.string),
|
|
834
|
+
// TODO(burdon): Define query DSL.
|
|
835
|
+
filter: S.array(S.struct({
|
|
836
|
+
type: S.string,
|
|
837
|
+
props: S.optional(S.record(S.string, S.any))
|
|
838
|
+
})),
|
|
839
|
+
options: S.optional(S.struct({
|
|
840
|
+
// Watch changes to object (not just creation).
|
|
841
|
+
deep: S.optional(S.boolean),
|
|
842
|
+
// Debounce changes (delay in ms).
|
|
843
|
+
delay: S.optional(S.number)
|
|
844
|
+
}))
|
|
845
|
+
});
|
|
846
|
+
var FunctionTriggerSchema = S.struct({
|
|
847
|
+
function: S.string.pipe(S.description("Function ID/URI.")),
|
|
848
|
+
// Context passed to function.
|
|
849
|
+
meta: S.optional(S.record(S.string, S.any)),
|
|
850
|
+
// Triggers.
|
|
851
|
+
timer: S.optional(TimerTriggerSchema),
|
|
852
|
+
webhook: S.optional(WebhookTriggerSchema),
|
|
853
|
+
websocket: S.optional(WebsocketTriggerSchema),
|
|
854
|
+
subscription: S.optional(SubscriptionTriggerSchema)
|
|
855
|
+
});
|
|
856
|
+
var FunctionDefSchema = S.struct({
|
|
857
|
+
id: S.string,
|
|
858
|
+
// name: S.string,
|
|
859
|
+
description: S.optional(S.string),
|
|
860
|
+
// TODO(burdon): Rename route?
|
|
861
|
+
path: S.string,
|
|
862
|
+
// TODO(burdon): NPM/GitHub/Docker/CF URL?
|
|
863
|
+
handler: S.string
|
|
864
|
+
});
|
|
865
|
+
var FunctionManifestSchema = S.struct({
|
|
866
|
+
functions: S.mutable(S.array(FunctionDefSchema)),
|
|
867
|
+
triggers: S.optional(S.mutable(S.array(FunctionTriggerSchema)))
|
|
868
|
+
});
|
|
525
869
|
// Annotate the CommonJS export names for ESM import in node:
|
|
526
870
|
0 && (module.exports = {
|
|
527
871
|
DevServer,
|
|
872
|
+
FunctionManifestSchema,
|
|
528
873
|
Scheduler,
|
|
529
874
|
subscriptionHandler
|
|
530
875
|
});
|