diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 063ccc76ab..515ae389cd 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -69,7 +69,6 @@ body:
options:
- 20.x
- 18.x
- - 16.x
validations:
required: true
- type: dropdown
diff --git a/.github/actions/cached-node-modules/action.yml b/.github/actions/cached-node-modules/action.yml
index a5dbabd829..e8945e2779 100644
--- a/.github/actions/cached-node-modules/action.yml
+++ b/.github/actions/cached-node-modules/action.yml
@@ -14,10 +14,6 @@ outputs:
runs:
using: "composite"
steps:
- - name: Install npm
- # We need to keep this npm version until we drop Node.js 16 support because Node.js 16 doesn't support npm 10
- run: npm i -g npm@next-9
- shell: bash
- name: Cache node modules
id: cache-node-modules
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0
diff --git a/.github/workflows/reusable-run-linting-check-and-unit-tests.yml b/.github/workflows/reusable-run-linting-check-and-unit-tests.yml
index 20b761ac55..f3926b9bc9 100644
--- a/.github/workflows/reusable-run-linting-check-and-unit-tests.yml
+++ b/.github/workflows/reusable-run-linting-check-and-unit-tests.yml
@@ -13,7 +13,7 @@ jobs:
NODE_ENV: dev
strategy:
matrix:
- version: [16, 18, 20]
+ version: [18, 20]
fail-fast: false
steps:
- name: Checkout code
diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml
index cfec183523..d67c48f869 100644
--- a/.github/workflows/run-e2e-tests.yml
+++ b/.github/workflows/run-e2e-tests.yml
@@ -32,7 +32,7 @@ jobs:
packages/parameters,
packages/idempotency,
]
- version: [16, 18, 20]
+ version: [18, 20]
arch: [x86_64, arm64]
fail-fast: false
steps:
diff --git a/docs/index.md b/docs/index.md
index d0ad50cd0a..64a9537f1d 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -250,7 +250,7 @@ You can use Powertools for AWS Lambda (TypeScript) by installing it with your fa
[Lambda Layer](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html){target="_blank"} is a `.zip` file archive that can contain additional code, pre-packaged dependencies, data, or configuration files. We compile and optimize [all dependencies](#install) to achieve an optimal build.
-You can use the Lambda Layer both with CommonJS and ESM (ECMAScript modules) for Node.js 18.x and newer runtimes. **If you are using the managed Node.js 16.x runtime and cannot upgrade, you should use the CommonJS version only**.
+You can use the Lambda Layer both with CommonJS and ESM (ECMAScript modules) for Node.js 18.x and newer runtimes.
??? note "Click to expand and copy any regional Lambda Layer ARN"
| Region | Layer ARN |
diff --git a/docs/overrides/main.html b/docs/overrides/main.html
index 55f86b6188..67a91d25dd 100644
--- a/docs/overrides/main.html
+++ b/docs/overrides/main.html
@@ -8,6 +8,8 @@
{% endblock %}
{% block announce %}
-Starting from July 1, 2024 we will end support for Node.js 16. Learn more here.
+On September 1st, 2024 v1 of Powertools for AWS Lambda (TypeScript) will reach
+ End-of-Life. We recommend you to upgrade to v2.
{% endblock %}
\ No newline at end of file
diff --git a/docs/upgrade.md b/docs/upgrade.md
index af4485814e..7de95af604 100644
--- a/docs/upgrade.md
+++ b/docs/upgrade.md
@@ -31,7 +31,7 @@ V2 is focused on official support for ESM (ECMAScript modules). We've made other
Before you start, we suggest making a copy of your current working project or create a new git branch.
-1. Upgrade Node.js to v16 or higher, Node.js v20 is recommended.
+1. Upgrade Node.js to v18 or higher, Node.js v20 is recommended.
2. Ensure that you have the latest Powertools for AWS Lambda (TypeScript) version via [Lambda Layer](./index.md#lambda-layer) or npm.
3. Review the following sections to confirm whether they apply to your codebase.
diff --git a/layers/src/canary-stack.ts b/layers/src/canary-stack.ts
index 2d7aab1cee..645c9c40c9 100644
--- a/layers/src/canary-stack.ts
+++ b/layers/src/canary-stack.ts
@@ -41,7 +41,7 @@ export class CanaryStack extends Stack {
'../tests/e2e/layerPublisher.class.test.functionCode.ts'
),
handler: 'handler',
- runtime: Runtime.NODEJS_16_X,
+ runtime: Runtime.NODEJS_18_X,
functionName: `canary-${suffix}`,
timeout: Duration.seconds(30),
bundling: {
diff --git a/layers/src/layer-publisher-stack.ts b/layers/src/layer-publisher-stack.ts
index 653a157a7a..71da06737c 100644
--- a/layers/src/layer-publisher-stack.ts
+++ b/layers/src/layer-publisher-stack.ts
@@ -36,11 +36,7 @@ export class LayerPublisherStack extends Stack {
this.lambdaLayerVersion = new LayerVersion(this, 'LambdaPowertoolsLayer', {
layerVersionName: props?.layerName,
description: `Powertools for AWS Lambda (TypeScript) version ${powertoolsPackageVersion}`,
- compatibleRuntimes: [
- Runtime.NODEJS_16_X,
- Runtime.NODEJS_18_X,
- Runtime.NODEJS_20_X,
- ],
+ compatibleRuntimes: [Runtime.NODEJS_18_X, Runtime.NODEJS_20_X],
license: 'MIT-0',
// This is needed because the following regions do not support the compatibleArchitectures property #1400
// ...(![ 'eu-south-2', 'eu-central-2', 'ap-southeast-4' ].includes(Stack.of(this).region) ? { compatibleArchitectures: [Architecture.X86_64] } : {}),
@@ -105,7 +101,7 @@ export class LayerPublisherStack extends Stack {
'node_modules/@aws-sdk/**/README.md ',
];
const buildCommands: string[] = [];
- // We need these modules because they are not included in the nodejs16x runtimes
+ // We install these to get the latest version of the packages
const modulesToInstall: string[] = [
'@aws-sdk/client-dynamodb',
'@aws-sdk/util-dynamodb',
diff --git a/layers/tests/e2e/layerPublisher.test.ts b/layers/tests/e2e/layerPublisher.test.ts
index 821fdd7a13..a00f2cb742 100644
--- a/layers/tests/e2e/layerPublisher.test.ts
+++ b/layers/tests/e2e/layerPublisher.test.ts
@@ -11,7 +11,6 @@ import {
TestInvocationLogs,
invokeFunctionOnce,
generateTestUniqueName,
- getRuntimeKey,
} from '@aws-lambda-powertools/testing-utils';
import { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils/resources/lambda';
import {
@@ -48,13 +47,7 @@ describe(`Layers E2E tests`, () => {
},
});
- /**
- * Node.js 16.x does not support importing ESM modules from Lambda Layers reliably.
- *
- * The feature is available in Node.js 18.x and later.
- * @see https://aws.amazon.com/blogs/compute/node-js-18-x-runtime-now-available-in-aws-lambda/
- */
- const cases = getRuntimeKey() === 'nodejs16x' ? ['CJS'] : ['CJS', 'ESM'];
+ const cases = ['CJS', 'ESM'];
const invocationLogsMap: Map<(typeof cases)[number], TestInvocationLogs> =
new Map();
diff --git a/layers/tests/unit/layer-publisher.test.ts b/layers/tests/unit/layer-publisher.test.ts
index bec61bc0f5..092a05ddce 100644
--- a/layers/tests/unit/layer-publisher.test.ts
+++ b/layers/tests/unit/layer-publisher.test.ts
@@ -25,7 +25,7 @@ describe('Class: LayerPublisherStack', () => {
// Assess
template.resourceCountIs('AWS::Lambda::LayerVersion', 1);
template.hasResourceProperties('AWS::Lambda::LayerVersion', {
- CompatibleRuntimes: ['nodejs16.x', 'nodejs18.x', 'nodejs20.x'],
+ CompatibleRuntimes: ['nodejs18.x', 'nodejs20.x'],
LicenseInfo: 'MIT-0',
/* CompatibleArchitectures: [
'x86_64',
diff --git a/package.json b/package.json
index 8afb42e4eb..a1297ce903 100644
--- a/package.json
+++ b/package.json
@@ -80,7 +80,7 @@
"*.md": "markdownlint-cli2 --fix"
},
"engines": {
- "node": ">=16"
+ "node": ">=18"
},
"overrides": {
"lerna": {
diff --git a/packages/batch/package.json b/packages/batch/package.json
index 35f0d17ff8..6ed76525ef 100644
--- a/packages/batch/package.json
+++ b/packages/batch/package.json
@@ -13,7 +13,6 @@
"test": "npm run test:unit",
"test:unit": "jest --group=unit --detectOpenHandles --coverage --verbose",
"jest": "jest --detectOpenHandles --verbose",
- "test:e2e:nodejs16x": "echo 'Not Implemented'",
"test:e2e:nodejs18x": "echo 'Not Implemented'",
"test:e2e:nodejs20x": "echo 'Not Implemented'",
"test:e2e": "echo 'Not Implemented'",
diff --git a/packages/commons/src/middleware/cleanupMiddlewares.ts b/packages/commons/src/middleware/cleanupMiddlewares.ts
index 231a7ff4c1..7c393077e1 100644
--- a/packages/commons/src/middleware/cleanupMiddlewares.ts
+++ b/packages/commons/src/middleware/cleanupMiddlewares.ts
@@ -69,7 +69,7 @@ const cleanupMiddlewares = async (request: MiddyLikeRequest): Promise => {
IDEMPOTENCY_KEY,
];
for (const functionName of cleanupFunctionNames) {
- if (Object(request.internal).hasOwnProperty(functionName)) {
+ if (Object.hasOwn(request.internal, functionName)) {
const functionReference = request.internal[functionName];
if (isFunction(functionReference)) {
await functionReference(request);
diff --git a/packages/idempotency/package.json b/packages/idempotency/package.json
index 86e1991550..eb5b5ef146 100644
--- a/packages/idempotency/package.json
+++ b/packages/idempotency/package.json
@@ -13,7 +13,6 @@
"test": "npm run test:unit",
"test:unit": "jest --group=unit --detectOpenHandles --coverage --verbose",
"jest": "jest --detectOpenHandles --verbose",
- "test:e2e:nodejs16x": "RUNTIME=nodejs16x jest --group=e2e",
"test:e2e:nodejs18x": "RUNTIME=nodejs18x jest --group=e2e",
"test:e2e:nodejs20x": "RUNTIME=nodejs20x jest --group=e2e",
"test:e2e": "jest --group=e2e",
diff --git a/packages/idempotency/tests/helpers/populateEnvironmentVariables.ts b/packages/idempotency/tests/helpers/populateEnvironmentVariables.ts
index 9d95c74e9c..9dc602e5f0 100644
--- a/packages/idempotency/tests/helpers/populateEnvironmentVariables.ts
+++ b/packages/idempotency/tests/helpers/populateEnvironmentVariables.ts
@@ -1,7 +1,7 @@
// Reserved variables
process.env._X_AMZN_TRACE_ID = '1-abcdef12-3456abcdef123456abcdef12';
process.env.AWS_LAMBDA_FUNCTION_NAME = 'my-lambda-function';
-process.env.AWS_EXECUTION_ENV = 'nodejs16.x';
+process.env.AWS_EXECUTION_ENV = 'nodejs20.x';
process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE = '128';
if (
process.env.AWS_REGION === undefined &&
diff --git a/packages/jmespath/src/errors.ts b/packages/jmespath/src/errors.ts
index c0d7112539..87ec88ed0e 100644
--- a/packages/jmespath/src/errors.ts
+++ b/packages/jmespath/src/errors.ts
@@ -108,13 +108,6 @@ class ParseError extends JMESPathError {
* Error thrown when an incomplete expression is encountered during parsing.
*/
class IncompleteExpressionError extends ParseError {
- /**
- * Expression that was being parsed when the error occurred.
- *
- * Can be set by whatever catches the error.
- */
- public expression?: string;
-
public constructor(options: {
lexPosition: number;
tokenValue: Token['value'];
diff --git a/packages/logger/package.json b/packages/logger/package.json
index 8b26a51b5c..b70bc92b9c 100644
--- a/packages/logger/package.json
+++ b/packages/logger/package.json
@@ -13,7 +13,6 @@
"test": "npm run test:unit",
"test:unit": "jest --group=unit --detectOpenHandles --coverage --verbose",
"jest": "jest --detectOpenHandles --verbose",
- "test:e2e:nodejs16x": "RUNTIME=nodejs16x jest --group=e2e",
"test:e2e:nodejs18x": "RUNTIME=nodejs18x jest --group=e2e",
"test:e2e:nodejs20x": "RUNTIME=nodejs20x jest --group=e2e",
"test:e2e": "jest --group=e2e",
diff --git a/packages/logger/src/Logger.ts b/packages/logger/src/Logger.ts
index d91b9ce39f..6c0c37e740 100644
--- a/packages/logger/src/Logger.ts
+++ b/packages/logger/src/Logger.ts
@@ -491,7 +491,7 @@ class Logger extends Utility implements LoggerInterface {
logger.addContext(context);
let shouldLogEvent = undefined;
- if (options && options.hasOwnProperty('logEvent')) {
+ if (options && Object.hasOwn(options, 'logEvent')) {
shouldLogEvent = options.logEvent;
}
logger.logEventIfEnabled(event, shouldLogEvent);
diff --git a/packages/logger/src/formatter/LogFormatter.ts b/packages/logger/src/formatter/LogFormatter.ts
index f3192ea43c..b072f6140c 100644
--- a/packages/logger/src/formatter/LogFormatter.ts
+++ b/packages/logger/src/formatter/LogFormatter.ts
@@ -7,22 +7,6 @@ import type {
import type { UnformattedAttributes } from '../types/Logger.js';
import { LogItem } from './LogItem.js';
-/**
- * Typeguard to monkey patch Error to add a cause property.
- *
- * This is needed because the `cause` property is present in ES2022 or newer.
- * Since we want to be able to format errors in Node 16.x, we need to
- * add this property ourselves. We can remove this once we drop support
- * for Node 16.x.
- *
- * @see https://nodejs.org/api/errors.html#errors_error_cause
- */
-const isErrorWithCause = (
- error: Error
-): error is Error & { cause: unknown } => {
- return 'cause' in error;
-};
-
/**
* This class defines and implements common methods for the formatting of log attributes.
*
@@ -65,11 +49,10 @@ abstract class LogFormatter implements LogFormatterInterface {
location: this.getCodeLocation(error.stack),
message: error.message,
stack: error.stack,
- cause: isErrorWithCause(error)
- ? error.cause instanceof Error
+ cause:
+ error.cause instanceof Error
? this.formatError(error.cause)
- : error.cause
- : undefined,
+ : error.cause,
};
}
diff --git a/packages/metrics/package.json b/packages/metrics/package.json
index f113c5cb86..8bc08aedf5 100644
--- a/packages/metrics/package.json
+++ b/packages/metrics/package.json
@@ -12,7 +12,6 @@
"scripts": {
"test": "npm run test:unit",
"test:unit": "jest --group=unit --detectOpenHandles --coverage --verbose",
- "test:e2e:nodejs16x": "RUNTIME=nodejs16x jest --group=e2e",
"test:e2e:nodejs18x": "RUNTIME=nodejs18x jest --group=e2e",
"test:e2e:nodejs20x": "RUNTIME=nodejs20x jest --group=e2e",
"test:e2e": "jest --group=e2e",
diff --git a/packages/parameters/package.json b/packages/parameters/package.json
index e6ce8d0ef8..1b89f513c6 100644
--- a/packages/parameters/package.json
+++ b/packages/parameters/package.json
@@ -13,7 +13,6 @@
"test": "npm run test:unit",
"test:unit": "jest --group=unit --detectOpenHandles --coverage --verbose",
"jest": "jest --detectOpenHandles --verbose",
- "test:e2e:nodejs16x": "RUNTIME=nodejs16x jest --group=e2e",
"test:e2e:nodejs18x": "RUNTIME=nodejs18x jest --group=e2e",
"test:e2e:nodejs20x": "RUNTIME=nodejs20x jest --group=e2e",
"test:e2e": "jest --group=e2e",
diff --git a/packages/parameters/src/appconfig/getAppConfig.ts b/packages/parameters/src/appconfig/getAppConfig.ts
index f7ec65f7f0..feac798a68 100644
--- a/packages/parameters/src/appconfig/getAppConfig.ts
+++ b/packages/parameters/src/appconfig/getAppConfig.ts
@@ -153,7 +153,7 @@ const getAppConfig = <
| AppConfigGetOutput
| undefined
> => {
- if (!DEFAULT_PROVIDERS.hasOwnProperty('appconfig')) {
+ if (!Object.hasOwn(DEFAULT_PROVIDERS, 'appconfig')) {
DEFAULT_PROVIDERS.appconfig = new AppConfigProvider({
application: options?.application,
environment: options.environment,
diff --git a/packages/parameters/src/secrets/getSecret.ts b/packages/parameters/src/secrets/getSecret.ts
index ef84d5d58b..1eb080cd91 100644
--- a/packages/parameters/src/secrets/getSecret.ts
+++ b/packages/parameters/src/secrets/getSecret.ts
@@ -118,7 +118,7 @@ const getSecret = async <
| SecretsGetOutput
| undefined
> => {
- if (!DEFAULT_PROVIDERS.hasOwnProperty('secrets')) {
+ if (!Object.hasOwn(DEFAULT_PROVIDERS, 'secrets')) {
DEFAULT_PROVIDERS.secrets = new SecretsProvider();
}
diff --git a/packages/parameters/src/ssm/SSMProvider.ts b/packages/parameters/src/ssm/SSMProvider.ts
index 787bd8915c..a205db3716 100644
--- a/packages/parameters/src/ssm/SSMProvider.ts
+++ b/packages/parameters/src/ssm/SSMProvider.ts
@@ -845,7 +845,7 @@ class SSMProvider extends BaseProvider {
reservedParameter: string,
throwOnError: boolean
): void {
- if (!throwOnError && parameters.hasOwnProperty(reservedParameter)) {
+ if (!throwOnError && Object.hasOwn(parameters, reservedParameter)) {
throw new GetParameterError(
`You cannot fetch a parameter named ${reservedParameter} in graceful error mode.`
);
diff --git a/packages/parameters/src/ssm/getParameter.ts b/packages/parameters/src/ssm/getParameter.ts
index 0f9e452a69..0c0b0a330a 100644
--- a/packages/parameters/src/ssm/getParameter.ts
+++ b/packages/parameters/src/ssm/getParameter.ts
@@ -146,7 +146,7 @@ const getParameter = async <
): Promise<
SSMGetOutput | undefined
> => {
- if (!DEFAULT_PROVIDERS.hasOwnProperty('ssm')) {
+ if (!Object.hasOwn(DEFAULT_PROVIDERS, 'ssm')) {
DEFAULT_PROVIDERS.ssm = new SSMProvider();
}
diff --git a/packages/parameters/src/ssm/getParameters.ts b/packages/parameters/src/ssm/getParameters.ts
index 7f2a3b1fc1..e09420c04d 100644
--- a/packages/parameters/src/ssm/getParameters.ts
+++ b/packages/parameters/src/ssm/getParameters.ts
@@ -153,7 +153,7 @@ const getParameters = async <
| SSMGetMultipleOutput
| undefined
> => {
- if (!DEFAULT_PROVIDERS.hasOwnProperty('ssm')) {
+ if (!Object.hasOwn(DEFAULT_PROVIDERS, 'ssm')) {
DEFAULT_PROVIDERS.ssm = new SSMProvider();
}
diff --git a/packages/parameters/src/ssm/getParametersByName.ts b/packages/parameters/src/ssm/getParametersByName.ts
index 72128db5ae..dce867387a 100644
--- a/packages/parameters/src/ssm/getParametersByName.ts
+++ b/packages/parameters/src/ssm/getParametersByName.ts
@@ -166,7 +166,7 @@ const getParametersByName = async (
parameters: Record,
options?: SSMGetParametersByNameOptions
): Promise> => {
- if (!DEFAULT_PROVIDERS.hasOwnProperty('ssm')) {
+ if (!Object.hasOwn(DEFAULT_PROVIDERS, 'ssm')) {
DEFAULT_PROVIDERS.ssm = new SSMProvider();
}
diff --git a/packages/parser/src/envelopes/apigw.ts b/packages/parser/src/envelopes/apigw.ts
index 064fe8477c..89145c567a 100644
--- a/packages/parser/src/envelopes/apigw.ts
+++ b/packages/parser/src/envelopes/apigw.ts
@@ -23,10 +23,9 @@ export class ApiGatewayEnvelope extends Envelope {
if (!parsedEnvelope.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse ApiGatewayEnvelope',
- parsedEnvelope.error
- ),
+ error: new ParseError('Failed to parse ApiGatewayEnvelope', {
+ cause: parsedEnvelope.error,
+ }),
originalEvent: data,
};
}
@@ -36,10 +35,9 @@ export class ApiGatewayEnvelope extends Envelope {
if (!parsedBody.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse ApiGatewayEnvelope body',
- parsedBody.error
- ),
+ error: new ParseError('Failed to parse ApiGatewayEnvelope body', {
+ cause: parsedBody.error,
+ }),
originalEvent: data,
};
}
diff --git a/packages/parser/src/envelopes/apigwv2.ts b/packages/parser/src/envelopes/apigwv2.ts
index d4abbc4738..3b66fd8588 100644
--- a/packages/parser/src/envelopes/apigwv2.ts
+++ b/packages/parser/src/envelopes/apigwv2.ts
@@ -23,10 +23,9 @@ export class ApiGatewayV2Envelope extends Envelope {
if (!parsedEnvelope.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse API Gateway V2 envelope',
- parsedEnvelope.error
- ),
+ error: new ParseError('Failed to parse API Gateway V2 envelope', {
+ cause: parsedEnvelope.error,
+ }),
originalEvent: data,
};
}
@@ -36,10 +35,9 @@ export class ApiGatewayV2Envelope extends Envelope {
if (!parsedBody.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse API Gateway V2 envelope body',
- parsedBody.error
- ),
+ error: new ParseError('Failed to parse API Gateway V2 envelope body', {
+ cause: parsedBody.error,
+ }),
originalEvent: data,
};
}
diff --git a/packages/parser/src/envelopes/cloudwatch.ts b/packages/parser/src/envelopes/cloudwatch.ts
index 4778d9b508..8d3182cb9c 100644
--- a/packages/parser/src/envelopes/cloudwatch.ts
+++ b/packages/parser/src/envelopes/cloudwatch.ts
@@ -34,10 +34,9 @@ export class CloudWatchEnvelope extends Envelope {
if (!parsedEnvelope.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse CloudWatch envelope',
- parsedEnvelope.error
- ),
+ error: new ParseError('Failed to parse CloudWatch envelope', {
+ cause: parsedEnvelope.error,
+ }),
originalEvent: data,
};
}
@@ -48,10 +47,9 @@ export class CloudWatchEnvelope extends Envelope {
if (!parsedMessage.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse CloudWatch log event',
- parsedMessage.error
- ),
+ error: new ParseError('Failed to parse CloudWatch log event', {
+ cause: parsedMessage.error,
+ }),
originalEvent: data,
};
} else {
diff --git a/packages/parser/src/envelopes/dynamodb.ts b/packages/parser/src/envelopes/dynamodb.ts
index 59bf83a850..39d3b3504e 100644
--- a/packages/parser/src/envelopes/dynamodb.ts
+++ b/packages/parser/src/envelopes/dynamodb.ts
@@ -39,10 +39,9 @@ export class DynamoDBStreamEnvelope extends Envelope {
if (!parsedEnvelope.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse DynamoDB Stream envelope',
- parsedEnvelope.error
- ),
+ error: new ParseError('Failed to parse DynamoDB Stream envelope', {
+ cause: parsedEnvelope.error,
+ }),
originalEvent: data,
};
}
@@ -55,11 +54,12 @@ export class DynamoDBStreamEnvelope extends Envelope {
return {
success: false,
error: !parsedNewImage.success
- ? new ParseError('Failed to parse NewImage', parsedNewImage.error)
- : new ParseError(
- 'Failed to parse OldImage',
- (parsedOldImage as ParsedResultError).error
- ),
+ ? new ParseError('Failed to parse NewImage', {
+ cause: parsedNewImage.error,
+ })
+ : new ParseError('Failed to parse OldImage', {
+ cause: (parsedOldImage as ParsedResultError).error,
+ }),
originalEvent: data,
};
} else {
diff --git a/packages/parser/src/envelopes/envelope.ts b/packages/parser/src/envelopes/envelope.ts
index 9e0ae3a8cf..caf977c49c 100644
--- a/packages/parser/src/envelopes/envelope.ts
+++ b/packages/parser/src/envelopes/envelope.ts
@@ -27,7 +27,7 @@ export class Envelope {
return schema.parse(data);
}
} catch (e) {
- throw new ParseError(`Failed to parse envelope`, e as Error);
+ throw new ParseError(`Failed to parse envelope`, { cause: e as Error });
}
};
@@ -63,13 +63,17 @@ export class Envelope {
}
: {
success: false,
- error: new ParseError(`Failed to parse envelope`, parsed.error),
+ error: new ParseError(`Failed to parse envelope`, {
+ cause: parsed.error,
+ }),
originalEvent: input,
};
} catch (e) {
return {
success: false,
- error: new ParseError(`Failed to parse envelope`, e as Error),
+ error: new ParseError(`Failed to parse envelope`, {
+ cause: e as Error,
+ }),
originalEvent: input,
};
}
diff --git a/packages/parser/src/envelopes/event-bridge.ts b/packages/parser/src/envelopes/event-bridge.ts
index 1659dd93ed..b6e53a4897 100644
--- a/packages/parser/src/envelopes/event-bridge.ts
+++ b/packages/parser/src/envelopes/event-bridge.ts
@@ -24,10 +24,9 @@ export class EventBridgeEnvelope extends Envelope {
if (!parsedEnvelope.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse EventBridge envelope',
- parsedEnvelope.error
- ),
+ error: new ParseError('Failed to parse EventBridge envelope', {
+ cause: parsedEnvelope.error,
+ }),
originalEvent: data,
};
}
@@ -37,10 +36,9 @@ export class EventBridgeEnvelope extends Envelope {
if (!parsedDetail.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse EventBridge envelope detail',
- parsedDetail.error
- ),
+ error: new ParseError('Failed to parse EventBridge envelope detail', {
+ cause: parsedDetail.error,
+ }),
originalEvent: data,
};
}
diff --git a/packages/parser/src/envelopes/kafka.ts b/packages/parser/src/envelopes/kafka.ts
index 8c8e481a07..3481781698 100644
--- a/packages/parser/src/envelopes/kafka.ts
+++ b/packages/parser/src/envelopes/kafka.ts
@@ -53,10 +53,9 @@ export class KafkaEnvelope extends Envelope {
if (!parsedEnvelope.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse Kafka envelope',
- parsedEnvelope.error
- ),
+ error: new ParseError('Failed to parse Kafka envelope', {
+ cause: parsedEnvelope.error,
+ }),
originalEvent: data,
};
}
@@ -68,10 +67,9 @@ export class KafkaEnvelope extends Envelope {
if (!parsedRecord.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse Kafka record',
- parsedRecord.error
- ),
+ error: new ParseError('Failed to parse Kafka record', {
+ cause: parsedRecord.error,
+ }),
originalEvent: data,
};
}
diff --git a/packages/parser/src/envelopes/kinesis-firehose.ts b/packages/parser/src/envelopes/kinesis-firehose.ts
index 71535894c3..acae22347a 100644
--- a/packages/parser/src/envelopes/kinesis-firehose.ts
+++ b/packages/parser/src/envelopes/kinesis-firehose.ts
@@ -37,10 +37,9 @@ export class KinesisFirehoseEnvelope extends Envelope {
if (!parsedEnvelope.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse Kinesis Firehose envelope',
- parsedEnvelope.error
- ),
+ error: new ParseError('Failed to parse Kinesis Firehose envelope', {
+ cause: parsedEnvelope.error,
+ }),
originalEvent: data,
};
}
@@ -51,10 +50,9 @@ export class KinesisFirehoseEnvelope extends Envelope {
if (!parsedData.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse Kinesis Firehose record',
- parsedData.error
- ),
+ error: new ParseError('Failed to parse Kinesis Firehose record', {
+ cause: parsedData.error,
+ }),
originalEvent: data,
};
}
diff --git a/packages/parser/src/envelopes/kinesis.ts b/packages/parser/src/envelopes/kinesis.ts
index 8ddfb3d34e..009d28ce75 100644
--- a/packages/parser/src/envelopes/kinesis.ts
+++ b/packages/parser/src/envelopes/kinesis.ts
@@ -34,10 +34,9 @@ export class KinesisEnvelope extends Envelope {
if (!parsedEnvelope.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse Kinesis Data Stream envelope',
- parsedEnvelope.error
- ),
+ error: new ParseError('Failed to parse Kinesis Data Stream envelope', {
+ cause: parsedEnvelope.error,
+ }),
originalEvent: data,
};
}
@@ -49,10 +48,9 @@ export class KinesisEnvelope extends Envelope {
if (!parsedRecord.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse Kinesis Data Stream record',
- parsedRecord.error
- ),
+ error: new ParseError('Failed to parse Kinesis Data Stream record', {
+ cause: parsedRecord.error,
+ }),
originalEvent: data,
};
}
diff --git a/packages/parser/src/envelopes/lambda.ts b/packages/parser/src/envelopes/lambda.ts
index 9d694cc517..a86a2871fd 100644
--- a/packages/parser/src/envelopes/lambda.ts
+++ b/packages/parser/src/envelopes/lambda.ts
@@ -39,10 +39,9 @@ export class LambdaFunctionUrlEnvelope extends Envelope {
if (!parsedBody.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse Lambda function URL body',
- parsedBody.error
- ),
+ error: new ParseError('Failed to parse Lambda function URL body', {
+ cause: parsedBody.error,
+ }),
originalEvent: data,
};
}
diff --git a/packages/parser/src/envelopes/sns.ts b/packages/parser/src/envelopes/sns.ts
index 50083d7ca7..d4b82e54e9 100644
--- a/packages/parser/src/envelopes/sns.ts
+++ b/packages/parser/src/envelopes/sns.ts
@@ -35,10 +35,9 @@ export class SnsEnvelope extends Envelope {
if (!parsedEnvelope.success) {
return {
success: false,
- error: new ParseError(
- `Failed to parse SNS envelope`,
- parsedEnvelope.error
- ),
+ error: new ParseError(`Failed to parse SNS envelope`, {
+ cause: parsedEnvelope.error,
+ }),
originalEvent: data,
};
}
@@ -49,10 +48,9 @@ export class SnsEnvelope extends Envelope {
if (!parsedMessage.success) {
return {
success: false,
- error: new ParseError(
- `Failed to parse SNS message`,
- parsedMessage.error
- ),
+ error: new ParseError(`Failed to parse SNS message`, {
+ cause: parsedMessage.error,
+ }),
originalEvent: data,
};
}
@@ -101,10 +99,9 @@ export class SnsSqsEnvelope extends Envelope {
if (!parsedEnvelope.success) {
return {
success: false,
- error: new ParseError(
- `Failed to parse SQS envelope`,
- parsedEnvelope.error
- ),
+ error: new ParseError(`Failed to parse SQS envelope`, {
+ cause: parsedEnvelope.error,
+ }),
originalEvent: data,
};
}
@@ -120,10 +117,9 @@ export class SnsSqsEnvelope extends Envelope {
if (!snsNotification.success) {
return {
success: false,
- error: new ParseError(
- `Failed to parse SNS notification`,
- snsNotification.error
- ),
+ error: new ParseError(`Failed to parse SNS notification`, {
+ cause: snsNotification.error,
+ }),
originalEvent: data,
};
}
@@ -134,10 +130,9 @@ export class SnsSqsEnvelope extends Envelope {
if (!parsedMessage.success) {
return {
success: false,
- error: new ParseError(
- `Failed to parse SNS message`,
- parsedMessage.error
- ),
+ error: new ParseError(`Failed to parse SNS message`, {
+ cause: parsedMessage.error,
+ }),
originalEvent: data,
};
}
diff --git a/packages/parser/src/envelopes/sqs.ts b/packages/parser/src/envelopes/sqs.ts
index e5350365d4..0863a9decf 100644
--- a/packages/parser/src/envelopes/sqs.ts
+++ b/packages/parser/src/envelopes/sqs.ts
@@ -33,10 +33,9 @@ export class SqsEnvelope extends Envelope {
if (!parsedEnvelope.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse Sqs Envelope',
- parsedEnvelope.error
- ),
+ error: new ParseError('Failed to parse Sqs Envelope', {
+ cause: parsedEnvelope.error,
+ }),
originalEvent: data,
};
}
@@ -47,10 +46,9 @@ export class SqsEnvelope extends Envelope {
if (!parsedRecord.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse Sqs Record',
- parsedRecord.error
- ),
+ error: new ParseError('Failed to parse Sqs Record', {
+ cause: parsedRecord.error,
+ }),
originalEvent: data,
};
}
diff --git a/packages/parser/src/envelopes/vpc-lattice.ts b/packages/parser/src/envelopes/vpc-lattice.ts
index 1f157eac3d..2bdb69bac3 100644
--- a/packages/parser/src/envelopes/vpc-lattice.ts
+++ b/packages/parser/src/envelopes/vpc-lattice.ts
@@ -26,10 +26,9 @@ export class VpcLatticeEnvelope extends Envelope {
if (!parsedEnvelope.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse VpcLattice envelope',
- parsedEnvelope.error
- ),
+ error: new ParseError('Failed to parse VpcLattice envelope', {
+ cause: parsedEnvelope.error,
+ }),
originalEvent: data,
};
}
@@ -39,10 +38,9 @@ export class VpcLatticeEnvelope extends Envelope {
if (!parsedBody.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse VpcLattice envelope body',
- parsedBody.error
- ),
+ error: new ParseError('Failed to parse VpcLattice envelope body', {
+ cause: parsedBody.error,
+ }),
originalEvent: data,
};
}
diff --git a/packages/parser/src/envelopes/vpc-latticev2.ts b/packages/parser/src/envelopes/vpc-latticev2.ts
index 50a1b058f3..e9fd0c3c2a 100644
--- a/packages/parser/src/envelopes/vpc-latticev2.ts
+++ b/packages/parser/src/envelopes/vpc-latticev2.ts
@@ -25,10 +25,9 @@ export class VpcLatticeV2Envelope extends Envelope {
if (!parsedEnvelope.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse VpcLatticeV2 envelope.',
- parsedEnvelope.error
- ),
+ error: new ParseError('Failed to parse VpcLatticeV2 envelope.', {
+ cause: parsedEnvelope.error,
+ }),
originalEvent: data,
};
}
@@ -38,10 +37,9 @@ export class VpcLatticeV2Envelope extends Envelope {
if (!parsedBody.success) {
return {
success: false,
- error: new ParseError(
- 'Failed to parse VpcLatticeV2 body.',
- parsedBody.error
- ),
+ error: new ParseError('Failed to parse VpcLatticeV2 body.', {
+ cause: parsedBody.error,
+ }),
originalEvent: data,
};
}
diff --git a/packages/parser/src/errors.ts b/packages/parser/src/errors.ts
index 2806b2d4cd..c5dc0ebd0b 100644
--- a/packages/parser/src/errors.ts
+++ b/packages/parser/src/errors.ts
@@ -3,22 +3,11 @@
* The cause of the error is included in the message, if possible.
*/
class ParseError extends Error {
- /**
- * we use the `cause` property, which is present in ES2022 or newer, to store the cause of the error.
- * because we have to support Node 16.x, we need to add this property ourselves.
- * We can remove this once we drop support for Node 16.x.
- * see: https://github.com/aws-powertools/powertools-lambda-typescript/issues/2223
- *
- * @see https://nodejs.org/api/errors.html#errors_error_cause
- */
- public readonly cause: Error | undefined;
-
- public constructor(message: string, cause?: Error) {
- const errorMessage = cause
- ? `${message}. This error was caused by: ${cause.message}.`
+ public constructor(message: string, options?: { cause?: Error }) {
+ const errorMessage = options?.cause
+ ? `${message}. This error was caused by: ${options?.cause.message}.`
: message;
super(errorMessage);
- this.cause = cause;
this.name = 'ParseError';
}
}
diff --git a/packages/parser/src/parser.ts b/packages/parser/src/parser.ts
index b7537de77a..9a307dc1f5 100644
--- a/packages/parser/src/parser.ts
+++ b/packages/parser/src/parser.ts
@@ -44,8 +44,8 @@ const parse = (
}
try {
return schema.parse(data);
- } catch (e) {
- throw new ParseError('Failed to parse schema', e as Error);
+ } catch (error) {
+ throw new ParseError('Failed to parse schema', { cause: error as Error });
}
};
@@ -66,7 +66,9 @@ const safeParseSchema = (
? result
: {
success: false,
- error: new ParseError('Failed to parse schema safely', result.error),
+ error: new ParseError('Failed to parse schema safely', {
+ cause: result.error,
+ }),
originalEvent: data,
};
};
diff --git a/packages/testing/src/constants.ts b/packages/testing/src/constants.ts
index 0699c4efb4..8fab08bff4 100644
--- a/packages/testing/src/constants.ts
+++ b/packages/testing/src/constants.ts
@@ -9,7 +9,6 @@ const defaultRuntime = 'nodejs20x';
* The AWS Lambda runtimes that are supported by the project.
*/
const TEST_RUNTIMES = {
- nodejs16x: Runtime.NODEJS_16_X,
nodejs18x: Runtime.NODEJS_18_X,
[defaultRuntime]: Runtime.NODEJS_20_X,
} as const;
diff --git a/packages/testing/tests/helpers/populateEnvironmentVariables.ts b/packages/testing/tests/helpers/populateEnvironmentVariables.ts
index 4d4166743d..ed8dde8cd5 100644
--- a/packages/testing/tests/helpers/populateEnvironmentVariables.ts
+++ b/packages/testing/tests/helpers/populateEnvironmentVariables.ts
@@ -1,7 +1,7 @@
// Reserved variables
process.env._X_AMZN_TRACE_ID = '1-abcdef12-3456abcdef123456abcdef12';
process.env.AWS_LAMBDA_FUNCTION_NAME = 'my-lambda-function';
-process.env.AWS_EXECUTION_ENV = 'nodejs16.x';
+process.env.AWS_EXECUTION_ENV = 'nodejs20.x';
process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE = '128';
if (
process.env.AWS_REGION === undefined &&
diff --git a/packages/tracer/package.json b/packages/tracer/package.json
index 958b7ec680..d0b332f8a1 100644
--- a/packages/tracer/package.json
+++ b/packages/tracer/package.json
@@ -13,7 +13,6 @@
"test": "npm run test:unit",
"test:unit": "jest --group=unit --detectOpenHandles --coverage --verbose",
"jest": "jest --detectOpenHandles --verbose",
- "test:e2e:nodejs16x": "RUNTIME=nodejs16x jest --group=e2e",
"test:e2e:nodejs18x": "RUNTIME=nodejs18x jest --group=e2e",
"test:e2e:nodejs20x": "RUNTIME=nodejs20x jest --group=e2e",
"test:e2e": "jest --group=e2e",
diff --git a/packages/tracer/tests/e2e/allFeatures.decorator.test.ts b/packages/tracer/tests/e2e/allFeatures.decorator.test.ts
index 7d00633067..9277dc64b4 100644
--- a/packages/tracer/tests/e2e/allFeatures.decorator.test.ts
+++ b/packages/tracer/tests/e2e/allFeatures.decorator.test.ts
@@ -332,7 +332,7 @@ describe(`Tracer E2E tests, all features with decorator instantiation`, () => {
expect(invocationSubsegment.error).toBe(true);
expect(handlerSubsegment.error).toBe(true);
// Assert that no error was captured on the subsegment
- expect(handlerSubsegment.hasOwnProperty('cause')).toBe(false);
+ expect(Object.hasOwn(handlerSubsegment, 'cause')).toBe(false);
}
}
},
diff --git a/packages/tracer/tests/e2e/allFeatures.middy.test.ts b/packages/tracer/tests/e2e/allFeatures.middy.test.ts
index b9488bf6ed..492a5c60aa 100644
--- a/packages/tracer/tests/e2e/allFeatures.middy.test.ts
+++ b/packages/tracer/tests/e2e/allFeatures.middy.test.ts
@@ -325,7 +325,7 @@ describe(`Tracer E2E tests, all features with middy instantiation`, () => {
expect(invocationSubsegment.error).toBe(true);
expect(handlerSubsegment.error).toBe(true);
// Assert that no error was captured on the subsegment
- expect(handlerSubsegment.hasOwnProperty('cause')).toBe(false);
+ expect(Object.hasOwn(handlerSubsegment, 'cause')).toBe(false);
}
}
},
diff --git a/packages/tracer/tests/e2e/asyncHandler.decorator.test.functionCode.ts b/packages/tracer/tests/e2e/asyncHandler.decorator.test.functionCode.ts
index 65d9cbf937..25d1b4b180 100644
--- a/packages/tracer/tests/e2e/asyncHandler.decorator.test.functionCode.ts
+++ b/packages/tracer/tests/e2e/asyncHandler.decorator.test.functionCode.ts
@@ -55,16 +55,10 @@ export class MyFunctionBase {
Item: { id: `${serviceName}-${event.invocation}-sdkv3` },
})
);
- const url = 'https://docs.powertools.aws.dev/lambda/typescript/latest/';
- // Add conditional behavior because fetch is not available in Node.js 16 - this can be removed once we drop support for Node.js 16
- if (process.version.startsWith('v16')) {
- await httpRequest({
- hostname: 'docs.powertools.aws.dev',
- path: '/lambda/typescript/latest/',
- });
- } else {
- await fetch(url);
- }
+ await httpRequest({
+ hostname: 'docs.powertools.aws.dev',
+ path: '/lambda/typescript/latest/',
+ });
const res = this.myMethod();
if (event.throw) {
diff --git a/packages/tracer/tests/helpers/populateEnvironmentVariables.ts b/packages/tracer/tests/helpers/populateEnvironmentVariables.ts
index 096b3c67be..3d3087b9e3 100644
--- a/packages/tracer/tests/helpers/populateEnvironmentVariables.ts
+++ b/packages/tracer/tests/helpers/populateEnvironmentVariables.ts
@@ -1,7 +1,7 @@
// Reserved variables
process.env._X_AMZN_TRACE_ID = '1-abcdef12-3456abcdef123456abcdef12';
process.env.AWS_LAMBDA_FUNCTION_NAME = 'my-lambda-function';
-process.env.AWS_EXECUTION_ENV = 'nodejs16.x';
+process.env.AWS_EXECUTION_ENV = 'nodejs20.x';
process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE = '128';
if (
process.env.AWS_REGION === undefined &&
diff --git a/packages/tracer/tests/helpers/resources.ts b/packages/tracer/tests/helpers/resources.ts
index 7acd9730db..a86e5211db 100644
--- a/packages/tracer/tests/helpers/resources.ts
+++ b/packages/tracer/tests/helpers/resources.ts
@@ -1,7 +1,4 @@
-import {
- getRuntimeKey,
- type TestStack,
-} from '@aws-lambda-powertools/testing-utils';
+import { type TestStack } from '@aws-lambda-powertools/testing-utils';
import type {
ExtraTestProps,
TestNodejsFunctionProps,
@@ -15,9 +12,6 @@ class TracerTestNodejsFunction extends TestNodejsFunction {
props: TestNodejsFunctionProps,
extraProps: ExtraTestProps
) {
- const isEsm = extraProps.outputFormat === 'ESM';
- const isNodejs16x = getRuntimeKey() === 'nodejs16x';
-
super(
scope,
{
@@ -33,18 +27,6 @@ class TracerTestNodejsFunction extends TestNodejsFunction {
),
...props.environment,
},
- /**
- * For Node.js 16.x, we need to set `externalModules` to an empty array
- * so that the `aws-sdk` is bundled with the function.
- *
- * @see https://github.com/aws/aws-sdk-js-v3/issues/3230#issuecomment-1561973247
- */
- bundling: {
- ...(isEsm &&
- isNodejs16x && {
- externalModules: [],
- }),
- },
},
extraProps
);
diff --git a/packages/tracer/tests/helpers/traceAssertions.ts b/packages/tracer/tests/helpers/traceAssertions.ts
index 559be2dfa1..14b80e5c69 100644
--- a/packages/tracer/tests/helpers/traceAssertions.ts
+++ b/packages/tracer/tests/helpers/traceAssertions.ts
@@ -31,7 +31,7 @@ export const assertErrorAndFault = (
const handlerSubsegment = getFirstSubsegment(invocationSubsegment);
expect(handlerSubsegment.fault).toBe(true);
- expect(handlerSubsegment.hasOwnProperty('cause')).toBe(true);
+ expect(Object.hasOwn(handlerSubsegment, 'cause')).toBe(true);
expect(handlerSubsegment.cause?.exceptions[0].message).toBe(
expectedCustomErrorMessage
);
diff --git a/packages/tracer/tests/unit/EnvironmentVariablesService.test.ts b/packages/tracer/tests/unit/EnvironmentVariablesService.test.ts
index d20692b6dd..2e3fb652dd 100644
--- a/packages/tracer/tests/unit/EnvironmentVariablesService.test.ts
+++ b/packages/tracer/tests/unit/EnvironmentVariablesService.test.ts
@@ -91,14 +91,14 @@ describe('Class: EnvironmentVariablesService', () => {
describe('Method: getAwsExecutionEnv', () => {
test('It returns the value of the environment variable AWS_EXECUTION_ENV', () => {
// Prepare
- process.env.AWS_EXECUTION_ENV = 'nodejs16.x';
+ process.env.AWS_EXECUTION_ENV = 'nodejs20.x';
const service = new EnvironmentVariablesService();
// Act
const value = service.getAwsExecutionEnv();
// Assess
- expect(value).toEqual('nodejs16.x');
+ expect(value).toEqual('nodejs20.x');
});
});
});
diff --git a/packages/tracer/tests/unit/Tracer.test.ts b/packages/tracer/tests/unit/Tracer.test.ts
index c958d3d0ad..0119fe7972 100644
--- a/packages/tracer/tests/unit/Tracer.test.ts
+++ b/packages/tracer/tests/unit/Tracer.test.ts
@@ -229,7 +229,7 @@ describe('Class: Tracer', () => {
test('when AWS_EXECUTION_ENV environment variable is set, tracing is enabled', () => {
// Prepare
- process.env.AWS_EXECUTION_ENV = 'nodejs16.x';
+ process.env.AWS_EXECUTION_ENV = 'nodejs20.x';
// Act
const tracer = new Tracer();
diff --git a/tsconfig.json b/tsconfig.json
index c801ef543c..474a0b34ce 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,7 +2,7 @@
"compilerOptions": {
"incremental": true,
"composite": true,
- "target": "ES2021", // Node.js 16
+ "target": "ES2022", // Node.js 18
"experimentalDecorators": true,
"module": "commonjs",
"moduleResolution": "node",