@dxos/functions 0.3.8-next.a5a9bfc → 0.3.8
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/README.md +2 -2
- package/dist/lib/browser/index.mjs +203 -164
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +218 -178
- 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 +14 -2
- 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 -7
- package/dist/types/src/runtime/scheduler.d.ts.map +1 -0
- package/package.json +11 -10
- package/src/handler.ts +7 -8
- package/src/manifest.ts +8 -4
- package/src/runtime/dev-server.ts +91 -58
- 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 -140
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ runtime:
|
|
|
50
50
|
Start functions in dev mode (from the related package):
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
|
-
dx function dev
|
|
53
|
+
dx function dev -r ts-node/register --verbose
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
> NOTE: `-r ts-node/register` configures native TypesScript support.
|
|
@@ -61,7 +61,7 @@ Install `nodemon` to support live reloading:
|
|
|
61
61
|
npm i -g nodemon
|
|
62
62
|
|
|
63
63
|
nodemon -w ./src -e ts --exec $(git rev-parse --show-toplevel)/packages/devtools/cli/bin/dev \
|
|
64
|
-
function dev
|
|
64
|
+
function dev -r ts-node/register --verbose
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
## Invoking functions
|
|
@@ -9,53 +9,46 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
9
9
|
|
|
10
10
|
// packages/core/functions/src/runtime/dev-server.ts
|
|
11
11
|
import express from "express";
|
|
12
|
+
import { getPort } from "get-port-please";
|
|
12
13
|
import { join } from "@dxos/node-std/path";
|
|
13
|
-
import { getPortPromise } from "portfinder";
|
|
14
14
|
import { Trigger } from "@dxos/async";
|
|
15
|
+
import { invariant } from "@dxos/invariant";
|
|
15
16
|
import { log } from "@dxos/log";
|
|
16
|
-
var __dxlog_file = "/home/
|
|
17
|
-
var DEFAULT_PORT = 7001;
|
|
17
|
+
var __dxlog_file = "/home/circleci/project/packages/core/functions/src/runtime/dev-server.ts";
|
|
18
18
|
var DevServer = class {
|
|
19
19
|
// prettier-ignore
|
|
20
20
|
constructor(_client, _options) {
|
|
21
21
|
this._client = _client;
|
|
22
22
|
this._options = _options;
|
|
23
23
|
this._handlers = {};
|
|
24
|
-
|
|
25
|
-
get port() {
|
|
26
|
-
return this._port;
|
|
24
|
+
this._seq = 0;
|
|
27
25
|
}
|
|
28
26
|
get endpoint() {
|
|
29
|
-
|
|
27
|
+
invariant(this._port, void 0, {
|
|
28
|
+
F: __dxlog_file,
|
|
29
|
+
L: 45,
|
|
30
|
+
S: this,
|
|
31
|
+
A: [
|
|
32
|
+
"this._port",
|
|
33
|
+
""
|
|
34
|
+
]
|
|
35
|
+
});
|
|
36
|
+
return `http://localhost:${this._port}`;
|
|
37
|
+
}
|
|
38
|
+
get proxy() {
|
|
39
|
+
return this._proxy;
|
|
30
40
|
}
|
|
31
41
|
get functions() {
|
|
32
42
|
return Object.values(this._handlers);
|
|
33
43
|
}
|
|
34
44
|
async initialize() {
|
|
35
45
|
for (const def of this._options.manifest.functions) {
|
|
36
|
-
const { id, endpoint, handler: path } = def;
|
|
37
46
|
try {
|
|
38
|
-
|
|
39
|
-
const handler = module.default;
|
|
40
|
-
if (typeof handler !== "function") {
|
|
41
|
-
throw new Error(`Handler must export default function: ${id}`);
|
|
42
|
-
}
|
|
43
|
-
if (this._handlers[endpoint]) {
|
|
44
|
-
log.warn(`Function already registered: ${id}`, void 0, {
|
|
45
|
-
F: __dxlog_file,
|
|
46
|
-
L: 64,
|
|
47
|
-
S: this,
|
|
48
|
-
C: (f, a) => f(...a)
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
this._handlers[endpoint] = {
|
|
52
|
-
def,
|
|
53
|
-
handler
|
|
54
|
-
};
|
|
47
|
+
await this._load(def);
|
|
55
48
|
} catch (err) {
|
|
56
49
|
log.error("parsing function (check manifest)", err, {
|
|
57
50
|
F: __dxlog_file,
|
|
58
|
-
L:
|
|
51
|
+
L: 62,
|
|
59
52
|
S: this,
|
|
60
53
|
C: (f, a) => f(...a)
|
|
61
54
|
});
|
|
@@ -65,66 +58,55 @@ var DevServer = class {
|
|
|
65
58
|
async start() {
|
|
66
59
|
const app = express();
|
|
67
60
|
app.use(express.json());
|
|
68
|
-
app.post("/:
|
|
69
|
-
const {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
},
|
|
75
|
-
succeed: (result = {}) => {
|
|
76
|
-
res.end(JSON.stringify(result));
|
|
77
|
-
return response;
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
const context = {
|
|
81
|
-
client: this._client,
|
|
82
|
-
status: response.status.bind(response)
|
|
83
|
-
};
|
|
84
|
-
void (async () => {
|
|
85
|
-
try {
|
|
86
|
-
log("invoking", {
|
|
87
|
-
endpoint
|
|
88
|
-
}, {
|
|
89
|
-
F: __dxlog_file,
|
|
90
|
-
L: 100,
|
|
91
|
-
S: this,
|
|
92
|
-
C: (f, a) => f(...a)
|
|
93
|
-
});
|
|
94
|
-
const { handler } = this._handlers[endpoint];
|
|
95
|
-
const response2 = await handler({
|
|
96
|
-
context,
|
|
97
|
-
event: req.body
|
|
98
|
-
});
|
|
99
|
-
log("done", {
|
|
100
|
-
response: response2
|
|
101
|
-
}, {
|
|
102
|
-
F: __dxlog_file,
|
|
103
|
-
L: 103,
|
|
104
|
-
S: this,
|
|
105
|
-
C: (f, a) => f(...a)
|
|
106
|
-
});
|
|
107
|
-
} catch (err) {
|
|
108
|
-
res.statusCode = 500;
|
|
109
|
-
res.end(err.message);
|
|
61
|
+
app.post("/:name", async (req, res) => {
|
|
62
|
+
const { name } = req.params;
|
|
63
|
+
try {
|
|
64
|
+
if (this._options.reload) {
|
|
65
|
+
const { def } = this._handlers[name];
|
|
66
|
+
await this._load(def, true);
|
|
110
67
|
}
|
|
111
|
-
|
|
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
|
+
}
|
|
112
80
|
});
|
|
113
|
-
this._port = await
|
|
114
|
-
|
|
81
|
+
this._port = await getPort({
|
|
82
|
+
port: 7200,
|
|
83
|
+
portRange: [
|
|
84
|
+
7200,
|
|
85
|
+
7299
|
|
86
|
+
]
|
|
115
87
|
});
|
|
116
88
|
this._server = app.listen(this._port);
|
|
117
89
|
try {
|
|
118
|
-
const { registrationId } = await this._client.services.services.FunctionRegistryService.register({
|
|
90
|
+
const { registrationId, endpoint } = await this._client.services.services.FunctionRegistryService.register({
|
|
119
91
|
endpoint: this.endpoint,
|
|
120
|
-
functions: this.functions.map(({ def: {
|
|
121
|
-
name
|
|
92
|
+
functions: this.functions.map(({ def: { name } }) => ({
|
|
93
|
+
name
|
|
122
94
|
}))
|
|
123
95
|
});
|
|
96
|
+
log.info("registered", {
|
|
97
|
+
registrationId,
|
|
98
|
+
endpoint
|
|
99
|
+
}, {
|
|
100
|
+
F: __dxlog_file,
|
|
101
|
+
L: 98,
|
|
102
|
+
S: this,
|
|
103
|
+
C: (f, a) => f(...a)
|
|
104
|
+
});
|
|
124
105
|
this._registrationId = registrationId;
|
|
106
|
+
this._proxy = endpoint;
|
|
125
107
|
} catch (err) {
|
|
126
108
|
await this.stop();
|
|
127
|
-
throw new Error("FunctionRegistryService not available
|
|
109
|
+
throw new Error("FunctionRegistryService not available (check plugin is configured).");
|
|
128
110
|
}
|
|
129
111
|
}
|
|
130
112
|
async stop() {
|
|
@@ -134,31 +116,110 @@ var DevServer = class {
|
|
|
134
116
|
await this._client.services.services.FunctionRegistryService.unregister({
|
|
135
117
|
registrationId: this._registrationId
|
|
136
118
|
});
|
|
119
|
+
log.info("unregistered", {
|
|
120
|
+
registrationId: this._registrationId
|
|
121
|
+
}, {
|
|
122
|
+
F: __dxlog_file,
|
|
123
|
+
L: 115,
|
|
124
|
+
S: this,
|
|
125
|
+
C: (f, a) => f(...a)
|
|
126
|
+
});
|
|
137
127
|
this._registrationId = void 0;
|
|
128
|
+
this._proxy = void 0;
|
|
138
129
|
}
|
|
139
130
|
trigger.wake();
|
|
140
131
|
});
|
|
141
132
|
await trigger.wait();
|
|
142
|
-
this._server = void 0;
|
|
143
133
|
this._port = void 0;
|
|
134
|
+
this._server = void 0;
|
|
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;
|
|
144
205
|
}
|
|
145
206
|
};
|
|
146
207
|
|
|
147
|
-
// packages/core/functions/src/runtime/
|
|
208
|
+
// packages/core/functions/src/runtime/scheduler.ts
|
|
209
|
+
import { CronJob } from "cron";
|
|
148
210
|
import { DeferredTask } from "@dxos/async";
|
|
149
211
|
import { Context } from "@dxos/context";
|
|
150
212
|
import { Filter, createSubscription } from "@dxos/echo-schema";
|
|
151
|
-
import { invariant } from "@dxos/invariant";
|
|
213
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
152
214
|
import { log as log2 } from "@dxos/log";
|
|
153
215
|
import { ComplexMap } from "@dxos/util";
|
|
154
|
-
var __dxlog_file2 = "/home/
|
|
155
|
-
var
|
|
156
|
-
constructor(_client, _manifest,
|
|
216
|
+
var __dxlog_file2 = "/home/circleci/project/packages/core/functions/src/runtime/scheduler.ts";
|
|
217
|
+
var Scheduler = class {
|
|
218
|
+
constructor(_client, _manifest, _options) {
|
|
157
219
|
this._client = _client;
|
|
158
220
|
this._manifest = _manifest;
|
|
159
|
-
this.
|
|
160
|
-
this._mounts = new ComplexMap(({
|
|
161
|
-
this._queries = /* @__PURE__ */ new Set();
|
|
221
|
+
this._options = _options;
|
|
222
|
+
this._mounts = new ComplexMap(({ id, spaceKey }) => `${spaceKey.toHex()}:${id}`);
|
|
162
223
|
}
|
|
163
224
|
async start() {
|
|
164
225
|
this._client.spaces.subscribe(async (spaces) => {
|
|
@@ -171,22 +232,22 @@ var TriggerManager = class {
|
|
|
171
232
|
});
|
|
172
233
|
}
|
|
173
234
|
async stop() {
|
|
174
|
-
for (const {
|
|
175
|
-
await this.unmount(
|
|
235
|
+
for (const { id, spaceKey } of this._mounts.keys()) {
|
|
236
|
+
await this.unmount(id, spaceKey);
|
|
176
237
|
}
|
|
177
238
|
}
|
|
178
239
|
async mount(ctx, space, trigger) {
|
|
179
240
|
const key = {
|
|
180
|
-
|
|
241
|
+
id: trigger.function,
|
|
181
242
|
spaceKey: space.key
|
|
182
243
|
};
|
|
183
|
-
const
|
|
184
|
-
|
|
244
|
+
const def = this._manifest.functions.find((config) => config.id === trigger.function);
|
|
245
|
+
invariant2(def, `Function not found: ${trigger.function}`, {
|
|
185
246
|
F: __dxlog_file2,
|
|
186
|
-
L:
|
|
247
|
+
L: 59,
|
|
187
248
|
S: this,
|
|
188
249
|
A: [
|
|
189
|
-
"
|
|
250
|
+
"def",
|
|
190
251
|
"`Function not found: ${trigger.function}`"
|
|
191
252
|
]
|
|
192
253
|
});
|
|
@@ -201,57 +262,55 @@ var TriggerManager = class {
|
|
|
201
262
|
trigger
|
|
202
263
|
}, {
|
|
203
264
|
F: __dxlog_file2,
|
|
204
|
-
L:
|
|
265
|
+
L: 64,
|
|
205
266
|
S: this,
|
|
206
267
|
C: (f, a) => f(...a)
|
|
207
268
|
});
|
|
208
269
|
if (ctx.disposed) {
|
|
209
270
|
return;
|
|
210
271
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
272
|
+
if (trigger.schedule) {
|
|
273
|
+
const task = new DeferredTask(ctx, async () => {
|
|
274
|
+
await this.execFunction(def, {
|
|
275
|
+
space: space.key
|
|
276
|
+
});
|
|
216
277
|
});
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
space: space.key,
|
|
229
|
-
objects: objectIds.size,
|
|
230
|
-
count
|
|
231
|
-
}, {
|
|
232
|
-
F: __dxlog_file2,
|
|
233
|
-
L: 85,
|
|
234
|
-
S: this,
|
|
235
|
-
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
|
+
});
|
|
236
289
|
});
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
subscription
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
+
}
|
|
250
309
|
}
|
|
251
310
|
}
|
|
252
|
-
async unmount(
|
|
311
|
+
async unmount(id, spaceKey) {
|
|
253
312
|
const key = {
|
|
254
|
-
|
|
313
|
+
id,
|
|
255
314
|
spaceKey
|
|
256
315
|
};
|
|
257
316
|
const { ctx } = this._mounts.get(key) ?? {};
|
|
@@ -260,59 +319,39 @@ var TriggerManager = class {
|
|
|
260
319
|
await ctx.dispose();
|
|
261
320
|
}
|
|
262
321
|
}
|
|
263
|
-
async execFunction(
|
|
264
|
-
const { endpoint, runtime } = options;
|
|
265
|
-
invariant(endpoint, "Missing endpoint", {
|
|
266
|
-
F: __dxlog_file2,
|
|
267
|
-
L: 121,
|
|
268
|
-
S: this,
|
|
269
|
-
A: [
|
|
270
|
-
"endpoint",
|
|
271
|
-
"'Missing endpoint'"
|
|
272
|
-
]
|
|
273
|
-
});
|
|
274
|
-
invariant(runtime, "Missing runtime", {
|
|
275
|
-
F: __dxlog_file2,
|
|
276
|
-
L: 122,
|
|
277
|
-
S: this,
|
|
278
|
-
A: [
|
|
279
|
-
"runtime",
|
|
280
|
-
"'Missing runtime'"
|
|
281
|
-
]
|
|
282
|
-
});
|
|
322
|
+
async execFunction(def, data) {
|
|
283
323
|
try {
|
|
284
|
-
log2("
|
|
285
|
-
function:
|
|
324
|
+
log2("request", {
|
|
325
|
+
function: def.id
|
|
286
326
|
}, {
|
|
287
327
|
F: __dxlog_file2,
|
|
288
|
-
L:
|
|
328
|
+
L: 130,
|
|
289
329
|
S: this,
|
|
290
330
|
C: (f, a) => f(...a)
|
|
291
331
|
});
|
|
292
|
-
const
|
|
293
|
-
const res = await fetch(url, {
|
|
332
|
+
const response = await fetch(`${this._options.endpoint}/${def.name}`, {
|
|
294
333
|
method: "POST",
|
|
295
|
-
body: JSON.stringify(data),
|
|
296
334
|
headers: {
|
|
297
335
|
"Content-Type": "application/json"
|
|
298
|
-
}
|
|
336
|
+
},
|
|
337
|
+
body: JSON.stringify(data)
|
|
299
338
|
});
|
|
300
339
|
log2("result", {
|
|
301
|
-
function:
|
|
302
|
-
result:
|
|
340
|
+
function: def.id,
|
|
341
|
+
result: response.status
|
|
303
342
|
}, {
|
|
304
343
|
F: __dxlog_file2,
|
|
305
|
-
L:
|
|
344
|
+
L: 140,
|
|
306
345
|
S: this,
|
|
307
346
|
C: (f, a) => f(...a)
|
|
308
347
|
});
|
|
309
348
|
} catch (err) {
|
|
310
349
|
log2.error("error", {
|
|
311
|
-
function:
|
|
350
|
+
function: def.id,
|
|
312
351
|
error: err.message
|
|
313
352
|
}, {
|
|
314
353
|
F: __dxlog_file2,
|
|
315
|
-
L:
|
|
354
|
+
L: 142,
|
|
316
355
|
S: this,
|
|
317
356
|
C: (f, a) => f(...a)
|
|
318
357
|
});
|
|
@@ -321,6 +360,6 @@ var TriggerManager = class {
|
|
|
321
360
|
};
|
|
322
361
|
export {
|
|
323
362
|
DevServer,
|
|
324
|
-
|
|
363
|
+
Scheduler
|
|
325
364
|
};
|
|
326
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 type http from 'http';\nimport { join } from 'node:path';\nimport {
|
|
5
|
-
"mappings": ";;;;;;;;;;AAIA,OAAOA,aAAa;
|
|
6
|
-
"names": ["express", "
|
|
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
|
}
|