@dxos/functions 0.3.8-main.152ec6f → 0.3.8-main.1b6c849
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 +159 -141
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +159 -141
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/handler.d.ts +2 -6
- package/dist/types/src/handler.d.ts.map +1 -1
- package/dist/types/src/manifest.d.ts +3 -2
- package/dist/types/src/manifest.d.ts.map +1 -1
- package/dist/types/src/runtime/dev-server.d.ts +10 -0
- package/dist/types/src/runtime/dev-server.d.ts.map +1 -1
- package/dist/types/src/runtime/index.d.ts +1 -1
- package/dist/types/src/runtime/index.d.ts.map +1 -1
- package/dist/types/src/runtime/{trigger-manager.d.ts → scheduler.d.ts} +9 -6
- package/dist/types/src/runtime/scheduler.d.ts.map +1 -0
- package/package.json +10 -9
- package/src/handler.ts +7 -8
- package/src/manifest.ts +7 -3
- package/src/runtime/dev-server.ts +69 -45
- package/src/runtime/index.ts +1 -1
- package/src/runtime/scheduler.ts +145 -0
- package/dist/types/src/runtime/trigger-manager.d.ts.map +0 -1
- package/src/runtime/trigger-manager.ts +0 -138
|
@@ -21,11 +21,12 @@ var DevServer = class {
|
|
|
21
21
|
this._client = _client;
|
|
22
22
|
this._options = _options;
|
|
23
23
|
this._handlers = {};
|
|
24
|
+
this._seq = 0;
|
|
24
25
|
}
|
|
25
26
|
get endpoint() {
|
|
26
27
|
invariant(this._port, void 0, {
|
|
27
28
|
F: __dxlog_file,
|
|
28
|
-
L:
|
|
29
|
+
L: 45,
|
|
29
30
|
S: this,
|
|
30
31
|
A: [
|
|
31
32
|
"this._port",
|
|
@@ -42,29 +43,12 @@ var DevServer = class {
|
|
|
42
43
|
}
|
|
43
44
|
async initialize() {
|
|
44
45
|
for (const def of this._options.manifest.functions) {
|
|
45
|
-
const { id, path, handler: dir } = def;
|
|
46
46
|
try {
|
|
47
|
-
|
|
48
|
-
const handler = module.default;
|
|
49
|
-
if (typeof handler !== "function") {
|
|
50
|
-
throw new Error(`Handler must export default function: ${id}`);
|
|
51
|
-
}
|
|
52
|
-
if (this._handlers[path]) {
|
|
53
|
-
log.warn(`Function already registered: ${id}`, void 0, {
|
|
54
|
-
F: __dxlog_file,
|
|
55
|
-
L: 67,
|
|
56
|
-
S: this,
|
|
57
|
-
C: (f, a) => f(...a)
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
this._handlers[path] = {
|
|
61
|
-
def,
|
|
62
|
-
handler
|
|
63
|
-
};
|
|
47
|
+
await this._load(def);
|
|
64
48
|
} catch (err) {
|
|
65
49
|
log.error("parsing function (check manifest)", err, {
|
|
66
50
|
F: __dxlog_file,
|
|
67
|
-
L:
|
|
51
|
+
L: 62,
|
|
68
52
|
S: this,
|
|
69
53
|
C: (f, a) => f(...a)
|
|
70
54
|
});
|
|
@@ -76,46 +60,23 @@ var DevServer = class {
|
|
|
76
60
|
app.use(express.json());
|
|
77
61
|
app.post("/:name", async (req, res) => {
|
|
78
62
|
const { name } = req.params;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
},
|
|
84
|
-
succeed: (result = {}) => {
|
|
85
|
-
res.end(JSON.stringify(result));
|
|
86
|
-
return response;
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
const context = {
|
|
90
|
-
client: this._client,
|
|
91
|
-
status: response.status.bind(response)
|
|
92
|
-
};
|
|
93
|
-
void (async () => {
|
|
94
|
-
try {
|
|
95
|
-
log(`invoking: ${name}`, void 0, {
|
|
96
|
-
F: __dxlog_file,
|
|
97
|
-
L: 103,
|
|
98
|
-
S: this,
|
|
99
|
-
C: (f, a) => f(...a)
|
|
100
|
-
});
|
|
101
|
-
const { handler } = this._handlers[name];
|
|
102
|
-
const response2 = await handler({
|
|
103
|
-
context,
|
|
104
|
-
event: req.body
|
|
105
|
-
});
|
|
106
|
-
log("done", {
|
|
107
|
-
response: response2
|
|
108
|
-
}, {
|
|
109
|
-
F: __dxlog_file,
|
|
110
|
-
L: 106,
|
|
111
|
-
S: this,
|
|
112
|
-
C: (f, a) => f(...a)
|
|
113
|
-
});
|
|
114
|
-
} catch (err) {
|
|
115
|
-
res.statusCode = 500;
|
|
116
|
-
res.end(err.message);
|
|
63
|
+
try {
|
|
64
|
+
if (this._options.reload) {
|
|
65
|
+
const { def } = this._handlers[name];
|
|
66
|
+
await this._load(def, true);
|
|
117
67
|
}
|
|
118
|
-
|
|
68
|
+
res.statusCode = await this._invoke(name, req.body);
|
|
69
|
+
res.end();
|
|
70
|
+
} catch (err) {
|
|
71
|
+
log.error(err, void 0, {
|
|
72
|
+
F: __dxlog_file,
|
|
73
|
+
L: 82,
|
|
74
|
+
S: this,
|
|
75
|
+
C: (f, a) => f(...a)
|
|
76
|
+
});
|
|
77
|
+
res.statusCode = 500;
|
|
78
|
+
res.end();
|
|
79
|
+
}
|
|
119
80
|
});
|
|
120
81
|
this._port = await getPort({
|
|
121
82
|
port: 7200,
|
|
@@ -128,8 +89,8 @@ var DevServer = class {
|
|
|
128
89
|
try {
|
|
129
90
|
const { registrationId, endpoint } = await this._client.services.services.FunctionRegistryService.register({
|
|
130
91
|
endpoint: this.endpoint,
|
|
131
|
-
functions: this.functions.map(({ def: {
|
|
132
|
-
name
|
|
92
|
+
functions: this.functions.map(({ def: { name } }) => ({
|
|
93
|
+
name
|
|
133
94
|
}))
|
|
134
95
|
});
|
|
135
96
|
log.info("registered", {
|
|
@@ -137,7 +98,7 @@ var DevServer = class {
|
|
|
137
98
|
endpoint
|
|
138
99
|
}, {
|
|
139
100
|
F: __dxlog_file,
|
|
140
|
-
L:
|
|
101
|
+
L: 98,
|
|
141
102
|
S: this,
|
|
142
103
|
C: (f, a) => f(...a)
|
|
143
104
|
});
|
|
@@ -159,7 +120,7 @@ var DevServer = class {
|
|
|
159
120
|
registrationId: this._registrationId
|
|
160
121
|
}, {
|
|
161
122
|
F: __dxlog_file,
|
|
162
|
-
L:
|
|
123
|
+
L: 115,
|
|
163
124
|
S: this,
|
|
164
125
|
C: (f, a) => f(...a)
|
|
165
126
|
});
|
|
@@ -172,23 +133,93 @@ var DevServer = class {
|
|
|
172
133
|
this._port = void 0;
|
|
173
134
|
this._server = void 0;
|
|
174
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Load function.
|
|
138
|
+
*/
|
|
139
|
+
async _load(def, flush = false) {
|
|
140
|
+
const { id, name, handler } = def;
|
|
141
|
+
const path = join(this._options.directory, handler);
|
|
142
|
+
log.info("loading", {
|
|
143
|
+
id
|
|
144
|
+
}, {
|
|
145
|
+
F: __dxlog_file,
|
|
146
|
+
L: 134,
|
|
147
|
+
S: this,
|
|
148
|
+
C: (f, a) => f(...a)
|
|
149
|
+
});
|
|
150
|
+
if (flush) {
|
|
151
|
+
Object.keys(__require.cache).filter((key) => key.startsWith(path)).forEach((key) => delete __require.cache[key]);
|
|
152
|
+
}
|
|
153
|
+
const module = __require(path);
|
|
154
|
+
if (typeof module.default !== "function") {
|
|
155
|
+
throw new Error(`Handler must export default function: ${id}`);
|
|
156
|
+
}
|
|
157
|
+
this._handlers[name] = {
|
|
158
|
+
def,
|
|
159
|
+
handler: module.default
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Invoke function handler.
|
|
164
|
+
*/
|
|
165
|
+
async _invoke(name, event) {
|
|
166
|
+
const seq = ++this._seq;
|
|
167
|
+
const now = Date.now();
|
|
168
|
+
log.info("req", {
|
|
169
|
+
seq,
|
|
170
|
+
name
|
|
171
|
+
}, {
|
|
172
|
+
F: __dxlog_file,
|
|
173
|
+
L: 159,
|
|
174
|
+
S: this,
|
|
175
|
+
C: (f, a) => f(...a)
|
|
176
|
+
});
|
|
177
|
+
const { handler } = this._handlers[name];
|
|
178
|
+
const context = {
|
|
179
|
+
client: this._client
|
|
180
|
+
};
|
|
181
|
+
let statusCode = 200;
|
|
182
|
+
const response = {
|
|
183
|
+
status: (code) => {
|
|
184
|
+
statusCode = code;
|
|
185
|
+
return response;
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
await handler({
|
|
189
|
+
context,
|
|
190
|
+
event,
|
|
191
|
+
response
|
|
192
|
+
});
|
|
193
|
+
log.info("res", {
|
|
194
|
+
seq,
|
|
195
|
+
name,
|
|
196
|
+
statusCode,
|
|
197
|
+
duration: Date.now() - now
|
|
198
|
+
}, {
|
|
199
|
+
F: __dxlog_file,
|
|
200
|
+
L: 175,
|
|
201
|
+
S: this,
|
|
202
|
+
C: (f, a) => f(...a)
|
|
203
|
+
});
|
|
204
|
+
return statusCode;
|
|
205
|
+
}
|
|
175
206
|
};
|
|
176
207
|
|
|
177
|
-
// packages/core/functions/src/runtime/
|
|
208
|
+
// packages/core/functions/src/runtime/scheduler.ts
|
|
209
|
+
import { CronJob } from "cron";
|
|
178
210
|
import { DeferredTask } from "@dxos/async";
|
|
179
211
|
import { Context } from "@dxos/context";
|
|
180
212
|
import { Filter, createSubscription } from "@dxos/echo-schema";
|
|
181
213
|
import { invariant as invariant2 } from "@dxos/invariant";
|
|
182
214
|
import { log as log2 } from "@dxos/log";
|
|
183
215
|
import { ComplexMap } from "@dxos/util";
|
|
184
|
-
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/
|
|
185
|
-
var
|
|
186
|
-
constructor(_client, _manifest,
|
|
216
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/scheduler.ts";
|
|
217
|
+
var Scheduler = class {
|
|
218
|
+
constructor(_client, _manifest, _options) {
|
|
187
219
|
this._client = _client;
|
|
188
220
|
this._manifest = _manifest;
|
|
189
|
-
this.
|
|
190
|
-
this._mounts = new ComplexMap(({
|
|
191
|
-
this._queries = /* @__PURE__ */ new Set();
|
|
221
|
+
this._options = _options;
|
|
222
|
+
this._mounts = new ComplexMap(({ id, spaceKey }) => `${spaceKey.toHex()}:${id}`);
|
|
192
223
|
}
|
|
193
224
|
async start() {
|
|
194
225
|
this._client.spaces.subscribe(async (spaces) => {
|
|
@@ -201,22 +232,22 @@ var TriggerManager = class {
|
|
|
201
232
|
});
|
|
202
233
|
}
|
|
203
234
|
async stop() {
|
|
204
|
-
for (const {
|
|
205
|
-
await this.unmount(
|
|
235
|
+
for (const { id, spaceKey } of this._mounts.keys()) {
|
|
236
|
+
await this.unmount(id, spaceKey);
|
|
206
237
|
}
|
|
207
238
|
}
|
|
208
239
|
async mount(ctx, space, trigger) {
|
|
209
240
|
const key = {
|
|
210
|
-
|
|
241
|
+
id: trigger.function,
|
|
211
242
|
spaceKey: space.key
|
|
212
243
|
};
|
|
213
|
-
const
|
|
214
|
-
invariant2(
|
|
244
|
+
const def = this._manifest.functions.find((config) => config.id === trigger.function);
|
|
245
|
+
invariant2(def, `Function not found: ${trigger.function}`, {
|
|
215
246
|
F: __dxlog_file2,
|
|
216
|
-
L:
|
|
247
|
+
L: 59,
|
|
217
248
|
S: this,
|
|
218
249
|
A: [
|
|
219
|
-
"
|
|
250
|
+
"def",
|
|
220
251
|
"`Function not found: ${trigger.function}`"
|
|
221
252
|
]
|
|
222
253
|
});
|
|
@@ -231,57 +262,55 @@ var TriggerManager = class {
|
|
|
231
262
|
trigger
|
|
232
263
|
}, {
|
|
233
264
|
F: __dxlog_file2,
|
|
234
|
-
L:
|
|
265
|
+
L: 64,
|
|
235
266
|
S: this,
|
|
236
267
|
C: (f, a) => f(...a)
|
|
237
268
|
});
|
|
238
269
|
if (ctx.disposed) {
|
|
239
270
|
return;
|
|
240
271
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
272
|
+
if (trigger.schedule) {
|
|
273
|
+
const task = new DeferredTask(ctx, async () => {
|
|
274
|
+
await this.execFunction(def, {
|
|
275
|
+
space: space.key
|
|
276
|
+
});
|
|
246
277
|
});
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
space: space.key,
|
|
259
|
-
objects: objectIds.size,
|
|
260
|
-
count
|
|
261
|
-
}, {
|
|
262
|
-
F: __dxlog_file2,
|
|
263
|
-
L: 84,
|
|
264
|
-
S: this,
|
|
265
|
-
C: (f, a) => f(...a)
|
|
278
|
+
const job = new CronJob(trigger.schedule, () => task.schedule());
|
|
279
|
+
job.start();
|
|
280
|
+
ctx.onDispose(() => job.stop());
|
|
281
|
+
}
|
|
282
|
+
if (trigger.subscription) {
|
|
283
|
+
const objectIds = /* @__PURE__ */ new Set();
|
|
284
|
+
const task = new DeferredTask(ctx, async () => {
|
|
285
|
+
await this.execFunction(def, {
|
|
286
|
+
space: space.key,
|
|
287
|
+
objects: Array.from(objectIds)
|
|
288
|
+
});
|
|
266
289
|
});
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
subscription
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
290
|
+
const subscription = createSubscription(({ added, updated }) => {
|
|
291
|
+
for (const object of added) {
|
|
292
|
+
objectIds.add(object.id);
|
|
293
|
+
}
|
|
294
|
+
for (const object of updated) {
|
|
295
|
+
objectIds.add(object.id);
|
|
296
|
+
}
|
|
297
|
+
task.schedule();
|
|
298
|
+
});
|
|
299
|
+
const { type, props } = trigger.subscription;
|
|
300
|
+
const query = space.db.query(Filter.typename(type, props));
|
|
301
|
+
const unsubscribe = query.subscribe(({ objects }) => {
|
|
302
|
+
subscription.update(objects);
|
|
303
|
+
}, true);
|
|
304
|
+
ctx.onDispose(() => {
|
|
305
|
+
subscription.unsubscribe();
|
|
306
|
+
unsubscribe();
|
|
307
|
+
});
|
|
308
|
+
}
|
|
280
309
|
}
|
|
281
310
|
}
|
|
282
|
-
async unmount(
|
|
311
|
+
async unmount(id, spaceKey) {
|
|
283
312
|
const key = {
|
|
284
|
-
|
|
313
|
+
id,
|
|
285
314
|
spaceKey
|
|
286
315
|
};
|
|
287
316
|
const { ctx } = this._mounts.get(key) ?? {};
|
|
@@ -290,50 +319,39 @@ var TriggerManager = class {
|
|
|
290
319
|
await ctx.dispose();
|
|
291
320
|
}
|
|
292
321
|
}
|
|
293
|
-
async execFunction(
|
|
294
|
-
const { endpoint } = options;
|
|
295
|
-
invariant2(endpoint, "Missing endpoint", {
|
|
296
|
-
F: __dxlog_file2,
|
|
297
|
-
L: 120,
|
|
298
|
-
S: this,
|
|
299
|
-
A: [
|
|
300
|
-
"endpoint",
|
|
301
|
-
"'Missing endpoint'"
|
|
302
|
-
]
|
|
303
|
-
});
|
|
322
|
+
async execFunction(def, data) {
|
|
304
323
|
try {
|
|
305
|
-
log2("
|
|
306
|
-
function:
|
|
324
|
+
log2("request", {
|
|
325
|
+
function: def.id
|
|
307
326
|
}, {
|
|
308
327
|
F: __dxlog_file2,
|
|
309
|
-
L:
|
|
328
|
+
L: 130,
|
|
310
329
|
S: this,
|
|
311
330
|
C: (f, a) => f(...a)
|
|
312
331
|
});
|
|
313
|
-
const
|
|
314
|
-
const res = await fetch(url, {
|
|
332
|
+
const response = await fetch(`${this._options.endpoint}/${def.name}`, {
|
|
315
333
|
method: "POST",
|
|
316
|
-
body: JSON.stringify(data),
|
|
317
334
|
headers: {
|
|
318
335
|
"Content-Type": "application/json"
|
|
319
|
-
}
|
|
336
|
+
},
|
|
337
|
+
body: JSON.stringify(data)
|
|
320
338
|
});
|
|
321
339
|
log2("result", {
|
|
322
|
-
function:
|
|
323
|
-
result:
|
|
340
|
+
function: def.id,
|
|
341
|
+
result: response.status
|
|
324
342
|
}, {
|
|
325
343
|
F: __dxlog_file2,
|
|
326
|
-
L:
|
|
344
|
+
L: 140,
|
|
327
345
|
S: this,
|
|
328
346
|
C: (f, a) => f(...a)
|
|
329
347
|
});
|
|
330
348
|
} catch (err) {
|
|
331
349
|
log2.error("error", {
|
|
332
|
-
function:
|
|
350
|
+
function: def.id,
|
|
333
351
|
error: err.message
|
|
334
352
|
}, {
|
|
335
353
|
F: __dxlog_file2,
|
|
336
|
-
L:
|
|
354
|
+
L: 142,
|
|
337
355
|
S: this,
|
|
338
356
|
C: (f, a) => f(...a)
|
|
339
357
|
});
|
|
@@ -342,6 +360,6 @@ var TriggerManager = class {
|
|
|
342
360
|
};
|
|
343
361
|
export {
|
|
344
362
|
DevServer,
|
|
345
|
-
|
|
363
|
+
Scheduler
|
|
346
364
|
};
|
|
347
365
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../../src/runtime/dev-server.ts", "../../../src/runtime/
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport express from 'express';\nimport { getPort } from 'get-port-please';\nimport type http from 'http';\nimport { join } from 'node:path';\n\nimport { Trigger } from '@dxos/async';\nimport { type Client } from '@dxos/client';\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\n\nimport { type FunctionContext, type FunctionHandler, type Response } from '../handler';\nimport { type FunctionDef, type FunctionManifest } from '../manifest';\n\nexport type DevServerOptions = {\n port?: number;\n directory: string;\n manifest: FunctionManifest;\n};\n\n/**\n * Functions dev server provides a local HTTP server for testing functions.\n */\n//
|
|
5
|
-
"mappings": ";;;;;;;;;;AAIA,OAAOA,aAAa;AACpB,SAASC,eAAe;AAExB,SAASC,YAAY;AAErB,SAASC,eAAe;AAExB,SAASC,iBAAiB;AAC1B,SAASC,WAAW;;AAeb,IAAMC,YAAN,MAAMA;;
|
|
6
|
-
"names": ["express", "getPort", "join", "Trigger", "invariant", "log", "DevServer", "constructor", "_client", "_options", "_handlers", "endpoint", "invariant", "_port", "proxy", "_proxy", "functions", "Object", "values", "initialize", "def", "manifest", "
|
|
3
|
+
"sources": ["../../../src/runtime/dev-server.ts", "../../../src/runtime/scheduler.ts"],
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport express from 'express';\nimport { getPort } from 'get-port-please';\nimport type http from 'http';\nimport { join } from 'node:path';\n\nimport { Trigger } from '@dxos/async';\nimport { type Client } from '@dxos/client';\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\n\nimport { type FunctionContext, type FunctionHandler, type Response } from '../handler';\nimport { type FunctionDef, type FunctionManifest } from '../manifest';\n\nexport type DevServerOptions = {\n port?: number;\n directory: string;\n manifest: FunctionManifest;\n reload?: boolean;\n};\n\n/**\n * Functions dev server provides a local HTTP server for testing functions.\n */\nexport class DevServer {\n // Function handlers indexed by name (URL path).\n private readonly _handlers: Record<string, { def: FunctionDef; handler: FunctionHandler<any> }> = {};\n\n private _server?: http.Server;\n private _port?: number;\n private _registrationId?: string;\n private _proxy?: string;\n private _seq = 0;\n\n // prettier-ignore\n constructor(\n private readonly _client: Client,\n private readonly _options: DevServerOptions,\n ) {}\n\n get endpoint() {\n invariant(this._port);\n return `http://localhost:${this._port}`;\n }\n\n get proxy() {\n return this._proxy;\n }\n\n get functions() {\n return Object.values(this._handlers);\n }\n\n async initialize() {\n for (const def of this._options.manifest.functions) {\n try {\n await this._load(def);\n } catch (err) {\n log.error('parsing function (check manifest)', err);\n }\n }\n }\n\n async start() {\n const app = express();\n app.use(express.json());\n\n app.post('/:name', async (req, res) => {\n const { name } = req.params;\n try {\n if (this._options.reload) {\n const { def } = this._handlers[name];\n await this._load(def, true);\n }\n\n res.statusCode = await this._invoke(name, req.body);\n res.end();\n } catch (err: any) {\n log.error(err);\n res.statusCode = 500;\n res.end();\n }\n });\n\n this._port = await getPort({ port: 7200, portRange: [7200, 7299] });\n this._server = app.listen(this._port);\n\n try {\n // Register functions.\n const { registrationId, endpoint } = await this._client.services.services.FunctionRegistryService!.register({\n endpoint: this.endpoint,\n functions: this.functions.map(({ def: { name } }) => ({ name })),\n });\n\n log.info('registered', { registrationId, endpoint });\n this._registrationId = registrationId;\n this._proxy = endpoint;\n } catch (err: any) {\n await this.stop();\n throw new Error('FunctionRegistryService not available (check plugin is configured).');\n }\n }\n\n async stop() {\n const trigger = new Trigger();\n this._server?.close(async () => {\n if (this._registrationId) {\n await this._client.services.services.FunctionRegistryService!.unregister({\n registrationId: this._registrationId,\n });\n\n log.info('unregistered', { registrationId: this._registrationId });\n this._registrationId = undefined;\n this._proxy = undefined;\n }\n\n trigger.wake();\n });\n\n await trigger.wait();\n this._port = undefined;\n this._server = undefined;\n }\n\n /**\n * Load function.\n */\n private async _load(def: FunctionDef, flush = false) {\n const { id, name, handler } = def;\n const path = join(this._options.directory, handler);\n log.info('loading', { id });\n\n // Remove from cache.\n if (flush) {\n Object.keys(require.cache)\n .filter((key) => key.startsWith(path))\n .forEach((key) => delete require.cache[key]);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const module = require(path);\n if (typeof module.default !== 'function') {\n throw new Error(`Handler must export default function: ${id}`);\n }\n\n this._handlers[name] = { def, handler: module.default };\n }\n\n /**\n * Invoke function handler.\n */\n private async _invoke(name: string, event: any) {\n const seq = ++this._seq;\n const now = Date.now();\n\n log.info('req', { seq, name });\n const { handler } = this._handlers[name];\n\n const context: FunctionContext = {\n client: this._client,\n };\n\n let statusCode = 200;\n const response: Response = {\n status: (code: number) => {\n statusCode = code;\n return response;\n },\n };\n\n await handler({ context, event, response });\n log.info('res', { seq, name, statusCode, duration: Date.now() - now });\n\n return statusCode;\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { CronJob } from 'cron';\n\nimport { DeferredTask } from '@dxos/async';\nimport { type Client, type PublicKey } from '@dxos/client';\nimport { type Space } from '@dxos/client/echo';\nimport { Context } from '@dxos/context';\nimport { Filter, createSubscription } from '@dxos/echo-schema';\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\nimport { ComplexMap } from '@dxos/util';\n\nimport { type FunctionDef, type FunctionManifest, type FunctionTrigger } from '../manifest';\n\ntype SchedulerOptions = {\n endpoint: string;\n};\n\n/**\n * Functions scheduler.\n */\n// TODO(burdon): Create tests.\nexport class Scheduler {\n // Map of mounted functions.\n private readonly _mounts = new ComplexMap<\n { id: string; spaceKey: PublicKey },\n { ctx: Context; trigger: FunctionTrigger }\n >(({ id, spaceKey }) => `${spaceKey.toHex()}:${id}`);\n\n constructor(\n private readonly _client: Client,\n private readonly _manifest: FunctionManifest,\n private readonly _options: SchedulerOptions,\n ) {}\n\n async start() {\n this._client.spaces.subscribe(async (spaces) => {\n for (const space of spaces) {\n await space.waitUntilReady();\n for (const trigger of this._manifest.triggers ?? []) {\n await this.mount(new Context(), space, trigger);\n }\n }\n });\n }\n\n async stop() {\n for (const { id, spaceKey } of this._mounts.keys()) {\n await this.unmount(id, spaceKey);\n }\n }\n\n private async mount(ctx: Context, space: Space, trigger: FunctionTrigger) {\n const key = { id: trigger.function, spaceKey: space.key };\n const def = this._manifest.functions.find((config) => config.id === trigger.function);\n invariant(def, `Function not found: ${trigger.function}`);\n\n const exists = this._mounts.get(key);\n if (!exists) {\n this._mounts.set(key, { ctx, trigger });\n log('mount', { space: space.key, trigger });\n if (ctx.disposed) {\n return;\n }\n\n // Cron schedule.\n if (trigger.schedule) {\n const task = new DeferredTask(ctx, async () => {\n await this.execFunction(def, {\n space: space.key,\n });\n });\n\n // TODO(burdon): Check greater than 30s min (use cron-parser).\n const job = new CronJob(trigger.schedule, () => task.schedule());\n\n job.start();\n ctx.onDispose(() => job.stop());\n }\n\n // ECHO subscription.\n if (trigger.subscription) {\n const objectIds = new Set<string>();\n const task = new DeferredTask(ctx, async () => {\n await this.execFunction(def, {\n space: space.key,\n objects: Array.from(objectIds),\n });\n });\n\n const subscription = createSubscription(({ added, updated }) => {\n for (const object of added) {\n objectIds.add(object.id);\n }\n for (const object of updated) {\n objectIds.add(object.id);\n }\n\n task.schedule();\n });\n\n const { type, props } = trigger.subscription;\n const query = space.db.query(Filter.typename(type, props));\n const unsubscribe = query.subscribe(({ objects }) => {\n subscription.update(objects);\n }, true);\n\n ctx.onDispose(() => {\n subscription.unsubscribe();\n unsubscribe();\n });\n }\n }\n }\n\n private async unmount(id: string, spaceKey: PublicKey) {\n const key = { id, spaceKey };\n const { ctx } = this._mounts.get(key) ?? {};\n if (ctx) {\n this._mounts.delete(key);\n await ctx.dispose();\n }\n }\n\n private async execFunction(def: FunctionDef, data: any) {\n try {\n log('request', { function: def.id });\n const response = await fetch(`${this._options.endpoint}/${def.name}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(data),\n });\n\n // const result = await response.json();\n log('result', { function: def.id, result: response.status });\n } catch (err: any) {\n log.error('error', { function: def.id, error: err.message });\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["express", "getPort", "join", "Trigger", "invariant", "log", "DevServer", "constructor", "_client", "_options", "_handlers", "_seq", "endpoint", "invariant", "_port", "proxy", "_proxy", "functions", "Object", "values", "initialize", "def", "manifest", "_load", "err", "log", "error", "start", "app", "express", "use", "json", "post", "req", "res", "name", "params", "reload", "statusCode", "_invoke", "body", "end", "getPort", "port", "portRange", "_server", "listen", "registrationId", "services", "FunctionRegistryService", "register", "map", "info", "_registrationId", "stop", "Error", "trigger", "Trigger", "close", "unregister", "undefined", "wake", "wait", "flush", "id", "handler", "path", "join", "directory", "keys", "require", "cache", "filter", "key", "startsWith", "forEach", "module", "default", "event", "seq", "now", "Date", "context", "client", "response", "status", "code", "duration", "CronJob", "DeferredTask", "Context", "Filter", "createSubscription", "invariant", "log", "ComplexMap", "Scheduler", "constructor", "_client", "_manifest", "_options", "_mounts", "id", "spaceKey", "toHex", "start", "spaces", "subscribe", "space", "waitUntilReady", "trigger", "triggers", "mount", "stop", "keys", "unmount", "ctx", "key", "function", "def", "functions", "find", "config", "exists", "get", "set", "disposed", "schedule", "task", "execFunction", "job", "onDispose", "subscription", "objectIds", "Set", "objects", "Array", "from", "added", "updated", "object", "add", "type", "props", "query", "db", "typename", "unsubscribe", "update", "delete", "dispose", "data", "response", "fetch", "endpoint", "name", "method", "headers", "body", "JSON", "stringify", "result", "status", "err", "error", "message"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"packages/core/functions/src/handler.ts":{"bytes":
|
|
1
|
+
{"inputs":{"packages/core/functions/src/handler.ts":{"bytes":1313,"imports":[],"format":"esm"},"packages/core/functions/src/manifest.ts":{"bytes":1735,"imports":[],"format":"esm"},"packages/core/functions/src/runtime/dev-server.ts":{"bytes":18553,"imports":[{"path":"express","kind":"import-statement","external":true},{"path":"get-port-please","kind":"import-statement","external":true},{"path":"@dxos/node-std/path","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"},"packages/core/functions/src/runtime/scheduler.ts":{"bytes":16641,"imports":[{"path":"cron","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"format":"esm"},"packages/core/functions/src/runtime/index.ts":{"bytes":566,"imports":[{"path":"packages/core/functions/src/runtime/dev-server.ts","kind":"import-statement","original":"./dev-server"},{"path":"packages/core/functions/src/runtime/scheduler.ts","kind":"import-statement","original":"./scheduler"}],"format":"esm"},"packages/core/functions/src/index.ts":{"bytes":637,"imports":[{"path":"packages/core/functions/src/handler.ts","kind":"import-statement","original":"./handler"},{"path":"packages/core/functions/src/manifest.ts","kind":"import-statement","original":"./manifest"},{"path":"packages/core/functions/src/runtime/index.ts","kind":"import-statement","original":"./runtime"}],"format":"esm"}},"outputs":{"packages/core/functions/dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":16609},"packages/core/functions/dist/lib/browser/index.mjs":{"imports":[{"path":"express","kind":"import-statement","external":true},{"path":"get-port-please","kind":"import-statement","external":true},{"path":"@dxos/node-std/path","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"cron","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"exports":["DevServer","Scheduler"],"entryPoint":"packages/core/functions/src/index.ts","inputs":{"packages/core/functions/src/index.ts":{"bytesInOutput":0},"packages/core/functions/src/runtime/dev-server.ts":{"bytesInOutput":4739},"packages/core/functions/src/runtime/index.ts":{"bytesInOutput":0},"packages/core/functions/src/runtime/scheduler.ts":{"bytesInOutput":4189}},"bytes":9509}}}
|