diff --git a/packages/event-handler/package.json b/packages/event-handler/package.json index e25a9352c9..c049df3f3f 100644 --- a/packages/event-handler/package.json +++ b/packages/event-handler/package.json @@ -67,6 +67,16 @@ "types": "./lib/esm/types/index.d.ts", "default": "./lib/esm/types/index.js" } + }, + "./experimental-rest": { + "require": { + "types": "./lib/cjs/rest/index.d.ts", + "default": "./lib/cjs/rest/index.js" + }, + "import": { + "types": "./lib/esm/rest/index.d.ts", + "default": "./lib/esm/rest/index.js" + } } }, "typesVersions": { @@ -86,6 +96,10 @@ "types": [ "./lib/cjs/types/index.d.ts", "./lib/esm/types/index.d.ts" + ], + "experimental-rest": [ + "./lib/cjs/rest/index.d.ts", + "./lib/esm/rest/index.d.ts" ] } }, diff --git a/packages/event-handler/src/rest/RouteHandlerRegistry.ts b/packages/event-handler/src/rest/RouteHandlerRegistry.ts index 971fe51e33..0c338f141d 100644 --- a/packages/event-handler/src/rest/RouteHandlerRegistry.ts +++ b/packages/event-handler/src/rest/RouteHandlerRegistry.ts @@ -3,7 +3,7 @@ import type { DynamicRoute, HttpMethod, Path, - RouteHandlerOptions, + RestRouteHandlerOptions, RouteRegistryOptions, ValidationResult, } from '../types/rest.js'; @@ -147,7 +147,10 @@ class RouteHandlerRegistry { * @param path - The path to match * @returns Route handler options or null if no match found */ - public resolve(method: HttpMethod, path: Path): RouteHandlerOptions | null { + public resolve( + method: HttpMethod, + path: Path + ): RestRouteHandlerOptions | null { if (this.#shouldSort) { this.#dynamicRoutes.sort(this.#compareRouteSpecificity); this.#shouldSort = false; diff --git a/packages/event-handler/src/rest/Router.ts b/packages/event-handler/src/rest/Router.ts index 8c88dd1760..46aa21f167 100644 --- a/packages/event-handler/src/rest/Router.ts +++ b/packages/event-handler/src/rest/Router.ts @@ -13,16 +13,16 @@ import type { Middleware, Path, RequestContext, + RestRouteOptions, + RestRouterOptions, RouteHandler, - RouteOptions, - RouterOptions, } from '../types/rest.js'; import { HttpErrorCodes, HttpVerbs } from './constants.js'; import { handlerResultToProxyResult, - handlerResultToResponse, + handlerResultToWebResponse, proxyEventToWebRequest, - responseToProxyResult, + webResponseToProxyResult, } from './converters.js'; import { ErrorHandlerRegistry } from './ErrorHandlerRegistry.js'; import { @@ -57,7 +57,7 @@ class Router { */ protected readonly isDev: boolean = false; - public constructor(options?: RouterOptions) { + public constructor(options?: RestRouterOptions) { this.context = {}; const alcLogLevel = getStringFromEnv({ key: 'AWS_LAMBDA_LOG_LEVEL', @@ -236,7 +236,7 @@ class Router { const handlerMiddleware: Middleware = async (params, options, next) => { const handlerResult = await handler(params, options); - options.res = handlerResultToResponse( + options.res = handlerResultToWebResponse( handlerResult, options.res.headers ); @@ -266,11 +266,11 @@ class Router { ...handlerOptions, scope: options?.scope, }); - return await responseToProxyResult(result); + return await webResponseToProxyResult(result); } } - public route(handler: RouteHandler, options: RouteOptions): void { + public route(handler: RouteHandler, options: RestRouteOptions): void { const { method, path, middleware = [] } = options; const methods = Array.isArray(method) ? method : [method]; diff --git a/packages/event-handler/src/rest/converters.ts b/packages/event-handler/src/rest/converters.ts index 2b6ba5527f..de12ad74df 100644 --- a/packages/event-handler/src/rest/converters.ts +++ b/packages/event-handler/src/rest/converters.ts @@ -74,7 +74,7 @@ export const proxyEventToWebRequest = ( * @param response - The Web API Response object * @returns An API Gateway proxy result */ -export const responseToProxyResult = async ( +export const webResponseToProxyResult = async ( response: Response ): Promise => { const headers: Record = {}; @@ -111,7 +111,7 @@ export const responseToProxyResult = async ( * @param headers - Optional headers to be included in the response * @returns A Web API Response object */ -export const handlerResultToResponse = ( +export const handlerResultToWebResponse = ( response: HandlerResponse, resHeaders?: Headers ): Response => { @@ -159,7 +159,7 @@ export const handlerResultToProxyResult = async ( return response; } if (response instanceof Response) { - return await responseToProxyResult(response); + return await webResponseToProxyResult(response); } return { statusCode: 200, diff --git a/packages/event-handler/src/rest/index.ts b/packages/event-handler/src/rest/index.ts new file mode 100644 index 0000000000..06efe93224 --- /dev/null +++ b/packages/event-handler/src/rest/index.ts @@ -0,0 +1,28 @@ +export { HttpErrorCodes, HttpVerbs } from './constants.js'; +export { + handlerResultToProxyResult, + handlerResultToWebResponse, + proxyEventToWebRequest, + webResponseToProxyResult, +} from './converters.js'; +export { + BadRequestError, + ForbiddenError, + InternalServerError, + MethodNotAllowedError, + NotFoundError, + ParameterValidationError, + RequestEntityTooLargeError, + RequestTimeoutError, + RouteMatchingError, + ServiceError, + ServiceUnavailableError, + UnauthorizedError, +} from './errors.js'; +export { Router } from './Router.js'; +export { + composeMiddleware, + isAPIGatewayProxyEvent, + isAPIGatewayProxyResult, + isHttpMethod, +} from './utils.js'; diff --git a/packages/event-handler/src/types/index.ts b/packages/event-handler/src/types/index.ts index 8a6189533e..4625fcf1fe 100644 --- a/packages/event-handler/src/types/index.ts +++ b/packages/event-handler/src/types/index.ts @@ -30,3 +30,19 @@ export type { Anything, ResolveOptions, } from './common.js'; + +export type { + ErrorHandler, + ErrorResolveOptions, + ErrorResponse, + HandlerResponse, + HttpMethod, + HttpStatusCode, + Middleware, + Path, + RequestContext, + RestRouteHandlerOptions, + RestRouteOptions, + RestRouterOptions, + RouteHandler, +} from './rest.js'; diff --git a/packages/event-handler/src/types/rest.ts b/packages/event-handler/src/types/rest.ts index 8ed80437bd..7586c2859f 100644 --- a/packages/event-handler/src/types/rest.ts +++ b/packages/event-handler/src/types/rest.ts @@ -36,7 +36,7 @@ interface ErrorConstructor { /** * Options for the {@link Router} class */ -type RouterOptions = { +type RestRouterOptions = { /** * A logger instance to be used for logging debug, warning, and error messages. * @@ -67,14 +67,14 @@ type HttpStatusCode = (typeof HttpErrorCodes)[keyof typeof HttpErrorCodes]; type Path = `/${string}`; -type RouteHandlerOptions = { +type RestRouteHandlerOptions = { handler: RouteHandler; params: Record; rawParams: Record; middleware: Middleware[]; }; -type RouteOptions = { +type RestRouteOptions = { method: HttpMethod | HttpMethod[]; path: Path; middleware?: Middleware[]; @@ -125,10 +125,10 @@ export type { Middleware, Path, RequestContext, - RouterOptions, + RestRouterOptions, RouteHandler, - RouteOptions, - RouteHandlerOptions, + RestRouteOptions, + RestRouteHandlerOptions, RouteRegistryOptions, ValidationResult, }; diff --git a/packages/event-handler/tests/unit/rest/ErrorHandlerRegistry.test.ts b/packages/event-handler/tests/unit/rest/ErrorHandlerRegistry.test.ts index 64aea416fe..c7bd918a75 100644 --- a/packages/event-handler/tests/unit/rest/ErrorHandlerRegistry.test.ts +++ b/packages/event-handler/tests/unit/rest/ErrorHandlerRegistry.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; -import { HttpErrorCodes } from '../../../src/rest/constants.js'; import { ErrorHandlerRegistry } from '../../../src/rest/ErrorHandlerRegistry.js'; +import { HttpErrorCodes } from '../../../src/rest/index.js'; import type { HttpStatusCode, RequestContext, diff --git a/packages/event-handler/tests/unit/rest/RouteHandlerRegistry.test.ts b/packages/event-handler/tests/unit/rest/RouteHandlerRegistry.test.ts index 2ef46f2a03..24dac6f6f9 100644 --- a/packages/event-handler/tests/unit/rest/RouteHandlerRegistry.test.ts +++ b/packages/event-handler/tests/unit/rest/RouteHandlerRegistry.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { HttpVerbs } from '../../../src/rest/constants.js'; +import { HttpVerbs } from '../../../src/rest/index.js'; import { Route } from '../../../src/rest/Route.js'; import { RouteHandlerRegistry } from '../../../src/rest/RouteHandlerRegistry.js'; import type { Path } from '../../../src/types/rest.js'; diff --git a/packages/event-handler/tests/unit/rest/Router/basic-routing.test.ts b/packages/event-handler/tests/unit/rest/Router/basic-routing.test.ts index 3f892e0180..b86a560492 100644 --- a/packages/event-handler/tests/unit/rest/Router/basic-routing.test.ts +++ b/packages/event-handler/tests/unit/rest/Router/basic-routing.test.ts @@ -1,8 +1,11 @@ import context from '@aws-lambda-powertools/testing-utils/context'; import { describe, expect, it } from 'vitest'; -import { HttpErrorCodes, HttpVerbs } from '../../../../src/rest/constants.js'; -import { InternalServerError } from '../../../../src/rest/errors.js'; -import { Router } from '../../../../src/rest/Router.js'; +import { + HttpErrorCodes, + HttpVerbs, + InternalServerError, + Router, +} from '../../../../src/rest/index.js'; import type { HttpMethod, RouteHandler } from '../../../../src/types/rest.js'; import { createTestEvent } from '../helpers.js'; diff --git a/packages/event-handler/tests/unit/rest/Router/decorators.test.ts b/packages/event-handler/tests/unit/rest/Router/decorators.test.ts index 8f3e9e965c..79dd448bed 100644 --- a/packages/event-handler/tests/unit/rest/Router/decorators.test.ts +++ b/packages/event-handler/tests/unit/rest/Router/decorators.test.ts @@ -1,13 +1,13 @@ import context from '@aws-lambda-powertools/testing-utils/context'; import type { Context } from 'aws-lambda'; import { describe, expect, it } from 'vitest'; -import { HttpErrorCodes } from '../../../../src/rest/constants.js'; import { BadRequestError, + HttpErrorCodes, MethodNotAllowedError, type NotFoundError, -} from '../../../../src/rest/errors.js'; -import { Router } from '../../../../src/rest/Router.js'; + Router, +} from '../../../../src/rest/index.js'; import { createTestEvent, createTrackingMiddleware } from '../helpers.js'; describe('Class: Router - Decorators', () => { diff --git a/packages/event-handler/tests/unit/rest/Router/error-handling.test.ts b/packages/event-handler/tests/unit/rest/Router/error-handling.test.ts index 522a975e54..ab1f9b4f4c 100644 --- a/packages/event-handler/tests/unit/rest/Router/error-handling.test.ts +++ b/packages/event-handler/tests/unit/rest/Router/error-handling.test.ts @@ -1,12 +1,12 @@ import context from '@aws-lambda-powertools/testing-utils/context'; import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { HttpErrorCodes } from '../../../../src/rest/constants.js'; import { BadRequestError, + HttpErrorCodes, InternalServerError, MethodNotAllowedError, -} from '../../../../src/rest/errors.js'; -import { Router } from '../../../../src/rest/Router.js'; + Router, +} from '../../../../src/rest/index.js'; import { createTestEvent } from '../helpers.js'; describe('Class: Router - Error Handling', () => { diff --git a/packages/event-handler/tests/unit/rest/Router/logging.test.ts b/packages/event-handler/tests/unit/rest/Router/logging.test.ts index 66966f576f..d964352c2b 100644 --- a/packages/event-handler/tests/unit/rest/Router/logging.test.ts +++ b/packages/event-handler/tests/unit/rest/Router/logging.test.ts @@ -1,11 +1,10 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { HttpVerbs } from '../../../../src/rest/constants.js'; -import { Router } from '../../../../src/rest/Router.js'; -import type { RouterOptions } from '../../../../src/types/rest.js'; +import { HttpVerbs, Router } from '../../../../src/rest/index.js'; +import type { RestRouterOptions } from '../../../../src/types/rest.js'; describe('Class: Router - Logging', () => { class TestResolver extends Router { - constructor(options?: RouterOptions) { + constructor(options?: RestRouterOptions) { super(options); this.logger.debug('test debug'); this.logger.warn('test warn'); diff --git a/packages/event-handler/tests/unit/rest/Router/middleware.test.ts b/packages/event-handler/tests/unit/rest/Router/middleware.test.ts index e90a1ce68a..ee5c192afb 100644 --- a/packages/event-handler/tests/unit/rest/Router/middleware.test.ts +++ b/packages/event-handler/tests/unit/rest/Router/middleware.test.ts @@ -1,8 +1,7 @@ import context from '@aws-lambda-powertools/testing-utils/context'; import type { Context } from 'aws-lambda'; import { describe, expect, it, vi } from 'vitest'; -import { HttpErrorCodes } from '../../../../src/rest/constants.js'; -import { Router } from '../../../../src/rest/Router.js'; +import { HttpErrorCodes, Router } from '../../../../src/rest/index.js'; import type { Middleware, Path, diff --git a/packages/event-handler/tests/unit/rest/converters.test.ts b/packages/event-handler/tests/unit/rest/converters.test.ts index e864a9d9e2..435ac6d167 100644 --- a/packages/event-handler/tests/unit/rest/converters.test.ts +++ b/packages/event-handler/tests/unit/rest/converters.test.ts @@ -2,10 +2,10 @@ import type { APIGatewayProxyEvent } from 'aws-lambda'; import { describe, expect, it } from 'vitest'; import { handlerResultToProxyResult, - handlerResultToResponse, + handlerResultToWebResponse, proxyEventToWebRequest, - responseToProxyResult, -} from '../../../src/rest/converters.js'; + webResponseToProxyResult, +} from '../../../src/rest/index.js'; describe('Converters', () => { describe('proxyEventToWebRequest', () => { @@ -320,7 +320,7 @@ describe('Converters', () => { }, }); - const result = await responseToProxyResult(response); + const result = await webResponseToProxyResult(response); expect(result.statusCode).toBe(200); expect(result.body).toBe('Hello World'); @@ -334,7 +334,7 @@ describe('Converters', () => { headers: { 'content-type': 'text/plain', 'x-custom': 'value' }, }); - const result = await responseToProxyResult(response); + const result = await webResponseToProxyResult(response); expect(result.statusCode).toBe(201); expect(result.headers).toEqual({ @@ -352,7 +352,7 @@ describe('Converters', () => { }, }); - const result = await responseToProxyResult(response); + const result = await webResponseToProxyResult(response); expect(result.headers).toEqual({ 'content-type': 'application/json' }); expect(result.multiValueHeaders).toEqual({ @@ -369,7 +369,7 @@ describe('Converters', () => { }, }); - const result = await responseToProxyResult(response); + const result = await webResponseToProxyResult(response); expect(result.headers).toEqual({ 'content-type': 'application/json', @@ -382,7 +382,7 @@ describe('Converters', () => { it('handles different status codes', async () => { const response = new Response('Not Found', { status: 404 }); - const result = await responseToProxyResult(response); + const result = await webResponseToProxyResult(response); expect(result.statusCode).toBe(404); expect(result.body).toBe('Not Found'); @@ -391,7 +391,7 @@ describe('Converters', () => { it('handles empty response body', async () => { const response = new Response(null, { status: 204 }); - const result = await responseToProxyResult(response); + const result = await webResponseToProxyResult(response); expect(result.statusCode).toBe(204); expect(result.body).toBe(''); @@ -438,7 +438,7 @@ describe('Converters', () => { it('returns Response object as-is', () => { const response = new Response('Hello', { status: 201 }); - const result = handlerResultToResponse(response); + const result = handlerResultToWebResponse(response); expect(result).toBe(response); }); @@ -451,7 +451,7 @@ describe('Converters', () => { isBase64Encoded: false, }; - const result = handlerResultToResponse(proxyResult); + const result = handlerResultToWebResponse(proxyResult); expect(result).toBeInstanceOf(Response); expect(result.status).toBe(201); @@ -470,7 +470,7 @@ describe('Converters', () => { isBase64Encoded: false, }; - const result = handlerResultToResponse(proxyResult); + const result = handlerResultToWebResponse(proxyResult); expect(result.headers.get('content-type')).toBe('application/json'); expect(result.headers.get('Set-Cookie')).toBe( @@ -481,7 +481,7 @@ describe('Converters', () => { it('converts plain object to JSON Response with default headers', async () => { const obj = { message: 'success' }; - const result = handlerResultToResponse(obj); + const result = handlerResultToWebResponse(obj); expect(result).toBeInstanceOf(Response); expect(result.status).toBe(200); @@ -493,7 +493,7 @@ describe('Converters', () => { const obj = { message: 'success' }; const headers = new Headers({ 'x-custom': 'value' }); - const result = handlerResultToResponse(obj, headers); + const result = handlerResultToWebResponse(obj, headers); expect(result.headers.get('Content-Type')).toBe('application/json'); expect(result.headers.get('x-custom')).toBe('value'); @@ -507,7 +507,7 @@ describe('Converters', () => { isBase64Encoded: false, }; - const result = handlerResultToResponse(proxyResult); + const result = handlerResultToWebResponse(proxyResult); expect(result).toBeInstanceOf(Response); expect(result.status).toBe(200); @@ -522,7 +522,7 @@ describe('Converters', () => { isBase64Encoded: false, }; - const result = handlerResultToResponse(proxyResult); + const result = handlerResultToWebResponse(proxyResult); expect(result.headers.get('content-type')).toBe('text/plain'); }); @@ -536,7 +536,7 @@ describe('Converters', () => { isBase64Encoded: false, }; - const result = handlerResultToResponse(proxyResult); + const result = handlerResultToWebResponse(proxyResult); expect(result.headers.get('content-type')).toBe('text/plain'); }); diff --git a/packages/event-handler/tests/unit/rest/errors.test.ts b/packages/event-handler/tests/unit/rest/errors.test.ts index becacd2b80..6f26cf678c 100644 --- a/packages/event-handler/tests/unit/rest/errors.test.ts +++ b/packages/event-handler/tests/unit/rest/errors.test.ts @@ -1,8 +1,8 @@ import { describe, expect, it } from 'vitest'; -import { HttpErrorCodes } from '../../../src/rest/constants.js'; import { BadRequestError, ForbiddenError, + HttpErrorCodes, InternalServerError, MethodNotAllowedError, NotFoundError, @@ -10,7 +10,7 @@ import { RequestTimeoutError, ServiceUnavailableError, UnauthorizedError, -} from '../../../src/rest/errors.js'; +} from '../../../src/rest/index.js'; describe('HTTP Error Classes', () => { it.each([ diff --git a/packages/event-handler/tests/unit/rest/utils.test.ts b/packages/event-handler/tests/unit/rest/utils.test.ts index d8b6be7229..58463494f0 100644 --- a/packages/event-handler/tests/unit/rest/utils.test.ts +++ b/packages/event-handler/tests/unit/rest/utils.test.ts @@ -1,12 +1,11 @@ import type { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda'; import { describe, expect, it } from 'vitest'; import { - compilePath, composeMiddleware, isAPIGatewayProxyEvent, isAPIGatewayProxyResult, - validatePathPattern, -} from '../../../src/rest/utils.js'; +} from '../../../src/rest/index.js'; +import { compilePath, validatePathPattern } from '../../../src/rest/utils.js'; import type { Middleware, Path,