@stone-js/aws-lambda-http-adapter 0.8.7 → 0.8.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/AWSLambdaHttpAdapter.d.ts +1 -1
- package/dist/AwsLambdaHttpErrorHandler.d.ts +1 -1
- package/dist/RawHttpResponseWrapper.d.ts +1 -1
- package/dist/browser/decorators/AwsLambdaHttp.d.ts +1 -1
- package/dist/browser/options/NodeHttpAdapterBlueprint.d.ts +1 -1
- package/dist/declarations.d.ts +1 -1
- package/dist/decorators/AwsLambdaHttp.d.ts +1 -1
- package/dist/event-normalizer.d.ts +1 -1
- package/dist/index.d.ts +15 -15
- package/dist/index.js +169 -159
- package/dist/middleware/BodyEventMiddleware.d.ts +1 -1
- package/dist/middleware/FilesEventMiddleware.d.ts +1 -1
- package/dist/middleware/IncomingEventMiddleware.d.ts +1 -1
- package/dist/middleware/ServerResponseMiddleware.d.ts +1 -1
- package/dist/options/AwsLambdaHttpAdapterBlueprint.d.ts +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -180,8 +180,8 @@ Explore the full documentation: [https://stonejs.dev](https://stonejs.dev)
|
|
|
180
180
|
|
|
181
181
|
## API documentation
|
|
182
182
|
|
|
183
|
-
* [API](https://
|
|
183
|
+
* [API](https://stonejs.dev/api)
|
|
184
184
|
|
|
185
185
|
## Contributing
|
|
186
186
|
|
|
187
|
-
See [Contributing Guide](https://github.com/stone-foundation/stone-js-
|
|
187
|
+
See [Contributing Guide](https://github.com/stone-foundation/stone-js-framework/blob/main/CONTRIBUTING.md)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RawHttpResponse, AwsLambdaContext, AwsLambdaHttpEvent, AwsLambdaHttpAdapterContext, AwsLambdaEventHandlerFunction } from './declarations';
|
|
1
|
+
import { RawHttpResponse, AwsLambdaContext, AwsLambdaHttpEvent, AwsLambdaHttpAdapterContext, AwsLambdaEventHandlerFunction } from './declarations.js';
|
|
2
2
|
import { Adapter, IBlueprint } from '@stone-js/core';
|
|
3
3
|
import { IncomingHttpEvent, IncomingHttpEventOptions, OutgoingHttpResponse } from '@stone-js/http-core';
|
|
4
4
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IBlueprint, AdapterErrorContext, IAdapterErrorHandler, AdapterEventBuilderType } from '@stone-js/core';
|
|
2
|
-
import { AwsLambdaContext, AwsLambdaHttpEvent, RawHttpResponse } from './declarations';
|
|
2
|
+
import { AwsLambdaContext, AwsLambdaHttpEvent, RawHttpResponse } from './declarations.js';
|
|
3
3
|
/**
|
|
4
4
|
* AwsLambdaHttpErrorHandler options.
|
|
5
5
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ClassType } from '@stone-js/core';
|
|
2
|
-
import { AwsLambdaHttpAdapterAdapterConfig } from '../../options/AwsLambdaHttpAdapterBlueprint';
|
|
2
|
+
import { AwsLambdaHttpAdapterAdapterConfig } from '../../options/AwsLambdaHttpAdapterBlueprint.js';
|
|
3
3
|
/**
|
|
4
4
|
* Configuration options for the `AwsLambdaHttp` decorator.
|
|
5
5
|
* These options extend the default AWS Lambda HTTP adapter configuration.
|
package/dist/declarations.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RawHttpResponseWrapper } from './RawHttpResponseWrapper';
|
|
1
|
+
import { RawHttpResponseWrapper } from './RawHttpResponseWrapper.js';
|
|
2
2
|
import { AdapterContext, IAdapterEventBuilder, RawResponseOptions } from '@stone-js/core';
|
|
3
3
|
import { IncomingHttpEvent, IncomingHttpEventOptions, OutgoingHttpResponse } from '@stone-js/http-core';
|
|
4
4
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ClassType } from '@stone-js/core';
|
|
2
|
-
import { AwsLambdaHttpAdapterAdapterConfig } from '../options/AwsLambdaHttpAdapterBlueprint';
|
|
2
|
+
import { AwsLambdaHttpAdapterAdapterConfig } from '../options/AwsLambdaHttpAdapterBlueprint.js';
|
|
3
3
|
/**
|
|
4
4
|
* Configuration options for the `AwsLambdaHttp` decorator.
|
|
5
5
|
* These options extend the default AWS Lambda HTTP adapter configuration.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
export * from './AWSLambdaHttpAdapter';
|
|
2
|
-
export * from './AwsLambdaHttpErrorHandler';
|
|
3
|
-
export * from './RawHttpResponseWrapper';
|
|
4
|
-
export * from './constants';
|
|
5
|
-
export * from './declarations';
|
|
6
|
-
export * from './decorators/AwsLambdaHttp';
|
|
7
|
-
export * from './errors/AwsLambdaHttpAdapterError';
|
|
8
|
-
export * from './event-normalizer';
|
|
9
|
-
export * from './middleware/BlueprintMiddleware';
|
|
10
|
-
export * from './middleware/BodyEventMiddleware';
|
|
11
|
-
export * from './middleware/FilesEventMiddleware';
|
|
12
|
-
export * from './middleware/IncomingEventMiddleware';
|
|
13
|
-
export * from './middleware/ServerResponseMiddleware';
|
|
14
|
-
export * from './options/AwsLambdaHttpAdapterBlueprint';
|
|
15
|
-
export * from './resolvers';
|
|
1
|
+
export * from './AWSLambdaHttpAdapter.js';
|
|
2
|
+
export * from './AwsLambdaHttpErrorHandler.js';
|
|
3
|
+
export * from './RawHttpResponseWrapper.js';
|
|
4
|
+
export * from './constants.js';
|
|
5
|
+
export * from './declarations.js';
|
|
6
|
+
export * from './decorators/AwsLambdaHttp.js';
|
|
7
|
+
export * from './errors/AwsLambdaHttpAdapterError.js';
|
|
8
|
+
export * from './event-normalizer.js';
|
|
9
|
+
export * from './middleware/BlueprintMiddleware.js';
|
|
10
|
+
export * from './middleware/BodyEventMiddleware.js';
|
|
11
|
+
export * from './middleware/FilesEventMiddleware.js';
|
|
12
|
+
export * from './middleware/IncomingEventMiddleware.js';
|
|
13
|
+
export * from './middleware/ServerResponseMiddleware.js';
|
|
14
|
+
export * from './options/AwsLambdaHttpAdapterBlueprint.js';
|
|
15
|
+
export * from './resolvers.js';
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { IntegrationError, Adapter, AdapterEventBuilder, defaultLoggerResolver,
|
|
2
|
-
import { IncomingHttpEvent, HTTP_INTERNAL_SERVER_ERROR, BinaryFileResponse, OutgoingHttpResponse, CookieCollection, isIpTrusted, getHostname, getProtocol, httpCoreBlueprint,
|
|
1
|
+
import { IntegrationError, Adapter, AdapterEventBuilder, defaultLoggerResolver, isNotEmpty, Logger, defaultKernelResolver, classDecoratorLegacyWrapper, addBlueprint } from '@stone-js/core';
|
|
2
|
+
import { IncomingHttpEvent, HTTP_INTERNAL_SERVER_ERROR, BinaryFileResponse, OutgoingHttpResponse, isMultipart, getCharset, CookieCollection, isIpTrusted, getHostname, getProtocol, httpCoreBlueprint, getFilesUploads } from '@stone-js/http-core';
|
|
3
3
|
import mime from 'mime';
|
|
4
4
|
import accepts from 'accepts';
|
|
5
5
|
import statuses from 'statuses';
|
|
6
6
|
import { cloneValue, deepMerge } from '@stone-js/config';
|
|
7
7
|
import { getString } from '@stone-js/env';
|
|
8
8
|
import { File } from '@stone-js/filesystem';
|
|
9
|
-
import proxyAddr from 'proxy-addr';
|
|
10
9
|
import bytes from 'bytes';
|
|
11
10
|
import typeIs from 'type-is';
|
|
11
|
+
import proxyAddr from 'proxy-addr';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Wrapper for HTTP raw responses in AWS Lambda.
|
|
@@ -497,6 +497,169 @@ const metaAdapterBlueprintMiddleware = [
|
|
|
497
497
|
{ module: SetAwsLambdaHttpResponseResolverMiddleware, priority: 6 }
|
|
498
498
|
];
|
|
499
499
|
|
|
500
|
+
/**
|
|
501
|
+
* Class representing a BodyEventMiddleware.
|
|
502
|
+
*
|
|
503
|
+
* This middleware handles platform-specific messages and transforms them into Stone.js IncomingEvent objects.
|
|
504
|
+
*
|
|
505
|
+
* @author Mr. Stone
|
|
506
|
+
*/
|
|
507
|
+
class BodyEventMiddleware {
|
|
508
|
+
/**
|
|
509
|
+
* The blueprint for resolving configuration and dependencies.
|
|
510
|
+
*/
|
|
511
|
+
blueprint;
|
|
512
|
+
/**
|
|
513
|
+
* Create a BodyEventMiddleware.
|
|
514
|
+
*
|
|
515
|
+
* @param {blueprint} options - Options for creating the BodyEventMiddleware.
|
|
516
|
+
*/
|
|
517
|
+
constructor({ blueprint }) {
|
|
518
|
+
this.blueprint = blueprint;
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Handles the incoming event, processes it, and invokes the next middleware in the pipeline.
|
|
522
|
+
*
|
|
523
|
+
* @param context - The adapter context containing the raw event, execution context, and other data.
|
|
524
|
+
* @param next - The next middleware to be invoked in the pipeline.
|
|
525
|
+
* @returns A promise that resolves to the destination type after processing.
|
|
526
|
+
*
|
|
527
|
+
* @throws {AwsLambdaHttpAdapterError} If required components such as the rawEvent or IncomingEventBuilder are not provided.
|
|
528
|
+
*/
|
|
529
|
+
async handle(context, next) {
|
|
530
|
+
if (context.rawEvent === undefined || context.incomingEventBuilder?.add === undefined) {
|
|
531
|
+
throw new AwsLambdaHttpAdapterError('The context is missing required components.');
|
|
532
|
+
}
|
|
533
|
+
if (!isMultipart(this.toNodeMessage(context.rawEvent))) {
|
|
534
|
+
const body = this.getBody(this.toNodeMessage(context.rawEvent), context.rawEvent);
|
|
535
|
+
const method = this.extractSpoofedMethod(body);
|
|
536
|
+
context
|
|
537
|
+
.incomingEventBuilder
|
|
538
|
+
.add('body', body)
|
|
539
|
+
// Keep the untouched payload available even after parsing (webhook signatures, etc.).
|
|
540
|
+
.add('metadata', { rawBody: getRawBody(context.rawEvent) });
|
|
541
|
+
// In fullstack forms, the method is spoofed and sent as a hidden field.
|
|
542
|
+
isNotEmpty(method) && context.incomingEventBuilder.add('method', method);
|
|
543
|
+
}
|
|
544
|
+
return await next(context);
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Extract a spoofed HTTP method from a parsed body, supporting both JSON objects and
|
|
548
|
+
* urlencoded bodies (whose fields live on a `URLSearchParams`, not as own properties).
|
|
549
|
+
*
|
|
550
|
+
* @param body - The parsed body.
|
|
551
|
+
* @returns The spoofed method, or undefined.
|
|
552
|
+
*/
|
|
553
|
+
extractSpoofedMethod(body) {
|
|
554
|
+
if (body instanceof URLSearchParams) {
|
|
555
|
+
return body.get('$method$') ?? undefined;
|
|
556
|
+
}
|
|
557
|
+
if (typeof body === 'object' && body !== null) {
|
|
558
|
+
return body.$method$;
|
|
559
|
+
}
|
|
560
|
+
return undefined;
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Convert the raw event into a Node.js IncomingMessage.
|
|
564
|
+
*
|
|
565
|
+
* @param rawEvent - The raw event from the platform.
|
|
566
|
+
* @returns The converted IncomingMessage.
|
|
567
|
+
*/
|
|
568
|
+
toNodeMessage(rawEvent) {
|
|
569
|
+
const headers = normalizeHeaders(rawEvent);
|
|
570
|
+
return {
|
|
571
|
+
headers: {
|
|
572
|
+
'content-type': headers['content-type'],
|
|
573
|
+
'content-length': headers['content-length'],
|
|
574
|
+
'transfer-encoding': headers['transfer-encoding']
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Extract and parse the body from the message.
|
|
580
|
+
*
|
|
581
|
+
* @param message - The incoming HTTP message.
|
|
582
|
+
* @returns A Promise resolving to the parsed body.
|
|
583
|
+
* @throws {AwsLambdaHttpAdapterError} If the body parsing fails or is invalid.
|
|
584
|
+
*/
|
|
585
|
+
getBody(message, rawEvent) {
|
|
586
|
+
if (!typeIs.hasBody(message)) {
|
|
587
|
+
// `hasBody` needs `content-length` or `transfer-encoding`. API Gateway sends one in practice,
|
|
588
|
+
// but a synthetic event or a hand-rolled invoker may not, and the payload would then vanish
|
|
589
|
+
// without a trace. Say so instead of returning an empty body in silence.
|
|
590
|
+
if (isNotEmpty(rawEvent.body)) {
|
|
591
|
+
Logger.getInstance().debug('A request body was present but ignored: the event carries neither `content-length` nor ' +
|
|
592
|
+
'`transfer-encoding`, so it cannot be read. Set `content-length` on the event.');
|
|
593
|
+
}
|
|
594
|
+
return {};
|
|
595
|
+
}
|
|
596
|
+
const defaultOptions = { limit: '100kb', defaultType: 'text/plain', defaultCharset: 'utf-8' };
|
|
597
|
+
const { defaultType, defaultCharset, limit: rawLimit } = this.blueprint.get('stone.http.body', defaultOptions);
|
|
598
|
+
const limit = bytes.parse(rawLimit) ?? 100000;
|
|
599
|
+
const encoding = getCharset(message, defaultCharset);
|
|
600
|
+
const type = typeIs(message, ['urlencoded', 'json', 'text', 'bin']) ?? defaultType;
|
|
601
|
+
// Decode ONCE to a Buffer. Binary payloads must never round-trip through a lossy UTF-8 string
|
|
602
|
+
// (which corrupts any non-UTF-8 byte); the limit is measured on the true byte length.
|
|
603
|
+
const rawBuffer = this.getRawBuffer(rawEvent, encoding);
|
|
604
|
+
if (rawBuffer.byteLength > limit) {
|
|
605
|
+
throw new AwsLambdaHttpAdapterError('Body payload exceeds configured limit.');
|
|
606
|
+
}
|
|
607
|
+
return this.parseBodyContent(type, rawBuffer, encoding);
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Decode the request body into a Buffer, honouring base64 encoding.
|
|
611
|
+
*
|
|
612
|
+
* @param rawEvent - The raw event containing the body.
|
|
613
|
+
* @param encoding - The charset for a plain-text (non-base64) body.
|
|
614
|
+
* @returns The body as a Buffer.
|
|
615
|
+
*/
|
|
616
|
+
getRawBuffer(rawEvent, encoding) {
|
|
617
|
+
if (typeof rawEvent.body === 'string') {
|
|
618
|
+
return rawEvent.isBase64Encoded === true
|
|
619
|
+
? Buffer.from(rawEvent.body, 'base64')
|
|
620
|
+
: Buffer.from(rawEvent.body, encoding);
|
|
621
|
+
}
|
|
622
|
+
if (typeof rawEvent.body === 'object' && rawEvent.body !== null) {
|
|
623
|
+
return Buffer.from(JSON.stringify(rawEvent.body), encoding);
|
|
624
|
+
}
|
|
625
|
+
return Buffer.alloc(0);
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Parse the body content based on the specified type and encoding.
|
|
629
|
+
*
|
|
630
|
+
* @param type - The content type of the body.
|
|
631
|
+
* @param buffer - The raw body content as a Buffer.
|
|
632
|
+
* @param encoding - The encoding of the body content.
|
|
633
|
+
* @returns The parsed body content as an object, string, or Buffer.
|
|
634
|
+
* @throws {AwsLambdaHttpAdapterError} If parsing fails.
|
|
635
|
+
*/
|
|
636
|
+
parseBodyContent(type, buffer, encoding) {
|
|
637
|
+
try {
|
|
638
|
+
switch (type) {
|
|
639
|
+
case 'json': {
|
|
640
|
+
const text = buffer.toString(encoding);
|
|
641
|
+
return isNotEmpty(text) ? JSON.parse(text) : {};
|
|
642
|
+
}
|
|
643
|
+
case 'text':
|
|
644
|
+
return buffer.toString(encoding);
|
|
645
|
+
case 'urlencoded':
|
|
646
|
+
return new URLSearchParams(buffer.toString(encoding));
|
|
647
|
+
case 'bin':
|
|
648
|
+
return buffer; // Return the raw bytes untouched — no lossy re-encoding.
|
|
649
|
+
default:
|
|
650
|
+
return {};
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
catch (error) {
|
|
654
|
+
throw new AwsLambdaHttpAdapterError('Failed to parse request body.', { cause: error });
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Meta Middleware for processing the request body.
|
|
660
|
+
*/
|
|
661
|
+
const MetaBodyEventMiddleware = { module: BodyEventMiddleware, isClass: true };
|
|
662
|
+
|
|
500
663
|
/**
|
|
501
664
|
* Middleware for handling incoming events and transforming them into Stone.js events.
|
|
502
665
|
*
|
|
@@ -721,6 +884,9 @@ const awsLambdaHttpAdapterBlueprint = {
|
|
|
721
884
|
platform: AWS_LAMBDA_HTTP_PLATFORM,
|
|
722
885
|
middleware: [
|
|
723
886
|
MetaIncomingEventMiddleware,
|
|
887
|
+
// On by default: an app whose Lambda adapter was missing this line worked locally and
|
|
888
|
+
// received an empty body in production, with no error anywhere. Inert without a body.
|
|
889
|
+
MetaBodyEventMiddleware,
|
|
724
890
|
MetaServerResponseMiddleware
|
|
725
891
|
],
|
|
726
892
|
resolver: awsLambdaHttpAdapterResolver,
|
|
@@ -772,162 +938,6 @@ const AwsLambdaHttp = (options = {}) => {
|
|
|
772
938
|
});
|
|
773
939
|
};
|
|
774
940
|
|
|
775
|
-
/**
|
|
776
|
-
* Class representing a BodyEventMiddleware.
|
|
777
|
-
*
|
|
778
|
-
* This middleware handles platform-specific messages and transforms them into Stone.js IncomingEvent objects.
|
|
779
|
-
*
|
|
780
|
-
* @author Mr. Stone
|
|
781
|
-
*/
|
|
782
|
-
class BodyEventMiddleware {
|
|
783
|
-
/**
|
|
784
|
-
* The blueprint for resolving configuration and dependencies.
|
|
785
|
-
*/
|
|
786
|
-
blueprint;
|
|
787
|
-
/**
|
|
788
|
-
* Create a BodyEventMiddleware.
|
|
789
|
-
*
|
|
790
|
-
* @param {blueprint} options - Options for creating the BodyEventMiddleware.
|
|
791
|
-
*/
|
|
792
|
-
constructor({ blueprint }) {
|
|
793
|
-
this.blueprint = blueprint;
|
|
794
|
-
}
|
|
795
|
-
/**
|
|
796
|
-
* Handles the incoming event, processes it, and invokes the next middleware in the pipeline.
|
|
797
|
-
*
|
|
798
|
-
* @param context - The adapter context containing the raw event, execution context, and other data.
|
|
799
|
-
* @param next - The next middleware to be invoked in the pipeline.
|
|
800
|
-
* @returns A promise that resolves to the destination type after processing.
|
|
801
|
-
*
|
|
802
|
-
* @throws {AwsLambdaHttpAdapterError} If required components such as the rawEvent or IncomingEventBuilder are not provided.
|
|
803
|
-
*/
|
|
804
|
-
async handle(context, next) {
|
|
805
|
-
if (context.rawEvent === undefined || context.incomingEventBuilder?.add === undefined) {
|
|
806
|
-
throw new AwsLambdaHttpAdapterError('The context is missing required components.');
|
|
807
|
-
}
|
|
808
|
-
if (!isMultipart(this.toNodeMessage(context.rawEvent))) {
|
|
809
|
-
const body = this.getBody(this.toNodeMessage(context.rawEvent), context.rawEvent);
|
|
810
|
-
const method = this.extractSpoofedMethod(body);
|
|
811
|
-
context
|
|
812
|
-
.incomingEventBuilder
|
|
813
|
-
.add('body', body)
|
|
814
|
-
// Keep the untouched payload available even after parsing (webhook signatures, etc.).
|
|
815
|
-
.add('metadata', { rawBody: getRawBody(context.rawEvent) });
|
|
816
|
-
// In fullstack forms, the method is spoofed and sent as a hidden field.
|
|
817
|
-
isNotEmpty(method) && context.incomingEventBuilder.add('method', method);
|
|
818
|
-
}
|
|
819
|
-
return await next(context);
|
|
820
|
-
}
|
|
821
|
-
/**
|
|
822
|
-
* Extract a spoofed HTTP method from a parsed body, supporting both JSON objects and
|
|
823
|
-
* urlencoded bodies (whose fields live on a `URLSearchParams`, not as own properties).
|
|
824
|
-
*
|
|
825
|
-
* @param body - The parsed body.
|
|
826
|
-
* @returns The spoofed method, or undefined.
|
|
827
|
-
*/
|
|
828
|
-
extractSpoofedMethod(body) {
|
|
829
|
-
if (body instanceof URLSearchParams) {
|
|
830
|
-
return body.get('$method$') ?? undefined;
|
|
831
|
-
}
|
|
832
|
-
if (typeof body === 'object' && body !== null) {
|
|
833
|
-
return body.$method$;
|
|
834
|
-
}
|
|
835
|
-
return undefined;
|
|
836
|
-
}
|
|
837
|
-
/**
|
|
838
|
-
* Convert the raw event into a Node.js IncomingMessage.
|
|
839
|
-
*
|
|
840
|
-
* @param rawEvent - The raw event from the platform.
|
|
841
|
-
* @returns The converted IncomingMessage.
|
|
842
|
-
*/
|
|
843
|
-
toNodeMessage(rawEvent) {
|
|
844
|
-
const headers = normalizeHeaders(rawEvent);
|
|
845
|
-
return {
|
|
846
|
-
headers: {
|
|
847
|
-
'content-type': headers['content-type'],
|
|
848
|
-
'content-length': headers['content-length'],
|
|
849
|
-
'transfer-encoding': headers['transfer-encoding']
|
|
850
|
-
}
|
|
851
|
-
};
|
|
852
|
-
}
|
|
853
|
-
/**
|
|
854
|
-
* Extract and parse the body from the message.
|
|
855
|
-
*
|
|
856
|
-
* @param message - The incoming HTTP message.
|
|
857
|
-
* @returns A Promise resolving to the parsed body.
|
|
858
|
-
* @throws {AwsLambdaHttpAdapterError} If the body parsing fails or is invalid.
|
|
859
|
-
*/
|
|
860
|
-
getBody(message, rawEvent) {
|
|
861
|
-
if (!typeIs.hasBody(message)) {
|
|
862
|
-
return {};
|
|
863
|
-
}
|
|
864
|
-
const defaultOptions = { limit: '100kb', defaultType: 'text/plain', defaultCharset: 'utf-8' };
|
|
865
|
-
const { defaultType, defaultCharset, limit: rawLimit } = this.blueprint.get('stone.http.body', defaultOptions);
|
|
866
|
-
const limit = bytes.parse(rawLimit) ?? 100000;
|
|
867
|
-
const encoding = getCharset(message, defaultCharset);
|
|
868
|
-
const type = typeIs(message, ['urlencoded', 'json', 'text', 'bin']) ?? defaultType;
|
|
869
|
-
// Decode ONCE to a Buffer. Binary payloads must never round-trip through a lossy UTF-8 string
|
|
870
|
-
// (which corrupts any non-UTF-8 byte); the limit is measured on the true byte length.
|
|
871
|
-
const rawBuffer = this.getRawBuffer(rawEvent, encoding);
|
|
872
|
-
if (rawBuffer.byteLength > limit) {
|
|
873
|
-
throw new AwsLambdaHttpAdapterError('Body payload exceeds configured limit.');
|
|
874
|
-
}
|
|
875
|
-
return this.parseBodyContent(type, rawBuffer, encoding);
|
|
876
|
-
}
|
|
877
|
-
/**
|
|
878
|
-
* Decode the request body into a Buffer, honouring base64 encoding.
|
|
879
|
-
*
|
|
880
|
-
* @param rawEvent - The raw event containing the body.
|
|
881
|
-
* @param encoding - The charset for a plain-text (non-base64) body.
|
|
882
|
-
* @returns The body as a Buffer.
|
|
883
|
-
*/
|
|
884
|
-
getRawBuffer(rawEvent, encoding) {
|
|
885
|
-
if (typeof rawEvent.body === 'string') {
|
|
886
|
-
return rawEvent.isBase64Encoded === true
|
|
887
|
-
? Buffer.from(rawEvent.body, 'base64')
|
|
888
|
-
: Buffer.from(rawEvent.body, encoding);
|
|
889
|
-
}
|
|
890
|
-
if (typeof rawEvent.body === 'object' && rawEvent.body !== null) {
|
|
891
|
-
return Buffer.from(JSON.stringify(rawEvent.body), encoding);
|
|
892
|
-
}
|
|
893
|
-
return Buffer.alloc(0);
|
|
894
|
-
}
|
|
895
|
-
/**
|
|
896
|
-
* Parse the body content based on the specified type and encoding.
|
|
897
|
-
*
|
|
898
|
-
* @param type - The content type of the body.
|
|
899
|
-
* @param buffer - The raw body content as a Buffer.
|
|
900
|
-
* @param encoding - The encoding of the body content.
|
|
901
|
-
* @returns The parsed body content as an object, string, or Buffer.
|
|
902
|
-
* @throws {AwsLambdaHttpAdapterError} If parsing fails.
|
|
903
|
-
*/
|
|
904
|
-
parseBodyContent(type, buffer, encoding) {
|
|
905
|
-
try {
|
|
906
|
-
switch (type) {
|
|
907
|
-
case 'json': {
|
|
908
|
-
const text = buffer.toString(encoding);
|
|
909
|
-
return isNotEmpty(text) ? JSON.parse(text) : {};
|
|
910
|
-
}
|
|
911
|
-
case 'text':
|
|
912
|
-
return buffer.toString(encoding);
|
|
913
|
-
case 'urlencoded':
|
|
914
|
-
return new URLSearchParams(buffer.toString(encoding));
|
|
915
|
-
case 'bin':
|
|
916
|
-
return buffer; // Return the raw bytes untouched — no lossy re-encoding.
|
|
917
|
-
default:
|
|
918
|
-
return {};
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
catch (error) {
|
|
922
|
-
throw new AwsLambdaHttpAdapterError('Failed to parse request body.', { cause: error });
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
/**
|
|
927
|
-
* Meta Middleware for processing the request body.
|
|
928
|
-
*/
|
|
929
|
-
const MetaBodyEventMiddleware = { module: BodyEventMiddleware, isClass: true };
|
|
930
|
-
|
|
931
941
|
/**
|
|
932
942
|
* Class representing a FilesEventMiddleware.
|
|
933
943
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IBlueprint, NextMiddleware, type MetaMiddleware } from '@stone-js/core';
|
|
2
|
-
import { AwsLambdaHttpAdapterContext, AwsLambdaHttpAdapterResponseBuilder } from '../declarations';
|
|
2
|
+
import { AwsLambdaHttpAdapterContext, AwsLambdaHttpAdapterResponseBuilder } from '../declarations.js';
|
|
3
3
|
/**
|
|
4
4
|
* Class representing a BodyEventMiddleware.
|
|
5
5
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IBlueprint, NextMiddleware, type MetaMiddleware } from '@stone-js/core';
|
|
2
|
-
import { AwsLambdaHttpAdapterContext, AwsLambdaHttpAdapterResponseBuilder } from '../declarations';
|
|
2
|
+
import { AwsLambdaHttpAdapterContext, AwsLambdaHttpAdapterResponseBuilder } from '../declarations.js';
|
|
3
3
|
/**
|
|
4
4
|
* Class representing a FilesEventMiddleware.
|
|
5
5
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IBlueprint, NextMiddleware, type MetaMiddleware } from '@stone-js/core';
|
|
2
|
-
import { AwsLambdaHttpAdapterContext, AwsLambdaHttpAdapterResponseBuilder } from '../declarations';
|
|
2
|
+
import { AwsLambdaHttpAdapterContext, AwsLambdaHttpAdapterResponseBuilder } from '../declarations.js';
|
|
3
3
|
/**
|
|
4
4
|
* Middleware for handling incoming events and transforming them into Stone.js events.
|
|
5
5
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NextMiddleware, type MetaMiddleware } from '@stone-js/core';
|
|
2
|
-
import { AwsLambdaHttpAdapterContext, AwsLambdaHttpAdapterResponseBuilder } from '../declarations';
|
|
2
|
+
import { AwsLambdaHttpAdapterContext, AwsLambdaHttpAdapterResponseBuilder } from '../declarations.js';
|
|
3
3
|
/**
|
|
4
4
|
* Middleware for handling server responses and transforming them into the appropriate HTTP responses.
|
|
5
5
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AwsLambdaContext, AwsLambdaHttpEvent, RawHttpResponse } from '../declarations';
|
|
1
|
+
import { AwsLambdaContext, AwsLambdaHttpEvent, RawHttpResponse } from '../declarations.js';
|
|
2
2
|
import { AdapterConfig, AppConfig, StoneBlueprint } from '@stone-js/core';
|
|
3
3
|
import { HttpConfig, IncomingHttpEvent, IncomingHttpEventOptions, OutgoingHttpResponse } from '@stone-js/http-core';
|
|
4
4
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stone-js/aws-lambda-http-adapter",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.9",
|
|
4
4
|
"description": "Official AWS Lambda HTTP adapter for Stone.js. Run your Stone.js apps on AWS Lambda behind API Gateway with full Continuum lifecycle support.",
|
|
5
5
|
"author": "Mr. Stone <evensstone@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -50,10 +50,10 @@
|
|
|
50
50
|
"node": ">=18.17.0"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
|
-
"@stone-js/core": "0.8.
|
|
54
|
-
"@stone-js/env": "0.8.
|
|
55
|
-
"@stone-js/filesystem": "0.8.
|
|
56
|
-
"@stone-js/http-core": "0.8.
|
|
53
|
+
"@stone-js/core": "0.8.9",
|
|
54
|
+
"@stone-js/env": "0.8.9",
|
|
55
|
+
"@stone-js/filesystem": "0.8.9",
|
|
56
|
+
"@stone-js/http-core": "0.8.9"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"accepts": "^1.3.8",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"proxy-addr": "^2.0.7",
|
|
64
64
|
"statuses": "^2.0.1",
|
|
65
65
|
"type-is": "^2.0.1",
|
|
66
|
-
"@stone-js/config": "0.8.
|
|
66
|
+
"@stone-js/config": "0.8.9"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@commitlint/cli": "^19.6.1",
|