@dxos/functions 0.4.9 → 0.4.10-main.068c3d8
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 +15 -16
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +17 -18
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/handler.d.ts +2 -2
- package/dist/types/src/handler.d.ts.map +1 -1
- package/dist/types/src/runtime/scheduler.d.ts.map +1 -1
- package/package.json +11 -10
- package/src/handler.ts +3 -6
- package/src/runtime/scheduler.ts +7 -4
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
|
|
17
17
|
// packages/core/functions/src/handler.ts
|
|
18
18
|
import { PublicKey } from "@dxos/client";
|
|
19
|
-
import { isTypedObject } from "@dxos/echo-schema";
|
|
20
19
|
import { log } from "@dxos/log";
|
|
21
20
|
import { nonNullable } from "@dxos/util";
|
|
22
21
|
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/functions/src/handler.ts";
|
|
@@ -24,13 +23,13 @@ var subscriptionHandler = (handler) => {
|
|
|
24
23
|
return ({ event, context, ...rest }) => {
|
|
25
24
|
const { client } = context;
|
|
26
25
|
const space = event.space ? client.spaces.get(PublicKey.from(event.space)) : void 0;
|
|
27
|
-
const objects = space && event.objects?.map((id) => space.db.getObjectById(id)).filter(nonNullable)
|
|
26
|
+
const objects = space && event.objects?.map((id) => space.db.getObjectById(id)).filter(nonNullable);
|
|
28
27
|
if (!!event.space && !space) {
|
|
29
28
|
log.warn("invalid space", {
|
|
30
29
|
event
|
|
31
30
|
}, {
|
|
32
31
|
F: __dxlog_file,
|
|
33
|
-
L:
|
|
32
|
+
L: 61,
|
|
34
33
|
S: void 0,
|
|
35
34
|
C: (f, a) => f(...a)
|
|
36
35
|
});
|
|
@@ -40,7 +39,7 @@ var subscriptionHandler = (handler) => {
|
|
|
40
39
|
objects: objects?.length
|
|
41
40
|
}, {
|
|
42
41
|
F: __dxlog_file,
|
|
43
|
-
L:
|
|
42
|
+
L: 63,
|
|
44
43
|
S: void 0,
|
|
45
44
|
C: (f, a) => f(...a)
|
|
46
45
|
});
|
|
@@ -258,10 +257,10 @@ var DevServer = class {
|
|
|
258
257
|
|
|
259
258
|
// packages/core/functions/src/runtime/scheduler.ts
|
|
260
259
|
import { CronJob } from "cron";
|
|
260
|
+
import { TextV0Type } from "@braneframe/types";
|
|
261
261
|
import { debounce, DeferredTask } from "@dxos/async";
|
|
262
|
-
import { TextObject } from "@dxos/client/echo";
|
|
263
262
|
import { Context } from "@dxos/context";
|
|
264
|
-
import { Filter, createSubscription,
|
|
263
|
+
import { Filter, createSubscription, getAutomergeObjectCore } from "@dxos/echo-schema";
|
|
265
264
|
import { invariant as invariant2 } from "@dxos/invariant";
|
|
266
265
|
import { log as log3 } from "@dxos/log";
|
|
267
266
|
import { ComplexMap } from "@dxos/util";
|
|
@@ -296,7 +295,7 @@ var Scheduler = class {
|
|
|
296
295
|
const def = this._manifest.functions.find((config) => config.id === trigger.function);
|
|
297
296
|
invariant2(def, `Function not found: ${trigger.function}`, {
|
|
298
297
|
F: __dxlog_file3,
|
|
299
|
-
L:
|
|
298
|
+
L: 64,
|
|
300
299
|
S: this,
|
|
301
300
|
A: [
|
|
302
301
|
"def",
|
|
@@ -314,7 +313,7 @@ var Scheduler = class {
|
|
|
314
313
|
trigger
|
|
315
314
|
}, {
|
|
316
315
|
F: __dxlog_file3,
|
|
317
|
-
L:
|
|
316
|
+
L: 70,
|
|
318
317
|
S: this,
|
|
319
318
|
C: (f, a) => f(...a)
|
|
320
319
|
});
|
|
@@ -348,7 +347,7 @@ var Scheduler = class {
|
|
|
348
347
|
});
|
|
349
348
|
invariant2(trigger.schedule, void 0, {
|
|
350
349
|
F: __dxlog_file3,
|
|
351
|
-
L:
|
|
350
|
+
L: 103,
|
|
352
351
|
S: this,
|
|
353
352
|
A: [
|
|
354
353
|
"trigger.schedule",
|
|
@@ -371,7 +370,7 @@ var Scheduler = class {
|
|
|
371
370
|
delta
|
|
372
371
|
}, {
|
|
373
372
|
F: __dxlog_file3,
|
|
374
|
-
L:
|
|
373
|
+
L: 117,
|
|
375
374
|
S: this,
|
|
376
375
|
C: (f, a) => f(...a)
|
|
377
376
|
});
|
|
@@ -410,14 +409,14 @@ var Scheduler = class {
|
|
|
410
409
|
objects: objects.length
|
|
411
410
|
}, {
|
|
412
411
|
F: __dxlog_file3,
|
|
413
|
-
L:
|
|
412
|
+
L: 158,
|
|
414
413
|
S: this,
|
|
415
414
|
C: (f, a) => f(...a)
|
|
416
415
|
});
|
|
417
416
|
for (const object of objects) {
|
|
418
417
|
const content = object.content;
|
|
419
|
-
if (content instanceof
|
|
420
|
-
subscriptions.push(content
|
|
418
|
+
if (content instanceof TextV0Type) {
|
|
419
|
+
subscriptions.push(getAutomergeObjectCore(content).updates.on(debounce(() => subscription.update([
|
|
421
420
|
object
|
|
422
421
|
]), 1e3)));
|
|
423
422
|
}
|
|
@@ -436,7 +435,7 @@ var Scheduler = class {
|
|
|
436
435
|
function: def.id
|
|
437
436
|
}, {
|
|
438
437
|
F: __dxlog_file3,
|
|
439
|
-
L:
|
|
438
|
+
L: 182,
|
|
440
439
|
S: this,
|
|
441
440
|
C: (f, a) => f(...a)
|
|
442
441
|
});
|
|
@@ -459,7 +458,7 @@ var Scheduler = class {
|
|
|
459
458
|
result: status
|
|
460
459
|
}, {
|
|
461
460
|
F: __dxlog_file3,
|
|
462
|
-
L:
|
|
461
|
+
L: 201,
|
|
463
462
|
S: this,
|
|
464
463
|
C: (f, a) => f(...a)
|
|
465
464
|
});
|
|
@@ -469,7 +468,7 @@ var Scheduler = class {
|
|
|
469
468
|
error: err.message
|
|
470
469
|
}, {
|
|
471
470
|
F: __dxlog_file3,
|
|
472
|
-
L:
|
|
471
|
+
L: 203,
|
|
473
472
|
S: this,
|
|
474
473
|
C: (f, a) => f(...a)
|
|
475
474
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/handler.ts", "../../../src/runtime/dev-server.ts", "../../../src/runtime/scheduler.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Client, PublicKey } from '@dxos/client';\nimport { type Space } from '@dxos/client/echo';\nimport { isTypedObject, type TypedObject } from '@dxos/echo-schema';\nimport { log } from '@dxos/log';\nimport { nonNullable } from '@dxos/util';\n\n// TODO(burdon): No response?\nexport interface Response {\n status(code: number): Response;\n}\n\n// TODO(burdon): Limit access to individual space?\nexport interface FunctionContext {\n client: Client;\n dataDir?: string;\n}\n\n// TODO(burdon): Model after http request. Ref Lambda/OpenFaaS.\n// https://docs.aws.amazon.com/lambda/latest/dg/typescript-handler.html\nexport type FunctionHandler<T extends {}> = (params: {\n event: T;\n context: FunctionContext;\n response: Response;\n}) => Promise<Response | void>;\n\nexport type FunctionSubscriptionEvent = {\n space?: string; // TODO(burdon): Convert to PublicKey.\n objects?: string[];\n};\n\nexport type FunctionSubscriptionEvent2 = {\n space?: Space;\n objects?: TypedObject[];\n};\n\n/**\n * Handler wrapper for subscription events; extracts space and objects.\n *\n * To test:\n * ```\n * curl -s -X POST -H \"Content-Type: application/json\" --data '{\"space\": \"0446...1cbb\"}' http://localhost:7100/dev/email-extractor\n * ```\n *\n * NOTE: Get space key from devtools or `dx space list --json`\n */\nexport const subscriptionHandler = (\n handler: FunctionHandler<FunctionSubscriptionEvent2>,\n): FunctionHandler<FunctionSubscriptionEvent> => {\n return ({ event, context, ...rest }) => {\n const { client } = context;\n const space = event.space ? client.spaces.get(PublicKey.from(event.space)) : undefined;\n const objects =\n space &&\n event.objects\n ?.map<TypedObject | undefined>((id) => space!.db.getObjectById(id))\n .filter(nonNullable)\n .filter(isTypedObject);\n\n if (!!event.space && !space) {\n log.warn('invalid space', { event });\n } else {\n log.info('handler', { space: space?.key.truncate(), objects: objects?.length });\n }\n\n return handler({ event: { space, objects }, context, ...rest });\n };\n};\n", "//\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.error(`Function failed: ${name}`, 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 FunctionSubscriptionEvent } from '../handler';\nimport { type FunctionDef, type FunctionManifest, type FunctionTrigger, type TriggerSubscription } from '../manifest';\n\ntype Callback = (data: FunctionSubscriptionEvent) => Promise<number>;\n\ntype SchedulerOptions = {\n endpoint?: string;\n callback?: Callback;\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 // Currently supports only one trigger declaration per function.\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 // Timer.\n if (trigger.schedule) {\n this._createTimer(ctx, space, def, trigger);\n }\n\n // Subscription.\n for (const triggerSubscription of trigger.subscriptions ?? []) {\n this._createSubscription(ctx, space, def, triggerSubscription);\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 _createTimer(ctx: Context, space: Space, def: FunctionDef, trigger: FunctionTrigger) {\n const task = new DeferredTask(ctx, async () => {\n await this._execFunction(def, {\n space: space.key,\n });\n });\n\n invariant(trigger.schedule);\n let last = 0;\n let run = 0;\n // https://www.npmjs.com/package/cron#constructor\n const job = CronJob.from({\n cronTime: trigger.schedule,\n runOnInit: false,\n onTick: () => {\n // TODO(burdon): Check greater than 30s (use cron-parser).\n const now = Date.now();\n const delta = last ? now - last : 0;\n last = now;\n\n run++;\n log.info('tick', { space: space.key.truncate(), count: run, delta });\n task.schedule();\n },\n });\n\n job.start();\n ctx.onDispose(() => job.stop());\n }\n\n private _createSubscription(ctx: Context, space: Space, def: FunctionDef, triggerSubscription: TriggerSubscription) {\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): Don't fire initially.\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 // TODO(burdon): Create queue. Only allow one invocation per trigger at a time?\n // TODO(burdon): Disable trigger if keeps failing.\n const { type, props, deep, delay } = triggerSubscription;\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 private async _execFunction(def: FunctionDef, data: any) {\n try {\n log('request', { function: def.id });\n const { endpoint, callback } = this._options;\n let status = 0;\n if (endpoint) {\n // TODO(burdon): Move out of scheduler (generalize as callback).\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 status = response.status;\n } else if (callback) {\n status = await callback(data);\n }\n\n // const result = await response.json();\n log('result', { function: def.id, result: status });\n } catch (err: any) {\n log.error('error', { function: def.id, error: err.message });\n }\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;AAIA,SAAsBA,iBAAiB;
|
|
6
|
-
"names": ["PublicKey", "
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Client, PublicKey } from '@dxos/client';\nimport { type Space } from '@dxos/client/echo';\nimport { type EchoReactiveObject } from '@dxos/echo-schema';\nimport { log } from '@dxos/log';\nimport { nonNullable } from '@dxos/util';\n\n// TODO(burdon): No response?\nexport interface Response {\n status(code: number): Response;\n}\n\n// TODO(burdon): Limit access to individual space?\nexport interface FunctionContext {\n client: Client;\n dataDir?: string;\n}\n\n// TODO(burdon): Model after http request. Ref Lambda/OpenFaaS.\n// https://docs.aws.amazon.com/lambda/latest/dg/typescript-handler.html\nexport type FunctionHandler<T extends {}> = (params: {\n event: T;\n context: FunctionContext;\n response: Response;\n}) => Promise<Response | void>;\n\nexport type FunctionSubscriptionEvent = {\n space?: string; // TODO(burdon): Convert to PublicKey.\n objects?: string[];\n};\n\nexport type FunctionSubscriptionEvent2 = {\n space?: Space;\n objects?: EchoReactiveObject<any>[];\n};\n\n/**\n * Handler wrapper for subscription events; extracts space and objects.\n *\n * To test:\n * ```\n * curl -s -X POST -H \"Content-Type: application/json\" --data '{\"space\": \"0446...1cbb\"}' http://localhost:7100/dev/email-extractor\n * ```\n *\n * NOTE: Get space key from devtools or `dx space list --json`\n */\nexport const subscriptionHandler = (\n handler: FunctionHandler<FunctionSubscriptionEvent2>,\n): FunctionHandler<FunctionSubscriptionEvent> => {\n return ({ event, context, ...rest }) => {\n const { client } = context;\n const space = event.space ? client.spaces.get(PublicKey.from(event.space)) : undefined;\n const objects =\n space &&\n event.objects?.map<EchoReactiveObject<any> | undefined>((id) => space!.db.getObjectById(id)).filter(nonNullable);\n\n if (!!event.space && !space) {\n log.warn('invalid space', { event });\n } else {\n log.info('handler', { space: space?.key.truncate(), objects: objects?.length });\n }\n\n return handler({ event: { space, objects }, context, ...rest });\n };\n};\n", "//\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.error(`Function failed: ${name}`, 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 { TextV0Type } from '@braneframe/types';\nimport { debounce, 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, type Query, getAutomergeObjectCore } from '@dxos/echo-schema';\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\nimport { ComplexMap } from '@dxos/util';\n\nimport { type FunctionSubscriptionEvent } from '../handler';\nimport { type FunctionDef, type FunctionManifest, type FunctionTrigger, type TriggerSubscription } from '../manifest';\n\ntype Callback = (data: FunctionSubscriptionEvent) => Promise<number>;\n\ntype SchedulerOptions = {\n endpoint?: string;\n callback?: Callback;\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 // Currently supports only one trigger declaration per function.\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 // Timer.\n if (trigger.schedule) {\n this._createTimer(ctx, space, def, trigger);\n }\n\n // Subscription.\n for (const triggerSubscription of trigger.subscriptions ?? []) {\n this._createSubscription(ctx, space, def, triggerSubscription);\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 _createTimer(ctx: Context, space: Space, def: FunctionDef, trigger: FunctionTrigger) {\n const task = new DeferredTask(ctx, async () => {\n await this._execFunction(def, {\n space: space.key,\n });\n });\n\n invariant(trigger.schedule);\n let last = 0;\n let run = 0;\n // https://www.npmjs.com/package/cron#constructor\n const job = CronJob.from({\n cronTime: trigger.schedule,\n runOnInit: false,\n onTick: () => {\n // TODO(burdon): Check greater than 30s (use cron-parser).\n const now = Date.now();\n const delta = last ? now - last : 0;\n last = now;\n\n run++;\n log.info('tick', { space: space.key.truncate(), count: run, delta });\n task.schedule();\n },\n });\n\n job.start();\n ctx.onDispose(() => job.stop());\n }\n\n private _createSubscription(ctx: Context, space: Space, def: FunctionDef, triggerSubscription: TriggerSubscription) {\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): Don't fire initially.\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 // TODO(burdon): Create queue. Only allow one invocation per trigger at a time?\n // TODO(burdon): Disable trigger if keeps failing.\n const { type, props, deep, delay } = triggerSubscription;\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 TextV0Type) {\n subscriptions.push(\n getAutomergeObjectCore(content).updates.on(debounce(() => subscription.update([object]), 1_000)),\n );\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 private async _execFunction(def: FunctionDef, data: any) {\n try {\n log('request', { function: def.id });\n const { endpoint, callback } = this._options;\n let status = 0;\n if (endpoint) {\n // TODO(burdon): Move out of scheduler (generalize as callback).\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 status = response.status;\n } else if (callback) {\n status = await callback(data);\n }\n\n // const result = await response.json();\n log('result', { function: def.id, result: status });\n } catch (err: any) {\n log.error('error', { function: def.id, error: err.message });\n }\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;AAIA,SAAsBA,iBAAiB;AAGvC,SAASC,WAAW;AACpB,SAASC,mBAAmB;;AAyCrB,IAAMC,sBAAsB,CACjCC,YAAAA;AAEA,SAAO,CAAC,EAAEC,OAAOC,SAAS,GAAGC,KAAAA,MAAM;AACjC,UAAM,EAAEC,OAAM,IAAKF;AACnB,UAAMG,QAAQJ,MAAMI,QAAQD,OAAOE,OAAOC,IAAIX,UAAUY,KAAKP,MAAMI,KAAK,CAAA,IAAKI;AAC7E,UAAMC,UACJL,SACAJ,MAAMS,SAASC,IAAyC,CAACC,OAAOP,MAAOQ,GAAGC,cAAcF,EAAAA,CAAAA,EAAKG,OAAOjB,WAAAA;AAEtG,QAAI,CAAC,CAACG,MAAMI,SAAS,CAACA,OAAO;AAC3BR,UAAImB,KAAK,iBAAiB;QAAEf;MAAM,GAAA;;;;;;IACpC,OAAO;AACLJ,UAAIoB,KAAK,WAAW;QAAEZ,OAAOA,OAAOa,IAAIC,SAAAA;QAAYT,SAASA,SAASU;MAAO,GAAA;;;;;;IAC/E;AAEA,WAAOpB,QAAQ;MAAEC,OAAO;QAAEI;QAAOK;MAAQ;MAAGR;MAAS,GAAGC;IAAK,CAAA;EAC/D;AACF;;;AC/DA,OAAOkB,aAAa;AACpB,SAASC,eAAe;AAExB,SAASC,YAAY;AAErB,SAASC,eAAe;AAExB,SAASC,iBAAiB;AAC1B,SAASC,OAAAA,YAAW;;AAgBb,IAAMC,YAAN,MAAMA;;EAWXC,YACmBC,SACAC,UACjB;SAFiBD,UAAAA;SACAC,WAAAA;SAXFC,YAAiF,CAAC;SAM3FC,OAAO;EAMZ;EAEH,IAAIC,WAAW;AACbC,cAAU,KAAKC,OAAK,QAAA;;;;;;;;;AACpB,WAAO,oBAAoB,KAAKA,KAAK;EACvC;EAEA,IAAIC,QAAQ;AACV,WAAO,KAAKC;EACd;EAEA,IAAIC,YAAY;AACd,WAAOC,OAAOC,OAAO,KAAKT,SAAS;EACrC;EAEA,MAAMU,aAAa;AACjB,eAAWC,OAAO,KAAKZ,SAASa,SAASL,WAAW;AAClD,UAAI;AACF,cAAM,KAAKM,MAAMF,GAAAA;MACnB,SAASG,KAAK;AACZC,QAAAA,KAAIC,MAAM,qCAAqCF,KAAAA;;;;;;MACjD;IACF;EACF;EAEA,MAAMG,QAAQ;AACZ,UAAMC,MAAMC,QAAAA;AACZD,QAAIE,IAAID,QAAQE,KAAI,CAAA;AAEpBH,QAAII,KAAK,UAAU,OAAOC,KAAKC,QAAAA;AAC7B,YAAM,EAAEC,KAAI,IAAKF,IAAIG;AACrB,UAAI;AACF,YAAI,KAAK3B,SAAS4B,QAAQ;AACxB,gBAAM,EAAEhB,IAAG,IAAK,KAAKX,UAAUyB,IAAAA;AAC/B,gBAAM,KAAKZ,MAAMF,KAAK,IAAA;QACxB;AAEAa,YAAII,aAAa,MAAM,KAAKC,QAAQJ,MAAMF,IAAIO,IAAI;AAClDN,YAAIO,IAAG;MACT,SAASjB,KAAU;AACjBC,QAAAA,KAAIC,MAAM,oBAAoBS,IAAAA,IAAQX,KAAAA;;;;;;AACtCU,YAAII,aAAa;AACjBJ,YAAIO,IAAG;MACT;IACF,CAAA;AAEA,SAAK3B,QAAQ,MAAM4B,QAAQ;MAAEC,MAAM;MAAaC,MAAM;MAAMC,WAAW;QAAC;QAAM;;IAAM,CAAA;AACpF,SAAKC,UAAUlB,IAAImB,OAAO,KAAKjC,KAAK;AAEpC,QAAI;AAEF,YAAM,EAAEkC,gBAAgBpC,SAAQ,IAAK,MAAM,KAAKJ,QAAQyC,SAASA,SAASC,wBAAyBC,SAAS;QAC1GvC,UAAU,KAAKA;QACfK,WAAW,KAAKA,UAAUmC,IAAI,CAAC,EAAE/B,KAAK,EAAEc,KAAI,EAAE,OAAQ;UAAEA;QAAK,EAAA;MAC/D,CAAA;AAEAV,MAAAA,KAAI4B,KAAK,cAAc;QAAEL;QAAgBpC;MAAS,GAAA;;;;;;AAClD,WAAK0C,kBAAkBN;AACvB,WAAKhC,SAASJ;IAChB,SAASY,KAAU;AACjB,YAAM,KAAK+B,KAAI;AACf,YAAM,IAAIC,MAAM,qEAAA;IAClB;EACF;EAEA,MAAMD,OAAO;AACX,UAAME,UAAU,IAAIC,QAAAA;AACpB,SAAKZ,SAASa,MAAM,YAAA;AAClB,UAAI,KAAKL,iBAAiB;AACxB,cAAM,KAAK9C,QAAQyC,SAASA,SAASC,wBAAyBU,WAAW;UACvEZ,gBAAgB,KAAKM;QACvB,CAAA;AAEA7B,QAAAA,KAAI4B,KAAK,gBAAgB;UAAEL,gBAAgB,KAAKM;QAAgB,GAAA;;;;;;AAChE,aAAKA,kBAAkBO;AACvB,aAAK7C,SAAS6C;MAChB;AAEAJ,cAAQK,KAAI;IACd,CAAA;AAEA,UAAML,QAAQM,KAAI;AAClB,SAAKjD,QAAQ+C;AACb,SAAKf,UAAUe;EACjB;;;;EAKA,MAActC,MAAMF,KAAkB2C,QAAQ,OAAO;AACnD,UAAM,EAAEC,IAAI9B,MAAM+B,QAAO,IAAK7C;AAC9B,UAAM8C,OAAOC,KAAK,KAAK3D,SAAS4D,WAAWH,OAAAA;AAC3CzC,IAAAA,KAAI4B,KAAK,WAAW;MAAEY;IAAG,GAAA;;;;;;AAGzB,QAAID,OAAO;AACT9C,aAAOoD,KAAKC,UAAQC,KAAK,EACtBC,OAAO,CAACC,QAAQA,IAAIC,WAAWR,IAAAA,CAAAA,EAC/BS,QAAQ,CAACF,QAAQ,OAAOH,UAAQC,MAAME,GAAAA,CAAI;IAC/C;AAGA,UAAMG,SAASN,UAAQJ,IAAAA;AACvB,QAAI,OAAOU,OAAOC,YAAY,YAAY;AACxC,YAAM,IAAItB,MAAM,yCAAyCS,EAAAA,EAAI;IAC/D;AAEA,SAAKvD,UAAUyB,IAAAA,IAAQ;MAAEd;MAAK6C,SAASW,OAAOC;IAAQ;EACxD;;;;EAKA,MAAcvC,QAAQJ,MAAc4C,OAAY;AAC9C,UAAMC,MAAM,EAAE,KAAKrE;AACnB,UAAMsE,MAAMC,KAAKD,IAAG;AAEpBxD,IAAAA,KAAI4B,KAAK,OAAO;MAAE2B;MAAK7C;IAAK,GAAA;;;;;;AAC5B,UAAM,EAAE+B,QAAO,IAAK,KAAKxD,UAAUyB,IAAAA;AAEnC,UAAMgD,UAA2B;MAC/BC,QAAQ,KAAK5E;MACb6E,SAAS,KAAK5E,SAAS4E;IACzB;AAEA,QAAI/C,aAAa;AACjB,UAAMgD,WAAqB;MACzBC,QAAQ,CAACC,SAAAA;AACPlD,qBAAakD;AACb,eAAOF;MACT;IACF;AAEA,UAAMpB,QAAQ;MAAEiB;MAASJ;MAAOO;IAAS,CAAA;AACzC7D,IAAAA,KAAI4B,KAAK,OAAO;MAAE2B;MAAK7C;MAAMG;MAAYmD,UAAUP,KAAKD,IAAG,IAAKA;IAAI,GAAA;;;;;;AAEpE,WAAO3C;EACT;AACF;;;AChLA,SAASoD,eAAe;AAExB,SAASC,kBAAkB;AAC3B,SAASC,UAAUC,oBAAoB;AAGvC,SAASC,eAAe;AACxB,SAASC,QAAQC,oBAAgCC,8BAA8B;AAC/E,SAASC,aAAAA,kBAAiB;AAC1B,SAASC,OAAAA,YAAW;AACpB,SAASC,kBAAkB;;AAgBpB,IAAMC,YAAN,MAAMA;EAOXC,YACmBC,SACAC,WACAC,WAA6B,CAAC,GAC/C;SAHiBF,UAAAA;SACAC,YAAAA;SACAC,WAAAA;SARFC,UAAU,IAAIN,WAG7B,CAAC,EAAEO,IAAIC,SAAQ,MAAO,GAAGA,SAASC,MAAK,CAAA,IAAMF,EAAAA,EAAI;EAMhD;EAEH,MAAMG,QAAQ;AACZ,SAAKP,QAAQQ,OAAOC,UAAU,OAAOD,WAAAA;AACnC,iBAAWE,SAASF,QAAQ;AAC1B,cAAME,MAAMC,eAAc;AAC1B,mBAAWC,WAAW,KAAKX,UAAUY,YAAY,CAAA,GAAI;AACnD,gBAAM,KAAKC,MAAM,IAAIvB,QAAAA,GAAWmB,OAAOE,OAAAA;QACzC;MACF;IACF,CAAA;EACF;EAEA,MAAMG,OAAO;AACX,eAAW,EAAEX,IAAIC,SAAQ,KAAM,KAAKF,QAAQa,KAAI,GAAI;AAClD,YAAM,KAAKC,QAAQb,IAAIC,QAAAA;IACzB;EACF;EAEA,MAAcS,MAAMI,KAAcR,OAAcE,SAA0B;AACxE,UAAMO,MAAM;MAAEf,IAAIQ,QAAQQ;MAAUf,UAAUK,MAAMS;IAAI;AACxD,UAAME,MAAM,KAAKpB,UAAUqB,UAAUC,KAAK,CAACC,WAAWA,OAAOpB,OAAOQ,QAAQQ,QAAQ;AACpFzB,IAAAA,WAAU0B,KAAK,uBAAuBT,QAAQQ,QAAQ,IAAE;;;;;;;;;AAGxD,UAAMK,SAAS,KAAKtB,QAAQuB,IAAIP,GAAAA;AAChC,QAAI,CAACM,QAAQ;AACX,WAAKtB,QAAQwB,IAAIR,KAAK;QAAED;QAAKN;MAAQ,CAAA;AACrChB,MAAAA,KAAI,SAAS;QAAEc,OAAOA,MAAMS;QAAKP;MAAQ,GAAA;;;;;;AACzC,UAAIM,IAAIU,UAAU;AAChB;MACF;AAGA,UAAIhB,QAAQiB,UAAU;AACpB,aAAKC,aAAaZ,KAAKR,OAAOW,KAAKT,OAAAA;MACrC;AAGA,iBAAWmB,uBAAuBnB,QAAQoB,iBAAiB,CAAA,GAAI;AAC7D,aAAKC,oBAAoBf,KAAKR,OAAOW,KAAKU,mBAAAA;MAC5C;IACF;EACF;EAEA,MAAcd,QAAQb,IAAYC,UAAqB;AACrD,UAAMc,MAAM;MAAEf;MAAIC;IAAS;AAC3B,UAAM,EAAEa,IAAG,IAAK,KAAKf,QAAQuB,IAAIP,GAAAA,KAAQ,CAAC;AAC1C,QAAID,KAAK;AACP,WAAKf,QAAQ+B,OAAOf,GAAAA;AACpB,YAAMD,IAAIiB,QAAO;IACnB;EACF;EAEQL,aAAaZ,KAAcR,OAAcW,KAAkBT,SAA0B;AAC3F,UAAMwB,OAAO,IAAI9C,aAAa4B,KAAK,YAAA;AACjC,YAAM,KAAKmB,cAAchB,KAAK;QAC5BX,OAAOA,MAAMS;MACf,CAAA;IACF,CAAA;AAEAxB,IAAAA,WAAUiB,QAAQiB,UAAQ,QAAA;;;;;;;;;AAC1B,QAAIS,OAAO;AACX,QAAIC,MAAM;AAEV,UAAMC,MAAMrD,QAAQsD,KAAK;MACvBC,UAAU9B,QAAQiB;MAClBc,WAAW;MACXC,QAAQ,MAAA;AAEN,cAAMC,MAAMC,KAAKD,IAAG;AACpB,cAAME,QAAQT,OAAOO,MAAMP,OAAO;AAClCA,eAAOO;AAEPN;AACA3C,QAAAA,KAAIoD,KAAK,QAAQ;UAAEtC,OAAOA,MAAMS,IAAI8B,SAAQ;UAAIC,OAAOX;UAAKQ;QAAM,GAAA;;;;;;AAClEX,aAAKP,SAAQ;MACf;IACF,CAAA;AAEAW,QAAIjC,MAAK;AACTW,QAAIiC,UAAU,MAAMX,IAAIzB,KAAI,CAAA;EAC9B;EAEQkB,oBAAoBf,KAAcR,OAAcW,KAAkBU,qBAA0C;AAClH,UAAMqB,YAAY,oBAAIC,IAAAA;AACtB,UAAMjB,OAAO,IAAI9C,aAAa4B,KAAK,YAAA;AACjC,YAAM,KAAKmB,cAAchB,KAAK;QAC5BX,OAAOA,MAAMS;QACbmC,SAASC,MAAMd,KAAKW,SAAAA;MACtB,CAAA;IACF,CAAA;AAIA,UAAMpB,gBAAgC,CAAA;AACtC,UAAMwB,eAAe/D,mBAAmB,CAAC,EAAEgE,OAAOC,QAAO,MAAE;AACzD,iBAAWC,UAAUF,OAAO;AAC1BL,kBAAUQ,IAAID,OAAOvD,EAAE;MACzB;AACA,iBAAWuD,UAAUD,SAAS;AAC5BN,kBAAUQ,IAAID,OAAOvD,EAAE;MACzB;AAEAgC,WAAKP,SAAQ;IACf,CAAA;AACAG,kBAAc6B,KAAK,MAAML,aAAaM,YAAW,CAAA;AAIjD,UAAM,EAAEC,MAAMC,OAAOC,MAAMC,MAAK,IAAKnC;AACrC,UAAMoC,SAAS,CAAC,EAAEb,QAAO,MAAS;AAChCE,mBAAaW,OAAOb,OAAAA;AAGpB,UAAIW,MAAM;AACRrE,QAAAA,KAAIoD,KAAK,UAAU;UAAEe;UAAME;UAAMX,SAASA,QAAQc;QAAO,GAAA;;;;;;AACzD,mBAAWT,UAAUL,SAAS;AAC5B,gBAAMe,UAAUV,OAAOU;AACvB,cAAIA,mBAAmBjF,YAAY;AACjC4C,0BAAc6B,KACZnE,uBAAuB2E,OAAAA,EAASC,QAAQC,GAAGlF,SAAS,MAAMmE,aAAaW,OAAO;cAACR;aAAO,GAAG,GAAA,CAAA,CAAA;UAE7F;QACF;MACF;IACF;AAIA,UAAMa,QAAQ9D,MAAM+D,GAAGD,MAAMhF,OAAOkF,SAASX,MAAMC,KAAAA,CAAAA;AACnDhC,kBAAc6B,KAAKW,MAAM/D,UAAUyD,QAAQ7E,SAAS8E,QAAQD,QAAQ,GAAA,IAASC,MAAAA,CAAAA;AAE7EjD,QAAIiC,UAAU,MAAA;AACZnB,oBAAc2C,QAAQ,CAACb,gBAAgBA,YAAAA,CAAAA;IACzC,CAAA;EACF;EAEA,MAAczB,cAAchB,KAAkBuD,MAAW;AACvD,QAAI;AACFhF,MAAAA,KAAI,WAAW;QAAEwB,UAAUC,IAAIjB;MAAG,GAAA;;;;;;AAClC,YAAM,EAAEyE,UAAUC,SAAQ,IAAK,KAAK5E;AACpC,UAAI6E,SAAS;AACb,UAAIF,UAAU;AAEZ,cAAMG,WAAW,MAAMC,MAAM,GAAG,KAAK/E,SAAS2E,QAAQ,IAAIxD,IAAI6D,IAAI,IAAI;UACpEC,QAAQ;UACRC,SAAS;YACP,gBAAgB;UAClB;UACAC,MAAMC,KAAKC,UAAUX,IAAAA;QACvB,CAAA;AAEAG,iBAASC,SAASD;MACpB,WAAWD,UAAU;AACnBC,iBAAS,MAAMD,SAASF,IAAAA;MAC1B;AAGAhF,MAAAA,KAAI,UAAU;QAAEwB,UAAUC,IAAIjB;QAAIoF,QAAQT;MAAO,GAAA;;;;;;IACnD,SAASU,KAAU;AACjB7F,MAAAA,KAAI8F,MAAM,SAAS;QAAEtE,UAAUC,IAAIjB;QAAIsF,OAAOD,IAAIE;MAAQ,GAAA;;;;;;IAC5D;EACF;AACF;",
|
|
6
|
+
"names": ["PublicKey", "log", "nonNullable", "subscriptionHandler", "handler", "event", "context", "rest", "client", "space", "spaces", "get", "from", "undefined", "objects", "map", "id", "db", "getObjectById", "filter", "warn", "info", "key", "truncate", "length", "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", "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", "TextV0Type", "debounce", "DeferredTask", "Context", "Filter", "createSubscription", "getAutomergeObjectCore", "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", "_createTimer", "triggerSubscription", "subscriptions", "_createSubscription", "delete", "dispose", "task", "_execFunction", "last", "run", "job", "from", "cronTime", "runOnInit", "onTick", "now", "Date", "delta", "info", "truncate", "count", "onDispose", "objectIds", "Set", "objects", "Array", "subscription", "added", "updated", "object", "add", "push", "unsubscribe", "type", "props", "deep", "delay", "update", "length", "content", "updates", "on", "query", "db", "typename", "forEach", "data", "endpoint", "callback", "status", "response", "fetch", "name", "method", "headers", "body", "JSON", "stringify", "result", "err", "error", "message"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"inject-globals:@inject-globals":{"bytes":384,"imports":[{"path":"@dxos/node-std/inject-globals","kind":"import-statement","external":true}],"format":"esm"},"packages/core/functions/src/handler.ts":{"bytes":
|
|
1
|
+
{"inputs":{"inject-globals:@inject-globals":{"bytes":384,"imports":[{"path":"@dxos/node-std/inject-globals","kind":"import-statement","external":true}],"format":"esm"},"packages/core/functions/src/handler.ts":{"bytes":5873,"imports":[{"path":"@dxos/client","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"@inject-globals","kind":"import-statement","external":true}],"format":"esm"},"packages/core/functions/src/manifest.ts":{"bytes":1910,"imports":[{"path":"@inject-globals","kind":"import-statement","external":true}],"format":"esm"},"packages/core/functions/src/runtime/dev-server.ts":{"bytes":18960,"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":"@inject-globals","kind":"import-statement","external":true},{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"},"packages/core/functions/src/runtime/scheduler.ts":{"bytes":24937,"imports":[{"path":"cron","kind":"import-statement","external":true},{"path":"@braneframe/types","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"@inject-globals","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"},{"path":"@inject-globals","kind":"import-statement","external":true}],"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"},{"path":"@inject-globals","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"packages/core/functions/dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":23779},"packages/core/functions/dist/lib/browser/index.mjs":{"imports":[{"path":"@dxos/node-std/inject-globals","kind":"import-statement","external":true},{"path":"@dxos/client","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"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":"@braneframe/types","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"exports":["DevServer","Scheduler","subscriptionHandler"],"entryPoint":"packages/core/functions/src/index.ts","inputs":{"inject-globals:@inject-globals":{"bytesInOutput":90},"packages/core/functions/src/handler.ts":{"bytesInOutput":1057},"packages/core/functions/src/index.ts":{"bytesInOutput":0},"packages/core/functions/src/runtime/dev-server.ts":{"bytesInOutput":4846},"packages/core/functions/src/runtime/index.ts":{"bytesInOutput":0},"packages/core/functions/src/runtime/scheduler.ts":{"bytesInOutput":5967}},"bytes":12642}}}
|
package/dist/lib/node/index.cjs
CHANGED
|
@@ -34,7 +34,6 @@ __export(node_exports, {
|
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(node_exports);
|
|
36
36
|
var import_client = require("@dxos/client");
|
|
37
|
-
var import_echo_schema = require("@dxos/echo-schema");
|
|
38
37
|
var import_log = require("@dxos/log");
|
|
39
38
|
var import_util = require("@dxos/util");
|
|
40
39
|
var import_express = __toESM(require("express"));
|
|
@@ -44,10 +43,10 @@ var import_async = require("@dxos/async");
|
|
|
44
43
|
var import_invariant = require("@dxos/invariant");
|
|
45
44
|
var import_log2 = require("@dxos/log");
|
|
46
45
|
var import_cron = require("cron");
|
|
46
|
+
var import_types = require("@braneframe/types");
|
|
47
47
|
var import_async2 = require("@dxos/async");
|
|
48
|
-
var import_echo = require("@dxos/client/echo");
|
|
49
48
|
var import_context = require("@dxos/context");
|
|
50
|
-
var
|
|
49
|
+
var import_echo_schema = require("@dxos/echo-schema");
|
|
51
50
|
var import_invariant2 = require("@dxos/invariant");
|
|
52
51
|
var import_log3 = require("@dxos/log");
|
|
53
52
|
var import_util2 = require("@dxos/util");
|
|
@@ -63,13 +62,13 @@ var subscriptionHandler = (handler) => {
|
|
|
63
62
|
return ({ event, context, ...rest }) => {
|
|
64
63
|
const { client } = context;
|
|
65
64
|
const space = event.space ? client.spaces.get(import_client.PublicKey.from(event.space)) : void 0;
|
|
66
|
-
const objects = space && event.objects?.map((id) => space.db.getObjectById(id)).filter(import_util.nonNullable)
|
|
65
|
+
const objects = space && event.objects?.map((id) => space.db.getObjectById(id)).filter(import_util.nonNullable);
|
|
67
66
|
if (!!event.space && !space) {
|
|
68
67
|
import_log.log.warn("invalid space", {
|
|
69
68
|
event
|
|
70
69
|
}, {
|
|
71
70
|
F: __dxlog_file,
|
|
72
|
-
L:
|
|
71
|
+
L: 61,
|
|
73
72
|
S: void 0,
|
|
74
73
|
C: (f, a) => f(...a)
|
|
75
74
|
});
|
|
@@ -79,7 +78,7 @@ var subscriptionHandler = (handler) => {
|
|
|
79
78
|
objects: objects?.length
|
|
80
79
|
}, {
|
|
81
80
|
F: __dxlog_file,
|
|
82
|
-
L:
|
|
81
|
+
L: 63,
|
|
83
82
|
S: void 0,
|
|
84
83
|
C: (f, a) => f(...a)
|
|
85
84
|
});
|
|
@@ -317,7 +316,7 @@ var Scheduler = class {
|
|
|
317
316
|
const def = this._manifest.functions.find((config) => config.id === trigger.function);
|
|
318
317
|
(0, import_invariant2.invariant)(def, `Function not found: ${trigger.function}`, {
|
|
319
318
|
F: __dxlog_file3,
|
|
320
|
-
L:
|
|
319
|
+
L: 64,
|
|
321
320
|
S: this,
|
|
322
321
|
A: [
|
|
323
322
|
"def",
|
|
@@ -335,7 +334,7 @@ var Scheduler = class {
|
|
|
335
334
|
trigger
|
|
336
335
|
}, {
|
|
337
336
|
F: __dxlog_file3,
|
|
338
|
-
L:
|
|
337
|
+
L: 70,
|
|
339
338
|
S: this,
|
|
340
339
|
C: (f, a) => f(...a)
|
|
341
340
|
});
|
|
@@ -369,7 +368,7 @@ var Scheduler = class {
|
|
|
369
368
|
});
|
|
370
369
|
(0, import_invariant2.invariant)(trigger.schedule, void 0, {
|
|
371
370
|
F: __dxlog_file3,
|
|
372
|
-
L:
|
|
371
|
+
L: 103,
|
|
373
372
|
S: this,
|
|
374
373
|
A: [
|
|
375
374
|
"trigger.schedule",
|
|
@@ -392,7 +391,7 @@ var Scheduler = class {
|
|
|
392
391
|
delta
|
|
393
392
|
}, {
|
|
394
393
|
F: __dxlog_file3,
|
|
395
|
-
L:
|
|
394
|
+
L: 117,
|
|
396
395
|
S: this,
|
|
397
396
|
C: (f, a) => f(...a)
|
|
398
397
|
});
|
|
@@ -411,7 +410,7 @@ var Scheduler = class {
|
|
|
411
410
|
});
|
|
412
411
|
});
|
|
413
412
|
const subscriptions = [];
|
|
414
|
-
const subscription = (0,
|
|
413
|
+
const subscription = (0, import_echo_schema.createSubscription)(({ added, updated }) => {
|
|
415
414
|
for (const object of added) {
|
|
416
415
|
objectIds.add(object.id);
|
|
417
416
|
}
|
|
@@ -431,21 +430,21 @@ var Scheduler = class {
|
|
|
431
430
|
objects: objects.length
|
|
432
431
|
}, {
|
|
433
432
|
F: __dxlog_file3,
|
|
434
|
-
L:
|
|
433
|
+
L: 158,
|
|
435
434
|
S: this,
|
|
436
435
|
C: (f, a) => f(...a)
|
|
437
436
|
});
|
|
438
437
|
for (const object of objects) {
|
|
439
438
|
const content = object.content;
|
|
440
|
-
if (content instanceof
|
|
441
|
-
subscriptions.push(content
|
|
439
|
+
if (content instanceof import_types.TextV0Type) {
|
|
440
|
+
subscriptions.push((0, import_echo_schema.getAutomergeObjectCore)(content).updates.on((0, import_async2.debounce)(() => subscription.update([
|
|
442
441
|
object
|
|
443
442
|
]), 1e3)));
|
|
444
443
|
}
|
|
445
444
|
}
|
|
446
445
|
}
|
|
447
446
|
};
|
|
448
|
-
const query = space.db.query(
|
|
447
|
+
const query = space.db.query(import_echo_schema.Filter.typename(type, props));
|
|
449
448
|
subscriptions.push(query.subscribe(delay ? (0, import_async2.debounce)(update, delay * 1e3) : update));
|
|
450
449
|
ctx.onDispose(() => {
|
|
451
450
|
subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
@@ -457,7 +456,7 @@ var Scheduler = class {
|
|
|
457
456
|
function: def.id
|
|
458
457
|
}, {
|
|
459
458
|
F: __dxlog_file3,
|
|
460
|
-
L:
|
|
459
|
+
L: 182,
|
|
461
460
|
S: this,
|
|
462
461
|
C: (f, a) => f(...a)
|
|
463
462
|
});
|
|
@@ -480,7 +479,7 @@ var Scheduler = class {
|
|
|
480
479
|
result: status
|
|
481
480
|
}, {
|
|
482
481
|
F: __dxlog_file3,
|
|
483
|
-
L:
|
|
482
|
+
L: 201,
|
|
484
483
|
S: this,
|
|
485
484
|
C: (f, a) => f(...a)
|
|
486
485
|
});
|
|
@@ -490,7 +489,7 @@ var Scheduler = class {
|
|
|
490
489
|
error: err.message
|
|
491
490
|
}, {
|
|
492
491
|
F: __dxlog_file3,
|
|
493
|
-
L:
|
|
492
|
+
L: 203,
|
|
494
493
|
S: this,
|
|
495
494
|
C: (f, a) => f(...a)
|
|
496
495
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/handler.ts", "../../../src/runtime/dev-server.ts", "../../../src/runtime/scheduler.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Client, PublicKey } from '@dxos/client';\nimport { type Space } from '@dxos/client/echo';\nimport { isTypedObject, type TypedObject } from '@dxos/echo-schema';\nimport { log } from '@dxos/log';\nimport { nonNullable } from '@dxos/util';\n\n// TODO(burdon): No response?\nexport interface Response {\n status(code: number): Response;\n}\n\n// TODO(burdon): Limit access to individual space?\nexport interface FunctionContext {\n client: Client;\n dataDir?: string;\n}\n\n// TODO(burdon): Model after http request. Ref Lambda/OpenFaaS.\n// https://docs.aws.amazon.com/lambda/latest/dg/typescript-handler.html\nexport type FunctionHandler<T extends {}> = (params: {\n event: T;\n context: FunctionContext;\n response: Response;\n}) => Promise<Response | void>;\n\nexport type FunctionSubscriptionEvent = {\n space?: string; // TODO(burdon): Convert to PublicKey.\n objects?: string[];\n};\n\nexport type FunctionSubscriptionEvent2 = {\n space?: Space;\n objects?: TypedObject[];\n};\n\n/**\n * Handler wrapper for subscription events; extracts space and objects.\n *\n * To test:\n * ```\n * curl -s -X POST -H \"Content-Type: application/json\" --data '{\"space\": \"0446...1cbb\"}' http://localhost:7100/dev/email-extractor\n * ```\n *\n * NOTE: Get space key from devtools or `dx space list --json`\n */\nexport const subscriptionHandler = (\n handler: FunctionHandler<FunctionSubscriptionEvent2>,\n): FunctionHandler<FunctionSubscriptionEvent> => {\n return ({ event, context, ...rest }) => {\n const { client } = context;\n const space = event.space ? client.spaces.get(PublicKey.from(event.space)) : undefined;\n const objects =\n space &&\n event.objects\n ?.map<TypedObject | undefined>((id) => space!.db.getObjectById(id))\n .filter(nonNullable)\n .filter(isTypedObject);\n\n if (!!event.space && !space) {\n log.warn('invalid space', { event });\n } else {\n log.info('handler', { space: space?.key.truncate(), objects: objects?.length });\n }\n\n return handler({ event: { space, objects }, context, ...rest });\n };\n};\n", "//\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.error(`Function failed: ${name}`, 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 FunctionSubscriptionEvent } from '../handler';\nimport { type FunctionDef, type FunctionManifest, type FunctionTrigger, type TriggerSubscription } from '../manifest';\n\ntype Callback = (data: FunctionSubscriptionEvent) => Promise<number>;\n\ntype SchedulerOptions = {\n endpoint?: string;\n callback?: Callback;\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 // Currently supports only one trigger declaration per function.\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 // Timer.\n if (trigger.schedule) {\n this._createTimer(ctx, space, def, trigger);\n }\n\n // Subscription.\n for (const triggerSubscription of trigger.subscriptions ?? []) {\n this._createSubscription(ctx, space, def, triggerSubscription);\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 _createTimer(ctx: Context, space: Space, def: FunctionDef, trigger: FunctionTrigger) {\n const task = new DeferredTask(ctx, async () => {\n await this._execFunction(def, {\n space: space.key,\n });\n });\n\n invariant(trigger.schedule);\n let last = 0;\n let run = 0;\n // https://www.npmjs.com/package/cron#constructor\n const job = CronJob.from({\n cronTime: trigger.schedule,\n runOnInit: false,\n onTick: () => {\n // TODO(burdon): Check greater than 30s (use cron-parser).\n const now = Date.now();\n const delta = last ? now - last : 0;\n last = now;\n\n run++;\n log.info('tick', { space: space.key.truncate(), count: run, delta });\n task.schedule();\n },\n });\n\n job.start();\n ctx.onDispose(() => job.stop());\n }\n\n private _createSubscription(ctx: Context, space: Space, def: FunctionDef, triggerSubscription: TriggerSubscription) {\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): Don't fire initially.\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 // TODO(burdon): Create queue. Only allow one invocation per trigger at a time?\n // TODO(burdon): Disable trigger if keeps failing.\n const { type, props, deep, delay } = triggerSubscription;\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 private async _execFunction(def: FunctionDef, data: any) {\n try {\n log('request', { function: def.id });\n const { endpoint, callback } = this._options;\n let status = 0;\n if (endpoint) {\n // TODO(burdon): Move out of scheduler (generalize as callback).\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 status = response.status;\n } else if (callback) {\n status = await callback(data);\n }\n\n // const result = await response.json();\n log('result', { function: def.id, result: status });\n } catch (err: any) {\n log.error('error', { function: def.id, error: err.message });\n }\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,oBAAuC;
|
|
6
|
-
"names": ["import_log", "import_async", "
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Client, PublicKey } from '@dxos/client';\nimport { type Space } from '@dxos/client/echo';\nimport { type EchoReactiveObject } from '@dxos/echo-schema';\nimport { log } from '@dxos/log';\nimport { nonNullable } from '@dxos/util';\n\n// TODO(burdon): No response?\nexport interface Response {\n status(code: number): Response;\n}\n\n// TODO(burdon): Limit access to individual space?\nexport interface FunctionContext {\n client: Client;\n dataDir?: string;\n}\n\n// TODO(burdon): Model after http request. Ref Lambda/OpenFaaS.\n// https://docs.aws.amazon.com/lambda/latest/dg/typescript-handler.html\nexport type FunctionHandler<T extends {}> = (params: {\n event: T;\n context: FunctionContext;\n response: Response;\n}) => Promise<Response | void>;\n\nexport type FunctionSubscriptionEvent = {\n space?: string; // TODO(burdon): Convert to PublicKey.\n objects?: string[];\n};\n\nexport type FunctionSubscriptionEvent2 = {\n space?: Space;\n objects?: EchoReactiveObject<any>[];\n};\n\n/**\n * Handler wrapper for subscription events; extracts space and objects.\n *\n * To test:\n * ```\n * curl -s -X POST -H \"Content-Type: application/json\" --data '{\"space\": \"0446...1cbb\"}' http://localhost:7100/dev/email-extractor\n * ```\n *\n * NOTE: Get space key from devtools or `dx space list --json`\n */\nexport const subscriptionHandler = (\n handler: FunctionHandler<FunctionSubscriptionEvent2>,\n): FunctionHandler<FunctionSubscriptionEvent> => {\n return ({ event, context, ...rest }) => {\n const { client } = context;\n const space = event.space ? client.spaces.get(PublicKey.from(event.space)) : undefined;\n const objects =\n space &&\n event.objects?.map<EchoReactiveObject<any> | undefined>((id) => space!.db.getObjectById(id)).filter(nonNullable);\n\n if (!!event.space && !space) {\n log.warn('invalid space', { event });\n } else {\n log.info('handler', { space: space?.key.truncate(), objects: objects?.length });\n }\n\n return handler({ event: { space, objects }, context, ...rest });\n };\n};\n", "//\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.error(`Function failed: ${name}`, 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 { TextV0Type } from '@braneframe/types';\nimport { debounce, 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, type Query, getAutomergeObjectCore } from '@dxos/echo-schema';\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\nimport { ComplexMap } from '@dxos/util';\n\nimport { type FunctionSubscriptionEvent } from '../handler';\nimport { type FunctionDef, type FunctionManifest, type FunctionTrigger, type TriggerSubscription } from '../manifest';\n\ntype Callback = (data: FunctionSubscriptionEvent) => Promise<number>;\n\ntype SchedulerOptions = {\n endpoint?: string;\n callback?: Callback;\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 // Currently supports only one trigger declaration per function.\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 // Timer.\n if (trigger.schedule) {\n this._createTimer(ctx, space, def, trigger);\n }\n\n // Subscription.\n for (const triggerSubscription of trigger.subscriptions ?? []) {\n this._createSubscription(ctx, space, def, triggerSubscription);\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 _createTimer(ctx: Context, space: Space, def: FunctionDef, trigger: FunctionTrigger) {\n const task = new DeferredTask(ctx, async () => {\n await this._execFunction(def, {\n space: space.key,\n });\n });\n\n invariant(trigger.schedule);\n let last = 0;\n let run = 0;\n // https://www.npmjs.com/package/cron#constructor\n const job = CronJob.from({\n cronTime: trigger.schedule,\n runOnInit: false,\n onTick: () => {\n // TODO(burdon): Check greater than 30s (use cron-parser).\n const now = Date.now();\n const delta = last ? now - last : 0;\n last = now;\n\n run++;\n log.info('tick', { space: space.key.truncate(), count: run, delta });\n task.schedule();\n },\n });\n\n job.start();\n ctx.onDispose(() => job.stop());\n }\n\n private _createSubscription(ctx: Context, space: Space, def: FunctionDef, triggerSubscription: TriggerSubscription) {\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): Don't fire initially.\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 // TODO(burdon): Create queue. Only allow one invocation per trigger at a time?\n // TODO(burdon): Disable trigger if keeps failing.\n const { type, props, deep, delay } = triggerSubscription;\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 TextV0Type) {\n subscriptions.push(\n getAutomergeObjectCore(content).updates.on(debounce(() => subscription.update([object]), 1_000)),\n );\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 private async _execFunction(def: FunctionDef, data: any) {\n try {\n log('request', { function: def.id });\n const { endpoint, callback } = this._options;\n let status = 0;\n if (endpoint) {\n // TODO(burdon): Move out of scheduler (generalize as callback).\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 status = response.status;\n } else if (callback) {\n status = await callback(data);\n }\n\n // const result = await response.json();\n log('result', { function: def.id, result: 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_log", "import_async", "import_invariant", "import_util", "subscriptionHandler", "handler", "event", "context", "rest", "client", "space", "spaces", "get", "PublicKey", "from", "undefined", "objects", "map", "id", "db", "getObjectById", "filter", "nonNullable", "log", "warn", "info", "key", "truncate", "length", "DevServer", "constructor", "_client", "_options", "_handlers", "_seq", "endpoint", "invariant", "_port", "proxy", "_proxy", "functions", "Object", "values", "initialize", "def", "manifest", "_load", "err", "error", "start", "app", "express", "use", "json", "post", "req", "res", "name", "params", "reload", "statusCode", "_invoke", "body", "end", "getPort", "host", "port", "portRange", "_server", "listen", "registrationId", "services", "FunctionRegistryService", "register", "_registrationId", "stop", "Error", "trigger", "Trigger", "close", "unregister", "wake", "wait", "flush", "path", "join", "directory", "keys", "require", "cache", "startsWith", "forEach", "module", "default", "seq", "now", "Date", "dataDir", "response", "status", "code", "duration", "Scheduler", "_manifest", "_mounts", "ComplexMap", "spaceKey", "toHex", "subscribe", "waitUntilReady", "triggers", "mount", "Context", "unmount", "ctx", "function", "find", "config", "exists", "set", "disposed", "schedule", "_createTimer", "triggerSubscription", "subscriptions", "_createSubscription", "delete", "dispose", "task", "DeferredTask", "_execFunction", "last", "run", "job", "CronJob", "cronTime", "runOnInit", "onTick", "delta", "count", "onDispose", "objectIds", "Set", "Array", "subscription", "createSubscription", "added", "updated", "object", "add", "push", "unsubscribe", "type", "props", "deep", "delay", "update", "content", "TextV0Type", "getAutomergeObjectCore", "updates", "on", "debounce", "query", "Filter", "typename", "data", "callback", "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":5873,"imports":[{"path":"@dxos/client","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/manifest.ts":{"bytes":1910,"imports":[],"format":"esm"},"packages/core/functions/src/runtime/dev-server.ts":{"bytes":18960,"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":24937,"imports":[{"path":"cron","kind":"import-statement","external":true},{"path":"@braneframe/types","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"format":"esm"},"packages/core/functions/src/runtime/index.ts":{"bytes":566,"imports":[{"path":"packages/core/functions/src/runtime/dev-server.ts","kind":"import-statement","original":"./dev-server"},{"path":"packages/core/functions/src/runtime/scheduler.ts","kind":"import-statement","original":"./scheduler"}],"format":"esm"},"packages/core/functions/src/index.ts":{"bytes":637,"imports":[{"path":"packages/core/functions/src/handler.ts","kind":"import-statement","original":"./handler"},{"path":"packages/core/functions/src/manifest.ts","kind":"import-statement","original":"./manifest"},{"path":"packages/core/functions/src/runtime/index.ts","kind":"import-statement","original":"./runtime"}],"format":"esm"}},"outputs":{"packages/core/functions/dist/lib/node/index.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":23771},"packages/core/functions/dist/lib/node/index.cjs":{"imports":[{"path":"@dxos/client","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"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":"@braneframe/types","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"exports":["DevServer","Scheduler","subscriptionHandler"],"entryPoint":"packages/core/functions/src/index.ts","inputs":{"packages/core/functions/src/handler.ts":{"bytesInOutput":1057},"packages/core/functions/src/index.ts":{"bytesInOutput":0},"packages/core/functions/src/runtime/dev-server.ts":{"bytesInOutput":4836},"packages/core/functions/src/runtime/index.ts":{"bytesInOutput":0},"packages/core/functions/src/runtime/scheduler.ts":{"bytesInOutput":5967}},"bytes":12474}}}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Client } from '@dxos/client';
|
|
2
2
|
import { type Space } from '@dxos/client/echo';
|
|
3
|
-
import { type
|
|
3
|
+
import { type EchoReactiveObject } from '@dxos/echo-schema';
|
|
4
4
|
export interface Response {
|
|
5
5
|
status(code: number): Response;
|
|
6
6
|
}
|
|
@@ -19,7 +19,7 @@ export type FunctionSubscriptionEvent = {
|
|
|
19
19
|
};
|
|
20
20
|
export type FunctionSubscriptionEvent2 = {
|
|
21
21
|
space?: Space;
|
|
22
|
-
objects?:
|
|
22
|
+
objects?: EchoReactiveObject<any>[];
|
|
23
23
|
};
|
|
24
24
|
/**
|
|
25
25
|
* Handler wrapper for subscription events; extracts space and objects.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/handler.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,MAAM,EAAa,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/handler.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,MAAM,EAAa,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAK5D,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,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,OAAO,CAAC,EAAE,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;CACrC,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,YACrB,gBAAgB,0BAA0B,CAAC,KACnD,gBAAgB,yBAAyB,CAgB3C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../../../src/runtime/scheduler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../../../src/runtime/scheduler.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,MAAM,EAAkB,MAAM,cAAc,CAAC;AAQ3D,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAoB,KAAK,gBAAgB,EAAkD,MAAM,aAAa,CAAC;AAEtH,KAAK,QAAQ,GAAG,CAAC,IAAI,EAAE,yBAAyB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAErE,KAAK,gBAAgB,GAAG;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB,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,GAAE,gBAAqB;IAG5C,KAAK;IAWL,IAAI;YAMI,KAAK;YA0BL,OAAO;IASrB,OAAO,CAAC,YAAY;IA8BpB,OAAO,CAAC,mBAAmB;YAsDb,aAAa;CA0B5B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/functions",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.10-main.068c3d8",
|
|
4
4
|
"description": "Functions SDK and runtime.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -21,16 +21,17 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@preact/signals-core": "^1.6.0",
|
|
23
23
|
"cron": "^3.1.6",
|
|
24
|
-
"express": "^4.
|
|
24
|
+
"express": "^4.19.2",
|
|
25
25
|
"get-port-please": "^3.1.1",
|
|
26
|
-
"@
|
|
27
|
-
"@dxos/
|
|
28
|
-
"@dxos/
|
|
29
|
-
"@dxos/
|
|
30
|
-
"@dxos/echo-schema": "0.4.
|
|
31
|
-
"@dxos/
|
|
32
|
-
"@dxos/node-std": "0.4.
|
|
33
|
-
"@dxos/
|
|
26
|
+
"@braneframe/types": "0.4.10-main.068c3d8",
|
|
27
|
+
"@dxos/client": "0.4.10-main.068c3d8",
|
|
28
|
+
"@dxos/context": "0.4.10-main.068c3d8",
|
|
29
|
+
"@dxos/async": "0.4.10-main.068c3d8",
|
|
30
|
+
"@dxos/echo-schema": "0.4.10-main.068c3d8",
|
|
31
|
+
"@dxos/invariant": "0.4.10-main.068c3d8",
|
|
32
|
+
"@dxos/node-std": "0.4.10-main.068c3d8",
|
|
33
|
+
"@dxos/log": "0.4.10-main.068c3d8",
|
|
34
|
+
"@dxos/util": "0.4.10-main.068c3d8"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@types/express": "^4.17.17"
|
package/src/handler.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { type Client, PublicKey } from '@dxos/client';
|
|
6
6
|
import { type Space } from '@dxos/client/echo';
|
|
7
|
-
import {
|
|
7
|
+
import { type EchoReactiveObject } from '@dxos/echo-schema';
|
|
8
8
|
import { log } from '@dxos/log';
|
|
9
9
|
import { nonNullable } from '@dxos/util';
|
|
10
10
|
|
|
@@ -34,7 +34,7 @@ export type FunctionSubscriptionEvent = {
|
|
|
34
34
|
|
|
35
35
|
export type FunctionSubscriptionEvent2 = {
|
|
36
36
|
space?: Space;
|
|
37
|
-
objects?:
|
|
37
|
+
objects?: EchoReactiveObject<any>[];
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
/**
|
|
@@ -55,10 +55,7 @@ export const subscriptionHandler = (
|
|
|
55
55
|
const space = event.space ? client.spaces.get(PublicKey.from(event.space)) : undefined;
|
|
56
56
|
const objects =
|
|
57
57
|
space &&
|
|
58
|
-
event.objects
|
|
59
|
-
?.map<TypedObject | undefined>((id) => space!.db.getObjectById(id))
|
|
60
|
-
.filter(nonNullable)
|
|
61
|
-
.filter(isTypedObject);
|
|
58
|
+
event.objects?.map<EchoReactiveObject<any> | undefined>((id) => space!.db.getObjectById(id)).filter(nonNullable);
|
|
62
59
|
|
|
63
60
|
if (!!event.space && !space) {
|
|
64
61
|
log.warn('invalid space', { event });
|
package/src/runtime/scheduler.ts
CHANGED
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
import { CronJob } from 'cron';
|
|
6
6
|
|
|
7
|
+
import { TextV0Type } from '@braneframe/types';
|
|
7
8
|
import { debounce, DeferredTask } from '@dxos/async';
|
|
8
9
|
import { type Client, type PublicKey } from '@dxos/client';
|
|
9
|
-
import { type Space
|
|
10
|
+
import { type Space } from '@dxos/client/echo';
|
|
10
11
|
import { Context } from '@dxos/context';
|
|
11
|
-
import { Filter, createSubscription, type Query,
|
|
12
|
+
import { Filter, createSubscription, type Query, getAutomergeObjectCore } from '@dxos/echo-schema';
|
|
12
13
|
import { invariant } from '@dxos/invariant';
|
|
13
14
|
import { log } from '@dxos/log';
|
|
14
15
|
import { ComplexMap } from '@dxos/util';
|
|
@@ -157,8 +158,10 @@ export class Scheduler {
|
|
|
157
158
|
log.info('update', { type, deep, objects: objects.length });
|
|
158
159
|
for (const object of objects) {
|
|
159
160
|
const content = object.content;
|
|
160
|
-
if (content instanceof
|
|
161
|
-
subscriptions.push(
|
|
161
|
+
if (content instanceof TextV0Type) {
|
|
162
|
+
subscriptions.push(
|
|
163
|
+
getAutomergeObjectCore(content).updates.on(debounce(() => subscription.update([object]), 1_000)),
|
|
164
|
+
);
|
|
162
165
|
}
|
|
163
166
|
}
|
|
164
167
|
}
|