@dxos/functions 0.3.8-main.56eb422 → 0.3.8-main.64c4d0a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -50,7 +50,7 @@ runtime:
50
50
  Start functions in dev mode (from the related package):
51
51
 
52
52
  ```bash
53
- dx function dev-server -r ts-node/register --verbose
53
+ dx function dev -r ts-node/register --verbose
54
54
  ```
55
55
 
56
56
  > NOTE: `-r ts-node/register` configures native TypesScript support.
@@ -61,7 +61,7 @@ Install `nodemon` to support live reloading:
61
61
  npm i -g nodemon
62
62
 
63
63
  nodemon -w ./src -e ts --exec $(git rev-parse --show-toplevel)/packages/devtools/cli/bin/dev \
64
- function dev-server -r ts-node/register --verbose
64
+ function dev -r ts-node/register --verbose
65
65
  ```
66
66
 
67
67
  ## Invoking functions
@@ -9,12 +9,12 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
9
9
 
10
10
  // packages/core/functions/src/runtime/dev-server.ts
11
11
  import express from "express";
12
+ import { getPort } from "get-port-please";
12
13
  import { join } from "@dxos/node-std/path";
13
- import { getPortPromise } from "portfinder";
14
14
  import { Trigger } from "@dxos/async";
15
+ import { invariant } from "@dxos/invariant";
15
16
  import { log } from "@dxos/log";
16
17
  var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/dev-server.ts";
17
- var DEFAULT_PORT = 7001;
18
18
  var DevServer = class {
19
19
  // prettier-ignore
20
20
  constructor(_client, _options) {
@@ -22,40 +22,49 @@ var DevServer = class {
22
22
  this._options = _options;
23
23
  this._handlers = {};
24
24
  }
25
- get port() {
26
- return this._port;
27
- }
28
25
  get endpoint() {
29
- return this._port ? `http://localhost:${this._port}` : void 0;
26
+ invariant(this._port, void 0, {
27
+ F: __dxlog_file,
28
+ L: 43,
29
+ S: this,
30
+ A: [
31
+ "this._port",
32
+ ""
33
+ ]
34
+ });
35
+ return `http://localhost:${this._port}`;
36
+ }
37
+ get proxy() {
38
+ return this._proxy;
30
39
  }
31
40
  get functions() {
32
41
  return Object.values(this._handlers);
33
42
  }
34
43
  async initialize() {
35
44
  for (const def of this._options.manifest.functions) {
36
- const { id, endpoint, handler: path } = def;
45
+ const { id, path, handler: dir } = def;
37
46
  try {
38
- const module = __require(join(this._options.directory, path));
47
+ const module = __require(join(this._options.directory, dir));
39
48
  const handler = module.default;
40
49
  if (typeof handler !== "function") {
41
50
  throw new Error(`Handler must export default function: ${id}`);
42
51
  }
43
- if (this._handlers[endpoint]) {
52
+ if (this._handlers[path]) {
44
53
  log.warn(`Function already registered: ${id}`, void 0, {
45
54
  F: __dxlog_file,
46
- L: 64,
55
+ L: 67,
47
56
  S: this,
48
57
  C: (f, a) => f(...a)
49
58
  });
50
59
  }
51
- this._handlers[endpoint] = {
60
+ this._handlers[path] = {
52
61
  def,
53
62
  handler
54
63
  };
55
64
  } catch (err) {
56
65
  log.error("parsing function (check manifest)", err, {
57
66
  F: __dxlog_file,
58
- L: 69,
67
+ L: 72,
59
68
  S: this,
60
69
  C: (f, a) => f(...a)
61
70
  });
@@ -65,8 +74,8 @@ var DevServer = class {
65
74
  async start() {
66
75
  const app = express();
67
76
  app.use(express.json());
68
- app.post("/:endpoint", async (req, res) => {
69
- const { endpoint } = req.params;
77
+ app.post("/:name", async (req, res) => {
78
+ const { name } = req.params;
70
79
  const response = {
71
80
  status: (code) => {
72
81
  res.statusCode = code;
@@ -83,15 +92,13 @@ var DevServer = class {
83
92
  };
84
93
  void (async () => {
85
94
  try {
86
- log("invoking", {
87
- endpoint
88
- }, {
95
+ log(`invoking: ${name}`, void 0, {
89
96
  F: __dxlog_file,
90
- L: 100,
97
+ L: 103,
91
98
  S: this,
92
99
  C: (f, a) => f(...a)
93
100
  });
94
- const { handler } = this._handlers[endpoint];
101
+ const { handler } = this._handlers[name];
95
102
  const response2 = await handler({
96
103
  context,
97
104
  event: req.body
@@ -100,7 +107,7 @@ var DevServer = class {
100
107
  response: response2
101
108
  }, {
102
109
  F: __dxlog_file,
103
- L: 103,
110
+ L: 106,
104
111
  S: this,
105
112
  C: (f, a) => f(...a)
106
113
  });
@@ -110,21 +117,35 @@ var DevServer = class {
110
117
  }
111
118
  })();
112
119
  });
113
- this._port = await getPortPromise({
114
- startPort: DEFAULT_PORT
120
+ this._port = await getPort({
121
+ port: 7200,
122
+ portRange: [
123
+ 7200,
124
+ 7299
125
+ ]
115
126
  });
116
127
  this._server = app.listen(this._port);
117
128
  try {
118
- const { registrationId } = await this._client.services.services.FunctionRegistryService.register({
129
+ const { registrationId, endpoint } = await this._client.services.services.FunctionRegistryService.register({
119
130
  endpoint: this.endpoint,
120
- functions: this.functions.map(({ def: { endpoint } }) => ({
121
- name: endpoint
131
+ functions: this.functions.map(({ def: { path } }) => ({
132
+ name: path
122
133
  }))
123
134
  });
135
+ log.info("registered", {
136
+ registrationId,
137
+ endpoint
138
+ }, {
139
+ F: __dxlog_file,
140
+ L: 126,
141
+ S: this,
142
+ C: (f, a) => f(...a)
143
+ });
124
144
  this._registrationId = registrationId;
145
+ this._proxy = endpoint;
125
146
  } catch (err) {
126
147
  await this.stop();
127
- throw new Error("FunctionRegistryService not available; check config (agent.plugins.functions).");
148
+ throw new Error("FunctionRegistryService not available (check plugin is configured).");
128
149
  }
129
150
  }
130
151
  async stop() {
@@ -134,13 +155,22 @@ var DevServer = class {
134
155
  await this._client.services.services.FunctionRegistryService.unregister({
135
156
  registrationId: this._registrationId
136
157
  });
158
+ log.info("unregistered", {
159
+ registrationId: this._registrationId
160
+ }, {
161
+ F: __dxlog_file,
162
+ L: 143,
163
+ S: this,
164
+ C: (f, a) => f(...a)
165
+ });
137
166
  this._registrationId = void 0;
167
+ this._proxy = void 0;
138
168
  }
139
169
  trigger.wake();
140
170
  });
141
171
  await trigger.wait();
142
- this._server = void 0;
143
172
  this._port = void 0;
173
+ this._server = void 0;
144
174
  }
145
175
  };
146
176
 
@@ -148,7 +178,7 @@ var DevServer = class {
148
178
  import { DeferredTask } from "@dxos/async";
149
179
  import { Context } from "@dxos/context";
150
180
  import { Filter, createSubscription } from "@dxos/echo-schema";
151
- import { invariant } from "@dxos/invariant";
181
+ import { invariant as invariant2 } from "@dxos/invariant";
152
182
  import { log as log2 } from "@dxos/log";
153
183
  import { ComplexMap } from "@dxos/util";
154
184
  var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/trigger-manager.ts";
@@ -181,9 +211,9 @@ var TriggerManager = class {
181
211
  spaceKey: space.key
182
212
  };
183
213
  const config = this._manifest.functions.find((config2) => config2.id === trigger.function);
184
- invariant(config, `Function not found: ${trigger.function}`, {
214
+ invariant2(config, `Function not found: ${trigger.function}`, {
185
215
  F: __dxlog_file2,
186
- L: 58,
216
+ L: 57,
187
217
  S: this,
188
218
  A: [
189
219
  "config",
@@ -201,7 +231,7 @@ var TriggerManager = class {
201
231
  trigger
202
232
  }, {
203
233
  F: __dxlog_file2,
204
- L: 62,
234
+ L: 61,
205
235
  S: this,
206
236
  C: (f, a) => f(...a)
207
237
  });
@@ -210,7 +240,7 @@ var TriggerManager = class {
210
240
  }
211
241
  const objectIds = /* @__PURE__ */ new Set();
212
242
  const task = new DeferredTask(ctx, async () => {
213
- await this.execFunction(this._invokeOptions, config.endpoint, {
243
+ await this.execFunction(this._invokeOptions, config.path, {
214
244
  space: space.key,
215
245
  objects: Array.from(objectIds)
216
246
  });
@@ -230,7 +260,7 @@ var TriggerManager = class {
230
260
  count
231
261
  }, {
232
262
  F: __dxlog_file2,
233
- L: 85,
263
+ L: 84,
234
264
  S: this,
235
265
  C: (f, a) => f(...a)
236
266
  });
@@ -261,35 +291,26 @@ var TriggerManager = class {
261
291
  }
262
292
  }
263
293
  async execFunction(options, functionName, data) {
264
- const { endpoint, runtime } = options;
265
- invariant(endpoint, "Missing endpoint", {
294
+ const { endpoint } = options;
295
+ invariant2(endpoint, "Missing endpoint", {
266
296
  F: __dxlog_file2,
267
- L: 121,
297
+ L: 120,
268
298
  S: this,
269
299
  A: [
270
300
  "endpoint",
271
301
  "'Missing endpoint'"
272
302
  ]
273
303
  });
274
- invariant(runtime, "Missing runtime", {
275
- F: __dxlog_file2,
276
- L: 122,
277
- S: this,
278
- A: [
279
- "runtime",
280
- "'Missing runtime'"
281
- ]
282
- });
283
304
  try {
284
305
  log2("invoke", {
285
306
  function: functionName
286
307
  }, {
287
308
  F: __dxlog_file2,
288
- L: 125,
309
+ L: 123,
289
310
  S: this,
290
311
  C: (f, a) => f(...a)
291
312
  });
292
- const url = `${endpoint}/${runtime}/${functionName}`;
313
+ const url = `${endpoint}/${functionName}`;
293
314
  const res = await fetch(url, {
294
315
  method: "POST",
295
316
  body: JSON.stringify(data),
@@ -302,7 +323,7 @@ var TriggerManager = class {
302
323
  result: await res.json()
303
324
  }, {
304
325
  F: __dxlog_file2,
305
- L: 135,
326
+ L: 133,
306
327
  S: this,
307
328
  C: (f, a) => f(...a)
308
329
  });
@@ -312,7 +333,7 @@ var TriggerManager = class {
312
333
  error: err.message
313
334
  }, {
314
335
  F: __dxlog_file2,
315
- L: 137,
336
+ L: 135,
316
337
  S: this,
317
338
  C: (f, a) => f(...a)
318
339
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/runtime/dev-server.ts", "../../../src/runtime/trigger-manager.ts"],
4
- "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport express from 'express';\nimport type http from 'http';\nimport { join } from 'node:path';\nimport { getPortPromise } from 'portfinder';\n\nimport { Trigger } from '@dxos/async';\nimport { type Client } from '@dxos/client';\nimport { log } from '@dxos/log';\n\nimport { type FunctionContext, type FunctionHandler, type Response } from '../handler';\nimport { type FunctionDef, type FunctionManifest } from '../manifest';\n\nconst DEFAULT_PORT = 7001;\n\nexport type DevServerOptions = {\n directory: string;\n manifest: FunctionManifest;\n};\n\n/**\n * Functions dev server provides a local HTTP server for testing functions.\n */\nexport class DevServer {\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\n // prettier-ignore\n constructor(\n private readonly _client: Client,\n private readonly _options: DevServerOptions,\n ) {}\n\n get port() {\n return this._port;\n }\n\n get endpoint() {\n return this._port ? `http://localhost:${this._port}` : undefined;\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 const { id, endpoint, handler: path } = def;\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const module = require(join(this._options.directory, path));\n const handler = module.default;\n if (typeof handler !== 'function') {\n throw new Error(`Handler must export default function: ${id}`);\n }\n\n if (this._handlers[endpoint]) {\n log.warn(`Function already registered: ${id}`);\n }\n\n this._handlers[endpoint] = { def, handler };\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('/:endpoint', async (req, res) => {\n const { endpoint } = req.params;\n\n const response: Response = {\n status: (code: number) => {\n res.statusCode = code;\n return response;\n },\n\n succeed: (result = {}) => {\n res.end(JSON.stringify(result));\n return response;\n },\n };\n\n const context: FunctionContext = {\n client: this._client,\n status: response.status.bind(response),\n };\n\n void (async () => {\n try {\n log('invoking', { endpoint });\n const { handler } = this._handlers[endpoint];\n const response = await handler({ context, event: req.body });\n log('done', { response });\n } catch (err: any) {\n res.statusCode = 500;\n res.end(err.message);\n }\n })();\n });\n\n this._port = await getPortPromise({ startPort: DEFAULT_PORT });\n this._server = app.listen(this._port);\n\n // TODO(burdon): Check plugin is registered.\n // TypeError: Cannot read properties of undefined (reading 'register')\n try {\n const { registrationId } = await this._client.services.services.FunctionRegistryService!.register({\n endpoint: this.endpoint!,\n functions: this.functions.map(({ def: { endpoint } }) => ({ name: endpoint })), // TODO(burdon): Change proto name => id.\n });\n\n this._registrationId = registrationId;\n } catch (err: any) {\n await this.stop();\n throw new Error('FunctionRegistryService not available; check config (agent.plugins.functions).');\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 this._registrationId = undefined;\n }\n\n trigger.wake();\n });\n\n await trigger.wait();\n this._server = undefined;\n this._port = undefined;\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { DeferredTask } from '@dxos/async';\nimport { type Client, type PublicKey } from '@dxos/client';\nimport type { Query, Space } from '@dxos/client/echo';\nimport { Context } from '@dxos/context';\nimport { Filter, createSubscription } from '@dxos/echo-schema';\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\nimport { ComplexMap } from '@dxos/util';\n\nimport { type FunctionManifest, type FunctionTrigger } from '../manifest';\n\n// TODO(burdon): Rename.\nexport type InvokeOptions = {\n endpoint: string;\n runtime: string;\n};\n\nexport class TriggerManager {\n private readonly _mounts = new ComplexMap<\n { name: string; spaceKey: PublicKey },\n { ctx: Context; trigger: FunctionTrigger }\n >(({ name, spaceKey }) => `${spaceKey.toHex()}:${name}`);\n\n private readonly _queries = new Set<Query>();\n\n constructor(\n private readonly _client: Client,\n private readonly _manifest: FunctionManifest,\n private readonly _invokeOptions: InvokeOptions,\n ) {}\n\n async start() {\n // TODO(burdon): Make runtime configurable (via CLI)?\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 // TODO(burdon): New context? Shared?\n await this.mount(new Context(), space, trigger);\n }\n }\n });\n }\n\n async stop() {\n for (const { name, spaceKey } of this._mounts.keys()) {\n await this.unmount(name, spaceKey);\n }\n }\n\n private async mount(ctx: Context, space: Space, trigger: FunctionTrigger) {\n const key = { name: trigger.function, spaceKey: space.key };\n const config = this._manifest.functions.find((config) => config.id === trigger.function);\n invariant(config, `Function not found: ${trigger.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 // TODO(burdon): Why DeferredTask? How to pass objectIds to function?\n const objectIds = new Set<string>();\n const task = new DeferredTask(ctx, async () => {\n await this.execFunction(this._invokeOptions, config.endpoint, {\n space: space.key,\n objects: Array.from(objectIds),\n });\n });\n\n let count = 0;\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 log('updated', {\n trigger,\n space: space.key,\n objects: objectIds.size,\n count,\n });\n\n task.schedule();\n count++;\n });\n // TODO(burdon): DSL for query (replace props).\n const query = space.db.query(Filter.typename(trigger.subscription.type, trigger.subscription.props));\n this._queries.add(query);\n const unsubscribe = query.subscribe(({ objects }) => {\n subscription.update(objects);\n }, true);\n\n ctx.onDispose(() => {\n subscription.unsubscribe();\n unsubscribe();\n this._queries.delete(query);\n });\n }\n }\n\n private async unmount(name: string, spaceKey: PublicKey) {\n const key = { name, spaceKey };\n const { ctx } = this._mounts.get(key) ?? {};\n if (ctx) {\n this._mounts.delete(key);\n await ctx.dispose();\n }\n }\n\n private async execFunction(options: InvokeOptions, functionName: string, data: any) {\n const { endpoint, runtime } = options;\n invariant(endpoint, 'Missing endpoint');\n invariant(runtime, 'Missing runtime');\n\n try {\n log('invoke', { function: functionName });\n const url = `${endpoint}/${runtime}/${functionName}`;\n const res = await fetch(url, {\n method: 'POST',\n body: JSON.stringify(data),\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n log('result', { function: functionName, result: await res.json() });\n } catch (err: any) {\n log.error('error', { function: functionName, error: err.message });\n }\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["express", "join", "getPortPromise", "Trigger", "log", "DEFAULT_PORT", "DevServer", "constructor", "_client", "_options", "_handlers", "port", "_port", "endpoint", "undefined", "functions", "Object", "values", "initialize", "def", "manifest", "id", "handler", "path", "module", "require", "join", "directory", "default", "Error", "log", "warn", "err", "error", "start", "app", "express", "use", "json", "post", "req", "res", "params", "response", "status", "code", "statusCode", "succeed", "result", "end", "JSON", "stringify", "context", "client", "bind", "event", "body", "message", "getPortPromise", "startPort", "_server", "listen", "registrationId", "services", "FunctionRegistryService", "register", "map", "name", "_registrationId", "stop", "trigger", "Trigger", "close", "unregister", "wake", "wait", "DeferredTask", "Context", "Filter", "createSubscription", "invariant", "log", "ComplexMap", "TriggerManager", "constructor", "_client", "_manifest", "_invokeOptions", "_mounts", "name", "spaceKey", "toHex", "_queries", "Set", "start", "spaces", "subscribe", "space", "waitUntilReady", "trigger", "triggers", "mount", "stop", "keys", "unmount", "ctx", "key", "function", "config", "functions", "find", "id", "exists", "get", "set", "disposed", "objectIds", "task", "execFunction", "endpoint", "objects", "Array", "from", "count", "subscription", "added", "updated", "object", "add", "size", "schedule", "query", "db", "typename", "type", "props", "unsubscribe", "update", "onDispose", "delete", "dispose", "options", "functionName", "data", "runtime", "url", "res", "fetch", "method", "body", "JSON", "stringify", "headers", "result", "json", "err", "error", "message"]
4
+ "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport express from 'express';\nimport { getPort } from 'get-port-please';\nimport type http from 'http';\nimport { join } from 'node:path';\n\nimport { Trigger } from '@dxos/async';\nimport { type Client } from '@dxos/client';\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\n\nimport { type FunctionContext, type FunctionHandler, type Response } from '../handler';\nimport { type FunctionDef, type FunctionManifest } from '../manifest';\n\nexport type DevServerOptions = {\n port?: number;\n directory: string;\n manifest: FunctionManifest;\n};\n\n/**\n * Functions dev server provides a local HTTP server for testing functions.\n */\n// TODO(burdon): Reconcile with agent/functions dev dispatcher.\nexport class DevServer {\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\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 const { id, path, handler: dir } = def;\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const module = require(join(this._options.directory, dir));\n const handler = module.default;\n if (typeof handler !== 'function') {\n throw new Error(`Handler must export default function: ${id}`);\n }\n\n if (this._handlers[path]) {\n log.warn(`Function already registered: ${id}`);\n }\n\n this._handlers[path] = { def, handler };\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\n const response: Response = {\n status: (code: number) => {\n res.statusCode = code;\n return response;\n },\n\n succeed: (result = {}) => {\n res.end(JSON.stringify(result));\n return response;\n },\n };\n\n const context: FunctionContext = {\n client: this._client,\n status: response.status.bind(response),\n };\n\n void (async () => {\n try {\n log(`invoking: ${name}`);\n const { handler } = this._handlers[name];\n const response = await handler({ context, event: req.body });\n log('done', { response });\n } catch (err: any) {\n res.statusCode = 500;\n res.end(err.message);\n }\n })();\n });\n\n // TODO(burdon): Push down port management to agent.\n this._port = await getPort({ port: 7200, portRange: [7200, 7299] });\n this._server = app.listen(this._port);\n\n // TODO(burdon): Test during initialization.\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: { path } }) => ({ name: path })),\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// Copyright 2023 DXOS.org\n//\n\nimport { DeferredTask } from '@dxos/async';\nimport { type Client, type PublicKey } from '@dxos/client';\nimport type { Query, Space } from '@dxos/client/echo';\nimport { Context } from '@dxos/context';\nimport { Filter, createSubscription } from '@dxos/echo-schema';\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\nimport { ComplexMap } from '@dxos/util';\n\nimport { type FunctionManifest, type FunctionTrigger } from '../manifest';\n\n// TODO(burdon): Rename.\nexport type InvokeOptions = {\n endpoint: string;\n};\n\nexport class TriggerManager {\n private readonly _mounts = new ComplexMap<\n { name: string; spaceKey: PublicKey },\n { ctx: Context; trigger: FunctionTrigger }\n >(({ name, spaceKey }) => `${spaceKey.toHex()}:${name}`);\n\n private readonly _queries = new Set<Query>();\n\n constructor(\n private readonly _client: Client,\n private readonly _manifest: FunctionManifest,\n private readonly _invokeOptions: InvokeOptions,\n ) {}\n\n async start() {\n // TODO(burdon): Make runtime configurable (via CLI)?\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 // TODO(burdon): New context? Shared?\n await this.mount(new Context(), space, trigger);\n }\n }\n });\n }\n\n async stop() {\n for (const { name, spaceKey } of this._mounts.keys()) {\n await this.unmount(name, spaceKey);\n }\n }\n\n private async mount(ctx: Context, space: Space, trigger: FunctionTrigger) {\n const key = { name: trigger.function, spaceKey: space.key };\n const config = this._manifest.functions.find((config) => config.id === trigger.function);\n invariant(config, `Function not found: ${trigger.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 // TODO(burdon): Why DeferredTask? How to pass objectIds to function?\n const objectIds = new Set<string>();\n const task = new DeferredTask(ctx, async () => {\n await this.execFunction(this._invokeOptions, config.path, {\n space: space.key,\n objects: Array.from(objectIds),\n });\n });\n\n let count = 0;\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 log('updated', {\n trigger,\n space: space.key,\n objects: objectIds.size,\n count,\n });\n\n task.schedule();\n count++;\n });\n // TODO(burdon): DSL for query (replace props).\n const query = space.db.query(Filter.typename(trigger.subscription.type, trigger.subscription.props));\n this._queries.add(query);\n const unsubscribe = query.subscribe(({ objects }) => {\n subscription.update(objects);\n }, true);\n\n ctx.onDispose(() => {\n subscription.unsubscribe();\n unsubscribe();\n this._queries.delete(query);\n });\n }\n }\n\n private async unmount(name: string, spaceKey: PublicKey) {\n const key = { name, spaceKey };\n const { ctx } = this._mounts.get(key) ?? {};\n if (ctx) {\n this._mounts.delete(key);\n await ctx.dispose();\n }\n }\n\n private async execFunction(options: InvokeOptions, functionName: string, data: any) {\n const { endpoint } = options;\n invariant(endpoint, 'Missing endpoint');\n\n try {\n log('invoke', { function: functionName });\n const url = `${endpoint}/${functionName}`;\n const res = await fetch(url, {\n method: 'POST',\n body: JSON.stringify(data),\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n log('result', { function: functionName, result: await res.json() });\n } catch (err: any) {\n log.error('error', { function: functionName, error: err.message });\n }\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["express", "getPort", "join", "Trigger", "invariant", "log", "DevServer", "constructor", "_client", "_options", "_handlers", "endpoint", "invariant", "_port", "proxy", "_proxy", "functions", "Object", "values", "initialize", "def", "manifest", "id", "path", "handler", "dir", "module", "require", "join", "directory", "default", "Error", "log", "warn", "err", "error", "start", "app", "express", "use", "json", "post", "req", "res", "name", "params", "response", "status", "code", "statusCode", "succeed", "result", "end", "JSON", "stringify", "context", "client", "bind", "event", "body", "message", "getPort", "port", "portRange", "_server", "listen", "registrationId", "services", "FunctionRegistryService", "register", "map", "info", "_registrationId", "stop", "trigger", "Trigger", "close", "unregister", "undefined", "wake", "wait", "DeferredTask", "Context", "Filter", "createSubscription", "invariant", "log", "ComplexMap", "TriggerManager", "constructor", "_client", "_manifest", "_invokeOptions", "_mounts", "name", "spaceKey", "toHex", "_queries", "Set", "start", "spaces", "subscribe", "space", "waitUntilReady", "trigger", "triggers", "mount", "stop", "keys", "unmount", "ctx", "key", "function", "config", "functions", "find", "id", "exists", "get", "set", "disposed", "objectIds", "task", "execFunction", "path", "objects", "Array", "from", "count", "subscription", "added", "updated", "object", "add", "size", "schedule", "query", "db", "typename", "type", "props", "unsubscribe", "update", "onDispose", "delete", "dispose", "options", "functionName", "data", "endpoint", "url", "res", "fetch", "method", "body", "JSON", "stringify", "headers", "result", "json", "err", "error", "message"]
7
7
  }
@@ -1 +1 @@
1
- {"inputs":{"packages/core/functions/src/handler.ts":{"bytes":1109,"imports":[],"format":"esm"},"packages/core/functions/src/manifest.ts":{"bytes":1358,"imports":[],"format":"esm"},"packages/core/functions/src/runtime/dev-server.ts":{"bytes":15536,"imports":[{"path":"express","kind":"import-statement","external":true},{"path":"@dxos/node-std/path","kind":"import-statement","external":true},{"path":"portfinder","kind":"import-statement","external":true},{"path":"@dxos/async","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/trigger-manager.ts":{"bytes":17315,"imports":[{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"format":"esm"},"packages/core/functions/src/runtime/index.ts":{"bytes":584,"imports":[{"path":"packages/core/functions/src/runtime/dev-server.ts","kind":"import-statement","original":"./dev-server"},{"path":"packages/core/functions/src/runtime/trigger-manager.ts","kind":"import-statement","original":"./trigger-manager"}],"format":"esm"},"packages/core/functions/src/index.ts":{"bytes":637,"imports":[{"path":"packages/core/functions/src/handler.ts","kind":"import-statement","original":"./handler"},{"path":"packages/core/functions/src/manifest.ts","kind":"import-statement","original":"./manifest"},{"path":"packages/core/functions/src/runtime/index.ts","kind":"import-statement","original":"./runtime"}],"format":"esm"}},"outputs":{"packages/core/functions/dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":15421},"packages/core/functions/dist/lib/browser/index.mjs":{"imports":[{"path":"express","kind":"import-statement","external":true},{"path":"@dxos/node-std/path","kind":"import-statement","external":true},{"path":"portfinder","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"exports":["DevServer","TriggerManager"],"entryPoint":"packages/core/functions/src/index.ts","inputs":{"packages/core/functions/src/index.ts":{"bytesInOutput":0},"packages/core/functions/src/runtime/dev-server.ts":{"bytesInOutput":3815},"packages/core/functions/src/runtime/index.ts":{"bytesInOutput":0},"packages/core/functions/src/runtime/trigger-manager.ts":{"bytesInOutput":4642}},"bytes":9049}}}
1
+ {"inputs":{"packages/core/functions/src/handler.ts":{"bytes":1109,"imports":[],"format":"esm"},"packages/core/functions/src/manifest.ts":{"bytes":1346,"imports":[],"format":"esm"},"packages/core/functions/src/runtime/dev-server.ts":{"bytes":17196,"imports":[{"path":"express","kind":"import-statement","external":true},{"path":"get-port-please","kind":"import-statement","external":true},{"path":"@dxos/node-std/path","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"},"packages/core/functions/src/runtime/trigger-manager.ts":{"bytes":16867,"imports":[{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"format":"esm"},"packages/core/functions/src/runtime/index.ts":{"bytes":584,"imports":[{"path":"packages/core/functions/src/runtime/dev-server.ts","kind":"import-statement","original":"./dev-server"},{"path":"packages/core/functions/src/runtime/trigger-manager.ts","kind":"import-statement","original":"./trigger-manager"}],"format":"esm"},"packages/core/functions/src/index.ts":{"bytes":637,"imports":[{"path":"packages/core/functions/src/handler.ts","kind":"import-statement","original":"./handler"},{"path":"packages/core/functions/src/manifest.ts","kind":"import-statement","original":"./manifest"},{"path":"packages/core/functions/src/runtime/index.ts","kind":"import-statement","original":"./runtime"}],"format":"esm"}},"outputs":{"packages/core/functions/dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":15788},"packages/core/functions/dist/lib/browser/index.mjs":{"imports":[{"path":"express","kind":"import-statement","external":true},{"path":"get-port-please","kind":"import-statement","external":true},{"path":"@dxos/node-std/path","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"exports":["DevServer","TriggerManager"],"entryPoint":"packages/core/functions/src/index.ts","inputs":{"packages/core/functions/src/index.ts":{"bytesInOutput":0},"packages/core/functions/src/runtime/dev-server.ts":{"bytesInOutput":4383},"packages/core/functions/src/runtime/index.ts":{"bytesInOutput":0},"packages/core/functions/src/runtime/trigger-manager.ts":{"bytesInOutput":4463}},"bytes":9438}}}
@@ -37,12 +37,12 @@ module.exports = __toCommonJS(src_exports);
37
37
 
38
38
  // packages/core/functions/src/runtime/dev-server.ts
39
39
  var import_express = __toESM(require("express"));
40
+ var import_get_port_please = require("get-port-please");
40
41
  var import_node_path = require("node:path");
41
- var import_portfinder = require("portfinder");
42
42
  var import_async = require("@dxos/async");
43
+ var import_invariant = require("@dxos/invariant");
43
44
  var import_log = require("@dxos/log");
44
45
  var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/dev-server.ts";
45
- var DEFAULT_PORT = 7001;
46
46
  var DevServer = class {
47
47
  // prettier-ignore
48
48
  constructor(_client, _options) {
@@ -50,40 +50,49 @@ var DevServer = class {
50
50
  this._options = _options;
51
51
  this._handlers = {};
52
52
  }
53
- get port() {
54
- return this._port;
55
- }
56
53
  get endpoint() {
57
- return this._port ? `http://localhost:${this._port}` : void 0;
54
+ (0, import_invariant.invariant)(this._port, void 0, {
55
+ F: __dxlog_file,
56
+ L: 43,
57
+ S: this,
58
+ A: [
59
+ "this._port",
60
+ ""
61
+ ]
62
+ });
63
+ return `http://localhost:${this._port}`;
64
+ }
65
+ get proxy() {
66
+ return this._proxy;
58
67
  }
59
68
  get functions() {
60
69
  return Object.values(this._handlers);
61
70
  }
62
71
  async initialize() {
63
72
  for (const def of this._options.manifest.functions) {
64
- const { id, endpoint, handler: path } = def;
73
+ const { id, path, handler: dir } = def;
65
74
  try {
66
- const module2 = require((0, import_node_path.join)(this._options.directory, path));
75
+ const module2 = require((0, import_node_path.join)(this._options.directory, dir));
67
76
  const handler = module2.default;
68
77
  if (typeof handler !== "function") {
69
78
  throw new Error(`Handler must export default function: ${id}`);
70
79
  }
71
- if (this._handlers[endpoint]) {
80
+ if (this._handlers[path]) {
72
81
  import_log.log.warn(`Function already registered: ${id}`, void 0, {
73
82
  F: __dxlog_file,
74
- L: 64,
83
+ L: 67,
75
84
  S: this,
76
85
  C: (f, a) => f(...a)
77
86
  });
78
87
  }
79
- this._handlers[endpoint] = {
88
+ this._handlers[path] = {
80
89
  def,
81
90
  handler
82
91
  };
83
92
  } catch (err) {
84
93
  import_log.log.error("parsing function (check manifest)", err, {
85
94
  F: __dxlog_file,
86
- L: 69,
95
+ L: 72,
87
96
  S: this,
88
97
  C: (f, a) => f(...a)
89
98
  });
@@ -93,8 +102,8 @@ var DevServer = class {
93
102
  async start() {
94
103
  const app = (0, import_express.default)();
95
104
  app.use(import_express.default.json());
96
- app.post("/:endpoint", async (req, res) => {
97
- const { endpoint } = req.params;
105
+ app.post("/:name", async (req, res) => {
106
+ const { name } = req.params;
98
107
  const response = {
99
108
  status: (code) => {
100
109
  res.statusCode = code;
@@ -111,15 +120,13 @@ var DevServer = class {
111
120
  };
112
121
  void (async () => {
113
122
  try {
114
- (0, import_log.log)("invoking", {
115
- endpoint
116
- }, {
123
+ (0, import_log.log)(`invoking: ${name}`, void 0, {
117
124
  F: __dxlog_file,
118
- L: 100,
125
+ L: 103,
119
126
  S: this,
120
127
  C: (f, a) => f(...a)
121
128
  });
122
- const { handler } = this._handlers[endpoint];
129
+ const { handler } = this._handlers[name];
123
130
  const response2 = await handler({
124
131
  context,
125
132
  event: req.body
@@ -128,7 +135,7 @@ var DevServer = class {
128
135
  response: response2
129
136
  }, {
130
137
  F: __dxlog_file,
131
- L: 103,
138
+ L: 106,
132
139
  S: this,
133
140
  C: (f, a) => f(...a)
134
141
  });
@@ -138,21 +145,35 @@ var DevServer = class {
138
145
  }
139
146
  })();
140
147
  });
141
- this._port = await (0, import_portfinder.getPortPromise)({
142
- startPort: DEFAULT_PORT
148
+ this._port = await (0, import_get_port_please.getPort)({
149
+ port: 7200,
150
+ portRange: [
151
+ 7200,
152
+ 7299
153
+ ]
143
154
  });
144
155
  this._server = app.listen(this._port);
145
156
  try {
146
- const { registrationId } = await this._client.services.services.FunctionRegistryService.register({
157
+ const { registrationId, endpoint } = await this._client.services.services.FunctionRegistryService.register({
147
158
  endpoint: this.endpoint,
148
- functions: this.functions.map(({ def: { endpoint } }) => ({
149
- name: endpoint
159
+ functions: this.functions.map(({ def: { path } }) => ({
160
+ name: path
150
161
  }))
151
162
  });
163
+ import_log.log.info("registered", {
164
+ registrationId,
165
+ endpoint
166
+ }, {
167
+ F: __dxlog_file,
168
+ L: 126,
169
+ S: this,
170
+ C: (f, a) => f(...a)
171
+ });
152
172
  this._registrationId = registrationId;
173
+ this._proxy = endpoint;
153
174
  } catch (err) {
154
175
  await this.stop();
155
- throw new Error("FunctionRegistryService not available; check config (agent.plugins.functions).");
176
+ throw new Error("FunctionRegistryService not available (check plugin is configured).");
156
177
  }
157
178
  }
158
179
  async stop() {
@@ -162,13 +183,22 @@ var DevServer = class {
162
183
  await this._client.services.services.FunctionRegistryService.unregister({
163
184
  registrationId: this._registrationId
164
185
  });
186
+ import_log.log.info("unregistered", {
187
+ registrationId: this._registrationId
188
+ }, {
189
+ F: __dxlog_file,
190
+ L: 143,
191
+ S: this,
192
+ C: (f, a) => f(...a)
193
+ });
165
194
  this._registrationId = void 0;
195
+ this._proxy = void 0;
166
196
  }
167
197
  trigger.wake();
168
198
  });
169
199
  await trigger.wait();
170
- this._server = void 0;
171
200
  this._port = void 0;
201
+ this._server = void 0;
172
202
  }
173
203
  };
174
204
 
@@ -176,7 +206,7 @@ var DevServer = class {
176
206
  var import_async2 = require("@dxos/async");
177
207
  var import_context = require("@dxos/context");
178
208
  var import_echo_schema = require("@dxos/echo-schema");
179
- var import_invariant = require("@dxos/invariant");
209
+ var import_invariant2 = require("@dxos/invariant");
180
210
  var import_log2 = require("@dxos/log");
181
211
  var import_util = require("@dxos/util");
182
212
  var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/functions/src/runtime/trigger-manager.ts";
@@ -209,9 +239,9 @@ var TriggerManager = class {
209
239
  spaceKey: space.key
210
240
  };
211
241
  const config = this._manifest.functions.find((config2) => config2.id === trigger.function);
212
- (0, import_invariant.invariant)(config, `Function not found: ${trigger.function}`, {
242
+ (0, import_invariant2.invariant)(config, `Function not found: ${trigger.function}`, {
213
243
  F: __dxlog_file2,
214
- L: 58,
244
+ L: 57,
215
245
  S: this,
216
246
  A: [
217
247
  "config",
@@ -229,7 +259,7 @@ var TriggerManager = class {
229
259
  trigger
230
260
  }, {
231
261
  F: __dxlog_file2,
232
- L: 62,
262
+ L: 61,
233
263
  S: this,
234
264
  C: (f, a) => f(...a)
235
265
  });
@@ -238,7 +268,7 @@ var TriggerManager = class {
238
268
  }
239
269
  const objectIds = /* @__PURE__ */ new Set();
240
270
  const task = new import_async2.DeferredTask(ctx, async () => {
241
- await this.execFunction(this._invokeOptions, config.endpoint, {
271
+ await this.execFunction(this._invokeOptions, config.path, {
242
272
  space: space.key,
243
273
  objects: Array.from(objectIds)
244
274
  });
@@ -258,7 +288,7 @@ var TriggerManager = class {
258
288
  count
259
289
  }, {
260
290
  F: __dxlog_file2,
261
- L: 85,
291
+ L: 84,
262
292
  S: this,
263
293
  C: (f, a) => f(...a)
264
294
  });
@@ -289,35 +319,26 @@ var TriggerManager = class {
289
319
  }
290
320
  }
291
321
  async execFunction(options, functionName, data) {
292
- const { endpoint, runtime } = options;
293
- (0, import_invariant.invariant)(endpoint, "Missing endpoint", {
322
+ const { endpoint } = options;
323
+ (0, import_invariant2.invariant)(endpoint, "Missing endpoint", {
294
324
  F: __dxlog_file2,
295
- L: 121,
325
+ L: 120,
296
326
  S: this,
297
327
  A: [
298
328
  "endpoint",
299
329
  "'Missing endpoint'"
300
330
  ]
301
331
  });
302
- (0, import_invariant.invariant)(runtime, "Missing runtime", {
303
- F: __dxlog_file2,
304
- L: 122,
305
- S: this,
306
- A: [
307
- "runtime",
308
- "'Missing runtime'"
309
- ]
310
- });
311
332
  try {
312
333
  (0, import_log2.log)("invoke", {
313
334
  function: functionName
314
335
  }, {
315
336
  F: __dxlog_file2,
316
- L: 125,
337
+ L: 123,
317
338
  S: this,
318
339
  C: (f, a) => f(...a)
319
340
  });
320
- const url = `${endpoint}/${runtime}/${functionName}`;
341
+ const url = `${endpoint}/${functionName}`;
321
342
  const res = await fetch(url, {
322
343
  method: "POST",
323
344
  body: JSON.stringify(data),
@@ -330,7 +351,7 @@ var TriggerManager = class {
330
351
  result: await res.json()
331
352
  }, {
332
353
  F: __dxlog_file2,
333
- L: 135,
354
+ L: 133,
334
355
  S: this,
335
356
  C: (f, a) => f(...a)
336
357
  });
@@ -340,7 +361,7 @@ var TriggerManager = class {
340
361
  error: err.message
341
362
  }, {
342
363
  F: __dxlog_file2,
343
- L: 137,
364
+ L: 135,
344
365
  S: this,
345
366
  C: (f, a) => f(...a)
346
367
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/index.ts", "../../../src/runtime/dev-server.ts", "../../../src/runtime/trigger-manager.ts"],
4
- "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nexport * from './handler';\nexport * from './manifest';\nexport * from './runtime';\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport express from 'express';\nimport type http from 'http';\nimport { join } from 'node:path';\nimport { getPortPromise } from 'portfinder';\n\nimport { Trigger } from '@dxos/async';\nimport { type Client } from '@dxos/client';\nimport { log } from '@dxos/log';\n\nimport { type FunctionContext, type FunctionHandler, type Response } from '../handler';\nimport { type FunctionDef, type FunctionManifest } from '../manifest';\n\nconst DEFAULT_PORT = 7001;\n\nexport type DevServerOptions = {\n directory: string;\n manifest: FunctionManifest;\n};\n\n/**\n * Functions dev server provides a local HTTP server for testing functions.\n */\nexport class DevServer {\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\n // prettier-ignore\n constructor(\n private readonly _client: Client,\n private readonly _options: DevServerOptions,\n ) {}\n\n get port() {\n return this._port;\n }\n\n get endpoint() {\n return this._port ? `http://localhost:${this._port}` : undefined;\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 const { id, endpoint, handler: path } = def;\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const module = require(join(this._options.directory, path));\n const handler = module.default;\n if (typeof handler !== 'function') {\n throw new Error(`Handler must export default function: ${id}`);\n }\n\n if (this._handlers[endpoint]) {\n log.warn(`Function already registered: ${id}`);\n }\n\n this._handlers[endpoint] = { def, handler };\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('/:endpoint', async (req, res) => {\n const { endpoint } = req.params;\n\n const response: Response = {\n status: (code: number) => {\n res.statusCode = code;\n return response;\n },\n\n succeed: (result = {}) => {\n res.end(JSON.stringify(result));\n return response;\n },\n };\n\n const context: FunctionContext = {\n client: this._client,\n status: response.status.bind(response),\n };\n\n void (async () => {\n try {\n log('invoking', { endpoint });\n const { handler } = this._handlers[endpoint];\n const response = await handler({ context, event: req.body });\n log('done', { response });\n } catch (err: any) {\n res.statusCode = 500;\n res.end(err.message);\n }\n })();\n });\n\n this._port = await getPortPromise({ startPort: DEFAULT_PORT });\n this._server = app.listen(this._port);\n\n // TODO(burdon): Check plugin is registered.\n // TypeError: Cannot read properties of undefined (reading 'register')\n try {\n const { registrationId } = await this._client.services.services.FunctionRegistryService!.register({\n endpoint: this.endpoint!,\n functions: this.functions.map(({ def: { endpoint } }) => ({ name: endpoint })), // TODO(burdon): Change proto name => id.\n });\n\n this._registrationId = registrationId;\n } catch (err: any) {\n await this.stop();\n throw new Error('FunctionRegistryService not available; check config (agent.plugins.functions).');\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 this._registrationId = undefined;\n }\n\n trigger.wake();\n });\n\n await trigger.wait();\n this._server = undefined;\n this._port = undefined;\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { DeferredTask } from '@dxos/async';\nimport { type Client, type PublicKey } from '@dxos/client';\nimport type { Query, Space } from '@dxos/client/echo';\nimport { Context } from '@dxos/context';\nimport { Filter, createSubscription } from '@dxos/echo-schema';\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\nimport { ComplexMap } from '@dxos/util';\n\nimport { type FunctionManifest, type FunctionTrigger } from '../manifest';\n\n// TODO(burdon): Rename.\nexport type InvokeOptions = {\n endpoint: string;\n runtime: string;\n};\n\nexport class TriggerManager {\n private readonly _mounts = new ComplexMap<\n { name: string; spaceKey: PublicKey },\n { ctx: Context; trigger: FunctionTrigger }\n >(({ name, spaceKey }) => `${spaceKey.toHex()}:${name}`);\n\n private readonly _queries = new Set<Query>();\n\n constructor(\n private readonly _client: Client,\n private readonly _manifest: FunctionManifest,\n private readonly _invokeOptions: InvokeOptions,\n ) {}\n\n async start() {\n // TODO(burdon): Make runtime configurable (via CLI)?\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 // TODO(burdon): New context? Shared?\n await this.mount(new Context(), space, trigger);\n }\n }\n });\n }\n\n async stop() {\n for (const { name, spaceKey } of this._mounts.keys()) {\n await this.unmount(name, spaceKey);\n }\n }\n\n private async mount(ctx: Context, space: Space, trigger: FunctionTrigger) {\n const key = { name: trigger.function, spaceKey: space.key };\n const config = this._manifest.functions.find((config) => config.id === trigger.function);\n invariant(config, `Function not found: ${trigger.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 // TODO(burdon): Why DeferredTask? How to pass objectIds to function?\n const objectIds = new Set<string>();\n const task = new DeferredTask(ctx, async () => {\n await this.execFunction(this._invokeOptions, config.endpoint, {\n space: space.key,\n objects: Array.from(objectIds),\n });\n });\n\n let count = 0;\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 log('updated', {\n trigger,\n space: space.key,\n objects: objectIds.size,\n count,\n });\n\n task.schedule();\n count++;\n });\n // TODO(burdon): DSL for query (replace props).\n const query = space.db.query(Filter.typename(trigger.subscription.type, trigger.subscription.props));\n this._queries.add(query);\n const unsubscribe = query.subscribe(({ objects }) => {\n subscription.update(objects);\n }, true);\n\n ctx.onDispose(() => {\n subscription.unsubscribe();\n unsubscribe();\n this._queries.delete(query);\n });\n }\n }\n\n private async unmount(name: string, spaceKey: PublicKey) {\n const key = { name, spaceKey };\n const { ctx } = this._mounts.get(key) ?? {};\n if (ctx) {\n this._mounts.delete(key);\n await ctx.dispose();\n }\n }\n\n private async execFunction(options: InvokeOptions, functionName: string, data: any) {\n const { endpoint, runtime } = options;\n invariant(endpoint, 'Missing endpoint');\n invariant(runtime, 'Missing runtime');\n\n try {\n log('invoke', { function: functionName });\n const url = `${endpoint}/${runtime}/${functionName}`;\n const res = await fetch(url, {\n method: 'POST',\n body: JSON.stringify(data),\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n log('result', { function: functionName, result: await res.json() });\n } catch (err: any) {\n log.error('error', { function: functionName, error: err.message });\n }\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["DEFAULT_PORT", "DevServer", "constructor", "_client", "_options", "_handlers", "port", "_port", "endpoint", "undefined", "functions", "Object", "values", "initialize", "def", "manifest", "id", "handler", "path", "module", "require", "join", "directory", "default", "Error", "log", "warn", "err", "error", "start", "app", "express", "use", "json", "post", "req", "res", "params", "response", "status", "code", "statusCode", "succeed", "result", "end", "JSON", "stringify", "context", "client", "bind", "event", "body", "message", "getPortPromise", "startPort", "_server", "listen", "registrationId", "services", "FunctionRegistryService", "register", "map", "name", "_registrationId", "stop", "trigger", "Trigger", "close", "unregister", "wake", "wait", "import_async", "import_log", "TriggerManager", "constructor", "_client", "_manifest", "_invokeOptions", "_mounts", "ComplexMap", "name", "spaceKey", "toHex", "_queries", "Set", "start", "spaces", "subscribe", "space", "waitUntilReady", "trigger", "triggers", "mount", "Context", "stop", "keys", "unmount", "ctx", "key", "function", "config", "functions", "find", "id", "invariant", "exists", "get", "set", "log", "disposed", "objectIds", "task", "DeferredTask", "execFunction", "endpoint", "objects", "Array", "from", "count", "subscription", "createSubscription", "added", "updated", "object", "add", "size", "schedule", "query", "db", "Filter", "typename", "type", "props", "unsubscribe", "update", "onDispose", "delete", "dispose", "options", "functionName", "data", "runtime", "url", "res", "fetch", "method", "body", "JSON", "stringify", "headers", "result", "json", "err", "error", "message"]
4
+ "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nexport * from './handler';\nexport * from './manifest';\nexport * from './runtime';\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};\n\n/**\n * Functions dev server provides a local HTTP server for testing functions.\n */\n// TODO(burdon): Reconcile with agent/functions dev dispatcher.\nexport class DevServer {\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\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 const { id, path, handler: dir } = def;\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const module = require(join(this._options.directory, dir));\n const handler = module.default;\n if (typeof handler !== 'function') {\n throw new Error(`Handler must export default function: ${id}`);\n }\n\n if (this._handlers[path]) {\n log.warn(`Function already registered: ${id}`);\n }\n\n this._handlers[path] = { def, handler };\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\n const response: Response = {\n status: (code: number) => {\n res.statusCode = code;\n return response;\n },\n\n succeed: (result = {}) => {\n res.end(JSON.stringify(result));\n return response;\n },\n };\n\n const context: FunctionContext = {\n client: this._client,\n status: response.status.bind(response),\n };\n\n void (async () => {\n try {\n log(`invoking: ${name}`);\n const { handler } = this._handlers[name];\n const response = await handler({ context, event: req.body });\n log('done', { response });\n } catch (err: any) {\n res.statusCode = 500;\n res.end(err.message);\n }\n })();\n });\n\n // TODO(burdon): Push down port management to agent.\n this._port = await getPort({ port: 7200, portRange: [7200, 7299] });\n this._server = app.listen(this._port);\n\n // TODO(burdon): Test during initialization.\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: { path } }) => ({ name: path })),\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// Copyright 2023 DXOS.org\n//\n\nimport { DeferredTask } from '@dxos/async';\nimport { type Client, type PublicKey } from '@dxos/client';\nimport type { Query, Space } from '@dxos/client/echo';\nimport { Context } from '@dxos/context';\nimport { Filter, createSubscription } from '@dxos/echo-schema';\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\nimport { ComplexMap } from '@dxos/util';\n\nimport { type FunctionManifest, type FunctionTrigger } from '../manifest';\n\n// TODO(burdon): Rename.\nexport type InvokeOptions = {\n endpoint: string;\n};\n\nexport class TriggerManager {\n private readonly _mounts = new ComplexMap<\n { name: string; spaceKey: PublicKey },\n { ctx: Context; trigger: FunctionTrigger }\n >(({ name, spaceKey }) => `${spaceKey.toHex()}:${name}`);\n\n private readonly _queries = new Set<Query>();\n\n constructor(\n private readonly _client: Client,\n private readonly _manifest: FunctionManifest,\n private readonly _invokeOptions: InvokeOptions,\n ) {}\n\n async start() {\n // TODO(burdon): Make runtime configurable (via CLI)?\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 // TODO(burdon): New context? Shared?\n await this.mount(new Context(), space, trigger);\n }\n }\n });\n }\n\n async stop() {\n for (const { name, spaceKey } of this._mounts.keys()) {\n await this.unmount(name, spaceKey);\n }\n }\n\n private async mount(ctx: Context, space: Space, trigger: FunctionTrigger) {\n const key = { name: trigger.function, spaceKey: space.key };\n const config = this._manifest.functions.find((config) => config.id === trigger.function);\n invariant(config, `Function not found: ${trigger.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 // TODO(burdon): Why DeferredTask? How to pass objectIds to function?\n const objectIds = new Set<string>();\n const task = new DeferredTask(ctx, async () => {\n await this.execFunction(this._invokeOptions, config.path, {\n space: space.key,\n objects: Array.from(objectIds),\n });\n });\n\n let count = 0;\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 log('updated', {\n trigger,\n space: space.key,\n objects: objectIds.size,\n count,\n });\n\n task.schedule();\n count++;\n });\n // TODO(burdon): DSL for query (replace props).\n const query = space.db.query(Filter.typename(trigger.subscription.type, trigger.subscription.props));\n this._queries.add(query);\n const unsubscribe = query.subscribe(({ objects }) => {\n subscription.update(objects);\n }, true);\n\n ctx.onDispose(() => {\n subscription.unsubscribe();\n unsubscribe();\n this._queries.delete(query);\n });\n }\n }\n\n private async unmount(name: string, spaceKey: PublicKey) {\n const key = { name, spaceKey };\n const { ctx } = this._mounts.get(key) ?? {};\n if (ctx) {\n this._mounts.delete(key);\n await ctx.dispose();\n }\n }\n\n private async execFunction(options: InvokeOptions, functionName: string, data: any) {\n const { endpoint } = options;\n invariant(endpoint, 'Missing endpoint');\n\n try {\n log('invoke', { function: functionName });\n const url = `${endpoint}/${functionName}`;\n const res = await fetch(url, {\n method: 'POST',\n body: JSON.stringify(data),\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n log('result', { function: functionName, result: await res.json() });\n } catch (err: any) {\n log.error('error', { function: functionName, error: err.message });\n }\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["DevServer", "constructor", "_client", "_options", "_handlers", "endpoint", "invariant", "_port", "proxy", "_proxy", "functions", "Object", "values", "initialize", "def", "manifest", "id", "path", "handler", "dir", "module", "require", "join", "directory", "default", "Error", "log", "warn", "err", "error", "start", "app", "express", "use", "json", "post", "req", "res", "name", "params", "response", "status", "code", "statusCode", "succeed", "result", "end", "JSON", "stringify", "context", "client", "bind", "event", "body", "message", "getPort", "port", "portRange", "_server", "listen", "registrationId", "services", "FunctionRegistryService", "register", "map", "info", "_registrationId", "stop", "trigger", "Trigger", "close", "unregister", "undefined", "wake", "wait", "import_async", "import_invariant", "import_log", "TriggerManager", "constructor", "_client", "_manifest", "_invokeOptions", "_mounts", "ComplexMap", "name", "spaceKey", "toHex", "_queries", "Set", "start", "spaces", "subscribe", "space", "waitUntilReady", "trigger", "triggers", "mount", "Context", "stop", "keys", "unmount", "ctx", "key", "function", "config", "functions", "find", "id", "invariant", "exists", "get", "set", "log", "disposed", "objectIds", "task", "DeferredTask", "execFunction", "path", "objects", "Array", "from", "count", "subscription", "createSubscription", "added", "updated", "object", "add", "size", "schedule", "query", "db", "Filter", "typename", "type", "props", "unsubscribe", "update", "onDispose", "delete", "dispose", "options", "functionName", "data", "endpoint", "url", "res", "fetch", "method", "body", "JSON", "stringify", "headers", "result", "json", "err", "error", "message"]
7
7
  }
@@ -1 +1 @@
1
- {"inputs":{"packages/core/functions/src/handler.ts":{"bytes":1109,"imports":[],"format":"esm"},"packages/core/functions/src/manifest.ts":{"bytes":1358,"imports":[],"format":"esm"},"packages/core/functions/src/runtime/dev-server.ts":{"bytes":15536,"imports":[{"path":"express","kind":"import-statement","external":true},{"path":"node:path","kind":"import-statement","external":true},{"path":"portfinder","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true}],"format":"esm"},"packages/core/functions/src/runtime/trigger-manager.ts":{"bytes":17315,"imports":[{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"format":"esm"},"packages/core/functions/src/runtime/index.ts":{"bytes":584,"imports":[{"path":"packages/core/functions/src/runtime/dev-server.ts","kind":"import-statement","original":"./dev-server"},{"path":"packages/core/functions/src/runtime/trigger-manager.ts","kind":"import-statement","original":"./trigger-manager"}],"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":15521},"packages/core/functions/dist/lib/node/index.cjs":{"imports":[{"path":"express","kind":"require-call","external":true},{"path":"node:path","kind":"require-call","external":true},{"path":"portfinder","kind":"require-call","external":true},{"path":"@dxos/async","kind":"require-call","external":true},{"path":"@dxos/log","kind":"require-call","external":true},{"path":"@dxos/async","kind":"require-call","external":true},{"path":"@dxos/context","kind":"require-call","external":true},{"path":"@dxos/echo-schema","kind":"require-call","external":true},{"path":"@dxos/invariant","kind":"require-call","external":true},{"path":"@dxos/log","kind":"require-call","external":true},{"path":"@dxos/util","kind":"require-call","external":true}],"exports":[],"entryPoint":"packages/core/functions/src/index.ts","inputs":{"packages/core/functions/src/index.ts":{"bytesInOutput":163},"packages/core/functions/src/runtime/dev-server.ts":{"bytesInOutput":3994},"packages/core/functions/src/runtime/index.ts":{"bytesInOutput":0},"packages/core/functions/src/runtime/trigger-manager.ts":{"bytesInOutput":4869}},"bytes":10732}}}
1
+ {"inputs":{"packages/core/functions/src/handler.ts":{"bytes":1109,"imports":[],"format":"esm"},"packages/core/functions/src/manifest.ts":{"bytes":1346,"imports":[],"format":"esm"},"packages/core/functions/src/runtime/dev-server.ts":{"bytes":17196,"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}],"format":"esm"},"packages/core/functions/src/runtime/trigger-manager.ts":{"bytes":16867,"imports":[{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"format":"esm"},"packages/core/functions/src/runtime/index.ts":{"bytes":584,"imports":[{"path":"packages/core/functions/src/runtime/dev-server.ts","kind":"import-statement","original":"./dev-server"},{"path":"packages/core/functions/src/runtime/trigger-manager.ts","kind":"import-statement","original":"./trigger-manager"}],"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":15890},"packages/core/functions/dist/lib/node/index.cjs":{"imports":[{"path":"express","kind":"require-call","external":true},{"path":"get-port-please","kind":"require-call","external":true},{"path":"node:path","kind":"require-call","external":true},{"path":"@dxos/async","kind":"require-call","external":true},{"path":"@dxos/invariant","kind":"require-call","external":true},{"path":"@dxos/log","kind":"require-call","external":true},{"path":"@dxos/async","kind":"require-call","external":true},{"path":"@dxos/context","kind":"require-call","external":true},{"path":"@dxos/echo-schema","kind":"require-call","external":true},{"path":"@dxos/invariant","kind":"require-call","external":true},{"path":"@dxos/log","kind":"require-call","external":true},{"path":"@dxos/util","kind":"require-call","external":true}],"exports":[],"entryPoint":"packages/core/functions/src/index.ts","inputs":{"packages/core/functions/src/index.ts":{"bytesInOutput":163},"packages/core/functions/src/runtime/dev-server.ts":{"bytesInOutput":4629},"packages/core/functions/src/runtime/index.ts":{"bytesInOutput":0},"packages/core/functions/src/runtime/trigger-manager.ts":{"bytesInOutput":4655}},"bytes":11153}}}
@@ -1,6 +1,6 @@
1
1
  export type FunctionDef = {
2
2
  id: string;
3
- endpoint: string;
3
+ path: string;
4
4
  handler: string;
5
5
  description?: string;
6
6
  };
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/manifest.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,WAAW,GAAG;IAExB,EAAE,EAAE,MAAM,CAAC;IAEX,QAAQ,EAAE,MAAM,CAAC;IAEjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAKF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,mBAAmB,CAAC;CACnC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,EAAE,WAAW,EAAE,CAAC;IACzB,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B,CAAC"}
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/manifest.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,WAAW,GAAG;IAExB,EAAE,EAAE,MAAM,CAAC;IAEX,IAAI,EAAE,MAAM,CAAC;IAEb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAKF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,mBAAmB,CAAC;CACnC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,EAAE,WAAW,EAAE,CAAC;IACzB,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B,CAAC"}
@@ -2,6 +2,7 @@ import { type Client } from '@dxos/client';
2
2
  import { type FunctionHandler } from '../handler';
3
3
  import { type FunctionDef, type FunctionManifest } from '../manifest';
4
4
  export type DevServerOptions = {
5
+ port?: number;
5
6
  directory: string;
6
7
  manifest: FunctionManifest;
7
8
  };
@@ -15,9 +16,10 @@ export declare class DevServer {
15
16
  private _server?;
16
17
  private _port?;
17
18
  private _registrationId?;
19
+ private _proxy?;
18
20
  constructor(_client: Client, _options: DevServerOptions);
19
- get port(): number | undefined;
20
- get endpoint(): string | undefined;
21
+ get endpoint(): string;
22
+ get proxy(): string | undefined;
21
23
  get functions(): {
22
24
  def: FunctionDef;
23
25
  handler: FunctionHandler<any>;
@@ -1 +1 @@
1
- {"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../../../../src/runtime/dev-server.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,EAAwB,KAAK,eAAe,EAAiB,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAItE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,gBAAgB,CAAC;CAC5B,CAAC;AAEF;;GAEG;AACH,qBAAa,SAAS;IASlB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAT3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA2E;IAErG,OAAO,CAAC,OAAO,CAAC,CAAc;IAC9B,OAAO,CAAC,KAAK,CAAC,CAAS;IACvB,OAAO,CAAC,eAAe,CAAC,CAAS;gBAId,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,gBAAgB;IAG7C,IAAI,IAAI,uBAEP;IAED,IAAI,QAAQ,uBAEX;IAED,IAAI,SAAS;;;QAEZ;IAEK,UAAU;IAsBV,KAAK;IAuDL,IAAI;CAiBX"}
1
+ {"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../../../../src/runtime/dev-server.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAI3C,OAAO,EAAwB,KAAK,eAAe,EAAiB,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEtE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,gBAAgB,CAAC;CAC5B,CAAC;AAEF;;GAEG;AAEH,qBAAa,SAAS;IAUlB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAV3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA2E;IAErG,OAAO,CAAC,OAAO,CAAC,CAAc;IAC9B,OAAO,CAAC,KAAK,CAAC,CAAS;IACvB,OAAO,CAAC,eAAe,CAAC,CAAS;IACjC,OAAO,CAAC,MAAM,CAAC,CAAS;gBAIL,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,gBAAgB;IAG7C,IAAI,QAAQ,WAGX;IAED,IAAI,KAAK,uBAER;IAED,IAAI,SAAS;;;QAEZ;IAEK,UAAU;IAsBV,KAAK;IA0DL,IAAI;CAoBX"}
@@ -2,7 +2,6 @@ import { type Client } from '@dxos/client';
2
2
  import { type FunctionManifest } from '../manifest';
3
3
  export type InvokeOptions = {
4
4
  endpoint: string;
5
- runtime: string;
6
5
  };
7
6
  export declare class TriggerManager {
8
7
  private readonly _client;
@@ -1 +1 @@
1
- {"version":3,"file":"trigger-manager.d.ts","sourceRoot":"","sources":["../../../../src/runtime/trigger-manager.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,MAAM,EAAkB,MAAM,cAAc,CAAC;AAQ3D,OAAO,EAAE,KAAK,gBAAgB,EAAwB,MAAM,aAAa,CAAC;AAG1E,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,qBAAa,cAAc;IASvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAVjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAGiC;IAEzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;gBAG1B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,gBAAgB,EAC3B,cAAc,EAAE,aAAa;IAG1C,KAAK;IAaL,IAAI;YAMI,KAAK;YAuDL,OAAO;YASP,YAAY;CAqB3B"}
1
+ {"version":3,"file":"trigger-manager.d.ts","sourceRoot":"","sources":["../../../../src/runtime/trigger-manager.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,MAAM,EAAkB,MAAM,cAAc,CAAC;AAQ3D,OAAO,EAAE,KAAK,gBAAgB,EAAwB,MAAM,aAAa,CAAC;AAG1E,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,qBAAa,cAAc;IASvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAVjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAGiC;IAEzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;gBAG1B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,gBAAgB,EAC3B,cAAc,EAAE,aAAa;IAG1C,KAAK;IAaL,IAAI;YAMI,KAAK;YAuDL,OAAO;YASP,YAAY;CAoB3B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/functions",
3
- "version": "0.3.8-main.56eb422",
3
+ "version": "0.3.8-main.64c4d0a",
4
4
  "description": "Functions SDK and runtime.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -20,15 +20,15 @@
20
20
  ],
21
21
  "dependencies": {
22
22
  "express": "^4.17.1",
23
- "portfinder": "^1.0.32",
24
- "@dxos/async": "0.3.8-main.56eb422",
25
- "@dxos/client": "0.3.8-main.56eb422",
26
- "@dxos/context": "0.3.8-main.56eb422",
27
- "@dxos/echo-schema": "0.3.8-main.56eb422",
28
- "@dxos/invariant": "0.3.8-main.56eb422",
29
- "@dxos/log": "0.3.8-main.56eb422",
30
- "@dxos/node-std": "0.3.8-main.56eb422",
31
- "@dxos/util": "0.3.8-main.56eb422"
23
+ "get-port-please": "^3.1.1",
24
+ "@dxos/async": "0.3.8-main.64c4d0a",
25
+ "@dxos/context": "0.3.8-main.64c4d0a",
26
+ "@dxos/client": "0.3.8-main.64c4d0a",
27
+ "@dxos/echo-schema": "0.3.8-main.64c4d0a",
28
+ "@dxos/invariant": "0.3.8-main.64c4d0a",
29
+ "@dxos/util": "0.3.8-main.64c4d0a",
30
+ "@dxos/log": "0.3.8-main.64c4d0a",
31
+ "@dxos/node-std": "0.3.8-main.64c4d0a"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/express": "^4.17.17"
package/src/manifest.ts CHANGED
@@ -5,8 +5,8 @@
5
5
  export type FunctionDef = {
6
6
  // FQ function name.
7
7
  id: string;
8
- // HTTP endpoint.
9
- endpoint: string;
8
+ // URL path.
9
+ path: string;
10
10
  // Path of handler.
11
11
  handler: string;
12
12
  description?: string;
@@ -3,20 +3,20 @@
3
3
  //
4
4
 
5
5
  import express from 'express';
6
+ import { getPort } from 'get-port-please';
6
7
  import type http from 'http';
7
8
  import { join } from 'node:path';
8
- import { getPortPromise } from 'portfinder';
9
9
 
10
10
  import { Trigger } from '@dxos/async';
11
11
  import { type Client } from '@dxos/client';
12
+ import { invariant } from '@dxos/invariant';
12
13
  import { log } from '@dxos/log';
13
14
 
14
15
  import { type FunctionContext, type FunctionHandler, type Response } from '../handler';
15
16
  import { type FunctionDef, type FunctionManifest } from '../manifest';
16
17
 
17
- const DEFAULT_PORT = 7001;
18
-
19
18
  export type DevServerOptions = {
19
+ port?: number;
20
20
  directory: string;
21
21
  manifest: FunctionManifest;
22
22
  };
@@ -24,12 +24,14 @@ export type DevServerOptions = {
24
24
  /**
25
25
  * Functions dev server provides a local HTTP server for testing functions.
26
26
  */
27
+ // TODO(burdon): Reconcile with agent/functions dev dispatcher.
27
28
  export class DevServer {
28
29
  private readonly _handlers: Record<string, { def: FunctionDef; handler: FunctionHandler<any> }> = {};
29
30
 
30
31
  private _server?: http.Server;
31
32
  private _port?: number;
32
33
  private _registrationId?: string;
34
+ private _proxy?: string;
33
35
 
34
36
  // prettier-ignore
35
37
  constructor(
@@ -37,12 +39,13 @@ export class DevServer {
37
39
  private readonly _options: DevServerOptions,
38
40
  ) {}
39
41
 
40
- get port() {
41
- return this._port;
42
+ get endpoint() {
43
+ invariant(this._port);
44
+ return `http://localhost:${this._port}`;
42
45
  }
43
46
 
44
- get endpoint() {
45
- return this._port ? `http://localhost:${this._port}` : undefined;
47
+ get proxy() {
48
+ return this._proxy;
46
49
  }
47
50
 
48
51
  get functions() {
@@ -51,20 +54,20 @@ export class DevServer {
51
54
 
52
55
  async initialize() {
53
56
  for (const def of this._options.manifest.functions) {
54
- const { id, endpoint, handler: path } = def;
57
+ const { id, path, handler: dir } = def;
55
58
  try {
56
59
  // eslint-disable-next-line @typescript-eslint/no-var-requires
57
- const module = require(join(this._options.directory, path));
60
+ const module = require(join(this._options.directory, dir));
58
61
  const handler = module.default;
59
62
  if (typeof handler !== 'function') {
60
63
  throw new Error(`Handler must export default function: ${id}`);
61
64
  }
62
65
 
63
- if (this._handlers[endpoint]) {
66
+ if (this._handlers[path]) {
64
67
  log.warn(`Function already registered: ${id}`);
65
68
  }
66
69
 
67
- this._handlers[endpoint] = { def, handler };
70
+ this._handlers[path] = { def, handler };
68
71
  } catch (err) {
69
72
  log.error('parsing function (check manifest)', err);
70
73
  }
@@ -75,8 +78,8 @@ export class DevServer {
75
78
  const app = express();
76
79
  app.use(express.json());
77
80
 
78
- app.post('/:endpoint', async (req, res) => {
79
- const { endpoint } = req.params;
81
+ app.post('/:name', async (req, res) => {
82
+ const { name } = req.params;
80
83
 
81
84
  const response: Response = {
82
85
  status: (code: number) => {
@@ -97,8 +100,8 @@ export class DevServer {
97
100
 
98
101
  void (async () => {
99
102
  try {
100
- log('invoking', { endpoint });
101
- const { handler } = this._handlers[endpoint];
103
+ log(`invoking: ${name}`);
104
+ const { handler } = this._handlers[name];
102
105
  const response = await handler({ context, event: req.body });
103
106
  log('done', { response });
104
107
  } catch (err: any) {
@@ -108,21 +111,24 @@ export class DevServer {
108
111
  })();
109
112
  });
110
113
 
111
- this._port = await getPortPromise({ startPort: DEFAULT_PORT });
114
+ // TODO(burdon): Push down port management to agent.
115
+ this._port = await getPort({ port: 7200, portRange: [7200, 7299] });
112
116
  this._server = app.listen(this._port);
113
117
 
114
- // TODO(burdon): Check plugin is registered.
115
- // TypeError: Cannot read properties of undefined (reading 'register')
118
+ // TODO(burdon): Test during initialization.
116
119
  try {
117
- const { registrationId } = await this._client.services.services.FunctionRegistryService!.register({
118
- endpoint: this.endpoint!,
119
- functions: this.functions.map(({ def: { endpoint } }) => ({ name: endpoint })), // TODO(burdon): Change proto name => id.
120
+ // Register functions.
121
+ const { registrationId, endpoint } = await this._client.services.services.FunctionRegistryService!.register({
122
+ endpoint: this.endpoint,
123
+ functions: this.functions.map(({ def: { path } }) => ({ name: path })),
120
124
  });
121
125
 
126
+ log.info('registered', { registrationId, endpoint });
122
127
  this._registrationId = registrationId;
128
+ this._proxy = endpoint;
123
129
  } catch (err: any) {
124
130
  await this.stop();
125
- throw new Error('FunctionRegistryService not available; check config (agent.plugins.functions).');
131
+ throw new Error('FunctionRegistryService not available (check plugin is configured).');
126
132
  }
127
133
  }
128
134
 
@@ -133,14 +139,17 @@ export class DevServer {
133
139
  await this._client.services.services.FunctionRegistryService!.unregister({
134
140
  registrationId: this._registrationId,
135
141
  });
142
+
143
+ log.info('unregistered', { registrationId: this._registrationId });
136
144
  this._registrationId = undefined;
145
+ this._proxy = undefined;
137
146
  }
138
147
 
139
148
  trigger.wake();
140
149
  });
141
150
 
142
151
  await trigger.wait();
143
- this._server = undefined;
144
152
  this._port = undefined;
153
+ this._server = undefined;
145
154
  }
146
155
  }
@@ -16,7 +16,6 @@ import { type FunctionManifest, type FunctionTrigger } from '../manifest';
16
16
  // TODO(burdon): Rename.
17
17
  export type InvokeOptions = {
18
18
  endpoint: string;
19
- runtime: string;
20
19
  };
21
20
 
22
21
  export class TriggerManager {
@@ -67,7 +66,7 @@ export class TriggerManager {
67
66
  // TODO(burdon): Why DeferredTask? How to pass objectIds to function?
68
67
  const objectIds = new Set<string>();
69
68
  const task = new DeferredTask(ctx, async () => {
70
- await this.execFunction(this._invokeOptions, config.endpoint, {
69
+ await this.execFunction(this._invokeOptions, config.path, {
71
70
  space: space.key,
72
71
  objects: Array.from(objectIds),
73
72
  });
@@ -117,13 +116,12 @@ export class TriggerManager {
117
116
  }
118
117
 
119
118
  private async execFunction(options: InvokeOptions, functionName: string, data: any) {
120
- const { endpoint, runtime } = options;
119
+ const { endpoint } = options;
121
120
  invariant(endpoint, 'Missing endpoint');
122
- invariant(runtime, 'Missing runtime');
123
121
 
124
122
  try {
125
123
  log('invoke', { function: functionName });
126
- const url = `${endpoint}/${runtime}/${functionName}`;
124
+ const url = `${endpoint}/${functionName}`;
127
125
  const res = await fetch(url, {
128
126
  method: 'POST',
129
127
  body: JSON.stringify(data),