@stone-js/aws-lambda-http-adapter 0.1.0 → 0.3.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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Stone.js
3
+ Copyright © 2026 Stone Foundation
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![npm](https://img.shields.io/npm/l/@stone-js/aws-lambda-http-adapter)](https://opensource.org/licenses/MIT)
4
4
  [![npm](https://img.shields.io/npm/v/@stone-js/aws-lambda-http-adapter)](https://www.npmjs.com/package/@stone-js/aws-lambda-http-adapter)
5
5
  [![npm](https://img.shields.io/npm/dm/@stone-js/aws-lambda-http-adapter)](https://www.npmjs.com/package/@stone-js/aws-lambda-http-adapter)
6
- ![Maintenance](https://img.shields.io/maintenance/yes/2025)
6
+ ![Maintenance](https://img.shields.io/maintenance/yes/2026)
7
7
  [![Build Status](https://github.com/stone-foundation/stone-js-aws-lambda-http-adapter/actions/workflows/main.yml/badge.svg)](https://github.com/stone-foundation/stone-js-aws-lambda-http-adapter/actions/workflows/main.yml)
8
8
  [![Publish Package to npmjs](https://github.com/stone-foundation/stone-js-aws-lambda-http-adapter/actions/workflows/release.yml/badge.svg)](https://github.com/stone-foundation/stone-js-aws-lambda-http-adapter/actions/workflows/release.yml)
9
9
  [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=stone-foundation_stone-js-aws-lambda-http-adapter&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=stone-foundation_stone-js-aws-lambda-http-adapter)
@@ -0,0 +1,68 @@
1
+ import { IntegrationError, classDecoratorLegacyWrapper } from '@stone-js/core';
2
+
3
+ /**
4
+ * A constant representing the AWS Lambda HTTP platform identifier.
5
+ *
6
+ * This constant is used as an alias for the AWS Lambda HTTP Adapter within the Stone.js framework.
7
+ * It helps in identifying and configuring platform-specific adapters or components for handling
8
+ * HTTP requests and responses.
9
+ */
10
+ const AWS_LAMBDA_HTTP_PLATFORM = 'aws_lambda_http';
11
+
12
+ /**
13
+ * Custom error for AWS Lambda adapter operations.
14
+ */
15
+ class AwsLambdaHttpAdapterError extends IntegrationError {
16
+ constructor(message, options) {
17
+ super(message, options);
18
+ this.name = 'AwsLambdaHttpAdapterError';
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Default blueprint configuration for the AWS Lambda Http Adapter.
24
+ *
25
+ * This blueprint defines the initial configuration for the AWS Lambda Http adapter
26
+ * within the Stone.js framework. It includes:
27
+ * - An alias for the AWS Lambda platform (`AWS_LAMBDA_HTTP_PLATFORM`).
28
+ * - A default resolver function (currently a placeholder).
29
+ * - Middleware, hooks, and state flags (`current`, `default`, `preferred`).
30
+ *
31
+ * NB: This is a stub for browser environments and does not perform any actual functionality in the browser.
32
+ */
33
+ const awsLambdaHttpAdapterBlueprint = { stone: { http: {}, adapters: [] } };
34
+
35
+ /**
36
+ * A Stone.js decorator that integrates the AWS Lambda HTTP Adapter with a class.
37
+ *
38
+ * This decorator modifies the class to seamlessly enable AWS Lambda HTTP as the
39
+ * execution environment for a Stone.js application. By applying this decorator,
40
+ * the class is automatically configured with the necessary blueprint for AWS Lambda HTTP.
41
+ *
42
+ * NB: This decorator is stubbed for browser environments compatibility and does not
43
+ * perform any actual functionality in the browser. It is intended for use in Node.js environments
44
+ * where the Node.js HTTP adapter is applicable.
45
+ *
46
+ * @template T - The type of the class being decorated. Defaults to `ClassType`.
47
+ * @param options - Optional configuration to customize the AWS Lambda HTTP Adapter.
48
+ *
49
+ * @returns A class decorator that applies the AWS Lambda HTTP adapter configuration.
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * import { AwsLambdaHttp } from '@stone-js/aws-lambda-http-adapter';
54
+ *
55
+ * @AwsLambdaHttp({
56
+ * alias: 'MyAwsLambdaHttpAdapter',
57
+ * current: true,
58
+ * })
59
+ * class App {
60
+ * // Your application logic here
61
+ * }
62
+ * ```
63
+ */
64
+ const AwsLambdaHttp = (_options = {}) => {
65
+ return classDecoratorLegacyWrapper((_target, _context) => { });
66
+ };
67
+
68
+ export { AWS_LAMBDA_HTTP_PLATFORM, AwsLambdaHttp, AwsLambdaHttpAdapterError, awsLambdaHttpAdapterBlueprint };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { IRawResponseWrapper, RawResponseOptions, AdapterContext, IAdapterEventBuilder, Adapter, IBlueprint, IAdapterErrorHandler, AdapterErrorContext, AdapterEventBuilderType, AdapterResolver, AdapterConfig, AppConfig, StoneBlueprint, ClassType, IntegrationError, ErrorOptions, BlueprintContext, NextMiddleware, MetaMiddleware } from '@stone-js/core';
1
+ import { IRawResponseWrapper, RawResponseOptions, AdapterContext, IAdapterEventBuilder, Adapter, IBlueprint, IAdapterErrorHandler, AdapterErrorContext, AdapterEventBuilderType, AdapterResolver, AdapterConfig, StoneBlueprint, AppConfig, ClassType, IntegrationError, ErrorOptions, BlueprintContext, NextMiddleware, MetaMiddleware } from '@stone-js/core';
2
2
  import { IncomingHttpEvent, IncomingHttpEventOptions, OutgoingHttpResponse, HttpConfig } from '@stone-js/http-core';
3
3
 
4
4
  /**
@@ -59,6 +59,16 @@ declare class RawHttpResponseWrapper implements IRawResponseWrapper<RawHttpRespo
59
59
  * ```
60
60
  */
61
61
  respond(): RawHttpResponse;
62
+ /**
63
+ * Normalizes the headers to a consistent format.
64
+ *
65
+ * Converts Headers or Record<string, string> to a normalized Record<string, string>
66
+ * with all keys in lowercase.
67
+ *
68
+ * @param headers - The headers to normalize.
69
+ * @returns A normalized record of headers.
70
+ */
71
+ private normalizeHeaders;
62
72
  }
63
73
 
64
74
  /**
@@ -451,6 +461,24 @@ declare class BodyEventMiddleware {
451
461
  * @throws {AwsLambdaHttpAdapterError} If the body parsing fails or is invalid.
452
462
  */
453
463
  private getBody;
464
+ /**
465
+ * Get the normalized raw body from the event.
466
+ *
467
+ * @param rawEvent - The raw event containing the body.
468
+ * @param encoding - The encoding to use for the body.
469
+ * @returns The normalized body as a string.
470
+ */
471
+ private getNormalizedRawBody;
472
+ /**
473
+ * Parse the body content based on the specified type and encoding.
474
+ *
475
+ * @param type - The content type of the body.
476
+ * @param body - The raw body content as a string.
477
+ * @param encoding - The encoding of the body content.
478
+ * @returns The parsed body content as an object, string, or Buffer.
479
+ * @throws {AwsLambdaHttpAdapterError} If parsing fails.
480
+ */
481
+ private parseBodyContent;
454
482
  }
455
483
  /**
456
484
  * Meta Middleware for processing the request body.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { IntegrationError, Adapter, AdapterEventBuilder, defaultLoggerResolver, isNotEmpty, defaultKernelResolver, classDecoratorLegacyWrapper, addBlueprint } from '@stone-js/core';
2
- import { IncomingHttpEvent, HTTP_INTERNAL_SERVER_ERROR, BinaryFileResponse, OutgoingHttpResponse, CookieCollection, isIpTrusted, getHostname, getProtocol, httpCoreBlueprint, isMultipart, getCharset, getType, getFilesUploads } from '@stone-js/http-core';
1
+ import { IntegrationError, Adapter, AdapterEventBuilder, defaultLoggerResolver, defaultKernelResolver, isNotEmpty, classDecoratorLegacyWrapper, addBlueprint } from '@stone-js/core';
2
+ import { IncomingHttpEvent, HTTP_INTERNAL_SERVER_ERROR, BinaryFileResponse, OutgoingHttpResponse, CookieCollection, isIpTrusted, getHostname, getProtocol, httpCoreBlueprint, isMultipart, getCharset, getFilesUploads } from '@stone-js/http-core';
3
3
  import mime from 'mime';
4
4
  import accepts from 'accepts';
5
5
  import statuses from 'statuses';
@@ -73,9 +73,22 @@ class RawHttpResponseWrapper {
73
73
  respond() {
74
74
  return {
75
75
  ...this.options,
76
- statusCode: this.options.statusCode ?? 500
76
+ statusCode: this.options.statusCode ?? 500,
77
+ headers: this.normalizeHeaders(this.options.headers)
77
78
  };
78
79
  }
80
+ /**
81
+ * Normalizes the headers to a consistent format.
82
+ *
83
+ * Converts Headers or Record<string, string> to a normalized Record<string, string>
84
+ * with all keys in lowercase.
85
+ *
86
+ * @param headers - The headers to normalize.
87
+ * @returns A normalized record of headers.
88
+ */
89
+ normalizeHeaders(headers) {
90
+ return Object.fromEntries(new Headers(headers ?? {}));
91
+ }
79
92
  }
80
93
 
81
94
  /**
@@ -800,19 +813,58 @@ class BodyEventMiddleware {
800
813
  const { defaultType, defaultCharset, limit: rawLimit } = this.blueprint.get('stone.http.body', defaultOptions);
801
814
  const limit = bytes.parse(rawLimit) ?? 100000;
802
815
  const encoding = getCharset(message, defaultCharset);
803
- const type = getType(message, defaultType);
804
- if (typeIs.is(type, ['urlencoded', 'json', 'text', 'bin']) === false) {
805
- return {};
816
+ const type = typeIs(message, ['urlencoded', 'json', 'text', 'bin']) ?? defaultType;
817
+ const rawBodyContent = this.getNormalizedRawBody(rawEvent, encoding);
818
+ if (Buffer.byteLength(rawBodyContent, encoding) > limit) {
819
+ throw new AwsLambdaHttpAdapterError('Body payload exceeds configured limit.');
806
820
  }
807
- const stringifiedBody = typeof rawEvent.body === 'string'
808
- ? rawEvent.body
809
- : (typeof rawEvent.body === 'object' && rawEvent.body !== null
810
- ? JSON.stringify(rawEvent.body)
811
- : '');
812
- if (Buffer.byteLength(stringifiedBody, encoding) > limit) {
813
- throw new AwsLambdaHttpAdapterError('The context is missing required components.');
821
+ return this.parseBodyContent(type, rawBodyContent, encoding);
822
+ }
823
+ /**
824
+ * Get the normalized raw body from the event.
825
+ *
826
+ * @param rawEvent - The raw event containing the body.
827
+ * @param encoding - The encoding to use for the body.
828
+ * @returns The normalized body as a string.
829
+ */
830
+ getNormalizedRawBody(rawEvent, encoding) {
831
+ if (typeof rawEvent.body === 'string') {
832
+ return rawEvent.isBase64Encoded === true
833
+ ? Buffer.from(rawEvent.body, 'base64').toString(encoding)
834
+ : rawEvent.body;
835
+ }
836
+ if (typeof rawEvent.body === 'object' && rawEvent.body !== null) {
837
+ return JSON.stringify(rawEvent.body);
838
+ }
839
+ return '';
840
+ }
841
+ /**
842
+ * Parse the body content based on the specified type and encoding.
843
+ *
844
+ * @param type - The content type of the body.
845
+ * @param body - The raw body content as a string.
846
+ * @param encoding - The encoding of the body content.
847
+ * @returns The parsed body content as an object, string, or Buffer.
848
+ * @throws {AwsLambdaHttpAdapterError} If parsing fails.
849
+ */
850
+ parseBodyContent(type, body, encoding) {
851
+ try {
852
+ switch (type) {
853
+ case 'json':
854
+ return isNotEmpty(body) ? JSON.parse(body) : {};
855
+ case 'text':
856
+ return body;
857
+ case 'urlencoded':
858
+ return new URLSearchParams(body);
859
+ case 'bin':
860
+ return Buffer.from(body, encoding);
861
+ default:
862
+ return {};
863
+ }
864
+ }
865
+ catch (error) {
866
+ throw new AwsLambdaHttpAdapterError('Failed to parse request body.', { cause: error });
814
867
  }
815
- return rawEvent.body;
816
868
  }
817
869
  }
818
870
  /**
@@ -871,8 +923,14 @@ class FilesEventMiddleware {
871
923
  * @returns The normalized event.
872
924
  */
873
925
  normalizeEvent(rawEvent) {
926
+ let body = rawEvent.body;
927
+ if (typeof rawEvent.body === 'string') {
928
+ body = rawEvent.isBase64Encoded === true
929
+ ? Buffer.from(rawEvent.body, 'base64')
930
+ : Buffer.from(rawEvent.body, 'utf-8');
931
+ }
874
932
  return {
875
- body: rawEvent.body,
933
+ body,
876
934
  headers: {
877
935
  'content-type': rawEvent.headers['content-type'] ?? rawEvent.headers['Content-Type'],
878
936
  'content-length': rawEvent.headers['content-length'] ?? rawEvent.headers['Content-Length'],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stone-js/aws-lambda-http-adapter",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
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",
@@ -34,12 +34,18 @@
34
34
  "types": "./dist/index.d.ts",
35
35
  "exports": {
36
36
  ".": {
37
- "types": "./dist/index.d.ts",
38
- "default": "./dist/index.js"
37
+ "browser": {
38
+ "types": "./dist/index.d.ts",
39
+ "default": "./dist/browser.js"
40
+ },
41
+ "default": {
42
+ "types": "./dist/index.d.ts",
43
+ "default": "./dist/index.js"
44
+ }
39
45
  }
40
46
  },
41
47
  "engines": {
42
- "node": ">=18.0.0"
48
+ "node": ">=18.17.0"
43
49
  },
44
50
  "scripts": {
45
51
  "lint": "ts-standard src",
@@ -65,7 +71,7 @@
65
71
  "accepts": "^1.3.8",
66
72
  "bytes": "^3.1.2",
67
73
  "content-type": "^1.0.5",
68
- "mime": "^4.0.7",
74
+ "mime": "^4.1.0",
69
75
  "proxy-addr": "^2.0.7",
70
76
  "statuses": "^2.0.1",
71
77
  "type-is": "^2.0.1"
@@ -76,27 +82,27 @@
76
82
  "@rollup/plugin-commonjs": "^28.0.2",
77
83
  "@rollup/plugin-multi-entry": "^6.0.1",
78
84
  "@rollup/plugin-node-resolve": "^15.2.3",
79
- "@rollup/plugin-typescript": "^12.1.1",
85
+ "@rollup/plugin-typescript": "^12.3.0",
80
86
  "@types/accepts": "^1.3.7",
81
87
  "@types/bytes": "^3.1.5",
82
88
  "@types/content-type": "^1.1.8",
83
- "@types/node": "^24.0.1",
89
+ "@types/node": "^24.9.2",
84
90
  "@types/proxy-addr": "^2.0.3",
85
91
  "@types/statuses": "^2.0.5",
86
92
  "@types/type-is": "^1.6.7",
87
- "@vitest/coverage-v8": "^3.2.3",
93
+ "@vitest/coverage-v8": "^3.2.4",
88
94
  "husky": "^9.1.7",
89
- "rimraf": "^6.0.1",
90
- "rollup": "^4.43.0",
95
+ "rimraf": "^6.1.0",
96
+ "rollup": "^4.52.5",
91
97
  "rollup-plugin-delete": "^3.0.1",
92
- "rollup-plugin-dts": "^6.2.1",
93
- "rollup-plugin-node-externals": "^8.0.0",
98
+ "rollup-plugin-dts": "^6.2.3",
99
+ "rollup-plugin-node-externals": "^8.1.1",
94
100
  "ts-standard": "^12.0.2",
95
101
  "tslib": "^2.8.1",
96
- "typedoc": "^0.28.5",
97
- "typedoc-plugin-markdown": "^4.6.4",
98
- "typescript": "^5.6.3",
99
- "vitest": "^3.2.3"
102
+ "typedoc": "^0.28.14",
103
+ "typedoc-plugin-markdown": "^4.9.0",
104
+ "typescript": "^5.9.3",
105
+ "vitest": "^3.2.4"
100
106
  },
101
107
  "ts-standard": {
102
108
  "globals": [