powertools-lambda-typescript icon indicating copy to clipboard operation
powertools-lambda-typescript copied to clipboard

Feature (all): build as esmodule

Open flochaz opened this issue 2 years ago • 3 comments

Description of the feature request

Problem statement Powertools is packaged as commonJS which does not give the capability to javascript developers to use esmodule import syntax and feature.

Summary of the feature Now that AWS Lambda support es module import it might worth generating esmodule.

Code examples

Before:

const { Tracer } = require('@aws-lambda-powertools/tracer');

After:

import { Tracer } from '@aws-lambda-powertools/tracer';

Benefits for you and the wider AWS community See https://aws.amazon.com/blogs/compute/using-node-js-es-modules-and-top-level-await-in-aws-lambda/ for more details ...

Additional context slack channel discussion

Related issues, RFCs

https://github.com/awslabs/aws-lambda-powertools-typescript/issues/490

flochaz avatar Jan 28 '22 10:01 flochaz

Thanks @flochaz for opening this issue! If I look at the AWS SDK v3, this is how @trivikr and the team handles it:

  1. They transpile the TS code three times, once for CommonJS, once for ES6 and once for the types. To achieve this, they use 2 tsconfig files: tsconfig.cjs and tsconfig.es.
  2. In their docs, they use both import styles in their examples: https://github.com/aws/aws-sdk-js-v3/tree/cc17fa2296654192d2a9c2cf567d2ce58d81d15b/lib/lib-dynamodb#constructor.

Full example for the DynamoDB client: https://github.com/aws/aws-sdk-js-v3/tree/cc17fa2296654192d2a9c2cf567d2ce58d81d15b/lib/lib-dynamodb


Personally, I think that right now supporting both CommonJS and ES6 is not a high priority, but a nice to have that would definitely improve the dev experience. I would like to see a small proof of concept but I don't think this is something we should prioritize for the production ready milestone. (Maybe some folks from our community can help out in the future?)

saragerion avatar Feb 16 '22 15:02 saragerion

If we want, at least for now, make sure that the three core utilities available (Logger, Metrics, and Tracer) all advance at the same pace, the main dependency for Tracer (aws-xray-sdk-node) constitutes a major blocker for this effort as it currently doesn't support ESM bundling.

Given that our main focus at the moment is to bring these utils to a production ready status. Marking this issue as blocked and on-hold till further notice.

dreamorosi avatar Feb 28 '22 14:02 dreamorosi

I'm only using @aws-lambda-powertools/logger and would really appreciate an ESM build even if aws-xray-sdk-node is blocking @aws-lambda-powertools/tracer. Thank you!

bestickley avatar Aug 08 '22 01:08 bestickley

Recap of the status and actions required before being able to move forward with this:

While we acknowledge that there's an option to independently support ESM on some utilities while leaving others only with CJS support, we are still keen in dedicating some effort in investigating ways to bring ESM support for all utilities. We believe that doing so would result in a less confusing developer experience for users, since they wouldn't have to check which utility supports what.

At the moment, before starting to work on the feature we need to resolve the following topics:

  • Should we support both CJS and ESM? What are the tradeoffs in doing so? (i.e. package size, cold start, etc.)
  • If we were to support both, what would be the impact on our CI/CD and release processes?
  • If we were to change from CJS to ESM, or support both, would there be differences in how users install, import, use the libraries?
  • If we were to bundle exclusively to ESM, would the utilities still work on Lambda functions that don't use ESM?

The resolver for this issue will have to primarily investigate the scope of the changes and propose options to move forward along with considerations on the tradeoffs for each option. Ideally, each solution/option should also propose high-level examples of where the changes would be (i.e. tsconfig.json, build system, release process, etc.), as well as detail any changes in the public API (i.e. will there be changes in how users import the libraries?).

If anyone is willing to take this on, please leave a comment below before starting to work on it.

dreamorosi avatar Nov 13 '22 14:11 dreamorosi

I'm researching this

ConnorKirk avatar Nov 15 '22 18:11 ConnorKirk

Update with my research so far. I've got some answers to the questions above from @dreamorosi. There's more I don't know yet.

Caveat: This is based on my research so far. The domain is new to me, so please point out things that seem odd.

I propose that we dual package both CommonJs and ES modules. This change would add a small size overhead to the published package, and a little extra complexity in building the packages. It shouldn't require changes to existing CI/CD pipelines. It would allow users to consume the utilities in ES Module Lambda functions, as well as CommonJS modules which are currently supported.

Questions

Should we support both CJS and ESM? What are the tradeoffs in doing so? (i.e. package size, cold start, etc.)

I think we should support both (for now). If we dual packaged both formats there would be a slight increase in package size and build complexity.

Size

Each utility package size would increase by ~50%, as we would publish each package twice.

For example, the logger utility /lib is 212KB ( commonjs with type declaration)

If we dual package CommonJS and ES Modules, it will increase to 344KB (132KB increase)

132KB ./lib/es 120KB ./lib/cjs 92KB ./lib/types (separate type declaration)

Cold Start

Ideally would gather empirical evidence. Can't comment on this yet. I suspect the cold start would increase due to the increase package size, but it would be negligible

Maintenance burden

There is a additional complexity of creating two packages for each utility. The build process is slightly more complicated.

If we were to support both, what would be the impact on our CI/CD and release processes?

This proposal is based on aws-js-sdk-v3 approach. At a high level, we would generate a CommonJS output, an ES Module output and Types output.

Each output would have a seperate tsconfig file. The base file would be for CommonJS output. The ES Module and Types outputs would extend this base config file.

  • Propose tsconfig.es.json and tsconfig.types.json files that extends the base tsconfig.json file. This is similar to the proposal in #617 .
  • The build process for each util would build three outputs, once with each tsconfig file (cjs, es, and types). * There would be an increased build time, but this would not be significant, nor would affect users of the library.
  • The outputs would be three separate directories.
    • I propose lib/cjs, lib/es and lib/types

For each utlity, we would update the package.json to support dual packaging. We can add the exportsproperty in package.json to support both ES Modules and CommonJS. To support older versions of node, we can also include the older style module, main and types properties as well. This might not be needed, I need to do more research.

  • These chnges shouldn't impact CI/CD or release process. Code changes would be localised to tsconfig and package.json files. CI/CD and release would stay the same.

If we were to change from CJS to ESM, or support both, would there be differences in how users install, import, use the libraries?

There would be no change in how users install or use the libraries with either approach. There would be a difference in how users import the libraries.

If we supported only ESM, then users would only be able to use import the packages using import() or import ... from .... They could not use require() (commonJs syntax).

If we were to bundle exclusively to ESM, would the utilities still work on Lambda functions that don't use ESM?

Not easily. CommonJS lambda functions could no longer use require() to load a utility. I believe they could use the dynamic import() function instead, but I need to do more research, and ideally a POC.

References

ConnorKirk avatar Nov 21 '22 12:11 ConnorKirk

Hi @ConnorKirk thanks a lot for putting these together, it's going to be very useful and I like the approach so far.

Dual packaging seems like the way to go, at least on the surface and it seems to be in line with how most of the ecosystem is handling this.

However before making a choice it would beneficial to have more data on the actual impact on cold start and other metrics if we were in fact to dual build.

Ideally we should be able to compare the existing utilities like this on the three latest runtimes:

  • current state
  • dual building - importing CJS
  • dual building - importing ESM
  • dual building - importing ESM with ESM output

Once we have this data we can start and open up the discussion further.

dreamorosi avatar Nov 22 '22 06:11 dreamorosi

@ConnorKirk
Thanks for the research!

@dreamorosi
I am not familiar with this domain. But I want to check that this is probably a one-way decision, right?

  • Once we support both in the current package, we cannot revert back without a breaking change (in a major version). Cold start will always be there.
  • On the other hand, if we decide to split the packages (e.g. aws-lambda-powertools-logger-esm and another for -cjs. This is also not reversible. Users have to migrate for newer version.

One possible thought is that we implement option 1 first (for ease of use, at the (calculated) cost of cold start), and extend to implement option 2 in our release script later if there is a demand for performance. Would that be feasible?

ijemmy avatar Nov 22 '22 14:11 ijemmy

@ijemmy good points.

Having separate packages would be possible, however as it stands this would mean already 6 different packages only for the core utilities.

I have seen this done with different versions instead of suffix in the name, but regardless I find that it adds a bit of cognitive load.

Two additional datapoints/notes:

  • If we go with dual-bundling, we should make sure that the two bundles are 100% independent. My current understanding is that if they are, tools like esbuild should be able to tree-shake, also there are options like node-prune. I don't have a completely formed idea about this yet, but this sounds like the kind of optimization/pruning that should be done in userland (the lib should allow it though).
  • Another place where we can bring optimizations are Lambda layers. Those are more isolated and we control the whole pipeline from build to delivery, plus it would mean no additional action on customers' side.

Aside from all this, I think we need data to understand what's the actual impact on cold-start and bundle size of the dual-bundle option.

I agree that it's not a decision we should make in a rush and take lightly. Let's wait for Connor to come back with benchmarks and then see what we are dealing with.

dreamorosi avatar Nov 22 '22 15:11 dreamorosi

The task is back on the backlog.

The latest status is that currently we are inclined towards shipping both CJS & ESM and let customers do their own tree-shaking. However, before deciding we need to make an assessment and understand the impact that this will have on the deployment package, as well as understanding if the tree-shaking is actually effective.

If anyone is interested in picking this up, please leave a comment below.

dreamorosi avatar Jan 10 '23 09:01 dreamorosi

This issue has not received a response in 2 weeks. If you still think there is a problem, please leave a comment to avoid the issue from automatically closing.

github-actions[bot] avatar Feb 28 '23 00:02 github-actions[bot]

Hey there!

I've been working on this for some time and I am ready to share the results:

After some discussion on discord, we decided to start with the Logger package. As the result, we should be able to ship the package with both CommonJS and ECMAScript modules.

TL;DR

Changes made to Logger that allows building it as a package with both nodules using npm run build.

Repo with benchmarks and results. You're welcome to adjust the app and tests, run them and share your results.

I want details!

I'll go through all the changes and decisions I made.

This is a new lib directory structure:

  • cjs - CommonJS code
  • esmES code
  • types – extract types to share between both modules.

To achieve this tsconfig.json was slightly changed and tsconfig.cjs.json, tsconfig.mjs.json, and tsconfig.types.json were added to transpile typescript code and put javascript code to directories above.

Configs extend tsconfig with basically two differences module and outDir. For types it only emit declarations.

Explanation of package.json:

"type": "module", // To treat package as ES module by default
"exports": { // Node.js 12+ as an alternative to the “main”
    ".": {
      "types": "./lib/types/index.d.ts", // Entry-point for TypeScript resolution - must occur first!
      "import": "./lib/esm/index.js", // Entry-point for `import "my-package"` in ESM
      "require": "./lib/cjs/index.js", // Entry-point for `require("my-package") in CJS
      "default": "./lib/esm/index.js" // Default fallback
    }
  },
 "main": "./lib/cjs/index.js", // CJS fall-back for older versions of Node.js
 "module": "./lib/ems/index.js", // For build tools such as esbuild, used the ES module entry point, since Node.js ignored (and still ignores) the top-level "module" field.
 "types": "./lib/types/index.d.ts", // Fall-back for older versions of TypeScript

Reference: https://www.typescriptlang.org/docs/handbook/esm-node.html https://nodejs.org/api/packages.html#dual-commonjses-module-packages

To make it work as a double bundled package. each module directory should have additional package.json that explicitly says what kind of module it is:

  • cjs/package.json with {"type":"commonjs"}
  • esm/package.json with {"type":"module"}

A script that runs right after the build generates these files.

Why this way instead of using .cjs and .mjs file extensions?

  • Powertools don't use any bundler to produce javascript, only tsc. TypeScript compiler can produce .cjs or .mjs extensions, but from files with .mts or .cts file extensions. But we need both, as a result generating package.json via script is a better option.
  • No need for consumers to concern themselves with extensions during import/require as they can utilize the library as is.

Changes required to Logger codebase

Running JS code with Logger as ES module would work in node, but because of relative paths node should be run with the --experimental-specifier-resolution=node flag, which is dropped also in favor to custom loaders. It uses https://nodejs.org/api/esm.html#resolution-algorithm to evaluate paths.

To avoid using this flag or custom loader for our users, I followed the typescript recommendation for EMS and updated imports in the source code to make it work for both modules:

import { Logger } from “./Logger”; // only works in CJS`
import { Logger } from “./Logger.js"; // works in ESM & CJS

Reference: https://www.typescriptlang.org/docs/handbook/esm-node.html#type-in-packagejson-and-new-extensions

IMO ESM support is worth changing imports.

These changes broke unit tests, but I fixed that with a small jest config change:

moduleNameMapper: {
    '^(\\.{1,2}/.*)\\.js$': '$1',
},

Docs: https://jestjs.io/docs/next/configuration#modulenamemapper-objectstring-string--arraystring

This regular expression matches any string that starts with one or two dots followed by a forward slash, followed by any number of characters and ends with the ".js" file extension. All the following paths will be a match:

  • ./app.js
  • ../src/utils/helpers.js
  • ../../lib/bundle.min.js
  • ./../scripts/data.js

I think I covered all the changes. If you have any questions feel free to ask.

Performance benchmarks

The reason is to understand the difference between an app that used the current version of Logger from npm (1.6.0), an app with the new version of Logger build as CJS module, and an app with the new version of Logger build as ES module.

For the testing created a template for AWS SAM to build and deploy three different lambdas. Functions bundled using esbuild and load-tested with artillery.io.

I created this repository with a description, so you can run tests yourself if you want to try other target platforms or the amount of load.

I used this article as a reference: https://aws.amazon.com/blogs/compute/optimizing-node-js-dependencies-in-aws-lambda/

Results

The screenshot under the spoiler is the test I did today with a more extensive load for functions on the Node 16.x platform. ESM looks really good, the cold start for current and CJS builds is relatively the same, and warm invocations with CJS are better than Current Logger.

Spoiler Logger ES module benchmarks

How to read this?

Percentile (p) indicates the relative standing of a value in a dataset. For example, p95 is the 95th percentile and means that 95 percent of the data within the period is lower than this value and 5 percent of the data is higher than this value. Percentiles help you get a better understanding of the distribution of your metric data.

shdq avatar Mar 20 '23 16:03 shdq

Wow @shdq, thank you SO much for this huge effort and for taking the time to not only look into this but also providing a deep dive on the decision taken and a reproducible benchmark suite.

The results definitely look promising. Please allow me some time to play with the benchmarks a bit, do some tests with the new builds, and think what could be the next steps (cc @am29d).

dreamorosi avatar Mar 20 '23 21:03 dreamorosi

@dreamorosi thanks for your support and encouragement!

Double packaging

I want to raise a discussion on the pros and cons of double packaging:

Pros

  • Tree shaking is working by default (!) If a user imports the powertools package inside its app (encouraged by docs way of import tools), but has it bundled as commonJS by mistake won't be a problem (bloated bundle). Since the library will be ESM by default, the bundler will eliminate all the dead code and then transpile the app as CommonJS. Only in the case whenrequire is intentionally used in the app, tree shaking won't work, and code from lib/cjs directory will be used for bundling. Tree shaking doesn't work with CommonJS modules.
  • Users don't need to choose what package to use powertools-utility or powertools-utility-es Prone to a mistake. You need to educate users and help them to choose the proper one. Lazy ones never discover ES version, cause it just works. Getting an update with a double-packaged library tree shakes the dead code from their dependency without even knowing that.
  • Easier to maintain It's 5 packages now, which will double with every utility released in case of splitting.
  • No need to "deprecate" in case of dropping Common JS in the future If it will be an issue, then there will be no need to deprecate or instruct users to install another dependency. Users who are already encouraged to use import won't notice, only those who use require will be impacted.

Cons

  • Package size will increase by about 45% packed (25.6 kB vs 37.1 kB) and 58% unpacked (106.4kb vs 168.9 kB) With bundler (encouraged way to serve lambda to improve performance) it is not a problem, only the necessary code will be in the output. It can lead to the cold start time increase (not billed) when you copy-paste node_modules as is. And when you use layers (ES is only supported in Node.js 18+), since it serves dependency for lambda in a different way (I will appreciate some help with understanding how the size impacts with layers) Dual package hazard in node.js docs. This approach is used for double bundling.
  • We won't get any data on modules usage Having two packages allows us to use npm downloads stats which is not a very accurate metric but still.

Tree shaking POC

To demonstrate tree shaking, cause it doesn't have noticeable results in the previous tests, I decided to convert the Commons package to double package, because it is Logger dependency and it has the dead code. So it is a good opportunity to tree-shake that.

I vendored the new Logger package to test it. I added console.log() to the source code of both Logger and Commons packages, to understand that it uses proper sources during compilation by tsc:

echo 'console.log("Log from CJS-logger package");' >> node_modules/@aws-lambda-powertools/logger/lib/cjs/index.js 
echo 'console.log("Log from CJS-commons package");' >> node_modules/@aws-lambda-powertools/commons/lib/cjs/index.js 

and to the ES module

echo 'console.log("Log from ESM-logger package");' >> node_modules/@aws-lambda-powertools/logger/lib/esm/index.js 
echo 'console.log("Log from ESM-commons package");' >> node_modules/@aws-lambda-powertools/commons/lib/esm/index.js 

I build my app as CommonJS with tsc and these settings among others:

package.json

"type": "commonjs",

tsconfig.json

"module": "commonjs",  

And I ran my CommonJS app.

Then I build my app as ES with tsc and these settings among others:

package.json

"type": "module",

tsconfig.json

"module": "ESNext",  
"moduleResolution":"node",                  

Then I ran my ES app.

And it works as expected:

commons+logger

For tree shaking, I used the same hello world apps as in tests in the previous comment and esbuild to create two bundles. Inside the CJS app require is used to import the logger, ES app uses import to import the logger. You can see the parameters used for esbuild and bundle sizes on the screenshot.

cjs-vs-esm-with-commons-double-bundled
Spoiler: CJS bundle code

"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// node_modules/aws-lambda-powertools-commons/lib/cjs/utils/lambda/LambdaInterface.js
var require_LambdaInterface = __commonJS({
  "node_modules/aws-lambda-powertools-commons/lib/cjs/utils/lambda/LambdaInterface.js"(exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
  }
});

// node_modules/aws-lambda-powertools-commons/lib/cjs/utils/lambda/index.js
var require_lambda = __commonJS({
  "node_modules/aws-lambda-powertools-commons/lib/cjs/utils/lambda/index.js"(exports) {
    "use strict";
    var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
      if (k2 === void 0)
        k2 = k;
      var desc = Object.getOwnPropertyDescriptor(m, k);
      if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
        desc = { enumerable: true, get: function() {
          return m[k];
        } };
      }
      Object.defineProperty(o, k2, desc);
    } : function(o, m, k, k2) {
      if (k2 === void 0)
        k2 = k;
      o[k2] = m[k];
    });
    var __exportStar = exports && exports.__exportStar || function(m, exports2) {
      for (var p in m)
        if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p))
          __createBinding(exports2, m, p);
    };
    Object.defineProperty(exports, "__esModule", { value: true });
    __exportStar(require_LambdaInterface(), exports);
  }
});

// node_modules/aws-lambda-powertools-commons/lib/cjs/Utility.js
var require_Utility = __commonJS({
  "node_modules/aws-lambda-powertools-commons/lib/cjs/Utility.js"(exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.Utility = void 0;
    var Utility = class {
      constructor() {
        this.coldStart = true;
        this.defaultServiceName = "service_undefined";
      }
      getColdStart() {
        if (this.coldStart) {
          this.coldStart = false;
          return true;
        }
        return false;
      }
      isColdStart() {
        return this.getColdStart();
      }
      getDefaultServiceName() {
        return this.defaultServiceName;
      }
      /**
      * Validate that the service name provided is valid.
      * Used internally during initialization.
      *
      * @param serviceName - Service name to validate
      */
      isValidServiceName(serviceName) {
        return typeof serviceName === "string" && serviceName.trim().length > 0;
      }
    };
    exports.Utility = Utility;
  }
});

// node_modules/aws-lambda-powertools-commons/lib/cjs/config/ConfigService.js
var require_ConfigService = __commonJS({
  "node_modules/aws-lambda-powertools-commons/lib/cjs/config/ConfigService.js"(exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.ConfigService = void 0;
    var ConfigService = class {
    };
    exports.ConfigService = ConfigService;
  }
});

// node_modules/aws-lambda-powertools-commons/lib/cjs/config/EnvironmentVariablesService.js
var require_EnvironmentVariablesService = __commonJS({
  "node_modules/aws-lambda-powertools-commons/lib/cjs/config/EnvironmentVariablesService.js"(exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.EnvironmentVariablesService = void 0;
    var index_js_1 = require_config();
    var EnvironmentVariablesService = class extends index_js_1.ConfigService {
      constructor() {
        super(...arguments);
        this.serviceNameVariable = "POWERTOOLS_SERVICE_NAME";
        this.xRayTraceIdVariable = "_X_AMZN_TRACE_ID";
      }
      /**
       * It returns the value of an environment variable that has given name.
       *
       * @param {string} name
       * @returns {string}
       */
      get(name) {
        return process.env[name]?.trim() || "";
      }
      /**
       * It returns the value of the POWERTOOLS_SERVICE_NAME environment variable.
       *
       * @returns {string}
       */
      getServiceName() {
        return this.get(this.serviceNameVariable);
      }
      /**
       * It returns the value of the _X_AMZN_TRACE_ID environment variable.
       *
       * The AWS X-Ray Trace data available in the environment variable has this format:
       * `Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1`,
       *
       * The actual Trace ID is: `1-5759e988-bd862e3fe1be46a994272793`.
       *
       * @returns {string}
       */
      getXrayTraceId() {
        const xRayTraceId = this.get(this.xRayTraceIdVariable);
        if (xRayTraceId === "")
          return void 0;
        return xRayTraceId.split(";")[0].replace("Root=", "");
      }
    };
    exports.EnvironmentVariablesService = EnvironmentVariablesService;
  }
});

// node_modules/aws-lambda-powertools-commons/lib/cjs/config/index.js
var require_config = __commonJS({
  "node_modules/aws-lambda-powertools-commons/lib/cjs/config/index.js"(exports) {
    "use strict";
    var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
      if (k2 === void 0)
        k2 = k;
      var desc = Object.getOwnPropertyDescriptor(m, k);
      if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
        desc = { enumerable: true, get: function() {
          return m[k];
        } };
      }
      Object.defineProperty(o, k2, desc);
    } : function(o, m, k, k2) {
      if (k2 === void 0)
        k2 = k;
      o[k2] = m[k];
    });
    var __exportStar = exports && exports.__exportStar || function(m, exports2) {
      for (var p in m)
        if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p))
          __createBinding(exports2, m, p);
    };
    Object.defineProperty(exports, "__esModule", { value: true });
    __exportStar(require_ConfigService(), exports);
    __exportStar(require_EnvironmentVariablesService(), exports);
  }
});

// node_modules/aws-lambda-powertools-commons/lib/cjs/samples/resources/contexts/hello-world.js
var require_hello_world = __commonJS({
  "node_modules/aws-lambda-powertools-commons/lib/cjs/samples/resources/contexts/hello-world.js"(exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.helloworldContext = void 0;
    var helloworldContext = {
      callbackWaitsForEmptyEventLoop: true,
      functionVersion: "$LATEST",
      functionName: "foo-bar-function",
      memoryLimitInMB: "128",
      logGroupName: "/aws/lambda/foo-bar-function-123456abcdef",
      logStreamName: "2021/03/09/[$LATEST]abcdef123456abcdef123456abcdef123456",
      invokedFunctionArn: "arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function",
      awsRequestId: "c6af9ac6-7b61-11e6-9a41-93e812345678",
      getRemainingTimeInMillis: () => 1234,
      done: () => console.log("Done!"),
      fail: () => console.log("Failed!"),
      succeed: () => console.log("Succeeded!")
    };
    exports.helloworldContext = helloworldContext;
  }
});

// node_modules/aws-lambda-powertools-commons/lib/cjs/samples/resources/contexts/index.js
var require_contexts = __commonJS({
  "node_modules/aws-lambda-powertools-commons/lib/cjs/samples/resources/contexts/index.js"(exports) {
    "use strict";
    var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
      if (k2 === void 0)
        k2 = k;
      var desc = Object.getOwnPropertyDescriptor(m, k);
      if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
        desc = { enumerable: true, get: function() {
          return m[k];
        } };
      }
      Object.defineProperty(o, k2, desc);
    } : function(o, m, k, k2) {
      if (k2 === void 0)
        k2 = k;
      o[k2] = m[k];
    });
    var __exportStar = exports && exports.__exportStar || function(m, exports2) {
      for (var p in m)
        if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p))
          __createBinding(exports2, m, p);
    };
    Object.defineProperty(exports, "__esModule", { value: true });
    __exportStar(require_hello_world(), exports);
  }
});

// node_modules/aws-lambda-powertools-commons/lib/cjs/samples/resources/events/custom/index.js
var require_custom = __commonJS({
  "node_modules/aws-lambda-powertools-commons/lib/cjs/samples/resources/events/custom/index.js"(exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.CustomEvent = void 0;
    exports.CustomEvent = {
      key1: "value1",
      key2: "value2",
      key3: "value3"
    };
  }
});

// node_modules/aws-lambda-powertools-commons/lib/cjs/samples/resources/events/index.js
var require_events = __commonJS({
  "node_modules/aws-lambda-powertools-commons/lib/cjs/samples/resources/events/index.js"(exports) {
    "use strict";
    var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
      if (k2 === void 0)
        k2 = k;
      var desc = Object.getOwnPropertyDescriptor(m, k);
      if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
        desc = { enumerable: true, get: function() {
          return m[k];
        } };
      }
      Object.defineProperty(o, k2, desc);
    } : function(o, m, k, k2) {
      if (k2 === void 0)
        k2 = k;
      o[k2] = m[k];
    });
    var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
      Object.defineProperty(o, "default", { enumerable: true, value: v });
    } : function(o, v) {
      o["default"] = v;
    });
    var __importStar = exports && exports.__importStar || function(mod) {
      if (mod && mod.__esModule)
        return mod;
      var result = {};
      if (mod != null) {
        for (var k in mod)
          if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k))
            __createBinding(result, mod, k);
      }
      __setModuleDefault(result, mod);
      return result;
    };
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.Custom = void 0;
    exports.Custom = __importStar(require_custom());
  }
});

// node_modules/aws-lambda-powertools-commons/lib/cjs/types/middy.js
var require_middy = __commonJS({
  "node_modules/aws-lambda-powertools-commons/lib/cjs/types/middy.js"(exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
  }
});

// node_modules/aws-lambda-powertools-commons/lib/cjs/index.js
var require_cjs = __commonJS({
  "node_modules/aws-lambda-powertools-commons/lib/cjs/index.js"(exports) {
    "use strict";
    var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
      if (k2 === void 0)
        k2 = k;
      var desc = Object.getOwnPropertyDescriptor(m, k);
      if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
        desc = { enumerable: true, get: function() {
          return m[k];
        } };
      }
      Object.defineProperty(o, k2, desc);
    } : function(o, m, k, k2) {
      if (k2 === void 0)
        k2 = k;
      o[k2] = m[k];
    });
    var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
      Object.defineProperty(o, "default", { enumerable: true, value: v });
    } : function(o, v) {
      o["default"] = v;
    });
    var __exportStar = exports && exports.__exportStar || function(m, exports2) {
      for (var p in m)
        if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p))
          __createBinding(exports2, m, p);
    };
    var __importStar = exports && exports.__importStar || function(mod) {
      if (mod && mod.__esModule)
        return mod;
      var result = {};
      if (mod != null) {
        for (var k in mod)
          if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k))
            __createBinding(result, mod, k);
      }
      __setModuleDefault(result, mod);
      return result;
    };
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.Events = exports.ContextExamples = void 0;
    __exportStar(require_lambda(), exports);
    __exportStar(require_Utility(), exports);
    __exportStar(require_config(), exports);
    exports.ContextExamples = __importStar(require_contexts());
    exports.Events = __importStar(require_events());
    __exportStar(require_middy(), exports);
  }
});

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/formatter/LogFormatter.js
var require_LogFormatter = __commonJS({
  "node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/formatter/LogFormatter.js"(exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.LogFormatter = void 0;
    var LogFormatter = class {
      /**
       * It formats a given Error parameter.
       *
       * @param {Error} error
       * @returns {LogAttributes}
       */
      formatError(error) {
        return {
          name: error.name,
          location: this.getCodeLocation(error.stack),
          message: error.message,
          stack: error.stack
        };
      }
      /**
       * It formats a date into a string in simplified extended ISO format (ISO 8601).
       *
       * @param {Date} now
       * @returns {string}
       */
      formatTimestamp(now) {
        return now.toISOString();
      }
      /**
       * It returns a string containing the location of an error, given a particular stack trace.
       *
       * @param stack
       * @returns {string}
       */
      getCodeLocation(stack) {
        if (!stack) {
          return "";
        }
        const stackLines = stack.split("\n");
        const regex = /\((.*):(\d+):(\d+)\)\\?$/;
        let i;
        for (i = 0; i < stackLines.length; i++) {
          const match = regex.exec(stackLines[i]);
          if (Array.isArray(match)) {
            return `${match[1]}:${Number(match[2])}`;
          }
        }
        return "";
      }
    };
    exports.LogFormatter = LogFormatter;
  }
});

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/formatter/LogFormatterInterface.js
var require_LogFormatterInterface = __commonJS({
  "node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/formatter/LogFormatterInterface.js"(exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
  }
});

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/formatter/PowertoolLogFormatter.js
var require_PowertoolLogFormatter = __commonJS({
  "node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/formatter/PowertoolLogFormatter.js"(exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.PowertoolLogFormatter = void 0;
    var index_js_1 = require_formatter();
    var PowertoolLogFormatter = class extends index_js_1.LogFormatter {
      /**
       * It formats key-value pairs of log attributes.
       *
       * @param {UnformattedAttributes} attributes
       * @returns {PowertoolLog}
       */
      formatAttributes(attributes) {
        return {
          cold_start: attributes.lambdaContext?.coldStart,
          function_arn: attributes.lambdaContext?.invokedFunctionArn,
          function_memory_size: attributes.lambdaContext?.memoryLimitInMB,
          function_name: attributes.lambdaContext?.functionName,
          function_request_id: attributes.lambdaContext?.awsRequestId,
          level: attributes.logLevel,
          message: attributes.message,
          sampling_rate: attributes.sampleRateValue,
          service: attributes.serviceName,
          timestamp: this.formatTimestamp(attributes.timestamp),
          xray_trace_id: attributes.xRayTraceId
        };
      }
    };
    exports.PowertoolLogFormatter = PowertoolLogFormatter;
  }
});

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/formatter/index.js
var require_formatter = __commonJS({
  "node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/formatter/index.js"(exports) {
    "use strict";
    var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
      if (k2 === void 0)
        k2 = k;
      var desc = Object.getOwnPropertyDescriptor(m, k);
      if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
        desc = { enumerable: true, get: function() {
          return m[k];
        } };
      }
      Object.defineProperty(o, k2, desc);
    } : function(o, m, k, k2) {
      if (k2 === void 0)
        k2 = k;
      o[k2] = m[k];
    });
    var __exportStar = exports && exports.__exportStar || function(m, exports2) {
      for (var p in m)
        if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p))
          __createBinding(exports2, m, p);
    };
    Object.defineProperty(exports, "__esModule", { value: true });
    __exportStar(require_LogFormatter(), exports);
    __exportStar(require_LogFormatterInterface(), exports);
    __exportStar(require_PowertoolLogFormatter(), exports);
  }
});

// node_modules/lodash.merge/index.js
// I REMOVED THIS HUGE CHUNK OF LODASH DEPENDENCY FOR BETTER READABILITY 

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/log/LogItem.js
var require_LogItem = __commonJS({
  "node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/log/LogItem.js"(exports) {
    "use strict";
    var __importDefault = exports && exports.__importDefault || function(mod) {
      return mod && mod.__esModule ? mod : { "default": mod };
    };
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.LogItem = void 0;
    var lodash_merge_1 = __importDefault(require_lodash());
    var LogItem = class {
      constructor(params) {
        this.attributes = {};
        this.addAttributes(params.baseAttributes);
        this.addAttributes(params.persistentAttributes);
      }
      addAttributes(attributes) {
        this.attributes = (0, lodash_merge_1.default)(this.attributes, attributes);
        return this;
      }
      getAttributes() {
        return this.attributes;
      }
      prepareForPrint() {
        this.setAttributes(this.removeEmptyKeys(this.getAttributes()));
      }
      removeEmptyKeys(attributes) {
        const newAttributes = {};
        for (const key in attributes) {
          if (attributes[key] !== void 0 && attributes[key] !== "" && attributes[key] !== null) {
            newAttributes[key] = attributes[key];
          }
        }
        return newAttributes;
      }
      setAttributes(attributes) {
        this.attributes = attributes;
      }
    };
    exports.LogItem = LogItem;
  }
});

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/log/LogItemInterface.js
var require_LogItemInterface = __commonJS({
  "node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/log/LogItemInterface.js"(exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
  }
});

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/log/index.js
var require_log = __commonJS({
  "node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/log/index.js"(exports) {
    "use strict";
    var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
      if (k2 === void 0)
        k2 = k;
      var desc = Object.getOwnPropertyDescriptor(m, k);
      if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
        desc = { enumerable: true, get: function() {
          return m[k];
        } };
      }
      Object.defineProperty(o, k2, desc);
    } : function(o, m, k, k2) {
      if (k2 === void 0)
        k2 = k;
      o[k2] = m[k];
    });
    var __exportStar = exports && exports.__exportStar || function(m, exports2) {
      for (var p in m)
        if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p))
          __createBinding(exports2, m, p);
    };
    Object.defineProperty(exports, "__esModule", { value: true });
    __exportStar(require_LogItem(), exports);
    __exportStar(require_LogItemInterface(), exports);
  }
});

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/config/ConfigServiceInterface.js
var require_ConfigServiceInterface = __commonJS({
  "node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/config/ConfigServiceInterface.js"(exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
  }
});

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/config/EnvironmentVariablesService.js
var require_EnvironmentVariablesService2 = __commonJS({
  "node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/config/EnvironmentVariablesService.js"(exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.EnvironmentVariablesService = void 0;
    var commons_1 = require_cjs();
    var EnvironmentVariablesService = class extends commons_1.EnvironmentVariablesService {
      constructor() {
        super(...arguments);
        this.awsRegionVariable = "AWS_REGION";
        this.currentEnvironmentVariable = "ENVIRONMENT";
        this.devModeVariable = "POWERTOOLS_DEV";
        this.functionNameVariable = "AWS_LAMBDA_FUNCTION_NAME";
        this.functionVersionVariable = "AWS_LAMBDA_FUNCTION_VERSION";
        this.logEventVariable = "POWERTOOLS_LOGGER_LOG_EVENT";
        this.logLevelVariable = "LOG_LEVEL";
        this.memoryLimitInMBVariable = "AWS_LAMBDA_FUNCTION_MEMORY_SIZE";
        this.sampleRateValueVariable = "POWERTOOLS_LOGGER_SAMPLE_RATE";
      }
      /**
       * It returns the value of the AWS_REGION environment variable.
       *
       * @returns {string}
       */
      getAwsRegion() {
        return this.get(this.awsRegionVariable);
      }
      /**
       * It returns the value of the ENVIRONMENT environment variable.
       *
       * @returns {string}
       */
      getCurrentEnvironment() {
        return this.get(this.currentEnvironmentVariable);
      }
      /**
       * It returns the value of the AWS_LAMBDA_FUNCTION_MEMORY_SIZE environment variable.
       *
       * @returns {string}
       */
      getFunctionMemory() {
        const value = this.get(this.memoryLimitInMBVariable);
        return Number(value);
      }
      /**
       * It returns the value of the AWS_LAMBDA_FUNCTION_NAME environment variable.
       *
       * @returns {string}
       */
      getFunctionName() {
        return this.get(this.functionNameVariable);
      }
      /**
       * It returns the value of the AWS_LAMBDA_FUNCTION_VERSION environment variable.
       *
       * @returns {string}
       */
      getFunctionVersion() {
        return this.get(this.functionVersionVariable);
      }
      /**
       * It returns the value of the POWERTOOLS_LOGGER_LOG_EVENT environment variable.
       *
       * @returns {boolean}
       */
      getLogEvent() {
        const value = this.get(this.logEventVariable);
        return this.isValueTrue(value);
      }
      /**
       * It returns the value of the LOG_LEVEL environment variable.
       *
       * @returns {string}
       */
      getLogLevel() {
        return this.get(this.logLevelVariable);
      }
      /**
       * It returns the value of the POWERTOOLS_LOGGER_SAMPLE_RATE environment variable.
       *
       * @returns {string|undefined}
       */
      getSampleRateValue() {
        const value = this.get(this.sampleRateValueVariable);
        return value && value.length > 0 ? Number(value) : void 0;
      }
      /**
       * It returns true if the POWERTOOLS_DEV environment variable is set to truthy value.
       *
       * @returns {boolean}
       */
      isDevMode() {
        const value = this.get(this.devModeVariable);
        return this.isValueTrue(value);
      }
      /**
       * It returns true if the string value represents a boolean true value.
       *
       * @param {string} value
       * @returns boolean
       */
      isValueTrue(value) {
        const truthyValues = ["1", "y", "yes", "t", "true", "on"];
        return truthyValues.includes(value.toLowerCase());
      }
    };
    exports.EnvironmentVariablesService = EnvironmentVariablesService;
  }
});

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/config/index.js
var require_config2 = __commonJS({
  "node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/config/index.js"(exports) {
    "use strict";
    var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
      if (k2 === void 0)
        k2 = k;
      var desc = Object.getOwnPropertyDescriptor(m, k);
      if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
        desc = { enumerable: true, get: function() {
          return m[k];
        } };
      }
      Object.defineProperty(o, k2, desc);
    } : function(o, m, k, k2) {
      if (k2 === void 0)
        k2 = k;
      o[k2] = m[k];
    });
    var __exportStar = exports && exports.__exportStar || function(m, exports2) {
      for (var p in m)
        if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p))
          __createBinding(exports2, m, p);
    };
    Object.defineProperty(exports, "__esModule", { value: true });
    __exportStar(require_ConfigServiceInterface(), exports);
    __exportStar(require_EnvironmentVariablesService2(), exports);
  }
});

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/Logger.js
var require_Logger = __commonJS({
  "node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/Logger.js"(exports) {
    "use strict";
    var __importDefault = exports && exports.__importDefault || function(mod) {
      return mod && mod.__esModule ? mod : { "default": mod };
    };
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.Logger = void 0;
    var console_1 = require("console");
    var commons_1 = require_cjs();
    var index_js_1 = require_formatter();
    var index_js_2 = require_log();
    var lodash_merge_1 = __importDefault(require_lodash());
    var index_js_3 = require_config2();
    var Logger2 = class extends commons_1.Utility {
      /**
       * It initializes the Logger class with an optional set of options (settings).
       * *
       * @param {ConstructorOptions} options
       */
      constructor(options = {}) {
        super();
        this.logEvent = false;
        this.logIndentation = 0;
        this.logLevelThresholds = {
          DEBUG: 8,
          INFO: 12,
          WARN: 16,
          ERROR: 20,
          SILENT: 24
        };
        this.logsSampled = false;
        this.persistentLogAttributes = {};
        this.powertoolLogData = {};
        this.setOptions(options);
      }
      /**
       * It adds the current Lambda function's invocation context data to the powertoolLogData property of the instance.
       * This context data will be part of all printed log items.
       *
       * @param {Context} context
       * @returns {void}
       */
      addContext(context) {
        const lambdaContext = {
          invokedFunctionArn: context.invokedFunctionArn,
          coldStart: this.getColdStart(),
          awsRequestId: context.awsRequestId,
          memoryLimitInMB: Number(context.memoryLimitInMB),
          functionName: context.functionName,
          functionVersion: context.functionVersion
        };
        this.addToPowertoolLogData({
          lambdaContext
        });
      }
      /**
       * It adds the given attributes (key-value pairs) to all log items generated by this Logger instance.
       *
       * @param {LogAttributes} attributes
       * @returns {void}
       */
      addPersistentLogAttributes(attributes) {
        (0, lodash_merge_1.default)(this.persistentLogAttributes, attributes);
      }
      /**
       * Alias for addPersistentLogAttributes.
       *
       * @param {LogAttributes} attributes
       * @returns {void}
       */
      appendKeys(attributes) {
        this.addPersistentLogAttributes(attributes);
      }
      /**
       * It creates a separate Logger instance, identical to the current one
       * It's possible to overwrite the new instance options by passing them.
       *
       * @param {ConstructorOptions} options
       * @returns {Logger}
       */
      createChild(options = {}) {
        const parentsOptions = {
          logLevel: this.getLogLevel(),
          customConfigService: this.getCustomConfigService(),
          logFormatter: this.getLogFormatter()
        };
        const parentsPowertoolsLogData = this.getPowertoolLogData();
        const childLogger = new Logger2((0, lodash_merge_1.default)(parentsOptions, parentsPowertoolsLogData, options));
        const parentsPersistentLogAttributes = this.getPersistentLogAttributes();
        childLogger.addPersistentLogAttributes(parentsPersistentLogAttributes);
        if (parentsPowertoolsLogData.lambdaContext) {
          childLogger.addContext(parentsPowertoolsLogData.lambdaContext);
        }
        return childLogger;
      }
      /**
       * It prints a log item with level DEBUG.
       *
       * @param {LogItemMessage} input
       * @param {Error | LogAttributes | string} extraInput
       * @returns {void}
       */
      debug(input, ...extraInput) {
        this.processLogItem("DEBUG", input, extraInput);
      }
      /**
       * It prints a log item with level ERROR.
       *
       * @param {LogItemMessage} input
       * @param {Error | LogAttributes | string} extraInput
       * @returns {void}
       */
      error(input, ...extraInput) {
        this.processLogItem("ERROR", input, extraInput);
      }
      /**
       * It returns a boolean value. True means that the Lambda invocation events
       * are printed in the logs.
       *
       * @returns {boolean}
       */
      getLogEvent() {
        return this.logEvent;
      }
      /**
       * It returns a boolean value, if true all the logs will be printed.
       *
       * @returns {boolean}
       */
      getLogsSampled() {
        return this.logsSampled;
      }
      /**
       * It returns the persistent log attributes, which are the attributes
       * that will be logged in all log items.
       *
       * @private
       * @returns {LogAttributes}
       */
      getPersistentLogAttributes() {
        return this.persistentLogAttributes;
      }
      /**
       * It prints a log item with level INFO.
       *
       * @param {LogItemMessage} input
       * @param {Error | LogAttributes | string} extraInput
       * @returns {void}
       */
      info(input, ...extraInput) {
        this.processLogItem("INFO", input, extraInput);
      }
      /**
       * Method decorator that adds the current Lambda function context as extra
       * information in all log items.
       *
       * The decorator can be used only when attached to a Lambda function handler which
       * is written as method of a class, and should be declared just before the handler declaration.
       *
       * Note: Currently TypeScript only supports decorators on classes and methods. If you are using the
       * function syntax, you should use the middleware instead.
       *
       * @example
       * ```typescript
       * import { Logger } from '@aws-lambda-powertools/logger';
       * import { LambdaInterface } from '@aws-lambda-powertools/commons';
       *
       * const logger = new Logger();
       *
       * class Lambda implements LambdaInterface {
       *     // Decorate your handler class method
       *     @logger.injectLambdaContext()
       *     public async handler(_event: any, _context: any): Promise<void> {
       *         logger.info('This is an INFO log with some context');
       *     }
       * }
       *
       * const handlerClass = new Lambda();
       * export const handler = handlerClass.handler.bind(handlerClass);
       * ```
       *
       * @see https://www.typescriptlang.org/docs/handbook/decorators.html#method-decorators
       * @returns {HandlerMethodDecorator}
       */
      injectLambdaContext(options) {
        return (_target, _propertyKey, descriptor) => {
          const originalMethod = descriptor.value;
          const loggerRef = this;
          descriptor.value = async function(event, context, callback) {
            let initialPersistentAttributes = {};
            if (options && options.clearState === true) {
              initialPersistentAttributes = { ...loggerRef.getPersistentLogAttributes() };
            }
            Logger2.injectLambdaContextBefore(loggerRef, event, context, options);
            let result;
            try {
              result = await originalMethod.apply(this, [event, context, callback]);
            } catch (error) {
              throw error;
            } finally {
              Logger2.injectLambdaContextAfterOrOnError(loggerRef, initialPersistentAttributes, options);
            }
            return result;
          };
        };
      }
      static injectLambdaContextAfterOrOnError(logger2, initialPersistentAttributes, options) {
        if (options && options.clearState === true) {
          logger2.setPersistentLogAttributes(initialPersistentAttributes);
        }
      }
      static injectLambdaContextBefore(logger2, event, context, options) {
        logger2.addContext(context);
        let shouldLogEvent = void 0;
        if (options && options.hasOwnProperty("logEvent")) {
          shouldLogEvent = options.logEvent;
        }
        logger2.logEventIfEnabled(event, shouldLogEvent);
      }
      /**
       * Logs a Lambda invocation event, if it *should*.
       *
       ** @param {unknown} event
       * @param {boolean} [overwriteValue]
       * @returns {void}
       */
      logEventIfEnabled(event, overwriteValue) {
        if (!this.shouldLogEvent(overwriteValue)) {
          return;
        }
        this.info("Lambda invocation event", { event });
      }
      /**
       * If the sample rate feature is enabled, the calculation that determines whether the logs
       * will actually be printed or not for this invocation is done when the Logger class is
       * initialized.
       * This method will repeat that calculation (with possible different outcome).
       *
       * @returns {void}
       */
      refreshSampleRateCalculation() {
        this.setLogsSampled();
      }
      /**
       * Alias for removePersistentLogAttributes.
       *
       * @param {string[]} keys
       * @returns {void}
       */
      removeKeys(keys) {
        this.removePersistentLogAttributes(keys);
      }
      /**
       * It removes attributes based on provided keys to all log items generated by this Logger instance.
       *
       * @param {string[]} keys
       * @returns {void}
       */
      removePersistentLogAttributes(keys) {
        keys.forEach((key) => {
          if (this.persistentLogAttributes && key in this.persistentLogAttributes) {
            delete this.persistentLogAttributes[key];
          }
        });
      }
      /**
       * It sets the given attributes (key-value pairs) to all log items generated by this Logger instance.
       * Note: this replaces the pre-existing value.
       *
       * @param {LogAttributes} attributes
       * @returns {void}
       */
      setPersistentLogAttributes(attributes) {
        this.persistentLogAttributes = attributes;
      }
      /**
       * It sets the user-provided sample rate value.
       *
       * @param {number} [sampleRateValue]
       * @returns {void}
       */
      setSampleRateValue(sampleRateValue) {
        this.powertoolLogData.sampleRateValue = sampleRateValue || this.getCustomConfigService()?.getSampleRateValue() || this.getEnvVarsService().getSampleRateValue();
      }
      /**
       * It checks whether the current Lambda invocation event should be printed in the logs or not.
       *
       * @private
       * @param {boolean} [overwriteValue]
       * @returns {boolean}
       */
      shouldLogEvent(overwriteValue) {
        if (typeof overwriteValue === "boolean") {
          return overwriteValue;
        }
        return this.getLogEvent();
      }
      /**
       * It prints a log item with level WARN.
       *
       * @param {LogItemMessage} input
       * @param {Error | LogAttributes | string} extraInput
       * @returns {void}
       */
      warn(input, ...extraInput) {
        this.processLogItem("WARN", input, extraInput);
      }
      /**
       * It stores information that is printed in all log items.
       *
       * @param {Partial<PowertoolLogData>} attributesArray
       * @private
       * @returns {void}
       */
      addToPowertoolLogData(...attributesArray) {
        attributesArray.forEach((attributes) => {
          (0, lodash_merge_1.default)(this.powertoolLogData, attributes);
        });
      }
      /**
       * It processes a particular log item so that it can be printed to stdout:
       * - Merges ephemeral log attributes with persistent log attributes (printed for all logs) and additional info;
       * - Formats all the log attributes;
       *
       * @private
       * @param {LogLevel} logLevel
       * @param {LogItemMessage} input
       * @param {LogItemExtraInput} extraInput
       * @returns {LogItem}
       */
      createAndPopulateLogItem(logLevel, input, extraInput) {
        const unformattedBaseAttributes = (0, lodash_merge_1.default)({
          logLevel,
          timestamp: /* @__PURE__ */ new Date(),
          message: typeof input === "string" ? input : input.message,
          xRayTraceId: this.envVarsService.getXrayTraceId()
        }, this.getPowertoolLogData());
        const logItem = new index_js_2.LogItem({
          baseAttributes: this.getLogFormatter().formatAttributes(unformattedBaseAttributes),
          persistentAttributes: this.getPersistentLogAttributes()
        });
        if (typeof input !== "string") {
          logItem.addAttributes(input);
        }
        extraInput.forEach((item) => {
          const attributes = item instanceof Error ? { error: item } : typeof item === "string" ? { extra: item } : item;
          logItem.addAttributes(attributes);
        });
        return logItem;
      }
      /**
       * It returns the custom config service, an abstraction used to fetch environment variables.
       *
       * @private
       * @returns {ConfigServiceInterface | undefined}
       */
      getCustomConfigService() {
        return this.customConfigService;
      }
      /**
       * It returns the instance of a service that fetches environment variables.
       *
       * @private
       * @returns {EnvironmentVariablesService}
       */
      getEnvVarsService() {
        return this.envVarsService;
      }
      /**
       * It returns the instance of a service that formats the structure of a
       * log item's keys and values in the desired way.
       *
       * @private
       * @returns {LogFormatterInterface}
       */
      getLogFormatter() {
        return this.logFormatter;
      }
      /**
       * It returns the log level set for the Logger instance.
       *
       * Even though logLevel starts as undefined, it will always be set to a value
       * during the Logger instance's initialization. So, we can safely use the non-null
       * assertion operator here.
       *
       * @private
       * @returns {LogLevel}
       */
      getLogLevel() {
        return this.logLevel;
      }
      /**
       * It returns information that will be added in all log item by
       * this Logger instance (different from user-provided persistent attributes).
       *
       * @private
       * @returns {LogAttributes}
       */
      getPowertoolLogData() {
        return this.powertoolLogData;
      }
      /**
       * When the data added in the log item contains object references or BigInt values,
       * `JSON.stringify()` can't handle them and instead throws errors:
       * `TypeError: cyclic object value` or `TypeError: Do not know how to serialize a BigInt`.
       * To mitigate these issues, this method will find and remove all cyclic references and convert BigInt values to strings.
       *
       * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#exceptions
       * @private
       */
      getReplacer() {
        const references = /* @__PURE__ */ new WeakSet();
        return (key, value) => {
          let item = value;
          if (item instanceof Error) {
            item = this.getLogFormatter().formatError(item);
          }
          if (typeof item === "bigint") {
            return item.toString();
          }
          if (typeof item === "object" && value !== null) {
            if (references.has(item)) {
              return;
            }
            references.add(item);
          }
          return item;
        };
      }
      /**
       * It returns the numeric sample rate value.
       *
       * @private
       * @returns {number}
       */
      getSampleRateValue() {
        if (!this.powertoolLogData.sampleRateValue) {
          this.setSampleRateValue();
        }
        return this.powertoolLogData.sampleRateValue;
      }
      /**
       * It returns true and type guards the log level if a given log level is valid.
       *
       * @param {LogLevel} logLevel
       * @private
       * @returns {boolean}
       */
      isValidLogLevel(logLevel) {
        return typeof logLevel === "string" && logLevel in this.logLevelThresholds;
      }
      /**
       * It prints a given log with given log level.
       *
       * @param {LogLevel} logLevel
       * @param {LogItem} log
       * @private
       */
      printLog(logLevel, log) {
        log.prepareForPrint();
        const consoleMethod = logLevel.toLowerCase();
        this.console[consoleMethod](JSON.stringify(log.getAttributes(), this.getReplacer(), this.logIndentation));
      }
      /**
       * It prints a given log with given log level.
       *
       * @param {Uppercase<LogLevel>} logLevel
       * @param {LogItemMessage} input
       * @param {LogItemExtraInput} extraInput
       * @private
       */
      processLogItem(logLevel, input, extraInput) {
        if (!this.shouldPrint(logLevel)) {
          return;
        }
        this.printLog(logLevel, this.createAndPopulateLogItem(logLevel, input, extraInput));
      }
      /**
       * It initializes console property as an instance of the internal version of Console() class (PR #748)
       * or as the global node console if the `POWERTOOLS_DEV' env variable is set and has truthy value.
       *
       * @private
       * @returns {void}
       */
      setConsole() {
        if (!this.getEnvVarsService().isDevMode()) {
          this.console = new console_1.Console({ stdout: process.stdout, stderr: process.stderr });
        } else {
          this.console = console;
        }
      }
      /**
       * Sets the Logger's customer config service instance, which will be used
       * to fetch environment variables.
       *
       * @private
       * @param {ConfigServiceInterface} customConfigService
       * @returns {void}
       */
      setCustomConfigService(customConfigService) {
        this.customConfigService = customConfigService ? customConfigService : void 0;
      }
      /**
       * Sets the Logger's custom config service instance, which will be used
       * to fetch environment variables.
       *
       * @private
       * @param {ConfigServiceInterface} customConfigService
       * @returns {void}
       */
      setEnvVarsService() {
        this.envVarsService = new index_js_3.EnvironmentVariablesService();
      }
      /**
       * If the log event feature is enabled via env variable, it sets a property that tracks whether
       * the event passed to the Lambda function handler should be logged or not.
       *
       * @private
       * @returns {void}
       */
      setLogEvent() {
        if (this.getEnvVarsService().getLogEvent()) {
          this.logEvent = true;
        }
      }
      /**
       * It sets the log formatter instance, in charge of giving a custom format
       * to the structured logs
       *
       * @private
       * @param {LogFormatterInterface} logFormatter
       * @returns {void}
       */
      setLogFormatter(logFormatter) {
        this.logFormatter = logFormatter || new index_js_1.PowertoolLogFormatter();
      }
      /**
       * If the `POWERTOOLS_DEV' env variable is set,
       * it adds JSON indentation for pretty printing logs.
       *
       * @private
       * @returns {void}
       */
      setLogIndentation() {
        if (this.getEnvVarsService().isDevMode()) {
          this.logIndentation = 4;
        }
      }
      /**
       * It sets the Logger's instance log level.
       *
       * @private
       * @param {LogLevel} logLevel
       * @returns {void}
       */
      setLogLevel(logLevel) {
        const constructorLogLevel = logLevel?.toUpperCase();
        if (this.isValidLogLevel(constructorLogLevel)) {
          this.logLevel = constructorLogLevel;
          return;
        }
        const customConfigValue = this.getCustomConfigService()?.getLogLevel()?.toUpperCase();
        if (this.isValidLogLevel(customConfigValue)) {
          this.logLevel = customConfigValue;
          return;
        }
        const envVarsValue = this.getEnvVarsService()?.getLogLevel()?.toUpperCase();
        if (this.isValidLogLevel(envVarsValue)) {
          this.logLevel = envVarsValue;
          return;
        }
        this.logLevel = Logger2.defaultLogLevel;
      }
      /**
       * If the sample rate feature is enabled, it sets a property that tracks whether this Lambda function invocation
       * will print logs or not.
       *
       * @private
       * @returns {void}
       */
      setLogsSampled() {
        const sampleRateValue = this.getSampleRateValue();
        this.logsSampled = sampleRateValue !== void 0 && (sampleRateValue === 1 || Math.random() < sampleRateValue);
      }
      /**
       * It configures the Logger instance settings that will affect the Logger's behaviour
       * and the content of all logs.
       *
       * @private
       * @param {ConstructorOptions} options
       * @returns {Logger}
       */
      setOptions(options) {
        const { logLevel, serviceName, sampleRateValue, logFormatter, customConfigService, persistentLogAttributes, environment } = options;
        this.setEnvVarsService();
        this.setConsole();
        this.setCustomConfigService(customConfigService);
        this.setLogLevel(logLevel);
        this.setSampleRateValue(sampleRateValue);
        this.setLogsSampled();
        this.setLogFormatter(logFormatter);
        this.setPowertoolLogData(serviceName, environment);
        this.setLogEvent();
        this.setLogIndentation();
        this.addPersistentLogAttributes(persistentLogAttributes);
        return this;
      }
      /**
       * It adds important data to the Logger instance that will affect the content of all logs.
       *
       * @param {string} serviceName
       * @param {Environment} environment
       * @param {LogAttributes} persistentLogAttributes
       * @private
       * @returns {void}
       */
      setPowertoolLogData(serviceName, environment, persistentLogAttributes = {}) {
        this.addToPowertoolLogData({
          awsRegion: this.getEnvVarsService().getAwsRegion(),
          environment: environment || this.getCustomConfigService()?.getCurrentEnvironment() || this.getEnvVarsService().getCurrentEnvironment(),
          sampleRateValue: this.getSampleRateValue(),
          serviceName: serviceName || this.getCustomConfigService()?.getServiceName() || this.getEnvVarsService().getServiceName() || this.getDefaultServiceName()
        }, persistentLogAttributes);
      }
      /**
       * It checks whether the current log item should/can be printed.
       *
       * @param {Uppercase<LogLevel>} logLevel
       * @private
       * @returns {boolean}
       */
      shouldPrint(logLevel) {
        if (this.logLevelThresholds[logLevel] >= this.logLevelThresholds[this.getLogLevel()]) {
          return true;
        }
        return this.getLogsSampled();
      }
    };
    exports.Logger = Logger2;
    Logger2.defaultLogLevel = "INFO";
  }
});

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/helpers.js
var require_helpers = __commonJS({
  "node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/helpers.js"(exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.createLogger = void 0;
    var Logger_js_1 = require_Logger();
    var createLogger = (options = {}) => new Logger_js_1.Logger(options);
    exports.createLogger = createLogger;
  }
});

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/middleware/middy.js
var require_middy2 = __commonJS({
  "node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/middleware/middy.js"(exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.injectLambdaContext = void 0;
    var Logger_js_1 = require_Logger();
    var injectLambdaContext = (target, options) => {
      const loggers = target instanceof Array ? target : [target];
      const persistentAttributes = [];
      const injectLambdaContextBefore = async (request) => {
        loggers.forEach((logger2, index) => {
          if (options && options.clearState === true) {
            persistentAttributes[index] = { ...logger2.getPersistentLogAttributes() };
          }
          Logger_js_1.Logger.injectLambdaContextBefore(logger2, request.event, request.context, options);
        });
      };
      const injectLambdaContextAfterOrOnError = async () => {
        if (options && options.clearState === true) {
          loggers.forEach((logger2, index) => {
            Logger_js_1.Logger.injectLambdaContextAfterOrOnError(logger2, persistentAttributes[index], options);
          });
        }
      };
      return {
        before: injectLambdaContextBefore,
        after: injectLambdaContextAfterOrOnError,
        onError: injectLambdaContextAfterOrOnError
      };
    };
    exports.injectLambdaContext = injectLambdaContext;
  }
});

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/middleware/index.js
var require_middleware = __commonJS({
  "node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/middleware/index.js"(exports) {
    "use strict";
    var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
      if (k2 === void 0)
        k2 = k;
      var desc = Object.getOwnPropertyDescriptor(m, k);
      if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
        desc = { enumerable: true, get: function() {
          return m[k];
        } };
      }
      Object.defineProperty(o, k2, desc);
    } : function(o, m, k, k2) {
      if (k2 === void 0)
        k2 = k;
      o[k2] = m[k];
    });
    var __exportStar = exports && exports.__exportStar || function(m, exports2) {
      for (var p in m)
        if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p))
          __createBinding(exports2, m, p);
    };
    Object.defineProperty(exports, "__esModule", { value: true });
    __exportStar(require_middy2(), exports);
  }
});

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/index.js
var require_cjs2 = __commonJS({
  "node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/cjs/index.js"(exports) {
    "use strict";
    var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
      if (k2 === void 0)
        k2 = k;
      var desc = Object.getOwnPropertyDescriptor(m, k);
      if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
        desc = { enumerable: true, get: function() {
          return m[k];
        } };
      }
      Object.defineProperty(o, k2, desc);
    } : function(o, m, k, k2) {
      if (k2 === void 0)
        k2 = k;
      o[k2] = m[k];
    });
    var __exportStar = exports && exports.__exportStar || function(m, exports2) {
      for (var p in m)
        if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p))
          __createBinding(exports2, m, p);
    };
    Object.defineProperty(exports, "__esModule", { value: true });
    __exportStar(require_helpers(), exports);
    __exportStar(require_Logger(), exports);
    __exportStar(require_middleware(), exports);
    __exportStar(require_formatter(), exports);
  }
});

// commons-cjs.cts
var commons_cjs_exports = {};
__export(commons_cjs_exports, {
  lambdaHandler: () => lambdaHandler
});
module.exports = __toCommonJS(commons_cjs_exports);
var { Logger } = require_cjs2();
var logger = new Logger();
var lambdaHandler = async (event, context) => {
  logger.info("Lambda invocation event", { event });
  logger.appendKeys({
    awsRequestId: context.awsRequestId
  });
  let response;
  try {
    response = {
      statusCode: 200,
      body: JSON.stringify({
        message: "hello from ESM version of logger with ESM version of Commons"
      })
    };
    logger.info(
      `Successful response from API enpoint: ${event.path}`,
      response.body
    );
  } catch (err) {
    response = {
      statusCode: 500,
      body: JSON.stringify({
        message: "Some error happened"
      })
    };
    logger.error(
      `Error response from API enpoint: ${event.path}`,
      response.body
    );
  }
  return response;
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  lambdaHandler
});
Spoiler: ESM bundle code

var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));

// node_modules/lodash.merge/index.js
// I REMOVED THIS HUGE CHUNK OF LODASH DEPENDENCY FOR BETTER READABILITY 

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/esm/Logger.js
import { Console } from "console";

// node_modules/aws-lambda-powertools-commons/lib/esm/Utility.js
var Utility = class {
  constructor() {
    this.coldStart = true;
    this.defaultServiceName = "service_undefined";
  }
  getColdStart() {
    if (this.coldStart) {
      this.coldStart = false;
      return true;
    }
    return false;
  }
  isColdStart() {
    return this.getColdStart();
  }
  getDefaultServiceName() {
    return this.defaultServiceName;
  }
  /**
  * Validate that the service name provided is valid.
  * Used internally during initialization.
  *
  * @param serviceName - Service name to validate
  */
  isValidServiceName(serviceName) {
    return typeof serviceName === "string" && serviceName.trim().length > 0;
  }
};

// node_modules/aws-lambda-powertools-commons/lib/esm/config/ConfigService.js
var ConfigService = class {
};

// node_modules/aws-lambda-powertools-commons/lib/esm/config/EnvironmentVariablesService.js
var EnvironmentVariablesService = class extends ConfigService {
  constructor() {
    super(...arguments);
    this.serviceNameVariable = "POWERTOOLS_SERVICE_NAME";
    this.xRayTraceIdVariable = "_X_AMZN_TRACE_ID";
  }
  /**
   * It returns the value of an environment variable that has given name.
   *
   * @param {string} name
   * @returns {string}
   */
  get(name) {
    return process.env[name]?.trim() || "";
  }
  /**
   * It returns the value of the POWERTOOLS_SERVICE_NAME environment variable.
   *
   * @returns {string}
   */
  getServiceName() {
    return this.get(this.serviceNameVariable);
  }
  /**
   * It returns the value of the _X_AMZN_TRACE_ID environment variable.
   *
   * The AWS X-Ray Trace data available in the environment variable has this format:
   * `Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1`,
   *
   * The actual Trace ID is: `1-5759e988-bd862e3fe1be46a994272793`.
   *
   * @returns {string}
   */
  getXrayTraceId() {
    const xRayTraceId = this.get(this.xRayTraceIdVariable);
    if (xRayTraceId === "")
      return void 0;
    return xRayTraceId.split(";")[0].replace("Root=", "");
  }
};

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/esm/formatter/LogFormatter.js
var LogFormatter = class {
  /**
   * It formats a given Error parameter.
   *
   * @param {Error} error
   * @returns {LogAttributes}
   */
  formatError(error) {
    return {
      name: error.name,
      location: this.getCodeLocation(error.stack),
      message: error.message,
      stack: error.stack
    };
  }
  /**
   * It formats a date into a string in simplified extended ISO format (ISO 8601).
   *
   * @param {Date} now
   * @returns {string}
   */
  formatTimestamp(now) {
    return now.toISOString();
  }
  /**
   * It returns a string containing the location of an error, given a particular stack trace.
   *
   * @param stack
   * @returns {string}
   */
  getCodeLocation(stack) {
    if (!stack) {
      return "";
    }
    const stackLines = stack.split("\n");
    const regex = /\((.*):(\d+):(\d+)\)\\?$/;
    let i;
    for (i = 0; i < stackLines.length; i++) {
      const match = regex.exec(stackLines[i]);
      if (Array.isArray(match)) {
        return `${match[1]}:${Number(match[2])}`;
      }
    }
    return "";
  }
};

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/esm/formatter/PowertoolLogFormatter.js
var PowertoolLogFormatter = class extends LogFormatter {
  /**
   * It formats key-value pairs of log attributes.
   *
   * @param {UnformattedAttributes} attributes
   * @returns {PowertoolLog}
   */
  formatAttributes(attributes) {
    return {
      cold_start: attributes.lambdaContext?.coldStart,
      function_arn: attributes.lambdaContext?.invokedFunctionArn,
      function_memory_size: attributes.lambdaContext?.memoryLimitInMB,
      function_name: attributes.lambdaContext?.functionName,
      function_request_id: attributes.lambdaContext?.awsRequestId,
      level: attributes.logLevel,
      message: attributes.message,
      sampling_rate: attributes.sampleRateValue,
      service: attributes.serviceName,
      timestamp: this.formatTimestamp(attributes.timestamp),
      xray_trace_id: attributes.xRayTraceId
    };
  }
};

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/esm/log/LogItem.js
var import_lodash = __toESM(require_lodash(), 1);
var LogItem = class {
  constructor(params) {
    this.attributes = {};
    this.addAttributes(params.baseAttributes);
    this.addAttributes(params.persistentAttributes);
  }
  addAttributes(attributes) {
    this.attributes = (0, import_lodash.default)(this.attributes, attributes);
    return this;
  }
  getAttributes() {
    return this.attributes;
  }
  prepareForPrint() {
    this.setAttributes(this.removeEmptyKeys(this.getAttributes()));
  }
  removeEmptyKeys(attributes) {
    const newAttributes = {};
    for (const key in attributes) {
      if (attributes[key] !== void 0 && attributes[key] !== "" && attributes[key] !== null) {
        newAttributes[key] = attributes[key];
      }
    }
    return newAttributes;
  }
  setAttributes(attributes) {
    this.attributes = attributes;
  }
};

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/esm/Logger.js
var import_lodash2 = __toESM(require_lodash(), 1);

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/esm/config/EnvironmentVariablesService.js
var EnvironmentVariablesService2 = class extends EnvironmentVariablesService {
  constructor() {
    super(...arguments);
    this.awsRegionVariable = "AWS_REGION";
    this.currentEnvironmentVariable = "ENVIRONMENT";
    this.devModeVariable = "POWERTOOLS_DEV";
    this.functionNameVariable = "AWS_LAMBDA_FUNCTION_NAME";
    this.functionVersionVariable = "AWS_LAMBDA_FUNCTION_VERSION";
    this.logEventVariable = "POWERTOOLS_LOGGER_LOG_EVENT";
    this.logLevelVariable = "LOG_LEVEL";
    this.memoryLimitInMBVariable = "AWS_LAMBDA_FUNCTION_MEMORY_SIZE";
    this.sampleRateValueVariable = "POWERTOOLS_LOGGER_SAMPLE_RATE";
  }
  /**
   * It returns the value of the AWS_REGION environment variable.
   *
   * @returns {string}
   */
  getAwsRegion() {
    return this.get(this.awsRegionVariable);
  }
  /**
   * It returns the value of the ENVIRONMENT environment variable.
   *
   * @returns {string}
   */
  getCurrentEnvironment() {
    return this.get(this.currentEnvironmentVariable);
  }
  /**
   * It returns the value of the AWS_LAMBDA_FUNCTION_MEMORY_SIZE environment variable.
   *
   * @returns {string}
   */
  getFunctionMemory() {
    const value = this.get(this.memoryLimitInMBVariable);
    return Number(value);
  }
  /**
   * It returns the value of the AWS_LAMBDA_FUNCTION_NAME environment variable.
   *
   * @returns {string}
   */
  getFunctionName() {
    return this.get(this.functionNameVariable);
  }
  /**
   * It returns the value of the AWS_LAMBDA_FUNCTION_VERSION environment variable.
   *
   * @returns {string}
   */
  getFunctionVersion() {
    return this.get(this.functionVersionVariable);
  }
  /**
   * It returns the value of the POWERTOOLS_LOGGER_LOG_EVENT environment variable.
   *
   * @returns {boolean}
   */
  getLogEvent() {
    const value = this.get(this.logEventVariable);
    return this.isValueTrue(value);
  }
  /**
   * It returns the value of the LOG_LEVEL environment variable.
   *
   * @returns {string}
   */
  getLogLevel() {
    return this.get(this.logLevelVariable);
  }
  /**
   * It returns the value of the POWERTOOLS_LOGGER_SAMPLE_RATE environment variable.
   *
   * @returns {string|undefined}
   */
  getSampleRateValue() {
    const value = this.get(this.sampleRateValueVariable);
    return value && value.length > 0 ? Number(value) : void 0;
  }
  /**
   * It returns true if the POWERTOOLS_DEV environment variable is set to truthy value.
   *
   * @returns {boolean}
   */
  isDevMode() {
    const value = this.get(this.devModeVariable);
    return this.isValueTrue(value);
  }
  /**
   * It returns true if the string value represents a boolean true value.
   *
   * @param {string} value
   * @returns boolean
   */
  isValueTrue(value) {
    const truthyValues = ["1", "y", "yes", "t", "true", "on"];
    return truthyValues.includes(value.toLowerCase());
  }
};

// node_modules/aws-lambda-powertools-logger-with-esm-commons/lib/esm/Logger.js
var Logger = class extends Utility {
  /**
   * It initializes the Logger class with an optional set of options (settings).
   * *
   * @param {ConstructorOptions} options
   */
  constructor(options = {}) {
    super();
    this.logEvent = false;
    this.logIndentation = 0;
    this.logLevelThresholds = {
      DEBUG: 8,
      INFO: 12,
      WARN: 16,
      ERROR: 20,
      SILENT: 24
    };
    this.logsSampled = false;
    this.persistentLogAttributes = {};
    this.powertoolLogData = {};
    this.setOptions(options);
  }
  /**
   * It adds the current Lambda function's invocation context data to the powertoolLogData property of the instance.
   * This context data will be part of all printed log items.
   *
   * @param {Context} context
   * @returns {void}
   */
  addContext(context) {
    const lambdaContext = {
      invokedFunctionArn: context.invokedFunctionArn,
      coldStart: this.getColdStart(),
      awsRequestId: context.awsRequestId,
      memoryLimitInMB: Number(context.memoryLimitInMB),
      functionName: context.functionName,
      functionVersion: context.functionVersion
    };
    this.addToPowertoolLogData({
      lambdaContext
    });
  }
  /**
   * It adds the given attributes (key-value pairs) to all log items generated by this Logger instance.
   *
   * @param {LogAttributes} attributes
   * @returns {void}
   */
  addPersistentLogAttributes(attributes) {
    (0, import_lodash2.default)(this.persistentLogAttributes, attributes);
  }
  /**
   * Alias for addPersistentLogAttributes.
   *
   * @param {LogAttributes} attributes
   * @returns {void}
   */
  appendKeys(attributes) {
    this.addPersistentLogAttributes(attributes);
  }
  /**
   * It creates a separate Logger instance, identical to the current one
   * It's possible to overwrite the new instance options by passing them.
   *
   * @param {ConstructorOptions} options
   * @returns {Logger}
   */
  createChild(options = {}) {
    const parentsOptions = {
      logLevel: this.getLogLevel(),
      customConfigService: this.getCustomConfigService(),
      logFormatter: this.getLogFormatter()
    };
    const parentsPowertoolsLogData = this.getPowertoolLogData();
    const childLogger = new Logger((0, import_lodash2.default)(parentsOptions, parentsPowertoolsLogData, options));
    const parentsPersistentLogAttributes = this.getPersistentLogAttributes();
    childLogger.addPersistentLogAttributes(parentsPersistentLogAttributes);
    if (parentsPowertoolsLogData.lambdaContext) {
      childLogger.addContext(parentsPowertoolsLogData.lambdaContext);
    }
    return childLogger;
  }
  /**
   * It prints a log item with level DEBUG.
   *
   * @param {LogItemMessage} input
   * @param {Error | LogAttributes | string} extraInput
   * @returns {void}
   */
  debug(input, ...extraInput) {
    this.processLogItem("DEBUG", input, extraInput);
  }
  /**
   * It prints a log item with level ERROR.
   *
   * @param {LogItemMessage} input
   * @param {Error | LogAttributes | string} extraInput
   * @returns {void}
   */
  error(input, ...extraInput) {
    this.processLogItem("ERROR", input, extraInput);
  }
  /**
   * It returns a boolean value. True means that the Lambda invocation events
   * are printed in the logs.
   *
   * @returns {boolean}
   */
  getLogEvent() {
    return this.logEvent;
  }
  /**
   * It returns a boolean value, if true all the logs will be printed.
   *
   * @returns {boolean}
   */
  getLogsSampled() {
    return this.logsSampled;
  }
  /**
   * It returns the persistent log attributes, which are the attributes
   * that will be logged in all log items.
   *
   * @private
   * @returns {LogAttributes}
   */
  getPersistentLogAttributes() {
    return this.persistentLogAttributes;
  }
  /**
   * It prints a log item with level INFO.
   *
   * @param {LogItemMessage} input
   * @param {Error | LogAttributes | string} extraInput
   * @returns {void}
   */
  info(input, ...extraInput) {
    this.processLogItem("INFO", input, extraInput);
  }
  /**
   * Method decorator that adds the current Lambda function context as extra
   * information in all log items.
   *
   * The decorator can be used only when attached to a Lambda function handler which
   * is written as method of a class, and should be declared just before the handler declaration.
   *
   * Note: Currently TypeScript only supports decorators on classes and methods. If you are using the
   * function syntax, you should use the middleware instead.
   *
   * @example
   * ```typescript
   * import { Logger } from '@aws-lambda-powertools/logger';
   * import { LambdaInterface } from '@aws-lambda-powertools/commons';
   *
   * const logger = new Logger();
   *
   * class Lambda implements LambdaInterface {
   *     // Decorate your handler class method
   *     @logger.injectLambdaContext()
   *     public async handler(_event: any, _context: any): Promise<void> {
   *         logger.info('This is an INFO log with some context');
   *     }
   * }
   *
   * const handlerClass = new Lambda();
   * export const handler = handlerClass.handler.bind(handlerClass);
   * ```
   *
   * @see https://www.typescriptlang.org/docs/handbook/decorators.html#method-decorators
   * @returns {HandlerMethodDecorator}
   */
  injectLambdaContext(options) {
    return (_target, _propertyKey, descriptor) => {
      const originalMethod = descriptor.value;
      const loggerRef = this;
      descriptor.value = async function(event, context, callback) {
        let initialPersistentAttributes = {};
        if (options && options.clearState === true) {
          initialPersistentAttributes = { ...loggerRef.getPersistentLogAttributes() };
        }
        Logger.injectLambdaContextBefore(loggerRef, event, context, options);
        let result;
        try {
          result = await originalMethod.apply(this, [event, context, callback]);
        } catch (error) {
          throw error;
        } finally {
          Logger.injectLambdaContextAfterOrOnError(loggerRef, initialPersistentAttributes, options);
        }
        return result;
      };
    };
  }
  static injectLambdaContextAfterOrOnError(logger2, initialPersistentAttributes, options) {
    if (options && options.clearState === true) {
      logger2.setPersistentLogAttributes(initialPersistentAttributes);
    }
  }
  static injectLambdaContextBefore(logger2, event, context, options) {
    logger2.addContext(context);
    let shouldLogEvent = void 0;
    if (options && options.hasOwnProperty("logEvent")) {
      shouldLogEvent = options.logEvent;
    }
    logger2.logEventIfEnabled(event, shouldLogEvent);
  }
  /**
   * Logs a Lambda invocation event, if it *should*.
   *
   ** @param {unknown} event
   * @param {boolean} [overwriteValue]
   * @returns {void}
   */
  logEventIfEnabled(event, overwriteValue) {
    if (!this.shouldLogEvent(overwriteValue)) {
      return;
    }
    this.info("Lambda invocation event", { event });
  }
  /**
   * If the sample rate feature is enabled, the calculation that determines whether the logs
   * will actually be printed or not for this invocation is done when the Logger class is
   * initialized.
   * This method will repeat that calculation (with possible different outcome).
   *
   * @returns {void}
   */
  refreshSampleRateCalculation() {
    this.setLogsSampled();
  }
  /**
   * Alias for removePersistentLogAttributes.
   *
   * @param {string[]} keys
   * @returns {void}
   */
  removeKeys(keys) {
    this.removePersistentLogAttributes(keys);
  }
  /**
   * It removes attributes based on provided keys to all log items generated by this Logger instance.
   *
   * @param {string[]} keys
   * @returns {void}
   */
  removePersistentLogAttributes(keys) {
    keys.forEach((key) => {
      if (this.persistentLogAttributes && key in this.persistentLogAttributes) {
        delete this.persistentLogAttributes[key];
      }
    });
  }
  /**
   * It sets the given attributes (key-value pairs) to all log items generated by this Logger instance.
   * Note: this replaces the pre-existing value.
   *
   * @param {LogAttributes} attributes
   * @returns {void}
   */
  setPersistentLogAttributes(attributes) {
    this.persistentLogAttributes = attributes;
  }
  /**
   * It sets the user-provided sample rate value.
   *
   * @param {number} [sampleRateValue]
   * @returns {void}
   */
  setSampleRateValue(sampleRateValue) {
    this.powertoolLogData.sampleRateValue = sampleRateValue || this.getCustomConfigService()?.getSampleRateValue() || this.getEnvVarsService().getSampleRateValue();
  }
  /**
   * It checks whether the current Lambda invocation event should be printed in the logs or not.
   *
   * @private
   * @param {boolean} [overwriteValue]
   * @returns {boolean}
   */
  shouldLogEvent(overwriteValue) {
    if (typeof overwriteValue === "boolean") {
      return overwriteValue;
    }
    return this.getLogEvent();
  }
  /**
   * It prints a log item with level WARN.
   *
   * @param {LogItemMessage} input
   * @param {Error | LogAttributes | string} extraInput
   * @returns {void}
   */
  warn(input, ...extraInput) {
    this.processLogItem("WARN", input, extraInput);
  }
  /**
   * It stores information that is printed in all log items.
   *
   * @param {Partial<PowertoolLogData>} attributesArray
   * @private
   * @returns {void}
   */
  addToPowertoolLogData(...attributesArray) {
    attributesArray.forEach((attributes) => {
      (0, import_lodash2.default)(this.powertoolLogData, attributes);
    });
  }
  /**
   * It processes a particular log item so that it can be printed to stdout:
   * - Merges ephemeral log attributes with persistent log attributes (printed for all logs) and additional info;
   * - Formats all the log attributes;
   *
   * @private
   * @param {LogLevel} logLevel
   * @param {LogItemMessage} input
   * @param {LogItemExtraInput} extraInput
   * @returns {LogItem}
   */
  createAndPopulateLogItem(logLevel, input, extraInput) {
    const unformattedBaseAttributes = (0, import_lodash2.default)({
      logLevel,
      timestamp: /* @__PURE__ */ new Date(),
      message: typeof input === "string" ? input : input.message,
      xRayTraceId: this.envVarsService.getXrayTraceId()
    }, this.getPowertoolLogData());
    const logItem = new LogItem({
      baseAttributes: this.getLogFormatter().formatAttributes(unformattedBaseAttributes),
      persistentAttributes: this.getPersistentLogAttributes()
    });
    if (typeof input !== "string") {
      logItem.addAttributes(input);
    }
    extraInput.forEach((item) => {
      const attributes = item instanceof Error ? { error: item } : typeof item === "string" ? { extra: item } : item;
      logItem.addAttributes(attributes);
    });
    return logItem;
  }
  /**
   * It returns the custom config service, an abstraction used to fetch environment variables.
   *
   * @private
   * @returns {ConfigServiceInterface | undefined}
   */
  getCustomConfigService() {
    return this.customConfigService;
  }
  /**
   * It returns the instance of a service that fetches environment variables.
   *
   * @private
   * @returns {EnvironmentVariablesService}
   */
  getEnvVarsService() {
    return this.envVarsService;
  }
  /**
   * It returns the instance of a service that formats the structure of a
   * log item's keys and values in the desired way.
   *
   * @private
   * @returns {LogFormatterInterface}
   */
  getLogFormatter() {
    return this.logFormatter;
  }
  /**
   * It returns the log level set for the Logger instance.
   *
   * Even though logLevel starts as undefined, it will always be set to a value
   * during the Logger instance's initialization. So, we can safely use the non-null
   * assertion operator here.
   *
   * @private
   * @returns {LogLevel}
   */
  getLogLevel() {
    return this.logLevel;
  }
  /**
   * It returns information that will be added in all log item by
   * this Logger instance (different from user-provided persistent attributes).
   *
   * @private
   * @returns {LogAttributes}
   */
  getPowertoolLogData() {
    return this.powertoolLogData;
  }
  /**
   * When the data added in the log item contains object references or BigInt values,
   * `JSON.stringify()` can't handle them and instead throws errors:
   * `TypeError: cyclic object value` or `TypeError: Do not know how to serialize a BigInt`.
   * To mitigate these issues, this method will find and remove all cyclic references and convert BigInt values to strings.
   *
   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#exceptions
   * @private
   */
  getReplacer() {
    const references = /* @__PURE__ */ new WeakSet();
    return (key, value) => {
      let item = value;
      if (item instanceof Error) {
        item = this.getLogFormatter().formatError(item);
      }
      if (typeof item === "bigint") {
        return item.toString();
      }
      if (typeof item === "object" && value !== null) {
        if (references.has(item)) {
          return;
        }
        references.add(item);
      }
      return item;
    };
  }
  /**
   * It returns the numeric sample rate value.
   *
   * @private
   * @returns {number}
   */
  getSampleRateValue() {
    if (!this.powertoolLogData.sampleRateValue) {
      this.setSampleRateValue();
    }
    return this.powertoolLogData.sampleRateValue;
  }
  /**
   * It returns true and type guards the log level if a given log level is valid.
   *
   * @param {LogLevel} logLevel
   * @private
   * @returns {boolean}
   */
  isValidLogLevel(logLevel) {
    return typeof logLevel === "string" && logLevel in this.logLevelThresholds;
  }
  /**
   * It prints a given log with given log level.
   *
   * @param {LogLevel} logLevel
   * @param {LogItem} log
   * @private
   */
  printLog(logLevel, log) {
    log.prepareForPrint();
    const consoleMethod = logLevel.toLowerCase();
    this.console[consoleMethod](JSON.stringify(log.getAttributes(), this.getReplacer(), this.logIndentation));
  }
  /**
   * It prints a given log with given log level.
   *
   * @param {Uppercase<LogLevel>} logLevel
   * @param {LogItemMessage} input
   * @param {LogItemExtraInput} extraInput
   * @private
   */
  processLogItem(logLevel, input, extraInput) {
    if (!this.shouldPrint(logLevel)) {
      return;
    }
    this.printLog(logLevel, this.createAndPopulateLogItem(logLevel, input, extraInput));
  }
  /**
   * It initializes console property as an instance of the internal version of Console() class (PR #748)
   * or as the global node console if the `POWERTOOLS_DEV' env variable is set and has truthy value.
   *
   * @private
   * @returns {void}
   */
  setConsole() {
    if (!this.getEnvVarsService().isDevMode()) {
      this.console = new Console({ stdout: process.stdout, stderr: process.stderr });
    } else {
      this.console = console;
    }
  }
  /**
   * Sets the Logger's customer config service instance, which will be used
   * to fetch environment variables.
   *
   * @private
   * @param {ConfigServiceInterface} customConfigService
   * @returns {void}
   */
  setCustomConfigService(customConfigService) {
    this.customConfigService = customConfigService ? customConfigService : void 0;
  }
  /**
   * Sets the Logger's custom config service instance, which will be used
   * to fetch environment variables.
   *
   * @private
   * @param {ConfigServiceInterface} customConfigService
   * @returns {void}
   */
  setEnvVarsService() {
    this.envVarsService = new EnvironmentVariablesService2();
  }
  /**
   * If the log event feature is enabled via env variable, it sets a property that tracks whether
   * the event passed to the Lambda function handler should be logged or not.
   *
   * @private
   * @returns {void}
   */
  setLogEvent() {
    if (this.getEnvVarsService().getLogEvent()) {
      this.logEvent = true;
    }
  }
  /**
   * It sets the log formatter instance, in charge of giving a custom format
   * to the structured logs
   *
   * @private
   * @param {LogFormatterInterface} logFormatter
   * @returns {void}
   */
  setLogFormatter(logFormatter) {
    this.logFormatter = logFormatter || new PowertoolLogFormatter();
  }
  /**
   * If the `POWERTOOLS_DEV' env variable is set,
   * it adds JSON indentation for pretty printing logs.
   *
   * @private
   * @returns {void}
   */
  setLogIndentation() {
    if (this.getEnvVarsService().isDevMode()) {
      this.logIndentation = 4;
    }
  }
  /**
   * It sets the Logger's instance log level.
   *
   * @private
   * @param {LogLevel} logLevel
   * @returns {void}
   */
  setLogLevel(logLevel) {
    const constructorLogLevel = logLevel?.toUpperCase();
    if (this.isValidLogLevel(constructorLogLevel)) {
      this.logLevel = constructorLogLevel;
      return;
    }
    const customConfigValue = this.getCustomConfigService()?.getLogLevel()?.toUpperCase();
    if (this.isValidLogLevel(customConfigValue)) {
      this.logLevel = customConfigValue;
      return;
    }
    const envVarsValue = this.getEnvVarsService()?.getLogLevel()?.toUpperCase();
    if (this.isValidLogLevel(envVarsValue)) {
      this.logLevel = envVarsValue;
      return;
    }
    this.logLevel = Logger.defaultLogLevel;
  }
  /**
   * If the sample rate feature is enabled, it sets a property that tracks whether this Lambda function invocation
   * will print logs or not.
   *
   * @private
   * @returns {void}
   */
  setLogsSampled() {
    const sampleRateValue = this.getSampleRateValue();
    this.logsSampled = sampleRateValue !== void 0 && (sampleRateValue === 1 || Math.random() < sampleRateValue);
  }
  /**
   * It configures the Logger instance settings that will affect the Logger's behaviour
   * and the content of all logs.
   *
   * @private
   * @param {ConstructorOptions} options
   * @returns {Logger}
   */
  setOptions(options) {
    const { logLevel, serviceName, sampleRateValue, logFormatter, customConfigService, persistentLogAttributes, environment } = options;
    this.setEnvVarsService();
    this.setConsole();
    this.setCustomConfigService(customConfigService);
    this.setLogLevel(logLevel);
    this.setSampleRateValue(sampleRateValue);
    this.setLogsSampled();
    this.setLogFormatter(logFormatter);
    this.setPowertoolLogData(serviceName, environment);
    this.setLogEvent();
    this.setLogIndentation();
    this.addPersistentLogAttributes(persistentLogAttributes);
    return this;
  }
  /**
   * It adds important data to the Logger instance that will affect the content of all logs.
   *
   * @param {string} serviceName
   * @param {Environment} environment
   * @param {LogAttributes} persistentLogAttributes
   * @private
   * @returns {void}
   */
  setPowertoolLogData(serviceName, environment, persistentLogAttributes = {}) {
    this.addToPowertoolLogData({
      awsRegion: this.getEnvVarsService().getAwsRegion(),
      environment: environment || this.getCustomConfigService()?.getCurrentEnvironment() || this.getEnvVarsService().getCurrentEnvironment(),
      sampleRateValue: this.getSampleRateValue(),
      serviceName: serviceName || this.getCustomConfigService()?.getServiceName() || this.getEnvVarsService().getServiceName() || this.getDefaultServiceName()
    }, persistentLogAttributes);
  }
  /**
   * It checks whether the current log item should/can be printed.
   *
   * @param {Uppercase<LogLevel>} logLevel
   * @private
   * @returns {boolean}
   */
  shouldPrint(logLevel) {
    if (this.logLevelThresholds[logLevel] >= this.logLevelThresholds[this.getLogLevel()]) {
      return true;
    }
    return this.getLogsSampled();
  }
};
Logger.defaultLogLevel = "INFO";

// commons-esm.mts
var logger = new Logger();
var lambdaHandler = async (event, context) => {
  logger.info("Lambda invocation event", { event });
  logger.appendKeys({
    awsRequestId: context.awsRequestId
  });
  let response;
  try {
    response = {
      statusCode: 200,
      body: JSON.stringify({
        message: "hello from ESM version of logger with ESM version of Commons"
      })
    };
    logger.info(
      `Successful response from API enpoint: ${event.path}`,
      response.body
    );
  } catch (err) {
    response = {
      statusCode: 500,
      body: JSON.stringify({
        message: "Some error happened"
      })
    };
    logger.error(
      `Error response from API enpoint: ${event.path}`,
      response.body
    );
  }
  return response;
};
export {
  lambdaHandler
};


What to look at in the source code?

Basically, CommonJS has all the dead code such as samples, pay attention to the comments paths like this:

// node_modules/aws-lambda-powertools-commons/lib/cjs/samples/resources/contexts/hello-world.js

But ES bundle eliminated all the dead code, you can see it by the size and the actual code under the spoiler.

It also tree shake when you build an app as CJS but with import for logger in your app code. It is resulted in 55.3kb (a bit more). That's why I stated that it is tree-shaking by default in the pros section.

The script and the approach to generate package.json for subdirectories

I want to add some more info about package.json generation with the script with module types:

When I was researching I came across this approach and tools that use it when you want to mix typescript and emit as pure .js files for both modules. Node.js docs say that it is better to explicitly use "type" for your modules.

But it's a simple task to generate files with the script without bringing any dependency or a bundler.

Conclusion

I hope this additional information will help to make a better decision on how to serve the PowerTools library. If you have any questions or concerns, feel free to ask. I think we should have a section in the docs with recommendations on how to serve and tree-shake apps with PowerTools for TypeScript.

Please join the discussion on this if you use the library and have any feedback.

shdq avatar Mar 22 '23 16:03 shdq

Hi @shdq, thank you so much for kicking things off with the initial proof of concept and also for providing such an exhaustive prospective on the implications and potential objections.

The numbers shown on Logger are promising and even though there is a slight size increase in terms of output, these are outweighed by the benefits that ESM brings and the use cases it unlocks.

This is not a decision that we are taking lightly as we know that in a compute environment like Lambda every byte counts. However we must acknowledge that without ESM, use cases like top-level async/await are simply not possible. In addition to that, the performance benchmarks at runtime that you have shared, show that there are slight performance improvements at runtime.

As you have mentioned in the arguments in favor of this solution, tree-shaking appears to compensate for most of the size increase. For this reason, the rollout of this feature will be accompanied by new documentation around best practices for bundling and tree-shaking your functions. Additionally, it's important to mention that even though we don't have a timeline, this is a temporary solution, as the Node.js ecosystem will eventually move towards ESM and potentially deprecate CJS.

So to sum up: the proposal of using dual bundling appears a valid one and the benchmarks show positive results. We have mitigation plans for the areas that show less positive results and a plan for the rollout that we will share later on, once we have a clearer picture.

In terms of next steps, it would be ideal to see a similar experiment with benchmarks for all other Powertools utilities. This will help understand whether the new bundling will have effects in line with the ones we are seeing for Logger.

Again, thank you on behalf of myself and all users of Powertools for this amazing work Sergei!

dreamorosi avatar Apr 03 '23 17:04 dreamorosi

Hello 👋

I discovered a small improvement in our take on at double packaging.

Spotify introduced their TypeScript SDK for Web API and they serve it as both ESM and CommonJS.

I looked inside and instead of generating a separate package.json with the script as I suggested before to have both:

  • cjs/package.json with {"type":"commonjs"}
  • esm/package.json with {"type":"module"}

They simply copy it from a folder (content is the same all the time):

"build": "npm run build:cjs && npm run build:mjs",
"build:mjs": "tsc --project tsconfig.mjs.json && cp res/package.mjs.json dist/mjs/package.json",
"build:cjs": "tsc --project tsconfig.cjs.json && cp res/package.cjs.json dist/cjs/package.json",

I found it a better decision since no extra script is required.

Details: spotify-web-api-ts-sdk

@dreamorosi any updates on this issue and the next major release?

shdq avatar Jul 07 '23 11:07 shdq

Hi @shdq apologies for the long delay in my response, I meant to give you a proper answer but failed to do so in a reasonable amount of time.

With two utilities now in public beta (Idempotency and Batch Processing), and the next ones (see Roadmap) in RFC stage we can finally turn our focus on this topic and on v2 of Powertools for AWS (TypeScript) as a whole.

In the coming weeks we'll continue the work that you have started and finalize a viable build config to add support for ESM before the end of the year.

As soon as I have more news to share I'll make sure to do so under this thread.

dreamorosi avatar Aug 02 '23 16:08 dreamorosi

Hey Folks!

Just checking in on the status of this Issue. I've recently switched from using variations of Pino to using Powertools, but the lack of ESM support is hard blocker on using Powertools with ES Modules in Lambda.

I'm happy to take on the work to get shipping of dual packages. I've done it a few times before, and written about migrating from CommonJs to ES Modules.

The actual change to build and ship ES modules is pretty quick. The approach of the AWS SDK v3 team is generally accepted as best practice for shipping dual module packages.

  • Two tsconfig.json files, one per module system
  • Two build scripts, one for each module system, both called by a single build script
  • main field in package.json refers to the CommonJS build
  • module field in package.json refers to the ESM build

Where there could be challenges is with downstream dependencies that the libraries use. If any use CommonJs, it might break an ES build, the caveat being "might". Based on the experiments by @shdq I don't think thats an issue.

Let me know if you want assistance.

Ant

antstanley avatar Aug 21 '23 22:08 antstanley

Hey @antstanley, if you're bundling your code, you can add a banner like import module from 'module';if (typeof globalThis.require === "undefined"){globalThis.require = module.createRequire(import.meta.url);} to allow using CJS in ESM code. I agree that powertools should provide an ESM distribution, but that's a workaround for now.

bestickley avatar Aug 22 '23 13:08 bestickley

Hi both, first of all thank you for the continued interest in this feature.

I've mentioned this in the past in a couple of places but since the discussion about the topic has been going on for a while I'd like to take a second to explain why it has been taking so long.

Even though each utility is published independently, we strive to offer a cohesive experience throughout all utilities. In the case of Tracer, which is backed by the AWS X-Ray SDK, we were unable to find a path forward due to the SDK not supporting ESM bundling.

We could have enabled ESM support for all other utilities except for Tracer, but we decided against this to avoid having features supported only by some utilities. At the same time, we made the decision to prioritize new utilities (Idempotency & Batch Processing) over this feature also hoping that the situation upstream would change.

The X-Ray SDK still doesn't support ESM bundling, however the workaround proposed by @bestickley seems promising and I was able to run a function that uses the current Tracer & X-Ray SDK bundled as ESM with esbuild. Thanks to this I think we can finally move forward with the topic.

To manage expectations, we still plan on releasing ESM support as part of a v2 release. The reasoning behind this is that due to CJS+ESM dual bundling we might have to change some of the imports/exports thus increasing the likelihood of a breaking change.

We committed to include three areas in our v2 release (see roadmap):

  • one has already been merged in the feat/v2 branch (Logging providers)
  • another is supporting TypeScript v5.x - still ongoing
  • and the third is ESM bundling

@antstanley thank you for the generous offer, we would be incredibly happy to accept your offer 🎉

Based on your experience, do you think it'd be possible to approach this conversion one utility at the time? Our codebase is a monorepo but each utility (under packages/*) has its own set of tsconfig.json files. We already have a v2 branch so my initial suggestion would be to have each utility in a separate PR.

This would help with keep the blast radius contained, have faster reviews, and also allow us to potentially work on replicating the changes from the first PR to other utility in parallel.

What do you think?

dreamorosi avatar Aug 22 '23 15:08 dreamorosi

Hey folks! Apologies for going quiet on this. Had other things come up. I've got time now to work on this. I'll do a PR on the v2 branch with a potential way to implement this. Doing a little more investigation using the exports property of package.json to implement conditional exports could be a good way to do this vs main and module field, (or maybe do all to satisfy all bundlers/environments)

Conditional Exports: https://nodejs.org/api/packages.html#conditional-exports

antstanley avatar Oct 11 '23 10:10 antstanley

Hi @antstanley, thanks for the offer and don't worry about the timing.

I have already started working on this and have a working branch where I was able to implement this the hybrid bundling on at least two of the modules (commons & tracer).

I have a couple of issues to fix but after that I should be able to open a PR, which I expect to complete this week.

Would you be open to help review the PR instead?

dreamorosi avatar Oct 11 '23 12:10 dreamorosi

@dreamorosi Github notifications are letting me down today! I've created a PR here #1734 , forked from your v2 branch.

As in the PR description, it's there mainly as a discussion point.

It fully works, and all tests pass. It's also isolated, so can use that approach on a package by package basis. It also doesn't require commons to have an ESM build.

Probably spent more time fixing the tests and getting the jest / ts-jest config right.

antstanley avatar Oct 11 '23 14:10 antstanley

All the modules have now been converted to esm + cjs on the feat/v2 branch.

In the next days I'll work on creating an alpha release channel so that you can start testing the new version and let us know if there's any issue with the way things are built.

Once again, a huge thank you to everyone who interacted with the issue and helped push this forward - especially @shdq and @antstanley.

dreamorosi avatar Oct 12 '23 16:10 dreamorosi

⚠️ COMMENT VISIBILITY WARNING ⚠️

Comments on closed issues are hard for our team to see. If you need more assistance, please either tag a team member or open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.

github-actions[bot] avatar Oct 12 '23 16:10 github-actions[bot]