@dxos/functions 0.3.8 → 0.3.9-main.03b62b6
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 +48 -23
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +46 -21
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/handler.d.ts +1 -0
- package/dist/types/src/handler.d.ts.map +1 -1
- package/dist/types/src/manifest.d.ts +2 -1
- package/dist/types/src/manifest.d.ts.map +1 -1
- package/dist/types/src/runtime/dev-server.d.ts +1 -0
- package/dist/types/src/runtime/dev-server.d.ts.map +1 -1
- package/dist/types/src/runtime/scheduler.d.ts.map +1 -1
- package/package.json +11 -9
- package/src/handler.ts +1 -0
- package/src/manifest.ts +3 -1
- package/src/runtime/dev-server.ts +4 -2
- package/src/runtime/scheduler.ts +26 -9
|
@@ -14,7 +14,7 @@ import { join } from "@dxos/node-std/path";
|
|
|
14
14
|
import { Trigger } from "@dxos/async";
|
|
15
15
|
import { invariant } from "@dxos/invariant";
|
|
16
16
|
import { log } from "@dxos/log";
|
|
17
|
-
var __dxlog_file = "/home/
|
|
17
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/dev-server.ts";
|
|
18
18
|
var DevServer = class {
|
|
19
19
|
// prettier-ignore
|
|
20
20
|
constructor(_client, _options) {
|
|
@@ -26,7 +26,7 @@ var DevServer = class {
|
|
|
26
26
|
get endpoint() {
|
|
27
27
|
invariant(this._port, void 0, {
|
|
28
28
|
F: __dxlog_file,
|
|
29
|
-
L:
|
|
29
|
+
L: 46,
|
|
30
30
|
S: this,
|
|
31
31
|
A: [
|
|
32
32
|
"this._port",
|
|
@@ -48,7 +48,7 @@ var DevServer = class {
|
|
|
48
48
|
} catch (err) {
|
|
49
49
|
log.error("parsing function (check manifest)", err, {
|
|
50
50
|
F: __dxlog_file,
|
|
51
|
-
L:
|
|
51
|
+
L: 63,
|
|
52
52
|
S: this,
|
|
53
53
|
C: (f, a) => f(...a)
|
|
54
54
|
});
|
|
@@ -68,9 +68,9 @@ var DevServer = class {
|
|
|
68
68
|
res.statusCode = await this._invoke(name, req.body);
|
|
69
69
|
res.end();
|
|
70
70
|
} catch (err) {
|
|
71
|
-
log.
|
|
71
|
+
log.catch(err, void 0, {
|
|
72
72
|
F: __dxlog_file,
|
|
73
|
-
L:
|
|
73
|
+
L: 83,
|
|
74
74
|
S: this,
|
|
75
75
|
C: (f, a) => f(...a)
|
|
76
76
|
});
|
|
@@ -79,6 +79,7 @@ var DevServer = class {
|
|
|
79
79
|
}
|
|
80
80
|
});
|
|
81
81
|
this._port = await getPort({
|
|
82
|
+
host: "localhost",
|
|
82
83
|
port: 7200,
|
|
83
84
|
portRange: [
|
|
84
85
|
7200,
|
|
@@ -98,7 +99,7 @@ var DevServer = class {
|
|
|
98
99
|
endpoint
|
|
99
100
|
}, {
|
|
100
101
|
F: __dxlog_file,
|
|
101
|
-
L:
|
|
102
|
+
L: 99,
|
|
102
103
|
S: this,
|
|
103
104
|
C: (f, a) => f(...a)
|
|
104
105
|
});
|
|
@@ -120,7 +121,7 @@ var DevServer = class {
|
|
|
120
121
|
registrationId: this._registrationId
|
|
121
122
|
}, {
|
|
122
123
|
F: __dxlog_file,
|
|
123
|
-
L:
|
|
124
|
+
L: 116,
|
|
124
125
|
S: this,
|
|
125
126
|
C: (f, a) => f(...a)
|
|
126
127
|
});
|
|
@@ -143,7 +144,7 @@ var DevServer = class {
|
|
|
143
144
|
id
|
|
144
145
|
}, {
|
|
145
146
|
F: __dxlog_file,
|
|
146
|
-
L:
|
|
147
|
+
L: 135,
|
|
147
148
|
S: this,
|
|
148
149
|
C: (f, a) => f(...a)
|
|
149
150
|
});
|
|
@@ -170,13 +171,14 @@ var DevServer = class {
|
|
|
170
171
|
name
|
|
171
172
|
}, {
|
|
172
173
|
F: __dxlog_file,
|
|
173
|
-
L:
|
|
174
|
+
L: 160,
|
|
174
175
|
S: this,
|
|
175
176
|
C: (f, a) => f(...a)
|
|
176
177
|
});
|
|
177
178
|
const { handler } = this._handlers[name];
|
|
178
179
|
const context = {
|
|
179
|
-
client: this._client
|
|
180
|
+
client: this._client,
|
|
181
|
+
dataDir: this._options.dataDir
|
|
180
182
|
};
|
|
181
183
|
let statusCode = 200;
|
|
182
184
|
const response = {
|
|
@@ -197,7 +199,7 @@ var DevServer = class {
|
|
|
197
199
|
duration: Date.now() - now
|
|
198
200
|
}, {
|
|
199
201
|
F: __dxlog_file,
|
|
200
|
-
L:
|
|
202
|
+
L: 177,
|
|
201
203
|
S: this,
|
|
202
204
|
C: (f, a) => f(...a)
|
|
203
205
|
});
|
|
@@ -207,13 +209,14 @@ var DevServer = class {
|
|
|
207
209
|
|
|
208
210
|
// packages/core/functions/src/runtime/scheduler.ts
|
|
209
211
|
import { CronJob } from "cron";
|
|
210
|
-
import { DeferredTask } from "@dxos/async";
|
|
212
|
+
import { debounce, DeferredTask } from "@dxos/async";
|
|
213
|
+
import { TextObject } from "@dxos/client/echo";
|
|
211
214
|
import { Context } from "@dxos/context";
|
|
212
|
-
import { Filter, createSubscription } from "@dxos/echo-schema";
|
|
215
|
+
import { Filter, createSubscription, subscribe } from "@dxos/echo-schema";
|
|
213
216
|
import { invariant as invariant2 } from "@dxos/invariant";
|
|
214
217
|
import { log as log2 } from "@dxos/log";
|
|
215
218
|
import { ComplexMap } from "@dxos/util";
|
|
216
|
-
var __dxlog_file2 = "/home/
|
|
219
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/scheduler.ts";
|
|
217
220
|
var Scheduler = class {
|
|
218
221
|
constructor(_client, _manifest, _options) {
|
|
219
222
|
this._client = _client;
|
|
@@ -287,6 +290,7 @@ var Scheduler = class {
|
|
|
287
290
|
objects: Array.from(objectIds)
|
|
288
291
|
});
|
|
289
292
|
});
|
|
293
|
+
const subscriptions = [];
|
|
290
294
|
const subscription = createSubscription(({ added, updated }) => {
|
|
291
295
|
for (const object of added) {
|
|
292
296
|
objectIds.add(object.id);
|
|
@@ -296,14 +300,35 @@ var Scheduler = class {
|
|
|
296
300
|
}
|
|
297
301
|
task.schedule();
|
|
298
302
|
});
|
|
299
|
-
|
|
300
|
-
const
|
|
301
|
-
const
|
|
303
|
+
subscriptions.push(() => subscription.unsubscribe());
|
|
304
|
+
const { type, props, deep, delay } = trigger.subscription;
|
|
305
|
+
const update = ({ objects }) => {
|
|
302
306
|
subscription.update(objects);
|
|
303
|
-
|
|
307
|
+
if (deep) {
|
|
308
|
+
log2.info("update", {
|
|
309
|
+
type,
|
|
310
|
+
deep,
|
|
311
|
+
objects: objects.length
|
|
312
|
+
}, {
|
|
313
|
+
F: __dxlog_file2,
|
|
314
|
+
L: 114,
|
|
315
|
+
S: this,
|
|
316
|
+
C: (f, a) => f(...a)
|
|
317
|
+
});
|
|
318
|
+
for (const object of objects) {
|
|
319
|
+
const content = object.content;
|
|
320
|
+
if (content instanceof TextObject) {
|
|
321
|
+
subscriptions.push(content[subscribe](debounce(() => subscription.update([
|
|
322
|
+
object
|
|
323
|
+
]), 1e3)));
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
const query = space.db.query(Filter.typename(type, props));
|
|
329
|
+
subscriptions.push(query.subscribe(delay ? debounce(update, delay * 1e3) : update));
|
|
304
330
|
ctx.onDispose(() => {
|
|
305
|
-
|
|
306
|
-
unsubscribe();
|
|
331
|
+
subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
307
332
|
});
|
|
308
333
|
}
|
|
309
334
|
}
|
|
@@ -325,7 +350,7 @@ var Scheduler = class {
|
|
|
325
350
|
function: def.id
|
|
326
351
|
}, {
|
|
327
352
|
F: __dxlog_file2,
|
|
328
|
-
L:
|
|
353
|
+
L: 147,
|
|
329
354
|
S: this,
|
|
330
355
|
C: (f, a) => f(...a)
|
|
331
356
|
});
|
|
@@ -341,7 +366,7 @@ var Scheduler = class {
|
|
|
341
366
|
result: response.status
|
|
342
367
|
}, {
|
|
343
368
|
F: __dxlog_file2,
|
|
344
|
-
L:
|
|
369
|
+
L: 157,
|
|
345
370
|
S: this,
|
|
346
371
|
C: (f, a) => f(...a)
|
|
347
372
|
});
|
|
@@ -351,7 +376,7 @@ var Scheduler = class {
|
|
|
351
376
|
error: err.message
|
|
352
377
|
}, {
|
|
353
378
|
F: __dxlog_file2,
|
|
354
|
-
L:
|
|
379
|
+
L: 159,
|
|
355
380
|
S: this,
|
|
356
381
|
C: (f, a) => f(...a)
|
|
357
382
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
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": ";;;;;;;;;;AAIA,OAAOA,aAAa;AACpB,SAASC,eAAe;AAExB,SAASC,YAAY;AAErB,SAASC,eAAe;AAExB,SAASC,iBAAiB;AAC1B,SAASC,WAAW;;
|
|
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", "
|
|
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 dataDir?: string;\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.catch(err);\n res.statusCode = 500;\n res.end();\n }\n });\n\n this._port = await getPort({ host: 'localhost', 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 dataDir: this._options.dataDir,\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 { debounce, DeferredTask } from '@dxos/async';\nimport { type Client, type PublicKey } from '@dxos/client';\nimport { type Space, TextObject } from '@dxos/client/echo';\nimport { Context } from '@dxos/context';\nimport { Filter, createSubscription, type Query, subscribe } 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 // TODO(burdon): Standardize subscription handles.\n const subscriptions: (() => void)[] = [];\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 subscriptions.push(() => subscription.unsubscribe());\n\n const { type, props, deep, delay } = trigger.subscription;\n const update = ({ objects }: Query) => {\n subscription.update(objects);\n\n // TODO(burdon): Hack to monitor changes to Document's text object.\n if (deep) {\n log.info('update', { type, deep, objects: objects.length });\n for (const object of objects) {\n const content = object.content;\n if (content instanceof TextObject) {\n subscriptions.push(content[subscribe](debounce(() => subscription.update([object]), 1_000)));\n }\n }\n }\n };\n\n // TODO(burdon): [Bug]: all callbacks are fired on the first mutation.\n // TODO(burdon): [Bug]: not updated when document is deleted (either top or hierarchically).\n const query = space.db.query(Filter.typename(type, props));\n subscriptions.push(query.subscribe(delay ? debounce(update, delay * 1_000) : update));\n\n ctx.onDispose(() => {\n subscriptions.forEach((unsubscribe) => 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", "catch", "getPort", "host", "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", "dataDir", "response", "status", "code", "duration", "CronJob", "debounce", "DeferredTask", "TextObject", "Context", "Filter", "createSubscription", "subscribe", "invariant", "log", "ComplexMap", "Scheduler", "constructor", "_client", "_manifest", "_options", "_mounts", "id", "spaceKey", "toHex", "start", "spaces", "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", "subscriptions", "added", "updated", "object", "add", "push", "unsubscribe", "type", "props", "deep", "delay", "update", "info", "length", "content", "query", "db", "typename", "forEach", "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":1341,"imports":[],"format":"esm"},"packages/core/functions/src/manifest.ts":{"bytes":1795,"imports":[],"format":"esm"},"packages/core/functions/src/runtime/dev-server.ts":{"bytes":18840,"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":20292,"imports":[{"path":"cron","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/client/echo","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":18300},"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/client/echo","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":4802},"packages/core/functions/src/runtime/index.ts":{"bytesInOutput":0},"packages/core/functions/src/runtime/scheduler.ts":{"bytesInOutput":5040}},"bytes":10423}}}
|
package/dist/lib/node/index.cjs
CHANGED
|
@@ -40,6 +40,7 @@ var import_invariant = require("@dxos/invariant");
|
|
|
40
40
|
var import_log = require("@dxos/log");
|
|
41
41
|
var import_cron = require("cron");
|
|
42
42
|
var import_async2 = require("@dxos/async");
|
|
43
|
+
var import_echo = require("@dxos/client/echo");
|
|
43
44
|
var import_context = require("@dxos/context");
|
|
44
45
|
var import_echo_schema = require("@dxos/echo-schema");
|
|
45
46
|
var import_invariant2 = require("@dxos/invariant");
|
|
@@ -52,7 +53,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
52
53
|
return require.apply(this, arguments);
|
|
53
54
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
54
55
|
});
|
|
55
|
-
var __dxlog_file = "/home/
|
|
56
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/dev-server.ts";
|
|
56
57
|
var DevServer = class {
|
|
57
58
|
// prettier-ignore
|
|
58
59
|
constructor(_client, _options) {
|
|
@@ -64,7 +65,7 @@ var DevServer = class {
|
|
|
64
65
|
get endpoint() {
|
|
65
66
|
(0, import_invariant.invariant)(this._port, void 0, {
|
|
66
67
|
F: __dxlog_file,
|
|
67
|
-
L:
|
|
68
|
+
L: 46,
|
|
68
69
|
S: this,
|
|
69
70
|
A: [
|
|
70
71
|
"this._port",
|
|
@@ -86,7 +87,7 @@ var DevServer = class {
|
|
|
86
87
|
} catch (err) {
|
|
87
88
|
import_log.log.error("parsing function (check manifest)", err, {
|
|
88
89
|
F: __dxlog_file,
|
|
89
|
-
L:
|
|
90
|
+
L: 63,
|
|
90
91
|
S: this,
|
|
91
92
|
C: (f, a) => f(...a)
|
|
92
93
|
});
|
|
@@ -106,9 +107,9 @@ var DevServer = class {
|
|
|
106
107
|
res.statusCode = await this._invoke(name, req.body);
|
|
107
108
|
res.end();
|
|
108
109
|
} catch (err) {
|
|
109
|
-
import_log.log.
|
|
110
|
+
import_log.log.catch(err, void 0, {
|
|
110
111
|
F: __dxlog_file,
|
|
111
|
-
L:
|
|
112
|
+
L: 83,
|
|
112
113
|
S: this,
|
|
113
114
|
C: (f, a) => f(...a)
|
|
114
115
|
});
|
|
@@ -117,6 +118,7 @@ var DevServer = class {
|
|
|
117
118
|
}
|
|
118
119
|
});
|
|
119
120
|
this._port = await (0, import_get_port_please.getPort)({
|
|
121
|
+
host: "localhost",
|
|
120
122
|
port: 7200,
|
|
121
123
|
portRange: [
|
|
122
124
|
7200,
|
|
@@ -136,7 +138,7 @@ var DevServer = class {
|
|
|
136
138
|
endpoint
|
|
137
139
|
}, {
|
|
138
140
|
F: __dxlog_file,
|
|
139
|
-
L:
|
|
141
|
+
L: 99,
|
|
140
142
|
S: this,
|
|
141
143
|
C: (f, a) => f(...a)
|
|
142
144
|
});
|
|
@@ -158,7 +160,7 @@ var DevServer = class {
|
|
|
158
160
|
registrationId: this._registrationId
|
|
159
161
|
}, {
|
|
160
162
|
F: __dxlog_file,
|
|
161
|
-
L:
|
|
163
|
+
L: 116,
|
|
162
164
|
S: this,
|
|
163
165
|
C: (f, a) => f(...a)
|
|
164
166
|
});
|
|
@@ -181,7 +183,7 @@ var DevServer = class {
|
|
|
181
183
|
id
|
|
182
184
|
}, {
|
|
183
185
|
F: __dxlog_file,
|
|
184
|
-
L:
|
|
186
|
+
L: 135,
|
|
185
187
|
S: this,
|
|
186
188
|
C: (f, a) => f(...a)
|
|
187
189
|
});
|
|
@@ -208,13 +210,14 @@ var DevServer = class {
|
|
|
208
210
|
name
|
|
209
211
|
}, {
|
|
210
212
|
F: __dxlog_file,
|
|
211
|
-
L:
|
|
213
|
+
L: 160,
|
|
212
214
|
S: this,
|
|
213
215
|
C: (f, a) => f(...a)
|
|
214
216
|
});
|
|
215
217
|
const { handler } = this._handlers[name];
|
|
216
218
|
const context = {
|
|
217
|
-
client: this._client
|
|
219
|
+
client: this._client,
|
|
220
|
+
dataDir: this._options.dataDir
|
|
218
221
|
};
|
|
219
222
|
let statusCode = 200;
|
|
220
223
|
const response = {
|
|
@@ -235,14 +238,14 @@ var DevServer = class {
|
|
|
235
238
|
duration: Date.now() - now
|
|
236
239
|
}, {
|
|
237
240
|
F: __dxlog_file,
|
|
238
|
-
L:
|
|
241
|
+
L: 177,
|
|
239
242
|
S: this,
|
|
240
243
|
C: (f, a) => f(...a)
|
|
241
244
|
});
|
|
242
245
|
return statusCode;
|
|
243
246
|
}
|
|
244
247
|
};
|
|
245
|
-
var __dxlog_file2 = "/home/
|
|
248
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/scheduler.ts";
|
|
246
249
|
var Scheduler = class {
|
|
247
250
|
constructor(_client, _manifest, _options) {
|
|
248
251
|
this._client = _client;
|
|
@@ -316,6 +319,7 @@ var Scheduler = class {
|
|
|
316
319
|
objects: Array.from(objectIds)
|
|
317
320
|
});
|
|
318
321
|
});
|
|
322
|
+
const subscriptions = [];
|
|
319
323
|
const subscription = (0, import_echo_schema.createSubscription)(({ added, updated }) => {
|
|
320
324
|
for (const object of added) {
|
|
321
325
|
objectIds.add(object.id);
|
|
@@ -325,14 +329,35 @@ var Scheduler = class {
|
|
|
325
329
|
}
|
|
326
330
|
task.schedule();
|
|
327
331
|
});
|
|
328
|
-
|
|
329
|
-
const
|
|
330
|
-
const
|
|
332
|
+
subscriptions.push(() => subscription.unsubscribe());
|
|
333
|
+
const { type, props, deep, delay } = trigger.subscription;
|
|
334
|
+
const update = ({ objects }) => {
|
|
331
335
|
subscription.update(objects);
|
|
332
|
-
|
|
336
|
+
if (deep) {
|
|
337
|
+
import_log2.log.info("update", {
|
|
338
|
+
type,
|
|
339
|
+
deep,
|
|
340
|
+
objects: objects.length
|
|
341
|
+
}, {
|
|
342
|
+
F: __dxlog_file2,
|
|
343
|
+
L: 114,
|
|
344
|
+
S: this,
|
|
345
|
+
C: (f, a) => f(...a)
|
|
346
|
+
});
|
|
347
|
+
for (const object of objects) {
|
|
348
|
+
const content = object.content;
|
|
349
|
+
if (content instanceof import_echo.TextObject) {
|
|
350
|
+
subscriptions.push(content[import_echo_schema.subscribe]((0, import_async2.debounce)(() => subscription.update([
|
|
351
|
+
object
|
|
352
|
+
]), 1e3)));
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
const query = space.db.query(import_echo_schema.Filter.typename(type, props));
|
|
358
|
+
subscriptions.push(query.subscribe(delay ? (0, import_async2.debounce)(update, delay * 1e3) : update));
|
|
333
359
|
ctx.onDispose(() => {
|
|
334
|
-
|
|
335
|
-
unsubscribe();
|
|
360
|
+
subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
336
361
|
});
|
|
337
362
|
}
|
|
338
363
|
}
|
|
@@ -354,7 +379,7 @@ var Scheduler = class {
|
|
|
354
379
|
function: def.id
|
|
355
380
|
}, {
|
|
356
381
|
F: __dxlog_file2,
|
|
357
|
-
L:
|
|
382
|
+
L: 147,
|
|
358
383
|
S: this,
|
|
359
384
|
C: (f, a) => f(...a)
|
|
360
385
|
});
|
|
@@ -370,7 +395,7 @@ var Scheduler = class {
|
|
|
370
395
|
result: response.status
|
|
371
396
|
}, {
|
|
372
397
|
F: __dxlog_file2,
|
|
373
|
-
L:
|
|
398
|
+
L: 157,
|
|
374
399
|
S: this,
|
|
375
400
|
C: (f, a) => f(...a)
|
|
376
401
|
});
|
|
@@ -380,7 +405,7 @@ var Scheduler = class {
|
|
|
380
405
|
error: err.message
|
|
381
406
|
}, {
|
|
382
407
|
F: __dxlog_file2,
|
|
383
|
-
L:
|
|
408
|
+
L: 159,
|
|
384
409
|
S: this,
|
|
385
410
|
C: (f, a) => f(...a)
|
|
386
411
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
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": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,qBAAoB;AACpB,6BAAwB;AAExB,uBAAqB;AAErB,mBAAwB;AAExB,uBAA0B;AAC1B,iBAAoB;ACRpB,kBAAwB;AAExB,IAAAA,
|
|
6
|
-
"names": ["import_async", "import_invariant", "import_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", "Scheduler", "_manifest", "_mounts", "ComplexMap", "spaceKey", "toHex", "spaces", "subscribe", "space", "waitUntilReady", "triggers", "mount", "Context", "unmount", "ctx", "function", "find", "config", "exists", "get", "set", "disposed", "schedule", "task", "DeferredTask", "execFunction", "job", "CronJob", "onDispose", "subscription", "objectIds", "Set", "objects", "Array", "from", "createSubscription", "added", "updated", "object", "add", "type", "props", "
|
|
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 dataDir?: string;\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.catch(err);\n res.statusCode = 500;\n res.end();\n }\n });\n\n this._port = await getPort({ host: 'localhost', 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 dataDir: this._options.dataDir,\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 { debounce, DeferredTask } from '@dxos/async';\nimport { type Client, type PublicKey } from '@dxos/client';\nimport { type Space, TextObject } from '@dxos/client/echo';\nimport { Context } from '@dxos/context';\nimport { Filter, createSubscription, type Query, subscribe } 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 // TODO(burdon): Standardize subscription handles.\n const subscriptions: (() => void)[] = [];\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 subscriptions.push(() => subscription.unsubscribe());\n\n const { type, props, deep, delay } = trigger.subscription;\n const update = ({ objects }: Query) => {\n subscription.update(objects);\n\n // TODO(burdon): Hack to monitor changes to Document's text object.\n if (deep) {\n log.info('update', { type, deep, objects: objects.length });\n for (const object of objects) {\n const content = object.content;\n if (content instanceof TextObject) {\n subscriptions.push(content[subscribe](debounce(() => subscription.update([object]), 1_000)));\n }\n }\n }\n };\n\n // TODO(burdon): [Bug]: all callbacks are fired on the first mutation.\n // TODO(burdon): [Bug]: not updated when document is deleted (either top or hierarchically).\n const query = space.db.query(Filter.typename(type, props));\n subscriptions.push(query.subscribe(delay ? debounce(update, delay * 1_000) : update));\n\n ctx.onDispose(() => {\n subscriptions.forEach((unsubscribe) => 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": ["import_async", "import_invariant", "import_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", "catch", "getPort", "host", "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", "dataDir", "response", "status", "code", "duration", "Scheduler", "_manifest", "_mounts", "ComplexMap", "spaceKey", "toHex", "spaces", "subscribe", "space", "waitUntilReady", "triggers", "mount", "Context", "unmount", "ctx", "function", "find", "config", "exists", "get", "set", "disposed", "schedule", "task", "DeferredTask", "execFunction", "job", "CronJob", "onDispose", "subscription", "objectIds", "Set", "objects", "Array", "from", "subscriptions", "createSubscription", "added", "updated", "object", "add", "push", "unsubscribe", "type", "props", "deep", "delay", "update", "length", "content", "TextObject", "debounce", "query", "db", "Filter", "typename", "delete", "dispose", "data", "fetch", "method", "headers", "JSON", "stringify", "result", "message"]
|
|
7
7
|
}
|
package/dist/lib/node/meta.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"packages/core/functions/src/handler.ts":{"bytes":
|
|
1
|
+
{"inputs":{"packages/core/functions/src/handler.ts":{"bytes":1341,"imports":[],"format":"esm"},"packages/core/functions/src/manifest.ts":{"bytes":1795,"imports":[],"format":"esm"},"packages/core/functions/src/runtime/dev-server.ts":{"bytes":18840,"imports":[{"path":"express","kind":"import-statement","external":true},{"path":"get-port-please","kind":"import-statement","external":true},{"path":"node: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":20292,"imports":[{"path":"cron","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/client/echo","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/node/index.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":18299},"packages/core/functions/dist/lib/node/index.cjs":{"imports":[{"path":"express","kind":"import-statement","external":true},{"path":"get-port-please","kind":"import-statement","external":true},{"path":"node: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/client/echo","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":4792},"packages/core/functions/src/runtime/index.ts":{"bytesInOutput":0},"packages/core/functions/src/runtime/scheduler.ts":{"bytesInOutput":5040}},"bytes":10380}}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/handler.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C,MAAM,WAAW,QAAQ;IACvB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;CAChC;AAGD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/handler.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C,MAAM,WAAW,QAAQ;IACvB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;CAChC;AAGD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE;IACnD,KAAK,EAAE,CAAC,CAAC;IACT,OAAO,EAAE,eAAe,CAAC;IACzB,QAAQ,EAAE,QAAQ,CAAC;CACpB,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;AAE/B,MAAM,MAAM,yBAAyB,GAAG;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/manifest.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,WAAW,GAAG;IAExB,EAAE,EAAE,MAAM,CAAC;IAEX,IAAI,EAAE,MAAM,CAAC;IAEb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/manifest.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,WAAW,GAAG;IAExB,EAAE,EAAE,MAAM,CAAC;IAEX,IAAI,EAAE,MAAM,CAAC;IAEb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAGF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAKF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,mBAAmB,CAAC;CACpC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,EAAE,WAAW,EAAE,CAAC;IACzB,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../../../../src/runtime/dev-server.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAI3C,OAAO,EAAwB,KAAK,eAAe,EAAiB,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEtE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;GAEG;AACH,qBAAa,SAAS;IAYlB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAX3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA2E;IAErG,OAAO,CAAC,OAAO,CAAC,CAAc;IAC9B,OAAO,CAAC,KAAK,CAAC,CAAS;IACvB,OAAO,CAAC,eAAe,CAAC,CAAS;IACjC,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,IAAI,CAAK;gBAIE,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,gBAAgB;IAG7C,IAAI,QAAQ,WAGX;IAED,IAAI,KAAK,uBAER;IAED,IAAI,SAAS;;;QAEZ;IAEK,UAAU;IAUV,KAAK;IAwCL,IAAI;IAqBV;;OAEG;YACW,KAAK;IAqBnB;;OAEG;YACW,OAAO;
|
|
1
|
+
{"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../../../../src/runtime/dev-server.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAI3C,OAAO,EAAwB,KAAK,eAAe,EAAiB,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEtE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;GAEG;AACH,qBAAa,SAAS;IAYlB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAX3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA2E;IAErG,OAAO,CAAC,OAAO,CAAC,CAAc;IAC9B,OAAO,CAAC,KAAK,CAAC,CAAS;IACvB,OAAO,CAAC,eAAe,CAAC,CAAS;IACjC,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,IAAI,CAAK;gBAIE,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,gBAAgB;IAG7C,IAAI,QAAQ,WAGX;IAED,IAAI,KAAK,uBAER;IAED,IAAI,SAAS;;;QAEZ;IAEK,UAAU;IAUV,KAAK;IAwCL,IAAI;IAqBV;;OAEG;YACW,KAAK;IAqBnB;;OAEG;YACW,OAAO;CAyBtB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../../../src/runtime/scheduler.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,MAAM,EAAkB,MAAM,cAAc,CAAC;AAQ3D,OAAO,EAAoB,KAAK,gBAAgB,EAAwB,MAAM,aAAa,CAAC;AAE5F,KAAK,gBAAgB,GAAG;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;GAEG;AAEH,qBAAa,SAAS;IAQlB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAR3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAG6B;gBAGlC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,gBAAgB,EAC3B,QAAQ,EAAE,gBAAgB;IAGvC,KAAK;IAWL,IAAI;YAMI,KAAK;
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../../../src/runtime/scheduler.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,MAAM,EAAkB,MAAM,cAAc,CAAC;AAQ3D,OAAO,EAAoB,KAAK,gBAAgB,EAAwB,MAAM,aAAa,CAAC;AAE5F,KAAK,gBAAgB,GAAG;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;GAEG;AAEH,qBAAa,SAAS;IAQlB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAR3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAG6B;gBAGlC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,gBAAgB,EAC3B,QAAQ,EAAE,gBAAgB;IAGvC,KAAK;IAWL,IAAI;YAMI,KAAK;YAgFL,OAAO;YASP,YAAY;CAiB3B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/functions",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9-main.03b62b6",
|
|
4
4
|
"description": "Functions SDK and runtime.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -19,17 +19,19 @@
|
|
|
19
19
|
"src"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
+
"@preact/signals-core": "^1.5.0",
|
|
22
23
|
"cron": "^3.1.6",
|
|
24
|
+
"deepsignal": "1.4.0-shallow.0",
|
|
23
25
|
"express": "^4.17.1",
|
|
24
26
|
"get-port-please": "^3.1.1",
|
|
25
|
-
"@dxos/async": "0.3.
|
|
26
|
-
"@dxos/
|
|
27
|
-
"@dxos/
|
|
28
|
-
"@dxos/
|
|
29
|
-
"@dxos/
|
|
30
|
-
"@dxos/
|
|
31
|
-
"@dxos/node-std": "0.3.
|
|
32
|
-
"@dxos/
|
|
27
|
+
"@dxos/async": "0.3.9-main.03b62b6",
|
|
28
|
+
"@dxos/client": "0.3.9-main.03b62b6",
|
|
29
|
+
"@dxos/invariant": "0.3.9-main.03b62b6",
|
|
30
|
+
"@dxos/log": "0.3.9-main.03b62b6",
|
|
31
|
+
"@dxos/context": "0.3.9-main.03b62b6",
|
|
32
|
+
"@dxos/util": "0.3.9-main.03b62b6",
|
|
33
|
+
"@dxos/node-std": "0.3.9-main.03b62b6",
|
|
34
|
+
"@dxos/echo-schema": "0.3.9-main.03b62b6"
|
|
33
35
|
},
|
|
34
36
|
"devDependencies": {
|
|
35
37
|
"@types/express": "^4.17.17"
|
package/src/handler.ts
CHANGED
package/src/manifest.ts
CHANGED
|
@@ -15,11 +15,13 @@ export type FunctionDef = {
|
|
|
15
15
|
description?: string;
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
// TODO(burdon): Query DSL.
|
|
18
19
|
export type TriggerSubscription = {
|
|
19
20
|
type: string;
|
|
20
21
|
spaceKey: string;
|
|
21
22
|
props?: Record<string, any>;
|
|
22
|
-
|
|
23
|
+
deep?: boolean;
|
|
24
|
+
delay?: number;
|
|
23
25
|
};
|
|
24
26
|
|
|
25
27
|
// TODO(burdon): Generalize binding.
|
|
@@ -20,6 +20,7 @@ export type DevServerOptions = {
|
|
|
20
20
|
directory: string;
|
|
21
21
|
manifest: FunctionManifest;
|
|
22
22
|
reload?: boolean;
|
|
23
|
+
dataDir?: string;
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
/**
|
|
@@ -79,13 +80,13 @@ export class DevServer {
|
|
|
79
80
|
res.statusCode = await this._invoke(name, req.body);
|
|
80
81
|
res.end();
|
|
81
82
|
} catch (err: any) {
|
|
82
|
-
log.
|
|
83
|
+
log.catch(err);
|
|
83
84
|
res.statusCode = 500;
|
|
84
85
|
res.end();
|
|
85
86
|
}
|
|
86
87
|
});
|
|
87
88
|
|
|
88
|
-
this._port = await getPort({ port: 7200, portRange: [7200, 7299] });
|
|
89
|
+
this._port = await getPort({ host: 'localhost', port: 7200, portRange: [7200, 7299] });
|
|
89
90
|
this._server = app.listen(this._port);
|
|
90
91
|
|
|
91
92
|
try {
|
|
@@ -161,6 +162,7 @@ export class DevServer {
|
|
|
161
162
|
|
|
162
163
|
const context: FunctionContext = {
|
|
163
164
|
client: this._client,
|
|
165
|
+
dataDir: this._options.dataDir,
|
|
164
166
|
};
|
|
165
167
|
|
|
166
168
|
let statusCode = 200;
|
package/src/runtime/scheduler.ts
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
import { CronJob } from 'cron';
|
|
6
6
|
|
|
7
|
-
import { DeferredTask } from '@dxos/async';
|
|
7
|
+
import { debounce, DeferredTask } from '@dxos/async';
|
|
8
8
|
import { type Client, type PublicKey } from '@dxos/client';
|
|
9
|
-
import { type Space } from '@dxos/client/echo';
|
|
9
|
+
import { type Space, TextObject } from '@dxos/client/echo';
|
|
10
10
|
import { Context } from '@dxos/context';
|
|
11
|
-
import { Filter, createSubscription } from '@dxos/echo-schema';
|
|
11
|
+
import { Filter, createSubscription, type Query, subscribe } from '@dxos/echo-schema';
|
|
12
12
|
import { invariant } from '@dxos/invariant';
|
|
13
13
|
import { log } from '@dxos/log';
|
|
14
14
|
import { ComplexMap } from '@dxos/util';
|
|
@@ -91,6 +91,8 @@ export class Scheduler {
|
|
|
91
91
|
});
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
+
// TODO(burdon): Standardize subscription handles.
|
|
95
|
+
const subscriptions: (() => void)[] = [];
|
|
94
96
|
const subscription = createSubscription(({ added, updated }) => {
|
|
95
97
|
for (const object of added) {
|
|
96
98
|
objectIds.add(object.id);
|
|
@@ -101,16 +103,31 @@ export class Scheduler {
|
|
|
101
103
|
|
|
102
104
|
task.schedule();
|
|
103
105
|
});
|
|
106
|
+
subscriptions.push(() => subscription.unsubscribe());
|
|
104
107
|
|
|
105
|
-
const { type, props } = trigger.subscription;
|
|
106
|
-
const
|
|
107
|
-
const unsubscribe = query.subscribe(({ objects }) => {
|
|
108
|
+
const { type, props, deep, delay } = trigger.subscription;
|
|
109
|
+
const update = ({ objects }: Query) => {
|
|
108
110
|
subscription.update(objects);
|
|
109
|
-
|
|
111
|
+
|
|
112
|
+
// TODO(burdon): Hack to monitor changes to Document's text object.
|
|
113
|
+
if (deep) {
|
|
114
|
+
log.info('update', { type, deep, objects: objects.length });
|
|
115
|
+
for (const object of objects) {
|
|
116
|
+
const content = object.content;
|
|
117
|
+
if (content instanceof TextObject) {
|
|
118
|
+
subscriptions.push(content[subscribe](debounce(() => subscription.update([object]), 1_000)));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// TODO(burdon): [Bug]: all callbacks are fired on the first mutation.
|
|
125
|
+
// TODO(burdon): [Bug]: not updated when document is deleted (either top or hierarchically).
|
|
126
|
+
const query = space.db.query(Filter.typename(type, props));
|
|
127
|
+
subscriptions.push(query.subscribe(delay ? debounce(update, delay * 1_000) : update));
|
|
110
128
|
|
|
111
129
|
ctx.onDispose(() => {
|
|
112
|
-
|
|
113
|
-
unsubscribe();
|
|
130
|
+
subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
114
131
|
});
|
|
115
132
|
}
|
|
116
133
|
}
|