diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..4a3973967 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +.github/workflows/ @aws-solutions/sb-csne \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index ffc769f27..f5dcf3850 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -8,6 +8,7 @@ **Checklist** +- [ ] :wave: I have added unit tests for all code changes. - [ ] :wave: I have run the unit tests, and all unit tests have passed. - [ ] :warning: This pull request might incur a breaking change. diff --git a/.gitignore b/.gitignore index ff9a9a40b..a13a99296 100755 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ **/dist **/global-s3-assets **/regional-s3-assets +**/staging **/open-source **/.zip **/tmp @@ -9,18 +10,25 @@ # dependencies **/node_modules +**/modules # test assets **/coverage +**/coverage-reports **/.nyc_output +**/mock-* # misc **/npm-debug.log **/testem.log -**/package-lock.json **/.vscode/settings.json demo-ui-config.js +.temp_redpencil +bom.json # System Files **/.DS_Store **/.vscode +**/.idea +build +dist diff --git a/CHANGELOG.md b/CHANGELOG.md index 350513e8b..06253dbba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,123 +1,458 @@ # Change Log + All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [7.0.6] - 2025-07-28 + +### Security +- Bump `form-data` to mitigate [CVE-2025-7783](https://github.com/advisories/GHSA-fjxv-7rqg-78g4) + +## [7.0.5] - 2025-07-07 + +### Changed + +- Migrated to [AWS SDK for JavaScript v3](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/introduction/) +- Bundling instruction for sharp module as per [cross-platform installation instruction](https://sharp.pixelplumbing.com/install/#npm-v10) +- Bumped dependencies + +### Fixed + +- Return image metadata for all cases by default using [withMetadata()](https://sharp.pixelplumbing.com/api-output/#withmetadata) +- In thumbor-styled requests, align `filters:rotate()` with sharp [rotate behavior](https://sharp.pixelplumbing.com/api-operation/#rotate), if no angle is provided `autoOrient()` will be called + +## [7.0.4] - 2025-06-09 + +### Security + +- Bump `tar-fs` to mitigate [CVE-2025-48387](https://avd.aquasec.com/nvd/cve-2025-48387) +- Bump `aws-cdk-lib` to mitigate [GHSA-5pq3-h73f-66hr](https://github.com/advisories/GHSA-5pq3-h73f-66hr) + +## [7.0.3] - 2025-05-10 + +### Fixed + +- `SOLUTION_VERSION` environment variable in metrics lambda construct + +## [7.0.2] - 2025-04-09 + +### Security + +- Bump aws-cdk-lib to `2.188.0` +- Update solution metrics lambda runtime to `nodejs22.x` + +## [7.0.1] - 2025-03-14 + +### Changed + +- Updated metrics module to support identical metrics for different resources +- Updated aws-cdk-lib and aws-cdk package versions + +### Security + +- Upgraded esbuild to v0.25.0 for advisory [GHSA-67mh-4wv8-2f99](https://github.com/advisories/GHSA-67mh-4wv8-2f99) +- Upgraded axios to 1.8.2 for advisory [GHSA-jr5f-v2jv-69x6](https://github.com/axios/axios/security/advisories/GHSA-jr5f-v2jv-69x6) + +### Fixed + +- Minor eslint warnings + +## [7.0.0] - 2025-01-27 + +### Changed + +- Location of API Gateway infrastructure resources +- **Breaking** New condition on API gateway will cause a delete/create of ApiGateway::Deployment on stack update +- **Breaking:** Exception thrown on invalid resize parameters [#463](https://github.com/aws-solutions/serverless-image-handler/pull/463) +- Code formatting to align with ESLint rules +- **Breaking** Reduced passthrough of errors from external APIs to response body. Errors will still be logged. +- Modified CloudFront logging bucket to have versioning enabled by default +- CloudFront behaviour to redirect http requests to https rather than throwing forbidden error +- Set-Cookie was added to list of deny-listed response headers +- Name of solution from Serverless Image Handler on AWS to Dynamic Image Transformation for Amazon CloudFront. + +### Added + +- Ability to enable origin shield through a deployment parameter +- Ability to deploy solution without creating a CloudFront distribution +- CloudFront function to normalize accept headers when AutoWebP is enabled +- Alternative infrastructure using S3 Object Lambda to overcome 6 MB response size limit +- Query param named expires which can be used to define when a generated image should no longer be accessible +- Ability to include smart_crop as a filter for Thumbor style requests, taking advantage of AWS Rekognition face cropping +- Ability to set CloudWatch log retention period to Infinite +- Ability to specify Sharp input image size limit [#465](https://github.com/aws-solutions/serverless-image-handler/issues/465) [#476](https://github.com/aws-solutions/serverless-image-handler/pull/476) +- Query parameter based image editing [#184](https://github.com/aws-solutions/serverless-image-handler/issues/184) +- Query parameter normalization to improve cache hit rate +- CloudWatch dashboard to improve Solution observability +- Additional anonymized metrics to help understand how the solution is being used, identify areas of improvement, and drive future roadmap decisions. + +### Removed + +- Accept header being used in cache policy when AutoWebP is disabled + +### Fixed + +- Broken URLs in Signature and Fallback Image template parameters + +## [6.3.3] - 2024-12-27 + +### Fixed + +- Overlays not checking for valid S3 buckets +- Failures when updating deployments created in version 6.1.0 and prior [#559](https://github.com/aws-solutions/serverless-image-handler/issues/559) + +### Security + +- Added allowlist on sharp operations. [Info](https://docs.aws.amazon.com/solutions/latest/serverless-image-handler/create-and-use-image-requests.html#restricted-operations) +- Added deny list on custom headers for base64 encoded requests. [Info](https://docs.aws.amazon.com/solutions/latest/serverless-image-handler/create-and-use-image-requests.html#include-custom-response-headers) +- Added inference of Content-Type header if S3 Metadata provides an unsupported value + +## [6.3.2] - 2024-11-22 + +### Fixed + +- Upgrade cross-spawn to v7.0.6 for vulnerability [CVE-2024-9506](https://github.com/advisories/GHSA-5j4c-8p2g-v4jx) + +## [6.3.1] - 2024-10-02 + +### Fixed + +- Base-64 encoded overlayWith call requiring strings in top/left options rather than numbers +- CloudFront anonymized metrics missing for deployments outside of us-east-1 + +## [6.3.0] - 2024-09-09 + +### Added + +- Additional anonymized metrics system to help understand how the solution is being used, identify areas of improvement, and drive future roadmap decisions. + +### Changed + +- Cdk update to 2.151.0 +- Default log retention to 180 days +- Cache-control header on fallback images to use (in order of priority), fallback image metadata, header provided in image request, and default cache control [#563](https://github.com/aws-solutions/serverless-image-handler/issues/563) + +### Security + +- Upgraded micromatch to v4.0.8 for vulnerability CVE-2024-4067 + +## [6.2.7] - 2024-08-19 + +### Security + +- Upgraded axios to v1.7.4 for vulnerability CVE-2024-39338 + +## [6.2.6] - 2024-06-27 + +### Added + +- StackId tag to CloudFrontLoggingBucket and its bucket name as a CfnOutput [#529](https://github.com/aws-solutions/serverless-image-handler/issues/529) +- Test case to verify UTF-8 support in object key [#320](https://github.com/aws-solutions/serverless-image-handler/pull/320) +- Test cases to verify crop functionality [#459](https://github.com/aws-solutions/serverless-image-handler/pull/459) +- VERSION.txt and build script change to auto-update local package versions +- S3:bucket-name tag for defining which source bucket to use in thumbor style requests [#521](https://github.com/aws-solutions/serverless-image-handler/pull/521) +- Ability to override whether an image should be animated [#456](https://github.com/aws-solutions/serverless-image-handler/issues/456) +- Support for 8-bit depth AVIF image type inference [#360](https://github.com/aws-solutions/serverless-image-handler/issues/360) + +### Changed + +- Decreased permissions allotted to CustomResource Lambda and ImageHandler Lambda +- cdk update to 2.124.0 +- aws-solutions-constructs update to 2.51.0 +- SourceBucketsParameter to require explicit bucket names +- Demo-ui dependency update +- Demo-ui to be a package and manage script/stylesheet dependencies through NPM +- Modified JPEG SOI marker parsing to only check first 2 bytes [#429] + +### Security + +- Upgraded follow-redirects to v1.15.6 for vulnerability CVE-2024-28849 +- Upgraded braces to v3.0.3 for vulnerability CVE-2024-4068 + +### Removed + +- Unused CopyS3Assets custom resource + +### Fixed + +- Some error messages indicating incorrect file types +- Solution version and id not being passed to Backend Lambda +- Thumbor-style URL matching being overly permissive + +## [6.2.5] - 2024-01-03 + +### Fixed + +- Ensure accurate image metadata when generating Amazon Rekognition compatible images [#374](https://github.com/aws-solutions/serverless-image-handler/issues/374) +- Exclude demo-ui-config from being deleted upon BucketDeployment update sync when updating to a new version + +### Changed + +- Overlay requests with an overlay image with one or both dimensions greater than the base image now returns a 400 bad request status with the message "Image to overlay must have same dimensions or smaller", previously returned a 500 internal error [#405](https://github.com/aws-solutions/serverless-image-handler/issues/405) +- cdk update to 2.118.0 +- typescript update to 5.3.3 +- GIF files without multiple pages are now treated as non-animated, allowing all filters to be used on them [#460](https://github.com/aws-solutions/serverless-image-handler/issues/460) + +### Security + +- Upgraded axios to v1.6.5 for vulnerability CVE-2023-26159 + +## [6.2.4] - 2023-12-06 + +### Changed + +- node 20.x Lambda runtimes +- cdk update to 2.111.0 +- disable gzip compression in cloudfront cache option to improve cache hit ratio [#373](https://github.com/aws-solutions/serverless-image-handler/pull/373) +- requests for webp images supported for upper/lower case Accept header [#490](https://github.com/aws-solutions/serverless-image-handler/pull/490) +- changed axios version to 1.6.2 for github dependabot reported vulnerability CVE-2023-45857 +- enabled thumbor filter chaining [#343](https://github.com/aws-solutions/serverless-image-handler/issues/343) + +## [6.2.3] - 2023-10-20 + +### Fixed + +- Fixing Security Vulnerabilities + +### Changed + +- Updated the versions of multiple dependencies + +## [6.2.2] - 2023-09-29 + +### Changed + +- Update package.json Author +- Modify some license headers to maintain consistency + +### Security + +- Upgraded sharp to v0.32.6 for vulnerability CVE-2023-4863 +- Upgraded outdated NPM packages + +## [6.2.1] - 2023-08-03 + +### Fixed + +- Template fails to deploy unless demo UI is enabled [#499](https://github.com/aws-solutions/serverless-image-handler/issues/499) +- Thumbor requests of images without a file extension would fail +- CloudFormation template description was not being generated + +### Changed + +- Upgraded build requirement to Node 16 + +## [6.2.0] - 2023-08-01 + +### Added + +- Add `cdk-helper` module to help with packaging cdk generated assets in solutions internal pipelines +- Use [DefaultStackSynthesizer](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.DefaultStackSynthesizer.html) with different configurations to generate template for `cdk deploy` and on internal solutions pipeline +- Add esbuild bundler for lambda functions using `NodejsFunction`, reference [aws_lambda_nodejs](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda_nodejs-readme.html) +- Refactor pipeline scripts +- Changes semver dependency version to 7.5.2 for github reported vulnerability CVE-2022-25883 +- Changes word-wrap dependency version to aashutoshrathi/word-wrap for github reported vulnerability CVE-2023-26115 + +## [6.1.2] - 2023-04-14 + +### Changed + +- added s3 bucket ownership control permission and ownership parameter to S3 logging bucket to account for [changes in S3 default behavior](https://docs.aws.amazon.com/AmazonS3/latest/userguide/create-bucket-faq.html) +- changed xml2js version to 0.5.0 for github dependabot reported vulnerability CVE-2023-0842 + +## [6.1.1] - 2023-02-09 + +### Added + +- package-lock.json for all modules [#426](https://github.com/aws-solutions/serverless-image-handler/pull/426) +- github workflows for running unit test, eslint and prettier formatting, cdk nag, security scans [#402](https://github.com/aws-solutions/serverless-image-handler/pull/402) +- demo-ui unicode support [#416](https://github.com/aws-solutions/serverless-image-handler/issues/416) +- support for multiple cloudformation stack deployments in the same region [#438](https://github.com/aws-solutions/serverless-image-handler/pull/438) + +### Changed + +- axios version update to 1.2.3 [#425](https://github.com/aws-solutions/serverless-image-handler/pull/425) +- json5 version update to 1.0.2 [#428](https://github.com/aws-solutions/serverless-image-handler/pull/428) +- CodeQL advisory resolutions +- contributing guidelines + +## [6.1.0] - 2022-11-10 + +### Added + +- gif support +- tif support +- AWS Service Catalog AppRegistry + +### Changed + +- package version updates +- CDK v2 migration +- node 16.x Lambda runtimes + +## [6.0.0] - 2021-12-21 + +### Changed + +- **Note that** Version 6.0.0 does not support upgrading from previous versions due to the update that uses the AWS CDK to generate the AWS CloudFormation template. + +### Added + +- Crop feature in Thumbor URLs: [#202](https://github.com/aws-solutions/serverless-image-handler/pull/202) +- TypeScript typings: [#293](https://github.com/aws-solutions/serverless-image-handler/issues/293) +- Reduction effort support: [#289](https://github.com/aws-solutions/serverless-image-handler/issues/289) +- Allow custom requests for keys without file extensions: [#273](https://github.com/aws-solutions/serverless-image-handler/issues/273) + +### Fixed + +- Unexpected behavior after adding support for images without extension: [#307](https://github.com/aws-solutions/serverless-image-handler/issues/307) +- Quality filter does not work with format filter (thumbor): [#266](https://github.com/aws-solutions/serverless-image-handler/issues/266) +- Auto WebP activated, `Content-Type: image/webp` returned, but still it's JPG encoded: [#305](https://github.com/aws-solutions/serverless-image-handler/issues/305) +- `inferImageType` doesn't support binary/octet-stream but not application/octet-stream: [#306](https://github.com/aws-solutions/serverless-image-handler/issues/306) +- SmartCrop boundary exceeded: [#263](https://github.com/aws-solutions/serverless-image-handler/issues/263) +- Custom rewrite does not work without file extensions: [#268](https://github.com/aws-solutions/serverless-image-handler/issues/268) +- Secrets manager cost issue: [#291](https://github.com/aws-solutions/serverless-image-handler/issues/291) +- `inferImageType` is slow: [#303](https://github.com/aws-solutions/serverless-image-handler/issues/303) +- If the file name contain `()`,the API will return 404,NoSuchKey,The specified key does not exist: [#299](https://github.com/aws-solutions/serverless-image-handler/issues/299) +- `fit-in` segment in URL path generates 404: [#281](https://github.com/aws-solutions/serverless-image-handler/issues/281) +- `overlayWith` top/left return int after percent conversion: [#276](https://github.com/aws-solutions/serverless-image-handler/issues/276) + ## [5.2.0] - 2021-01-29 + ### Added -- Support for ap-east-1 and me-south-1 regions: [#192](https://github.com/awslabs/serverless-image-handler/issues/192), [#228](https://github.com/awslabs/serverless-image-handler/issues/228), [#232](https://github.com/awslabs/serverless-image-handler/issues/232) + +- Support for ap-east-1 and me-south-1 regions: [#192](https://github.com/aws-solutions/serverless-image-handler/issues/192), [#228](https://github.com/aws-solutions/serverless-image-handler/issues/228), [#232](https://github.com/aws-solutions/serverless-image-handler/issues/232) - Unit tests for custom-resource: `100%` coverage -- Cloudfront cache policy and origin request policy: [#229](https://github.com/awslabs/serverless-image-handler/issues/229) -- Circular cropping feature: [#214](https://github.com/awslabs/serverless-image-handler/issues/214), [#216](https://github.com/awslabs/serverless-image-handler/issues/216) +- Cloudfront cache policy and origin request policy: [#229](https://github.com/aws-solutions/serverless-image-handler/issues/229) +- Circular cropping feature: [#214](https://github.com/aws-solutions/serverless-image-handler/issues/214), [#216](https://github.com/aws-solutions/serverless-image-handler/issues/216) - Unit tests for image-handler: `100%` coverage -- Support for files without extension on thumbor requests: [#169](https://github.com/awslabs/serverless-image-handler/issues/169), [#188](https://github.com/awslabs/serverless-image-handler/issues/188) -- Inappropriate content detection feature: [#243](https://github.com/awslabs/serverless-image-handler/issues/243) +- Support for files without extension on thumbor requests: [#169](https://github.com/aws-solutions/serverless-image-handler/issues/169), [#188](https://github.com/aws-solutions/serverless-image-handler/issues/188) +- Inappropriate content detection feature: [#243](https://github.com/aws-solutions/serverless-image-handler/issues/243) - Unit tests for image-request: `100%` coverage ### Fixed -- Graceful failure when no faces are detected using smartCrop and fail on resizing before smartCrop: [#132](https://github.com/awslabs/serverless-image-handler/issues/132), [#133](https://github.com/awslabs/serverless-image-handler/issues/133) -- Broken SVG returned if no edits specified and Auto-WebP enabled: [#247](https://github.com/awslabs/serverless-image-handler/issues/247) -- Removed "--recursive" from README.md: [#255](https://github.com/awslabs/serverless-image-handler/pull/255) -- fixed issue with failure on resize if width or height is float: [#254](https://github.com/awslabs/serverless-image-handler/issues/254) -### Changed +- Graceful failure when no faces are detected using smartCrop and fail on resizing before smartCrop: [#132](https://github.com/aws-solutions/serverless-image-handler/issues/132), [#133](https://github.com/aws-solutions/serverless-image-handler/issues/133) +- Broken SVG returned if no edits specified and Auto-WebP enabled: [#247](https://github.com/aws-solutions/serverless-image-handler/issues/247) +- Removed "--recursive" from README.md: [#255](https://github.com/aws-solutions/serverless-image-handler/pull/255) +- fixed issue with failure on resize if width or height is float: [#254](https://github.com/aws-solutions/serverless-image-handler/issues/254) + +### Changed + - Constructs test template for constructs unit test: `100%` coverage ## [5.1.0] - 2020-11-19 -### ⚠ BREAKING CHANGES -- **Image URL Signature**: When image URL signature is enabled, all URLs including existing URLs should have `signature` query parameter. ### Added -- Image URL signature: [#111](https://github.com/awslabs/serverless-image-handler/issues/111), [#203](https://github.com/awslabs/serverless-image-handler/issues/203), [#221](https://github.com/awslabs/serverless-image-handler/issues/221), [#227](https://github.com/awslabs/serverless-image-handler/pull/227) -- AWS Lambda `413` error handling. When the response payload is bigger than 6MB, it throws `TooLargeImageException`: [#35](https://github.com/awslabs/serverless-image-handler/issues/35), [#97](https://github.com/awslabs/serverless-image-handler/issues/97), [#193](https://github.com/awslabs/serverless-image-handler/issues/193), [#204](https://github.com/awslabs/serverless-image-handler/issues/204) -- Default fallback image: [#137](https://github.com/awslabs/serverless-image-handler/issues/137) + +- Image URL signature: [#111](https://github.com/aws-solutions/serverless-image-handler/issues/111), [#203](https://github.com/aws-solutions/serverless-image-handler/issues/203), [#221](https://github.com/aws-solutions/serverless-image-handler/issues/221), [#227](https://github.com/aws-solutions/serverless-image-handler/pull/227) +- AWS Lambda `413` error handling. When the response payload is bigger than 6MB, it throws `TooLargeImageException`: [#35](https://github.com/aws-solutions/serverless-image-handler/issues/35), [#97](https://github.com/aws-solutions/serverless-image-handler/issues/97), [#193](https://github.com/aws-solutions/serverless-image-handler/issues/193), [#204](https://github.com/aws-solutions/serverless-image-handler/issues/204) +- Default fallback image: [#137](https://github.com/aws-solutions/serverless-image-handler/issues/137) - Unit tests for custom resource: `100%` coverage -- Add `SVG` support. When any edits are used, the output would be automatically `PNG` unless the output format is specified: [#31](https://github.com/awslabs/serverless-image-handler/issues/31), [#234](https://github.com/awslabs/serverless-image-handler/issues/234) -- Custom headers: [#182](https://github.com/awslabs/serverless-image-handler/pull/182) -- Enabling ALB Support : [#201](https://github.com/awslabs/serverless-image-handler/pull/201) +- Add `SVG` support. When any edits are used, the output would be automatically `PNG` unless the output format is specified: [#31](https://github.com/aws-solutions/serverless-image-handler/issues/31), [#234](https://github.com/aws-solutions/serverless-image-handler/issues/234) +- Custom headers: [#182](https://github.com/aws-solutions/serverless-image-handler/pull/182) +- Enabling ALB Support : [#201](https://github.com/aws-solutions/serverless-image-handler/pull/201) ### Fixed -- Thumbor paths broken if they include "-" and "100x100": [#208](https://github.com/awslabs/serverless-image-handler/issues/208) -- Rewrite doesn't seem to be working: [#121](https://github.com/awslabs/serverless-image-handler/issues/121) -- Correct EXIF: [#197](https://github.com/awslabs/serverless-image-handler/issues/197), [#220](https://github.com/awslabs/serverless-image-handler/issues/220), [#235](https://github.com/awslabs/serverless-image-handler/issues/235), [#236](https://github.com/awslabs/serverless-image-handler/issues/236), [#240](https://github.com/awslabs/serverless-image-handler/issues/240) -- Sub folder support in Thumbor `watermark` filter: [#231](https://github.com/awslabs/serverless-image-handler/issues/231) + +- Thumbor paths broken if they include "-" and "100x100": [#208](https://github.com/aws-solutions/serverless-image-handler/issues/208) +- Rewrite doesn't seem to be working: [#121](https://github.com/aws-solutions/serverless-image-handler/issues/121) +- Correct EXIF: [#197](https://github.com/aws-solutions/serverless-image-handler/issues/197), [#220](https://github.com/aws-solutions/serverless-image-handler/issues/220), [#235](https://github.com/aws-solutions/serverless-image-handler/issues/235), [#236](https://github.com/aws-solutions/serverless-image-handler/issues/236), [#240](https://github.com/aws-solutions/serverless-image-handler/issues/240) +- Sub folder support in Thumbor `watermark` filter: [#231](https://github.com/aws-solutions/serverless-image-handler/issues/231) ### Changed + - AWS CDK and AWS Solutions Constructs version (from 1.57.0 to 1.64.1) - sharp base version (from 0.25.4 to 0.26.1) - Refactors the custom resource Lambda source code - Migrate unit tests to use `jest` -- Move all `aws-sdk` in `ImageHandler` Labmda function to `index.js` for the best practice -- Enhance the default error message not to show empty JSON: [#206](https://github.com/awslabs/serverless-image-handler/issues/206) +- Move all `aws-sdk` in `ImageHandler` Lambda function to `index.js` for the best practice +- Enhance the default error message not to show empty JSON: [#206](https://github.com/aws-solutions/serverless-image-handler/issues/206) +- **Image URL Signature**: When image URL signature is enabled, all URLs including existing URLs should have `signature` query parameter. ### Removed + - Remove `manifest-generator` ## [5.0.0] - 2020-08-31 + ### Added + - AWS CDK and AWS Solutions Constructs to create AWS CloudFormation template ### Fixed -- Auto WebP does not work properly: [#195](https://github.com/awslabs/serverless-image-handler/pull/195), [#200](https://github.com/awslabs/serverless-image-handler/issues/200), [#205](https://github.com/awslabs/serverless-image-handler/issues/205) -- A bug where base64 encoding containing slash: [#194](https://github.com/awslabs/serverless-image-handler/pull/194) + +- Auto WebP does not work properly: [#195](https://github.com/aws-solutions/serverless-image-handler/pull/195), [#200](https://github.com/aws-solutions/serverless-image-handler/issues/200), [#205](https://github.com/aws-solutions/serverless-image-handler/issues/205) +- A bug where base64 encoding containing slash: [#194](https://github.com/aws-solutions/serverless-image-handler/pull/194) - Thumbor issues: - - `0` size support: [#183](https://github.com/awslabs/serverless-image-handler/issues/183) - - `convolution` filter does not work: [#187](https://github.com/awslabs/serverless-image-handler/issues/187) - - `fill` filter does not work: [#190](https://github.com/awslabs/serverless-image-handler/issues/190) -- __Note that__ duplicated features has been merged gracefully. + - `0` size support: [#183](https://github.com/aws-solutions/serverless-image-handler/issues/183) + - `convolution` filter does not work: [#187](https://github.com/aws-solutions/serverless-image-handler/issues/187) + - `fill` filter does not work: [#190](https://github.com/aws-solutions/serverless-image-handler/issues/190) +- **Note that** duplicated features has been merged gracefully. ### Removed + - AWS CloudFormation template: `serverless-image-handler.template` ### Changed + - sharp base version (from 0.23.4 to 0.25.4) -- Remove `Promise` to return since `async` functions return promises: [#189](https://github.com/awslabs/serverless-image-handler/issues/189) +- Remove `Promise` to return since `async` functions return promises: [#189](https://github.com/aws-solutions/serverless-image-handler/issues/189) - Unit test statement coverage improvement: - `image-handler.js`: `79.05%` to `100%` - `image-request.js`: `93.58%` to `100%` - `thumbor-mapping.js`: `99.29%` to `100%` - `overall`: `91.55%` to `100%` -## [4.2] - 2020-02-06 +## [4.2.0] - 2020-02-06 + ### Added -- Honor outputFormat Parameter from the pull request [#117](https://github.com/awslabs/serverless-image-handler/pull/117) -- Support serving images under s3 subdirectories, Fix to make /fit-in/ work; Fix for VipsJpeg: Invalid SOS error plus several other critical fixes from the pull request [#130](https://github.com/awslabs/serverless-image-handler/pull/130) -- Allow regex in SOURCE_BUCKETS for environment variable from the pull request [#138](https://github.com/awslabs/serverless-image-handler/pull/138) -- Fix build script on other platforms from the pull request [#139](https://github.com/awslabs/serverless-image-handler/pull/139) -- Add Cache-Control response header from the pull request [#151](https://github.com/awslabs/serverless-image-handler/pull/151) -- Add AUTO_WEBP option to automatically serve WebP if the client supports it from the pull request [#152](https://github.com/awslabs/serverless-image-handler/pull/152) -- Use HTTP 404 & forward Cache-Control, Content-Type, Expires, and Last-Modified headers from S3 from the pull request [#158](https://github.com/awslabs/serverless-image-handler/pull/158) -- fix: DeprecationWarning: Buffer() is deprecated from the pull request [#174](https://github.com/awslabs/serverless-image-handler/pull/174) -- Add hex color support for Thumbor ```filters:background_color``` and ```filters:fill``` [#154](https://github.com/awslabs/serverless-image-handler/issues/154) -- Add format and watermark support for Thumbor [#109](https://github.com/awslabs/serverless-image-handler/issues/109), [#131](https://github.com/awslabs/serverless-image-handler/issues/131), [#109](https://github.com/awslabs/serverless-image-handler/issues/142) -- __Note that__ duplicated features has been merged gracefully. + +- Honor outputFormat Parameter from the pull request [#117](https://github.com/aws-solutions/serverless-image-handler/pull/117) +- Support serving images under s3 subdirectories, Fix to make /fit-in/ work; Fix for VipsJpeg: Invalid SOS error plus several other critical fixes from the pull request [#130](https://github.com/aws-solutions/serverless-image-handler/pull/130) +- Allow regex in SOURCE_BUCKETS for environment variable from the pull request [#138](https://github.com/aws-solutions/serverless-image-handler/pull/138) +- Fix build script on other platforms from the pull request [#139](https://github.com/aws-solutions/serverless-image-handler/pull/139) +- Add Cache-Control response header from the pull request [#151](https://github.com/aws-solutions/serverless-image-handler/pull/151) +- Add AUTO_WEBP option to automatically serve WebP if the client supports it from the pull request [#152](https://github.com/aws-solutions/serverless-image-handler/pull/152) +- Use HTTP 404 & forward Cache-Control, Content-Type, Expires, and Last-Modified headers from S3 from the pull request [#158](https://github.com/aws-solutions/serverless-image-handler/pull/158) +- fix: DeprecationWarning: Buffer() is deprecated from the pull request [#174](https://github.com/aws-solutions/serverless-image-handler/pull/174) +- Add hex color support for Thumbor `filters:background_color` and `filters:fill` [#154](https://github.com/aws-solutions/serverless-image-handler/issues/154) +- Add format and watermark support for Thumbor [#109](https://github.com/aws-solutions/serverless-image-handler/issues/109), [#131](https://github.com/aws-solutions/serverless-image-handler/issues/131), [#109](https://github.com/aws-solutions/serverless-image-handler/issues/142) +- **Note that** duplicated features has been merged gracefully. ### Changed + - sharp base version (from 0.23.3 to 0.23.4) -- Image handler Amazon CloudFront distribution ```DefaultCacheBehavior.ForwaredValues.Header``` to ```["Origin", "Accept"]``` for webp -- Image resize process change for ```filters:no_upscale()``` handling by ```withoutEnlargement``` edit key [#144](https://github.com/awslabs/serverless-image-handler/issues/144) +- Image handler Amazon CloudFront distribution `DefaultCacheBehavior.ForwardedValues.Header` to `["Origin", "Accept"]` for WebP +- Image resize process change for `filters:no_upscale()` handling by `withoutEnlargement` edit key [#144](https://github.com/aws-solutions/serverless-image-handler/issues/144) ### Fixed -- Add and fix Cache-control, Content-Type, Expires, and Last-Modified headers to response: [#103](https://github.com/awslabs/serverless-image-handler/issues/103), [#107](https://github.com/awslabs/serverless-image-handler/issues/107), [#120](https://github.com/awslabs/serverless-image-handler/issues/120) -- Fix Amazon S3 bucket subfolder issue: [#106](https://github.com/awslabs/serverless-image-handler/issues/106), [#112](https://github.com/awslabs/serverless-image-handler/issues/112), [#119](https://github.com/awslabs/serverless-image-handler/issues/119), [#123](https://github.com/awslabs/serverless-image-handler/issues/123), [#167](https://github.com/awslabs/serverless-image-handler/issues/167), [#175](https://github.com/awslabs/serverless-image-handler/issues/175) -- Fix HTTP status code for missing images from 500 to 404: [#159](https://github.com/awslabs/serverless-image-handler/issues/159) -- Fix European character in filename issue: [#149](https://github.com/awslabs/serverless-image-handler/issues/149) -- Fix image scaling issue for filename containing 'x' character: [#163](https://github.com/awslabs/serverless-image-handler/issues/163), [#176](https://github.com/awslabs/serverless-image-handler/issues/176) -- Fix regular expression issue: [#114](https://github.com/awslabs/serverless-image-handler/issues/114), [#121](https://github.com/awslabs/serverless-image-handler/issues/121), [#125](https://github.com/awslabs/serverless-image-handler/issues/125) -- Fix not working quality parameter: [#129](https://github.com/awslabs/serverless-image-handler/issues/129) -## [4.1] - 2019-12-31 +- Add and fix Cache-control, Content-Type, Expires, and Last-Modified headers to response: [#103](https://github.com/aws-solutions/serverless-image-handler/issues/103), [#107](https://github.com/aws-solutions/serverless-image-handler/issues/107), [#120](https://github.com/aws-solutions/serverless-image-handler/issues/120) +- Fix Amazon S3 bucket subfolder issue: [#106](https://github.com/aws-solutions/serverless-image-handler/issues/106), [#112](https://github.com/aws-solutions/serverless-image-handler/issues/112), [#119](https://github.com/aws-solutions/serverless-image-handler/issues/119), [#123](https://github.com/aws-solutions/serverless-image-handler/issues/123), [#167](https://github.com/aws-solutions/serverless-image-handler/issues/167), [#175](https://github.com/aws-solutions/serverless-image-handler/issues/175) +- Fix HTTP status code for missing images from 500 to 404: [#159](https://github.com/aws-solutions/serverless-image-handler/issues/159) +- Fix European character in filename issue: [#149](https://github.com/aws-solutions/serverless-image-handler/issues/149) +- Fix image scaling issue for filename containing 'x' character: [#163](https://github.com/aws-solutions/serverless-image-handler/issues/163), [#176](https://github.com/aws-solutions/serverless-image-handler/issues/176) +- Fix regular expression issue: [#114](https://github.com/aws-solutions/serverless-image-handler/issues/114), [#121](https://github.com/aws-solutions/serverless-image-handler/issues/121), [#125](https://github.com/aws-solutions/serverless-image-handler/issues/125) +- Fix not working quality parameter: [#129](https://github.com/aws-solutions/serverless-image-handler/issues/129) + +## [4.1.0] - 2019-12-31 + ### Added + - CHANGELOG file - Access logging to API Gateway ### Changed + - Lambda functions runtime to nodejs12.x - sharp version (from 0.21.3 to 0.23.3) -- Image handler function to use Composite API (https://sharp.pixelplumbing.com/en/stable/api-composite/) +- Image handler function to use Composite API () - License to Apache-2.0 ### Removed + - Reference to deprecated sharp function (overlayWith) - Capability to resize images proportionally if width or height is set to 0 (sharp v0.23.1 and later check that the width and height - if present - are positive integers) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 3b6446687..5b627cfa6 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,4 +1,4 @@ ## Code of Conduct -This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). -For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact +This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). +For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact opensource-codeofconduct@amazon.com with any additional questions or comments. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 99f3ecffb..f6e1c633e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,5 @@ # Contributing Guidelines + Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional documentation, we greatly value feedback and contributions from our community. @@ -6,46 +7,54 @@ Please read through this document before submitting any issues or pull requests information to effectively respond to your bug report or contribution. ## Reporting Bugs/Feature Requests + We welcome you to use the GitHub issue tracker to report bugs or suggest features. -When filing an issue, please check [existing open](https://github.com/awslabs/serverless-image-handler/issues), or [recently closed](https://github.com/awslabs/serverless-image-handler/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already -reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: +When filing an issue, please check [existing open](https://github.com/aws-solutions/serverless-image-handler/issues), or [recently closed](https://github.com/aws-solutions/serverless-image-handler/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: -* A reproducible test case or series of steps -* The version of our code being used -* Any modifications you've made relevant to the bug -* Anything unusual about your environment or deployment +- A reproducible test case or series of steps +- The version of our code being used +- The region being used +- Any modifications you've made relevant to the bug +- Anything unusual about your environment or deployment ## Contributing via Pull Requests + Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: -1. You are working against the latest source on the *master* branch. -2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. -3. You open an issue to discuss any significant work - we would hate for your time to be wasted. +1. You are working against the latest source on the _main_ branch. +1. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. +1. You open an issue to discuss any significant work - we would hate for your time to be wasted. To send us a pull request, please: 1. Fork the repository. -2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. -3. Ensure local tests pass. -4. Commit to your fork using clear commit messages. -5. Send us a pull request, answering any default questions in the pull request interface. -6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. +1. Modify the source; please focus on the specific change you are contributing. +1. Add new unit tests for the new code. +1. Run _npx npm run prettier-format_ in _source_ to ensure that code format standards are maintained. +1. If your changes include new capabilities, include in the PR description text that can be folded into the solution documentation. +1. Commit to your fork using clear commit messages. +1. In your repository _Security_ section, ensure that security advisories are enabled and address any Dependabot issues that appear. +1. Send us a pull request, answering any default questions in the pull request interface. +1. If the changes are complex or may involve additional communication, we may create a feature branch specific to your PR and ask you to rebase using that branch. -GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and -[creating a pull request](https://help.github.com/articles/creating-a-pull-request/). +GitHub provides additional documentation on [forking a repository](https://help.github.com/articles/fork-a-repo/) and [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). ## Finding contributions to work on -Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/awslabs/serverless-image-handler/labels/help%20wanted) issues is a great place to start. + +Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/aws-solutions/serverless-image-handler/labels/help%20wanted) issues is a great place to start. ## Code of Conduct + This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). -For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact -opensource-codeofconduct@amazon.com with any additional questions or comments. +For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact opensource-codeofconduct@amazon.com with any additional questions or comments. ## Security issue notifications + If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. ## Licensing -See the [LICENSE](https://github.com/awslabs/serverless-image-handler/blob/master/LICENSE.txt) file for our project's licensing. We will ask you to confirm the licensing of your contribution. -We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. + +See the [LICENSE](https://github.com/aws-solutions/serverless-image-handler/blob/main/LICENSE.txt) file for our project's licensing. We will ask you to confirm the licensing of your contribution. + +We may ask you to sign a [Contributor License Agreement (CLA)](https://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. diff --git a/LICENSE.txt b/LICENSE similarity index 100% rename from LICENSE.txt rename to LICENSE diff --git a/NOTICE b/NOTICE new file mode 100644 index 000000000..0708790f8 --- /dev/null +++ b/NOTICE @@ -0,0 +1,759 @@ +Dynamic Image Transformation for Amazon CloudFront + +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +Licensed under the Apache License Version 2.0 (the "License"). You may not use this file except +in compliance with the License. A copy of the License is located at http://www.apache.org/licenses/ +or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the +specific language governing permissions and limitations under the License. + +********************** +THIRD PARTY COMPONENTS +********************** + +This software includes third party software subject to the following copyrights: + +jquery under the MIT license. +bootstrap under the MIT license. +@types/node under the MIT license. +undici-types under the MIT license. +@typescript-eslint/eslint-plugin under the MIT license. +@typescript-eslint/parser under the BSD-2-Clause license. +eslint under the MIT license. +eslint-scope under the BSD-2-Clause license. +esrecurse under the BSD-2-Clause license. +estraverse under the BSD-2-Clause license. +@eslint-community/eslint-utils under the MIT license. +eslint-visitor-keys under the Apache-2.0 license. +@eslint-community/regexpp under the MIT license. +@eslint/eslintrc under the MIT license. +ajv under the MIT license. +fast-deep-equal under the MIT license. +fast-json-stable-stringify under the MIT license. +json-schema-traverse under the MIT license. +uri-js under the BSD-2-Clause license. +punycode under the MIT license. +debug under the MIT license. +ms under the MIT license. +espree under the BSD-2-Clause license. +acorn under the MIT license. +acorn-jsx under the MIT license. +globals under the MIT license. +type-fest under the MIT license. +ignore under the MIT license. +import-fresh under the MIT license. +parent-module under the MIT license. +callsites under the MIT license. +resolve-from under the MIT license. +js-yaml under the MIT license. +argparse under the MIT license. +minimatch under the ISC license. +brace-expansion under the MIT license. +balanced-match under the MIT license. +concat-map under the MIT license. +strip-json-comments under the MIT license. +@eslint/js under the MIT license. +@humanwhocodes/config-array under the Apache-2.0 license. +@humanwhocodes/object-schema under the BSD-3-Clause license. +@humanwhocodes/module-importer under the Apache-2.0 license. +@nodelib/fs.walk under the MIT license. +@nodelib/fs.scandir under the MIT license. +@nodelib/fs.stat under the MIT license. +run-parallel under the MIT license. +queue-microtask under the MIT license. +fastq under the ISC license. +reusify under the MIT license. +@ungap/structured-clone under the ISC license. +chalk under the MIT license. +ansi-styles under the MIT license. +color-convert under the MIT license. +color-name under the MIT license. +supports-color under the MIT license. +has-flag under the MIT license. +cross-spawn under the MIT license. +path-key under the MIT license. +shebang-command under the MIT license. +shebang-regex under the MIT license. +which under the ISC license. +isexe under the ISC license. +doctrine under the Apache-2.0 license. +esutils under the BSD-2-Clause license. +escape-string-regexp under the MIT license. +esquery under the BSD-3-Clause license. +file-entry-cache under the MIT license. +flat-cache under the MIT license. +flatted under the ISC license. +keyv under the MIT license. +json-buffer under the MIT license. +rimraf under the ISC license. +glob under the ISC license. +fs.realpath under the ISC license. +inflight under the ISC license. +once under the ISC license. +wrappy under the ISC license. +inherits under the ISC license. +path-is-absolute under the MIT license. +find-up under the MIT license. +locate-path under the MIT license. +p-locate under the MIT license. +p-limit under the MIT license. +yocto-queue under the MIT license. +path-exists under the MIT license. +glob-parent under the ISC license. +is-glob under the MIT license. +is-extglob under the MIT license. +graphemer under the MIT license. +imurmurhash under the MIT license. +is-path-inside under the MIT license. +json-stable-stringify-without-jsonify under the MIT license. +levn under the MIT license. +prelude-ls under the MIT license. +type-check under the MIT license. +lodash.merge under the MIT license. +natural-compare under the MIT license. +optionator under the MIT license. +deep-is under the MIT license. +fast-levenshtein under the MIT license. +word-wrap under the MIT license. +strip-ansi under the MIT license. +ansi-regex under the MIT license. +text-table under the MIT license. +@typescript-eslint/scope-manager under the MIT license. +@typescript-eslint/types under the MIT license. +@typescript-eslint/visitor-keys under the MIT license. +@typescript-eslint/typescript-estree under the BSD-2-Clause license. +globby under the MIT license. +array-union under the MIT license. +dir-glob under the MIT license. +path-type under the MIT license. +fast-glob under the MIT license. +merge2 under the MIT license. +micromatch under the MIT license. +braces under the MIT license. +fill-range under the MIT license. +to-regex-range under the MIT license. +is-number under the MIT license. +picomatch under the MIT license. +slash under the MIT license. +semver under the ISC license. +tsutils under the MIT license. +typescript under the Apache-2.0 license. +tslib under the 0BSD license. +@typescript-eslint/type-utils under the MIT license. +@typescript-eslint/utils under the MIT license. +@types/json-schema under the MIT license. +@types/semver under the MIT license. +natural-compare-lite under the MIT license. +eslint-config-prettier under the MIT license. +eslint-config-standard under the MIT license. +eslint-plugin-import under the MIT license. +@rtsao/scc under the MIT license. +array-includes under the MIT license. +call-bind under the MIT license. +call-bind-apply-helpers under the MIT license. +es-errors under the MIT license. +function-bind under the MIT license. +es-define-property under the MIT license. +get-intrinsic under the MIT license. +es-object-atoms under the MIT license. +get-proto under the MIT license. +dunder-proto under the MIT license. +gopd under the MIT license. +has-symbols under the MIT license. +hasown under the MIT license. +math-intrinsics under the MIT license. +set-function-length under the MIT license. +define-data-property under the MIT license. +has-property-descriptors under the MIT license. +call-bound under the MIT license. +define-properties under the MIT license. +object-keys under the MIT license. +es-abstract under the MIT license. +array-buffer-byte-length under the MIT license. +is-array-buffer under the MIT license. +arraybuffer.prototype.slice under the MIT license. +available-typed-arrays under the MIT license. +possible-typed-array-names under the MIT license. +data-view-buffer under the MIT license. +is-data-view under the MIT license. +is-typed-array under the MIT license. +which-typed-array under the MIT license. +for-each under the MIT license. +is-callable under the MIT license. +has-tostringtag under the MIT license. +data-view-byte-length under the MIT license. +data-view-byte-offset under the MIT license. +es-set-tostringtag under the MIT license. +es-to-primitive under the MIT license. +is-date-object under the MIT license. +is-symbol under the MIT license. +safe-regex-test under the MIT license. +is-regex under the MIT license. +function.prototype.name under the MIT license. +functions-have-names under the MIT license. +get-symbol-description under the MIT license. +globalthis under the MIT license. +has-proto under the MIT license. +internal-slot under the MIT license. +side-channel under the MIT license. +object-inspect under the MIT license. +side-channel-list under the MIT license. +side-channel-map under the MIT license. +side-channel-weakmap under the MIT license. +is-negative-zero under the MIT license. +is-set under the MIT license. +is-shared-array-buffer under the MIT license. +is-string under the MIT license. +is-weakref under the MIT license. +object.assign under the MIT license. +own-keys under the MIT license. +safe-push-apply under the MIT license. +isarray under the MIT license. +regexp.prototype.flags under the MIT license. +set-function-name under the MIT license. +safe-array-concat under the MIT license. +set-proto under the MIT license. +stop-iteration-iterator under the MIT license. +string.prototype.trim under the MIT license. +string.prototype.trimend under the MIT license. +string.prototype.trimstart under the MIT license. +typed-array-buffer under the MIT license. +typed-array-byte-length under the MIT license. +typed-array-byte-offset under the MIT license. +reflect.getprototypeof under the MIT license. +which-builtin-type under the MIT license. +is-async-function under the MIT license. +async-function under the MIT license. +is-finalizationregistry under the MIT license. +is-generator-function under the MIT license. +which-boxed-primitive under the MIT license. +is-bigint under the MIT license. +has-bigints under the MIT license. +is-boolean-object under the MIT license. +is-number-object under the MIT license. +which-collection under the MIT license. +is-map under the MIT license. +is-weakmap under the MIT license. +is-weakset under the MIT license. +typed-array-length under the MIT license. +unbox-primitive under the MIT license. +array.prototype.findlastindex under the MIT license. +es-shim-unscopables under the MIT license. +array.prototype.flat under the MIT license. +array.prototype.flatmap under the MIT license. +eslint-import-resolver-node under the MIT license. +is-core-module under the MIT license. +resolve under the MIT license. +path-parse under the MIT license. +supports-preserve-symlinks-flag under the MIT license. +eslint-module-utils under the MIT license. +object.fromentries under the MIT license. +object.groupby under the MIT license. +object.values under the MIT license. +tsconfig-paths under the MIT license. +@types/json5 under the MIT license. +json5 under the MIT license. +minimist under the MIT license. +strip-bom under the MIT license. +eslint-plugin-n under the MIT license. +builtins under the MIT license. +eslint-plugin-es-x under the MIT license. +eslint-compat-utils under the MIT license. +get-tsconfig under the MIT license. +resolve-pkg-maps under the MIT license. +is-builtin-module under the MIT license. +builtin-modules under the MIT license. +eslint-plugin-promise under the ISC license. +eslint-plugin-header under the MIT license. +eslint-plugin-jsdoc under the BSD-3-Clause license. +@es-joy/jsdoccomment under the MIT license. +comment-parser under the MIT license. +jsdoc-type-pratt-parser under the MIT license. +are-docs-informative under the MIT license. +spdx-expression-parse under the MIT license. +spdx-exceptions under the CC-BY-3.0 license. +spdx-license-ids under the CC0-1.0 license. +eslint-plugin-node under the MIT license. +eslint-plugin-es under the MIT license. +eslint-utils under the MIT license. +regexpp under the MIT license. +eslint-plugin-prettier under the MIT license. +prettier under the MIT license. +prettier-linter-helpers under the MIT license. +fast-diff under the Apache-2.0 license. +@types/jest under the MIT license. +expect under the MIT license. +@jest/expect-utils under the MIT license. +jest-get-type under the MIT license. +jest-matcher-utils under the MIT license. +jest-diff under the MIT license. +diff-sequences under the MIT license. +pretty-format under the MIT license. +@jest/schemas under the MIT license. +@sinclair/typebox under the MIT license. +react-is under the MIT license. +jest-message-util under the MIT license. +@babel/code-frame under the MIT license. +@babel/helper-validator-identifier under the MIT license. +js-tokens under the MIT license. +picocolors under the ISC license. +@jest/types under the MIT license. +@types/istanbul-lib-coverage under the MIT license. +@types/istanbul-reports under the MIT license. +@types/istanbul-lib-report under the MIT license. +@types/yargs under the MIT license. +@types/yargs-parser under the MIT license. +@types/stack-utils under the MIT license. +graceful-fs under the ISC license. +stack-utils under the MIT license. +jest-util under the MIT license. +ci-info under the MIT license. +jest under the MIT license. +@jest/core under the MIT license. +@jest/console under the MIT license. +@jest/reporters under the MIT license. +@bcoe/v8-coverage under the MIT license. +@jest/test-result under the MIT license. +collect-v8-coverage under the MIT license. +@jest/transform under the MIT license. +@babel/core under the MIT license. +@ampproject/remapping under the Apache-2.0 license. +@jridgewell/gen-mapping under the MIT license. +@jridgewell/set-array under the MIT license. +@jridgewell/sourcemap-codec under the MIT license. +@jridgewell/trace-mapping under the MIT license. +@jridgewell/resolve-uri under the MIT license. +@babel/generator under the MIT license. +@babel/parser under the MIT license. +@babel/types under the MIT license. +@babel/helper-string-parser under the MIT license. +jsesc under the MIT license. +@babel/helper-compilation-targets under the MIT license. +@babel/compat-data under the MIT license. +@babel/helper-validator-option under the MIT license. +browserslist under the MIT license. +caniuse-lite under the CC-BY-4.0 license. +electron-to-chromium under the ISC license. +node-releases under the MIT license. +update-browserslist-db under the MIT license. +escalade under the MIT license. +lru-cache under the ISC license. +yallist under the ISC license. +@babel/helper-module-transforms under the MIT license. +@babel/helper-module-imports under the MIT license. +@babel/traverse under the MIT license. +@babel/template under the MIT license. +@babel/helpers under the MIT license. +convert-source-map under the MIT license. +gensync under the MIT license. +babel-plugin-istanbul under the BSD-3-Clause license. +istanbul-lib-instrument under the BSD-3-Clause license. +@istanbuljs/schema under the MIT license. +istanbul-lib-coverage under the BSD-3-Clause license. +@babel/helper-plugin-utils under the MIT license. +@istanbuljs/load-nyc-config under the ISC license. +camelcase under the MIT license. +p-try under the MIT license. +get-package-type under the MIT license. +sprintf-js under the BSD-3-Clause license. +esprima under the BSD-2-Clause license. +test-exclude under the ISC license. +jest-haste-map under the MIT license. +@types/graceful-fs under the MIT license. +anymatch under the ISC license. +normalize-path under the MIT license. +fb-watchman under the Apache-2.0 license. +bser under the Apache-2.0 license. +node-int64 under the MIT license. +jest-regex-util under the MIT license. +jest-worker under the MIT license. +merge-stream under the MIT license. +walker under the Apache-2.0 license. +makeerror under the BSD-3-Clause license. +tmpl under the BSD-3-Clause license. +fsevents under the MIT license. +pirates under the MIT license. +write-file-atomic under the ISC license. +signal-exit under the ISC license. +exit under the MIT license. +istanbul-lib-report under the BSD-3-Clause license. +make-dir under the MIT license. +istanbul-lib-source-maps under the BSD-3-Clause license. +source-map under the BSD-3-Clause license. +istanbul-reports under the BSD-3-Clause license. +html-escaper under the MIT license. +string-length under the MIT license. +char-regex under the MIT license. +v8-to-istanbul under the ISC license. +ansi-escapes under the MIT license. +jest-changed-files under the MIT license. +execa under the MIT license. +get-stream under the MIT license. +human-signals under the Apache-2.0 license. +is-stream under the MIT license. +npm-run-path under the MIT license. +onetime under the MIT license. +mimic-fn under the MIT license. +strip-final-newline under the MIT license. +jest-config under the MIT license. +ts-node under the MIT license. +@cspotcode/source-map-support under the MIT license. +@tsconfig/node10 under the MIT license. +@tsconfig/node12 under the MIT license. +@tsconfig/node14 under the MIT license. +@tsconfig/node16 under the MIT license. +acorn-walk under the MIT license. +arg under the MIT license. +create-require under the MIT license. +diff under the BSD-3-Clause license. +make-error under the ISC license. +v8-compile-cache-lib under the MIT license. +yn under the MIT license. +@jest/test-sequencer under the MIT license. +babel-jest under the MIT license. +@types/babel__core under the MIT license. +@types/babel__generator under the MIT license. +@types/babel__template under the MIT license. +@types/babel__traverse under the MIT license. +babel-preset-jest under the MIT license. +babel-plugin-jest-hoist under the MIT license. +babel-preset-current-node-syntax under the MIT license. +@babel/plugin-syntax-async-generators under the MIT license. +@babel/plugin-syntax-bigint under the MIT license. +@babel/plugin-syntax-class-properties under the MIT license. +@babel/plugin-syntax-class-static-block under the MIT license. +@babel/plugin-syntax-import-attributes under the MIT license. +@babel/plugin-syntax-import-meta under the MIT license. +@babel/plugin-syntax-json-strings under the MIT license. +@babel/plugin-syntax-logical-assignment-operators under the MIT license. +@babel/plugin-syntax-nullish-coalescing-operator under the MIT license. +@babel/plugin-syntax-numeric-separator under the MIT license. +@babel/plugin-syntax-object-rest-spread under the MIT license. +@babel/plugin-syntax-optional-catch-binding under the MIT license. +@babel/plugin-syntax-optional-chaining under the MIT license. +@babel/plugin-syntax-private-property-in-object under the MIT license. +@babel/plugin-syntax-top-level-await under the MIT license. +deepmerge under the MIT license. +jest-circus under the MIT license. +@jest/environment under the MIT license. +@jest/fake-timers under the MIT license. +@sinonjs/fake-timers under the BSD-3-Clause license. +@sinonjs/commons under the BSD-3-Clause license. +type-detect under the MIT license. +jest-mock under the MIT license. +@jest/expect under the MIT license. +jest-snapshot under the MIT license. +@babel/plugin-syntax-jsx under the MIT license. +@babel/plugin-syntax-typescript under the MIT license. +co under the MIT license. +dedent under the MIT license. +is-generator-fn under the MIT license. +jest-each under the MIT license. +jest-runtime under the MIT license. +@jest/globals under the MIT license. +@jest/source-map under the MIT license. +cjs-module-lexer under the MIT license. +jest-resolve under the MIT license. +jest-pnp-resolver under the MIT license. +jest-validate under the MIT license. +leven under the MIT license. +resolve.exports under the MIT license. +pure-rand under the MIT license. +jest-environment-node under the MIT license. +jest-runner under the MIT license. +emittery under the MIT license. +jest-docblock under the MIT license. +detect-newline under the MIT license. +jest-leak-detector under the MIT license. +jest-watcher under the MIT license. +source-map-support under the MIT license. +buffer-from under the MIT license. +parse-json under the MIT license. +error-ex under the MIT license. +is-arrayish under the MIT license. +json-parse-even-better-errors under the MIT license. +lines-and-columns under the MIT license. +jest-resolve-dependencies under the MIT license. +import-local under the MIT license. +pkg-dir under the MIT license. +resolve-cwd under the MIT license. +jest-cli under the MIT license. +create-jest under the MIT license. +prompts under the MIT license. +kleur under the MIT license. +sisteransi under the MIT license. +yargs under the MIT license. +cliui under the ISC license. +string-width under the MIT license. +emoji-regex under the MIT license. +is-fullwidth-code-point under the MIT license. +wrap-ansi under the MIT license. +get-caller-file under the ISC license. +require-directory under the MIT license. +y18n under the ISC license. +yargs-parser under the ISC license. +ts-jest under the MIT license. +bs-logger under the MIT license. +ejs under the Apache-2.0 license. +jake under the Apache-2.0 license. +async under the MIT license. +filelist under the Apache-2.0 license. +lodash.memoize under the MIT license. +@aws-sdk/client-cloudwatch under the Apache-2.0 license. +@aws-crypto/sha256-browser under the Apache-2.0 license. +@smithy/is-array-buffer under the Apache-2.0 license. +@smithy/util-buffer-from under the Apache-2.0 license. +@smithy/util-utf8 under the Apache-2.0 license. +@aws-crypto/sha256-js under the Apache-2.0 license. +@aws-crypto/util under the Apache-2.0 license. +@aws-sdk/types under the Apache-2.0 license. +@smithy/types under the Apache-2.0 license. +@aws-crypto/supports-web-crypto under the Apache-2.0 license. +@aws-sdk/util-locate-window under the Apache-2.0 license. +@aws-sdk/core under the Apache-2.0 license. +@aws-sdk/xml-builder under the Apache-2.0 license. +@smithy/core under the Apache-2.0 license. +@smithy/middleware-serde under the Apache-2.0 license. +@smithy/protocol-http under the Apache-2.0 license. +@smithy/util-base64 under the Apache-2.0 license. +@smithy/util-body-length-browser under the Apache-2.0 license. +@smithy/util-middleware under the Apache-2.0 license. +@smithy/util-stream under the Apache-2.0 license. +@smithy/fetch-http-handler under the Apache-2.0 license. +@smithy/querystring-builder under the Apache-2.0 license. +@smithy/util-uri-escape under the Apache-2.0 license. +@smithy/node-http-handler under the Apache-2.0 license. +@smithy/abort-controller under the Apache-2.0 license. +@smithy/util-hex-encoding under the Apache-2.0 license. +@smithy/node-config-provider under the Apache-2.0 license. +@smithy/property-provider under the Apache-2.0 license. +@smithy/shared-ini-file-loader under the Apache-2.0 license. +@smithy/signature-v4 under the Apache-2.0 license. +@smithy/smithy-client under the Apache-2.0 license. +@smithy/middleware-endpoint under the Apache-2.0 license. +@smithy/url-parser under the Apache-2.0 license. +@smithy/querystring-parser under the Apache-2.0 license. +@smithy/middleware-stack under the Apache-2.0 license. +fast-xml-parser under the MIT license. +strnum under the MIT license. +@aws-sdk/credential-provider-node under the Apache-2.0 license. +@aws-sdk/credential-provider-env under the Apache-2.0 license. +@aws-sdk/credential-provider-http under the Apache-2.0 license. +@aws-sdk/credential-provider-ini under the Apache-2.0 license. +@aws-sdk/credential-provider-process under the Apache-2.0 license. +@aws-sdk/credential-provider-sso under the Apache-2.0 license. +@aws-sdk/client-sso under the Apache-2.0 license. +@aws-sdk/middleware-host-header under the Apache-2.0 license. +@aws-sdk/middleware-logger under the Apache-2.0 license. +@aws-sdk/middleware-recursion-detection under the Apache-2.0 license. +@aws-sdk/middleware-user-agent under the Apache-2.0 license. +@aws-sdk/util-endpoints under the Apache-2.0 license. +@smithy/util-endpoints under the Apache-2.0 license. +@aws-sdk/region-config-resolver under the Apache-2.0 license. +@smithy/util-config-provider under the Apache-2.0 license. +@aws-sdk/util-user-agent-browser under the Apache-2.0 license. +bowser under the MIT license. +@aws-sdk/util-user-agent-node under the Apache-2.0 license. +@smithy/config-resolver under the Apache-2.0 license. +@smithy/hash-node under the Apache-2.0 license. +@smithy/invalid-dependency under the Apache-2.0 license. +@smithy/middleware-content-length under the Apache-2.0 license. +@smithy/middleware-retry under the Apache-2.0 license. +@smithy/service-error-classification under the Apache-2.0 license. +@smithy/util-retry under the Apache-2.0 license. +uuid under the MIT license. +@smithy/util-body-length-node under the Apache-2.0 license. +@smithy/util-defaults-mode-browser under the Apache-2.0 license. +@smithy/util-defaults-mode-node under the Apache-2.0 license. +@smithy/credential-provider-imds under the Apache-2.0 license. +@aws-sdk/token-providers under the Apache-2.0 license. +@aws-sdk/nested-clients under the Apache-2.0 license. +@aws-sdk/credential-provider-web-identity under the Apache-2.0 license. +@smithy/middleware-compression under the Apache-2.0 license. +fflate under the MIT license. +@smithy/util-waiter under the Apache-2.0 license. +@aws-sdk/client-cloudwatch-logs under the Apache-2.0 license. +@smithy/eventstream-serde-browser under the Apache-2.0 license. +@smithy/eventstream-serde-universal under the Apache-2.0 license. +@smithy/eventstream-codec under the Apache-2.0 license. +@aws-crypto/crc32 under the Apache-2.0 license. +@smithy/eventstream-serde-config-resolver under the Apache-2.0 license. +@smithy/eventstream-serde-node under the Apache-2.0 license. +@types/uuid under the MIT license. +@aws-sdk/client-sqs under the Apache-2.0 license. +@aws-sdk/middleware-sdk-sqs under the Apache-2.0 license. +@smithy/md5-js under the Apache-2.0 license. +@aws-solutions-constructs/aws-eventbridge-lambda under the Apache-2.0 license. +aws-cdk-lib under the Apache-2.0 license. +@balena/dockerignore under the Apache-2.0 license. +astral-regex under the MIT license. +case under the MIT license. +fast-uri under the BSD-3-Clause license. +fs-extra under the MIT license. +jsonfile under the MIT license. +jsonschema under the MIT license. +lodash.truncate under the MIT license. +mime-db under the MIT license. +mime-types under the MIT license. +require-from-string under the MIT license. +slice-ansi under the MIT license. +table under the BSD-3-Clause license. +universalify under the MIT license. +yaml under the ISC license. +constructs under the Apache-2.0 license. +@aws-cdk/asset-awscli-v1 under the Apache-2.0 license. +@aws-cdk/asset-node-proxy-agent-v6 under the Apache-2.0 license. +@aws-cdk/cloud-assembly-schema under the Apache-2.0 license. +@aws-solutions-constructs/core under the Apache-2.0 license. +deep-diff under the MIT license. +npmlog under the ISC license. +are-we-there-yet under the ISC license. +console-control-strings under the ISC license. +gauge under the ISC license. +aproba under the ISC license. +color-support under the ISC license. +has-unicode under the ISC license. +wide-align under the ISC license. +set-blocking under the ISC license. +@aws-solutions-constructs/aws-lambda-sqs-lambda under the Apache-2.0 license. +@aws-solutions-constructs/aws-lambda-sqs under the Apache-2.0 license. +@aws-solutions-constructs/aws-sqs-lambda under the Apache-2.0 license. +@types/aws-lambda under the MIT license. +axios under the MIT license. +follow-redirects under the MIT license. +form-data under the MIT license. +asynckit under the MIT license. +combined-stream under the MIT license. +delayed-stream under the MIT license. +proxy-from-env under the MIT license. +esbuild under the MIT license. +@esbuild/aix-ppc64 under the MIT license. +@esbuild/android-arm under the MIT license. +@esbuild/android-arm64 under the MIT license. +@esbuild/android-x64 under the MIT license. +@esbuild/darwin-arm64 under the MIT license. +@esbuild/darwin-x64 under the MIT license. +@esbuild/freebsd-arm64 under the MIT license. +@esbuild/freebsd-x64 under the MIT license. +@esbuild/linux-arm under the MIT license. +@esbuild/linux-arm64 under the MIT license. +@esbuild/linux-ia32 under the MIT license. +@esbuild/linux-loong64 under the MIT license. +@esbuild/linux-mips64el under the MIT license. +@esbuild/linux-ppc64 under the MIT license. +@esbuild/linux-riscv64 under the MIT license. +@esbuild/linux-s390x under the MIT license. +@esbuild/linux-x64 under the MIT license. +@esbuild/netbsd-arm64 under the MIT license. +@esbuild/netbsd-x64 under the MIT license. +@esbuild/openbsd-arm64 under the MIT license. +@esbuild/openbsd-x64 under the MIT license. +@esbuild/sunos-x64 under the MIT license. +@esbuild/win32-arm64 under the MIT license. +@esbuild/win32-ia32 under the MIT license. +@esbuild/win32-x64 under the MIT license. +aws-sdk under the Apache-2.0 license. +buffer under the MIT license. +base64-js under the MIT license. +ieee754 under the BSD-3-Clause license. +events under the MIT license. +jmespath under the Apache-2.0 license. +querystring under the MIT license. +sax under the ISC license. +url under the MIT license. +util under the MIT license. +is-arguments under the MIT license. +xml2js under the MIT license. +xmlbuilder under the MIT license. +color under the MIT license. +color-string under the MIT license. +simple-swizzle under the MIT license. +dayjs under the MIT license. +sharp under the Apache-2.0 license. +detect-libc under the Apache-2.0 license. +@img/sharp-darwin-arm64 under the Apache-2.0 license. +@img/sharp-libvips-darwin-arm64 under the LGPL-3.0-or-later license. +@img/sharp-darwin-x64 under the Apache-2.0 license. +@img/sharp-libvips-darwin-x64 under the LGPL-3.0-or-later license. +@img/sharp-libvips-linux-arm under the LGPL-3.0-or-later license. +@img/sharp-libvips-linux-arm64 under the LGPL-3.0-or-later license. +@img/sharp-libvips-linux-ppc64 under the LGPL-3.0-or-later license. +@img/sharp-libvips-linux-s390x under the LGPL-3.0-or-later license. +@img/sharp-libvips-linux-x64 under the LGPL-3.0-or-later license. +@img/sharp-libvips-linuxmusl-arm64 under the LGPL-3.0-or-later license. +@img/sharp-libvips-linuxmusl-x64 under the LGPL-3.0-or-later license. +@img/sharp-linux-arm under the Apache-2.0 license. +@img/sharp-linux-arm64 under the Apache-2.0 license. +@img/sharp-linux-s390x under the Apache-2.0 license. +@img/sharp-linux-x64 under the Apache-2.0 license. +@img/sharp-linuxmusl-arm64 under the Apache-2.0 license. +@img/sharp-linuxmusl-x64 under the Apache-2.0 license. +@img/sharp-wasm32 under the Apache-2.0 AND LGPL-3.0-or-later AND MIT licenses. +@emnapi/runtime under the MIT license. +@img/sharp-win32-arm64 under the Apache-2.0 AND LGPL-3.0-or-later licenses. +@img/sharp-win32-ia32 under the Apache-2.0 AND LGPL-3.0-or-later licenses. +@img/sharp-win32-x64 under the Apache-2.0 AND LGPL-3.0-or-later licenses. +@types/color under the MIT license. +@types/color-convert under the MIT license. +@types/color-name under the MIT license. +@popperjs/core under the MIT license. +@aws-sdk/client-cloudformation under the Apache-2.0 license. +@aws-sdk/client-cloudfront under the Apache-2.0 license. +@aws-sdk/client-ec2 under the Apache-2.0 license. +@aws-sdk/middleware-sdk-ec2 under the Apache-2.0 license. +@aws-sdk/util-format-url under the Apache-2.0 license. +@aws-sdk/client-s3 under the Apache-2.0 license. +@aws-crypto/sha1-browser under the Apache-2.0 license. +@aws-sdk/middleware-bucket-endpoint under the Apache-2.0 license. +@aws-sdk/util-arn-parser under the Apache-2.0 license. +@aws-sdk/middleware-expect-continue under the Apache-2.0 license. +@aws-sdk/middleware-flexible-checksums under the Apache-2.0 license. +@aws-crypto/crc32c under the Apache-2.0 license. +@aws-sdk/middleware-location-constraint under the Apache-2.0 license. +@aws-sdk/middleware-sdk-s3 under the Apache-2.0 license. +@aws-sdk/middleware-ssec under the Apache-2.0 license. +@aws-sdk/signature-v4-multi-region under the Apache-2.0 license. +@smithy/hash-blob-browser under the Apache-2.0 license. +@smithy/chunked-blob-reader under the Apache-2.0 license. +@smithy/chunked-blob-reader-native under the Apache-2.0 license. +@smithy/hash-stream-node under the Apache-2.0 license. +@aws-sdk/client-secrets-manager under the Apache-2.0 license. +@aws-sdk/client-service-catalog-appregistry under the Apache-2.0 license. +@aws-sdk/client-rekognition under the Apache-2.0 license. +moment under the MIT license. +metrics-utils under the Apache-2.0 license. +@aws-cdk/aws-servicecatalogappregistry-alpha under the Apache-2.0 license. +@aws-solutions-constructs/aws-apigateway-lambda under the Apache-2.0 license. +code-point-at under the MIT license. +core-util-is under the MIT license. +delegates under the MIT license. +number-is-nan under the MIT license. +object-assign under the MIT license. +process-nextick-args under the MIT license. +readable-stream under the MIT license. +safe-buffer under the MIT license. +string_decoder under the MIT license. +util-deprecate under the MIT license. +@aws-solutions-constructs/aws-cloudfront-apigateway-lambda under the Apache-2.0 license. +@aws-solutions-constructs/aws-cloudfront-apigateway under the Apache-2.0 license. +@aws-solutions-constructs/aws-cloudfront-s3 under the Apache-2.0 license. +@aws-solutions-constructs/resources under the Apache-2.0 license. +aws-cdk under the Apache-2.0 license. +adm-zip under the MIT license. +@types/adm-zip under the MIT license. + +******************** +OPEN SOURCE LICENSES +******************** + +0BSD - http://landley.net/toybox/license.html +Apache-2.0 - https://www.apache.org/licenses/LICENSE-2.0 +BSD-2-Clause - https://opensource.org/licenses/BSD-2-Clause +BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause +CC-BY-3.0 - https://creativecommons.org/licenses/by/3.0/legalcode +CC-BY-4.0 - https://creativecommons.org/licenses/by/4.0/legalcode +CC0-1.0 - https://creativecommons.org/publicdomain/zero/1.0/legalcode +ISC - https://www.isc.org/licenses/ +LGPL-3.0-or-later - https://www.gnu.org/licenses/lgpl-3.0-standalone.html +MIT - https://opensource.org/license/mit/ +Python-2.0 - https://opensource.org/licenses/Python-2.0 \ No newline at end of file diff --git a/NOTICE.txt b/NOTICE.txt deleted file mode 100644 index a772ce15e..000000000 --- a/NOTICE.txt +++ /dev/null @@ -1,18 +0,0 @@ -Serverless Image Handler -Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - -********************** -THIRD PARTY COMPONENTS -********************** -This software includes third party software subject to the following copyrights: - -aws-sdk under the Apache License Version 2.0 -axios under the Massachusetts Institute of Technology (MIT) license -axios-mock-adapter under the Massachusetts Institute of Technology (MIT) license -bootstrap under the Massachusetts Institute of Technology (MIT) license -color under the Massachusetts Institute of Technology (MIT) license -color-name under the Massachusetts Institute of Technology (MIT) license -jest under the Massachusetts Institute of Technology (MIT) license -mocha under the Massachusetts Institute of Technology (MIT) license -sharp under the Apache License Version 2.0 -uuid under the Massachusetts Institute of Technology (MIT) license \ No newline at end of file diff --git a/README.md b/README.md index 1b640c71a..0adddf7d6 100644 --- a/README.md +++ b/README.md @@ -1,92 +1,130 @@ -**_Important Notice:_** -Due to a [change in the AWS Lambda execution environment](https://aws.amazon.com/blogs/compute/upcoming-updates-to-the-aws-lambda-execution-environment/), Serverless Image Handler v3 deployments are functionally broken. To address the issue we have released [minor version update v3.1.1](https://solutions-reference.s3.amazonaws.com/serverless-image-handler/v3.1.1/serverless-image-handler.template). We recommend all users of v3 to run cloudformation stack update with v3.1.1. Additionally, we suggest you to look at v5 of the solution and migrate to v5 if it addresses all of your use cases. +**[Dynamic Image Transformation for Amazon CloudFront](https://aws.amazon.com/solutions/implementations/dynamic-image-transformation-for-amazon-cloudfront/)** | **[🚧 Feature request](https://github.com/aws-solutions/serverless-image-handler/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=)** | **[🐛 Bug Report](https://github.com/aws-solutions/serverless-image-handler/issues/new?assignees=&labels=bug&template=bug_report.md&title=)** | **[❓ General Question](https://github.com/aws-solutions/serverless-image-handler/issues/new?assignees=&labels=question&template=general_question.md&title=)** -# AWS Serverless Image Handler Lambda wrapper for SharpJS -A solution to dynamically handle images on the fly, utilizing Sharp (https://sharp.pixelplumbing.com/en/stable/). -Published version, additional details and documentation are available here: https://aws.amazon.com/solutions/serverless-image-handler/ +**Note**: If you want to use the solution without building from source, navigate to [Solution Landing Page](https://aws.amazon.com/solutions/implementations/dynamic-image-transformation-for-amazon-cloudfront/). -_Note:_ it is recommended to build the application binary on Amazon Linux. +## Table of Content -## On This Page -- [Architecture Overview](#architecture-overview) -- [Creating a custom build](#creating-a-custom-build) +- [Solution Overview](#solution-overview) +- [Architecture Diagram](#architecture-diagram) +- [AWS CDK and Solutions Constructs](#aws-cdk-and-solutions-constructs) +- [Customizing the Solution](#customizing-the-solution) + - [Prerequisites for Customization](#prerequisites-for-customization) + - [1. Clone the repository](#1-clone-the-repository) + - [2. Unit Test](#2-unit-test) + - [3. Build & Deploy](#3-build-and-deploy) +- [Collection of operational metrics](#collection-of-operational-metrics) - [External Contributors](#external-contributors) - [License](#license) -## Architecture Overview -![Architecture](architecture.png) +# Solution Overview -The AWS CloudFormation template deploys an Amazon CloudFront distribution, Amazon API Gateway REST API, and an AWS Lambda function. Amazon CloudFront provides a caching layer to reduce the cost of image processing and the latency of subsequent image delivery. The Amazon API Gateway provides endpoint resources and triggers the AWS Lambda function. The AWS Lambda function retrieves the image from the customer's Amazon Simple Storage Service (Amazon S3) bucket and uses Sharp to return a modified version of the image to the API Gateway. Additionally, the solution generates a CloudFront domain name that provides cached access to the image handler API. +The Dynamic Image Transformation for Amazon CloudFront solution helps to embed images on websites and mobile applications to drive user engagement. It uses [Sharp](https://sharp.pixelplumbing.com/en/stable/) to provide high-speed image processing without sacrificing image quality. To minimize costs of image optimization, manipulation, and processing, this solution automates version control and provides flexible storage and compute options for file reprocessing. -_**Note**:_ From v5.0, all AWS CloudFormation template resources are created be [AWS CDK](https://aws.amazon.com/cdk/) and [AWS Solutions Constructs](https://aws.amazon.com/solutions/constructs/). Since the AWS CloudFormation template resources have the same logical ID comparing to v4.x, it makes the solution upgradable mostly from v4.x to v5. +This solution automatically deploys and configures a serverless architecture optimized for dynamic image manipulation. Images can be rendered and returned spontaneously. For example, an image can be resized based on different screen sizes by adding code on a website that leverages this solution to resize the image before being sent to the screen using the image. It uses [Amazon CloudFront](https://aws.amazon.com/cloudfront) for global content delivery and [Amazon Simple Storage Service](https://aws.amazon.com/s3) (Amazon S3) for reliable and durable cloud storage. -## Creating a custom build -The solution can be deployed through the CloudFormation template available on the solution home page. -To make changes to the solution, download or clone this repo, update the source code and then run the deployment/build-s3-dist.sh script to deploy the updated Lambda code to an Amazon S3 bucket in your account. +For more information and a detailed deployment guide, visit the [Dynamic Image Transformation for Amazon CloudFront](https://aws.amazon.com/solutions/implementations/dynamic-image-transformation-for-amazon-cloudfront/) solution page. -### Prerequisites: -* [AWS Command Line Interface](https://aws.amazon.com/cli/) -* Node.js 12.x or later +# Architecture Diagram + +Dynamic Image Transformation for Amazon CloudFront supports two architectures, one using an Amazon API Gateway REST API, and another using S3 Object Lambda. The Amazon API Gateway REST API architecture maintains the structure used in v6.3.3 and below of the Dynamic Image Transformation for Amazon CloudFront. The S3 Object Lambda architecture maintains very similar functionality, while also allowing for images larger than 6 MB to be returned. For more information, refer to the [Architecture Overview](https://docs.aws.amazon.com/solutions/latest/serverless-image-handler/architecture-overview.html) in the implementation guide. + +The AWS CloudFormation template deploys an Amazon CloudFront distribution, Amazon API Gateway REST API/S3 Object Lambda, and an AWS Lambda function. Amazon CloudFront provides a caching layer to reduce the cost of image processing and the latency of subsequent image delivery. The Amazon API Gateway/S3 Object Lambda provides endpoint resources and triggers the AWS Lambda function. The AWS Lambda function retrieves the image from the customer's Amazon Simple Storage Service (Amazon S3) bucket and uses Sharp to return a modified version of the image. Additionally, the solution generates a CloudFront domain name that provides cached access to the image handler API. There is limited use of CloudFront functions for consistency and cache hit rate purposes. + +## Default Architecture + +![Architecture Diagram (Default Architecture)](./default_architecture.png) + +## S3 Object Lambda Architecture + +![Architecture Diagram (S3 Object Lambda Architecture)](./object_lambda_architecture.png) + + +# AWS CDK and Solutions Constructs + +[AWS Cloud Development Kit (AWS CDK)](https://aws.amazon.com/cdk/) and [AWS Solutions Constructs](https://aws.amazon.com/solutions/constructs/) make it easier to consistently create well-architected infrastructure applications. All AWS Solutions Constructs are reviewed by AWS and use best practices established by the AWS Well-Architected Framework. This solution uses the following AWS Solutions Constructs: + +- [aws-cloudfront-s3](https://docs.aws.amazon.com/solutions/latest/constructs/aws-cloudfront-s3.html) +- [aws-cloudfront-apigateway-lambda](https://docs.aws.amazon.com/solutions/latest/constructs/aws-cloudfront-apigateway-lambda.html) + +In addition to the AWS Solutions Constructs, the solution uses AWS CDK directly to create infrastructure resources. + +# Customizing the Solution + +## Prerequisites for Customization + +- [AWS Command Line Interface](https://aws.amazon.com/cli/) +- Node.js 20.x or later ### 1. Clone the repository -```bash -git clone https://github.com/awslabs/serverless-image-handler.git -``` -### 2. Run unit tests for customization -Run unit tests to make sure added customization passes the tests: ```bash -cd ./deployment -chmod +x ./run-unit-tests.sh -./run-unit-tests.sh +git clone https://github.com/aws-solutions/dynamic-image-transformation-for-amazon-cloudfront.git +cd dynamic-image-transformation-for-amazon-cloudfront +export MAIN_DIRECTORY=$PWD ``` -### 3. Declare environment variables -```bash -export REGION=aws-region-code # the AWS region to launch the solution (e.g. us-east-1) -export DIST_OUTPUT_BUCKET=my-bucket-name # bucket where customized code will reside -export SOLUTION_NAME=my-solution-name # the solution name -export VERSION=my-version # version number for the customized code -``` -### 4. Create an Amazon S3 Bucket -The CloudFormation template is configured to pull the Lambda deployment packages from Amazon S3 bucket in the region the template is being launched in. Create a bucket in the desired region with the region name appended to the name of the bucket. -```bash -aws s3 mb s3://$DIST_OUTPUT_BUCKET-$REGION --region $REGION -``` +### 2. Unit Test + +After making changes, run unit tests to make sure added customization passes the tests: -### 5. Create the deployment packages -Build the distributable: ```bash -chmod +x ./build-s3-dist.sh -./build-s3-dist.sh $DIST_OUTPUT_BUCKET $SOLUTION_NAME $VERSION +cd $MAIN_DIRECTORY/deployment +chmod +x run-unit-tests.sh && ./run-unit-tests.sh ``` -Deploy the distributable to the Amazon S3 bucket in your account: +### 3. Build and Deploy ```bash -aws s3 sync ./regional-s3-assets/ s3://$DIST_OUTPUT_BUCKET-$REGION/$SOLUTION_NAME/$VERSION/ --acl bucket-owner-full-control -aws s3 sync ./global-s3-assets/ s3://$DIST_OUTPUT_BUCKET-$REGION/$SOLUTION_NAME/$VERSION/ --acl bucket-owner-full-control +cd $MAIN_DIRECTORY/source/constructs +npm run clean:install +overrideWarningsEnabled=false npx cdk bootstrap --profile +overrideWarningsEnabled=false npx cdk deploy\ + --parameters DeployDemoUIParameter=Yes\ + --parameters SourceBucketsParameter=\ + --profile ``` -### 6. Launch the CloudFormation template. -* Get the link of the `serverless-image-handler.template` uploaded to your Amazon S3 bucket. -* Deploy the Serverless Image Handler solution to your account by launching a new AWS CloudFormation stack using the S3 link of the `serverless-image-handler.template`. - -## External Contributors -- [@leviwilson](https://github.com/leviwilson) for [#117](https://github.com/awslabs/serverless-image-handler/pull/117) -- [@rpong](https://github.com/rpong) for [#130](https://github.com/awslabs/serverless-image-handler/pull/130) -- [@harriswong](https://github.com/harriswong) for [#138](https://github.com/awslabs/serverless-image-handler/pull/138) -- [@ganey](https://github.com/ganey) for [#139](https://github.com/awslabs/serverless-image-handler/pull/139) -- [@browniebroke](https://github.com/browniebroke) for [#151](https://github.com/awslabs/serverless-image-handler/pull/151), [#152](https://github.com/awslabs/serverless-image-handler/pull/152) -- [@john-shaffer](https://github.com/john-shaffer) for [#158](https://github.com/awslabs/serverless-image-handler/pull/158) -- [@toredash](https://github.com/toredash) for [#174](https://github.com/awslabs/serverless-image-handler/pull/174), [#195](https://github.com/awslabs/serverless-image-handler/pull/195) -- [@lith-imad](https://github.com/lith-imad) for [#194](https://github.com/awslabs/serverless-image-handler/pull/194) -- [@pch](https://github.com/pch) for [#227](https://github.com/awslabs/serverless-image-handler/pull/227) -- [@atrope](https://github.com/atrope) for [#201](https://github.com/awslabs/serverless-image-handler/pull/201) -- [@bretto36](https://github.com/bretto36) for [#182](https://github.com/awslabs/serverless-image-handler/pull/182) -- [@makoncline](https://github.com/makoncline) for [#255](https://github.com/awslabs/serverless-image-handler/pull/255) - -*** -## License -Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
-SPDX-License-Identifier: Apache-2.0 +_Note:_ +- **MY_BUCKET**: name of an existing bucket or the list of comma-separated bucket names in your account +- **PROFILE_NAME**: name of an AWS CLI profile that has appropriate credentials for deploying in your preferred region + +# Collection of operational metrics + +This solution collects anonymous operational metrics to help AWS improve the quality and features of the solution. For more information, including how to disable this capability, please see the [implementation guide](https://docs.aws.amazon.com/solutions/latest/serverless-image-handler/reference.html#anonymized-data-collection). + +# External Contributors + +- [@leviwilson](https://github.com/leviwilson) for [#117](https://github.com/aws-solutions/serverless-image-handler/pull/117) +- [@rpong](https://github.com/rpong) for [#130](https://github.com/aws-solutions/serverless-image-handler/pull/130) +- [@harriswong](https://github.com/harriswong) for [#138](https://github.com/aws-solutions/serverless-image-handler/pull/138) +- [@ganey](https://github.com/ganey) for [#139](https://github.com/aws-solutions/serverless-image-handler/pull/139) +- [@browniebroke](https://github.com/browniebroke) for [#151](https://github.com/aws-solutions/serverless-image-handler/pull/151), [#152](https://github.com/aws-solutions/serverless-image-handler/pull/152) +- [@john-shaffer](https://github.com/john-shaffer) for [#158](https://github.com/aws-solutions/serverless-image-handler/pull/158) +- [@toredash](https://github.com/toredash) for [#174](https://github.com/aws-solutions/serverless-image-handler/pull/174), [#195](https://github.com/aws-solutions/serverless-image-handler/pull/195) +- [@lith-imad](https://github.com/lith-imad) for [#194](https://github.com/aws-solutions/serverless-image-handler/pull/194) +- [@pch](https://github.com/pch) for [#227](https://github.com/aws-solutions/serverless-image-handler/pull/227) +- [@atrope](https://github.com/atrope) for [#201](https://github.com/aws-solutions/serverless-image-handler/pull/201), [#202](https://github.com/aws-solutions/serverless-image-handler/pull/202) +- [@bretto36](https://github.com/bretto36) for [#182](https://github.com/aws-solutions/serverless-image-handler/pull/182) +- [@makoncline](https://github.com/makoncline) for [#255](https://github.com/aws-solutions/serverless-image-handler/pull/255) +- [@frankenbubble](https://github.com/frankenbubble) for [#302](https://github.com/aws-solutions/serverless-image-handler/pull/302) +- [@guidev](https://github.com/guidev) for [#309](https://github.com/aws-solutions/serverless-image-handler/pull/309) +- [@njtmead](https://github.com/njtmead) for [#276](https://github.com/aws-solutions/serverless-image-handler/pull/276) +- [@StaymanHou](https://github.com/StaymanHou) for [#320](https://github.com/aws-solutions/serverless-image-handler/pull/320) +- [@alenpaulvarghese](https://github.com/alenpaulvarghese) for [#392](https://github.com/aws-solutions/serverless-image-handler/pull/392) +- [@Fjool](https://github.com/Fjool) for [#489](https://github.com/aws-solutions/serverless-image-handler/pull/489) +- [@fvsnippets](https://github.com/fvsnippets) for [#373](https://github.com/aws-solutions/serverless-image-handler/pull/373), [#380](https://github.com/aws-solutions/serverless-image-handler/pull/380) +- [@ccchapman](https://github.com/ccchapman) for [#490](https://github.com/aws-solutions/serverless-image-handler/pull/490) +- [@bennet-esyoil][https://github.com/bennet-esyoil] for [#521](https://github.com/aws-solutions/serverless-image-handler/pull/521) +- [@vaniyokk][https://github.com/vaniyokk] for [#511](https://github.com/aws-solutions/serverless-image-handler/pull/511) +- [@ericbuehl](https://github.com/ericbuehl) for [#463](https://github.com/aws-solutions/serverless-image-handler/pull/463) +- [@fvsnippets](https://github.com/fvsnippets) for [#372](https://github.com/aws-solutions/serverless-image-handler/pull/372) +- [@markuscolourbox](https://github.com/markuscolourbox) for [#349](https://github.com/aws-solutions/serverless-image-handler/pull/349) +- [@madhubalaji](https://github.com/madhubalaji) for [#476](https://github.com/aws-solutions/serverless-image-handler/pull/476) +- [@nicolasbuch](https://github.com/nicolasbuch) for [#569](https://github.com/aws-solutions/serverless-image-handler/pull/569) +- [@mrnonz](https://github.com/mrnonz) for [#567](https://github.com/aws-solutions/serverless-image-handler/pull/567) +- [@ilich](https://github.com/ilich) for [#574](https://github.com/aws-solutions/serverless-image-handler/pull/574) + +# License + +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..c8b27623e --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,11 @@ +# Reporting Security Issues + +We take all security reports seriously. +When we receive such reports, +we will investigate and subsequently address +any potential vulnerabilities as quickly as possible. +If you discover a potential security issue in this project, +please notify AWS/Amazon Security via our +[vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/) +or directly via email to [AWS Security](mailto:aws-security@amazon.com). +Please do *not* create a public GitHub issue in this project. \ No newline at end of file diff --git a/VERSION.txt b/VERSION.txt new file mode 100644 index 000000000..024b4b9b5 --- /dev/null +++ b/VERSION.txt @@ -0,0 +1 @@ +7.0.6 diff --git a/architecture.png b/architecture.png deleted file mode 100644 index 94f3e90cd..000000000 Binary files a/architecture.png and /dev/null differ diff --git a/default_architecture.png b/default_architecture.png new file mode 100644 index 000000000..68a999e31 Binary files /dev/null and b/default_architecture.png differ diff --git a/deployment/build-s3-dist.sh b/deployment/build-s3-dist.sh index 844da2bb3..c223eb6b1 100755 --- a/deployment/build-s3-dist.sh +++ b/deployment/build-s3-dist.sh @@ -1,82 +1,50 @@ #!/bin/bash -# -# This assumes all of the OS-level configuration has been completed and git repo has already been cloned -# -# This script should be run from the repo's deployment directory -# cd deployment -# ./build-s3-dist.sh source-bucket-base-name trademarked-solution-name version-code -# -# Paramenters: -# - source-bucket-base-name: Name for the S3 bucket location where the template will source the Lambda -# code from. The template will append '-[region_name]' to this bucket name. -# For example: ./build-s3-dist.sh solutions my-solution v1.0.0 -# The template will then expect the source code to be located in the solutions-[region_name] bucket -# -# - trademarked-solution-name: name of the solution for consistency -# -# - version-code: version of the package +# The script is for aws-solutions internal purposes only + +[ "$DEBUG" == 'true' ] && set -x +set -e # Check to see if input has been provided: -if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then - echo "Please provide the base source bucket name, trademark approved solution name and version where the lambda code will eventually reside." - echo "For example: ./build-s3-dist.sh solutions trademarked-solution-name v1.0.0" +if [ -z "$SOLUTION_NAME" ]; then + echo "Please provide the trademark approved solution name through environment variables" exit 1 fi -set -e - -# Get reference for all important folders -template_dir="$PWD" -template_dist_dir="$template_dir/global-s3-assets" -build_dist_dir="$template_dir/regional-s3-assets" -source_dir="$template_dir/../source" - -echo "------------------------------------------------------------------------------" -echo "Rebuild distribution" -echo "------------------------------------------------------------------------------" -rm -rf $template_dist_dir -mkdir -p $template_dist_dir -rm -rf $build_dist_dir -mkdir -p $build_dist_dir - -echo "------------------------------------------------------------------------------" -echo "CloudFormation template with CDK and Constructs" -echo "------------------------------------------------------------------------------" -export BUCKET_NAME=$1 -export SOLUTION_NAME=$2 -export VERSION=$3 - -cd $source_dir/constructs -npm install -npm run build && cdk synth --asset-metadata false --path-metadata false --json true > serverless-image-handler.json -mv serverless-image-handler.json $template_dist_dir/serverless-image-handler.template - -echo "------------------------------------------------------------------------------" -echo "Package the image-handler code" -echo "------------------------------------------------------------------------------" -cd $source_dir/image-handler -npm install -npm run build -cp dist/image-handler.zip $build_dist_dir/image-handler.zip - -echo "------------------------------------------------------------------------------" -echo "Package the demo-ui assets" -echo "------------------------------------------------------------------------------" -mkdir $build_dist_dir/demo-ui/ -cp -r $source_dir/demo-ui/** $build_dist_dir/demo-ui/ - -echo "------------------------------------------------------------------------------" -echo "Package the custom-resource code" -echo "------------------------------------------------------------------------------" -cd $source_dir/custom-resource -npm install -npm run build -cp dist/custom-resource.zip $build_dist_dir/custom-resource.zip - -echo "------------------------------------------------------------------------------" -echo "Generate the demo-ui manifest document" -echo "------------------------------------------------------------------------------" -cd $source_dir/demo-ui -manifest=(`find * -type f ! -iname ".DS_Store"`) -manifest_json=$(IFS=,;printf "%s" "${manifest[*]}") -echo "{\"files\":[\"$manifest_json\"]}" | sed 's/,/","/g' >> $build_dist_dir/demo-ui-manifest.json +function headline(){ + echo "------------------------------------------------------------------------------" + echo "$1" + echo "------------------------------------------------------------------------------" +} + +headline "[Init] Setting up paths and variables" +deployment_dir="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +staging_dist_dir="$deployment_dir/staging" +template_dist_dir="$deployment_dir/global-s3-assets" +build_dist_dir="$deployment_dir/regional-s3-assets" +source_dir="$deployment_dir/../source" +cdk_source_dir="$source_dir/constructs" + +headline "[Init] Clean old folders" +rm -rf "$staging_dist_dir" +mkdir -p "$staging_dist_dir" +rm -rf "$template_dist_dir" +mkdir -p "$template_dist_dir" +rm -rf "$build_dist_dir" +mkdir -p "$build_dist_dir" + +headline "[Init] Ensure package versions are updated" +npm --prefix "$source_dir" run bump-version + +headline "[Build] Synthesize cdk template and assets" +cd "$cdk_source_dir" +npm run clean:install +overrideWarningsEnabled=false npx cdk synth --quiet --asset-metadata false --path-metadata --output="$staging_dist_dir" +cd "$staging_dist_dir" +rm tree.json manifest.json cdk.out ./*.assets.json +cp "$staging_dist_dir"/*.template.json "$template_dist_dir"/"$SOLUTION_NAME".template +rm ./*.template.json + +headline "[Package] Generate public assets for lambda and ui" +cd "$deployment_dir"/cdk-solution-helper/asset-packager && npm ci +npx ts-node ./index "$staging_dist_dir" "$build_dist_dir" +rm -rf $staging_dist_dir \ No newline at end of file diff --git a/deployment/cdk-solution-helper/README.md b/deployment/cdk-solution-helper/README.md new file mode 100644 index 000000000..c22d6db1c --- /dev/null +++ b/deployment/cdk-solution-helper/README.md @@ -0,0 +1,6 @@ +## CDK Solution Helper + +**For aws-solutions internal purposes only** + +`cdk-solution-helper` runs on solution internal pipeline and makes needed artifact modifications to support 1-click deployment using solution CloudFormation template. +Additionally, it packages templates and lambda binaries and prepares them to be staged on the solution buckets. \ No newline at end of file diff --git a/deployment/cdk-solution-helper/asset-packager/__tests__/asset-packager.test.ts b/deployment/cdk-solution-helper/asset-packager/__tests__/asset-packager.test.ts new file mode 100644 index 000000000..28cbdc7c3 --- /dev/null +++ b/deployment/cdk-solution-helper/asset-packager/__tests__/asset-packager.test.ts @@ -0,0 +1,126 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +const readdirMock = jest.fn(); +const addLocalFolderMock = jest.fn(); +const renameMock = jest.fn(); +const writeZipMock = jest.fn(); +const lstatMock = jest.fn(); +import path from "path"; +import { CDKAssetPackager } from "../asset-packager"; +jest + .mock("node:fs/promises", () => { + const originalModule = jest.requireActual("node:fs/promises"); + return { + ...originalModule, + __esModule: true, + readdir: readdirMock, + lstat: lstatMock, + rename: renameMock, + }; + }) + .mock("adm-zip", () => { + const originalModule = jest.requireActual("adm-zip"); + return { + ...originalModule, + __esModule: true, + default: jest.fn(() => ({ + addLocalFolder: addLocalFolderMock, + writeZip: writeZipMock, + })), + }; + }); + +const __assetPath = "/myTestPath"; +const __outputPath = "/outputPath"; +const assetPackager = new CDKAssetPackager(__assetPath); +const __asset1 = "asset.1"; +const __asset2 = "asset.2.zip"; + +describe("CDKAssetPackager", () => { + describe("getAssetPaths", function () { + beforeEach(function () { + readdirMock.mockClear(); + }); + + it("should return empty array for invalid path", async function () { + readdirMock.mockRejectedValue("invalid path"); + expect(await assetPackager.getAssetPaths()).toEqual([]); + }); + + it("should return empty array when no assets found", async function () { + readdirMock.mockResolvedValue([]); + expect(await assetPackager.getAssetPaths()).toEqual([]); + }); + + it("should return array of paths when assets found", async function () { + readdirMock.mockResolvedValue([__asset1, __asset2]); + expect(await assetPackager.getAssetPaths()).toEqual([ + path.join(__assetPath, __asset1), + path.join(__assetPath, __asset2), + ]); + }); + }); + + describe("createAssetZip", function () { + beforeEach(function () { + readdirMock.mockClear(); + lstatMock.mockClear(); + addLocalFolderMock.mockClear(); + writeZipMock.mockClear(); + }); + + it("should skip doing anything if path not a folder", async function () { + // Arrange + lstatMock.mockResolvedValue({ + isDirectory: () => false, + }); + + // Act, Assert + await expect(assetPackager.createAssetZip(__assetPath)).resolves.toBeUndefined(); + expect(addLocalFolderMock).toBeCalledTimes(0); + }); + + it("should zip assets in the folder for valid path", async function () { + // Arrange + lstatMock.mockResolvedValue({ + isDirectory: () => true, + }); + addLocalFolderMock.mockResolvedValue(undefined); + writeZipMock.mockResolvedValue(undefined); + + // Act, Assert + await expect(assetPackager.createAssetZip(__asset1)).resolves.toBeUndefined(); + expect(addLocalFolderMock).toBeCalledTimes(1); + expect(writeZipMock).toBeCalledWith(`${path.join(__assetPath, __asset1)}.zip`); + }); + + it("should throw error if error encountered", async function () { + lstatMock.mockRejectedValue(new Error("error encountered")); + await expect(assetPackager.createAssetZip("")).rejects.toThrowError("error encountered"); + }); + }); + + describe("moveZips", function () { + beforeEach(function () { + renameMock.mockClear(); + readdirMock.mockClear(); + }); + + it("should move zips for valid paths", async function () { + readdirMock.mockResolvedValue([__asset2]); + await assetPackager.moveZips(__outputPath); + expect(renameMock).toBeCalledWith( + path.join(__assetPath, __asset2), + path.join(__outputPath, __asset2.split("asset.").pop()!) + ); + }); + + it("should throw error if error encountered", async function () { + readdirMock.mockRejectedValue(new Error("error encountered")); + await expect(assetPackager.moveZips("")).rejects.toThrowError("error encountered"); + }); + }); +}); diff --git a/deployment/cdk-solution-helper/asset-packager/__tests__/handler.test.ts b/deployment/cdk-solution-helper/asset-packager/__tests__/handler.test.ts new file mode 100644 index 000000000..d474ac9c4 --- /dev/null +++ b/deployment/cdk-solution-helper/asset-packager/__tests__/handler.test.ts @@ -0,0 +1,59 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import path from "path"; +import { existsSync } from "fs"; +import { mkdir, writeFile, rm } from "node:fs/promises"; +import { handler } from "../index"; + +const __assetDirectoryPath = path.join(__dirname, "mock-dir"); +const __outputPath = path.join(__dirname, "mock-dir-output"); +describe("Handler", () => { + beforeAll(async function Arrange() { + await rm(__assetDirectoryPath, { recursive: true, force: true }); + await rm(__outputPath, { recursive: true, force: true }); + await mkdir(__assetDirectoryPath); + await mkdir(__outputPath); + }); + + it("should fail in absence of path inputs ", async function () { + expect.assertions(2); + await expect(handler("", "")).rejects.toThrowError("undefined input path"); + await expect(handler(undefined, undefined)).rejects.toThrowError("undefined input path"); + }); + + it("should fail for invalid cdk asset path", async function () { + expect.assertions(1); + await expect(handler("invalidPath", __outputPath)).rejects.toThrowError(/(ENOENT).+(invalidPath)/g); + }); + + it("should succeed if cdk assets not found", async function () { + await expect(handler(__assetDirectoryPath, "invalidPath")).resolves.toBeUndefined(); + }); + + it("should fail for invalid output path", async function () { + // Arrange + expect.assertions(1); + const mockAssetPath = path.join(__assetDirectoryPath, "./asset.cdkAsset.zip"); + await writeFile(mockAssetPath, "NoOp"); + // Act, Assert + await expect(handler(__assetDirectoryPath, "invalidPath")).rejects.toThrowError(/(ENOENT).+(invalidPath)/g); + // Cleanup + await rm(mockAssetPath); + }); + + it("should successfully stage zip for valid paths", async function () { + const zipName = "asset.cdkAsset.zip"; + const mockAssetPath = path.join(__assetDirectoryPath, zipName); + await writeFile(mockAssetPath, "NoOp"); + await expect(handler(__assetDirectoryPath, __outputPath)).resolves.toBeUndefined(); + expect(existsSync(path.join(__outputPath, zipName.split("asset.").pop()!))).toBe(true); + }); + + afterAll(async function Cleanup() { + await rm(__assetDirectoryPath, { recursive: true, force: true }); + await rm(__outputPath, { recursive: true, force: true }); + }); +}); diff --git a/deployment/cdk-solution-helper/asset-packager/asset-packager.ts b/deployment/cdk-solution-helper/asset-packager/asset-packager.ts new file mode 100644 index 000000000..6a6f10a4e --- /dev/null +++ b/deployment/cdk-solution-helper/asset-packager/asset-packager.ts @@ -0,0 +1,60 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { readdir, lstat, rename } from "node:fs/promises"; +import path from "path"; +import AdmZip from "adm-zip"; + +/** + * @description Class to help with packaging and staging cdk assets + * on solution internal pipelines + */ +export class CDKAssetPackager { + constructor(private readonly assetFolderPath: string) { } + + /** + * @description get cdk asset paths + * All cdk generated assets are prepended with "asset" + */ + async getAssetPaths() { + try { + const allFiles = await readdir(this.assetFolderPath); + const assetFilePaths = allFiles + .filter((file) => file.includes("asset")) + .map((file) => path.join(this.assetFolderPath, file)); + return assetFilePaths; + } catch (err) { + console.error(err); + return []; + } + } + + /** + * @description creates zip from folder + * @param folderPath + */ + async createAssetZip(folderPath: string) { + const isDir = (await lstat(folderPath)).isDirectory(); + if (isDir) { + const zip = new AdmZip(); + zip.addLocalFolder(path.join(folderPath, "./")); + const zipName = `${folderPath.split("/").pop()}.zip`; + zip.writeZip(path.join(this.assetFolderPath, zipName)); + } + } + + /** + * @description moves zips to staging output directory in internal pipelines + * @param outputPath + */ + async moveZips(outputPath: string) { + const allFiles = await readdir(this.assetFolderPath); + const allZipPaths = allFiles.filter((file) => path.extname(file) === ".zip"); + for (const zipPath of allZipPaths) { + await rename(path.join(this.assetFolderPath, zipPath), path.join(outputPath, zipPath.split("asset.").pop()!)); + // remove cdk prepended string "asset.*" + } + } +} diff --git a/deployment/cdk-solution-helper/asset-packager/index.ts b/deployment/cdk-solution-helper/asset-packager/index.ts new file mode 100644 index 000000000..8c70652f3 --- /dev/null +++ b/deployment/cdk-solution-helper/asset-packager/index.ts @@ -0,0 +1,29 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { CDKAssetPackager } from "./asset-packager"; + +export async function handler(cdkAssetFolderPath: string | undefined, outputPath: string | undefined) { + if (!cdkAssetFolderPath || !outputPath) throw new Error("undefined input path"); + const assetPackager = new CDKAssetPackager(cdkAssetFolderPath); + const assetPaths = await assetPackager.getAssetPaths(); + for (const path of assetPaths) { + await assetPackager.createAssetZip(path); + } + await assetPackager.moveZips(outputPath); +} + +if (require.main === module) { + // this module was run directly from the command line, getting command line arguments + // e.g. npx ts-node index.ts cdkAssetPath outputPath + const cdkAssetPath = process.argv[2]; + const outputPath = process.argv[3]; + handler(cdkAssetPath, outputPath) + .then(() => console.log("all assets packaged")) + .catch((err) => { + console.error(err); + throw err; + }); +} diff --git a/deployment/cdk-solution-helper/jest.config.ts b/deployment/cdk-solution-helper/jest.config.ts new file mode 100644 index 000000000..0a8d8ffc1 --- /dev/null +++ b/deployment/cdk-solution-helper/jest.config.ts @@ -0,0 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import type { Config } from "jest"; + +const config: Config = { + collectCoverage: true, + coverageDirectory: "coverage", + transform: { + "^.+\\.(t)sx?$": "ts-jest", + }, + collectCoverageFrom: ["**/*.ts", "!**/*.test.ts", "!./jest.config.ts", "!./jest.setup.ts"], + coverageReporters: [["lcov", { projectRoot: "../" }], "text"], +}; + +export default config; diff --git a/deployment/cdk-solution-helper/package-lock.json b/deployment/cdk-solution-helper/package-lock.json new file mode 100644 index 000000000..11d673b07 --- /dev/null +++ b/deployment/cdk-solution-helper/package-lock.json @@ -0,0 +1,4425 @@ +{ + "name": "cdk-solution-helper", + "version": "7.0.5", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cdk-solution-helper", + "version": "7.0.5", + "license": "Apache-2.0", + "dependencies": { + "adm-zip": "^0.5.10", + "aws-cdk-lib": "^2.124.0" + }, + "devDependencies": { + "@types/adm-zip": "^0.5.2", + "@types/jest": "^29.5.6", + "@types/node": "^20.10.4", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "ts-node": "^10.9.2", + "typescript": "^5.3.3" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@aws-cdk/asset-awscli-v1": { + "version": "2.2.240", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.240.tgz", + "integrity": "sha512-Ry5yvGVf8s7j1Gf1aBFs0mBnWzRkkRtgSVpRGkDWXvZoPbRODAH33S1mAxkETNb+dNnTPGE2Gvws0XbhpJ6RzA==", + "license": "Apache-2.0" + }, + "node_modules/@aws-cdk/asset-node-proxy-agent-v6": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.1.0.tgz", + "integrity": "sha512-7bY3J8GCVxLupn/kNmpPc5VJz8grx+4RKfnnJiO1LG+uxkZfANZG3RMHhE+qQxxwkyQ9/MfPtTpf748UhR425A==", + "license": "Apache-2.0" + }, + "node_modules/@aws-cdk/cloud-assembly-schema": { + "version": "44.8.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-44.8.0.tgz", + "integrity": "sha512-Bxyj0VH8phE1uHJ6LiG3/UC/HYK91EBZnXSOzwtLsMJ0ZPuaQCYDRVAAfjDCSsEOwAk56/Waks8b5pXHpgz/xw==", + "bundleDependencies": [ + "jsonschema", + "semver" + ], + "license": "Apache-2.0", + "dependencies": { + "jsonschema": "~1.4.1", + "semver": "^7.7.2" + }, + "engines": { + "node": ">= 18.0.0" + } + }, + "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/jsonschema": { + "version": "1.4.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/semver": { + "version": "7.7.2", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.5.tgz", + "integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz", + "integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.4", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.27.4", + "@babel/types": "^7.27.3", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", + "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.5", + "@babel/types": "^7.27.3", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", + "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", + "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", + "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/adm-zip": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.7.tgz", + "integrity": "sha512-DNEs/QvmyRLurdQPChqq0Md4zGvPwHerAJYWk9l2jCbD1VPpnzRJorOdiq4zsw09NFbYnhfsoEhWtxIzXpn2yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/node": { + "version": "20.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.1.tgz", + "integrity": "sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "license": "MIT", + "engines": { + "node": ">=12.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib": { + "version": "2.202.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.202.0.tgz", + "integrity": "sha512-JDycQoE8AxUAeCFXFoCx6FGvR78e6W9zYxPgmfW/uPPbntyNCXXBqwyAYo17RGS/lr0RO3zqD/oCBZSNU2e/Yg==", + "bundleDependencies": [ + "@balena/dockerignore", + "case", + "fs-extra", + "ignore", + "jsonschema", + "minimatch", + "punycode", + "semver", + "table", + "yaml", + "mime-types" + ], + "license": "Apache-2.0", + "dependencies": { + "@aws-cdk/asset-awscli-v1": "2.2.240", + "@aws-cdk/asset-node-proxy-agent-v6": "^2.1.0", + "@aws-cdk/cloud-assembly-schema": "^44.2.0", + "@balena/dockerignore": "^1.0.2", + "case": "1.6.3", + "fs-extra": "^11.3.0", + "ignore": "^5.3.2", + "jsonschema": "^1.5.0", + "mime-types": "^2.1.35", + "minimatch": "^3.1.2", + "punycode": "^2.3.1", + "semver": "^7.7.2", + "table": "^6.9.0", + "yaml": "1.10.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "constructs": "^10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/@balena/dockerignore": { + "version": "1.0.2", + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/aws-cdk-lib/node_modules/ajv": { + "version": "8.17.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-regex": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/astral-regex": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/balanced-match": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/brace-expansion": { + "version": "1.1.12", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/aws-cdk-lib/node_modules/case": { + "version": "1.6.3", + "inBundle": true, + "license": "(MIT OR GPL-3.0-or-later)", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-convert": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-name": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/concat-map": { + "version": "0.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/emoji-regex": { + "version": "8.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fast-deep-equal": { + "version": "3.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fast-uri": { + "version": "3.0.6", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/aws-cdk-lib/node_modules/fs-extra": { + "version": "11.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/aws-cdk-lib/node_modules/graceful-fs": { + "version": "4.2.11", + "inBundle": true, + "license": "ISC" + }, + "node_modules/aws-cdk-lib/node_modules/ignore": { + "version": "5.3.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/aws-cdk-lib/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/json-schema-traverse": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/jsonfile": { + "version": "6.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/aws-cdk-lib/node_modules/jsonschema": { + "version": "1.5.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/aws-cdk-lib/node_modules/lodash.truncate": { + "version": "4.4.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/mime-db": { + "version": "1.52.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/aws-cdk-lib/node_modules/mime-types": { + "version": "2.1.35", + "inBundle": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/aws-cdk-lib/node_modules/minimatch": { + "version": "3.1.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/aws-cdk-lib/node_modules/punycode": { + "version": "2.3.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/aws-cdk-lib/node_modules/require-from-string": { + "version": "2.0.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/semver": { + "version": "7.7.2", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aws-cdk-lib/node_modules/slice-ansi": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/string-width": { + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/strip-ansi": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/table": { + "version": "6.9.0", + "inBundle": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/universalify": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/yaml": { + "version": "1.10.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001726", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz", + "integrity": "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/constructs": { + "version": "10.4.2", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.4.2.tgz", + "integrity": "sha512-wsNxBlAott2qg8Zv87q3eYZYgheb9lchtBfjHzzLHtXbttwSrHPs1NNQbBrmbb1YZvYg2+Vh0Dor76w4mFxJkA==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", + "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.176", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.176.tgz", + "integrity": "sha512-2nDK9orkm7M9ZZkjO3PjbEd3VUulQLyg5T9O3enJdFvUg46Hzd4DUvTvAuEgbdHYXyFsiG4A5sO9IzToMH1cDg==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-jest": { + "version": "29.4.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.0.tgz", + "integrity": "sha512-d423TJMnJGu80/eSgfQ5w/R+0zFJvdtTxwtF9KzFFunOpSeD+79lHJQIiAhluJoyGRbvj9NZJsl9WjCUo0ND7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "^2.1.0", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.2", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/deployment/cdk-solution-helper/package.json b/deployment/cdk-solution-helper/package.json new file mode 100644 index 000000000..188374d31 --- /dev/null +++ b/deployment/cdk-solution-helper/package.json @@ -0,0 +1,33 @@ +{ + "name": "cdk-solution-helper", + "version": "7.0.5", + "description": "helper to update references in cdk generated cfn template and package lambda assets", + "main": "index.js", + "scripts": { + "test": "jest" + }, + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com/solutions" + }, + "license": "Apache-2.0", + "devDependencies": { + "@types/adm-zip": "^0.5.2", + "@types/jest": "^29.5.6", + "@types/node": "^20.10.4", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "ts-node": "^10.9.2", + "typescript": "^5.3.3" + }, + "dependencies": { + "adm-zip": "^0.5.10", + "aws-cdk-lib": "^2.124.0" + }, + "overrides": { + "semver": "7.5.4" + }, + "resolutions": { + "semver": "7.5.4" + } +} diff --git a/deployment/cdk-solution-helper/tsconfig.json b/deployment/cdk-solution-helper/tsconfig.json new file mode 100644 index 000000000..394891a80 --- /dev/null +++ b/deployment/cdk-solution-helper/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es2020", + "module": "commonjs", + "moduleResolution": "node", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "ts-node": { + "esm": false + } +} \ No newline at end of file diff --git a/deployment/run-unit-tests.sh b/deployment/run-unit-tests.sh index 59e7933cb..0d0da6cc9 100755 --- a/deployment/run-unit-tests.sh +++ b/deployment/run-unit-tests.sh @@ -1,16 +1,52 @@ #!/bin/bash +# +# This script should be run from the repo's deployment directory +# cd deployment +# ./run-unit-tests.sh +# +[ "$DEBUG" == 'true' ] && set -x set -e -current_dir=$PWD -source_dir=$current_dir/../source +function headline(){ + echo "------------------------------------------------------------------------------" + echo "$1" + echo "------------------------------------------------------------------------------" +} -cd $source_dir/constructs -npm install -npm test +prepare_jest_coverage_report() { + local component_name=$(basename "$1") -cd $source_dir/image-handler -npm test + if [ ! -d "coverage" ]; then + echo "ValidationError: Missing required directory coverage after running unit tests" + exit 129 + fi -cd $source_dir/custom-resource -npm test \ No newline at end of file + # prepare coverage reports + rm -fr coverage/lcov-report + mkdir -p $coverage_reports_top_path/jest + coverage_report_path=$coverage_reports_top_path/jest/$component_name + rm -fr $coverage_report_path + mv coverage $coverage_report_path +} + +headline "[Setup] Configure paths" +template_dir="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +cdk_dir="$template_dir/../source/constructs" +image_handler_dir="$template_dir/../source/image-handler" +custom_resource_dir="$template_dir/../source/custom-resource" +metrics_utils_dir="$template_dir/../source/metrics-utils" +coverage_reports_top_path="$template_dir/../source/test/coverage-reports" + +headline "[Tests] Run unit tests" +declare -a packages=( + "$cdk_dir" + "$image_handler_dir" + "$custom_resource_dir" + "$metrics_utils_dir" +) +for package in "${packages[@]}"; do + cd "$package" + npm test + prepare_jest_coverage_report "$package" +done; \ No newline at end of file diff --git a/object_lambda_architecture.png b/object_lambda_architecture.png new file mode 100644 index 000000000..0c1e153e5 Binary files /dev/null and b/object_lambda_architecture.png differ diff --git a/source/.eslintignore b/source/.eslintignore new file mode 100644 index 000000000..8897a165d --- /dev/null +++ b/source/.eslintignore @@ -0,0 +1,5 @@ +**/*.js +*.d.ts +node_modules +coverage +**/test/* diff --git a/source/.eslintrc.json b/source/.eslintrc.json new file mode 100644 index 000000000..c08f49341 --- /dev/null +++ b/source/.eslintrc.json @@ -0,0 +1,46 @@ +{ + "root": true, + "env": { + "jest": true, + "node": true + }, + "plugins": ["@typescript-eslint", "import", "header"], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module", + "project": "**/tsconfig.json" + }, + "ignorePatterns": ["**/*.js", "**/node_modules/**"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:import/recommended", + "plugin:import/typescript", + "standard", + "plugin:jsdoc/recommended", + "plugin:prettier/recommended" + ], + "rules": { + "arrow-body-style": ["warn", "as-needed"], + "prefer-arrow-callback": ["warn"], + "no-inferrable-types": ["off", "ignore-params"], + "no-unused-vars": ["off"], + "no-useless-constructor": ["off"], + "no-throw-literal": ["off"], + + "header/header": ["error", "line", [" Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.", " SPDX-License-Identifier: Apache-2.0"], 2], + + "@typescript-eslint/no-inferrable-types": ["off", { "ignoreParameters": true, "ignoreProperties": true }], + "@typescript-eslint/no-useless-constructor": ["off"], + "@typescript-eslint/no-unused-vars": ["error", { "args": "none", "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }], + "@typescript-eslint/no-throw-literal": ["error"], + + "jsdoc/require-param-type": ["off"], + "jsdoc/require-returns-type": ["off"], + "jsdoc/newline-after-description": ["off"], + + "import/no-unresolved": 1, // warn only on Unable to resolve path import/no-unresolved + "dot-notation": "off" + } +} diff --git a/source/.prettierrc.yml b/source/.prettierrc.yml new file mode 100644 index 000000000..c05527fc7 --- /dev/null +++ b/source/.prettierrc.yml @@ -0,0 +1,11 @@ +# .prettierrc or .prettierrc.yaml +arrowParens: "always" +bracketSpacing: true +endOfLine: "lf" +htmlWhitespaceSensitivity: "css" +proseWrap: "preserve" +trailingComma: "es5" +tabWidth: 2 +semi: true +quoteProps: "as-needed" +printWidth: 120 \ No newline at end of file diff --git a/source/constructs/.gitignore b/source/constructs/.gitignore index ad34eb457..f60797b6a 100644 --- a/source/constructs/.gitignore +++ b/source/constructs/.gitignore @@ -1,13 +1,8 @@ *.js !jest.config.js -!issue-fixer/index.js *.d.ts node_modules # CDK asset staging directory .cdk.staging cdk.out - -# Parcel build directories -.cache -.build diff --git a/source/constructs/bin/constructs.ts b/source/constructs/bin/constructs.ts index b2a269b69..6a3a03e7d 100644 --- a/source/constructs/bin/constructs.ts +++ b/source/constructs/bin/constructs.ts @@ -1,8 +1,32 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import * as cdk from '@aws-cdk/core'; -import { ConstructsStack } from '../lib/constructs-stack'; +import { App, DefaultStackSynthesizer } from "aws-cdk-lib"; +import { ServerlessImageHandlerStack } from "../lib/serverless-image-stack"; -const app = new cdk.App(); -new ConstructsStack(app, 'ConstructsStack'); \ No newline at end of file +// CDK and default deployment +let synthesizer = new DefaultStackSynthesizer({ + generateBootstrapVersionRule: false, +}); + +// Solutions pipeline deployment +const { DIST_OUTPUT_BUCKET, SOLUTION_NAME, VERSION } = process.env; +if (DIST_OUTPUT_BUCKET && SOLUTION_NAME && VERSION) + synthesizer = new DefaultStackSynthesizer({ + generateBootstrapVersionRule: false, + fileAssetsBucketName: `${DIST_OUTPUT_BUCKET}-\${AWS::Region}`, + bucketPrefix: `${SOLUTION_NAME}/${VERSION}/`, + }); + +const app = new App(); +const solutionDisplayName = "Dynamic Image Transformation for Amazon CloudFront"; +const solutionVersion = VERSION ?? app.node.tryGetContext("solutionVersion"); +const description = `(${app.node.tryGetContext("solutionId")}) - ${solutionDisplayName}. Version ${solutionVersion}`; +// eslint-disable-next-line no-new +new ServerlessImageHandlerStack(app, "ServerlessImageHandlerStack", { + synthesizer, + description, + solutionId: app.node.tryGetContext("solutionId"), + solutionVersion, + solutionName: app.node.tryGetContext("solutionName"), +}); diff --git a/source/constructs/cdk.json b/source/constructs/cdk.json index ef09a7a9e..1c2e0428e 100644 --- a/source/constructs/cdk.json +++ b/source/constructs/cdk.json @@ -1,7 +1,8 @@ -{ - "app": "npx ts-node bin/constructs.ts", - "context": { - "@aws-cdk/core:enableStackNameDuplicates": "false", - "aws-cdk:enableDiffNoFail": "true" - } -} +{ + "app": "npx ts-node --prefer-ts-exts bin/constructs.ts", + "context": { + "solutionId": "SO0023", + "solutionVersion": "custom-v7.0.5", + "solutionName": "dynamic-image-transformation-for-amazon-cloudfront" + } +} \ No newline at end of file diff --git a/source/constructs/jest.config.js b/source/constructs/jest.config.js index 772f97490..25173761c 100644 --- a/source/constructs/jest.config.js +++ b/source/constructs/jest.config.js @@ -3,5 +3,9 @@ module.exports = { testMatch: ['**/*.test.ts'], transform: { '^.+\\.tsx?$': 'ts-jest' - } + }, + coverageReporters: [ + 'text', + ['lcov', { 'projectRoot': '../' }] + ] }; diff --git a/source/constructs/lib/api.json b/source/constructs/lib/api.json deleted file mode 100644 index 29844eb8e..000000000 --- a/source/constructs/lib/api.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "ServerlessImageHandler" - }, - "basePath": "/image", - "schemes": [ "https" ], - "paths": { - "/{proxy+}": { - "x-amazon-apigateway-any-method": { - "produces": [ "application/json" ], - "parameters": [ - { - "name": "proxy", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "signature", - "in": "query", - "description": "Signature of the image", - "required": false, - "type": "string" - } - ], - "responses": {}, - "x-amazon-apigateway-integration": { - "responses": { - "default": { "statusCode": "200" } - }, - "uri": { - "Fn::Join": [ - "", - [ - "arn:aws:apigateway:", - { - "Ref": "AWS::Region" - }, - ":", - "lambda:path/2015-03-31/functions/", - { - "Fn::GetAtt": [ - "ImageHandlerFunction", - "Arn" - ] - }, - "/invocations" - ] - ] - }, - "passthroughBehavior": "when_no_match", - "httpMethod": "POST", - "cacheNamespace": "xh7gp9", - "cacheKeyParameters": [ "method.request.path.proxy" ], - "contentHandling": "CONVERT_TO_TEXT", - "type": "aws_proxy" - } - } - } - }, - "x-amazon-apigateway-binary-media-types": [ - "*/*" - ] -} \ No newline at end of file diff --git a/source/constructs/lib/back-end/api-gateway-architecture.ts b/source/constructs/lib/back-end/api-gateway-architecture.ts new file mode 100644 index 000000000..65b9307ef --- /dev/null +++ b/source/constructs/lib/back-end/api-gateway-architecture.ts @@ -0,0 +1,192 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import * as path from "path"; +import { LambdaRestApiProps, RestApi } from "aws-cdk-lib/aws-apigateway"; +import { + AllowedMethods, + CachePolicy, + DistributionProps, + IOrigin, + OriginRequestPolicy, + OriginSslPolicy, + PriceClass, + ViewerProtocolPolicy, + Function, + FunctionCode, + FunctionEventType, + CfnDistribution, + Distribution, + FunctionRuntime, + IDistribution, +} from "aws-cdk-lib/aws-cloudfront"; +import { HttpOrigin } from "aws-cdk-lib/aws-cloudfront-origins"; +import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs"; +import { CfnLogGroup } from "aws-cdk-lib/aws-logs"; +import { Aspects, Aws, CfnCondition, Duration, Fn, Lazy } from "aws-cdk-lib"; +import { CloudFrontToApiGatewayToLambda } from "@aws-solutions-constructs/aws-cloudfront-apigateway-lambda"; + +import { addCfnSuppressRules } from "../../utils/utils"; +import * as api from "aws-cdk-lib/aws-apigateway"; +import { ConditionAspect } from "../../utils/aspects"; +import { readFileSync } from "fs"; +import { BackEnd, BackEndProps } from "./back-end-construct"; + +export interface ApiGatewayArchitectureProps extends BackEndProps { + originRequestPolicy: OriginRequestPolicy; + cachePolicy: CachePolicy; + imageHandlerLambdaFunction: NodejsFunction; + existingDistribution: IDistribution; +} + +export class ApiGatewayArchitecture { + public readonly imageHandlerCloudFrontDistribution: Distribution; + constructor(scope: BackEnd, props: ApiGatewayArchitectureProps) { + const apiGatewayRestApi = RestApi.fromRestApiId( + scope, + "ApiGatewayRestApi", + Lazy.string({ + produce: () => imageHandlerCloudFrontApiGatewayLambda.apiGateway.restApiId, + }) + ); + + const origin: IOrigin = new HttpOrigin(`${apiGatewayRestApi.restApiId}.execute-api.${Aws.REGION}.amazonaws.com`, { + originPath: "/image", + originSslProtocols: [OriginSslPolicy.TLS_V1_1, OriginSslPolicy.TLS_V1_2], + }); + + // Slice off the last line since CloudFront functions can't have module exports but we need to export the handler to unit test it. + const inlineCloudFrontFunction: string[] = readFileSync( + path.join(__dirname, "../../../image-handler/cloudfront-function-handlers/apig-request-modifier.js"), + "utf-8" + ) + .split("\n") + .slice(0, -1); + + const requestModifierFunction = new Function(scope, "ApigRequestModifierFunction", { + functionName: `sih-apig-request-modifier-${props.uuid}`, + code: FunctionCode.fromInline(inlineCloudFrontFunction.join("\n")), + runtime: FunctionRuntime.JS_2_0, + }); + Aspects.of(requestModifierFunction).add(new ConditionAspect(props.conditions.disableS3ObjectLambdaCondition)); + + const cloudFrontDistributionProps: DistributionProps = { + comment: "Image Handler Distribution for Dynamic Image Transformation for Amazon CloudFront", + defaultBehavior: { + origin, + allowedMethods: AllowedMethods.ALLOW_GET_HEAD, + viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS, + originRequestPolicy: props.originRequestPolicy, + cachePolicy: props.cachePolicy, + functionAssociations: [ + { + function: requestModifierFunction, + eventType: FunctionEventType.VIEWER_REQUEST, + }, + ], + }, + priceClass: props.cloudFrontPriceClass as PriceClass, + enableLogging: true, + logBucket: props.logsBucket, + logFilePrefix: "api-cloudfront/", + errorResponses: [ + { httpStatus: 500, ttl: Duration.minutes(10) }, + { httpStatus: 501, ttl: Duration.minutes(10) }, + { httpStatus: 502, ttl: Duration.minutes(10) }, + { httpStatus: 503, ttl: Duration.minutes(10) }, + { httpStatus: 504, ttl: Duration.minutes(10) }, + ], + }; + + const apiGatewayProps: LambdaRestApiProps = { + handler: props.imageHandlerLambdaFunction, + deployOptions: { + stageName: "image", + }, + binaryMediaTypes: ["*/*"], + defaultMethodOptions: { + authorizationType: api.AuthorizationType.NONE, + }, + }; + + const imageHandlerCloudFrontApiGatewayLambda = new CloudFrontToApiGatewayToLambda( + scope, + "ImageHandlerCloudFrontApiGatewayLambda", + { + existingLambdaObj: props.imageHandlerLambdaFunction, + insertHttpSecurityHeaders: false, + cloudFrontDistributionProps, + apiGatewayProps, + } + ); + this.imageHandlerCloudFrontDistribution = imageHandlerCloudFrontApiGatewayLambda.cloudFrontWebDistribution; + Aspects.of(imageHandlerCloudFrontApiGatewayLambda).add( + new ConditionAspect(props.conditions.disableS3ObjectLambdaCondition) + ); + + imageHandlerCloudFrontApiGatewayLambda.apiGateway.node.tryRemoveChild("Endpoint"); // we don't need the RestApi endpoint in the outputs + + const cfnDistribution = imageHandlerCloudFrontApiGatewayLambda.cloudFrontWebDistribution.node + .defaultChild as CfnDistribution; + cfnDistribution.addOverride("Properties.DistributionConfig.Origins.0.OriginShield", { + "Fn::If": [ + props.conditions.enableOriginShieldCondition.logicalId, + { Enabled: true, OriginShieldRegion: props.originShieldRegion }, + { Enabled: false }, + ], + }); + Aspects.of(cfnDistribution).add( + new ConditionAspect( + new CfnCondition(scope, "DeployAPIGDistribution", { + expression: Fn.conditionAnd( + props.conditions.disableS3ObjectLambdaCondition, + Fn.conditionNot(props.conditions.useExistingCloudFrontDistributionCondition) + ), + }) + ) + ); + + // Access the underlying CfnLogGroup to add conditions + const cfnLogGroup = imageHandlerCloudFrontApiGatewayLambda.apiGatewayLogGroup.node.defaultChild as CfnLogGroup; + + cfnLogGroup.addOverride( + "Properties.RetentionInDays", + Fn.conditionIf(props.conditions.isLogRetentionPeriodInfinite.logicalId, Aws.NO_VALUE, props.logRetentionPeriod) + ); + + addCfnSuppressRules(imageHandlerCloudFrontApiGatewayLambda.apiGateway, [ + { + id: "W59", + reason: + "AWS::ApiGateway::Method AuthorizationType is set to 'NONE' because API Gateway behind CloudFront does not support AWS_IAM authentication", + }, + ]); + addCfnSuppressRules(imageHandlerCloudFrontApiGatewayLambda.apiGateway.deploymentStage, [ + { + id: "W87", + reason: "Cache not enabled, using CloudFront for caching viewer response", + }, + ]); + addCfnSuppressRules(imageHandlerCloudFrontApiGatewayLambda.apiGatewayCloudWatchRole, [ + { + id: "F10", + reason: "Inline policy used in solutions construct", + }, + ]); + imageHandlerCloudFrontApiGatewayLambda.apiGateway.methods.forEach((method) => { + addCfnSuppressRules(method, [ + { + id: "W59", + reason: "No authorization currently on the API Gateway", + }, + ]); + }); + + imageHandlerCloudFrontApiGatewayLambda.apiGateway.node.tryRemoveChild("Endpoint"); // we don't need the RestApi endpoint in the outputs + scope.domainName = Fn.conditionIf( + props.conditions.useExistingCloudFrontDistributionCondition.logicalId, + props.existingDistribution.distributionDomainName, + imageHandlerCloudFrontApiGatewayLambda.cloudFrontWebDistribution.distributionDomainName + ).toString(); + } +} diff --git a/source/constructs/lib/back-end/back-end-construct.ts b/source/constructs/lib/back-end/back-end-construct.ts new file mode 100644 index 000000000..c2f980c54 --- /dev/null +++ b/source/constructs/lib/back-end/back-end-construct.ts @@ -0,0 +1,284 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import * as path from "path"; +import { + CacheHeaderBehavior, + CachePolicy, + CacheQueryStringBehavior, + Distribution, + OriginRequestHeaderBehavior, + OriginRequestPolicy, + OriginRequestQueryStringBehavior, +} from "aws-cdk-lib/aws-cloudfront"; +import { Policy, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam"; +import { Conditions } from "../common-resources/common-resources-construct"; +import { Runtime } from "aws-cdk-lib/aws-lambda"; +import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs"; +import { CfnLogGroup, LogGroup, QueryString } from "aws-cdk-lib/aws-logs"; +import { IBucket } from "aws-cdk-lib/aws-s3"; +import { ArnFormat, Aspects, Aws, CfnCondition, CfnResource, Duration, Fn, Stack } from "aws-cdk-lib"; +import { Construct } from "constructs"; +import { addCfnSuppressRules } from "../../utils/utils"; +import { SolutionConstructProps } from "../types"; +import { ApiGatewayArchitecture } from "./api-gateway-architecture"; +import { S3ObjectLambdaArchitecture } from "./s3-object-lambda-architecture"; +import { SolutionsMetrics, ExecutionDay } from "metrics-utils"; +import { ConditionAspect } from "../../utils/aspects"; +import { OperationalInsightsDashboard } from "../dashboard/ops-insights-dashboard"; +import { Dashboard } from "aws-cdk-lib/aws-cloudwatch"; + +export interface BackEndProps extends SolutionConstructProps { + readonly solutionVersion: string; + readonly solutionId: string; + readonly solutionName: string; + readonly sendAnonymousStatistics: CfnCondition; + readonly deployCloudWatchDashboard: CfnCondition; + readonly secretsManagerPolicy: Policy; + readonly logsBucket: IBucket; + readonly uuid: string; + readonly regionedBucketName: string; + readonly regionedBucketHash: string; + readonly cloudFrontPriceClass: string; + readonly conditions: Conditions; + readonly sharpSizeLimit: string; + readonly createSourceBucketsResource: (key?: string) => string[]; +} + +export class BackEnd extends Construct { + public domainName: string; + public olDomainName: string; + public operationalDashboard: Dashboard; + + constructor(scope: Construct, id: string, props: BackEndProps) { + super(scope, id); + + const imageHandlerLambdaFunctionRole = new Role(this, "ImageHandlerFunctionRole", { + assumedBy: new ServicePrincipal("lambda.amazonaws.com"), + path: "/", + }); + props.secretsManagerPolicy.attachToRole(imageHandlerLambdaFunctionRole); + + const imageHandlerLambdaFunctionRolePolicy = new Policy(this, "ImageHandlerFunctionPolicy", { + statements: [ + new PolicyStatement({ + actions: ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"], + resources: [ + Stack.of(this).formatArn({ + service: "logs", + resource: "log-group", + resourceName: "/aws/lambda/*", + arnFormat: ArnFormat.COLON_RESOURCE_NAME, + }), + ], + }), + new PolicyStatement({ + actions: ["s3:GetObject"], + resources: props.createSourceBucketsResource("/*"), + }), + new PolicyStatement({ + actions: ["s3:ListBucket"], + resources: props.createSourceBucketsResource(), + }), + new PolicyStatement({ + actions: ["s3:GetObject"], + resources: [`arn:aws:s3:::${props.fallbackImageS3Bucket}/${props.fallbackImageS3KeyBucket}`], + }), + new PolicyStatement({ + actions: ["rekognition:DetectFaces", "rekognition:DetectModerationLabels"], + resources: ["*"], + }), + ], + }); + + addCfnSuppressRules(imageHandlerLambdaFunctionRolePolicy, [ + { id: "W12", reason: "rekognition:DetectFaces requires '*' resources." }, + ]); + imageHandlerLambdaFunctionRole.attachInlinePolicy(imageHandlerLambdaFunctionRolePolicy); + + const imageHandlerLambdaFunction = new NodejsFunction(this, "ImageHandlerLambdaFunction", { + description: `${props.solutionName} (${props.solutionVersion}): Performs image edits and manipulations`, + memorySize: 1024, + runtime: Runtime.NODEJS_20_X, + timeout: Duration.seconds(29), + role: imageHandlerLambdaFunctionRole, + entry: path.join(__dirname, "../../../image-handler/index.ts"), + environment: { + AUTO_WEBP: props.autoWebP, + CORS_ENABLED: props.corsEnabled, + CORS_ORIGIN: props.corsOrigin, + SOURCE_BUCKETS: props.sourceBuckets, + REWRITE_MATCH_PATTERN: "", + REWRITE_SUBSTITUTION: "", + ENABLE_SIGNATURE: props.enableSignature, + SECRETS_MANAGER: props.secretsManager, + SECRET_KEY: props.secretsManagerKey, + ENABLE_DEFAULT_FALLBACK_IMAGE: props.enableDefaultFallbackImage, + DEFAULT_FALLBACK_IMAGE_BUCKET: props.fallbackImageS3Bucket, + DEFAULT_FALLBACK_IMAGE_KEY: props.fallbackImageS3KeyBucket, + ENABLE_S3_OBJECT_LAMBDA: props.enableS3ObjectLambda, + SOLUTION_VERSION: props.solutionVersion, + SOLUTION_ID: props.solutionId, + SHARP_SIZE_LIMIT: props.sharpSizeLimit, + }, + bundling: { + externalModules: ["sharp"], + nodeModules: ["sharp"], + commandHooks: { + beforeBundling(inputDir: string, outputDir: string): string[] { + return []; + }, + beforeInstall(inputDir: string, outputDir: string): string[] { + return []; + }, + afterBundling(inputDir: string, outputDir: string): string[] { + return [ + `cd ${outputDir}`, + "rm -rf node_modules/sharp && npm install --cpu=x64 --os=linux --libc=glibc sharp", // npm 10.4.0+ --libc=glibc is needed for the platform-specific deps to be installed when cross-compiling sharp from mac to linux + ]; + }, + }, + }, + }); + + const imageHandlerLogGroup = new LogGroup(this, "ImageHandlerLogGroup", { + logGroupName: `/aws/lambda/${imageHandlerLambdaFunction.functionName}`, + }); + + // Access the underlying CfnLogGroup to add conditions + const cfnLogGroup = imageHandlerLogGroup.node.defaultChild as CfnLogGroup; + + cfnLogGroup.addOverride( + "Properties.RetentionInDays", + Fn.conditionIf(props.conditions.isLogRetentionPeriodInfinite.logicalId, Aws.NO_VALUE, props.logRetentionPeriod) + ); + + addCfnSuppressRules(imageHandlerLogGroup, [ + { + id: "W84", + reason: "CloudWatch log group is always encrypted by default.", + }, + { + id: "W86", + reason: "Retention days are configured with property override", + }, + ]); + + const cachePolicy = new CachePolicy(this, "CachePolicy", { + cachePolicyName: `ServerlessImageHandler-${props.uuid}`, + defaultTtl: Duration.days(1), + minTtl: Duration.seconds(1), + maxTtl: Duration.days(365), + enableAcceptEncodingGzip: false, + headerBehavior: CacheHeaderBehavior.allowList("origin", "accept"), + queryStringBehavior: CacheQueryStringBehavior.all(), + }); + + const cachePolicyResource = this.node.findChild("CachePolicy").node.defaultChild as CfnResource; + cachePolicyResource.addOverride( + "Properties.CachePolicyConfig.ParametersInCacheKeyAndForwardedToOrigin.HeadersConfig.Headers", + { + "Fn::If": [props.conditions.autoWebPCondition.logicalId, ["origin", "accept"], ["origin"]], + } + ); + + const originRequestPolicy = new OriginRequestPolicy(this, "OriginRequestPolicy", { + originRequestPolicyName: `ServerlessImageHandler-${props.uuid}`, + headerBehavior: OriginRequestHeaderBehavior.allowList("origin", "accept"), + queryStringBehavior: OriginRequestQueryStringBehavior.all(), + }); + + const existingDistribution = Distribution.fromDistributionAttributes(this, "ExistingDistribution", { + domainName: "", + distributionId: props.existingCloudFrontDistributionId, + }); + + const apiGatewayArchitecture = new ApiGatewayArchitecture(this, { + imageHandlerLambdaFunction, + originRequestPolicy, + cachePolicy, + existingDistribution, + ...props, + }); + + const s3ObjectLambdaArchitecture = new S3ObjectLambdaArchitecture(this, { + imageHandlerLambdaFunction, + originRequestPolicy, + cachePolicy, + existingDistribution, + ...props, + }); + + const shortLogRetentionCondition: CfnCondition = new CfnCondition(this, "ShortLogRetentionCondition", { + expression: Fn.conditionOr( + Fn.conditionEquals(props.logRetentionPeriod.toString(), "1"), + Fn.conditionEquals(props.logRetentionPeriod.toString(), "3"), + Fn.conditionEquals(props.logRetentionPeriod.toString(), "5") + ), + }); + const solutionsMetrics = new SolutionsMetrics(this, "SolutionMetrics", { + uuid: props.uuid, + executionDay: Fn.conditionIf( + shortLogRetentionCondition.logicalId, + ExecutionDay.DAILY, + ExecutionDay.MONDAY + ).toString(), + }); + + const conditionalCloudFrontDistributionId = Fn.conditionIf( + props.conditions.useExistingCloudFrontDistributionCondition.logicalId, + existingDistribution.distributionId, + Fn.conditionIf( + props.conditions.enableS3ObjectLambdaCondition.logicalId, + s3ObjectLambdaArchitecture.imageHandlerCloudFrontDistribution.distributionId, + apiGatewayArchitecture.imageHandlerCloudFrontDistribution.distributionId + ).toString() + ).toString(); + + solutionsMetrics.addLambdaInvocationCount({ functionName: imageHandlerLambdaFunction.functionName }); + solutionsMetrics.addLambdaBilledDurationMemorySize({ + logGroups: [imageHandlerLogGroup], + queryDefinitionName: "BilledDurationMemorySizeQuery", + }); + solutionsMetrics.addQueryDefinition({ + logGroups: [imageHandlerLogGroup], + queryString: new QueryString({ + parseStatements: [ + `@message "requestType: 'Default'" as DefaultRequests`, + `@message "requestType: 'Thumbor'" as ThumborRequests`, + `@message "requestType: 'Custom'" as CustomRequests`, + `@message "Query param edits:" as QueryParamRequests`, + `@message "expires" as ExpiresRequests`, + ], + stats: + "count(DefaultRequests) as DefaultRequestsCount, count(ThumborRequests) as ThumborRequestsCount, count(CustomRequests) as CustomRequestsCount, count(QueryParamRequests) as QueryParamRequestsCount, count(ExpiresRequests) as ExpiresRequestsCount", + }), + queryDefinitionName: "RequestInfoQuery", + }); + + solutionsMetrics.addCloudFrontMetric({ + distributionId: conditionalCloudFrontDistributionId, + metricName: "Requests", + }); + solutionsMetrics.addCloudFrontMetric({ + distributionId: conditionalCloudFrontDistributionId, + metricName: "BytesDownloaded", + }); + + Aspects.of(solutionsMetrics).add(new ConditionAspect(props.sendAnonymousStatistics)); + + const operationalInsightsDashboard = new OperationalInsightsDashboard( + Stack.of(this), + "OperationalInsightsDashboard", + { + enabled: props.conditions.deployUICondition, + backendLambdaFunctionName: imageHandlerLambdaFunction.functionName, + cloudFrontDistributionId: conditionalCloudFrontDistributionId, + namespace: Aws.REGION, + } + ); + this.operationalDashboard = operationalInsightsDashboard.dashboard; + + Aspects.of(operationalInsightsDashboard).add(new ConditionAspect(props.deployCloudWatchDashboard)); + } +} diff --git a/source/constructs/lib/back-end/s3-object-lambda-architecture.ts b/source/constructs/lib/back-end/s3-object-lambda-architecture.ts new file mode 100644 index 000000000..eb39f1f4c --- /dev/null +++ b/source/constructs/lib/back-end/s3-object-lambda-architecture.ts @@ -0,0 +1,251 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import * as path from "path"; +import { CfnAccessPoint } from "aws-cdk-lib/aws-s3"; +import * as s3objectlambda from "aws-cdk-lib/aws-s3objectlambda"; +import { + AllowedMethods, + CachePolicy, + DistributionProps, + IOrigin, + OriginRequestPolicy, + PriceClass, + ViewerProtocolPolicy, + Function, + FunctionCode, + FunctionEventType, + CfnDistribution, + FunctionRuntime, + Distribution, + CfnOriginAccessControl, + IDistribution, +} from "aws-cdk-lib/aws-cloudfront"; +import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs"; +import { Aspects, Aws, CfnCondition, Duration, Fn } from "aws-cdk-lib"; +import { ConditionAspect } from "../../utils/aspects"; +import { readFileSync } from "fs"; +import { BackEnd, BackEndProps } from "./back-end-construct"; +import { Effect, Policy, PolicyStatement, ServicePrincipal } from "aws-cdk-lib/aws-iam"; +import { S3ObjectLambdaOrigin } from "./s3-object-lambda-origin"; +import { addCfnSuppressRules } from "../../utils/utils"; + +export interface S3ObjectLambdaArchitectureProps extends BackEndProps { + originRequestPolicy: OriginRequestPolicy; + cachePolicy: CachePolicy; + imageHandlerLambdaFunction: NodejsFunction; + existingDistribution: IDistribution; +} + +export class S3ObjectLambdaArchitecture { + public readonly imageHandlerCloudFrontDistribution: Distribution; + constructor(scope: BackEnd, props: S3ObjectLambdaArchitectureProps) { + const accessPointName = `sih-ap-${props.uuid}-${props.regionedBucketHash}`; + + const s3AccessPointPolicy = new PolicyStatement({ + effect: Effect.ALLOW, + actions: ["s3:*"], + resources: [ + `arn:aws:s3:${Aws.REGION}:${Aws.ACCOUNT_ID}:accesspoint/${accessPointName}`, + `arn:aws:s3:${Aws.REGION}:${Aws.ACCOUNT_ID}:accesspoint/${accessPointName}/object/*`, + ], + principals: [new ServicePrincipal("cloudfront.amazonaws.com")], + conditions: { + "ForAnyValue:StringEquals": { + "aws:CalledVia": "s3-object-lambda.amazonaws.com", + }, + }, + }).toJSON(); + const accessPoint = new CfnAccessPoint(scope, "AccessPoint", { + bucket: props.regionedBucketName, + name: accessPointName, + policy: { Statement: s3AccessPointPolicy }, + }); + Aspects.of(accessPoint).add(new ConditionAspect(props.conditions.enableS3ObjectLambdaCondition)); + + props.imageHandlerLambdaFunction.grantInvoke(new ServicePrincipal("cloudfront.amazonaws.com")); + + // Slice off the last line since CloudFront functions can't have module exports but we need to export the handler to unit test it. + const inlineResponseModifierCode: string[] = readFileSync( + path.join(__dirname, "../../../image-handler/cloudfront-function-handlers/ol-response-modifier.js"), + "utf-8" + ) + .split("\n") + .slice(0, -1); + + const responseModifierCloudFrontFunction = new Function(scope, "OlResponseModifierFunction", { + code: FunctionCode.fromInline(inlineResponseModifierCode.join("\n")), + functionName: `sih-ol-response-modifier-${props.uuid}`, + runtime: FunctionRuntime.JS_2_0, + }); + Aspects.of(responseModifierCloudFrontFunction).add( + new ConditionAspect(props.conditions.enableS3ObjectLambdaCondition) + ); + + // Slice off the last line since CloudFront functions can't have module exports but we need to export the handler to unit test it. + const inlineRequestModifierCode: string[] = readFileSync( + path.join(__dirname, "../../../image-handler/cloudfront-function-handlers/ol-request-modifier.js"), + "utf-8" + ) + .split("\n") + .slice(0, -1); + + const requestModifierCloudFrontFunction = new Function(scope, "OlRequestModifierFunction", { + code: FunctionCode.fromInline(inlineRequestModifierCode.join("\n")), + functionName: `sih-ol-request-modifier-${props.uuid}`, + runtime: FunctionRuntime.JS_2_0, + }); + Aspects.of(requestModifierCloudFrontFunction).add( + new ConditionAspect(props.conditions.enableS3ObjectLambdaCondition) + ); + + const objectLambdaAccessPointName = `sih-olap-${props.uuid}`; + const objectLambdaAccessPoint = new s3objectlambda.CfnAccessPoint(scope, "ObjectLambdaAccessPoint", { + objectLambdaConfiguration: { + supportingAccessPoint: accessPoint.attrArn, + transformationConfigurations: [ + { + actions: ["GetObject", "HeadObject"], + contentTransformation: { + AwsLambda: { + FunctionArn: props.imageHandlerLambdaFunction.functionArn, + }, + }, + }, + ], + }, + name: objectLambdaAccessPointName, + }); + Aspects.of(objectLambdaAccessPoint).add(new ConditionAspect(props.conditions.enableS3ObjectLambdaCondition)); + + const writeGetObjectResponsePolicy = new Policy(scope, "WriteGetObjectResponsePolicy", { + statements: [ + new PolicyStatement({ + actions: ["s3-object-lambda:WriteGetObjectResponse"], + resources: [objectLambdaAccessPoint.attrArn], + }), + ], + }); + Aspects.of(writeGetObjectResponsePolicy).add(new ConditionAspect(props.conditions.enableS3ObjectLambdaCondition)); + props.imageHandlerLambdaFunction.role?.attachInlinePolicy(writeGetObjectResponsePolicy); + + const origin: IOrigin = new S3ObjectLambdaOrigin( + `${objectLambdaAccessPoint.attrAliasValue}.s3.${Aws.REGION}.amazonaws.com`, + { originShieldEnabled: true, originShieldRegion: Aws.REGION, originPath: "/image", connectionAttempts: 1 } + ); + + const cloudFrontDistributionProps: DistributionProps = { + comment: "Image Handler Distribution for Dynamic Image Transformation for Amazon CloudFront", + defaultBehavior: { + origin, + allowedMethods: AllowedMethods.ALLOW_GET_HEAD, + viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS, + originRequestPolicy: props.originRequestPolicy, + cachePolicy: props.cachePolicy, + functionAssociations: [ + { + function: responseModifierCloudFrontFunction, + eventType: FunctionEventType.VIEWER_RESPONSE, + }, + { + function: requestModifierCloudFrontFunction, + eventType: FunctionEventType.VIEWER_REQUEST, + }, + ], + }, + priceClass: props.cloudFrontPriceClass as PriceClass, + enableLogging: true, + logBucket: props.logsBucket, + logFilePrefix: "api-cloudfront/", + errorResponses: [ + { httpStatus: 500, ttl: Duration.minutes(10) }, + { httpStatus: 501, ttl: Duration.minutes(10) }, + { httpStatus: 502, ttl: Duration.minutes(10) }, + { httpStatus: 503, ttl: Duration.minutes(10) }, + { httpStatus: 504, ttl: Duration.minutes(10) }, + ], + }; + + this.imageHandlerCloudFrontDistribution = new Distribution( + scope, + "ImageHandlerCloudFrontDistribution", + cloudFrontDistributionProps + ); + Aspects.of(this.imageHandlerCloudFrontDistribution).add( + new ConditionAspect( + new CfnCondition(scope, "DeployS3OLDistribution", { + expression: Fn.conditionAnd( + props.conditions.enableS3ObjectLambdaCondition, + Fn.conditionNot(props.conditions.useExistingCloudFrontDistributionCondition) + ), + }) + ) + ); + addCfnSuppressRules(this.imageHandlerCloudFrontDistribution, [ + { + id: "W70", + reason: + "Since the distribution uses the CloudFront domain name, CloudFront automatically sets the security policy to TLSv1 regardless of the value of MinimumProtocolVersion", + }, + ]); + + const conditionalCloudFrontDistributionId = Fn.conditionIf( + props.conditions.useExistingCloudFrontDistributionCondition.logicalId, + props.existingDistribution.distributionId, + this.imageHandlerCloudFrontDistribution.distributionId + ).toString(); + + const objectLambdaAccessPointPolicy = new s3objectlambda.CfnAccessPointPolicy( + scope, + "ObjectLambdaAccessPointPolicy", + { + objectLambdaAccessPoint: objectLambdaAccessPoint.ref, + policyDocument: { + Version: "2012-10-17", + Statement: [ + { + Effect: "Allow", + Principal: { + Service: "cloudfront.amazonaws.com", + }, + Action: "s3-object-lambda:Get*", + Resource: objectLambdaAccessPoint.attrArn, + Condition: { + StringEquals: { + "aws:SourceArn": `arn:aws:cloudfront::${Aws.ACCOUNT_ID}:distribution/${conditionalCloudFrontDistributionId}`, + }, + }, + }, + ], + }, + } + ); + + Aspects.of(objectLambdaAccessPointPolicy).add(new ConditionAspect(props.conditions.enableS3ObjectLambdaCondition)); + + const oac = new CfnOriginAccessControl(scope, `SIH-origin-access-control`, { + originAccessControlConfig: { + name: `SIH-origin-access-control-${props.uuid}`, + originAccessControlOriginType: "s3", + signingBehavior: "always", + signingProtocol: "sigv4", + }, + }); + Aspects.of(oac).add(new ConditionAspect(props.conditions.enableS3ObjectLambdaCondition)); + + const cfnDistribution = this.imageHandlerCloudFrontDistribution.node.defaultChild as CfnDistribution; + cfnDistribution.addPropertyOverride("DistributionConfig.Origins.0.OriginAccessControlId", oac.attrId); + cfnDistribution.addOverride("Properties.DistributionConfig.Origins.0.OriginShield", { + "Fn::If": [ + props.conditions.enableOriginShieldCondition.logicalId, + { Enabled: true, OriginShieldRegion: props.originShieldRegion }, + { Enabled: false }, + ], + }); + scope.olDomainName = Fn.conditionIf( + props.conditions.useExistingCloudFrontDistributionCondition.logicalId, + props.existingDistribution.distributionDomainName, + this.imageHandlerCloudFrontDistribution.domainName + ).toString(); + } +} diff --git a/source/constructs/lib/back-end/s3-object-lambda-origin.ts b/source/constructs/lib/back-end/s3-object-lambda-origin.ts new file mode 100644 index 000000000..6ae707b0e --- /dev/null +++ b/source/constructs/lib/back-end/s3-object-lambda-origin.ts @@ -0,0 +1,14 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { CfnDistribution, OriginBase, OriginProps } from "aws-cdk-lib/aws-cloudfront"; + +export class S3ObjectLambdaOrigin extends OriginBase { + public constructor(domainName: string, props: OriginProps = {}) { + super(domainName, props); + } + + protected renderS3OriginConfig(): CfnDistribution.S3OriginConfigProperty { + return {}; + } +} diff --git a/source/constructs/lib/common-resources/common-resources-construct.ts b/source/constructs/lib/common-resources/common-resources-construct.ts new file mode 100644 index 000000000..9ab2f2230 --- /dev/null +++ b/source/constructs/lib/common-resources/common-resources-construct.ts @@ -0,0 +1,141 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { Policy, PolicyStatement } from "aws-cdk-lib/aws-iam"; +import { IBucket } from "aws-cdk-lib/aws-s3"; +import { ArnFormat, Aws, CfnCondition, Fn, Stack, Tags } from "aws-cdk-lib"; +import { Construct } from "constructs"; +import { addCfnCondition } from "../../utils/utils"; +import { SolutionConstructProps } from "../types"; +import { CustomResourcesConstruct } from "./custom-resources/custom-resource-construct"; +import * as appreg from "@aws-cdk/aws-servicecatalogappregistry-alpha"; + +export interface CommonResourcesProps extends SolutionConstructProps { + readonly solutionId: string; + readonly solutionVersion: string; + readonly solutionName: string; +} + +export interface Conditions { + readonly deployUICondition: CfnCondition; + readonly enableSignatureCondition: CfnCondition; + readonly enableDefaultFallbackImageCondition: CfnCondition; + readonly enableCorsCondition: CfnCondition; + readonly autoWebPCondition: CfnCondition; + readonly enableOriginShieldCondition: CfnCondition; + readonly enableS3ObjectLambdaCondition: CfnCondition; + readonly disableS3ObjectLambdaCondition: CfnCondition; + readonly isLogRetentionPeriodInfinite: CfnCondition; + readonly useExistingCloudFrontDistributionCondition: CfnCondition; +} + +export interface AppRegistryApplicationProps { + readonly description: string; + readonly solutionId: string; + readonly applicationName: string; + readonly solutionName: string; + readonly solutionVersion: string; +} + +/** + * Construct that creates Common Resources for the solution. + */ +export class CommonResources extends Construct { + public readonly conditions: Conditions; + public readonly logsBucket: IBucket; + public readonly secretsManagerPolicy: Policy; + public readonly customResources: CustomResourcesConstruct; + + constructor(scope: Construct, id: string, props: CommonResourcesProps) { + super(scope, id); + + this.conditions = { + deployUICondition: new CfnCondition(this, "DeployDemoUICondition", { + expression: Fn.conditionEquals(props.deployUI, "Yes"), + }), + enableSignatureCondition: new CfnCondition(this, "EnableSignatureCondition", { + expression: Fn.conditionEquals(props.enableSignature, "Yes"), + }), + enableDefaultFallbackImageCondition: new CfnCondition(this, "EnableDefaultFallbackImageCondition", { + expression: Fn.conditionEquals(props.enableDefaultFallbackImage, "Yes"), + }), + enableCorsCondition: new CfnCondition(this, "EnableCorsCondition", { + expression: Fn.conditionEquals(props.corsEnabled, "Yes"), + }), + autoWebPCondition: new CfnCondition(this, "AutoWebPCondition", { + expression: Fn.conditionEquals(props.autoWebP, "Yes"), + }), + enableOriginShieldCondition: new CfnCondition(this, "EnableOriginShieldCondition", { + expression: Fn.conditionNot(Fn.conditionEquals(props.originShieldRegion, "Disabled")), + }), + enableS3ObjectLambdaCondition: new CfnCondition(this, "EnableS3ObjectLambdaCondition", { + expression: Fn.conditionEquals(props.enableS3ObjectLambda, "Yes"), + }), + disableS3ObjectLambdaCondition: new CfnCondition(this, "DisableS3ObjectLambdaCondition", { + expression: Fn.conditionNot(Fn.conditionEquals(props.enableS3ObjectLambda, "Yes")), + }), + isLogRetentionPeriodInfinite: new CfnCondition(this, "IsLogRetentionPeriodInfinite", { + expression: Fn.conditionEquals(props.logRetentionPeriod, "Infinite"), + }), + useExistingCloudFrontDistributionCondition: new CfnCondition(this, "UseExistingCloudFrontDistributionCondition", { + expression: Fn.conditionEquals(props.useExistingCloudFrontDistribution, "Yes"), + }), + }; + + this.secretsManagerPolicy = new Policy(this, "SecretsManagerPolicy", { + statements: [ + new PolicyStatement({ + actions: ["secretsmanager:GetSecretValue"], + resources: [ + Stack.of(this).formatArn({ + partition: Aws.PARTITION, + service: "secretsmanager", + region: Aws.REGION, + account: Aws.ACCOUNT_ID, + resource: "secret", + resourceName: `${props.secretsManager}*`, + arnFormat: ArnFormat.COLON_RESOURCE_NAME, + }), + ], + }), + ], + }); + addCfnCondition(this.secretsManagerPolicy, this.conditions.enableSignatureCondition); + + this.customResources = new CustomResourcesConstruct(this, "CustomResources", { + conditions: this.conditions, + secretsManagerPolicy: this.secretsManagerPolicy, + ...props, + }); + + this.logsBucket = this.customResources.createLogBucket(); + } + + public appRegistryApplication(props: AppRegistryApplicationProps) { + const stack = Stack.of(this); + const applicationType = "AWS-Solutions"; + + const application = new appreg.Application(stack, "AppRegistry", { + applicationName: props.applicationName, + description: `Service Catalog application to track and manage all your resources for the solution ${props.solutionName}`, + }); + application.associateApplicationWithStack(stack); + + Tags.of(application).add("Solutions:SolutionID", props.solutionId); + Tags.of(application).add("Solutions:SolutionName", props.solutionName); + Tags.of(application).add("Solutions:SolutionVersion", props.solutionVersion); + Tags.of(application).add("Solutions:ApplicationType", applicationType); + + const attributeGroup = new appreg.AttributeGroup(stack, "DefaultApplicationAttributeGroup", { + attributeGroupName: `A30-AppRegistry-${Aws.STACK_NAME}`, + description: "Attribute group for solution information", + attributes: { + applicationType, + version: props.solutionVersion, + solutionID: props.solutionId, + solutionName: props.solutionName, + }, + }); + attributeGroup.associateWith(application); + } +} diff --git a/source/constructs/lib/common-resources/custom-resources/custom-resource-construct.ts b/source/constructs/lib/common-resources/custom-resources/custom-resource-construct.ts new file mode 100644 index 000000000..6d7776ad3 --- /dev/null +++ b/source/constructs/lib/common-resources/custom-resources/custom-resource-construct.ts @@ -0,0 +1,422 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import * as path from "path"; +import { Effect, Policy, PolicyDocument, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam"; +import { Function as LambdaFunction, Runtime } from "aws-cdk-lib/aws-lambda"; +import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs"; +import { Bucket, IBucket } from "aws-cdk-lib/aws-s3"; +import { BucketDeployment, Source as S3Source } from "aws-cdk-lib/aws-s3-deployment"; +import { + ArnFormat, + Aspects, + Aws, + CfnCondition, + CfnResource, + CustomResource, + Duration, + Fn, + Lazy, + Stack, +} from "aws-cdk-lib"; +import { Construct } from "constructs"; +import { addCfnCondition, addCfnSuppressRules } from "../../../utils/utils"; + +import { SolutionConstructProps } from "../../types"; +import { CommonResourcesProps, Conditions } from "../common-resources-construct"; +import { ConditionAspect } from "../../../utils/aspects"; + +export interface CustomResourcesConstructProps extends CommonResourcesProps { + readonly conditions: Conditions; + readonly secretsManagerPolicy: Policy; +} + +export interface AnonymousMetricCustomResourceProps extends SolutionConstructProps { + readonly anonymousData: string; +} + +export interface ValidateSourceAndFallbackImageBucketsCustomResourceProps { + readonly sourceBuckets: string; + readonly fallbackImageS3Bucket: string; + readonly fallbackImageS3Key: string; + readonly enableS3ObjectLambda: string; +} + +export interface SetupCopyWebsiteCustomResourceProps { + readonly hostingBucket: Bucket; +} + +export interface SetupPutWebsiteConfigCustomResourceProps { + readonly hostingBucket: Bucket; + readonly apiEndpoint: string; +} + +export interface SetupValidateSecretsManagerProps { + readonly secretsManager: string; + readonly secretsManagerKey: string; +} + +export interface SetupValidateExistingDistributionProps { + readonly existingDistributionId: string; + readonly condition: CfnCondition; +} + +export class CustomResourcesConstruct extends Construct { + private readonly conditions: Conditions; + private readonly customResourceRole: Role; + private readonly customResourceLambda: LambdaFunction; + public readonly uuid: string; + public regionedBucketName: string; + public regionedBucketHash: string; + public appRegApplicationName: string; + public existingDistributionDomainName: string; + + constructor(scope: Construct, id: string, props: CustomResourcesConstructProps) { + super(scope, id); + + this.conditions = props.conditions; + + this.customResourceRole = new Role(this, "CustomResourceRole", { + assumedBy: new ServicePrincipal("lambda.amazonaws.com"), + path: "/", + inlinePolicies: { + CloudWatchLogsPolicy: new PolicyDocument({ + statements: [ + new PolicyStatement({ + effect: Effect.ALLOW, + actions: ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"], + resources: [ + Stack.of(this).formatArn({ + service: "logs", + resource: "log-group", + resourceName: "/aws/lambda/*", + arnFormat: ArnFormat.COLON_RESOURCE_NAME, + }), + ], + }), + ], + }), + S3AccessPolicy: new PolicyDocument({ + statements: [ + new PolicyStatement({ + actions: ["s3:ListBucket", "s3:GetBucketLocation"], + resources: this.createSourceBucketsResource(), + }), + new PolicyStatement({ + actions: ["s3:GetObject"], + resources: [`arn:aws:s3:::${props.fallbackImageS3Bucket}/${props.fallbackImageS3KeyBucket}`], + }), + new PolicyStatement({ + actions: [ + "s3:putBucketAcl", + "s3:putEncryptionConfiguration", + "s3:putBucketPolicy", + "s3:CreateBucket", + "s3:PutBucketOwnershipControls", + "s3:PutBucketTagging", + "s3:PutBucketVersioning", + ], + resources: [ + Stack.of(this).formatArn({ + partition: Aws.PARTITION, + service: "s3", + region: "", + account: "", + resource: "*", + arnFormat: ArnFormat.COLON_RESOURCE_NAME, + }), + ], + }), + new PolicyStatement({ + actions: ["s3:ListBucket"], + resources: [`arn:aws:s3:::sih-dummy-*`], + }), + ], + }), + EC2Policy: new PolicyDocument({ + statements: [ + new PolicyStatement({ + effect: Effect.ALLOW, + actions: ["ec2:DescribeRegions"], + resources: ["*"], + }), + ], + }), + AppRegistryPolicy: new PolicyDocument({ + statements: [ + new PolicyStatement({ + effect: Effect.ALLOW, + actions: ["cloudformation:DescribeStackResources"], + resources: [ + Stack.of(this).formatArn({ + partition: Aws.PARTITION, + service: "cloudformation", + region: Aws.REGION, + account: Aws.ACCOUNT_ID, + resource: "stack", + resourceName: `${Aws.STACK_NAME}/*`, + arnFormat: ArnFormat.SLASH_RESOURCE_NAME, + }), + ], + }), + new PolicyStatement({ + effect: Effect.ALLOW, + actions: ["servicecatalog:GetApplication"], + resources: [ + Stack.of(this).formatArn({ + partition: Aws.PARTITION, + service: "servicecatalog", + region: Aws.REGION, + account: Aws.ACCOUNT_ID, + resource: "applications", + resourceName: `*`, + arnFormat: ArnFormat.SLASH_RESOURCE_SLASH_RESOURCE_NAME, + }), + ], + }), + ], + }), + ExistingDistributionPolicy: new PolicyDocument({ + statements: [ + new PolicyStatement({ + effect: Effect.ALLOW, + actions: ["cloudfront:GetDistribution"], + resources: [ + Stack.of(this).formatArn({ + partition: Aws.PARTITION, + service: "cloudfront", + region: "", + account: Aws.ACCOUNT_ID, + resource: `distribution/${props.existingCloudFrontDistributionId}`, + arnFormat: ArnFormat.COLON_RESOURCE_NAME, + }), + ], + }), + ], + }), + }, + }); + + addCfnSuppressRules(this.customResourceRole, [ + { + id: "W11", + reason: + "Allow '*' because it is required for making DescribeRegions API call as it doesn't support resource-level permissions and require to choose all resources.", + }, + { + id: "F10", + reason: "Using inline policy", + }, + ]); + + props.secretsManagerPolicy.attachToRole(this.customResourceRole); + + this.customResourceLambda = new NodejsFunction(this, "CustomResourceFunction", { + description: `${props.solutionName} (${props.solutionVersion}): Custom resource`, + runtime: Runtime.NODEJS_20_X, + timeout: Duration.minutes(1), + memorySize: 128, + role: this.customResourceRole, + entry: path.join(__dirname, "../../../../custom-resource/index.ts"), + environment: { + SOLUTION_ID: props.solutionId, + RETRY_SECONDS: "5", + SOLUTION_VERSION: props.solutionVersion, + }, + }); + + const customResourceUuid = this.createCustomResource("CustomResourceUuid", this.customResourceLambda, { + Region: Aws.REGION, + CustomAction: "createUuid", + }); + this.uuid = customResourceUuid.getAttString("UUID"); + } + + public setupWebsiteHostingBucketPolicy(websiteHostingBucket: IBucket) { + const websiteHostingBucketPolicy = new Policy(this, "WebsiteHostingBucketPolicy", { + document: new PolicyDocument({ + statements: [ + new PolicyStatement({ + actions: ["s3:GetObject", "s3:PutObject"], + resources: [websiteHostingBucket.bucketArn + "/*"], + }), + ], + }), + roles: [this.customResourceRole], + }); + addCfnCondition(websiteHostingBucketPolicy, this.conditions.deployUICondition); + } + + public setupAnonymousMetric(props: AnonymousMetricCustomResourceProps) { + this.createCustomResource("CustomResourceAnonymousMetric", this.customResourceLambda, { + CustomAction: "sendMetric", + Region: Aws.REGION, + UUID: this.uuid, + AnonymousData: props.anonymousData, + CorsEnabled: props.corsEnabled, + SourceBuckets: props.sourceBuckets, + DeployDemoUi: props.deployUI, + LogRetentionPeriod: props.logRetentionPeriod, + AutoWebP: props.autoWebP, + EnableSignature: props.enableSignature, + EnableDefaultFallbackImage: props.enableDefaultFallbackImage, + EnableS3ObjectLambda: props.enableS3ObjectLambda, + OriginShieldRegion: props.originShieldRegion, + UseExistingCloudFrontDistribution: props.useExistingCloudFrontDistribution, + }); + } + + public setupValidateSourceAndFallbackImageBuckets(props: ValidateSourceAndFallbackImageBucketsCustomResourceProps) { + this.createCustomResource("CustomResourceCheckSourceBuckets", this.customResourceLambda, { + CustomAction: "checkSourceBuckets", + Region: Aws.REGION, + SourceBuckets: props.sourceBuckets, + }); + + const regionedBucketValidationResults = this.createCustomResource( + "CustomResourceCheckFirstBucketRegion", + this.customResourceLambda, + { + CustomAction: "checkFirstBucketRegion", + Region: Aws.REGION, + SourceBuckets: Fn.select(0, Fn.split(",", props.sourceBuckets)), // Only pass the first bucket to prevent unecessary execution on SourceBucketsParameter changes + UUID: this.uuid, + S3ObjectLambda: props.enableS3ObjectLambda, + } + ); + this.regionedBucketName = Lazy.string({ + produce: () => regionedBucketValidationResults.getAttString("BucketName"), + }); + this.regionedBucketHash = Lazy.string({ + produce: () => regionedBucketValidationResults.getAttString("BucketHash"), + }); + + const getAppRegApplicationNameResults = this.createCustomResource( + "CustomResourceGetAppRegApplicationName", + this.customResourceLambda, + { + CustomAction: "getAppRegApplicationName", + Region: Aws.REGION, + DefaultName: Fn.join("-", ["AppRegistry", Aws.STACK_NAME, Aws.REGION, Aws.ACCOUNT_ID]), + } + ); + this.appRegApplicationName = getAppRegApplicationNameResults.getAttString("ApplicationName"); + + this.createCustomResource( + "CustomResourceCheckFallbackImage", + this.customResourceLambda, + { + CustomAction: "checkFallbackImage", + FallbackImageS3Bucket: props.fallbackImageS3Bucket, + FallbackImageS3Key: props.fallbackImageS3Key, + }, + this.conditions.enableDefaultFallbackImageCondition + ); + } + + public setupCopyWebsiteCustomResource(props: SetupCopyWebsiteCustomResourceProps) { + // Stage static assets for the front-end from the local + /* eslint-disable no-new */ + const bucketDeployment = new BucketDeployment(this, "DeployWebsite", { + sources: [S3Source.asset(path.join(__dirname, "../../../../demo-ui"), { exclude: ["node_modules/*"] })], + destinationBucket: props.hostingBucket, + exclude: ["demo-ui-config.js"], + }); + Aspects.of(bucketDeployment).add(new ConditionAspect(this.conditions.deployUICondition)); + } + + public setupPutWebsiteConfigCustomResource(props: SetupPutWebsiteConfigCustomResourceProps) { + this.createCustomResource( + "PutWebsiteConfig", + this.customResourceLambda, + { + CustomAction: "putConfigFile", + Region: Aws.REGION, + ConfigItem: { apiEndpoint: props.apiEndpoint }, + DestS3Bucket: props.hostingBucket.bucketName, + DestS3key: "demo-ui-config.js", + }, + this.conditions.deployUICondition + ); + } + + public setupValidateSecretsManager(props: SetupValidateSecretsManagerProps) { + this.createCustomResource( + "CustomResourceCheckSecretsManager", + this.customResourceLambda, + { + CustomAction: "checkSecretsManager", + SecretsManagerName: props.secretsManager, + SecretsManagerKey: props.secretsManagerKey, + }, + this.conditions.enableSignatureCondition + ); + } + + public setupValidateExistingDistribution(props: SetupValidateExistingDistributionProps) { + const validateExistingDistributionResults = this.createCustomResource( + "CustomResourceValidateExistingDistribution", + this.customResourceLambda, + { + CustomAction: "validateExistingDistribution", + Region: Aws.REGION, + ExistingDistributionID: props.existingDistributionId, + }, + props.condition + ); + this.existingDistributionDomainName = validateExistingDistributionResults.getAttString("DistributionDomainName"); + } + + public createLogBucket(): IBucket { + const bucketSuffix = `${Aws.STACK_NAME}-${Aws.REGION}-${Aws.ACCOUNT_ID}`; + const logBucketCreationResult = this.createCustomResource("LogBucketCustomResource", this.customResourceLambda, { + CustomAction: "createCloudFrontLoggingBucket", + BucketSuffix: bucketSuffix, + }); + + const optInRegionAccessLogBucket = Bucket.fromBucketAttributes(this, "CloudFrontLoggingBucket", { + bucketName: Lazy.string({ + produce: () => logBucketCreationResult.getAttString("BucketName"), + }), + region: Lazy.string({ + produce: () => logBucketCreationResult.getAttString("Region"), + }), + }); + + return optInRegionAccessLogBucket; + } + + public createSourceBucketsResource(resourceName: string = "") { + return Fn.split( + ",", + Fn.sub( + `arn:aws:s3:::\${rest}${resourceName}`, + + { + rest: Fn.join( + `${resourceName},arn:aws:s3:::`, + Fn.split(",", Fn.join("", Fn.split(" ", Fn.ref("SourceBucketsParameter")))) + ), + } + ) + ); + } + + private createCustomResource( + id: string, + customResourceFunction: LambdaFunction, + props?: Record, + condition?: CfnCondition + ): CustomResource { + const customResource = new CustomResource(this, id, { + serviceToken: customResourceFunction.functionArn, + properties: props, + }); + + if (condition) { + (customResource.node.defaultChild as CfnResource).cfnOptions.condition = condition; + } + + return customResource; + } +} diff --git a/source/constructs/lib/constructs-stack.ts b/source/constructs/lib/constructs-stack.ts deleted file mode 100644 index f313b9e8a..000000000 --- a/source/constructs/lib/constructs-stack.ts +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import * as cdk from '@aws-cdk/core'; -import { ServerlessImageHandler, ServerlessImageHandlerProps } from './serverless-image-handler'; -import { CfnParameter } from '@aws-cdk/core'; - -const { VERSION } = process.env; - -/** - * @class ConstructsStack - */ -export class ConstructsStack extends cdk.Stack { - constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { - super(scope, id, props); - - // CFN parameters - const corsEnabledParameter = new CfnParameter(this, 'CorsEnabled', { - type: 'String', - description: `Would you like to enable Cross-Origin Resource Sharing (CORS) for the image handler API? Select 'Yes' if so.`, - default: 'No', - allowedValues: [ 'Yes', 'No' ] - }); - const corsOriginParameter = new CfnParameter(this, 'CorsOrigin', { - type: 'String', - description: `If you selected 'Yes' above, please specify an origin value here. A wildcard (*) value will support any origin. We recommend specifying an origin (i.e. https://example.domain) to restrict cross-site access to your API.`, - default: '*' - }); - const sourceBucketsParameter = new CfnParameter(this, 'SourceBuckets', { - type: 'String', - description: '(Required) List the buckets (comma-separated) within your account that contain original image files. If you plan to use Thumbor or Custom image requests with this solution, the source bucket for those requests will be the first bucket listed in this field.', - default: 'defaultBucket, bucketNo2, bucketNo3, ...', - allowedPattern: '.+' - }); - const deployDemoUiParameter = new CfnParameter(this, 'DeployDemoUI', { - type: 'String', - description: 'Would you like to deploy a demo UI to explore the features and capabilities of this solution? This will create an additional Amazon S3 bucket and Amazon CloudFront distribution in your account.', - default: 'Yes', - allowedValues: [ 'Yes', 'No' ] - }); - const logRetentionPeriodParameter = new CfnParameter(this, 'LogRetentionPeriod', { - type: 'Number', - description: 'This solution automatically logs events to Amazon CloudWatch. Select the amount of time for CloudWatch logs from this solution to be retained (in days).', - default: '1', - allowedValues: [ '1', '3', '5', '7', '14', '30', '60', '90', '120', '150', '180', '365', '400', '545', '731', '1827', '3653' ] - }); - const autoWebPParameter = new CfnParameter(this, 'AutoWebP', { - type: 'String', - description: `Would you like to enable automatic WebP based on accept headers? Select 'Yes' if so.`, - default: 'No', - allowedValues: [ 'Yes', 'No' ] - }); - const enableSignatureParameter = new CfnParameter(this, 'EnableSignature', { - type: 'String', - description: `Would you like to enable the signature? If so, select 'Yes' and provide SecretsManagerSecret and SecretsManagerKey values.`, - default: 'No', - allowedValues: [ 'Yes', 'No' ] - }); - const secretsManagerParameter = new CfnParameter(this, 'SecretsManagerSecret', { - type: 'String', - description: 'The name of AWS Secrets Manager secret. You need to create your secret under this name.', - default: '' - }); - const secretsManagerKeyParameter = new CfnParameter(this, 'SecretsManagerKey', { - type: 'String', - description: 'The name of AWS Secrets Manager secret key. You need to create secret key with this key name. The secret value would be used to check signature.', - default: '' - }); - const enableDefaultFallbackImageParameter = new CfnParameter(this, 'EnableDefaultFallbackImage', { - type: 'String', - description: `Would you like to enable the default fallback image? If so, select 'Yes' and provide FallbackImageS3Bucket and FallbackImageS3Key values.`, - default: 'No', - allowedValues: [ 'Yes', 'No' ] - }); - const fallbackImageS3BucketParameter = new CfnParameter(this, 'FallbackImageS3Bucket', { - type: 'String', - description: 'The name of the Amazon S3 bucket which contains the default fallback image. e.g. my-fallback-image-bucket', - default: '' - }); - const fallbackImageS3KeyParameter = new CfnParameter(this, 'FallbackImageS3Key', { - type: 'String', - description: 'The name of the default fallback image object key including prefix. e.g. prefix/image.jpg', - default: '' - }); - - // CFN descrption - this.templateOptions.description = `(SO0023) - Serverless Image Handler with aws-solutions-constructs: This template deploys and configures a serverless architecture that is optimized for dynamic image manipulation and delivery at low latency and cost. Leverages SharpJS for image processing. Template version ${VERSION}`; - - // CFN template format version - this.templateOptions.templateFormatVersion = '2010-09-09'; - - // CFN metadata - this.templateOptions.metadata = { - 'AWS::CloudFormation::Interface': { - ParameterGroups: [ - { - Label: { default: 'CORS Options' }, - Parameters: [ corsEnabledParameter.logicalId, corsOriginParameter.logicalId ] - }, - { - Label: { default: 'Image Sources' }, - Parameters: [ sourceBucketsParameter.logicalId ] - }, - { - Label: { default: 'Demo UI' }, - Parameters: [ deployDemoUiParameter.logicalId ] - }, - { - Label: { default: 'Event Logging' }, - Parameters: [ logRetentionPeriodParameter.logicalId ] - }, - { - Label: { default: 'Image URL Signature (Note: Enabling signature is not compatible with previous image URLs, which could result in broken image links. Please refer to the implementation guide for details: https://docs.aws.amazon.com/solutions/latest/serverless-image-handler/considerations.html)' }, - Parameters: [ enableSignatureParameter.logicalId, secretsManagerParameter.logicalId, secretsManagerKeyParameter.logicalId ] - }, - { - Label: { default: 'Default Fallback Image (Note: Enabling default fallback image returns the default fallback image instead of JSON object when error happens. Please refer to the implementation guide for details: https://docs.aws.amazon.com/solutions/latest/serverless-image-handler/considerations.html)' }, - Parameters: [ enableDefaultFallbackImageParameter.logicalId, fallbackImageS3BucketParameter.logicalId, fallbackImageS3KeyParameter.logicalId ] - }, - { - Label: { default: 'Auto WebP' }, - Parameters: [ autoWebPParameter.logicalId ] - } - ] - } - }; - - // Mappings - new cdk.CfnMapping(this, 'Send', { - mapping: { - AnonymousUsage: { - Data: 'Yes' - } - } - }); - - // Serverless Image Handler props - const sihProps: ServerlessImageHandlerProps = { - corsEnabledParameter, - corsOriginParameter, - sourceBucketsParameter, - deployDemoUiParameter, - logRetentionPeriodParameter, - autoWebPParameter, - enableSignatureParameter, - secretsManagerParameter, - secretsManagerKeyParameter, - enableDefaultFallbackImageParameter, - fallbackImageS3BucketParameter, - fallbackImageS3KeyParameter - }; - - // Serverless Image Handler Construct - const serverlessImageHander = new ServerlessImageHandler(this, 'ServerlessImageHandler', sihProps); - - // Outputs - new cdk.CfnOutput(this, 'ApiEndpoint', { - value: cdk.Fn.sub('https://${ImageHandlerDistribution.DomainName}'), - description: 'Link to API endpoint for sending image requests to.' - }); - new cdk.CfnOutput(this, 'DemoUrl', { - value: cdk.Fn.sub('https://${DemoDistribution.DomainName}/index.html'), - description: 'Link to the demo user interface for the solution.', - condition: serverlessImageHander.node.findChild('DeployDemoUICondition') as cdk.CfnCondition - }); - new cdk.CfnOutput(this, 'SourceBucketsOutput', { - value: sourceBucketsParameter.valueAsString, - description: 'Amazon S3 bucket location containing original image files.' - }).overrideLogicalId('SourceBuckets'); - new cdk.CfnOutput(this, 'CorsEnabledOutput', { - value: corsEnabledParameter.valueAsString, - description: 'Indicates whether Cross-Origin Resource Sharing (CORS) has been enabled for the image handler API.' - }).overrideLogicalId('CorsEnabled'); - new cdk.CfnOutput(this, 'CorsOriginOutput', { - value: corsOriginParameter.valueAsString, - description: 'Origin value returned in the Access-Control-Allow-Origin header of image handler API responses.', - condition: serverlessImageHander.node.findChild('EnableCorsCondition') as cdk.CfnCondition - }).overrideLogicalId('CorsOrigin'); - new cdk.CfnOutput(this, 'LogRetentionPeriodOutput', { - value: cdk.Fn.ref('LogRetentionPeriod'), - description: 'Number of days for event logs from Lambda to be retained in CloudWatch.' - }).overrideLogicalId('LogRetentionPeriod'); - } -} diff --git a/source/constructs/lib/dashboard/ops-insights-dashboard.ts b/source/constructs/lib/dashboard/ops-insights-dashboard.ts new file mode 100644 index 000000000..17cd31d49 --- /dev/null +++ b/source/constructs/lib/dashboard/ops-insights-dashboard.ts @@ -0,0 +1,121 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { Aws, CfnCondition, Duration } from "aws-cdk-lib"; +import { Dashboard, PeriodOverride, TextWidget } from "aws-cdk-lib/aws-cloudwatch"; +import { Size, DefaultGraphWidget, DefaultSingleValueWidget } from "./widgets"; +import { SIHMetrics, SUPPORTED_CLOUDFRONT_METRICS, SUPPORTED_LAMBDA_METRICS } from "./sih-metrics"; +import { Construct } from "constructs"; + +export interface OperationalInsightsDashboardProps { + readonly enabled: CfnCondition; + readonly backendLambdaFunctionName: string; + readonly cloudFrontDistributionId: string; + readonly namespace: string; +} +export class OperationalInsightsDashboard extends Construct { + public readonly dashboard: Dashboard; + constructor(scope: Construct, id: string, props: OperationalInsightsDashboardProps) { + super(scope, id); + this.dashboard = new Dashboard(this, id, { + dashboardName: `${Aws.STACK_NAME}-${props.namespace}-Operational-Insights-Dashboard`, + defaultInterval: Duration.days(7), + periodOverride: PeriodOverride.INHERIT, + }); + + if (!props.backendLambdaFunctionName || !props.cloudFrontDistributionId) { + throw new Error("backendLambdaFunctionName and cloudFrontDistributionId are required"); + } + + const metrics = new SIHMetrics({ + backendLambdaFunctionName: props.backendLambdaFunctionName, + cloudFrontDistributionId: props.cloudFrontDistributionId, + }); + + this.dashboard.addWidgets( + new TextWidget({ + markdown: "# Lambda", + width: Size.FULL_WIDTH, + height: 1, + }) + ); + + this.dashboard.addWidgets( + new DefaultGraphWidget({ + width: Size.THIRD_WIDTH, + height: Size.THIRD_WIDTH, + title: "Lambda Errors", + metric: metrics.createLambdaMetric(SUPPORTED_LAMBDA_METRICS.ERRORS), + label: "Lambda Errors", + unit: "Count", + }), + new DefaultGraphWidget({ + width: Size.THIRD_WIDTH, + height: Size.THIRD_WIDTH, + title: "Lambda Duration", + metric: metrics.createLambdaMetric(SUPPORTED_LAMBDA_METRICS.DURATION), + label: "Lambda Duration", + unit: "Milliseconds", + }), + new DefaultGraphWidget({ + width: Size.THIRD_WIDTH, + height: Size.THIRD_WIDTH, + title: "Lambda Invocations", + metric: metrics.createLambdaMetric(SUPPORTED_LAMBDA_METRICS.INVOCATIONS), + label: "Lambda Invocations", + unit: "Count", + }) + ); + + this.dashboard.addWidgets( + new TextWidget({ + markdown: "# CloudFront", + width: Size.FULL_WIDTH, + height: 1, + }) + ); + + this.dashboard.addWidgets( + new DefaultGraphWidget({ + title: "CloudFront Requests", + metric: metrics.createCloudFrontMetric(SUPPORTED_CLOUDFRONT_METRICS.REQUESTS), + label: "CloudFront Requests", + unit: "Count", + }), + new DefaultGraphWidget({ + title: "CloudFront Bytes Downloaded", + metric: metrics.createCloudFrontMetric(SUPPORTED_CLOUDFRONT_METRICS.BYTES_DOWNLOAD), + label: "CloudFront Bytes Downloaded", + unit: "Bytes", + }), + new DefaultSingleValueWidget({ + title: "Cache Hit Rate", + metric: metrics.getCacheHitRate(), + label: "Cache Hit Rate (%)", + }), + new DefaultSingleValueWidget({ + title: "Average Image Size", + metric: metrics.getAverageImageSize(), + label: "Average Image Size (Bytes)", + }) + ); + + this.dashboard.addWidgets( + new TextWidget({ + markdown: "# Overall", + width: Size.FULL_WIDTH, + height: 1, + }) + ); + + this.dashboard.addWidgets( + new DefaultSingleValueWidget({ + title: "Estimated Cost", + width: Size.FULL_WIDTH, + metric: metrics.getEstimatedCost(), + label: "Estimated Cost($)", + fullPrecision: true, + }) + ); + } +} diff --git a/source/constructs/lib/dashboard/sih-metrics.ts b/source/constructs/lib/dashboard/sih-metrics.ts new file mode 100644 index 000000000..d7c9a3b23 --- /dev/null +++ b/source/constructs/lib/dashboard/sih-metrics.ts @@ -0,0 +1,144 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { MathExpression, Metric } from "aws-cdk-lib/aws-cloudwatch"; + +/** + Properties for configuring metrics + */ +export interface MetricProps { + /** The name of the backend Lambda function to monitor */ + readonly backendLambdaFunctionName: string; + /** The CloudFront distribution ID to monitor */ + readonly cloudFrontDistributionId: string; +} + +export enum SUPPORTED_LAMBDA_METRICS { + ERRORS = "Errors", + INVOCATIONS = "Invocations", + DURATION = "Duration", +} + +export enum SUPPORTED_CLOUDFRONT_METRICS { + REQUESTS = "Requests", + BYTES_DOWNLOAD = "BytesDownloaded", +} + +enum Namespace { + LAMBDA = "AWS/Lambda", + CLOUDFRONT = "AWS/CloudFront", +} + +// Relevant AWS Pricing as of Dec 2024 for us-east-1 +const PRICING = { + CLOUDFRONT_BYTES: 0.085 / 1024 / 1024 / 1024, + CLOUDFRONT_REQUESTS: 0.0075 / 10000, + LAMBDA_DURATION: 1.66667 / 100000 / 1000, + LAMBDA_INVOCATIONS: 0.2 / 1000000, +}; + +/** + * Helper class for defining the underlying metrics available to the solution for ingestion into dashboard widgets + */ +export class SIHMetrics { + private readonly props; + + constructor(props: MetricProps) { + this.props = props; + } + + /** + * + * @param metric Creates a MathExpression to represent the running sum of a given metric + * @returns {MathExpression} The running sum of the provided metric + */ + runningSum(metric: Metric) { + return new MathExpression({ + expression: `RUNNING_SUM(metric)`, + usingMetrics: { + metric, + }, + }); + } + + /** + * Creates a Lambda metric with standard dimensions and statistics + * @param metricName The name of the Lambda metric to create + * @returns {Metric} Configured Lambda metric + */ + createLambdaMetric(metricName: SUPPORTED_LAMBDA_METRICS) { + return new Metric({ + namespace: Namespace.LAMBDA, + metricName, + dimensionsMap: { + FunctionName: this.props.backendLambdaFunctionName, + }, + statistic: "SUM", + }); + } + + /** + * Creates a CloudFront metric with standard dimensions and statistics + * @param metricName The name of the CloudFront metric to create + * @returns {Metric} Configured CloudFront metric + */ + createCloudFrontMetric(metricName: SUPPORTED_CLOUDFRONT_METRICS) { + return new Metric({ + namespace: Namespace.CLOUDFRONT, + metricName, + region: "us-east-1", + dimensionsMap: { + Region: "Global", + DistributionId: this.props.cloudFrontDistributionId, + }, + statistic: "SUM", + }); + } + + /** + * Calculates the cache hit rate for the Image Handler distribution. This is represented as the % of requests which were returned from the cache. + * @returns {MathExpression} The cache hit rate as a percentage + */ + getCacheHitRate() { + return new MathExpression({ + expression: "100 * (cloudFrontRequests - lambdaInvocations) / (cloudFrontRequests)", + usingMetrics: { + cloudFrontRequests: this.createCloudFrontMetric(SUPPORTED_CLOUDFRONT_METRICS.REQUESTS), + lambdaInvocations: this.createLambdaMetric(SUPPORTED_LAMBDA_METRICS.INVOCATIONS), + }, + }); + } + + /** + * Calculates estimated cost in USD based on AWS pricing as of Dec 2024 in us-east-1. + * Note: This is an approximation for the us-east-1 region only and includes + * CloudFront data transfer and requests, and Lambda duration and invocations. + * Some additional charges may apply for other services or regions. + * @returns {MathExpression} Estimated cost in USD + */ + getEstimatedCost() { + return new MathExpression({ + expression: `${PRICING.CLOUDFRONT_BYTES} * cloudFrontBytesDownloaded + ${PRICING.CLOUDFRONT_REQUESTS} * cloudFrontRequests + ${PRICING.LAMBDA_DURATION} * lambdaDuration + ${PRICING.LAMBDA_INVOCATIONS} * lambdaInvocations`, + usingMetrics: { + cloudFrontBytesDownloaded: this.createCloudFrontMetric(SUPPORTED_CLOUDFRONT_METRICS.BYTES_DOWNLOAD), + cloudFrontRequests: this.createCloudFrontMetric(SUPPORTED_CLOUDFRONT_METRICS.REQUESTS), + lambdaDuration: this.createLambdaMetric(SUPPORTED_LAMBDA_METRICS.DURATION), + lambdaInvocations: this.createLambdaMetric(SUPPORTED_LAMBDA_METRICS.INVOCATIONS), + }, + }); + } + + /** + * Calculates the average size of images served through CloudFront + * @returns {MathExpression} Average image size in bytes per request + */ + getAverageImageSize() { + return new MathExpression({ + expression: "cloudFrontBytesDownloaded / cloudFrontRequests", + usingMetrics: { + cloudFrontBytesDownloaded: this.createCloudFrontMetric(SUPPORTED_CLOUDFRONT_METRICS.BYTES_DOWNLOAD), + cloudFrontRequests: this.createCloudFrontMetric(SUPPORTED_CLOUDFRONT_METRICS.REQUESTS), + }, + }); + } +} diff --git a/source/constructs/lib/dashboard/widgets.ts b/source/constructs/lib/dashboard/widgets.ts new file mode 100644 index 000000000..477984110 --- /dev/null +++ b/source/constructs/lib/dashboard/widgets.ts @@ -0,0 +1,125 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { + GraphWidget, + GraphWidgetProps, + GraphWidgetView, + LegendPosition, + MathExpression, + Metric, + SingleValueWidget, + SingleValueWidgetProps, + Stats, +} from "aws-cdk-lib/aws-cloudwatch"; +import { Duration } from "aws-cdk-lib"; + +/** + * Represents standard widget sizes for CloudWatch dashboards + * Values indicate the number of grid units the widget will occupy + */ +export enum Size { + FULL_WIDTH = 24, + HALF_WIDTH = 12, + THIRD_WIDTH = 8, + QUARTER_WIDTH = 6, +} + +export interface WidgetProps { + width: number; + height: number; +} + +export interface DefaultGraphWidgetProps extends GraphWidgetProps { + title: string; + width?: number; + height?: number; + metric: Metric; + label: string; + unit: string; +} + +export interface DefaultSingleValueWidgetProps extends Omit { + title: string; + width?: number; + height?: number; + metric: Metric | MathExpression; + label: string; +} + +/** + * Creates a standardized graph widget which adds a RUNNING_SUM line to the metric being graphed + * @augments GraphWidget + */ +export class RunningSumGraphWidget extends GraphWidget { + constructor(props: GraphWidgetProps) { + if (!props?.left?.length) { + throw new Error("RunningSumGraphWidget requires at least one left metric to be defined"); + } + if (!props.leftYAxis || !props.leftYAxis.label) { + throw new Error("Left Y axis and Left Y axis label are required"); + } + super({ ...props, rightYAxis: { ...props.leftYAxis, label: `Running-Total ${props.leftYAxis?.label}`, min: 0 } }); + this.addRightMetric( + new MathExpression({ + expression: `RUNNING_SUM(metric)`, + usingMetrics: { + metric: props.left[0], + }, + }).with({ label: "Total" }) + ); + } +} + +/** + * Creates a standardized graph widget with running sum functionality + * @augments RunningSumGraphWidget + */ +export class DefaultGraphWidget extends RunningSumGraphWidget { + constructor(props: DefaultGraphWidgetProps) { + super({ + title: props.title, + width: props.width || Size.HALF_WIDTH, + height: props.height || Size.HALF_WIDTH, + view: GraphWidgetView.TIME_SERIES, + period: props.period || Duration.days(1), + liveData: props.liveData ?? true, + left: [ + props.metric.with({ + label: props.label, + }), + ], + leftYAxis: { + label: props.unit, + showUnits: false, + min: 0, + }, + legendPosition: LegendPosition.BOTTOM, + statistic: Stats.SUM, + }); + } +} + +/** + * Creates a standardized single value widget which adds the provided label to the metric being graphed + * and sets the period as time range by default. + * @augments SingleValueWidget + */ +export class DefaultSingleValueWidget extends SingleValueWidget { + constructor(props: DefaultSingleValueWidgetProps) { + super({ + title: props.title, + width: props.width || Size.HALF_WIDTH, + height: props.height || Size.QUARTER_WIDTH, + metrics: [ + props.metric.with({ + label: props.label, + }), + ], + period: props.period, + setPeriodToTimeRange: props.sparkline ? false : props.setPeriodToTimeRange ?? true, + fullPrecision: props.fullPrecision, + sparkline: props.sparkline, + }); + } +} diff --git a/source/constructs/lib/front-end/front-end-construct.ts b/source/constructs/lib/front-end/front-end-construct.ts new file mode 100644 index 000000000..5cdcda967 --- /dev/null +++ b/source/constructs/lib/front-end/front-end-construct.ts @@ -0,0 +1,63 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { Bucket, IBucket } from "aws-cdk-lib/aws-s3"; +import { Aspects } from "aws-cdk-lib"; +import { Construct } from "constructs"; +import { CloudFrontToS3 } from "@aws-solutions-constructs/aws-cloudfront-s3"; + +import { ConditionAspect } from "../../utils/aspects"; +import { addCfnSuppressRules } from "../../utils/utils"; +import { Conditions } from "../common-resources/common-resources-construct"; + +export interface FrontEndProps { + readonly logsBucket: IBucket; + readonly conditions: Conditions; +} + +/** + * Construct that creates the front-end resources for the solution. A CloudFront Distribution, S3 bucket. + */ +export class FrontEndConstruct extends Construct { + public readonly domainName: string; + public readonly websiteHostingBucket: Bucket; + + constructor(scope: Construct, id: string, props: FrontEndProps) { + super(scope, id); + + const cloudFrontToS3 = new CloudFrontToS3(this, "DistributionToS3", { + bucketProps: { serverAccessLogsBucket: undefined }, + cloudFrontDistributionProps: { + comment: "Demo UI Distribution for Dynamic Image Transformation for Amazon CloudFront", + enableLogging: true, + logBucket: props.logsBucket, + logFilePrefix: "ui-cloudfront/", + errorResponses: [ + { + httpStatus: 403, + responseHttpStatus: 200, + responsePagePath: "/index.html", + }, + { + httpStatus: 404, + responseHttpStatus: 200, + responsePagePath: "/index.html", + }, + ], + }, + insertHttpSecurityHeaders: false, + }); + + // S3 bucket does not require access logging, calls are logged by CloudFront + cloudFrontToS3.node.tryRemoveChild("S3LoggingBucket"); + addCfnSuppressRules(cloudFrontToS3.s3Bucket, [ + { id: "W35", reason: "This S3 bucket does not require access logging." }, + ]); + + this.domainName = cloudFrontToS3.cloudFrontWebDistribution.domainName; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.websiteHostingBucket = cloudFrontToS3.s3Bucket!; + + Aspects.of(this).add(new ConditionAspect(props.conditions.deployUICondition)); + } +} diff --git a/source/constructs/lib/serverless-image-handler.ts b/source/constructs/lib/serverless-image-handler.ts deleted file mode 100644 index da60248fa..000000000 --- a/source/constructs/lib/serverless-image-handler.ts +++ /dev/null @@ -1,771 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { Construct, CfnParameter } from "@aws-cdk/core"; -import * as cdkLambda from '@aws-cdk/aws-lambda'; -import * as cdkS3 from '@aws-cdk/aws-s3'; -import * as cdkIam from '@aws-cdk/aws-iam'; -import * as cdk from '@aws-cdk/core'; -import * as cdkCloudFront from '@aws-cdk/aws-cloudfront'; -import * as cdkApiGateway from '@aws-cdk/aws-apigateway'; -import * as cdkLogs from '@aws-cdk/aws-logs'; -import { CloudFrontToApiGatewayToLambda } from '@aws-solutions-constructs/aws-cloudfront-apigateway-lambda'; -import { CloudFrontToS3 } from '@aws-solutions-constructs/aws-cloudfront-s3'; -import apiBody from './api.json'; - -const { BUCKET_NAME, SOLUTION_NAME, VERSION } = process.env; - -/** - * Serverless Image Handler props interface - * These props are AWS CloudFormation parameters. - */ -export interface ServerlessImageHandlerProps { - readonly corsEnabledParameter: CfnParameter; - readonly corsOriginParameter: CfnParameter; - readonly sourceBucketsParameter: CfnParameter; - readonly deployDemoUiParameter: CfnParameter; - readonly logRetentionPeriodParameter: CfnParameter; - readonly autoWebPParameter: CfnParameter; - readonly enableSignatureParameter: CfnParameter; - readonly secretsManagerParameter: CfnParameter; - readonly secretsManagerKeyParameter: CfnParameter; - readonly enableDefaultFallbackImageParameter: CfnParameter; - readonly fallbackImageS3BucketParameter: CfnParameter; - readonly fallbackImageS3KeyParameter: CfnParameter; -} - -/** - * Serverless Image Handler custom resource config interface - */ -interface CustomResourceConfig { - readonly properties?: { path: string, value: any }[]; - readonly condition?: cdk.CfnCondition; - readonly dependencies?: cdk.CfnResource[]; -} - -/** - * cfn-nag suppression rule interface - */ -interface CfnNagSuppressRule { - readonly id: string; - readonly reason: string; -} - -/** - * Serverless Image Handler Construct using AWS Solutions Constructs patterns and AWS CDK - * @version 5.1.0 - */ -export class ServerlessImageHandler extends Construct { - constructor(scope: Construct, id: string, props: ServerlessImageHandlerProps) { - super(scope, id); - - try { - // CFN Conditions - const deployDemoUiCondition = new cdk.CfnCondition(this, 'DeployDemoUICondition', { - expression: cdk.Fn.conditionEquals(props.deployDemoUiParameter.valueAsString, 'Yes') - }); - deployDemoUiCondition.overrideLogicalId('DeployDemoUICondition'); - - const enableCorsCondition = new cdk.CfnCondition(this, 'EnableCorsCondition', { - expression: cdk.Fn.conditionEquals(props.corsEnabledParameter.valueAsString, 'Yes') - }); - enableCorsCondition.overrideLogicalId('EnableCorsCondition'); - - const enableSignatureCondition = new cdk.CfnCondition(this, 'EnableSignatureCondition', { - expression: cdk.Fn.conditionEquals(props.enableSignatureParameter.valueAsString, 'Yes') - }); - enableSignatureCondition.overrideLogicalId('EnableSignatureCondition'); - - const enableDefaultFallbackImageCondition = new cdk.CfnCondition(this, 'EnableDefaultFallbackImageCondition', { - expression: cdk.Fn.conditionEquals(props.enableDefaultFallbackImageParameter.valueAsString, 'Yes') - }); - enableDefaultFallbackImageCondition.overrideLogicalId('EnableDefaultFallbackImageCondition'); - - const isOptInRegion = new cdk.CfnCondition(this, 'IsOptInRegion', { - expression: cdk.Fn.conditionOr( - cdk.Fn.conditionEquals("af-south-1", cdk.Aws.REGION), - cdk.Fn.conditionEquals("ap-east-1", cdk.Aws.REGION), - cdk.Fn.conditionEquals("eu-south-1" , cdk.Aws.REGION), - cdk.Fn.conditionEquals("me-south-1" , cdk.Aws.REGION) - ) - }); - isOptInRegion.overrideLogicalId('IsOptInRegion'); - - const isNotOptInRegion = new cdk.CfnCondition(this, 'IsNotOptInRegion', { - expression: cdk.Fn.conditionNot(isOptInRegion) - }); - isNotOptInRegion.overrideLogicalId('IsNotOptInRegion') - - // ImageHandlerFunctionRole - const imageHandlerFunctionRole = new cdkIam.Role(this, 'ImageHandlerFunctionRole', { - assumedBy: new cdkIam.ServicePrincipal('lambda.amazonaws.com'), - path: '/', - roleName: `${cdk.Aws.STACK_NAME}ImageHandlerFunctionRole-${cdk.Aws.REGION}` - }); - const cfnImageHandlerFunctionRole = imageHandlerFunctionRole.node.defaultChild as cdkIam.CfnRole; - this.addCfnNagSuppressRules(cfnImageHandlerFunctionRole, [ - { - id: 'W28', - reason: 'Resource name validated and found to pose no risk to updates that require replacement of this resource.' - } - ]); - cfnImageHandlerFunctionRole.overrideLogicalId('ImageHandlerFunctionRole'); - - // ImageHandlerPolicy - const imageHandlerPolicy = new cdkIam.Policy(this, 'ImageHandlerPolicy', { - policyName: `${cdk.Aws.STACK_NAME}ImageHandlerPolicy`, - statements: [ - new cdkIam.PolicyStatement({ - actions: [ - 'logs:CreateLogStream', - 'logs:CreateLogGroup', - 'logs:PutLogEvents' - ], - resources: [ - `arn:${cdk.Aws.PARTITION}:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/lambda/*` - ] - }), - new cdkIam.PolicyStatement({ - actions: [ - 's3:GetObject', - 's3:PutObject', - 's3:ListBucket' - ], - resources: [ - `arn:${cdk.Aws.PARTITION}:s3:::*` - ] - }), - new cdkIam.PolicyStatement({ - actions: [ - 'rekognition:DetectFaces', - 'rekognition:DetectModerationLabels' - ], - resources: [ - '*' - ] - }) - ] - }); - imageHandlerPolicy.attachToRole(imageHandlerFunctionRole); - const cfnImageHandlerPolicy = imageHandlerPolicy.node.defaultChild as cdkIam.CfnPolicy; - this.addCfnNagSuppressRules(cfnImageHandlerPolicy, [ - { - id: 'W12', - reason: 'rekognition:DetectFaces requires \'*\' resources.' - } - ]); - cfnImageHandlerPolicy.overrideLogicalId('ImageHandlerPolicy'); - - // ImageHandlerFunction - const imageHandlerFunction = new cdkLambda.Function(this, 'ImageHanlderFunction', { - description: 'Serverless Image Handler - Function for performing image edits and manipulations.', - code: new cdkLambda.S3Code( - cdkS3.Bucket.fromBucketArn(this, 'ImageHandlerLambdaSource', `arn:${cdk.Aws.PARTITION}:s3:::${BUCKET_NAME}-${cdk.Aws.REGION}`), - `${SOLUTION_NAME}/${VERSION}/image-handler.zip` - ), - handler: 'index.handler', - runtime: cdkLambda.Runtime.NODEJS_12_X, - timeout: cdk.Duration.seconds(30), - memorySize: 1024, - role: imageHandlerFunctionRole, - environment: { - AUTO_WEBP: props.autoWebPParameter.valueAsString, - CORS_ENABLED: props.corsEnabledParameter.valueAsString, - CORS_ORIGIN: props.corsOriginParameter.valueAsString, - SOURCE_BUCKETS: props.sourceBucketsParameter.valueAsString, - REWRITE_MATCH_PATTERN: '', - REWRITE_SUBSTITUTION: '', - ENABLE_SIGNATURE: props.enableSignatureParameter.valueAsString, - SECRETS_MANAGER: props.secretsManagerParameter.valueAsString, - SECRET_KEY: props.secretsManagerKeyParameter.valueAsString, - ENABLE_DEFAULT_FALLBACK_IMAGE: props.enableDefaultFallbackImageParameter.valueAsString, - DEFAULT_FALLBACK_IMAGE_BUCKET: props.fallbackImageS3BucketParameter.valueAsString, - DEFAULT_FALLBACK_IMAGE_KEY: props.fallbackImageS3KeyParameter.valueAsString - } - }); - const cfnImageHandlerFunction = imageHandlerFunction.node.defaultChild as cdkLambda.CfnFunction; - this.addCfnNagSuppressRules(cfnImageHandlerFunction, [ - { - id: 'W58', - reason: 'False alarm: The Lambda function does have the permission to write CloudWatch Logs.' - } - ]); - cfnImageHandlerFunction.overrideLogicalId('ImageHandlerFunction'); - - // ImageHandlerLogGroup - const lambdaFunctionLogs = new cdkLogs.LogGroup(this, 'ImageHandlerLogGroup', { - logGroupName: `/aws/lambda/${imageHandlerFunction.functionName}` - }); - const cfnLambdaFunctionLogs = lambdaFunctionLogs.node.defaultChild as cdkLogs.CfnLogGroup; - cfnLambdaFunctionLogs.retentionInDays = props.logRetentionPeriodParameter.valueAsNumber; - this.addCfnNagSuppressRules(cfnLambdaFunctionLogs, [ - { - "id": "W84", - "reason": "Used to store store function info" - } - ]); - cfnLambdaFunctionLogs.overrideLogicalId('ImageHandlerLogGroup'); - - // CloudFrontToApiGatewayToLambda pattern - const cloudFrontApiGatewayLambda = new CloudFrontToApiGatewayToLambda(this, 'CloudFrontApiGatewayLambda', { - existingLambdaObj: imageHandlerFunction, - insertHttpSecurityHeaders: false - }); - const { apiGatewayLogGroup, apiGateway, cloudFrontWebDistribution } = cloudFrontApiGatewayLambda; - - // ApiLogs - const cfnApiGatewayLogGroup = apiGatewayLogGroup.node.defaultChild as cdkLogs.CfnLogGroup; - this.addCfnNagSuppressRules(cfnApiGatewayLogGroup, [ - { - "id": "W84", - "reason": "Used to store store api log info, not using kms" - }, - { - "id": "W86", - "reason": "Log retention specified in CloudFromation parameters." - } - ]); - cfnApiGatewayLogGroup.overrideLogicalId('ApiLogs'); - - // ImageHandlerApi - this.removeChildren(apiGateway, [ 'Endpoint', 'UsagePlan', 'Deployment', 'Default', 'DeploymentStage.prod' ]); - const cfnApiGateway = apiGateway.node.defaultChild as cdkApiGateway.CfnRestApi; - cfnApiGateway.name = 'ServerlessImageHandler'; - cfnApiGateway.body = apiBody; - cfnApiGateway.overrideLogicalId('ImageHandlerApi'); - - // ImageHandlerPermission - imageHandlerFunction.addPermission('ImageHandlerPermission', { - action: 'lambda:InvokeFunction', - sourceArn: `arn:${cdk.Aws.PARTITION}:execute-api:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:${apiGateway.restApiId}/*/*/*`, - principal: new cdkIam.ServicePrincipal('apigateway.amazonaws.com') - }); - (imageHandlerFunction.node.findChild('ImageHandlerPermission') as cdkLambda.CfnPermission).overrideLogicalId('ImageHandlerPermission'); - - // ApiLoggingRole - const cfnApiGatewayLogRole = cloudFrontApiGatewayLambda.apiGatewayCloudWatchRole.node.defaultChild as cdkIam.CfnRole; - cfnApiGatewayLogRole.overrideLogicalId('ApiLoggingRole'); - - // ApiAccountConfig - const cfnApiGatewayAccount = cloudFrontApiGatewayLambda.node.findChild('LambdaRestApiAccount') as cdkApiGateway.CfnAccount; - cfnApiGatewayAccount.overrideLogicalId('ApiAccountConfig'); - - // ImageHandlerApiDeployment - const cfnApiGatewayDeployment = new cdkApiGateway.CfnDeployment(this, 'ImageHanlderApiDeployment', { - restApiId: apiGateway.restApiId, - stageName: 'image', - stageDescription: { - accessLogSetting: { - destinationArn: cfnApiGatewayLogGroup.attrArn, - format: '$context.identity.sourceIp $context.identity.caller $context.identity.user [$context.requestTime] "$context.httpMethod $context.resourcePath $context.protocol" $context.status $context.responseLength $context.requestId' - } - } - }); - this.addCfnNagSuppressRules(cfnApiGatewayDeployment, [ - { - id: 'W68', - reason: 'The solution does not require the usage plan.' - } - ]); - this.addDependencies(cfnApiGatewayDeployment, [ cfnApiGatewayAccount ]); - cfnApiGatewayDeployment.overrideLogicalId('ImageHandlerApiDeployment'); - - // Logs - const cloudFrontToApiGateway = cloudFrontApiGatewayLambda.node.findChild('CloudFrontToApiGateway'); - const accessLogBucket = cloudFrontToApiGateway.node.findChild('CloudfrontLoggingBucket') as cdkS3.Bucket; - const cfnAccessLogBucket = accessLogBucket.node.defaultChild as cdkS3.CfnBucket; - cfnAccessLogBucket.cfnOptions.condition = isNotOptInRegion; - this.addCfnNagSuppressRules(cfnAccessLogBucket, [ - { - "id": "W35", - "reason": "Used to store access logs for other buckets" - } - ]); - cfnAccessLogBucket.overrideLogicalId('Logs'); - - // LogsBucketPolicy - const accessLogBucketPolicy = accessLogBucket.node.findChild('Policy') as cdkS3.BucketPolicy; - const cfnAccessLogBucketPolicy = accessLogBucketPolicy.node.defaultChild as cdkS3.CfnBucketPolicy; - (accessLogBucketPolicy.node.defaultChild as cdkS3.CfnBucketPolicy).cfnOptions.condition = isNotOptInRegion; - (accessLogBucketPolicy.node.defaultChild as cdkS3.CfnBucketPolicy).overrideLogicalId('LogsBucketPolicy'); - - //OptInRegionLogBucket - const optInRegionAccessLogBucket = cdkS3.Bucket.fromBucketAttributes(this, 'CloudFrontLoggingBucket', { - bucketName: - cdk.Fn.getAtt( - cdk.Lazy.stringValue({ - produce(context) { - return cfLoggingBucket.logicalId} - }), - 'bucketName').toString(), - region: 'us-east-1' - }); - - //OptInRegionLogBucketPolicy - const optInRegionPolicyStatement = cfnAccessLogBucketPolicy.policyDocument.toJSON().Statement[0]; - optInRegionPolicyStatement.Resource = ""; - - //Choose Log Bucket - const cloudFrontLogsBucket = cdk.Fn.conditionIf(isOptInRegion.logicalId, optInRegionAccessLogBucket.bucketRegionalDomainName, accessLogBucket.bucketRegionalDomainName).toString(); - - - //ImagehandlerCachePolicy - const cfnCachePolicy = new cdkCloudFront.CfnCachePolicy( - this, - 'CachePolicy', - { - cachePolicyConfig: { - name: `${cdk.Aws.STACK_NAME}-${cdk.Aws.REGION}-ImageHandlerCachePolicy`, - defaultTtl: 86400, - minTtl: 1, - maxTtl: 31536000, - parametersInCacheKeyAndForwardedToOrigin: { - cookiesConfig: {cookieBehavior: "none"}, - enableAcceptEncodingGzip: true, - headersConfig: { - headerBehavior: "whitelist", - headers:['origin', 'accept'] - }, - queryStringsConfig: { - queryStringBehavior: "whitelist", - queryStrings: ["signature"] - }, - } - } - }); - cfnCachePolicy.overrideLogicalId("ImageHandlerCachePolicy"); - - //ImageHandlerOriginRequestPolicy - const cfnOriginRequestPolicy = new cdkCloudFront.CfnOriginRequestPolicy( - this, - "OriginRequestPolicy", - { - originRequestPolicyConfig: { - cookiesConfig: {cookieBehavior: "none"}, - headersConfig: { - headerBehavior: "whitelist", - headers: ['origin', 'accept'] - }, - name: `${cdk.Aws.STACK_NAME}-${cdk.Aws.REGION}-ImageHandlerOriginRequestPolicy`, - queryStringsConfig: { - queryStringBehavior: "whitelist", - queryStrings: ["signature"] - }, - } - }); - cfnOriginRequestPolicy.overrideLogicalId("ImageHandlerOriginRequestPolicy"); - - // ImageHandlerDistribution - const cfnCloudFrontDistribution = cloudFrontWebDistribution.node.defaultChild as cdkCloudFront.CfnDistribution; - cfnCloudFrontDistribution.distributionConfig = { - origins: [{ - domainName: `${apiGateway.restApiId}.execute-api.${cdk.Aws.REGION}.amazonaws.com`, - id: apiGateway.restApiId, - originPath: '/image', - customOriginConfig: { - httpsPort: 443, - originProtocolPolicy: 'https-only', - originSslProtocols: [ 'TLSv1.1', 'TLSv1.2' ] - } - }], - enabled: true, - httpVersion: 'http2', - comment: 'Image handler distribution', - defaultCacheBehavior: { - allowedMethods: [ 'GET', 'HEAD' ], - targetOriginId: apiGateway.restApiId, - viewerProtocolPolicy: 'https-only', - cachePolicyId: cfnCachePolicy.ref, - originRequestPolicyId: cfnOriginRequestPolicy.ref - - }, - customErrorResponses: [ - { errorCode: 500, errorCachingMinTtl: 10 }, - { errorCode: 501, errorCachingMinTtl: 10 }, - { errorCode: 502, errorCachingMinTtl: 10 }, - { errorCode: 503, errorCachingMinTtl: 10 }, - { errorCode: 504, errorCachingMinTtl: 10 } - ], - priceClass: 'PriceClass_All', - logging: { - includeCookies: false, - bucket: cloudFrontLogsBucket, - prefix: 'image-handler-cf-logs/' - } - }; - cfnCloudFrontDistribution.overrideLogicalId('ImageHandlerDistribution'); - - // CloudFrontToS3 pattern - const cloudFrontToS3 = new CloudFrontToS3(this, 'CloudFrontToS3', { - bucketProps: { - versioned: false, - websiteIndexDocument: 'index.html', - websiteErrorDocument: 'index.html', - serverAccessLogsBucket: undefined, - accessControl: cdkS3.BucketAccessControl.PRIVATE - }, - insertHttpSecurityHeaders: false - }); - this.removeChildren(cloudFrontToS3, [ 'S3LoggingBucket', 'CloudfrontLoggingBucket' ]); - - // DemoBucket - const demoBucket = cloudFrontToS3.s3Bucket as cdkS3.Bucket; - const cfnDemoBucket = demoBucket.node.defaultChild as cdkS3.CfnBucket; - cfnDemoBucket.cfnOptions.condition = deployDemoUiCondition; - this.addCfnNagSuppressRules(cfnDemoBucket, [ - { - id: 'W35', - reason: 'This S3 bucket does not require access logging. API calls and image operations are logged to CloudWatch with custom reporting.' - } - ]) - cfnDemoBucket.overrideLogicalId('DemoBucket'); - - // DemoOriginAccessIdentity - const cfnDemoOriginAccessIdentity = cloudFrontToS3.node.findChild('CloudFrontOriginAccessIdentity') as cdkCloudFront.CfnCloudFrontOriginAccessIdentity; - cfnDemoOriginAccessIdentity.cloudFrontOriginAccessIdentityConfig = { - comment: `access-identity-${demoBucket.bucketName}` - }; - cfnDemoOriginAccessIdentity.cfnOptions.condition = deployDemoUiCondition; - cfnDemoOriginAccessIdentity.overrideLogicalId('DemoOriginAccessIdentity'); - - // DemoBucketPolicy - const demoBucketPolicy = demoBucket.node.findChild('Policy'); - const cfnDemoBucketPolicy = demoBucketPolicy.node.defaultChild as cdkS3.CfnBucketPolicy; - cfnDemoBucketPolicy.policyDocument = { - Statement: [ - { - Action: [ 's3:GetObject' ], - Effect: 'Allow', - Resource: `${demoBucket.bucketArn}/*`, - Principal: { - CanonicalUser: cfnDemoOriginAccessIdentity.attrS3CanonicalUserId - } - } - ] - }; - cfnDemoBucketPolicy.cfnOptions.condition = deployDemoUiCondition; - cfnDemoBucketPolicy.cfnOptions.metadata = {}; - cfnDemoBucketPolicy.overrideLogicalId('DemoBucketPolicy'); - - // DemoDistribution - const demoDistribution = cloudFrontToS3.cloudFrontWebDistribution; - const cfnDemoDistribution = demoDistribution.node.defaultChild as cdkCloudFront.CfnDistribution; - cfnDemoDistribution.distributionConfig = { - comment: 'Website distribution for solution', - origins: [{ - id: 'S3-solution-website', - domainName: demoBucket.bucketRegionalDomainName, - s3OriginConfig: { - originAccessIdentity: `origin-access-identity/cloudfront/${cfnDemoOriginAccessIdentity.ref}` - } - }], - defaultCacheBehavior: { - targetOriginId: 'S3-solution-website', - allowedMethods: [ 'GET', 'HEAD' ], - cachedMethods: [ 'GET', 'HEAD' ], - forwardedValues: { - queryString: false - }, - viewerProtocolPolicy: 'redirect-to-https' - }, - ipv6Enabled: true, - viewerCertificate: { - cloudFrontDefaultCertificate: true - }, - enabled: true, - httpVersion: 'http2', - logging: { - includeCookies: false, - bucket: cloudFrontLogsBucket, - prefix: 'demo-cf-logs/' - } - }; - cfnDemoDistribution.cfnOptions.condition = deployDemoUiCondition; - cfnDemoDistribution.overrideLogicalId('DemoDistribution'); - - // CustomResourceRole - const customResourceRole = new cdkIam.Role(this, 'CustomResourceRole', { - assumedBy: new cdkIam.ServicePrincipal('lambda.amazonaws.com'), - path: '/', - roleName: `${cdk.Aws.STACK_NAME}CustomResourceRole-${cdk.Aws.REGION}` - }); - const cfnCustomResourceRole = customResourceRole.node.defaultChild as cdkIam.CfnRole; - this.addCfnNagSuppressRules(cfnCustomResourceRole, [ - { - id: 'W28', - reason: 'Resource name validated and found to pose no risk to updates that require replacement of this resource.' - } - ]); - cfnCustomResourceRole.overrideLogicalId('CustomResourceRole'); - - // CustomResourcePolicy - const customResourcePolicy = new cdkIam.Policy(this, 'CustomResourcePolicy', { - policyName: `${cdk.Aws.STACK_NAME}CustomResourcePolicy`, - statements: [ - new cdkIam.PolicyStatement({ - actions: [ - 'logs:CreateLogStream', - 'logs:CreateLogGroup', - 'logs:PutLogEvents' - ], - resources: [ - `arn:${cdk.Aws.PARTITION}:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/lambda/*` - ] - }), - new cdkIam.PolicyStatement({ - actions: [ - 's3:putBucketAcl', - 's3:putEncryptionConfiguration', - 's3:putBucketPolicy', - 's3:CreateBucket', - 's3:GetObject', - 's3:PutObject', - 's3:ListBucket' - ], - resources: [ - `arn:${cdk.Aws.PARTITION}:s3:::*` - ] - }) - ] - }); - customResourcePolicy.attachToRole(customResourceRole); - const cfnCustomResourcePolicy = customResourcePolicy.node.defaultChild as cdkIam.CfnPolicy; - cfnCustomResourcePolicy.overrideLogicalId('CustomResourcePolicy'); - - // CustomResourceFunction - const customResourceFunction = new cdkLambda.Function(this, 'CustomResourceFunction', { - description: 'Serverless Image Handler - Custom resource', - code: new cdkLambda.S3Code( - cdkS3.Bucket.fromBucketArn(this, 'CustomResourceLambdaSource', `arn:${cdk.Aws.PARTITION}:s3:::${BUCKET_NAME}-${cdk.Aws.REGION}`), - `${SOLUTION_NAME}/${VERSION}/custom-resource.zip` - ), - handler: 'index.handler', - runtime: cdkLambda.Runtime.NODEJS_12_X, - timeout: cdk.Duration.seconds(60), - memorySize: 128, - role: customResourceRole, - environment: { - RETRY_SECONDS: '5' - } - }); - const cfnCustomResourceFuction = customResourceFunction.node.defaultChild as cdkLambda.CfnFunction; - this.addCfnNagSuppressRules(cfnCustomResourceFuction, [ - { - id: 'W58', - reason: 'False alarm: The Lambda function does have the permission to write CloudWatch Logs.' - } - ]); - cfnCustomResourceFuction.overrideLogicalId('CustomResourceFunction'); - - // CustomResourceLogGroup - const customResourceLogGroup = new cdkLogs.LogGroup(this, 'CustomResourceLogGroup', { - logGroupName: `/aws/lambda/${customResourceFunction.functionName}` - }); - const cfnCustomResourceLogGroup = customResourceLogGroup.node.defaultChild as cdkLogs.CfnLogGroup; - cfnCustomResourceLogGroup.retentionInDays = props.logRetentionPeriodParameter.valueAsNumber; - this.addCfnNagSuppressRules(cfnCustomResourceLogGroup, [ - { - "id": "W84", - "reason": "Used to store store function info, no kms used" - } - ]); - cfnCustomResourceLogGroup.overrideLogicalId('CustomResourceLogGroup'); - - // CustomResourceCopyS3 - this.createCustomResource('CustomResourceCopyS3', customResourceFunction, { - properties: [ - { path: 'Region', value: cdk.Aws.REGION }, - { path: 'manifestKey', value: `${SOLUTION_NAME}/${VERSION}/demo-ui-manifest.json` }, - { path: 'sourceS3Bucket', value: `${BUCKET_NAME}-${cdk.Aws.REGION}` }, - { path: 'sourceS3key', value: `${SOLUTION_NAME}/${VERSION}/demo-ui` }, - { path: 'destS3Bucket', value: demoBucket.bucketName }, - { path: 'version', value: VERSION }, - { path: 'customAction', value: 'copyS3assets' }, - ], - condition: deployDemoUiCondition, - dependencies: [ cfnCustomResourceRole, cfnCustomResourcePolicy ] - }); - - // CustomResourceConfig - this.createCustomResource('CustomResourceConfig', customResourceFunction, { - properties: [ - { path: 'Region', value: cdk.Aws.REGION }, - { path: 'configItem', value: { apiEndpoint: `https://${cloudFrontWebDistribution.distributionDomainName}` } }, - { path: 'destS3Bucket', value: demoBucket.bucketName }, - { path: 'destS3key', value: 'demo-ui-config.js' }, - { path: 'customAction', value: 'putConfigFile' }, - ], - condition: deployDemoUiCondition, - dependencies: [ cfnCustomResourceRole, cfnCustomResourcePolicy ] - }); - - // CustomResourceUuid - const customResourceUuid = this.createCustomResource('CustomResourceUuid', customResourceFunction, { - properties: [ - { path: 'Region', value: cdk.Aws.REGION }, - { path: 'customAction', value: 'createUuid' } - ], - dependencies: [ cfnCustomResourceRole, cfnCustomResourcePolicy ] - }); - - // CustomResourceAnonymousMetric - this.createCustomResource('CustomResourceAnonymousMetric', customResourceFunction, { - properties: [ - { path: 'Region', value: cdk.Aws.REGION }, - { path: 'solutionId', value: 'SO0023' }, - { path: 'UUID', value: cdk.Fn.getAtt(customResourceUuid.logicalId, 'UUID').toString() }, - { path: 'version', value: VERSION }, - { path: 'anonymousData', value: cdk.Fn.findInMap('Send', 'AnonymousUsage', 'Data') }, - { path: 'enableSignature', value: props.enableSignatureParameter.valueAsString }, - { path: 'enableDefaultFallbackImage', value: props.enableDefaultFallbackImageParameter.valueAsString }, - { path: 'customAction', value: 'sendMetric' } - ], - dependencies: [ cfnCustomResourceRole, cfnCustomResourcePolicy ] - }); - - // CustomResourceCheckSourceBuckets - this.createCustomResource('CustomResourceCheckSourceBuckets', customResourceFunction, { - properties: [ - { path: 'Region', value: cdk.Aws.REGION }, - { path: 'sourceBuckets', value: props.sourceBucketsParameter.valueAsString }, - { path: 'customAction', value: 'checkSourceBuckets' }, - ], - dependencies: [ cfnCustomResourceRole, cfnCustomResourcePolicy ] - }); - - // SecretsManagerPolicy - const secretsManagerPolicy = new cdkIam.Policy(this, 'secretsManagerPolicy', { - statements: [ - new cdkIam.PolicyStatement({ - actions: [ - 'secretsmanager:GetSecretValue' - ], - resources: [ - `arn:${cdk.Aws.PARTITION}:secretsmanager:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:secret:${props.secretsManagerParameter.valueAsString}*` - ] - }) - ] - }); - secretsManagerPolicy.attachToRole(customResourceRole); - secretsManagerPolicy.attachToRole(imageHandlerFunctionRole); - const cfnSecretsManagerPolicy = secretsManagerPolicy.node.defaultChild as cdkIam.CfnPolicy; - cfnSecretsManagerPolicy.cfnOptions.condition = enableSignatureCondition; - cfnSecretsManagerPolicy.overrideLogicalId('SecretsManagerPolicy'); - - // CustomResourceCheckSecretsManager - this.createCustomResource('CustomResourceCheckSecretsManager', customResourceFunction, { - properties: [ - { path: 'customAction', value: 'checkSecretsManager' }, - { path: 'secretsManagerName', value: props.secretsManagerParameter.valueAsString }, - { path: 'secretsManagerKey', value: props.secretsManagerKeyParameter.valueAsString } - ], - condition: enableSignatureCondition, - dependencies: [ cfnCustomResourceRole, cfnCustomResourcePolicy, cfnSecretsManagerPolicy ] - }); - - // CustomResourceCheckFallbackImage - this.createCustomResource('CustomResourceCheckFallbackImage', customResourceFunction, { - properties: [ - { path: 'customAction', value: 'checkFallbackImage' }, - { path: 'fallbackImageS3Bucket', value: props.fallbackImageS3BucketParameter.valueAsString }, - { path: 'fallbackImageS3Key', value: props.fallbackImageS3KeyParameter.valueAsString } - ], - condition: enableDefaultFallbackImageCondition, - dependencies: [ cfnCustomResourceRole, cfnCustomResourcePolicy ] - }); - - const bucketSuffix = cdk.Aws.STACK_NAME + cdk.Aws.REGION + cdk.Aws.ACCOUNT_ID; - const cfLoggingBucket = this.createCustomResource('CustomCFLoggingBucket', customResourceFunction, { - properties: [ - { path: 'customAction', value: 'createCFLoggingBucket' }, - { path: 'stackName', value: cdk.Aws.STACK_NAME }, - { path: 'bucketSuffix', value: bucketSuffix }, - { path: 'policy', value: optInRegionPolicyStatement } - ], - condition: isOptInRegion, - dependencies: [ cfnCustomResourceRole, cfnCustomResourcePolicy ] - - }); - } catch (error) { - console.error(error); - } - } - - /** - * Adds cfn-nag suppression rules to the AWS CloudFormation resource metadata. - * @param {cdk.CfnResource} resource Resource to add cfn-nag suppression rules - * @param {CfnNagSuppressRule[]} rules Rules to suppress - */ - addCfnNagSuppressRules(resource: cdk.CfnResource, rules: CfnNagSuppressRule[]) { - resource.addMetadata('cfn_nag', { - rules_to_suppress: rules - }); - } - - /** - * Adds dependencies to the AWS CloudFormation resource. - * @param {cdk.CfnResource} resource Resource to add AWS CloudFormation dependencies - * @param {cdk.CfnResource[]} dependencies Dependencies to be added to the AWS CloudFormation resource - */ - addDependencies(resource: cdk.CfnResource, dependencies: cdk.CfnResource[]) { - for (let dependency of dependencies) { - resource.addDependsOn(dependency); - } - } - - /** - * Removes AWS CDK created children from the AWS CloudFormation resource. - * @param {cdk.IConstruct} resource Resource to delete children - * @param {string[]} children The list of children to delete from the resource - */ - removeChildren(resource: cdk.IConstruct, children: string[]) { - for (let child of children) { - resource.node.tryRemoveChild(child); - } - } - - /** - * Removes all dependent children of the resource. - * @param {cdk.IConstruct} resource Resource to delete all dependent children - */ - removeAllChildren(resource: cdk.IConstruct) { - let children = resource.node.children; - for (let child of children) { - this.removeAllChildren(child); - resource.node.tryRemoveChild(child.node.id); - } - } - - /** - * Creates custom resource to the AWS CloudFormation template. - * @param {string} id Custom resource ID - * @param {cdkLambda.Function} customResourceFunction Custom resource Lambda function - * @param {CustomResourceConfig} config Custom resource configuration - * @return {cdk.CfnCustomResource} - */ - createCustomResource(id: string, customResourceFunction: cdkLambda.Function, config?: CustomResourceConfig): cdk.CfnCustomResource { - const customResource = new cdk.CfnCustomResource(this, id, { - serviceToken: customResourceFunction.functionArn - }); - customResource.addOverride('Type', 'Custom::CustomResource'); - customResource.overrideLogicalId(id); - - if (config) { - const { properties, condition, dependencies } = config; - - if (properties) { - for (let property of properties) { - customResource.addPropertyOverride(property.path, property.value); - } - } - - if (dependencies) { - this.addDependencies(customResource, dependencies); - } - - customResource.cfnOptions.condition = condition; - } - - return customResource; - } -} \ No newline at end of file diff --git a/source/constructs/lib/serverless-image-stack.ts b/source/constructs/lib/serverless-image-stack.ts new file mode 100644 index 000000000..b098dcbb2 --- /dev/null +++ b/source/constructs/lib/serverless-image-stack.ts @@ -0,0 +1,470 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { PriceClass } from "aws-cdk-lib/aws-cloudfront"; +import { + Aspects, + CfnCondition, + CfnMapping, + CfnOutput, + CfnParameter, + CfnRule, + Fn, + Stack, + StackProps, + Tags, +} from "aws-cdk-lib"; +import { Construct } from "constructs"; +import { ConditionAspect, SuppressLambdaFunctionCfnRulesAspect } from "../utils/aspects"; +import { BackEnd } from "./back-end/back-end-construct"; +import { CommonResources } from "./common-resources/common-resources-construct"; +import { FrontEndConstruct as FrontEnd } from "./front-end/front-end-construct"; +import { SolutionConstructProps, YesNo } from "./types"; + +export interface ServerlessImageHandlerStackProps extends StackProps { + readonly solutionId: string; + readonly solutionName: string; + readonly solutionVersion: string; +} + +export class ServerlessImageHandlerStack extends Stack { + constructor(scope: Construct, id: string, props: ServerlessImageHandlerStackProps) { + super(scope, id, props); + + const corsEnabledParameter = new CfnParameter(this, "CorsEnabledParameter", { + type: "String", + description: `Would you like to enable Cross-Origin Resource Sharing (CORS) for the image handler API? Select 'Yes' if so.`, + allowedValues: ["Yes", "No"], + default: "No", + }); + + const corsOriginParameter = new CfnParameter(this, "CorsOriginParameter", { + type: "String", + description: `If you selected 'Yes' above, please specify an origin value here. A wildcard (*) value will support any origin. We recommend specifying an origin (i.e. https://example.domain) to restrict cross-site access to your API.`, + default: "*", + }); + + const sourceBucketsParameter = new CfnParameter(this, "SourceBucketsParameter", { + type: "String", + description: + "(Required) List the buckets (comma-separated) within your account that contain original image files. If you plan to use Thumbor or Custom image requests with this solution, the source bucket for those requests will default to the first bucket listed in this field.", + allowedPattern: ".+", + default: "defaultBucket, bucketNo2, bucketNo3, ...", + }); + + const deployDemoUIParameter = new CfnParameter(this, "DeployDemoUIParameter", { + type: "String", + description: + "Would you like to deploy a demo UI to explore the features and capabilities of this solution? This will create an additional Amazon S3 bucket and Amazon CloudFront distribution in your account.", + allowedValues: ["Yes", "No"], + default: "Yes", + }); + + const logRetentionPeriodParameter = new CfnParameter(this, "LogRetentionPeriodParameter", { + type: "String", + description: + "This solution automatically logs events to Amazon CloudWatch. Select the amount of time for CloudWatch logs from this solution to be retained (in days).", + allowedValues: [ + "1", + "3", + "5", + "7", + "14", + "30", + "60", + "90", + "120", + "150", + "180", + "365", + "400", + "545", + "731", + "1827", + "3653", + "Infinite", + ], + default: "180", + }); + + const autoWebPParameter = new CfnParameter(this, "AutoWebPParameter", { + type: "String", + description: `Would you like to enable automatic formatting to WebP images when accept headers include "image/webp"? Select 'Yes' if so.`, + allowedValues: ["Yes", "No"], + default: "No", + }); + + const enableSignatureParameter = new CfnParameter(this, "EnableSignatureParameter", { + type: "String", + description: `Would you like to enable the signature? If so, select 'Yes' and provide SecretsManagerSecret and SecretsManagerKey values.`, + allowedValues: ["Yes", "No"], + default: "No", + }); + + const secretsManagerSecretParameter = new CfnParameter(this, "SecretsManagerSecretParameter", { + type: "String", + description: "The name of AWS Secrets Manager secret. You need to create your secret under this name.", + default: "", + }); + + const secretsManagerKeyParameter = new CfnParameter(this, "SecretsManagerKeyParameter", { + type: "String", + description: + "The name of AWS Secrets Manager secret key. You need to create secret key with this key name. The secret value would be used to check signature.", + default: "", + }); + + const enableDefaultFallbackImageParameter = new CfnParameter(this, "EnableDefaultFallbackImageParameter", { + type: "String", + description: `Would you like to enable the default fallback image? If so, select 'Yes' and provide FallbackImageS3Bucket and FallbackImageS3Key values.`, + allowedValues: ["Yes", "No"], + default: "No", + }); + + const fallbackImageS3BucketParameter = new CfnParameter(this, "FallbackImageS3BucketParameter", { + type: "String", + description: + "The name of the Amazon S3 bucket which contains the default fallback image. e.g. my-fallback-image-bucket", + default: "", + }); + + const fallbackImageS3KeyParameter = new CfnParameter(this, "FallbackImageS3KeyParameter", { + type: "String", + description: "The name of the default fallback image object key including prefix. e.g. prefix/image.jpg", + default: "", + }); + + const cloudFrontPriceClassParameter = new CfnParameter(this, "CloudFrontPriceClassParameter", { + type: "String", + description: + "The AWS CloudFront price class to use. Lower price classes will avoid high cost edge locations, reducing cost at the expense of possibly increasing request latency. For more information see: https://docs.aws.amazon.com/cdk/api/v2/python/aws_cdk.aws_cloudfront/PriceClass.html", + allowedValues: [PriceClass.PRICE_CLASS_ALL, PriceClass.PRICE_CLASS_200, PriceClass.PRICE_CLASS_100], + default: PriceClass.PRICE_CLASS_ALL, + }); + + const originShieldRegionParameter = new CfnParameter(this, "OriginShieldRegionParameter", { + type: "String", + description: + "Enabling Origin Shield may see reduced latency and increased cache hit ratios if your requests often come from many regions. If a region is selected, Origin Shield will be enabled and the Origin Shield caching layer will be set up in that region. For information on choosing a region, see https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/origin-shield.html#choose-origin-shield-region", + allowedValues: [ + "Disabled", + "us-east-1", + "us-east-2", + "us-west-2", + "ap-south-1", + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "ap-northeast-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + ], + default: "Disabled", + }); + + const enableS3ObjectLambdaParameter = new CfnParameter(this, "EnableS3ObjectLambdaParameter", { + type: "String", + description: + "Enable S3 Object Lambda to improve the maximum response size of image requests beyond 6 MB. If enabled, an S3 Object Lambda Access Point will replace the API Gateway proxying requests to your image handler function. **Important: Modifying this value after initial template deployment will delete the existing CloudFront Distribution and create a new one, providing a new domain name and clearing the cache**", + allowedValues: ["Yes", "No"], + default: "No", + }); + + const useExistingCloudFrontDistribution = new CfnParameter(this, "UseExistingCloudFrontDistributionParameter", { + type: "String", + description: + "If you would like to use an existing CloudFront distribution, select 'Yes'. Otherwise, select 'No' to create a new CloudFront distribution. This option will require additional manual setup after deployment. Please refer to the implementation guide for details: https://docs.aws.amazon.com/solutions/latest/serverless-image-handler/attaching-existing-distribution.html", + allowedValues: ["Yes", "No"], + default: "No", + }); + + const existingCloudFrontDistributionId = new CfnParameter(this, "ExistingCloudFrontDistributionIdParameter", { + type: "String", + description: + "The ID of the existing CloudFront distribution. This parameter is required if 'Use Existing CloudFront Distribution' is set to 'Yes'.", + default: "", + allowedPattern: "^$|^E[A-Z0-9]{8,}$", + }); + + /* eslint-disable no-new */ + new CfnRule(this, "ExistingDistributionIdRequiredRule", { + ruleCondition: Fn.conditionEquals(useExistingCloudFrontDistribution.valueAsString, "Yes"), + assertions: [ + { + assert: Fn.conditionNot(Fn.conditionEquals(existingCloudFrontDistributionId.valueAsString, "")), + assertDescription: + "If 'UseExistingCloudFrontDistribution' is set to 'Yes', 'ExistingCloudFrontDistributionId' must be provided.", + }, + ], + }); + + const solutionMapping = new CfnMapping(this, "Solution", { + mapping: { + Config: { + AnonymousUsage: "Yes", + DeployCloudWatchDashboard: "Yes", + SolutionId: props.solutionId, + Version: props.solutionVersion, + SharpSizeLimit: "", + }, + }, + lazy: false, + }); + + const anonymousUsage = `${solutionMapping.findInMap("Config", "AnonymousUsage")}`; + const sharpSizeLimit = `${solutionMapping.findInMap("Config", "SharpSizeLimit")}`; + const sendAnonymousStatistics = new CfnCondition(this, "SendAnonymousStatistics", { + expression: Fn.conditionEquals(anonymousUsage, "Yes"), + }); + const deployCloudWatchDashboard = new CfnCondition(this, "DeployCloudWatchDashboard", { + expression: Fn.conditionEquals(`${solutionMapping.findInMap("Config", "DeployCloudWatchDashboard")}`, "Yes"), + }); + + const solutionConstructProps: SolutionConstructProps = { + corsEnabled: corsEnabledParameter.valueAsString, + corsOrigin: corsOriginParameter.valueAsString, + sourceBuckets: sourceBucketsParameter.valueAsString, + deployUI: deployDemoUIParameter.valueAsString as YesNo, + logRetentionPeriod: logRetentionPeriodParameter.valueAsString, + autoWebP: autoWebPParameter.valueAsString, + enableSignature: enableSignatureParameter.valueAsString as YesNo, + secretsManager: secretsManagerSecretParameter.valueAsString, + secretsManagerKey: secretsManagerKeyParameter.valueAsString, + enableDefaultFallbackImage: enableDefaultFallbackImageParameter.valueAsString as YesNo, + fallbackImageS3Bucket: fallbackImageS3BucketParameter.valueAsString, + fallbackImageS3KeyBucket: fallbackImageS3KeyParameter.valueAsString, + originShieldRegion: originShieldRegionParameter.valueAsString, + enableS3ObjectLambda: enableS3ObjectLambdaParameter.valueAsString, + useExistingCloudFrontDistribution: useExistingCloudFrontDistribution.valueAsString as YesNo, + existingCloudFrontDistributionId: existingCloudFrontDistributionId.valueAsString, + }; + + const commonResources = new CommonResources(this, "CommonResources", { + solutionId: props.solutionId, + solutionVersion: props.solutionVersion, + solutionName: props.solutionName, + ...solutionConstructProps, + }); + + commonResources.customResources.setupValidateSourceAndFallbackImageBuckets({ + sourceBuckets: sourceBucketsParameter.valueAsString, + fallbackImageS3Bucket: fallbackImageS3BucketParameter.valueAsString, + fallbackImageS3Key: fallbackImageS3KeyParameter.valueAsString, + enableS3ObjectLambda: enableS3ObjectLambdaParameter.valueAsString, + }); + + const frontEnd = new FrontEnd(this, "FrontEnd", { + logsBucket: commonResources.logsBucket, + conditions: commonResources.conditions, + }); + + const backEnd = new BackEnd(this, "BackEnd", { + solutionVersion: props.solutionVersion, + solutionId: props.solutionId, + solutionName: props.solutionName, + secretsManagerPolicy: commonResources.secretsManagerPolicy, + sendAnonymousStatistics, + deployCloudWatchDashboard, + logsBucket: commonResources.logsBucket, + uuid: commonResources.customResources.uuid, + regionedBucketName: commonResources.customResources.regionedBucketName, + regionedBucketHash: commonResources.customResources.regionedBucketHash, + cloudFrontPriceClass: cloudFrontPriceClassParameter.valueAsString, + conditions: commonResources.conditions, + sharpSizeLimit, + createSourceBucketsResource: commonResources.customResources.createSourceBucketsResource, + ...solutionConstructProps, + }); + + commonResources.customResources.setupWebsiteHostingBucketPolicy(frontEnd.websiteHostingBucket); + + commonResources.customResources.setupAnonymousMetric({ + anonymousData: anonymousUsage, + ...solutionConstructProps, + }); + + commonResources.customResources.setupValidateSecretsManager({ + secretsManager: secretsManagerSecretParameter.valueAsString, + secretsManagerKey: secretsManagerKeyParameter.valueAsString, + }); + + commonResources.customResources.setupValidateExistingDistribution({ + existingDistributionId: existingCloudFrontDistributionId.valueAsString, + condition: commonResources.conditions.useExistingCloudFrontDistributionCondition, + }); + + commonResources.customResources.setupCopyWebsiteCustomResource({ + hostingBucket: frontEnd.websiteHostingBucket, + }); + const singletonFunction = this.node.findChild("Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C"); + Aspects.of(singletonFunction).add(new ConditionAspect(commonResources.conditions.deployUICondition)); + + const apiEndpointConditionString = Fn.conditionIf( + commonResources.conditions.useExistingCloudFrontDistributionCondition.logicalId, + `https://` + commonResources.customResources.existingDistributionDomainName, + Fn.conditionIf( + commonResources.conditions.disableS3ObjectLambdaCondition.logicalId, + `https://` + backEnd.domainName, + `https://` + backEnd.olDomainName + ) + ).toString(); + + commonResources.customResources.setupPutWebsiteConfigCustomResource({ + hostingBucket: frontEnd.websiteHostingBucket, + apiEndpoint: apiEndpointConditionString, + }); + + commonResources.appRegistryApplication({ + description: `${props.solutionId} - ${props.solutionName}. Version ${props.solutionVersion}`, + solutionVersion: props.solutionVersion, + solutionId: props.solutionId, + solutionName: props.solutionName, + applicationName: commonResources.customResources.appRegApplicationName, + }); + + this.templateOptions.metadata = { + "AWS::CloudFormation::Interface": { + ParameterGroups: [ + { + Label: { default: "S3 Object Lambda" }, + Parameters: [enableS3ObjectLambdaParameter.logicalId], + }, + { + Label: { default: "CORS Options" }, + Parameters: [corsEnabledParameter.logicalId, corsOriginParameter.logicalId], + }, + { + Label: { default: "Image Sources" }, + Parameters: [sourceBucketsParameter.logicalId], + }, + { + Label: { default: "Demo UI" }, + Parameters: [deployDemoUIParameter.logicalId], + }, + { + Label: { default: "Event Logging" }, + Parameters: [logRetentionPeriodParameter.logicalId], + }, + { + Label: { + default: + "Image URL Signature (Note: Enabling signature is not compatible with previous image URLs, which could result in broken image links. Please refer to the implementation guide for details: https://docs.aws.amazon.com/solutions/latest/serverless-image-handler/architecture-details.html#image-url-signature)", + }, + Parameters: [ + enableSignatureParameter.logicalId, + secretsManagerSecretParameter.logicalId, + secretsManagerKeyParameter.logicalId, + ], + }, + { + Label: { + default: + "Default Fallback Image (Note: Enabling default fallback image returns the default fallback image instead of JSON object when error happens. Please refer to the implementation guide for details: https://docs.aws.amazon.com/solutions/latest/serverless-image-handler/architecture-details.html#default-fallback-image)", + }, + Parameters: [ + enableDefaultFallbackImageParameter.logicalId, + fallbackImageS3BucketParameter.logicalId, + fallbackImageS3KeyParameter.logicalId, + ], + }, + { + Label: { default: "Auto WebP" }, + Parameters: [autoWebPParameter.logicalId], + }, + { + Label: { default: "CloudFront" }, + Parameters: [ + originShieldRegionParameter.logicalId, + cloudFrontPriceClassParameter.logicalId, + useExistingCloudFrontDistribution.logicalId, + existingCloudFrontDistributionId.logicalId, + ], + }, + ], + ParameterLabels: { + [enableS3ObjectLambdaParameter.logicalId]: { + default: "Enable S3 Object Lambda", + }, + [corsEnabledParameter.logicalId]: { default: "CORS Enabled" }, + [corsOriginParameter.logicalId]: { default: "CORS Origin" }, + [sourceBucketsParameter.logicalId]: { default: "Source Buckets" }, + [deployDemoUIParameter.logicalId]: { default: "Deploy Demo UI" }, + [logRetentionPeriodParameter.logicalId]: { + default: "Log Retention Period", + }, + [autoWebPParameter.logicalId]: { default: "AutoWebP" }, + [enableSignatureParameter.logicalId]: { default: "Enable Signature" }, + [secretsManagerSecretParameter.logicalId]: { + default: "SecretsManager Secret", + }, + [secretsManagerKeyParameter.logicalId]: { + default: "SecretsManager Key", + }, + [enableDefaultFallbackImageParameter.logicalId]: { + default: "Enable Default Fallback Image", + }, + [fallbackImageS3BucketParameter.logicalId]: { + default: "Fallback Image S3 Bucket", + }, + [fallbackImageS3KeyParameter.logicalId]: { + default: "Fallback Image S3 Key", + }, + [cloudFrontPriceClassParameter.logicalId]: { + default: "CloudFront PriceClass", + }, + [originShieldRegionParameter.logicalId]: { + default: "Origin Shield Region", + }, + [useExistingCloudFrontDistribution.logicalId]: { + default: "Use Existing CloudFront Distribution", + }, + [existingCloudFrontDistributionId.logicalId]: { + default: "Existing CloudFront Distribution Id", + }, + }, + }, + }; + + /* eslint-disable no-new */ + new CfnOutput(this, "ApiEndpoint", { + value: apiEndpointConditionString, + description: "Link to API endpoint for sending image requests to.", + }); + new CfnOutput(this, "DemoUrl", { + value: `https://${frontEnd.domainName}/index.html`, + description: "Link to the demo user interface for the solution.", + condition: commonResources.conditions.deployUICondition, + }); + new CfnOutput(this, "SourceBuckets", { + value: sourceBucketsParameter.valueAsString, + description: "Amazon S3 bucket location containing original image files.", + }); + new CfnOutput(this, "CorsEnabled", { + value: corsEnabledParameter.valueAsString, + description: "Indicates whether Cross-Origin Resource Sharing (CORS) has been enabled for the image handler API.", + }); + new CfnOutput(this, "CorsOrigin", { + value: corsOriginParameter.valueAsString, + description: "Origin value returned in the Access-Control-Allow-Origin header of image handler API responses.", + condition: commonResources.conditions.enableCorsCondition, + }); + new CfnOutput(this, "LogRetentionPeriod", { + value: logRetentionPeriodParameter.valueAsString, + description: "Number of days for event logs from Lambda to be retained in CloudWatch.", + }); + new CfnOutput(this, "CloudFrontLoggingBucket", { + value: commonResources.logsBucket.bucketName, + description: "Amazon S3 bucket for storing CloudFront access logs.", + }); + new CfnOutput(this, "CloudFrontDashboard", { + value: `https://console.aws.amazon.com/cloudwatch/home?#dashboards/dashboard/${backEnd.operationalDashboard.dashboardName}`, + description: "CloudFront metrics dashboard for the distribution.", + condition: deployCloudWatchDashboard, + }); + + Aspects.of(this).add(new SuppressLambdaFunctionCfnRulesAspect()); + Tags.of(this).add("SolutionId", props.solutionId); + } +} diff --git a/source/constructs/lib/types.ts b/source/constructs/lib/types.ts new file mode 100644 index 000000000..9f2d2037f --- /dev/null +++ b/source/constructs/lib/types.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +export type YesNo = "Yes" | "No"; + +export interface SolutionConstructProps { + readonly corsEnabled: string; + readonly corsOrigin: string; + readonly sourceBuckets: string; + readonly deployUI: YesNo; + readonly logRetentionPeriod: string; + readonly autoWebP: string; + readonly enableSignature: YesNo; + readonly originShieldRegion: string; + readonly secretsManager: string; + readonly secretsManagerKey: string; + readonly enableDefaultFallbackImage: YesNo; + readonly fallbackImageS3Bucket: string; + readonly fallbackImageS3KeyBucket: string; + readonly enableS3ObjectLambda: string; + readonly useExistingCloudFrontDistribution: YesNo; + readonly existingCloudFrontDistributionId: string; +} diff --git a/source/constructs/package-lock.json b/source/constructs/package-lock.json new file mode 100644 index 000000000..ebbf868b0 --- /dev/null +++ b/source/constructs/package-lock.json @@ -0,0 +1,5919 @@ +{ + "name": "constructs", + "version": "7.0.6", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "constructs", + "version": "7.0.6", + "license": "Apache-2.0", + "dependencies": { + "metrics-utils": "file:../metrics-utils", + "sharp": "^0.34.2" + }, + "bin": { + "constructs": "bin/constructs.js" + }, + "devDependencies": { + "@aws-cdk/aws-servicecatalogappregistry-alpha": "v2.118.0-alpha.0", + "@aws-solutions-constructs/aws-apigateway-lambda": "2.51.0", + "@aws-solutions-constructs/aws-cloudfront-apigateway-lambda": "2.51.0", + "@aws-solutions-constructs/aws-cloudfront-s3": "2.51.0", + "@aws-solutions-constructs/core": "2.51.0", + "@types/jest": "^29.5.6", + "@types/node": "^20.10.4", + "aws-cdk": "^2.1018.0", + "aws-cdk-lib": "^2.182.0", + "constructs": "^10.3.0", + "esbuild": "^0.25.0", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "ts-node": "^10.9.2", + "typescript": "^5.3.3" + } + }, + "../metrics-utils": { + "version": "7.0.4", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-cloudwatch": "^3.637.0", + "@aws-sdk/client-cloudwatch-logs": "^3.637.0", + "@aws-sdk/client-sqs": "^3.637.0", + "@aws-solutions-constructs/aws-eventbridge-lambda": "^2.59.0", + "@aws-solutions-constructs/aws-lambda-sqs-lambda": "^2.59.0", + "@types/aws-lambda": "^8.10.143", + "axios": "^1.7.4", + "esbuild": "^0.25.0" + }, + "devDependencies": { + "@types/jest": "^29.5.12", + "@types/node": "20.4.5", + "aws-cdk-lib": "^2.182.0", + "constructs": "^10.0.0", + "jest": "^29.6.2", + "ts-jest": "^29.2.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@aws-cdk/asset-awscli-v1": { + "version": "2.2.240", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.240.tgz", + "integrity": "sha512-Ry5yvGVf8s7j1Gf1aBFs0mBnWzRkkRtgSVpRGkDWXvZoPbRODAH33S1mAxkETNb+dNnTPGE2Gvws0XbhpJ6RzA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@aws-cdk/asset-node-proxy-agent-v6": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.1.0.tgz", + "integrity": "sha512-7bY3J8GCVxLupn/kNmpPc5VJz8grx+4RKfnnJiO1LG+uxkZfANZG3RMHhE+qQxxwkyQ9/MfPtTpf748UhR425A==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@aws-cdk/aws-servicecatalogappregistry-alpha": { + "version": "2.118.0-alpha.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-servicecatalogappregistry-alpha/-/aws-servicecatalogappregistry-alpha-2.118.0-alpha.0.tgz", + "integrity": "sha512-wOiIDV02FrcysrZ5n/FU9Uxr72/TNjYhj6jMmUTXIto51iOPJwXx8n9pU3wVYecB0oyk0hGED4tyDBM3Oi1L2g==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.118.0", + "constructs": "^10.0.0" + } + }, + "node_modules/@aws-cdk/cloud-assembly-schema": { + "version": "44.8.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-44.8.0.tgz", + "integrity": "sha512-Bxyj0VH8phE1uHJ6LiG3/UC/HYK91EBZnXSOzwtLsMJ0ZPuaQCYDRVAAfjDCSsEOwAk56/Waks8b5pXHpgz/xw==", + "bundleDependencies": [ + "jsonschema", + "semver" + ], + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jsonschema": "~1.4.1", + "semver": "^7.7.2" + }, + "engines": { + "node": ">= 18.0.0" + } + }, + "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/jsonschema": { + "version": "1.4.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/semver": { + "version": "7.7.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@aws-solutions-constructs/aws-apigateway-lambda": { + "version": "2.51.0", + "resolved": "https://registry.npmjs.org/@aws-solutions-constructs/aws-apigateway-lambda/-/aws-apigateway-lambda-2.51.0.tgz", + "integrity": "sha512-7RWaXGa//9j3YVB8tZxfsy8S0c9EokdMv+JdSNwBzygPuaZBnR8eDnjiPTor8MHv9fhhtSaLolMDpqdYpzFo7g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-solutions-constructs/core": "2.51.0", + "constructs": "^10.0.0" + }, + "peerDependencies": { + "@aws-solutions-constructs/core": "2.51.0", + "aws-cdk-lib": "^2.118.0", + "constructs": "^10.0.0" + } + }, + "node_modules/@aws-solutions-constructs/aws-cloudfront-apigateway": { + "version": "2.51.0", + "resolved": "https://registry.npmjs.org/@aws-solutions-constructs/aws-cloudfront-apigateway/-/aws-cloudfront-apigateway-2.51.0.tgz", + "integrity": "sha512-3UTEc+76cNOe7qn3qAwhNHlq7JIfwe07nijS9cfkUThAsfi4faP49+rcAzluZPx7dPGFUp0zK4MYiiXaVuN8vQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-solutions-constructs/core": "2.51.0", + "constructs": "^10.0.0" + }, + "peerDependencies": { + "@aws-solutions-constructs/core": "2.51.0", + "aws-cdk-lib": "^2.118.0", + "constructs": "^10.0.0" + } + }, + "node_modules/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda": { + "version": "2.51.0", + "resolved": "https://registry.npmjs.org/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/-/aws-cloudfront-apigateway-lambda-2.51.0.tgz", + "integrity": "sha512-juVPg0h9hP1i2YKh+avyBCpud9pOPPzZHW9+hcf6LlqdcYp0VrrmNFta3W7GhFFmXLxU77Nwn80i56Wn2chzxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-solutions-constructs/aws-cloudfront-apigateway": "2.51.0", + "@aws-solutions-constructs/core": "2.51.0", + "constructs": "^10.0.0" + }, + "peerDependencies": { + "@aws-solutions-constructs/aws-cloudfront-apigateway": "2.51.0", + "@aws-solutions-constructs/core": "2.51.0", + "aws-cdk-lib": "^2.118.0", + "constructs": "^10.0.0" + } + }, + "node_modules/@aws-solutions-constructs/aws-cloudfront-s3": { + "version": "2.51.0", + "resolved": "https://registry.npmjs.org/@aws-solutions-constructs/aws-cloudfront-s3/-/aws-cloudfront-s3-2.51.0.tgz", + "integrity": "sha512-11TV5xdrT48lQams3fR8b7GYcECqrQ6lbvCfyENqbmozWwVrnmNZxoFcIy4VF9oQPwx07kKsAsgsHnuDJhQjBQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-solutions-constructs/core": "2.51.0", + "@aws-solutions-constructs/resources": "2.51.0", + "constructs": "^10.0.0" + }, + "peerDependencies": { + "@aws-solutions-constructs/core": "2.51.0", + "@aws-solutions-constructs/resources": "2.51.0", + "aws-cdk-lib": "^2.118.0", + "constructs": "^10.0.0" + } + }, + "node_modules/@aws-solutions-constructs/core": { + "version": "2.51.0", + "resolved": "https://registry.npmjs.org/@aws-solutions-constructs/core/-/core-2.51.0.tgz", + "integrity": "sha512-55LQfMgxbXSpfLduOygTNUrAOxlj09lDufbL+mcncB/1hDkJiawYQYx/OUcFWFfGSheXRwNoxOP4FYVS/7cCDQ==", + "bundleDependencies": [ + "deepmerge", + "npmlog", + "deep-diff" + ], + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "constructs": "^10.0.0", + "deep-diff": "^1.0.2", + "deepmerge": "^4.0.0", + "npmlog": "^4.1.2" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.118.0", + "constructs": "^10.0.0" + } + }, + "node_modules/@aws-solutions-constructs/core/node_modules/ansi-regex": { + "version": "2.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@aws-solutions-constructs/core/node_modules/aproba": { + "version": "1.2.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/@aws-solutions-constructs/core/node_modules/are-we-there-yet": { + "version": "1.1.7", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node_modules/@aws-solutions-constructs/core/node_modules/code-point-at": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@aws-solutions-constructs/core/node_modules/console-control-strings": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/@aws-solutions-constructs/core/node_modules/core-util-is": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/@aws-solutions-constructs/core/node_modules/deep-diff": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/@aws-solutions-constructs/core/node_modules/deepmerge": { + "version": "4.3.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@aws-solutions-constructs/core/node_modules/delegates": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/@aws-solutions-constructs/core/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/@aws-solutions-constructs/core/node_modules/gauge": { + "version": "2.7.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/@aws-solutions-constructs/core/node_modules/has-unicode": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/@aws-solutions-constructs/core/node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/@aws-solutions-constructs/core/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@aws-solutions-constructs/core/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/@aws-solutions-constructs/core/node_modules/npmlog": { + "version": "4.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/@aws-solutions-constructs/core/node_modules/number-is-nan": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@aws-solutions-constructs/core/node_modules/object-assign": { + "version": "4.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@aws-solutions-constructs/core/node_modules/process-nextick-args": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/@aws-solutions-constructs/core/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/@aws-solutions-constructs/core/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/@aws-solutions-constructs/core/node_modules/set-blocking": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/@aws-solutions-constructs/core/node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/@aws-solutions-constructs/core/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/@aws-solutions-constructs/core/node_modules/string-width": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@aws-solutions-constructs/core/node_modules/strip-ansi": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@aws-solutions-constructs/core/node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/@aws-solutions-constructs/core/node_modules/wide-align": { + "version": "1.1.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/@aws-solutions-constructs/core/node_modules/wide-align/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@aws-solutions-constructs/core/node_modules/wide-align/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@aws-solutions-constructs/core/node_modules/wide-align/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@aws-solutions-constructs/core/node_modules/wide-align/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@aws-solutions-constructs/resources": { + "version": "2.51.0", + "resolved": "https://registry.npmjs.org/@aws-solutions-constructs/resources/-/resources-2.51.0.tgz", + "integrity": "sha512-QYZjnyGnMsdKYxYPfQ6vVn2AfbnuwuzVBbWiC/mJhWBE6N5IPXd7YRj+dWT0D440fdFx/sBUpsWWPWeWbgwNWw==", + "bundleDependencies": [ + "@aws-sdk/client-kms", + "@aws-sdk/client-s3", + "aws-sdk-client-mock" + ], + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-kms": "^3.478.0", + "@aws-sdk/client-s3": "^3.478.0", + "@aws-solutions-constructs/core": "2.51.0", + "aws-sdk-client-mock": "^3.0.0", + "constructs": "^10.0.0" + }, + "peerDependencies": { + "@aws-solutions-constructs/core": "2.51.0", + "aws-cdk-lib": "^2.118.0", + "constructs": "^10.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.5.tgz", + "integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz", + "integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.4", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.27.4", + "@babel/types": "^7.27.3", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", + "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.5", + "@babel/types": "^7.27.3", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", + "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", + "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", + "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", + "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", + "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", + "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", + "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", + "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", + "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", + "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", + "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", + "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", + "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", + "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", + "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", + "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", + "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", + "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", + "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", + "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", + "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", + "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", + "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", + "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", + "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", + "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", + "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", + "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", + "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz", + "integrity": "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.1.0" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz", + "integrity": "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.1.0" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz", + "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", + "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz", + "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz", + "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz", + "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz", + "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz", + "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz", + "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz", + "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.2.tgz", + "integrity": "sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.1.0" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.2.tgz", + "integrity": "sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.1.0" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.2.tgz", + "integrity": "sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.1.0" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.2.tgz", + "integrity": "sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.1.0" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.2.tgz", + "integrity": "sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.2.tgz", + "integrity": "sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.1.0" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz", + "integrity": "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.4.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.2.tgz", + "integrity": "sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.2.tgz", + "integrity": "sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.2.tgz", + "integrity": "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/node": { + "version": "20.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.1.tgz", + "integrity": "sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/aws-cdk": { + "version": "2.1019.2", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1019.2.tgz", + "integrity": "sha512-LkWZ3IKBkfCPTCu60t4Wb9JMSkb+0Uzk+HIxZeW5sFohq8bxDGV0OP1hcqEC2+KbVYRn7q+YhMeSJ/FOQcgpiw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "cdk": "bin/cdk" + }, + "engines": { + "node": ">= 18.0.0" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/aws-cdk-lib": { + "version": "2.202.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.202.0.tgz", + "integrity": "sha512-JDycQoE8AxUAeCFXFoCx6FGvR78e6W9zYxPgmfW/uPPbntyNCXXBqwyAYo17RGS/lr0RO3zqD/oCBZSNU2e/Yg==", + "bundleDependencies": [ + "@balena/dockerignore", + "case", + "fs-extra", + "ignore", + "jsonschema", + "minimatch", + "punycode", + "semver", + "table", + "yaml", + "mime-types" + ], + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-cdk/asset-awscli-v1": "2.2.240", + "@aws-cdk/asset-node-proxy-agent-v6": "^2.1.0", + "@aws-cdk/cloud-assembly-schema": "^44.2.0", + "@balena/dockerignore": "^1.0.2", + "case": "1.6.3", + "fs-extra": "^11.3.0", + "ignore": "^5.3.2", + "jsonschema": "^1.5.0", + "mime-types": "^2.1.35", + "minimatch": "^3.1.2", + "punycode": "^2.3.1", + "semver": "^7.7.2", + "table": "^6.9.0", + "yaml": "1.10.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "constructs": "^10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/@balena/dockerignore": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/aws-cdk-lib/node_modules/ajv": { + "version": "8.17.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/astral-regex": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/brace-expansion": { + "version": "1.1.12", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/aws-cdk-lib/node_modules/case": { + "version": "1.6.3", + "dev": true, + "inBundle": true, + "license": "(MIT OR GPL-3.0-or-later)", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fast-deep-equal": { + "version": "3.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fast-uri": { + "version": "3.0.6", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/aws-cdk-lib/node_modules/fs-extra": { + "version": "11.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/aws-cdk-lib/node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/aws-cdk-lib/node_modules/ignore": { + "version": "5.3.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/aws-cdk-lib/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/aws-cdk-lib/node_modules/jsonschema": { + "version": "1.5.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/aws-cdk-lib/node_modules/lodash.truncate": { + "version": "4.4.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/mime-db": { + "version": "1.52.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/aws-cdk-lib/node_modules/mime-types": { + "version": "2.1.35", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/aws-cdk-lib/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/aws-cdk-lib/node_modules/punycode": { + "version": "2.3.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/aws-cdk-lib/node_modules/require-from-string": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/semver": { + "version": "7.7.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aws-cdk-lib/node_modules/slice-ansi": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/table": { + "version": "6.9.0", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/yaml": { + "version": "1.10.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001726", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz", + "integrity": "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/constructs": { + "version": "10.4.2", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.4.2.tgz", + "integrity": "sha512-wsNxBlAott2qg8Zv87q3eYZYgheb9lchtBfjHzzLHtXbttwSrHPs1NNQbBrmbb1YZvYg2+Vh0Dor76w4mFxJkA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", + "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.174", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.174.tgz", + "integrity": "sha512-HE43yYdUUiJVjewV2A9EP8o89Kb4AqMKplMQP2IxEPUws1Etu/ZkdsgUDabUZ/WmbP4ZbvJDOcunvbBUPPIfmw==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/esbuild": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", + "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.5", + "@esbuild/android-arm": "0.25.5", + "@esbuild/android-arm64": "0.25.5", + "@esbuild/android-x64": "0.25.5", + "@esbuild/darwin-arm64": "0.25.5", + "@esbuild/darwin-x64": "0.25.5", + "@esbuild/freebsd-arm64": "0.25.5", + "@esbuild/freebsd-x64": "0.25.5", + "@esbuild/linux-arm": "0.25.5", + "@esbuild/linux-arm64": "0.25.5", + "@esbuild/linux-ia32": "0.25.5", + "@esbuild/linux-loong64": "0.25.5", + "@esbuild/linux-mips64el": "0.25.5", + "@esbuild/linux-ppc64": "0.25.5", + "@esbuild/linux-riscv64": "0.25.5", + "@esbuild/linux-s390x": "0.25.5", + "@esbuild/linux-x64": "0.25.5", + "@esbuild/netbsd-arm64": "0.25.5", + "@esbuild/netbsd-x64": "0.25.5", + "@esbuild/openbsd-arm64": "0.25.5", + "@esbuild/openbsd-x64": "0.25.5", + "@esbuild/sunos-x64": "0.25.5", + "@esbuild/win32-arm64": "0.25.5", + "@esbuild/win32-ia32": "0.25.5", + "@esbuild/win32-x64": "0.25.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/metrics-utils": { + "resolved": "../metrics-utils", + "link": true + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/sharp": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.2.tgz", + "integrity": "sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.4", + "semver": "^7.7.2" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.2", + "@img/sharp-darwin-x64": "0.34.2", + "@img/sharp-libvips-darwin-arm64": "1.1.0", + "@img/sharp-libvips-darwin-x64": "1.1.0", + "@img/sharp-libvips-linux-arm": "1.1.0", + "@img/sharp-libvips-linux-arm64": "1.1.0", + "@img/sharp-libvips-linux-ppc64": "1.1.0", + "@img/sharp-libvips-linux-s390x": "1.1.0", + "@img/sharp-libvips-linux-x64": "1.1.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", + "@img/sharp-libvips-linuxmusl-x64": "1.1.0", + "@img/sharp-linux-arm": "0.34.2", + "@img/sharp-linux-arm64": "0.34.2", + "@img/sharp-linux-s390x": "0.34.2", + "@img/sharp-linux-x64": "0.34.2", + "@img/sharp-linuxmusl-arm64": "0.34.2", + "@img/sharp-linuxmusl-x64": "0.34.2", + "@img/sharp-wasm32": "0.34.2", + "@img/sharp-win32-arm64": "0.34.2", + "@img/sharp-win32-ia32": "0.34.2", + "@img/sharp-win32-x64": "0.34.2" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-jest": { + "version": "29.4.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.0.tgz", + "integrity": "sha512-d423TJMnJGu80/eSgfQ5w/R+0zFJvdtTxwtF9KzFFunOpSeD+79lHJQIiAhluJoyGRbvj9NZJsl9WjCUo0ND7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "^2.1.0", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.2", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/source/constructs/package.json b/source/constructs/package.json index 546f8e93d..72446389a 100644 --- a/source/constructs/package.json +++ b/source/constructs/package.json @@ -1,37 +1,44 @@ -{ - "name": "constructs", - "description": "Serverless Image Handler Constructs", - "version": "5.2.0", - "license": "Apache-2.0", - "bin": { - "constructs": "bin/constructs.js" - }, - "scripts": { - "build": "tsc", - "watch": "tsc -w", - "test": "export BUCKET_NAME=TEST && export SOLUTION_NAME=serverless-image-handler && export VERSION=TEST_VERSION && jest", - "cdk": "cdk" - }, - "devDependencies": { - "@aws-cdk/assert": "1.64.1", - "@types/jest": "^26.0.14", - "@types/node": "^14.11.2", - "aws-cdk": "1.64.1", - "jest": "^26.4.2", - "ts-jest": "^26.4.0", - "ts-node": "^9.0.0", - "typescript": "~4.0.3" - }, - "dependencies": { - "@aws-cdk/aws-apigateway": "1.64.1", - "@aws-cdk/aws-cloudfront": "1.64.1", - "@aws-cdk/aws-iam": "1.64.1", - "@aws-cdk/aws-lambda": "1.64.1", - "@aws-cdk/aws-s3": "1.64.1", - "@aws-cdk/core": "1.64.1", - "@aws-solutions-constructs/aws-apigateway-lambda": "1.64.1", - "@aws-solutions-constructs/aws-cloudfront-apigateway-lambda": "1.64.1", - "@aws-solutions-constructs/aws-cloudfront-s3": "1.64.1", - "@aws-solutions-constructs/core": "1.64.1" - } -} +{ + "name": "constructs", + "version": "7.0.6", + "description": "Dynamic Image Transformation for Amazon CloudFront Constructs", + "license": "Apache-2.0", + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com/solutions" + }, + "bin": { + "constructs": "bin/constructs.js" + }, + "scripts": { + "cdk": "cdk", + "clean:install": "rm -rf node_modules/ cdk.out/ coverage/ && npm ci && cd ../ && npm run install:dependencies", + "cdk:synth": "npm run clean:install && overrideWarningsEnabled=false npx cdk synth --asset-metadata false --path-metadata false --json false", + "pretest": "npm run clean:install", + "build": "tsc", + "watch": "tsc -w", + "test": "overrideWarningsEnabled=false jest --coverage", + "bump-version": "npm version $(cat ../../VERSION.txt) --allow-same-version" + }, + "devDependencies": { + "@aws-cdk/aws-servicecatalogappregistry-alpha": "v2.118.0-alpha.0", + "@aws-solutions-constructs/aws-apigateway-lambda": "2.51.0", + "@aws-solutions-constructs/aws-cloudfront-apigateway-lambda": "2.51.0", + "@aws-solutions-constructs/aws-cloudfront-s3": "2.51.0", + "@aws-solutions-constructs/core": "2.51.0", + "@types/jest": "^29.5.6", + "@types/node": "^20.10.4", + "aws-cdk": "^2.1018.0", + "aws-cdk-lib": "^2.182.0", + "constructs": "^10.3.0", + "esbuild": "^0.25.0", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "ts-node": "^10.9.2", + "typescript": "^5.3.3" + }, + "dependencies": { + "metrics-utils": "file:../metrics-utils", + "sharp": "^0.34.2" + } +} diff --git a/source/constructs/test/__snapshots__/constructs.test.ts.snap b/source/constructs/test/__snapshots__/constructs.test.ts.snap new file mode 100644 index 000000000..fb845bf4b --- /dev/null +++ b/source/constructs/test/__snapshots__/constructs.test.ts.snap @@ -0,0 +1,4488 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Dynamic Image Transformation for Amazon CloudFront Stack Snapshot 1`] = ` +{ + "Conditions": { + "BackEndDeployAPIGDistributionF4E75280": { + "Fn::And": [ + { + "Condition": "CommonResourcesDisableS3ObjectLambdaCondition017AC33C", + }, + { + "Fn::Not": [ + { + "Condition": "CommonResourcesUseExistingCloudFrontDistributionConditionEBC48184", + }, + ], + }, + ], + }, + "BackEndDeployS3OLDistribution869FCC7C": { + "Fn::And": [ + { + "Condition": "CommonResourcesEnableS3ObjectLambdaConditionF2C07DCD", + }, + { + "Fn::Not": [ + { + "Condition": "CommonResourcesUseExistingCloudFrontDistributionConditionEBC48184", + }, + ], + }, + ], + }, + "BackEndShortLogRetentionCondition72EA1A33": { + "Fn::Or": [ + { + "Fn::Equals": [ + { + "Ref": "LogRetentionPeriodParameter", + }, + "1", + ], + }, + { + "Fn::Equals": [ + { + "Ref": "LogRetentionPeriodParameter", + }, + "3", + ], + }, + { + "Fn::Equals": [ + { + "Ref": "LogRetentionPeriodParameter", + }, + "5", + ], + }, + ], + }, + "CommonResourcesAutoWebPCondition7119C768": { + "Fn::Equals": [ + { + "Ref": "AutoWebPParameter", + }, + "Yes", + ], + }, + "CommonResourcesDeployDemoUICondition308D3B09": { + "Fn::Equals": [ + { + "Ref": "DeployDemoUIParameter", + }, + "Yes", + ], + }, + "CommonResourcesDisableS3ObjectLambdaCondition017AC33C": { + "Fn::Not": [ + { + "Fn::Equals": [ + { + "Ref": "EnableS3ObjectLambdaParameter", + }, + "Yes", + ], + }, + ], + }, + "CommonResourcesEnableCorsConditionA0615348": { + "Fn::Equals": [ + { + "Ref": "CorsEnabledParameter", + }, + "Yes", + ], + }, + "CommonResourcesEnableDefaultFallbackImageConditionD1A10983": { + "Fn::Equals": [ + { + "Ref": "EnableDefaultFallbackImageParameter", + }, + "Yes", + ], + }, + "CommonResourcesEnableOriginShieldConditionCEE94847": { + "Fn::Not": [ + { + "Fn::Equals": [ + { + "Ref": "OriginShieldRegionParameter", + }, + "Disabled", + ], + }, + ], + }, + "CommonResourcesEnableS3ObjectLambdaConditionF2C07DCD": { + "Fn::Equals": [ + { + "Ref": "EnableS3ObjectLambdaParameter", + }, + "Yes", + ], + }, + "CommonResourcesEnableSignatureCondition909DC7A1": { + "Fn::Equals": [ + { + "Ref": "EnableSignatureParameter", + }, + "Yes", + ], + }, + "CommonResourcesIsLogRetentionPeriodInfinite129C8A82": { + "Fn::Equals": [ + { + "Ref": "LogRetentionPeriodParameter", + }, + "Infinite", + ], + }, + "CommonResourcesUseExistingCloudFrontDistributionConditionEBC48184": { + "Fn::Equals": [ + { + "Ref": "UseExistingCloudFrontDistributionParameter", + }, + "Yes", + ], + }, + "DeployCloudWatchDashboard": { + "Fn::Equals": [ + { + "Fn::FindInMap": [ + "Solution", + "Config", + "DeployCloudWatchDashboard", + ], + }, + "Yes", + ], + }, + "SendAnonymousStatistics": { + "Fn::Equals": [ + { + "Fn::FindInMap": [ + "Solution", + "Config", + "AnonymousUsage", + ], + }, + "Yes", + ], + }, + }, + "Mappings": { + "Solution": { + "Config": { + "AnonymousUsage": "Yes", + "DeployCloudWatchDashboard": "Yes", + "SharpSizeLimit": "", + "SolutionId": "S0ABC", + "Version": "v7.0.1", + }, + }, + }, + "Metadata": { + "AWS::CloudFormation::Interface": { + "ParameterGroups": [ + { + "Label": { + "default": "S3 Object Lambda", + }, + "Parameters": [ + "EnableS3ObjectLambdaParameter", + ], + }, + { + "Label": { + "default": "CORS Options", + }, + "Parameters": [ + "CorsEnabledParameter", + "CorsOriginParameter", + ], + }, + { + "Label": { + "default": "Image Sources", + }, + "Parameters": [ + "SourceBucketsParameter", + ], + }, + { + "Label": { + "default": "Demo UI", + }, + "Parameters": [ + "DeployDemoUIParameter", + ], + }, + { + "Label": { + "default": "Event Logging", + }, + "Parameters": [ + "LogRetentionPeriodParameter", + ], + }, + { + "Label": { + "default": "Image URL Signature (Note: Enabling signature is not compatible with previous image URLs, which could result in broken image links. Please refer to the implementation guide for details: https://docs.aws.amazon.com/solutions/latest/serverless-image-handler/architecture-details.html#image-url-signature)", + }, + "Parameters": [ + "EnableSignatureParameter", + "SecretsManagerSecretParameter", + "SecretsManagerKeyParameter", + ], + }, + { + "Label": { + "default": "Default Fallback Image (Note: Enabling default fallback image returns the default fallback image instead of JSON object when error happens. Please refer to the implementation guide for details: https://docs.aws.amazon.com/solutions/latest/serverless-image-handler/architecture-details.html#default-fallback-image)", + }, + "Parameters": [ + "EnableDefaultFallbackImageParameter", + "FallbackImageS3BucketParameter", + "FallbackImageS3KeyParameter", + ], + }, + { + "Label": { + "default": "Auto WebP", + }, + "Parameters": [ + "AutoWebPParameter", + ], + }, + { + "Label": { + "default": "CloudFront", + }, + "Parameters": [ + "OriginShieldRegionParameter", + "CloudFrontPriceClassParameter", + "UseExistingCloudFrontDistributionParameter", + "ExistingCloudFrontDistributionIdParameter", + ], + }, + ], + "ParameterLabels": { + "AutoWebPParameter": { + "default": "AutoWebP", + }, + "CloudFrontPriceClassParameter": { + "default": "CloudFront PriceClass", + }, + "CorsEnabledParameter": { + "default": "CORS Enabled", + }, + "CorsOriginParameter": { + "default": "CORS Origin", + }, + "DeployDemoUIParameter": { + "default": "Deploy Demo UI", + }, + "EnableDefaultFallbackImageParameter": { + "default": "Enable Default Fallback Image", + }, + "EnableS3ObjectLambdaParameter": { + "default": "Enable S3 Object Lambda", + }, + "EnableSignatureParameter": { + "default": "Enable Signature", + }, + "ExistingCloudFrontDistributionIdParameter": { + "default": "Existing CloudFront Distribution Id", + }, + "FallbackImageS3BucketParameter": { + "default": "Fallback Image S3 Bucket", + }, + "FallbackImageS3KeyParameter": { + "default": "Fallback Image S3 Key", + }, + "LogRetentionPeriodParameter": { + "default": "Log Retention Period", + }, + "OriginShieldRegionParameter": { + "default": "Origin Shield Region", + }, + "SecretsManagerKeyParameter": { + "default": "SecretsManager Key", + }, + "SecretsManagerSecretParameter": { + "default": "SecretsManager Secret", + }, + "SourceBucketsParameter": { + "default": "Source Buckets", + }, + "UseExistingCloudFrontDistributionParameter": { + "default": "Use Existing CloudFront Distribution", + }, + }, + }, + }, + "Outputs": { + "ApiEndpoint": { + "Description": "Link to API endpoint for sending image requests to.", + "Value": { + "Fn::If": [ + "CommonResourcesUseExistingCloudFrontDistributionConditionEBC48184", + { + "Fn::Join": [ + "", + [ + "https://", + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesCustomResourceValidateExistingDistribution34A6673C", + "DistributionDomainName", + ], + }, + ], + ], + }, + { + "Fn::If": [ + "CommonResourcesDisableS3ObjectLambdaCondition017AC33C", + { + "Fn::Join": [ + "", + [ + "https://", + { + "Fn::If": [ + "CommonResourcesUseExistingCloudFrontDistributionConditionEBC48184", + "", + { + "Fn::GetAtt": [ + "BackEndImageHandlerCloudFrontApiGatewayLambdaCloudFrontToApiGatewayCloudFrontDistribution03AA31B2", + "DomainName", + ], + }, + ], + }, + ], + ], + }, + { + "Fn::Join": [ + "", + [ + "https://", + { + "Fn::If": [ + "CommonResourcesUseExistingCloudFrontDistributionConditionEBC48184", + "", + { + "Fn::GetAtt": [ + "BackEndImageHandlerCloudFrontDistributionB5464C90", + "DomainName", + ], + }, + ], + }, + ], + ], + }, + ], + }, + ], + }, + }, + "CloudFrontDashboard": { + "Condition": "DeployCloudWatchDashboard", + "Description": "CloudFront metrics dashboard for the distribution.", + "Value": { + "Fn::Join": [ + "", + [ + "https://console.aws.amazon.com/cloudwatch/home?#dashboards/dashboard/", + { + "Ref": "OperationalInsightsDashboard00409C46", + }, + ], + ], + }, + }, + "CloudFrontLoggingBucket": { + "Description": "Amazon S3 bucket for storing CloudFront access logs.", + "Value": { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesLogBucketCustomResource2445A3AB", + "BucketName", + ], + }, + }, + "CorsEnabled": { + "Description": "Indicates whether Cross-Origin Resource Sharing (CORS) has been enabled for the image handler API.", + "Value": { + "Ref": "CorsEnabledParameter", + }, + }, + "CorsOrigin": { + "Condition": "CommonResourcesEnableCorsConditionA0615348", + "Description": "Origin value returned in the Access-Control-Allow-Origin header of image handler API responses.", + "Value": { + "Ref": "CorsOriginParameter", + }, + }, + "DemoUrl": { + "Condition": "CommonResourcesDeployDemoUICondition308D3B09", + "Description": "Link to the demo user interface for the solution.", + "Value": { + "Fn::Join": [ + "", + [ + "https://", + { + "Fn::GetAtt": [ + "FrontEndDistributionToS3CloudFrontDistribution15FE13D0", + "DomainName", + ], + }, + "/index.html", + ], + ], + }, + }, + "LogRetentionPeriod": { + "Description": "Number of days for event logs from Lambda to be retained in CloudWatch.", + "Value": { + "Ref": "LogRetentionPeriodParameter", + }, + }, + "SourceBuckets": { + "Description": "Amazon S3 bucket location containing original image files.", + "Value": { + "Ref": "SourceBucketsParameter", + }, + }, + }, + "Parameters": { + "AutoWebPParameter": { + "AllowedValues": [ + "Yes", + "No", + ], + "Default": "No", + "Description": "Would you like to enable automatic formatting to WebP images when accept headers include "image/webp"? Select 'Yes' if so.", + "Type": "String", + }, + "BootstrapVersion": { + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]", + "Type": "AWS::SSM::Parameter::Value", + }, + "CloudFrontPriceClassParameter": { + "AllowedValues": [ + "PriceClass_All", + "PriceClass_200", + "PriceClass_100", + ], + "Default": "PriceClass_All", + "Description": "The AWS CloudFront price class to use. Lower price classes will avoid high cost edge locations, reducing cost at the expense of possibly increasing request latency. For more information see: https://docs.aws.amazon.com/cdk/api/v2/python/aws_cdk.aws_cloudfront/PriceClass.html", + "Type": "String", + }, + "CorsEnabledParameter": { + "AllowedValues": [ + "Yes", + "No", + ], + "Default": "No", + "Description": "Would you like to enable Cross-Origin Resource Sharing (CORS) for the image handler API? Select 'Yes' if so.", + "Type": "String", + }, + "CorsOriginParameter": { + "Default": "*", + "Description": "If you selected 'Yes' above, please specify an origin value here. A wildcard (*) value will support any origin. We recommend specifying an origin (i.e. https://example.domain) to restrict cross-site access to your API.", + "Type": "String", + }, + "DeployDemoUIParameter": { + "AllowedValues": [ + "Yes", + "No", + ], + "Default": "Yes", + "Description": "Would you like to deploy a demo UI to explore the features and capabilities of this solution? This will create an additional Amazon S3 bucket and Amazon CloudFront distribution in your account.", + "Type": "String", + }, + "EnableDefaultFallbackImageParameter": { + "AllowedValues": [ + "Yes", + "No", + ], + "Default": "No", + "Description": "Would you like to enable the default fallback image? If so, select 'Yes' and provide FallbackImageS3Bucket and FallbackImageS3Key values.", + "Type": "String", + }, + "EnableS3ObjectLambdaParameter": { + "AllowedValues": [ + "Yes", + "No", + ], + "Default": "No", + "Description": "Enable S3 Object Lambda to improve the maximum response size of image requests beyond 6 MB. If enabled, an S3 Object Lambda Access Point will replace the API Gateway proxying requests to your image handler function. **Important: Modifying this value after initial template deployment will delete the existing CloudFront Distribution and create a new one, providing a new domain name and clearing the cache**", + "Type": "String", + }, + "EnableSignatureParameter": { + "AllowedValues": [ + "Yes", + "No", + ], + "Default": "No", + "Description": "Would you like to enable the signature? If so, select 'Yes' and provide SecretsManagerSecret and SecretsManagerKey values.", + "Type": "String", + }, + "ExistingCloudFrontDistributionIdParameter": { + "AllowedPattern": "^$|^E[A-Z0-9]{8,}$", + "Default": "", + "Description": "The ID of the existing CloudFront distribution. This parameter is required if 'Use Existing CloudFront Distribution' is set to 'Yes'.", + "Type": "String", + }, + "FallbackImageS3BucketParameter": { + "Default": "", + "Description": "The name of the Amazon S3 bucket which contains the default fallback image. e.g. my-fallback-image-bucket", + "Type": "String", + }, + "FallbackImageS3KeyParameter": { + "Default": "", + "Description": "The name of the default fallback image object key including prefix. e.g. prefix/image.jpg", + "Type": "String", + }, + "LogRetentionPeriodParameter": { + "AllowedValues": [ + "1", + "3", + "5", + "7", + "14", + "30", + "60", + "90", + "120", + "150", + "180", + "365", + "400", + "545", + "731", + "1827", + "3653", + "Infinite", + ], + "Default": "180", + "Description": "This solution automatically logs events to Amazon CloudWatch. Select the amount of time for CloudWatch logs from this solution to be retained (in days).", + "Type": "String", + }, + "OriginShieldRegionParameter": { + "AllowedValues": [ + "Disabled", + "us-east-1", + "us-east-2", + "us-west-2", + "ap-south-1", + "ap-northeast-1", + "ap-southeast-1", + "ap-southeast-2", + "ap-northeast-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "sa-east-1", + ], + "Default": "Disabled", + "Description": "Enabling Origin Shield may see reduced latency and increased cache hit ratios if your requests often come from many regions. If a region is selected, Origin Shield will be enabled and the Origin Shield caching layer will be set up in that region. For information on choosing a region, see https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/origin-shield.html#choose-origin-shield-region", + "Type": "String", + }, + "SecretsManagerKeyParameter": { + "Default": "", + "Description": "The name of AWS Secrets Manager secret key. You need to create secret key with this key name. The secret value would be used to check signature.", + "Type": "String", + }, + "SecretsManagerSecretParameter": { + "Default": "", + "Description": "The name of AWS Secrets Manager secret. You need to create your secret under this name.", + "Type": "String", + }, + "SourceBucketsParameter": { + "AllowedPattern": ".+", + "Default": "defaultBucket, bucketNo2, bucketNo3, ...", + "Description": "(Required) List the buckets (comma-separated) within your account that contain original image files. If you plan to use Thumbor or Custom image requests with this solution, the source bucket for those requests will default to the first bucket listed in this field.", + "Type": "String", + }, + "UseExistingCloudFrontDistributionParameter": { + "AllowedValues": [ + "Yes", + "No", + ], + "Default": "No", + "Description": "If you would like to use an existing CloudFront distribution, select 'Yes'. Otherwise, select 'No' to create a new CloudFront distribution. This option will require additional manual setup after deployment. Please refer to the implementation guide for details: https://docs.aws.amazon.com/solutions/latest/serverless-image-handler/attaching-existing-distribution.html", + "Type": "String", + }, + }, + "Resources": { + "AppRegistry968496A3": { + "Properties": { + "Description": "Service Catalog application to track and manage all your resources for the solution dit", + "Name": { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesCustomResourceGetAppRegApplicationName62472E55", + "ApplicationName", + ], + }, + "Tags": { + "SolutionId": "S0ABC", + "Solutions:ApplicationType": "AWS-Solutions", + "Solutions:SolutionID": "S0ABC", + "Solutions:SolutionName": "dit", + "Solutions:SolutionVersion": "v7.0.1", + }, + }, + "Type": "AWS::ServiceCatalogAppRegistry::Application", + }, + "AppRegistryAssociation": { + "Properties": { + "Application": { + "Fn::GetAtt": [ + "AppRegistry968496A3", + "Id", + ], + }, + "Resource": { + "Ref": "AWS::StackId", + }, + "ResourceType": "CFN_STACK", + }, + "Type": "AWS::ServiceCatalogAppRegistry::ResourceAssociation", + }, + "BackEndAccessPoint6DA9B104": { + "Condition": "CommonResourcesEnableS3ObjectLambdaConditionF2C07DCD", + "Properties": { + "Bucket": { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesCustomResourceCheckFirstBucketRegionE663CC31", + "BucketName", + ], + }, + "Name": { + "Fn::Join": [ + "", + [ + "sih-ap-", + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesCustomResourceUuid64E7CCAD", + "UUID", + ], + }, + "-", + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesCustomResourceCheckFirstBucketRegionE663CC31", + "BucketHash", + ], + }, + ], + ], + }, + "Policy": { + "Statement": { + "Action": "s3:*", + "Condition": { + "ForAnyValue:StringEquals": { + "aws:CalledVia": "s3-object-lambda.amazonaws.com", + }, + }, + "Effect": "Allow", + "Principal": { + "Service": "cloudfront.amazonaws.com", + }, + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:aws:s3:", + { + "Ref": "AWS::Region", + }, + ":", + { + "Ref": "AWS::AccountId", + }, + ":accesspoint/sih-ap-", + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesCustomResourceUuid64E7CCAD", + "UUID", + ], + }, + "-", + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesCustomResourceCheckFirstBucketRegionE663CC31", + "BucketHash", + ], + }, + ], + ], + }, + { + "Fn::Join": [ + "", + [ + "arn:aws:s3:", + { + "Ref": "AWS::Region", + }, + ":", + { + "Ref": "AWS::AccountId", + }, + ":accesspoint/sih-ap-", + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesCustomResourceUuid64E7CCAD", + "UUID", + ], + }, + "-", + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesCustomResourceCheckFirstBucketRegionE663CC31", + "BucketHash", + ], + }, + "/object/*", + ], + ], + }, + ], + }, + }, + }, + "Type": "AWS::S3::AccessPoint", + }, + "BackEndApigRequestModifierFunction400C4F08": { + "Condition": "CommonResourcesDisableS3ObjectLambdaCondition017AC33C", + "Properties": { + "AutoPublish": true, + "FunctionCode": "// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + + +function handler(event) { + // Normalize accept header to only include values used on the backend + if(event.request.headers && event.request.headers.accept && event.request.headers.accept.value) { + event.request.headers.accept.value = event.request.headers.accept.value.indexOf("image/webp") > -1 ? "image/webp" : "" + } + event.request.querystring = processQueryParams(event.request.querystring).join('&') + return event.request; +} + +function processQueryParams(querystring) { + if (querystring == null) { + return []; + } + + const ALLOWED_PARAMS = ['signature', 'expires', 'format', 'fit', 'width', 'height', 'rotate', 'flip', 'flop', 'grayscale']; + + let qs = []; + for (const key in querystring) { + if (!ALLOWED_PARAMS.includes(key)) { + continue; + } + const value = querystring[key]; + qs.push( + value.multiValue + ? \`\${key}=\${value.multiValue[value.multiValue.length - 1].value}\` + : \`\${key}=\${value.value}\` + ) + } + + return qs.sort(); +}", + "FunctionConfig": { + "Comment": { + "Fn::Join": [ + "", + [ + "sih-apig-request-modifier-", + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesCustomResourceUuid64E7CCAD", + "UUID", + ], + }, + ], + ], + }, + "Runtime": "cloudfront-js-2.0", + }, + "Name": { + "Fn::Join": [ + "", + [ + "sih-apig-request-modifier-", + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesCustomResourceUuid64E7CCAD", + "UUID", + ], + }, + ], + ], + }, + }, + "Type": "AWS::CloudFront::Function", + }, + "BackEndCachePolicy1DCE9B1B": { + "Properties": { + "CachePolicyConfig": { + "DefaultTTL": 86400, + "MaxTTL": 31536000, + "MinTTL": 1, + "Name": { + "Fn::Join": [ + "", + [ + "ServerlessImageHandler-", + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesCustomResourceUuid64E7CCAD", + "UUID", + ], + }, + ], + ], + }, + "ParametersInCacheKeyAndForwardedToOrigin": { + "CookiesConfig": { + "CookieBehavior": "none", + }, + "EnableAcceptEncodingBrotli": false, + "EnableAcceptEncodingGzip": false, + "HeadersConfig": { + "HeaderBehavior": "whitelist", + "Headers": { + "Fn::If": [ + "CommonResourcesAutoWebPCondition7119C768", + [ + "origin", + "accept", + ], + [ + "origin", + ], + ], + }, + }, + "QueryStringsConfig": { + "QueryStringBehavior": "all", + }, + }, + }, + }, + "Type": "AWS::CloudFront::CachePolicy", + }, + "BackEndImageHandlerCloudFrontApiGatewayLambdaApiAccessLogGroup9B786692": { + "Condition": "CommonResourcesDisableS3ObjectLambdaCondition017AC33C", + "DeletionPolicy": "Retain", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W86", + "reason": "Retention period for CloudWatchLogs LogGroups are set to 'Never Expire' to preserve customer data indefinitely", + }, + { + "id": "W84", + "reason": "By default CloudWatchLogs LogGroups data is encrypted using the CloudWatch server-side encryption keys (AWS Managed Keys)", + }, + ], + }, + }, + "Properties": { + "RetentionInDays": { + "Fn::If": [ + "CommonResourcesIsLogRetentionPeriodInfinite129C8A82", + { + "Ref": "AWS::NoValue", + }, + { + "Ref": "LogRetentionPeriodParameter", + }, + ], + }, + "Tags": [ + { + "Key": "SolutionId", + "Value": "S0ABC", + }, + ], + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain", + }, + "BackEndImageHandlerCloudFrontApiGatewayLambdaCloudFrontToApiGatewayCloudFrontDistribution03AA31B2": { + "Condition": "BackEndDeployAPIGDistributionF4E75280", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W70", + "reason": "Since the distribution uses the CloudFront domain name, CloudFront automatically sets the security policy to TLSv1 regardless of the value of MinimumProtocolVersion", + }, + ], + }, + }, + "Properties": { + "DistributionConfig": { + "Comment": "Image Handler Distribution for Dynamic Image Transformation for Amazon CloudFront", + "CustomErrorResponses": [ + { + "ErrorCachingMinTTL": 600, + "ErrorCode": 500, + }, + { + "ErrorCachingMinTTL": 600, + "ErrorCode": 501, + }, + { + "ErrorCachingMinTTL": 600, + "ErrorCode": 502, + }, + { + "ErrorCachingMinTTL": 600, + "ErrorCode": 503, + }, + { + "ErrorCachingMinTTL": 600, + "ErrorCode": 504, + }, + ], + "DefaultCacheBehavior": { + "AllowedMethods": [ + "GET", + "HEAD", + ], + "CachePolicyId": { + "Ref": "BackEndCachePolicy1DCE9B1B", + }, + "Compress": true, + "FunctionAssociations": [ + { + "EventType": "viewer-request", + "FunctionARN": { + "Fn::GetAtt": [ + "BackEndApigRequestModifierFunction400C4F08", + "FunctionARN", + ], + }, + }, + ], + "OriginRequestPolicyId": { + "Ref": "BackEndOriginRequestPolicy771345D7", + }, + "TargetOriginId": "TestStackBackEndImageHandlerCloudFrontApiGatewayLambdaCloudFrontToApiGatewayCloudFrontDistributionOrigin1A053AEB7", + "ViewerProtocolPolicy": "redirect-to-https", + }, + "Enabled": true, + "HttpVersion": "http2", + "IPV6Enabled": true, + "Logging": { + "Bucket": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesLogBucketCustomResource2445A3AB", + "BucketName", + ], + }, + ".s3.", + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesLogBucketCustomResource2445A3AB", + "Region", + ], + }, + ".", + { + "Ref": "AWS::URLSuffix", + }, + ], + ], + }, + "Prefix": "api-cloudfront/", + }, + "Origins": [ + { + "CustomOriginConfig": { + "OriginProtocolPolicy": "https-only", + "OriginSSLProtocols": [ + "TLSv1.1", + "TLSv1.2", + ], + }, + "DomainName": { + "Fn::Join": [ + "", + [ + { + "Ref": "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApi5A77D109", + }, + ".execute-api.", + { + "Ref": "AWS::Region", + }, + ".amazonaws.com", + ], + ], + }, + "Id": "TestStackBackEndImageHandlerCloudFrontApiGatewayLambdaCloudFrontToApiGatewayCloudFrontDistributionOrigin1A053AEB7", + "OriginPath": "/image", + "OriginShield": { + "Fn::If": [ + "CommonResourcesEnableOriginShieldConditionCEE94847", + { + "Enabled": true, + "OriginShieldRegion": { + "Ref": "OriginShieldRegionParameter", + }, + }, + { + "Enabled": false, + }, + ], + }, + }, + ], + "PriceClass": { + "Ref": "CloudFrontPriceClassParameter", + }, + }, + "Tags": [ + { + "Key": "SolutionId", + "Value": "S0ABC", + }, + ], + }, + "Type": "AWS::CloudFront::Distribution", + }, + "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApi5A77D109": { + "Condition": "CommonResourcesDisableS3ObjectLambdaCondition017AC33C", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W59", + "reason": "AWS::ApiGateway::Method AuthorizationType is set to 'NONE' because API Gateway behind CloudFront does not support AWS_IAM authentication", + }, + ], + }, + }, + "Properties": { + "BinaryMediaTypes": [ + "*/*", + ], + "EndpointConfiguration": { + "Types": [ + "REGIONAL", + ], + }, + "Name": "LambdaRestApi", + "Tags": [ + { + "Key": "SolutionId", + "Value": "S0ABC", + }, + ], + }, + "Type": "AWS::ApiGateway::RestApi", + }, + "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiANYApiPermissionTestStackBackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApi9D692DD2ANY979F1429": { + "Condition": "CommonResourcesDisableS3ObjectLambdaCondition017AC33C", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "BackEndImageHandlerLambdaFunctionADEF7FF2", + "Arn", + ], + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":execute-api:", + { + "Ref": "AWS::Region", + }, + ":", + { + "Ref": "AWS::AccountId", + }, + ":", + { + "Ref": "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApi5A77D109", + }, + "/", + { + "Ref": "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiDeploymentStageimageB55D20E3", + }, + "/*/", + ], + ], + }, + }, + "Type": "AWS::Lambda::Permission", + }, + "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiANYApiPermissionTestTestStackBackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApi9D692DD2ANY932D3700": { + "Condition": "CommonResourcesDisableS3ObjectLambdaCondition017AC33C", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "BackEndImageHandlerLambdaFunctionADEF7FF2", + "Arn", + ], + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":execute-api:", + { + "Ref": "AWS::Region", + }, + ":", + { + "Ref": "AWS::AccountId", + }, + ":", + { + "Ref": "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApi5A77D109", + }, + "/test-invoke-stage/*/", + ], + ], + }, + }, + "Type": "AWS::Lambda::Permission", + }, + "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiANYE4494B31": { + "Condition": "CommonResourcesDisableS3ObjectLambdaCondition017AC33C", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W59", + "reason": "No authorization currently on the API Gateway", + }, + ], + }, + }, + "Properties": { + "AuthorizationType": "NONE", + "HttpMethod": "ANY", + "Integration": { + "IntegrationHttpMethod": "POST", + "Type": "AWS_PROXY", + "Uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":apigateway:", + { + "Ref": "AWS::Region", + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "BackEndImageHandlerLambdaFunctionADEF7FF2", + "Arn", + ], + }, + "/invocations", + ], + ], + }, + }, + "ResourceId": { + "Fn::GetAtt": [ + "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApi5A77D109", + "RootResourceId", + ], + }, + "RestApiId": { + "Ref": "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApi5A77D109", + }, + }, + "Type": "AWS::ApiGateway::Method", + }, + "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiAccountE5522E5D": { + "Condition": "CommonResourcesDisableS3ObjectLambdaCondition017AC33C", + "DependsOn": [ + "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApi5A77D109", + ], + "Properties": { + "CloudWatchRoleArn": { + "Fn::GetAtt": [ + "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiCloudWatchRole12575C4D", + "Arn", + ], + }, + }, + "Type": "AWS::ApiGateway::Account", + }, + "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiCloudWatchRole12575C4D": { + "Condition": "CommonResourcesDisableS3ObjectLambdaCondition017AC33C", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "F10", + "reason": "Inline policy used in solutions construct", + }, + ], + }, + }, + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com", + }, + }, + ], + "Version": "2012-10-17", + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:DescribeLogGroups", + "logs:DescribeLogStreams", + "logs:PutLogEvents", + "logs:GetLogEvents", + "logs:FilterLogEvents", + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":logs:", + { + "Ref": "AWS::Region", + }, + ":", + { + "Ref": "AWS::AccountId", + }, + ":*", + ], + ], + }, + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "LambdaRestApiCloudWatchRolePolicy", + }, + ], + "Tags": [ + { + "Key": "SolutionId", + "Value": "S0ABC", + }, + ], + }, + "Type": "AWS::IAM::Role", + }, + "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiDeployment663240D68d2558b8e7783b0edad2e0b11793c95c": { + "Condition": "CommonResourcesDisableS3ObjectLambdaCondition017AC33C", + "DependsOn": [ + "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiproxyANY8F9763E1", + "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiproxyBDF0A131", + "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiANYE4494B31", + ], + "Metadata": { + "aws:cdk:do-not-refactor": true, + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W45", + "reason": "ApiGateway has AccessLogging enabled in AWS::ApiGateway::Stage resource, but cfn_nag checks for it in AWS::ApiGateway::Deployment resource", + }, + ], + }, + }, + "Properties": { + "Description": "Automatically created by the RestApi construct", + "RestApiId": { + "Ref": "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApi5A77D109", + }, + }, + "Type": "AWS::ApiGateway::Deployment", + }, + "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiDeploymentStageimageB55D20E3": { + "Condition": "CommonResourcesDisableS3ObjectLambdaCondition017AC33C", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W87", + "reason": "Cache not enabled, using CloudFront for caching viewer response", + }, + ], + }, + }, + "Properties": { + "AccessLogSetting": { + "DestinationArn": { + "Fn::GetAtt": [ + "BackEndImageHandlerCloudFrontApiGatewayLambdaApiAccessLogGroup9B786692", + "Arn", + ], + }, + "Format": "{"requestId":"$context.requestId","ip":"$context.identity.sourceIp","user":"$context.identity.user","caller":"$context.identity.caller","requestTime":"$context.requestTime","httpMethod":"$context.httpMethod","resourcePath":"$context.resourcePath","status":"$context.status","protocol":"$context.protocol","responseLength":"$context.responseLength"}", + }, + "DeploymentId": { + "Ref": "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiDeployment663240D68d2558b8e7783b0edad2e0b11793c95c", + }, + "MethodSettings": [ + { + "DataTraceEnabled": false, + "HttpMethod": "*", + "LoggingLevel": "INFO", + "ResourcePath": "/*", + }, + ], + "RestApiId": { + "Ref": "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApi5A77D109", + }, + "StageName": "image", + "Tags": [ + { + "Key": "SolutionId", + "Value": "S0ABC", + }, + ], + "TracingEnabled": true, + }, + "Type": "AWS::ApiGateway::Stage", + }, + "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiUsagePlan76CA1E70": { + "Condition": "CommonResourcesDisableS3ObjectLambdaCondition017AC33C", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApi5A77D109", + }, + "Stage": { + "Ref": "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiDeploymentStageimageB55D20E3", + }, + "Throttle": {}, + }, + ], + "Tags": [ + { + "Key": "SolutionId", + "Value": "S0ABC", + }, + ], + }, + "Type": "AWS::ApiGateway::UsagePlan", + }, + "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiproxyANY8F9763E1": { + "Condition": "CommonResourcesDisableS3ObjectLambdaCondition017AC33C", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W59", + "reason": "No authorization currently on the API Gateway", + }, + ], + }, + }, + "Properties": { + "AuthorizationType": "NONE", + "HttpMethod": "ANY", + "Integration": { + "IntegrationHttpMethod": "POST", + "Type": "AWS_PROXY", + "Uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":apigateway:", + { + "Ref": "AWS::Region", + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "BackEndImageHandlerLambdaFunctionADEF7FF2", + "Arn", + ], + }, + "/invocations", + ], + ], + }, + }, + "ResourceId": { + "Ref": "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiproxyBDF0A131", + }, + "RestApiId": { + "Ref": "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApi5A77D109", + }, + }, + "Type": "AWS::ApiGateway::Method", + }, + "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiproxyANYApiPermissionTestStackBackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApi9D692DD2ANYproxyB5CBD1F7": { + "Condition": "CommonResourcesDisableS3ObjectLambdaCondition017AC33C", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "BackEndImageHandlerLambdaFunctionADEF7FF2", + "Arn", + ], + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":execute-api:", + { + "Ref": "AWS::Region", + }, + ":", + { + "Ref": "AWS::AccountId", + }, + ":", + { + "Ref": "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApi5A77D109", + }, + "/", + { + "Ref": "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiDeploymentStageimageB55D20E3", + }, + "/*/*", + ], + ], + }, + }, + "Type": "AWS::Lambda::Permission", + }, + "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiproxyANYApiPermissionTestTestStackBackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApi9D692DD2ANYproxyAEADD71A": { + "Condition": "CommonResourcesDisableS3ObjectLambdaCondition017AC33C", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "BackEndImageHandlerLambdaFunctionADEF7FF2", + "Arn", + ], + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":execute-api:", + { + "Ref": "AWS::Region", + }, + ":", + { + "Ref": "AWS::AccountId", + }, + ":", + { + "Ref": "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApi5A77D109", + }, + "/test-invoke-stage/*/*", + ], + ], + }, + }, + "Type": "AWS::Lambda::Permission", + }, + "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApiproxyBDF0A131": { + "Condition": "CommonResourcesDisableS3ObjectLambdaCondition017AC33C", + "Properties": { + "ParentId": { + "Fn::GetAtt": [ + "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApi5A77D109", + "RootResourceId", + ], + }, + "PathPart": "{proxy+}", + "RestApiId": { + "Ref": "BackEndImageHandlerCloudFrontApiGatewayLambdaLambdaRestApi5A77D109", + }, + }, + "Type": "AWS::ApiGateway::Resource", + }, + "BackEndImageHandlerCloudFrontDistributionB5464C90": { + "Condition": "BackEndDeployS3OLDistribution869FCC7C", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W70", + "reason": "Since the distribution uses the CloudFront domain name, CloudFront automatically sets the security policy to TLSv1 regardless of the value of MinimumProtocolVersion", + }, + ], + }, + }, + "Properties": { + "DistributionConfig": { + "Comment": "Image Handler Distribution for Dynamic Image Transformation for Amazon CloudFront", + "CustomErrorResponses": [ + { + "ErrorCachingMinTTL": 600, + "ErrorCode": 500, + }, + { + "ErrorCachingMinTTL": 600, + "ErrorCode": 501, + }, + { + "ErrorCachingMinTTL": 600, + "ErrorCode": 502, + }, + { + "ErrorCachingMinTTL": 600, + "ErrorCode": 503, + }, + { + "ErrorCachingMinTTL": 600, + "ErrorCode": 504, + }, + ], + "DefaultCacheBehavior": { + "AllowedMethods": [ + "GET", + "HEAD", + ], + "CachePolicyId": { + "Ref": "BackEndCachePolicy1DCE9B1B", + }, + "Compress": true, + "FunctionAssociations": [ + { + "EventType": "viewer-response", + "FunctionARN": { + "Fn::GetAtt": [ + "BackEndOlResponseModifierFunctionB47B3834", + "FunctionARN", + ], + }, + }, + { + "EventType": "viewer-request", + "FunctionARN": { + "Fn::GetAtt": [ + "BackEndOlRequestModifierFunction7E5192E3", + "FunctionARN", + ], + }, + }, + ], + "OriginRequestPolicyId": { + "Ref": "BackEndOriginRequestPolicy771345D7", + }, + "TargetOriginId": "TestStackBackEndImageHandlerCloudFrontDistributionOrigin1781ABF9C", + "ViewerProtocolPolicy": "redirect-to-https", + }, + "Enabled": true, + "HttpVersion": "http2", + "IPV6Enabled": true, + "Logging": { + "Bucket": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesLogBucketCustomResource2445A3AB", + "BucketName", + ], + }, + ".s3.", + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesLogBucketCustomResource2445A3AB", + "Region", + ], + }, + ".", + { + "Ref": "AWS::URLSuffix", + }, + ], + ], + }, + "Prefix": "api-cloudfront/", + }, + "Origins": [ + { + "ConnectionAttempts": 1, + "DomainName": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "BackEndObjectLambdaAccessPointBEE6B960", + "Alias.Value", + ], + }, + ".s3.", + { + "Ref": "AWS::Region", + }, + ".amazonaws.com", + ], + ], + }, + "Id": "TestStackBackEndImageHandlerCloudFrontDistributionOrigin1781ABF9C", + "OriginAccessControlId": { + "Fn::GetAtt": [ + "BackEndSIHoriginaccesscontrolAFC8496A", + "Id", + ], + }, + "OriginPath": "/image", + "OriginShield": { + "Fn::If": [ + "CommonResourcesEnableOriginShieldConditionCEE94847", + { + "Enabled": true, + "OriginShieldRegion": { + "Ref": "OriginShieldRegionParameter", + }, + }, + { + "Enabled": false, + }, + ], + }, + "S3OriginConfig": {}, + }, + ], + "PriceClass": { + "Ref": "CloudFrontPriceClassParameter", + }, + }, + "Tags": [ + { + "Key": "SolutionId", + "Value": "S0ABC", + }, + ], + }, + "Type": "AWS::CloudFront::Distribution", + }, + "BackEndImageHandlerFunctionPolicy437940B5": { + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W12", + "reason": "rekognition:DetectFaces requires '*' resources.", + }, + ], + }, + }, + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents", + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":logs:", + { + "Ref": "AWS::Region", + }, + ":", + { + "Ref": "AWS::AccountId", + }, + ":log-group:/aws/lambda/*", + ], + ], + }, + }, + { + "Action": "s3:GetObject", + "Effect": "Allow", + "Resource": { + "Fn::Split": [ + ",", + { + "Fn::Sub": [ + "arn:aws:s3:::\${rest}/*", + { + "rest": { + "Fn::Join": [ + "/*,arn:aws:s3:::", + { + "Fn::Split": [ + ",", + { + "Fn::Join": [ + "", + { + "Fn::Split": [ + " ", + { + "Ref": "SourceBucketsParameter", + }, + ], + }, + ], + }, + ], + }, + ], + }, + }, + ], + }, + ], + }, + }, + { + "Action": "s3:ListBucket", + "Effect": "Allow", + "Resource": { + "Fn::Split": [ + ",", + { + "Fn::Sub": [ + "arn:aws:s3:::\${rest}", + { + "rest": { + "Fn::Join": [ + ",arn:aws:s3:::", + { + "Fn::Split": [ + ",", + { + "Fn::Join": [ + "", + { + "Fn::Split": [ + " ", + { + "Ref": "SourceBucketsParameter", + }, + ], + }, + ], + }, + ], + }, + ], + }, + }, + ], + }, + ], + }, + }, + { + "Action": "s3:GetObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:s3:::", + { + "Ref": "FallbackImageS3BucketParameter", + }, + "/", + { + "Ref": "FallbackImageS3KeyParameter", + }, + ], + ], + }, + }, + { + "Action": [ + "rekognition:DetectFaces", + "rekognition:DetectModerationLabels", + ], + "Effect": "Allow", + "Resource": "*", + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "BackEndImageHandlerFunctionPolicy437940B5", + "Roles": [ + { + "Ref": "BackEndImageHandlerFunctionRoleABF81E5C", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, + "BackEndImageHandlerFunctionRoleABF81E5C": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com", + }, + }, + ], + "Version": "2012-10-17", + }, + "Path": "/", + "Tags": [ + { + "Key": "SolutionId", + "Value": "S0ABC", + }, + ], + }, + "Type": "AWS::IAM::Role", + }, + "BackEndImageHandlerLambdaFunctionADEF7FF2": { + "DependsOn": [ + "BackEndImageHandlerFunctionRoleABF81E5C", + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "The function does have permission to write CloudWatch Logs.", + }, + { + "id": "W89", + "reason": "The Lambda function does not require any VPC connection at all.", + }, + { + "id": "W92", + "reason": "The Lambda function does not require ReservedConcurrentExecutions.", + }, + ], + }, + }, + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}", + }, + "S3Key": "Omitted to remove snapshot dependency on hash", + }, + "Description": "dit (v7.0.1): Performs image edits and manipulations", + "Environment": { + "Variables": { + "AUTO_WEBP": { + "Ref": "AutoWebPParameter", + }, + "CORS_ENABLED": { + "Ref": "CorsEnabledParameter", + }, + "CORS_ORIGIN": { + "Ref": "CorsOriginParameter", + }, + "DEFAULT_FALLBACK_IMAGE_BUCKET": { + "Ref": "FallbackImageS3BucketParameter", + }, + "DEFAULT_FALLBACK_IMAGE_KEY": { + "Ref": "FallbackImageS3KeyParameter", + }, + "ENABLE_DEFAULT_FALLBACK_IMAGE": { + "Ref": "EnableDefaultFallbackImageParameter", + }, + "ENABLE_S3_OBJECT_LAMBDA": { + "Ref": "EnableS3ObjectLambdaParameter", + }, + "ENABLE_SIGNATURE": { + "Ref": "EnableSignatureParameter", + }, + "REWRITE_MATCH_PATTERN": "", + "REWRITE_SUBSTITUTION": "", + "SECRETS_MANAGER": { + "Ref": "SecretsManagerSecretParameter", + }, + "SECRET_KEY": { + "Ref": "SecretsManagerKeyParameter", + }, + "SHARP_SIZE_LIMIT": { + "Fn::FindInMap": [ + "Solution", + "Config", + "SharpSizeLimit", + ], + }, + "SOLUTION_ID": "S0ABC", + "SOLUTION_VERSION": "Omitted to remove snapshot dependency on solution version", + "SOURCE_BUCKETS": { + "Ref": "SourceBucketsParameter", + }, + }, + }, + "Handler": "index.handler", + "MemorySize": 1024, + "Role": { + "Fn::GetAtt": [ + "BackEndImageHandlerFunctionRoleABF81E5C", + "Arn", + ], + }, + "Runtime": "nodejs20.x", + "Tags": [ + { + "Key": "SolutionId", + "Value": "S0ABC", + }, + ], + "Timeout": 29, + }, + "Type": "AWS::Lambda::Function", + }, + "BackEndImageHandlerLambdaFunctionInvokeOmXtXvfmnVyX0ul6SNprKOdkal6YvZiIw5QCpGsJFo09B7B011": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "BackEndImageHandlerLambdaFunctionADEF7FF2", + "Arn", + ], + }, + "Principal": "cloudfront.amazonaws.com", + }, + "Type": "AWS::Lambda::Permission", + }, + "BackEndImageHandlerLogGroupA0941EEC": { + "DeletionPolicy": "Retain", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W84", + "reason": "CloudWatch log group is always encrypted by default.", + }, + { + "id": "W86", + "reason": "Retention days are configured with property override", + }, + ], + }, + }, + "Properties": { + "LogGroupName": { + "Fn::Join": [ + "", + [ + "/aws/lambda/", + { + "Ref": "BackEndImageHandlerLambdaFunctionADEF7FF2", + }, + ], + ], + }, + "RetentionInDays": { + "Fn::If": [ + "CommonResourcesIsLogRetentionPeriodInfinite129C8A82", + { + "Ref": "AWS::NoValue", + }, + { + "Ref": "LogRetentionPeriodParameter", + }, + ], + }, + "Tags": [ + { + "Key": "SolutionId", + "Value": "S0ABC", + }, + ], + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain", + }, + "BackEndObjectLambdaAccessPointBEE6B960": { + "Condition": "CommonResourcesEnableS3ObjectLambdaConditionF2C07DCD", + "Properties": { + "Name": { + "Fn::Join": [ + "", + [ + "sih-olap-", + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesCustomResourceUuid64E7CCAD", + "UUID", + ], + }, + ], + ], + }, + "ObjectLambdaConfiguration": { + "SupportingAccessPoint": { + "Fn::GetAtt": [ + "BackEndAccessPoint6DA9B104", + "Arn", + ], + }, + "TransformationConfigurations": [ + { + "Actions": [ + "GetObject", + "HeadObject", + ], + "ContentTransformation": { + "AwsLambda": { + "FunctionArn": { + "Fn::GetAtt": [ + "BackEndImageHandlerLambdaFunctionADEF7FF2", + "Arn", + ], + }, + }, + }, + }, + ], + }, + }, + "Type": "AWS::S3ObjectLambda::AccessPoint", + }, + "BackEndObjectLambdaAccessPointPolicy1FC842E3": { + "Condition": "CommonResourcesEnableS3ObjectLambdaConditionF2C07DCD", + "Properties": { + "ObjectLambdaAccessPoint": { + "Ref": "BackEndObjectLambdaAccessPointBEE6B960", + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "s3-object-lambda:Get*", + "Condition": { + "StringEquals": { + "aws:SourceArn": { + "Fn::Join": [ + "", + [ + "arn:aws:cloudfront::", + { + "Ref": "AWS::AccountId", + }, + ":distribution/", + { + "Fn::If": [ + "CommonResourcesUseExistingCloudFrontDistributionConditionEBC48184", + { + "Ref": "ExistingCloudFrontDistributionIdParameter", + }, + { + "Ref": "BackEndImageHandlerCloudFrontDistributionB5464C90", + }, + ], + }, + ], + ], + }, + }, + }, + "Effect": "Allow", + "Principal": { + "Service": "cloudfront.amazonaws.com", + }, + "Resource": { + "Fn::GetAtt": [ + "BackEndObjectLambdaAccessPointBEE6B960", + "Arn", + ], + }, + }, + ], + "Version": "2012-10-17", + }, + }, + "Type": "AWS::S3ObjectLambda::AccessPointPolicy", + }, + "BackEndOlRequestModifierFunction7E5192E3": { + "Condition": "CommonResourcesEnableS3ObjectLambdaConditionF2C07DCD", + "Properties": { + "AutoPublish": true, + "FunctionCode": "// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +function handler(event) { + // Normalize accept header to only include values used on the backend + if(event.request.headers && event.request.headers.accept && event.request.headers.accept.value) { + event.request.headers.accept.value = event.request.headers.accept.value.indexOf("image/webp") > -1 ? "image/webp" : "" + } + event.request.querystring = processQueryParams(event.request.querystring).join('&') + return event.request; +} + +function processQueryParams(querystring) { + if (querystring == null) { + return []; + } + + const ALLOWED_PARAMS = ['signature', 'expires', 'format', 'fit', 'width', 'height', 'rotate', 'flip', 'flop', 'grayscale']; + const OL_PARAMS = {'signature': 'ol-signature', 'expires': 'ol-expires'}; + + let qs = []; + for (const key in querystring) { + if (!ALLOWED_PARAMS.includes(key)) { + continue; + } + const value = querystring[key]; + const mappedKey = OL_PARAMS[key] || key; + qs.push( + value.multiValue + ? \`\${mappedKey}=\${value.multiValue[value.multiValue.length - 1].value}\` + : \`\${mappedKey}=\${value.value}\` + ) + } + + return qs.sort(); +}", + "FunctionConfig": { + "Comment": { + "Fn::Join": [ + "", + [ + "sih-ol-request-modifier-", + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesCustomResourceUuid64E7CCAD", + "UUID", + ], + }, + ], + ], + }, + "Runtime": "cloudfront-js-2.0", + }, + "Name": { + "Fn::Join": [ + "", + [ + "sih-ol-request-modifier-", + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesCustomResourceUuid64E7CCAD", + "UUID", + ], + }, + ], + ], + }, + }, + "Type": "AWS::CloudFront::Function", + }, + "BackEndOlResponseModifierFunctionB47B3834": { + "Condition": "CommonResourcesEnableS3ObjectLambdaConditionF2C07DCD", + "Properties": { + "AutoPublish": true, + "FunctionCode": "// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + + +function handler(event) { + const response = event.response; + + try { + Object.keys(response.headers).forEach(key => { + if (key.startsWith("x-amz-meta-") && key !== "x-amz-meta-statuscode") { + const headerName = key.replace("x-amz-meta-", ""); + response.headers[headerName] = response.headers[key]; + delete response.headers[key]; + } + }); + + const statusCodeHeader = response.headers["x-amz-meta-statuscode"]; + if (statusCodeHeader) { + const status = parseInt(statusCodeHeader.value); + if (status >= 400 && status <= 599) { + response.statusCode = status; + } + + delete response.headers["x-amz-meta-statuscode"]; + } + } catch (e) { + console.log("Error: ", e); + } + return response; +} +", + "FunctionConfig": { + "Comment": { + "Fn::Join": [ + "", + [ + "sih-ol-response-modifier-", + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesCustomResourceUuid64E7CCAD", + "UUID", + ], + }, + ], + ], + }, + "Runtime": "cloudfront-js-2.0", + }, + "Name": { + "Fn::Join": [ + "", + [ + "sih-ol-response-modifier-", + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesCustomResourceUuid64E7CCAD", + "UUID", + ], + }, + ], + ], + }, + }, + "Type": "AWS::CloudFront::Function", + }, + "BackEndOriginRequestPolicy771345D7": { + "Properties": { + "OriginRequestPolicyConfig": { + "CookiesConfig": { + "CookieBehavior": "none", + }, + "HeadersConfig": { + "HeaderBehavior": "whitelist", + "Headers": [ + "origin", + "accept", + ], + }, + "Name": { + "Fn::Join": [ + "", + [ + "ServerlessImageHandler-", + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesCustomResourceUuid64E7CCAD", + "UUID", + ], + }, + ], + ], + }, + "QueryStringsConfig": { + "QueryStringBehavior": "all", + }, + }, + }, + "Type": "AWS::CloudFront::OriginRequestPolicy", + }, + "BackEndSIHoriginaccesscontrolAFC8496A": { + "Condition": "CommonResourcesEnableS3ObjectLambdaConditionF2C07DCD", + "Properties": { + "OriginAccessControlConfig": { + "Name": { + "Fn::Join": [ + "", + [ + "SIH-origin-access-control-", + { + "Fn::GetAtt": [ + "CommonResourcesCustomResourcesCustomResourceUuid64E7CCAD", + "UUID", + ], + }, + ], + ], + }, + "OriginAccessControlOriginType": "s3", + "SigningBehavior": "always", + "SigningProtocol": "sigv4", + }, + }, + "Type": "AWS::CloudFront::OriginAccessControl", + }, + "BackEndSolutionMetricsBilledDurationMemorySizeQuery39F16D58": { + "Condition": "SendAnonymousStatistics", + "Properties": { + "LogGroupNames": [ + { + "Ref": "BackEndImageHandlerLogGroupA0941EEC", + }, + ], + "Name": { + "Fn::Join": [ + "", + [ + { + "Ref": "AWS::StackName", + }, + "-BilledDurationMemorySizeQuery", + ], + ], + }, + "QueryString": "stats sum(@billedDuration) as AWSLambdaBilledDuration, max(@memorySize) as AWSLambdaMemorySize", + }, + "Type": "AWS::Logs::QueryDefinition", + }, + "BackEndSolutionMetricsEventbridgeRuleToLambdaEventsRule05009025": { + "Condition": "SendAnonymousStatistics", + "Properties": { + "ScheduleExpression": { + "Fn::Join": [ + "", + [ + "cron(0 23 ? * ", + { + "Fn::If": [ + "BackEndShortLogRetentionCondition72EA1A33", + "*", + "MON", + ], + }, + " *)", + ], + ], + }, + "State": "ENABLED", + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "BackEndSolutionMetricsMetricsLambdaF4FA4AF7", + "Arn", + ], + }, + "Id": "Target0", + "InputTransformer": { + "InputPathsMap": { + "detail-type": "$.detail-type", + "time": "$.time", + }, + "InputTemplate": { + "Fn::Join": [ + "", + [ + "{"detail-type": , "time":