lambda-reactor 1.0.0 → 1.0.1

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.
Files changed (69) hide show
  1. package/{src/build-handlers.ts → dist/build-handlers.d.ts} +3 -16
  2. package/dist/build-handlers.js +13 -0
  3. package/dist/build-handlers.js.map +10 -0
  4. package/dist/dispatch.d.ts +21 -0
  5. package/dist/dispatch.js +58 -0
  6. package/dist/dispatch.js.map +10 -0
  7. package/dist/env.d.ts +6 -0
  8. package/dist/env.js +13 -0
  9. package/dist/env.js.map +10 -0
  10. package/dist/handler.d.ts +26 -0
  11. package/dist/handler.js +29 -0
  12. package/dist/handler.js.map +10 -0
  13. package/dist/logger.d.ts +8 -0
  14. package/dist/logger.js +21 -0
  15. package/dist/logger.js.map +10 -0
  16. package/dist/method.d.ts +53 -0
  17. package/dist/method.js +57 -0
  18. package/dist/method.js.map +10 -0
  19. package/{src/middleware.ts → dist/middleware.d.ts} +5 -22
  20. package/dist/middleware.js +19 -0
  21. package/dist/middleware.js.map +10 -0
  22. package/dist/response.d.ts +48 -0
  23. package/dist/response.js +46 -0
  24. package/dist/response.js.map +10 -0
  25. package/{src/route-handler.ts → dist/route-handler.d.ts} +12 -14
  26. package/dist/route-handler.js +2 -0
  27. package/dist/route-handler.js.map +9 -0
  28. package/dist/router-class.d.ts +54 -0
  29. package/dist/router-class.js +46 -0
  30. package/dist/router-class.js.map +10 -0
  31. package/{src/router.ts → dist/router.d.ts} +4 -16
  32. package/dist/router.js +18 -0
  33. package/dist/router.js.map +10 -0
  34. package/package.json +12 -2
  35. package/.prettierrc +0 -8
  36. package/AGENTS.md +0 -7
  37. package/bun.lock +0 -477
  38. package/eslint.config.ts +0 -31
  39. package/examples/cdk-stack.ts +0 -20
  40. package/examples/health.ts +0 -7
  41. package/examples/items.ts +0 -25
  42. package/examples/users-get.ts +0 -20
  43. package/examples/users-post.ts +0 -26
  44. package/lefthook.yml +0 -16
  45. package/src/dispatch.ts +0 -91
  46. package/src/env.ts +0 -23
  47. package/src/handler.ts +0 -58
  48. package/src/logger.ts +0 -19
  49. package/src/method.ts +0 -92
  50. package/src/response.ts +0 -86
  51. package/src/router-class.ts +0 -98
  52. package/tests/api-get-methods.test.ts +0 -14
  53. package/tests/api-router-factory.test.ts +0 -46
  54. package/tests/api-router.test.ts +0 -72
  55. package/tests/config-cors.test.ts +0 -50
  56. package/tests/config.test.ts +0 -79
  57. package/tests/dispatch-error-logging.test.ts +0 -61
  58. package/tests/env.test.ts +0 -38
  59. package/tests/handler-error-logging.test.ts +0 -61
  60. package/tests/handler-routing-validation.test.ts +0 -86
  61. package/tests/handler-routing.test.ts +0 -37
  62. package/tests/handler.test.ts +0 -48
  63. package/tests/method.test.ts +0 -40
  64. package/tests/response.test.ts +0 -29
  65. package/tsconfig.build.json +0 -26
  66. package/tsconfig.json +0 -3
  67. package/tsconfig.node.json +0 -24
  68. package/vitest.config.ts +0 -21
  69. package/vitest.setup.ts +0 -2
@@ -1,8 +1,7 @@
1
- import {type Middleware} from "#src/middleware"
2
- import {Response} from "#src/response"
3
- import type {APIGatewayProxyEvent, Context} from "aws-lambda"
4
- import type {ZodType} from "zod"
5
-
1
+ import { type Middleware } from "#src/middleware";
2
+ import { Response } from "#src/response";
3
+ import type { APIGatewayProxyEvent, Context } from "aws-lambda";
4
+ import type { ZodType } from "zod";
6
5
  /**
7
6
  * The user-supplied handler function for a single HTTP method on a route.
8
7
  *
@@ -10,19 +9,18 @@ import type {ZodType} from "zod"
10
9
  * @typeParam TOutput - Shape of the value returned by the callback.
11
10
  */
12
11
  export type EndpointCallback<TInput, TOutput> = (props: {
13
- body: TInput
14
- event: APIGatewayProxyEvent
15
- context: Context
16
- }) => Promise<TOutput | Response> | TOutput | Response
17
-
12
+ body: TInput;
13
+ event: APIGatewayProxyEvent;
14
+ context: Context;
15
+ }) => Promise<TOutput | Response> | TOutput | Response;
18
16
  /**
19
17
  * Internal read-only view of a {@link Method} used by {@link dispatch} and
20
18
  * {@link createHandler}. The type parameters are erased so that handlers for
21
19
  * different routes can be stored in the same map.
22
20
  */
23
21
  export interface RouteHandler {
24
- readonly middlewares: Middleware[]
25
- readonly bodySchema?: ZodType<unknown>
26
- readonly outputSchema?: ZodType<unknown>
27
- readonly callback?: (props: never) => unknown
22
+ readonly middlewares: Middleware[];
23
+ readonly bodySchema?: ZodType<unknown>;
24
+ readonly outputSchema?: ZodType<unknown>;
25
+ readonly callback?: (props: never) => unknown;
28
26
  }
@@ -0,0 +1,2 @@
1
+
2
+ //# debugId=25332E27D5EF449C64756E2164756E21
@@ -0,0 +1,9 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [
5
+ ],
6
+ "mappings": "",
7
+ "debugId": "25332E27D5EF449C64756E2164756E21",
8
+ "names": []
9
+ }
@@ -0,0 +1,54 @@
1
+ import { type FunctionFactory } from "#src/build-handlers";
2
+ import { type CorsOptions, IRestApi } from "aws-cdk-lib/aws-apigateway";
3
+ import { IFunction } from "aws-cdk-lib/aws-lambda";
4
+ /**
5
+ * Immutable CDK router that maps route paths to Lambda-backed API Gateway
6
+ * resources.
7
+ *
8
+ * ```ts
9
+ * const api = router()
10
+ * .cors({allowOrigins: ["*"]})
11
+ * .route("/users")
12
+ * .route("/items")
13
+ * .defineRestApi(restApi, factory)
14
+ * ```
15
+ *
16
+ * @typeParam TPaths - Union of all route path strings registered so far.
17
+ */
18
+ export declare class Router<TPaths extends string = never> {
19
+ srcDir: string;
20
+ corsOptions?: CorsOptions;
21
+ paths: TPaths[];
22
+ constructor(srcDir: string);
23
+ /**
24
+ * Returns a new `Router` with CORS preflight options applied to every
25
+ * resource added via {@link Router.route}.
26
+ *
27
+ * @param options - CDK `CorsOptions` passed to `addCorsPreflight`.
28
+ */
29
+ cors(options: CorsOptions): Router<TPaths>;
30
+ /**
31
+ * Registers a new route path and returns a new `Router` with the path
32
+ * added to its path union.
33
+ *
34
+ * @param path - Route path string (e.g. `"/users"`). Must correspond to a
35
+ * handler file at `<srcDir>/<path>.ts`.
36
+ */
37
+ route<TPath extends string>(path: TPath): Router<TPaths | TPath>;
38
+ /**
39
+ * Wires all registered routes into the given API Gateway REST API.
40
+ *
41
+ * For each route path the method:
42
+ * 1. Creates (or reuses) nested API Gateway resources for each path segment.
43
+ * 2. Optionally calls `addCorsPreflight` when CORS options are configured.
44
+ * 3. Reads the handler source file and extracts HTTP method names via
45
+ * {@link getMethods}.
46
+ * 4. Adds an `addMethod` entry backed by a `LambdaIntegration` for each
47
+ * discovered method.
48
+ *
49
+ * @param api - The CDK `IRestApi` to attach resources and methods to.
50
+ * @param factory - {@link FunctionFactory} used to build Lambda functions.
51
+ * @returns A record mapping each route path to its {@link IFunction}.
52
+ */
53
+ defineRestApi<TApi extends IRestApi>(api: TApi, factory: FunctionFactory): Record<TPaths, IFunction>;
54
+ }
@@ -0,0 +1,46 @@
1
+ // src/router-class.ts
2
+ import { readFileSync } from "fs";
3
+ import { join } from "path";
4
+ import { buildHandlers } from "#src/build-handlers";
5
+ import { getMethods } from "#src/router";
6
+ import { LambdaIntegration } from "aws-cdk-lib/aws-apigateway";
7
+
8
+ class Router {
9
+ srcDir;
10
+ corsOptions;
11
+ paths = [];
12
+ constructor(srcDir) {
13
+ this.srcDir = srcDir;
14
+ }
15
+ cors(options) {
16
+ const r = new Router(this.srcDir);
17
+ r.corsOptions = options;
18
+ r.paths = this.paths;
19
+ return r;
20
+ }
21
+ route(path) {
22
+ const r = new Router(this.srcDir);
23
+ if (this.corsOptions)
24
+ r.corsOptions = this.corsOptions;
25
+ r.paths = [...this.paths, path];
26
+ return r;
27
+ }
28
+ defineRestApi(api, factory) {
29
+ const handlers = buildHandlers(this.paths, factory);
30
+ for (const path of this.paths) {
31
+ const resource = path.split("/").filter(Boolean).reduce((r, part) => r.getResource(part) ?? r.addResource(part), api.root);
32
+ if (this.corsOptions)
33
+ resource.addCorsPreflight(this.corsOptions);
34
+ const src = readFileSync(join(process.cwd(), this.srcDir, `${path}.ts`), "utf-8");
35
+ for (const method of getMethods(src)) {
36
+ resource.addMethod(method, new LambdaIntegration(handlers[path]));
37
+ }
38
+ }
39
+ return handlers;
40
+ }
41
+ }
42
+ export {
43
+ Router
44
+ };
45
+
46
+ //# debugId=C6AD2943C8356E8564756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/router-class.ts"],
4
+ "sourcesContent": [
5
+ "import {readFileSync} from \"fs\"\nimport {join} from \"path\"\n\nimport {buildHandlers, type FunctionFactory} from \"#src/build-handlers\"\nimport {getMethods} from \"#src/router\"\nimport {type CorsOptions, IRestApi, LambdaIntegration} from \"aws-cdk-lib/aws-apigateway\"\nimport {IFunction} from \"aws-cdk-lib/aws-lambda\"\n\n/**\n * Immutable CDK router that maps route paths to Lambda-backed API Gateway\n * resources.\n *\n * ```ts\n * const api = router()\n * .cors({allowOrigins: [\"*\"]})\n * .route(\"/users\")\n * .route(\"/items\")\n * .defineRestApi(restApi, factory)\n * ```\n *\n * @typeParam TPaths - Union of all route path strings registered so far.\n */\nexport class Router<TPaths extends string = never> {\n srcDir: string\n corsOptions?: CorsOptions\n paths: TPaths[] = []\n\n constructor(srcDir: string) {\n this.srcDir = srcDir\n }\n\n /**\n * Returns a new `Router` with CORS preflight options applied to every\n * resource added via {@link Router.route}.\n *\n * @param options - CDK `CorsOptions` passed to `addCorsPreflight`.\n */\n cors(options: CorsOptions): Router<TPaths> {\n const r = new Router<TPaths>(this.srcDir)\n r.corsOptions = options\n r.paths = this.paths\n return r\n }\n\n /**\n * Registers a new route path and returns a new `Router` with the path\n * added to its path union.\n *\n * @param path - Route path string (e.g. `\"/users\"`). Must correspond to a\n * handler file at `<srcDir>/<path>.ts`.\n */\n route<TPath extends string>(path: TPath): Router<TPaths | TPath> {\n const r = new Router<TPaths | TPath>(this.srcDir)\n if (this.corsOptions) r.corsOptions = this.corsOptions\n r.paths = [...this.paths, path as unknown as TPaths | TPath]\n return r\n }\n\n /**\n * Wires all registered routes into the given API Gateway REST API.\n *\n * For each route path the method:\n * 1. Creates (or reuses) nested API Gateway resources for each path segment.\n * 2. Optionally calls `addCorsPreflight` when CORS options are configured.\n * 3. Reads the handler source file and extracts HTTP method names via\n * {@link getMethods}.\n * 4. Adds an `addMethod` entry backed by a `LambdaIntegration` for each\n * discovered method.\n *\n * @param api - The CDK `IRestApi` to attach resources and methods to.\n * @param factory - {@link FunctionFactory} used to build Lambda functions.\n * @returns A record mapping each route path to its {@link IFunction}.\n */\n defineRestApi<TApi extends IRestApi>(\n api: TApi,\n factory: FunctionFactory,\n ): Record<TPaths, IFunction> {\n const handlers = buildHandlers(this.paths, factory)\n for (const path of this.paths) {\n const resource = path\n .split(\"/\")\n .filter(Boolean)\n .reduce(\n (r, part) => r.getResource(part) ?? r.addResource(part),\n api.root,\n )\n if (this.corsOptions) resource.addCorsPreflight(this.corsOptions)\n const src = readFileSync(\n join(process.cwd(), this.srcDir, `${path}.ts`),\n \"utf-8\",\n )\n for (const method of getMethods(src)) {\n resource.addMethod(method, new LambdaIntegration(handlers[path]!))\n }\n }\n return handlers\n }\n}\n"
6
+ ],
7
+ "mappings": ";AAAA;AACA;AAEA;AACA;AACA;AAAA;AAiBO,MAAM,OAAsC;AAAA,EAC/C;AAAA,EACA;AAAA,EACA,QAAkB,CAAC;AAAA,EAEnB,WAAW,CAAC,QAAgB;AAAA,IACxB,KAAK,SAAS;AAAA;AAAA,EASlB,IAAI,CAAC,SAAsC;AAAA,IACvC,MAAM,IAAI,IAAI,OAAe,KAAK,MAAM;AAAA,IACxC,EAAE,cAAc;AAAA,IAChB,EAAE,QAAQ,KAAK;AAAA,IACf,OAAO;AAAA;AAAA,EAUX,KAA2B,CAAC,MAAqC;AAAA,IAC7D,MAAM,IAAI,IAAI,OAAuB,KAAK,MAAM;AAAA,IAChD,IAAI,KAAK;AAAA,MAAa,EAAE,cAAc,KAAK;AAAA,IAC3C,EAAE,QAAQ,CAAC,GAAG,KAAK,OAAO,IAAiC;AAAA,IAC3D,OAAO;AAAA;AAAA,EAkBX,aAAoC,CAChC,KACA,SACyB;AAAA,IACzB,MAAM,WAAW,cAAc,KAAK,OAAO,OAAO;AAAA,IAClD,WAAW,QAAQ,KAAK,OAAO;AAAA,MAC3B,MAAM,WAAW,KACZ,MAAM,GAAG,EACT,OAAO,OAAO,EACd,OACG,CAAC,GAAG,SAAS,EAAE,YAAY,IAAI,KAAK,EAAE,YAAY,IAAI,GACtD,IAAI,IACR;AAAA,MACJ,IAAI,KAAK;AAAA,QAAa,SAAS,iBAAiB,KAAK,WAAW;AAAA,MAChE,MAAM,MAAM,aACR,KAAK,QAAQ,IAAI,GAAG,KAAK,QAAQ,GAAG,SAAS,GAC7C,OACJ;AAAA,MACA,WAAW,UAAU,WAAW,GAAG,GAAG;AAAA,QAClC,SAAS,UAAU,QAAQ,IAAI,kBAAkB,SAAS,KAAM,CAAC;AAAA,MACrE;AAAA,IACJ;AAAA,IACA,OAAO;AAAA;AAEf;",
8
+ "debugId": "C6AD2943C8356E8564756E2164756E21",
9
+ "names": []
10
+ }
@@ -1,7 +1,5 @@
1
- import {Router} from "#src/router-class"
2
-
3
- export {Router}
4
-
1
+ import { Router } from "#src/router-class";
2
+ export { Router };
5
3
  /**
6
4
  * Extracts the HTTP method names declared inside a `createHandler({…})` call
7
5
  * by statically scanning the source text of a handler file.
@@ -10,21 +8,11 @@ export {Router}
10
8
  * @returns Array of upper-case HTTP method names (e.g. `["GET", "POST"]`),
11
9
  * or an empty array when no `createHandler` call is found.
12
10
  */
13
- export function getMethods(src: string): string[] {
14
- const match = src.match(/createHandler\(\{([^}]*)\}\)/)
15
- if (!match || !match[1]) return []
16
- return match[1]
17
- .split(",")
18
- .map((s) => s.trim())
19
- .filter(Boolean)
20
- }
21
-
11
+ export declare function getMethods(src: string): string[];
22
12
  /**
23
13
  * Creates a new, empty {@link Router} builder.
24
14
  *
25
15
  * @param srcDir - Directory (relative to `cwd`) where handler `.ts` files
26
16
  * live. Defaults to `"src"`.
27
17
  */
28
- export function router(srcDir: string = "src"): Router {
29
- return new Router(srcDir)
30
- }
18
+ export declare function router(srcDir?: string): Router;
package/dist/router.js ADDED
@@ -0,0 +1,18 @@
1
+ // src/router.ts
2
+ import { Router } from "#src/router-class";
3
+ function getMethods(src) {
4
+ const match = src.match(/createHandler\(\{([^}]*)\}\)/);
5
+ if (!match || !match[1])
6
+ return [];
7
+ return match[1].split(",").map((s) => s.trim()).filter(Boolean);
8
+ }
9
+ function router(srcDir = "src") {
10
+ return new Router(srcDir);
11
+ }
12
+ export {
13
+ router,
14
+ getMethods,
15
+ Router
16
+ };
17
+
18
+ //# debugId=555487F73388CB1F64756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/router.ts"],
4
+ "sourcesContent": [
5
+ "import {Router} from \"#src/router-class\"\n\nexport {Router}\n\n/**\n * Extracts the HTTP method names declared inside a `createHandler({…})` call\n * by statically scanning the source text of a handler file.\n *\n * @param src - Raw TypeScript source of a handler module.\n * @returns Array of upper-case HTTP method names (e.g. `[\"GET\", \"POST\"]`),\n * or an empty array when no `createHandler` call is found.\n */\nexport function getMethods(src: string): string[] {\n const match = src.match(/createHandler\\(\\{([^}]*)\\}\\)/)\n if (!match || !match[1]) return []\n return match[1]\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean)\n}\n\n/**\n * Creates a new, empty {@link Router} builder.\n *\n * @param srcDir - Directory (relative to `cwd`) where handler `.ts` files\n * live. Defaults to `\"src\"`.\n */\nexport function router(srcDir: string = \"src\"): Router {\n return new Router(srcDir)\n}\n"
6
+ ],
7
+ "mappings": ";AAAA;AAYO,SAAS,UAAU,CAAC,KAAuB;AAAA,EAC9C,MAAM,QAAQ,IAAI,MAAM,8BAA8B;AAAA,EACtD,IAAI,CAAC,SAAS,CAAC,MAAM;AAAA,IAAI,OAAO,CAAC;AAAA,EACjC,OAAO,MAAM,GACR,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AAAA;AAShB,SAAS,MAAM,CAAC,SAAiB,OAAe;AAAA,EACnD,OAAO,IAAI,OAAO,MAAM;AAAA;",
8
+ "debugId": "555487F73388CB1F64756E2164756E21",
9
+ "names": []
10
+ }
package/package.json CHANGED
@@ -1,18 +1,28 @@
1
1
  {
2
2
  "name": "lambda-reactor",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "scripts": {
5
5
  "prepare": "lefthook install",
6
6
  "check": "tsgo",
7
7
  "lint": "eslint --fix",
8
8
  "format": "prettier -uwu .",
9
9
  "test": "vitest",
10
- "build": "rm -rf dist && tsgo -p tsconfig.build.json && bun build --target=node --external='*' --splitting --sourcemap=external --keep-names --root=src --outdir=dist src/*.ts"
10
+ "build": "rm -rf dist && tsgo -p tsconfig.build.json && bun build --target=node --external='*' --splitting --sourcemap=external --keep-names --root=src --outdir=dist src/*.ts",
11
+ "prepublishOnly": "bun run build"
11
12
  },
12
13
  "type": "module",
13
14
  "imports": {
14
15
  "#src/*": "./src/*.ts"
15
16
  },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "exports": {
21
+ "./*": {
22
+ "types": "./dist/*.d.ts",
23
+ "default": "./dist/*.js"
24
+ }
25
+ },
16
26
  "devDependencies": {
17
27
  "@eslint/js": "^10.0.1",
18
28
  "@types/aws-lambda": "^8.10.161",
package/.prettierrc DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "semi": false,
3
- "tabWidth": 4,
4
- "bracketSpacing": false,
5
- "experimentalTernaries": true,
6
- "printWidth": 88,
7
- "endOfLine": "lf"
8
- }
package/AGENTS.md DELETED
@@ -1,7 +0,0 @@
1
- use absolute imports like `"#src/router"`
2
- run `.git/hooks/pre-commit && bun run check` to ensure code quality
3
- whenever you add a feature or fix a bug, add tests and documentation for it (jsdoc)
4
- you may not suppress eslint rules or typescript errors
5
- functions must either be 100% pure, or be 100% side-effectful
6
- if a function returns a value other than undefined, then it must be pure
7
- if a function has side-effects, then it must only return undefined