From 2d45e354ce956109b47bc8b6ed874a8ceb1e6017 Mon Sep 17 00:00:00 2001 From: hrodmn Date: Tue, 29 Apr 2025 05:44:18 -0500 Subject: [PATCH 1/2] chore: setup prettier formatting via lint-staged, enforce in CI --- .devcontainer/devcontainer.json | 4 +- .github/pull_request_template.md | 1 + .github/workflows/deploy.yaml | 1 - .github/workflows/distribute.yaml | 1 - .github/workflows/lint.yaml | 12 + .husky/pre-commit | 1 + .prettierignore | 28 + README.md | 121 ++-- integration_tests/cdk/README.md | 1 - integration_tests/cdk/cdk.json | 9 +- integration_tests/cdk/package-lock.json | 78 +-- integration_tests/cdk/package.json | 10 +- lib/stac-browser/index.ts | 314 +++++----- lib/tipg-api/index.ts | 201 +++--- lib/titiler-pgstac-api/index.ts | 277 ++++---- package-lock.json | 802 +++++++++++++++++++++++- package.json | 8 +- 17 files changed, 1353 insertions(+), 516 deletions(-) create mode 100644 .husky/pre-commit create mode 100644 .prettierignore diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 4bfa3af..042b590 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,4 +1,4 @@ { - "name": "VS Code DEV Container for AWS CDK development", - "image": "jsii/superchain:1-buster-slim-node16" + "name": "VS Code DEV Container for AWS CDK development", + "image": "jsii/superchain:1-buster-slim-node16" } diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 01aff37..6245a37 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,4 +1,5 @@ ## :warning: Checklist if your PR is changing anything else than documentation + - [ ] Posted the link to a successful manually triggered deployment workflow (successful including the resources destruction) ## Merge request description diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 69514f9..6134e59 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -30,7 +30,6 @@ jobs: - name: Generate distribution packages run: npm run package - - name: Install deployment environment id: install_deploy_env run: | diff --git a/.github/workflows/distribute.yaml b/.github/workflows/distribute.yaml index 0af0cd8..f07d0f2 100644 --- a/.github/workflows/distribute.yaml +++ b/.github/workflows/distribute.yaml @@ -13,7 +13,6 @@ jobs: runs-on: ubuntu-latest needs: package steps: - - uses: actions/download-artifact@v4 with: name: python diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 1ba24ca..c6b90f0 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -24,3 +24,15 @@ jobs: - name: Run pre-commit run: pre-commit run --all-files + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "18" + cache: "npm" + + - name: Install Node.js dependencies + run: npm ci + + - name: Run Prettier check + run: npx prettier --check . diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..2312dc5 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npx lint-staged diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..cc57ef5 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,28 @@ +CHANGELOG.md + +# Dependencies +node_modules/ + +# Build / Dist output +dist/ +lib/ # If TS output directory, make sure it's ignored if not meant to be formatted + +# Other generated files +.jsii +coverage/ +build/ + +# Package manager lock files +package-lock.json +yarn.lock +pnpm-lock.yaml + +# Diagrams or other specific directories +diagrams/ + +# Python specific (optional, if ruff handles *.py formatting) +.venv/ +toxenv/ +integration_tests/cdk/cdk.out/ +*.py + diff --git a/README.md b/README.md index 83f4ffc..43b4671 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,64 @@ -# eoAPI CDK Constructs - -eoapi-cdk is a package of [AWS CDK constructs](https://docs.aws.amazon.com/prescriptive-guidance/latest/best-practices-cdk-typescript-iac/constructs-best-practices.html) designed to encapsulate eoAPI services and best practices as simple reusable components. - - -For more background on the included services see [The Earth Observation API](https://eoapi.dev/) - -## Included constructs -Detailed API docs for the constructs can be found [here](https://developmentseed.org/eoapi-cdk/). - -### [pgSTAC Database](https://developmentseed.org/eoapi-cdk/#pgstacdatabase-) -An [RDS](https://aws.amazon.com/rds/) instance with [pgSTAC](https://github.com/stac-utils/pgstac) installed and the Postgres parameters optimized for the selected instance type. - -### [STAC API](https://developmentseed.org/eoapi-cdk/#pgstacapilambda-) -A STAC API implementation using [stac-fastapi](https://github.com/stac-utils/stac-fastapi) with a [pgSTAC backend](https://github.com/stac-utils/stac-fastapi-pgstac). Packaged as a complete runtime for deployment with API Gateway and Lambda. - -### [pgSTAC Titiler API](https://developmentseed.org/eoapi-cdk/#titilerpgstacapilambda-) -A complete dynamic tiling API using [titiler-pgstac](https://github.com/stac-utils/titiler-pgstac) to create dynamic mosaics of assets based on [STAC Search queries](https://github.com/radiantearth/stac-api-spec/tree/master/item-search). Packaged as a complete runtime for deployment with API Gateway and Lambda and fully integrated with the pgSTAC Database construct. - -### [STAC browser](https://developmentseed.org/eoapi-cdk/#stacbrowser-) -A CDK construct to host a static [Radiant Earth STAC browser](https://github.com/radiantearth/stac-browser) on S3. - -### [OGC Features/Tiles API](https://developmentseed.org/eoapi-cdk/#titilerpgstacapilambda-) -A complete OGC Features/Tiles API using [tipg](https://github.com/developmentseed/tipg). Packaged as a complete runtime for deployment with API Gateway and Lambda. By default the API will be connected to the Database's `public` schema. - -### [STAC Ingestor](https://developmentseed.org/eoapi-cdk/#stacingestor-) -An API for large scale STAC data ingestion and validation into a pgSTAC instance. - -![ingestor](/diagrams/ingestor_diagram.png) - -Authentication for the STAC Ingestor API can be configured with JWTs authenticated by JWKS. To learn more about securing FastAPI applications with this approach see [Securing FastAPI with JWKS (AWS Cognito, Auth0)](https://alukach.com/posts/fastapi-rs256-jwt/). - -A sample Cognito-based authentication system is available at [aws-asdi-auth](https://github.com/developmentseed/aws-asdi-auth). - -### [Bastion Host](https://developmentseed.org/eoapi-cdk/#bastionhost-) -A bastion host is a secure gateway that provides access to resources in a private subnet. In this case it provides the ability to make administrative connections to eoAPI's pgSTAC instance. - -![Alt text](/diagrams/bastion_diagram.png) - -For more background on bastion hosts in AWS see [this article](https://dev.to/aws-builders/bastion-host-in-aws-vpc-2i63). - -And for configuration instructions for this construct see [the docs](https://developmentseed.org/eoapi-cdk/#bastionhost-). - - -## Published Packages - -- https://pypi.org/project/eoapi-cdk/ -- https://www.npmjs.com/package/eoapi-cdk/ - -## Release - -Versioning is automatically handled via [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) and [Semantic Release](https://semantic-release.gitbook.io/semantic-release/). - -_Warning_: If you rebase `main`, you must ensure that the commits referenced by tags point to commits that are within the `main` branch. If a commit references a commit that is no longer on the `main` branch, Semantic Release will fail to detect the correct version of the project. [More information](https://github.com/semantic-release/semantic-release/issues/1121#issuecomment-517945233). - - -## Tests - +# eoAPI CDK Constructs + +eoapi-cdk is a package of [AWS CDK constructs](https://docs.aws.amazon.com/prescriptive-guidance/latest/best-practices-cdk-typescript-iac/constructs-best-practices.html) designed to encapsulate eoAPI services and best practices as simple reusable components. + +For more background on the included services see [The Earth Observation API](https://eoapi.dev/) + +## Included constructs + +Detailed API docs for the constructs can be found [here](https://developmentseed.org/eoapi-cdk/). + +### [pgSTAC Database](https://developmentseed.org/eoapi-cdk/#pgstacdatabase-) + +An [RDS](https://aws.amazon.com/rds/) instance with [pgSTAC](https://github.com/stac-utils/pgstac) installed and the Postgres parameters optimized for the selected instance type. + +### [STAC API](https://developmentseed.org/eoapi-cdk/#pgstacapilambda-) + +A STAC API implementation using [stac-fastapi](https://github.com/stac-utils/stac-fastapi) with a [pgSTAC backend](https://github.com/stac-utils/stac-fastapi-pgstac). Packaged as a complete runtime for deployment with API Gateway and Lambda. + +### [pgSTAC Titiler API](https://developmentseed.org/eoapi-cdk/#titilerpgstacapilambda-) + +A complete dynamic tiling API using [titiler-pgstac](https://github.com/stac-utils/titiler-pgstac) to create dynamic mosaics of assets based on [STAC Search queries](https://github.com/radiantearth/stac-api-spec/tree/master/item-search). Packaged as a complete runtime for deployment with API Gateway and Lambda and fully integrated with the pgSTAC Database construct. + +### [STAC browser](https://developmentseed.org/eoapi-cdk/#stacbrowser-) + +A CDK construct to host a static [Radiant Earth STAC browser](https://github.com/radiantearth/stac-browser) on S3. + +### [OGC Features/Tiles API](https://developmentseed.org/eoapi-cdk/#titilerpgstacapilambda-) + +A complete OGC Features/Tiles API using [tipg](https://github.com/developmentseed/tipg). Packaged as a complete runtime for deployment with API Gateway and Lambda. By default the API will be connected to the Database's `public` schema. + +### [STAC Ingestor](https://developmentseed.org/eoapi-cdk/#stacingestor-) + +An API for large scale STAC data ingestion and validation into a pgSTAC instance. + +![ingestor](/diagrams/ingestor_diagram.png) + +Authentication for the STAC Ingestor API can be configured with JWTs authenticated by JWKS. To learn more about securing FastAPI applications with this approach see [Securing FastAPI with JWKS (AWS Cognito, Auth0)](https://alukach.com/posts/fastapi-rs256-jwt/). + +A sample Cognito-based authentication system is available at [aws-asdi-auth](https://github.com/developmentseed/aws-asdi-auth). + +### [Bastion Host](https://developmentseed.org/eoapi-cdk/#bastionhost-) + +A bastion host is a secure gateway that provides access to resources in a private subnet. In this case it provides the ability to make administrative connections to eoAPI's pgSTAC instance. + +![Alt text](/diagrams/bastion_diagram.png) + +For more background on bastion hosts in AWS see [this article](https://dev.to/aws-builders/bastion-host-in-aws-vpc-2i63). + +And for configuration instructions for this construct see [the docs](https://developmentseed.org/eoapi-cdk/#bastionhost-). + +## Published Packages + +- https://pypi.org/project/eoapi-cdk/ +- https://www.npmjs.com/package/eoapi-cdk/ + +## Release + +Versioning is automatically handled via [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) and [Semantic Release](https://semantic-release.gitbook.io/semantic-release/). + +_Warning_: If you rebase `main`, you must ensure that the commits referenced by tags point to commits that are within the `main` branch. If a commit references a commit that is no longer on the `main` branch, Semantic Release will fail to detect the correct version of the project. [More information](https://github.com/semantic-release/semantic-release/issues/1121#issuecomment-517945233). + +## Tests + Each pull request to `main` is added to a [merge queue](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue#triggering-merge-group-checks-with-github-actions) so that a "deployment test" workflow can run before the merge actually happens. If the deployment fails, the merge is cancelled. Here is [the definition of this workflow](https://github.com/developmentseed/eoapi-cdk/blob/main/.github/workflows/deploy.yaml) and the [tests definition](https://github.com/developmentseed/eoapi-cdk/blob/main/tests). diff --git a/integration_tests/cdk/README.md b/integration_tests/cdk/README.md index bd70e8d..2a85625 100644 --- a/integration_tests/cdk/README.md +++ b/integration_tests/cdk/README.md @@ -1,4 +1,3 @@ - # Deployment CDK code for eoapi-cdk deployment tests This is a wrapper CDK code that is used to test a deployment of the `eoapi-cdk` constructs. diff --git a/integration_tests/cdk/cdk.json b/integration_tests/cdk/cdk.json index 294adbb..b87d4e0 100644 --- a/integration_tests/cdk/cdk.json +++ b/integration_tests/cdk/cdk.json @@ -1,9 +1,7 @@ { "app": "python3 app.py", "watch": { - "include": [ - "**" - ], + "include": ["**"], "exclude": [ "README.md", "cdk*.json", @@ -24,9 +22,6 @@ "@aws-cdk/aws-lambda:recognizeVersionProps": true, "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, - "@aws-cdk/core:target-partitions": [ - "aws", - "aws-cn" - ] + "@aws-cdk/core:target-partitions": ["aws", "aws-cn"] } } diff --git a/integration_tests/cdk/package-lock.json b/integration_tests/cdk/package-lock.json index e400afb..c70e983 100644 --- a/integration_tests/cdk/package-lock.json +++ b/integration_tests/cdk/package-lock.json @@ -1,42 +1,42 @@ { - "name": "eoapi-template", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "eoapi-template", - "version": "0.1.0", - "dependencies": { - "aws-cdk": "2.130.0" - } - }, - "node_modules/aws-cdk": { - "version": "2.130.0", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.130.0.tgz", - "integrity": "sha512-MsjGzQ2kZv0FEfXvpW7FTJRnefew0GrYt9M2SMN2Yn45+yjugGl2X8to416kABeFz1OFqW56hq8Y5BiLuFDVLQ==", - "bin": { - "cdk": "bin/cdk" - }, - "engines": { - "node": ">= 14.15.0" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - } + "name": "eoapi-template", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "eoapi-template", + "version": "0.1.0", + "dependencies": { + "aws-cdk": "2.130.0" + } + }, + "node_modules/aws-cdk": { + "version": "2.130.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.130.0.tgz", + "integrity": "sha512-MsjGzQ2kZv0FEfXvpW7FTJRnefew0GrYt9M2SMN2Yn45+yjugGl2X8to416kABeFz1OFqW56hq8Y5BiLuFDVLQ==", + "bin": { + "cdk": "bin/cdk" + }, + "engines": { + "node": ">= 14.15.0" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } } + } } diff --git a/integration_tests/cdk/package.json b/integration_tests/cdk/package.json index e5a5a6b..e421d0d 100644 --- a/integration_tests/cdk/package.json +++ b/integration_tests/cdk/package.json @@ -1,7 +1,7 @@ { - "name": "eoapi-template", - "version": "0.1.0", - "dependencies": { - "aws-cdk": "2.130.0" - } + "name": "eoapi-template", + "version": "0.1.0", + "dependencies": { + "aws-cdk": "2.130.0" } +} diff --git a/lib/stac-browser/index.ts b/lib/stac-browser/index.ts index d7263fc..373933a 100644 --- a/lib/stac-browser/index.ts +++ b/lib/stac-browser/index.ts @@ -1,167 +1,185 @@ -import { Stack, aws_s3 as s3, aws_s3_deployment as s3_deployment} from "aws-cdk-lib"; +import { + Stack, + aws_s3 as s3, + aws_s3_deployment as s3_deployment, +} from "aws-cdk-lib"; import { RemovalPolicy, CfnOutput } from "aws-cdk-lib"; import { PolicyStatement, ServicePrincipal, Effect } from "aws-cdk-lib/aws-iam"; import { Construct } from "constructs"; import { execSync } from "child_process"; -import * as fs from 'fs'; +import * as fs from "fs"; -const DEFAULT_CLONE_DIRECTORY = './stac-browser'; +const DEFAULT_CLONE_DIRECTORY = "./stac-browser"; export class StacBrowser extends Construct { - - public bucket: s3.IBucket; - public bucketDeployment: s3_deployment.BucketDeployment; - - constructor(scope: Construct, id: string, props: StacBrowserProps) { - super(scope, id); - - const buildPath = this.buildApp(props, props.cloneDirectory || DEFAULT_CLONE_DIRECTORY); - - // import a bucket from props.bucketArn if defined, otherwise create a new bucket - if (props.bucketArn) { - this.bucket = s3.Bucket.fromBucketArn(this, 'Bucket', props.bucketArn); - } else { - this.bucket = new s3.Bucket(this, 'Bucket', { - accessControl: s3.BucketAccessControl.PRIVATE, - removalPolicy: RemovalPolicy.DESTROY, - websiteIndexDocument: props.websiteIndexDocument - }) - } - - // if props.cloudFrontDistributionArn is defined and props.bucketArn is not defined, add a bucket policy to allow read access from the cloudfront distribution - if (props.cloudFrontDistributionArn && !props.bucketArn) { - this.bucket.addToResourcePolicy(new PolicyStatement({ - sid: 'AllowCloudFrontServicePrincipal', - effect: Effect.ALLOW, - actions: ['s3:GetObject'], - principals: [new ServicePrincipal('cloudfront.amazonaws.com')], - resources: [this.bucket.arnForObjects('*')], - conditions: { - 'StringEquals': { - 'aws:SourceArn': props.cloudFrontDistributionArn - } - } - })); - } - - // add the compiled code to the bucket as a bucket deployment - this.bucketDeployment = new s3_deployment.BucketDeployment(this, 'BucketDeployment', { - destinationBucket: this.bucket, - sources: [s3_deployment.Source.asset(buildPath)] - }); - - new CfnOutput(this, "bucket-name", { - exportName: `${Stack.of(this).stackName}-bucket-name`, - value: this.bucket.bucketName, - }); - + public bucket: s3.IBucket; + public bucketDeployment: s3_deployment.BucketDeployment; + + constructor(scope: Construct, id: string, props: StacBrowserProps) { + super(scope, id); + + const buildPath = this.buildApp( + props, + props.cloneDirectory || DEFAULT_CLONE_DIRECTORY + ); + + // import a bucket from props.bucketArn if defined, otherwise create a new bucket + if (props.bucketArn) { + this.bucket = s3.Bucket.fromBucketArn(this, "Bucket", props.bucketArn); + } else { + this.bucket = new s3.Bucket(this, "Bucket", { + accessControl: s3.BucketAccessControl.PRIVATE, + removalPolicy: RemovalPolicy.DESTROY, + websiteIndexDocument: props.websiteIndexDocument, + }); } - private buildApp(props: StacBrowserProps, cloneDirectory: string): string { - - // Define where to clone and build - const githubRepoUrl = 'https://github.com/radiantearth/stac-browser.git'; - - - // Maybe the repo already exists in cloneDirectory. Try checking out the desired version and if it fails, delete and reclone. - try { - console.log(`Checking if a valid cloned repo exists with version ${props.githubRepoTag}...`) - execSync(`git checkout tags/${props.githubRepoTag}`, { cwd: cloneDirectory }); - } - catch (error) { - - // if directory exists, raise an error - if (fs.existsSync(cloneDirectory)) { - throw new Error(`Directory ${cloneDirectory} already exists and is not a valid clone of ${githubRepoUrl}. Please delete this directory or specify a different cloneDirectory.`); - } - - // else, we clone and check out the version. - - // Clone the repo - console.log(`Cloning ${githubRepoUrl} into ${cloneDirectory}...`) - execSync(`git clone ${githubRepoUrl} ${cloneDirectory}`); - - // Check out the desired version - console.log(`Checking out version ${props.githubRepoTag}...`) - execSync(`git checkout tags/${props.githubRepoTag}`, { cwd: cloneDirectory }); - - } - - // Install the dependencies and build the application - console.log(`Installing dependencies`) - execSync('npm install', { cwd: cloneDirectory }); - - // If a config file is provided, copy it to the stac-browser directory at "config.js", replaces the default config.js. - if (props.configFilePath) { - // check that the file exists at this location. if not, raise an error and print current working directory. - if (!fs.existsSync(props.configFilePath)) { - throw new Error(`Config file ${props.configFilePath} does not exist. Current working directory is ${process.cwd()}`); - } - console.log(`Copying config file ${props.configFilePath} to ${cloneDirectory}/config.js`) - fs.copyFileSync(props.configFilePath, `${cloneDirectory}/config.js`); - } - - // Build the app with catalogUrl - console.log(`Building app with catalogUrl=${props.stacCatalogUrl} into ${cloneDirectory}`) - execSync(`npm run build -- --catalogUrl=${props.stacCatalogUrl}`, { cwd: cloneDirectory }); + // if props.cloudFrontDistributionArn is defined and props.bucketArn is not defined, add a bucket policy to allow read access from the cloudfront distribution + if (props.cloudFrontDistributionArn && !props.bucketArn) { + this.bucket.addToResourcePolicy( + new PolicyStatement({ + sid: "AllowCloudFrontServicePrincipal", + effect: Effect.ALLOW, + actions: ["s3:GetObject"], + principals: [new ServicePrincipal("cloudfront.amazonaws.com")], + resources: [this.bucket.arnForObjects("*")], + conditions: { + StringEquals: { + "aws:SourceArn": props.cloudFrontDistributionArn, + }, + }, + }) + ); + } - return './stac-browser/dist' + // add the compiled code to the bucket as a bucket deployment + this.bucketDeployment = new s3_deployment.BucketDeployment( + this, + "BucketDeployment", + { + destinationBucket: this.bucket, + sources: [s3_deployment.Source.asset(buildPath)], + } + ); + + new CfnOutput(this, "bucket-name", { + exportName: `${Stack.of(this).stackName}-bucket-name`, + value: this.bucket.bucketName, + }); + } + + private buildApp(props: StacBrowserProps, cloneDirectory: string): string { + // Define where to clone and build + const githubRepoUrl = "https://github.com/radiantearth/stac-browser.git"; + + // Maybe the repo already exists in cloneDirectory. Try checking out the desired version and if it fails, delete and reclone. + try { + console.log( + `Checking if a valid cloned repo exists with version ${props.githubRepoTag}...` + ); + execSync(`git checkout tags/${props.githubRepoTag}`, { + cwd: cloneDirectory, + }); + } catch (error) { + // if directory exists, raise an error + if (fs.existsSync(cloneDirectory)) { + throw new Error( + `Directory ${cloneDirectory} already exists and is not a valid clone of ${githubRepoUrl}. Please delete this directory or specify a different cloneDirectory.` + ); + } + + // else, we clone and check out the version. + + // Clone the repo + console.log(`Cloning ${githubRepoUrl} into ${cloneDirectory}...`); + execSync(`git clone ${githubRepoUrl} ${cloneDirectory}`); + + // Check out the desired version + console.log(`Checking out version ${props.githubRepoTag}...`); + execSync(`git checkout tags/${props.githubRepoTag}`, { + cwd: cloneDirectory, + }); + } + // Install the dependencies and build the application + console.log(`Installing dependencies`); + execSync("npm install", { cwd: cloneDirectory }); + + // If a config file is provided, copy it to the stac-browser directory at "config.js", replaces the default config.js. + if (props.configFilePath) { + // check that the file exists at this location. if not, raise an error and print current working directory. + if (!fs.existsSync(props.configFilePath)) { + throw new Error( + `Config file ${ + props.configFilePath + } does not exist. Current working directory is ${process.cwd()}` + ); + } + console.log( + `Copying config file ${props.configFilePath} to ${cloneDirectory}/config.js` + ); + fs.copyFileSync(props.configFilePath, `${cloneDirectory}/config.js`); } + // Build the app with catalogUrl + console.log( + `Building app with catalogUrl=${props.stacCatalogUrl} into ${cloneDirectory}` + ); + execSync(`npm run build -- --catalogUrl=${props.stacCatalogUrl}`, { + cwd: cloneDirectory, + }); + return "./stac-browser/dist"; + } } export interface StacBrowserProps { - - /** - * Bucket ARN. If specified, the identity used to deploy the stack must have the appropriate permissions to create a deployment for this bucket. - * In addition, if specified, `cloudFrontDistributionArn` is ignored since the policy of an imported resource can't be modified. - * - * @default - No bucket ARN. A new bucket will be created. - */ - - readonly bucketArn?: string; - - /** - * STAC catalog URL. Overrides the catalog URL in the stac-browser configuration. - */ - readonly stacCatalogUrl: string; - - /** - * Path to config file for the STAC browser. If not provided, default configuration in the STAC browser - * repository is used. - */ - readonly configFilePath?: string; - - /** - * Tag of the radiant earth stac-browser repo to use to build the app. - */ - readonly githubRepoTag: string; - - - /** - * The ARN of the cloudfront distribution that will be added to the bucket policy with read access. - * If `bucketArn` is specified, this parameter is ignored since the policy of an imported bucket can't be modified. - * - * @default - No cloudfront distribution ARN. The bucket policy will not be modified. - */ - readonly cloudFrontDistributionArn?: string; - - /** - * The name of the index document (e.g. "index.html") for the website. Enables static website - * hosting for this bucket. - * - * @default - No index document. - */ - readonly websiteIndexDocument?: string; - - /** - * Location in the filesystem where to compile the browser code. - * - * @default - DEFAULT_CLONE_DIRECTORY - */ - readonly cloneDirectory?: string; - + /** + * Bucket ARN. If specified, the identity used to deploy the stack must have the appropriate permissions to create a deployment for this bucket. + * In addition, if specified, `cloudFrontDistributionArn` is ignored since the policy of an imported resource can't be modified. + * + * @default - No bucket ARN. A new bucket will be created. + */ + + readonly bucketArn?: string; + + /** + * STAC catalog URL. Overrides the catalog URL in the stac-browser configuration. + */ + readonly stacCatalogUrl: string; + + /** + * Path to config file for the STAC browser. If not provided, default configuration in the STAC browser + * repository is used. + */ + readonly configFilePath?: string; + + /** + * Tag of the radiant earth stac-browser repo to use to build the app. + */ + readonly githubRepoTag: string; + + /** + * The ARN of the cloudfront distribution that will be added to the bucket policy with read access. + * If `bucketArn` is specified, this parameter is ignored since the policy of an imported bucket can't be modified. + * + * @default - No cloudfront distribution ARN. The bucket policy will not be modified. + */ + readonly cloudFrontDistributionArn?: string; + + /** + * The name of the index document (e.g. "index.html") for the website. Enables static website + * hosting for this bucket. + * + * @default - No index document. + */ + readonly websiteIndexDocument?: string; + + /** + * Location in the filesystem where to compile the browser code. + * + * @default - DEFAULT_CLONE_DIRECTORY + */ + readonly cloneDirectory?: string; } diff --git a/lib/tipg-api/index.ts b/lib/tipg-api/index.ts index a54813c..2e2f474 100644 --- a/lib/tipg-api/index.ts +++ b/lib/tipg-api/index.ts @@ -12,120 +12,123 @@ import { } from "aws-cdk-lib"; import { Construct } from "constructs"; import { CustomLambdaFunctionProps } from "../utils"; -import * as path from 'path'; +import * as path from "path"; - export class TiPgApiLambda extends Construct { - readonly url: string; - public tiPgLambdaFunction: lambda.Function; +export class TiPgApiLambda extends Construct { + readonly url: string; + public tiPgLambdaFunction: lambda.Function; - constructor(scope: Construct, id: string, props: TiPgApiLambdaProps) { - super(scope, id); + constructor(scope: Construct, id: string, props: TiPgApiLambdaProps) { + super(scope, id); - this.tiPgLambdaFunction = new lambda.Function(this, "lambda", { - // defaults - runtime: lambda.Runtime.PYTHON_3_11, - handler: "handler.handler", - memorySize: 1024, - logRetention: logs.RetentionDays.ONE_WEEK, - timeout: Duration.seconds(30), - code: lambda.Code.fromDockerBuild(path.join(__dirname, '..'), { - file: "tipg-api/runtime/Dockerfile", - buildArgs: { PYTHON_VERSION: '3.11' }, - }), - vpc: props.vpc, - vpcSubnets: props.subnetSelection, - allowPublicSubnet: true, - environment: { - PGSTAC_SECRET_ARN: props.dbSecret.secretArn, - DB_MIN_CONN_SIZE: "1", - DB_MAX_CONN_SIZE: "1", - ...props.apiEnv, - }, - // overwrites defaults with user-provided configurable properties - ...props.lambdaFunctionOptions - }); + this.tiPgLambdaFunction = new lambda.Function(this, "lambda", { + // defaults + runtime: lambda.Runtime.PYTHON_3_11, + handler: "handler.handler", + memorySize: 1024, + logRetention: logs.RetentionDays.ONE_WEEK, + timeout: Duration.seconds(30), + code: lambda.Code.fromDockerBuild(path.join(__dirname, ".."), { + file: "tipg-api/runtime/Dockerfile", + buildArgs: { PYTHON_VERSION: "3.11" }, + }), + vpc: props.vpc, + vpcSubnets: props.subnetSelection, + allowPublicSubnet: true, + environment: { + PGSTAC_SECRET_ARN: props.dbSecret.secretArn, + DB_MIN_CONN_SIZE: "1", + DB_MAX_CONN_SIZE: "1", + ...props.apiEnv, + }, + // overwrites defaults with user-provided configurable properties + ...props.lambdaFunctionOptions, + }); - props.dbSecret.grantRead(this.tiPgLambdaFunction); + props.dbSecret.grantRead(this.tiPgLambdaFunction); - if (props.vpc){ - this.tiPgLambdaFunction.connections.allowTo(props.db, ec2.Port.tcp(5432), "allow connections from tipg"); - } + if (props.vpc) { + this.tiPgLambdaFunction.connections.allowTo( + props.db, + ec2.Port.tcp(5432), + "allow connections from tipg" + ); + } - const tipgApi = new apigatewayv2.HttpApi( - this, - `${Stack.of(this).stackName}-tipg-api`, - { - defaultDomainMapping: props.tipgApiDomainName + const tipgApi = new apigatewayv2.HttpApi( + this, + `${Stack.of(this).stackName}-tipg-api`, + { + defaultDomainMapping: props.tipgApiDomainName + ? { + domainName: props.tipgApiDomainName, + } + : undefined, + defaultIntegration: new apigatewayv2_integrations.HttpLambdaIntegration( + "integration", + this.tiPgLambdaFunction, + props.tipgApiDomainName ? { - domainName: props.tipgApiDomainName, + parameterMapping: + new apigatewayv2.ParameterMapping().overwriteHeader( + "host", + apigatewayv2.MappingValue.custom( + props.tipgApiDomainName.name + ) + ), } - : undefined, - defaultIntegration: - new apigatewayv2_integrations.HttpLambdaIntegration( - "integration", - this.tiPgLambdaFunction, - props.tipgApiDomainName - ? { - parameterMapping: - new apigatewayv2.ParameterMapping().overwriteHeader( - "host", - apigatewayv2.MappingValue.custom( - props.tipgApiDomainName.name - ) - ), - } - : undefined - ), - } - ); + : undefined + ), + } + ); - this.url = tipgApi.url!; + this.url = tipgApi.url!; - new CfnOutput(this, "tipg-api-output", { - exportName: `${Stack.of(this).stackName}-tip-url`, - value: this.url, - }); - } + new CfnOutput(this, "tipg-api-output", { + exportName: `${Stack.of(this).stackName}-tip-url`, + value: this.url, + }); } +} - export interface TiPgApiLambdaProps { - /** - * VPC into which the lambda should be deployed. - */ - readonly vpc?: ec2.IVpc; +export interface TiPgApiLambdaProps { + /** + * VPC into which the lambda should be deployed. + */ + readonly vpc?: ec2.IVpc; - /** - * RDS Instance with installed pgSTAC or pgbouncer server. - */ - readonly db: rds.IDatabaseInstance | ec2.IInstance; + /** + * RDS Instance with installed pgSTAC or pgbouncer server. + */ + readonly db: rds.IDatabaseInstance | ec2.IInstance; - /** - * Subnet into which the lambda should be deployed. - */ - readonly subnetSelection?: ec2.SubnetSelection; + /** + * Subnet into which the lambda should be deployed. + */ + readonly subnetSelection?: ec2.SubnetSelection; - /** - * Secret containing connection information for pgSTAC database. - */ - readonly dbSecret: secretsmanager.ISecret; + /** + * Secret containing connection information for pgSTAC database. + */ + readonly dbSecret: secretsmanager.ISecret; - /** - * Customized environment variables to send to titiler-pgstac runtime. - */ - readonly apiEnv?: Record; + /** + * Customized environment variables to send to titiler-pgstac runtime. + */ + readonly apiEnv?: Record; - /** - * Custom Domain Name for tipg API. If defined, will create the - * domain name and integrate it with the tipg API. - * - * @default - undefined - */ - readonly tipgApiDomainName?: apigatewayv2.IDomainName; + /** + * Custom Domain Name for tipg API. If defined, will create the + * domain name and integrate it with the tipg API. + * + * @default - undefined + */ + readonly tipgApiDomainName?: apigatewayv2.IDomainName; - /** - * Can be used to override the default lambda function properties. - * - * @default - defined in the construct. - */ - readonly lambdaFunctionOptions?: CustomLambdaFunctionProps; - } + /** + * Can be used to override the default lambda function properties. + * + * @default - defined in the construct. + */ + readonly lambdaFunctionOptions?: CustomLambdaFunctionProps; +} diff --git a/lib/titiler-pgstac-api/index.ts b/lib/titiler-pgstac-api/index.ts index f4a9748..0289a45 100644 --- a/lib/titiler-pgstac-api/index.ts +++ b/lib/titiler-pgstac-api/index.ts @@ -13,147 +13,162 @@ import { } from "aws-cdk-lib"; import { Construct } from "constructs"; import { CustomLambdaFunctionProps } from "../utils"; -import * as path from 'path'; - - // default settings that can be overridden by the user-provided environment. - let defaultTitilerPgstacEnv :{ [key: string]: any } = { - "CPL_VSIL_CURL_ALLOWED_EXTENSIONS": ".tif,.TIF,.tiff", - "GDAL_CACHEMAX": "200", - "GDAL_DISABLE_READDIR_ON_OPEN": "EMPTY_DIR", - "GDAL_INGESTED_BYTES_AT_OPEN": "32768", - "GDAL_HTTP_MERGE_CONSECUTIVE_RANGES": "YES", - "GDAL_HTTP_MULTIPLEX": "YES", - "GDAL_HTTP_VERSION": "2", - "PYTHONWARNINGS": "ignore", - "VSI_CACHE": "TRUE", - "VSI_CACHE_SIZE": "5000000", - "DB_MIN_CONN_SIZE": "1", - "DB_MAX_CONN_SIZE": "1" - } - - export class TitilerPgstacApiLambda extends Construct { - readonly url: string; - public titilerPgstacLambdaFunction: lambda.Function; - - constructor(scope: Construct, id: string, props: TitilerPgStacApiLambdaProps) { - super(scope, id); - - this.titilerPgstacLambdaFunction = new lambda.Function(this, "lambda", { - // defaults - runtime: lambda.Runtime.PYTHON_3_11, - handler: "handler.handler", - memorySize: 3008, - logRetention: aws_logs.RetentionDays.ONE_WEEK, - timeout: Duration.seconds(30), - code: lambda.Code.fromDockerBuild(path.join(__dirname, '..'), { - file: "titiler-pgstac-api/runtime/Dockerfile", - buildArgs: { PYTHON_VERSION: '3.11' } - }), - vpc: props.vpc, - vpcSubnets: props.subnetSelection, - allowPublicSubnet: true, - // if user provided environment variables, merge them with the defaults. - environment: props.apiEnv ? { ...defaultTitilerPgstacEnv, ...props.apiEnv, "PGSTAC_SECRET_ARN": props.dbSecret.secretArn } : defaultTitilerPgstacEnv, - // overwrites defaults with user-provided configurable properties - ...props.lambdaFunctionOptions, - }); - - // grant access to buckets using addToRolePolicy - if (props.buckets) { - props.buckets.forEach(bucket => { - this.titilerPgstacLambdaFunction.addToRolePolicy(new iam.PolicyStatement({ +import * as path from "path"; + +// default settings that can be overridden by the user-provided environment. +let defaultTitilerPgstacEnv: { [key: string]: any } = { + CPL_VSIL_CURL_ALLOWED_EXTENSIONS: ".tif,.TIF,.tiff", + GDAL_CACHEMAX: "200", + GDAL_DISABLE_READDIR_ON_OPEN: "EMPTY_DIR", + GDAL_INGESTED_BYTES_AT_OPEN: "32768", + GDAL_HTTP_MERGE_CONSECUTIVE_RANGES: "YES", + GDAL_HTTP_MULTIPLEX: "YES", + GDAL_HTTP_VERSION: "2", + PYTHONWARNINGS: "ignore", + VSI_CACHE: "TRUE", + VSI_CACHE_SIZE: "5000000", + DB_MIN_CONN_SIZE: "1", + DB_MAX_CONN_SIZE: "1", +}; + +export class TitilerPgstacApiLambda extends Construct { + readonly url: string; + public titilerPgstacLambdaFunction: lambda.Function; + + constructor( + scope: Construct, + id: string, + props: TitilerPgStacApiLambdaProps + ) { + super(scope, id); + + this.titilerPgstacLambdaFunction = new lambda.Function(this, "lambda", { + // defaults + runtime: lambda.Runtime.PYTHON_3_11, + handler: "handler.handler", + memorySize: 3008, + logRetention: aws_logs.RetentionDays.ONE_WEEK, + timeout: Duration.seconds(30), + code: lambda.Code.fromDockerBuild(path.join(__dirname, ".."), { + file: "titiler-pgstac-api/runtime/Dockerfile", + buildArgs: { PYTHON_VERSION: "3.11" }, + }), + vpc: props.vpc, + vpcSubnets: props.subnetSelection, + allowPublicSubnet: true, + // if user provided environment variables, merge them with the defaults. + environment: props.apiEnv + ? { + ...defaultTitilerPgstacEnv, + ...props.apiEnv, + PGSTAC_SECRET_ARN: props.dbSecret.secretArn, + } + : defaultTitilerPgstacEnv, + // overwrites defaults with user-provided configurable properties + ...props.lambdaFunctionOptions, + }); + + // grant access to buckets using addToRolePolicy + if (props.buckets) { + props.buckets.forEach((bucket) => { + this.titilerPgstacLambdaFunction.addToRolePolicy( + new iam.PolicyStatement({ actions: ["s3:GetObject"], resources: [`arn:aws:s3:::${bucket}/*`], - })); - }); - } + }) + ); + }); + } - props.dbSecret.grantRead(this.titilerPgstacLambdaFunction); + props.dbSecret.grantRead(this.titilerPgstacLambdaFunction); - if (props.vpc) { - this.titilerPgstacLambdaFunction.connections.allowTo(props.db, ec2.Port.tcp(5432), "allow connections from titiler"); - } + if (props.vpc) { + this.titilerPgstacLambdaFunction.connections.allowTo( + props.db, + ec2.Port.tcp(5432), + "allow connections from titiler" + ); + } - const stacApi = new apigatewayv2.HttpApi( - this, - `${Stack.of(this).stackName}-titiler-pgstac-api`, - { - defaultDomainMapping: props.titilerPgstacApiDomainName + const stacApi = new apigatewayv2.HttpApi( + this, + `${Stack.of(this).stackName}-titiler-pgstac-api`, + { + defaultDomainMapping: props.titilerPgstacApiDomainName + ? { + domainName: props.titilerPgstacApiDomainName, + } + : undefined, + defaultIntegration: new apigatewayv2_integrations.HttpLambdaIntegration( + "integration", + this.titilerPgstacLambdaFunction, + props.titilerPgstacApiDomainName ? { - domainName: props.titilerPgstacApiDomainName, + parameterMapping: + new apigatewayv2.ParameterMapping().overwriteHeader( + "host", + apigatewayv2.MappingValue.custom( + props.titilerPgstacApiDomainName.name + ) + ), } - : undefined, - defaultIntegration: - new apigatewayv2_integrations.HttpLambdaIntegration( - "integration", - this.titilerPgstacLambdaFunction, - props.titilerPgstacApiDomainName - ? { - parameterMapping: - new apigatewayv2.ParameterMapping().overwriteHeader( - "host", - apigatewayv2.MappingValue.custom( - props.titilerPgstacApiDomainName.name - ) - ), - } - : undefined - ), - } - ); + : undefined + ), + } + ); - this.url = stacApi.url!; + this.url = stacApi.url!; - new CfnOutput(this, "titiler-pgstac-api-output", { - exportName: `${Stack.of(this).stackName}-titiler-pgstac-url`, - value: this.url, - }); - } + new CfnOutput(this, "titiler-pgstac-api-output", { + exportName: `${Stack.of(this).stackName}-titiler-pgstac-url`, + value: this.url, + }); } - - export interface TitilerPgStacApiLambdaProps { - /** - * VPC into which the lambda should be deployed. - */ - readonly vpc?: ec2.IVpc; - - /** - * RDS Instance with installed pgSTAC or pgbouncer server. - */ - readonly db: rds.IDatabaseInstance | ec2.IInstance; - - /** - * Subnet into which the lambda should be deployed. - */ - readonly subnetSelection?: ec2.SubnetSelection; - - /** - * Secret containing connection information for pgSTAC database. - */ - readonly dbSecret: secretsmanager.ISecret; - - /** +} + +export interface TitilerPgStacApiLambdaProps { + /** + * VPC into which the lambda should be deployed. + */ + readonly vpc?: ec2.IVpc; + + /** + * RDS Instance with installed pgSTAC or pgbouncer server. + */ + readonly db: rds.IDatabaseInstance | ec2.IInstance; + + /** + * Subnet into which the lambda should be deployed. + */ + readonly subnetSelection?: ec2.SubnetSelection; + + /** + * Secret containing connection information for pgSTAC database. + */ + readonly dbSecret: secretsmanager.ISecret; + + /** * Customized environment variables to send to titiler-pgstac runtime. These will be merged with `defaultTitilerPgstacEnv`. * The database secret arn is automatically added to the environment variables at deployment. /*/ - readonly apiEnv?: Record; - - /** - * list of buckets the lambda will be granted access to. - */ - readonly buckets?: string[]; - - /** - * Custom Domain Name Options for Titiler Pgstac API, - * - * @default - undefined. - */ - readonly titilerPgstacApiDomainName?: apigatewayv2.IDomainName; - - /** - * Can be used to override the default lambda function properties. - * - * @default - defined in the construct. - */ - readonly lambdaFunctionOptions?: CustomLambdaFunctionProps; - } + readonly apiEnv?: Record; + + /** + * list of buckets the lambda will be granted access to. + */ + readonly buckets?: string[]; + + /** + * Custom Domain Name Options for Titiler Pgstac API, + * + * @default - undefined. + */ + readonly titilerPgstacApiDomainName?: apigatewayv2.IDomainName; + + /** + * Can be used to override the default lambda function properties. + * + * @default - defined in the construct. + */ + readonly lambdaFunctionOptions?: CustomLambdaFunctionProps; +} diff --git a/package-lock.json b/package-lock.json index 16a6fb2..0d7e90a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,9 +15,11 @@ "@types/node": "^18.7.23", "aws-cdk-lib": "^2.190.0", "constructs": "10.3.0", + "husky": "^9.1.7", "jsii": "5.7.4", "jsii-docgen": "10.6.1", "jsii-pacmak": "1.106.0", + "lint-staged": "^15.5.1", "nodemon": "^2.0.20", "npm-run-all": "^4.1.5", "prettier": "^2.7.1", @@ -1523,12 +1525,13 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "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.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -1684,6 +1687,22 @@ "node": ">=6" } }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cli-table3": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", @@ -1699,6 +1718,77 @@ "@colors/colors": "1.5.0" } }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -1770,6 +1860,13 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -2007,12 +2104,13 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -2284,6 +2382,19 @@ "node": ">=10.17" } }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2434,6 +2545,13 @@ "node": ">=4" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true, + "license": "MIT" + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -2546,10 +2664,11 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "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" }, @@ -2740,6 +2859,19 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -3134,6 +3266,22 @@ "node": ">=10.17.0" } }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -3395,6 +3543,7 @@ "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" } @@ -4020,12 +4169,349 @@ "node": ">=0.10.0" } }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, "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 }, + "node_modules/lint-staged": { + "version": "15.5.1", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.5.1.tgz", + "integrity": "sha512-6m7u8mue4Xn6wK6gZvSCQwBvMBR36xfY24nF5bMTf2MHDYG6S3yhJuOgdYVw99hsjyDt2d4z168b3naI8+NWtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.4.1", + "commander": "^13.1.0", + "debug": "^4.4.0", + "execa": "^8.0.1", + "lilconfig": "^3.1.3", + "listr2": "^8.2.5", + "micromatch": "^4.0.8", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.7.0" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/lint-staged/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/lint-staged/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/lint-staged/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/lint-staged/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/yaml": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", + "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/listr2": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.3.2.tgz", + "integrity": "sha512-vsBzcU4oE+v0lj4FhVLzr9dBTv4/fHIa57l+GCwovP8MoFNZJTOhGU8PXd4v2VJCbECAaijBiHntiekFMLvo0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/listr2/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -4117,6 +4603,160 @@ "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", "dev": true }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/log4js": { "version": "6.9.1", "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", @@ -4321,12 +4961,13 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "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.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -4375,6 +5016,19 @@ "node": ">=6" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -4429,10 +5083,11 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "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/neo-async": { "version": "2.6.2", @@ -8089,6 +8744,52 @@ "node": ">=8" } }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -8100,10 +8801,11 @@ } }, "node_modules/rfdc": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", - "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", - "dev": true + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" }, "node_modules/rimraf": { "version": "3.0.2", @@ -8550,6 +9252,49 @@ "node": ">=8" } }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/sort-json": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/sort-json/-/sort-json-2.0.1.tgz", @@ -8742,6 +9487,16 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -9038,6 +9793,7 @@ "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" }, diff --git a/package.json b/package.json index 8dd2bbb..d365d79 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "docgen": "mkdir -p docs && jsii-docgen -o docs/index.md", "test": "echo \"Error: no test specified\" && exit 1", "semantic-release": "semantic-release", - "install:all": "npm ci && cd lib/database/lambda/pgbouncer-secret-updater && npm ci && cd ../../../../" + "install:all": "npm ci && cd lib/database/lambda/pgbouncer-secret-updater && npm ci && cd ../../../../", + "prepare": "husky" }, "keywords": [], "author": { @@ -38,6 +39,9 @@ } } }, + "lint-staged": { + "**/*.{js,ts,jsx,tsx,json,css,scss,md,yaml,yml}": "prettier --write --ignore-unknown" + }, "devDependencies": { "@qiwi/semantic-release-gh-pages-plugin": "^5.2.3", "@semantic-release/changelog": "^6.0.1", @@ -45,9 +49,11 @@ "@types/node": "^18.7.23", "aws-cdk-lib": "^2.190.0", "constructs": "10.3.0", + "husky": "^9.1.7", "jsii": "5.7.4", "jsii-docgen": "10.6.1", "jsii-pacmak": "1.106.0", + "lint-staged": "^15.5.1", "nodemon": "^2.0.20", "npm-run-all": "^4.1.5", "prettier": "^2.7.1", From de37d26d230264725e62591ad2eb1603581ef4b3 Mon Sep 17 00:00:00 2001 From: hrodmn Date: Tue, 29 Apr 2025 05:46:54 -0500 Subject: [PATCH 2/2] remove trailing whitespace --- .prettierignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.prettierignore b/.prettierignore index cc57ef5..702734d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -25,4 +25,3 @@ diagrams/ toxenv/ integration_tests/cdk/cdk.out/ *.py -