@h4ad/serverless-adapter 3.1.0 → 3.2.0
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 +0 -5
- package/lib/adapters/apollo-server/apollo-server-mutation.adapter.js.map +1 -1
- package/lib/adapters/aws/alb.adapter.js.map +1 -1
- package/lib/adapters/aws/api-gateway-v1.adapter.js.map +1 -1
- package/lib/adapters/aws/api-gateway-v2.adapter.js.map +1 -1
- package/lib/adapters/aws/lambda-edge.adapter.js.map +1 -1
- package/lib/adapters/aws/request-lambda-edge.adapter.js.map +1 -1
- package/lib/adapters/azure/http-trigger-v4.adapter.d.ts +1 -1
- package/lib/adapters/azure/http-trigger-v4.adapter.d.ts.map +1 -1
- package/lib/adapters/azure/http-trigger-v4.adapter.js +1 -1
- package/lib/adapters/azure/http-trigger-v4.adapter.js.map +1 -1
- package/lib/adapters/digital-ocean/http-function.adapter.js.map +1 -1
- package/lib/adapters/huawei/huawei-api-gateway.adapter.js.map +1 -1
- package/lib/core/base-handler.js.map +1 -1
- package/lib/core/headers.js.map +1 -1
- package/lib/core/path.js.map +1 -1
- package/lib/frameworks/apollo-server/apollo-server.framework.js.map +1 -1
- package/lib/frameworks/cors/cors.framework.js.map +1 -1
- package/lib/handlers/base/raw-request.js.map +1 -1
- package/lib/handlers/firebase/http-firebase.handler.d.ts.map +1 -1
- package/lib/handlers/firebase/http-firebase.handler.js +1 -1
- package/lib/handlers/firebase/http-firebase.handler.js.map +1 -1
- package/lib/handlers/huawei/http-huawei.handler.js.map +1 -1
- package/lib/network/request.js.map +1 -1
- package/lib/network/response-stream.js.map +1 -1
- package/lib/network/response.js +1 -1
- package/lib/network/response.js.map +1 -1
- package/lib/network/utils.js.map +1 -1
- package/package.json +46 -52
package/README.md
CHANGED
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
[![Issues][issues-img]][issues-url]
|
|
19
19
|
[![Code Coverage][codecov-img]][codecov-url]
|
|
20
20
|
[![Commitizen Friendly][commitizen-img]][commitizen-url]
|
|
21
|
-
[![Semantic Release][semantic-release-img]][semantic-release-url]
|
|
22
21
|
|
|
23
22
|
Run REST APIs and other web applications using your existing Node.js application framework (NestJS, Deepkit, Express, Koa, Hapi,
|
|
24
23
|
Fastify, tRPC and Apollo Server), on top of AWS Lambda, Azure, Digital Ocean and many other clouds.
|
|
@@ -131,10 +130,6 @@ for existing and giving us a brilliant library that is the core of my current co
|
|
|
131
130
|
|
|
132
131
|
[codecov-url]:https://codecov.io/gh/H4ad/serverless-adapter
|
|
133
132
|
|
|
134
|
-
[semantic-release-img]:https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
|
|
135
|
-
|
|
136
|
-
[semantic-release-url]:https://github.com/semantic-release/semantic-release
|
|
137
|
-
|
|
138
133
|
[commitizen-img]:https://img.shields.io/badge/commitizen-friendly-brightgreen.svg
|
|
139
134
|
|
|
140
135
|
[commitizen-url]:http://commitizen.github.io/cz-cli/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apollo-server-mutation.adapter.js","sources":["../../../src/adapters/apollo-server/apollo-server-mutation.adapter.ts"],"sourcesContent":["//#region Imports\n\nimport {\n AdapterContract,\n AdapterRequest,\n GetResponseAdapterProps,\n OnErrorProps,\n} from '../../contracts';\nimport {\n ILogger,\n getDefaultIfUndefined,\n getEventBodyAsBuffer,\n} from '../../core';\n\n//#endregion\n\n/**\n * The options for {@link ApolloServerMutationAdapter}\n *\n * @breadcrumb Adapters / Apollo Server / ApolloServerMutationAdapter\n * @public\n */\nexport type ApolloServerMutationAdapterOptions = {\n /**\n * Specify the name of mutation that will be called when an event was received\n */\n mutationName: string;\n\n /**\n * Specify the mutation result schema.\n * Use this to customize the behavior when you need to return a specific object to be handled by the Adapter, like SQS with Batch Mode.\n *\n * @defaultValue `{ __typename }`\n */\n mutationResultQuery?: string;\n};\n\n/**\n * The adapter that wraps another adapter to force a transformation of the event data as a mutation to Apollo Server be able to handle.\n *\n * @breadcrumb Adapters / Apollo Server / ApolloServerMutationAdapter\n * @public\n */\nexport class ApolloServerMutationAdapter<\n TEvent,\n TContext,\n TResponse,\n TBaseAdapter extends AdapterContract<TEvent, TContext, TResponse>,\n> implements AdapterContract<TEvent, TContext, TResponse>\n{\n //#region Constructor\n\n /**\n * The default constructor\n */\n constructor(\n protected readonly baseAdapter: TBaseAdapter,\n protected readonly options: ApolloServerMutationAdapterOptions,\n ) {}\n\n //#endregion\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public canHandle(event: unknown, context: TContext, log: ILogger): boolean {\n return this.baseAdapter.canHandle(event, context, log);\n }\n\n /**\n * {@inheritDoc}\n */\n public getAdapterName(): string {\n return this.baseAdapter.getAdapterName() + 'Mutation';\n }\n\n /**\n * {@inheritDoc}\n */\n public getRequest(\n event: TEvent,\n context: TContext,\n log: ILogger,\n ): AdapterRequest {\n const request = this.baseAdapter.getRequest(event, context, log);\n\n request.method = 'POST';\n\n const operationName = this.options.mutationName;\n const mutationResultQuery = getDefaultIfUndefined(\n this.options.mutationResultQuery,\n '{ __typename }',\n );\n\n const mutationBody = JSON.stringify({\n operationName,\n query: `mutation ${operationName} ($event: String) { ${operationName} (event: $event) ${mutationResultQuery} }`,\n variables: {\n event: request.body?.toString() || '',\n },\n });\n\n const [buffer, contentLength] = getEventBodyAsBuffer(mutationBody, false);\n\n request.body = buffer;\n request.headers['content-type'] = 'application/json';\n request.headers['content-length'] = String(contentLength);\n\n return request;\n }\n\n /**\n * {@inheritDoc}\n */\n public getResponse(props: GetResponseAdapterProps<TEvent>): TResponse {\n const { data, errors } = JSON.parse(props.body);\n\n if (!errors) {\n return this.baseAdapter.getResponse({\n ...props,\n body: JSON.stringify(data[this.options.mutationName]),\n });\n }\n\n // when error happens, is the responsability of base adapter\n // to deal with error status code.\n return this.baseAdapter.getResponse(props);\n }\n\n /**\n * {@inheritDoc}\n */\n public onErrorWhileForwarding(props: OnErrorProps<TEvent, TResponse>): void {\n return this.baseAdapter.onErrorWhileForwarding(props);\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"ooBAAA,uCAAA,CAAA,EAAA,SAAA,uCAAA,CAAA,4BAAA,IAAA,2BAAA,CAAA,EAAA,OAAA,QAAA,aAAA,sCAAA,EAQA,IAIO,YAAA,QAAA,YAAA,EA+BA,MAAM,2BAMb,CAME,YACqB,YACA,QACnB,CAFmB,KAAA,YAAA,YACA,KAAA,QAAA,OAClB,CASI,UAAU,MAAgB,QAAmB,IAAuB,CACzE,OAAO,KAAK,YAAY,UAAU,MAAO,QAAS,GAAG,CACvD,CAKO,gBAAyB,CACvB,OAAA,KAAK,YAAY,eAAA,EAAmB,UAC7C,CAKO,WACL,MACA,QACA,IACgB,QAChB,MAAM,QAAU,KAAK,YAAY,WAAW,MAAO,QAAS,GAAG,EAE/D,QAAQ,OAAS,OAEX,MAAA,cAAgB,KAAK,QAAQ,aAC7B,uBAAsB,YAAA,uBAC1B,KAAK,QAAQ,oBACb,gBAAA,EAGI,aAAe,KAAK,UAAU,CAClC,cACA,MAAO,YAAY,
|
|
1
|
+
{"version":3,"file":"apollo-server-mutation.adapter.js","sources":["../../../src/adapters/apollo-server/apollo-server-mutation.adapter.ts"],"sourcesContent":["//#region Imports\n\nimport {\n AdapterContract,\n AdapterRequest,\n GetResponseAdapterProps,\n OnErrorProps,\n} from '../../contracts';\nimport {\n ILogger,\n getDefaultIfUndefined,\n getEventBodyAsBuffer,\n} from '../../core';\n\n//#endregion\n\n/**\n * The options for {@link ApolloServerMutationAdapter}\n *\n * @breadcrumb Adapters / Apollo Server / ApolloServerMutationAdapter\n * @public\n */\nexport type ApolloServerMutationAdapterOptions = {\n /**\n * Specify the name of mutation that will be called when an event was received\n */\n mutationName: string;\n\n /**\n * Specify the mutation result schema.\n * Use this to customize the behavior when you need to return a specific object to be handled by the Adapter, like SQS with Batch Mode.\n *\n * @defaultValue `{ __typename }`\n */\n mutationResultQuery?: string;\n};\n\n/**\n * The adapter that wraps another adapter to force a transformation of the event data as a mutation to Apollo Server be able to handle.\n *\n * @breadcrumb Adapters / Apollo Server / ApolloServerMutationAdapter\n * @public\n */\nexport class ApolloServerMutationAdapter<\n TEvent,\n TContext,\n TResponse,\n TBaseAdapter extends AdapterContract<TEvent, TContext, TResponse>,\n> implements AdapterContract<TEvent, TContext, TResponse>\n{\n //#region Constructor\n\n /**\n * The default constructor\n */\n constructor(\n protected readonly baseAdapter: TBaseAdapter,\n protected readonly options: ApolloServerMutationAdapterOptions,\n ) {}\n\n //#endregion\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public canHandle(event: unknown, context: TContext, log: ILogger): boolean {\n return this.baseAdapter.canHandle(event, context, log);\n }\n\n /**\n * {@inheritDoc}\n */\n public getAdapterName(): string {\n return this.baseAdapter.getAdapterName() + 'Mutation';\n }\n\n /**\n * {@inheritDoc}\n */\n public getRequest(\n event: TEvent,\n context: TContext,\n log: ILogger,\n ): AdapterRequest {\n const request = this.baseAdapter.getRequest(event, context, log);\n\n request.method = 'POST';\n\n const operationName = this.options.mutationName;\n const mutationResultQuery = getDefaultIfUndefined(\n this.options.mutationResultQuery,\n '{ __typename }',\n );\n\n const mutationBody = JSON.stringify({\n operationName,\n query: `mutation ${operationName} ($event: String) { ${operationName} (event: $event) ${mutationResultQuery} }`,\n variables: {\n event: request.body?.toString() || '',\n },\n });\n\n const [buffer, contentLength] = getEventBodyAsBuffer(mutationBody, false);\n\n request.body = buffer;\n request.headers['content-type'] = 'application/json';\n request.headers['content-length'] = String(contentLength);\n\n return request;\n }\n\n /**\n * {@inheritDoc}\n */\n public getResponse(props: GetResponseAdapterProps<TEvent>): TResponse {\n const { data, errors } = JSON.parse(props.body);\n\n if (!errors) {\n return this.baseAdapter.getResponse({\n ...props,\n body: JSON.stringify(data[this.options.mutationName]),\n });\n }\n\n // when error happens, is the responsability of base adapter\n // to deal with error status code.\n return this.baseAdapter.getResponse(props);\n }\n\n /**\n * {@inheritDoc}\n */\n public onErrorWhileForwarding(props: OnErrorProps<TEvent, TResponse>): void {\n return this.baseAdapter.onErrorWhileForwarding(props);\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"ooBAAA,uCAAA,CAAA,EAAA,SAAA,uCAAA,CAAA,4BAAA,IAAA,2BAAA,CAAA,EAAA,OAAA,QAAA,aAAA,sCAAA,EAQA,IAIO,YAAA,QAAA,YAAA,EA+BA,MAAM,2BAMb,CAME,YACqB,YACA,QACnB,CAFmB,KAAA,YAAA,YACA,KAAA,QAAA,OAClB,CASI,UAAU,MAAgB,QAAmB,IAAuB,CACzE,OAAO,KAAK,YAAY,UAAU,MAAO,QAAS,GAAG,CACvD,CAKO,gBAAyB,CACvB,OAAA,KAAK,YAAY,eAAA,EAAmB,UAC7C,CAKO,WACL,MACA,QACA,IACgB,QAChB,MAAM,QAAU,KAAK,YAAY,WAAW,MAAO,QAAS,GAAG,EAE/D,QAAQ,OAAS,OAEX,MAAA,cAAgB,KAAK,QAAQ,aAC7B,uBAAsB,YAAA,uBAC1B,KAAK,QAAQ,oBACb,gBAAA,EAGI,aAAe,KAAK,UAAU,CAClC,cACA,MAAO,YAAY,aAAa,uBAAuB,aAAa,oBAAoB,mBAAmB,KAC3G,UAAW,CACT,QAAO,WAAQ,OAAR,eAAc,aAAc,EACrC,CAAA,CACD,EAEK,CAAC,OAAQ,aAAa,KAAI,YAAA,sBAAqB,aAAc,EAAK,EAExE,eAAQ,KAAO,OACP,QAAA,QAAQ,cAAc,EAAI,mBAClC,QAAQ,QAAQ,gBAAgB,EAAI,OAAO,aAAa,EAEjD,OACT,CAKO,YAAY,MAAmD,CACpE,KAAM,CAAE,KAAM,QAAW,KAAK,MAAM,MAAM,IAAI,EAE9C,OAAK,OASE,KAAK,YAAY,YAAY,KAAK,EARhC,KAAK,YAAY,YAAY,CAClC,GAAG,MACH,KAAM,KAAK,UAAU,KAAK,KAAK,QAAQ,YAAY,CAAC,CAAA,CACrD,CAML,CAKO,uBAAuB,MAA8C,CACnE,OAAA,KAAK,YAAY,uBAAuB,KAAK,CACtD,CAGF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alb.adapter.js","sources":["../../../src/adapters/aws/alb.adapter.ts"],"sourcesContent":["//#region Imports\n\nimport type { ALBEvent, ALBResult, Context } from 'aws-lambda';\nimport {\n AdapterContract,\n AdapterRequest,\n GetResponseAdapterProps,\n OnErrorProps,\n} from '../../contracts';\nimport {\n StripBasePathFn,\n buildStripBasePath,\n getEventBodyAsBuffer,\n getFlattenedHeadersMap,\n getMultiValueHeadersMap,\n getPathWithQueryStringParams,\n} from '../../core';\n\n//#endregion\n\n/**\n * The options to customize the {@link AlbAdapter}\n *\n * @breadcrumb Adapters / AWS / AlbAdapter\n * @public\n */\nexport interface AlbAdapterOptions {\n /**\n * Strip base path for custom domains\n *\n * @defaultValue ''\n */\n stripBasePath?: string;\n}\n\n/**\n * The adapter to handle requests from AWS ALB\n *\n * @example\n * ```typescript\n * const stripBasePath = '/any/custom/base/path'; // default ''\n * const adapter = new AlbAdapter({ stripBasePath });\n * ```\n *\n * {@link https://docs.aws.amazon.com/lambda/latest/dg/services-alb.html | Event Reference}\n *\n * @breadcrumb Adapters / AWS / AlbAdapter\n * @public\n */\nexport class AlbAdapter\n implements AdapterContract<ALBEvent, Context, ALBResult>\n{\n //#region Constructor\n\n /**\n * Default constructor\n *\n * @param options - The options to customize the {@link AlbAdapter}\n */\n constructor(protected readonly options?: AlbAdapterOptions) {\n this.stripPathFn = buildStripBasePath(this.options?.stripBasePath);\n }\n\n //#endregion\n\n //#region Protected Properties\n\n /**\n * Strip base path function\n */\n protected stripPathFn: StripBasePathFn;\n\n //#endregion\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public getAdapterName(): string {\n return AlbAdapter.name;\n }\n\n /**\n * {@inheritDoc}\n */\n public canHandle(event: unknown): event is ALBEvent {\n const albEvent = event as Partial<ALBEvent>;\n\n return !!(albEvent?.requestContext && albEvent.requestContext.elb);\n }\n\n /**\n * {@inheritDoc}\n */\n public getRequest(event: ALBEvent): AdapterRequest {\n const method = event.httpMethod;\n const path = this.getPathFromEvent(event);\n\n const headers = event.multiValueHeaders\n ? getFlattenedHeadersMap(event.multiValueHeaders, ',', true)\n : event.headers!;\n\n let body: Buffer | undefined;\n\n if (event.body) {\n const [bufferBody, contentLength] = getEventBodyAsBuffer(\n event.body,\n event.isBase64Encoded,\n );\n\n body = bufferBody;\n headers['content-length'] = String(contentLength);\n }\n\n let remoteAddress = '';\n\n // ref: https://docs.aws.amazon.com/elasticloadbalancing/latest/application/x-forwarded-headers.html#x-forwarded-for\n if (headers['x-forwarded-for']) remoteAddress = headers['x-forwarded-for'];\n\n return {\n method,\n headers,\n body,\n remoteAddress,\n path,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public getResponse({\n event,\n headers: responseHeaders,\n body,\n isBase64Encoded,\n statusCode,\n }: GetResponseAdapterProps<ALBEvent>): ALBResult {\n const multiValueHeaders = !event.headers\n ? getMultiValueHeadersMap(responseHeaders)\n : undefined;\n\n const headers = event.headers\n ? getFlattenedHeadersMap(responseHeaders)\n : undefined;\n\n return {\n statusCode,\n body,\n headers,\n multiValueHeaders,\n isBase64Encoded,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public onErrorWhileForwarding({\n error,\n delegatedResolver,\n respondWithErrors,\n event,\n log,\n }: OnErrorProps<ALBEvent, ALBResult>): void {\n const body = respondWithErrors ? error.stack || '' : '';\n const errorResponse = this.getResponse({\n event,\n statusCode: 500,\n body,\n headers: {},\n isBase64Encoded: false,\n log,\n });\n\n delegatedResolver.succeed(errorResponse);\n }\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * Get path from event with query strings\n *\n * @param event - The event sent by serverless\n */\n protected getPathFromEvent(event: ALBEvent): string {\n const path = this.stripPathFn(event.path);\n\n const queryParams = event.headers\n ? event.queryStringParameters\n : event.multiValueQueryStringParameters;\n\n return getPathWithQueryStringParams(path, queryParams || {});\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"ooBAAA,oBAAA,CAAA,EAAA,SAAA,oBAAA,CAAA,WAAA,IAAA,UAAA,CAAA,EAAA,OAAA,QAAA,aAAA,mBAAA,EASA,IAOO,YAAA,QAAA,YAAA,EAiCA,MAAM,UAEb,CAQE,YAA+B,QAA6B,QAA7B,KAAA,QAAA,QAC7B,KAAK,eAAc,YAAA,qBAAmB,QAAK,UAAL,eAAc,aAAa,CACnE,CAkBO,gBAAyB,CAC9B,OAAO,WAAW,IACpB,CAKO,UAAU,MAAmC,CAClD,MAAM,SAAW,MAEjB,MAAO,CAAC,EAAE,yBAAU,gBAAkB,SAAS,eAAe,IAChE,CAKO,WAAW,MAAiC,CACjD,MAAM,OAAS,MAAM,WACf,KAAO,KAAK,iBAAiB,KAAK,EAElC,QAAU,MAAM,qBAClB,YAAA,wBAAuB,MAAM,kBAAmB,IAAK,EAAI,EACzD,MAAM,QAEN,IAAA,KAEJ,GAAI,MAAM,KAAM,CACR,KAAA,CAAC,WAAY,aAAa,KAAI,YAAA,sBAClC,MAAM,KACN,MAAM,eAAA,EAGD,KAAA,WACC,QAAA,gBAAgB,EAAI,OAAO,aAAa,
|
|
1
|
+
{"version":3,"file":"alb.adapter.js","sources":["../../../src/adapters/aws/alb.adapter.ts"],"sourcesContent":["//#region Imports\n\nimport type { ALBEvent, ALBResult, Context } from 'aws-lambda';\nimport {\n AdapterContract,\n AdapterRequest,\n GetResponseAdapterProps,\n OnErrorProps,\n} from '../../contracts';\nimport {\n StripBasePathFn,\n buildStripBasePath,\n getEventBodyAsBuffer,\n getFlattenedHeadersMap,\n getMultiValueHeadersMap,\n getPathWithQueryStringParams,\n} from '../../core';\n\n//#endregion\n\n/**\n * The options to customize the {@link AlbAdapter}\n *\n * @breadcrumb Adapters / AWS / AlbAdapter\n * @public\n */\nexport interface AlbAdapterOptions {\n /**\n * Strip base path for custom domains\n *\n * @defaultValue ''\n */\n stripBasePath?: string;\n}\n\n/**\n * The adapter to handle requests from AWS ALB\n *\n * @example\n * ```typescript\n * const stripBasePath = '/any/custom/base/path'; // default ''\n * const adapter = new AlbAdapter({ stripBasePath });\n * ```\n *\n * {@link https://docs.aws.amazon.com/lambda/latest/dg/services-alb.html | Event Reference}\n *\n * @breadcrumb Adapters / AWS / AlbAdapter\n * @public\n */\nexport class AlbAdapter\n implements AdapterContract<ALBEvent, Context, ALBResult>\n{\n //#region Constructor\n\n /**\n * Default constructor\n *\n * @param options - The options to customize the {@link AlbAdapter}\n */\n constructor(protected readonly options?: AlbAdapterOptions) {\n this.stripPathFn = buildStripBasePath(this.options?.stripBasePath);\n }\n\n //#endregion\n\n //#region Protected Properties\n\n /**\n * Strip base path function\n */\n protected stripPathFn: StripBasePathFn;\n\n //#endregion\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public getAdapterName(): string {\n return AlbAdapter.name;\n }\n\n /**\n * {@inheritDoc}\n */\n public canHandle(event: unknown): event is ALBEvent {\n const albEvent = event as Partial<ALBEvent>;\n\n return !!(albEvent?.requestContext && albEvent.requestContext.elb);\n }\n\n /**\n * {@inheritDoc}\n */\n public getRequest(event: ALBEvent): AdapterRequest {\n const method = event.httpMethod;\n const path = this.getPathFromEvent(event);\n\n const headers = event.multiValueHeaders\n ? getFlattenedHeadersMap(event.multiValueHeaders, ',', true)\n : event.headers!;\n\n let body: Buffer | undefined;\n\n if (event.body) {\n const [bufferBody, contentLength] = getEventBodyAsBuffer(\n event.body,\n event.isBase64Encoded,\n );\n\n body = bufferBody;\n headers['content-length'] = String(contentLength);\n }\n\n let remoteAddress = '';\n\n // ref: https://docs.aws.amazon.com/elasticloadbalancing/latest/application/x-forwarded-headers.html#x-forwarded-for\n if (headers['x-forwarded-for']) remoteAddress = headers['x-forwarded-for'];\n\n return {\n method,\n headers,\n body,\n remoteAddress,\n path,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public getResponse({\n event,\n headers: responseHeaders,\n body,\n isBase64Encoded,\n statusCode,\n }: GetResponseAdapterProps<ALBEvent>): ALBResult {\n const multiValueHeaders = !event.headers\n ? getMultiValueHeadersMap(responseHeaders)\n : undefined;\n\n const headers = event.headers\n ? getFlattenedHeadersMap(responseHeaders)\n : undefined;\n\n return {\n statusCode,\n body,\n headers,\n multiValueHeaders,\n isBase64Encoded,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public onErrorWhileForwarding({\n error,\n delegatedResolver,\n respondWithErrors,\n event,\n log,\n }: OnErrorProps<ALBEvent, ALBResult>): void {\n const body = respondWithErrors ? error.stack || '' : '';\n const errorResponse = this.getResponse({\n event,\n statusCode: 500,\n body,\n headers: {},\n isBase64Encoded: false,\n log,\n });\n\n delegatedResolver.succeed(errorResponse);\n }\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * Get path from event with query strings\n *\n * @param event - The event sent by serverless\n */\n protected getPathFromEvent(event: ALBEvent): string {\n const path = this.stripPathFn(event.path);\n\n const queryParams = event.headers\n ? event.queryStringParameters\n : event.multiValueQueryStringParameters;\n\n return getPathWithQueryStringParams(path, queryParams || {});\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"ooBAAA,oBAAA,CAAA,EAAA,SAAA,oBAAA,CAAA,WAAA,IAAA,UAAA,CAAA,EAAA,OAAA,QAAA,aAAA,mBAAA,EASA,IAOO,YAAA,QAAA,YAAA,EAiCA,MAAM,UAEb,CAQE,YAA+B,QAA6B,QAA7B,KAAA,QAAA,QAC7B,KAAK,eAAc,YAAA,qBAAmB,QAAK,UAAL,eAAc,aAAa,CACnE,CAkBO,gBAAyB,CAC9B,OAAO,WAAW,IACpB,CAKO,UAAU,MAAmC,CAClD,MAAM,SAAW,MAEjB,MAAO,CAAC,EAAE,yBAAU,gBAAkB,SAAS,eAAe,IAChE,CAKO,WAAW,MAAiC,CACjD,MAAM,OAAS,MAAM,WACf,KAAO,KAAK,iBAAiB,KAAK,EAElC,QAAU,MAAM,qBAClB,YAAA,wBAAuB,MAAM,kBAAmB,IAAK,EAAI,EACzD,MAAM,QAEN,IAAA,KAEJ,GAAI,MAAM,KAAM,CACR,KAAA,CAAC,WAAY,aAAa,KAAI,YAAA,sBAClC,MAAM,KACN,MAAM,eAAA,EAGD,KAAA,WACC,QAAA,gBAAgB,EAAI,OAAO,aAAa,CAClD,CAEA,IAAI,cAAgB,GAGpB,OAAI,QAAQ,iBAAiB,IAAG,cAAgB,QAAQ,iBAAiB,GAElE,CACL,OACA,QACA,KACA,cACA,IAAA,CAEJ,CAKO,YAAY,CACjB,MACA,QAAS,gBACT,KACA,gBACA,UAAA,EAC+C,CAC/C,MAAM,kBAAqB,MAAM,QAE7B,UADA,YAAA,yBAAwB,eAAe,EAGrC,QAAU,MAAM,WAClB,YAAA,wBAAuB,eAAe,EACtC,OAEG,MAAA,CACL,WACA,KACA,QACA,kBACA,eAAA,CAEJ,CAKO,uBAAuB,CAC5B,MACA,kBACA,kBACA,MACA,GAAA,EAC0C,CAC1C,MAAM,KAAO,mBAAoB,MAAM,OAAS,GAC1C,cAAgB,KAAK,YAAY,CACrC,MACA,WAAY,IACZ,KACA,QAAS,CAAC,EACV,gBAAiB,GACjB,GAAA,CACD,EAED,kBAAkB,QAAQ,aAAa,CACzC,CAWU,iBAAiB,MAAyB,CAClD,MAAM,KAAO,KAAK,YAAY,MAAM,IAAI,EAElC,YAAc,MAAM,QACtB,MAAM,sBACN,MAAM,gCAEV,SAAO,YAA6B,8BAAA,KAAM,aAAe,CAAE,CAAA,CAC7D,CAGF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-gateway-v1.adapter.js","sources":["../../../src/adapters/aws/api-gateway-v1.adapter.ts"],"sourcesContent":["//#region Imports\n\nimport type { APIGatewayProxyResult, Context } from 'aws-lambda';\nimport type { APIGatewayProxyEvent } from 'aws-lambda/trigger/api-gateway-proxy';\nimport {\n AdapterContract,\n AdapterRequest,\n GetResponseAdapterProps,\n OnErrorProps,\n} from '../../contracts';\nimport {\n StripBasePathFn,\n buildStripBasePath,\n getEventBodyAsBuffer,\n getMultiValueHeadersMap,\n getPathWithQueryStringParams,\n} from '../../core';\n\n//#endregion\n\n/**\n * The options to customize the {@link ApiGatewayV1Adapter}\n *\n * @breadcrumb Adapters / AWS / ApiGatewayV1Adapter\n * @public\n */\nexport interface ApiGatewayV1Options {\n /**\n * Strip base path for custom domains\n *\n * @defaultValue ''\n */\n stripBasePath?: string;\n}\n\n/**\n * The adapter to handle requests from AWS Api Gateway V1\n *\n * As per {@link https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-known-issues.html | know issues}, we throw an exception when you send the `transfer-encoding=chunked`, currently, API Gateway doesn't support chunked transfer.\n *\n * @remarks This adapter is not fully compatible with \\@vendia/serverless-express, on \\@vendia they filter `transfer-encoding=chunked` but we throw an exception.\n *\n * @example\n * ```typescript\n * const stripBasePath = '/any/custom/base/path'; // default ''\n * const adapter = new ApiGatewayV1Adapter({ stripBasePath });\n * ```\n *\n * {@link https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html | Event Reference}\n *\n * @breadcrumb Adapters / AWS / ApiGatewayV1Adapter\n * @public\n */\nexport class ApiGatewayV1Adapter\n implements\n AdapterContract<APIGatewayProxyEvent, Context, APIGatewayProxyResult>\n{\n //#region Constructor\n\n /**\n * Default constructor\n *\n * @param options - The options to customize the {@link ApiGatewayV1Adapter}\n */\n constructor(protected readonly options?: ApiGatewayV1Options) {\n this.stripPathFn = buildStripBasePath(this.options?.stripBasePath);\n }\n\n //#endregion\n\n //#region Protected Properties\n\n /**\n * Strip base path function\n */\n protected stripPathFn: StripBasePathFn;\n\n //#endregion\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public getAdapterName(): string {\n return ApiGatewayV1Adapter.name;\n }\n\n /**\n * {@inheritDoc}\n */\n public canHandle(event: unknown): event is APIGatewayProxyEvent {\n const partialEventV1 = event as Partial<APIGatewayProxyEvent> & {\n version?: '2.0';\n };\n\n return !!(\n partialEventV1?.requestContext &&\n partialEventV1.version !== '2.0' &&\n partialEventV1.headers &&\n partialEventV1.multiValueHeaders &&\n ((partialEventV1.queryStringParameters === null &&\n partialEventV1.multiValueQueryStringParameters === null) ||\n (partialEventV1.queryStringParameters &&\n partialEventV1.multiValueQueryStringParameters))\n );\n }\n\n /**\n * {@inheritDoc}\n */\n public getRequest(event: APIGatewayProxyEvent): AdapterRequest {\n const method = event.httpMethod;\n const headers = { ...event.headers };\n\n for (const multiValueHeaderKey of Object.keys(\n event.multiValueHeaders || {},\n )) {\n const headerValue = event.multiValueHeaders[multiValueHeaderKey];\n\n // event.headers by default only stick with first value if they see multiple headers\n // the other values will only appear on multiValueHeaderKey, in this case\n // we look for headers with more than 1 length which is the wrong values on event.headers\n // https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html\n if (!headerValue || headerValue?.length <= 1) continue;\n\n headers[multiValueHeaderKey] = headerValue.join(',');\n }\n\n const path = this.getPathFromEvent(event);\n\n let body: Buffer | undefined;\n\n if (event.body) {\n const [bufferBody, contentLength] = getEventBodyAsBuffer(\n event.body,\n event.isBase64Encoded,\n );\n\n body = bufferBody;\n // eslint-disable-next-line @typescript-eslint/restrict-plus-operands\n headers['content-length'] = contentLength + '';\n }\n\n const remoteAddress = event.requestContext.identity.sourceIp;\n\n return {\n method,\n headers,\n body,\n remoteAddress,\n path,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public getResponse({\n headers: responseHeaders,\n body,\n isBase64Encoded,\n statusCode,\n response,\n }: GetResponseAdapterProps<APIGatewayProxyEvent>): APIGatewayProxyResult {\n const multiValueHeaders = getMultiValueHeadersMap(responseHeaders);\n\n const transferEncodingHeader = multiValueHeaders['transfer-encoding'];\n const hasTransferEncodingChunked = transferEncodingHeader?.some(value =>\n value.includes('chunked'),\n );\n\n if (hasTransferEncodingChunked) {\n throw new Error(\n 'chunked encoding in headers is not supported by API Gateway V1',\n );\n }\n\n if (response?.chunkedEncoding) {\n throw new Error(\n 'chunked encoding in response is not supported by API Gateway V1',\n );\n }\n\n return {\n statusCode,\n body,\n multiValueHeaders,\n isBase64Encoded,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public onErrorWhileForwarding({\n error,\n delegatedResolver,\n respondWithErrors,\n event,\n log,\n }: OnErrorProps<APIGatewayProxyEvent, APIGatewayProxyResult>): void {\n const body = respondWithErrors ? error.stack : '';\n const errorResponse = this.getResponse({\n event,\n statusCode: 500,\n body: body || '',\n headers: {},\n isBase64Encoded: false,\n log,\n });\n\n delegatedResolver.succeed(errorResponse);\n }\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * Get path from event with query strings\n *\n * @param event - The event sent by serverless\n */\n protected getPathFromEvent(event: APIGatewayProxyEvent): string {\n const path = this.stripPathFn(event.path);\n const queryParams = event.multiValueQueryStringParameters || {};\n\n if (event.queryStringParameters) {\n for (const queryStringKey of Object.keys(event.queryStringParameters)) {\n const queryStringValue = event.queryStringParameters[queryStringKey];\n\n if (queryStringValue === undefined) continue;\n\n if (!Array.isArray(queryParams[queryStringKey]))\n queryParams[queryStringKey] = [];\n\n if (queryParams[queryStringKey]!.includes(queryStringValue)) continue;\n\n queryParams[queryStringKey]!.push(queryStringValue);\n }\n }\n\n return getPathWithQueryStringParams(path, queryParams);\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"ooBAAA,+BAAA,CAAA,EAAA,SAAA,+BAAA,CAAA,oBAAA,IAAA,mBAAA,CAAA,EAAA,OAAA,QAAA,aAAA,8BAAA,EAUA,IAMO,YAAA,QAAA,YAAA,EAqCA,MAAM,mBAGb,CAQE,YAA+B,QAA+B,QAA/B,KAAA,QAAA,QAC7B,KAAK,eAAc,YAAA,qBAAmB,QAAK,UAAL,eAAc,aAAa,CACnE,CAkBO,gBAAyB,CAC9B,OAAO,oBAAoB,IAC7B,CAKO,UAAU,MAA+C,CAC9D,MAAM,eAAiB,MAIvB,MAAO,CAAC,EACN,qCAAgB,gBAChB,eAAe,UAAY,OAC3B,eAAe,SACf,eAAe,oBACb,eAAe,wBAA0B,MACzC,eAAe,kCAAoC,MAClD,eAAe,uBACd,eAAe,iCAEvB,CAKO,WAAW,MAA6C,CAC7D,MAAM,OAAS,MAAM,WACf,QAAU,CAAE,GAAG,MAAM,OAAQ,EAEnC,UAAW,uBAAuB,OAAO,KACvC,MAAM,mBAAqB,CAAC,CAAA,EAC3B,CACK,MAAA,YAAc,MAAM,kBAAkB,mBAAmB,EAM3D,CAAC,cAAe,qCAAa,SAAU,IAE3C,QAAQ,mBAAmB,EAAI,YAAY,KAAK,GAAG,
|
|
1
|
+
{"version":3,"file":"api-gateway-v1.adapter.js","sources":["../../../src/adapters/aws/api-gateway-v1.adapter.ts"],"sourcesContent":["//#region Imports\n\nimport type { APIGatewayProxyResult, Context } from 'aws-lambda';\nimport type { APIGatewayProxyEvent } from 'aws-lambda/trigger/api-gateway-proxy';\nimport {\n AdapterContract,\n AdapterRequest,\n GetResponseAdapterProps,\n OnErrorProps,\n} from '../../contracts';\nimport {\n StripBasePathFn,\n buildStripBasePath,\n getEventBodyAsBuffer,\n getMultiValueHeadersMap,\n getPathWithQueryStringParams,\n} from '../../core';\n\n//#endregion\n\n/**\n * The options to customize the {@link ApiGatewayV1Adapter}\n *\n * @breadcrumb Adapters / AWS / ApiGatewayV1Adapter\n * @public\n */\nexport interface ApiGatewayV1Options {\n /**\n * Strip base path for custom domains\n *\n * @defaultValue ''\n */\n stripBasePath?: string;\n}\n\n/**\n * The adapter to handle requests from AWS Api Gateway V1\n *\n * As per {@link https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-known-issues.html | know issues}, we throw an exception when you send the `transfer-encoding=chunked`, currently, API Gateway doesn't support chunked transfer.\n *\n * @remarks This adapter is not fully compatible with \\@vendia/serverless-express, on \\@vendia they filter `transfer-encoding=chunked` but we throw an exception.\n *\n * @example\n * ```typescript\n * const stripBasePath = '/any/custom/base/path'; // default ''\n * const adapter = new ApiGatewayV1Adapter({ stripBasePath });\n * ```\n *\n * {@link https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html | Event Reference}\n *\n * @breadcrumb Adapters / AWS / ApiGatewayV1Adapter\n * @public\n */\nexport class ApiGatewayV1Adapter\n implements\n AdapterContract<APIGatewayProxyEvent, Context, APIGatewayProxyResult>\n{\n //#region Constructor\n\n /**\n * Default constructor\n *\n * @param options - The options to customize the {@link ApiGatewayV1Adapter}\n */\n constructor(protected readonly options?: ApiGatewayV1Options) {\n this.stripPathFn = buildStripBasePath(this.options?.stripBasePath);\n }\n\n //#endregion\n\n //#region Protected Properties\n\n /**\n * Strip base path function\n */\n protected stripPathFn: StripBasePathFn;\n\n //#endregion\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public getAdapterName(): string {\n return ApiGatewayV1Adapter.name;\n }\n\n /**\n * {@inheritDoc}\n */\n public canHandle(event: unknown): event is APIGatewayProxyEvent {\n const partialEventV1 = event as Partial<APIGatewayProxyEvent> & {\n version?: '2.0';\n };\n\n return !!(\n partialEventV1?.requestContext &&\n partialEventV1.version !== '2.0' &&\n partialEventV1.headers &&\n partialEventV1.multiValueHeaders &&\n ((partialEventV1.queryStringParameters === null &&\n partialEventV1.multiValueQueryStringParameters === null) ||\n (partialEventV1.queryStringParameters &&\n partialEventV1.multiValueQueryStringParameters))\n );\n }\n\n /**\n * {@inheritDoc}\n */\n public getRequest(event: APIGatewayProxyEvent): AdapterRequest {\n const method = event.httpMethod;\n const headers = { ...event.headers };\n\n for (const multiValueHeaderKey of Object.keys(\n event.multiValueHeaders || {},\n )) {\n const headerValue = event.multiValueHeaders[multiValueHeaderKey];\n\n // event.headers by default only stick with first value if they see multiple headers\n // the other values will only appear on multiValueHeaderKey, in this case\n // we look for headers with more than 1 length which is the wrong values on event.headers\n // https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html\n if (!headerValue || headerValue?.length <= 1) continue;\n\n headers[multiValueHeaderKey] = headerValue.join(',');\n }\n\n const path = this.getPathFromEvent(event);\n\n let body: Buffer | undefined;\n\n if (event.body) {\n const [bufferBody, contentLength] = getEventBodyAsBuffer(\n event.body,\n event.isBase64Encoded,\n );\n\n body = bufferBody;\n // eslint-disable-next-line @typescript-eslint/restrict-plus-operands\n headers['content-length'] = contentLength + '';\n }\n\n const remoteAddress = event.requestContext.identity.sourceIp;\n\n return {\n method,\n headers,\n body,\n remoteAddress,\n path,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public getResponse({\n headers: responseHeaders,\n body,\n isBase64Encoded,\n statusCode,\n response,\n }: GetResponseAdapterProps<APIGatewayProxyEvent>): APIGatewayProxyResult {\n const multiValueHeaders = getMultiValueHeadersMap(responseHeaders);\n\n const transferEncodingHeader = multiValueHeaders['transfer-encoding'];\n const hasTransferEncodingChunked = transferEncodingHeader?.some(value =>\n value.includes('chunked'),\n );\n\n if (hasTransferEncodingChunked) {\n throw new Error(\n 'chunked encoding in headers is not supported by API Gateway V1',\n );\n }\n\n if (response?.chunkedEncoding) {\n throw new Error(\n 'chunked encoding in response is not supported by API Gateway V1',\n );\n }\n\n return {\n statusCode,\n body,\n multiValueHeaders,\n isBase64Encoded,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public onErrorWhileForwarding({\n error,\n delegatedResolver,\n respondWithErrors,\n event,\n log,\n }: OnErrorProps<APIGatewayProxyEvent, APIGatewayProxyResult>): void {\n const body = respondWithErrors ? error.stack : '';\n const errorResponse = this.getResponse({\n event,\n statusCode: 500,\n body: body || '',\n headers: {},\n isBase64Encoded: false,\n log,\n });\n\n delegatedResolver.succeed(errorResponse);\n }\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * Get path from event with query strings\n *\n * @param event - The event sent by serverless\n */\n protected getPathFromEvent(event: APIGatewayProxyEvent): string {\n const path = this.stripPathFn(event.path);\n const queryParams = event.multiValueQueryStringParameters || {};\n\n if (event.queryStringParameters) {\n for (const queryStringKey of Object.keys(event.queryStringParameters)) {\n const queryStringValue = event.queryStringParameters[queryStringKey];\n\n if (queryStringValue === undefined) continue;\n\n if (!Array.isArray(queryParams[queryStringKey]))\n queryParams[queryStringKey] = [];\n\n if (queryParams[queryStringKey]!.includes(queryStringValue)) continue;\n\n queryParams[queryStringKey]!.push(queryStringValue);\n }\n }\n\n return getPathWithQueryStringParams(path, queryParams);\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"ooBAAA,+BAAA,CAAA,EAAA,SAAA,+BAAA,CAAA,oBAAA,IAAA,mBAAA,CAAA,EAAA,OAAA,QAAA,aAAA,8BAAA,EAUA,IAMO,YAAA,QAAA,YAAA,EAqCA,MAAM,mBAGb,CAQE,YAA+B,QAA+B,QAA/B,KAAA,QAAA,QAC7B,KAAK,eAAc,YAAA,qBAAmB,QAAK,UAAL,eAAc,aAAa,CACnE,CAkBO,gBAAyB,CAC9B,OAAO,oBAAoB,IAC7B,CAKO,UAAU,MAA+C,CAC9D,MAAM,eAAiB,MAIvB,MAAO,CAAC,EACN,qCAAgB,gBAChB,eAAe,UAAY,OAC3B,eAAe,SACf,eAAe,oBACb,eAAe,wBAA0B,MACzC,eAAe,kCAAoC,MAClD,eAAe,uBACd,eAAe,iCAEvB,CAKO,WAAW,MAA6C,CAC7D,MAAM,OAAS,MAAM,WACf,QAAU,CAAE,GAAG,MAAM,OAAQ,EAEnC,UAAW,uBAAuB,OAAO,KACvC,MAAM,mBAAqB,CAAC,CAAA,EAC3B,CACK,MAAA,YAAc,MAAM,kBAAkB,mBAAmB,EAM3D,CAAC,cAAe,qCAAa,SAAU,IAE3C,QAAQ,mBAAmB,EAAI,YAAY,KAAK,GAAG,EACrD,CAEM,MAAA,KAAO,KAAK,iBAAiB,KAAK,EAEpC,IAAA,KAEJ,GAAI,MAAM,KAAM,CACR,KAAA,CAAC,WAAY,aAAa,KAAI,YAAA,sBAClC,MAAM,KACN,MAAM,eAAA,EAGD,KAAA,WAEC,QAAA,gBAAgB,EAAI,cAAgB,EAC9C,CAEM,MAAA,cAAgB,MAAM,eAAe,SAAS,SAE7C,MAAA,CACL,OACA,QACA,KACA,cACA,IAAA,CAEJ,CAKO,YAAY,CACjB,QAAS,gBACT,KACA,gBACA,WACA,QAAA,EACuE,CACjE,MAAA,qBAAoB,qCAAwB,eAAe,EAE3D,uBAAyB,kBAAkB,mBAAmB,EAKpE,GAJmC,2DAAwB,KAAK,OAC9D,MAAM,SAAS,SAAS,GAIxB,MAAM,IAAI,MACR,gEAAA,EAIJ,GAAI,yBAAU,gBACZ,MAAM,IAAI,MACR,iEAAA,EAIG,MAAA,CACL,WACA,KACA,kBACA,eAAA,CAEJ,CAKO,uBAAuB,CAC5B,MACA,kBACA,kBACA,MACA,GAAA,EACkE,CAC5D,MAAA,KAAO,kBAAoB,MAAM,MAAQ,GACzC,cAAgB,KAAK,YAAY,CACrC,MACA,WAAY,IACZ,KAAM,MAAQ,GACd,QAAS,CAAC,EACV,gBAAiB,GACjB,GAAA,CACD,EAED,kBAAkB,QAAQ,aAAa,CACzC,CAWU,iBAAiB,MAAqC,CAC9D,MAAM,KAAO,KAAK,YAAY,MAAM,IAAI,EAClC,YAAc,MAAM,iCAAmC,GAE7D,GAAI,MAAM,sBACR,UAAW,kBAAkB,OAAO,KAAK,MAAM,qBAAqB,EAAG,CAC/D,MAAA,iBAAmB,MAAM,sBAAsB,cAAc,EAE/D,mBAAqB,SAEpB,MAAM,QAAQ,YAAY,cAAc,CAAC,IAChC,YAAA,cAAc,EAAI,IAE5B,aAAY,cAAc,EAAG,SAAS,gBAAgB,GAE9C,YAAA,cAAc,EAAG,KAAK,gBAAgB,EACpD,CAGK,SAAA,YAAA,8BAA6B,KAAM,WAAW,CACvD,CAGF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-gateway-v2.adapter.js","sources":["../../../src/adapters/aws/api-gateway-v2.adapter.ts"],"sourcesContent":["//#region Imports\n\nimport type { APIGatewayProxyEventV2, Context } from 'aws-lambda';\nimport type { APIGatewayProxyStructuredResultV2 } from 'aws-lambda/trigger/api-gateway-proxy';\nimport {\n AdapterContract,\n AdapterRequest,\n GetResponseAdapterProps,\n OnErrorProps,\n} from '../../contracts';\nimport {\n StripBasePathFn,\n buildStripBasePath,\n getEventBodyAsBuffer,\n getFlattenedHeadersMapAndCookies,\n getPathWithQueryStringParams,\n} from '../../core';\n\n//#endregion\n\n/**\n * The options to customize the {@link ApiGatewayV2Adapter}\n *\n * @breadcrumb Adapters / AWS / ApiGatewayV2Adapter\n * @public\n */\nexport interface ApiGatewayV2Options {\n /**\n * Strip base path for custom domains\n *\n * @defaultValue ''\n */\n stripBasePath?: string;\n}\n\n/**\n * The adapter to handle requests from AWS Api Gateway V2\n *\n * As per {@link https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-known-issues.html | know issues}, we throw an exception when you send the `transfer-encoding=chunked`.\n * But, if you use this adapter to accept requests from Function URL, you can accept the `transfer-encoding=chunked` changing the method of invocation from `BUFFERED` to `RESPONSE_STREAM`.\n *\n * @example\n * ```typescript\n * const stripBasePath = '/any/custom/base/path'; // default ''\n * const adapter = new ApiGatewayV2Adapter({ stripBasePath });\n * ```\n *\n * {@link https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html | Event Reference}\n *\n * @breadcrumb Adapters / AWS / ApiGatewayV2Adapter\n * @public\n */\nexport class ApiGatewayV2Adapter\n implements\n AdapterContract<\n APIGatewayProxyEventV2,\n Context,\n APIGatewayProxyStructuredResultV2\n >\n{\n //#region Constructor\n\n /**\n * Default constructor\n *\n * @param options - The options to customize the {@link ApiGatewayV2Adapter}\n */\n constructor(protected readonly options?: ApiGatewayV2Options) {\n this.stripPathFn = buildStripBasePath(this.options?.stripBasePath);\n }\n\n //#endregion\n\n //#region Protected Properties\n\n /**\n * Strip base path function\n */\n protected stripPathFn: StripBasePathFn;\n\n //#endregion\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public getAdapterName(): string {\n return ApiGatewayV2Adapter.name;\n }\n\n /**\n * {@inheritDoc}\n */\n public canHandle(event: unknown): event is APIGatewayProxyEventV2 {\n const apiGatewayEvent = event as Partial<APIGatewayProxyEventV2> & {\n version?: string;\n };\n\n return !!(\n apiGatewayEvent?.requestContext && apiGatewayEvent.version === '2.0'\n );\n }\n\n /**\n * {@inheritDoc}\n */\n public getRequest(event: APIGatewayProxyEventV2): AdapterRequest {\n const method = event.requestContext.http.method;\n const path = this.getPathFromEvent(event);\n // accords https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html\n // all headers are lowercased and cannot be array\n // so no need to format, just a shallow copy will work here\n const headers = { ...event.headers };\n\n if (event.cookies) headers.cookie = event.cookies.join('; ');\n\n let body: Buffer | undefined;\n\n if (event.body) {\n const [bufferBody, contentLength] = getEventBodyAsBuffer(\n event.body,\n event.isBase64Encoded,\n );\n\n body = bufferBody;\n // eslint-disable-next-line @typescript-eslint/restrict-plus-operands\n headers['content-length'] = contentLength + '';\n }\n\n const remoteAddress = event.requestContext.http.sourceIp;\n\n return {\n method,\n headers,\n body,\n remoteAddress,\n path,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public getResponse({\n headers: responseHeaders,\n body,\n isBase64Encoded,\n statusCode,\n response,\n }: GetResponseAdapterProps<APIGatewayProxyEventV2>): APIGatewayProxyStructuredResultV2 {\n const { cookies, headers } =\n getFlattenedHeadersMapAndCookies(responseHeaders);\n\n const transferEncodingHeader: string | undefined =\n headers['transfer-encoding'];\n\n const hasTransferEncodingChunked =\n transferEncodingHeader && transferEncodingHeader.includes('chunked');\n\n if (hasTransferEncodingChunked) {\n throw new Error(\n 'chunked encoding in headers is not supported by API Gateway V2',\n );\n }\n\n if (response?.chunkedEncoding) {\n throw new Error(\n 'chunked encoding in response is not supported by API Gateway V2',\n );\n }\n\n return {\n statusCode,\n body,\n headers,\n isBase64Encoded,\n cookies,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public onErrorWhileForwarding({\n error,\n delegatedResolver,\n respondWithErrors,\n event,\n log,\n }: OnErrorProps<\n APIGatewayProxyEventV2,\n APIGatewayProxyStructuredResultV2\n >): void {\n const body = respondWithErrors ? error.stack : '';\n const errorResponse = this.getResponse({\n event,\n statusCode: 500,\n body: body || '',\n headers: {},\n isBase64Encoded: false,\n log,\n });\n\n delegatedResolver.succeed(errorResponse);\n }\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * Get path from event with query strings\n *\n * @param event - The event sent by serverless\n */\n protected getPathFromEvent(event: APIGatewayProxyEventV2): string {\n const path = this.stripPathFn(event.rawPath);\n const queryParams = event.rawQueryString;\n\n return getPathWithQueryStringParams(path, queryParams || {});\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"ooBAAA,+BAAA,CAAA,EAAA,SAAA,+BAAA,CAAA,oBAAA,IAAA,mBAAA,CAAA,EAAA,OAAA,QAAA,aAAA,8BAAA,EAUA,IAMO,YAAA,QAAA,YAAA,EAoCA,MAAM,mBAOb,CAQE,YAA+B,QAA+B,QAA/B,KAAA,QAAA,QAC7B,KAAK,eAAc,YAAA,qBAAmB,QAAK,UAAL,eAAc,aAAa,CACnE,CAkBO,gBAAyB,CAC9B,OAAO,oBAAoB,IAC7B,CAKO,UAAU,MAAiD,CAChE,MAAM,gBAAkB,MAIxB,MAAO,CAAC,EACN,uCAAiB,gBAAkB,gBAAgB,UAAY,MAEnE,CAKO,WAAW,MAA+C,CACzD,MAAA,OAAS,MAAM,eAAe,KAAK,OACnC,KAAO,KAAK,iBAAiB,KAAK,EAIlC,QAAU,CAAE,GAAG,MAAM,OAAQ,EAE/B,MAAM,UAAS,QAAQ,OAAS,MAAM,QAAQ,KAAK,IAAI,GAEvD,IAAA,KAEJ,GAAI,MAAM,KAAM,CACR,KAAA,CAAC,WAAY,aAAa,KAAI,YAAA,sBAClC,MAAM,KACN,MAAM,eAAA,EAGD,KAAA,WAEC,QAAA,gBAAgB,EAAI,cAAgB,
|
|
1
|
+
{"version":3,"file":"api-gateway-v2.adapter.js","sources":["../../../src/adapters/aws/api-gateway-v2.adapter.ts"],"sourcesContent":["//#region Imports\n\nimport type { APIGatewayProxyEventV2, Context } from 'aws-lambda';\nimport type { APIGatewayProxyStructuredResultV2 } from 'aws-lambda/trigger/api-gateway-proxy';\nimport {\n AdapterContract,\n AdapterRequest,\n GetResponseAdapterProps,\n OnErrorProps,\n} from '../../contracts';\nimport {\n StripBasePathFn,\n buildStripBasePath,\n getEventBodyAsBuffer,\n getFlattenedHeadersMapAndCookies,\n getPathWithQueryStringParams,\n} from '../../core';\n\n//#endregion\n\n/**\n * The options to customize the {@link ApiGatewayV2Adapter}\n *\n * @breadcrumb Adapters / AWS / ApiGatewayV2Adapter\n * @public\n */\nexport interface ApiGatewayV2Options {\n /**\n * Strip base path for custom domains\n *\n * @defaultValue ''\n */\n stripBasePath?: string;\n}\n\n/**\n * The adapter to handle requests from AWS Api Gateway V2\n *\n * As per {@link https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-known-issues.html | know issues}, we throw an exception when you send the `transfer-encoding=chunked`.\n * But, if you use this adapter to accept requests from Function URL, you can accept the `transfer-encoding=chunked` changing the method of invocation from `BUFFERED` to `RESPONSE_STREAM`.\n *\n * @example\n * ```typescript\n * const stripBasePath = '/any/custom/base/path'; // default ''\n * const adapter = new ApiGatewayV2Adapter({ stripBasePath });\n * ```\n *\n * {@link https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html | Event Reference}\n *\n * @breadcrumb Adapters / AWS / ApiGatewayV2Adapter\n * @public\n */\nexport class ApiGatewayV2Adapter\n implements\n AdapterContract<\n APIGatewayProxyEventV2,\n Context,\n APIGatewayProxyStructuredResultV2\n >\n{\n //#region Constructor\n\n /**\n * Default constructor\n *\n * @param options - The options to customize the {@link ApiGatewayV2Adapter}\n */\n constructor(protected readonly options?: ApiGatewayV2Options) {\n this.stripPathFn = buildStripBasePath(this.options?.stripBasePath);\n }\n\n //#endregion\n\n //#region Protected Properties\n\n /**\n * Strip base path function\n */\n protected stripPathFn: StripBasePathFn;\n\n //#endregion\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public getAdapterName(): string {\n return ApiGatewayV2Adapter.name;\n }\n\n /**\n * {@inheritDoc}\n */\n public canHandle(event: unknown): event is APIGatewayProxyEventV2 {\n const apiGatewayEvent = event as Partial<APIGatewayProxyEventV2> & {\n version?: string;\n };\n\n return !!(\n apiGatewayEvent?.requestContext && apiGatewayEvent.version === '2.0'\n );\n }\n\n /**\n * {@inheritDoc}\n */\n public getRequest(event: APIGatewayProxyEventV2): AdapterRequest {\n const method = event.requestContext.http.method;\n const path = this.getPathFromEvent(event);\n // accords https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html\n // all headers are lowercased and cannot be array\n // so no need to format, just a shallow copy will work here\n const headers = { ...event.headers };\n\n if (event.cookies) headers.cookie = event.cookies.join('; ');\n\n let body: Buffer | undefined;\n\n if (event.body) {\n const [bufferBody, contentLength] = getEventBodyAsBuffer(\n event.body,\n event.isBase64Encoded,\n );\n\n body = bufferBody;\n // eslint-disable-next-line @typescript-eslint/restrict-plus-operands\n headers['content-length'] = contentLength + '';\n }\n\n const remoteAddress = event.requestContext.http.sourceIp;\n\n return {\n method,\n headers,\n body,\n remoteAddress,\n path,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public getResponse({\n headers: responseHeaders,\n body,\n isBase64Encoded,\n statusCode,\n response,\n }: GetResponseAdapterProps<APIGatewayProxyEventV2>): APIGatewayProxyStructuredResultV2 {\n const { cookies, headers } =\n getFlattenedHeadersMapAndCookies(responseHeaders);\n\n const transferEncodingHeader: string | undefined =\n headers['transfer-encoding'];\n\n const hasTransferEncodingChunked =\n transferEncodingHeader && transferEncodingHeader.includes('chunked');\n\n if (hasTransferEncodingChunked) {\n throw new Error(\n 'chunked encoding in headers is not supported by API Gateway V2',\n );\n }\n\n if (response?.chunkedEncoding) {\n throw new Error(\n 'chunked encoding in response is not supported by API Gateway V2',\n );\n }\n\n return {\n statusCode,\n body,\n headers,\n isBase64Encoded,\n cookies,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public onErrorWhileForwarding({\n error,\n delegatedResolver,\n respondWithErrors,\n event,\n log,\n }: OnErrorProps<\n APIGatewayProxyEventV2,\n APIGatewayProxyStructuredResultV2\n >): void {\n const body = respondWithErrors ? error.stack : '';\n const errorResponse = this.getResponse({\n event,\n statusCode: 500,\n body: body || '',\n headers: {},\n isBase64Encoded: false,\n log,\n });\n\n delegatedResolver.succeed(errorResponse);\n }\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * Get path from event with query strings\n *\n * @param event - The event sent by serverless\n */\n protected getPathFromEvent(event: APIGatewayProxyEventV2): string {\n const path = this.stripPathFn(event.rawPath);\n const queryParams = event.rawQueryString;\n\n return getPathWithQueryStringParams(path, queryParams || {});\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"ooBAAA,+BAAA,CAAA,EAAA,SAAA,+BAAA,CAAA,oBAAA,IAAA,mBAAA,CAAA,EAAA,OAAA,QAAA,aAAA,8BAAA,EAUA,IAMO,YAAA,QAAA,YAAA,EAoCA,MAAM,mBAOb,CAQE,YAA+B,QAA+B,QAA/B,KAAA,QAAA,QAC7B,KAAK,eAAc,YAAA,qBAAmB,QAAK,UAAL,eAAc,aAAa,CACnE,CAkBO,gBAAyB,CAC9B,OAAO,oBAAoB,IAC7B,CAKO,UAAU,MAAiD,CAChE,MAAM,gBAAkB,MAIxB,MAAO,CAAC,EACN,uCAAiB,gBAAkB,gBAAgB,UAAY,MAEnE,CAKO,WAAW,MAA+C,CACzD,MAAA,OAAS,MAAM,eAAe,KAAK,OACnC,KAAO,KAAK,iBAAiB,KAAK,EAIlC,QAAU,CAAE,GAAG,MAAM,OAAQ,EAE/B,MAAM,UAAS,QAAQ,OAAS,MAAM,QAAQ,KAAK,IAAI,GAEvD,IAAA,KAEJ,GAAI,MAAM,KAAM,CACR,KAAA,CAAC,WAAY,aAAa,KAAI,YAAA,sBAClC,MAAM,KACN,MAAM,eAAA,EAGD,KAAA,WAEC,QAAA,gBAAgB,EAAI,cAAgB,EAC9C,CAEM,MAAA,cAAgB,MAAM,eAAe,KAAK,SAEzC,MAAA,CACL,OACA,QACA,KACA,cACA,IAAA,CAEJ,CAKO,YAAY,CACjB,QAAS,gBACT,KACA,gBACA,WACA,QAAA,EACqF,CACrF,KAAM,CAAE,QAAS,OAAQ,KACvB,8CAAiC,eAAe,EAE5C,uBACJ,QAAQ,mBAAmB,EAK7B,GAFE,wBAA0B,uBAAuB,SAAS,SAAS,EAGnE,MAAM,IAAI,MACR,gEAAA,EAIJ,GAAI,yBAAU,gBACZ,MAAM,IAAI,MACR,iEAAA,EAIG,MAAA,CACL,WACA,KACA,QACA,gBACA,OAAA,CAEJ,CAKO,uBAAuB,CAC5B,MACA,kBACA,kBACA,MACA,GAAA,EAIO,CACD,MAAA,KAAO,kBAAoB,MAAM,MAAQ,GACzC,cAAgB,KAAK,YAAY,CACrC,MACA,WAAY,IACZ,KAAM,MAAQ,GACd,QAAS,CAAC,EACV,gBAAiB,GACjB,GAAA,CACD,EAED,kBAAkB,QAAQ,aAAa,CACzC,CAWU,iBAAiB,MAAuC,CAChE,MAAM,KAAO,KAAK,YAAY,MAAM,OAAO,EACrC,YAAc,MAAM,eAE1B,SAAO,YAA6B,8BAAA,KAAM,aAAe,CAAE,CAAA,CAC7D,CAGF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lambda-edge.adapter.js","sources":["../../../src/adapters/aws/lambda-edge.adapter.ts"],"sourcesContent":["//#region Imports\n\nimport type { CloudFrontRequest, Context } from 'aws-lambda';\nimport type {\n CloudFrontEvent,\n CloudFrontHeaders,\n CloudFrontResultResponse,\n} from 'aws-lambda/common/cloudfront';\nimport type {\n CloudFrontRequestEvent,\n CloudFrontRequestResult,\n} from 'aws-lambda/trigger/cloudfront-request';\nimport { BothValueHeaders, Concrete, SingleValueHeaders } from '../../@types';\nimport {\n AdapterContract,\n AdapterRequest,\n GetResponseAdapterProps,\n OnErrorProps,\n} from '../../contracts';\nimport {\n getDefaultIfUndefined,\n getEventBodyAsBuffer,\n getPathWithQueryStringParams,\n} from '../../core';\n\n//#endregion\n\n/**\n * The type alias to indicate where we get the default value of query string to create the request.\n *\n * @breadcrumb Adapters / AWS / LambdaEdgeAdapter\n * @public\n */\nexport type DefaultQueryString =\n CloudFrontRequestEvent['Records'][number]['cf']['request']['querystring'];\n\n/**\n * The type alias to indicate where we get the default value of path to create the request.\n *\n * @breadcrumb Adapters / AWS / LambdaEdgeAdapter\n * @public\n */\nexport type DefaultForwardPath =\n CloudFrontRequestEvent['Records'][number]['cf']['request']['uri'];\n\n/**\n * Represents the body of the new version of Lambda\\@edge, which uses the `body` property inside `request` as the body (library) of the request.\n *\n * @breadcrumb Adapters / AWS / LambdaEdgeAdapter\n * @public\n */\nexport type NewLambdaEdgeBody =\n CloudFrontRequestEvent['Records'][number]['cf']['request']['body'];\n\n/**\n * Represents the body of the old version of Lambda\\@edge supported by \\@vendia/serverless-express which returns the `data` property within `body` for the body (library) of the request.\n *\n * @breadcrumb Adapters / AWS / LambdaEdgeAdapter\n * @public\n */\nexport type OldLambdaEdgeBody = Concrete<\n CloudFrontRequestEvent['Records'][number]['cf']['request']\n>['body']['data'];\n\n/**\n * The list was created based on {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/edge-functions-restrictions.html | these docs} in the \"Disallowed Headers\" section.\n *\n * @breadcrumb Adapters / AWS / LambdaEdgeAdapter / Constants\n * @public\n */\nexport const DEFAULT_LAMBDA_EDGE_DISALLOWED_HEADERS: (string | RegExp)[] = [\n 'Connection',\n 'Expect',\n 'Keep-Alive',\n 'Proxy-Authenticate',\n 'Proxy-Authorization',\n 'Proxy-Connection',\n 'Trailer',\n 'Upgrade',\n 'X-Accel-Buffering',\n 'X-Accel-Charset',\n 'X-Accel-Limit-Rate',\n 'X-Accel-Redirect',\n /(X-Amz-Cf-)(.*)/gim,\n 'X-Cache',\n /(X-Edge-)(.*)/gim,\n 'X-Forwarded-Proto',\n 'X-Real-IP',\n];\n\n/**\n * The default max response size in bytes of viewer request and viewer response.\n *\n * @defaultValue 1024 * 40 = 40960 = 40KB\n *\n * {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html | Reference}\n *\n * @breadcrumb Adapters / AWS / LambdaEdgeAdapter / Constants\n * @public\n */\nexport const DEFAULT_VIEWER_MAX_RESPONSE_SIZE_IN_BYTES = 1024 * 40;\n\n/**\n * The default max response size in bytes of origin request and origin response.\n *\n * @defaultValue 1024 * 1024 = 1048576 = 1MB\n *\n * {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html | Reference}\n *\n * @breadcrumb Adapters / AWS / LambdaEdgeAdapter / Constants\n * @public\n */\nexport const DEFAULT_ORIGIN_MAX_RESPONSE_SIZE_IN_BYTES = 1024 * 1024;\n\n/**\n * The options to customize the {@link LambdaEdgeAdapter}.\n *\n * @breadcrumb Adapters / AWS / LambdaEdgeAdapter\n * @public\n */\nexport interface LambdaEdgeAdapterOptions {\n /**\n * The max response size in bytes of viewer request and viewer response.\n *\n * {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html | Reference}\n *\n * @defaultValue {@link DEFAULT_VIEWER_MAX_RESPONSE_SIZE_IN_BYTES}\n */\n viewerMaxResponseSizeInBytes?: number;\n\n /**\n * The max response size in bytes of origin request and origin response.\n *\n * {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html | Reference}\n *\n * @defaultValue {@link DEFAULT_ORIGIN_MAX_RESPONSE_SIZE_IN_BYTES}\n */\n originMaxResponseSizeInBytes?: number;\n\n /**\n * The function called when the response size exceed the max limits of the Lambda\\@edge\n *\n * @param response - The response from framework that exceed the limit of Lambda\\@edge\n * @defaultValue undefined\n */\n onResponseSizeExceedLimit?: (\n response: CloudFrontRequestResult,\n ) => CloudFrontRequestResult;\n\n /**\n * Return the path to be used to create a request to the framework\n *\n * @remarks You MUST append the query params from {@link DefaultQueryString}, you can use the helper {@link getPathWithQueryStringParams}.\n *\n * @param event - The event sent by the serverless\n * @defaultValue The value from {@link DefaultForwardPath}\n */\n getPathFromEvent?: (\n event: CloudFrontRequestEvent['Records'][number],\n ) => string;\n\n /**\n * The headers that will be stripped from the headers object because Lambda\\@edge will fail if these headers are passed in the response.\n *\n * @remarks All headers will be compared with other headers using toLowerCase, but for the RegExp, if you modify this list, you must put the flag `/gmi` at the end of the RegExp (ex: `/(X-Amz-Cf-)(.*)/gim`)\n *\n * @defaultValue To get the full list, see {@link DEFAULT_LAMBDA_EDGE_DISALLOWED_HEADERS}.\n */\n disallowedHeaders?: (string | RegExp)[];\n\n /**\n * If you want to change how we check against the header if it should be stripped, you can pass a function to this property.\n *\n * @param header - The header of the response\n * @defaultValue The default method is implemented to test the header against the list {@link LambdaEdgeAdapterOptions.disallowedHeaders}.\n */\n shouldStripHeader?: (header: string) => boolean;\n\n /**\n * By default, the {@link aws-lambda#CloudFrontRequestResult} has the `headers` property, but we also have the headers sent by the framework too.\n * So this setting tells us how to handle this case, if you pass `true` to this property, we will use the framework headers.\n * Otherwise, we will forward the body back to cloudfront without modifying or trying to set the `headers` property inside {@link aws-lambda#CloudFrontRequestResult}.\n *\n * @defaultValue false\n */\n shouldUseHeadersFromFramework?: boolean;\n}\n\n/**\n * The adapter to handle requests from AWS Lambda\\@Edge.\n *\n * This adapter is not fully compatible with Lambda\\@edge supported by \\@vendia/serverless-express, the request body was modified to return {@link NewLambdaEdgeBody} instead {@link OldLambdaEdgeBody}.\n * Also, the response has been modified to return entire body sent by the framework, in this form you MUST return the body from the framework in the format of {@link aws-lambda#CloudFrontRequestResult}.\n * And when we get an error during the forwarding to the framework, we call `resolver.fail` instead of trying to return status 500 like the old implementation was.\n *\n * {@link https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html | Lambda edge docs}\n * {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html | Event Reference}\n *\n * @example\n * ```typescript\n * const getPathFromEvent = () => '/lambda/edge'; // will forward all requests to the same endpoint\n * const adapter = new LambdaEdgeAdapter({ getPathFromEvent });\n * ```\n *\n * @breadcrumb Adapters / AWS / LambdaEdgeAdapter\n * @public\n */\nexport class LambdaEdgeAdapter\n implements\n AdapterContract<CloudFrontRequestEvent, Context, CloudFrontRequestResult>\n{\n //#region Constructor\n\n /**\n * Default constructor\n *\n * @param options - The options to customize the {@link LambdaEdgeAdapter}\n */\n constructor(protected readonly options?: LambdaEdgeAdapterOptions) {\n const disallowedHeaders = getDefaultIfUndefined(\n this.options?.disallowedHeaders,\n DEFAULT_LAMBDA_EDGE_DISALLOWED_HEADERS,\n );\n\n this.cachedDisallowedHeaders = disallowedHeaders.map(disallowedHeader => {\n if (disallowedHeader instanceof RegExp) return disallowedHeader;\n\n return new RegExp(`(${disallowedHeader})`, 'gim');\n });\n }\n\n //#endregion\n\n //#region Protected Properties\n\n /**\n * This property is used to cache the disallowed headers in `RegExp` version, even if you provide a string in `disallowedHeader`, we will cache it in an instance of `RegExp`.\n */\n protected readonly cachedDisallowedHeaders: RegExp[];\n\n //#endregion\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public getAdapterName(): string {\n return LambdaEdgeAdapter.name;\n }\n\n /**\n * {@inheritDoc}\n */\n public canHandle(event: unknown): event is CloudFrontRequestEvent {\n const lambdaEdgeEvent = event as Partial<CloudFrontRequestEvent>;\n\n if (!Array.isArray(lambdaEdgeEvent?.Records)) return false;\n\n const eventType = lambdaEdgeEvent.Records[0]?.cf?.config?.eventType;\n const validEventTypes: CloudFrontEvent['config']['eventType'][] = [\n 'origin-response',\n 'origin-request',\n 'viewer-response',\n 'viewer-request',\n ];\n\n return validEventTypes.includes(eventType);\n }\n\n /**\n * {@inheritDoc}\n */\n public getRequest(event: CloudFrontRequestEvent): AdapterRequest {\n const request = event.Records[0];\n const cloudFrontRequest = request.cf.request;\n\n const method = cloudFrontRequest.method;\n\n const pathFromOptions = this.options?.getPathFromEvent\n ? this.options.getPathFromEvent(request)\n : undefined;\n const defaultPath = getPathWithQueryStringParams(\n cloudFrontRequest.uri,\n cloudFrontRequest.querystring,\n );\n const path = getDefaultIfUndefined(pathFromOptions, defaultPath);\n\n const remoteAddress = cloudFrontRequest.clientIp;\n\n const headers =\n this.getFlattenedHeadersFromCloudfrontRequest(cloudFrontRequest);\n\n let body: Buffer | undefined;\n\n if (cloudFrontRequest.body) {\n const [buffer, contentLength] = getEventBodyAsBuffer(\n JSON.stringify(cloudFrontRequest.body),\n false,\n );\n\n body = buffer;\n headers['content-length'] = contentLength.toString();\n }\n\n const { host } = headers;\n\n return {\n method,\n path,\n headers,\n body,\n remoteAddress,\n host,\n hostname: host,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public getResponse(\n props: GetResponseAdapterProps<CloudFrontRequestEvent>,\n ): CloudFrontRequestResult {\n const response = this.getResponseToLambdaEdge(props);\n const responseToServiceBytes = new TextEncoder().encode(\n JSON.stringify(response),\n ).length;\n\n const isOriginRequestOrResponse = this.isEventTypeOrigin(\n props.event.Records[0].cf.config,\n );\n const maxSizeInBytes = isOriginRequestOrResponse\n ? getDefaultIfUndefined(\n this.options?.originMaxResponseSizeInBytes,\n DEFAULT_ORIGIN_MAX_RESPONSE_SIZE_IN_BYTES,\n )\n : getDefaultIfUndefined(\n this.options?.viewerMaxResponseSizeInBytes,\n DEFAULT_VIEWER_MAX_RESPONSE_SIZE_IN_BYTES,\n );\n\n if (responseToServiceBytes <= maxSizeInBytes) return response;\n\n if (this.options?.onResponseSizeExceedLimit)\n this.options.onResponseSizeExceedLimit(response);\n else {\n props.log.error(\n `SERVERLESS_ADAPTER:LAMBDA_EDGE_ADAPTER: Max response size exceeded: ${responseToServiceBytes} of the max of ${maxSizeInBytes}.`,\n );\n }\n\n return response;\n }\n\n /**\n * {@inheritDoc}\n */\n public onErrorWhileForwarding({\n error,\n delegatedResolver,\n }: OnErrorProps<CloudFrontRequestEvent, CloudFrontRequestResult>): void {\n delegatedResolver.fail(error);\n }\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * Returns the headers with the flattened (non-list) values of the cloudfront request headers\n *\n * @param cloudFrontRequest - The cloudfront request\n */\n protected getFlattenedHeadersFromCloudfrontRequest(\n cloudFrontRequest: CloudFrontRequest,\n ): SingleValueHeaders {\n return Object.keys(cloudFrontRequest.headers).reduce((acc, headerKey) => {\n const headerValue = cloudFrontRequest.headers[headerKey];\n\n acc[headerKey] = headerValue.map(header => header.value).join(',');\n\n return acc;\n }, {} as SingleValueHeaders);\n }\n\n /**\n * Returns the framework response in the format required by the Lambda\\@edge.\n *\n * @param body - The body of the response\n * @param frameworkHeaders - The headers from the framework\n */\n protected getResponseToLambdaEdge({\n body,\n headers: frameworkHeaders,\n }: GetResponseAdapterProps<CloudFrontRequestEvent>): CloudFrontRequestResult {\n const shouldUseHeadersFromFramework = getDefaultIfUndefined(\n this.options?.shouldUseHeadersFromFramework,\n false,\n );\n\n const parsedBody: CloudFrontResultResponse | CloudFrontRequest =\n JSON.parse(body);\n\n if (parsedBody.headers) {\n parsedBody.headers = Object.keys(parsedBody.headers).reduce(\n (acc, header) => {\n if (this.shouldStripHeader(header)) return acc;\n\n acc[header] = parsedBody.headers![header];\n\n return acc;\n },\n {} as CloudFrontHeaders,\n );\n }\n\n if (!shouldUseHeadersFromFramework) return parsedBody;\n\n parsedBody.headers = this.getHeadersForCloudfrontResponse(frameworkHeaders);\n\n return parsedBody;\n }\n\n /**\n * Returns headers in Cloudfront Response format.\n *\n * @param originalHeaders - The original version of the request sent by the framework\n */\n protected getHeadersForCloudfrontResponse(\n originalHeaders: BothValueHeaders,\n ): CloudFrontHeaders {\n return Object.keys(originalHeaders).reduce((acc, headerKey) => {\n if (this.shouldStripHeader(headerKey)) return acc;\n\n if (!acc[headerKey]) acc[headerKey] = [];\n\n const headerValue = originalHeaders[headerKey];\n\n if (!Array.isArray(headerValue)) {\n acc[headerKey].push({\n key: headerKey,\n value: headerValue || '',\n });\n\n return acc;\n }\n\n const headersArray = headerValue.map(value => ({\n key: headerKey,\n value: value,\n }));\n\n acc[headerKey].push(...headersArray);\n\n return acc;\n }, {} as CloudFrontHeaders);\n }\n\n /**\n * Returns the information if we should remove the response header\n *\n * @param headerKey - The header that will be tested\n */\n protected shouldStripHeader(headerKey: string): boolean {\n if (this.options?.shouldStripHeader)\n return this.options.shouldStripHeader(headerKey);\n\n const headerKeyLowerCase = headerKey.toLowerCase();\n\n for (const stripHeaderIf of this.cachedDisallowedHeaders) {\n if (!stripHeaderIf.test(headerKeyLowerCase)) continue;\n\n return true;\n }\n\n return false;\n }\n\n /**\n * Determines whether the event is from origin or is from viewer.\n *\n * @param content - The event sent by AWS or the response sent by the framework\n */\n protected isEventTypeOrigin(content: CloudFrontEvent['config']): boolean {\n return content.eventType.includes('origin');\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lambda-edge.adapter.js","sources":["../../../src/adapters/aws/lambda-edge.adapter.ts"],"sourcesContent":["//#region Imports\n\nimport type { CloudFrontRequest, Context } from 'aws-lambda';\nimport type {\n CloudFrontEvent,\n CloudFrontHeaders,\n CloudFrontResultResponse,\n} from 'aws-lambda/common/cloudfront';\nimport type {\n CloudFrontRequestEvent,\n CloudFrontRequestResult,\n} from 'aws-lambda/trigger/cloudfront-request';\nimport { BothValueHeaders, Concrete, SingleValueHeaders } from '../../@types';\nimport {\n AdapterContract,\n AdapterRequest,\n GetResponseAdapterProps,\n OnErrorProps,\n} from '../../contracts';\nimport {\n getDefaultIfUndefined,\n getEventBodyAsBuffer,\n getPathWithQueryStringParams,\n} from '../../core';\n\n//#endregion\n\n/**\n * The type alias to indicate where we get the default value of query string to create the request.\n *\n * @breadcrumb Adapters / AWS / LambdaEdgeAdapter\n * @public\n */\nexport type DefaultQueryString =\n CloudFrontRequestEvent['Records'][number]['cf']['request']['querystring'];\n\n/**\n * The type alias to indicate where we get the default value of path to create the request.\n *\n * @breadcrumb Adapters / AWS / LambdaEdgeAdapter\n * @public\n */\nexport type DefaultForwardPath =\n CloudFrontRequestEvent['Records'][number]['cf']['request']['uri'];\n\n/**\n * Represents the body of the new version of Lambda\\@edge, which uses the `body` property inside `request` as the body (library) of the request.\n *\n * @breadcrumb Adapters / AWS / LambdaEdgeAdapter\n * @public\n */\nexport type NewLambdaEdgeBody =\n CloudFrontRequestEvent['Records'][number]['cf']['request']['body'];\n\n/**\n * Represents the body of the old version of Lambda\\@edge supported by \\@vendia/serverless-express which returns the `data` property within `body` for the body (library) of the request.\n *\n * @breadcrumb Adapters / AWS / LambdaEdgeAdapter\n * @public\n */\nexport type OldLambdaEdgeBody = Concrete<\n CloudFrontRequestEvent['Records'][number]['cf']['request']\n>['body']['data'];\n\n/**\n * The list was created based on {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/edge-functions-restrictions.html | these docs} in the \"Disallowed Headers\" section.\n *\n * @breadcrumb Adapters / AWS / LambdaEdgeAdapter / Constants\n * @public\n */\nexport const DEFAULT_LAMBDA_EDGE_DISALLOWED_HEADERS: (string | RegExp)[] = [\n 'Connection',\n 'Expect',\n 'Keep-Alive',\n 'Proxy-Authenticate',\n 'Proxy-Authorization',\n 'Proxy-Connection',\n 'Trailer',\n 'Upgrade',\n 'X-Accel-Buffering',\n 'X-Accel-Charset',\n 'X-Accel-Limit-Rate',\n 'X-Accel-Redirect',\n /(X-Amz-Cf-)(.*)/gim,\n 'X-Cache',\n /(X-Edge-)(.*)/gim,\n 'X-Forwarded-Proto',\n 'X-Real-IP',\n];\n\n/**\n * The default max response size in bytes of viewer request and viewer response.\n *\n * @defaultValue 1024 * 40 = 40960 = 40KB\n *\n * {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html | Reference}\n *\n * @breadcrumb Adapters / AWS / LambdaEdgeAdapter / Constants\n * @public\n */\nexport const DEFAULT_VIEWER_MAX_RESPONSE_SIZE_IN_BYTES = 1024 * 40;\n\n/**\n * The default max response size in bytes of origin request and origin response.\n *\n * @defaultValue 1024 * 1024 = 1048576 = 1MB\n *\n * {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html | Reference}\n *\n * @breadcrumb Adapters / AWS / LambdaEdgeAdapter / Constants\n * @public\n */\nexport const DEFAULT_ORIGIN_MAX_RESPONSE_SIZE_IN_BYTES = 1024 * 1024;\n\n/**\n * The options to customize the {@link LambdaEdgeAdapter}.\n *\n * @breadcrumb Adapters / AWS / LambdaEdgeAdapter\n * @public\n */\nexport interface LambdaEdgeAdapterOptions {\n /**\n * The max response size in bytes of viewer request and viewer response.\n *\n * {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html | Reference}\n *\n * @defaultValue {@link DEFAULT_VIEWER_MAX_RESPONSE_SIZE_IN_BYTES}\n */\n viewerMaxResponseSizeInBytes?: number;\n\n /**\n * The max response size in bytes of origin request and origin response.\n *\n * {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html | Reference}\n *\n * @defaultValue {@link DEFAULT_ORIGIN_MAX_RESPONSE_SIZE_IN_BYTES}\n */\n originMaxResponseSizeInBytes?: number;\n\n /**\n * The function called when the response size exceed the max limits of the Lambda\\@edge\n *\n * @param response - The response from framework that exceed the limit of Lambda\\@edge\n * @defaultValue undefined\n */\n onResponseSizeExceedLimit?: (\n response: CloudFrontRequestResult,\n ) => CloudFrontRequestResult;\n\n /**\n * Return the path to be used to create a request to the framework\n *\n * @remarks You MUST append the query params from {@link DefaultQueryString}, you can use the helper {@link getPathWithQueryStringParams}.\n *\n * @param event - The event sent by the serverless\n * @defaultValue The value from {@link DefaultForwardPath}\n */\n getPathFromEvent?: (\n event: CloudFrontRequestEvent['Records'][number],\n ) => string;\n\n /**\n * The headers that will be stripped from the headers object because Lambda\\@edge will fail if these headers are passed in the response.\n *\n * @remarks All headers will be compared with other headers using toLowerCase, but for the RegExp, if you modify this list, you must put the flag `/gmi` at the end of the RegExp (ex: `/(X-Amz-Cf-)(.*)/gim`)\n *\n * @defaultValue To get the full list, see {@link DEFAULT_LAMBDA_EDGE_DISALLOWED_HEADERS}.\n */\n disallowedHeaders?: (string | RegExp)[];\n\n /**\n * If you want to change how we check against the header if it should be stripped, you can pass a function to this property.\n *\n * @param header - The header of the response\n * @defaultValue The default method is implemented to test the header against the list {@link LambdaEdgeAdapterOptions.disallowedHeaders}.\n */\n shouldStripHeader?: (header: string) => boolean;\n\n /**\n * By default, the {@link aws-lambda#CloudFrontRequestResult} has the `headers` property, but we also have the headers sent by the framework too.\n * So this setting tells us how to handle this case, if you pass `true` to this property, we will use the framework headers.\n * Otherwise, we will forward the body back to cloudfront without modifying or trying to set the `headers` property inside {@link aws-lambda#CloudFrontRequestResult}.\n *\n * @defaultValue false\n */\n shouldUseHeadersFromFramework?: boolean;\n}\n\n/**\n * The adapter to handle requests from AWS Lambda\\@Edge.\n *\n * This adapter is not fully compatible with Lambda\\@edge supported by \\@vendia/serverless-express, the request body was modified to return {@link NewLambdaEdgeBody} instead {@link OldLambdaEdgeBody}.\n * Also, the response has been modified to return entire body sent by the framework, in this form you MUST return the body from the framework in the format of {@link aws-lambda#CloudFrontRequestResult}.\n * And when we get an error during the forwarding to the framework, we call `resolver.fail` instead of trying to return status 500 like the old implementation was.\n *\n * {@link https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html | Lambda edge docs}\n * {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html | Event Reference}\n *\n * @example\n * ```typescript\n * const getPathFromEvent = () => '/lambda/edge'; // will forward all requests to the same endpoint\n * const adapter = new LambdaEdgeAdapter({ getPathFromEvent });\n * ```\n *\n * @breadcrumb Adapters / AWS / LambdaEdgeAdapter\n * @public\n */\nexport class LambdaEdgeAdapter\n implements\n AdapterContract<CloudFrontRequestEvent, Context, CloudFrontRequestResult>\n{\n //#region Constructor\n\n /**\n * Default constructor\n *\n * @param options - The options to customize the {@link LambdaEdgeAdapter}\n */\n constructor(protected readonly options?: LambdaEdgeAdapterOptions) {\n const disallowedHeaders = getDefaultIfUndefined(\n this.options?.disallowedHeaders,\n DEFAULT_LAMBDA_EDGE_DISALLOWED_HEADERS,\n );\n\n this.cachedDisallowedHeaders = disallowedHeaders.map(disallowedHeader => {\n if (disallowedHeader instanceof RegExp) return disallowedHeader;\n\n return new RegExp(`(${disallowedHeader})`, 'gim');\n });\n }\n\n //#endregion\n\n //#region Protected Properties\n\n /**\n * This property is used to cache the disallowed headers in `RegExp` version, even if you provide a string in `disallowedHeader`, we will cache it in an instance of `RegExp`.\n */\n protected readonly cachedDisallowedHeaders: RegExp[];\n\n //#endregion\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public getAdapterName(): string {\n return LambdaEdgeAdapter.name;\n }\n\n /**\n * {@inheritDoc}\n */\n public canHandle(event: unknown): event is CloudFrontRequestEvent {\n const lambdaEdgeEvent = event as Partial<CloudFrontRequestEvent>;\n\n if (!Array.isArray(lambdaEdgeEvent?.Records)) return false;\n\n const eventType = lambdaEdgeEvent.Records[0]?.cf?.config?.eventType;\n const validEventTypes: CloudFrontEvent['config']['eventType'][] = [\n 'origin-response',\n 'origin-request',\n 'viewer-response',\n 'viewer-request',\n ];\n\n return validEventTypes.includes(eventType);\n }\n\n /**\n * {@inheritDoc}\n */\n public getRequest(event: CloudFrontRequestEvent): AdapterRequest {\n const request = event.Records[0];\n const cloudFrontRequest = request.cf.request;\n\n const method = cloudFrontRequest.method;\n\n const pathFromOptions = this.options?.getPathFromEvent\n ? this.options.getPathFromEvent(request)\n : undefined;\n const defaultPath = getPathWithQueryStringParams(\n cloudFrontRequest.uri,\n cloudFrontRequest.querystring,\n );\n const path = getDefaultIfUndefined(pathFromOptions, defaultPath);\n\n const remoteAddress = cloudFrontRequest.clientIp;\n\n const headers =\n this.getFlattenedHeadersFromCloudfrontRequest(cloudFrontRequest);\n\n let body: Buffer | undefined;\n\n if (cloudFrontRequest.body) {\n const [buffer, contentLength] = getEventBodyAsBuffer(\n JSON.stringify(cloudFrontRequest.body),\n false,\n );\n\n body = buffer;\n headers['content-length'] = contentLength.toString();\n }\n\n const { host } = headers;\n\n return {\n method,\n path,\n headers,\n body,\n remoteAddress,\n host,\n hostname: host,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public getResponse(\n props: GetResponseAdapterProps<CloudFrontRequestEvent>,\n ): CloudFrontRequestResult {\n const response = this.getResponseToLambdaEdge(props);\n const responseToServiceBytes = new TextEncoder().encode(\n JSON.stringify(response),\n ).length;\n\n const isOriginRequestOrResponse = this.isEventTypeOrigin(\n props.event.Records[0].cf.config,\n );\n const maxSizeInBytes = isOriginRequestOrResponse\n ? getDefaultIfUndefined(\n this.options?.originMaxResponseSizeInBytes,\n DEFAULT_ORIGIN_MAX_RESPONSE_SIZE_IN_BYTES,\n )\n : getDefaultIfUndefined(\n this.options?.viewerMaxResponseSizeInBytes,\n DEFAULT_VIEWER_MAX_RESPONSE_SIZE_IN_BYTES,\n );\n\n if (responseToServiceBytes <= maxSizeInBytes) return response;\n\n if (this.options?.onResponseSizeExceedLimit)\n this.options.onResponseSizeExceedLimit(response);\n else {\n props.log.error(\n `SERVERLESS_ADAPTER:LAMBDA_EDGE_ADAPTER: Max response size exceeded: ${responseToServiceBytes} of the max of ${maxSizeInBytes}.`,\n );\n }\n\n return response;\n }\n\n /**\n * {@inheritDoc}\n */\n public onErrorWhileForwarding({\n error,\n delegatedResolver,\n }: OnErrorProps<CloudFrontRequestEvent, CloudFrontRequestResult>): void {\n delegatedResolver.fail(error);\n }\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * Returns the headers with the flattened (non-list) values of the cloudfront request headers\n *\n * @param cloudFrontRequest - The cloudfront request\n */\n protected getFlattenedHeadersFromCloudfrontRequest(\n cloudFrontRequest: CloudFrontRequest,\n ): SingleValueHeaders {\n return Object.keys(cloudFrontRequest.headers).reduce((acc, headerKey) => {\n const headerValue = cloudFrontRequest.headers[headerKey];\n\n acc[headerKey] = headerValue.map(header => header.value).join(',');\n\n return acc;\n }, {} as SingleValueHeaders);\n }\n\n /**\n * Returns the framework response in the format required by the Lambda\\@edge.\n *\n * @param body - The body of the response\n * @param frameworkHeaders - The headers from the framework\n */\n protected getResponseToLambdaEdge({\n body,\n headers: frameworkHeaders,\n }: GetResponseAdapterProps<CloudFrontRequestEvent>): CloudFrontRequestResult {\n const shouldUseHeadersFromFramework = getDefaultIfUndefined(\n this.options?.shouldUseHeadersFromFramework,\n false,\n );\n\n const parsedBody: CloudFrontResultResponse | CloudFrontRequest =\n JSON.parse(body);\n\n if (parsedBody.headers) {\n parsedBody.headers = Object.keys(parsedBody.headers).reduce(\n (acc, header) => {\n if (this.shouldStripHeader(header)) return acc;\n\n acc[header] = parsedBody.headers![header];\n\n return acc;\n },\n {} as CloudFrontHeaders,\n );\n }\n\n if (!shouldUseHeadersFromFramework) return parsedBody;\n\n parsedBody.headers = this.getHeadersForCloudfrontResponse(frameworkHeaders);\n\n return parsedBody;\n }\n\n /**\n * Returns headers in Cloudfront Response format.\n *\n * @param originalHeaders - The original version of the request sent by the framework\n */\n protected getHeadersForCloudfrontResponse(\n originalHeaders: BothValueHeaders,\n ): CloudFrontHeaders {\n return Object.keys(originalHeaders).reduce((acc, headerKey) => {\n if (this.shouldStripHeader(headerKey)) return acc;\n\n if (!acc[headerKey]) acc[headerKey] = [];\n\n const headerValue = originalHeaders[headerKey];\n\n if (!Array.isArray(headerValue)) {\n acc[headerKey].push({\n key: headerKey,\n value: headerValue || '',\n });\n\n return acc;\n }\n\n const headersArray = headerValue.map(value => ({\n key: headerKey,\n value: value,\n }));\n\n acc[headerKey].push(...headersArray);\n\n return acc;\n }, {} as CloudFrontHeaders);\n }\n\n /**\n * Returns the information if we should remove the response header\n *\n * @param headerKey - The header that will be tested\n */\n protected shouldStripHeader(headerKey: string): boolean {\n if (this.options?.shouldStripHeader)\n return this.options.shouldStripHeader(headerKey);\n\n const headerKeyLowerCase = headerKey.toLowerCase();\n\n for (const stripHeaderIf of this.cachedDisallowedHeaders) {\n if (!stripHeaderIf.test(headerKeyLowerCase)) continue;\n\n return true;\n }\n\n return false;\n }\n\n /**\n * Determines whether the event is from origin or is from viewer.\n *\n * @param content - The event sent by AWS or the response sent by the framework\n */\n protected isEventTypeOrigin(content: CloudFrontEvent['config']): boolean {\n return content.eventType.includes('origin');\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-lambda-edge.adapter.js","sources":["../../../src/adapters/aws/request-lambda-edge.adapter.ts"],"sourcesContent":["//#region Imports\n\nimport type { CloudFrontRequest, Context } from 'aws-lambda';\nimport type {\n CloudFrontHeaders,\n CloudFrontResultResponse,\n} from 'aws-lambda/common/cloudfront';\nimport type {\n CloudFrontRequestEvent,\n CloudFrontRequestResult,\n} from 'aws-lambda/trigger/cloudfront-request';\nimport { BothValueHeaders, SingleValueHeaders } from '../../@types';\nimport {\n AdapterContract,\n AdapterRequest,\n GetResponseAdapterProps,\n OnErrorProps,\n} from '../../contracts';\nimport {\n StripBasePathFn,\n buildStripBasePath,\n getDefaultIfUndefined,\n getEventBodyAsBuffer,\n getPathWithQueryStringParams,\n} from '../../core';\nimport {\n DEFAULT_LAMBDA_EDGE_DISALLOWED_HEADERS,\n DEFAULT_ORIGIN_MAX_RESPONSE_SIZE_IN_BYTES,\n DEFAULT_VIEWER_MAX_RESPONSE_SIZE_IN_BYTES,\n} from './lambda-edge.adapter';\n\n//#endregion\n\n//#endregion\n\n/**\n * The options to customize the {@link RequestLambdaEdgeAdapter}.\n *\n * @breadcrumb Adapters / AWS / RequestLambdaEdgeAdapter\n * @public\n */\nexport interface RequestLambdaEdgeAdapterOptions {\n /**\n * Strip base path for custom paths, like `/api`.\n *\n * @defaultValue ''\n */\n stripBasePath?: string;\n\n /**\n * The max response size in bytes of viewer request and viewer response.\n *\n * {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html | Reference}\n *\n * @defaultValue {@link DEFAULT_VIEWER_MAX_RESPONSE_SIZE_IN_BYTES}\n */\n viewerMaxResponseSizeInBytes?: number;\n\n /**\n * The max response size in bytes of origin request and origin response.\n *\n * {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html | Reference}\n *\n * @defaultValue {@link DEFAULT_ORIGIN_MAX_RESPONSE_SIZE_IN_BYTES}\n */\n originMaxResponseSizeInBytes?: number;\n\n /**\n * The function called when the response size exceed the max limits of the Lambda\\@edge\n *\n * @param response - The response from framework that exceed the limit of Lambda\\@edge\n * @defaultValue undefined\n */\n onResponseSizeExceedLimit?: (\n response: CloudFrontRequestResult,\n ) => CloudFrontRequestResult;\n\n /**\n * The headers that will be stripped from the headers object because Lambda\\@edge will fail if these headers are passed in the response.\n *\n * @remarks All headers will be compared with other headers using toLowerCase, but for the RegExp, if you modify this list, you must put the flag `/gmi` at the end of the RegExp (ex: `/(X-Amz-Cf-)(.*)/gim`)\n *\n * @defaultValue To get the full list, see {@link DEFAULT_LAMBDA_EDGE_DISALLOWED_HEADERS}.\n */\n disallowedHeaders?: (string | RegExp)[];\n\n /**\n * If you want to change how we check against the header if it should be stripped, you can pass a function to this property.\n *\n * @param header - The header of the response\n * @defaultValue The default method is implemented to test the header against the list {@link RequestLambdaEdgeAdapterOptions.disallowedHeaders}.\n */\n shouldStripHeader?: (header: string) => boolean;\n}\n\n/**\n * The adapter to handle requests from AWS Lambda\\@Edge of the type Viewer Request.\n *\n * The idea of this Adapter is to you be able to expose your framework to the Edge, like when you build for Cloudfront.\n *\n * {@link https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html | Lambda edge docs}\n * {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html | Event Reference}\n *\n * @example\n * ```typescript\n * const stripBasePath = '/api'; // in case you have configure the cloudfront to forward the path /api to your lambda\n * const adapter = new RequestLambdaEdgeAdapter({ stripBasePath });\n * ```\n *\n * @breadcrumb Adapters / AWS / RequestLambdaEdgeAdapter\n * @public\n */\nexport class RequestLambdaEdgeAdapter\n implements\n AdapterContract<CloudFrontRequestEvent, Context, CloudFrontResultResponse>\n{\n //#region Constructor\n\n /**\n * Default constructor\n *\n * @param options - The options to customize the {@link RequestLambdaEdgeAdapter}\n */\n constructor(protected readonly options?: RequestLambdaEdgeAdapterOptions) {\n this.stripPathFn = buildStripBasePath(this.options?.stripBasePath);\n\n const disallowedHeaders = getDefaultIfUndefined(\n this.options?.disallowedHeaders,\n DEFAULT_LAMBDA_EDGE_DISALLOWED_HEADERS,\n );\n\n this.cachedDisallowedHeaders = disallowedHeaders.map(disallowedHeader => {\n if (disallowedHeader instanceof RegExp) return disallowedHeader;\n\n return new RegExp(`(${disallowedHeader})`, 'gim');\n });\n }\n\n //#endregion\n\n //#region Protected Properties\n\n /**\n * Strip base path function\n */\n protected readonly stripPathFn: StripBasePathFn;\n\n /**\n * This property is used to cache the disallowed headers in `RegExp` version, even if you provide a string in `disallowedHeader`, we will cache it in an instance of `RegExp`.\n */\n protected readonly cachedDisallowedHeaders: RegExp[];\n\n //#endregion\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public getAdapterName(): string {\n return RequestLambdaEdgeAdapter.name;\n }\n\n /**\n * {@inheritDoc}\n */\n public canHandle(event: unknown): event is CloudFrontRequestEvent {\n const lambdaEdgeEvent = event as Partial<CloudFrontRequestEvent>;\n\n if (!Array.isArray(lambdaEdgeEvent?.Records)) return false;\n\n const eventType = lambdaEdgeEvent.Records[0]?.cf?.config?.eventType;\n\n return eventType === 'viewer-request' || eventType === 'origin-request';\n }\n\n /**\n * {@inheritDoc}\n */\n public getRequest(event: CloudFrontRequestEvent): AdapterRequest {\n const request = event.Records[0];\n const cloudFrontRequest = request.cf.request;\n\n const method = cloudFrontRequest.method;\n\n const path = this.stripPathFn(\n getPathWithQueryStringParams(\n cloudFrontRequest.uri,\n cloudFrontRequest.querystring,\n ),\n );\n const remoteAddress = cloudFrontRequest.clientIp;\n\n const headers =\n this.getFlattenedHeadersFromCloudfrontRequest(cloudFrontRequest);\n\n let body: Buffer | undefined;\n\n if (cloudFrontRequest.body) {\n const [buffer, contentLength] = getEventBodyAsBuffer(\n cloudFrontRequest.body.data,\n cloudFrontRequest.body.encoding === 'base64',\n );\n\n body = buffer;\n headers['content-length'] = contentLength.toString();\n }\n\n const { host } = headers;\n\n return {\n method,\n path,\n headers,\n body,\n remoteAddress,\n host,\n hostname: host,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public getResponse({\n body,\n headers: frameworkHeaders,\n isBase64Encoded,\n statusCode,\n log,\n event,\n }: GetResponseAdapterProps<CloudFrontRequestEvent>): CloudFrontResultResponse {\n const headers = this.getHeadersForCloudfrontResponse(frameworkHeaders);\n\n const maxSizeInBytes =\n event.Records[0].cf.config.eventType === 'origin-request'\n ? getDefaultIfUndefined(\n this.options?.originMaxResponseSizeInBytes,\n DEFAULT_ORIGIN_MAX_RESPONSE_SIZE_IN_BYTES,\n )\n : getDefaultIfUndefined(\n this.options?.viewerMaxResponseSizeInBytes,\n DEFAULT_VIEWER_MAX_RESPONSE_SIZE_IN_BYTES,\n );\n\n const response: CloudFrontResultResponse = {\n body,\n status: statusCode.toString(),\n bodyEncoding: isBase64Encoded ? 'base64' : 'text',\n headers,\n };\n\n // probably is not correctly accurate, but it's a good approximation\n const bodyLength = body.length;\n\n if (bodyLength <= maxSizeInBytes) return response;\n\n if (this.options?.onResponseSizeExceedLimit)\n this.options.onResponseSizeExceedLimit(response);\n else {\n log.error(\n `SERVERLESS_ADAPTER:LAMBDA_EDGE_ADAPTER: Max response size exceeded: ${bodyLength} of the max of ${maxSizeInBytes}.`,\n );\n }\n\n return response;\n }\n\n /**\n * {@inheritDoc}\n */\n public onErrorWhileForwarding({\n error,\n delegatedResolver,\n respondWithErrors,\n log,\n event,\n }: OnErrorProps<CloudFrontRequestEvent, CloudFrontRequestResult>): void {\n const body = respondWithErrors ? error.stack : '';\n const errorResponse = this.getResponse({\n event,\n statusCode: 500,\n body: body || '',\n headers: {},\n isBase64Encoded: false,\n log,\n });\n\n delegatedResolver.succeed(errorResponse);\n }\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * Returns the headers with the flattened (non-list) values of the cloudfront request headers\n *\n * @param cloudFrontRequest - The cloudfront request\n */\n protected getFlattenedHeadersFromCloudfrontRequest(\n cloudFrontRequest: CloudFrontRequest,\n ): SingleValueHeaders {\n return Object.keys(cloudFrontRequest.headers).reduce((acc, headerKey) => {\n const headerValue = cloudFrontRequest.headers[headerKey];\n\n if (headerValue.length === 1) acc[headerKey] = headerValue[0].value;\n else acc[headerKey] = headerValue.map(header => header.value).join(',');\n\n return acc;\n }, {} as SingleValueHeaders);\n }\n\n /**\n * Returns headers in Cloudfront Response format.\n *\n * @param originalHeaders - The original version of the request sent by the framework\n */\n protected getHeadersForCloudfrontResponse(\n originalHeaders: BothValueHeaders,\n ): CloudFrontHeaders {\n return Object.keys(originalHeaders).reduce((acc, headerKey) => {\n if (this.shouldStripHeader(headerKey)) return acc;\n\n const lowercaseHeaderKey = headerKey.toLowerCase();\n\n if (!acc[lowercaseHeaderKey]) acc[lowercaseHeaderKey] = [];\n\n const headerValue = originalHeaders[headerKey];\n\n if (!Array.isArray(headerValue)) {\n acc[lowercaseHeaderKey].push({\n key: headerKey,\n value: headerValue || '',\n });\n\n return acc;\n }\n\n const headersArray = headerValue.map(value => ({\n key: headerKey,\n value: value,\n }));\n\n acc[lowercaseHeaderKey].push(...headersArray);\n\n return acc;\n }, {} as CloudFrontHeaders);\n }\n\n /**\n * Returns the information if we should remove the response header\n *\n * @param headerKey - The header that will be tested\n */\n protected shouldStripHeader(headerKey: string): boolean {\n if (this.options?.shouldStripHeader)\n return this.options.shouldStripHeader(headerKey);\n\n const headerKeyLowerCase = headerKey.toLowerCase();\n\n for (const stripHeaderIf of this.cachedDisallowedHeaders) {\n if (!stripHeaderIf.test(headerKeyLowerCase)) continue;\n\n return true;\n }\n\n return false;\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"request-lambda-edge.adapter.js","sources":["../../../src/adapters/aws/request-lambda-edge.adapter.ts"],"sourcesContent":["//#region Imports\n\nimport type { CloudFrontRequest, Context } from 'aws-lambda';\nimport type {\n CloudFrontHeaders,\n CloudFrontResultResponse,\n} from 'aws-lambda/common/cloudfront';\nimport type {\n CloudFrontRequestEvent,\n CloudFrontRequestResult,\n} from 'aws-lambda/trigger/cloudfront-request';\nimport { BothValueHeaders, SingleValueHeaders } from '../../@types';\nimport {\n AdapterContract,\n AdapterRequest,\n GetResponseAdapterProps,\n OnErrorProps,\n} from '../../contracts';\nimport {\n StripBasePathFn,\n buildStripBasePath,\n getDefaultIfUndefined,\n getEventBodyAsBuffer,\n getPathWithQueryStringParams,\n} from '../../core';\nimport {\n DEFAULT_LAMBDA_EDGE_DISALLOWED_HEADERS,\n DEFAULT_ORIGIN_MAX_RESPONSE_SIZE_IN_BYTES,\n DEFAULT_VIEWER_MAX_RESPONSE_SIZE_IN_BYTES,\n} from './lambda-edge.adapter';\n\n//#endregion\n\n//#endregion\n\n/**\n * The options to customize the {@link RequestLambdaEdgeAdapter}.\n *\n * @breadcrumb Adapters / AWS / RequestLambdaEdgeAdapter\n * @public\n */\nexport interface RequestLambdaEdgeAdapterOptions {\n /**\n * Strip base path for custom paths, like `/api`.\n *\n * @defaultValue ''\n */\n stripBasePath?: string;\n\n /**\n * The max response size in bytes of viewer request and viewer response.\n *\n * {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html | Reference}\n *\n * @defaultValue {@link DEFAULT_VIEWER_MAX_RESPONSE_SIZE_IN_BYTES}\n */\n viewerMaxResponseSizeInBytes?: number;\n\n /**\n * The max response size in bytes of origin request and origin response.\n *\n * {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html | Reference}\n *\n * @defaultValue {@link DEFAULT_ORIGIN_MAX_RESPONSE_SIZE_IN_BYTES}\n */\n originMaxResponseSizeInBytes?: number;\n\n /**\n * The function called when the response size exceed the max limits of the Lambda\\@edge\n *\n * @param response - The response from framework that exceed the limit of Lambda\\@edge\n * @defaultValue undefined\n */\n onResponseSizeExceedLimit?: (\n response: CloudFrontRequestResult,\n ) => CloudFrontRequestResult;\n\n /**\n * The headers that will be stripped from the headers object because Lambda\\@edge will fail if these headers are passed in the response.\n *\n * @remarks All headers will be compared with other headers using toLowerCase, but for the RegExp, if you modify this list, you must put the flag `/gmi` at the end of the RegExp (ex: `/(X-Amz-Cf-)(.*)/gim`)\n *\n * @defaultValue To get the full list, see {@link DEFAULT_LAMBDA_EDGE_DISALLOWED_HEADERS}.\n */\n disallowedHeaders?: (string | RegExp)[];\n\n /**\n * If you want to change how we check against the header if it should be stripped, you can pass a function to this property.\n *\n * @param header - The header of the response\n * @defaultValue The default method is implemented to test the header against the list {@link RequestLambdaEdgeAdapterOptions.disallowedHeaders}.\n */\n shouldStripHeader?: (header: string) => boolean;\n}\n\n/**\n * The adapter to handle requests from AWS Lambda\\@Edge of the type Viewer Request.\n *\n * The idea of this Adapter is to you be able to expose your framework to the Edge, like when you build for Cloudfront.\n *\n * {@link https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html | Lambda edge docs}\n * {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html | Event Reference}\n *\n * @example\n * ```typescript\n * const stripBasePath = '/api'; // in case you have configure the cloudfront to forward the path /api to your lambda\n * const adapter = new RequestLambdaEdgeAdapter({ stripBasePath });\n * ```\n *\n * @breadcrumb Adapters / AWS / RequestLambdaEdgeAdapter\n * @public\n */\nexport class RequestLambdaEdgeAdapter\n implements\n AdapterContract<CloudFrontRequestEvent, Context, CloudFrontResultResponse>\n{\n //#region Constructor\n\n /**\n * Default constructor\n *\n * @param options - The options to customize the {@link RequestLambdaEdgeAdapter}\n */\n constructor(protected readonly options?: RequestLambdaEdgeAdapterOptions) {\n this.stripPathFn = buildStripBasePath(this.options?.stripBasePath);\n\n const disallowedHeaders = getDefaultIfUndefined(\n this.options?.disallowedHeaders,\n DEFAULT_LAMBDA_EDGE_DISALLOWED_HEADERS,\n );\n\n this.cachedDisallowedHeaders = disallowedHeaders.map(disallowedHeader => {\n if (disallowedHeader instanceof RegExp) return disallowedHeader;\n\n return new RegExp(`(${disallowedHeader})`, 'gim');\n });\n }\n\n //#endregion\n\n //#region Protected Properties\n\n /**\n * Strip base path function\n */\n protected readonly stripPathFn: StripBasePathFn;\n\n /**\n * This property is used to cache the disallowed headers in `RegExp` version, even if you provide a string in `disallowedHeader`, we will cache it in an instance of `RegExp`.\n */\n protected readonly cachedDisallowedHeaders: RegExp[];\n\n //#endregion\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public getAdapterName(): string {\n return RequestLambdaEdgeAdapter.name;\n }\n\n /**\n * {@inheritDoc}\n */\n public canHandle(event: unknown): event is CloudFrontRequestEvent {\n const lambdaEdgeEvent = event as Partial<CloudFrontRequestEvent>;\n\n if (!Array.isArray(lambdaEdgeEvent?.Records)) return false;\n\n const eventType = lambdaEdgeEvent.Records[0]?.cf?.config?.eventType;\n\n return eventType === 'viewer-request' || eventType === 'origin-request';\n }\n\n /**\n * {@inheritDoc}\n */\n public getRequest(event: CloudFrontRequestEvent): AdapterRequest {\n const request = event.Records[0];\n const cloudFrontRequest = request.cf.request;\n\n const method = cloudFrontRequest.method;\n\n const path = this.stripPathFn(\n getPathWithQueryStringParams(\n cloudFrontRequest.uri,\n cloudFrontRequest.querystring,\n ),\n );\n const remoteAddress = cloudFrontRequest.clientIp;\n\n const headers =\n this.getFlattenedHeadersFromCloudfrontRequest(cloudFrontRequest);\n\n let body: Buffer | undefined;\n\n if (cloudFrontRequest.body) {\n const [buffer, contentLength] = getEventBodyAsBuffer(\n cloudFrontRequest.body.data,\n cloudFrontRequest.body.encoding === 'base64',\n );\n\n body = buffer;\n headers['content-length'] = contentLength.toString();\n }\n\n const { host } = headers;\n\n return {\n method,\n path,\n headers,\n body,\n remoteAddress,\n host,\n hostname: host,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public getResponse({\n body,\n headers: frameworkHeaders,\n isBase64Encoded,\n statusCode,\n log,\n event,\n }: GetResponseAdapterProps<CloudFrontRequestEvent>): CloudFrontResultResponse {\n const headers = this.getHeadersForCloudfrontResponse(frameworkHeaders);\n\n const maxSizeInBytes =\n event.Records[0].cf.config.eventType === 'origin-request'\n ? getDefaultIfUndefined(\n this.options?.originMaxResponseSizeInBytes,\n DEFAULT_ORIGIN_MAX_RESPONSE_SIZE_IN_BYTES,\n )\n : getDefaultIfUndefined(\n this.options?.viewerMaxResponseSizeInBytes,\n DEFAULT_VIEWER_MAX_RESPONSE_SIZE_IN_BYTES,\n );\n\n const response: CloudFrontResultResponse = {\n body,\n status: statusCode.toString(),\n bodyEncoding: isBase64Encoded ? 'base64' : 'text',\n headers,\n };\n\n // probably is not correctly accurate, but it's a good approximation\n const bodyLength = body.length;\n\n if (bodyLength <= maxSizeInBytes) return response;\n\n if (this.options?.onResponseSizeExceedLimit)\n this.options.onResponseSizeExceedLimit(response);\n else {\n log.error(\n `SERVERLESS_ADAPTER:LAMBDA_EDGE_ADAPTER: Max response size exceeded: ${bodyLength} of the max of ${maxSizeInBytes}.`,\n );\n }\n\n return response;\n }\n\n /**\n * {@inheritDoc}\n */\n public onErrorWhileForwarding({\n error,\n delegatedResolver,\n respondWithErrors,\n log,\n event,\n }: OnErrorProps<CloudFrontRequestEvent, CloudFrontRequestResult>): void {\n const body = respondWithErrors ? error.stack : '';\n const errorResponse = this.getResponse({\n event,\n statusCode: 500,\n body: body || '',\n headers: {},\n isBase64Encoded: false,\n log,\n });\n\n delegatedResolver.succeed(errorResponse);\n }\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * Returns the headers with the flattened (non-list) values of the cloudfront request headers\n *\n * @param cloudFrontRequest - The cloudfront request\n */\n protected getFlattenedHeadersFromCloudfrontRequest(\n cloudFrontRequest: CloudFrontRequest,\n ): SingleValueHeaders {\n return Object.keys(cloudFrontRequest.headers).reduce((acc, headerKey) => {\n const headerValue = cloudFrontRequest.headers[headerKey];\n\n if (headerValue.length === 1) acc[headerKey] = headerValue[0].value;\n else acc[headerKey] = headerValue.map(header => header.value).join(',');\n\n return acc;\n }, {} as SingleValueHeaders);\n }\n\n /**\n * Returns headers in Cloudfront Response format.\n *\n * @param originalHeaders - The original version of the request sent by the framework\n */\n protected getHeadersForCloudfrontResponse(\n originalHeaders: BothValueHeaders,\n ): CloudFrontHeaders {\n return Object.keys(originalHeaders).reduce((acc, headerKey) => {\n if (this.shouldStripHeader(headerKey)) return acc;\n\n const lowercaseHeaderKey = headerKey.toLowerCase();\n\n if (!acc[lowercaseHeaderKey]) acc[lowercaseHeaderKey] = [];\n\n const headerValue = originalHeaders[headerKey];\n\n if (!Array.isArray(headerValue)) {\n acc[lowercaseHeaderKey].push({\n key: headerKey,\n value: headerValue || '',\n });\n\n return acc;\n }\n\n const headersArray = headerValue.map(value => ({\n key: headerKey,\n value: value,\n }));\n\n acc[lowercaseHeaderKey].push(...headersArray);\n\n return acc;\n }, {} as CloudFrontHeaders);\n }\n\n /**\n * Returns the information if we should remove the response header\n *\n * @param headerKey - The header that will be tested\n */\n protected shouldStripHeader(headerKey: string): boolean {\n if (this.options?.shouldStripHeader)\n return this.options.shouldStripHeader(headerKey);\n\n const headerKeyLowerCase = headerKey.toLowerCase();\n\n for (const stripHeaderIf of this.cachedDisallowedHeaders) {\n if (!stripHeaderIf.test(headerKeyLowerCase)) continue;\n\n return true;\n }\n\n return false;\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -52,7 +52,7 @@ export declare class HttpTriggerV4Adapter implements AdapterContract<HttpRequest
|
|
|
52
52
|
/**
|
|
53
53
|
* {@inheritDoc}
|
|
54
54
|
*/
|
|
55
|
-
getResponse({ body,
|
|
55
|
+
getResponse({ body, statusCode, headers: originalHeaders, }: GetResponseAdapterProps<HttpRequest>): HttpResponseSimple;
|
|
56
56
|
/**
|
|
57
57
|
* {@inheritDoc}
|
|
58
58
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-trigger-v4.adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/azure/http-trigger-v4.adapter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,OAAO,EACP,MAAM,EACN,WAAW,EACX,kBAAkB,EACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EACL,eAAe,EACf,cAAc,EACd,uBAAuB,EACvB,YAAY,EACb,MAAM,iBAAiB,CAAC;AAUzB;;;;;GAKG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,oBACX,YAAW,eAAe,CAAC,WAAW,EAAE,OAAO,EAAE,kBAAkB,CAAC;IASxD,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC;IALvC;;;;OAIG;gBAC4B,OAAO,CAAC,yCAA6B;IAMpE;;OAEG;IACI,cAAc,IAAI,MAAM;IAI/B;;OAEG;IACI,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO;IAmB3D;;OAEG;IACI,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,cAAc;IA6BrD;;OAEG;IACI,WAAW,CAAC,EACjB,IAAI,EACJ,
|
|
1
|
+
{"version":3,"file":"http-trigger-v4.adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/azure/http-trigger-v4.adapter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,OAAO,EACP,MAAM,EACN,WAAW,EACX,kBAAkB,EACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EACL,eAAe,EACf,cAAc,EACd,uBAAuB,EACvB,YAAY,EACb,MAAM,iBAAiB,CAAC;AAUzB;;;;;GAKG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,oBACX,YAAW,eAAe,CAAC,WAAW,EAAE,OAAO,EAAE,kBAAkB,CAAC;IASxD,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC;IALvC;;;;OAIG;gBAC4B,OAAO,CAAC,yCAA6B;IAMpE;;OAEG;IACI,cAAc,IAAI,MAAM;IAI/B;;OAEG;IACI,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO;IAmB3D;;OAEG;IACI,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,cAAc;IA6BrD;;OAEG;IACI,WAAW,CAAC,EACjB,IAAI,EACJ,UAAU,EACV,OAAO,EAAE,eAAe,GACzB,EAAE,uBAAuB,CAAC,WAAW,CAAC,GAAG,kBAAkB;IAoB5D;;OAEG;IACI,sBAAsB,CAAC,EAC5B,KAAK,EACL,iBAAiB,EACjB,KAAK,EACL,iBAAiB,EACjB,GAAG,GACJ,EAAE,YAAY,CAAC,WAAW,EAAE,kBAAkB,CAAC,GAAG,IAAI;IAkBvD;;;;OAIG;IACH,SAAS,CAAC,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM;IAiBtD;;;;OAIG;IACH,SAAS,CAAC,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,EAAE;IAYzE;;;;;;OAMG;IACH,SAAS,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;CA8C9C"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var __defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod),http_trigger_v4_adapter_exports={};__export(http_trigger_v4_adapter_exports,{HttpTriggerV4Adapter:()=>HttpTriggerV4Adapter});module.exports=__toCommonJS(http_trigger_v4_adapter_exports);var import_url=require("url"),import_core=require("../../core");class HttpTriggerV4Adapter{constructor(options){this.options=options}getAdapterName(){return HttpTriggerV4Adapter.name}canHandle(event,context){const maybeEvent=event,maybeContext=context;return!!(maybeEvent&&maybeEvent.method&&maybeEvent.headers&&maybeEvent.url&&maybeEvent.query&&maybeContext&&maybeContext.traceContext&&maybeContext.bindingDefinitions&&maybeContext.log&&maybeContext.log.info&&maybeContext.bindingData)}getRequest(event){const path=this.getPathFromEvent(event),method=event.method,headers=(0,import_core.getFlattenedHeadersMap)(event.headers,",",!0);let body;if(event.body){const[bufferBody,contentLength]=(0,import_core.getEventBodyAsBuffer)(event.rawBody,!1);body=bufferBody,headers["content-length"]=String(contentLength)}const remoteAddress=headers["x-forwarded-for"];return{method,path,headers,remoteAddress,body}}getResponse({body,
|
|
1
|
+
"use strict";var __defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod),http_trigger_v4_adapter_exports={};__export(http_trigger_v4_adapter_exports,{HttpTriggerV4Adapter:()=>HttpTriggerV4Adapter});module.exports=__toCommonJS(http_trigger_v4_adapter_exports);var import_url=require("url"),import_core=require("../../core");class HttpTriggerV4Adapter{constructor(options){this.options=options}getAdapterName(){return HttpTriggerV4Adapter.name}canHandle(event,context){const maybeEvent=event,maybeContext=context;return!!(maybeEvent&&maybeEvent.method&&maybeEvent.headers&&maybeEvent.url&&maybeEvent.query&&maybeContext&&maybeContext.traceContext&&maybeContext.bindingDefinitions&&maybeContext.log&&maybeContext.log.info&&maybeContext.bindingData)}getRequest(event){const path=this.getPathFromEvent(event),method=event.method,headers=(0,import_core.getFlattenedHeadersMap)(event.headers,",",!0);let body;if(event.body){const[bufferBody,contentLength]=(0,import_core.getEventBodyAsBuffer)(event.rawBody,!1);body=bufferBody,headers["content-length"]=String(contentLength)}const remoteAddress=headers["x-forwarded-for"];return{method,path,headers,remoteAddress,body}}getResponse({body,statusCode,headers:originalHeaders}){const headers=(0,import_core.getFlattenedHeadersMap)(originalHeaders,",",!0),cookies=this.getAzureCookiesFromHeaders(originalHeaders);return headers["set-cookie"]&&delete headers["set-cookie"],{body,statusCode,headers,enableContentNegotiation:!1,cookies}}onErrorWhileForwarding({error,respondWithErrors,event,delegatedResolver,log}){const body=respondWithErrors?error.stack:"",errorResponse=this.getResponse({event,statusCode:500,body:body||"",headers:{},isBase64Encoded:!1,log});delegatedResolver.succeed(errorResponse)}getPathFromEvent(event){var _a;const stripBasePath=(0,import_core.getDefaultIfUndefined)((_a=this.options)==null?void 0:_a.stripBasePath,""),originalPath=new import_url.URL(event.url).pathname,replaceRegex=new RegExp(`^${stripBasePath}`),path=originalPath.replace(replaceRegex,""),queryParams=event.query;return(0,import_core.getPathWithQueryStringParams)(path,queryParams)}getAzureCookiesFromHeaders(headers){const setCookie=headers["set-cookie"];return(Array.isArray(setCookie)?setCookie:setCookie?[setCookie]:[]).map(cookie=>this.parseCookie(cookie))}parseCookie(cookie){return cookie.split(";").reduce((azureCookieObject,cookieProperty,index)=>{const[key,value]=cookieProperty.split("="),sanitizedKey=key.toLowerCase().trim(),sanitizedValue=value&&value.trim();if(index===0)return azureCookieObject.name=key,azureCookieObject.value=sanitizedValue,azureCookieObject;switch(sanitizedKey){case"domain":azureCookieObject.domain=sanitizedValue;break;case"path":azureCookieObject.path=sanitizedValue;break;case"expires":azureCookieObject.expires=new Date(sanitizedValue);break;case"secure":azureCookieObject.secure=!0;break;case"httponly":azureCookieObject.httpOnly=!0;break;case"samesite":azureCookieObject.sameSite=sanitizedValue;break;case"max-age":azureCookieObject.maxAge=Number(sanitizedValue);break}return azureCookieObject},{name:"",value:""})}}
|
|
2
2
|
//# sourceMappingURL=http-trigger-v4.adapter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-trigger-v4.adapter.js","sources":["../../../src/adapters/azure/http-trigger-v4.adapter.ts"],"sourcesContent":["//#region Imports\n\nimport { URL } from 'url';\nimport type {\n Context,\n Cookie,\n HttpRequest,\n HttpResponseSimple,\n} from '@azure/functions';\nimport { BothValueHeaders } from '../../@types';\nimport {\n AdapterContract,\n AdapterRequest,\n GetResponseAdapterProps,\n OnErrorProps,\n} from '../../contracts';\nimport {\n getDefaultIfUndefined,\n getEventBodyAsBuffer,\n getFlattenedHeadersMap,\n getPathWithQueryStringParams,\n} from '../../core';\n\n//#endregion\n\n/**\n * The options to customize the {@link HttpTriggerV4Adapter}\n *\n * @breadcrumb Adapters / Azure / HttpTriggerV4Adapter\n * @public\n */\nexport interface HttpTriggerV4AdapterOptions {\n /**\n * Strip base path for custom domains\n *\n * @defaultValue ''\n */\n stripBasePath?: string;\n}\n\n/**\n * The adapter to handle requests from Http Trigger on Azure Function V4.\n *\n * @example\n * ```typescript\n * const stripBasePath = '/any/custom/base/path'; // default ''\n * const adapter = new HttpTriggerV4Adapter({ stripBasePath });\n * ```\n *\n * @see {@link https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-node | Reference}\n *\n * @breadcrumb Adapters / Azure / HttpTriggerV4Adapter\n * @public\n */\nexport class HttpTriggerV4Adapter\n implements AdapterContract<HttpRequest, Context, HttpResponseSimple>\n{\n //#region Constructor\n\n /**\n * Default constructor\n *\n * @param options - The options to customize the {@link HttpTriggerV4Adapter}\n */\n constructor(protected readonly options?: HttpTriggerV4AdapterOptions) {}\n\n //#endregion\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public getAdapterName(): string {\n return HttpTriggerV4Adapter.name;\n }\n\n /**\n * {@inheritDoc}\n */\n public canHandle(event: unknown, context: unknown): boolean {\n const maybeEvent = event as Partial<HttpRequest> | undefined;\n const maybeContext = context as Partial<Context> | undefined;\n\n return !!(\n maybeEvent &&\n maybeEvent.method &&\n maybeEvent.headers &&\n maybeEvent.url &&\n maybeEvent.query &&\n maybeContext &&\n maybeContext.traceContext &&\n maybeContext.bindingDefinitions &&\n maybeContext.log &&\n !!maybeContext.log.info &&\n maybeContext.bindingData\n );\n }\n\n /**\n * {@inheritDoc}\n */\n public getRequest(event: HttpRequest): AdapterRequest {\n const path = this.getPathFromEvent(event);\n\n const method = event.method
|
|
1
|
+
{"version":3,"file":"http-trigger-v4.adapter.js","sources":["../../../src/adapters/azure/http-trigger-v4.adapter.ts"],"sourcesContent":["//#region Imports\n\nimport { URL } from 'url';\nimport type {\n Context,\n Cookie,\n HttpRequest,\n HttpResponseSimple,\n} from '@azure/functions';\nimport { BothValueHeaders } from '../../@types';\nimport {\n AdapterContract,\n AdapterRequest,\n GetResponseAdapterProps,\n OnErrorProps,\n} from '../../contracts';\nimport {\n getDefaultIfUndefined,\n getEventBodyAsBuffer,\n getFlattenedHeadersMap,\n getPathWithQueryStringParams,\n} from '../../core';\n\n//#endregion\n\n/**\n * The options to customize the {@link HttpTriggerV4Adapter}\n *\n * @breadcrumb Adapters / Azure / HttpTriggerV4Adapter\n * @public\n */\nexport interface HttpTriggerV4AdapterOptions {\n /**\n * Strip base path for custom domains\n *\n * @defaultValue ''\n */\n stripBasePath?: string;\n}\n\n/**\n * The adapter to handle requests from Http Trigger on Azure Function V4.\n *\n * @example\n * ```typescript\n * const stripBasePath = '/any/custom/base/path'; // default ''\n * const adapter = new HttpTriggerV4Adapter({ stripBasePath });\n * ```\n *\n * @see {@link https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-node | Reference}\n *\n * @breadcrumb Adapters / Azure / HttpTriggerV4Adapter\n * @public\n */\nexport class HttpTriggerV4Adapter\n implements AdapterContract<HttpRequest, Context, HttpResponseSimple>\n{\n //#region Constructor\n\n /**\n * Default constructor\n *\n * @param options - The options to customize the {@link HttpTriggerV4Adapter}\n */\n constructor(protected readonly options?: HttpTriggerV4AdapterOptions) {}\n\n //#endregion\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public getAdapterName(): string {\n return HttpTriggerV4Adapter.name;\n }\n\n /**\n * {@inheritDoc}\n */\n public canHandle(event: unknown, context: unknown): boolean {\n const maybeEvent = event as Partial<HttpRequest> | undefined;\n const maybeContext = context as Partial<Context> | undefined;\n\n return !!(\n maybeEvent &&\n maybeEvent.method &&\n maybeEvent.headers &&\n maybeEvent.url &&\n maybeEvent.query &&\n maybeContext &&\n maybeContext.traceContext &&\n maybeContext.bindingDefinitions &&\n maybeContext.log &&\n !!maybeContext.log.info &&\n maybeContext.bindingData\n );\n }\n\n /**\n * {@inheritDoc}\n */\n public getRequest(event: HttpRequest): AdapterRequest {\n const path = this.getPathFromEvent(event);\n\n const method = event.method!;\n const headers = getFlattenedHeadersMap(event.headers, ',', true);\n\n let body: Buffer | undefined;\n\n if (event.body) {\n const [bufferBody, contentLength] = getEventBodyAsBuffer(\n event.rawBody,\n false,\n );\n\n body = bufferBody;\n headers['content-length'] = String(contentLength);\n }\n\n const remoteAddress = headers['x-forwarded-for'];\n\n return {\n method,\n path,\n headers,\n remoteAddress,\n body,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public getResponse({\n body,\n statusCode,\n headers: originalHeaders,\n }: GetResponseAdapterProps<HttpRequest>): HttpResponseSimple {\n const headers = getFlattenedHeadersMap(originalHeaders, ',', true);\n const cookies = this.getAzureCookiesFromHeaders(originalHeaders);\n\n if (headers['set-cookie']) delete headers['set-cookie'];\n\n return {\n body,\n statusCode,\n headers,\n // I tried to understand this property with\n // https://docs.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/content-negotiation\n // but I don't know if it's worth implementing this guy as an option\n // I found out when this guy is set to true and the framework sets content-type, azure returns 500\n // So I'll leave it as is and hope no one has any problems.\n enableContentNegotiation: false,\n cookies,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public onErrorWhileForwarding({\n error,\n respondWithErrors,\n event,\n delegatedResolver,\n log,\n }: OnErrorProps<HttpRequest, HttpResponseSimple>): void {\n const body = respondWithErrors ? error.stack : '';\n const errorResponse = this.getResponse({\n event,\n statusCode: 500,\n body: body || '',\n headers: {},\n isBase64Encoded: false,\n log,\n });\n\n delegatedResolver.succeed(errorResponse);\n }\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * Get path from event with query strings\n *\n * @param event - The event sent by serverless\n */\n protected getPathFromEvent(event: HttpRequest): string {\n const stripBasePath = getDefaultIfUndefined(\n this.options?.stripBasePath,\n '',\n );\n\n const url = new URL(event.url);\n const originalPath = url.pathname;\n\n const replaceRegex = new RegExp(`^${stripBasePath}`);\n const path = originalPath.replace(replaceRegex, '');\n\n const queryParams = event.query;\n\n return getPathWithQueryStringParams(path, queryParams);\n }\n\n /**\n * Get the Azure Cookie list parsed from set-cookie header.\n *\n * @param headers - The headers object\n */\n protected getAzureCookiesFromHeaders(headers: BothValueHeaders): Cookie[] {\n const setCookie = headers['set-cookie'];\n\n const headerCookies = Array.isArray(setCookie)\n ? setCookie\n : setCookie\n ? [setCookie]\n : [];\n\n return headerCookies.map(cookie => this.parseCookie(cookie));\n }\n\n /**\n * Parse the string cookie to the Azure Cookie Object.\n * This code was written by {@link https://github.com/zachabney | @zachabney}\n * on {@link https://github.com/zachabney/azure-aws-serverless-express/blob/241d2d5c4d5906e4817662cad6426ec2cbbf9ca7/src/index.js#L4-L49 | this library}.\n *\n * @param cookie - The cookie\n */\n protected parseCookie(cookie: string): Cookie {\n return cookie.split(';').reduce(\n (azureCookieObject, cookieProperty, index) => {\n const [key, value] = cookieProperty.split('=');\n\n const sanitizedKey = key.toLowerCase().trim();\n const sanitizedValue = value && value.trim();\n\n if (index === 0) {\n azureCookieObject.name = key;\n azureCookieObject.value = sanitizedValue;\n\n return azureCookieObject;\n }\n\n switch (sanitizedKey) {\n case 'domain':\n azureCookieObject.domain = sanitizedValue;\n break;\n case 'path':\n azureCookieObject.path = sanitizedValue;\n break;\n case 'expires':\n azureCookieObject.expires = new Date(sanitizedValue);\n break;\n case 'secure':\n azureCookieObject.secure = true;\n break;\n case 'httponly':\n azureCookieObject.httpOnly = true;\n break;\n case 'samesite':\n azureCookieObject.sameSite = sanitizedValue as Cookie['sameSite'];\n break;\n case 'max-age':\n azureCookieObject.maxAge = Number(sanitizedValue);\n break;\n }\n\n return azureCookieObject;\n },\n { name: '', value: '' } as Cookie,\n );\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-function.adapter.js","sources":["../../../src/adapters/digital-ocean/http-function.adapter.ts"],"sourcesContent":["//#region Imports\n\nimport { DigitalOceanHttpEvent } from '../../@types/digital-ocean';\nimport { DigitalOceanHttpResponse } from '../../@types/digital-ocean/digital-ocean-http-response';\nimport {\n AdapterContract,\n AdapterRequest,\n GetResponseAdapterProps,\n OnErrorProps,\n} from '../../contracts';\nimport {\n getDefaultIfUndefined,\n getEventBodyAsBuffer,\n getFlattenedHeadersMap,\n getPathWithQueryStringParams,\n} from '../../core';\n\n//#endregion\n\n/**\n * The options to customize the {@link HttpFunctionAdapter}\n *\n * @breadcrumb Adapters / Digital Ocean / HttpFunctionAdapter\n * @public\n */\nexport interface HttpFunctionAdapterOptions {\n /**\n * Strip base path for custom domains\n *\n * @defaultValue ''\n */\n stripBasePath?: string;\n}\n\n/**\n * The adapter to handle requests from Digital Ocean Functions when called from HTTP Endpoint.\n *\n * @example\n * ```typescript\n * const stripBasePath = '/any/custom/base/path'; // default ''\n * const adapter = new HttpFunctionAdapter({ stripBasePath });\n * ```\n *\n * @breadcrumb Adapters / Digital Ocean / HttpFunctionAdapter\n * @public\n */\nexport class HttpFunctionAdapter\n implements\n AdapterContract<DigitalOceanHttpEvent, void, DigitalOceanHttpResponse>\n{\n //#region Constructor\n\n /**\n * Default constructor\n *\n * @param options - The options to customize the {@link HttpFunctionAdapter}\n */\n constructor(protected readonly options?: HttpFunctionAdapterOptions) {}\n\n //#endregion\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public getAdapterName(): string {\n return HttpFunctionAdapter.name;\n }\n\n /**\n * {@inheritDoc}\n */\n public canHandle(event: unknown): event is DigitalOceanHttpEvent {\n const digitalOceanHttpEvent = event as DigitalOceanHttpEvent;\n\n return (\n !!digitalOceanHttpEvent &&\n digitalOceanHttpEvent.__ow_path !== undefined &&\n digitalOceanHttpEvent.__ow_method !== undefined &&\n digitalOceanHttpEvent.__ow_headers !== undefined\n );\n }\n\n /**\n * {@inheritDoc}\n */\n public getRequest(event: DigitalOceanHttpEvent): AdapterRequest {\n const headers = event.__ow_headers;\n const method = event.__ow_method.toUpperCase();\n const path = this.getPathFromEvent(event);\n\n let body: Buffer | undefined;\n\n if (event.__ow_body) {\n const [bufferBody, contentLength] = getEventBodyAsBuffer(\n event.__ow_body,\n !!event.__ow_isBase64Encoded,\n );\n\n body = bufferBody;\n headers['content-length'] = String(contentLength);\n }\n\n const remoteAddress = headers['x-forwarded-for'];\n\n return {\n method,\n headers,\n body,\n remoteAddress,\n path,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public getResponse({\n headers: responseHeaders,\n body,\n statusCode,\n }: GetResponseAdapterProps<DigitalOceanHttpEvent>): DigitalOceanHttpResponse {\n const headers = getFlattenedHeadersMap(responseHeaders);\n\n return {\n statusCode,\n body,\n headers,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public onErrorWhileForwarding({\n error,\n delegatedResolver,\n respondWithErrors,\n event,\n log,\n }: OnErrorProps<DigitalOceanHttpEvent, DigitalOceanHttpResponse>): void {\n const body = respondWithErrors ? error.stack : '';\n const errorResponse = this.getResponse({\n event,\n statusCode: 500,\n body: body || '',\n headers: {},\n isBase64Encoded: false,\n log,\n });\n\n delegatedResolver.succeed(errorResponse);\n }\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * Get path from event with query strings\n *\n * @param event - The event sent by digital ocean\n */\n protected getPathFromEvent(event: DigitalOceanHttpEvent): string {\n const stripBasePath = getDefaultIfUndefined(\n this.options?.stripBasePath,\n '',\n );\n const replaceRegex = new RegExp(`^${stripBasePath}`);\n\n const path = event.__ow_path.replace(replaceRegex, '');\n const queryParams = event.__ow_query;\n\n return getPathWithQueryStringParams(path, queryParams || {});\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"ooBAAA,8BAAA,CAAA,EAAA,SAAA,8BAAA,CAAA,oBAAA,IAAA,mBAAA,CAAA,EAAA,OAAA,QAAA,aAAA,6BAAA,EAUA,IAKO,YAAA,QAAA,YAAA,EA+BA,MAAM,mBAGb,CAQE,YAA+B,QAAsC,CAAtC,KAAA,QAAA,OAAuC,CAS/D,gBAAyB,CAC9B,OAAO,oBAAoB,IAC7B,CAKO,UAAU,MAAgD,CAC/D,MAAM,sBAAwB,MAG5B,MAAA,CAAC,CAAC,uBACF,sBAAsB,YAAc,QACpC,sBAAsB,cAAgB,QACtC,sBAAsB,eAAiB,MAE3C,CAKO,WAAW,MAA8C,CAC9D,MAAM,QAAU,MAAM,aAChB,OAAS,MAAM,YAAY,YAAY,EACvC,KAAO,KAAK,iBAAiB,KAAK,EAEpC,IAAA,KAEJ,GAAI,MAAM,UAAW,CACb,KAAA,CAAC,WAAY,aAAa,KAAI,YAAA,sBAClC,MAAM,UACN,CAAC,CAAC,MAAM,oBAAA,EAGH,KAAA,WACC,QAAA,gBAAgB,EAAI,OAAO,aAAa,
|
|
1
|
+
{"version":3,"file":"http-function.adapter.js","sources":["../../../src/adapters/digital-ocean/http-function.adapter.ts"],"sourcesContent":["//#region Imports\n\nimport { DigitalOceanHttpEvent } from '../../@types/digital-ocean';\nimport { DigitalOceanHttpResponse } from '../../@types/digital-ocean/digital-ocean-http-response';\nimport {\n AdapterContract,\n AdapterRequest,\n GetResponseAdapterProps,\n OnErrorProps,\n} from '../../contracts';\nimport {\n getDefaultIfUndefined,\n getEventBodyAsBuffer,\n getFlattenedHeadersMap,\n getPathWithQueryStringParams,\n} from '../../core';\n\n//#endregion\n\n/**\n * The options to customize the {@link HttpFunctionAdapter}\n *\n * @breadcrumb Adapters / Digital Ocean / HttpFunctionAdapter\n * @public\n */\nexport interface HttpFunctionAdapterOptions {\n /**\n * Strip base path for custom domains\n *\n * @defaultValue ''\n */\n stripBasePath?: string;\n}\n\n/**\n * The adapter to handle requests from Digital Ocean Functions when called from HTTP Endpoint.\n *\n * @example\n * ```typescript\n * const stripBasePath = '/any/custom/base/path'; // default ''\n * const adapter = new HttpFunctionAdapter({ stripBasePath });\n * ```\n *\n * @breadcrumb Adapters / Digital Ocean / HttpFunctionAdapter\n * @public\n */\nexport class HttpFunctionAdapter\n implements\n AdapterContract<DigitalOceanHttpEvent, void, DigitalOceanHttpResponse>\n{\n //#region Constructor\n\n /**\n * Default constructor\n *\n * @param options - The options to customize the {@link HttpFunctionAdapter}\n */\n constructor(protected readonly options?: HttpFunctionAdapterOptions) {}\n\n //#endregion\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public getAdapterName(): string {\n return HttpFunctionAdapter.name;\n }\n\n /**\n * {@inheritDoc}\n */\n public canHandle(event: unknown): event is DigitalOceanHttpEvent {\n const digitalOceanHttpEvent = event as DigitalOceanHttpEvent;\n\n return (\n !!digitalOceanHttpEvent &&\n digitalOceanHttpEvent.__ow_path !== undefined &&\n digitalOceanHttpEvent.__ow_method !== undefined &&\n digitalOceanHttpEvent.__ow_headers !== undefined\n );\n }\n\n /**\n * {@inheritDoc}\n */\n public getRequest(event: DigitalOceanHttpEvent): AdapterRequest {\n const headers = event.__ow_headers;\n const method = event.__ow_method.toUpperCase();\n const path = this.getPathFromEvent(event);\n\n let body: Buffer | undefined;\n\n if (event.__ow_body) {\n const [bufferBody, contentLength] = getEventBodyAsBuffer(\n event.__ow_body,\n !!event.__ow_isBase64Encoded,\n );\n\n body = bufferBody;\n headers['content-length'] = String(contentLength);\n }\n\n const remoteAddress = headers['x-forwarded-for'];\n\n return {\n method,\n headers,\n body,\n remoteAddress,\n path,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public getResponse({\n headers: responseHeaders,\n body,\n statusCode,\n }: GetResponseAdapterProps<DigitalOceanHttpEvent>): DigitalOceanHttpResponse {\n const headers = getFlattenedHeadersMap(responseHeaders);\n\n return {\n statusCode,\n body,\n headers,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public onErrorWhileForwarding({\n error,\n delegatedResolver,\n respondWithErrors,\n event,\n log,\n }: OnErrorProps<DigitalOceanHttpEvent, DigitalOceanHttpResponse>): void {\n const body = respondWithErrors ? error.stack : '';\n const errorResponse = this.getResponse({\n event,\n statusCode: 500,\n body: body || '',\n headers: {},\n isBase64Encoded: false,\n log,\n });\n\n delegatedResolver.succeed(errorResponse);\n }\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * Get path from event with query strings\n *\n * @param event - The event sent by digital ocean\n */\n protected getPathFromEvent(event: DigitalOceanHttpEvent): string {\n const stripBasePath = getDefaultIfUndefined(\n this.options?.stripBasePath,\n '',\n );\n const replaceRegex = new RegExp(`^${stripBasePath}`);\n\n const path = event.__ow_path.replace(replaceRegex, '');\n const queryParams = event.__ow_query;\n\n return getPathWithQueryStringParams(path, queryParams || {});\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"ooBAAA,8BAAA,CAAA,EAAA,SAAA,8BAAA,CAAA,oBAAA,IAAA,mBAAA,CAAA,EAAA,OAAA,QAAA,aAAA,6BAAA,EAUA,IAKO,YAAA,QAAA,YAAA,EA+BA,MAAM,mBAGb,CAQE,YAA+B,QAAsC,CAAtC,KAAA,QAAA,OAAuC,CAS/D,gBAAyB,CAC9B,OAAO,oBAAoB,IAC7B,CAKO,UAAU,MAAgD,CAC/D,MAAM,sBAAwB,MAG5B,MAAA,CAAC,CAAC,uBACF,sBAAsB,YAAc,QACpC,sBAAsB,cAAgB,QACtC,sBAAsB,eAAiB,MAE3C,CAKO,WAAW,MAA8C,CAC9D,MAAM,QAAU,MAAM,aAChB,OAAS,MAAM,YAAY,YAAY,EACvC,KAAO,KAAK,iBAAiB,KAAK,EAEpC,IAAA,KAEJ,GAAI,MAAM,UAAW,CACb,KAAA,CAAC,WAAY,aAAa,KAAI,YAAA,sBAClC,MAAM,UACN,CAAC,CAAC,MAAM,oBAAA,EAGH,KAAA,WACC,QAAA,gBAAgB,EAAI,OAAO,aAAa,CAClD,CAEM,MAAA,cAAgB,QAAQ,iBAAiB,EAExC,MAAA,CACL,OACA,QACA,KACA,cACA,IAAA,CAEJ,CAKO,YAAY,CACjB,QAAS,gBACT,KACA,UAAA,EAC2E,CACrE,MAAA,WAAU,oCAAuB,eAAe,EAE/C,MAAA,CACL,WACA,KACA,OAAA,CAEJ,CAKO,uBAAuB,CAC5B,MACA,kBACA,kBACA,MACA,GAAA,EACsE,CAChE,MAAA,KAAO,kBAAoB,MAAM,MAAQ,GACzC,cAAgB,KAAK,YAAY,CACrC,MACA,WAAY,IACZ,KAAM,MAAQ,GACd,QAAS,CAAC,EACV,gBAAiB,GACjB,GAAA,CACD,EAED,kBAAkB,QAAQ,aAAa,CACzC,CAWU,iBAAiB,MAAsC,QAC/D,MAAM,iBAAgB,YAAA,wBACpB,QAAK,UAAL,eAAc,cACd,EAAA,EAEI,aAAe,IAAI,OAAO,IAAI,aAAa,EAAE,EAE7C,KAAO,MAAM,UAAU,QAAQ,aAAc,EAAE,EAC/C,YAAc,MAAM,WAE1B,SAAO,YAA6B,8BAAA,KAAM,aAAe,CAAE,CAAA,CAC7D,CAGF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"huawei-api-gateway.adapter.js","sources":["../../../src/adapters/huawei/huawei-api-gateway.adapter.ts"],"sourcesContent":["//#region Imports\n\nimport {\n HuaweiApiGatewayEvent,\n HuaweiApiGatewayResponse,\n HuaweiContext,\n} from '../../@types/huawei';\nimport {\n AdapterContract,\n AdapterRequest,\n GetResponseAdapterProps,\n OnErrorProps,\n} from '../../contracts';\nimport {\n getDefaultIfUndefined,\n getEventBodyAsBuffer,\n getFlattenedHeadersMap,\n getMultiValueHeadersMap,\n getPathWithQueryStringParams,\n} from '../../core';\n\n//#endregion\n\n/**\n * The options to customize the {@link HuaweiApiGatewayAdapter}\n *\n * @breadcrumb Adapters / Huawei / HuaweiApiGatewayAdapter\n * @public\n */\nexport interface HuaweiApiGatewayOptions {\n /**\n * Strip base path for custom domains\n *\n * @defaultValue ''\n */\n stripBasePath?: string;\n}\n\n/**\n * The adapter to handle requests from Huawei Api Gateway\n *\n * @example\n * ```typescript\n * const stripBasePath = '/any/custom/base/path'; // default ''\n * const adapter = new ApiGatewayAdapter({ stripBasePath });\n * ```\n *\n * @breadcrumb Adapters / Huawei / HuaweiApiGatewayAdapter\n * @public\n *\n * {@link https://support.huaweicloud.com/intl/en-us/devg-functiongraph/functiongraph_02_0102.html#functiongraph_02_0102__li5178638110137 | Event Reference}\n */\nexport class HuaweiApiGatewayAdapter\n implements\n AdapterContract<\n HuaweiApiGatewayEvent,\n HuaweiContext,\n HuaweiApiGatewayResponse\n >\n{\n //#region Constructor\n\n /**\n * Default constructor\n *\n * @param options - The options to customize the {@link HuaweiApiGatewayAdapter}\n */\n constructor(protected readonly options?: HuaweiApiGatewayOptions) {}\n\n //#endregion\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public getAdapterName(): string {\n return HuaweiApiGatewayAdapter.name;\n }\n\n /**\n * {@inheritDoc}\n */\n public canHandle(event: unknown): event is HuaweiApiGatewayEvent {\n const apiGatewayEvent = event as Partial<HuaweiApiGatewayEvent>;\n\n return !!(\n apiGatewayEvent &&\n apiGatewayEvent.httpMethod &&\n apiGatewayEvent.requestContext &&\n apiGatewayEvent.requestContext.apiId &&\n apiGatewayEvent.requestContext.stage &&\n apiGatewayEvent.requestContext.requestId &&\n // to avoid conflict with api gateway v1 of aws\n !('multiValueQueryStringParameters' in apiGatewayEvent)\n );\n }\n\n /**\n * {@inheritDoc}\n */\n public getRequest(event: HuaweiApiGatewayEvent): AdapterRequest {\n const method = event.httpMethod;\n const path = this.getPathFromEvent(event);\n\n const headers = getFlattenedHeadersMap(event.headers, ',', true);\n\n let body: Buffer | undefined;\n\n if (event.body) {\n const [bufferBody, contentLength] = getEventBodyAsBuffer(\n event.body,\n event.isBase64Encoded,\n );\n\n body = bufferBody;\n headers['content-length'] = String(contentLength);\n }\n\n const remoteAddress = headers['x-real-ip'];\n\n return {\n method,\n headers,\n body,\n remoteAddress,\n path,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public getResponse({\n headers: responseHeaders,\n body,\n isBase64Encoded,\n statusCode,\n }: GetResponseAdapterProps<HuaweiApiGatewayEvent>): HuaweiApiGatewayResponse {\n const headers = getMultiValueHeadersMap(responseHeaders);\n\n return {\n statusCode,\n body,\n headers,\n isBase64Encoded,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public onErrorWhileForwarding({\n error,\n delegatedResolver,\n respondWithErrors,\n event,\n log,\n }: OnErrorProps<HuaweiApiGatewayEvent, HuaweiApiGatewayResponse>): void {\n const body = respondWithErrors ? error.stack : '';\n const errorResponse = this.getResponse({\n event,\n statusCode: 500,\n body: body || '',\n headers: {},\n isBase64Encoded: false,\n log,\n });\n\n delegatedResolver.succeed(errorResponse);\n }\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * Get path from event with query strings\n *\n * @param event - The event sent by serverless\n */\n protected getPathFromEvent(event: HuaweiApiGatewayEvent): string {\n const stripBasePath = getDefaultIfUndefined(\n this.options?.stripBasePath,\n '',\n );\n const replaceRegex = new RegExp(`^${stripBasePath}`);\n const path = event.path.replace(replaceRegex, '');\n\n const queryParams = event.queryStringParameters;\n\n return getPathWithQueryStringParams(path, queryParams);\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"ooBAAA,mCAAA,CAAA,EAAA,SAAA,mCAAA,CAAA,wBAAA,IAAA,uBAAA,CAAA,EAAA,OAAA,QAAA,aAAA,kCAAA,EAaA,IAMO,YAAA,QAAA,YAAA,EAiCA,MAAM,uBAOb,CAQE,YAA+B,QAAmC,CAAnC,KAAA,QAAA,OAAoC,CAS5D,gBAAyB,CAC9B,OAAO,wBAAwB,IACjC,CAKO,UAAU,MAAgD,CAC/D,MAAM,gBAAkB,MAExB,MAAO,CAAC,EACN,iBACA,gBAAgB,YAChB,gBAAgB,gBAChB,gBAAgB,eAAe,OAC/B,gBAAgB,eAAe,OAC/B,gBAAgB,eAAe,WAE/B,EAAE,oCAAqC,iBAE3C,CAKO,WAAW,MAA8C,CAC9D,MAAM,OAAS,MAAM,WACf,KAAO,KAAK,iBAAiB,KAAK,EAElC,WAAU,YAAA,wBAAuB,MAAM,QAAS,IAAK,EAAI,EAE3D,IAAA,KAEJ,GAAI,MAAM,KAAM,CACR,KAAA,CAAC,WAAY,aAAa,KAAI,YAAA,sBAClC,MAAM,KACN,MAAM,eAAA,EAGD,KAAA,WACC,QAAA,gBAAgB,EAAI,OAAO,aAAa,
|
|
1
|
+
{"version":3,"file":"huawei-api-gateway.adapter.js","sources":["../../../src/adapters/huawei/huawei-api-gateway.adapter.ts"],"sourcesContent":["//#region Imports\n\nimport {\n HuaweiApiGatewayEvent,\n HuaweiApiGatewayResponse,\n HuaweiContext,\n} from '../../@types/huawei';\nimport {\n AdapterContract,\n AdapterRequest,\n GetResponseAdapterProps,\n OnErrorProps,\n} from '../../contracts';\nimport {\n getDefaultIfUndefined,\n getEventBodyAsBuffer,\n getFlattenedHeadersMap,\n getMultiValueHeadersMap,\n getPathWithQueryStringParams,\n} from '../../core';\n\n//#endregion\n\n/**\n * The options to customize the {@link HuaweiApiGatewayAdapter}\n *\n * @breadcrumb Adapters / Huawei / HuaweiApiGatewayAdapter\n * @public\n */\nexport interface HuaweiApiGatewayOptions {\n /**\n * Strip base path for custom domains\n *\n * @defaultValue ''\n */\n stripBasePath?: string;\n}\n\n/**\n * The adapter to handle requests from Huawei Api Gateway\n *\n * @example\n * ```typescript\n * const stripBasePath = '/any/custom/base/path'; // default ''\n * const adapter = new ApiGatewayAdapter({ stripBasePath });\n * ```\n *\n * @breadcrumb Adapters / Huawei / HuaweiApiGatewayAdapter\n * @public\n *\n * {@link https://support.huaweicloud.com/intl/en-us/devg-functiongraph/functiongraph_02_0102.html#functiongraph_02_0102__li5178638110137 | Event Reference}\n */\nexport class HuaweiApiGatewayAdapter\n implements\n AdapterContract<\n HuaweiApiGatewayEvent,\n HuaweiContext,\n HuaweiApiGatewayResponse\n >\n{\n //#region Constructor\n\n /**\n * Default constructor\n *\n * @param options - The options to customize the {@link HuaweiApiGatewayAdapter}\n */\n constructor(protected readonly options?: HuaweiApiGatewayOptions) {}\n\n //#endregion\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public getAdapterName(): string {\n return HuaweiApiGatewayAdapter.name;\n }\n\n /**\n * {@inheritDoc}\n */\n public canHandle(event: unknown): event is HuaweiApiGatewayEvent {\n const apiGatewayEvent = event as Partial<HuaweiApiGatewayEvent>;\n\n return !!(\n apiGatewayEvent &&\n apiGatewayEvent.httpMethod &&\n apiGatewayEvent.requestContext &&\n apiGatewayEvent.requestContext.apiId &&\n apiGatewayEvent.requestContext.stage &&\n apiGatewayEvent.requestContext.requestId &&\n // to avoid conflict with api gateway v1 of aws\n !('multiValueQueryStringParameters' in apiGatewayEvent)\n );\n }\n\n /**\n * {@inheritDoc}\n */\n public getRequest(event: HuaweiApiGatewayEvent): AdapterRequest {\n const method = event.httpMethod;\n const path = this.getPathFromEvent(event);\n\n const headers = getFlattenedHeadersMap(event.headers, ',', true);\n\n let body: Buffer | undefined;\n\n if (event.body) {\n const [bufferBody, contentLength] = getEventBodyAsBuffer(\n event.body,\n event.isBase64Encoded,\n );\n\n body = bufferBody;\n headers['content-length'] = String(contentLength);\n }\n\n const remoteAddress = headers['x-real-ip'];\n\n return {\n method,\n headers,\n body,\n remoteAddress,\n path,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public getResponse({\n headers: responseHeaders,\n body,\n isBase64Encoded,\n statusCode,\n }: GetResponseAdapterProps<HuaweiApiGatewayEvent>): HuaweiApiGatewayResponse {\n const headers = getMultiValueHeadersMap(responseHeaders);\n\n return {\n statusCode,\n body,\n headers,\n isBase64Encoded,\n };\n }\n\n /**\n * {@inheritDoc}\n */\n public onErrorWhileForwarding({\n error,\n delegatedResolver,\n respondWithErrors,\n event,\n log,\n }: OnErrorProps<HuaweiApiGatewayEvent, HuaweiApiGatewayResponse>): void {\n const body = respondWithErrors ? error.stack : '';\n const errorResponse = this.getResponse({\n event,\n statusCode: 500,\n body: body || '',\n headers: {},\n isBase64Encoded: false,\n log,\n });\n\n delegatedResolver.succeed(errorResponse);\n }\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * Get path from event with query strings\n *\n * @param event - The event sent by serverless\n */\n protected getPathFromEvent(event: HuaweiApiGatewayEvent): string {\n const stripBasePath = getDefaultIfUndefined(\n this.options?.stripBasePath,\n '',\n );\n const replaceRegex = new RegExp(`^${stripBasePath}`);\n const path = event.path.replace(replaceRegex, '');\n\n const queryParams = event.queryStringParameters;\n\n return getPathWithQueryStringParams(path, queryParams);\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"ooBAAA,mCAAA,CAAA,EAAA,SAAA,mCAAA,CAAA,wBAAA,IAAA,uBAAA,CAAA,EAAA,OAAA,QAAA,aAAA,kCAAA,EAaA,IAMO,YAAA,QAAA,YAAA,EAiCA,MAAM,uBAOb,CAQE,YAA+B,QAAmC,CAAnC,KAAA,QAAA,OAAoC,CAS5D,gBAAyB,CAC9B,OAAO,wBAAwB,IACjC,CAKO,UAAU,MAAgD,CAC/D,MAAM,gBAAkB,MAExB,MAAO,CAAC,EACN,iBACA,gBAAgB,YAChB,gBAAgB,gBAChB,gBAAgB,eAAe,OAC/B,gBAAgB,eAAe,OAC/B,gBAAgB,eAAe,WAE/B,EAAE,oCAAqC,iBAE3C,CAKO,WAAW,MAA8C,CAC9D,MAAM,OAAS,MAAM,WACf,KAAO,KAAK,iBAAiB,KAAK,EAElC,WAAU,YAAA,wBAAuB,MAAM,QAAS,IAAK,EAAI,EAE3D,IAAA,KAEJ,GAAI,MAAM,KAAM,CACR,KAAA,CAAC,WAAY,aAAa,KAAI,YAAA,sBAClC,MAAM,KACN,MAAM,eAAA,EAGD,KAAA,WACC,QAAA,gBAAgB,EAAI,OAAO,aAAa,CAClD,CAEM,MAAA,cAAgB,QAAQ,WAAW,EAElC,MAAA,CACL,OACA,QACA,KACA,cACA,IAAA,CAEJ,CAKO,YAAY,CACjB,QAAS,gBACT,KACA,gBACA,UAAA,EAC2E,CACrE,MAAA,WAAU,qCAAwB,eAAe,EAEhD,MAAA,CACL,WACA,KACA,QACA,eAAA,CAEJ,CAKO,uBAAuB,CAC5B,MACA,kBACA,kBACA,MACA,GAAA,EACsE,CAChE,MAAA,KAAO,kBAAoB,MAAM,MAAQ,GACzC,cAAgB,KAAK,YAAY,CACrC,MACA,WAAY,IACZ,KAAM,MAAQ,GACd,QAAS,CAAC,EACV,gBAAiB,GACjB,GAAA,CACD,EAED,kBAAkB,QAAQ,aAAa,CACzC,CAWU,iBAAiB,MAAsC,QAC/D,MAAM,iBAAgB,YAAA,wBACpB,QAAK,UAAL,eAAc,cACd,EAAA,EAEI,aAAe,IAAI,OAAO,IAAI,aAAa,EAAE,EAC7C,KAAO,MAAM,KAAK,QAAQ,aAAc,EAAE,EAE1C,YAAc,MAAM,sBAEnB,SAAA,YAAA,8BAA6B,KAAM,WAAW,CACvD,CAGF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-handler.js","sources":["../../src/core/base-handler.ts"],"sourcesContent":["//#region Imports\n\nimport { BinarySettings } from '../@types';\nimport {\n AdapterContract,\n AdapterRequest,\n FrameworkContract,\n HandlerContract,\n ResolverContract,\n ServerlessHandler,\n} from '../contracts';\nimport { ServerlessRequest, ServerlessResponse } from '../network';\nimport { ILogger } from './index';\n\n//#endregion\n\n/**\n * The abstract class that represents the base class for a handler\n *\n * @breadcrumb Core\n * @public\n */\nexport abstract class BaseHandler<\n TApp,\n TEvent,\n TContext,\n TCallback,\n TResponse,\n TReturn,\n> implements\n HandlerContract<TApp, TEvent, TContext, TCallback, TResponse, TReturn>\n{\n //#region Public Methods\n\n /**\n * Get the handler that will handle serverless requests\n */\n public abstract getHandler(\n app: TApp,\n framework: FrameworkContract<TApp>,\n adapters: AdapterContract<TEvent, TContext, TResponse>[],\n resolverFactory: ResolverContract<\n TEvent,\n TContext,\n TCallback,\n TResponse,\n TReturn\n >,\n binarySettings: BinarySettings,\n respondWithErrors: boolean,\n log: ILogger,\n ): ServerlessHandler<TReturn>;\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * Get the adapter to handle a specific event and context\n *\n * @param event - The event sent by serverless\n * @param context - The context sent by serverless\n * @param adapters - The list of adapters\n * @param log - The instance of logger\n */\n protected getAdapterByEventAndContext(\n event: TEvent,\n context: TContext,\n adapters: AdapterContract<TEvent, TContext, TResponse>[],\n log: ILogger,\n ): AdapterContract<TEvent, TContext, TResponse> {\n const resolvedAdapters = adapters.filter(adapter =>\n adapter.canHandle(event, context, log),\n );\n\n if (resolvedAdapters.length === 0) {\n throw new Error(\n \"SERVERLESS_ADAPTER: Couldn't find adapter to handle this event.\",\n );\n }\n\n if (resolvedAdapters.length > 1) {\n throw new Error(\n `SERVERLESS_ADAPTER: Two or more adapters was resolved by the event, the adapters are: ${adapters\n .map(adapter => adapter.getAdapterName())\n .join(', ')}.`,\n );\n }\n\n return resolvedAdapters[0];\n }\n\n /**\n * Get serverless request and response frmo the adapter request\n *\n * @param requestValues - The request values from adapter\n */\n protected getServerlessRequestResponseFromAdapterRequest(\n requestValues: AdapterRequest,\n ): [request: ServerlessRequest, response: ServerlessResponse] {\n const request = new ServerlessRequest({\n method: requestValues.method,\n headers: requestValues.headers,\n body: requestValues.body,\n remoteAddress: requestValues.remoteAddress,\n url: requestValues.path,\n });\n\n const response = new ServerlessResponse({\n method: requestValues.method,\n });\n\n return [request, response];\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"ooBAAA,qBAAA,CAAA,EAAA,SAAA,qBAAA,CAAA,YAAA,IAAA,WAAA,CAAA,EAAA,OAAA,QAAA,aAAA,oBAAA,EAWA,IAAsD,eAAA,QAAA,YAAA,EAW/C,MAAe,WAStB,CAkCY,4BACR,MACA,QACA,SACA,IAC8C,CAC9C,MAAM,iBAAmB,SAAS,OAChC,SAAA,QAAQ,UAAU,MAAO,QAAS,GAAG,CAAA,EAGnC,GAAA,iBAAiB,SAAW,EAC9B,MAAM,IAAI,MACR,iEAAA,EAIA,GAAA,iBAAiB,OAAS,EAC5B,MAAM,IAAI,MACR,yFAAyF,SACtF,
|
|
1
|
+
{"version":3,"file":"base-handler.js","sources":["../../src/core/base-handler.ts"],"sourcesContent":["//#region Imports\n\nimport { BinarySettings } from '../@types';\nimport {\n AdapterContract,\n AdapterRequest,\n FrameworkContract,\n HandlerContract,\n ResolverContract,\n ServerlessHandler,\n} from '../contracts';\nimport { ServerlessRequest, ServerlessResponse } from '../network';\nimport { ILogger } from './index';\n\n//#endregion\n\n/**\n * The abstract class that represents the base class for a handler\n *\n * @breadcrumb Core\n * @public\n */\nexport abstract class BaseHandler<\n TApp,\n TEvent,\n TContext,\n TCallback,\n TResponse,\n TReturn,\n> implements\n HandlerContract<TApp, TEvent, TContext, TCallback, TResponse, TReturn>\n{\n //#region Public Methods\n\n /**\n * Get the handler that will handle serverless requests\n */\n public abstract getHandler(\n app: TApp,\n framework: FrameworkContract<TApp>,\n adapters: AdapterContract<TEvent, TContext, TResponse>[],\n resolverFactory: ResolverContract<\n TEvent,\n TContext,\n TCallback,\n TResponse,\n TReturn\n >,\n binarySettings: BinarySettings,\n respondWithErrors: boolean,\n log: ILogger,\n ): ServerlessHandler<TReturn>;\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * Get the adapter to handle a specific event and context\n *\n * @param event - The event sent by serverless\n * @param context - The context sent by serverless\n * @param adapters - The list of adapters\n * @param log - The instance of logger\n */\n protected getAdapterByEventAndContext(\n event: TEvent,\n context: TContext,\n adapters: AdapterContract<TEvent, TContext, TResponse>[],\n log: ILogger,\n ): AdapterContract<TEvent, TContext, TResponse> {\n const resolvedAdapters = adapters.filter(adapter =>\n adapter.canHandle(event, context, log),\n );\n\n if (resolvedAdapters.length === 0) {\n throw new Error(\n \"SERVERLESS_ADAPTER: Couldn't find adapter to handle this event.\",\n );\n }\n\n if (resolvedAdapters.length > 1) {\n throw new Error(\n `SERVERLESS_ADAPTER: Two or more adapters was resolved by the event, the adapters are: ${adapters\n .map(adapter => adapter.getAdapterName())\n .join(', ')}.`,\n );\n }\n\n return resolvedAdapters[0];\n }\n\n /**\n * Get serverless request and response frmo the adapter request\n *\n * @param requestValues - The request values from adapter\n */\n protected getServerlessRequestResponseFromAdapterRequest(\n requestValues: AdapterRequest,\n ): [request: ServerlessRequest, response: ServerlessResponse] {\n const request = new ServerlessRequest({\n method: requestValues.method,\n headers: requestValues.headers,\n body: requestValues.body,\n remoteAddress: requestValues.remoteAddress,\n url: requestValues.path,\n });\n\n const response = new ServerlessResponse({\n method: requestValues.method,\n });\n\n return [request, response];\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"ooBAAA,qBAAA,CAAA,EAAA,SAAA,qBAAA,CAAA,YAAA,IAAA,WAAA,CAAA,EAAA,OAAA,QAAA,aAAA,oBAAA,EAWA,IAAsD,eAAA,QAAA,YAAA,EAW/C,MAAe,WAStB,CAkCY,4BACR,MACA,QACA,SACA,IAC8C,CAC9C,MAAM,iBAAmB,SAAS,OAChC,SAAA,QAAQ,UAAU,MAAO,QAAS,GAAG,CAAA,EAGnC,GAAA,iBAAiB,SAAW,EAC9B,MAAM,IAAI,MACR,iEAAA,EAIA,GAAA,iBAAiB,OAAS,EAC5B,MAAM,IAAI,MACR,yFAAyF,SACtF,IAAe,SAAA,QAAQ,gBAAgB,EACvC,KAAK,IAAI,CAAC,GAAA,EAIjB,OAAO,iBAAiB,CAAC,CAC3B,CAOU,+CACR,cAC4D,CACtD,MAAA,QAAU,IAAI,eAAkB,kBAAA,CACpC,OAAQ,cAAc,OACtB,QAAS,cAAc,QACvB,KAAM,cAAc,KACpB,cAAe,cAAc,cAC7B,IAAK,cAAc,IAAA,CACpB,EAEK,SAAW,IAAI,eAAmB,mBAAA,CACtC,OAAQ,cAAc,MAAA,CACvB,EAEM,MAAA,CAAC,QAAS,QAAQ,CAC3B,CAGF"}
|
package/lib/core/headers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headers.js","sources":["../../src/core/headers.ts"],"sourcesContent":["//#region Imports\n\nimport { BothValueHeaders } from '../@types';\n\n//#endregion\n\n/**\n * Transform a header map and make sure the value is not an array\n *\n * @example\n * ```typescript\n * const headers = { 'accept-encoding': 'gzip', 'accept-language': ['en-US', 'en;q=0.9'] };\n * const flattenedHeaders = getFlattenedHeadersMap(headers, ',', true);\n * console.log(flattenedHeaders);\n * // { 'accept-encoding': 'gzip', 'accept-language': 'en-US,en;q=0.9' }\n * ```\n *\n * @param headersMap - The initial headers\n * @param separator - The separator used when we join the array of header's value\n * @param lowerCaseKey - Should put all keys in lowercase\n *\n * @breadcrumb Core / Headers\n * @public\n */\nexport function getFlattenedHeadersMap(\n headersMap: BothValueHeaders,\n separator: string = ',',\n lowerCaseKey: boolean = false,\n): Record<string, string> {\n return Object.keys(headersMap).reduce((acc, headerKey) => {\n const newKey = lowerCaseKey ? headerKey.toLowerCase() : headerKey;\n const headerValue = headersMap[headerKey];\n\n if (Array.isArray(headerValue)) acc[newKey] = headerValue.join(separator);\n else acc[newKey] = (headerValue ?? '') + '';\n\n return acc;\n }, {});\n}\n\n/**\n * Transforms a header map into a multi-value map header.\n *\n * @example\n * ```typescript\n * const headers = { 'accept-encoding': 'gzip', 'connection': ['keep-alive'] };\n * const multiValueHeaders = getMultiValueHeadersMap(headers);\n * console.log(multiValueHeaders);\n * // { 'accept-encoding': ['gzip'], 'connection': ['keep-alive'] }\n * ```\n *\n * @param headersMap - The initial headers\n *\n * @breadcrumb Core / Headers\n * @public\n */\nexport function getMultiValueHeadersMap(\n headersMap: BothValueHeaders,\n): Record<string, string[]> {\n return Object.keys(headersMap).reduce((acc, headerKey) => {\n const headerValue = headersMap[headerKey];\n\n acc[headerKey.toLowerCase()] = Array.isArray(headerValue)\n ? headerValue.map(String)\n : [String(headerValue)];\n\n return acc;\n }, {});\n}\n\n/**\n * The wrapper that holds the information about single value headers and cookies\n *\n * @breadcrumb Core / Headers\n * @public\n */\nexport type FlattenedHeadersAndCookies = {\n /**\n * Just the single value headers\n */\n headers: Record<string, string>;\n\n /**\n * The list of cookies\n */\n cookies: string[];\n};\n\n/**\n * Transforms a header map into a single value headers and cookies\n *\n * @param headersMap - The initial headers\n *\n * @breadcrumb Core / Headers\n * @public\n */\nexport function getFlattenedHeadersMapAndCookies(\n headersMap: BothValueHeaders,\n): FlattenedHeadersAndCookies {\n return Object.keys(headersMap).reduce(\n (acc, headerKey) => {\n const headerValue = headersMap[headerKey];\n const lowerHeaderKey = headerKey.toLowerCase();\n\n if (Array.isArray(headerValue)) {\n if (lowerHeaderKey !== 'set-cookie')\n acc.headers[headerKey] = headerValue.join(',');\n else acc.cookies.push(...headerValue);\n } else {\n if (lowerHeaderKey === 'set-cookie' && headerValue !== undefined)\n acc.cookies.push(headerValue ?? '');\n else acc.headers[headerKey] = String(headerValue ?? '');\n }\n\n return acc;\n },\n {\n cookies: [],\n headers: {},\n } as FlattenedHeadersAndCookies,\n );\n}\n\n/**\n * Parse HTTP Raw Headers\n * Attribution to {@link https://github.com/kesla/parse-headers/blob/master/parse-headers.js}\n *\n * @param headers - The raw headers\n *\n * @breadcrumb Core / Headers\n * @public\n */\nexport function parseHeaders(\n headers: string,\n): Record<string, string | string[]> {\n if (!headers) return {};\n\n const result = {};\n const headersArr = headers.trim().split('\\n');\n\n for (let i = 0; i < headersArr.length; i++) {\n const row = headersArr[i];\n const index = row.indexOf(':');\n const key = row.slice(0, index).trim().toLowerCase();\n const value = row.slice(index + 1).trim();\n\n if (typeof result[key] === 'undefined') result[key] = value;\n else if (Array.isArray(result[key])) result[key].push(value);\n else result[key] = [result[key], value];\n }\n\n return result;\n}\n"],"names":[],"mappings":"ooBAAA,gBAAA,CAAA,EAAA,SAAA,gBAAA,CAAA,uBAAA,IAAA,uBAAA,iCAAA,IAAA,iCAAA,wBAAA,IAAA,wBAAA,aAAA,IAAA,YAAA,CAAA,EAAA,OAAA,QAAA,aAAA,eAAA,EAwBO,SAAS,uBACd,WACA,UAAoB,IACpB,aAAwB,GACA,CACxB,OAAO,OAAO,KAAK,UAAU,EAAE,OAAO,CAAC,IAAK,YAAc,CACxD,MAAM,OAAS,aAAe,UAAU,YAAA,EAAgB,UAClD,YAAc,WAAW,SAAS,EAEpC,OAAA,MAAM,QAAQ,WAAW,EAAG,IAAI,MAAM,EAAI,YAAY,KAAK,SAAS,EAC/D,IAAA,MAAM,GAAK,aAAe,IAAM,GAElC,GACT,EAAG,CAAE,CAAA,CACP,CAkBO,SAAS,wBACd,WAC0B,CAC1B,OAAO,OAAO,KAAK,UAAU,EAAE,OAAO,CAAC,IAAK,YAAc,CAClD,MAAA,YAAc,WAAW,SAAS,EAExC,WAAI,UAAU,YAAA,CAAa,EAAI,MAAM,QAAQ,WAAW,EACpD,YAAY,IAAI,MAAM,EACtB,CAAC,OAAO,WAAW,CAAC,EAEjB,GACT,EAAG,CAAE,CAAA,CACP,CA4BO,SAAS,iCACd,WAC4B,CACrB,OAAA,OAAO,KAAK,UAAU,EAAE,OAC7B,CAAC,IAAK,YAAc,CACZ,MAAA,YAAc,WAAW,SAAS,EAClC,eAAiB,UAAU,cAE7B,OAAA,MAAM,QAAQ,WAAW,EACvB,iBAAmB,aACrB,IAAI,QAAQ,SAAS,EAAI,YAAY,KAAK,GAAG,EACtC,IAAA,QAAQ,KAAK,GAAG,WAAW,EAEhC,iBAAmB,cAAgB,cAAgB,OACjD,IAAA,QAAQ,KAAK,aAAe,EAAE,EAC/B,IAAI,QAAQ,SAAS,EAAI,OAAO,aAAe,EAAE,EAGjD,GACT,EACA,CACE,QAAS,CAAC,EACV,QAAS,CAAC,CACZ,CAAA,CAEJ,CAWO,SAAS,aACd,QACmC,CACnC,GAAI,CAAC,QAAS,MAAO,GAErB,MAAM,OAAS,CAAA,EACT,WAAa,QAAQ,KAAK,EAAE,MAAM;AAAA,CAAI,EAE5C,QAAS,EAAI,EAAG,EAAI,WAAW,OAAQ,IAAK,CACpC,MAAA,IAAM,WAAW,CAAC,EAClB,MAAQ,IAAI,QAAQ,GAAG,EACvB,IAAM,IAAI,MAAM,EAAG,KAAK,EAAE,OAAO,cACjC,MAAQ,IAAI,MAAM,MAAQ,CAAC,EAAE,OAE/B,OAAO,OAAO,GAAG,EAAM,IAAa,OAAO,GAAG,EAAI,MAC7C,MAAM,QAAQ,OAAO,GAAG,CAAC,EAAU,OAAA,GAAG,EAAE,KAAK,KAAK,EACtD,OAAO,GAAG,EAAI,CAAC,OAAO,GAAG,EAAG,KAAK,
|
|
1
|
+
{"version":3,"file":"headers.js","sources":["../../src/core/headers.ts"],"sourcesContent":["//#region Imports\n\nimport { BothValueHeaders } from '../@types';\n\n//#endregion\n\n/**\n * Transform a header map and make sure the value is not an array\n *\n * @example\n * ```typescript\n * const headers = { 'accept-encoding': 'gzip', 'accept-language': ['en-US', 'en;q=0.9'] };\n * const flattenedHeaders = getFlattenedHeadersMap(headers, ',', true);\n * console.log(flattenedHeaders);\n * // { 'accept-encoding': 'gzip', 'accept-language': 'en-US,en;q=0.9' }\n * ```\n *\n * @param headersMap - The initial headers\n * @param separator - The separator used when we join the array of header's value\n * @param lowerCaseKey - Should put all keys in lowercase\n *\n * @breadcrumb Core / Headers\n * @public\n */\nexport function getFlattenedHeadersMap(\n headersMap: BothValueHeaders,\n separator: string = ',',\n lowerCaseKey: boolean = false,\n): Record<string, string> {\n return Object.keys(headersMap).reduce((acc, headerKey) => {\n const newKey = lowerCaseKey ? headerKey.toLowerCase() : headerKey;\n const headerValue = headersMap[headerKey];\n\n if (Array.isArray(headerValue)) acc[newKey] = headerValue.join(separator);\n else acc[newKey] = (headerValue ?? '') + '';\n\n return acc;\n }, {});\n}\n\n/**\n * Transforms a header map into a multi-value map header.\n *\n * @example\n * ```typescript\n * const headers = { 'accept-encoding': 'gzip', 'connection': ['keep-alive'] };\n * const multiValueHeaders = getMultiValueHeadersMap(headers);\n * console.log(multiValueHeaders);\n * // { 'accept-encoding': ['gzip'], 'connection': ['keep-alive'] }\n * ```\n *\n * @param headersMap - The initial headers\n *\n * @breadcrumb Core / Headers\n * @public\n */\nexport function getMultiValueHeadersMap(\n headersMap: BothValueHeaders,\n): Record<string, string[]> {\n return Object.keys(headersMap).reduce((acc, headerKey) => {\n const headerValue = headersMap[headerKey];\n\n acc[headerKey.toLowerCase()] = Array.isArray(headerValue)\n ? headerValue.map(String)\n : [String(headerValue)];\n\n return acc;\n }, {});\n}\n\n/**\n * The wrapper that holds the information about single value headers and cookies\n *\n * @breadcrumb Core / Headers\n * @public\n */\nexport type FlattenedHeadersAndCookies = {\n /**\n * Just the single value headers\n */\n headers: Record<string, string>;\n\n /**\n * The list of cookies\n */\n cookies: string[];\n};\n\n/**\n * Transforms a header map into a single value headers and cookies\n *\n * @param headersMap - The initial headers\n *\n * @breadcrumb Core / Headers\n * @public\n */\nexport function getFlattenedHeadersMapAndCookies(\n headersMap: BothValueHeaders,\n): FlattenedHeadersAndCookies {\n return Object.keys(headersMap).reduce(\n (acc, headerKey) => {\n const headerValue = headersMap[headerKey];\n const lowerHeaderKey = headerKey.toLowerCase();\n\n if (Array.isArray(headerValue)) {\n if (lowerHeaderKey !== 'set-cookie')\n acc.headers[headerKey] = headerValue.join(',');\n else acc.cookies.push(...headerValue);\n } else {\n if (lowerHeaderKey === 'set-cookie' && headerValue !== undefined)\n acc.cookies.push(headerValue ?? '');\n else acc.headers[headerKey] = String(headerValue ?? '');\n }\n\n return acc;\n },\n {\n cookies: [],\n headers: {},\n } as FlattenedHeadersAndCookies,\n );\n}\n\n/**\n * Parse HTTP Raw Headers\n * Attribution to {@link https://github.com/kesla/parse-headers/blob/master/parse-headers.js}\n *\n * @param headers - The raw headers\n *\n * @breadcrumb Core / Headers\n * @public\n */\nexport function parseHeaders(\n headers: string,\n): Record<string, string | string[]> {\n if (!headers) return {};\n\n const result = {};\n const headersArr = headers.trim().split('\\n');\n\n for (let i = 0; i < headersArr.length; i++) {\n const row = headersArr[i];\n const index = row.indexOf(':');\n const key = row.slice(0, index).trim().toLowerCase();\n const value = row.slice(index + 1).trim();\n\n if (typeof result[key] === 'undefined') result[key] = value;\n else if (Array.isArray(result[key])) result[key].push(value);\n else result[key] = [result[key], value];\n }\n\n return result;\n}\n"],"names":[],"mappings":"ooBAAA,gBAAA,CAAA,EAAA,SAAA,gBAAA,CAAA,uBAAA,IAAA,uBAAA,iCAAA,IAAA,iCAAA,wBAAA,IAAA,wBAAA,aAAA,IAAA,YAAA,CAAA,EAAA,OAAA,QAAA,aAAA,eAAA,EAwBO,SAAS,uBACd,WACA,UAAoB,IACpB,aAAwB,GACA,CACxB,OAAO,OAAO,KAAK,UAAU,EAAE,OAAO,CAAC,IAAK,YAAc,CACxD,MAAM,OAAS,aAAe,UAAU,YAAA,EAAgB,UAClD,YAAc,WAAW,SAAS,EAEpC,OAAA,MAAM,QAAQ,WAAW,EAAG,IAAI,MAAM,EAAI,YAAY,KAAK,SAAS,EAC/D,IAAA,MAAM,GAAK,aAAe,IAAM,GAElC,GACT,EAAG,CAAE,CAAA,CACP,CAkBO,SAAS,wBACd,WAC0B,CAC1B,OAAO,OAAO,KAAK,UAAU,EAAE,OAAO,CAAC,IAAK,YAAc,CAClD,MAAA,YAAc,WAAW,SAAS,EAExC,WAAI,UAAU,YAAA,CAAa,EAAI,MAAM,QAAQ,WAAW,EACpD,YAAY,IAAI,MAAM,EACtB,CAAC,OAAO,WAAW,CAAC,EAEjB,GACT,EAAG,CAAE,CAAA,CACP,CA4BO,SAAS,iCACd,WAC4B,CACrB,OAAA,OAAO,KAAK,UAAU,EAAE,OAC7B,CAAC,IAAK,YAAc,CACZ,MAAA,YAAc,WAAW,SAAS,EAClC,eAAiB,UAAU,cAE7B,OAAA,MAAM,QAAQ,WAAW,EACvB,iBAAmB,aACrB,IAAI,QAAQ,SAAS,EAAI,YAAY,KAAK,GAAG,EACtC,IAAA,QAAQ,KAAK,GAAG,WAAW,EAEhC,iBAAmB,cAAgB,cAAgB,OACjD,IAAA,QAAQ,KAAK,aAAe,EAAE,EAC/B,IAAI,QAAQ,SAAS,EAAI,OAAO,aAAe,EAAE,EAGjD,GACT,EACA,CACE,QAAS,CAAC,EACV,QAAS,CAAC,CACZ,CAAA,CAEJ,CAWO,SAAS,aACd,QACmC,CACnC,GAAI,CAAC,QAAS,MAAO,GAErB,MAAM,OAAS,CAAA,EACT,WAAa,QAAQ,KAAK,EAAE,MAAM;AAAA,CAAI,EAE5C,QAAS,EAAI,EAAG,EAAI,WAAW,OAAQ,IAAK,CACpC,MAAA,IAAM,WAAW,CAAC,EAClB,MAAQ,IAAI,QAAQ,GAAG,EACvB,IAAM,IAAI,MAAM,EAAG,KAAK,EAAE,OAAO,cACjC,MAAQ,IAAI,MAAM,MAAQ,CAAC,EAAE,OAE/B,OAAO,OAAO,GAAG,EAAM,IAAa,OAAO,GAAG,EAAI,MAC7C,MAAM,QAAQ,OAAO,GAAG,CAAC,EAAU,OAAA,GAAG,EAAE,KAAK,KAAK,EACtD,OAAO,GAAG,EAAI,CAAC,OAAO,GAAG,EAAG,KAAK,CACxC,CAEO,OAAA,MACT"}
|
package/lib/core/path.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path.js","sources":["../../src/core/path.ts"],"sourcesContent":["/**\n * Transform the path and a map of query params to a string with formatted query params\n *\n * @example\n * ```typescript\n * const path = '/pets/search';\n * const queryParams = { batata: undefined, petType: [ 'dog', 'fish' ] };\n * const result = getPathWithQueryStringParams(path, queryParams);\n * console.log(result);\n * // /pets/search?batata=&petType=dog&petType=fish\n * ```\n *\n * @param path - The path\n * @param queryParams - The query params\n *\n * @breadcrumb Core / Path\n * @public\n */\nexport function getPathWithQueryStringParams(\n path: string,\n queryParams:\n | string\n | Record<string, string | string[] | undefined>\n | undefined\n | null,\n): string {\n if (String(queryParams || '').length === 0) return path;\n\n if (typeof queryParams === 'string') return `${path}?${queryParams}`;\n\n const queryParamsString = getQueryParamsStringFromRecord(queryParams);\n\n if (!queryParamsString) return path;\n\n return `${path}?${queryParamsString}`;\n}\n\n/**\n * Map query params to a string with formatted query params\n *\n * @example\n * ```typescript\n * const queryParams = { batata: undefined, petType: [ 'dog', 'fish' ] };\n * const result = getQueryParamsStringFromRecord(queryParams);\n * console.log(result);\n * // batata=&petType=dog&petType=fish\n * ```\n *\n * @param queryParamsRecord - The query params record\n *\n * @breadcrumb Core / Path\n * @public\n */\nexport function getQueryParamsStringFromRecord(\n queryParamsRecord:\n | Record<string, string | string[] | undefined>\n | undefined\n | null,\n): string {\n const searchParams = new URLSearchParams();\n\n const multiValueHeadersEntries: [string, string | string[] | undefined][] =\n Object.entries(queryParamsRecord || {});\n\n if (multiValueHeadersEntries.length === 0) return '';\n\n for (const [key, value] of multiValueHeadersEntries) {\n if (!Array.isArray(value)) {\n searchParams.append(key, value || '');\n continue;\n }\n\n for (const arrayValue of value) searchParams.append(key, arrayValue);\n }\n\n return searchParams.toString();\n}\n\n/**\n * Type of the function to strip base path\n *\n * @breadcrumb Core / Path\n * @public\n */\nexport type StripBasePathFn = (path: string) => string;\n\nconst NOOPBasePath: StripBasePathFn = (path: string) => path;\n\n/**\n * Get the strip base path function\n *\n * @param basePath - The base path\n *\n * @breadcrumb Core / Path\n * @public\n */\nexport function buildStripBasePath(\n basePath: string | undefined,\n): StripBasePathFn {\n if (!basePath) return NOOPBasePath;\n\n const length = basePath.length;\n\n return (path: string) => {\n if (path.startsWith(basePath))\n return path.slice(path.indexOf(basePath) + length, path.length) || '/';\n\n return path;\n };\n}\n"],"names":[],"mappings":"ooBAAA,aAAA,CAAA,EAAA,SAAA,aAAA,CAAA,mBAAA,IAAA,mBAAA,6BAAA,IAAA,6BAAA,+BAAA,IAAA,8BAAA,CAAA,EAAA,OAAA,QAAA,aAAA,YAAA,EAkBO,SAAS,6BACd,KACA,YAKQ,CACR,GAAI,OAAO,aAAe,EAAE,EAAE,SAAW,EAAU,OAAA,KAEnD,GAAI,OAAO,aAAgB,
|
|
1
|
+
{"version":3,"file":"path.js","sources":["../../src/core/path.ts"],"sourcesContent":["/**\n * Transform the path and a map of query params to a string with formatted query params\n *\n * @example\n * ```typescript\n * const path = '/pets/search';\n * const queryParams = { batata: undefined, petType: [ 'dog', 'fish' ] };\n * const result = getPathWithQueryStringParams(path, queryParams);\n * console.log(result);\n * // /pets/search?batata=&petType=dog&petType=fish\n * ```\n *\n * @param path - The path\n * @param queryParams - The query params\n *\n * @breadcrumb Core / Path\n * @public\n */\nexport function getPathWithQueryStringParams(\n path: string,\n queryParams:\n | string\n | Record<string, string | string[] | undefined>\n | undefined\n | null,\n): string {\n if (String(queryParams || '').length === 0) return path;\n\n if (typeof queryParams === 'string') return `${path}?${queryParams}`;\n\n const queryParamsString = getQueryParamsStringFromRecord(queryParams);\n\n if (!queryParamsString) return path;\n\n return `${path}?${queryParamsString}`;\n}\n\n/**\n * Map query params to a string with formatted query params\n *\n * @example\n * ```typescript\n * const queryParams = { batata: undefined, petType: [ 'dog', 'fish' ] };\n * const result = getQueryParamsStringFromRecord(queryParams);\n * console.log(result);\n * // batata=&petType=dog&petType=fish\n * ```\n *\n * @param queryParamsRecord - The query params record\n *\n * @breadcrumb Core / Path\n * @public\n */\nexport function getQueryParamsStringFromRecord(\n queryParamsRecord:\n | Record<string, string | string[] | undefined>\n | undefined\n | null,\n): string {\n const searchParams = new URLSearchParams();\n\n const multiValueHeadersEntries: [string, string | string[] | undefined][] =\n Object.entries(queryParamsRecord || {});\n\n if (multiValueHeadersEntries.length === 0) return '';\n\n for (const [key, value] of multiValueHeadersEntries) {\n if (!Array.isArray(value)) {\n searchParams.append(key, value || '');\n continue;\n }\n\n for (const arrayValue of value) searchParams.append(key, arrayValue);\n }\n\n return searchParams.toString();\n}\n\n/**\n * Type of the function to strip base path\n *\n * @breadcrumb Core / Path\n * @public\n */\nexport type StripBasePathFn = (path: string) => string;\n\nconst NOOPBasePath: StripBasePathFn = (path: string) => path;\n\n/**\n * Get the strip base path function\n *\n * @param basePath - The base path\n *\n * @breadcrumb Core / Path\n * @public\n */\nexport function buildStripBasePath(\n basePath: string | undefined,\n): StripBasePathFn {\n if (!basePath) return NOOPBasePath;\n\n const length = basePath.length;\n\n return (path: string) => {\n if (path.startsWith(basePath))\n return path.slice(path.indexOf(basePath) + length, path.length) || '/';\n\n return path;\n };\n}\n"],"names":[],"mappings":"ooBAAA,aAAA,CAAA,EAAA,SAAA,aAAA,CAAA,mBAAA,IAAA,mBAAA,6BAAA,IAAA,6BAAA,+BAAA,IAAA,8BAAA,CAAA,EAAA,OAAA,QAAA,aAAA,YAAA,EAkBO,SAAS,6BACd,KACA,YAKQ,CACR,GAAI,OAAO,aAAe,EAAE,EAAE,SAAW,EAAU,OAAA,KAEnD,GAAI,OAAO,aAAgB,SAAiB,MAAA,GAAG,IAAI,IAAI,WAAW,GAE5D,MAAA,kBAAoB,+BAA+B,WAAW,EAEpE,OAAK,kBAEE,GAAG,IAAI,IAAI,iBAAiB,GAFJ,IAGjC,CAkBO,SAAS,+BACd,kBAIQ,CACF,MAAA,aAAe,IAAI,gBAEnB,yBACJ,OAAO,QAAQ,mBAAqB,CAAE,CAAA,EAExC,GAAI,yBAAyB,SAAW,EAAU,MAAA,GAElD,SAAW,CAAC,IAAK,KAAK,IAAK,yBAA0B,CACnD,GAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,CACZ,aAAA,OAAO,IAAK,OAAS,EAAE,EACpC,QACF,CAEA,UAAW,cAAc,MAAoB,aAAA,OAAO,IAAK,UAAU,CACrE,CAEA,OAAO,aAAa,UACtB,CAUA,MAAM,aAAiC,MAAiB,KAUjD,SAAS,mBACd,SACiB,CACjB,GAAI,CAAC,SAAiB,OAAA,aAEtB,MAAM,OAAS,SAAS,OAExB,OAAQ,MACF,KAAK,WAAW,QAAQ,EACnB,KAAK,MAAM,KAAK,QAAQ,QAAQ,EAAI,OAAQ,KAAK,MAAM,GAAK,IAE9D,IAEX"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apollo-server.framework.js","sources":["../../../src/frameworks/apollo-server/apollo-server.framework.ts"],"sourcesContent":["//#region\n\nimport { IncomingMessage, ServerResponse } from 'http';\nimport { ApolloServer, BaseContext, HeaderMap } from '@apollo/server';\nimport { FrameworkContract } from '../../contracts';\nimport { ServerlessRequest } from '../../network';\nimport { getDefaultIfUndefined } from '../../core';\n\n//#endregion\n\n/**\n * The default context of Apollo Server when you integrate and don't pass any context.\n *\n * @breadcrumb Frameworks / ApolloServerFramework\n * @public\n */\nexport interface DefaultServerlessApolloServerContext extends BaseContext {\n /**\n * The request reference\n */\n request: IncomingMessage;\n /**\n * The response reference\n */\n response: ServerResponse;\n}\n\n/**\n * The arguments used to create a Context inside {@link ApolloServerOptions}\n *\n * @breadcrumb Frameworks / ApolloServerFramework\n * @public\n */\nexport type ApolloServerContextArguments = {\n /**\n * The request reference\n */\n request: IncomingMessage;\n /**\n * The response reference\n */\n response: ServerResponse;\n};\n\n/**\n * The options to customize {@link ApolloServerFramework}\n *\n * @breadcrumb Frameworks / ApolloServerFramework\n * @public\n */\nexport interface ApolloServerOptions<TContext extends BaseContext> {\n /**\n * Define a function to create the context of Apollo Server\n *\n * @param options - Default options passed by library\n */\n context?: (options: ApolloServerContextArguments) => Promise<TContext>;\n}\n\n/**\n * The framework that forwards requests to Apollo Server\n *\n * @breadcrumb Frameworks / ApolloServerFramework\n * @public\n */\nexport class ApolloServerFramework<TContext extends BaseContext>\n implements FrameworkContract<ApolloServer<TContext>>\n{\n //#region Constructor\n\n /**\n * Construtor padrão\n */\n constructor(protected readonly options?: ApolloServerOptions<TContext>) {}\n\n //#endregion\n\n /**\n * {@inheritDoc}\n */\n public sendRequest(\n app: ApolloServer<TContext>,\n request: ServerlessRequest,\n response: ServerResponse,\n ): void {\n const headers = new HeaderMap();\n\n for (const [key, value] of Object.entries(request.headers)) {\n if (value === undefined) continue;\n\n headers.set(\n key,\n Array.isArray(value) ? value.join(', ') : value.toString(),\n );\n }\n\n const defaultContext: ApolloServerOptions<any>['context'] = context =>\n Promise.resolve(context);\n\n const context = () =>\n getDefaultIfUndefined(\n this.options?.context,\n defaultContext,\n )({ request, response });\n\n const search = request.url?.startsWith('http')\n ? new URL(request.url).search ?? ''\n : request.url?.split('?')[1] || '';\n\n // we don't need to handle catch because of https://www.apollographql.com/docs/apollo-server/integrations/building-integrations/#handle-errors\n app\n .executeHTTPGraphQLRequest({\n httpGraphQLRequest: {\n method: request.method!.toUpperCase(),\n headers,\n body: request.body,\n search,\n },\n context,\n })\n .then(async httpGraphQLResponse => {\n // this section was copy and pasted from https://github.com/apollographql/apollo-server/blob/main/packages/server/src/express4/index.ts#L95\n\n for (const [key, value] of httpGraphQLResponse.headers)\n response.setHeader(key, value);\n\n response.statusCode = httpGraphQLResponse.status || 200;\n\n if (httpGraphQLResponse.body.kind === 'complete') {\n response.end(httpGraphQLResponse.body.string);\n return;\n }\n\n for await (const chunk of httpGraphQLResponse.body.asyncIterator) {\n response.write(chunk);\n // Express/Node doesn't define a way of saying \"it's time to send this\n // data over the wire\"... but the popular `compression` middleware\n // (which implements `accept-encoding: gzip` and friends) does, by\n // monkey-patching a `flush` method onto the response. So we call it\n // if it's there.\n if (typeof (response as any).flush === 'function')\n (response as any).flush();\n }\n\n response.end();\n });\n }\n}\n"],"names":["context","_a"],"mappings":"ooBAAA,gCAAA,CAAA,EAAA,SAAA,gCAAA,CAAA,sBAAA,IAAA,qBAAA,CAAA,EAAA,OAAA,QAAA,aAAA,+BAAA,EAGA,IAAqD,cAAA,QAAA,gBAAA,EAGf,YAAA,QAAA,YAAA,EA2D/B,MAAM,qBAEb,CAME,YAA+B,QAAyC,CAAzC,KAAA,QAAA,OAA0C,CAOlE,YACL,IACA,QACA,SACM,WACA,MAAA,QAAU,IAAI,cAAU,UAEnB,SAAA,CAAC,IAAK,KAAK,IAAK,OAAO,QAAQ,QAAQ,OAAO,EACnD,QAAU,QAEN,QAAA,IACN,IACA,MAAM,QAAQ,KAAK,EAAI,MAAM,KAAK,IAAI,EAAI,MAAM,SAAS,CAAA,EAI7D,MAAM,eAAsDA,UAC1D,QAAQ,QAAQA,QAAO,EAEnB,QAAU,aACd,SAAA,YAAA,wBACEC,IAAA,KAAK,UAAL,YAAAA,IAAc,QACd,cAAA,EACA,CAAE,QAAS,QAAA,CAAU,GAEnB,QAAS,WAAQ,MAAR,SAAa,WAAW,QACnC,IAAI,IAAI,QAAQ,GAAG,EAAE,QAAU,KAC/B,WAAQ,MAAR,eAAa,MAAM,KAAK,KAAM,GAGlC,IACG,0BAA0B,CACzB,mBAAoB,CAClB,OAAQ,QAAQ,OAAQ,YAAY,EACpC,QACA,KAAM,QAAQ,KACd,MACF,EACA,OAAA,CACD,EACA,KAAK,MAAM,qBAAuB,CAGjC,SAAW,CAAC,IAAK,KAAK,IAAK,oBAAoB,QACpC,SAAA,UAAU,IAAK,KAAK,EAI3B,GAFK,SAAA,WAAa,oBAAoB,QAAU,IAEhD,oBAAoB,KAAK,OAAS,WAAY,CACvC,SAAA,IAAI,oBAAoB,KAAK,MAAM,EAC5C,
|
|
1
|
+
{"version":3,"file":"apollo-server.framework.js","sources":["../../../src/frameworks/apollo-server/apollo-server.framework.ts"],"sourcesContent":["//#region\n\nimport { IncomingMessage, ServerResponse } from 'http';\nimport { ApolloServer, BaseContext, HeaderMap } from '@apollo/server';\nimport { FrameworkContract } from '../../contracts';\nimport { ServerlessRequest } from '../../network';\nimport { getDefaultIfUndefined } from '../../core';\n\n//#endregion\n\n/**\n * The default context of Apollo Server when you integrate and don't pass any context.\n *\n * @breadcrumb Frameworks / ApolloServerFramework\n * @public\n */\nexport interface DefaultServerlessApolloServerContext extends BaseContext {\n /**\n * The request reference\n */\n request: IncomingMessage;\n /**\n * The response reference\n */\n response: ServerResponse;\n}\n\n/**\n * The arguments used to create a Context inside {@link ApolloServerOptions}\n *\n * @breadcrumb Frameworks / ApolloServerFramework\n * @public\n */\nexport type ApolloServerContextArguments = {\n /**\n * The request reference\n */\n request: IncomingMessage;\n /**\n * The response reference\n */\n response: ServerResponse;\n};\n\n/**\n * The options to customize {@link ApolloServerFramework}\n *\n * @breadcrumb Frameworks / ApolloServerFramework\n * @public\n */\nexport interface ApolloServerOptions<TContext extends BaseContext> {\n /**\n * Define a function to create the context of Apollo Server\n *\n * @param options - Default options passed by library\n */\n context?: (options: ApolloServerContextArguments) => Promise<TContext>;\n}\n\n/**\n * The framework that forwards requests to Apollo Server\n *\n * @breadcrumb Frameworks / ApolloServerFramework\n * @public\n */\nexport class ApolloServerFramework<TContext extends BaseContext>\n implements FrameworkContract<ApolloServer<TContext>>\n{\n //#region Constructor\n\n /**\n * Construtor padrão\n */\n constructor(protected readonly options?: ApolloServerOptions<TContext>) {}\n\n //#endregion\n\n /**\n * {@inheritDoc}\n */\n public sendRequest(\n app: ApolloServer<TContext>,\n request: ServerlessRequest,\n response: ServerResponse,\n ): void {\n const headers = new HeaderMap();\n\n for (const [key, value] of Object.entries(request.headers)) {\n if (value === undefined) continue;\n\n headers.set(\n key,\n Array.isArray(value) ? value.join(', ') : value.toString(),\n );\n }\n\n const defaultContext: ApolloServerOptions<any>['context'] = context =>\n Promise.resolve(context);\n\n const context = () =>\n getDefaultIfUndefined(\n this.options?.context,\n defaultContext,\n )({ request, response });\n\n const search = request.url?.startsWith('http')\n ? new URL(request.url).search ?? ''\n : request.url?.split('?')[1] || '';\n\n // we don't need to handle catch because of https://www.apollographql.com/docs/apollo-server/integrations/building-integrations/#handle-errors\n app\n .executeHTTPGraphQLRequest({\n httpGraphQLRequest: {\n method: request.method!.toUpperCase(),\n headers,\n body: request.body,\n search,\n },\n context,\n })\n .then(async httpGraphQLResponse => {\n // this section was copy and pasted from https://github.com/apollographql/apollo-server/blob/main/packages/server/src/express4/index.ts#L95\n\n for (const [key, value] of httpGraphQLResponse.headers)\n response.setHeader(key, value);\n\n response.statusCode = httpGraphQLResponse.status || 200;\n\n if (httpGraphQLResponse.body.kind === 'complete') {\n response.end(httpGraphQLResponse.body.string);\n return;\n }\n\n for await (const chunk of httpGraphQLResponse.body.asyncIterator) {\n response.write(chunk);\n // Express/Node doesn't define a way of saying \"it's time to send this\n // data over the wire\"... but the popular `compression` middleware\n // (which implements `accept-encoding: gzip` and friends) does, by\n // monkey-patching a `flush` method onto the response. So we call it\n // if it's there.\n if (typeof (response as any).flush === 'function')\n (response as any).flush();\n }\n\n response.end();\n });\n }\n}\n"],"names":["context","_a"],"mappings":"ooBAAA,gCAAA,CAAA,EAAA,SAAA,gCAAA,CAAA,sBAAA,IAAA,qBAAA,CAAA,EAAA,OAAA,QAAA,aAAA,+BAAA,EAGA,IAAqD,cAAA,QAAA,gBAAA,EAGf,YAAA,QAAA,YAAA,EA2D/B,MAAM,qBAEb,CAME,YAA+B,QAAyC,CAAzC,KAAA,QAAA,OAA0C,CAOlE,YACL,IACA,QACA,SACM,WACA,MAAA,QAAU,IAAI,cAAU,UAEnB,SAAA,CAAC,IAAK,KAAK,IAAK,OAAO,QAAQ,QAAQ,OAAO,EACnD,QAAU,QAEN,QAAA,IACN,IACA,MAAM,QAAQ,KAAK,EAAI,MAAM,KAAK,IAAI,EAAI,MAAM,SAAS,CAAA,EAI7D,MAAM,eAAsDA,UAC1D,QAAQ,QAAQA,QAAO,EAEnB,QAAU,aACd,SAAA,YAAA,wBACEC,IAAA,KAAK,UAAL,YAAAA,IAAc,QACd,cAAA,EACA,CAAE,QAAS,QAAA,CAAU,GAEnB,QAAS,WAAQ,MAAR,SAAa,WAAW,QACnC,IAAI,IAAI,QAAQ,GAAG,EAAE,QAAU,KAC/B,WAAQ,MAAR,eAAa,MAAM,KAAK,KAAM,GAGlC,IACG,0BAA0B,CACzB,mBAAoB,CAClB,OAAQ,QAAQ,OAAQ,YAAY,EACpC,QACA,KAAM,QAAQ,KACd,MACF,EACA,OAAA,CACD,EACA,KAAK,MAAM,qBAAuB,CAGjC,SAAW,CAAC,IAAK,KAAK,IAAK,oBAAoB,QACpC,SAAA,UAAU,IAAK,KAAK,EAI3B,GAFK,SAAA,WAAa,oBAAoB,QAAU,IAEhD,oBAAoB,KAAK,OAAS,WAAY,CACvC,SAAA,IAAI,oBAAoB,KAAK,MAAM,EAC5C,MACF,CAEiB,gBAAA,SAAS,oBAAoB,KAAK,cACjD,SAAS,MAAM,KAAK,EAMhB,OAAQ,SAAiB,OAAU,YACpC,SAAiB,MAAM,EAG5B,SAAS,IAAI,CAAA,CACd,CACL,CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cors.framework.js","sources":["../../../src/frameworks/cors/cors.framework.ts"],"sourcesContent":["//#region Imports\n\nimport { IncomingMessage, ServerResponse } from 'http';\nimport cors, { CorsOptions } from 'cors';\nimport { FrameworkContract } from '../../contracts';\nimport { getDefaultIfUndefined } from '../../core';\n\n//#endregion\n\n/**\n * The options to customize {@link CorsFramework}\n *\n * @breadcrumb Frameworks / CorsFramework\n * @public\n */\nexport type CorsFrameworkOptions = CorsOptions & {\n /**\n * Send error 403 when cors is invalid. From what I read in `cors`, `fastify/cors` and [this problem](https://stackoverflow.com/questions/57212248/why-is-http-request-been-processed-in-action-even-when-cors-is-not-enabled)\n * it is normal to process the request even if the origin is invalid.\n * So this option will respond with error if this method was called from an invalid origin (or not allowed method) like [access control lib](https://github.com/primus/access-control/blob/master/index.js#L95-L115) .\n *\n * @defaultValue true\n */\n forbiddenOnInvalidOriginOrMethod?: boolean;\n};\n\n/**\n * The framework that handles cors for your api without relying on internals of the framework\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { ServerlessAdapter } from '@h4ad/serverless-adapter';\n * import { ExpressFramework } from '@h4ad/serverless-adapter/lib/frameworks/express';\n * import { CorsFramework } from '@h4ad/serverless-adapter/lib/frameworks/cors';\n *\n * const expressFramework = new ExpressFramework();\n * const options: CorsOptions = {}; // customize the options\n * const framework = new CorsFramework(expressFramework, options);\n *\n * export const handler = ServerlessAdapter.new(null)\n * .setFramework(framework)\n * // set other configurations and then build\n * .build();\n * ```\n *\n * @breadcrumb Frameworks / CorsFramework\n * @public\n */\nexport class CorsFramework<TApp> implements FrameworkContract<TApp> {\n //#region Constructor\n\n /**\n * Default Constructor\n */\n constructor(\n protected readonly framework: FrameworkContract<TApp>,\n protected readonly options?: CorsFrameworkOptions,\n ) {\n this.cachedCorsInstance = cors(this.options);\n }\n\n //#endregion\n\n /**\n * All cors headers that can be added by cors package\n */\n protected readonly corsHeaders: string[] = [\n 'Access-Control-Max-Age',\n 'Access-Control-Expose-Headers',\n 'Access-Control-Allow-Headers',\n 'Access-Control-Request-Headers',\n 'Access-Control-Allow-Credentials',\n 'Access-Control-Allow-Methods',\n 'Access-Control-Allow-Origin',\n ];\n\n /**\n * The cached instance of cors\n */\n protected readonly cachedCorsInstance: ReturnType<typeof cors>;\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public sendRequest(\n app: TApp,\n request: IncomingMessage,\n response: ServerResponse,\n ): void {\n this.cachedCorsInstance(\n request,\n response,\n this.onCorsNext(app, request, response),\n );\n }\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * Handle next execution called by the cors package\n */\n protected onCorsNext(\n app: TApp,\n request: IncomingMessage,\n response: ServerResponse,\n ): () => void {\n return () => {\n this.formatHeaderValuesAddedByCorsPackage(response);\n\n const errorOnInvalidOrigin = getDefaultIfUndefined(\n this.options?.forbiddenOnInvalidOriginOrMethod,\n true,\n );\n\n if (errorOnInvalidOrigin) {\n const allowedOrigin = response.getHeader('access-control-allow-origin');\n const isInvalidOrigin = this.isInvalidOriginOrMethodIsNotAllowed(\n request,\n allowedOrigin,\n );\n\n if (isInvalidOrigin) {\n response.statusCode = 403;\n response.setHeader('Content-Type', 'text/plain');\n response.end(\n [\n 'Invalid HTTP Access Control (CORS) request:',\n ` Origin: ${request.headers.origin}`,\n ` Method: ${request.method}`,\n ].join('\\n'),\n );\n\n return;\n }\n }\n\n this.framework.sendRequest(app, request, response);\n };\n }\n\n /**\n * Format the headers to be standardized with the rest of the library, such as ApiGatewayV2.\n * Also, some frameworks don't support headers as an array, so we need to format the values.\n */\n protected formatHeaderValuesAddedByCorsPackage(\n response: ServerResponse,\n ): void {\n for (const corsHeader of this.corsHeaders) {\n const value = response.getHeader(corsHeader);\n\n if (value === undefined) continue;\n\n response.removeHeader(corsHeader);\n response.setHeader(\n corsHeader.toLowerCase(),\n Array.isArray(value) ? value.join(',') : value,\n );\n }\n }\n\n /**\n * Check if the origin is invalid or if the method is not allowed.\n * Highly inspired by [access-control](https://github.com/primus/access-control/blob/master/index.js#L95-L115)\n */\n protected isInvalidOriginOrMethodIsNotAllowed(\n request: IncomingMessage,\n allowedOrigin: number | string | string[] | undefined,\n ): boolean {\n if (!allowedOrigin) return true;\n\n if (\n !!request.headers.origin &&\n allowedOrigin !== '*' &&\n request.headers.origin !== allowedOrigin\n )\n return true;\n\n const notPermitedInMethods =\n this.options &&\n Array.isArray(this.options.methods) &&\n this.options.methods.every(\n m => m.toLowerCase() !== request.method?.toLowerCase(),\n );\n const differentMethod =\n this.options &&\n typeof this.options.methods === 'string' &&\n this.options.methods\n .split(',')\n .every(m => m.trim().toLowerCase() !== request.method?.toLowerCase());\n\n if (this.options?.methods && (notPermitedInMethods || differentMethod))\n return true;\n\n return false;\n }\n\n //#endregion\n}\n"],"names":["cors","_a"],"mappings":"i4BAAA,uBAAA,CAAA,EAAA,SAAA,uBAAA,CAAA,cAAA,IAAA,aAAA,CAAA,EAAA,OAAA,QAAA,aAAA,sBAAA,EAGA,IAAkC,YAAA,QAAA,QAAA,MAAA,CAAA,EAEI,YAAA,QAAA,YAAA,EA4C/B,MAAM,aAAuD,CAMlE,YACqB,UACA,QACnB,CAFmB,KAAA,UAAA,UACA,KAAA,QAAA,QAUrB,KAAmB,YAAwB,CACzC,yBACA,gCACA,+BACA,iCACA,mCACA,+BACA,6BAAA,EAfA,KAAK,sBAAqB,YAAAA,SAAK,KAAK,OAAO,CAC7C,CA2BO,YACL,IACA,QACA,SACM,CACD,KAAA,mBACH,QACA,SACA,KAAK,WAAW,IAAK,QAAS,QAAQ,CAAA,CAE1C,CASU,WACR,IACA,QACA,SACY,CACZ,MAAO,IAAM,QAQX,GAPA,KAAK,qCAAqC,QAAQ,KAErB,YAAA,wBAC3B,QAAK,UAAL,eAAc,iCACd,EAAA,EAGwB,CAClB,MAAA,cAAgB,SAAS,UAAU,6BAA6B,EAMtE,GALwB,KAAK,oCAC3B,QACA,aAAA,EAGmB,CACnB,SAAS,WAAa,IACb,SAAA,UAAU,eAAgB,YAAY,EACtC,SAAA,IACP,CACE,8CACA,aAAa,QAAQ,QAAQ,
|
|
1
|
+
{"version":3,"file":"cors.framework.js","sources":["../../../src/frameworks/cors/cors.framework.ts"],"sourcesContent":["//#region Imports\n\nimport { IncomingMessage, ServerResponse } from 'http';\nimport cors, { CorsOptions } from 'cors';\nimport { FrameworkContract } from '../../contracts';\nimport { getDefaultIfUndefined } from '../../core';\n\n//#endregion\n\n/**\n * The options to customize {@link CorsFramework}\n *\n * @breadcrumb Frameworks / CorsFramework\n * @public\n */\nexport type CorsFrameworkOptions = CorsOptions & {\n /**\n * Send error 403 when cors is invalid. From what I read in `cors`, `fastify/cors` and [this problem](https://stackoverflow.com/questions/57212248/why-is-http-request-been-processed-in-action-even-when-cors-is-not-enabled)\n * it is normal to process the request even if the origin is invalid.\n * So this option will respond with error if this method was called from an invalid origin (or not allowed method) like [access control lib](https://github.com/primus/access-control/blob/master/index.js#L95-L115) .\n *\n * @defaultValue true\n */\n forbiddenOnInvalidOriginOrMethod?: boolean;\n};\n\n/**\n * The framework that handles cors for your api without relying on internals of the framework\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { ServerlessAdapter } from '@h4ad/serverless-adapter';\n * import { ExpressFramework } from '@h4ad/serverless-adapter/lib/frameworks/express';\n * import { CorsFramework } from '@h4ad/serverless-adapter/lib/frameworks/cors';\n *\n * const expressFramework = new ExpressFramework();\n * const options: CorsOptions = {}; // customize the options\n * const framework = new CorsFramework(expressFramework, options);\n *\n * export const handler = ServerlessAdapter.new(null)\n * .setFramework(framework)\n * // set other configurations and then build\n * .build();\n * ```\n *\n * @breadcrumb Frameworks / CorsFramework\n * @public\n */\nexport class CorsFramework<TApp> implements FrameworkContract<TApp> {\n //#region Constructor\n\n /**\n * Default Constructor\n */\n constructor(\n protected readonly framework: FrameworkContract<TApp>,\n protected readonly options?: CorsFrameworkOptions,\n ) {\n this.cachedCorsInstance = cors(this.options);\n }\n\n //#endregion\n\n /**\n * All cors headers that can be added by cors package\n */\n protected readonly corsHeaders: string[] = [\n 'Access-Control-Max-Age',\n 'Access-Control-Expose-Headers',\n 'Access-Control-Allow-Headers',\n 'Access-Control-Request-Headers',\n 'Access-Control-Allow-Credentials',\n 'Access-Control-Allow-Methods',\n 'Access-Control-Allow-Origin',\n ];\n\n /**\n * The cached instance of cors\n */\n protected readonly cachedCorsInstance: ReturnType<typeof cors>;\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public sendRequest(\n app: TApp,\n request: IncomingMessage,\n response: ServerResponse,\n ): void {\n this.cachedCorsInstance(\n request,\n response,\n this.onCorsNext(app, request, response),\n );\n }\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * Handle next execution called by the cors package\n */\n protected onCorsNext(\n app: TApp,\n request: IncomingMessage,\n response: ServerResponse,\n ): () => void {\n return () => {\n this.formatHeaderValuesAddedByCorsPackage(response);\n\n const errorOnInvalidOrigin = getDefaultIfUndefined(\n this.options?.forbiddenOnInvalidOriginOrMethod,\n true,\n );\n\n if (errorOnInvalidOrigin) {\n const allowedOrigin = response.getHeader('access-control-allow-origin');\n const isInvalidOrigin = this.isInvalidOriginOrMethodIsNotAllowed(\n request,\n allowedOrigin,\n );\n\n if (isInvalidOrigin) {\n response.statusCode = 403;\n response.setHeader('Content-Type', 'text/plain');\n response.end(\n [\n 'Invalid HTTP Access Control (CORS) request:',\n ` Origin: ${request.headers.origin}`,\n ` Method: ${request.method}`,\n ].join('\\n'),\n );\n\n return;\n }\n }\n\n this.framework.sendRequest(app, request, response);\n };\n }\n\n /**\n * Format the headers to be standardized with the rest of the library, such as ApiGatewayV2.\n * Also, some frameworks don't support headers as an array, so we need to format the values.\n */\n protected formatHeaderValuesAddedByCorsPackage(\n response: ServerResponse,\n ): void {\n for (const corsHeader of this.corsHeaders) {\n const value = response.getHeader(corsHeader);\n\n if (value === undefined) continue;\n\n response.removeHeader(corsHeader);\n response.setHeader(\n corsHeader.toLowerCase(),\n Array.isArray(value) ? value.join(',') : value,\n );\n }\n }\n\n /**\n * Check if the origin is invalid or if the method is not allowed.\n * Highly inspired by [access-control](https://github.com/primus/access-control/blob/master/index.js#L95-L115)\n */\n protected isInvalidOriginOrMethodIsNotAllowed(\n request: IncomingMessage,\n allowedOrigin: number | string | string[] | undefined,\n ): boolean {\n if (!allowedOrigin) return true;\n\n if (\n !!request.headers.origin &&\n allowedOrigin !== '*' &&\n request.headers.origin !== allowedOrigin\n )\n return true;\n\n const notPermitedInMethods =\n this.options &&\n Array.isArray(this.options.methods) &&\n this.options.methods.every(\n m => m.toLowerCase() !== request.method?.toLowerCase(),\n );\n const differentMethod =\n this.options &&\n typeof this.options.methods === 'string' &&\n this.options.methods\n .split(',')\n .every(m => m.trim().toLowerCase() !== request.method?.toLowerCase());\n\n if (this.options?.methods && (notPermitedInMethods || differentMethod))\n return true;\n\n return false;\n }\n\n //#endregion\n}\n"],"names":["cors","_a"],"mappings":"i4BAAA,uBAAA,CAAA,EAAA,SAAA,uBAAA,CAAA,cAAA,IAAA,aAAA,CAAA,EAAA,OAAA,QAAA,aAAA,sBAAA,EAGA,IAAkC,YAAA,QAAA,QAAA,MAAA,CAAA,EAEI,YAAA,QAAA,YAAA,EA4C/B,MAAM,aAAuD,CAMlE,YACqB,UACA,QACnB,CAFmB,KAAA,UAAA,UACA,KAAA,QAAA,QAUrB,KAAmB,YAAwB,CACzC,yBACA,gCACA,+BACA,iCACA,mCACA,+BACA,6BAAA,EAfA,KAAK,sBAAqB,YAAAA,SAAK,KAAK,OAAO,CAC7C,CA2BO,YACL,IACA,QACA,SACM,CACD,KAAA,mBACH,QACA,SACA,KAAK,WAAW,IAAK,QAAS,QAAQ,CAAA,CAE1C,CASU,WACR,IACA,QACA,SACY,CACZ,MAAO,IAAM,QAQX,GAPA,KAAK,qCAAqC,QAAQ,KAErB,YAAA,wBAC3B,QAAK,UAAL,eAAc,iCACd,EAAA,EAGwB,CAClB,MAAA,cAAgB,SAAS,UAAU,6BAA6B,EAMtE,GALwB,KAAK,oCAC3B,QACA,aAAA,EAGmB,CACnB,SAAS,WAAa,IACb,SAAA,UAAU,eAAgB,YAAY,EACtC,SAAA,IACP,CACE,8CACA,aAAa,QAAQ,QAAQ,MAAM,GACnC,aAAa,QAAQ,MAAM,EAAA,EAC3B,KAAK;AAAA,CAAI,CAAA,EAGb,MACF,CACF,CAEA,KAAK,UAAU,YAAY,IAAK,QAAS,QAAQ,CAAA,CAErD,CAMU,qCACR,SACM,CACK,UAAA,cAAc,KAAK,YAAa,CACnC,MAAA,MAAQ,SAAS,UAAU,UAAU,EAEvC,QAAU,SAEd,SAAS,aAAa,UAAU,EACvB,SAAA,UACP,WAAW,YAAY,EACvB,MAAM,QAAQ,KAAK,EAAI,MAAM,KAAK,GAAG,EAAI,KAAA,EAE7C,CACF,CAMU,oCACR,QACA,cACS,QAIP,GAHE,CAAC,eAGD,QAAQ,QAAQ,QAClB,gBAAkB,KAClB,QAAQ,QAAQ,SAAW,cAEpB,MAAA,GAEH,MAAA,qBACJ,KAAK,SACL,MAAM,QAAQ,KAAK,QAAQ,OAAO,GAClC,KAAK,QAAQ,QAAQ,SACd,SAAA,SAAE,YAAkB,MAAAC,IAAA,QAAQ,SAAR,YAAAA,IAAgB,eAAY,EAEnD,gBACJ,KAAK,SACL,OAAO,KAAK,QAAQ,SAAY,UAChC,KAAK,QAAQ,QACV,MAAM,GAAG,EACT,MAAW,GAAA,SAAA,SAAE,KAAK,EAAE,kBAAkBA,IAAA,QAAQ,SAAR,YAAAA,IAAgB,eAAa,EAEpE,MAAA,YAAK,UAAL,SAAc,UAAY,sBAAwB,iBAIxD,CAGF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"raw-request.js","sources":["../../../src/handlers/base/raw-request.ts"],"sourcesContent":["//#region Imports\n\nimport { IncomingMessage, ServerResponse } from 'http';\nimport { FrameworkContract } from '../../contracts/index';\nimport { ServerlessRequest } from '../../network/index';\nimport { getEventBodyAsBuffer, getFlattenedHeadersMap } from '../../core/index';\n\n//#endregion\n\n/**\n * The class that expose some methods to be used to get raw request from Express HTTP Request\n *\n * @breadcrumb Handlers / Base / RawRequest\n * @public\n */\nexport abstract class RawRequest<TApp> {\n //#region Protected Methods\n\n /**\n * The callback to when receive some request from external source\n *\n * @param app - The instance of the app\n * @param framework - The framework for the app\n */\n protected onRequestCallback(\n app: TApp,\n framework: FrameworkContract<TApp>,\n ): (req: IncomingMessage, res: ServerResponse) => void | Promise<void> {\n return (request: IncomingMessage, response: ServerResponse) => {\n const customRequest = this.getRequestFromExpressRequest(request);\n\n return framework.sendRequest(app, customRequest, response);\n };\n }\n\n /**\n * Not sure why they think using Express instance with prebuilt middlewares was a good idea, but Firebase/GCP\n * decides to use `Express` and `body-parser` by default, so you don't get a raw request, instead, you get a modified version by\n * Express and also with the body parsed by `body-parser`.\n * If you use NestJS or Express it's awesome, but for the rest of the frameworks it's terrible!\n * That's why I have this method, just to try and create a raw request to be used and passed to the frameworks so they can handle the request\n * as if they received the request from the native http module.\n *\n * @param request - The Express request\n */\n protected getRequestFromExpressRequest(\n request: IncomingMessage,\n ): ServerlessRequest {\n const expressRequestParsed = request as unknown as {\n body: object | Buffer;\n };\n\n const headers = getFlattenedHeadersMap(request.headers, ',', true);\n const remoteAddress = headers['x-forwarded-for'];\n\n let body: Buffer | undefined;\n\n if (\n expressRequestParsed.body &&\n typeof expressRequestParsed.body === 'object'\n ) {\n const jsonContent = JSON.stringify(expressRequestParsed.body);\n\n const [bufferBody, contentLength] = getEventBodyAsBuffer(\n jsonContent,\n false,\n );\n\n body = bufferBody;\n headers['content-length'] = String(contentLength);\n }\n\n return new ServerlessRequest({\n method: request.method!,\n url: request.url!,\n body,\n headers,\n remoteAddress,\n });\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"ooBAAA,oBAAA,CAAA,EAAA,SAAA,oBAAA,CAAA,WAAA,IAAA,UAAA,CAAA,EAAA,OAAA,QAAA,aAAA,mBAAA,EAIA,IAAkC,eAAA,QAAA,qBAAA,EAC2B,YAAA,QAAA,kBAAA,EAUtD,MAAe,UAAiB,CAS3B,kBACR,IACA,UACqE,CAC9D,MAAA,CAAC,QAA0B,WAA6B,CACvD,MAAA,cAAgB,KAAK,6BAA6B,OAAO,EAE/D,OAAO,UAAU,YAAY,IAAK,cAAe,QAAQ,CAAA,CAE7D,CAYU,6BACR,QACmB,CACnB,MAAM,qBAAuB,QAIvB,WAAU,YAAA,wBAAuB,QAAQ,QAAS,IAAK,EAAI,EAC3D,cAAgB,QAAQ,iBAAiB,EAE3C,IAAA,KAEJ,GACE,qBAAqB,MACrB,OAAO,qBAAqB,MAAS,SACrC,CACA,MAAM,YAAc,KAAK,UAAU,qBAAqB,IAAI,EAEtD,CAAC,WAAY,aAAa,KAAI,YAAA,sBAClC,YACA,EAAA,EAGK,KAAA,WACC,QAAA,gBAAgB,EAAI,OAAO,aAAa,
|
|
1
|
+
{"version":3,"file":"raw-request.js","sources":["../../../src/handlers/base/raw-request.ts"],"sourcesContent":["//#region Imports\n\nimport { IncomingMessage, ServerResponse } from 'http';\nimport { FrameworkContract } from '../../contracts/index';\nimport { ServerlessRequest } from '../../network/index';\nimport { getEventBodyAsBuffer, getFlattenedHeadersMap } from '../../core/index';\n\n//#endregion\n\n/**\n * The class that expose some methods to be used to get raw request from Express HTTP Request\n *\n * @breadcrumb Handlers / Base / RawRequest\n * @public\n */\nexport abstract class RawRequest<TApp> {\n //#region Protected Methods\n\n /**\n * The callback to when receive some request from external source\n *\n * @param app - The instance of the app\n * @param framework - The framework for the app\n */\n protected onRequestCallback(\n app: TApp,\n framework: FrameworkContract<TApp>,\n ): (req: IncomingMessage, res: ServerResponse) => void | Promise<void> {\n return (request: IncomingMessage, response: ServerResponse) => {\n const customRequest = this.getRequestFromExpressRequest(request);\n\n return framework.sendRequest(app, customRequest, response);\n };\n }\n\n /**\n * Not sure why they think using Express instance with prebuilt middlewares was a good idea, but Firebase/GCP\n * decides to use `Express` and `body-parser` by default, so you don't get a raw request, instead, you get a modified version by\n * Express and also with the body parsed by `body-parser`.\n * If you use NestJS or Express it's awesome, but for the rest of the frameworks it's terrible!\n * That's why I have this method, just to try and create a raw request to be used and passed to the frameworks so they can handle the request\n * as if they received the request from the native http module.\n *\n * @param request - The Express request\n */\n protected getRequestFromExpressRequest(\n request: IncomingMessage,\n ): ServerlessRequest {\n const expressRequestParsed = request as unknown as {\n body: object | Buffer;\n };\n\n const headers = getFlattenedHeadersMap(request.headers, ',', true);\n const remoteAddress = headers['x-forwarded-for'];\n\n let body: Buffer | undefined;\n\n if (\n expressRequestParsed.body &&\n typeof expressRequestParsed.body === 'object'\n ) {\n const jsonContent = JSON.stringify(expressRequestParsed.body);\n\n const [bufferBody, contentLength] = getEventBodyAsBuffer(\n jsonContent,\n false,\n );\n\n body = bufferBody;\n headers['content-length'] = String(contentLength);\n }\n\n return new ServerlessRequest({\n method: request.method!,\n url: request.url!,\n body,\n headers,\n remoteAddress,\n });\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"ooBAAA,oBAAA,CAAA,EAAA,SAAA,oBAAA,CAAA,WAAA,IAAA,UAAA,CAAA,EAAA,OAAA,QAAA,aAAA,mBAAA,EAIA,IAAkC,eAAA,QAAA,qBAAA,EAC2B,YAAA,QAAA,kBAAA,EAUtD,MAAe,UAAiB,CAS3B,kBACR,IACA,UACqE,CAC9D,MAAA,CAAC,QAA0B,WAA6B,CACvD,MAAA,cAAgB,KAAK,6BAA6B,OAAO,EAE/D,OAAO,UAAU,YAAY,IAAK,cAAe,QAAQ,CAAA,CAE7D,CAYU,6BACR,QACmB,CACnB,MAAM,qBAAuB,QAIvB,WAAU,YAAA,wBAAuB,QAAQ,QAAS,IAAK,EAAI,EAC3D,cAAgB,QAAQ,iBAAiB,EAE3C,IAAA,KAEJ,GACE,qBAAqB,MACrB,OAAO,qBAAqB,MAAS,SACrC,CACA,MAAM,YAAc,KAAK,UAAU,qBAAqB,IAAI,EAEtD,CAAC,WAAY,aAAa,KAAI,YAAA,sBAClC,YACA,EAAA,EAGK,KAAA,WACC,QAAA,gBAAgB,EAAI,OAAO,aAAa,CAClD,CAEA,OAAO,IAAI,eAAkB,kBAAA,CAC3B,OAAQ,QAAQ,OAChB,IAAK,QAAQ,IACb,KACA,QACA,aAAA,CACD,CACH,CAGF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-firebase.handler.d.ts","sourceRoot":"","sources":["../../../src/handlers/firebase/http-firebase.handler.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"http-firebase.handler.d.ts","sourceRoot":"","sources":["../../../src/handlers/firebase/http-firebase.handler.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAGvD,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAGrC;;;;;;;GAOG;AACH,qBAAa,mBAAmB,CAAC,IAAI,CACnC,SAAQ,UAAU,CAAC,IAAI,CACvB,YACE,eAAe,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAIxE;;OAEG;IACI,UAAU,CACf,GAAG,EAAE,IAAI,EACT,SAAS,EAAE,iBAAiB,CAAC,IAAI,CAAC,GACjC,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;CAKvE"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var __defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod),http_firebase_handler_exports={};__export(http_firebase_handler_exports,{HttpFirebaseHandler:()=>HttpFirebaseHandler});module.exports=__toCommonJS(http_firebase_handler_exports);var
|
|
1
|
+
"use strict";var __defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod),http_firebase_handler_exports={};__export(http_firebase_handler_exports,{HttpFirebaseHandler:()=>HttpFirebaseHandler});module.exports=__toCommonJS(http_firebase_handler_exports);var import_v1=require("firebase-functions/v1"),import_base=require("../base");class HttpFirebaseHandler extends import_base.RawRequest{getHandler(app,framework){return import_v1.https.onRequest(this.onRequestCallback(app,framework))}}
|
|
2
2
|
//# sourceMappingURL=http-firebase.handler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-firebase.handler.js","sources":["../../../src/handlers/firebase/http-firebase.handler.ts"],"sourcesContent":["//#region Imports\n\nimport { IncomingMessage, ServerResponse } from 'http';\nimport { https } from 'firebase-functions';\nimport { FrameworkContract, HandlerContract } from '../../contracts';\nimport { RawRequest } from '../base';\n\n//#endregion\n/**\n * The class that implements a handler for Firebase Https Events\n *\n * @remarks Read more about Https Events {@link https://firebase.google.com/docs/functions/http-events | here}\n *\n * @breadcrumb Handlers / HttpFirebaseHandler\n * @public\n */\nexport class HttpFirebaseHandler<TApp>\n extends RawRequest<TApp>\n implements\n HandlerContract<TApp, never, never, never, void, void | Promise<void>>\n{\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public getHandler(\n app: TApp,\n framework: FrameworkContract<TApp>,\n ): (req: IncomingMessage, res: ServerResponse) => void | Promise<void> {\n return https.onRequest(this.onRequestCallback(app, framework));\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"ooBAAA,8BAAA,CAAA,EAAA,SAAA,8BAAA,CAAA,oBAAA,IAAA,mBAAA,CAAA,EAAA,OAAA,QAAA,aAAA,6BAAA,
|
|
1
|
+
{"version":3,"file":"http-firebase.handler.js","sources":["../../../src/handlers/firebase/http-firebase.handler.ts"],"sourcesContent":["//#region Imports\n\nimport { IncomingMessage, ServerResponse } from 'http';\n// eslint-disable-next-line import/no-unresolved\nimport { https } from 'firebase-functions/v1';\nimport { FrameworkContract, HandlerContract } from '../../contracts';\nimport { RawRequest } from '../base';\n\n//#endregion\n/**\n * The class that implements a handler for Firebase Https Events\n *\n * @remarks Read more about Https Events {@link https://firebase.google.com/docs/functions/http-events | here}\n *\n * @breadcrumb Handlers / HttpFirebaseHandler\n * @public\n */\nexport class HttpFirebaseHandler<TApp>\n extends RawRequest<TApp>\n implements\n HandlerContract<TApp, never, never, never, void, void | Promise<void>>\n{\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public getHandler(\n app: TApp,\n framework: FrameworkContract<TApp>,\n ): (req: IncomingMessage, res: ServerResponse) => void | Promise<void> {\n return https.onRequest(this.onRequestCallback(app, framework));\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"ooBAAA,8BAAA,CAAA,EAAA,SAAA,8BAAA,CAAA,oBAAA,IAAA,mBAAA,CAAA,EAAA,OAAA,QAAA,aAAA,6BAAA,EAIA,IAAsB,UAAA,QAAA,uBAAA,EAEK,YAAA,QAAA,SAAA,EAWpB,MAAM,4BACH,YAGV,UAAA,CAMS,WACL,IACA,UACqE,CACrE,OAAO,gBAAM,UAAU,KAAK,kBAAkB,IAAK,SAAS,CAAC,CAC/D,CAGF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-huawei.handler.js","sources":["../../../src/handlers/huawei/http-huawei.handler.ts"],"sourcesContent":["//#region Imports\n\nimport * as http from 'http';\nimport { RequestListener } from 'http';\nimport { BinarySettings } from '../../@types';\nimport {\n AdapterContract,\n FrameworkContract,\n HandlerContract,\n ResolverContract,\n ServerlessHandler,\n} from '../../contracts';\nimport { ILogger, getDefaultIfUndefined } from '../../core';\n\n//#endregion\n\n/**\n * The default port that huawei will proxy the request to your framework\n *\n * {@link https://support.huaweicloud.com/intl/en-us/ae-ad-1-usermanual-functiongraph/functiongraph_01_1442.html#functiongraph_01_1442__li194597302096 | Reference}\n *\n * @breadcrumb Handlers / HttpHuaweiHandler\n * @public\n */\nexport const DEFAULT_HUAWEI_LISTEN_PORT: number = 8000;\n\n/**\n * The options to customize {@link HttpHuaweiHandler}\n *\n * @breadcrumb Handlers / HttpHuaweiHandler\n * @public\n */\nexport type HttpHuaweiHandlerOptions = {\n /**\n * @defaultValue {@link DEFAULT_HUAWEI_LISTEN_PORT}\n */\n port?: number;\n\n /**\n * The factory to create a http server to use to listen huawei requests\n */\n httpServerFactory?: (requestListener: RequestListener) => http.Server;\n};\n\n/**\n * The class that implements a huawei serverless handler with http function that exposes a http server in specific port.\n *\n * In this Handler, you don't need to specific resolver and adapter, so you can use DummyAdapter and DummyResolver instead.\n *\n * @see https://support.huaweicloud.com/intl/en-us/ae-ad-1-usermanual-functiongraph/functiongraph_01_1442.html#functiongraph_01_1442__li194597302096\n *\n * @breadcrumb Handlers / HttpHuaweiHandler\n * @public\n */\nexport class HttpHuaweiHandler<TApp>\n implements HandlerContract<TApp, void, void, void, void, Promise<void>>\n{\n //#region Constructor\n\n /**\n * Construtor padrão\n */\n constructor(protected readonly options?: HttpHuaweiHandlerOptions) {}\n\n //#endregion\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public getHandler(\n app: TApp,\n framework: FrameworkContract<TApp>,\n _: AdapterContract<void, void, void>[],\n __: ResolverContract<void, void, void, void, void>,\n binarySettings: BinarySettings,\n respondWithErrors: boolean,\n log: ILogger,\n ): ServerlessHandler<Promise<void>> {\n const requestListener: RequestListener = (req, res) => {\n framework.sendRequest(app, req, res);\n };\n\n const server = getDefaultIfUndefined(\n this.options?.httpServerFactory,\n this.createHttpServer.bind(this),\n )(requestListener);\n\n const port = getDefaultIfUndefined(\n this.options?.port,\n DEFAULT_HUAWEI_LISTEN_PORT,\n );\n\n server.listen(port, () => {\n log.debug('SERVERLESS_ADAPTER:PROXY: Server started.');\n log.debug(\n `SERVERLESS_ADAPTER:PROXY: Listening the Huawei Requests in port ${port}`,\n );\n });\n\n return () => {\n log.debug('SERVERLESS_ADAPTER:PROXY: Disposing the server');\n\n return new Promise((resolve, reject) => {\n server.close(err => (err ? reject(err) : resolve()));\n });\n };\n }\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * The method that creates the instance of http server\n *\n * @param requestListener - O método que lidará com as requisições recebidas\n */\n protected createHttpServer(requestListener: RequestListener): http.Server {\n return http.createServer(requestListener);\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"i4BAAA,4BAAA,CAAA,EAAA,SAAA,4BAAA,CAAA,2BAAA,IAAA,2BAAA,kBAAA,IAAA,iBAAA,CAAA,EAAA,OAAA,QAAA,aAAA,2BAAA,EAEA,IAAsB,KAAA,QAAA,QAAA,MAAA,CAAA,EAUyB,YAAA,QAAA,YAAA,EAYxC,MAAM,2BAAqC,IA8B3C,MAAM,iBAEb,CAME,YAA+B,QAAoC,CAApC,KAAA,QAAA,OAAqC,CAS7D,WACL,IACA,UACA,EACA,GACA,eACA,kBACA,IACkC,WAC5B,MAAA,gBAAmC,CAAC,IAAK,MAAQ,CAC3C,UAAA,YAAY,IAAK,IAAK,GAAG,CAAA,EAG/B,UAAS,YAAA,wBACb,QAAK,UAAL,eAAc,kBACd,KAAK,iBAAiB,KAAK,IAAI,GAC/B,eAAe,EAEX,QAAO,YAAA,wBACX,QAAK,UAAL,eAAc,KACd,0BAAA,EAGK,cAAA,OAAO,KAAM,IAAM,CACxB,IAAI,MAAM,2CAA2C,EACjD,IAAA,MACF,mEAAmE,
|
|
1
|
+
{"version":3,"file":"http-huawei.handler.js","sources":["../../../src/handlers/huawei/http-huawei.handler.ts"],"sourcesContent":["//#region Imports\n\nimport * as http from 'http';\nimport { RequestListener } from 'http';\nimport { BinarySettings } from '../../@types';\nimport {\n AdapterContract,\n FrameworkContract,\n HandlerContract,\n ResolverContract,\n ServerlessHandler,\n} from '../../contracts';\nimport { ILogger, getDefaultIfUndefined } from '../../core';\n\n//#endregion\n\n/**\n * The default port that huawei will proxy the request to your framework\n *\n * {@link https://support.huaweicloud.com/intl/en-us/ae-ad-1-usermanual-functiongraph/functiongraph_01_1442.html#functiongraph_01_1442__li194597302096 | Reference}\n *\n * @breadcrumb Handlers / HttpHuaweiHandler\n * @public\n */\nexport const DEFAULT_HUAWEI_LISTEN_PORT: number = 8000;\n\n/**\n * The options to customize {@link HttpHuaweiHandler}\n *\n * @breadcrumb Handlers / HttpHuaweiHandler\n * @public\n */\nexport type HttpHuaweiHandlerOptions = {\n /**\n * @defaultValue {@link DEFAULT_HUAWEI_LISTEN_PORT}\n */\n port?: number;\n\n /**\n * The factory to create a http server to use to listen huawei requests\n */\n httpServerFactory?: (requestListener: RequestListener) => http.Server;\n};\n\n/**\n * The class that implements a huawei serverless handler with http function that exposes a http server in specific port.\n *\n * In this Handler, you don't need to specific resolver and adapter, so you can use DummyAdapter and DummyResolver instead.\n *\n * @see https://support.huaweicloud.com/intl/en-us/ae-ad-1-usermanual-functiongraph/functiongraph_01_1442.html#functiongraph_01_1442__li194597302096\n *\n * @breadcrumb Handlers / HttpHuaweiHandler\n * @public\n */\nexport class HttpHuaweiHandler<TApp>\n implements HandlerContract<TApp, void, void, void, void, Promise<void>>\n{\n //#region Constructor\n\n /**\n * Construtor padrão\n */\n constructor(protected readonly options?: HttpHuaweiHandlerOptions) {}\n\n //#endregion\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public getHandler(\n app: TApp,\n framework: FrameworkContract<TApp>,\n _: AdapterContract<void, void, void>[],\n __: ResolverContract<void, void, void, void, void>,\n binarySettings: BinarySettings,\n respondWithErrors: boolean,\n log: ILogger,\n ): ServerlessHandler<Promise<void>> {\n const requestListener: RequestListener = (req, res) => {\n framework.sendRequest(app, req, res);\n };\n\n const server = getDefaultIfUndefined(\n this.options?.httpServerFactory,\n this.createHttpServer.bind(this),\n )(requestListener);\n\n const port = getDefaultIfUndefined(\n this.options?.port,\n DEFAULT_HUAWEI_LISTEN_PORT,\n );\n\n server.listen(port, () => {\n log.debug('SERVERLESS_ADAPTER:PROXY: Server started.');\n log.debug(\n `SERVERLESS_ADAPTER:PROXY: Listening the Huawei Requests in port ${port}`,\n );\n });\n\n return () => {\n log.debug('SERVERLESS_ADAPTER:PROXY: Disposing the server');\n\n return new Promise((resolve, reject) => {\n server.close(err => (err ? reject(err) : resolve()));\n });\n };\n }\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * The method that creates the instance of http server\n *\n * @param requestListener - O método que lidará com as requisições recebidas\n */\n protected createHttpServer(requestListener: RequestListener): http.Server {\n return http.createServer(requestListener);\n }\n\n //#endregion\n}\n"],"names":[],"mappings":"i4BAAA,4BAAA,CAAA,EAAA,SAAA,4BAAA,CAAA,2BAAA,IAAA,2BAAA,kBAAA,IAAA,iBAAA,CAAA,EAAA,OAAA,QAAA,aAAA,2BAAA,EAEA,IAAsB,KAAA,QAAA,QAAA,MAAA,CAAA,EAUyB,YAAA,QAAA,YAAA,EAYxC,MAAM,2BAAqC,IA8B3C,MAAM,iBAEb,CAME,YAA+B,QAAoC,CAApC,KAAA,QAAA,OAAqC,CAS7D,WACL,IACA,UACA,EACA,GACA,eACA,kBACA,IACkC,WAC5B,MAAA,gBAAmC,CAAC,IAAK,MAAQ,CAC3C,UAAA,YAAY,IAAK,IAAK,GAAG,CAAA,EAG/B,UAAS,YAAA,wBACb,QAAK,UAAL,eAAc,kBACd,KAAK,iBAAiB,KAAK,IAAI,GAC/B,eAAe,EAEX,QAAO,YAAA,wBACX,QAAK,UAAL,eAAc,KACd,0BAAA,EAGK,cAAA,OAAO,KAAM,IAAM,CACxB,IAAI,MAAM,2CAA2C,EACjD,IAAA,MACF,mEAAmE,IAAI,EAAA,CACzE,CACD,EAEM,KACL,IAAI,MAAM,gDAAgD,EAEnD,IAAI,QAAQ,CAAC,QAAS,SAAW,CACtC,OAAO,MAAc,KAAA,IAAM,OAAO,GAAG,EAAI,SAAU,CAAA,CACpD,EAEL,CAWU,iBAAiB,gBAA+C,CACjE,OAAA,KAAK,aAAa,eAAe,CAC1C,CAGF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request.js","sources":["../../src/network/request.ts"],"sourcesContent":["// ATTRIBUTION: https://github.com/dougmoscrop/serverless-http\nimport { IncomingMessage } from 'http';\nimport { AddressInfo } from 'net';\nimport { SingleValueHeaders } from '../@types';\nimport { NO_OP } from '../core';\n\nconst HTTPS_PORT = 443;\n\n/**\n * The properties to create a {@link ServerlessRequest}\n *\n * @breadcrumb Network / ServerlessRequest\n * @public\n */\nexport interface ServerlessRequestProps {\n /**\n * The HTTP Method of the request\n */\n method: string;\n\n /**\n * The URL requested\n */\n url: string;\n\n /**\n * The headers from the event source\n */\n headers: SingleValueHeaders;\n\n /**\n * The body from the event source\n */\n body?: Buffer | Uint8Array;\n\n /**\n * The IP Address from caller\n */\n remoteAddress?: string;\n}\n\n/**\n * The class that represents an {@link http#IncomingMessage} created by the library to represent an actual request to the framework.\n *\n * @breadcrumb Network / ServerlessRequest\n * @public\n */\nexport class ServerlessRequest extends IncomingMessage {\n constructor({\n method,\n url,\n headers,\n body,\n remoteAddress,\n }: ServerlessRequestProps) {\n super({\n encrypted: true,\n readable: false,\n remoteAddress,\n address: () => ({ port: HTTPS_PORT } as AddressInfo
|
|
1
|
+
{"version":3,"file":"request.js","sources":["../../src/network/request.ts"],"sourcesContent":["// ATTRIBUTION: https://github.com/dougmoscrop/serverless-http\nimport { IncomingMessage } from 'http';\nimport { AddressInfo } from 'net';\nimport { SingleValueHeaders } from '../@types';\nimport { NO_OP } from '../core';\n\nconst HTTPS_PORT = 443;\n\n/**\n * The properties to create a {@link ServerlessRequest}\n *\n * @breadcrumb Network / ServerlessRequest\n * @public\n */\nexport interface ServerlessRequestProps {\n /**\n * The HTTP Method of the request\n */\n method: string;\n\n /**\n * The URL requested\n */\n url: string;\n\n /**\n * The headers from the event source\n */\n headers: SingleValueHeaders;\n\n /**\n * The body from the event source\n */\n body?: Buffer | Uint8Array;\n\n /**\n * The IP Address from caller\n */\n remoteAddress?: string;\n}\n\n/**\n * The class that represents an {@link http#IncomingMessage} created by the library to represent an actual request to the framework.\n *\n * @breadcrumb Network / ServerlessRequest\n * @public\n */\nexport class ServerlessRequest extends IncomingMessage {\n constructor({\n method,\n url,\n headers,\n body,\n remoteAddress,\n }: ServerlessRequestProps) {\n super({\n encrypted: true,\n readable: false,\n remoteAddress,\n address: () => ({ port: HTTPS_PORT }) as AddressInfo,\n end: NO_OP,\n destroy: NO_OP,\n } as any);\n\n this.statusCode = 200;\n this.statusMessage = 'OK';\n this.complete = true;\n this.httpVersion = '1.1';\n this.httpVersionMajor = 1;\n this.httpVersionMinor = 1;\n this.method = method;\n this.headers = headers;\n this.body = body;\n this.url = url;\n this.ip = remoteAddress;\n\n this._read = () => {\n this.push(body);\n this.push(null);\n };\n }\n\n ip?: string;\n body?: Buffer | Uint8Array;\n}\n"],"names":[],"mappings":"ooBAAA,gBAAA,CAAA,EAAA,SAAA,gBAAA,CAAA,kBAAA,IAAA,iBAAA,CAAA,EAAA,OAAA,QAAA,aAAA,eAAA,EACA,IAAgC,YAAA,QAAA,MAAA,EAGV,YAAA,QAAA,SAAA,EAEtB,MAAM,WAAa,IAyCZ,MAAM,0BAA0B,YAAgB,eAAA,CACrD,YAAY,CACV,OACA,IACA,QACA,KACA,aAAA,EACyB,CACnB,MAAA,CACJ,UAAW,GACX,SAAU,GACV,cACA,QAAS,KAAO,CAAE,KAAM,aACxB,IAAK,YAAA,MACL,QAAS,YAAA,KAAA,CACH,EAER,KAAK,WAAa,IAClB,KAAK,cAAgB,KACrB,KAAK,SAAW,GAChB,KAAK,YAAc,MACnB,KAAK,iBAAmB,EACxB,KAAK,iBAAmB,EACxB,KAAK,OAAS,OACd,KAAK,QAAU,QACf,KAAK,KAAO,KACZ,KAAK,IAAM,IACX,KAAK,GAAK,cAEV,KAAK,MAAQ,IAAM,CACjB,KAAK,KAAK,IAAI,EACd,KAAK,KAAK,IAAI,CAAA,CAElB,CAIF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response-stream.js","sources":["../../src/network/response-stream.ts"],"sourcesContent":["import { ServerResponse } from 'http';\nimport { Socket } from 'net';\nimport { Writable } from 'stream';\nimport { BothValueHeaders } from '../@types';\nimport { ILogger, NO_OP, parseHeaders } from '../core';\nimport { getString } from './utils';\n\nconst endChunked = '0\\r\\n\\r\\n';\nconst headerEnd = '\\r\\n\\r\\n';\nconst endStatusSeparator = '\\r\\n';\n\n/**\n * The properties to create a {@link ServerlessStreamResponse}.\n *\n * @breadcrumb Network / ServerlessStreamResponse\n * @public\n */\nexport interface ServerlessStreamResponseProps {\n /**\n * The HTTP Method from request\n */\n method?: string;\n\n /**\n * The callback to receive the headers when they are written to the stream\n * You need to return a writable stream be able to continue writing the response\n *\n * @param statusCode - The status code of the response\n * @param headers - The headers of the response\n */\n onReceiveHeaders: (statusCode: number, headers: BothValueHeaders) => Writable;\n\n /**\n * Instance of the logger\n */\n log: ILogger;\n}\n\n/**\n * The class that represents a response instance used to send to the framework and wait until the framework finishes processing the request.\n * This response is specially built to deal with transfer-encoding: chunked\n *\n * @breadcrumb Network / ServerlessStreamResponse\n * @public\n */\nexport class ServerlessStreamResponse extends ServerResponse {\n constructor({\n method,\n onReceiveHeaders,\n log,\n }: ServerlessStreamResponseProps) {\n super({ method } as any);\n\n this.useChunkedEncodingByDefault = true;\n this.chunkedEncoding = true;\n\n let internalWritable: Writable | null = null;\n let isFirstCall = true;\n // this ignore is used because I need to ignore these write calls:\n // https://github.com/nodejs/node/blob/main/lib/_http_outgoing.js#L934-L935\n // https://github.com/nodejs/node/blob/main/lib/_http_outgoing.js#L937\n let writesToIgnore = 0;\n\n const socket: Partial<Socket> & { _writableState: any } = {\n _writableState: {},\n writable: true,\n on: NO_OP,\n removeListener: NO_OP,\n destroy: NO_OP,\n cork: NO_OP,\n uncork: NO_OP,\n write: (\n data: Uint8Array | string,\n encoding?: string | null | (() => void),\n cb?: () => void,\n ): any => {\n // very unlikely, I don't even know how to reproduce this, but exist on types\n // istanbul ignore if\n if (typeof encoding === 'function') {\n cb = encoding;\n encoding = null;\n }\n\n log.debug('SERVERLESS_ADAPTER:RESPONSE_STREAM:DATA', () => ({\n data: Buffer.isBuffer(data) ? data.toString('utf8') : data,\n encoding,\n }));\n\n if (!isFirstCall && internalWritable) {\n if (data === endChunked) {\n internalWritable.end(cb);\n\n return true;\n }\n\n if (writesToIgnore > 0) {\n writesToIgnore--;\n return true;\n }\n\n internalWritable.write(data, cb);\n writesToIgnore = 3;\n } else if (isFirstCall) {\n isFirstCall = false;\n\n const stringData = getString(data);\n const endStatusIndex = stringData.indexOf(endStatusSeparator);\n const status = +stringData.slice(0, endStatusIndex).split(' ')[1];\n\n const headerData = stringData.slice(\n endStatusIndex + 2,\n stringData.indexOf(headerEnd),\n );\n const headers = parseHeaders(headerData);\n log.debug('SERVERLESS_ADAPTER:RESPONSE_STREAM:FRAMEWORK_HEADERS', {\n headers,\n });\n\n writesToIgnore = 1;\n internalWritable = onReceiveHeaders(status, headers);\n }\n\n return true;\n },\n };\n\n this.assignSocket(socket as unknown as Socket);\n }\n}\n"],"names":[],"mappings":"ooBAAA,wBAAA,CAAA,EAAA,SAAA,wBAAA,CAAA,yBAAA,IAAA,wBAAA,CAAA,EAAA,OAAA,QAAA,aAAA,uBAAA,EAAA,IAA+B,YAAA,QAAA,MAAA,EAIc,YAAA,QAAA,SAAA,EACnB,aAAA,QAAA,SAAA,EAE1B,MAAM,WAAa;AAAA;AAAA,EACb,UAAY;AAAA;AAAA,EACZ,mBAAqB;AAAA,EAoCpB,MAAM,iCAAiC,YAAe,cAAA,CAC3D,YAAY,CACV,OACA,iBACA,GAAA,EACgC,CAC1B,MAAA,CAAE,OAAe,EAEvB,KAAK,4BAA8B,GACnC,KAAK,gBAAkB,GAEvB,IAAI,iBAAoC,KACpC,YAAc,GAId,eAAiB,EAErB,MAAM,OAAoD,CACxD,eAAgB,CAAC,EACjB,SAAU,GACV,GAAI,YAAA,MACJ,eAAgB,YAAA,MAChB,QAAS,YAAA,MACT,KAAM,YAAA,MACN,OAAQ,YAAA,MACR,MAAO,CACL,KACA,SACA,KACQ,CAaJ,GAVA,OAAO,UAAa,aACjB,GAAA,SACM,SAAA,MAGT,IAAA,MAAM,0CAA2C,KAAO,CAC1D,KAAM,OAAO,SAAS,IAAI,EAAI,KAAK,SAAS,MAAM,EAAI,KACtD,QACA,EAAA,EAEE,CAAC,aAAe,iBAAkB,CACpC,GAAI,OAAS,WACX,wBAAiB,IAAI,EAAE,EAEhB,GAGT,GAAI,eAAiB,EACnB,wBACO,GAGQ,iBAAA,MAAM,KAAM,EAAE,EACd,eAAA,UACR,YAAa,CACR,YAAA,GAER,MAAA,cAAa,wBAAU,IAAI,EAC3B,eAAiB,WAAW,QAAQ,kBAAkB,EACtD,OAAS,CAAC,WAAW,MAAM,EAAG,cAAc,EAAE,MAAM,GAAG,EAAE,CAAC,EAE1D,WAAa,WAAW,MAC5B,eAAiB,EACjB,WAAW,QAAQ,SAAS,CAAA,EAExB,WAAU,0BAAa,UAAU,EACvC,IAAI,MAAM,uDAAwD,CAChE,OAAA,CACD,EAEgB,eAAA,EACE,iBAAA,iBAAiB,OAAQ,OAAO,
|
|
1
|
+
{"version":3,"file":"response-stream.js","sources":["../../src/network/response-stream.ts"],"sourcesContent":["import { ServerResponse } from 'http';\nimport { Socket } from 'net';\nimport { Writable } from 'stream';\nimport { BothValueHeaders } from '../@types';\nimport { ILogger, NO_OP, parseHeaders } from '../core';\nimport { getString } from './utils';\n\nconst endChunked = '0\\r\\n\\r\\n';\nconst headerEnd = '\\r\\n\\r\\n';\nconst endStatusSeparator = '\\r\\n';\n\n/**\n * The properties to create a {@link ServerlessStreamResponse}.\n *\n * @breadcrumb Network / ServerlessStreamResponse\n * @public\n */\nexport interface ServerlessStreamResponseProps {\n /**\n * The HTTP Method from request\n */\n method?: string;\n\n /**\n * The callback to receive the headers when they are written to the stream\n * You need to return a writable stream be able to continue writing the response\n *\n * @param statusCode - The status code of the response\n * @param headers - The headers of the response\n */\n onReceiveHeaders: (statusCode: number, headers: BothValueHeaders) => Writable;\n\n /**\n * Instance of the logger\n */\n log: ILogger;\n}\n\n/**\n * The class that represents a response instance used to send to the framework and wait until the framework finishes processing the request.\n * This response is specially built to deal with transfer-encoding: chunked\n *\n * @breadcrumb Network / ServerlessStreamResponse\n * @public\n */\nexport class ServerlessStreamResponse extends ServerResponse {\n constructor({\n method,\n onReceiveHeaders,\n log,\n }: ServerlessStreamResponseProps) {\n super({ method } as any);\n\n this.useChunkedEncodingByDefault = true;\n this.chunkedEncoding = true;\n\n let internalWritable: Writable | null = null;\n let isFirstCall = true;\n // this ignore is used because I need to ignore these write calls:\n // https://github.com/nodejs/node/blob/main/lib/_http_outgoing.js#L934-L935\n // https://github.com/nodejs/node/blob/main/lib/_http_outgoing.js#L937\n let writesToIgnore = 0;\n\n const socket: Partial<Socket> & { _writableState: any } = {\n _writableState: {},\n writable: true,\n on: NO_OP,\n removeListener: NO_OP,\n destroy: NO_OP,\n cork: NO_OP,\n uncork: NO_OP,\n write: (\n data: Uint8Array | string,\n encoding?: string | null | (() => void),\n cb?: () => void,\n ): any => {\n // very unlikely, I don't even know how to reproduce this, but exist on types\n // istanbul ignore if\n if (typeof encoding === 'function') {\n cb = encoding;\n encoding = null;\n }\n\n log.debug('SERVERLESS_ADAPTER:RESPONSE_STREAM:DATA', () => ({\n data: Buffer.isBuffer(data) ? data.toString('utf8') : data,\n encoding,\n }));\n\n if (!isFirstCall && internalWritable) {\n if (data === endChunked) {\n internalWritable.end(cb);\n\n return true;\n }\n\n if (writesToIgnore > 0) {\n writesToIgnore--;\n return true;\n }\n\n internalWritable.write(data, cb);\n writesToIgnore = 3;\n } else if (isFirstCall) {\n isFirstCall = false;\n\n const stringData = getString(data);\n const endStatusIndex = stringData.indexOf(endStatusSeparator);\n const status = +stringData.slice(0, endStatusIndex).split(' ')[1];\n\n const headerData = stringData.slice(\n endStatusIndex + 2,\n stringData.indexOf(headerEnd),\n );\n const headers = parseHeaders(headerData);\n log.debug('SERVERLESS_ADAPTER:RESPONSE_STREAM:FRAMEWORK_HEADERS', {\n headers,\n });\n\n writesToIgnore = 1;\n internalWritable = onReceiveHeaders(status, headers);\n }\n\n return true;\n },\n };\n\n this.assignSocket(socket as unknown as Socket);\n }\n}\n"],"names":[],"mappings":"ooBAAA,wBAAA,CAAA,EAAA,SAAA,wBAAA,CAAA,yBAAA,IAAA,wBAAA,CAAA,EAAA,OAAA,QAAA,aAAA,uBAAA,EAAA,IAA+B,YAAA,QAAA,MAAA,EAIc,YAAA,QAAA,SAAA,EACnB,aAAA,QAAA,SAAA,EAE1B,MAAM,WAAa;AAAA;AAAA,EACb,UAAY;AAAA;AAAA,EACZ,mBAAqB;AAAA,EAoCpB,MAAM,iCAAiC,YAAe,cAAA,CAC3D,YAAY,CACV,OACA,iBACA,GAAA,EACgC,CAC1B,MAAA,CAAE,OAAe,EAEvB,KAAK,4BAA8B,GACnC,KAAK,gBAAkB,GAEvB,IAAI,iBAAoC,KACpC,YAAc,GAId,eAAiB,EAErB,MAAM,OAAoD,CACxD,eAAgB,CAAC,EACjB,SAAU,GACV,GAAI,YAAA,MACJ,eAAgB,YAAA,MAChB,QAAS,YAAA,MACT,KAAM,YAAA,MACN,OAAQ,YAAA,MACR,MAAO,CACL,KACA,SACA,KACQ,CAaJ,GAVA,OAAO,UAAa,aACjB,GAAA,SACM,SAAA,MAGT,IAAA,MAAM,0CAA2C,KAAO,CAC1D,KAAM,OAAO,SAAS,IAAI,EAAI,KAAK,SAAS,MAAM,EAAI,KACtD,QACA,EAAA,EAEE,CAAC,aAAe,iBAAkB,CACpC,GAAI,OAAS,WACX,wBAAiB,IAAI,EAAE,EAEhB,GAGT,GAAI,eAAiB,EACnB,wBACO,GAGQ,iBAAA,MAAM,KAAM,EAAE,EACd,eAAA,UACR,YAAa,CACR,YAAA,GAER,MAAA,cAAa,wBAAU,IAAI,EAC3B,eAAiB,WAAW,QAAQ,kBAAkB,EACtD,OAAS,CAAC,WAAW,MAAM,EAAG,cAAc,EAAE,MAAM,GAAG,EAAE,CAAC,EAE1D,WAAa,WAAW,MAC5B,eAAiB,EACjB,WAAW,QAAQ,SAAS,CAAA,EAExB,WAAU,0BAAa,UAAU,EACvC,IAAI,MAAM,uDAAwD,CAChE,OAAA,CACD,EAEgB,eAAA,EACE,iBAAA,iBAAiB,OAAQ,OAAO,CACrD,CAEO,MAAA,EACT,CAAA,EAGF,KAAK,aAAa,MAA2B,CAC/C,CACF"}
|
package/lib/network/response.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
"use strict";var __defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod),response_exports={};__export(response_exports,{ServerlessResponse:()=>ServerlessResponse});module.exports=__toCommonJS(response_exports);var import_http=require("http"),import_core=require("../core"),import_utils=require("./utils");const headerEnd=`\r
|
|
2
2
|
\r
|
|
3
|
-
`,BODY=Symbol("Response body"),HEADERS=Symbol("Response headers");function addData(stream,data){if(Buffer.isBuffer(data)||typeof data=="string"||data instanceof Uint8Array)stream[BODY].push(Buffer.from(data));else throw new Error(`response.write() of unexpected type: ${typeof data}`)}
|
|
3
|
+
`,BODY=Symbol("Response body"),HEADERS=Symbol("Response headers");function addData(stream,data){if(Buffer.isBuffer(data)||typeof data=="string"||data instanceof Uint8Array)stream[BODY].push(Buffer.from(data));else throw new Error(`response.write() of unexpected type: ${typeof data}`)}class ServerlessResponse extends import_http.ServerResponse{constructor({method}){super({method}),this[BODY]=[],this[HEADERS]={},this.useChunkedEncodingByDefault=!1,this.chunkedEncoding=!1,this._header="";const socket={_writableState:{},writable:!0,on:import_core.NO_OP,removeListener:import_core.NO_OP,destroy:import_core.NO_OP,cork:import_core.NO_OP,uncork:import_core.NO_OP,write:(data,encoding,cb)=>{if(typeof encoding=="function"&&(cb=encoding,encoding=null),this._header===""||this._wroteHeader)addData(this,data);else{const string=(0,import_utils.getString)(data),index=string.indexOf(headerEnd);if(index!==-1){const remainder=string.slice(index+headerEnd.length);remainder&&addData(this,remainder),this._wroteHeader=!0}}typeof cb=="function"&&cb()}};this.assignSocket(socket)}get headers(){return this[HEADERS]}static from(res){const response=new ServerlessResponse({method:res.method});return response.statusCode=res.statusCode||0,response[HEADERS]=res.headers,response[BODY]=res.body?[Buffer.from(res.body)]:[],response.end(),response}static body(res){return Buffer.concat(res[BODY])}static headers(res){const headers=res.getHeaders();return Object.assign(headers,res[HEADERS])}setHeader(key,value){this._wroteHeader?this[HEADERS][key]=value:super.setHeader(key,value)}writeHead(statusCode,statusMessage,obj){const headersObjOrArray=typeof statusMessage=="string"?obj:statusMessage,arrayHeaders=Array.isArray(headersObjOrArray)?headersObjOrArray:[headersObjOrArray||{}];for(const headers of arrayHeaders)for(const name in headers)if(this.setHeader(name,headers[name]),!this._wroteHeader)break;return this.callNativeWriteHead(statusCode,statusMessage,obj)}callNativeWriteHead(statusCode,statusMessage,obj){return super.writeHead(statusCode,statusMessage,obj)}}
|
|
4
4
|
//# sourceMappingURL=response.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response.js","sources":["../../src/network/response.ts"],"sourcesContent":["// ATTRIBUTION: https://github.com/dougmoscrop/serverless-http\nimport { IncomingMessage, ServerResponse } from 'http';\nimport { Socket } from 'net';\nimport { NO_OP } from '../core';\nimport { getString } from './utils';\n\nconst headerEnd = '\\r\\n\\r\\n';\n\nconst BODY = Symbol('Response body');\nconst HEADERS = Symbol('Response headers');\n\nfunction addData(stream: ServerlessResponse, data: Uint8Array | string) {\n if (\n Buffer.isBuffer(data) ||\n typeof data === 'string' ||\n data instanceof Uint8Array\n )\n stream[BODY].push(Buffer.from(data));\n else throw new Error(`response.write() of unexpected type: ${typeof data}`);\n}\n\n/**\n * The properties to create a {@link ServerlessResponse}.\n *\n * @breadcrumb Network / ServerlessResponse\n * @public\n */\nexport interface ServerlessResponseProps {\n /**\n * The HTTP Method from request\n */\n method?: string;\n}\n\n/**\n * The class that represents a response instance used to send to the framework and wait until the framework finishes processing the request.\n * Once it's happens, we use the properties from this response to built the response to the cloud.\n *\n * @breadcrumb Network / ServerlessResponse\n * @public\n */\nexport class ServerlessResponse extends ServerResponse {\n constructor({ method }: ServerlessResponseProps) {\n super({ method } as any);\n\n this[BODY] = [];\n this[HEADERS] = {};\n\n this.useChunkedEncodingByDefault = false;\n this.chunkedEncoding = false;\n this._header = '';\n\n const socket: Partial<Socket> & { _writableState: any } = {\n _writableState: {},\n writable: true,\n on: NO_OP,\n removeListener: NO_OP,\n destroy: NO_OP,\n cork: NO_OP,\n uncork: NO_OP,\n write: (\n data: Uint8Array | string,\n encoding?: string | null | (() => void),\n cb?: () => void,\n ): any => {\n if (typeof encoding === 'function') {\n cb = encoding;\n encoding = null;\n }\n\n if (this._header === '' || this._wroteHeader) addData(this, data);\n else {\n const string = getString(data);\n const index = string.indexOf(headerEnd);\n\n if (index !== -1) {\n const remainder = string.slice(index + headerEnd.length);\n\n if (remainder) addData(this, remainder);\n\n this._wroteHeader = true;\n }\n }\n\n if (typeof cb === 'function') cb();\n },\n };\n\n this.assignSocket(socket as unknown as Socket);\n }\n\n _header: string;\n _headers?: Record<any, any>;\n _wroteHeader?: boolean;\n\n [BODY]: any[];\n [HEADERS]: Record<any, any>;\n\n get headers(): Record<any, any> {\n return this[HEADERS];\n }\n\n static from(res: IncomingMessage) {\n const response = new ServerlessResponse({ method: res.method });\n\n response.statusCode = res.statusCode || 0;\n response[HEADERS] = res.headers;\n response[BODY] = (res as any).body ? [Buffer.from((res as any).body)] : [];\n response.end();\n\n return response;\n }\n\n static body(res: ServerlessResponse): Buffer {\n return Buffer.concat(res[BODY]);\n }\n\n static headers(res: ServerlessResponse) {\n const headers = res.getHeaders();\n\n return Object.assign(headers, res[HEADERS]);\n }\n\n setHeader(key: string, value: number | string | readonly string[]): any {\n if (this._wroteHeader) this[HEADERS][key] = value;\n else super.setHeader(key, value);\n }\n\n writeHead(\n statusCode: number,\n statusMessage?: string | any | any[],\n obj?: any | any[],\n ): any {\n const headersObjOrArray =\n typeof statusMessage === 'string' ? obj : statusMessage;\n\n const arrayHeaders = Array.isArray(headersObjOrArray)\n ? headersObjOrArray\n : [headersObjOrArray || {}];\n\n for (const headers of arrayHeaders) {\n for (const name in headers) {\n this.setHeader(name, headers[name]!);\n\n if (!this._wroteHeader) {\n // we only need to initiate super.headers once\n // writeHead will add the other headers itself\n break;\n }\n }\n }\n\n return this.callNativeWriteHead(statusCode, statusMessage, obj);\n }\n\n /**\n * I use ignore here because in nodejs 12.x, statusMessage can be string | OutgoingHttpHeaders\n * But in nodejs \\>=14.x, statusMessage can also be OutgoingHttpHeaders[]\n * I take care of these cases above, but here I can't handle it well, so I give up\n * nodejs 12.x ref: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/node/v12/http.d.ts#L229\n * nodejs 14.x ref: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/node/v14/http.d.ts#L263\n */\n protected callNativeWriteHead(\n statusCode: number,\n statusMessage?: string | any | any[],\n obj?: any | any[],\n ): this {\n return super.writeHead(statusCode, statusMessage, obj);\n }\n}\n"],"names":[],"mappings":"ooBAAA,iBAAA,CAAA,EAAA,SAAA,iBAAA,CAAA,mBAAA,IAAA,kBAAA,CAAA,EAAA,OAAA,QAAA,aAAA,gBAAA,EACA,IAAgD,YAAA,QAAA,MAAA,EAE1B,YAAA,QAAA,SAAA,EACI,aAAA,QAAA,SAAA,EAE1B,MAAM,UAAY;AAAA;AAAA,EAEZ,KAAO,OAAO,eAAe,EAC7B,QAAU,OAAO,kBAAkB,EAEzC,SAAS,QAAQ,OAA4B,KAA2B,CACtE,GACE,OAAO,SAAS,IAAI,GACpB,OAAO,MAAS,UAChB,gBAAgB,WAEhB,OAAO,IAAI,EAAE,KAAK,OAAO,KAAK,IAAI,CAAC,MAChC,OAAM,IAAI,MAAM,wCAAwC,OAAO,
|
|
1
|
+
{"version":3,"file":"response.js","sources":["../../src/network/response.ts"],"sourcesContent":["// ATTRIBUTION: https://github.com/dougmoscrop/serverless-http\nimport { IncomingMessage, ServerResponse } from 'http';\nimport { Socket } from 'net';\nimport { NO_OP } from '../core';\nimport { getString } from './utils';\n\nconst headerEnd = '\\r\\n\\r\\n';\n\nconst BODY = Symbol('Response body');\nconst HEADERS = Symbol('Response headers');\n\nfunction addData(stream: ServerlessResponse, data: Uint8Array | string) {\n if (\n Buffer.isBuffer(data) ||\n typeof data === 'string' ||\n data instanceof Uint8Array\n )\n stream[BODY].push(Buffer.from(data));\n else throw new Error(`response.write() of unexpected type: ${typeof data}`);\n}\n\n/**\n * The properties to create a {@link ServerlessResponse}.\n *\n * @breadcrumb Network / ServerlessResponse\n * @public\n */\nexport interface ServerlessResponseProps {\n /**\n * The HTTP Method from request\n */\n method?: string;\n}\n\n/**\n * The class that represents a response instance used to send to the framework and wait until the framework finishes processing the request.\n * Once it's happens, we use the properties from this response to built the response to the cloud.\n *\n * @breadcrumb Network / ServerlessResponse\n * @public\n */\nexport class ServerlessResponse extends ServerResponse {\n constructor({ method }: ServerlessResponseProps) {\n super({ method } as any);\n\n this[BODY] = [];\n this[HEADERS] = {};\n\n this.useChunkedEncodingByDefault = false;\n this.chunkedEncoding = false;\n this._header = '';\n\n const socket: Partial<Socket> & { _writableState: any } = {\n _writableState: {},\n writable: true,\n on: NO_OP,\n removeListener: NO_OP,\n destroy: NO_OP,\n cork: NO_OP,\n uncork: NO_OP,\n write: (\n data: Uint8Array | string,\n encoding?: string | null | (() => void),\n cb?: () => void,\n ): any => {\n if (typeof encoding === 'function') {\n cb = encoding;\n encoding = null;\n }\n\n if (this._header === '' || this._wroteHeader) addData(this, data);\n else {\n const string = getString(data);\n const index = string.indexOf(headerEnd);\n\n if (index !== -1) {\n const remainder = string.slice(index + headerEnd.length);\n\n if (remainder) addData(this, remainder);\n\n this._wroteHeader = true;\n }\n }\n\n if (typeof cb === 'function') cb();\n },\n };\n\n this.assignSocket(socket as unknown as Socket);\n }\n\n _header: string;\n _headers?: Record<any, any>;\n _wroteHeader?: boolean;\n\n [BODY]: any[];\n [HEADERS]: Record<any, any>;\n\n get headers(): Record<any, any> {\n return this[HEADERS];\n }\n\n static from(res: IncomingMessage) {\n const response = new ServerlessResponse({ method: res.method });\n\n response.statusCode = res.statusCode || 0;\n response[HEADERS] = res.headers;\n response[BODY] = (res as any).body ? [Buffer.from((res as any).body)] : [];\n response.end();\n\n return response;\n }\n\n static body(res: ServerlessResponse): Buffer {\n return Buffer.concat(res[BODY]);\n }\n\n static headers(res: ServerlessResponse) {\n const headers = res.getHeaders();\n\n return Object.assign(headers, res[HEADERS]);\n }\n\n setHeader(key: string, value: number | string | readonly string[]): any {\n if (this._wroteHeader) this[HEADERS][key] = value;\n else super.setHeader(key, value);\n }\n\n writeHead(\n statusCode: number,\n statusMessage?: string | any | any[],\n obj?: any | any[],\n ): any {\n const headersObjOrArray =\n typeof statusMessage === 'string' ? obj : statusMessage;\n\n const arrayHeaders = Array.isArray(headersObjOrArray)\n ? headersObjOrArray\n : [headersObjOrArray || {}];\n\n for (const headers of arrayHeaders) {\n for (const name in headers) {\n this.setHeader(name, headers[name]!);\n\n if (!this._wroteHeader) {\n // we only need to initiate super.headers once\n // writeHead will add the other headers itself\n break;\n }\n }\n }\n\n return this.callNativeWriteHead(statusCode, statusMessage, obj);\n }\n\n /**\n * I use ignore here because in nodejs 12.x, statusMessage can be string | OutgoingHttpHeaders\n * But in nodejs \\>=14.x, statusMessage can also be OutgoingHttpHeaders[]\n * I take care of these cases above, but here I can't handle it well, so I give up\n * nodejs 12.x ref: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/node/v12/http.d.ts#L229\n * nodejs 14.x ref: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/node/v14/http.d.ts#L263\n */\n protected callNativeWriteHead(\n statusCode: number,\n statusMessage?: string | any | any[],\n obj?: any | any[],\n ): this {\n return super.writeHead(statusCode, statusMessage, obj);\n }\n}\n"],"names":[],"mappings":"ooBAAA,iBAAA,CAAA,EAAA,SAAA,iBAAA,CAAA,mBAAA,IAAA,kBAAA,CAAA,EAAA,OAAA,QAAA,aAAA,gBAAA,EACA,IAAgD,YAAA,QAAA,MAAA,EAE1B,YAAA,QAAA,SAAA,EACI,aAAA,QAAA,SAAA,EAE1B,MAAM,UAAY;AAAA;AAAA,EAEZ,KAAO,OAAO,eAAe,EAC7B,QAAU,OAAO,kBAAkB,EAEzC,SAAS,QAAQ,OAA4B,KAA2B,CACtE,GACE,OAAO,SAAS,IAAI,GACpB,OAAO,MAAS,UAChB,gBAAgB,WAEhB,OAAO,IAAI,EAAE,KAAK,OAAO,KAAK,IAAI,CAAC,MAChC,OAAM,IAAI,MAAM,wCAAwC,OAAO,IAAI,EAAE,CAC5E,CAsBO,MAAM,2BAA2B,YAAe,cAAA,CACrD,YAAY,CAAE,QAAmC,CACzC,MAAA,CAAE,OAAe,EAElB,KAAA,IAAI,EAAI,GACR,KAAA,OAAO,EAAI,GAEhB,KAAK,4BAA8B,GACnC,KAAK,gBAAkB,GACvB,KAAK,QAAU,GAEf,MAAM,OAAoD,CACxD,eAAgB,CAAC,EACjB,SAAU,GACV,GAAI,YAAA,MACJ,eAAgB,YAAA,MAChB,QAAS,YAAA,MACT,KAAM,YAAA,MACN,OAAQ,YAAA,MACR,MAAO,CACL,KACA,SACA,KACQ,CAMJ,GALA,OAAO,UAAa,aACjB,GAAA,SACM,SAAA,MAGT,KAAK,UAAY,IAAM,KAAK,aAAc,QAAQ,KAAM,IAAI,MAC3D,CACG,MAAA,UAAS,wBAAU,IAAI,EACvB,MAAQ,OAAO,QAAQ,SAAS,EAEtC,GAAI,QAAU,GAAI,CAChB,MAAM,UAAY,OAAO,MAAM,MAAQ,UAAU,MAAM,EAEnD,WAAW,QAAQ,KAAM,SAAS,EAEtC,KAAK,aAAe,EACtB,CACF,CAEI,OAAO,IAAO,YAAe,IACnC,CAAA,EAGF,KAAK,aAAa,MAA2B,CAC/C,CASA,IAAI,SAA4B,CAC9B,OAAO,KAAK,OAAO,CACrB,CAEA,OAAO,KAAK,IAAsB,CAChC,MAAM,SAAW,IAAI,mBAAmB,CAAE,OAAQ,IAAI,OAAQ,EAErD,gBAAA,WAAa,IAAI,YAAc,EAC/B,SAAA,OAAO,EAAI,IAAI,QACf,SAAA,IAAI,EAAK,IAAY,KAAO,CAAC,OAAO,KAAM,IAAY,IAAI,CAAC,EAAI,CAAA,EACxE,SAAS,IAAI,EAEN,QACT,CAEA,OAAO,KAAK,IAAiC,CAC3C,OAAO,OAAO,OAAO,IAAI,IAAI,CAAC,CAChC,CAEA,OAAO,QAAQ,IAAyB,CAChC,MAAA,QAAU,IAAI,aAEpB,OAAO,OAAO,OAAO,QAAS,IAAI,OAAO,CAAC,CAC5C,CAEA,UAAU,IAAa,MAAiD,CAClE,KAAK,aAAmB,KAAA,OAAO,EAAE,GAAG,EAAI,MACjC,MAAA,UAAU,IAAK,KAAK,CACjC,CAEA,UACE,WACA,cACA,IACK,CACL,MAAM,kBACJ,OAAO,eAAkB,SAAW,IAAM,cAEtC,aAAe,MAAM,QAAQ,iBAAiB,EAChD,kBACA,CAAC,mBAAqB,CAAA,CAAE,EAE5B,UAAW,WAAW,aACpB,UAAW,QAAQ,QAGb,GAFJ,KAAK,UAAU,KAAM,QAAQ,IAAI,CAAE,EAE/B,CAAC,KAAK,aAGR,MAKN,OAAO,KAAK,oBAAoB,WAAY,cAAe,GAAG,CAChE,CASU,oBACR,WACA,cACA,IACM,CACN,OAAO,MAAM,UAAU,WAAY,cAAe,GAAG,CACvD,CACF"}
|
package/lib/network/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../../src/network/utils.ts"],"sourcesContent":["/**\n * Get the data from a buffer, string, or Uint8Array\n *\n * @breadcrumb Network\n * @param data - The data that was written inside the stream\n */\nexport function getString(data: Buffer | string | unknown) {\n if (Buffer.isBuffer(data)) return data.toString('utf8');\n else if (typeof data === 'string') return data;\n else if (data instanceof Uint8Array) return new TextDecoder().decode(data);\n else throw new Error(`response.write() of unexpected type: ${typeof data}`);\n}\n"],"names":[],"mappings":"ooBAAA,cAAA,CAAA,EAAA,SAAA,cAAA,CAAA,UAAA,IAAA,SAAA,CAAA,EAAA,OAAA,QAAA,aAAA,aAAA,EAMO,SAAS,UAAU,KAAiC,CACrD,GAAA,OAAO,SAAS,IAAI,EAAU,OAAA,KAAK,SAAS,MAAM,EAAA,GAC7C,OAAO,MAAS,SAAiB,OAAA,KAAA,GACjC,gBAAgB,WAAY,OAAO,IAAI,YAAA,EAAc,OAAO,IAAI,EACpE,MAAM,IAAI,MAAM,wCAAwC,OAAO,
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../src/network/utils.ts"],"sourcesContent":["/**\n * Get the data from a buffer, string, or Uint8Array\n *\n * @breadcrumb Network\n * @param data - The data that was written inside the stream\n */\nexport function getString(data: Buffer | string | unknown) {\n if (Buffer.isBuffer(data)) return data.toString('utf8');\n else if (typeof data === 'string') return data;\n else if (data instanceof Uint8Array) return new TextDecoder().decode(data);\n else throw new Error(`response.write() of unexpected type: ${typeof data}`);\n}\n"],"names":[],"mappings":"ooBAAA,cAAA,CAAA,EAAA,SAAA,cAAA,CAAA,UAAA,IAAA,SAAA,CAAA,EAAA,OAAA,QAAA,aAAA,aAAA,EAMO,SAAS,UAAU,KAAiC,CACrD,GAAA,OAAO,SAAS,IAAI,EAAU,OAAA,KAAK,SAAS,MAAM,EAAA,GAC7C,OAAO,MAAS,SAAiB,OAAA,KAAA,GACjC,gBAAgB,WAAY,OAAO,IAAI,YAAA,EAAc,OAAO,IAAI,EACpE,MAAM,IAAI,MAAM,wCAAwC,OAAO,IAAI,EAAE,CAC5E"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h4ad/serverless-adapter",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Run REST APIs and other web applications using your existing Node.js application framework (NestJS, Express, Koa, Hapi, Fastify and many others), on top of AWS, Azure, Digital Ocean and many other clouds.",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"files": [
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
"cm": "cz",
|
|
15
15
|
"coverage": "codecov",
|
|
16
16
|
"lint": "eslint ./src/ ./test/ --fix",
|
|
17
|
-
"semantic-release": "semantic-release",
|
|
18
17
|
"docs:generate": "npm run docs:generate:parsing && npm run docs:generate:markdown && npm run docs:generate:api-pages",
|
|
19
18
|
"docs:generate:parsing": "npm run build:docs && ts-node scripts/parse-docs.ts",
|
|
20
19
|
"docs:generate:markdown": "ts-node scripts/generate-markdown.ts",
|
|
@@ -74,61 +73,56 @@
|
|
|
74
73
|
},
|
|
75
74
|
"homepage": "https://github.com/H4ad/serverless-adapter#readme",
|
|
76
75
|
"devDependencies": {
|
|
77
|
-
"@apollo/server": "
|
|
78
|
-
"@azure/functions": "
|
|
76
|
+
"@apollo/server": "4.9.5",
|
|
77
|
+
"@azure/functions": "3.5.1",
|
|
79
78
|
"@deepkit/app": "1.0.1-alpha.98",
|
|
80
79
|
"@deepkit/framework": "1.0.1-alpha.99",
|
|
81
80
|
"@deepkit/http": "1.0.1-alpha.98",
|
|
82
81
|
"@deepkit/workflow": "1.0.1-alpha.97",
|
|
83
|
-
"@google-cloud/functions-framework": "
|
|
84
|
-
"@hapi/hapi": "
|
|
85
|
-
"@microsoft/api-documenter": "7.
|
|
86
|
-
"@microsoft/api-extractor": "7.
|
|
87
|
-
"@microsoft/api-extractor-model": "7.
|
|
82
|
+
"@google-cloud/functions-framework": "3.3.0",
|
|
83
|
+
"@hapi/hapi": "21.3.2",
|
|
84
|
+
"@microsoft/api-documenter": "7.23.15",
|
|
85
|
+
"@microsoft/api-extractor": "7.39.0",
|
|
86
|
+
"@microsoft/api-extractor-model": "7.28.3",
|
|
88
87
|
"@microsoft/tsdoc": "0.14.2",
|
|
89
|
-
"@rushstack/node-core-library": "3.
|
|
90
|
-
"@
|
|
91
|
-
"@
|
|
92
|
-
"@
|
|
93
|
-
"@
|
|
94
|
-
"@
|
|
95
|
-
"@types/
|
|
96
|
-
"@types/body-parser": "^1.19.2",
|
|
97
|
-
"@types/cors": "^2.8.13",
|
|
98
|
-
"@types/express": "^4.17.17",
|
|
99
|
-
"@types/koa": "^2.13.6",
|
|
88
|
+
"@rushstack/node-core-library": "3.62.0",
|
|
89
|
+
"@trpc/server": "10.44.1",
|
|
90
|
+
"@types/aws-lambda": "8.10.130",
|
|
91
|
+
"@types/body-parser": "1.19.5",
|
|
92
|
+
"@types/cors": "2.8.17",
|
|
93
|
+
"@types/express": "4.17.21",
|
|
94
|
+
"@types/koa": "2.13.12",
|
|
100
95
|
"@types/node": "18.16.5",
|
|
101
|
-
"@types/supertest": "
|
|
102
|
-
"@typescript-eslint/eslint-plugin": "
|
|
103
|
-
"@typescript-eslint/parser": "
|
|
104
|
-
"@vitest/coverage-
|
|
105
|
-
"body-parser": "
|
|
106
|
-
"codecov": "
|
|
107
|
-
"commitizen": "
|
|
108
|
-
"cors": "
|
|
109
|
-
"cz-conventional-changelog": "
|
|
110
|
-
"eslint": "
|
|
111
|
-
"eslint-config-prettier": "
|
|
112
|
-
"eslint-plugin-import": "
|
|
113
|
-
"eslint-plugin-node": "
|
|
114
|
-
"eslint-plugin-prettier": "
|
|
115
|
-
"eslint-plugin-tsdoc": "
|
|
116
|
-
"express": "
|
|
117
|
-
"fastify": "
|
|
118
|
-
"firebase-admin": "
|
|
119
|
-
"firebase-functions": "
|
|
120
|
-
"glob": "
|
|
121
|
-
"husky": "
|
|
122
|
-
"koa": "
|
|
123
|
-
"prettier": "
|
|
124
|
-
"
|
|
125
|
-
"
|
|
126
|
-
"
|
|
127
|
-
"
|
|
128
|
-
"
|
|
129
|
-
"
|
|
130
|
-
"
|
|
131
|
-
"vitest": "^0.31.0"
|
|
96
|
+
"@types/supertest": "6.0.1",
|
|
97
|
+
"@typescript-eslint/eslint-plugin": "6.15.0",
|
|
98
|
+
"@typescript-eslint/parser": "6.15.0",
|
|
99
|
+
"@vitest/coverage-v8": "1.1.0",
|
|
100
|
+
"body-parser": "1.20.2",
|
|
101
|
+
"codecov": "3.8.3",
|
|
102
|
+
"commitizen": "4.3.0",
|
|
103
|
+
"cors": "2.8.5",
|
|
104
|
+
"cz-conventional-changelog": "3.3.0",
|
|
105
|
+
"eslint": "8.56.0",
|
|
106
|
+
"eslint-config-prettier": "9.1.0",
|
|
107
|
+
"eslint-plugin-import": "2.29.1",
|
|
108
|
+
"eslint-plugin-node": "11.1.0",
|
|
109
|
+
"eslint-plugin-prettier": "5.1.1",
|
|
110
|
+
"eslint-plugin-tsdoc": "0.2.17",
|
|
111
|
+
"express": "4.18.2",
|
|
112
|
+
"fastify": "4.25.1",
|
|
113
|
+
"firebase-admin": "11.11.1",
|
|
114
|
+
"firebase-functions": "4.5.0",
|
|
115
|
+
"glob": "10.3.10",
|
|
116
|
+
"husky": "8.0.3",
|
|
117
|
+
"koa": "2.14.2",
|
|
118
|
+
"prettier": "3.1.1",
|
|
119
|
+
"stream-mock": "2.0.5",
|
|
120
|
+
"supertest": "6.3.3",
|
|
121
|
+
"ts-node": "10.9.2",
|
|
122
|
+
"typedoc": "0.25.4",
|
|
123
|
+
"typescript": "~5.3.3",
|
|
124
|
+
"vite": "5.0.10",
|
|
125
|
+
"vitest": "1.1.0"
|
|
132
126
|
},
|
|
133
127
|
"peerDependencies": {
|
|
134
128
|
"@apollo/server": ">= 4.0.0",
|
|
@@ -147,7 +141,7 @@
|
|
|
147
141
|
"express": ">= 4.15.4",
|
|
148
142
|
"fastify": ">= 4.0.0",
|
|
149
143
|
"firebase-admin": ">= 11.0.0",
|
|
150
|
-
"firebase-functions": ">=
|
|
144
|
+
"firebase-functions": ">= 4.0.0",
|
|
151
145
|
"http-errors": ">= 2.0.0",
|
|
152
146
|
"koa": ">= 2.5.1",
|
|
153
147
|
"reflect-metadata": "^0.1.13"
|