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
package/examples/items.ts DELETED
@@ -1,25 +0,0 @@
1
- import {createHandler} from "#src/handler"
2
- import {method} from "#src/method"
3
- import {Response} from "#src/response"
4
- import {z} from "zod"
5
-
6
- const ItemSchema = z.object({id: z.string(), label: z.string(), qty: z.number().int()})
7
- const CreateItemSchema = ItemSchema.omit({id: true})
8
- const ListSchema = z.array(ItemSchema)
9
-
10
- const store: z.infer<typeof ItemSchema>[] = []
11
-
12
- export const handler = createHandler({
13
- GET: method()
14
- .output(ListSchema)
15
- .handle(() => store),
16
-
17
- POST: method()
18
- .input(CreateItemSchema)
19
- .output(ItemSchema)
20
- .handle(({body}) => {
21
- const item = {id: crypto.randomUUID(), ...body}
22
- store.push(item)
23
- return Response.json(201, item)
24
- }),
25
- })
@@ -1,20 +0,0 @@
1
- import {createHandler} from "#src/handler"
2
- import {method} from "#src/method"
3
- import {Response} from "#src/response"
4
- import {z} from "zod"
5
-
6
- const UserSchema = z.object({
7
- id: z.string(),
8
- name: z.string(),
9
- email: z.email(),
10
- })
11
-
12
- export const handler = createHandler({
13
- GET: method()
14
- .output(UserSchema)
15
- .handle(({event}) => {
16
- const id = event.pathParameters?.["id"]
17
- if (!id) return Response.text(400, "Missing id")
18
- return {id, name: "Alice", email: "alice@example.com"}
19
- }),
20
- })
@@ -1,26 +0,0 @@
1
- import {createHandler} from "#src/handler"
2
- import {method} from "#src/method"
3
- import {cors} from "#src/middleware"
4
- import {Response} from "#src/response"
5
- import {z} from "zod"
6
-
7
- const CreateUserSchema = z.object({
8
- name: z.string().min(1),
9
- email: z.email(),
10
- })
11
-
12
- const CreatedUserSchema = z.object({
13
- id: z.string(),
14
- name: z.string(),
15
- email: z.email(),
16
- })
17
-
18
- export const handler = createHandler({
19
- POST: method()
20
- .use(cors())
21
- .input(CreateUserSchema)
22
- .output(CreatedUserSchema)
23
- .handle(({body}) => {
24
- return Response.json(201, {id: crypto.randomUUID(), ...body})
25
- }),
26
- })
package/lefthook.yml DELETED
@@ -1,16 +0,0 @@
1
- pre-commit:
2
- parallel: true
3
- jobs:
4
- - name: format
5
- glob: "*.{ts,tsx,js,json,yml,yaml,md}"
6
- run: bunx prettier -w {staged_files} && git add {staged_files}
7
- - name: lint
8
- glob: "*.{ts,tsx}"
9
- run: bunx eslint --fix {staged_files} && git add {staged_files}
10
-
11
- pre-push:
12
- jobs:
13
- - name: check
14
- run: bun run check
15
- - name: test
16
- run: bun test
package/src/dispatch.ts DELETED
@@ -1,91 +0,0 @@
1
- import {logError} from "#src/logger"
2
- import {RouteHandler} from "#src/method"
3
- import {Response} from "#src/response"
4
- import type {APIGatewayProxyEvent, Context} from "aws-lambda"
5
- import {z} from "zod"
6
-
7
- import {applyMiddlewares} from "./middleware"
8
-
9
- /**
10
- * Executes a single {@link RouteHandler} for an incoming Lambda proxy event.
11
- *
12
- * Processing order:
13
- * 1. Parse `event.body` as JSON when possible.
14
- * 2. Validate the body against `route.bodySchema` (Zod); return `400` on
15
- * failure.
16
- * 3. Invoke `route.callback` with the validated body, event, and context.
17
- * 4. If the callback returns a {@link Response}, optionally validate its body
18
- * against `route.outputSchema`; return `500` on failure.
19
- * 5. If the callback returns a plain value, wrap it in a `200 JSON` response,
20
- * optionally validating against `route.outputSchema`.
21
- * 6. Apply all middlewares left-to-right before returning.
22
- *
23
- * @param route - The route handler to execute.
24
- * @param event - Raw API Gateway proxy event.
25
- * @param context - Lambda execution context.
26
- */
27
- export async function dispatch(
28
- route: RouteHandler,
29
- event: APIGatewayProxyEvent,
30
- context: Context,
31
- ) {
32
- let body
33
- try {
34
- body = event.body ? JSON.parse(event.body) : undefined
35
- } catch (error) {
36
- if (error instanceof Error) {
37
- return Response.text(400, error.message).toAPIGatewayProxyResult()
38
- }
39
- throw error
40
- }
41
- if (route.bodySchema) {
42
- const parsed = route.bodySchema.safeParse(body)
43
- if (!parsed.success) {
44
- return Response.text(
45
- 400,
46
- z.treeifyError(parsed.error).errors.join("\n"),
47
- ).toAPIGatewayProxyResult()
48
- }
49
- body = parsed.data
50
- }
51
- if (!route.callback) {
52
- const err = new Error("Route has no handler defined")
53
- logError(err)
54
- return Response.text(500, err.message).toAPIGatewayProxyResult()
55
- }
56
- const cb = route.callback as (props: {
57
- body: unknown
58
- event: APIGatewayProxyEvent
59
- context: Context
60
- }) => Promise<unknown>
61
- const result = await cb({body, event, context})
62
- if (result instanceof Response) {
63
- if (route.outputSchema) {
64
- const parsed = route.outputSchema.safeParse(result.body)
65
- if (!parsed.success) {
66
- logError(parsed.error)
67
- return Response.text(
68
- 500,
69
- z.treeifyError(parsed.error).errors.join("\n"),
70
- ).toAPIGatewayProxyResult()
71
- }
72
- result.body = parsed.data
73
- }
74
- return applyMiddlewares(result.toAPIGatewayProxyResult(), route)
75
- }
76
- if (route.outputSchema) {
77
- const parsed = route.outputSchema.safeParse(result)
78
- if (!parsed.success) {
79
- logError(parsed.error)
80
- return Response.text(
81
- 500,
82
- z.treeifyError(parsed.error).errors.join("\n"),
83
- ).toAPIGatewayProxyResult()
84
- }
85
- return applyMiddlewares(
86
- Response.json(200, parsed.data).toAPIGatewayProxyResult(),
87
- route,
88
- )
89
- }
90
- return applyMiddlewares(Response.json(200, result).toAPIGatewayProxyResult(), route)
91
- }
package/src/env.ts DELETED
@@ -1,23 +0,0 @@
1
- const PROD_VALUES = new Set(["production", "prod"])
2
-
3
- /**
4
- * Returns `true` if the given environment variable value represents a
5
- * production environment (`"production"` or `"prod"`, case-insensitive).
6
- */
7
- function isProductionValue(value: string | undefined): boolean {
8
- return value !== undefined && PROD_VALUES.has(value.toLowerCase())
9
- }
10
-
11
- /**
12
- * Returns `true` when any of the conventional environment variables
13
- * (`NODE_ENV`, `STAGE`, `ENV`, `ENVIRONMENT`) indicate a production
14
- * deployment.
15
- */
16
- export function isProduction(): boolean {
17
- return (
18
- isProductionValue(process.env["NODE_ENV"]) ||
19
- isProductionValue(process.env["STAGE"]) ||
20
- isProductionValue(process.env["ENV"]) ||
21
- isProductionValue(process.env["ENVIRONMENT"])
22
- )
23
- }
package/src/handler.ts DELETED
@@ -1,58 +0,0 @@
1
- import {dispatch} from "#src/dispatch"
2
- import {isProduction} from "#src/env"
3
- import {formatError, logError} from "#src/logger"
4
- import {RouteHandler} from "#src/method"
5
- import {Response} from "#src/response"
6
- import type {APIGatewayProxyEvent, APIGatewayProxyResult, Context} from "aws-lambda"
7
- import {install} from "source-map-support"
8
-
9
- install()
10
-
11
- /**
12
- * Creates an AWS Lambda handler function from a map of HTTP-method names to
13
- * {@link RouteHandler} instances.
14
- *
15
- * The returned handler:
16
- * - Returns `405 Method Not Allowed` (with an `Allow` header) for any HTTP
17
- * method not present in `routes`.
18
- * - Delegates matching requests to {@link dispatch}.
19
- * - Catches any `Error` thrown by `dispatch`, logs it, and returns
20
- * `500 Internal Server Error`. In non-production environments the error
21
- * message and stack trace are included in the response body.
22
- * - Re-throws non-`Error` throwables so they propagate to the Lambda runtime.
23
- *
24
- * @param routes - Map of upper-case HTTP method names (e.g. `"GET"`, `"POST"`)
25
- * to their corresponding {@link RouteHandler}.
26
- *
27
- * @example
28
- * ```ts
29
- * export const handler = createHandler({
30
- * GET: method().handle(async () => Response.json(200, {ok: true})),
31
- * })
32
- * ```
33
- */
34
- export function createHandler<T extends Record<string, RouteHandler>>(routes: T) {
35
- return async (
36
- event: APIGatewayProxyEvent,
37
- context: Context,
38
- ): Promise<APIGatewayProxyResult> => {
39
- const route = routes[event.httpMethod as keyof T]
40
- if (!route) {
41
- return Response.text(405, "Method Not Allowed")
42
- .header("Allow", Object.keys(routes).join(", "))
43
- .toAPIGatewayProxyResult()
44
- }
45
- try {
46
- return await dispatch(route, event, context)
47
- } catch (error) {
48
- if (error instanceof Error) {
49
- logError(error)
50
- return Response.text(
51
- 500,
52
- isProduction() ? "Internal Server Error" : formatError(error),
53
- ).toAPIGatewayProxyResult()
54
- }
55
- throw error
56
- }
57
- }
58
- }
package/src/logger.ts DELETED
@@ -1,19 +0,0 @@
1
- import * as z from "zod"
2
-
3
- /**
4
- * Formats an `Error` into a human-readable string that includes the error
5
- * name, message, an optional Zod validation detail block, and the stack
6
- * trace when available.
7
- */
8
- export function formatError(error: Error): string {
9
- let detail
10
- if (error instanceof z.ZodError) {
11
- detail = z.treeifyError(error).errors.join("\n")
12
- }
13
- return `${error.name}: ${error.message}${detail ? `\n${detail}` : ""}${error.stack ? `\n${error.stack}` : ""}`
14
- }
15
-
16
- /** Writes a formatted error to `stderr` via `console.error`. */
17
- export function logError(error: Error): void {
18
- console.error(formatError(error))
19
- }
package/src/method.ts DELETED
@@ -1,92 +0,0 @@
1
- import {type Middleware} from "#src/middleware"
2
- import {EndpointCallback, RouteHandler} from "#src/route-handler"
3
- import type {ZodType} from "zod"
4
-
5
- export type {EndpointCallback, RouteHandler}
6
-
7
- /**
8
- * Fluent, immutable builder for a single HTTP-method handler.
9
- *
10
- * Every method returns a new `Method` instance, leaving the original
11
- * unchanged. Build a method with the following chain:
12
- *
13
- * ```ts
14
- * method().use(cors()).input(schema).output(schema).handle(async ({body}) => …)
15
- * ```
16
- *
17
- * @typeParam TInput - Shape of the validated request body.
18
- * @typeParam TOutput - Return type of the handler callback.
19
- */
20
- export class Method<TInput = unknown, TOutput = unknown> {
21
- middlewares: Middleware[] = []
22
- bodySchema?: ZodType<TInput>
23
- outputSchema?: ZodType<TOutput>
24
- callback?: EndpointCallback<TInput, TOutput>
25
-
26
- /**
27
- * Appends a middleware to the chain. Middlewares are applied
28
- * left-to-right after the callback resolves.
29
- *
30
- * @param middleware - The {@link Middleware} to append.
31
- */
32
- use(middleware: Middleware): Method<TInput, TOutput> {
33
- const r = new Method<TInput, TOutput>()
34
- r.middlewares = [...this.middlewares, middleware]
35
- if (this.bodySchema) r.bodySchema = this.bodySchema
36
- if (this.outputSchema) r.outputSchema = this.outputSchema
37
- if (this.callback) r.callback = this.callback
38
- return r
39
- }
40
-
41
- /**
42
- * Attaches a Zod schema used to validate (and narrow) the request body.
43
- * When validation fails, `dispatch` returns a `400` response automatically.
44
- *
45
- * @param bodySchema - Zod schema for the request body.
46
- */
47
- input<U>(bodySchema: ZodType<U>): Method<U, TOutput> {
48
- const r = new Method<U, TOutput>()
49
- r.middlewares = this.middlewares
50
- r.bodySchema = bodySchema
51
- if (this.outputSchema) r.outputSchema = this.outputSchema as ZodType<TOutput>
52
- if (this.callback)
53
- r.callback = this.callback as unknown as EndpointCallback<U, TOutput>
54
- return r
55
- }
56
-
57
- /**
58
- * Attaches a Zod schema that describes the expected response shape.
59
- * Currently stored for documentation / code-generation purposes.
60
- *
61
- * @param schema - Zod schema for the response body.
62
- */
63
- output<U>(schema: ZodType<U>): Method<TInput, U> {
64
- const r = new Method<TInput, U>()
65
- r.middlewares = this.middlewares
66
- if (this.bodySchema) r.bodySchema = this.bodySchema
67
- r.outputSchema = schema
68
- if (this.callback)
69
- r.callback = this.callback as unknown as EndpointCallback<TInput, U>
70
- return r
71
- }
72
-
73
- /**
74
- * Registers the async handler callback for this method.
75
- *
76
- * @param callback - Function that receives the validated body, the raw
77
- * Lambda event, and the Lambda context, and returns the response.
78
- */
79
- handle(callback: EndpointCallback<TInput, TOutput>): Method<TInput, TOutput> {
80
- const r = new Method<TInput, TOutput>()
81
- r.middlewares = this.middlewares
82
- if (this.bodySchema) r.bodySchema = this.bodySchema
83
- if (this.outputSchema) r.outputSchema = this.outputSchema
84
- r.callback = callback
85
- return r
86
- }
87
- }
88
-
89
- /** Creates a new, empty {@link Method} builder. */
90
- export function method(): Method {
91
- return new Method()
92
- }
package/src/response.ts DELETED
@@ -1,86 +0,0 @@
1
- import type {APIGatewayProxyResult} from "aws-lambda"
2
-
3
- /**
4
- * Immutable builder for AWS Lambda proxy responses.
5
- *
6
- * All mutation methods return a new `Response` instance so that existing
7
- * instances are never modified. Construct instances through the static
8
- * factory methods {@link Response.text} and {@link Response.json}.
9
- */
10
- export class Response {
11
- status?: number
12
- body?: unknown
13
- text?: string
14
- headers?: Record<string, string>
15
-
16
- private constructor() {}
17
-
18
- /**
19
- * Creates a plain-text response.
20
- *
21
- * @param status - HTTP status code.
22
- * @param text - Response body as a plain string.
23
- */
24
- static text(status: number, text: string) {
25
- const r = new Response()
26
- r.status = status
27
- r.text = text
28
- return r
29
- }
30
-
31
- /**
32
- * Creates a JSON response.
33
- *
34
- * @param status - HTTP status code.
35
- * @param body - Value to be serialised with `JSON.stringify`.
36
- */
37
- static json(status: number, body: unknown) {
38
- const r = new Response()
39
- r.status = status
40
- r.body = body
41
- return r
42
- }
43
-
44
- /**
45
- * Returns a new `Response` with an additional HTTP response header.
46
- *
47
- * @param name - Header name (e.g. `"Allow"`).
48
- * @param value - Header value.
49
- */
50
- header(name: string, value: string): Response {
51
- const r = new Response()
52
- if (this.status !== undefined) r.status = this.status
53
- if (this.body !== undefined) r.body = this.body
54
- if (this.text !== undefined) r.text = this.text
55
- r.headers = {...this.headers, [name]: value}
56
- return r
57
- }
58
-
59
- /**
60
- * Serialises this `Response` into the shape expected by the AWS Lambda
61
- * proxy integration.
62
- *
63
- * - When constructed via {@link Response.json} the body is JSON-encoded
64
- * and `Content-Type` is set to `application/json; charset=utf-8`.
65
- * - When constructed via {@link Response.text} the body is returned as-is
66
- * and `Content-Type` is set to `text/plain; charset=utf-8`.
67
- * - Additional headers set via {@link Response.header} are merged last and
68
- * therefore take precedence over the defaults above.
69
- */
70
- toAPIGatewayProxyResult(): APIGatewayProxyResult {
71
- return {
72
- statusCode: this.status ?? 200,
73
- body:
74
- this.text === undefined ?
75
- (JSON.stringify(this.body) ?? "null")
76
- : this.text,
77
- headers: {
78
- "Content-Type":
79
- this.text === undefined ?
80
- "application/json; charset=utf-8"
81
- : "text/plain; charset=utf-8",
82
- ...this.headers,
83
- },
84
- }
85
- }
86
- }
@@ -1,98 +0,0 @@
1
- import {readFileSync} from "fs"
2
- import {join} from "path"
3
-
4
- import {buildHandlers, type FunctionFactory} from "#src/build-handlers"
5
- import {getMethods} from "#src/router"
6
- import {type CorsOptions, IRestApi, LambdaIntegration} from "aws-cdk-lib/aws-apigateway"
7
- import {IFunction} from "aws-cdk-lib/aws-lambda"
8
-
9
- /**
10
- * Immutable CDK router that maps route paths to Lambda-backed API Gateway
11
- * resources.
12
- *
13
- * ```ts
14
- * const api = router()
15
- * .cors({allowOrigins: ["*"]})
16
- * .route("/users")
17
- * .route("/items")
18
- * .defineRestApi(restApi, factory)
19
- * ```
20
- *
21
- * @typeParam TPaths - Union of all route path strings registered so far.
22
- */
23
- export class Router<TPaths extends string = never> {
24
- srcDir: string
25
- corsOptions?: CorsOptions
26
- paths: TPaths[] = []
27
-
28
- constructor(srcDir: string) {
29
- this.srcDir = srcDir
30
- }
31
-
32
- /**
33
- * Returns a new `Router` with CORS preflight options applied to every
34
- * resource added via {@link Router.route}.
35
- *
36
- * @param options - CDK `CorsOptions` passed to `addCorsPreflight`.
37
- */
38
- cors(options: CorsOptions): Router<TPaths> {
39
- const r = new Router<TPaths>(this.srcDir)
40
- r.corsOptions = options
41
- r.paths = this.paths
42
- return r
43
- }
44
-
45
- /**
46
- * Registers a new route path and returns a new `Router` with the path
47
- * added to its path union.
48
- *
49
- * @param path - Route path string (e.g. `"/users"`). Must correspond to a
50
- * handler file at `<srcDir>/<path>.ts`.
51
- */
52
- route<TPath extends string>(path: TPath): Router<TPaths | TPath> {
53
- const r = new Router<TPaths | TPath>(this.srcDir)
54
- if (this.corsOptions) r.corsOptions = this.corsOptions
55
- r.paths = [...this.paths, path as unknown as TPaths | TPath]
56
- return r
57
- }
58
-
59
- /**
60
- * Wires all registered routes into the given API Gateway REST API.
61
- *
62
- * For each route path the method:
63
- * 1. Creates (or reuses) nested API Gateway resources for each path segment.
64
- * 2. Optionally calls `addCorsPreflight` when CORS options are configured.
65
- * 3. Reads the handler source file and extracts HTTP method names via
66
- * {@link getMethods}.
67
- * 4. Adds an `addMethod` entry backed by a `LambdaIntegration` for each
68
- * discovered method.
69
- *
70
- * @param api - The CDK `IRestApi` to attach resources and methods to.
71
- * @param factory - {@link FunctionFactory} used to build Lambda functions.
72
- * @returns A record mapping each route path to its {@link IFunction}.
73
- */
74
- defineRestApi<TApi extends IRestApi>(
75
- api: TApi,
76
- factory: FunctionFactory,
77
- ): Record<TPaths, IFunction> {
78
- const handlers = buildHandlers(this.paths, factory)
79
- for (const path of this.paths) {
80
- const resource = path
81
- .split("/")
82
- .filter(Boolean)
83
- .reduce(
84
- (r, part) => r.getResource(part) ?? r.addResource(part),
85
- api.root,
86
- )
87
- if (this.corsOptions) resource.addCorsPreflight(this.corsOptions)
88
- const src = readFileSync(
89
- join(process.cwd(), this.srcDir, `${path}.ts`),
90
- "utf-8",
91
- )
92
- for (const method of getMethods(src)) {
93
- resource.addMethod(method, new LambdaIntegration(handlers[path]!))
94
- }
95
- }
96
- return handlers
97
- }
98
- }
@@ -1,14 +0,0 @@
1
- import {getMethods} from "#src/router"
2
- import {describe, expect, it} from "vitest"
3
-
4
- describe("getMethods", () => {
5
- it("extracts HTTP method names from a lambda handler file", () => {
6
- expect(
7
- getMethods(`export const handler = createHandler({GET, POST, DELETE})`),
8
- ).toEqual(["GET", "POST", "DELETE"])
9
- })
10
-
11
- it("returns empty array when no createHandler call is found", () => {
12
- expect(getMethods(`export const handler = () => {}`)).toEqual([])
13
- })
14
- })
@@ -1,46 +0,0 @@
1
- import {join} from "path"
2
-
3
- import type {IRestApi} from "aws-cdk-lib/aws-apigateway"
4
- import type {IFunction} from "aws-cdk-lib/aws-lambda"
5
- import {beforeEach, describe, expect, it, vi} from "vitest"
6
-
7
- vi.mock("fs", () => ({readFileSync: vi.fn()}))
8
- vi.mock("aws-cdk-lib/aws-apigateway", () => ({
9
- LambdaIntegration: vi.fn(),
10
- }))
11
-
12
- describe("router defineRestApi factory overload", () => {
13
- beforeEach(async () => {
14
- const {readFileSync} = await import("fs")
15
- ;(readFileSync as ReturnType<typeof vi.fn>).mockReturnValue(
16
- `export const handler = createHandler({GET})`,
17
- )
18
- })
19
-
20
- it("accepts a factory and returns a record keyed by path", async () => {
21
- const {router} = await import("#src/router")
22
- const fakeResource = {
23
- getResource: () => undefined,
24
- addResource: () => fakeResource,
25
- addMethod: vi.fn(),
26
- }
27
- const api = {root: fakeResource} as unknown as IRestApi
28
- const factory = vi.fn(
29
- (entry: string, id: string) => ({entry, id}) as unknown as IFunction,
30
- )
31
- const result = router()
32
- .route("/user/{user_id}")
33
- .route("/posts")
34
- .defineRestApi(api, factory)
35
- expect(factory).toHaveBeenCalledWith(
36
- join(process.cwd(), "src", "/user/{user_id}.ts"),
37
- "/user/{user_id}",
38
- )
39
- expect(factory).toHaveBeenCalledWith(
40
- join(process.cwd(), "src", "/posts.ts"),
41
- "/posts",
42
- )
43
- expect(Object.keys(result)).toEqual(["/user/{user_id}", "/posts"])
44
- expect(result).not.toBe(api)
45
- })
46
- })