@dxos/functions 0.3.7 → 0.3.8-main.0ab6c73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/lib/browser/index.mjs +213 -144
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +228 -158
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/handler.d.ts +17 -0
- package/dist/types/src/handler.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +2 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/manifest.d.ts +25 -0
- package/dist/types/src/manifest.d.ts.map +1 -0
- package/dist/types/src/runtime/dev-server.d.ts +21 -5
- package/dist/types/src/runtime/dev-server.d.ts.map +1 -1
- package/dist/types/src/runtime/index.d.ts +1 -1
- package/dist/types/src/runtime/index.d.ts.map +1 -1
- package/dist/types/src/runtime/scheduler.d.ts +22 -0
- package/dist/types/src/runtime/scheduler.d.ts.map +1 -0
- package/package.json +11 -10
- package/src/handler.ts +28 -0
- package/src/index.ts +2 -1
- package/src/manifest.ts +40 -0
- package/src/runtime/dev-server.ts +99 -57
- package/src/runtime/index.ts +1 -1
- package/src/runtime/scheduler.ts +145 -0
- package/dist/types/src/function.d.ts +0 -36
- package/dist/types/src/function.d.ts.map +0 -1
- package/dist/types/src/runtime/trigger-manager.d.ts +0 -20
- package/dist/types/src/runtime/trigger-manager.d.ts.map +0 -1
- package/src/function.ts +0 -51
- package/src/runtime/trigger-manager.ts +0 -139
package/dist/lib/node/meta.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"packages/core/functions/src/
|
|
1
|
+
{"inputs":{"packages/core/functions/src/handler.ts":{"bytes":1313,"imports":[],"format":"esm"},"packages/core/functions/src/manifest.ts":{"bytes":1735,"imports":[],"format":"esm"},"packages/core/functions/src/runtime/dev-server.ts":{"bytes":18553,"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":16641,"imports":[{"path":"cron","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"format":"esm"},"packages/core/functions/src/runtime/index.ts":{"bytes":566,"imports":[{"path":"packages/core/functions/src/runtime/dev-server.ts","kind":"import-statement","original":"./dev-server"},{"path":"packages/core/functions/src/runtime/scheduler.ts","kind":"import-statement","original":"./scheduler"}],"format":"esm"},"packages/core/functions/src/index.ts":{"bytes":637,"imports":[{"path":"packages/core/functions/src/handler.ts","kind":"import-statement","original":"./handler"},{"path":"packages/core/functions/src/manifest.ts","kind":"import-statement","original":"./manifest"},{"path":"packages/core/functions/src/runtime/index.ts","kind":"import-statement","original":"./runtime"}],"format":"esm"}},"outputs":{"packages/core/functions/dist/lib/node/index.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":16608},"packages/core/functions/dist/lib/node/index.cjs":{"imports":[{"path":"express","kind":"import-statement","external":true},{"path":"get-port-please","kind":"import-statement","external":true},{"path":"node:path","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"cron","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/context","kind":"import-statement","external":true},{"path":"@dxos/echo-schema","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"exports":["DevServer","Scheduler"],"entryPoint":"packages/core/functions/src/index.ts","inputs":{"packages/core/functions/src/index.ts":{"bytesInOutput":0},"packages/core/functions/src/runtime/dev-server.ts":{"bytesInOutput":4729},"packages/core/functions/src/runtime/index.ts":{"bytesInOutput":0},"packages/core/functions/src/runtime/scheduler.ts":{"bytesInOutput":4189}},"bytes":9466}}}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Client } from '@dxos/client';
|
|
2
|
+
export interface Response {
|
|
3
|
+
status(code: number): Response;
|
|
4
|
+
}
|
|
5
|
+
export interface FunctionContext {
|
|
6
|
+
client: Client;
|
|
7
|
+
}
|
|
8
|
+
export type FunctionHandler<T extends {}> = (params: {
|
|
9
|
+
event: T;
|
|
10
|
+
context: FunctionContext;
|
|
11
|
+
response: Response;
|
|
12
|
+
}) => Promise<Response | void>;
|
|
13
|
+
export type FunctionSubscriptionEvent = {
|
|
14
|
+
space: string;
|
|
15
|
+
objects: string[];
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/handler.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C,MAAM,WAAW,QAAQ;IACvB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;CAChC;AAGD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;CAChB;AAID,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE;IACnD,KAAK,EAAE,CAAC,CAAC;IACT,OAAO,EAAE,eAAe,CAAC;IACzB,QAAQ,EAAE,QAAQ,CAAC;CACpB,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;AAE/B,MAAM,MAAM,yBAAyB,GAAG;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type FunctionDef = {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
handler: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
};
|
|
7
|
+
export type TriggerSubscription = {
|
|
8
|
+
type: string;
|
|
9
|
+
spaceKey: string;
|
|
10
|
+
props?: Record<string, any>;
|
|
11
|
+
nested?: string[];
|
|
12
|
+
};
|
|
13
|
+
export type FunctionTrigger = {
|
|
14
|
+
function: string;
|
|
15
|
+
schedule?: string;
|
|
16
|
+
subscription?: TriggerSubscription;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Function manifest file.
|
|
20
|
+
*/
|
|
21
|
+
export type FunctionManifest = {
|
|
22
|
+
functions: FunctionDef[];
|
|
23
|
+
triggers: FunctionTrigger[];
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/manifest.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,WAAW,GAAG;IAExB,EAAE,EAAE,MAAM,CAAC;IAEX,IAAI,EAAE,MAAM,CAAC;IAEb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;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,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,mBAAmB,CAAC;CACpC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,EAAE,WAAW,EAAE,CAAC;IACzB,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B,CAAC"}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { type Client } from '@dxos/client';
|
|
2
|
-
import { type
|
|
2
|
+
import { type FunctionHandler } from '../handler';
|
|
3
|
+
import { type FunctionDef, type FunctionManifest } from '../manifest';
|
|
3
4
|
export type DevServerOptions = {
|
|
5
|
+
port?: number;
|
|
4
6
|
directory: string;
|
|
5
|
-
manifest:
|
|
7
|
+
manifest: FunctionManifest;
|
|
8
|
+
reload?: boolean;
|
|
6
9
|
};
|
|
7
10
|
/**
|
|
8
11
|
* Functions dev server provides a local HTTP server for testing functions.
|
|
@@ -14,12 +17,25 @@ export declare class DevServer {
|
|
|
14
17
|
private _server?;
|
|
15
18
|
private _port?;
|
|
16
19
|
private _registrationId?;
|
|
20
|
+
private _proxy?;
|
|
21
|
+
private _seq;
|
|
17
22
|
constructor(_client: Client, _options: DevServerOptions);
|
|
18
|
-
get
|
|
19
|
-
get
|
|
20
|
-
get functions():
|
|
23
|
+
get endpoint(): string;
|
|
24
|
+
get proxy(): string | undefined;
|
|
25
|
+
get functions(): {
|
|
26
|
+
def: FunctionDef;
|
|
27
|
+
handler: FunctionHandler<any>;
|
|
28
|
+
}[];
|
|
21
29
|
initialize(): Promise<void>;
|
|
22
30
|
start(): Promise<void>;
|
|
23
31
|
stop(): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Load function.
|
|
34
|
+
*/
|
|
35
|
+
private _load;
|
|
36
|
+
/**
|
|
37
|
+
* Invoke function handler.
|
|
38
|
+
*/
|
|
39
|
+
private _invoke;
|
|
24
40
|
}
|
|
25
41
|
//# sourceMappingURL=dev-server.d.ts.map
|
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../../../../src/runtime/dev-server.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAI3C,OAAO,EAAwB,KAAK,eAAe,EAAiB,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEtE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;GAEG;AACH,qBAAa,SAAS;IAYlB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAX3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA2E;IAErG,OAAO,CAAC,OAAO,CAAC,CAAc;IAC9B,OAAO,CAAC,KAAK,CAAC,CAAS;IACvB,OAAO,CAAC,eAAe,CAAC,CAAS;IACjC,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,IAAI,CAAK;gBAIE,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,gBAAgB;IAG7C,IAAI,QAAQ,WAGX;IAED,IAAI,KAAK,uBAER;IAED,IAAI,SAAS;;;QAEZ;IAEK,UAAU;IAUV,KAAK;IAwCL,IAAI;IAqBV;;OAEG;YACW,KAAK;IAqBnB;;OAEG;YACW,OAAO;CAwBtB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/runtime/index.ts"],"names":[],"mappings":"AAIA,cAAc,cAAc,CAAC;AAC7B,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/runtime/index.ts"],"names":[],"mappings":"AAIA,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type Client } from '@dxos/client';
|
|
2
|
+
import { type FunctionManifest } from '../manifest';
|
|
3
|
+
type SchedulerOptions = {
|
|
4
|
+
endpoint: string;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Functions scheduler.
|
|
8
|
+
*/
|
|
9
|
+
export declare class Scheduler {
|
|
10
|
+
private readonly _client;
|
|
11
|
+
private readonly _manifest;
|
|
12
|
+
private readonly _options;
|
|
13
|
+
private readonly _mounts;
|
|
14
|
+
constructor(_client: Client, _manifest: FunctionManifest, _options: SchedulerOptions);
|
|
15
|
+
start(): Promise<void>;
|
|
16
|
+
stop(): Promise<void>;
|
|
17
|
+
private mount;
|
|
18
|
+
private unmount;
|
|
19
|
+
private execFunction;
|
|
20
|
+
}
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=scheduler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../../../src/runtime/scheduler.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,MAAM,EAAkB,MAAM,cAAc,CAAC;AAQ3D,OAAO,EAAoB,KAAK,gBAAgB,EAAwB,MAAM,aAAa,CAAC;AAE5F,KAAK,gBAAgB,GAAG;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;GAEG;AAEH,qBAAa,SAAS;IAQlB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAR3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAG6B;gBAGlC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,gBAAgB,EAC3B,QAAQ,EAAE,gBAAgB;IAGvC,KAAK;IAWL,IAAI;YAMI,KAAK;YA+DL,OAAO;YASP,YAAY;CAiB3B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/functions",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8-main.0ab6c73",
|
|
4
4
|
"description": "Functions SDK and runtime.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -19,16 +19,17 @@
|
|
|
19
19
|
"src"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
+
"cron": "^3.1.6",
|
|
22
23
|
"express": "^4.17.1",
|
|
23
|
-
"
|
|
24
|
-
"@dxos/async": "0.3.
|
|
25
|
-
"@dxos/
|
|
26
|
-
"@dxos/
|
|
27
|
-
"@dxos/
|
|
28
|
-
"@dxos/invariant": "0.3.
|
|
29
|
-
"@dxos/
|
|
30
|
-
"@dxos/
|
|
31
|
-
"@dxos/util": "0.3.
|
|
24
|
+
"get-port-please": "^3.1.1",
|
|
25
|
+
"@dxos/async": "0.3.8-main.0ab6c73",
|
|
26
|
+
"@dxos/context": "0.3.8-main.0ab6c73",
|
|
27
|
+
"@dxos/echo-schema": "0.3.8-main.0ab6c73",
|
|
28
|
+
"@dxos/client": "0.3.8-main.0ab6c73",
|
|
29
|
+
"@dxos/invariant": "0.3.8-main.0ab6c73",
|
|
30
|
+
"@dxos/node-std": "0.3.8-main.0ab6c73",
|
|
31
|
+
"@dxos/log": "0.3.8-main.0ab6c73",
|
|
32
|
+
"@dxos/util": "0.3.8-main.0ab6c73"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
35
|
"@types/express": "^4.17.17"
|
package/src/handler.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Client } from '@dxos/client';
|
|
6
|
+
|
|
7
|
+
// TODO(burdon): No response?
|
|
8
|
+
export interface Response {
|
|
9
|
+
status(code: number): Response;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// TODO(burdon): Limit access to individual space?
|
|
13
|
+
export interface FunctionContext {
|
|
14
|
+
client: Client;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// TODO(burdon): Model after http request. Ref Lambda/OpenFaaS.
|
|
18
|
+
// https://docs.aws.amazon.com/lambda/latest/dg/typescript-handler.html
|
|
19
|
+
export type FunctionHandler<T extends {}> = (params: {
|
|
20
|
+
event: T;
|
|
21
|
+
context: FunctionContext;
|
|
22
|
+
response: Response;
|
|
23
|
+
}) => Promise<Response | void>;
|
|
24
|
+
|
|
25
|
+
export type FunctionSubscriptionEvent = {
|
|
26
|
+
space: string; // TODO(burdon): Convert to PublicKey.
|
|
27
|
+
objects: string[];
|
|
28
|
+
};
|
package/src/index.ts
CHANGED
package/src/manifest.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// Lambda-like function definitions.
|
|
6
|
+
// See: https://www.serverless.com/framework/docs/providers/aws/guide/serverless.yml/#functions
|
|
7
|
+
|
|
8
|
+
export type FunctionDef = {
|
|
9
|
+
// FQ function name.
|
|
10
|
+
id: string;
|
|
11
|
+
// URL path.
|
|
12
|
+
name: string;
|
|
13
|
+
// File path of handler.
|
|
14
|
+
handler: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type TriggerSubscription = {
|
|
19
|
+
type: string;
|
|
20
|
+
spaceKey: string;
|
|
21
|
+
props?: Record<string, any>;
|
|
22
|
+
nested?: string[];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// TODO(burdon): Generalize binding.
|
|
26
|
+
// https://www.npmjs.com/package/aws-lambda
|
|
27
|
+
// https://docs.aws.amazon.com/lambda/latest/dg/typescript-handler.html
|
|
28
|
+
export type FunctionTrigger = {
|
|
29
|
+
function: string;
|
|
30
|
+
schedule?: string;
|
|
31
|
+
subscription?: TriggerSubscription;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Function manifest file.
|
|
36
|
+
*/
|
|
37
|
+
export type FunctionManifest = {
|
|
38
|
+
functions: FunctionDef[];
|
|
39
|
+
triggers: FunctionTrigger[];
|
|
40
|
+
};
|
|
@@ -3,32 +3,37 @@
|
|
|
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
|
-
import { type FunctionContext, type FunctionHandler, type
|
|
15
|
-
|
|
16
|
-
const DEFAULT_PORT = 7000;
|
|
15
|
+
import { type FunctionContext, type FunctionHandler, type Response } from '../handler';
|
|
16
|
+
import { type FunctionDef, type FunctionManifest } from '../manifest';
|
|
17
17
|
|
|
18
18
|
export type DevServerOptions = {
|
|
19
|
+
port?: number;
|
|
19
20
|
directory: string;
|
|
20
|
-
manifest:
|
|
21
|
+
manifest: FunctionManifest;
|
|
22
|
+
reload?: boolean;
|
|
21
23
|
};
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
26
|
* Functions dev server provides a local HTTP server for testing functions.
|
|
25
27
|
*/
|
|
26
28
|
export class DevServer {
|
|
27
|
-
|
|
29
|
+
// Function handlers indexed by name (URL path).
|
|
30
|
+
private readonly _handlers: Record<string, { def: FunctionDef; handler: FunctionHandler<any> }> = {};
|
|
28
31
|
|
|
29
32
|
private _server?: http.Server;
|
|
30
33
|
private _port?: number;
|
|
31
34
|
private _registrationId?: string;
|
|
35
|
+
private _proxy?: string;
|
|
36
|
+
private _seq = 0;
|
|
32
37
|
|
|
33
38
|
// prettier-ignore
|
|
34
39
|
constructor(
|
|
@@ -36,31 +41,25 @@ export class DevServer {
|
|
|
36
41
|
private readonly _options: DevServerOptions,
|
|
37
42
|
) {}
|
|
38
43
|
|
|
39
|
-
get
|
|
40
|
-
|
|
44
|
+
get endpoint() {
|
|
45
|
+
invariant(this._port);
|
|
46
|
+
return `http://localhost:${this._port}`;
|
|
41
47
|
}
|
|
42
48
|
|
|
43
|
-
get
|
|
44
|
-
return this.
|
|
49
|
+
get proxy() {
|
|
50
|
+
return this._proxy;
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
get functions() {
|
|
48
|
-
return Object.
|
|
54
|
+
return Object.values(this._handlers);
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
async initialize() {
|
|
52
|
-
for (const
|
|
58
|
+
for (const def of this._options.manifest.functions) {
|
|
53
59
|
try {
|
|
54
|
-
|
|
55
|
-
const module = require(join(this._options.directory, id));
|
|
56
|
-
const handler = module.default;
|
|
57
|
-
if (typeof handler !== 'function') {
|
|
58
|
-
throw new Error(`Handler must export default function: ${id}`);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
this._handlers[id] = handler;
|
|
60
|
+
await this._load(def);
|
|
62
61
|
} catch (err) {
|
|
63
|
-
log.error('parsing function (check
|
|
62
|
+
log.error('parsing function (check manifest)', err);
|
|
64
63
|
}
|
|
65
64
|
}
|
|
66
65
|
}
|
|
@@ -69,51 +68,39 @@ export class DevServer {
|
|
|
69
68
|
const app = express();
|
|
70
69
|
app.use(express.json());
|
|
71
70
|
|
|
72
|
-
app.post('/:
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
res.statusCode = code;
|
|
79
|
-
return builder;
|
|
80
|
-
},
|
|
81
|
-
succeed: (result = {}) => {
|
|
82
|
-
res.end(JSON.stringify(result));
|
|
83
|
-
return builder;
|
|
84
|
-
},
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const context: FunctionContext = {
|
|
88
|
-
client: this._client,
|
|
89
|
-
status: builder.status.bind(builder),
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
void (async () => {
|
|
93
|
-
try {
|
|
94
|
-
// TODO(burdon): Typed event handler.
|
|
95
|
-
await this._handlers[functionName]({ event: req.body, context });
|
|
96
|
-
} catch (err: any) {
|
|
97
|
-
res.statusCode = 500;
|
|
98
|
-
res.end(err.message);
|
|
71
|
+
app.post('/:name', async (req, res) => {
|
|
72
|
+
const { name } = req.params;
|
|
73
|
+
try {
|
|
74
|
+
if (this._options.reload) {
|
|
75
|
+
const { def } = this._handlers[name];
|
|
76
|
+
await this._load(def, true);
|
|
99
77
|
}
|
|
100
|
-
|
|
78
|
+
|
|
79
|
+
res.statusCode = await this._invoke(name, req.body);
|
|
80
|
+
res.end();
|
|
81
|
+
} catch (err: any) {
|
|
82
|
+
log.error(err);
|
|
83
|
+
res.statusCode = 500;
|
|
84
|
+
res.end();
|
|
85
|
+
}
|
|
101
86
|
});
|
|
102
87
|
|
|
103
|
-
this._port = await
|
|
88
|
+
this._port = await getPort({ port: 7200, portRange: [7200, 7299] });
|
|
104
89
|
this._server = app.listen(this._port);
|
|
105
90
|
|
|
106
|
-
// TODO(burdon): Check plugin is registered.
|
|
107
|
-
// TypeError: Cannot read properties of undefined (reading 'register')
|
|
108
91
|
try {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
92
|
+
// Register functions.
|
|
93
|
+
const { registrationId, endpoint } = await this._client.services.services.FunctionRegistryService!.register({
|
|
94
|
+
endpoint: this.endpoint,
|
|
95
|
+
functions: this.functions.map(({ def: { name } }) => ({ name })),
|
|
112
96
|
});
|
|
97
|
+
|
|
98
|
+
log.info('registered', { registrationId, endpoint });
|
|
113
99
|
this._registrationId = registrationId;
|
|
100
|
+
this._proxy = endpoint;
|
|
114
101
|
} catch (err: any) {
|
|
115
102
|
await this.stop();
|
|
116
|
-
throw new Error('FunctionRegistryService not available
|
|
103
|
+
throw new Error('FunctionRegistryService not available (check plugin is configured).');
|
|
117
104
|
}
|
|
118
105
|
}
|
|
119
106
|
|
|
@@ -124,14 +111,69 @@ export class DevServer {
|
|
|
124
111
|
await this._client.services.services.FunctionRegistryService!.unregister({
|
|
125
112
|
registrationId: this._registrationId,
|
|
126
113
|
});
|
|
114
|
+
|
|
115
|
+
log.info('unregistered', { registrationId: this._registrationId });
|
|
127
116
|
this._registrationId = undefined;
|
|
117
|
+
this._proxy = undefined;
|
|
128
118
|
}
|
|
129
119
|
|
|
130
120
|
trigger.wake();
|
|
131
121
|
});
|
|
132
122
|
|
|
133
123
|
await trigger.wait();
|
|
134
|
-
this._server = undefined;
|
|
135
124
|
this._port = undefined;
|
|
125
|
+
this._server = undefined;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Load function.
|
|
130
|
+
*/
|
|
131
|
+
private async _load(def: FunctionDef, flush = false) {
|
|
132
|
+
const { id, name, handler } = def;
|
|
133
|
+
const path = join(this._options.directory, handler);
|
|
134
|
+
log.info('loading', { id });
|
|
135
|
+
|
|
136
|
+
// Remove from cache.
|
|
137
|
+
if (flush) {
|
|
138
|
+
Object.keys(require.cache)
|
|
139
|
+
.filter((key) => key.startsWith(path))
|
|
140
|
+
.forEach((key) => delete require.cache[key]);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
144
|
+
const module = require(path);
|
|
145
|
+
if (typeof module.default !== 'function') {
|
|
146
|
+
throw new Error(`Handler must export default function: ${id}`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
this._handlers[name] = { def, handler: module.default };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Invoke function handler.
|
|
154
|
+
*/
|
|
155
|
+
private async _invoke(name: string, event: any) {
|
|
156
|
+
const seq = ++this._seq;
|
|
157
|
+
const now = Date.now();
|
|
158
|
+
|
|
159
|
+
log.info('req', { seq, name });
|
|
160
|
+
const { handler } = this._handlers[name];
|
|
161
|
+
|
|
162
|
+
const context: FunctionContext = {
|
|
163
|
+
client: this._client,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
let statusCode = 200;
|
|
167
|
+
const response: Response = {
|
|
168
|
+
status: (code: number) => {
|
|
169
|
+
statusCode = code;
|
|
170
|
+
return response;
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
await handler({ context, event, response });
|
|
175
|
+
log.info('res', { seq, name, statusCode, duration: Date.now() - now });
|
|
176
|
+
|
|
177
|
+
return statusCode;
|
|
136
178
|
}
|
|
137
179
|
}
|
package/src/runtime/index.ts
CHANGED