@dxos/functions 0.3.9-main.d5f3fd3 → 0.3.9-main.de60860

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.
@@ -17,12 +17,34 @@ import {
17
17
  // packages/core/functions/src/handler.ts
18
18
  import { PublicKey } from "@dxos/client";
19
19
  import { isTypedObject } from "@dxos/echo-schema";
20
+ import { log } from "@dxos/log";
20
21
  import { nonNullable } from "@dxos/util";
22
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/functions/src/handler.ts";
21
23
  var subscriptionHandler = (handler) => {
22
24
  return ({ event, context, ...rest }) => {
23
25
  const { client } = context;
24
26
  const space = event.space ? client.spaces.get(PublicKey.from(event.space)) : void 0;
25
27
  const objects = space && event.objects?.map((id) => space.db.getObjectById(id)).filter(nonNullable).filter(isTypedObject);
28
+ if (!!event.space && !space) {
29
+ log.warn("invalid space", {
30
+ event
31
+ }, {
32
+ F: __dxlog_file,
33
+ L: 64,
34
+ S: void 0,
35
+ C: (f, a) => f(...a)
36
+ });
37
+ } else {
38
+ log.info("handler", {
39
+ space: space?.key.truncate(),
40
+ objects: objects?.length
41
+ }, {
42
+ F: __dxlog_file,
43
+ L: 66,
44
+ S: void 0,
45
+ C: (f, a) => f(...a)
46
+ });
47
+ }
26
48
  return handler({
27
49
  event: {
28
50
  space,
@@ -40,8 +62,8 @@ import { getPort } from "get-port-please";
40
62
  import { join } from "@dxos/node-std/path";
41
63
  import { Trigger } from "@dxos/async";
42
64
  import { invariant } from "@dxos/invariant";
43
- import { log } from "@dxos/log";
44
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/dev-server.ts";
65
+ import { log as log2 } from "@dxos/log";
66
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/dev-server.ts";
45
67
  var DevServer = class {
46
68
  // prettier-ignore
47
69
  constructor(_client, _options) {
@@ -52,7 +74,7 @@ var DevServer = class {
52
74
  }
53
75
  get endpoint() {
54
76
  invariant(this._port, void 0, {
55
- F: __dxlog_file,
77
+ F: __dxlog_file2,
56
78
  L: 46,
57
79
  S: this,
58
80
  A: [
@@ -73,8 +95,8 @@ var DevServer = class {
73
95
  try {
74
96
  await this._load(def);
75
97
  } catch (err) {
76
- log.error("parsing function (check manifest)", err, {
77
- F: __dxlog_file,
98
+ log2.error("parsing function (check manifest)", err, {
99
+ F: __dxlog_file2,
78
100
  L: 63,
79
101
  S: this,
80
102
  C: (f, a) => f(...a)
@@ -95,8 +117,8 @@ var DevServer = class {
95
117
  res.statusCode = await this._invoke(name, req.body);
96
118
  res.end();
97
119
  } catch (err) {
98
- log.catch(err, void 0, {
99
- F: __dxlog_file,
120
+ log2.error(`Function failed: ${name}`, err, {
121
+ F: __dxlog_file2,
100
122
  L: 83,
101
123
  S: this,
102
124
  C: (f, a) => f(...a)
@@ -121,11 +143,11 @@ var DevServer = class {
121
143
  name
122
144
  }))
123
145
  });
124
- log.info("registered", {
146
+ log2.info("registered", {
125
147
  registrationId,
126
148
  endpoint
127
149
  }, {
128
- F: __dxlog_file,
150
+ F: __dxlog_file2,
129
151
  L: 99,
130
152
  S: this,
131
153
  C: (f, a) => f(...a)
@@ -144,10 +166,10 @@ var DevServer = class {
144
166
  await this._client.services.services.FunctionRegistryService.unregister({
145
167
  registrationId: this._registrationId
146
168
  });
147
- log.info("unregistered", {
169
+ log2.info("unregistered", {
148
170
  registrationId: this._registrationId
149
171
  }, {
150
- F: __dxlog_file,
172
+ F: __dxlog_file2,
151
173
  L: 116,
152
174
  S: this,
153
175
  C: (f, a) => f(...a)
@@ -167,10 +189,10 @@ var DevServer = class {
167
189
  async _load(def, flush = false) {
168
190
  const { id, name, handler } = def;
169
191
  const path = join(this._options.directory, handler);
170
- log.info("loading", {
192
+ log2.info("loading", {
171
193
  id
172
194
  }, {
173
- F: __dxlog_file,
195
+ F: __dxlog_file2,
174
196
  L: 135,
175
197
  S: this,
176
198
  C: (f, a) => f(...a)
@@ -193,11 +215,11 @@ var DevServer = class {
193
215
  async _invoke(name, event) {
194
216
  const seq = ++this._seq;
195
217
  const now = Date.now();
196
- log.info("req", {
218
+ log2.info("req", {
197
219
  seq,
198
220
  name
199
221
  }, {
200
- F: __dxlog_file,
222
+ F: __dxlog_file2,
201
223
  L: 160,
202
224
  S: this,
203
225
  C: (f, a) => f(...a)
@@ -219,13 +241,13 @@ var DevServer = class {
219
241
  event,
220
242
  response
221
243
  });
222
- log.info("res", {
244
+ log2.info("res", {
223
245
  seq,
224
246
  name,
225
247
  statusCode,
226
248
  duration: Date.now() - now
227
249
  }, {
228
- F: __dxlog_file,
250
+ F: __dxlog_file2,
229
251
  L: 177,
230
252
  S: this,
231
253
  C: (f, a) => f(...a)
@@ -241,9 +263,9 @@ import { TextObject } from "@dxos/client/echo";
241
263
  import { Context } from "@dxos/context";
242
264
  import { Filter, createSubscription, subscribe } from "@dxos/echo-schema";
243
265
  import { invariant as invariant2 } from "@dxos/invariant";
244
- import { log as log2 } from "@dxos/log";
266
+ import { log as log3 } from "@dxos/log";
245
267
  import { ComplexMap } from "@dxos/util";
246
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/scheduler.ts";
268
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/scheduler.ts";
247
269
  var Scheduler = class {
248
270
  constructor(_client, _manifest, _options = {}) {
249
271
  this._client = _client;
@@ -273,7 +295,7 @@ var Scheduler = class {
273
295
  };
274
296
  const def = this._manifest.functions.find((config) => config.id === trigger.function);
275
297
  invariant2(def, `Function not found: ${trigger.function}`, {
276
- F: __dxlog_file2,
298
+ F: __dxlog_file3,
277
299
  L: 63,
278
300
  S: this,
279
301
  A: [
@@ -287,11 +309,11 @@ var Scheduler = class {
287
309
  ctx,
288
310
  trigger
289
311
  });
290
- log2("mount", {
312
+ log3("mount", {
291
313
  space: space.key,
292
314
  trigger
293
315
  }, {
294
- F: __dxlog_file2,
316
+ F: __dxlog_file3,
295
317
  L: 69,
296
318
  S: this,
297
319
  C: (f, a) => f(...a)
@@ -325,7 +347,7 @@ var Scheduler = class {
325
347
  });
326
348
  });
327
349
  invariant2(trigger.schedule, void 0, {
328
- F: __dxlog_file2,
350
+ F: __dxlog_file3,
329
351
  L: 103,
330
352
  S: this,
331
353
  A: [
@@ -360,13 +382,13 @@ var Scheduler = class {
360
382
  const update = ({ objects }) => {
361
383
  subscription.update(objects);
362
384
  if (deep) {
363
- log2.info("update", {
385
+ log3.info("update", {
364
386
  type,
365
387
  deep,
366
388
  objects: objects.length
367
389
  }, {
368
- F: __dxlog_file2,
369
- L: 139,
390
+ F: __dxlog_file3,
391
+ L: 142,
370
392
  S: this,
371
393
  C: (f, a) => f(...a)
372
394
  });
@@ -388,11 +410,11 @@ var Scheduler = class {
388
410
  }
389
411
  async _execFunction(def, data) {
390
412
  try {
391
- log2("request", {
413
+ log3("request", {
392
414
  function: def.id
393
415
  }, {
394
- F: __dxlog_file2,
395
- L: 161,
416
+ F: __dxlog_file3,
417
+ L: 164,
396
418
  S: this,
397
419
  C: (f, a) => f(...a)
398
420
  });
@@ -410,22 +432,22 @@ var Scheduler = class {
410
432
  } else if (callback) {
411
433
  status = await callback(data);
412
434
  }
413
- log2("result", {
435
+ log3("result", {
414
436
  function: def.id,
415
437
  result: status
416
438
  }, {
417
- F: __dxlog_file2,
418
- L: 180,
439
+ F: __dxlog_file3,
440
+ L: 183,
419
441
  S: this,
420
442
  C: (f, a) => f(...a)
421
443
  });
422
444
  } catch (err) {
423
- log2.error("error", {
445
+ log3.error("error", {
424
446
  function: def.id,
425
447
  error: err.message
426
448
  }, {
427
- F: __dxlog_file2,
428
- L: 182,
449
+ F: __dxlog_file3,
450
+ L: 185,
429
451
  S: this,
430
452
  C: (f, a) => f(...a)
431
453
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["inject-globals:@inject-globals", "../../../src/handler.ts", "../../../src/runtime/dev-server.ts", "../../../src/runtime/scheduler.ts"],
4
- "sourcesContent": ["\n export {\n global,\nBuffer,\nprocess\n } from '@dxos/node-std/inject-globals';\n ", "//\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 { 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\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 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.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 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 // TODO(burdon): Check greater than 30s min (use cron-parser).\n invariant(trigger.schedule);\n const job = new CronJob(trigger.schedule, () => task.schedule());\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): 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 } = 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": "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",
6
- "names": ["Buffer", "PublicKey", "isTypedObject", "nonNullable", "subscriptionHandler", "handler", "event", "context", "rest", "client", "space", "spaces", "get", "PublicKey", "from", "undefined", "objects", "map", "id", "db", "getObjectById", "filter", "nonNullable", "isTypedObject", "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", "_createTimer", "triggerSubscription", "subscriptions", "_createSubscription", "delete", "dispose", "task", "_execFunction", "job", "onDispose", "objectIds", "Set", "objects", "Array", "from", "subscription", "added", "updated", "object", "add", "push", "unsubscribe", "type", "props", "deep", "delay", "update", "info", "length", "content", "query", "db", "typename", "forEach", "data", "endpoint", "callback", "status", "response", "fetch", "name", "method", "headers", "body", "JSON", "stringify", "result", "err", "error", "message"]
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 // TODO(burdon): Check greater than 30s min (use cron-parser).\n invariant(trigger.schedule);\n const job = new CronJob(trigger.schedule, () => task.schedule());\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": "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",
6
+ "names": ["PublicKey", "isTypedObject", "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", "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", "_createTimer", "triggerSubscription", "subscriptions", "_createSubscription", "delete", "dispose", "task", "_execFunction", "job", "onDispose", "objectIds", "Set", "objects", "Array", "from", "subscription", "added", "updated", "object", "add", "push", "unsubscribe", "type", "props", "deep", "delay", "update", "info", "length", "content", "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":154,"imports":[{"path":"@dxos/node-std/inject-globals","kind":"import-statement","external":true}],"format":"esm"},"packages/core/functions/src/handler.ts":{"bytes":4072,"imports":[{"path":"@dxos/client","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","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":1863,"imports":[{"path":"@inject-globals","kind":"import-statement","external":true}],"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":"@inject-globals","kind":"import-statement","external":true},{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"},"packages/core/functions/src/runtime/scheduler.ts":{"bytes":22471,"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},{"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":22175},"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/echo-schema","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":"@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","subscriptionHandler"],"entryPoint":"packages/core/functions/src/index.ts","inputs":{"inject-globals:@inject-globals":{"bytesInOutput":90},"packages/core/functions/src/handler.ts":{"bytesInOutput":589},"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":5475}},"bytes":11638}}}
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":6074,"imports":[{"path":"@dxos/client","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","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":1863,"imports":[{"path":"@inject-globals","kind":"import-statement","external":true}],"format":"esm"},"packages/core/functions/src/runtime/dev-server.ts":{"bytes":18932,"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":22961,"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},{"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":22950},"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/echo-schema","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":"@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","subscriptionHandler"],"entryPoint":"packages/core/functions/src/index.ts","inputs":{"inject-globals:@inject-globals":{"bytesInOutput":90},"packages/core/functions/src/handler.ts":{"bytesInOutput":1130},"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":5475}},"bytes":12223}}}
@@ -35,20 +35,21 @@ __export(node_exports, {
35
35
  module.exports = __toCommonJS(node_exports);
36
36
  var import_client = require("@dxos/client");
37
37
  var import_echo_schema = require("@dxos/echo-schema");
38
+ var import_log = require("@dxos/log");
38
39
  var import_util = require("@dxos/util");
39
40
  var import_express = __toESM(require("express"));
40
41
  var import_get_port_please = require("get-port-please");
41
42
  var import_node_path = require("node:path");
42
43
  var import_async = require("@dxos/async");
43
44
  var import_invariant = require("@dxos/invariant");
44
- var import_log = require("@dxos/log");
45
+ var import_log2 = require("@dxos/log");
45
46
  var import_cron = require("cron");
46
47
  var import_async2 = require("@dxos/async");
47
48
  var import_echo = require("@dxos/client/echo");
48
49
  var import_context = require("@dxos/context");
49
50
  var import_echo_schema2 = require("@dxos/echo-schema");
50
51
  var import_invariant2 = require("@dxos/invariant");
51
- var import_log2 = require("@dxos/log");
52
+ var import_log3 = require("@dxos/log");
52
53
  var import_util2 = require("@dxos/util");
53
54
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
54
55
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
@@ -57,11 +58,32 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
57
58
  return require.apply(this, arguments);
58
59
  throw Error('Dynamic require of "' + x + '" is not supported');
59
60
  });
61
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/functions/src/handler.ts";
60
62
  var subscriptionHandler = (handler) => {
61
63
  return ({ event, context, ...rest }) => {
62
64
  const { client } = context;
63
65
  const space = event.space ? client.spaces.get(import_client.PublicKey.from(event.space)) : void 0;
64
66
  const objects = space && event.objects?.map((id) => space.db.getObjectById(id)).filter(import_util.nonNullable).filter(import_echo_schema.isTypedObject);
67
+ if (!!event.space && !space) {
68
+ import_log.log.warn("invalid space", {
69
+ event
70
+ }, {
71
+ F: __dxlog_file,
72
+ L: 64,
73
+ S: void 0,
74
+ C: (f, a) => f(...a)
75
+ });
76
+ } else {
77
+ import_log.log.info("handler", {
78
+ space: space?.key.truncate(),
79
+ objects: objects?.length
80
+ }, {
81
+ F: __dxlog_file,
82
+ L: 66,
83
+ S: void 0,
84
+ C: (f, a) => f(...a)
85
+ });
86
+ }
65
87
  return handler({
66
88
  event: {
67
89
  space,
@@ -72,7 +94,7 @@ var subscriptionHandler = (handler) => {
72
94
  });
73
95
  };
74
96
  };
75
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/dev-server.ts";
97
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/dev-server.ts";
76
98
  var DevServer = class {
77
99
  // prettier-ignore
78
100
  constructor(_client, _options) {
@@ -83,7 +105,7 @@ var DevServer = class {
83
105
  }
84
106
  get endpoint() {
85
107
  (0, import_invariant.invariant)(this._port, void 0, {
86
- F: __dxlog_file,
108
+ F: __dxlog_file2,
87
109
  L: 46,
88
110
  S: this,
89
111
  A: [
@@ -104,8 +126,8 @@ var DevServer = class {
104
126
  try {
105
127
  await this._load(def);
106
128
  } catch (err) {
107
- import_log.log.error("parsing function (check manifest)", err, {
108
- F: __dxlog_file,
129
+ import_log2.log.error("parsing function (check manifest)", err, {
130
+ F: __dxlog_file2,
109
131
  L: 63,
110
132
  S: this,
111
133
  C: (f, a) => f(...a)
@@ -126,8 +148,8 @@ var DevServer = class {
126
148
  res.statusCode = await this._invoke(name, req.body);
127
149
  res.end();
128
150
  } catch (err) {
129
- import_log.log.catch(err, void 0, {
130
- F: __dxlog_file,
151
+ import_log2.log.error(`Function failed: ${name}`, err, {
152
+ F: __dxlog_file2,
131
153
  L: 83,
132
154
  S: this,
133
155
  C: (f, a) => f(...a)
@@ -152,11 +174,11 @@ var DevServer = class {
152
174
  name
153
175
  }))
154
176
  });
155
- import_log.log.info("registered", {
177
+ import_log2.log.info("registered", {
156
178
  registrationId,
157
179
  endpoint
158
180
  }, {
159
- F: __dxlog_file,
181
+ F: __dxlog_file2,
160
182
  L: 99,
161
183
  S: this,
162
184
  C: (f, a) => f(...a)
@@ -175,10 +197,10 @@ var DevServer = class {
175
197
  await this._client.services.services.FunctionRegistryService.unregister({
176
198
  registrationId: this._registrationId
177
199
  });
178
- import_log.log.info("unregistered", {
200
+ import_log2.log.info("unregistered", {
179
201
  registrationId: this._registrationId
180
202
  }, {
181
- F: __dxlog_file,
203
+ F: __dxlog_file2,
182
204
  L: 116,
183
205
  S: this,
184
206
  C: (f, a) => f(...a)
@@ -198,10 +220,10 @@ var DevServer = class {
198
220
  async _load(def, flush = false) {
199
221
  const { id, name, handler } = def;
200
222
  const path = (0, import_node_path.join)(this._options.directory, handler);
201
- import_log.log.info("loading", {
223
+ import_log2.log.info("loading", {
202
224
  id
203
225
  }, {
204
- F: __dxlog_file,
226
+ F: __dxlog_file2,
205
227
  L: 135,
206
228
  S: this,
207
229
  C: (f, a) => f(...a)
@@ -224,11 +246,11 @@ var DevServer = class {
224
246
  async _invoke(name, event) {
225
247
  const seq = ++this._seq;
226
248
  const now = Date.now();
227
- import_log.log.info("req", {
249
+ import_log2.log.info("req", {
228
250
  seq,
229
251
  name
230
252
  }, {
231
- F: __dxlog_file,
253
+ F: __dxlog_file2,
232
254
  L: 160,
233
255
  S: this,
234
256
  C: (f, a) => f(...a)
@@ -250,13 +272,13 @@ var DevServer = class {
250
272
  event,
251
273
  response
252
274
  });
253
- import_log.log.info("res", {
275
+ import_log2.log.info("res", {
254
276
  seq,
255
277
  name,
256
278
  statusCode,
257
279
  duration: Date.now() - now
258
280
  }, {
259
- F: __dxlog_file,
281
+ F: __dxlog_file2,
260
282
  L: 177,
261
283
  S: this,
262
284
  C: (f, a) => f(...a)
@@ -264,7 +286,7 @@ var DevServer = class {
264
286
  return statusCode;
265
287
  }
266
288
  };
267
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/scheduler.ts";
289
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/scheduler.ts";
268
290
  var Scheduler = class {
269
291
  constructor(_client, _manifest, _options = {}) {
270
292
  this._client = _client;
@@ -294,7 +316,7 @@ var Scheduler = class {
294
316
  };
295
317
  const def = this._manifest.functions.find((config) => config.id === trigger.function);
296
318
  (0, import_invariant2.invariant)(def, `Function not found: ${trigger.function}`, {
297
- F: __dxlog_file2,
319
+ F: __dxlog_file3,
298
320
  L: 63,
299
321
  S: this,
300
322
  A: [
@@ -308,11 +330,11 @@ var Scheduler = class {
308
330
  ctx,
309
331
  trigger
310
332
  });
311
- (0, import_log2.log)("mount", {
333
+ (0, import_log3.log)("mount", {
312
334
  space: space.key,
313
335
  trigger
314
336
  }, {
315
- F: __dxlog_file2,
337
+ F: __dxlog_file3,
316
338
  L: 69,
317
339
  S: this,
318
340
  C: (f, a) => f(...a)
@@ -346,7 +368,7 @@ var Scheduler = class {
346
368
  });
347
369
  });
348
370
  (0, import_invariant2.invariant)(trigger.schedule, void 0, {
349
- F: __dxlog_file2,
371
+ F: __dxlog_file3,
350
372
  L: 103,
351
373
  S: this,
352
374
  A: [
@@ -381,13 +403,13 @@ var Scheduler = class {
381
403
  const update = ({ objects }) => {
382
404
  subscription.update(objects);
383
405
  if (deep) {
384
- import_log2.log.info("update", {
406
+ import_log3.log.info("update", {
385
407
  type,
386
408
  deep,
387
409
  objects: objects.length
388
410
  }, {
389
- F: __dxlog_file2,
390
- L: 139,
411
+ F: __dxlog_file3,
412
+ L: 142,
391
413
  S: this,
392
414
  C: (f, a) => f(...a)
393
415
  });
@@ -409,11 +431,11 @@ var Scheduler = class {
409
431
  }
410
432
  async _execFunction(def, data) {
411
433
  try {
412
- (0, import_log2.log)("request", {
434
+ (0, import_log3.log)("request", {
413
435
  function: def.id
414
436
  }, {
415
- F: __dxlog_file2,
416
- L: 161,
437
+ F: __dxlog_file3,
438
+ L: 164,
417
439
  S: this,
418
440
  C: (f, a) => f(...a)
419
441
  });
@@ -431,22 +453,22 @@ var Scheduler = class {
431
453
  } else if (callback) {
432
454
  status = await callback(data);
433
455
  }
434
- (0, import_log2.log)("result", {
456
+ (0, import_log3.log)("result", {
435
457
  function: def.id,
436
458
  result: status
437
459
  }, {
438
- F: __dxlog_file2,
439
- L: 180,
460
+ F: __dxlog_file3,
461
+ L: 183,
440
462
  S: this,
441
463
  C: (f, a) => f(...a)
442
464
  });
443
465
  } catch (err) {
444
- import_log2.log.error("error", {
466
+ import_log3.log.error("error", {
445
467
  function: def.id,
446
468
  error: err.message
447
469
  }, {
448
- F: __dxlog_file2,
449
- L: 182,
470
+ F: __dxlog_file3,
471
+ L: 185,
450
472
  S: this,
451
473
  C: (f, a) => f(...a)
452
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 { 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\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 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.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 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 // TODO(burdon): Check greater than 30s min (use cron-parser).\n invariant(trigger.schedule);\n const job = new CronJob(trigger.schedule, () => task.schedule());\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): 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 } = 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": "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",
6
- "names": ["import_async", "import_echo_schema", "import_invariant", "import_log", "import_util", "subscriptionHandler", "handler", "event", "context", "rest", "client", "space", "spaces", "get", "PublicKey", "from", "undefined", "objects", "map", "id", "db", "getObjectById", "filter", "nonNullable", "isTypedObject", "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", "info", "_registrationId", "stop", "Error", "trigger", "Trigger", "close", "unregister", "wake", "wait", "flush", "path", "join", "directory", "keys", "require", "cache", "key", "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", "job", "CronJob", "onDispose", "objectIds", "Set", "Array", "subscription", "createSubscription", "added", "updated", "object", "add", "push", "unsubscribe", "type", "props", "deep", "delay", "update", "length", "content", "TextObject", "debounce", "query", "Filter", "typename", "data", "callback", "fetch", "method", "headers", "JSON", "stringify", "result", "message"]
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 // TODO(burdon): Check greater than 30s min (use cron-parser).\n invariant(trigger.schedule);\n const job = new CronJob(trigger.schedule, () => task.schedule());\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": "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",
6
+ "names": ["import_log", "import_async", "import_echo_schema", "import_invariant", "import_util", "subscriptionHandler", "handler", "event", "context", "rest", "client", "space", "spaces", "get", "PublicKey", "from", "undefined", "objects", "map", "id", "db", "getObjectById", "filter", "nonNullable", "isTypedObject", "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", "job", "CronJob", "onDispose", "objectIds", "Set", "Array", "subscription", "createSubscription", "added", "updated", "object", "add", "push", "unsubscribe", "type", "props", "deep", "delay", "update", "content", "TextObject", "debounce", "query", "Filter", "typename", "data", "callback", "fetch", "method", "headers", "JSON", "stringify", "result", "message"]
7
7
  }
@@ -1 +1 @@
1
- {"inputs":{"packages/core/functions/src/handler.ts":{"bytes":4072,"imports":[{"path":"@dxos/client","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"format":"esm"},"packages/core/functions/src/manifest.ts":{"bytes":1863,"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":22471,"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":21909},"packages/core/functions/dist/lib/node/index.cjs":{"imports":[{"path":"@dxos/client","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","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":"@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","subscriptionHandler"],"entryPoint":"packages/core/functions/src/index.ts","inputs":{"packages/core/functions/src/handler.ts":{"bytesInOutput":589},"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":5475}},"bytes":11470}}}
1
+ {"inputs":{"packages/core/functions/src/handler.ts":{"bytes":6074,"imports":[{"path":"@dxos/client","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","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":1863,"imports":[],"format":"esm"},"packages/core/functions/src/runtime/dev-server.ts":{"bytes":18932,"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":22961,"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":22942},"packages/core/functions/dist/lib/node/index.cjs":{"imports":[{"path":"@dxos/client","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","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":"@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","subscriptionHandler"],"entryPoint":"packages/core/functions/src/index.ts","inputs":{"packages/core/functions/src/handler.ts":{"bytesInOutput":1130},"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":5475}},"bytes":12055}}}
@@ -21,5 +21,15 @@ export type FunctionSubscriptionEvent2 = {
21
21
  space?: Space;
22
22
  objects?: TypedObject[];
23
23
  };
24
+ /**
25
+ * Handler wrapper for subscription events; extracts space and objects.
26
+ *
27
+ * To test:
28
+ * ```
29
+ * curl -s -X POST -H "Content-Type: application/json" --data '{"space": "0446...1cbb"}' http://localhost:7100/dev/email-extractor
30
+ * ```
31
+ *
32
+ * NOTE: Get space key from devtools or `dx space list --json`
33
+ */
24
34
  export declare const subscriptionHandler: (handler: FunctionHandler<FunctionSubscriptionEvent2>) => FunctionHandler<FunctionSubscriptionEvent>;
25
35
  //# sourceMappingURL=handler.d.ts.map
@@ -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,EAAiB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIpE,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,WAAW,EAAE,CAAC;CACzB,CAAC;AAEF,eAAO,MAAM,mBAAmB,YACrB,gBAAgB,0BAA0B,CAAC,KACnD,gBAAgB,yBAAyB,CAa3C,CAAC"}
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,EAAiB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKpE,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,WAAW,EAAE,CAAC;CACzB,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,YACrB,gBAAgB,0BAA0B,CAAC,KACnD,gBAAgB,yBAAyB,CAmB3C,CAAC"}
@@ -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,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;IAepB,OAAO,CAAC,mBAAmB;YAiDb,aAAa;CA0B5B"}
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,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;IAepB,OAAO,CAAC,mBAAmB;YAoDb,aAAa;CA0B5B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/functions",
3
- "version": "0.3.9-main.d5f3fd3",
3
+ "version": "0.3.9-main.de60860",
4
4
  "description": "Functions SDK and runtime.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -24,14 +24,14 @@
24
24
  "deepsignal": "1.4.0-shallow.0",
25
25
  "express": "^4.17.1",
26
26
  "get-port-please": "^3.1.1",
27
- "@dxos/async": "0.3.9-main.d5f3fd3",
28
- "@dxos/echo-schema": "0.3.9-main.d5f3fd3",
29
- "@dxos/client": "0.3.9-main.d5f3fd3",
30
- "@dxos/context": "0.3.9-main.d5f3fd3",
31
- "@dxos/invariant": "0.3.9-main.d5f3fd3",
32
- "@dxos/log": "0.3.9-main.d5f3fd3",
33
- "@dxos/util": "0.3.9-main.d5f3fd3",
34
- "@dxos/node-std": "0.3.9-main.d5f3fd3"
27
+ "@dxos/async": "0.3.9-main.de60860",
28
+ "@dxos/client": "0.3.9-main.de60860",
29
+ "@dxos/context": "0.3.9-main.de60860",
30
+ "@dxos/echo-schema": "0.3.9-main.de60860",
31
+ "@dxos/invariant": "0.3.9-main.de60860",
32
+ "@dxos/log": "0.3.9-main.de60860",
33
+ "@dxos/node-std": "0.3.9-main.de60860",
34
+ "@dxos/util": "0.3.9-main.de60860"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/express": "^4.17.17"
package/src/handler.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  import { type Client, PublicKey } from '@dxos/client';
6
6
  import { type Space } from '@dxos/client/echo';
7
7
  import { isTypedObject, type TypedObject } from '@dxos/echo-schema';
8
+ import { log } from '@dxos/log';
8
9
  import { nonNullable } from '@dxos/util';
9
10
 
10
11
  // TODO(burdon): No response?
@@ -36,6 +37,16 @@ export type FunctionSubscriptionEvent2 = {
36
37
  objects?: TypedObject[];
37
38
  };
38
39
 
40
+ /**
41
+ * Handler wrapper for subscription events; extracts space and objects.
42
+ *
43
+ * To test:
44
+ * ```
45
+ * curl -s -X POST -H "Content-Type: application/json" --data '{"space": "0446...1cbb"}' http://localhost:7100/dev/email-extractor
46
+ * ```
47
+ *
48
+ * NOTE: Get space key from devtools or `dx space list --json`
49
+ */
39
50
  export const subscriptionHandler = (
40
51
  handler: FunctionHandler<FunctionSubscriptionEvent2>,
41
52
  ): FunctionHandler<FunctionSubscriptionEvent> => {
@@ -49,6 +60,12 @@ export const subscriptionHandler = (
49
60
  .filter(nonNullable)
50
61
  .filter(isTypedObject);
51
62
 
63
+ if (!!event.space && !space) {
64
+ log.warn('invalid space', { event });
65
+ } else {
66
+ log.info('handler', { space: space?.key.truncate(), objects: objects?.length });
67
+ }
68
+
52
69
  return handler({ event: { space, objects }, context, ...rest });
53
70
  };
54
71
  };
@@ -80,7 +80,7 @@ export class DevServer {
80
80
  res.statusCode = await this._invoke(name, req.body);
81
81
  res.end();
82
82
  } catch (err: any) {
83
- log.catch(err);
83
+ log.error(`Function failed: ${name}`, err);
84
84
  res.statusCode = 500;
85
85
  res.end();
86
86
  }
@@ -116,6 +116,7 @@ export class Scheduler {
116
116
  });
117
117
  });
118
118
 
119
+ // TODO(burdon): Don't fire initially.
119
120
  // TODO(burdon): Standardize subscription handles.
120
121
  const subscriptions: (() => void)[] = [];
121
122
  const subscription = createSubscription(({ added, updated }) => {
@@ -130,6 +131,8 @@ export class Scheduler {
130
131
  });
131
132
  subscriptions.push(() => subscription.unsubscribe());
132
133
 
134
+ // TODO(burdon): Create queue. Only allow one invocation per trigger at a time?
135
+ // TODO(burdon): Disable trigger if keeps failing.
133
136
  const { type, props, deep, delay } = triggerSubscription;
134
137
  const update = ({ objects }: Query) => {
135
138
  subscription.update(objects);